2021-01-18 13:20:06 +01:00
|
|
|
// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
|
2020-08-27 06:46:18 +02:00
|
|
|
// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
|
2020-01-18 23:26:14 +01:00
|
|
|
module checker
|
|
|
|
|
2020-11-05 09:12:32 +01:00
|
|
|
import os
|
2020-11-18 20:52:00 +01:00
|
|
|
import strings
|
2021-03-22 18:43:06 +01:00
|
|
|
import time
|
2020-04-25 17:49:16 +02:00
|
|
|
import v.ast
|
2020-11-05 09:12:32 +01:00
|
|
|
import v.vmod
|
2020-04-25 17:49:16 +02:00
|
|
|
import v.token
|
|
|
|
import v.pref
|
|
|
|
import v.util
|
2020-04-27 15:08:04 +02:00
|
|
|
import v.errors
|
2020-10-29 10:57:23 +01:00
|
|
|
import v.pkgconfig
|
2021-04-26 23:43:00 +02:00
|
|
|
import v.gen.native
|
2020-01-18 23:26:14 +01:00
|
|
|
|
2021-03-15 05:22:22 +01:00
|
|
|
const int_min = int(0x80000000)
|
|
|
|
|
|
|
|
const int_max = int(0x7FFFFFFF)
|
2020-02-19 19:54:36 +01:00
|
|
|
|
2020-09-18 00:58:54 +02:00
|
|
|
const (
|
2021-04-19 18:01:47 +02:00
|
|
|
valid_comp_if_os = ['windows', 'ios', 'macos', 'mach', 'darwin', 'hpux', 'gnu',
|
|
|
|
'qnx', 'linux', 'freebsd', 'openbsd', 'netbsd', 'bsd', 'dragonfly', 'android', 'solaris',
|
|
|
|
'haiku', 'linux_or_macos']
|
|
|
|
valid_comp_if_compilers = ['gcc', 'tinyc', 'clang', 'mingw', 'msvc', 'cplusplus']
|
2021-04-26 18:01:42 +02:00
|
|
|
valid_comp_if_platforms = ['amd64', 'aarch64', 'arm64', 'x64', 'x32', 'little_endian',
|
|
|
|
'big_endian',
|
|
|
|
]
|
2021-04-19 18:01:47 +02:00
|
|
|
valid_comp_if_other = ['js', 'debug', 'prod', 'test', 'glibc', 'prealloc',
|
2021-04-09 22:24:25 +02:00
|
|
|
'no_bounds_checking', 'freestanding']
|
2021-04-19 18:01:47 +02:00
|
|
|
array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort',
|
2021-03-19 13:19:38 +01:00
|
|
|
'contains', 'index', 'wait', 'any', 'all', 'first', 'last', 'pop']
|
2021-04-19 18:01:47 +02:00
|
|
|
vroot_is_deprecated_message = '@VROOT is deprecated, use @VMODROOT or @VEXEROOT instead'
|
2020-09-18 00:58:54 +02:00
|
|
|
)
|
|
|
|
|
2020-01-18 23:26:14 +01:00
|
|
|
pub struct Checker {
|
2021-01-12 04:38:43 +01:00
|
|
|
pref &pref.Preferences // Preferences shared from V struct
|
2020-05-09 15:16:48 +02:00
|
|
|
pub mut:
|
2021-04-02 00:57:09 +02:00
|
|
|
table &ast.Table
|
2021-01-12 04:38:43 +01:00
|
|
|
file &ast.File = 0
|
|
|
|
nr_errors int
|
|
|
|
nr_warnings int
|
2021-03-22 18:43:06 +01:00
|
|
|
nr_notices int
|
2021-01-12 04:38:43 +01:00
|
|
|
errors []errors.Error
|
|
|
|
warnings []errors.Warning
|
2021-03-22 18:43:06 +01:00
|
|
|
notices []errors.Notice
|
2021-01-12 04:38:43 +01:00
|
|
|
error_lines []int // to avoid printing multiple errors for the same line
|
2021-04-02 00:57:09 +02:00
|
|
|
expected_type ast.Type
|
|
|
|
expected_or_type ast.Type // fn() or { 'this type' } eg. string. expected or block type
|
2021-01-12 04:38:43 +01:00
|
|
|
cur_fn &ast.FnDecl // current function
|
|
|
|
const_decl string
|
|
|
|
const_deps []string
|
|
|
|
const_names []string
|
|
|
|
global_names []string
|
|
|
|
locked_names []string // vars that are currently locked
|
|
|
|
rlocked_names []string // vars that are currently read-locked
|
|
|
|
in_for_count int // if checker is currently in a for loop
|
2020-10-14 12:49:55 +02:00
|
|
|
// checked_ident string // to avoid infinite checker loops
|
2021-04-16 10:53:20 +02:00
|
|
|
returns bool
|
|
|
|
scope_returns bool
|
|
|
|
mod string // current module name
|
|
|
|
is_builtin_mod bool // are we in `builtin`?
|
|
|
|
inside_unsafe bool
|
|
|
|
inside_const bool
|
|
|
|
inside_anon_fn bool
|
|
|
|
inside_ref_lit bool
|
2021-04-25 20:40:38 +02:00
|
|
|
inside_fn_arg bool // `a`, `b` in `a.f(b)`
|
2021-04-16 10:53:20 +02:00
|
|
|
skip_flags bool // should `#flag` and `#include` be skipped
|
2020-06-08 13:10:47 +02:00
|
|
|
mut:
|
2021-02-05 08:05:13 +01:00
|
|
|
files []ast.File
|
2021-01-03 21:10:25 +01:00
|
|
|
expr_level int // to avoid infinite recursion segfaults due to compiler bugs
|
2020-11-11 09:18:15 +01:00
|
|
|
inside_sql bool // to handle sql table fields pseudo variables
|
2021-04-02 00:57:09 +02:00
|
|
|
cur_orm_ts ast.TypeSymbol
|
2020-11-11 09:18:15 +01:00
|
|
|
error_details []string
|
2021-04-02 00:57:09 +02:00
|
|
|
vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path**
|
|
|
|
vweb_gen_types []ast.Type // vweb route checks
|
|
|
|
prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type, stopping unwrapping then
|
|
|
|
loop_label string // set when inside a labelled for loop
|
2020-12-19 11:49:28 +01:00
|
|
|
timers &util.Timers = util.new_timers(false)
|
2021-04-02 00:57:09 +02:00
|
|
|
comptime_fields_type map[string]ast.Type
|
2021-01-21 11:09:19 +01:00
|
|
|
fn_scope &ast.Scope = voidptr(0)
|
2021-02-05 08:05:13 +01:00
|
|
|
used_fns map[string]bool // used_fns['println'] == true
|
|
|
|
main_fn_decl_node ast.FnDecl
|
2021-03-15 05:22:22 +01:00
|
|
|
match_exhaustive_cutoff_limit int = 10
|
2021-02-28 20:24:29 +01:00
|
|
|
// TODO: these are here temporarily and used for deprecations; remove soon
|
|
|
|
using_new_err_struct bool
|
|
|
|
inside_selector_expr bool
|
|
|
|
inside_println_arg bool
|
2021-04-28 06:47:00 +02:00
|
|
|
inside_decl_rhs bool
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn new_checker(table &ast.Table, pref &pref.Preferences) Checker {
|
2020-12-19 12:16:18 +01:00
|
|
|
mut timers_should_print := false
|
|
|
|
$if time_checking ? {
|
|
|
|
timers_should_print = true
|
|
|
|
}
|
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-12-19 11:24:29 +01:00
|
|
|
timers: util.new_timers(timers_should_print)
|
2021-03-15 05:22:22 +01:00
|
|
|
match_exhaustive_cutoff_limit: pref.checker_match_exhaustive_cutoff_limit
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-08 09:14:24 +01:00
|
|
|
pub fn (mut c Checker) check(ast_file &ast.File) {
|
2021-04-19 18:01:47 +02:00
|
|
|
c.change_current_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 {
|
2021-01-09 05:36:38 +01:00
|
|
|
c.error('module name `$ast_import.mod` duplicate', ast_import.mod_pos)
|
2020-04-30 09:33:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
for stmt in ast_file.stmts {
|
2020-06-08 13:10:47 +02:00
|
|
|
c.expr_level = 0
|
2020-01-18 23:26:14 +01:00
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
2020-06-20 04:42:08 +02:00
|
|
|
c.check_scope_vars(c.file.scope)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) {
|
|
|
|
for _, obj in sc.objects {
|
2020-11-25 12:09:40 +01:00
|
|
|
match obj {
|
2020-06-02 09:00:51 +02:00
|
|
|
ast.Var {
|
2020-06-20 04:42:08 +02:00
|
|
|
if !c.pref.is_repl {
|
2021-04-23 12:33:48 +02:00
|
|
|
if !obj.is_used && obj.name[0] != `_` && !c.file.is_test {
|
2020-10-24 19:29:24 +02:00
|
|
|
c.warn('unused variable: `$obj.name`', obj.pos)
|
2020-06-20 04:42:08 +02:00
|
|
|
}
|
2020-06-02 09:00:51 +02:00
|
|
|
}
|
2020-09-22 05:28:29 +02:00
|
|
|
if obj.is_mut && !obj.is_changed && !c.is_builtin_mod && obj.name != 'it' {
|
|
|
|
// if obj.is_mut && !obj.is_changed && !c.is_builtin { //TODO C error bad field not checked
|
|
|
|
// c.warn('`$obj.name` is declared as mutable, but it was never changed',
|
|
|
|
// obj.pos)
|
|
|
|
}
|
2020-06-02 09:00:51 +02:00
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
2021-02-18 10:30:43 +01:00
|
|
|
for child in sc.children {
|
2020-06-20 04:42:08 +02:00
|
|
|
c.check_scope_vars(child)
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-06-02 09:00:51 +02:00
|
|
|
// not used right now
|
2020-11-08 09:14:24 +01:00
|
|
|
pub fn (mut c Checker) check2(ast_file &ast.File) []errors.Error {
|
2021-04-19 18:01:47 +02:00
|
|
|
c.change_current_file(ast_file)
|
2020-02-20 17:05:16 +01:00
|
|
|
for stmt in ast_file.stmts {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
|
|
|
return c.errors
|
|
|
|
}
|
|
|
|
|
2021-04-19 18:01:47 +02:00
|
|
|
pub fn (mut c Checker) change_current_file(file &ast.File) {
|
|
|
|
c.file = file
|
|
|
|
c.vmod_file_content = ''
|
|
|
|
c.mod = file.mod.name
|
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
pub fn (mut c Checker) check_files(ast_files []ast.File) {
|
2021-02-05 08:05:13 +01:00
|
|
|
// c.files = ast_files
|
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-07-01 00:53:53 +02:00
|
|
|
mut files_from_main_module := []&ast.File{}
|
2020-07-03 15:10:39 +02:00
|
|
|
for i in 0 .. ast_files.len {
|
2020-12-23 19:13:42 +01:00
|
|
|
file := unsafe { &ast_files[i] }
|
2020-12-19 11:24:29 +01:00
|
|
|
c.timers.start('checker_check $file.path')
|
2020-10-05 17:40:51 +02:00
|
|
|
c.check(file)
|
|
|
|
if file.mod.name == 'main' {
|
|
|
|
files_from_main_module << file
|
|
|
|
has_main_mod_file = true
|
2021-01-27 13:54:33 +01:00
|
|
|
if c.file_has_main_fn(file) {
|
2020-10-05 17:40:51 +02:00
|
|
|
has_main_fn = true
|
2020-04-18 00:20:38 +02:00
|
|
|
}
|
|
|
|
}
|
2020-12-19 11:24:29 +01:00
|
|
|
c.timers.show('checker_check $file.path')
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-07-01 00:53:53 +02:00
|
|
|
if has_main_mod_file && !has_main_fn && files_from_main_module.len > 0 {
|
|
|
|
if c.pref.is_script && !c.pref.is_test {
|
2021-01-30 09:19:29 +01:00
|
|
|
// files_from_main_module contain preludes at the start
|
|
|
|
mut the_main_file := files_from_main_module.last()
|
|
|
|
the_main_file.stmts << ast.FnDecl{
|
2020-07-01 00:53:53 +02:00
|
|
|
name: 'main.main'
|
|
|
|
mod: 'main'
|
2021-03-05 12:19:39 +01:00
|
|
|
is_main: true
|
2021-01-30 09:19:29 +01:00
|
|
|
file: the_main_file.path
|
2021-04-02 00:57:09 +02:00
|
|
|
return_type: ast.void_type
|
2020-12-12 09:01:12 +01:00
|
|
|
scope: &ast.Scope{
|
|
|
|
parent: 0
|
|
|
|
}
|
2020-07-01 00:53:53 +02:00
|
|
|
}
|
|
|
|
has_main_fn = true
|
|
|
|
}
|
|
|
|
}
|
2020-12-19 11:24:29 +01:00
|
|
|
c.timers.start('checker_post_process_generic_fns')
|
2021-01-30 09:19:29 +01:00
|
|
|
last_file := c.file
|
2020-12-01 05:10:06 +01:00
|
|
|
// post process generic functions. must be done after all files have been
|
|
|
|
// checked, to eunsure all generic calls are processed as this information
|
|
|
|
// is needed when the generic type is auto inferred from the call argument
|
2020-12-01 06:43:10 +01:00
|
|
|
for i in 0 .. ast_files.len {
|
2020-12-23 19:13:42 +01:00
|
|
|
file := unsafe { &ast_files[i] }
|
2020-12-01 06:43:10 +01:00
|
|
|
if file.generic_fns.len > 0 {
|
2021-04-19 18:01:47 +02:00
|
|
|
c.change_current_file(file)
|
2020-12-01 06:43:10 +01:00
|
|
|
c.post_process_generic_fns()
|
2020-12-01 05:10:06 +01:00
|
|
|
}
|
|
|
|
}
|
2021-01-30 09:19:29 +01:00
|
|
|
// restore the original c.file && c.mod after post processing
|
2021-04-19 18:01:47 +02:00
|
|
|
c.change_current_file(last_file)
|
2020-12-19 11:24:29 +01:00
|
|
|
c.timers.show('checker_post_process_generic_fns')
|
|
|
|
//
|
|
|
|
c.timers.start('checker_verify_all_vweb_routes')
|
2020-11-08 09:14:24 +01:00
|
|
|
c.verify_all_vweb_routes()
|
2020-12-19 11:24:29 +01:00
|
|
|
c.timers.show('checker_verify_all_vweb_routes')
|
|
|
|
//
|
2021-01-30 09:19:29 +01:00
|
|
|
if c.pref.is_test {
|
|
|
|
mut n_test_fns := 0
|
|
|
|
for _, f in c.table.fns {
|
2021-03-05 12:19:39 +01:00
|
|
|
if f.is_test {
|
2021-01-30 09:19:29 +01:00
|
|
|
n_test_fns++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if n_test_fns == 0 {
|
|
|
|
c.add_error_detail('The name of a test function in V, should start with `test_`.')
|
|
|
|
c.add_error_detail('The test function should take 0 parameters, and no return type. Example:')
|
|
|
|
c.add_error_detail('fn test_xyz(){ assert 2 + 2 == 4 }')
|
|
|
|
c.error('a _test.v file should have *at least* one `test_` function', token.Position{})
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
// do checks specific to files in main module
|
|
|
|
// returns `true` if a main function is in the file
|
2021-01-27 13:54:33 +01:00
|
|
|
fn (mut c Checker) file_has_main_fn(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 {
|
2021-01-27 13:54:33 +01:00
|
|
|
if stmt is ast.FnDecl {
|
|
|
|
if stmt.name == 'main.main' {
|
|
|
|
if has_main_fn {
|
|
|
|
c.error('function `main` is already defined', stmt.pos)
|
|
|
|
}
|
|
|
|
has_main_fn = true
|
|
|
|
if stmt.params.len > 0 {
|
|
|
|
c.error('function `main` cannot have arguments', stmt.pos)
|
2020-04-19 00:07:57 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if stmt.return_type != ast.void_type {
|
2021-01-27 13:54:33 +01:00
|
|
|
c.error('function `main` cannot return values', stmt.pos)
|
|
|
|
}
|
|
|
|
if stmt.no_body {
|
|
|
|
c.error('function `main` must declare a body', stmt.pos)
|
2020-04-25 15:57:11 +02:00
|
|
|
}
|
2021-01-27 13:54:33 +01:00
|
|
|
} else if stmt.attrs.contains('console') {
|
|
|
|
c.error('only `main` can have the `[console]` attribute', stmt.pos)
|
2020-04-19 00:07:57 +02:00
|
|
|
}
|
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-10-15 12:00:46 +02:00
|
|
|
fn (mut c Checker) check_valid_snake_case(name string, identifier string, pos token.Position) {
|
2020-12-11 06:55:39 +01:00
|
|
|
if !c.pref.is_vweb && name.len > 0 && (name[0] == `_` || name.contains('._')) {
|
2020-05-16 16:12:23 +02:00
|
|
|
c.error('$identifier `$name` cannot start with `_`', pos)
|
|
|
|
}
|
2020-08-09 03:57:54 +02:00
|
|
|
if !c.pref.experimental && !c.pref.translated && util.contains_capital(name) {
|
2020-05-16 16:12:23 +02:00
|
|
|
c.error('$identifier `$name` cannot contain uppercase letters, use snake_case instead',
|
|
|
|
pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn stripped_name(name string) string {
|
2020-12-04 12:25:23 +01:00
|
|
|
idx := name.last_index('.') or { -1 }
|
2020-05-16 16:12:23 +02:00
|
|
|
return name[(idx + 1)..]
|
|
|
|
}
|
|
|
|
|
2020-10-15 12:00:46 +02:00
|
|
|
fn (mut c Checker) check_valid_pascal_case(name string, identifier string, pos token.Position) {
|
2020-07-01 00:53:53 +02:00
|
|
|
sname := stripped_name(name)
|
2020-12-11 06:55:39 +01:00
|
|
|
if sname.len > 0 && !sname[0].is_capital() && !c.pref.translated {
|
2020-05-16 16:12:23 +02:00
|
|
|
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) {
|
2020-11-25 12:09:40 +01:00
|
|
|
match node {
|
2021-03-10 19:18:48 +01:00
|
|
|
ast.AliasTypeDecl { c.alias_type_decl(node) }
|
|
|
|
ast.FnTypeDecl { c.fn_type_decl(node) }
|
|
|
|
ast.SumTypeDecl { c.sum_type_decl(node) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (mut c Checker) alias_type_decl(node ast.AliasTypeDecl) {
|
|
|
|
// TODO Replace `c.file.mod.name != 'time'` by `it.language != .v` once available
|
|
|
|
if c.file.mod.name != 'time' && c.file.mod.name != 'builtin' {
|
|
|
|
c.check_valid_pascal_case(node.name, 'type alias', node.pos)
|
|
|
|
}
|
|
|
|
typ_sym := c.table.get_type_symbol(node.parent_type)
|
|
|
|
if typ_sym.kind in [.placeholder, .int_literal, .float_literal] {
|
|
|
|
c.error("type `$typ_sym.name` doesn't exist", node.pos)
|
|
|
|
} else if typ_sym.kind == .alias {
|
2021-04-02 00:57:09 +02:00
|
|
|
orig_sym := c.table.get_type_symbol((typ_sym.info as ast.Alias).parent_type)
|
2021-03-10 19:18:48 +01:00
|
|
|
c.error('type `$typ_sym.str()` is an alias, use the original alias type `$orig_sym.name` instead',
|
|
|
|
node.pos)
|
|
|
|
} else if typ_sym.kind == .chan {
|
|
|
|
c.error('aliases of `chan` types are not allowed.', node.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (mut c Checker) fn_type_decl(node ast.FnTypeDecl) {
|
|
|
|
c.check_valid_pascal_case(node.name, 'fn type', node.pos)
|
|
|
|
typ_sym := c.table.get_type_symbol(node.typ)
|
2021-04-02 00:57:09 +02:00
|
|
|
fn_typ_info := typ_sym.info as ast.FnType
|
2021-03-10 19:18:48 +01:00
|
|
|
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", node.pos)
|
|
|
|
}
|
|
|
|
for arg in fn_info.params {
|
|
|
|
arg_sym := c.table.get_type_symbol(arg.typ)
|
|
|
|
if arg_sym.kind == .placeholder {
|
|
|
|
c.error("type `$arg_sym.name` doesn't exist", node.pos)
|
2020-04-26 06:40:54 +02:00
|
|
|
}
|
2021-03-10 19:18:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (mut c Checker) sum_type_decl(node ast.SumTypeDecl) {
|
|
|
|
c.check_valid_pascal_case(node.name, 'sum type', node.pos)
|
|
|
|
mut names_used := []string{}
|
|
|
|
for variant in node.variants {
|
|
|
|
if variant.typ.is_ptr() {
|
|
|
|
c.error('sum type cannot hold a reference type', variant.pos)
|
2020-04-26 06:40:54 +02:00
|
|
|
}
|
2021-03-10 19:18:48 +01:00
|
|
|
mut sym := c.table.get_type_symbol(variant.typ)
|
|
|
|
if sym.name in names_used {
|
|
|
|
c.error('sum type $node.name cannot hold the type `$sym.name` more than once',
|
|
|
|
variant.pos)
|
|
|
|
} else if sym.kind in [.placeholder, .int_literal, .float_literal] {
|
|
|
|
c.error("type `$sym.name` doesn't exist", variant.pos)
|
|
|
|
} else if sym.kind == .interface_ {
|
|
|
|
c.error('sum type cannot hold an interface', variant.pos)
|
2020-11-11 09:18:15 +01:00
|
|
|
}
|
2021-03-10 19:18:48 +01:00
|
|
|
names_used << sym.name
|
2020-04-26 06:40:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 {
|
2021-04-26 08:58:05 +02:00
|
|
|
if decl.language == .v {
|
|
|
|
c.check_valid_snake_case(method.name, 'method name', method.pos)
|
|
|
|
}
|
2021-04-20 11:11:41 +02:00
|
|
|
c.ensure_type_exists(method.return_type, method.return_type_pos) or { return }
|
2021-02-19 10:23:13 +01:00
|
|
|
for param in method.params {
|
|
|
|
c.ensure_type_exists(param.typ, param.pos) or { return }
|
|
|
|
}
|
2020-05-16 16:12:23 +02:00
|
|
|
}
|
2021-01-23 07:57:17 +01:00
|
|
|
for i, field in decl.fields {
|
2021-04-26 08:58:05 +02:00
|
|
|
if decl.language == .v {
|
|
|
|
c.check_valid_snake_case(field.name, 'field name', field.pos)
|
|
|
|
}
|
2021-04-20 11:11:41 +02:00
|
|
|
c.ensure_type_exists(field.typ, field.pos) or { return }
|
2021-01-23 07:57:17 +01:00
|
|
|
for j in 0 .. i {
|
|
|
|
if field.name == decl.fields[j].name {
|
|
|
|
c.error('field name `$field.name` duplicate', field.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-16 16:12:23 +02:00
|
|
|
}
|
|
|
|
|
2021-01-09 01:33:36 +01:00
|
|
|
pub fn (mut c Checker) struct_decl(mut 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)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
mut struct_sym := c.table.find_type(decl.name) or { ast.TypeSymbol{} }
|
|
|
|
if mut struct_sym.info is ast.Struct {
|
2020-12-23 19:12:49 +01:00
|
|
|
for embed in decl.embeds {
|
|
|
|
embed_sym := c.table.get_type_symbol(embed.typ)
|
|
|
|
if embed_sym.kind != .struct_ {
|
|
|
|
c.error('`$embed_sym.name` is not a struct', embed.pos)
|
2021-02-13 00:47:37 +01:00
|
|
|
} else {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := embed_sym.info as ast.Struct
|
2021-02-13 15:52:01 +01:00
|
|
|
if info.is_heap && !embed.typ.is_ptr() {
|
|
|
|
struct_sym.info.is_heap = true
|
2021-02-13 00:47:37 +01:00
|
|
|
}
|
2020-12-23 19:12:49 +01:00
|
|
|
}
|
|
|
|
}
|
2021-01-17 16:18:07 +01:00
|
|
|
for attr in decl.attrs {
|
|
|
|
if attr.name == 'typedef' && decl.language != .c {
|
|
|
|
c.error('`typedef` attribute can only be used with C structs', decl.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-11-24 13:58:29 +01:00
|
|
|
for i, field in decl.fields {
|
2021-02-19 10:23:13 +01:00
|
|
|
c.ensure_type_exists(field.typ, field.type_pos) or { return }
|
2020-12-23 19:12:49 +01:00
|
|
|
if decl.language == .v {
|
2020-11-24 13:58:29 +01:00
|
|
|
c.check_valid_snake_case(field.name, 'field name', field.pos)
|
|
|
|
}
|
|
|
|
sym := c.table.get_type_symbol(field.typ)
|
|
|
|
for j in 0 .. i {
|
|
|
|
if field.name == decl.fields[j].name {
|
|
|
|
c.error('field name `$field.name` duplicate', field.pos)
|
|
|
|
}
|
2020-05-28 18:38:53 +02:00
|
|
|
}
|
2020-11-24 13:58:29 +01:00
|
|
|
if sym.kind == .struct_ {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := sym.info as ast.Struct
|
2021-02-13 15:52:01 +01:00
|
|
|
if info.is_heap && !field.typ.is_ptr() {
|
|
|
|
struct_sym.info.is_heap = true
|
2020-11-24 13:58:29 +01:00
|
|
|
}
|
2020-04-24 20:27:18 +02:00
|
|
|
}
|
2020-11-24 13:58:29 +01:00
|
|
|
if field.has_default_expr {
|
|
|
|
c.expected_type = field.typ
|
2021-04-01 11:49:13 +02:00
|
|
|
mut field_expr_type := c.expr(field.default_expr)
|
|
|
|
if !field.typ.has_flag(.optional) {
|
|
|
|
c.check_expr_opt_call(field.default_expr, field_expr_type)
|
|
|
|
}
|
2021-02-08 16:33:05 +01:00
|
|
|
struct_sym.info.fields[i].default_expr_typ = field_expr_type
|
2020-11-24 13:58:29 +01:00
|
|
|
c.check_expected(field_expr_type, field.typ) or {
|
2021-02-08 16:33:05 +01:00
|
|
|
if !(sym.kind == .interface_
|
|
|
|
&& c.type_implements(field_expr_type, field.typ, field.pos)) {
|
2021-03-13 18:13:50 +01:00
|
|
|
c.error('incompatible initializer for field `$field.name`: $err.msg',
|
2021-02-08 16:33:05 +01:00
|
|
|
field.default_expr.position())
|
|
|
|
}
|
2020-09-09 13:59:52 +02:00
|
|
|
}
|
2020-11-24 13:58:29 +01:00
|
|
|
// Check for unnecessary inits like ` = 0` and ` = ''`
|
|
|
|
if field.typ.is_ptr() {
|
|
|
|
continue
|
2020-09-09 15:34:41 +02:00
|
|
|
}
|
2020-11-24 13:58:29 +01:00
|
|
|
if field.default_expr is ast.IntegerLiteral {
|
|
|
|
if field.default_expr.val == '0' {
|
|
|
|
c.warn('unnecessary default value of `0`: struct fields are zeroed by default',
|
|
|
|
field.default_expr.pos)
|
|
|
|
}
|
|
|
|
} else if field.default_expr is ast.StringLiteral {
|
|
|
|
if field.default_expr.val == '' {
|
|
|
|
c.warn("unnecessary default value of '': struct fields are zeroed by default",
|
|
|
|
field.default_expr.pos)
|
|
|
|
}
|
|
|
|
} else if field.default_expr is ast.BoolLiteral {
|
|
|
|
if field.default_expr.val == false {
|
|
|
|
c.warn('unnecessary default value `false`: struct fields are zeroed by default',
|
|
|
|
field.default_expr.pos)
|
|
|
|
}
|
2020-09-09 13:59:52 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-24 20:27:18 +02:00
|
|
|
}
|
2020-04-24 15:46:55 +02:00
|
|
|
}
|
2020-04-16 11:01:18 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.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
|
|
|
// }
|
2021-04-02 00:57:09 +02:00
|
|
|
if struct_init.typ == ast.void_type {
|
2020-04-01 23:23:20 +02:00
|
|
|
// Short syntax `({foo: bar})`
|
2021-04-02 00:57:09 +02:00
|
|
|
if c.expected_type == ast.void_type {
|
2020-04-01 23:23:20 +02:00
|
|
|
c.error('unexpected short struct syntax', struct_init.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-04-01 23:23:20 +02:00
|
|
|
}
|
2021-01-27 13:55:53 +01:00
|
|
|
sym := c.table.get_type_symbol(c.expected_type)
|
|
|
|
if sym.kind == .array {
|
|
|
|
struct_init.typ = c.table.value_type(c.expected_type)
|
|
|
|
} else {
|
|
|
|
struct_init.typ = c.expected_type
|
|
|
|
}
|
2020-04-01 23:23:20 +02:00
|
|
|
}
|
2021-02-02 14:42:00 +01:00
|
|
|
utyp := c.unwrap_generic(struct_init.typ)
|
2021-03-06 20:04:51 +01:00
|
|
|
c.ensure_type_exists(utyp, struct_init.pos) or {}
|
2021-02-02 14:42:00 +01:00
|
|
|
type_sym := c.table.get_type_symbol(utyp)
|
2021-03-31 10:13:15 +02:00
|
|
|
if !c.inside_unsafe && type_sym.kind == .sum_type {
|
2021-03-31 11:38:54 +02:00
|
|
|
c.note('direct sum type init (`x := SumType{}`) will be removed soon', struct_init.pos)
|
2021-03-31 10:13:15 +02:00
|
|
|
}
|
2021-03-13 16:20:46 +01:00
|
|
|
// Make sure the first letter is capital, do not allow e.g. `x := string{}`,
|
|
|
|
// but `x := T{}` is ok.
|
|
|
|
if !c.is_builtin_mod && !c.inside_unsafe && type_sym.language == .v
|
2021-04-16 10:53:20 +02:00
|
|
|
&& c.cur_fn.cur_generic_types.len == 0 {
|
2021-03-13 16:20:46 +01:00
|
|
|
pos := type_sym.name.last_index('.') or { -1 }
|
|
|
|
first_letter := type_sym.name[pos + 1]
|
|
|
|
if !first_letter.is_capital() {
|
|
|
|
c.error('cannot initialize builtin type `$type_sym.name`', struct_init.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-11-25 12:09:40 +01:00
|
|
|
if type_sym.kind == .sum_type && struct_init.fields.len == 1 {
|
2020-07-27 17:04:04 +02:00
|
|
|
sexpr := struct_init.fields[0].expr.str()
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('cast to sum type using `${type_sym.name}($sexpr)` not `$type_sym.name{$sexpr}`',
|
2020-07-27 17:04:04 +02:00
|
|
|
struct_init.pos)
|
|
|
|
}
|
2020-05-13 19:20:15 +02:00
|
|
|
if type_sym.kind == .interface_ {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('cannot instantiate interface `$type_sym.name`', struct_init.pos)
|
2020-05-13 19:20:15 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if type_sym.info is ast.Alias {
|
2021-02-02 14:42:00 +01:00
|
|
|
if type_sym.info.parent_type.is_number() {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('cannot instantiate number type alias `$type_sym.name`', struct_init.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-06-24 16:52:23 +02:00
|
|
|
}
|
2020-06-24 16:35:18 +02:00
|
|
|
}
|
2021-02-02 14:42:00 +01:00
|
|
|
// allow init structs from generic if they're private except the type is from builtin module
|
|
|
|
if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.language != .c
|
|
|
|
&& (type_sym.mod != c.mod && !(struct_init.typ.has_flag(.generic)
|
|
|
|
&& type_sym.mod != 'builtin')) {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('type `$type_sym.name` is private', struct_init.pos)
|
2020-05-04 21:56:41 +02:00
|
|
|
}
|
2021-01-08 04:49:13 +01:00
|
|
|
if type_sym.kind == .struct_ {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := type_sym.info as ast.Struct
|
2021-01-08 04:49:13 +01:00
|
|
|
if info.attrs.len > 0 && info.attrs[0].name == 'noinit' && type_sym.mod != c.mod {
|
2021-01-23 09:33:22 +01:00
|
|
|
c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' +
|
|
|
|
'it cannot be initialized with `$type_sym.name{}`', struct_init.pos)
|
2021-01-08 04:49:13 +01:00
|
|
|
}
|
2021-04-28 06:47:00 +02:00
|
|
|
if info.is_heap && c.inside_decl_rhs && !c.inside_ref_lit && !c.inside_unsafe
|
|
|
|
&& !struct_init.typ.is_ptr() {
|
2021-02-13 00:47:37 +01:00
|
|
|
c.error('`$type_sym.name` type can only be used as a reference `&$type_sym.name` or inside a `struct` reference',
|
|
|
|
struct_init.pos)
|
|
|
|
}
|
2021-01-08 04:49:13 +01:00
|
|
|
}
|
2021-04-11 08:02:57 +02:00
|
|
|
if type_sym.name.len == 1 && c.cur_fn.generic_names.len == 0 {
|
2021-01-10 20:16:53 +01:00
|
|
|
c.error('unknown struct `$type_sym.name`', struct_init.pos)
|
2021-01-22 13:49:56 +01:00
|
|
|
return 0
|
2021-01-10 20:16:53 +01:00
|
|
|
}
|
2020-04-16 11:01:18 +02:00
|
|
|
match type_sym.kind {
|
2020-01-18 23:26:14 +01:00
|
|
|
.placeholder {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('unknown struct: $type_sym.name', struct_init.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-07-04 17:28:01 +02:00
|
|
|
// string & array are also structs but .kind of string/array
|
2020-05-19 14:04:51 +02:00
|
|
|
.struct_, .string, .array, .alias {
|
2021-04-02 00:57:09 +02:00
|
|
|
mut info := ast.Struct{}
|
2020-05-19 14:04:51 +02:00
|
|
|
if type_sym.kind == .alias {
|
2021-04-02 00:57:09 +02:00
|
|
|
info_t := type_sym.info as ast.Alias
|
2020-06-24 22:12:33 +02:00
|
|
|
sym := c.table.get_type_symbol(info_t.parent_type)
|
2020-07-18 21:34:38 +02:00
|
|
|
if sym.kind == .placeholder { // pending import symbol did not resolve
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('unknown struct: $type_sym.name', struct_init.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-07-18 21:34:38 +02:00
|
|
|
}
|
2020-05-19 14:04:51 +02:00
|
|
|
if sym.kind != .struct_ {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('alias type name: $sym.name is not struct type', struct_init.pos)
|
2020-05-19 14:04:51 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
info = sym.info as ast.Struct
|
2020-05-19 14:04:51 +02:00
|
|
|
} else {
|
2021-04-02 00:57:09 +02:00
|
|
|
info = type_sym.info as ast.Struct
|
2020-05-19 14:04:51 +02:00
|
|
|
}
|
2020-08-31 22:17:59 +02:00
|
|
|
if struct_init.is_short {
|
|
|
|
exp_len := info.fields.len
|
|
|
|
got_len := struct_init.fields.len
|
|
|
|
if exp_len != got_len {
|
|
|
|
amount := if exp_len < got_len { 'many' } else { 'few' }
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('too $amount fields in `$type_sym.name` literal (expecting $exp_len, got $got_len)',
|
2020-08-31 22:17:59 +02:00
|
|
|
struct_init.pos)
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
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 {
|
2021-04-02 00:57:09 +02:00
|
|
|
mut info_field := ast.StructField{}
|
|
|
|
mut embed_type := ast.Type(0)
|
2020-12-23 19:12:49 +01:00
|
|
|
mut is_embed := false
|
2020-04-17 02:38:39 +02:00
|
|
|
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-12-23 19:12:49 +01:00
|
|
|
if !exists {
|
|
|
|
for embed in info.embeds {
|
|
|
|
embed_sym := c.table.get_type_symbol(embed)
|
|
|
|
if embed_sym.embed_name() == field_name {
|
|
|
|
exists = true
|
|
|
|
embed_type = embed
|
|
|
|
is_embed = true
|
2020-10-30 07:09:26 +01:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-17 02:38:39 +02:00
|
|
|
if !exists {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('unknown field `$field.name` in struct literal of type `$type_sym.name`',
|
2020-04-25 17:49:16 +02:00
|
|
|
field.pos)
|
2020-04-17 02:38:39 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if field_name in inited_fields {
|
2020-06-24 14:44:06 +02:00
|
|
|
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-12-23 19:12:49 +01:00
|
|
|
if is_embed {
|
|
|
|
c.expected_type = embed_type
|
|
|
|
expr_type := c.expr(field.expr)
|
|
|
|
expr_type_sym := c.table.get_type_symbol(expr_type)
|
2021-04-02 00:57:09 +02:00
|
|
|
if expr_type != ast.void_type && expr_type_sym.kind != .placeholder {
|
2020-12-23 19:12:49 +01:00
|
|
|
c.check_expected(expr_type, embed_type) or {
|
2021-03-13 18:13:50 +01:00
|
|
|
c.error('cannot assign to field `$info_field.name`: $err.msg',
|
2020-12-23 19:12:49 +01:00
|
|
|
field.pos)
|
|
|
|
}
|
2020-10-15 17:30:36 +02:00
|
|
|
}
|
2020-12-23 19:12:49 +01:00
|
|
|
struct_init.fields[i].typ = expr_type
|
|
|
|
struct_init.fields[i].expected_type = embed_type
|
|
|
|
} else {
|
|
|
|
inited_fields << field_name
|
|
|
|
field_type_sym := c.table.get_type_symbol(info_field.typ)
|
|
|
|
c.expected_type = info_field.typ
|
2021-04-01 11:49:13 +02:00
|
|
|
mut expr_type := c.expr(field.expr)
|
|
|
|
if !info_field.typ.has_flag(.optional) {
|
|
|
|
expr_type = c.check_expr_opt_call(field.expr, expr_type)
|
|
|
|
}
|
2020-12-23 19:12:49 +01:00
|
|
|
expr_type_sym := c.table.get_type_symbol(expr_type)
|
|
|
|
if field_type_sym.kind == .interface_ {
|
|
|
|
c.type_implements(expr_type, info_field.typ, field.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder {
|
2020-12-23 19:12:49 +01:00
|
|
|
c.check_expected(expr_type, info_field.typ) or {
|
2021-03-13 18:13:50 +01:00
|
|
|
c.error('cannot assign to field `$info_field.name`: $err.msg',
|
2020-12-23 19:12:49 +01:00
|
|
|
field.pos)
|
|
|
|
}
|
|
|
|
}
|
2021-02-08 00:28:29 +01:00
|
|
|
if info_field.typ.has_flag(.shared_f) {
|
|
|
|
if !expr_type.has_flag(.shared_f) && expr_type.is_ptr() {
|
|
|
|
c.error('`shared` field must be initialized with `shared` or value',
|
|
|
|
field.pos)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer()
|
|
|
|
&& !expr_type.is_number() {
|
|
|
|
c.error('reference field must be initialized with reference',
|
|
|
|
field.pos)
|
|
|
|
}
|
2020-12-23 19:12:49 +01: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
|
|
|
}
|
|
|
|
}
|
2021-03-31 11:15:06 +02:00
|
|
|
// Check uninitialized refs/sum types
|
2020-04-03 20:17:53 +02:00
|
|
|
for field in info.fields {
|
2020-12-23 19:12:49 +01:00
|
|
|
if field.has_default_expr || field.name in inited_fields {
|
2020-04-03 20:17:53 +02:00
|
|
|
continue
|
|
|
|
}
|
2021-04-11 09:30:23 +02:00
|
|
|
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f)
|
|
|
|
&& !struct_init.has_update_expr && !c.pref.translated {
|
2020-12-19 10:28:17 +01:00
|
|
|
c.error('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
|
|
|
}
|
2021-03-31 11:15:06 +02:00
|
|
|
// Do not allow empty uninitialized sum types
|
|
|
|
/*
|
|
|
|
sym := c.table.get_type_symbol(field.typ)
|
|
|
|
if sym.kind == .sum_type {
|
|
|
|
c.warn('sum type field `${type_sym.name}.$field.name` must be initialized',
|
|
|
|
struct_init.pos)
|
|
|
|
}
|
|
|
|
*/
|
2020-08-31 23:15:40 +02:00
|
|
|
// Check for `[required]` struct attr
|
|
|
|
if field.attrs.contains('required') && !struct_init.is_short {
|
|
|
|
mut found := false
|
|
|
|
for init_field in struct_init.fields {
|
|
|
|
if field.name == init_field.name {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('field `${type_sym.name}.$field.name` must be initialized',
|
2020-08-31 23:15:40 +02:00
|
|
|
struct_init.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-04-03 20:17:53 +02:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
2021-01-04 19:19:03 +01:00
|
|
|
if struct_init.has_update_expr {
|
|
|
|
update_type := c.expr(struct_init.update_expr)
|
|
|
|
struct_init.update_expr_type = update_type
|
2021-02-01 15:18:03 +01:00
|
|
|
if c.table.type_kind(update_type) != .struct_ {
|
|
|
|
s := c.table.type_to_str(update_type)
|
|
|
|
c.error('expected struct, found `$s`', struct_init.update_expr.position())
|
2021-01-04 19:19:03 +01:00
|
|
|
} else if update_type != struct_init.typ {
|
2021-02-01 20:08:25 +01:00
|
|
|
from_sym := c.table.get_type_symbol(update_type)
|
|
|
|
to_sym := c.table.get_type_symbol(struct_init.typ)
|
2021-04-02 00:57:09 +02:00
|
|
|
from_info := from_sym.info as ast.Struct
|
|
|
|
to_info := to_sym.info as ast.Struct
|
2021-02-01 20:08:25 +01:00
|
|
|
// TODO this check is too strict
|
|
|
|
if !c.check_struct_signature(from_info, to_info) {
|
|
|
|
c.error('struct `$from_sym.name` is not compatible with struct `$to_sym.name`',
|
|
|
|
struct_init.update_expr.position())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !struct_init.update_expr.is_lvalue() {
|
2021-02-01 15:18:03 +01:00
|
|
|
// cgen will repeat `update_expr` for each field
|
|
|
|
// so enforce an lvalue for efficiency
|
|
|
|
c.error('expression is not an lvalue', struct_init.update_expr.position())
|
2021-01-04 19:19:03 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
return struct_init.typ
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-12-11 04:44:07 +01:00
|
|
|
fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) {
|
|
|
|
match mut expr {
|
|
|
|
ast.FloatLiteral {
|
|
|
|
if expr.val.f64() == 0.0 {
|
|
|
|
oper := if op_kind == .div { 'division' } else { 'modulo' }
|
|
|
|
c.error('$oper by zero', expr.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.IntegerLiteral {
|
|
|
|
if expr.val.int() == 0 {
|
|
|
|
oper := if op_kind == .div { 'division' } else { 'modulo' }
|
|
|
|
c.error('$oper by zero', expr.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.CastExpr {
|
|
|
|
c.check_div_mod_by_zero(expr.expr, op_kind)
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) ast.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-09-22 05:28:29 +02:00
|
|
|
left_type := c.expr(infix_expr.left)
|
2020-06-26 16:31:36 +02:00
|
|
|
// left_type = c.unwrap_genric(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-06-26 16:31:36 +02:00
|
|
|
right_type := c.expr(infix_expr.right)
|
|
|
|
// right_type = c.unwrap_genric(c.expr(infix_expr.right))
|
2020-03-15 00:46:08 +01:00
|
|
|
infix_expr.right_type = right_type
|
2021-04-19 14:38:48 +02:00
|
|
|
if left_type.is_number() && !left_type.is_ptr()
|
|
|
|
&& right_type in [ast.int_literal_type, ast.float_literal_type] {
|
|
|
|
infix_expr.right_type = left_type
|
|
|
|
}
|
|
|
|
if right_type.is_number() && !right_type.is_ptr()
|
|
|
|
&& left_type in [ast.int_literal_type, ast.float_literal_type] {
|
|
|
|
infix_expr.left_type = right_type
|
|
|
|
}
|
2020-09-20 19:51:14 +02:00
|
|
|
mut right := c.table.get_type_symbol(right_type)
|
2021-02-08 01:41:47 +01:00
|
|
|
right_final := c.table.get_final_type_symbol(right_type)
|
2020-09-20 19:51:14 +02:00
|
|
|
mut left := c.table.get_type_symbol(left_type)
|
2021-02-08 01:41:47 +01:00
|
|
|
left_final := c.table.get_final_type_symbol(left_type)
|
2020-05-27 05:42:48 +02:00
|
|
|
left_pos := infix_expr.left.position()
|
|
|
|
right_pos := infix_expr.right.position()
|
2021-02-19 11:38:41 +01:00
|
|
|
left_right_pos := left_pos.extend(right_pos)
|
2020-12-07 21:43:38 +01:00
|
|
|
if (left_type.is_ptr() || left.is_pointer()) && infix_expr.op in [.plus, .minus] {
|
2021-02-21 11:15:36 +01:00
|
|
|
if !c.inside_unsafe && !infix_expr.left.is_auto_deref_var()
|
|
|
|
&& !infix_expr.right.is_auto_deref_var() {
|
2020-12-07 21:43:38 +01:00
|
|
|
c.warn('pointer arithmetic is only allowed in `unsafe` blocks', left_pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if left_type == ast.voidptr_type {
|
2020-12-07 21:43:38 +01:00
|
|
|
c.error('`$infix_expr.op` cannot be used with `voidptr`', left_pos)
|
|
|
|
}
|
2020-07-03 18:10:10 +02:00
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
mut return_type := left_type
|
2020-11-27 03:08:42 +01:00
|
|
|
if infix_expr.op != .key_is {
|
|
|
|
match mut infix_expr.left {
|
|
|
|
ast.Ident, ast.SelectorExpr {
|
|
|
|
if infix_expr.left.is_mut {
|
|
|
|
c.error('remove unnecessary `mut`', infix_expr.left.mut_pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
2021-03-22 06:39:07 +01:00
|
|
|
eq_ne := infix_expr.op in [.eq, .ne]
|
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-11-28 23:34:26 +01:00
|
|
|
.eq, .ne {
|
2021-03-22 06:39:07 +01:00
|
|
|
is_mismatch := (left.kind == .alias && right.kind in [.struct_, .array, .sum_type])
|
|
|
|
|| (right.kind == .alias && left.kind in [.struct_, .array, .sum_type])
|
2021-02-05 16:49:40 +01:00
|
|
|
if is_mismatch {
|
2020-11-28 23:34:26 +01:00
|
|
|
c.error('possible type mismatch of compared values of `$infix_expr.op` operation',
|
2021-02-19 11:38:41 +01:00
|
|
|
left_right_pos)
|
2020-11-28 23:34:26 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
.key_in, .not_in {
|
2021-04-25 17:17:54 +02:00
|
|
|
match right_final.kind {
|
2020-04-25 20:28:49 +02:00
|
|
|
.array {
|
2021-04-25 17:17:54 +02:00
|
|
|
elem_type := right_final.array_info().elem_type
|
2020-08-27 06:46:18 +02:00
|
|
|
// if left_default.kind != right_sym.kind {
|
2020-10-15 17:30:36 +02:00
|
|
|
c.check_expected(left_type, elem_type) or {
|
2021-03-13 18:13:50 +01:00
|
|
|
c.error('left operand to `$infix_expr.op` does not match the array element type: $err.msg',
|
2021-02-19 11:38:41 +01:00
|
|
|
left_right_pos)
|
2020-04-25 20:28:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
.map {
|
2021-04-25 17:17:54 +02:00
|
|
|
map_info := right_final.map_info()
|
2021-02-21 10:54:30 +01:00
|
|
|
c.check_expected(left_type, map_info.key_type) or {
|
2021-03-13 18:13:50 +01:00
|
|
|
c.error('left operand to `$infix_expr.op` does not match the map key type: $err.msg',
|
2021-02-19 11:38:41 +01:00
|
|
|
left_right_pos)
|
2020-04-25 21:51:44 +02:00
|
|
|
}
|
2021-02-21 10:54:30 +01:00
|
|
|
infix_expr.left_type = map_info.key_type
|
2020-04-25 21:51:44 +02:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
.string {
|
2021-03-23 09:38:56 +01:00
|
|
|
c.warn('use `str.contains(substr)` instead of `substr in str`', left_right_pos)
|
2020-10-15 17:30:36 +02:00
|
|
|
c.check_expected(left_type, right_type) or {
|
2021-03-13 18:13:50 +01:00
|
|
|
c.error('left operand to `$infix_expr.op` does not match: $err.msg',
|
2021-02-19 11:38:41 +01:00
|
|
|
left_right_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
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.bool_type
|
2020-02-19 19:54:36 +01:00
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
.plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe { // binary operators that expect matching types
|
2021-04-02 00:57:09 +02:00
|
|
|
if right.info is ast.Alias && (right.info as ast.Alias).language != .c
|
2021-03-30 11:39:54 +02:00
|
|
|
&& c.mod == c.table.type_to_str(right_type).split('.')[0]
|
2021-04-02 00:57:09 +02:00
|
|
|
&& c.table.get_type_symbol((right.info as ast.Alias).parent_type).is_primitive() {
|
|
|
|
right = c.table.get_type_symbol((right.info as ast.Alias).parent_type)
|
2020-09-20 19:51:14 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if left.info is ast.Alias && (left.info as ast.Alias).language != .c
|
2021-03-30 11:39:54 +02:00
|
|
|
&& c.mod == c.table.type_to_str(left_type).split('.')[0]
|
2021-04-02 00:57:09 +02:00
|
|
|
&& c.table.get_type_symbol((left.info as ast.Alias).parent_type).is_primitive() {
|
|
|
|
left = c.table.get_type_symbol((left.info as ast.Alias).parent_type)
|
2020-09-20 19:51:14 +02:00
|
|
|
}
|
2021-03-30 11:39:54 +02:00
|
|
|
// Check if the alias type is not a primitive then allow using operator overloading for aliased `arrays` and `maps`
|
2021-04-02 00:57:09 +02:00
|
|
|
if left.kind == .alias && left.info is ast.Alias
|
|
|
|
&& !(c.table.get_type_symbol((left.info as ast.Alias).parent_type).is_primitive()) {
|
2021-03-30 11:39:54 +02:00
|
|
|
if left.has_method(infix_expr.op.str()) {
|
|
|
|
if method := left.find_method(infix_expr.op.str()) {
|
|
|
|
return_type = method.return_type
|
|
|
|
} else {
|
|
|
|
return_type = left_type
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
left_name := c.table.type_to_str(left_type)
|
|
|
|
right_name := c.table.type_to_str(right_type)
|
|
|
|
if left_name == right_name {
|
|
|
|
c.error('undefined operation `$left_name` $infix_expr.op.str() `$right_name`',
|
|
|
|
left_right_pos)
|
|
|
|
} else {
|
|
|
|
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
|
|
|
}
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if right.kind == .alias && right.info is ast.Alias
|
|
|
|
&& !(c.table.get_type_symbol((right.info as ast.Alias).parent_type).is_primitive()) {
|
2021-03-30 11:39:54 +02:00
|
|
|
if right.has_method(infix_expr.op.str()) {
|
|
|
|
if method := right.find_method(infix_expr.op.str()) {
|
|
|
|
return_type = method.return_type
|
|
|
|
} else {
|
|
|
|
return_type = right_type
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
left_name := c.table.type_to_str(left_type)
|
|
|
|
right_name := c.table.type_to_str(right_type)
|
|
|
|
if left_name == right_name {
|
|
|
|
c.error('undefined operation `$left_name` $infix_expr.op.str() `$right_name`',
|
|
|
|
left_right_pos)
|
|
|
|
} else {
|
|
|
|
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
if left.kind in [.array, .array_fixed, .map, .struct_] {
|
|
|
|
if left.has_method(infix_expr.op.str()) {
|
2020-09-20 19:51:14 +02:00
|
|
|
if method := left.find_method(infix_expr.op.str()) {
|
|
|
|
return_type = method.return_type
|
|
|
|
} else {
|
|
|
|
return_type = left_type
|
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
} else {
|
2020-09-09 10:07:58 +02:00
|
|
|
left_name := c.table.type_to_str(left_type)
|
|
|
|
right_name := c.table.type_to_str(right_type)
|
2020-09-20 19:51:14 +02:00
|
|
|
if left_name == right_name {
|
2021-01-17 03:37:44 +01:00
|
|
|
c.error('undefined operation `$left_name` $infix_expr.op.str() `$right_name`',
|
2021-02-19 11:38:41 +01:00
|
|
|
left_right_pos)
|
2020-09-20 19:51:14 +02:00
|
|
|
} else {
|
2021-02-19 11:38:41 +01:00
|
|
|
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
2020-09-20 19:51:14 +02:00
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
}
|
|
|
|
} else if right.kind in [.array, .array_fixed, .map, .struct_] {
|
|
|
|
if right.has_method(infix_expr.op.str()) {
|
2020-09-20 19:51:14 +02:00
|
|
|
if method := right.find_method(infix_expr.op.str()) {
|
|
|
|
return_type = method.return_type
|
|
|
|
} else {
|
|
|
|
return_type = right_type
|
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
} else {
|
2020-09-09 10:07:58 +02:00
|
|
|
left_name := c.table.type_to_str(left_type)
|
|
|
|
right_name := c.table.type_to_str(right_type)
|
2020-09-20 19:51:14 +02:00
|
|
|
if left_name == right_name {
|
2021-01-17 03:37:44 +01:00
|
|
|
c.error('undefined operation `$left_name` $infix_expr.op.str() `$right_name`',
|
2021-02-19 11:38:41 +01:00
|
|
|
left_right_pos)
|
2020-09-20 19:51:14 +02:00
|
|
|
} else {
|
2021-02-19 11:38:41 +01:00
|
|
|
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
2020-09-20 19:51:14 +02:00
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
promoted_type := c.promote(c.table.unalias_num_type(left_type), c.table.unalias_num_type(right_type))
|
2021-04-02 00:57:09 +02:00
|
|
|
if promoted_type.idx() == ast.void_type_idx {
|
2020-09-09 10:07:58 +02:00
|
|
|
left_name := c.table.type_to_str(left_type)
|
|
|
|
right_name := c.table.type_to_str(right_type)
|
2021-02-19 11:38:41 +01:00
|
|
|
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
2020-09-18 01:03:55 +02:00
|
|
|
} else if promoted_type.has_flag(.optional) {
|
|
|
|
s := c.table.type_to_str(promoted_type)
|
|
|
|
c.error('`$infix_expr.op` cannot be used with `$s`', infix_expr.pos)
|
2020-05-27 05:42:48 +02:00
|
|
|
} else if promoted_type.is_float() {
|
|
|
|
if infix_expr.op in [.mod, .xor, .amp, .pipe] {
|
|
|
|
side := if left_type == promoted_type { 'left' } else { 'right' }
|
|
|
|
pos := if left_type == promoted_type { left_pos } else { right_pos }
|
2020-05-28 05:50:57 +02:00
|
|
|
name := if left_type == promoted_type { left.name } else { right.name }
|
2020-05-27 05:42:48 +02:00
|
|
|
if infix_expr.op == .mod {
|
2020-06-24 14:44:06 +02:00
|
|
|
c.error('float modulo not allowed, use math.fmod() instead',
|
|
|
|
pos)
|
2020-05-27 05:42:48 +02:00
|
|
|
} else {
|
2021-01-03 23:11:09 +01:00
|
|
|
c.error('$side type of `$infix_expr.op.str()` cannot be non-integer type `$name`',
|
2020-05-28 05:50:57 +02:00
|
|
|
pos)
|
2020-05-27 05:42:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-03 22:30:51 +02:00
|
|
|
if infix_expr.op in [.div, .mod] {
|
2020-12-11 04:44:07 +01:00
|
|
|
c.check_div_mod_by_zero(infix_expr.right, infix_expr.op)
|
2020-07-03 22:30:51 +02:00
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
return_type = promoted_type
|
2020-04-19 22:26:58 +02:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
}
|
2020-08-28 10:08:07 +02:00
|
|
|
.gt, .lt, .ge, .le {
|
|
|
|
if left.kind in [.array, .array_fixed] && right.kind in [.array, .array_fixed] {
|
|
|
|
c.error('only `==` and `!=` are defined on arrays', infix_expr.pos)
|
2021-02-03 15:18:38 +01:00
|
|
|
} else if left.kind == .struct_ && right.kind == .struct_ && infix_expr.op in [.eq, .lt] {
|
2021-01-10 11:24:46 +01:00
|
|
|
if !(left.has_method(infix_expr.op.str()) && right.has_method(infix_expr.op.str())) {
|
|
|
|
left_name := c.table.type_to_str(left_type)
|
|
|
|
right_name := c.table.type_to_str(right_type)
|
|
|
|
if left_name == right_name {
|
2021-01-17 03:37:44 +01:00
|
|
|
c.error('undefined operation `$left_name` $infix_expr.op.str() `$right_name`',
|
2021-02-19 11:38:41 +01:00
|
|
|
left_right_pos)
|
2021-01-10 11:24:46 +01:00
|
|
|
} else {
|
2021-02-19 11:38:41 +01:00
|
|
|
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
2021-01-10 11:24:46 +01:00
|
|
|
}
|
|
|
|
}
|
2020-08-28 10:08:07 +02:00
|
|
|
}
|
2021-02-03 15:18:38 +01:00
|
|
|
if left.kind == .struct_ && right.kind == .struct_ {
|
|
|
|
if !left.has_method('<') && infix_expr.op in [.ge, .le] {
|
|
|
|
c.error('cannot use `$infix_expr.op` as `<` operator method is not defined',
|
2021-02-19 11:38:41 +01:00
|
|
|
left_right_pos)
|
2021-02-03 15:18:38 +01:00
|
|
|
} else if !left.has_method('<') && infix_expr.op == .gt {
|
2021-02-19 11:38:41 +01:00
|
|
|
c.error('cannot use `>` as `<=` operator method is not defined', left_right_pos)
|
2021-02-03 15:18:38 +01:00
|
|
|
}
|
|
|
|
}
|
2020-08-28 10:08:07 +02:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
.left_shift {
|
2021-02-08 01:41:47 +01:00
|
|
|
if left_final.kind == .array {
|
2021-03-12 16:09:01 +01:00
|
|
|
if !infix_expr.is_stmt {
|
|
|
|
c.error('array append cannot be used in an expression', infix_expr.pos)
|
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
// `array << elm`
|
2021-04-01 11:49:13 +02:00
|
|
|
c.check_expr_opt_call(infix_expr.right, right_type)
|
2020-07-13 12:19:28 +02:00
|
|
|
infix_expr.auto_locked, _ = 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_ {
|
2021-02-08 01:41:47 +01:00
|
|
|
if right_final.kind != .array {
|
2020-05-06 11:29:37 +02:00
|
|
|
// []Animal << Cat
|
2020-05-27 05:42:48 +02:00
|
|
|
c.type_implements(right_type, left_value_type, right_pos)
|
2020-05-06 11:29:37 +02:00
|
|
|
} else {
|
2021-02-08 01:41:47 +01:00
|
|
|
// []Animal << []Cat
|
2020-05-28 05:50:57 +02:00
|
|
|
c.type_implements(c.table.value_type(right_type), left_value_type,
|
|
|
|
right_pos)
|
2020-05-06 11:29:37 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-05-06 11:29:37 +02:00
|
|
|
}
|
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
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-04-25 20:28:49 +02:00
|
|
|
}
|
2021-02-08 01:41:47 +01:00
|
|
|
if right_final.kind == .array
|
2021-01-23 09:33:22 +01:00
|
|
|
&& c.check_types(left_value_type, c.table.value_type(right_type)) {
|
2020-04-25 20:28:49 +02:00
|
|
|
// []T << []T
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-04-25 20:28:49 +02:00
|
|
|
}
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('cannot append `$right.name` to `$left.name`', right_pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-05-31 12:58:13 +02:00
|
|
|
} else {
|
|
|
|
return c.check_shift(left_type, right_type, left_pos, right_pos)
|
2020-04-25 06:14:17 +02:00
|
|
|
}
|
2020-04-19 22:26:58 +02:00
|
|
|
}
|
2020-05-31 12:58:13 +02:00
|
|
|
.right_shift {
|
|
|
|
return c.check_shift(left_type, right_type, left_pos, right_pos)
|
|
|
|
}
|
2020-06-02 16:18:12 +02:00
|
|
|
.key_is, .not_is {
|
2021-03-13 18:13:50 +01:00
|
|
|
right_expr := infix_expr.right
|
|
|
|
mut typ := match right_expr {
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.TypeNode {
|
2021-03-13 18:13:50 +01:00
|
|
|
right_expr.typ
|
|
|
|
}
|
|
|
|
ast.None {
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.none_type_idx
|
2021-03-13 18:13:50 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
c.error('invalid type `$right_expr`', right_expr.position())
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.Type(0)
|
2021-03-13 18:13:50 +01:00
|
|
|
}
|
|
|
|
}
|
2021-04-18 15:28:39 +02:00
|
|
|
if typ != ast.Type(0) {
|
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
op := infix_expr.op.str()
|
|
|
|
if typ_sym.kind == .placeholder {
|
|
|
|
c.error('$op: type `$typ_sym.name` does not exist', right_expr.position())
|
|
|
|
}
|
|
|
|
if left.kind !in [.interface_, .sum_type] {
|
|
|
|
c.error('`$op` can only be used with interfaces and sum types', infix_expr.pos)
|
|
|
|
} else if mut left.info is ast.SumType {
|
|
|
|
if typ !in left.info.variants {
|
|
|
|
c.error('`$left.name` has no variant `$right.name`', infix_expr.pos)
|
|
|
|
}
|
2020-11-11 09:18:15 +01:00
|
|
|
}
|
2020-05-29 06:45:58 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.bool_type
|
2020-04-20 14:49:26 +02:00
|
|
|
}
|
2020-08-17 20:12:00 +02:00
|
|
|
.arrow { // `chan <- elem`
|
|
|
|
if left.kind == .chan {
|
2020-09-06 21:24:41 +02:00
|
|
|
chan_info := left.chan_info()
|
|
|
|
elem_type := chan_info.elem_type
|
2020-08-17 20:12:00 +02:00
|
|
|
if !c.check_types(right_type, elem_type) {
|
|
|
|
c.error('cannot push `$right.name` on `$left.name`', right_pos)
|
|
|
|
}
|
2020-09-06 21:24:41 +02:00
|
|
|
if chan_info.is_mut {
|
|
|
|
// TODO: The error message of the following could be more specific...
|
|
|
|
c.fail_if_immutable(infix_expr.right)
|
|
|
|
}
|
|
|
|
if elem_type.is_ptr() && !right_type.is_ptr() {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('cannot push non-reference `$right.name` on `$left.name`',
|
2020-09-06 21:24:41 +02:00
|
|
|
right_pos)
|
|
|
|
}
|
2021-01-06 21:19:40 +01:00
|
|
|
c.stmts(infix_expr.or_block.stmts)
|
2020-08-17 20:12:00 +02:00
|
|
|
} else {
|
|
|
|
c.error('cannot push on non-channel `$left.name`', left_pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-08-17 20:12:00 +02:00
|
|
|
}
|
2020-11-28 23:34:26 +01:00
|
|
|
.and, .logical_or {
|
2021-04-02 00:57:09 +02:00
|
|
|
if infix_expr.left_type != ast.bool_type_idx {
|
2020-11-28 23:34:26 +01:00
|
|
|
c.error('left operand for `$infix_expr.op` is not a boolean', infix_expr.left.position())
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if infix_expr.right_type != ast.bool_type_idx {
|
2020-11-28 23:34:26 +01:00
|
|
|
c.error('right operand for `$infix_expr.op` is not a boolean', infix_expr.right.position())
|
2020-09-23 20:48:25 +02:00
|
|
|
}
|
2020-11-28 23:34:26 +01:00
|
|
|
if mut infix_expr.left is ast.InfixExpr {
|
|
|
|
if infix_expr.left.op != infix_expr.op && infix_expr.left.op in [.logical_or, .and] {
|
2021-04-14 18:55:26 +02:00
|
|
|
// for example: `(a && b) || c` instead of `a && b || c`
|
|
|
|
c.error('ambiguous boolean expression. use `()` to ensure correct order of operations',
|
|
|
|
infix_expr.pos)
|
2020-06-02 22:21:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-28 23:34:26 +01: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.
|
2021-04-02 00:57:09 +02:00
|
|
|
if left_type == ast.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)
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if left_type == ast.string_type
|
2021-01-23 09:33:22 +01:00
|
|
|
&& infix_expr.op !in [.plus, .eq, .ne, .lt, .gt, .le, .ge] {
|
2020-04-14 04:12:24 +02:00
|
|
|
// TODO broken !in
|
2020-06-14 19:03:29 +02:00
|
|
|
c.error('string types only have the following operators defined: `==`, `!=`, `<`, `>`, `<=`, `>=`, and `+`',
|
2020-04-14 04:12:24 +02:00
|
|
|
infix_expr.pos)
|
2021-03-22 06:39:07 +01:00
|
|
|
} else if left.kind == .enum_ && right.kind == .enum_ && !eq_ne {
|
2021-04-02 00:57:09 +02:00
|
|
|
left_enum := left.info as ast.Enum
|
|
|
|
right_enum := right.info as ast.Enum
|
2021-02-20 20:51:54 +01:00
|
|
|
if left_enum.is_flag && right_enum.is_flag {
|
|
|
|
// `[flag]` tagged enums are a special case that allow also `|` and `&` binary operators
|
|
|
|
if infix_expr.op !in [.pipe, .amp] {
|
|
|
|
c.error('only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed',
|
|
|
|
infix_expr.pos)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Regular enums
|
|
|
|
c.error('only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed',
|
|
|
|
infix_expr.pos)
|
|
|
|
}
|
2020-04-14 04:12:24 +02:00
|
|
|
}
|
2021-03-22 06:39:07 +01:00
|
|
|
// sum types can't have any infix operation except of `is`, `eq`, `ne`.
|
|
|
|
// `is` is checked before and doesn't reach this.
|
|
|
|
if c.table.type_kind(left_type) == .sum_type && !eq_ne {
|
2020-11-17 21:26:40 +01:00
|
|
|
c.error('cannot use operator `$infix_expr.op` with `$left.name`', infix_expr.pos)
|
2021-03-22 06:39:07 +01:00
|
|
|
} else if c.table.type_kind(right_type) == .sum_type && !eq_ne {
|
2020-11-17 21:26:40 +01:00
|
|
|
c.error('cannot use operator `$infix_expr.op` with `$right.name`', infix_expr.pos)
|
|
|
|
}
|
2020-12-29 13:49:43 +01:00
|
|
|
// TODO move this to symmetric_check? Right now it would break `return 0` for `fn()?int `
|
|
|
|
left_is_optional := left_type.has_flag(.optional)
|
|
|
|
right_is_optional := right_type.has_flag(.optional)
|
|
|
|
if (left_is_optional && !right_is_optional) || (!left_is_optional && right_is_optional) {
|
2021-02-19 11:38:41 +01:00
|
|
|
c.error('unwrapped optional cannot be used in an infix expression', left_right_pos)
|
2020-12-29 13:49:43 +01:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
// Dual sides check (compatibility check)
|
2020-08-11 00:51:15 +02:00
|
|
|
if !c.symmetric_check(right_type, left_type) && !c.pref.translated {
|
2020-04-20 14:49:26 +02:00
|
|
|
// for type-unresolved consts
|
2021-04-02 00:57:09 +02:00
|
|
|
if left_type == ast.void_type || right_type == ast.void_type {
|
|
|
|
return ast.void_type
|
2020-04-20 14:49:26 +02:00
|
|
|
}
|
2021-04-05 09:02:47 +02:00
|
|
|
if left_type.nr_muls() > 0 && right_type.is_int() {
|
|
|
|
// pointer arithmetic is fine, it is checked in other places
|
|
|
|
return return_type
|
|
|
|
}
|
2020-04-25 17:49:16 +02:00
|
|
|
c.error('infix expr: cannot use `$right.name` (right expression) as `$left.name`',
|
2021-02-19 11:38:41 +01:00
|
|
|
left_right_pos)
|
2020-04-20 14:49:26 +02:00
|
|
|
}
|
2020-07-16 19:40:14 +02:00
|
|
|
/*
|
|
|
|
if (infix_expr.left is ast.InfixExpr &&
|
|
|
|
(infix_expr.left as ast.InfixExpr).op == .inc) ||
|
|
|
|
(infix_expr.right is ast.InfixExpr && (infix_expr.right as ast.InfixExpr).op == .inc) {
|
|
|
|
c.warn('`++` and `--` are statements, not expressions', infix_expr.pos)
|
|
|
|
}
|
|
|
|
*/
|
2021-04-02 00:57:09 +02:00
|
|
|
return if infix_expr.op.is_relational() { ast.bool_type } else { return_type }
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-07-13 12:19:28 +02:00
|
|
|
// returns name and position of variable that needs write lock
|
2020-09-22 05:28:29 +02:00
|
|
|
// also sets `is_changed` to true (TODO update the name to reflect this?)
|
2020-07-13 12:19:28 +02:00
|
|
|
fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
|
|
|
|
mut to_lock := '' // name of variable that needs lock
|
|
|
|
mut pos := token.Position{} // and its position
|
|
|
|
mut explicit_lock_needed := false
|
2020-11-25 12:09:40 +01:00
|
|
|
match mut expr {
|
2020-06-01 15:43:54 +02:00
|
|
|
ast.CastExpr {
|
|
|
|
// TODO
|
2020-07-13 12:19:28 +02:00
|
|
|
return '', pos
|
2020-06-01 15:43:54 +02:00
|
|
|
}
|
2021-01-05 15:11:43 +01:00
|
|
|
ast.ComptimeSelector {
|
|
|
|
return '', pos
|
|
|
|
}
|
2020-04-27 22:53:26 +02:00
|
|
|
ast.Ident {
|
2020-07-23 23:16:36 +02:00
|
|
|
if expr.obj is ast.Var {
|
|
|
|
mut v := expr.obj as ast.Var
|
2020-09-27 03:40:59 +02:00
|
|
|
if !v.is_mut && !c.pref.translated && !c.inside_unsafe {
|
2020-09-27 09:36:26 +02:00
|
|
|
c.error('`$expr.name` is immutable, declare it with `mut` to make it mutable',
|
|
|
|
expr.pos)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
2020-06-02 09:00:51 +02:00
|
|
|
v.is_changed = true
|
2020-07-13 12:19:28 +02:00
|
|
|
if v.typ.share() == .shared_t {
|
|
|
|
if expr.name !in c.locked_names {
|
2021-02-03 15:16:52 +01:00
|
|
|
if c.locked_names.len > 0 || c.rlocked_names.len > 0 {
|
|
|
|
if expr.name in c.rlocked_names {
|
|
|
|
c.error('$expr.name has an `rlock` but needs a `lock`',
|
|
|
|
expr.pos)
|
|
|
|
} else {
|
|
|
|
c.error('$expr.name must be added to the `lock` list above',
|
|
|
|
expr.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-07-13 12:19:28 +02:00
|
|
|
to_lock = expr.name
|
|
|
|
pos = expr.pos
|
|
|
|
}
|
|
|
|
}
|
2021-01-24 10:01:10 +01:00
|
|
|
} else if expr.obj is ast.ConstField && expr.name in c.const_names {
|
2020-06-18 20:38:59 +02:00
|
|
|
c.error('cannot modify constant `$expr.name`', expr.pos)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.IndexExpr {
|
2021-03-15 14:54:06 +01:00
|
|
|
left_sym := c.table.get_type_symbol(expr.left_type)
|
2021-04-02 00:57:09 +02:00
|
|
|
mut elem_type := ast.Type(0)
|
2021-03-15 14:54:06 +01:00
|
|
|
mut kind := ''
|
|
|
|
match left_sym.info {
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.Array {
|
2021-03-15 14:54:06 +01:00
|
|
|
elem_type, kind = left_sym.info.elem_type, 'array'
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.ArrayFixed {
|
2021-03-15 14:54:06 +01:00
|
|
|
elem_type, kind = left_sym.info.elem_type, 'fixed array'
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.Map {
|
2021-03-15 14:54:06 +01:00
|
|
|
elem_type, kind = left_sym.info.value_type, 'map'
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
if elem_type.has_flag(.shared_f) {
|
|
|
|
c.error('you have to create a handle and `lock` it to modify `shared` $kind element',
|
|
|
|
expr.left.position().extend(expr.pos))
|
|
|
|
}
|
2020-07-13 12:19:28 +02:00
|
|
|
to_lock, pos = c.fail_if_immutable(expr.left)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
ast.ParExpr {
|
2020-07-13 12:19:28 +02:00
|
|
|
to_lock, pos = c.fail_if_immutable(expr.expr)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
ast.PrefixExpr {
|
2020-07-13 12:19:28 +02:00
|
|
|
to_lock, pos = c.fail_if_immutable(expr.right)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
ast.SelectorExpr {
|
2021-03-07 14:09:38 +01:00
|
|
|
if expr.expr_type == 0 {
|
|
|
|
return '', pos
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
// retrieve ast.Field
|
2021-02-19 10:23:13 +01:00
|
|
|
c.ensure_type_exists(expr.expr_type, expr.pos) or { return '', pos }
|
|
|
|
mut typ_sym := c.table.get_final_type_symbol(c.unwrap_generic(expr.expr_type))
|
2020-04-27 22:53:26 +02:00
|
|
|
match typ_sym.kind {
|
|
|
|
.struct_ {
|
2021-04-02 00:57:09 +02:00
|
|
|
struct_info := typ_sym.info as ast.Struct
|
2020-12-23 19:12:49 +01:00
|
|
|
mut has_field := true
|
|
|
|
mut field_info := struct_info.find_field(expr.field_name) or {
|
|
|
|
has_field = false
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.StructField{}
|
2020-12-23 19:12:49 +01:00
|
|
|
}
|
|
|
|
if !has_field {
|
|
|
|
for embed in struct_info.embeds {
|
|
|
|
embed_sym := c.table.get_type_symbol(embed)
|
2021-04-02 00:57:09 +02:00
|
|
|
embed_struct_info := embed_sym.info as ast.Struct
|
2020-12-23 19:12:49 +01:00
|
|
|
if embed_field_info := embed_struct_info.find_field(expr.field_name) {
|
|
|
|
has_field = true
|
|
|
|
field_info = embed_field_info
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !has_field {
|
2020-06-18 20:38:59 +02:00
|
|
|
type_str := c.table.type_to_str(expr.expr_type)
|
|
|
|
c.error('unknown field `${type_str}.$expr.field_name`', expr.pos)
|
2020-07-13 12:19:28 +02:00
|
|
|
return '', pos
|
2020-05-10 02:07:15 +02:00
|
|
|
}
|
2020-08-09 03:57:54 +02:00
|
|
|
if !field_info.is_mut && !c.pref.translated {
|
2020-06-18 20:38:59 +02:00
|
|
|
type_str := c.table.type_to_str(expr.expr_type)
|
|
|
|
c.error('field `$expr.field_name` of struct `$type_str` is immutable',
|
|
|
|
expr.pos)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
2021-03-15 14:54:06 +01:00
|
|
|
if field_info.typ.has_flag(.shared_f) {
|
|
|
|
type_str := c.table.type_to_str(expr.expr_type)
|
|
|
|
c.error('you have to create a handle and `lock` it to modify `shared` field `$expr.field_name` of struct `$type_str`',
|
|
|
|
expr.pos)
|
|
|
|
}
|
2020-07-13 12:19:28 +02:00
|
|
|
to_lock, pos = c.fail_if_immutable(expr.expr)
|
|
|
|
if to_lock != '' {
|
|
|
|
// No automatic lock for struct access
|
|
|
|
explicit_lock_needed = true
|
|
|
|
}
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
2021-01-23 07:57:17 +01:00
|
|
|
.interface_ {
|
2021-04-02 00:57:09 +02:00
|
|
|
interface_info := typ_sym.info as ast.Interface
|
2021-01-24 22:11:17 +01:00
|
|
|
mut field_info := interface_info.find_field(expr.field_name) or {
|
2021-01-23 07:57:17 +01:00
|
|
|
type_str := c.table.type_to_str(expr.expr_type)
|
|
|
|
c.error('unknown field `${type_str}.$expr.field_name`', expr.pos)
|
|
|
|
return '', pos
|
|
|
|
}
|
2021-01-24 22:11:17 +01:00
|
|
|
if !field_info.is_mut {
|
|
|
|
type_str := c.table.type_to_str(expr.expr_type)
|
|
|
|
c.error('field `$expr.field_name` of interface `$type_str` is immutable',
|
|
|
|
expr.pos)
|
|
|
|
}
|
|
|
|
c.fail_if_immutable(expr.expr)
|
2021-01-23 07:57:17 +01:00
|
|
|
}
|
2020-04-27 22:53:26 +02:00
|
|
|
.array, .string {
|
|
|
|
// This should only happen in `builtin`
|
2021-04-13 05:55:41 +02:00
|
|
|
if c.file.mod.name != 'builtin' {
|
2020-06-18 20:38:59 +02:00
|
|
|
c.error('`$typ_sym.kind` can not be modified', expr.pos)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
}
|
2021-02-10 10:59:56 +01:00
|
|
|
.aggregate {
|
|
|
|
c.fail_if_immutable(expr.expr)
|
|
|
|
}
|
2020-04-27 22:53:26 +02:00
|
|
|
else {
|
2020-06-18 20:38:59 +02:00
|
|
|
c.error('unexpected symbol `$typ_sym.kind`', expr.pos)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-30 14:19:22 +02:00
|
|
|
ast.CallExpr {
|
|
|
|
// TODO: should only work for builtin method
|
|
|
|
if expr.name == 'slice' {
|
2020-07-13 12:19:28 +02:00
|
|
|
to_lock, pos = c.fail_if_immutable(expr.left)
|
|
|
|
if to_lock != '' {
|
|
|
|
// No automatic lock for array slicing (yet(?))
|
|
|
|
explicit_lock_needed = true
|
|
|
|
}
|
2020-06-30 14:19:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.ArrayInit {
|
2021-03-11 13:57:04 +01:00
|
|
|
c.error('array literal can not be modified', expr.pos)
|
2020-07-13 12:19:28 +02:00
|
|
|
return '', pos
|
2020-06-30 14:19:22 +02:00
|
|
|
}
|
2021-01-28 09:05:09 +01:00
|
|
|
ast.StructInit {
|
|
|
|
return '', pos
|
|
|
|
}
|
2020-04-27 22:53:26 +02:00
|
|
|
else {
|
2021-01-07 20:32:02 +01:00
|
|
|
c.error('unexpected expression `$expr.type_name()`', expr.position())
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
}
|
2020-07-13 12:19:28 +02:00
|
|
|
if explicit_lock_needed {
|
2021-01-07 20:32:02 +01:00
|
|
|
c.error('`$to_lock` is `shared` and needs explicit lock for `$expr.type_name()`',
|
2020-07-14 17:18:10 +02:00
|
|
|
pos)
|
2020-07-13 12:19:28 +02:00
|
|
|
to_lock = ''
|
|
|
|
}
|
|
|
|
return to_lock, pos
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) ast.Type {
|
2020-09-27 03:14:24 +02:00
|
|
|
// First check everything that applies to both fns and methods
|
2021-04-04 16:05:29 +02:00
|
|
|
// TODO merge logic from method_call and fn_call
|
2020-09-27 03:14:24 +02:00
|
|
|
/*
|
|
|
|
for i, call_arg in call_expr.args {
|
|
|
|
if call_arg.is_mut {
|
|
|
|
c.fail_if_immutable(call_arg.expr)
|
|
|
|
if !arg.is_mut {
|
|
|
|
tok := call_arg.share.str()
|
|
|
|
c.error('`$call_expr.name` parameter `$arg.name` is not `$tok`, `$tok` is not needed`',
|
|
|
|
call_arg.expr.position())
|
|
|
|
} else if arg.typ.share() != call_arg.share {
|
|
|
|
c.error('wrong shared type', call_arg.expr.position())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if arg.is_mut && (!call_arg.is_mut || arg.typ.share() != call_arg.share) {
|
|
|
|
tok := call_arg.share.str()
|
|
|
|
c.error('`$call_expr.name` parameter `$arg.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${i+1}`',
|
|
|
|
call_arg.expr.position())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
2021-04-04 16:05:29 +02:00
|
|
|
// Now call `method_call` or `fn_call` for specific checks.
|
2021-04-25 20:40:38 +02:00
|
|
|
old_inside_fn_arg := c.inside_fn_arg
|
|
|
|
c.inside_fn_arg = true
|
2021-04-04 16:05:29 +02:00
|
|
|
typ := if call_expr.is_method { c.method_call(mut call_expr) } else { c.fn_call(mut call_expr) }
|
2021-04-25 20:40:38 +02:00
|
|
|
c.inside_fn_arg = old_inside_fn_arg
|
2020-10-22 03:51:25 +02:00
|
|
|
// autofree: mark args that have to be freed (after saving them in tmp exprs)
|
2021-01-23 09:33:22 +01:00
|
|
|
free_tmp_arg_vars := c.pref.autofree && !c.is_builtin_mod && call_expr.args.len > 0
|
|
|
|
&& !call_expr.args[0].typ.has_flag(.optional)
|
2020-11-21 19:07:47 +01:00
|
|
|
if free_tmp_arg_vars && !c.inside_const {
|
2020-09-05 12:00:35 +02:00
|
|
|
for i, arg in call_expr.args {
|
2021-04-02 00:57:09 +02:00
|
|
|
if arg.typ != ast.string_type {
|
2020-09-05 12:00:35 +02:00
|
|
|
continue
|
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
if arg.expr is ast.Ident || arg.expr is ast.StringLiteral
|
|
|
|
|| arg.expr is ast.SelectorExpr {
|
2020-11-02 00:57:40 +01:00
|
|
|
// Simple expressions like variables, string literals, selector expressions
|
|
|
|
// (`x.field`) can't result in allocations and don't need to be assigned to
|
|
|
|
// temporary vars.
|
|
|
|
// Only expressions like `str + 'b'` need to be freed.
|
2020-09-05 12:00:35 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
call_expr.args[i].is_tmp_autofree = true
|
|
|
|
}
|
2020-11-21 23:19:01 +01:00
|
|
|
// TODO copy pasta from above
|
2021-04-02 00:57:09 +02:00
|
|
|
if call_expr.receiver_type == ast.string_type && !(call_expr.left is ast.Ident
|
2021-01-23 09:33:22 +01:00
|
|
|
|| call_expr.left is ast.StringLiteral
|
|
|
|
|| call_expr.left is ast.SelectorExpr) {
|
2020-10-22 03:51:25 +02:00
|
|
|
call_expr.free_receiver = true
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
2020-12-05 15:37:00 +01:00
|
|
|
c.expected_or_type = call_expr.return_type.clear_flag(.optional)
|
2020-12-05 15:26:28 +01:00
|
|
|
c.stmts(call_expr.or_block.stmts)
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_or_type = ast.void_type
|
2021-01-30 16:53:31 +01:00
|
|
|
if call_expr.or_block.kind == .propagate && !c.cur_fn.return_type.has_flag(.optional)
|
|
|
|
&& !c.inside_const {
|
2021-03-05 12:19:39 +01:00
|
|
|
if !c.cur_fn.is_main {
|
2021-01-30 16:53:31 +01:00
|
|
|
c.error('to propagate the optional call, `$c.cur_fn.name` must return an optional',
|
|
|
|
call_expr.or_block.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-09-05 12:00:35 +02:00
|
|
|
return typ
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ ast.Type, call_expr ast.CallExpr) {
|
2021-01-03 16:57:29 +01:00
|
|
|
if call_expr.args.len != 1 {
|
2021-02-12 00:47:11 +01:00
|
|
|
c.error('expected 1 argument, but got $call_expr.args.len', call_expr.pos)
|
2021-01-03 16:57:29 +01:00
|
|
|
// Finish early so that it doesn't fail later
|
|
|
|
return
|
|
|
|
}
|
2020-06-11 13:14:07 +02:00
|
|
|
elem_sym := c.table.get_type_symbol(elem_typ)
|
2020-11-21 00:05:57 +01:00
|
|
|
arg_expr := call_expr.args[0].expr
|
2020-11-25 12:09:40 +01:00
|
|
|
match arg_expr {
|
2020-06-11 13:14:07 +02:00
|
|
|
ast.AnonFn {
|
2020-09-27 03:32:56 +02:00
|
|
|
if arg_expr.decl.params.len > 1 {
|
2020-11-26 11:28:54 +01:00
|
|
|
c.error('function needs exactly 1 argument', arg_expr.decl.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if is_map && (arg_expr.decl.return_type == ast.void_type
|
2021-02-07 23:10:39 +01:00
|
|
|
|| arg_expr.decl.params[0].typ != elem_typ) {
|
2020-12-13 03:29:48 +01:00
|
|
|
c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`', arg_expr.decl.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if !is_map && (arg_expr.decl.return_type != ast.bool_type
|
2021-02-07 23:10:39 +01:00
|
|
|
|| arg_expr.decl.params[0].typ != elem_typ) {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`',
|
2020-11-26 11:28:54 +01:00
|
|
|
arg_expr.decl.pos)
|
2020-06-11 13:14:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.Ident {
|
2020-07-04 11:23:41 +02:00
|
|
|
if arg_expr.kind == .function {
|
|
|
|
func := c.table.find_fn(arg_expr.name) or {
|
2021-02-05 08:05:13 +01:00
|
|
|
c.error('$arg_expr.name does not exist', arg_expr.pos)
|
2020-06-11 13:14:07 +02:00
|
|
|
return
|
|
|
|
}
|
2020-09-27 03:32:56 +02:00
|
|
|
if func.params.len > 1 {
|
2020-06-11 13:14:07 +02:00
|
|
|
c.error('function needs exactly 1 argument', call_expr.pos)
|
2021-01-23 09:33:22 +01:00
|
|
|
} else if is_map
|
2021-04-02 00:57:09 +02:00
|
|
|
&& (func.return_type == ast.void_type || func.params[0].typ != elem_typ) {
|
2020-12-13 03:29:48 +01:00
|
|
|
c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`',
|
2020-11-26 11:28:54 +01:00
|
|
|
arg_expr.pos)
|
2021-01-23 09:33:22 +01:00
|
|
|
} else if !is_map
|
2021-04-02 00:57:09 +02:00
|
|
|
&& (func.return_type != ast.bool_type || func.params[0].typ != elem_typ) {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`',
|
2020-11-26 11:28:54 +01:00
|
|
|
arg_expr.pos)
|
2020-06-11 13:14:07 +02:00
|
|
|
}
|
2021-03-18 15:24:16 +01:00
|
|
|
} else if arg_expr.kind == .variable {
|
|
|
|
if arg_expr.obj is ast.Var {
|
|
|
|
expr := arg_expr.obj.expr
|
|
|
|
if expr is ast.AnonFn {
|
|
|
|
// copied from above
|
|
|
|
if expr.decl.params.len > 1 {
|
|
|
|
c.error('function needs exactly 1 argument', expr.decl.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if is_map && (expr.decl.return_type == ast.void_type
|
2021-03-18 15:24:16 +01:00
|
|
|
|| expr.decl.params[0].typ != elem_typ) {
|
|
|
|
c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`',
|
|
|
|
expr.decl.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if !is_map && (expr.decl.return_type != ast.bool_type
|
2021-03-18 15:24:16 +01:00
|
|
|
|| expr.decl.params[0].typ != elem_typ) {
|
|
|
|
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`',
|
|
|
|
expr.decl.pos)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if !is_map && arg_expr.info.typ != ast.bool_type {
|
2021-03-18 15:24:16 +01:00
|
|
|
c.error('type mismatch, should be bool', arg_expr.pos)
|
|
|
|
}
|
2020-06-11 13:14:07 +02:00
|
|
|
}
|
|
|
|
}
|
2021-04-02 01:56:51 +02:00
|
|
|
ast.CallExpr {
|
2021-04-02 02:29:24 +02:00
|
|
|
if is_map && arg_expr.return_type == ast.void_type {
|
2021-04-02 01:56:51 +02:00
|
|
|
c.error('type mismatch, `$arg_expr.name` does not return anything', arg_expr.pos)
|
2021-04-02 02:29:24 +02:00
|
|
|
} else if !is_map && arg_expr.return_type != ast.bool_type {
|
2021-04-02 01:56:51 +02:00
|
|
|
c.error('type mismatch, `$arg_expr.name` must return a bool', arg_expr.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-06-11 13:14:07 +02:00
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-24 08:44:15 +02:00
|
|
|
fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_expr ast.CallExpr, concrete_types []ast.Type) {
|
2021-04-07 17:26:22 +02:00
|
|
|
rts := c.table.get_type_symbol(return_type)
|
|
|
|
if rts.info is ast.Struct {
|
2021-04-23 14:17:57 +02:00
|
|
|
if rts.info.is_generic {
|
2021-04-23 10:19:16 +02:00
|
|
|
mut nrt := '$rts.name<'
|
|
|
|
mut c_nrt := '${rts.name}_T_'
|
2021-04-24 08:44:15 +02:00
|
|
|
for i in 0 .. call_expr.concrete_types.len {
|
|
|
|
gts := c.table.get_type_symbol(call_expr.concrete_types[i])
|
2021-04-23 10:19:16 +02:00
|
|
|
nrt += gts.name
|
|
|
|
c_nrt += gts.name
|
2021-04-24 08:44:15 +02:00
|
|
|
if i != call_expr.concrete_types.len - 1 {
|
2021-04-23 10:19:16 +02:00
|
|
|
nrt += ','
|
|
|
|
c_nrt += '_'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nrt += '>'
|
2021-04-07 17:26:22 +02:00
|
|
|
idx := c.table.type_idxs[nrt]
|
|
|
|
if idx != 0 {
|
|
|
|
c.ensure_type_exists(idx, call_expr.pos) or {}
|
2021-04-26 19:01:01 +02:00
|
|
|
call_expr.return_type = ast.new_type(idx).derive(return_type).clear_flag(.generic)
|
2021-04-07 17:26:22 +02:00
|
|
|
} else {
|
|
|
|
mut fields := rts.info.fields.clone()
|
2021-04-24 08:44:15 +02:00
|
|
|
if rts.info.generic_types.len == concrete_types.len {
|
2021-04-21 11:22:42 +02:00
|
|
|
generic_names := rts.info.generic_types.map(c.table.get_type_symbol(it).name)
|
2021-04-07 17:26:22 +02:00
|
|
|
for i, _ in fields {
|
2021-04-21 11:22:42 +02:00
|
|
|
if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ,
|
2021-04-26 19:05:10 +02:00
|
|
|
generic_names, concrete_types, false)
|
2021-04-07 17:26:22 +02:00
|
|
|
{
|
|
|
|
fields[i].typ = t_typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mut info := rts.info
|
2021-04-23 14:17:57 +02:00
|
|
|
info.is_generic = false
|
2021-04-24 08:44:15 +02:00
|
|
|
info.concrete_types = concrete_types.clone()
|
2021-04-10 13:00:01 +02:00
|
|
|
info.parent_type = return_type
|
2021-04-07 17:26:22 +02:00
|
|
|
info.fields = fields
|
|
|
|
stru_idx := c.table.register_type_symbol(ast.TypeSymbol{
|
|
|
|
kind: .struct_
|
|
|
|
name: nrt
|
|
|
|
cname: util.no_dots(c_nrt)
|
|
|
|
mod: c.mod
|
|
|
|
info: info
|
|
|
|
})
|
2021-04-26 19:01:01 +02:00
|
|
|
call_expr.return_type = ast.new_type(stru_idx).derive(return_type).clear_flag(.generic)
|
2021-04-07 17:26:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-04 16:05:29 +02:00
|
|
|
pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
|
2020-04-12 17:45:04 +02:00
|
|
|
left_type := c.expr(call_expr.left)
|
2021-01-29 14:53:14 +01:00
|
|
|
c.expected_type = left_type
|
2021-04-10 04:00:53 +02:00
|
|
|
mut is_generic := left_type.has_flag(.generic)
|
2020-04-12 17:45:04 +02:00
|
|
|
call_expr.left_type = left_type
|
2020-10-30 19:26:08 +01:00
|
|
|
// Set default values for .return_type & .receiver_type too,
|
|
|
|
// or there will be hard to diagnose 0 type panics in cgen.
|
|
|
|
call_expr.return_type = left_type
|
|
|
|
call_expr.receiver_type = left_type
|
2020-05-28 01:19:03 +02:00
|
|
|
left_type_sym := c.table.get_type_symbol(c.unwrap_generic(left_type))
|
2020-04-12 17:45:04 +02:00
|
|
|
method_name := call_expr.name
|
2020-12-06 04:55:08 +01:00
|
|
|
mut unknown_method_msg := 'unknown method: `${left_type_sym.name}.$method_name`'
|
2020-06-23 11:39:58 +02:00
|
|
|
if left_type.has_flag(.optional) {
|
|
|
|
c.error('optional type cannot be called directly', call_expr.left.position())
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-06-23 11:39:58 +02:00
|
|
|
}
|
2021-02-15 14:29:44 +01:00
|
|
|
if left_type_sym.kind in [.sum_type, .interface_] && method_name == 'type_name' {
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2020-11-14 12:59:03 +01:00
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
mut has_generic := false // x.foo<T>() instead of x.foo<int>()
|
|
|
|
mut concrete_types := []ast.Type{}
|
|
|
|
for concrete_type in call_expr.concrete_types {
|
|
|
|
if concrete_type.has_flag(.generic) {
|
|
|
|
has_generic = true
|
|
|
|
concrete_types << c.unwrap_generic(concrete_type)
|
2021-01-22 13:49:56 +01:00
|
|
|
} else {
|
2021-04-24 08:44:15 +02:00
|
|
|
concrete_types << concrete_type
|
2021-01-22 13:49:56 +01:00
|
|
|
}
|
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
if has_generic {
|
|
|
|
c.table.register_fn_generic_types(call_expr.name, concrete_types)
|
2020-12-31 18:00:22 +01:00
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
// 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
|
2021-01-25 10:26:20 +01:00
|
|
|
if left_type_sym.kind == .array && method_name in checker.array_builtin_methods {
|
2021-04-04 16:05:29 +02:00
|
|
|
return c.array_builtin_method_call(mut call_expr, left_type, left_type_sym)
|
2021-04-07 14:12:12 +02:00
|
|
|
} else if left_type_sym.kind == .map && method_name in ['clone', 'keys', 'move', 'delete'] {
|
2021-04-04 16:05:29 +02:00
|
|
|
return c.map_builtin_method_call(mut call_expr, left_type, left_type_sym)
|
2020-06-19 10:49:43 +02:00
|
|
|
} else if left_type_sym.kind == .array && method_name in ['insert', 'prepend'] {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := left_type_sym.info as ast.Array
|
2020-12-27 14:20:30 +01:00
|
|
|
arg_expr := if method_name == 'insert' {
|
|
|
|
call_expr.args[1].expr
|
|
|
|
} else {
|
|
|
|
call_expr.args[0].expr
|
|
|
|
}
|
2020-12-23 07:05:44 +01:00
|
|
|
arg_type := c.expr(arg_expr)
|
|
|
|
arg_sym := c.table.get_type_symbol(arg_type)
|
|
|
|
if !c.check_types(arg_type, info.elem_type) && !c.check_types(left_type, arg_type) {
|
|
|
|
c.error('cannot $method_name `$arg_sym.name` to `$left_type_sym.name`', arg_expr.position())
|
2020-06-19 10:49:43 +02:00
|
|
|
}
|
2021-02-22 14:08:52 +01:00
|
|
|
} else if left_type_sym.kind == .thread && method_name == 'wait' {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := left_type_sym.info as ast.Thread
|
2021-01-15 13:45:26 +01:00
|
|
|
if call_expr.args.len > 0 {
|
|
|
|
c.error('wait() does not have any arguments', call_expr.args[0].pos)
|
|
|
|
}
|
2021-02-27 09:16:55 +01:00
|
|
|
call_expr.return_type = info.return_type
|
2021-01-15 13:45:26 +01:00
|
|
|
return info.return_type
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
mut method := ast.Fn{}
|
2020-12-23 19:12:49 +01:00
|
|
|
mut has_method := false
|
2020-12-30 23:49:02 +01:00
|
|
|
mut is_method_from_embed := false
|
2020-12-23 19:12:49 +01:00
|
|
|
if m := c.table.type_find_method(left_type_sym, method_name) {
|
|
|
|
method = m
|
|
|
|
has_method = true
|
|
|
|
} else {
|
2021-04-02 00:57:09 +02:00
|
|
|
// can this logic be moved to ast.type_find_method() so it can be used from anywhere
|
|
|
|
if left_type_sym.info is ast.Struct {
|
2021-04-10 04:00:53 +02:00
|
|
|
if left_type_sym.info.parent_type != 0 {
|
|
|
|
type_sym := c.table.get_type_symbol(left_type_sym.info.parent_type)
|
|
|
|
if m := c.table.type_find_method(type_sym, method_name) {
|
|
|
|
method = m
|
|
|
|
has_method = true
|
|
|
|
is_generic = true
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mut found_methods := []ast.Fn{}
|
|
|
|
mut embed_of_found_methods := []ast.Type{}
|
|
|
|
for embed in left_type_sym.info.embeds {
|
|
|
|
embed_sym := c.table.get_type_symbol(embed)
|
|
|
|
if m := c.table.type_find_method(embed_sym, method_name) {
|
|
|
|
found_methods << m
|
|
|
|
embed_of_found_methods << embed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if found_methods.len == 1 {
|
|
|
|
method = found_methods[0]
|
|
|
|
has_method = true
|
|
|
|
is_method_from_embed = true
|
|
|
|
call_expr.from_embed_type = embed_of_found_methods[0]
|
|
|
|
} else if found_methods.len > 1 {
|
|
|
|
c.error('ambiguous method `$method_name`', call_expr.pos)
|
2021-01-01 14:36:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-23 19:12:49 +01:00
|
|
|
if left_type_sym.kind == .aggregate {
|
|
|
|
// the error message contains the problematic type
|
2021-03-13 18:13:50 +01:00
|
|
|
unknown_method_msg = err.msg
|
2020-12-23 19:12:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if has_method {
|
2021-03-03 10:12:50 +01:00
|
|
|
if !method.is_pub && !c.pref.is_test && 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-12-06 04:55:08 +01:00
|
|
|
c.error('method `${left_type_sym.name}.$method_name` is private', call_expr.pos)
|
2020-04-17 17:16:14 +02:00
|
|
|
}
|
2021-02-03 00:20:19 +01:00
|
|
|
rec_share := method.params[0].typ.share()
|
|
|
|
if rec_share == .shared_t && (c.locked_names.len > 0 || c.rlocked_names.len > 0) {
|
|
|
|
c.error('method with `shared` receiver cannot be called inside `lock`/`rlock` block',
|
|
|
|
call_expr.pos)
|
|
|
|
}
|
2020-09-27 03:32:56 +02:00
|
|
|
if method.params[0].is_mut {
|
2021-02-03 00:20:19 +01:00
|
|
|
to_lock, pos := c.fail_if_immutable(call_expr.left)
|
2020-06-06 16:05:16 +02:00
|
|
|
// call_expr.is_mut = true
|
2021-02-03 00:20:19 +01:00
|
|
|
if to_lock != '' && rec_share != .shared_t {
|
|
|
|
c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`',
|
|
|
|
pos)
|
|
|
|
}
|
|
|
|
} else {
|
2021-03-12 16:08:39 +01:00
|
|
|
c.fail_if_unreadable(call_expr.left, left_type, 'receiver')
|
2020-06-01 15:43:54 +02:00
|
|
|
}
|
2021-01-26 11:19:48 +01:00
|
|
|
if (!left_type_sym.is_builtin() && method.mod != 'builtin') && method.language == .v
|
|
|
|
&& method.no_body {
|
|
|
|
c.error('cannot call a method that does not have a body', call_expr.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if method.return_type == ast.void_type && method.ctdefine.len > 0
|
2021-01-23 09:33:22 +01:00
|
|
|
&& method.ctdefine !in c.pref.compile_defines {
|
2020-04-25 15:57:11 +02:00
|
|
|
call_expr.should_be_skipped = true
|
|
|
|
}
|
2020-09-27 03:32:56 +02:00
|
|
|
nr_args := if method.params.len == 0 { 0 } else { method.params.len - 1 }
|
2021-01-23 09:33:22 +01:00
|
|
|
min_required_args := method.params.len - if method.is_variadic && method.params.len > 1 {
|
|
|
|
2
|
|
|
|
} else {
|
|
|
|
1
|
|
|
|
}
|
2020-03-30 12:39:20 +02:00
|
|
|
if call_expr.args.len < min_required_args {
|
2020-12-10 15:42:30 +01:00
|
|
|
c.error('expected $min_required_args arguments, but got $call_expr.args.len',
|
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 {
|
2020-12-10 15:42:30 +01:00
|
|
|
unexpected_arguments := call_expr.args[min_required_args..]
|
|
|
|
unexpected_arguments_pos := unexpected_arguments[0].pos.extend(unexpected_arguments.last().pos)
|
|
|
|
c.error('expected $nr_args arguments, but got $call_expr.args.len', unexpected_arguments_pos)
|
2020-04-12 17:45:04 +02:00
|
|
|
return method.return_type
|
2020-03-30 12:39:20 +02:00
|
|
|
}
|
2021-04-19 10:41:21 +02:00
|
|
|
mut exp_arg_typ := ast.Type(0) // type of 1st arg for special builtin methods
|
|
|
|
mut param_is_mut := false
|
|
|
|
mut no_type_promotion := false
|
|
|
|
if left_type_sym.kind == .chan {
|
|
|
|
elem_typ := (left_type_sym.info as ast.Chan).elem_type
|
|
|
|
if method_name == 'try_push' {
|
|
|
|
exp_arg_typ = elem_typ.to_ptr()
|
|
|
|
} else if method_name == 'try_pop' {
|
|
|
|
exp_arg_typ = elem_typ
|
|
|
|
param_is_mut = true
|
|
|
|
no_type_promotion = true
|
|
|
|
}
|
|
|
|
}
|
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 {
|
2021-04-19 10:41:21 +02:00
|
|
|
if i > 0 || exp_arg_typ == ast.Type(0) {
|
|
|
|
exp_arg_typ = if method.is_variadic && i >= method.params.len - 1 {
|
|
|
|
method.params[method.params.len - 1].typ
|
|
|
|
} else {
|
|
|
|
method.params[i + 1].typ
|
|
|
|
}
|
|
|
|
param_is_mut = false
|
|
|
|
no_type_promotion = false
|
2021-01-23 09:33:22 +01:00
|
|
|
}
|
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
|
2021-03-20 17:27:16 +01:00
|
|
|
got_arg_typ := c.check_expr_opt_call(arg.expr, c.expr(arg.expr))
|
2020-05-04 17:32:40 +02:00
|
|
|
call_expr.args[i].typ = got_arg_typ
|
2021-04-19 10:41:21 +02:00
|
|
|
if no_type_promotion {
|
|
|
|
if got_arg_typ != exp_arg_typ {
|
|
|
|
c.error('cannot use `${c.table.get_type_symbol(got_arg_typ).name}` as argument for `$method.name` (`$exp_arg_sym.name` expected)',
|
|
|
|
arg.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-06-24 14:44:06 +02:00
|
|
|
if method.is_variadic && got_arg_typ.has_flag(.variadic) && call_expr.args.len - 1 > i {
|
2021-02-24 19:35:32 +01:00
|
|
|
c.error('when forwarding a variadic variable, it must be the final argument',
|
|
|
|
arg.pos)
|
2020-04-22 05:28:47 +02:00
|
|
|
}
|
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
|
|
|
|
}
|
2021-01-22 13:49:56 +01:00
|
|
|
if method.generic_names.len > 0 {
|
2020-12-31 18:00:22 +01:00
|
|
|
continue
|
|
|
|
}
|
2021-04-27 14:25:42 +02:00
|
|
|
c.check_expected_call_arg(got_arg_typ, c.unwrap_generic(exp_arg_typ), call_expr.language) or {
|
2020-05-04 17:32:40 +02:00
|
|
|
// str method, allow type with str method if fn arg is string
|
2020-12-29 16:14:08 +01:00
|
|
|
// Passing an int or a string array produces a c error here
|
|
|
|
// Deleting this condition results in propper V error messages
|
|
|
|
// if arg_typ_sym.kind == .string && typ_sym.has_method('str') {
|
2020-06-24 14:44:06 +02:00
|
|
|
// continue
|
2020-06-23 00:51:18 +02:00
|
|
|
// }
|
2021-04-02 00:57:09 +02:00
|
|
|
if got_arg_typ != ast.void_type {
|
2021-03-13 18:13:50 +01:00
|
|
|
c.error('$err.msg in argument ${i + 1} to `${left_type_sym.name}.$method_name`',
|
2021-02-24 19:35:32 +01:00
|
|
|
arg.pos)
|
2020-06-19 11:15:15 +02:00
|
|
|
}
|
2020-05-04 17:32:40 +02:00
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
param := if method.is_variadic && i >= method.params.len - 1 {
|
|
|
|
method.params[method.params.len - 1]
|
|
|
|
} else {
|
|
|
|
method.params[i + 1]
|
|
|
|
}
|
2021-04-19 10:41:21 +02:00
|
|
|
param_is_mut = param_is_mut || param.is_mut
|
2021-02-03 00:20:19 +01:00
|
|
|
param_share := param.typ.share()
|
|
|
|
if param_share == .shared_t && (c.locked_names.len > 0 || c.rlocked_names.len > 0) {
|
|
|
|
c.error('method with `shared` arguments cannot be called inside `lock`/`rlock` block',
|
2021-02-24 19:35:32 +01:00
|
|
|
arg.pos)
|
2021-02-03 00:20:19 +01:00
|
|
|
}
|
2020-09-27 03:14:24 +02:00
|
|
|
if arg.is_mut {
|
2021-02-03 00:20:19 +01:00
|
|
|
to_lock, pos := c.fail_if_immutable(arg.expr)
|
2021-04-19 10:41:21 +02:00
|
|
|
if !param_is_mut {
|
2020-09-27 03:14:24 +02:00
|
|
|
tok := arg.share.str()
|
|
|
|
c.error('`$call_expr.name` parameter `$param.name` is not `$tok`, `$tok` is not needed`',
|
|
|
|
arg.expr.position())
|
2021-02-03 00:20:19 +01:00
|
|
|
} else {
|
2021-04-19 10:41:21 +02:00
|
|
|
if param_share != arg.share {
|
2021-02-03 00:20:19 +01:00
|
|
|
c.error('wrong shared type', arg.expr.position())
|
|
|
|
}
|
|
|
|
if to_lock != '' && param_share != .shared_t {
|
|
|
|
c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`',
|
|
|
|
pos)
|
|
|
|
}
|
2020-09-27 03:14:24 +02:00
|
|
|
}
|
|
|
|
} else {
|
2021-04-19 10:41:21 +02:00
|
|
|
if param_is_mut {
|
2020-09-27 03:14:24 +02:00
|
|
|
tok := arg.share.str()
|
2021-02-03 00:20:19 +01:00
|
|
|
c.error('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${
|
2021-01-23 09:33:22 +01:00
|
|
|
i + 1}`', arg.expr.position())
|
2021-02-03 00:20:19 +01:00
|
|
|
} else {
|
2021-03-12 16:08:39 +01:00
|
|
|
c.fail_if_unreadable(arg.expr, got_arg_typ, 'argument')
|
2020-09-27 03:14:24 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-06 10:52:03 +01:00
|
|
|
}
|
2020-07-17 19:14:12 +02:00
|
|
|
if method.is_unsafe && !c.inside_unsafe {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.warn('method `${left_type_sym.name}.$method_name` must be called from an `unsafe` block',
|
2020-07-17 19:14:12 +02:00
|
|
|
call_expr.pos)
|
|
|
|
}
|
2021-02-20 18:25:54 +01:00
|
|
|
if !c.cur_fn.is_deprecated && method.is_deprecated {
|
2021-03-22 18:43:06 +01:00
|
|
|
c.deprecate_fnmethod('method', '${left_type_sym.name}.$method.name', method,
|
|
|
|
call_expr)
|
2021-02-20 18:25:54 +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-09-27 03:32:56 +02:00
|
|
|
for i in 1 .. method.params.len {
|
|
|
|
call_expr.expected_arg_types << method.params[i].typ
|
2020-03-26 11:09:59 +01:00
|
|
|
}
|
|
|
|
}
|
2020-12-30 23:49:02 +01:00
|
|
|
if is_method_from_embed {
|
|
|
|
call_expr.receiver_type = call_expr.from_embed_type.derive(method.params[0].typ)
|
|
|
|
} else if is_generic {
|
2020-05-28 05:50:57 +02:00
|
|
|
// We need the receiver to be T in cgen.
|
2020-06-29 20:09:09 +02:00
|
|
|
// TODO: cant we just set all these to the concrete type in checker? then no need in gen
|
2020-09-27 03:32:56 +02:00
|
|
|
call_expr.receiver_type = left_type.derive(method.params[0].typ).set_flag(.generic)
|
2020-05-28 05:50:57 +02:00
|
|
|
} else {
|
2020-09-27 03:32:56 +02:00
|
|
|
call_expr.receiver_type = method.params[0].typ
|
2020-05-28 05:50:57 +02:00
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
if method.generic_names.len != call_expr.concrete_types.len {
|
2020-12-31 18:00:22 +01:00
|
|
|
// no type arguments given in call, attempt implicit instantiation
|
2021-04-15 10:00:23 +02:00
|
|
|
c.infer_fn_generic_types(method, mut call_expr)
|
2021-04-27 14:25:42 +02:00
|
|
|
concrete_types = call_expr.concrete_types
|
2020-12-31 18:00:22 +01:00
|
|
|
}
|
2021-04-26 18:54:22 +02:00
|
|
|
// resolve return generics struct to concrete type
|
|
|
|
if method.generic_names.len > 0 && method.return_type.has_flag(.generic) {
|
2021-04-27 14:25:42 +02:00
|
|
|
c.check_return_generics_struct(method.return_type, mut call_expr, concrete_types)
|
2021-04-26 18:54:22 +02:00
|
|
|
} else {
|
|
|
|
call_expr.return_type = method.return_type
|
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
if call_expr.concrete_types.len > 0 && method.return_type != 0 {
|
2021-04-21 11:22:42 +02:00
|
|
|
if typ := c.table.resolve_generic_to_concrete(method.return_type, method.generic_names,
|
2021-04-27 14:25:42 +02:00
|
|
|
concrete_types, false)
|
2021-01-26 11:19:32 +01:00
|
|
|
{
|
2020-12-31 18:00:22 +01:00
|
|
|
call_expr.return_type = typ
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
if call_expr.concrete_types.len > 0 && method.generic_names.len == 0 {
|
|
|
|
c.error('a non generic function called like a generic one', call_expr.concrete_list_pos)
|
2020-12-31 18:00:22 +01:00
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
if call_expr.concrete_types.len > method.generic_names.len {
|
|
|
|
c.error('too many generic parameters got $call_expr.concrete_types.len, expected $method.generic_names.len',
|
|
|
|
call_expr.concrete_list_pos)
|
2021-04-02 16:27:54 +02:00
|
|
|
}
|
2021-01-22 13:49:56 +01:00
|
|
|
if method.generic_names.len > 0 {
|
2020-12-31 18:00:22 +01:00
|
|
|
return call_expr.return_type
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
return method.return_type
|
|
|
|
}
|
|
|
|
// TODO: str methods
|
2020-04-28 07:22:46 +02:00
|
|
|
if method_name == 'str' {
|
2020-07-10 21:47:29 +02:00
|
|
|
if left_type_sym.kind == .interface_ {
|
2020-12-06 04:55:08 +01:00
|
|
|
iname := left_type_sym.name
|
2020-07-14 17:18:10 +02:00
|
|
|
c.error('interface `$iname` does not have a .str() method. Use typeof() instead',
|
|
|
|
call_expr.pos)
|
2020-07-10 21:47:29 +02:00
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
call_expr.receiver_type = left_type
|
2021-04-02 00:57:09 +02:00
|
|
|
call_expr.return_type = ast.string_type
|
2020-07-04 13:45:11 +02:00
|
|
|
if call_expr.args.len > 0 {
|
|
|
|
c.error('.str() method calls should have no arguments', call_expr.pos)
|
|
|
|
}
|
2021-03-12 16:08:39 +01:00
|
|
|
c.fail_if_unreadable(call_expr.left, left_type, 'receiver')
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
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
|
2021-01-23 07:57:17 +01:00
|
|
|
if field := c.table.find_field(left_type_sym, method_name) {
|
2020-04-15 04:28:18 +02:00
|
|
|
field_type_sym := c.table.get_type_symbol(field.typ)
|
|
|
|
if field_type_sym.kind == .function {
|
2020-07-13 19:59:00 +02:00
|
|
|
// call_expr.is_method = false
|
|
|
|
call_expr.is_field = true
|
2021-04-02 00:57:09 +02:00
|
|
|
info := field_type_sym.info as ast.FnType
|
2020-04-15 04:28:18 +02:00
|
|
|
call_expr.return_type = info.func.return_type
|
2021-04-02 00:57:09 +02:00
|
|
|
mut earg_types := []ast.Type{}
|
2021-01-15 18:16:34 +01:00
|
|
|
for mut arg in call_expr.args {
|
2021-03-20 17:27:16 +01:00
|
|
|
targ := c.check_expr_opt_call(arg.expr, c.expr(arg.expr))
|
2021-01-15 18:16:34 +01:00
|
|
|
arg.typ = targ
|
|
|
|
earg_types << targ
|
2020-05-01 12:12:21 +02:00
|
|
|
}
|
2021-01-15 18:16:34 +01:00
|
|
|
call_expr.expected_arg_types = earg_types
|
2020-04-15 04:28:18 +02:00
|
|
|
return info.func.return_type
|
|
|
|
}
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if left_type != ast.void_type {
|
2020-07-30 16:34:05 +02:00
|
|
|
suggestion := util.new_suggestion(method_name, left_type_sym.methods.map(it.name))
|
2020-10-01 01:07:36 +02:00
|
|
|
c.error(suggestion.say(unknown_method_msg), call_expr.pos)
|
2020-06-08 13:10:47 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
|
|
|
|
2021-04-04 16:05:29 +02:00
|
|
|
fn (mut c Checker) map_builtin_method_call(mut call_expr ast.CallExpr, left_type ast.Type, left_type_sym ast.TypeSymbol) ast.Type {
|
2021-03-19 13:19:38 +01:00
|
|
|
method_name := call_expr.name
|
2021-04-02 00:57:09 +02:00
|
|
|
mut ret_type := ast.void_type
|
2021-03-19 13:19:38 +01:00
|
|
|
match method_name {
|
|
|
|
'clone', 'move' {
|
|
|
|
if method_name[0] == `m` {
|
|
|
|
c.fail_if_immutable(call_expr.left)
|
|
|
|
}
|
|
|
|
if call_expr.left.is_auto_deref_var() {
|
|
|
|
ret_type = left_type.deref()
|
|
|
|
} else {
|
|
|
|
ret_type = left_type
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'keys' {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := left_type_sym.info as ast.Map
|
2021-03-19 13:19:38 +01:00
|
|
|
typ := c.table.find_or_register_array(info.key_type)
|
2021-04-02 00:57:09 +02:00
|
|
|
ret_type = ast.Type(typ)
|
2021-03-19 13:19:38 +01:00
|
|
|
}
|
2021-04-07 14:12:12 +02:00
|
|
|
'delete' {
|
|
|
|
c.fail_if_immutable(call_expr.left)
|
|
|
|
if call_expr.args.len != 1 {
|
|
|
|
c.error('expected 1 argument, but got $call_expr.args.len', call_expr.pos)
|
|
|
|
}
|
|
|
|
info := left_type_sym.info as ast.Map
|
|
|
|
arg_type := c.expr(call_expr.args[0].expr)
|
|
|
|
c.check_expected_call_arg(arg_type, info.key_type, call_expr.language) or {
|
|
|
|
c.error('$err.msg in argument 1 to `Map.delete`', call_expr.args[0].pos)
|
|
|
|
}
|
|
|
|
}
|
2021-03-19 13:19:38 +01:00
|
|
|
else {}
|
|
|
|
}
|
|
|
|
call_expr.receiver_type = left_type.to_ptr()
|
|
|
|
call_expr.return_type = ret_type
|
|
|
|
return call_expr.return_type
|
|
|
|
}
|
|
|
|
|
2021-04-04 16:05:29 +02:00
|
|
|
fn (mut c Checker) array_builtin_method_call(mut call_expr ast.CallExpr, left_type ast.Type, left_type_sym ast.TypeSymbol) ast.Type {
|
2021-03-10 19:19:39 +01:00
|
|
|
method_name := call_expr.name
|
2021-04-02 00:57:09 +02:00
|
|
|
mut elem_typ := ast.void_type
|
2021-03-17 01:42:51 +01:00
|
|
|
if method_name == 'slice' && !c.is_builtin_mod {
|
2021-03-10 19:19:39 +01:00
|
|
|
c.error('.slice() is a private method, use `x[start..end]` instead', call_expr.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
array_info := left_type_sym.info as ast.Array
|
2021-03-17 01:42:51 +01:00
|
|
|
elem_typ = array_info.elem_type
|
2021-03-18 15:24:16 +01:00
|
|
|
if method_name in ['filter', 'map', 'any', 'all'] {
|
2021-03-17 01:42:51 +01:00
|
|
|
// position of `it` doesn't matter
|
|
|
|
scope_register_it(mut call_expr.scope, call_expr.pos, elem_typ)
|
|
|
|
} else if method_name == 'sort' {
|
|
|
|
c.fail_if_immutable(call_expr.left)
|
|
|
|
// position of `a` and `b` doesn't matter, they're the same
|
|
|
|
scope_register_a_b(mut call_expr.scope, call_expr.pos, elem_typ)
|
2021-03-10 19:19:39 +01:00
|
|
|
|
2021-03-17 01:42:51 +01:00
|
|
|
if call_expr.args.len > 1 {
|
|
|
|
c.error('expected 0 or 1 argument, but got $call_expr.args.len', call_expr.pos)
|
|
|
|
} else if call_expr.args.len == 1 {
|
|
|
|
if call_expr.args[0].expr is ast.InfixExpr {
|
|
|
|
if call_expr.args[0].expr.op !in [.gt, .lt] {
|
|
|
|
c.error('`.sort()` can only use `<` or `>` comparison', call_expr.pos)
|
|
|
|
}
|
|
|
|
left_name := '${call_expr.args[0].expr.left}'[0]
|
|
|
|
right_name := '${call_expr.args[0].expr.right}'[0]
|
|
|
|
if left_name !in [`a`, `b`] || right_name !in [`a`, `b`] {
|
|
|
|
c.error('`.sort()` can only use `a` or `b` as argument, e.g. `arr.sort(a < b)`',
|
2021-03-10 19:19:39 +01:00
|
|
|
call_expr.pos)
|
2021-03-17 01:42:51 +01:00
|
|
|
} else if left_name == right_name {
|
|
|
|
c.error('`.sort()` cannot use same argument', call_expr.pos)
|
2021-03-10 19:19:39 +01:00
|
|
|
}
|
|
|
|
} else {
|
2021-03-17 01:42:51 +01:00
|
|
|
c.error(
|
|
|
|
'`.sort()` requires a `<` or `>` comparison as the first and only argument' +
|
|
|
|
'\ne.g. `users.sort(a.id < b.id)`', call_expr.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if method_name == 'wait' {
|
|
|
|
elem_sym := c.table.get_type_symbol(elem_typ)
|
|
|
|
if elem_sym.kind == .thread {
|
|
|
|
if call_expr.args.len != 0 {
|
|
|
|
c.error('`.wait()` does not have any arguments', call_expr.args[0].pos)
|
2021-03-10 19:19:39 +01:00
|
|
|
}
|
2021-03-17 01:42:51 +01:00
|
|
|
thread_ret_type := elem_sym.thread_info().return_type
|
|
|
|
if thread_ret_type.has_flag(.optional) {
|
|
|
|
c.error('`.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`.',
|
|
|
|
call_expr.pos)
|
|
|
|
}
|
|
|
|
call_expr.return_type = c.table.find_or_register_array(thread_ret_type)
|
|
|
|
} else {
|
|
|
|
c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)',
|
|
|
|
call_expr.left.position())
|
2021-03-10 19:19:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// map/filter are supposed to have 1 arg only
|
|
|
|
mut arg_type := left_type
|
|
|
|
for arg in call_expr.args {
|
2021-03-20 17:27:16 +01:00
|
|
|
arg_type = c.check_expr_opt_call(arg.expr, c.expr(arg.expr))
|
2021-03-10 19:19:39 +01:00
|
|
|
}
|
|
|
|
if method_name == 'map' {
|
|
|
|
// check fn
|
|
|
|
c.check_map_and_filter(true, elem_typ, call_expr)
|
|
|
|
arg_sym := c.table.get_type_symbol(arg_type)
|
|
|
|
ret_type := match arg_sym.info {
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.FnType { arg_sym.info.func.return_type }
|
2021-03-10 19:19:39 +01:00
|
|
|
else { arg_type }
|
|
|
|
}
|
|
|
|
call_expr.return_type = c.table.find_or_register_array(ret_type)
|
|
|
|
} else if method_name == 'filter' {
|
|
|
|
// check fn
|
|
|
|
c.check_map_and_filter(false, elem_typ, call_expr)
|
2021-03-18 15:24:16 +01:00
|
|
|
} else if method_name in ['any', 'all'] {
|
|
|
|
c.check_map_and_filter(false, elem_typ, call_expr)
|
2021-04-02 00:57:09 +02:00
|
|
|
call_expr.return_type = ast.bool_type
|
2021-03-10 19:19:39 +01:00
|
|
|
} else if method_name == 'clone' {
|
|
|
|
// need to return `array_xxx` instead of `array`
|
|
|
|
// in ['clone', 'str'] {
|
|
|
|
call_expr.receiver_type = left_type.to_ptr()
|
|
|
|
if call_expr.left.is_auto_deref_var() {
|
|
|
|
call_expr.return_type = left_type.deref()
|
|
|
|
} else {
|
|
|
|
call_expr.return_type = call_expr.receiver_type.set_nr_muls(0)
|
|
|
|
}
|
|
|
|
} else if method_name == 'sort' {
|
2021-04-02 00:57:09 +02:00
|
|
|
call_expr.return_type = ast.void_type
|
2021-03-10 19:19:39 +01:00
|
|
|
} else if method_name == 'contains' {
|
2021-04-02 00:57:09 +02:00
|
|
|
call_expr.return_type = ast.bool_type
|
2021-03-10 19:19:39 +01:00
|
|
|
} else if method_name == 'index' {
|
2021-04-02 00:57:09 +02:00
|
|
|
call_expr.return_type = ast.int_type
|
2021-03-19 13:19:38 +01:00
|
|
|
} else if method_name in ['first', 'last', 'pop'] {
|
|
|
|
call_expr.return_type = array_info.elem_type
|
|
|
|
if method_name == 'pop' {
|
|
|
|
call_expr.receiver_type = left_type.to_ptr()
|
|
|
|
} else {
|
|
|
|
call_expr.receiver_type = left_type
|
|
|
|
}
|
2021-03-10 19:19:39 +01:00
|
|
|
}
|
|
|
|
return call_expr.return_type
|
|
|
|
}
|
|
|
|
|
2021-04-04 16:05:29 +02:00
|
|
|
pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
|
2020-04-12 17:45:04 +02:00
|
|
|
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)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
mut has_generic := false // foo<T>() instead of foo<int>()
|
|
|
|
mut concrete_types := []ast.Type{}
|
|
|
|
for concrete_type in call_expr.concrete_types {
|
|
|
|
if concrete_type.has_flag(.generic) {
|
|
|
|
has_generic = true
|
|
|
|
concrete_types << c.unwrap_generic(concrete_type)
|
2021-01-22 13:49:56 +01:00
|
|
|
} else {
|
2021-04-24 08:44:15 +02:00
|
|
|
concrete_types << concrete_type
|
2021-01-22 13:49:56 +01:00
|
|
|
}
|
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
if has_generic {
|
2021-04-18 15:26:40 +02:00
|
|
|
if c.mod != '' && !fn_name.starts_with('${c.mod}.') {
|
2020-05-29 04:30:00 +02:00
|
|
|
// Need to prepend the module when adding a generic type to a function
|
2021-04-24 08:44:15 +02:00
|
|
|
c.table.register_fn_generic_types(c.mod + '.' + fn_name, concrete_types)
|
2020-05-29 04:30:00 +02:00
|
|
|
} else {
|
2021-04-24 08:44:15 +02:00
|
|
|
c.table.register_fn_generic_types(fn_name, concrete_types)
|
2020-05-29 04:30:00 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
if fn_name == 'json.encode' {
|
2020-12-11 06:55:39 +01:00
|
|
|
} else if fn_name == 'json.decode' && call_expr.args.len > 0 {
|
2021-03-12 18:02:09 +01:00
|
|
|
if call_expr.args.len != 2 {
|
|
|
|
c.error("json.decode expects 2 arguments, a type and a string (e.g `json.decode(T, '')`)",
|
|
|
|
call_expr.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2021-03-12 18:02:09 +01:00
|
|
|
}
|
2020-05-04 16:46:36 +02:00
|
|
|
expr := call_expr.args[0].expr
|
2021-04-02 00:57:09 +02:00
|
|
|
if expr !is ast.TypeNode {
|
2021-01-07 20:32:02 +01:00
|
|
|
typ := expr.type_name()
|
2020-05-05 14:19:31 +02:00
|
|
|
c.error('json.decode: first argument needs to be a type, got `$typ`', call_expr.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-05-04 16:46:36 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_type = ast.string_type
|
2020-05-06 20:44:46 +02:00
|
|
|
call_expr.args[1].typ = c.expr(call_expr.args[1].expr)
|
2021-04-02 00:57:09 +02:00
|
|
|
if call_expr.args[1].typ != ast.string_type {
|
2020-05-13 21:59:05 +02:00
|
|
|
c.error('json.decode: second argument needs to be a string', call_expr.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
typ := expr as ast.TypeNode
|
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-07-01 00:53:53 +02:00
|
|
|
// look for function in format `mod.fn` or `fn` (builtin)
|
2021-04-11 23:57:00 +02:00
|
|
|
mut func := ast.Fn{}
|
2020-04-12 17:45:04 +02:00
|
|
|
mut found := false
|
2020-04-28 15:27:49 +02:00
|
|
|
mut found_in_args := false
|
2020-07-07 17:10:39 +02:00
|
|
|
// anon fn direct call
|
2020-11-21 00:05:57 +01:00
|
|
|
if mut call_expr.left is ast.AnonFn {
|
2020-07-07 17:10:39 +02:00
|
|
|
// it was set to anon for checker errors, clear for gen
|
|
|
|
call_expr.name = ''
|
|
|
|
c.expr(call_expr.left)
|
2020-11-21 00:05:57 +01:00
|
|
|
anon_fn_sym := c.table.get_type_symbol(call_expr.left.typ)
|
2021-04-11 23:57:00 +02:00
|
|
|
func = (anon_fn_sym.info as ast.FnType).func
|
2020-07-07 17:10:39 +02:00
|
|
|
found = true
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
// try prefix with current module as it would have never gotten prefixed
|
2020-07-19 19:58:34 +02:00
|
|
|
if !found && !fn_name.contains('.') && call_expr.mod != 'builtin' {
|
2020-04-12 17:45:04 +02:00
|
|
|
name_prefixed := '${call_expr.mod}.$fn_name'
|
2021-04-11 23:57:00 +02:00
|
|
|
if f := c.table.find_fn(name_prefixed) {
|
2020-04-12 17:45:04 +02:00
|
|
|
call_expr.name = name_prefixed
|
|
|
|
found = true
|
2021-04-11 23:57:00 +02:00
|
|
|
func = f
|
2021-02-05 09:03:17 +01:00
|
|
|
c.table.fns[name_prefixed].usages++
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
|
|
|
}
|
2020-12-15 04:24:57 +01:00
|
|
|
if !found && call_expr.left is ast.IndexExpr {
|
|
|
|
c.expr(call_expr.left)
|
|
|
|
expr := call_expr.left as ast.IndexExpr
|
|
|
|
sym := c.table.get_type_symbol(expr.left_type)
|
|
|
|
if sym.kind == .array {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := sym.info as ast.Array
|
2020-12-15 04:24:57 +01:00
|
|
|
elem_typ := c.table.get_type_symbol(info.elem_type)
|
2021-04-02 00:57:09 +02:00
|
|
|
if elem_typ.info is ast.FnType {
|
2020-12-15 04:24:57 +01:00
|
|
|
return elem_typ.info.func.return_type
|
|
|
|
}
|
2021-02-02 03:58:32 +01:00
|
|
|
} else if sym.kind == .map {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := sym.info as ast.Map
|
2021-02-02 03:58:32 +01:00
|
|
|
value_typ := c.table.get_type_symbol(info.value_type)
|
2021-04-02 00:57:09 +02:00
|
|
|
if value_typ.info is ast.FnType {
|
2021-02-02 03:58:32 +01:00
|
|
|
return value_typ.info.func.return_type
|
|
|
|
}
|
2021-02-01 14:50:10 +01:00
|
|
|
} else if sym.kind == .array_fixed {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := sym.info as ast.ArrayFixed
|
2021-02-01 14:50:10 +01:00
|
|
|
elem_typ := c.table.get_type_symbol(info.elem_type)
|
2021-04-02 00:57:09 +02:00
|
|
|
if elem_typ.info is ast.FnType {
|
2021-02-01 14:50:10 +01:00
|
|
|
return elem_typ.info.func.return_type
|
|
|
|
}
|
2020-12-15 04:24:57 +01:00
|
|
|
}
|
|
|
|
found = true
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2020-12-15 04:24:57 +01:00
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
// already prefixed (mod.fn) or C/builtin/main
|
|
|
|
if !found {
|
2021-04-11 23:57:00 +02:00
|
|
|
if f := c.table.find_fn(fn_name) {
|
2020-04-12 17:45:04 +02:00
|
|
|
found = true
|
2021-04-11 23:57:00 +02:00
|
|
|
func = f
|
2021-02-05 09:03:17 +01:00
|
|
|
c.table.fns[fn_name].usages++
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
|
|
|
}
|
2021-04-26 23:43:00 +02:00
|
|
|
if !found && c.pref.backend == .native {
|
|
|
|
if fn_name in native.builtins {
|
|
|
|
c.table.fns[fn_name].usages++
|
2021-04-27 00:09:37 +02:00
|
|
|
return ast.void_type
|
2021-04-26 23:43:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found && c.pref.is_script && !found {
|
2020-07-04 23:37:41 +02:00
|
|
|
os_name := 'os.$fn_name'
|
2021-04-11 23:57:00 +02:00
|
|
|
if f := c.table.find_fn(os_name) {
|
2021-04-24 08:44:15 +02:00
|
|
|
if f.generic_names.len == call_expr.concrete_types.len {
|
2021-04-15 10:00:23 +02:00
|
|
|
c.table.fn_generic_types[os_name] = c.table.fn_generic_types['${call_expr.mod}.$call_expr.name']
|
2020-12-02 11:40:44 +01:00
|
|
|
}
|
2020-07-04 23:37:41 +02:00
|
|
|
call_expr.name = os_name
|
|
|
|
found = true
|
2021-04-11 23:57:00 +02:00
|
|
|
func = f
|
2021-02-05 09:03:17 +01:00
|
|
|
c.table.fns[os_name].usages++
|
2020-07-04 23:37:41 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
// check for arg (var) of fn type
|
|
|
|
if !found {
|
2020-12-12 09:01:12 +01:00
|
|
|
if v := call_expr.scope.find_var(fn_name) {
|
2020-04-15 01:45:27 +02:00
|
|
|
if v.typ != 0 {
|
|
|
|
vts := c.table.get_type_symbol(v.typ)
|
2020-04-12 17:45:04 +02:00
|
|
|
if vts.kind == .function {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := vts.info as ast.FnType
|
2021-04-11 23:57:00 +02:00
|
|
|
func = info.func
|
2020-04-12 17:45:04 +02:00
|
|
|
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)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
2020-07-01 00:53:53 +02:00
|
|
|
if !found_in_args {
|
2020-12-12 09:01:12 +01:00
|
|
|
if _ := call_expr.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
|
|
|
}
|
|
|
|
}
|
2021-04-11 23:57:00 +02:00
|
|
|
if !func.is_pub && func.language == .v && func.name.len > 0 && func.mod.len > 0
|
|
|
|
&& func.mod != c.mod {
|
|
|
|
c.error('function `$func.name` is private', call_expr.pos)
|
2020-06-23 16:25:24 +02:00
|
|
|
}
|
2021-04-15 07:49:25 +02:00
|
|
|
if c.cur_fn != 0 && !c.cur_fn.is_deprecated && func.is_deprecated {
|
2021-04-11 23:57:00 +02:00
|
|
|
c.deprecate_fnmethod('function', func.name, func, call_expr)
|
2020-05-18 22:54:08 +02:00
|
|
|
}
|
2021-04-11 23:57:00 +02:00
|
|
|
if func.is_unsafe && !c.inside_unsafe
|
|
|
|
&& (func.language != .c || (func.name[2] in [`m`, `s`] && func.mod == 'builtin')) {
|
2020-07-20 19:06:41 +02:00
|
|
|
// builtin C.m*, C.s* only - temp
|
2021-04-11 23:57:00 +02:00
|
|
|
c.warn('function `$func.name` must be called from an `unsafe` block', call_expr.pos)
|
2020-07-20 19:06:41 +02:00
|
|
|
}
|
2021-04-11 23:57:00 +02:00
|
|
|
call_expr.is_keep_alive = func.is_keep_alive
|
|
|
|
if func.mod != 'builtin' && func.language == .v && func.no_body && !c.pref.translated
|
|
|
|
&& !func.is_unsafe {
|
2021-01-26 11:19:48 +01:00
|
|
|
c.error('cannot call a function that does not have a body', call_expr.pos)
|
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
for concrete_type in call_expr.concrete_types {
|
|
|
|
c.ensure_type_exists(concrete_type, call_expr.concrete_list_pos) or {}
|
2021-01-08 07:04:06 +01:00
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
if func.generic_names.len > 0 && call_expr.args.len == 0 && call_expr.concrete_types.len == 0 {
|
2021-04-20 20:36:21 +02:00
|
|
|
c.error('no argument generic function must add concrete types, e.g. foo<int>()',
|
|
|
|
call_expr.pos)
|
|
|
|
return func.return_type
|
|
|
|
}
|
2021-04-11 23:57:00 +02:00
|
|
|
if func.return_type == ast.void_type && func.ctdefine.len > 0
|
|
|
|
&& func.ctdefine !in c.pref.compile_defines {
|
2020-04-25 15:57:11 +02:00
|
|
|
call_expr.should_be_skipped = true
|
|
|
|
}
|
2021-03-06 18:09:28 +01:00
|
|
|
// dont check number of args for JS functions since arguments are not required
|
2021-03-05 15:41:11 +01:00
|
|
|
if call_expr.language != .js {
|
2021-04-11 23:57:00 +02:00
|
|
|
min_required_args := if func.is_variadic { func.params.len - 1 } else { func.params.len }
|
2021-03-05 15:41:11 +01:00
|
|
|
if call_expr.args.len < min_required_args {
|
|
|
|
c.error('expected $min_required_args arguments, but got $call_expr.args.len',
|
|
|
|
call_expr.pos)
|
2021-04-11 23:57:00 +02:00
|
|
|
} else if !func.is_variadic && call_expr.args.len > func.params.len {
|
2021-03-05 15:41:11 +01:00
|
|
|
unexpected_arguments := call_expr.args[min_required_args..]
|
|
|
|
unexpected_arguments_pos := unexpected_arguments[0].pos.extend(unexpected_arguments.last().pos)
|
|
|
|
c.error('expected $min_required_args arguments, but got $call_expr.args.len',
|
|
|
|
unexpected_arguments_pos)
|
2021-04-11 23:57:00 +02:00
|
|
|
return func.return_type
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
|
|
|
}
|
2021-02-28 22:57:18 +01:00
|
|
|
// println / eprintln / panic can print anything
|
|
|
|
if fn_name in ['println', 'print', 'eprintln', 'eprint', 'panic'] && call_expr.args.len > 0 {
|
2021-02-28 20:24:29 +01:00
|
|
|
c.inside_println_arg = true
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_type = ast.string_type
|
2020-04-12 17:45:04 +02:00
|
|
|
call_expr.args[0].typ = c.expr(call_expr.args[0].expr)
|
2021-03-02 16:51:46 +01:00
|
|
|
if call_expr.args[0].typ.is_void() {
|
|
|
|
c.error('`$fn_name` can not print void expressions', call_expr.pos)
|
|
|
|
}
|
2021-03-12 16:08:39 +01:00
|
|
|
c.fail_if_unreadable(call_expr.args[0].expr, call_expr.args[0].typ, 'argument to print')
|
2021-02-28 20:24:29 +01:00
|
|
|
c.inside_println_arg = false
|
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()
|
2020-12-06 04:55:08 +01:00
|
|
|
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-05-08 11:48:01 +02:00
|
|
|
*/
|
2021-04-11 23:57:00 +02:00
|
|
|
return func.return_type
|
2020-03-26 11:32:29 +01:00
|
|
|
}
|
2021-02-28 20:24:29 +01:00
|
|
|
// `return error(err)` -> `return err`
|
|
|
|
if fn_name == 'error' {
|
|
|
|
arg := call_expr.args[0]
|
|
|
|
call_expr.args[0].typ = c.expr(arg.expr)
|
2021-04-02 00:57:09 +02:00
|
|
|
if call_expr.args[0].typ == ast.error_type {
|
2021-02-28 20:24:29 +01:00
|
|
|
c.warn('`error($arg)` can be shortened to just `$arg`', call_expr.pos)
|
|
|
|
}
|
|
|
|
}
|
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 {
|
2021-04-11 23:57:00 +02:00
|
|
|
for param in func.params {
|
2020-09-27 03:32:56 +02:00
|
|
|
call_expr.expected_arg_types << param.typ
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for i, call_arg in call_expr.args {
|
2021-04-11 23:57:00 +02:00
|
|
|
param := if func.is_variadic && i >= func.params.len - 1 {
|
|
|
|
func.params[func.params.len - 1]
|
2020-12-27 14:20:30 +01:00
|
|
|
} else {
|
2021-04-11 23:57:00 +02:00
|
|
|
func.params[i]
|
2020-12-27 14:20:30 +01:00
|
|
|
}
|
2021-04-11 23:57:00 +02:00
|
|
|
if func.is_variadic && call_arg.expr is ast.ArrayDecompose {
|
|
|
|
if i > func.params.len - 1 {
|
|
|
|
c.error('too many arguments in call to `$func.name`', call_expr.pos)
|
2021-02-22 14:26:54 +01:00
|
|
|
}
|
|
|
|
}
|
2021-03-26 09:42:40 +01:00
|
|
|
c.expected_type = param.typ
|
2021-03-20 17:27:16 +01:00
|
|
|
typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr))
|
2020-04-12 17:45:04 +02:00
|
|
|
call_expr.args[i].typ = typ
|
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2021-03-26 09:42:40 +01:00
|
|
|
arg_typ_sym := c.table.get_type_symbol(param.typ)
|
2021-04-11 23:57:00 +02:00
|
|
|
if func.is_variadic && typ.has_flag(.variadic) && call_expr.args.len - 1 > i {
|
2021-02-24 19:35:32 +01:00
|
|
|
c.error('when forwarding a variadic variable, it must be the final argument',
|
|
|
|
call_arg.pos)
|
2020-04-22 05:28:47 +02:00
|
|
|
}
|
2021-03-26 09:42:40 +01:00
|
|
|
arg_share := param.typ.share()
|
2021-02-03 00:20:19 +01:00
|
|
|
if arg_share == .shared_t && (c.locked_names.len > 0 || c.rlocked_names.len > 0) {
|
|
|
|
c.error('function with `shared` arguments cannot be called inside `lock`/`rlock` block',
|
2021-02-24 19:35:32 +01:00
|
|
|
call_arg.pos)
|
2021-02-03 00:20:19 +01:00
|
|
|
}
|
2021-04-11 23:57:00 +02:00
|
|
|
if call_arg.is_mut && func.language == .v {
|
2021-02-03 00:20:19 +01:00
|
|
|
to_lock, pos := c.fail_if_immutable(call_arg.expr)
|
2021-03-26 09:42:40 +01:00
|
|
|
if !param.is_mut {
|
2020-09-08 19:34:17 +02:00
|
|
|
tok := call_arg.share.str()
|
2021-03-26 09:42:40 +01:00
|
|
|
c.error('`$call_expr.name` parameter `$param.name` is not `$tok`, `$tok` is not needed`',
|
2020-09-08 19:34:17 +02:00
|
|
|
call_arg.expr.position())
|
2021-02-03 00:20:19 +01:00
|
|
|
} else {
|
2021-03-26 09:42:40 +01:00
|
|
|
if param.typ.share() != call_arg.share {
|
2021-02-03 00:20:19 +01:00
|
|
|
c.error('wrong shared type', call_arg.expr.position())
|
|
|
|
}
|
2021-03-26 09:42:40 +01:00
|
|
|
if to_lock != '' && !param.typ.has_flag(.shared_f) {
|
2021-02-03 00:20:19 +01:00
|
|
|
c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`',
|
|
|
|
pos)
|
|
|
|
}
|
2020-06-30 14:19:22 +02:00
|
|
|
}
|
|
|
|
} else {
|
2021-03-26 09:42:40 +01:00
|
|
|
if param.is_mut {
|
2020-09-08 19:34:17 +02:00
|
|
|
tok := call_arg.share.str()
|
2021-03-26 09:42:40 +01:00
|
|
|
c.error('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${
|
2021-01-23 09:33:22 +01:00
|
|
|
i + 1}`', call_arg.expr.position())
|
2021-02-03 00:20:19 +01:00
|
|
|
} else {
|
2021-03-12 16:08:39 +01:00
|
|
|
c.fail_if_unreadable(call_arg.expr, typ, 'argument')
|
2020-06-30 14:19:22 +02:00
|
|
|
}
|
2020-05-29 04:30:00 +02:00
|
|
|
}
|
2020-05-06 11:29:37 +02:00
|
|
|
// Handle expected interface
|
|
|
|
if arg_typ_sym.kind == .interface_ {
|
2021-03-26 09:42:40 +01:00
|
|
|
c.type_implements(typ, param.typ, call_arg.expr.position())
|
2020-05-06 11:29:37 +02:00
|
|
|
continue
|
|
|
|
}
|
2021-04-27 14:25:42 +02:00
|
|
|
c.check_expected_call_arg(typ, c.unwrap_generic(param.typ), call_expr.language) or {
|
2020-04-12 17:45:04 +02:00
|
|
|
// str method, allow type with str method if fn arg is string
|
2020-09-13 15:19:53 +02:00
|
|
|
// Passing an int or a string array produces a c error here
|
|
|
|
// Deleting this condition results in propper V error messages
|
|
|
|
// if arg_typ_sym.kind == .string && typ_sym.has_method('str') {
|
2020-09-13 16:21:40 +02:00
|
|
|
// continue
|
2020-09-13 15:19:53 +02:00
|
|
|
// }
|
2020-04-12 17:45:04 +02:00
|
|
|
if typ_sym.kind == .void && arg_typ_sym.kind == .string {
|
|
|
|
continue
|
|
|
|
}
|
2021-04-11 23:57:00 +02:00
|
|
|
if func.generic_names.len > 0 {
|
2021-04-13 02:06:24 +02:00
|
|
|
continue
|
2020-04-14 18:09:59 +02:00
|
|
|
}
|
2021-03-13 18:13:50 +01:00
|
|
|
c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos)
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
2021-03-26 09:42:40 +01:00
|
|
|
// Warn about automatic (de)referencing, which will be removed soon.
|
2021-04-11 23:57:00 +02:00
|
|
|
if func.language != .c && !c.inside_unsafe && typ.nr_muls() != param.typ.nr_muls()
|
2021-03-26 10:21:22 +01:00
|
|
|
&& !(call_arg.is_mut && param.is_mut) && !(!call_arg.is_mut && !param.is_mut)
|
2021-04-02 00:57:09 +02:00
|
|
|
&& param.typ !in [ast.byteptr_type, ast.charptr_type, ast.voidptr_type] {
|
2021-03-26 09:42:40 +01:00
|
|
|
// sym := c.table.get_type_symbol(typ)
|
|
|
|
c.warn('automatic referencing/dereferencing is deprecated and will be removed soon (got: $typ.nr_muls() references, expected: $param.typ.nr_muls() references)',
|
|
|
|
call_arg.pos)
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
if func.generic_names.len != call_expr.concrete_types.len {
|
2020-10-06 15:34:02 +02:00
|
|
|
// no type arguments given in call, attempt implicit instantiation
|
2021-04-15 10:00:23 +02:00
|
|
|
c.infer_fn_generic_types(func, mut call_expr)
|
2021-04-27 14:25:42 +02:00
|
|
|
concrete_types = call_expr.concrete_types
|
2020-10-06 15:34:02 +02:00
|
|
|
}
|
2021-04-13 02:06:24 +02:00
|
|
|
if func.generic_names.len > 0 {
|
|
|
|
for i, call_arg in call_expr.args {
|
|
|
|
param := if func.is_variadic && i >= func.params.len - 1 {
|
|
|
|
func.params[func.params.len - 1]
|
|
|
|
} else {
|
|
|
|
func.params[i]
|
|
|
|
}
|
|
|
|
c.expected_type = param.typ
|
|
|
|
typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr))
|
|
|
|
|
2021-04-24 08:44:15 +02:00
|
|
|
if param.typ.has_flag(.generic)
|
|
|
|
&& func.generic_names.len == call_expr.concrete_types.len {
|
2021-04-21 11:22:42 +02:00
|
|
|
if unwrap_typ := c.table.resolve_generic_to_concrete(param.typ, func.generic_names,
|
2021-04-27 14:25:42 +02:00
|
|
|
concrete_types, false)
|
2021-04-13 02:06:24 +02:00
|
|
|
{
|
2021-04-27 14:25:42 +02:00
|
|
|
c.check_expected_call_arg(c.unwrap_generic(typ), unwrap_typ, call_expr.language) or {
|
2021-04-13 02:06:24 +02:00
|
|
|
c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-26 18:54:22 +02:00
|
|
|
// resolve return generics struct to concrete type
|
|
|
|
if func.generic_names.len > 0 && func.return_type.has_flag(.generic) {
|
2021-04-27 14:25:42 +02:00
|
|
|
c.check_return_generics_struct(func.return_type, mut call_expr, concrete_types)
|
2021-04-26 18:54:22 +02:00
|
|
|
} else {
|
|
|
|
call_expr.return_type = func.return_type
|
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
if call_expr.concrete_types.len > 0 && func.return_type != 0 {
|
2021-04-21 11:22:42 +02:00
|
|
|
if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
|
2021-04-27 14:25:42 +02:00
|
|
|
concrete_types, false)
|
2021-04-11 23:57:00 +02:00
|
|
|
{
|
2020-11-07 17:27:10 +01:00
|
|
|
call_expr.return_type = typ
|
|
|
|
return typ
|
2020-05-21 18:15:04 +02:00
|
|
|
}
|
|
|
|
}
|
2021-04-24 08:44:15 +02:00
|
|
|
if call_expr.concrete_types.len > 0 && func.generic_names.len == 0 {
|
|
|
|
c.error('a non generic function called like a generic one', call_expr.concrete_list_pos)
|
2020-11-15 10:13:35 +01:00
|
|
|
}
|
2021-04-02 16:27:54 +02:00
|
|
|
|
2021-04-24 08:44:15 +02:00
|
|
|
if call_expr.concrete_types.len > func.generic_names.len {
|
|
|
|
c.error('too many generic parameters got $call_expr.concrete_types.len, expected $func.generic_names.len',
|
|
|
|
call_expr.concrete_list_pos)
|
2021-04-02 16:27:54 +02:00
|
|
|
}
|
2021-04-11 23:57:00 +02:00
|
|
|
if func.generic_names.len > 0 {
|
2020-06-29 20:09:09 +02:00
|
|
|
return call_expr.return_type
|
|
|
|
}
|
2021-04-11 23:57:00 +02:00
|
|
|
return func.return_type
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn ast.Fn, call_expr ast.CallExpr) {
|
2021-03-22 18:43:06 +01:00
|
|
|
start_message := '$kind `$name`'
|
|
|
|
mut deprecation_message := ''
|
|
|
|
now := time.now()
|
|
|
|
mut after_time := now
|
|
|
|
for attr in the_fn.attrs {
|
|
|
|
if attr.name == 'deprecated' && attr.arg != '' {
|
|
|
|
deprecation_message = attr.arg
|
|
|
|
}
|
|
|
|
if attr.name == 'deprecated_after' && attr.arg != '' {
|
|
|
|
after_time = time.parse_iso8601(attr.arg) or {
|
|
|
|
c.error('invalid time format', attr.pos)
|
|
|
|
time.now()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if after_time < now {
|
|
|
|
c.warn(semicolonize('$start_message has been deprecated since $after_time.ymmdd()',
|
|
|
|
deprecation_message), call_expr.pos)
|
|
|
|
} else if after_time == now {
|
|
|
|
c.warn(semicolonize('$start_message has been deprecated', deprecation_message),
|
|
|
|
call_expr.pos)
|
|
|
|
} else {
|
|
|
|
c.note(semicolonize('$start_message will be deprecated after $after_time.ymmdd()',
|
|
|
|
deprecation_message), call_expr.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn semicolonize(main string, details string) string {
|
|
|
|
if details == '' {
|
|
|
|
return main
|
|
|
|
}
|
|
|
|
return '$main; $details'
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) type_implements(typ ast.Type, inter_typ ast.Type, pos token.Position) bool {
|
2020-12-09 17:51:37 +01:00
|
|
|
$if debug_interface_type_implements ? {
|
|
|
|
eprintln('> type_implements typ: $typ.debug() | inter_typ: $inter_typ.debug()')
|
|
|
|
}
|
2021-03-05 05:59:18 +01:00
|
|
|
utyp := c.unwrap_generic(typ)
|
|
|
|
typ_sym := c.table.get_type_symbol(utyp)
|
2020-11-24 13:58:29 +01:00
|
|
|
mut inter_sym := c.table.get_type_symbol(inter_typ)
|
2021-03-13 18:13:50 +01:00
|
|
|
// do not check the same type more than once
|
2021-04-02 00:57:09 +02:00
|
|
|
if mut inter_sym.info is ast.Interface {
|
2021-03-13 18:13:50 +01:00
|
|
|
for t in inter_sym.info.types {
|
|
|
|
if t.idx() == utyp.idx() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-05 05:59:18 +01:00
|
|
|
styp := c.table.type_to_str(utyp)
|
2021-03-13 18:13:50 +01:00
|
|
|
if utyp.idx() == inter_typ.idx() {
|
|
|
|
// same type -> already casted to the interface
|
|
|
|
return true
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if inter_typ.idx() == ast.error_type_idx && utyp.idx() == ast.none_type_idx {
|
2021-03-13 18:13:50 +01:00
|
|
|
// `none` "implements" the Error interface
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ {
|
2021-01-29 00:41:59 +01:00
|
|
|
c.error('cannot implement interface `$inter_sym.name` with a different interface `$styp`',
|
|
|
|
pos)
|
|
|
|
}
|
2021-01-26 11:56:17 +01:00
|
|
|
imethods := if inter_sym.kind == .interface_ {
|
2021-04-02 00:57:09 +02:00
|
|
|
(inter_sym.info as ast.Interface).methods
|
2021-01-26 11:56:17 +01:00
|
|
|
} else {
|
|
|
|
inter_sym.methods
|
|
|
|
}
|
|
|
|
for imethod in imethods {
|
2020-05-06 11:29:37 +02:00
|
|
|
if method := typ_sym.find_method(imethod.name) {
|
2021-01-13 23:44:29 +01:00
|
|
|
msg := c.table.is_same_method(imethod, method)
|
|
|
|
if msg.len > 0 {
|
2021-01-14 19:50:06 +01:00
|
|
|
sig := c.table.fn_signature(imethod, skip_receiver: true)
|
|
|
|
c.add_error_detail('$inter_sym.name has `$sig`')
|
2021-01-13 23:44:29 +01:00
|
|
|
c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.name`: $msg',
|
2020-05-06 12:26:00 +02:00
|
|
|
pos)
|
2020-05-31 10:22:18 +02:00
|
|
|
return false
|
2020-05-06 11:29:37 +02:00
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2020-12-27 18:12:39 +01:00
|
|
|
c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`",
|
|
|
|
pos)
|
2020-05-06 11:29:37 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if mut inter_sym.info is ast.Interface {
|
2021-01-23 07:57:17 +01:00
|
|
|
for ifield in inter_sym.info.fields {
|
2021-03-01 21:47:00 +01:00
|
|
|
if field := c.table.find_field_with_embeds(typ_sym, ifield.name) {
|
2021-01-23 07:57:17 +01:00
|
|
|
if ifield.typ != field.typ {
|
|
|
|
exp := c.table.type_to_str(ifield.typ)
|
|
|
|
got := c.table.type_to_str(field.typ)
|
|
|
|
c.error('`$styp` incorrectly implements field `$ifield.name` of interface `$inter_sym.name`, expected `$exp`, got `$got`',
|
|
|
|
pos)
|
|
|
|
return false
|
2021-01-24 22:11:17 +01:00
|
|
|
} else if ifield.is_mut && !(field.is_mut || field.is_global) {
|
|
|
|
c.error('`$styp` incorrectly implements interface `$inter_sym.name`, field `$ifield.name` must be mutable',
|
|
|
|
pos)
|
|
|
|
return false
|
2021-01-23 07:57:17 +01:00
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
c.error("`$styp` doesn't implement field `$ifield.name` of interface `$inter_sym.name`",
|
|
|
|
pos)
|
|
|
|
}
|
2021-04-09 10:00:05 +02:00
|
|
|
inter_sym.info.types << utyp
|
2020-05-06 11:29:37 +02:00
|
|
|
}
|
2020-05-31 10:22:18 +02:00
|
|
|
return true
|
2020-05-06 11:29:37 +02:00
|
|
|
}
|
|
|
|
|
2020-05-25 11:32:14 +02:00
|
|
|
// return the actual type of the expression, once the optional is handled
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Type {
|
2020-05-21 22:35:43 +02:00
|
|
|
if expr is ast.CallExpr {
|
2020-07-09 17:14:14 +02:00
|
|
|
if expr.return_type.has_flag(.optional) {
|
|
|
|
if expr.or_block.kind == .absent {
|
2021-01-26 15:43:10 +01:00
|
|
|
c.error('${expr.name}() returns an option, so it should have either an `or {}` block, or `?` at the end',
|
|
|
|
expr.pos)
|
2020-05-25 11:32:14 +02:00
|
|
|
} else {
|
2020-11-02 04:23:58 +01:00
|
|
|
c.check_or_expr(expr.or_block, ret_type, expr.return_type.clear_flag(.optional))
|
2020-05-25 11:32:14 +02:00
|
|
|
}
|
2021-03-06 19:42:34 +01:00
|
|
|
return ret_type.clear_flag(.optional)
|
2020-07-09 17:14:14 +02:00
|
|
|
} else if expr.or_block.kind == .block {
|
|
|
|
c.error('unexpected `or` block, the function `$expr.name` does not return an optional',
|
2020-11-04 15:02:01 +01:00
|
|
|
expr.or_block.pos)
|
2020-07-09 17:14:14 +02:00
|
|
|
} else if expr.or_block.kind == .propagate {
|
2020-11-11 22:11:27 +01:00
|
|
|
c.error('unexpected `?`, the function `$expr.name` does not return an optional',
|
2020-11-04 15:02:01 +01:00
|
|
|
expr.or_block.pos)
|
2020-04-08 01:19:24 +02:00
|
|
|
}
|
2021-01-19 08:28:34 +01:00
|
|
|
} else if expr is ast.IndexExpr {
|
|
|
|
if expr.or_expr.kind != .absent {
|
|
|
|
c.check_or_expr(expr.or_expr, ret_type, ret_type)
|
|
|
|
}
|
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
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type ast.Type, expr_return_type ast.Type) {
|
2020-05-25 11:32:14 +02:00
|
|
|
if or_expr.kind == .propagate {
|
2021-04-01 11:49:13 +02:00
|
|
|
if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main'
|
|
|
|
&& !c.inside_const {
|
2020-11-11 22:11:27 +01:00
|
|
|
c.error('to propagate the optional call, `$c.cur_fn.name` must 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 {
|
2021-04-02 00:57:09 +02:00
|
|
|
if ret_type != ast.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
|
|
|
}
|
|
|
|
// allow `f() or {}`
|
|
|
|
return
|
|
|
|
}
|
2020-09-22 05:28:29 +02:00
|
|
|
last_stmt := or_expr.stmts[stmts_len - 1]
|
2021-04-02 00:57:09 +02:00
|
|
|
if ret_type != ast.void_type {
|
2020-11-25 12:09:40 +01:00
|
|
|
match last_stmt {
|
2020-04-08 01:19:24 +02:00
|
|
|
ast.ExprStmt {
|
2020-12-05 15:26:28 +01:00
|
|
|
c.expected_type = ret_type
|
|
|
|
c.expected_or_type = ret_type.clear_flag(.optional)
|
2020-11-02 04:23:58 +01:00
|
|
|
last_stmt_typ := c.expr(last_stmt.expr)
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_or_type = ast.void_type
|
2021-01-23 09:33:22 +01:00
|
|
|
type_fits := c.check_types(last_stmt_typ, ret_type)
|
|
|
|
&& last_stmt_typ.nr_muls() == ret_type.nr_muls()
|
2020-06-18 20:38:59 +02:00
|
|
|
is_panic_or_exit := is_expr_panic_or_exit(last_stmt.expr)
|
2020-04-08 01:19:24 +02:00
|
|
|
if type_fits || is_panic_or_exit {
|
|
|
|
return
|
2020-04-07 16:36:00 +02:00
|
|
|
}
|
2020-09-18 01:09:00 +02:00
|
|
|
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.optional))
|
2021-04-02 00:57:09 +02:00
|
|
|
if last_stmt.typ == ast.void_type {
|
2020-11-02 03:52:35 +01:00
|
|
|
c.error('`or` block must provide a default value of type `$expected_type_name`, or return/exit/continue/break/panic',
|
|
|
|
last_stmt.pos)
|
|
|
|
} else {
|
2020-11-02 04:23:58 +01:00
|
|
|
type_name := c.table.type_to_str(last_stmt_typ)
|
2020-11-02 03:52:35 +01:00
|
|
|
c.error('wrong return type `$type_name` in the `or {}` block, expected `$expected_type_name`',
|
|
|
|
last_stmt.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-11-04 12:34:12 +01:00
|
|
|
if last_stmt.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',
|
2020-11-04 12:34:12 +01:00
|
|
|
last_stmt.pos)
|
2020-04-08 01:19:24 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2020-09-18 01:09:00 +02:00
|
|
|
ast.Return {}
|
|
|
|
else {
|
|
|
|
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.optional))
|
|
|
|
c.error('last statement in the `or {}` block should be an expression of type `$expected_type_name` or exit parent scope',
|
|
|
|
or_expr.pos)
|
|
|
|
return
|
|
|
|
}
|
2020-04-07 16:36:00 +02:00
|
|
|
}
|
2020-11-02 04:23:58 +01:00
|
|
|
} else {
|
2020-11-25 12:09:40 +01:00
|
|
|
match last_stmt {
|
2020-11-02 04:23:58 +01:00
|
|
|
ast.ExprStmt {
|
2021-04-02 00:57:09 +02:00
|
|
|
if last_stmt.typ == ast.void_type {
|
2020-11-02 04:23:58 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if is_expr_panic_or_exit(last_stmt.expr) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if c.check_types(last_stmt.typ, expr_return_type) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// opt_returning_string() or { ... 123 }
|
|
|
|
type_name := c.table.type_to_str(last_stmt.typ)
|
|
|
|
expr_return_type_name := c.table.type_to_str(expr_return_type)
|
|
|
|
c.error('the default expression type in the `or` block should be `$expr_return_type_name`, instead you gave a value of type `$type_name`',
|
|
|
|
last_stmt.expr.position())
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
2020-04-07 16:36:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_expr_panic_or_exit(expr ast.Expr) bool {
|
2020-11-25 12:09:40 +01:00
|
|
|
match expr {
|
2021-04-20 10:49:06 +02:00
|
|
|
ast.CallExpr { return !expr.is_method && expr.name in ['panic', 'exit'] }
|
2020-04-25 17:49:16 +02:00
|
|
|
else { return false }
|
2020-04-07 16:36:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) ast.Type {
|
2020-11-11 09:18:15 +01:00
|
|
|
prevent_sum_type_unwrapping_once := c.prevent_sum_type_unwrapping_once
|
|
|
|
c.prevent_sum_type_unwrapping_once = false
|
2021-02-28 20:24:29 +01:00
|
|
|
|
|
|
|
using_new_err_struct_save := c.using_new_err_struct
|
|
|
|
// TODO remove; this avoids a breaking change in syntax
|
|
|
|
if '$selector_expr.expr' == 'err' {
|
|
|
|
c.using_new_err_struct = true
|
|
|
|
}
|
|
|
|
|
2020-11-09 14:35:26 +01:00
|
|
|
// T.name, typeof(expr).name
|
|
|
|
mut name_type := 0
|
2020-11-25 12:09:40 +01:00
|
|
|
match mut selector_expr.expr {
|
2020-11-09 14:35:26 +01:00
|
|
|
ast.Ident {
|
2021-01-22 13:49:56 +01:00
|
|
|
name := selector_expr.expr.name
|
2021-04-11 08:02:57 +02:00
|
|
|
valid_generic := util.is_generic_type_name(name) && name in c.cur_fn.generic_names
|
2021-01-22 13:49:56 +01:00
|
|
|
if valid_generic {
|
2021-04-02 00:57:09 +02:00
|
|
|
name_type = ast.Type(c.table.find_type_idx(name)).set_flag(.generic)
|
2020-11-09 14:35:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Note: in future typeof() should be a type known at compile-time
|
|
|
|
// sum types should not be handled dynamically
|
|
|
|
ast.TypeOf {
|
2020-11-21 00:05:57 +01:00
|
|
|
name_type = c.expr(selector_expr.expr.expr)
|
2020-11-09 14:35:26 +01:00
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
if name_type > 0 {
|
|
|
|
if selector_expr.field_name != 'name' {
|
|
|
|
c.error('invalid field `.$selector_expr.field_name` for type `$selector_expr.expr`',
|
|
|
|
selector_expr.pos)
|
|
|
|
}
|
|
|
|
selector_expr.name_type = name_type
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2020-11-09 14:35:26 +01:00
|
|
|
}
|
2021-02-28 20:24:29 +01:00
|
|
|
//
|
|
|
|
old_selector_expr := c.inside_selector_expr
|
|
|
|
c.inside_selector_expr = true
|
2020-02-06 13:57:35 +01:00
|
|
|
typ := c.expr(selector_expr.expr)
|
2021-02-28 20:24:29 +01:00
|
|
|
c.inside_selector_expr = old_selector_expr
|
|
|
|
//
|
|
|
|
c.using_new_err_struct = using_new_err_struct_save
|
2021-04-02 00:57:09 +02:00
|
|
|
if typ == ast.void_type_idx {
|
2021-04-23 12:33:48 +02:00
|
|
|
// This means that the variable's value was assigned to an
|
|
|
|
// unknown function or method, so the error was already handled
|
|
|
|
// earlier
|
|
|
|
// c.error('unknown selector expression', selector_expr.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-03-11 05:56:15 +01:00
|
|
|
}
|
2020-03-07 04:45:35 +01:00
|
|
|
selector_expr.expr_type = typ
|
2021-01-27 13:53:20 +01:00
|
|
|
if selector_expr.expr_type.has_flag(.optional) && !((selector_expr.expr is ast.Ident
|
|
|
|
&& (selector_expr.expr as ast.Ident).kind == .constant)) {
|
|
|
|
c.error('cannot access fields of an optional, handle the error with `or {...}` or propagate it with `?`',
|
|
|
|
selector_expr.pos)
|
|
|
|
}
|
2020-05-09 15:16:48 +02:00
|
|
|
field_name := selector_expr.field_name
|
2020-11-11 09:18:15 +01:00
|
|
|
utyp := c.unwrap_generic(typ)
|
|
|
|
sym := c.table.get_type_symbol(utyp)
|
2021-04-19 14:38:48 +02:00
|
|
|
if (typ.has_flag(.variadic) || sym.kind == .array_fixed) && field_name == 'len' {
|
|
|
|
selector_expr.typ = ast.int_type
|
|
|
|
return ast.int_type
|
|
|
|
}
|
|
|
|
if sym.kind == .chan {
|
|
|
|
if field_name == 'closed' {
|
2021-04-02 00:57:09 +02:00
|
|
|
selector_expr.typ = ast.bool_type
|
|
|
|
return ast.bool_type
|
2021-04-19 14:38:48 +02:00
|
|
|
} else if field_name in ['len', 'cap'] {
|
|
|
|
selector_expr.typ = ast.u32_type
|
|
|
|
return ast.u32_type
|
2021-01-22 08:37:29 +01:00
|
|
|
}
|
2020-02-29 09:04:47 +01:00
|
|
|
}
|
2020-12-06 04:55:08 +01:00
|
|
|
mut unknown_field_msg := 'type `$sym.name` has no field or method `$field_name`'
|
2020-12-23 19:12:49 +01:00
|
|
|
mut has_field := false
|
2021-04-02 00:57:09 +02:00
|
|
|
mut field := ast.StructField{}
|
|
|
|
if field_name.len > 0 && field_name[0].is_capital() && sym.info is ast.Struct
|
2021-01-25 17:47:14 +01:00
|
|
|
&& sym.language == .v {
|
2020-12-23 19:12:49 +01:00
|
|
|
// x.Foo.y => access the embedded struct
|
2021-04-02 00:57:09 +02:00
|
|
|
sym_info := sym.info as ast.Struct
|
2020-12-23 19:12:49 +01:00
|
|
|
for embed in sym_info.embeds {
|
|
|
|
embed_sym := c.table.get_type_symbol(embed)
|
|
|
|
if embed_sym.embed_name() == field_name {
|
|
|
|
selector_expr.typ = embed
|
|
|
|
return embed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2021-01-23 07:57:17 +01:00
|
|
|
if f := c.table.find_field(sym, field_name) {
|
2020-12-23 19:12:49 +01:00
|
|
|
has_field = true
|
|
|
|
field = f
|
|
|
|
} else {
|
2021-01-09 01:32:33 +01:00
|
|
|
// look for embedded field
|
2021-04-02 00:57:09 +02:00
|
|
|
if sym.info is ast.Struct {
|
|
|
|
mut found_fields := []ast.StructField{}
|
|
|
|
mut embed_of_found_fields := []ast.Type{}
|
2020-12-23 19:12:49 +01:00
|
|
|
for embed in sym.info.embeds {
|
|
|
|
embed_sym := c.table.get_type_symbol(embed)
|
2021-01-23 07:57:17 +01:00
|
|
|
if f := c.table.find_field(embed_sym, field_name) {
|
2020-12-23 19:12:49 +01:00
|
|
|
found_fields << f
|
|
|
|
embed_of_found_fields << embed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if found_fields.len == 1 {
|
|
|
|
field = found_fields[0]
|
|
|
|
has_field = true
|
|
|
|
selector_expr.from_embed_type = embed_of_found_fields[0]
|
|
|
|
} else if found_fields.len > 1 {
|
|
|
|
c.error('ambiguous field `$field_name`', selector_expr.pos)
|
|
|
|
}
|
|
|
|
}
|
2021-03-09 18:16:18 +01:00
|
|
|
if sym.kind in [.aggregate, .sum_type] {
|
2021-03-13 18:13:50 +01:00
|
|
|
unknown_field_msg = err.msg
|
2020-12-23 19:12:49 +01:00
|
|
|
}
|
|
|
|
}
|
2021-01-09 01:32:33 +01:00
|
|
|
if !c.inside_unsafe {
|
2021-04-02 00:57:09 +02:00
|
|
|
if sym.info is ast.Struct {
|
2021-01-09 01:32:33 +01:00
|
|
|
if sym.info.is_union && selector_expr.next_token !in token.assign_tokens {
|
|
|
|
c.warn('reading a union field (or its address) requires `unsafe`',
|
|
|
|
selector_expr.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-23 19:12:49 +01:00
|
|
|
}
|
|
|
|
if has_field {
|
2020-11-03 08:35:35 +01:00
|
|
|
if sym.mod != c.mod && !field.is_pub && sym.language != .c {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('field `${sym.name}.$field_name` is not public', selector_expr.pos)
|
2020-05-11 14:38:25 +02:00
|
|
|
}
|
2020-11-11 09:18:15 +01:00
|
|
|
field_sym := c.table.get_type_symbol(field.typ)
|
2021-04-09 10:00:05 +02:00
|
|
|
if field_sym.kind in [.sum_type, .interface_] {
|
2020-11-11 09:18:15 +01:00
|
|
|
if !prevent_sum_type_unwrapping_once {
|
2020-12-12 09:01:12 +01:00
|
|
|
if scope_field := selector_expr.scope.find_struct_field(utyp, field_name) {
|
2021-04-09 10:00:05 +02:00
|
|
|
return scope_field.smartcasts.last()
|
2020-11-11 09:18:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-25 22:23:19 +02:00
|
|
|
selector_expr.typ = field.typ
|
2020-03-11 10:48:45 +01:00
|
|
|
return field.typ
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2021-03-09 18:16:18 +01:00
|
|
|
if sym.kind !in [.struct_, .aggregate, .interface_, .sum_type] {
|
2020-06-19 11:15:15 +02:00
|
|
|
if sym.kind != .placeholder {
|
2021-03-12 13:18:52 +01:00
|
|
|
c.error('`$sym.name` has no property `$selector_expr.field_name`', selector_expr.pos)
|
2020-06-19 11:15:15 +02:00
|
|
|
}
|
2020-04-07 15:15:45 +02:00
|
|
|
} else {
|
2021-04-02 00:57:09 +02:00
|
|
|
if sym.info is ast.Struct {
|
2020-12-23 19:12:49 +01:00
|
|
|
suggestion := util.new_suggestion(field_name, sym.info.fields.map(it.name))
|
2020-10-01 14:36:47 +02:00
|
|
|
c.error(suggestion.say(unknown_field_msg), selector_expr.pos)
|
|
|
|
}
|
2020-10-01 01:07:36 +02:00
|
|
|
c.error(unknown_field_msg, selector_expr.pos)
|
2020-02-08 16:59:57 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.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
|
2020-11-26 18:40:31 +01:00
|
|
|
expected_type := c.unwrap_generic(c.expected_type)
|
|
|
|
expected_type_sym := c.table.get_type_symbol(expected_type)
|
2021-04-02 00:57:09 +02:00
|
|
|
if return_stmt.exprs.len > 0 && c.cur_fn.return_type == ast.void_type {
|
2021-02-12 00:47:11 +01:00
|
|
|
c.error('unexpected argument, current function does not return anything', return_stmt.exprs[0].position())
|
2020-01-22 21:34:38 +01:00
|
|
|
return
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if return_stmt.exprs.len == 0 && !(c.expected_type == ast.void_type
|
2021-01-23 09:33:22 +01:00
|
|
|
|| expected_type_sym.kind == .void) {
|
2021-02-12 00:47:11 +01:00
|
|
|
stype := c.table.type_to_str(expected_type)
|
|
|
|
arg := if expected_type_sym.kind == .multi_return { 'arguments' } else { 'argument' }
|
|
|
|
c.error('expected `$stype` $arg', 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-06-04 14:38:54 +02:00
|
|
|
exp_is_optional := expected_type.has_flag(.optional)
|
2020-02-06 13:57:35 +01:00
|
|
|
mut expected_types := [expected_type]
|
2021-04-02 00:57:09 +02:00
|
|
|
if expected_type_sym.info is ast.MultiReturn {
|
2021-01-22 13:49:56 +01:00
|
|
|
expected_types = expected_type_sym.info.types
|
2021-04-16 10:53:20 +02:00
|
|
|
if c.cur_fn.cur_generic_types.len > 0 {
|
2021-01-22 13:49:56 +01:00
|
|
|
expected_types = expected_types.map(c.unwrap_generic(it))
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
mut got_types := []ast.Type{}
|
2020-03-05 13:57:05 +01:00
|
|
|
for expr in return_stmt.exprs {
|
|
|
|
typ := c.expr(expr)
|
2020-05-27 14:47:38 +02:00
|
|
|
// Unpack multi return types
|
2020-05-28 05:50:57 +02:00
|
|
|
sym := c.table.get_type_symbol(typ)
|
2020-05-27 14:47:38 +02:00
|
|
|
if sym.kind == .multi_return {
|
|
|
|
for t in sym.mr_info().types {
|
|
|
|
got_types << t
|
2020-05-28 01:19:03 +02:00
|
|
|
}
|
2020-05-27 14:47:38 +02:00
|
|
|
} else {
|
|
|
|
got_types << typ
|
|
|
|
}
|
2020-03-05 13:57:05 +01:00
|
|
|
}
|
2020-03-16 07:42:45 +01:00
|
|
|
return_stmt.types = got_types
|
2021-03-14 01:54:46 +01:00
|
|
|
// allow `none` & `error` return types for function that returns optional
|
2021-04-27 07:18:48 +02:00
|
|
|
option_type_idx := c.table.type_idxs['Option']
|
|
|
|
got_types_0_idx := got_types[0].idx()
|
2021-02-22 17:44:15 +01:00
|
|
|
if exp_is_optional
|
2021-04-27 07:18:48 +02:00
|
|
|
&& got_types_0_idx in [ast.none_type_idx, ast.error_type_idx, option_type_idx] {
|
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 {
|
2021-02-12 00:47:11 +01:00
|
|
|
arg := if expected_types.len == 1 { 'argument' } else { 'arguments' }
|
|
|
|
c.error('expected $expected_types.len $arg, but got $got_types.len', return_stmt.pos)
|
2020-05-27 14:47:38 +02:00
|
|
|
return
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-05-21 21:51:49 +02:00
|
|
|
for i, exp_type in expected_types {
|
2020-06-06 04:24:27 +02:00
|
|
|
got_typ := c.unwrap_generic(got_types[i])
|
2021-02-07 23:10:39 +01:00
|
|
|
if got_typ.has_flag(.optional) && (!exp_type.has_flag(.optional)
|
|
|
|
|| c.table.type_to_str(got_typ) != c.table.type_to_str(exp_type)) {
|
2020-06-04 10:40:32 +02:00
|
|
|
pos := return_stmt.exprs[i].position()
|
2020-06-06 10:05:26 +02:00
|
|
|
c.error('cannot use `${c.table.type_to_str(got_typ)}` as type `${c.table.type_to_str(exp_type)}` in return argument',
|
|
|
|
pos)
|
2020-06-04 10:40:32 +02:00
|
|
|
}
|
2020-06-06 04:24:27 +02:00
|
|
|
if !c.check_types(got_typ, exp_type) {
|
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()
|
2021-03-14 18:11:21 +01:00
|
|
|
if return_stmt.exprs[i].is_auto_deref_var() {
|
|
|
|
continue
|
|
|
|
}
|
2020-05-30 19:54:16 +02:00
|
|
|
if exp_typ_sym.kind == .interface_ {
|
|
|
|
c.type_implements(got_typ, exp_type, return_stmt.pos)
|
|
|
|
continue
|
|
|
|
}
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('cannot use `$got_typ_sym.name` as type `$exp_typ_sym.name` in return argument',
|
2020-04-25 17:49:16 +02:00
|
|
|
pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
if (got_typ.is_ptr() || got_typ.is_pointer())
|
|
|
|
&& (!exp_type.is_ptr() && !exp_type.is_pointer()) {
|
2020-07-29 21:40:43 +02:00
|
|
|
pos := return_stmt.exprs[i].position()
|
2021-03-14 18:11:21 +01:00
|
|
|
if return_stmt.exprs[i].is_auto_deref_var() {
|
|
|
|
continue
|
|
|
|
}
|
2020-07-29 21:40:43 +02:00
|
|
|
c.error('fn `$c.cur_fn.name` expects you to return a non reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead',
|
|
|
|
pos)
|
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
if (exp_type.is_ptr() || exp_type.is_pointer())
|
2021-04-02 00:57:09 +02:00
|
|
|
&& (!got_typ.is_ptr() && !got_typ.is_pointer()) && got_typ != ast.int_literal_type {
|
2020-12-31 12:42:22 +01:00
|
|
|
pos := return_stmt.exprs[i].position()
|
2021-03-14 18:11:21 +01:00
|
|
|
if return_stmt.exprs[i].is_auto_deref_var() {
|
|
|
|
continue
|
|
|
|
}
|
2020-12-31 12:42:22 +01:00
|
|
|
c.error('fn `$c.cur_fn.name` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead',
|
|
|
|
pos)
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-12-28 10:09:43 +01:00
|
|
|
if exp_is_optional && return_stmt.exprs.len > 0 {
|
|
|
|
expr0 := return_stmt.exprs[0]
|
|
|
|
if expr0 is ast.CallExpr {
|
|
|
|
if expr0.or_block.kind == .propagate {
|
|
|
|
c.error('`?` is not needed, use `return ${expr0.name}()`', expr0.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-11-21 19:07:47 +01:00
|
|
|
pub fn (mut c Checker) const_decl(mut node ast.ConstDecl) {
|
|
|
|
mut field_names := []string{}
|
|
|
|
mut field_order := []int{}
|
2021-03-11 21:43:04 +01:00
|
|
|
if node.fields.len == 0 {
|
|
|
|
c.warn('const block must have at least 1 declaration', node.pos)
|
|
|
|
}
|
2020-11-21 19:07:47 +01:00
|
|
|
for i, field in node.fields {
|
|
|
|
// TODO Check const name once the syntax is decided
|
|
|
|
if field.name in c.const_names {
|
2021-03-03 08:23:11 +01:00
|
|
|
name_pos := token.Position{
|
|
|
|
...field.pos
|
|
|
|
len: util.no_cur_mod(field.name, c.mod).len
|
|
|
|
}
|
|
|
|
c.error('duplicate const `$field.name`', name_pos)
|
2020-11-21 19:07:47 +01:00
|
|
|
}
|
|
|
|
c.const_names << field.name
|
|
|
|
field_names << field.name
|
|
|
|
field_order << i
|
|
|
|
}
|
|
|
|
mut needs_order := false
|
|
|
|
mut done_fields := []int{}
|
|
|
|
for i, field in node.fields {
|
|
|
|
c.const_decl = field.name
|
|
|
|
c.const_deps << field.name
|
2021-04-01 11:49:13 +02:00
|
|
|
typ := c.check_expr_opt_call(field.expr, c.expr(field.expr))
|
2020-11-21 19:07:47 +01:00
|
|
|
node.fields[i].typ = c.table.mktyp(typ)
|
|
|
|
for cd in c.const_deps {
|
|
|
|
for j, f in node.fields {
|
|
|
|
if j != i && cd in field_names && cd == f.name && j !in done_fields {
|
|
|
|
needs_order = true
|
|
|
|
x := field_order[j]
|
|
|
|
field_order[j] = field_order[i]
|
|
|
|
field_order[i] = x
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done_fields << i
|
|
|
|
c.const_deps = []
|
|
|
|
}
|
|
|
|
if needs_order {
|
|
|
|
mut ordered_fields := []ast.ConstField{}
|
|
|
|
for order in field_order {
|
|
|
|
ordered_fields << node.fields[order]
|
|
|
|
}
|
|
|
|
node.fields = ordered_fields
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-12-29 19:27:57 +01:00
|
|
|
mut seen := []i64{}
|
2021-01-05 01:46:32 +01:00
|
|
|
if decl.fields.len == 0 {
|
|
|
|
c.error('enum cannot be empty', decl.pos)
|
|
|
|
}
|
2020-04-30 09:34:18 +02:00
|
|
|
for i, field in decl.fields {
|
2020-08-09 03:57:54 +02:00
|
|
|
if !c.pref.experimental && util.contains_capital(field.name) {
|
|
|
|
// TODO C2V uses hundreds of enums with capitals, remove -experimental check once it's handled
|
2020-05-16 16:12:23 +02:00
|
|
|
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 {
|
2020-11-25 12:09:40 +01:00
|
|
|
match field.expr {
|
2020-07-04 17:28:01 +02:00
|
|
|
ast.IntegerLiteral {
|
2020-11-21 00:05:57 +01:00
|
|
|
val := field.expr.val.i64()
|
2021-01-25 10:26:20 +01:00
|
|
|
if val < checker.int_min || val > checker.int_max {
|
2020-11-21 00:05:57 +01:00
|
|
|
c.error('enum value `$val` overflows int', field.expr.pos)
|
2020-12-29 19:27:57 +01:00
|
|
|
} else if !decl.is_multi_allowed && i64(val) in seen {
|
2020-11-21 00:05:57 +01:00
|
|
|
c.error('enum value `$val` already exists', field.expr.pos)
|
2020-07-04 17:28:01 +02:00
|
|
|
}
|
2020-12-29 19:27:57 +01:00
|
|
|
seen << i64(val)
|
2020-07-04 17:28:01 +02:00
|
|
|
}
|
2020-04-09 19:23:49 +02:00
|
|
|
ast.PrefixExpr {}
|
|
|
|
else {
|
2020-04-19 22:26:04 +02:00
|
|
|
if field.expr is ast.Ident {
|
2020-11-21 00:05:57 +01:00
|
|
|
if field.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-07-04 17:28:01 +02:00
|
|
|
} else {
|
|
|
|
if seen.len > 0 {
|
|
|
|
last := seen[seen.len - 1]
|
2021-01-25 10:26:20 +01:00
|
|
|
if last == checker.int_max {
|
2020-07-04 17:28:01 +02:00
|
|
|
c.error('enum value overflows', field.pos)
|
|
|
|
}
|
|
|
|
seen << last + 1
|
|
|
|
} else {
|
|
|
|
seen << 0
|
|
|
|
}
|
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) {
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_type = ast.none_type // TODO a hack to make `x := if ... work`
|
2020-08-10 19:54:38 +02:00
|
|
|
defer {
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_type = ast.void_type
|
2020-08-10 19:54:38 +02:00
|
|
|
}
|
2020-05-08 22:49:45 +02:00
|
|
|
right_first := assign_stmt.right[0]
|
|
|
|
mut right_len := assign_stmt.right.len
|
2021-04-02 00:57:09 +02:00
|
|
|
mut right_type0 := ast.void_type
|
2021-03-17 19:57:14 +01:00
|
|
|
for i, right in assign_stmt.right {
|
2021-02-09 16:09:10 +01:00
|
|
|
if right is ast.CallExpr || right is ast.IfExpr || right is ast.LockExpr
|
|
|
|
|| right is ast.MatchExpr {
|
2021-03-17 19:57:14 +01:00
|
|
|
right_type := c.expr(right)
|
|
|
|
if i == 0 {
|
|
|
|
right_type0 = right_type
|
|
|
|
assign_stmt.right_types = [
|
|
|
|
c.check_expr_opt_call(right, right_type0),
|
|
|
|
]
|
|
|
|
}
|
|
|
|
right_type_sym := c.table.get_type_symbol(right_type)
|
|
|
|
if right_type_sym.kind == .multi_return {
|
2021-01-27 22:10:51 +01:00
|
|
|
if assign_stmt.right.len > 1 {
|
2021-03-17 19:57:14 +01:00
|
|
|
c.error('cannot use multi-value $right_type_sym.name in single-value context',
|
2021-01-27 22:10:51 +01:00
|
|
|
right.position())
|
|
|
|
}
|
2021-03-17 19:57:14 +01:00
|
|
|
assign_stmt.right_types = right_type_sym.mr_info().types
|
2021-01-27 22:10:51 +01:00
|
|
|
right_len = assign_stmt.right_types.len
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if right_type == ast.void_type {
|
2021-01-27 22:10:51 +01:00
|
|
|
right_len = 0
|
|
|
|
}
|
2020-05-02 11:45:08 +02:00
|
|
|
}
|
2020-06-16 13:20:16 +02:00
|
|
|
}
|
|
|
|
if assign_stmt.left.len != right_len {
|
|
|
|
if right_first is ast.CallExpr {
|
2020-07-09 17:14:14 +02:00
|
|
|
c.error('assignment mismatch: $assign_stmt.left.len variable(s) but `${right_first.name}()` returns $right_len value(s)',
|
2020-06-16 13:20:16 +02:00
|
|
|
assign_stmt.pos)
|
|
|
|
} else {
|
|
|
|
c.error('assignment mismatch: $assign_stmt.left.len variable(s) $right_len value(s)',
|
|
|
|
assign_stmt.pos)
|
2020-05-02 11:45:08 +02:00
|
|
|
}
|
2020-05-08 22:49:45 +02:00
|
|
|
return
|
2020-05-02 11:45:08 +02:00
|
|
|
}
|
2020-07-25 18:32:31 +02:00
|
|
|
//
|
2020-06-16 13:20:16 +02:00
|
|
|
is_decl := assign_stmt.op == .decl_assign
|
|
|
|
for i, left in assign_stmt.left {
|
2020-07-29 17:33:00 +02:00
|
|
|
if left is ast.CallExpr {
|
|
|
|
c.error('cannot call function `${left.name}()` on the left side of an assignment',
|
|
|
|
left.pos)
|
|
|
|
}
|
2020-06-16 13:20:16 +02:00
|
|
|
is_blank_ident := left.is_blank_ident()
|
2021-04-02 00:57:09 +02:00
|
|
|
mut left_type := ast.void_type
|
2020-06-16 13:20:16 +02:00
|
|
|
if !is_decl && !is_blank_ident {
|
2020-11-11 09:18:15 +01:00
|
|
|
if left is ast.Ident || left is ast.SelectorExpr {
|
|
|
|
c.prevent_sum_type_unwrapping_once = true
|
|
|
|
}
|
2020-06-16 13:20:16 +02:00
|
|
|
left_type = c.expr(left)
|
|
|
|
c.expected_type = c.unwrap_generic(left_type)
|
2021-01-30 11:55:10 +01:00
|
|
|
// `map = {}`
|
|
|
|
sym := c.table.get_type_symbol(left_type)
|
|
|
|
if sym.kind == .map && assign_stmt.right[i] is ast.StructInit {
|
2021-02-08 15:57:42 +01:00
|
|
|
c.warn('assigning a struct literal to a map is deprecated - use `map{}` instead',
|
|
|
|
assign_stmt.right[i].position())
|
2021-01-30 11:55:10 +01:00
|
|
|
assign_stmt.right[i] = ast.MapInit{}
|
|
|
|
}
|
2020-06-16 13:20:16 +02:00
|
|
|
}
|
|
|
|
if assign_stmt.right_types.len < assign_stmt.left.len { // first type or multi return types added above
|
2021-02-13 00:47:37 +01:00
|
|
|
old_inside_ref_lit := c.inside_ref_lit
|
|
|
|
if left is ast.Ident {
|
|
|
|
if left.info is ast.IdentVar {
|
|
|
|
c.inside_ref_lit = c.inside_ref_lit || left.info.share == .shared_t
|
|
|
|
}
|
|
|
|
}
|
2021-04-28 06:47:00 +02:00
|
|
|
c.inside_decl_rhs = is_decl
|
2020-05-25 11:32:14 +02:00
|
|
|
right_type := c.expr(assign_stmt.right[i])
|
2021-04-28 06:47:00 +02:00
|
|
|
c.inside_decl_rhs = false
|
2021-02-13 00:47:37 +01:00
|
|
|
c.inside_ref_lit = old_inside_ref_lit
|
2020-07-07 14:18:51 +02:00
|
|
|
if assign_stmt.right_types.len == i {
|
2021-01-28 11:23:48 +01:00
|
|
|
assign_stmt.right_types << c.check_expr_opt_call(assign_stmt.right[i],
|
|
|
|
right_type)
|
2020-07-07 14:18:51 +02:00
|
|
|
}
|
2020-05-02 11:45:08 +02:00
|
|
|
}
|
2020-06-16 13:20:16 +02:00
|
|
|
right := if i < assign_stmt.right.len { assign_stmt.right[i] } else { assign_stmt.right[0] }
|
2020-11-11 09:18:15 +01:00
|
|
|
mut right_type := assign_stmt.right_types[i]
|
2020-06-08 00:47:04 +02:00
|
|
|
if is_decl {
|
2021-02-21 11:15:36 +01:00
|
|
|
if right.is_auto_deref_var() {
|
2021-02-16 13:24:19 +01:00
|
|
|
left_type = c.table.mktyp(right_type.deref())
|
|
|
|
} else {
|
|
|
|
left_type = c.table.mktyp(right_type)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if left_type == ast.int_type {
|
2021-01-24 22:09:51 +01:00
|
|
|
if right is ast.IntegerLiteral {
|
|
|
|
mut is_large := right.val.len > 13
|
|
|
|
if !is_large && right.val.len > 8 {
|
|
|
|
val := right.val.i64()
|
2021-01-25 10:26:20 +01:00
|
|
|
is_large = val > checker.int_max || val < checker.int_min
|
2020-09-20 16:29:01 +02:00
|
|
|
}
|
|
|
|
if is_large {
|
|
|
|
c.error('overflow in implicit type `int`, use explicit type casting instead',
|
2021-01-24 22:09:51 +01:00
|
|
|
right.pos)
|
2020-09-20 16:29:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-08 00:47:04 +02:00
|
|
|
} else {
|
2020-06-16 13:20:16 +02:00
|
|
|
// Make sure the variable is mutable
|
|
|
|
c.fail_if_immutable(left)
|
|
|
|
// left_type = c.expr(left)
|
|
|
|
}
|
|
|
|
assign_stmt.left_types << left_type
|
2020-11-25 12:09:40 +01:00
|
|
|
match mut left {
|
2020-06-16 13:20:16 +02:00
|
|
|
ast.Ident {
|
2020-06-18 20:38:59 +02:00
|
|
|
if left.kind == .blank_ident {
|
2020-06-16 13:20:16 +02:00
|
|
|
left_type = right_type
|
|
|
|
assign_stmt.left_types[i] = right_type
|
|
|
|
if assign_stmt.op !in [.assign, .decl_assign] {
|
2020-06-18 20:38:59 +02:00
|
|
|
c.error('cannot modify blank `_` identifier', left.pos)
|
2020-06-16 13:20:16 +02:00
|
|
|
}
|
2020-10-09 00:48:39 +02:00
|
|
|
} else if left.info !is ast.IdentVar {
|
2021-04-04 17:40:10 +02:00
|
|
|
c.error('cannot assign to $left.kind `$left.name`', left.pos)
|
2020-06-17 04:05:13 +02:00
|
|
|
} else {
|
|
|
|
if is_decl {
|
2020-06-18 20:38:59 +02:00
|
|
|
c.check_valid_snake_case(left.name, 'variable name', left.pos)
|
2020-06-17 04:05:13 +02:00
|
|
|
}
|
2020-10-15 17:30:36 +02:00
|
|
|
mut ident_var_info := left.info as ast.IdentVar
|
2020-07-07 01:57:31 +02:00
|
|
|
if ident_var_info.share == .shared_t {
|
2020-07-04 12:44:25 +02:00
|
|
|
left_type = left_type.set_flag(.shared_f)
|
2021-02-01 20:07:01 +01:00
|
|
|
if is_decl {
|
|
|
|
if left_type.nr_muls() > 1 {
|
|
|
|
c.error('shared cannot be multi level reference', left.pos)
|
|
|
|
}
|
|
|
|
left_type = left_type.set_nr_muls(1)
|
|
|
|
}
|
2021-02-03 15:16:52 +01:00
|
|
|
} else if left_type.has_flag(.shared_f) {
|
|
|
|
left_type = left_type.clear_flag(.shared_f)
|
2020-07-04 12:44:25 +02:00
|
|
|
}
|
2020-07-07 01:57:31 +02:00
|
|
|
if ident_var_info.share == .atomic_t {
|
|
|
|
left_type = left_type.set_flag(.atomic_f)
|
2020-07-04 12:44:25 +02:00
|
|
|
}
|
|
|
|
assign_stmt.left_types[i] = left_type
|
2020-06-16 13:20:16 +02:00
|
|
|
ident_var_info.typ = left_type
|
2020-06-18 20:38:59 +02:00
|
|
|
left.info = ident_var_info
|
2020-07-22 18:10:31 +02:00
|
|
|
if left_type != 0 {
|
2020-11-25 12:09:40 +01:00
|
|
|
match mut left.obj {
|
2020-11-24 13:58:29 +01:00
|
|
|
ast.Var { left.obj.typ = left_type }
|
|
|
|
ast.GlobalField { left.obj.typ = left_type }
|
2020-07-23 23:16:36 +02:00
|
|
|
else {}
|
|
|
|
}
|
|
|
|
/*
|
2020-07-22 18:10:31 +02:00
|
|
|
if left.obj is ast.Var as v {
|
|
|
|
v.typ = left_type
|
|
|
|
} else if left.obj is ast.GlobalDecl as v {
|
|
|
|
v.typ = left_type
|
|
|
|
}
|
2020-07-23 23:16:36 +02:00
|
|
|
*/
|
2020-07-22 18:10:31 +02:00
|
|
|
}
|
2021-01-28 18:34:55 +01:00
|
|
|
if is_decl {
|
2021-01-10 21:41:29 +01:00
|
|
|
full_name := '${left.mod}.$left.name'
|
2021-01-24 10:01:10 +01:00
|
|
|
if obj := c.file.global_scope.find(full_name) {
|
|
|
|
if obj is ast.ConstField {
|
2021-01-28 18:34:55 +01:00
|
|
|
c.warn('duplicate of a const name `$full_name`', left.pos)
|
2021-01-24 10:01:10 +01:00
|
|
|
}
|
2021-01-10 21:41:29 +01:00
|
|
|
}
|
|
|
|
}
|
2020-06-16 13:20:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.PrefixExpr {
|
|
|
|
// Do now allow `*x = y` outside `unsafe`
|
2020-06-18 20:38:59 +02:00
|
|
|
if left.op == .mul && !c.inside_unsafe {
|
2020-07-01 14:50:17 +02:00
|
|
|
c.error('modifying variables via dereferencing can only be done in `unsafe` blocks',
|
2020-06-17 04:05:13 +02:00
|
|
|
assign_stmt.pos)
|
2020-06-16 13:20:16 +02:00
|
|
|
}
|
2020-10-21 17:37:30 +02:00
|
|
|
if is_decl {
|
|
|
|
c.error('non-name on the left side of `:=`', left.pos)
|
|
|
|
}
|
2020-03-10 12:01:37 +01:00
|
|
|
}
|
2020-09-16 22:01:15 +02:00
|
|
|
else {
|
2021-02-16 14:08:01 +01:00
|
|
|
if mut left is ast.IndexExpr {
|
|
|
|
// eprintln('>>> left.is_setter: ${left.is_setter:10} | left.is_map: ${left.is_map:10} | left.is_array: ${left.is_array:10}')
|
|
|
|
if left.is_map && left.is_setter {
|
|
|
|
left.recursive_mapset_is_setter(true)
|
|
|
|
}
|
|
|
|
}
|
2020-09-16 22:01:15 +02:00
|
|
|
if is_decl {
|
|
|
|
c.error('non-name `$left` on left side of `:=`', left.position())
|
|
|
|
}
|
|
|
|
}
|
2020-06-16 13:20:16 +02:00
|
|
|
}
|
|
|
|
left_type_unwrapped := c.unwrap_generic(left_type)
|
|
|
|
right_type_unwrapped := c.unwrap_generic(right_type)
|
2020-11-14 09:42:56 +01:00
|
|
|
if right_type_unwrapped == 0 {
|
|
|
|
// right type was a generic `T`
|
|
|
|
continue
|
|
|
|
}
|
2020-06-16 13:20:16 +02:00
|
|
|
left_sym := c.table.get_type_symbol(left_type_unwrapped)
|
|
|
|
right_sym := c.table.get_type_symbol(right_type_unwrapped)
|
2020-08-10 19:54:38 +02:00
|
|
|
if c.pref.translated {
|
|
|
|
// TODO fix this in C2V instead, for example cast enums to int before using `|` on them.
|
2020-08-11 16:26:49 +02:00
|
|
|
// TODO replace all c.pref.translated checks with `$if !translated` for performance
|
2020-08-11 00:51:15 +02:00
|
|
|
continue
|
2020-08-10 19:54:38 +02:00
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
if left_sym.kind == .array && !c.inside_unsafe && assign_stmt.op in [.assign, .decl_assign]
|
|
|
|
&& right_sym.kind == .array && (left is ast.Ident && !left.is_blank_ident())
|
|
|
|
&& right is ast.Ident {
|
2020-12-20 10:42:46 +01:00
|
|
|
// Do not allow `a = b`, only `a = b.clone()`
|
2021-02-06 03:06:34 +01:00
|
|
|
c.error('use `array2 $assign_stmt.op.str() array1.clone()` instead of `array2 $assign_stmt.op.str() array1` (or use `unsafe`)',
|
|
|
|
assign_stmt.pos)
|
|
|
|
}
|
2021-02-17 05:19:25 +01:00
|
|
|
if left_sym.kind == .map && assign_stmt.op in [.assign, .decl_assign]
|
2021-03-11 15:19:36 +01:00
|
|
|
&& right_sym.kind == .map && ((right is ast.Ident && right.is_auto_deref_var())
|
|
|
|
|| !right_type.is_ptr()) && !left.is_blank_ident() && right.is_lvalue() {
|
2021-02-12 01:02:33 +01:00
|
|
|
// Do not allow `a = b`
|
2021-02-17 05:19:25 +01:00
|
|
|
c.error('cannot copy map: call `move` or `clone` method (or use a reference)',
|
2021-02-12 01:02:33 +01:00
|
|
|
right.position())
|
2020-12-20 10:42:46 +01:00
|
|
|
}
|
2020-12-12 10:42:07 +01:00
|
|
|
left_is_ptr := left_type.is_ptr() || left_sym.is_pointer()
|
2021-02-21 11:15:36 +01:00
|
|
|
if left_is_ptr && !left.is_auto_deref_var() {
|
2020-12-12 10:42:07 +01:00
|
|
|
if !c.inside_unsafe && assign_stmt.op !in [.assign, .decl_assign] {
|
|
|
|
// ptr op=
|
|
|
|
c.warn('pointer arithmetic is only allowed in `unsafe` blocks', assign_stmt.pos)
|
|
|
|
}
|
|
|
|
right_is_ptr := right_type.is_ptr() || right_sym.is_pointer()
|
|
|
|
if !right_is_ptr && assign_stmt.op == .assign && right_type_unwrapped.is_number() {
|
2021-04-04 17:40:10 +02:00
|
|
|
c.error('cannot assign to `$left`: ' +
|
2020-12-12 10:42:07 +01:00
|
|
|
c.expected_msg(right_type_unwrapped, left_type_unwrapped), right.position())
|
|
|
|
}
|
2021-02-01 14:39:36 +01:00
|
|
|
if (right is ast.StructInit || !right_is_ptr) && !(right_sym.is_number()
|
|
|
|
|| left_type.has_flag(.shared_f)) {
|
2020-12-12 10:42:07 +01:00
|
|
|
left_name := c.table.type_to_str(left_type_unwrapped)
|
|
|
|
mut rtype := right_type_unwrapped
|
|
|
|
if rtype.is_ptr() {
|
|
|
|
rtype = rtype.deref()
|
|
|
|
}
|
|
|
|
right_name := c.table.type_to_str(rtype)
|
|
|
|
c.error('mismatched types `$left_name` and `$right_name`', assign_stmt.pos)
|
2020-11-07 20:19:46 +01:00
|
|
|
}
|
|
|
|
}
|
2020-06-16 13:20:16 +02:00
|
|
|
// Single side check
|
|
|
|
match assign_stmt.op {
|
|
|
|
.assign {} // No need to do single side check for =. But here put it first for speed.
|
2020-12-07 18:37:17 +01:00
|
|
|
.plus_assign, .minus_assign {
|
2021-04-02 00:57:09 +02:00
|
|
|
if left_type == ast.string_type {
|
2020-12-07 18:37:17 +01:00
|
|
|
if assign_stmt.op != .plus_assign {
|
|
|
|
c.error('operator `$assign_stmt.op` not defined on left operand type `$left_sym.name`',
|
|
|
|
left.position())
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if right_type != ast.string_type {
|
2020-12-07 18:37:17 +01:00
|
|
|
c.error('invalid right operand: $left_sym.name $assign_stmt.op $right_sym.name',
|
|
|
|
right.position())
|
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
} else if !left_sym.is_number()
|
|
|
|
&& left_sym.kind !in [.byteptr, .charptr, .struct_, .alias] {
|
2020-12-07 18:37:17 +01:00
|
|
|
c.error('operator `$assign_stmt.op` not defined on left operand type `$left_sym.name`',
|
2020-06-17 04:05:13 +02:00
|
|
|
left.position())
|
2021-01-23 09:33:22 +01:00
|
|
|
} else if !right_sym.is_number()
|
|
|
|
&& left_sym.kind !in [.byteptr, .charptr, .struct_, .alias] {
|
2020-12-07 18:37:17 +01:00
|
|
|
c.error('invalid right operand: $left_sym.name $assign_stmt.op $right_sym.name',
|
2020-06-17 04:05:13 +02:00
|
|
|
right.position())
|
2020-06-16 13:20:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
.mult_assign, .div_assign {
|
2021-01-23 09:33:22 +01:00
|
|
|
if !left_sym.is_number()
|
|
|
|
&& !c.table.get_final_type_symbol(left_type_unwrapped).is_int()
|
|
|
|
&& left_sym.kind !in [.struct_, .alias] {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('operator $assign_stmt.op.str() not defined on left operand type `$left_sym.name`',
|
2020-06-16 13:20:16 +02:00
|
|
|
left.position())
|
2021-01-23 09:33:22 +01:00
|
|
|
} else if !right_sym.is_number()
|
|
|
|
&& !c.table.get_final_type_symbol(left_type_unwrapped).is_int()
|
|
|
|
&& left_sym.kind !in [.struct_, .alias] {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('operator $assign_stmt.op.str() not defined on right operand type `$right_sym.name`',
|
2020-06-16 13:20:16 +02:00
|
|
|
right.position())
|
|
|
|
}
|
|
|
|
}
|
2021-04-07 15:26:10 +02:00
|
|
|
.and_assign, .or_assign, .xor_assign, .mod_assign, .left_shift_assign,
|
|
|
|
.right_shift_assign {
|
2021-01-23 09:33:22 +01:00
|
|
|
if !left_sym.is_int()
|
|
|
|
&& !c.table.get_final_type_symbol(left_type_unwrapped).is_int() {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('operator $assign_stmt.op.str() not defined on left operand type `$left_sym.name`',
|
2020-06-16 13:20:16 +02:00
|
|
|
left.position())
|
2021-01-23 09:33:22 +01:00
|
|
|
} else if !right_sym.is_int()
|
|
|
|
&& !c.table.get_final_type_symbol(right_type_unwrapped).is_int() {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('operator $assign_stmt.op.str() not defined on right operand type `$right_sym.name`',
|
2020-06-16 13:20:16 +02:00
|
|
|
right.position())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
if assign_stmt.op in [.plus_assign, .minus_assign, .mod_assign, .mult_assign, .div_assign]
|
|
|
|
&& ((left_sym.kind == .struct_ && right_sym.kind == .struct_)
|
|
|
|
|| left_sym.kind == .alias) {
|
2021-01-15 02:27:19 +01:00
|
|
|
left_name := c.table.type_to_str(left_type)
|
|
|
|
right_name := c.table.type_to_str(right_type)
|
|
|
|
parent_sym := c.table.get_final_type_symbol(left_type)
|
|
|
|
if left_sym.kind == .alias && right_sym.kind != .alias {
|
|
|
|
c.error('mismatched types `$left_name` and `$right_name`', assign_stmt.pos)
|
|
|
|
}
|
2021-01-13 03:31:14 +01:00
|
|
|
extracted_op := match assign_stmt.op {
|
|
|
|
.plus_assign { '+' }
|
|
|
|
.minus_assign { '-' }
|
|
|
|
.div_assign { '/' }
|
|
|
|
.mod_assign { '%' }
|
|
|
|
.mult_assign { '*' }
|
|
|
|
else { 'unknown op' }
|
|
|
|
}
|
|
|
|
if method := left_sym.find_method(extracted_op) {
|
|
|
|
if method.return_type != left_type {
|
|
|
|
c.error('operator `$extracted_op` must return `$left_name` to be used as an assignment operator',
|
|
|
|
assign_stmt.pos)
|
|
|
|
}
|
|
|
|
} else {
|
2021-01-15 02:27:19 +01:00
|
|
|
if parent_sym.is_primitive() {
|
|
|
|
c.error('cannot use operator methods on type alias for `$parent_sym.name`',
|
|
|
|
assign_stmt.pos)
|
|
|
|
}
|
2021-01-13 03:31:14 +01:00
|
|
|
if left_name == right_name {
|
2021-01-17 03:37:44 +01:00
|
|
|
c.error('undefined operation `$left_name` $extracted_op `$right_name`',
|
2021-01-13 03:31:14 +01:00
|
|
|
assign_stmt.pos)
|
|
|
|
} else {
|
|
|
|
c.error('mismatched types `$left_name` and `$right_name`', assign_stmt.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-21 11:15:36 +01:00
|
|
|
if !is_blank_ident && !right.is_auto_deref_var() && right_sym.kind != .placeholder
|
2021-02-17 20:44:28 +01:00
|
|
|
&& left_sym.kind != .interface_ {
|
2020-11-11 09:18:15 +01:00
|
|
|
// Dual sides check (compatibility check)
|
2020-11-15 15:53:51 +01:00
|
|
|
c.check_expected(right_type_unwrapped, left_type_unwrapped) or {
|
2021-04-05 20:21:46 +02:00
|
|
|
// allow for ptr += 2
|
2021-04-05 21:01:18 +02:00
|
|
|
if left_type_unwrapped.is_ptr() && right_type_unwrapped.is_int()
|
|
|
|
&& assign_stmt.op in [.plus_assign, .minus_assign] {
|
|
|
|
if !c.inside_unsafe {
|
|
|
|
c.warn('pointer arithmetic is only allowed in `unsafe` blocks',
|
|
|
|
assign_stmt.pos)
|
|
|
|
}
|
|
|
|
} else {
|
2021-04-05 20:21:46 +02:00
|
|
|
c.error('cannot assign to `$left`: $err.msg', right.position())
|
|
|
|
}
|
2020-10-15 17:30:36 +02:00
|
|
|
}
|
2020-08-13 12:10:43 +02:00
|
|
|
}
|
2020-12-27 18:12:39 +01:00
|
|
|
if left_sym.kind == .interface_ {
|
|
|
|
c.type_implements(right_type, left_type, right.position())
|
|
|
|
}
|
2020-02-27 16:51:39 +01:00
|
|
|
}
|
2021-04-24 13:08:37 +02:00
|
|
|
// this needs to run after the assign stmt left exprs have been run through checker
|
|
|
|
// so that ident.obj is set
|
2020-12-12 09:01:12 +01:00
|
|
|
// Check `x := &y` and `mut x := <-ch`
|
|
|
|
if right_first is ast.PrefixExpr {
|
|
|
|
node := right_first
|
|
|
|
left_first := assign_stmt.left[0]
|
|
|
|
if left_first is ast.Ident {
|
|
|
|
assigned_var := left_first
|
2021-02-13 00:47:37 +01:00
|
|
|
mut is_shared := false
|
|
|
|
if left_first.info is ast.IdentVar {
|
|
|
|
is_shared = left_first.info.share == .shared_t
|
|
|
|
}
|
|
|
|
old_inside_ref_lit := c.inside_ref_lit
|
|
|
|
c.inside_ref_lit = (c.inside_ref_lit || node.op == .amp || is_shared)
|
2020-12-12 09:01:12 +01:00
|
|
|
c.expr(node.right)
|
2021-02-13 00:47:37 +01:00
|
|
|
c.inside_ref_lit = old_inside_ref_lit
|
2021-04-24 13:08:37 +02:00
|
|
|
if node.op == .amp {
|
|
|
|
if node.right is ast.Ident {
|
|
|
|
if node.right.obj is ast.Var {
|
|
|
|
v := node.right.obj
|
|
|
|
right_type0 = v.typ
|
2020-12-12 09:01:12 +01:00
|
|
|
if !v.is_mut && assigned_var.is_mut && !c.inside_unsafe {
|
|
|
|
c.error('`$node.right.name` is immutable, cannot have a mutable reference to it',
|
|
|
|
node.pos)
|
|
|
|
}
|
2021-04-24 13:08:37 +02:00
|
|
|
} else if node.right.obj is ast.ConstField {
|
|
|
|
if assigned_var.is_mut && !c.inside_unsafe {
|
|
|
|
c.error('`$node.right.name` is immutable, cannot have a mutable reference to it',
|
|
|
|
node.pos)
|
|
|
|
}
|
2020-12-12 09:01:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if node.op == .arrow {
|
|
|
|
if assigned_var.is_mut {
|
|
|
|
right_sym := c.table.get_type_symbol(right_type0)
|
|
|
|
if right_sym.kind == .chan {
|
|
|
|
chan_info := right_sym.chan_info()
|
|
|
|
if chan_info.elem_type.is_ptr() && !chan_info.is_mut {
|
|
|
|
c.error('cannot have a mutable reference to object from `$right_sym.name`',
|
|
|
|
node.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-20 10:42:46 +01:00
|
|
|
// right_sym := c.table.get_type_symbol(right_type_unwrapped)
|
2020-02-27 00:12:37 +01:00
|
|
|
}
|
2020-02-06 17:38:02 +01:00
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn scope_register_it(mut s ast.Scope, pos token.Position, typ ast.Type) {
|
2020-12-02 14:40:25 +01:00
|
|
|
s.register(ast.Var{
|
2020-11-26 11:28:54 +01:00
|
|
|
name: 'it'
|
|
|
|
pos: pos
|
|
|
|
typ: typ
|
|
|
|
is_used: true
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn scope_register_a_b(mut s ast.Scope, pos token.Position, typ ast.Type) {
|
2020-12-02 14:40:25 +01:00
|
|
|
s.register(ast.Var{
|
2020-11-26 11:28:54 +01:00
|
|
|
name: 'a'
|
|
|
|
pos: pos
|
2021-01-20 22:19:35 +01:00
|
|
|
typ: typ.to_ptr()
|
2020-11-26 11:28:54 +01:00
|
|
|
is_used: true
|
|
|
|
})
|
2020-12-02 14:40:25 +01:00
|
|
|
s.register(ast.Var{
|
2020-11-26 11:28:54 +01:00
|
|
|
name: 'b'
|
|
|
|
pos: pos
|
2021-01-20 22:19:35 +01:00
|
|
|
typ: typ.to_ptr()
|
2020-11-26 11:28:54 +01:00
|
|
|
is_used: true
|
|
|
|
})
|
2020-11-11 09:18:15 +01:00
|
|
|
}
|
|
|
|
|
2020-05-29 06:39:46 +02:00
|
|
|
fn (mut c Checker) check_array_init_para_type(para string, expr ast.Expr, pos token.Position) {
|
|
|
|
sym := c.table.get_type_symbol(c.expr(expr))
|
2021-01-11 22:58:15 +01:00
|
|
|
if sym.kind !in [.int, .int_literal] {
|
2020-05-29 06:39:46 +02:00
|
|
|
c.error('array $para needs to be an int', pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 10:07:36 +01:00
|
|
|
pub fn (mut c Checker) ensure_sumtype_array_has_default_value(array_init ast.ArrayInit) {
|
|
|
|
sym := c.table.get_type_symbol(array_init.elem_type)
|
|
|
|
if sym.kind == .sum_type && !array_init.has_default {
|
|
|
|
c.error('cannot initialize sum type array without default value', array_init.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) ast.Type {
|
2020-03-24 16:18:37 +01:00
|
|
|
// println('checker: array init $array_init.pos.line_nr $c.file.path')
|
2021-04-02 00:57:09 +02:00
|
|
|
mut elem_type := ast.void_type
|
2020-03-04 02:50:32 +01:00
|
|
|
// []string - was set in parser
|
2021-04-02 00:57:09 +02:00
|
|
|
if array_init.typ != ast.void_type {
|
2020-05-04 13:22:09 +02:00
|
|
|
if array_init.exprs.len == 0 {
|
|
|
|
if array_init.has_cap {
|
2020-05-29 06:39:46 +02:00
|
|
|
c.check_array_init_para_type('cap', array_init.cap_expr, array_init.pos)
|
2020-05-04 13:22:09 +02:00
|
|
|
}
|
|
|
|
if array_init.has_len {
|
2020-05-29 06:39:46 +02:00
|
|
|
c.check_array_init_para_type('len', array_init.len_expr, array_init.pos)
|
2020-05-04 13:22:09 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-02 03:12:33 +02:00
|
|
|
if array_init.has_default {
|
2021-04-01 11:49:13 +02:00
|
|
|
default_expr := array_init.default_expr
|
|
|
|
default_typ := c.check_expr_opt_call(default_expr, c.expr(default_expr))
|
2020-12-04 19:39:32 +01:00
|
|
|
c.check_expected(default_typ, array_init.elem_type) or {
|
2021-04-01 11:49:13 +02:00
|
|
|
c.error(err.msg, default_expr.position())
|
2020-12-04 19:39:32 +01:00
|
|
|
}
|
2020-10-02 03:12:33 +02:00
|
|
|
}
|
2021-03-10 10:07:36 +01:00
|
|
|
if array_init.has_len {
|
|
|
|
c.ensure_sumtype_array_has_default_value(array_init)
|
2020-11-21 00:07:25 +01:00
|
|
|
}
|
2021-04-20 11:11:41 +02:00
|
|
|
c.ensure_type_exists(array_init.elem_type, array_init.elem_type_pos) or {}
|
2020-03-04 02:50:32 +01:00
|
|
|
return array_init.typ
|
|
|
|
}
|
2021-03-10 10:07:36 +01:00
|
|
|
if array_init.is_fixed {
|
|
|
|
c.ensure_sumtype_array_has_default_value(array_init)
|
2021-04-20 11:11:41 +02:00
|
|
|
c.ensure_type_exists(array_init.elem_type, array_init.elem_type_pos) or {}
|
2021-03-10 10:07:36 +01:00
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
// a = []
|
2020-03-04 02:50:32 +01:00
|
|
|
if array_init.exprs.len == 0 {
|
2020-12-05 15:26:28 +01:00
|
|
|
// a := fn_returing_opt_array() or { [] }
|
2021-04-02 00:57:09 +02:00
|
|
|
if c.expected_type == ast.void_type && c.expected_or_type != ast.void_type {
|
2020-12-05 15:26:28 +01:00
|
|
|
c.expected_type = c.expected_or_type
|
|
|
|
}
|
|
|
|
mut type_sym := c.table.get_type_symbol(c.expected_type)
|
2020-04-04 13:32:16 +02:00
|
|
|
if type_sym.kind != .array {
|
2020-06-24 14:44:06 +02:00
|
|
|
c.error('array_init: no type specified (maybe: `[]Type{}` instead of `[]`)',
|
|
|
|
array_init.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-04-04 13:32:16 +02:00
|
|
|
}
|
|
|
|
// TODO: seperate errors once bug is fixed with `x := if expr { ... } else { ... }`
|
2021-04-02 00:57:09 +02:00
|
|
|
// if c.expected_type == ast.void_type {
|
2020-05-05 00:20:32 +02:00
|
|
|
// c.error('array_init: use `[]Type{}` instead of `[]`', array_init.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
// return ast.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
|
2021-02-12 12:30:30 +01:00
|
|
|
// clear optional flag incase of: `fn opt_arr ?[]int { return [] }`
|
|
|
|
return c.expected_type.clear_flag(.optional)
|
2020-03-04 02:50:32 +01:00
|
|
|
}
|
|
|
|
// [1,2,3]
|
2021-04-02 00:57:09 +02:00
|
|
|
if array_init.exprs.len > 0 && array_init.elem_type == ast.void_type {
|
|
|
|
mut expected_value_type := ast.void_type
|
2020-04-29 12:20:22 +02:00
|
|
|
mut expecting_interface_array := false
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 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 {
|
2021-03-18 14:11:51 +01:00
|
|
|
typ := c.check_expr_opt_call(expr, c.expr(expr))
|
2020-12-01 10:03:45 +01:00
|
|
|
array_init.expr_types << typ
|
|
|
|
// The first element's type
|
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
|
2020-12-09 17:51:37 +01:00
|
|
|
c.type_implements(typ, elem_type, expr.position())
|
2020-04-29 12:20:22 +02:00
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2020-03-04 02:50:32 +01:00
|
|
|
// The first element's type
|
|
|
|
if i == 0 {
|
2021-02-21 11:15:36 +01:00
|
|
|
if expr.is_auto_deref_var() {
|
2021-02-16 13:24:19 +01:00
|
|
|
elem_type = c.table.mktyp(typ.deref())
|
|
|
|
} else {
|
|
|
|
elem_type = c.table.mktyp(typ)
|
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
c.expected_type = elem_type
|
2020-03-04 02:50:32 +01:00
|
|
|
continue
|
|
|
|
}
|
2020-11-15 23:41:24 +01:00
|
|
|
c.check_expected(typ, elem_type) or {
|
2021-03-13 18:13:50 +01:00
|
|
|
c.error('invalid array element: $err.msg', expr.position())
|
2020-03-04 02:50:32 +01:00
|
|
|
}
|
2020-01-19 13:52:34 +01:00
|
|
|
}
|
2020-04-25 18:26:38 +02:00
|
|
|
if array_init.is_fixed {
|
2021-04-28 21:11:15 +02:00
|
|
|
idx := c.table.find_or_register_array_fixed(elem_type, array_init.exprs.len,
|
|
|
|
ast.EmptyExpr{})
|
2021-04-15 01:44:11 +02:00
|
|
|
if elem_type.has_flag(.generic) {
|
|
|
|
array_init.typ = ast.new_type(idx).set_flag(.generic)
|
|
|
|
} else {
|
|
|
|
array_init.typ = ast.new_type(idx)
|
|
|
|
}
|
2020-04-25 18:26:38 +02:00
|
|
|
} else {
|
2021-01-13 23:43:19 +01:00
|
|
|
idx := c.table.find_or_register_array(elem_type)
|
2021-04-15 01:44:11 +02:00
|
|
|
if elem_type.has_flag(.generic) {
|
|
|
|
array_init.typ = ast.new_type(idx).set_flag(.generic)
|
|
|
|
} else {
|
|
|
|
array_init.typ = ast.new_type(idx)
|
|
|
|
}
|
2020-04-25 18:26:38 +02:00
|
|
|
}
|
2020-03-06 14:06:52 +01:00
|
|
|
array_init.elem_type = elem_type
|
2021-01-23 09:33:22 +01:00
|
|
|
} else if array_init.is_fixed && array_init.exprs.len == 1
|
2021-04-02 00:57:09 +02:00
|
|
|
&& array_init.elem_type != ast.void_type {
|
2020-04-07 15:15:45 +02:00
|
|
|
// [50]byte
|
2021-01-20 22:17:49 +01:00
|
|
|
mut fixed_size := 0
|
2020-11-21 00:05:57 +01:00
|
|
|
init_expr := array_init.exprs[0]
|
2020-12-12 09:01:12 +01:00
|
|
|
c.expr(init_expr)
|
2020-11-25 12:09:40 +01:00
|
|
|
match init_expr {
|
2020-03-04 02:50:32 +01:00
|
|
|
ast.IntegerLiteral {
|
2020-07-04 11:23:41 +02:00
|
|
|
fixed_size = init_expr.val.int()
|
2020-03-04 02:50:32 +01:00
|
|
|
}
|
2020-04-08 19:15:16 +02:00
|
|
|
ast.Ident {
|
2020-12-12 09:01:12 +01:00
|
|
|
if init_expr.obj is ast.ConstField {
|
2021-03-09 19:06:34 +01:00
|
|
|
if cint := eval_int_expr(init_expr.obj.expr, 0) {
|
2020-04-11 21:31:54 +02:00
|
|
|
fixed_size = cint
|
|
|
|
}
|
2020-04-08 19:15:16 +02:00
|
|
|
} else {
|
2021-02-20 12:14:44 +01:00
|
|
|
c.error('non-constant array bound `$init_expr.name`', init_expr.pos)
|
2020-04-08 19:15:16 +02:00
|
|
|
}
|
2020-04-11 04:09:41 +02:00
|
|
|
}
|
2021-03-09 19:21:36 +01:00
|
|
|
ast.InfixExpr {
|
|
|
|
if cint := eval_int_expr(init_expr, 0) {
|
|
|
|
fixed_size = cint
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
}
|
2021-01-20 22:17:49 +01:00
|
|
|
if fixed_size <= 0 {
|
|
|
|
c.error('fixed size cannot be zero or negative', init_expr.position())
|
|
|
|
}
|
2021-04-28 21:11:15 +02:00
|
|
|
idx := c.table.find_or_register_array_fixed(array_init.elem_type, fixed_size,
|
|
|
|
init_expr)
|
2021-04-15 01:44:11 +02:00
|
|
|
if array_init.elem_type.has_flag(.generic) {
|
|
|
|
array_init.typ = ast.new_type(idx).set_flag(.generic)
|
|
|
|
} else {
|
|
|
|
array_init.typ = ast.new_type(idx)
|
|
|
|
}
|
2021-01-13 21:57:33 +01:00
|
|
|
if array_init.has_default {
|
|
|
|
c.expr(array_init.default_expr)
|
|
|
|
}
|
2020-02-19 14:34:44 +01:00
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
return array_init.typ
|
2020-01-19 13:52:34 +01:00
|
|
|
}
|
|
|
|
|
2021-03-09 19:06:34 +01:00
|
|
|
fn eval_int_expr(expr ast.Expr, nlevel int) ?int {
|
|
|
|
if nlevel > 100 {
|
|
|
|
// protect against a too deep comptime eval recursion:
|
|
|
|
return none
|
2020-04-11 12:35:41 +02:00
|
|
|
}
|
2021-03-09 19:06:34 +01:00
|
|
|
match expr {
|
|
|
|
ast.IntegerLiteral {
|
|
|
|
return expr.val.int()
|
|
|
|
}
|
|
|
|
ast.InfixExpr {
|
|
|
|
left := eval_int_expr(expr.left, nlevel + 1) ?
|
|
|
|
right := eval_int_expr(expr.right, nlevel + 1) ?
|
|
|
|
match expr.op {
|
|
|
|
.plus { return left + right }
|
|
|
|
.minus { return left - right }
|
|
|
|
.mul { return left * right }
|
|
|
|
.div { return left / right }
|
|
|
|
.mod { return left % right }
|
|
|
|
.xor { return left ^ right }
|
|
|
|
.pipe { return left | right }
|
|
|
|
.amp { return left & right }
|
|
|
|
.left_shift { return left << right }
|
|
|
|
.right_shift { return left >> right }
|
|
|
|
else { return none }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.Ident {
|
|
|
|
if expr.obj is ast.ConstField {
|
|
|
|
// an int constant?
|
|
|
|
cint := eval_int_expr(expr.obj.expr, nlevel + 1) ?
|
|
|
|
return cint
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// dump(expr)
|
|
|
|
return none
|
|
|
|
}
|
2020-04-10 21:00:54 +02:00
|
|
|
}
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
|
2020-11-22 20:51:07 +01:00
|
|
|
[inline]
|
|
|
|
fn (mut c Checker) check_loop_label(label string, pos token.Position) {
|
|
|
|
if label.len == 0 {
|
|
|
|
// ignore
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if c.loop_label.len != 0 {
|
|
|
|
c.error('nesting of labelled `for` loops is not supported', pos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.loop_label = label
|
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
fn (mut c Checker) stmt(node ast.Stmt) {
|
2020-07-14 17:18:10 +02:00
|
|
|
$if trace_checker ? {
|
2021-03-11 13:50:02 +01:00
|
|
|
stmt_pos := node.pos
|
2020-07-12 11:56:01 +02:00
|
|
|
eprintln('checking file: ${c.file.path:-30} | stmt pos: ${stmt_pos.str():-45} | stmt')
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
// c.expected_type = ast.void_type
|
2020-11-25 12:09:40 +01:00
|
|
|
match mut node {
|
2021-03-31 10:13:15 +02:00
|
|
|
ast.EmptyStmt {
|
2021-04-05 17:19:20 +02:00
|
|
|
if c.pref.is_verbose {
|
|
|
|
eprintln('Checker.stmt() EmptyStmt')
|
|
|
|
print_backtrace()
|
|
|
|
}
|
2021-03-31 10:13:15 +02:00
|
|
|
}
|
2021-03-30 09:33:29 +02:00
|
|
|
ast.NodeError {}
|
2021-03-17 01:43:17 +01:00
|
|
|
ast.AsmStmt {
|
|
|
|
c.asm_stmt(mut node)
|
|
|
|
}
|
2020-03-26 10:27:46 +01:00
|
|
|
ast.AssertStmt {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.assert_stmt(node)
|
2020-03-26 10:27:46 +01:00
|
|
|
}
|
2020-02-06 17:38:02 +01:00
|
|
|
ast.AssignStmt {
|
2020-06-18 20:38:59 +02:00
|
|
|
c.assign_stmt(mut node)
|
2020-02-06 17:38:02 +01:00
|
|
|
}
|
2020-03-25 14:24:48 +01:00
|
|
|
ast.Block {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.block(node)
|
2020-03-25 14:24:48 +01:00
|
|
|
}
|
2020-04-07 12:29:11 +02:00
|
|
|
ast.BranchStmt {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.branch_stmt(node)
|
2020-04-07 12:29:11 +02:00
|
|
|
}
|
2020-07-03 15:10:39 +02:00
|
|
|
ast.CompFor {
|
2021-02-26 15:26:50 +01:00
|
|
|
typ := c.unwrap_generic(node.typ)
|
|
|
|
sym := c.table.get_type_symbol(typ)
|
|
|
|
if sym.kind == .placeholder || typ.has_flag(.generic) {
|
|
|
|
c.error('unknown type `$sym.name`', node.typ_pos)
|
2021-02-26 07:28:19 +01:00
|
|
|
}
|
2020-07-03 15:10:39 +02:00
|
|
|
c.stmts(node.stmts)
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
ast.ConstDecl {
|
2020-11-21 19:07:47 +01:00
|
|
|
c.inside_const = true
|
|
|
|
c.const_decl(mut node)
|
|
|
|
c.inside_const = false
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
2020-04-09 19:23:49 +02:00
|
|
|
ast.DeferStmt {
|
2021-02-06 17:40:39 +01:00
|
|
|
if node.idx_in_fn < 0 {
|
|
|
|
node.idx_in_fn = c.cur_fn.defer_stmts.len
|
2021-04-25 20:40:38 +02:00
|
|
|
c.cur_fn.defer_stmts << unsafe { &node }
|
2021-02-06 17:40:39 +01:00
|
|
|
}
|
2020-06-18 20:38:59 +02:00
|
|
|
c.stmts(node.stmts)
|
2020-04-09 19:23:49 +02:00
|
|
|
}
|
|
|
|
ast.EnumDecl {
|
2020-06-18 20:38:59 +02:00
|
|
|
c.enum_decl(node)
|
2020-04-09 19:23:49 +02:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.ExprStmt {
|
2020-06-18 20:38:59 +02:00
|
|
|
node.typ = c.expr(node.expr)
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_type = ast.void_type
|
2021-04-10 16:57:18 +02:00
|
|
|
mut or_typ := ast.void_type
|
|
|
|
match node.expr {
|
|
|
|
ast.IndexExpr {
|
|
|
|
if node.expr.or_expr.kind != .absent {
|
|
|
|
node.is_expr = true
|
|
|
|
or_typ = node.typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.PrefixExpr {
|
|
|
|
if node.expr.or_block.kind != .absent {
|
|
|
|
node.is_expr = true
|
|
|
|
or_typ = node.typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
c.check_expr_opt_call(node.expr, or_typ)
|
2020-05-25 11:32:14 +02:00
|
|
|
// TODO This should work, even if it's prolly useless .-.
|
2021-04-02 00:57:09 +02:00
|
|
|
// node.typ = c.check_expr_opt_call(node.expr, ast.void_type)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
ast.FnDecl {
|
2020-07-08 16:01:17 +02:00
|
|
|
c.fn_decl(mut node)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.ForCStmt {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.for_c_stmt(node)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-11 19:00:51 +01:00
|
|
|
ast.ForInStmt {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.for_in_stmt(mut node)
|
2020-03-11 19:00:51 +01:00
|
|
|
}
|
2020-04-26 06:40:54 +02:00
|
|
|
ast.ForStmt {
|
2021-01-07 21:35:32 +01:00
|
|
|
c.for_stmt(mut node)
|
2020-04-26 06:40:54 +02:00
|
|
|
}
|
2020-05-16 16:12:23 +02:00
|
|
|
ast.GlobalDecl {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.global_decl(node)
|
2020-05-16 16:12:23 +02:00
|
|
|
}
|
2020-07-17 19:13:22 +02:00
|
|
|
ast.GotoLabel {}
|
2020-11-20 12:23:48 +01:00
|
|
|
ast.GotoStmt {
|
2021-02-15 14:48:24 +01:00
|
|
|
if !c.inside_unsafe {
|
|
|
|
c.warn('`goto` requires `unsafe` (consider using labelled break/continue)',
|
|
|
|
node.pos)
|
|
|
|
}
|
2021-02-03 15:20:10 +01:00
|
|
|
if node.name !in c.cur_fn.label_names {
|
|
|
|
c.error('unknown label `$node.name`', node.pos)
|
|
|
|
}
|
2020-11-20 12:23:48 +01:00
|
|
|
// TODO: check label doesn't bypass variable declarations
|
|
|
|
}
|
2020-08-28 08:24:04 +02:00
|
|
|
ast.HashStmt {
|
2020-09-27 03:14:24 +02:00
|
|
|
c.hash_stmt(mut node)
|
2020-08-28 08:24:04 +02:00
|
|
|
}
|
2020-07-18 21:34:38 +02:00
|
|
|
ast.Import {
|
|
|
|
c.import_stmt(node)
|
|
|
|
}
|
2020-05-06 12:26:00 +02:00
|
|
|
ast.InterfaceDecl {
|
2020-07-04 11:23:41 +02:00
|
|
|
c.interface_decl(node)
|
2020-05-06 12:26:00 +02:00
|
|
|
}
|
2020-04-17 17:16:14 +02:00
|
|
|
ast.Module {
|
2020-07-04 11:23:41 +02:00
|
|
|
c.mod = node.name
|
2020-09-05 12:00:35 +02:00
|
|
|
c.is_builtin_mod = node.name in ['builtin', 'os', 'strconv']
|
2020-07-04 11:23:41 +02:00
|
|
|
c.check_valid_snake_case(node.name, 'module name', node.pos)
|
2020-04-17 17:16:14 +02:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.Return {
|
2020-07-09 22:38:43 +02:00
|
|
|
// c.returns = true
|
2020-07-04 11:23:41 +02:00
|
|
|
c.return_stmt(mut node)
|
2020-04-29 12:31:18 +02:00
|
|
|
c.scope_returns = true
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2020-06-24 14:32:14 +02:00
|
|
|
ast.SqlStmt {
|
2020-09-27 03:14:24 +02:00
|
|
|
c.sql_stmt(mut node)
|
2020-06-21 23:09:17 +02:00
|
|
|
}
|
2020-04-16 11:01:18 +02:00
|
|
|
ast.StructDecl {
|
2021-01-09 01:33:36 +01:00
|
|
|
c.struct_decl(mut node)
|
2020-04-16 11:01:18 +02:00
|
|
|
}
|
2020-04-26 06:40:54 +02:00
|
|
|
ast.TypeDecl {
|
2020-07-04 11:23:41 +02:00
|
|
|
c.type_decl(node)
|
2020-04-26 06:40:54 +02:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
|
2021-02-10 17:51:41 +01:00
|
|
|
fn (mut c Checker) assert_stmt(node ast.AssertStmt) {
|
|
|
|
cur_exp_typ := c.expected_type
|
2021-04-01 11:49:13 +02:00
|
|
|
assert_type := c.check_expr_opt_call(node.expr, c.expr(node.expr))
|
2021-04-23 12:33:48 +02:00
|
|
|
if assert_type != ast.bool_type_idx && assert_type != ast.void_type_idx {
|
2021-02-10 17:51:41 +01:00
|
|
|
atype_name := c.table.get_type_symbol(assert_type).name
|
|
|
|
c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead',
|
|
|
|
node.pos)
|
|
|
|
}
|
|
|
|
c.expected_type = cur_exp_typ
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) block(node ast.Block) {
|
|
|
|
if node.is_unsafe {
|
|
|
|
c.inside_unsafe = true
|
|
|
|
c.stmts(node.stmts)
|
|
|
|
c.inside_unsafe = false
|
|
|
|
} else {
|
|
|
|
c.stmts(node.stmts)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) branch_stmt(node ast.BranchStmt) {
|
|
|
|
if c.in_for_count == 0 {
|
|
|
|
c.error('$node.kind.str() statement not within a loop', node.pos)
|
|
|
|
}
|
|
|
|
if node.label.len > 0 {
|
|
|
|
if node.label != c.loop_label {
|
|
|
|
c.error('invalid label name `$node.label`', node.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) for_c_stmt(node ast.ForCStmt) {
|
|
|
|
c.in_for_count++
|
|
|
|
prev_loop_label := c.loop_label
|
2021-03-31 10:13:15 +02:00
|
|
|
if node.has_init {
|
|
|
|
c.stmt(node.init)
|
|
|
|
}
|
2021-02-10 17:51:41 +01:00
|
|
|
c.expr(node.cond)
|
2021-03-31 10:13:15 +02:00
|
|
|
if node.has_inc {
|
|
|
|
c.stmt(node.inc)
|
|
|
|
}
|
2021-02-10 17:51:41 +01:00
|
|
|
c.check_loop_label(node.label, node.pos)
|
|
|
|
c.stmts(node.stmts)
|
|
|
|
c.loop_label = prev_loop_label
|
|
|
|
c.in_for_count--
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
|
|
|
|
c.in_for_count++
|
|
|
|
prev_loop_label := c.loop_label
|
|
|
|
typ := c.expr(node.cond)
|
|
|
|
typ_idx := typ.idx()
|
|
|
|
if node.key_var.len > 0 && node.key_var != '_' {
|
|
|
|
c.check_valid_snake_case(node.key_var, 'variable name', node.pos)
|
|
|
|
}
|
|
|
|
if node.val_var.len > 0 && node.val_var != '_' {
|
|
|
|
c.check_valid_snake_case(node.val_var, 'variable name', node.pos)
|
|
|
|
}
|
|
|
|
if node.is_range {
|
|
|
|
high_type := c.expr(node.high)
|
|
|
|
high_type_idx := high_type.idx()
|
2021-04-02 00:57:09 +02:00
|
|
|
if typ_idx in ast.integer_type_idxs && high_type_idx !in ast.integer_type_idxs {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.error('range types do not match', node.cond.position())
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if typ_idx in ast.float_type_idxs || high_type_idx in ast.float_type_idxs {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.error('range type can not be float', node.cond.position())
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if typ_idx == ast.bool_type_idx || high_type_idx == ast.bool_type_idx {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.error('range type can not be bool', node.cond.position())
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if typ_idx == ast.string_type_idx || high_type_idx == ast.string_type_idx {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.error('range type can not be string', node.cond.position())
|
|
|
|
}
|
2021-04-19 14:38:48 +02:00
|
|
|
if high_type in [ast.int_type, ast.int_literal_type] {
|
|
|
|
node.val_type = typ
|
|
|
|
} else {
|
|
|
|
node.val_type = high_type
|
|
|
|
}
|
|
|
|
node.scope.update_var_type(node.val_var, node.val_type)
|
2021-02-10 17:51:41 +01:00
|
|
|
} else {
|
|
|
|
sym := c.table.get_type_symbol(typ)
|
|
|
|
if sym.kind == .struct_ {
|
|
|
|
// iterators
|
|
|
|
next_fn := sym.find_method('next') or {
|
|
|
|
c.error('a struct must have a `next()` method to be an iterator', node.cond.position())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !next_fn.return_type.has_flag(.optional) {
|
|
|
|
c.error('iterator method `next()` must return an optional', node.cond.position())
|
|
|
|
}
|
|
|
|
// the receiver
|
|
|
|
if next_fn.params.len != 1 {
|
|
|
|
c.error('iterator method `next()` must have 0 parameters', node.cond.position())
|
|
|
|
}
|
|
|
|
val_type := next_fn.return_type.clear_flag(.optional)
|
|
|
|
node.cond_type = typ
|
|
|
|
node.kind = sym.kind
|
|
|
|
node.val_type = val_type
|
|
|
|
node.scope.update_var_type(node.val_var, val_type)
|
|
|
|
} else {
|
|
|
|
if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) {
|
|
|
|
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', node.pos)
|
|
|
|
}
|
|
|
|
if node.key_var.len > 0 {
|
|
|
|
key_type := match sym.kind {
|
|
|
|
.map { sym.map_info().key_type }
|
2021-04-02 00:57:09 +02:00
|
|
|
else { ast.int_type }
|
2021-02-10 17:51:41 +01:00
|
|
|
}
|
|
|
|
node.key_type = key_type
|
|
|
|
node.scope.update_var_type(node.key_var, key_type)
|
|
|
|
}
|
|
|
|
mut value_type := c.table.value_type(typ)
|
2021-04-02 00:57:09 +02:00
|
|
|
if value_type == ast.void_type || typ.has_flag(.optional) {
|
|
|
|
if typ != ast.void_type {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.position())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if node.val_is_mut {
|
|
|
|
value_type = value_type.to_ptr()
|
|
|
|
match node.cond {
|
|
|
|
ast.Ident {
|
|
|
|
if node.cond.obj is ast.Var {
|
|
|
|
obj := node.cond.obj as ast.Var
|
|
|
|
if !obj.is_mut {
|
|
|
|
c.error('`$obj.name` is immutable, it cannot be changed',
|
|
|
|
node.cond.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.ArrayInit {
|
|
|
|
c.error('array literal is immutable, it cannot be changed', node.cond.pos)
|
|
|
|
}
|
|
|
|
ast.MapInit {
|
|
|
|
c.error('map literal is immutable, it cannot be changed', node.cond.pos)
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
node.cond_type = typ
|
|
|
|
node.kind = sym.kind
|
|
|
|
node.val_type = value_type
|
|
|
|
node.scope.update_var_type(node.val_var, value_type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.check_loop_label(node.label, node.pos)
|
|
|
|
c.stmts(node.stmts)
|
|
|
|
c.loop_label = prev_loop_label
|
|
|
|
c.in_for_count--
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
|
|
|
|
c.in_for_count++
|
|
|
|
prev_loop_label := c.loop_label
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_type = ast.bool_type
|
2021-02-10 17:51:41 +01:00
|
|
|
typ := c.expr(node.cond)
|
2021-04-02 00:57:09 +02:00
|
|
|
if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated {
|
2021-02-10 17:51:41 +01:00
|
|
|
c.error('non-bool used as for condition', node.pos)
|
|
|
|
}
|
|
|
|
if node.cond is ast.InfixExpr {
|
|
|
|
infix := node.cond
|
|
|
|
if infix.op == .key_is {
|
|
|
|
if (infix.left is ast.Ident || infix.left is ast.SelectorExpr)
|
2021-04-02 00:57:09 +02:00
|
|
|
&& infix.right is ast.TypeNode {
|
|
|
|
right_expr := infix.right as ast.TypeNode
|
2021-02-10 17:51:41 +01:00
|
|
|
is_variable := if mut infix.left is ast.Ident {
|
|
|
|
infix.left.kind == .variable
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
left_type := c.expr(infix.left)
|
|
|
|
left_sym := c.table.get_type_symbol(left_type)
|
|
|
|
if is_variable {
|
2021-04-09 10:00:05 +02:00
|
|
|
if left_sym.kind in [.sum_type, .interface_] {
|
|
|
|
c.smartcast(infix.left, infix.left_type, right_expr.typ, mut node.scope)
|
2021-02-10 17:51:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO: update loop var type
|
|
|
|
// how does this work currenly?
|
|
|
|
c.check_loop_label(node.label, node.pos)
|
|
|
|
c.stmts(node.stmts)
|
|
|
|
c.loop_label = prev_loop_label
|
|
|
|
c.in_for_count--
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) global_decl(node ast.GlobalDecl) {
|
|
|
|
for field in node.fields {
|
|
|
|
c.check_valid_snake_case(field.name, 'global name', field.pos)
|
|
|
|
if field.name in c.global_names {
|
|
|
|
c.error('duplicate global `$field.name`', field.pos)
|
|
|
|
}
|
2021-04-23 11:28:08 +02:00
|
|
|
sym := c.table.get_type_symbol(field.typ)
|
|
|
|
if sym.kind == .placeholder {
|
|
|
|
c.error('unknown type `$sym.name`', field.typ_pos)
|
|
|
|
}
|
2021-02-10 17:51:41 +01:00
|
|
|
c.global_names << field.name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-11 23:56:25 +02:00
|
|
|
fn (mut c Checker) go_expr(mut node ast.GoExpr) ast.Type {
|
|
|
|
ret_type := c.call_expr(mut node.call_expr)
|
|
|
|
if node.call_expr.or_block.kind != .absent {
|
|
|
|
c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`',
|
|
|
|
node.call_expr.or_block.pos)
|
|
|
|
}
|
2021-02-10 17:51:41 +01:00
|
|
|
// Make sure there are no mutable arguments
|
|
|
|
for arg in node.call_expr.args {
|
|
|
|
if arg.is_mut && !arg.typ.is_ptr() {
|
|
|
|
c.error('function in `go` statement cannot contain mutable non-reference arguments',
|
|
|
|
arg.expr.position())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr()
|
|
|
|
&& !node.call_expr.left_type.is_ptr() {
|
|
|
|
c.error('method in `go` statement cannot have non-reference mutable receiver',
|
|
|
|
node.call_expr.left.position())
|
|
|
|
}
|
2021-04-11 23:56:25 +02:00
|
|
|
return c.table.find_or_register_thread(ret_type)
|
2021-02-10 17:51:41 +01:00
|
|
|
}
|
|
|
|
|
2021-03-17 01:43:17 +01:00
|
|
|
fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) {
|
|
|
|
if stmt.is_goto {
|
|
|
|
c.warn('inline assembly goto is not supported, it will most likely not work',
|
|
|
|
stmt.pos)
|
|
|
|
}
|
|
|
|
if c.pref.backend == .js {
|
2021-04-14 07:50:50 +02:00
|
|
|
c.error('inline assembly is not supported in the js backend', stmt.pos)
|
2021-03-17 01:43:17 +01:00
|
|
|
}
|
|
|
|
if c.pref.backend == .c && c.pref.ccompiler_type == .msvc {
|
|
|
|
c.error('msvc compiler does not support inline assembly', stmt.pos)
|
|
|
|
}
|
|
|
|
mut aliases := c.asm_ios(stmt.output, mut stmt.scope, true)
|
|
|
|
aliases2 := c.asm_ios(stmt.input, mut stmt.scope, false)
|
|
|
|
aliases << aliases2
|
|
|
|
for template in stmt.templates {
|
|
|
|
if template.is_directive {
|
|
|
|
/*
|
|
|
|
align n[,value]
|
|
|
|
.skip n[,value]
|
|
|
|
.space n[,value]
|
|
|
|
.byte value1[,...]
|
|
|
|
.word value1[,...]
|
|
|
|
.short value1[,...]
|
|
|
|
.int value1[,...]
|
|
|
|
.long value1[,...]
|
|
|
|
.quad immediate_value1[,...]
|
|
|
|
.globl symbol
|
|
|
|
.global symbol
|
|
|
|
.section section
|
|
|
|
.text
|
|
|
|
.data
|
|
|
|
.bss
|
|
|
|
.fill repeat[,size[,value]]
|
|
|
|
.org n
|
|
|
|
.previous
|
|
|
|
.string string[,...]
|
|
|
|
.asciz string[,...]
|
|
|
|
.ascii string[,...]
|
|
|
|
*/
|
|
|
|
if template.name !in ['skip', 'space', 'byte', 'word', 'short', 'int', 'long', 'quad',
|
|
|
|
'globl', 'global', 'section', 'text', 'data', 'bss', 'fill', 'org', 'previous',
|
2021-04-15 01:30:23 +02:00
|
|
|
'string', 'asciz', 'ascii'] { // all tcc-supported assembler directives
|
2021-03-17 01:43:17 +01:00
|
|
|
c.error('unknown assembler directive: `$template.name`', template.pos)
|
|
|
|
}
|
|
|
|
}
|
2021-04-01 08:58:33 +02:00
|
|
|
for mut arg in template.args {
|
2021-03-17 01:43:17 +01:00
|
|
|
c.asm_arg(arg, stmt, aliases)
|
|
|
|
}
|
|
|
|
}
|
2021-04-01 08:58:33 +02:00
|
|
|
for mut clob in stmt.clobbered {
|
2021-03-17 01:43:17 +01:00
|
|
|
c.asm_arg(clob.reg, stmt, aliases)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt, aliases []string) {
|
|
|
|
match mut arg {
|
2021-04-14 07:50:50 +02:00
|
|
|
ast.AsmAlias {}
|
2021-03-17 01:43:17 +01:00
|
|
|
ast.AsmAddressing {
|
|
|
|
if arg.scale !in [-1, 1, 2, 4, 8] {
|
|
|
|
c.error('scale must be one of 1, 2, 4, or 8', arg.pos)
|
|
|
|
}
|
2021-04-01 08:58:33 +02:00
|
|
|
c.asm_arg(arg.displacement, stmt, aliases)
|
2021-03-17 01:43:17 +01:00
|
|
|
c.asm_arg(arg.base, stmt, aliases)
|
|
|
|
c.asm_arg(arg.index, stmt, aliases)
|
|
|
|
}
|
|
|
|
ast.BoolLiteral {} // all of these are guarented to be correct.
|
|
|
|
ast.FloatLiteral {}
|
|
|
|
ast.CharLiteral {}
|
|
|
|
ast.IntegerLiteral {}
|
|
|
|
ast.AsmRegister {} // if the register is not found, the parser will register it as an alias
|
2021-04-01 08:58:33 +02:00
|
|
|
ast.AsmDisp {}
|
2021-03-17 01:43:17 +01:00
|
|
|
string {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) asm_ios(ios []ast.AsmIO, mut scope ast.Scope, output bool) []string {
|
|
|
|
mut aliases := []string{}
|
|
|
|
for io in ios {
|
|
|
|
typ := c.expr(io.expr)
|
|
|
|
if output {
|
|
|
|
c.fail_if_immutable(io.expr)
|
|
|
|
}
|
|
|
|
if io.alias != '' {
|
|
|
|
aliases << io.alias
|
|
|
|
if io.alias in scope.objects {
|
|
|
|
scope.objects[io.alias] = ast.Var{
|
|
|
|
name: io.alias
|
|
|
|
expr: io.expr
|
|
|
|
is_arg: true
|
|
|
|
typ: typ
|
|
|
|
orig_type: typ
|
|
|
|
pos: io.pos
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return aliases
|
|
|
|
}
|
|
|
|
|
2020-08-28 08:24:04 +02:00
|
|
|
fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
2020-09-18 00:58:54 +02:00
|
|
|
if c.skip_flags {
|
|
|
|
return
|
|
|
|
}
|
2020-08-28 08:24:04 +02:00
|
|
|
if c.pref.backend == .js {
|
|
|
|
if !c.file.path.ends_with('.js.v') {
|
2021-03-11 21:43:42 +01:00
|
|
|
c.error('hash statements are only allowed in backend specific files such "x.js.v"',
|
2020-08-28 08:24:04 +02:00
|
|
|
node.pos)
|
|
|
|
}
|
|
|
|
if c.mod == 'main' {
|
2021-03-11 21:43:42 +01:00
|
|
|
c.error('hash statements are not allowed in the main module. Please place them in a separate module.',
|
2020-08-28 08:24:04 +02:00
|
|
|
node.pos)
|
|
|
|
}
|
2020-10-15 10:58:01 +02:00
|
|
|
return
|
|
|
|
}
|
2021-03-11 21:43:42 +01:00
|
|
|
match node.kind {
|
|
|
|
'include' {
|
|
|
|
mut flag := node.main
|
|
|
|
if flag.contains('@VROOT') {
|
2021-04-19 18:01:47 +02:00
|
|
|
// c.note(checker.vroot_is_deprecated_message, node.pos)
|
|
|
|
vroot := util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or {
|
|
|
|
c.error(err.msg, node.pos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
node.val = 'include $vroot'
|
|
|
|
node.main = vroot
|
|
|
|
flag = vroot
|
|
|
|
}
|
|
|
|
if flag.contains('@VEXEROOT') {
|
|
|
|
vroot := flag.replace('@VEXEROOT', os.dir(pref.vexe_path()))
|
|
|
|
node.val = 'include $vroot'
|
|
|
|
node.main = vroot
|
|
|
|
flag = vroot
|
|
|
|
}
|
|
|
|
if flag.contains('@VMODROOT') {
|
|
|
|
vroot := util.resolve_vmodroot(flag, c.file.path) or {
|
2021-03-11 21:43:42 +01:00
|
|
|
c.error(err.msg, node.pos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
node.val = 'include $vroot'
|
|
|
|
node.main = vroot
|
|
|
|
flag = vroot
|
|
|
|
}
|
|
|
|
if flag.contains('\$env(') {
|
|
|
|
env := util.resolve_env_value(flag, true) or {
|
|
|
|
c.error(err.msg, node.pos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
node.main = env
|
|
|
|
}
|
|
|
|
flag_no_comment := flag.all_before('//').trim_space()
|
|
|
|
if !((flag_no_comment.starts_with('"') && flag_no_comment.ends_with('"'))
|
|
|
|
|| (flag_no_comment.starts_with('<') && flag_no_comment.ends_with('>'))) {
|
|
|
|
c.error('including C files should use either `"header_file.h"` or `<header_file.h>` quoting',
|
|
|
|
node.pos)
|
2020-08-28 08:24:04 +02:00
|
|
|
}
|
2021-01-31 18:22:42 +01:00
|
|
|
}
|
2021-03-11 21:43:42 +01:00
|
|
|
'pkgconfig' {
|
|
|
|
args := if node.main.contains('--') {
|
|
|
|
node.main.split(' ')
|
|
|
|
} else {
|
|
|
|
'--cflags --libs $node.main'.split(' ')
|
|
|
|
}
|
|
|
|
mut m := pkgconfig.main(args) or {
|
2021-02-28 21:20:21 +01:00
|
|
|
c.error(err.msg, node.pos)
|
2021-01-31 18:22:42 +01:00
|
|
|
return
|
|
|
|
}
|
2021-03-11 21:43:42 +01:00
|
|
|
cflags := m.run() or {
|
2021-02-28 21:20:21 +01:00
|
|
|
c.error(err.msg, node.pos)
|
2020-08-28 08:24:04 +02:00
|
|
|
return
|
|
|
|
}
|
2021-03-11 21:43:42 +01:00
|
|
|
c.table.parse_cflag(cflags, c.mod, c.pref.compile_defines_all) or {
|
2021-02-28 21:20:21 +01:00
|
|
|
c.error(err.msg, node.pos)
|
2021-01-31 18:22:42 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2021-03-11 21:43:42 +01:00
|
|
|
'flag' {
|
|
|
|
// #flag linux -lm
|
|
|
|
mut flag := node.main
|
|
|
|
if flag.contains('@VROOT') {
|
2021-04-19 18:01:47 +02:00
|
|
|
// c.note(checker.vroot_is_deprecated_message, node.pos)
|
|
|
|
flag = util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or {
|
|
|
|
c.error(err.msg, node.pos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if flag.contains('@VEXEROOT') {
|
|
|
|
// expand `@VEXEROOT` to its absolute path
|
|
|
|
flag = flag.replace('@VEXEROOT', os.dir(pref.vexe_path()))
|
|
|
|
}
|
|
|
|
if flag.contains('@VMODROOT') {
|
|
|
|
flag = util.resolve_vmodroot(flag, c.file.path) or {
|
2021-03-11 21:43:42 +01:00
|
|
|
c.error(err.msg, node.pos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if flag.contains('\$env(') {
|
|
|
|
flag = util.resolve_env_value(flag, true) or {
|
|
|
|
c.error(err.msg, node.pos)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for deprecated in ['@VMOD', '@VMODULE', '@VPATH', '@VLIB_PATH'] {
|
|
|
|
if flag.contains(deprecated) {
|
2021-04-19 18:01:47 +02:00
|
|
|
if !flag.contains('@VMODROOT') {
|
|
|
|
c.error('$deprecated had been deprecated, use @VMODROOT instead.',
|
|
|
|
node.pos)
|
|
|
|
}
|
2021-03-11 21:43:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// println('adding flag "$flag"')
|
|
|
|
c.table.parse_cflag(flag, c.mod, c.pref.compile_defines_all) or {
|
|
|
|
c.error(err.msg, node.pos)
|
2020-08-28 08:24:04 +02:00
|
|
|
}
|
|
|
|
}
|
2021-03-11 21:43:42 +01:00
|
|
|
else {
|
|
|
|
if node.kind != 'define' {
|
|
|
|
c.error('expected `#define`, `#flag`, `#include` or `#pkgconfig` not $node.val',
|
|
|
|
node.pos)
|
|
|
|
}
|
2020-10-15 10:58:01 +02:00
|
|
|
}
|
2020-08-28 08:24:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-18 21:34:38 +02:00
|
|
|
fn (mut c Checker) import_stmt(imp ast.Import) {
|
2021-04-09 12:14:27 +02:00
|
|
|
c.check_valid_snake_case(imp.alias, 'module alias', imp.pos)
|
2020-07-18 21:34:38 +02:00
|
|
|
for sym in imp.syms {
|
2020-11-27 03:07:37 +01:00
|
|
|
name := '${imp.mod}.$sym.name'
|
2020-12-07 18:13:03 +01:00
|
|
|
if sym.name[0].is_capital() {
|
2020-07-18 21:34:38 +02:00
|
|
|
if type_sym := c.table.find_type(name) {
|
2020-12-07 18:13:03 +01:00
|
|
|
if type_sym.kind != .placeholder {
|
|
|
|
if !type_sym.is_public {
|
|
|
|
c.error('module `$imp.mod` type `$sym.name` is private', sym.pos)
|
|
|
|
}
|
|
|
|
continue
|
2020-07-18 21:34:38 +02:00
|
|
|
}
|
|
|
|
}
|
2020-12-07 18:13:03 +01:00
|
|
|
c.error('module `$imp.mod` has no type `$sym.name`', sym.pos)
|
|
|
|
continue
|
2020-07-18 21:34:38 +02:00
|
|
|
}
|
2020-12-07 18:13:03 +01:00
|
|
|
if func := c.table.find_fn(name) {
|
|
|
|
if !func.is_pub {
|
|
|
|
c.error('module `$imp.mod` function `${sym.name}()` is private', sym.pos)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if _ := c.file.global_scope.find_const(name) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
c.error('module `$imp.mod` has no constant or function `$sym.name`', sym.pos)
|
2020-07-18 21:34:38 +02: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
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_type = ast.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 {
|
2021-03-11 13:50:02 +01:00
|
|
|
unreachable = stmt.pos
|
2020-04-29 12:31:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-02-29 18:34:25 +01:00
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
2020-04-29 12:31:18 +02:00
|
|
|
if unreachable.line_nr >= 0 {
|
2020-06-15 07:10:45 +02:00
|
|
|
c.error('unreachable code', unreachable)
|
2020-04-29 12:31:18 +02:00
|
|
|
}
|
|
|
|
c.scope_returns = false
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_type = ast.void_type
|
2020-02-29 18:34:25 +01:00
|
|
|
}
|
|
|
|
|
2021-04-11 08:02:57 +02:00
|
|
|
pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
|
2020-06-29 20:09:09 +02:00
|
|
|
if typ.has_flag(.generic) {
|
2021-04-26 19:05:10 +02:00
|
|
|
if t_typ := c.table.resolve_generic_to_concrete(typ, c.cur_fn.generic_names, c.cur_fn.cur_generic_types,
|
|
|
|
false)
|
|
|
|
{
|
2021-04-11 08:02:57 +02:00
|
|
|
return t_typ
|
2021-01-22 13:49:56 +01:00
|
|
|
}
|
2020-05-25 05:32:33 +02:00
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO node must be mut
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) expr(node ast.Expr) ast.Type {
|
2020-06-08 13:10:47 +02:00
|
|
|
c.expr_level++
|
2020-06-10 12:17:49 +02:00
|
|
|
defer {
|
|
|
|
c.expr_level--
|
|
|
|
}
|
2020-06-08 19:35:27 +02:00
|
|
|
if c.expr_level > 200 {
|
|
|
|
c.error('checker: too many expr levels: $c.expr_level ', node.position())
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-06-08 13:10:47 +02:00
|
|
|
}
|
2020-11-25 12:09:40 +01:00
|
|
|
match mut node {
|
2021-03-30 09:33:29 +02:00
|
|
|
ast.NodeError {}
|
2021-03-31 10:13:15 +02:00
|
|
|
ast.EmptyExpr {
|
|
|
|
c.error('checker.expr(): unhandled EmptyExpr', token.Position{})
|
|
|
|
}
|
2020-10-21 21:58:40 +02:00
|
|
|
ast.CTempVar {
|
|
|
|
return node.typ
|
|
|
|
}
|
2020-05-15 23:14:53 +02:00
|
|
|
ast.AnonFn {
|
2021-01-29 22:11:05 +01:00
|
|
|
c.inside_anon_fn = true
|
2020-05-23 08:51:15 +02:00
|
|
|
keep_fn := c.cur_fn
|
2021-04-25 20:40:38 +02:00
|
|
|
c.cur_fn = unsafe { &node.decl }
|
2020-06-18 20:38:59 +02:00
|
|
|
c.stmts(node.decl.stmts)
|
2021-01-29 22:11:05 +01:00
|
|
|
c.fn_decl(mut node.decl)
|
2020-05-23 08:51:15 +02:00
|
|
|
c.cur_fn = keep_fn
|
2021-01-29 22:11:05 +01:00
|
|
|
c.inside_anon_fn = false
|
2020-07-07 17:10:39 +02:00
|
|
|
return node.typ
|
2020-05-15 23:14:53 +02:00
|
|
|
}
|
2020-12-29 16:14:08 +01:00
|
|
|
ast.ArrayDecompose {
|
|
|
|
typ := c.expr(node.expr)
|
|
|
|
type_sym := c.table.get_type_symbol(typ)
|
|
|
|
if type_sym.kind != .array {
|
2021-01-03 15:45:39 +01:00
|
|
|
c.error('decomposition can only be used on arrays', node.expr.position())
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-12-29 16:14:08 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
array_info := type_sym.info as ast.Array
|
2020-12-29 16:14:08 +01:00
|
|
|
elem_type := array_info.elem_type.set_flag(.variadic)
|
|
|
|
node.expr_type = typ
|
|
|
|
node.arg_type = elem_type
|
|
|
|
return elem_type
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.ArrayInit {
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.array_init(mut node)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2020-03-02 19:00:33 +01:00
|
|
|
ast.AsCast {
|
2020-06-18 20:38:59 +02:00
|
|
|
node.expr_type = c.expr(node.expr)
|
|
|
|
expr_type_sym := c.table.get_type_symbol(node.expr_type)
|
|
|
|
type_sym := c.table.get_type_symbol(node.typ)
|
2020-11-25 12:09:40 +01:00
|
|
|
if expr_type_sym.kind == .sum_type {
|
2021-03-06 20:04:51 +01:00
|
|
|
c.ensure_type_exists(node.typ, node.pos) or {}
|
2020-07-06 13:27:48 +02:00
|
|
|
if !c.table.sumtype_has_variant(node.expr_type, node.typ) {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('cannot cast `$expr_type_sym.name` to `$type_sym.name`', node.pos)
|
2020-07-04 11:23:41 +02:00
|
|
|
// c.error('only $info.variants can be casted to `$typ`', node.pos)
|
2020-03-18 14:50:21 +01:00
|
|
|
}
|
2020-04-26 09:17:13 +02:00
|
|
|
} else {
|
2020-12-06 04:55:08 +01:00
|
|
|
mut s := 'cannot cast non-sum type `$expr_type_sym.name` using `as`'
|
2020-11-25 12:09:40 +01:00
|
|
|
if type_sym.kind == .sum_type {
|
2020-12-06 04:55:08 +01:00
|
|
|
s += ' - use e.g. `${type_sym.name}(some_expr)` instead.'
|
2020-10-09 16:12:12 +02:00
|
|
|
}
|
|
|
|
c.error(s, node.pos)
|
2020-03-18 14:50:21 +01:00
|
|
|
}
|
2020-11-25 12:09:40 +01:00
|
|
|
if expr_type_sym.kind == .sum_type {
|
2020-11-11 09:18:15 +01:00
|
|
|
return node.typ
|
|
|
|
}
|
2020-06-18 20:38:59 +02:00
|
|
|
return node.typ.to_ptr()
|
2020-03-02 19:00:33 +01:00
|
|
|
}
|
2020-02-29 18:07:29 +01:00
|
|
|
ast.Assoc {
|
2021-03-01 00:18:14 +01:00
|
|
|
v := node.scope.find_var(node.var_name) or { panic(err) }
|
2020-06-18 20:38:59 +02:00
|
|
|
for i, _ in node.fields {
|
|
|
|
c.expr(node.exprs[i])
|
2020-03-19 08:57:33 +01:00
|
|
|
}
|
2020-06-18 20:38:59 +02:00
|
|
|
node.typ = v.typ
|
2020-04-15 01:45:27 +02:00
|
|
|
return v.typ
|
2020-02-29 18:07:29 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.BoolLiteral {
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.bool_type
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
ast.CastExpr {
|
2020-11-02 01:17:35 +01:00
|
|
|
return c.cast_expr(mut node)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
ast.CallExpr {
|
2021-02-28 23:21:03 +01:00
|
|
|
mut ret_type := c.call_expr(mut node)
|
2021-03-10 12:53:23 +01:00
|
|
|
if !ret_type.has_flag(.optional) {
|
|
|
|
if node.or_block.kind == .block {
|
|
|
|
c.error('unexpected `or` block, the function `$node.name` does not return an optional',
|
|
|
|
node.or_block.pos)
|
|
|
|
} else if node.or_block.kind == .propagate {
|
|
|
|
c.error('unexpected `?`, the function `$node.name` does not return an optional',
|
|
|
|
node.or_block.pos)
|
|
|
|
}
|
|
|
|
}
|
2021-02-28 23:21:03 +01:00
|
|
|
if ret_type.has_flag(.optional) && node.or_block.kind != .absent {
|
|
|
|
ret_type = ret_type.clear_flag(.optional)
|
|
|
|
}
|
|
|
|
return ret_type
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2020-08-14 21:18:42 +02:00
|
|
|
ast.ChanInit {
|
|
|
|
return c.chan_init(mut node)
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.CharLiteral {
|
2021-01-11 22:58:15 +01:00
|
|
|
// return int_literal, not rune, so that we can do "bytes << `A`" without a cast etc
|
2021-04-02 00:57:09 +02:00
|
|
|
// return ast.int_literal_type
|
|
|
|
return ast.rune_type
|
|
|
|
// return ast.byte_type
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2020-07-17 19:13:22 +02:00
|
|
|
ast.Comment {
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-07-17 19:13:22 +02:00
|
|
|
}
|
2020-11-05 09:12:32 +01:00
|
|
|
ast.AtExpr {
|
|
|
|
return c.at_expr(mut node)
|
|
|
|
}
|
2020-06-06 10:05:26 +02:00
|
|
|
ast.ComptimeCall {
|
2021-01-17 06:24:03 +01:00
|
|
|
return c.comptime_call(mut node)
|
2021-01-05 15:11:43 +01:00
|
|
|
}
|
|
|
|
ast.ComptimeSelector {
|
|
|
|
node.left_type = c.unwrap_generic(c.expr(node.left))
|
|
|
|
expr_type := c.unwrap_generic(c.expr(node.field_expr))
|
|
|
|
expr_sym := c.table.get_type_symbol(expr_type)
|
2021-04-02 00:57:09 +02:00
|
|
|
if expr_type != ast.string_type {
|
2021-01-05 15:11:43 +01:00
|
|
|
c.error('expected `string` instead of `$expr_sym.name` (e.g. `field.name`)',
|
|
|
|
node.field_expr.position())
|
|
|
|
}
|
|
|
|
if node.field_expr is ast.SelectorExpr {
|
2021-01-29 00:45:00 +01:00
|
|
|
left_pos := node.field_expr.expr.position()
|
|
|
|
if c.comptime_fields_type.len == 0 {
|
|
|
|
c.error('compile time field access can only be used when iterating over `T.fields`',
|
|
|
|
left_pos)
|
|
|
|
}
|
2021-01-05 15:11:43 +01:00
|
|
|
expr_name := node.field_expr.expr.str()
|
|
|
|
if expr_name in c.comptime_fields_type {
|
|
|
|
return c.comptime_fields_type[expr_name]
|
|
|
|
}
|
2021-01-29 00:45:00 +01:00
|
|
|
c.error('unknown `\$for` variable `$expr_name`', left_pos)
|
|
|
|
} else {
|
|
|
|
c.error('expected selector expression e.g. `$(field.name)`', node.field_expr.position())
|
2021-01-05 15:11:43 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-06-06 10:05:26 +02:00
|
|
|
}
|
2020-05-15 23:14:53 +02:00
|
|
|
ast.ConcatExpr {
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.concat_expr(mut node)
|
2020-05-15 23:14:53 +02:00
|
|
|
}
|
2021-03-06 18:09:28 +01:00
|
|
|
ast.DumpExpr {
|
|
|
|
node.expr_type = c.expr(node.expr)
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.expr_type.idx() == ast.void_type_idx {
|
2021-03-06 18:09:28 +01:00
|
|
|
c.error('dump expression can not be void', node.expr.position())
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2021-03-06 18:09:28 +01:00
|
|
|
}
|
|
|
|
tsym := c.table.get_type_symbol(node.expr_type)
|
|
|
|
c.table.dumps[int(node.expr_type)] = tsym.cname
|
|
|
|
node.cname = tsym.cname
|
|
|
|
return node.expr_type
|
|
|
|
}
|
2020-02-25 15:02:34 +01:00
|
|
|
ast.EnumVal {
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.enum_val(mut node)
|
2020-02-25 15:02:34 +01:00
|
|
|
}
|
2020-02-17 14:15:42 +01:00
|
|
|
ast.FloatLiteral {
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.float_literal_type
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
2021-04-11 23:56:25 +02:00
|
|
|
ast.GoExpr {
|
|
|
|
return c.go_expr(mut node)
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.Ident {
|
2020-07-04 11:23:41 +02:00
|
|
|
// c.checked_ident = node.name
|
2020-06-18 20:38:59 +02:00
|
|
|
res := c.ident(mut node)
|
2020-04-08 19:15:16 +02:00
|
|
|
// c.checked_ident = ''
|
|
|
|
return res
|
2020-02-18 18:13:34 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.IfExpr {
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.if_expr(mut node)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.IfGuardExpr {
|
2020-06-18 20:38:59 +02:00
|
|
|
node.expr_type = c.expr(node.expr)
|
2020-11-02 00:56:28 +01:00
|
|
|
if !node.expr_type.has_flag(.optional) {
|
2021-03-03 09:10:38 +01:00
|
|
|
mut no_opt := true
|
|
|
|
match mut node.expr {
|
|
|
|
ast.IndexExpr {
|
|
|
|
no_opt = false
|
|
|
|
node.expr_type = node.expr_type.set_flag(.optional)
|
|
|
|
node.expr.is_option = true
|
|
|
|
}
|
|
|
|
ast.PrefixExpr {
|
|
|
|
if node.expr.op == .arrow {
|
|
|
|
no_opt = false
|
|
|
|
node.expr_type = node.expr_type.set_flag(.optional)
|
|
|
|
node.expr.is_option = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
if no_opt {
|
|
|
|
c.error('expression should return an option', node.expr.position())
|
|
|
|
}
|
2020-11-02 00:56:28 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.bool_type
|
2020-02-18 18:13:34 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.IndexExpr {
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.index_expr(mut node)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.InfixExpr {
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.infix_expr(mut node)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.IntegerLiteral {
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.int_literal_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-07-04 12:44:25 +02:00
|
|
|
ast.LockExpr {
|
|
|
|
return c.lock_expr(mut node)
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.MapInit {
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.map_init(mut node)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
ast.MatchExpr {
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.match_expr(mut node)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.PostfixExpr {
|
2020-09-27 03:14:24 +02:00
|
|
|
return c.postfix_expr(mut node)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.PrefixExpr {
|
2020-12-20 07:55:23 +01:00
|
|
|
return c.prefix_expr(mut node)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.None {
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.none_type
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2020-06-17 00:59:33 +02:00
|
|
|
ast.OrExpr {
|
|
|
|
// never happens
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-06-17 00:59:33 +02:00
|
|
|
}
|
2020-09-12 16:40:52 +02:00
|
|
|
// ast.OrExpr2 {
|
|
|
|
// return node.typ
|
|
|
|
// }
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.ParExpr {
|
2020-07-29 21:41:47 +02:00
|
|
|
return c.expr(node.expr)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-06-17 00:59:33 +02:00
|
|
|
ast.RangeExpr {
|
|
|
|
// never happens
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-06-17 00:59:33 +02:00
|
|
|
}
|
2020-09-16 15:34:57 +02:00
|
|
|
ast.SelectExpr {
|
2020-09-19 02:14:35 +02:00
|
|
|
return c.select_expr(mut node)
|
2020-09-16 15:34:57 +02:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
ast.SelectorExpr {
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.selector_expr(mut node)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.SizeOf {
|
2021-01-13 06:13:39 +01:00
|
|
|
if !node.is_type {
|
|
|
|
node.typ = c.expr(node.expr)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.u32_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2021-01-30 12:57:09 +01:00
|
|
|
ast.OffsetOf {
|
|
|
|
return c.offset_of(node)
|
|
|
|
}
|
2020-06-17 00:59:33 +02:00
|
|
|
ast.SqlExpr {
|
2020-09-27 03:14:24 +02:00
|
|
|
return c.sql_expr(mut node)
|
2020-06-17 00:59:33 +02:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.StringLiteral {
|
2020-06-18 20:38:59 +02:00
|
|
|
if node.language == .c {
|
2021-04-05 05:36:02 +02:00
|
|
|
// string literal starts with "c": `C.printf(c'hello')`
|
|
|
|
return ast.byte_type.set_nr_muls(1)
|
2020-04-01 17:14:17 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2020-02-10 14:43:17 +01:00
|
|
|
}
|
2020-03-21 07:01:06 +01:00
|
|
|
ast.StringInterLiteral {
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.string_inter_lit(mut node)
|
2020-03-21 07:01:06 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.StructInit {
|
2021-02-02 14:42:00 +01:00
|
|
|
if node.unresolved {
|
|
|
|
return c.expr(ast.resolve_init(node, c.unwrap_generic(node.typ), c.table))
|
|
|
|
}
|
2020-06-18 20:38:59 +02:00
|
|
|
return c.struct_init(mut node)
|
2020-02-29 05:36:39 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.TypeNode {
|
2020-06-18 20:38:59 +02:00
|
|
|
return node.typ
|
2020-03-02 10:53:38 +01:00
|
|
|
}
|
2020-03-19 12:15:39 +01:00
|
|
|
ast.TypeOf {
|
2020-06-18 20:38:59 +02:00
|
|
|
node.expr_type = c.expr(node.expr)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2020-03-19 12:15:39 +01:00
|
|
|
}
|
2020-07-12 12:58:33 +02:00
|
|
|
ast.UnsafeExpr {
|
|
|
|
return c.unsafe_expr(mut node)
|
|
|
|
}
|
2020-06-09 16:36:18 +02:00
|
|
|
ast.Likely {
|
2020-06-18 20:38:59 +02:00
|
|
|
ltype := c.expr(node.expr)
|
2021-04-02 00:57:09 +02:00
|
|
|
if !c.check_types(ltype, ast.bool_type) {
|
2020-06-09 16:36:18 +02:00
|
|
|
ltype_sym := c.table.get_type_symbol(ltype)
|
2020-06-18 20:38:59 +02:00
|
|
|
lname := if node.is_likely { '_likely_' } else { '_unlikely_' }
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('`${lname}()` expects a boolean expression, instead it got `$ltype_sym.name`',
|
2020-06-18 20:38:59 +02:00
|
|
|
node.pos)
|
2020-06-09 16:36:18 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.bool_type
|
2020-06-09 16:36:18 +02:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
// pub fn (mut c Checker) asm_reg(mut node ast.AsmRegister) ast.Type {
|
2021-03-17 01:43:17 +01:00
|
|
|
// name := node.name
|
|
|
|
|
|
|
|
// for bit_size, array in ast.x86_no_number_register_list {
|
|
|
|
// if name in array {
|
|
|
|
// return c.table.bitsize_to_type(bit_size)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// for bit_size, array in ast.x86_with_number_register_list {
|
|
|
|
// if name in array {
|
|
|
|
// return c.table.bitsize_to_type(bit_size)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// c.error('invalid register name: `$name`', node.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
// return ast.void_type
|
2021-03-17 01:43:17 +01:00
|
|
|
// }
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
|
2020-12-16 17:50:39 +01:00
|
|
|
node.expr_type = c.expr(node.expr) // type to be casted
|
2020-11-02 01:17:35 +01:00
|
|
|
from_type_sym := c.table.get_type_symbol(node.expr_type)
|
2020-12-16 17:50:39 +01:00
|
|
|
to_type_sym := c.table.get_type_symbol(node.typ) // type to be used as cast
|
2021-03-04 11:28:48 +01:00
|
|
|
if to_type_sym.language != .c {
|
2021-03-06 20:04:51 +01:00
|
|
|
c.ensure_type_exists(node.typ, node.pos) or {}
|
2021-02-26 07:26:36 +01:00
|
|
|
}
|
2021-04-27 07:18:48 +02:00
|
|
|
n_e_t_idx := node.expr_type.idx()
|
|
|
|
expr_is_ptr := node.expr_type.is_ptr() || n_e_t_idx in ast.pointer_type_idxs
|
2020-11-02 01:17:35 +01:00
|
|
|
if expr_is_ptr && to_type_sym.kind == .string && !node.in_prexpr {
|
|
|
|
if node.has_arg {
|
|
|
|
c.warn('to convert a C string buffer pointer to a V string, please use x.vstring_with_len(len) instead of string(x,len)',
|
|
|
|
node.pos)
|
|
|
|
} else {
|
|
|
|
c.warn('to convert a C string buffer pointer to a V string, please use x.vstring() instead of string(x)',
|
|
|
|
node.pos)
|
|
|
|
}
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.expr_type == ast.byte_type && to_type_sym.kind == .string {
|
2020-11-02 01:17:35 +01:00
|
|
|
c.error('can not cast type `byte` to string, use `${node.expr.str()}.str()` instead.',
|
|
|
|
node.pos)
|
|
|
|
}
|
2020-11-25 12:09:40 +01:00
|
|
|
if to_type_sym.kind == .sum_type {
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.expr_type in [ast.int_literal_type, ast.float_literal_type] {
|
|
|
|
node.expr_type = c.promote_num(node.expr_type, if node.expr_type == ast.int_literal_type {
|
|
|
|
ast.int_type
|
2020-12-27 14:20:30 +01:00
|
|
|
} else {
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.f64_type
|
2020-12-27 14:20:30 +01:00
|
|
|
})
|
2020-11-02 01:17:35 +01:00
|
|
|
}
|
2021-04-02 16:34:48 +02:00
|
|
|
if !c.table.sumtype_has_variant(node.typ, node.expr_type) && !node.typ.has_flag(.optional) {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('cannot cast `$from_type_sym.name` to `$to_type_sym.name`', node.pos)
|
2020-11-02 01:17:35 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if mut to_type_sym.info is ast.Alias {
|
2020-11-24 13:58:29 +01:00
|
|
|
if !c.check_types(node.expr_type, to_type_sym.info.parent_type) {
|
|
|
|
parent_type_sym := c.table.get_type_symbol(to_type_sym.info.parent_type)
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('cannot convert type `$from_type_sym.name` to `$to_type_sym.name` (alias to `$parent_type_sym.name`)',
|
2020-11-02 01:17:35 +01:00
|
|
|
node.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if node.typ == ast.string_type
|
2021-02-07 23:10:39 +01:00
|
|
|
&& (from_type_sym.kind in [.int_literal, .int, .byte, .byteptr, .bool]
|
|
|
|
|| (from_type_sym.kind == .array && from_type_sym.name == 'array_byte')) {
|
2020-11-02 01:17:35 +01:00
|
|
|
type_name := c.table.type_to_str(node.expr_type)
|
|
|
|
c.error('cannot cast type `$type_name` to string, use `x.str()` instead', node.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if node.expr_type == ast.string_type {
|
2020-11-02 01:17:35 +01:00
|
|
|
if to_type_sym.kind != .alias {
|
|
|
|
mut error_msg := 'cannot cast a string'
|
2020-11-21 00:05:57 +01:00
|
|
|
if mut node.expr is ast.StringLiteral {
|
|
|
|
if node.expr.val.len == 1 {
|
|
|
|
error_msg += ", for denoting characters use `$node.expr.val` instead of '$node.expr.val'"
|
2020-11-02 01:17:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
c.error(error_msg, node.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if to_type_sym.kind == .byte && node.expr_type != ast.voidptr_type
|
2021-01-23 09:33:22 +01:00
|
|
|
&& from_type_sym.kind != .enum_ && !node.expr_type.is_int() && !node.expr_type.is_float()
|
2021-04-11 12:24:24 +02:00
|
|
|
&& node.expr_type != ast.bool_type && !node.expr_type.is_ptr() {
|
2020-11-02 01:17:35 +01:00
|
|
|
type_name := c.table.type_to_str(node.expr_type)
|
|
|
|
c.error('cannot cast type `$type_name` to `byte`', node.pos)
|
2021-01-23 09:33:22 +01:00
|
|
|
} else if to_type_sym.kind == .struct_ && !node.typ.is_ptr()
|
2021-04-02 00:57:09 +02:00
|
|
|
&& !(to_type_sym.info as ast.Struct).is_typedef {
|
2020-11-02 01:17:35 +01:00
|
|
|
// For now we ignore C typedef because of `C.Window(C.None)` in vlib/clipboard
|
|
|
|
if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() {
|
2021-02-01 20:08:25 +01:00
|
|
|
c.warn('casting to struct is deprecated, use e.g. `Struct{...expr}` instead',
|
|
|
|
node.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
from_type_info := from_type_sym.info as ast.Struct
|
|
|
|
to_type_info := to_type_sym.info as ast.Struct
|
2020-11-02 01:17:35 +01:00
|
|
|
if !c.check_struct_signature(from_type_info, to_type_info) {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('cannot convert struct `$from_type_sym.name` to struct `$to_type_sym.name`',
|
2020-11-02 01:17:35 +01:00
|
|
|
node.pos)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
type_name := c.table.type_to_str(node.expr_type)
|
|
|
|
c.error('cannot cast `$type_name` to struct', node.pos)
|
|
|
|
}
|
2021-01-29 00:41:59 +01:00
|
|
|
} else if to_type_sym.kind == .interface_ {
|
|
|
|
c.type_implements(node.expr_type, node.typ, node.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if node.typ == ast.bool_type {
|
2020-11-02 01:17:35 +01:00
|
|
|
c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos)
|
2021-04-02 16:34:48 +02:00
|
|
|
} else if node.expr_type == ast.none_type && !node.typ.has_flag(.optional) {
|
2020-11-02 01:17:35 +01:00
|
|
|
type_name := c.table.type_to_str(node.typ)
|
|
|
|
c.error('cannot cast `none` to `$type_name`', node.pos)
|
2021-01-15 01:20:58 +01:00
|
|
|
} else if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() {
|
|
|
|
if (node.typ.is_ptr() || to_type_sym.kind !in [.sum_type, .interface_]) && !c.is_builtin_mod {
|
|
|
|
type_name := c.table.type_to_str(node.typ)
|
|
|
|
c.error('cannot cast struct to `$type_name`', node.pos)
|
|
|
|
}
|
2020-12-16 17:50:39 +01:00
|
|
|
} else if node.expr_type.has_flag(.optional) || node.expr_type.has_flag(.variadic) {
|
|
|
|
// variadic case can happen when arrays are converted into variadic
|
|
|
|
msg := if node.expr_type.has_flag(.optional) { 'an optional' } else { 'a variadic' }
|
|
|
|
c.error('cannot type cast $msg', node.pos)
|
2021-04-04 16:43:32 +02:00
|
|
|
} else if !c.inside_unsafe && node.typ.is_ptr() && node.expr_type.is_ptr()
|
|
|
|
&& node.typ.deref() != ast.char_type && node.expr_type.deref() != ast.char_type {
|
2021-01-05 16:02:04 +01:00
|
|
|
ft := c.table.type_to_str(node.expr_type)
|
|
|
|
tt := c.table.type_to_str(node.typ)
|
2021-01-05 19:26:48 +01:00
|
|
|
c.warn('casting `$ft` to `$tt` is only allowed in `unsafe` code', node.pos)
|
2021-02-17 20:45:11 +01:00
|
|
|
} else if from_type_sym.kind == .array_fixed && !node.expr_type.is_ptr() {
|
|
|
|
c.warn('cannot cast a fixed array (use e.g. `&arr[0]` instead)', node.pos)
|
2020-11-02 01:17:35 +01:00
|
|
|
}
|
|
|
|
if node.has_arg {
|
|
|
|
c.expr(node.arg)
|
|
|
|
}
|
|
|
|
node.typname = c.table.get_type_symbol(node.typ).name
|
|
|
|
return node.typ
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
|
2021-01-17 06:24:03 +01:00
|
|
|
node.sym = c.table.get_type_symbol(c.unwrap_generic(c.expr(node.left)))
|
2021-01-31 18:22:42 +01:00
|
|
|
if node.is_env {
|
|
|
|
env_value := util.resolve_env_value("\$env('$node.args_var')", false) or {
|
2021-02-28 21:20:21 +01:00
|
|
|
c.error(err.msg, node.env_pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2021-01-31 18:22:42 +01:00
|
|
|
}
|
|
|
|
node.env_value = env_value
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2021-01-31 18:22:42 +01:00
|
|
|
}
|
2021-01-17 06:24:03 +01:00
|
|
|
if node.is_embed {
|
|
|
|
c.file.embedded_files << node.embed_file
|
|
|
|
return c.table.find_type_idx('v.embed_file.EmbedFileData')
|
|
|
|
}
|
|
|
|
if node.is_vweb {
|
|
|
|
// TODO assoc parser bug
|
2021-01-22 23:24:48 +01:00
|
|
|
pref_ := *c.pref
|
2021-02-13 00:47:37 +01:00
|
|
|
pref2 := &pref.Preferences{
|
2021-01-22 23:24:48 +01:00
|
|
|
...pref_
|
2021-01-17 06:24:03 +01:00
|
|
|
is_vweb: true
|
|
|
|
}
|
2021-02-05 08:27:14 +01:00
|
|
|
mut c2 := new_checker(c.table, pref2)
|
2021-01-17 06:24:03 +01:00
|
|
|
c2.check(node.vweb_tmpl)
|
2021-01-21 11:09:19 +01:00
|
|
|
mut i := 0 // tmp counter var for skipping first three tmpl vars
|
|
|
|
for k, _ in c2.file.scope.children[0].objects {
|
2021-03-15 12:35:55 +01:00
|
|
|
if i < 2 {
|
2021-01-21 11:09:19 +01:00
|
|
|
// Skip first three because they are tmpl vars see vlib/vweb/tmpl/tmpl.v
|
|
|
|
i++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if k in c.fn_scope.objects && c.fn_scope.objects[k] is ast.Var {
|
|
|
|
mut vsc := c.fn_scope.objects[k] as ast.Var
|
|
|
|
vsc.is_used = true
|
|
|
|
c.fn_scope.objects[k] = vsc
|
|
|
|
}
|
|
|
|
}
|
2021-01-17 06:24:03 +01:00
|
|
|
c.warnings << c2.warnings
|
|
|
|
c.errors << c2.errors
|
2021-03-22 18:43:06 +01:00
|
|
|
c.notices << c2.notices
|
2021-01-17 06:24:03 +01:00
|
|
|
c.nr_warnings += c2.nr_warnings
|
|
|
|
c.nr_errors += c2.nr_errors
|
2021-03-22 18:43:06 +01:00
|
|
|
c.nr_notices += c2.nr_notices
|
2021-01-17 06:24:03 +01:00
|
|
|
}
|
|
|
|
if node.method_name == 'html' {
|
2021-01-30 15:24:16 +01:00
|
|
|
rtyp := c.table.find_type_idx('vweb.Result')
|
|
|
|
node.result_type = rtyp
|
|
|
|
return rtyp
|
2021-01-17 06:24:03 +01:00
|
|
|
}
|
2021-01-31 15:28:23 +01:00
|
|
|
if node.method_name == 'method' {
|
2021-03-11 14:04:34 +01:00
|
|
|
for i, arg in node.args {
|
|
|
|
// check each arg expression
|
|
|
|
node.args[i].typ = c.expr(arg.expr)
|
2021-01-31 15:28:23 +01:00
|
|
|
}
|
|
|
|
// assume string for now
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2021-01-31 15:28:23 +01:00
|
|
|
}
|
|
|
|
if node.is_vweb {
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2021-01-30 15:24:16 +01:00
|
|
|
}
|
|
|
|
// s.$my_str()
|
|
|
|
v := node.scope.find_var(node.method_name) or {
|
|
|
|
c.error('unknown identifier `$node.method_name`', node.method_pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2021-01-30 15:24:16 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if v.typ != ast.string_type {
|
|
|
|
s := c.expected_msg(v.typ, ast.string_type)
|
2021-01-30 15:24:16 +01:00
|
|
|
c.error('invalid string method call: $s', node.method_pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2021-01-21 20:36:11 +01:00
|
|
|
}
|
2021-01-30 15:24:16 +01:00
|
|
|
// note: we should use a compile-time evaluation function rather than handle here
|
|
|
|
// mut variables will not work after init
|
|
|
|
mut method_name := ''
|
|
|
|
if v.expr is ast.StringLiteral {
|
|
|
|
method_name = v.expr.val
|
|
|
|
} else {
|
|
|
|
c.error('todo: not a string literal', node.method_pos)
|
|
|
|
}
|
|
|
|
f := node.sym.find_method(method_name) or {
|
|
|
|
c.error('could not find method `$method_name`', node.method_pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2021-01-30 15:24:16 +01:00
|
|
|
}
|
2021-01-30 16:53:31 +01:00
|
|
|
// println(f.name + ' ' + c.table.type_to_str(f.return_type))
|
2021-01-30 15:24:16 +01:00
|
|
|
node.result_type = f.return_type
|
|
|
|
return f.return_type
|
2021-01-17 06:24:03 +01:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) at_expr(mut node ast.AtExpr) ast.Type {
|
2020-11-05 09:12:32 +01:00
|
|
|
match node.kind {
|
|
|
|
.fn_name {
|
|
|
|
node.val = c.cur_fn.name.all_after_last('.')
|
|
|
|
}
|
2021-02-05 15:30:58 +01:00
|
|
|
.method_name {
|
|
|
|
fname := c.cur_fn.name.all_after_last('.')
|
|
|
|
if c.cur_fn.is_method {
|
|
|
|
node.val = c.table.type_to_str(c.cur_fn.receiver.typ).all_after_last('.') + '.' +
|
|
|
|
fname
|
|
|
|
} else {
|
|
|
|
node.val = fname
|
|
|
|
}
|
|
|
|
}
|
2020-11-05 09:12:32 +01:00
|
|
|
.mod_name {
|
|
|
|
node.val = c.cur_fn.mod
|
|
|
|
}
|
|
|
|
.struct_name {
|
|
|
|
if c.cur_fn.is_method {
|
|
|
|
node.val = c.table.type_to_str(c.cur_fn.receiver.typ).all_after_last('.')
|
|
|
|
} else {
|
|
|
|
node.val = ''
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.vexe_path {
|
|
|
|
node.val = pref.vexe_path()
|
|
|
|
}
|
|
|
|
.file_path {
|
|
|
|
node.val = os.real_path(c.file.path)
|
|
|
|
}
|
|
|
|
.line_nr {
|
|
|
|
node.val = (node.pos.line_nr + 1).str()
|
|
|
|
}
|
|
|
|
.column_nr {
|
2021-03-23 06:23:46 +01:00
|
|
|
node.val = (node.pos.col + 1).str()
|
2020-11-05 09:12:32 +01:00
|
|
|
}
|
|
|
|
.vhash {
|
|
|
|
node.val = util.vhash()
|
|
|
|
}
|
|
|
|
.vmod_file {
|
2021-04-19 18:01:47 +02:00
|
|
|
// cache the vmod content, do not read it many times
|
2020-11-05 09:12:32 +01:00
|
|
|
if c.vmod_file_content.len == 0 {
|
|
|
|
mut mcache := vmod.get_cache()
|
|
|
|
vmod_file_location := mcache.get_by_file(c.file.path)
|
|
|
|
if vmod_file_location.vmod_file.len == 0 {
|
|
|
|
c.error('@VMOD_FILE can be used only in projects, that have v.mod file',
|
|
|
|
node.pos)
|
|
|
|
}
|
2020-12-04 12:25:23 +01:00
|
|
|
vmod_content := os.read_file(vmod_file_location.vmod_file) or { '' }
|
2021-04-19 18:01:47 +02:00
|
|
|
c.vmod_file_content = vmod_content.replace('\r\n', '\n') // normalise EOLs just in case
|
2020-11-05 09:12:32 +01:00
|
|
|
}
|
|
|
|
node.val = c.vmod_file_content
|
|
|
|
}
|
2021-04-19 18:01:47 +02:00
|
|
|
.vroot_path {
|
|
|
|
node.val = os.dir(pref.vexe_path())
|
|
|
|
}
|
|
|
|
.vexeroot_path {
|
|
|
|
node.val = os.dir(pref.vexe_path())
|
|
|
|
}
|
|
|
|
.vmodroot_path {
|
|
|
|
mut mcache := vmod.get_cache()
|
|
|
|
vmod_file_location := mcache.get_by_file(c.file.path)
|
|
|
|
node.val = os.dir(vmod_file_location.vmod_file)
|
|
|
|
}
|
2020-11-05 11:59:49 +01:00
|
|
|
.unknown {
|
|
|
|
c.error('unknown @ identifier: ${node.name}. Available identifiers: $token.valid_at_tokens',
|
|
|
|
node.pos)
|
2020-11-05 09:12:32 +01:00
|
|
|
}
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.string_type
|
2020-11-05 09:12:32 +01:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) ident(mut ident ast.Ident) ast.Type {
|
2020-04-04 05:14:40 +02:00
|
|
|
// TODO: move this
|
|
|
|
if c.const_deps.len > 0 {
|
|
|
|
mut name := ident.name
|
2020-07-19 19:58:34 +02:00
|
|
|
if !name.contains('.') && ident.mod != 'builtin' {
|
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)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-04-04 05:14:40 +02:00
|
|
|
}
|
|
|
|
c.const_deps << name
|
|
|
|
}
|
|
|
|
if ident.kind == .blank_ident {
|
2020-09-18 23:47:50 +02:00
|
|
|
if ident.tok_kind !in [.assign, .decl_assign] {
|
|
|
|
c.error('undefined ident: `_` (may only be used in assignments)', ident.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-04-04 05:14:40 +02:00
|
|
|
}
|
|
|
|
// second use
|
2020-06-10 08:59:31 +02:00
|
|
|
if ident.kind in [.constant, .global, .variable] {
|
2020-02-17 12:25:18 +01:00
|
|
|
info := ident.info as ast.IdentVar
|
2020-05-28 05:50:57 +02:00
|
|
|
// Got a var with type T, return current generic type
|
2020-04-04 05:14:40 +02:00
|
|
|
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-08-28 19:07:32 +02:00
|
|
|
if ident.tok_kind == .assign && ident.is_mut {
|
|
|
|
c.error('`mut` not allowed with `=` (use `:=` to declare a variable)', ident.pos)
|
|
|
|
}
|
2020-12-12 09:01:12 +01:00
|
|
|
if obj := ident.scope.find(ident.name) {
|
2020-11-25 12:09:40 +01:00
|
|
|
match mut obj {
|
2020-10-03 07:03:44 +02:00
|
|
|
ast.GlobalField {
|
2020-06-09 09:08:11 +02:00
|
|
|
ident.kind = .global
|
|
|
|
ident.info = ast.IdentVar{
|
2020-06-18 20:38:59 +02:00
|
|
|
typ: obj.typ
|
2020-06-09 09:08:11 +02:00
|
|
|
}
|
2020-11-24 13:58:29 +01:00
|
|
|
ident.obj = obj
|
2020-06-18 20:38:59 +02:00
|
|
|
return obj.typ
|
2020-06-09 09:08:11 +02:00
|
|
|
}
|
2020-04-04 13:32:16 +02:00
|
|
|
ast.Var {
|
2020-06-20 04:42:08 +02:00
|
|
|
// incase var was not marked as used yet (vweb tmpl)
|
2021-04-23 12:33:48 +02:00
|
|
|
// obj.is_used = true
|
2020-06-20 04:42:08 +02:00
|
|
|
if ident.pos.pos < obj.pos.pos {
|
2020-06-24 14:44:06 +02:00
|
|
|
c.error('undefined variable `$ident.name` (used before declaration)',
|
|
|
|
ident.pos)
|
2020-06-20 04:42:08 +02:00
|
|
|
}
|
2021-04-09 10:00:05 +02:00
|
|
|
is_sum_type_cast := obj.smartcasts.len != 0
|
2021-01-23 09:33:22 +01:00
|
|
|
&& !c.prevent_sum_type_unwrapping_once
|
2020-11-11 09:18:15 +01:00
|
|
|
c.prevent_sum_type_unwrapping_once = false
|
2021-04-09 10:00:05 +02:00
|
|
|
mut typ := if is_sum_type_cast { obj.smartcasts.last() } else { obj.typ }
|
2020-04-04 13:32:16 +02:00
|
|
|
if typ == 0 {
|
2020-11-21 00:05:57 +01:00
|
|
|
if mut obj.expr is ast.Ident {
|
|
|
|
if obj.expr.kind == .unresolved {
|
2020-05-24 19:55:04 +02:00
|
|
|
c.error('unresolved variable: `$ident.name`', ident.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-05-24 19:55:04 +02:00
|
|
|
}
|
|
|
|
}
|
2020-11-21 22:40:05 +01:00
|
|
|
if mut obj.expr is ast.IfGuardExpr {
|
|
|
|
// new variable from if guard shouldn't have the optional flag for further use
|
|
|
|
// a temp variable will be generated which unwraps it
|
|
|
|
if_guard_var_type := c.expr(obj.expr.expr)
|
|
|
|
typ = if_guard_var_type.clear_flag(.optional)
|
|
|
|
} else {
|
|
|
|
typ = c.expr(obj.expr)
|
|
|
|
}
|
2020-04-04 13:32:16 +02:00
|
|
|
}
|
2020-06-04 14:38:54 +02:00
|
|
|
is_optional := typ.has_flag(.optional)
|
2020-05-10 16:13:30 +02:00
|
|
|
ident.kind = .variable
|
|
|
|
ident.info = ast.IdentVar{
|
|
|
|
typ: typ
|
|
|
|
is_optional: is_optional
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if typ == ast.error_type && c.expected_type == ast.string_type
|
2021-02-28 20:24:29 +01:00
|
|
|
&& !c.using_new_err_struct && !c.inside_selector_expr
|
2021-03-23 09:38:56 +01:00
|
|
|
&& !c.inside_println_arg && !c.file.mod.name.contains('v.')
|
|
|
|
&& !c.is_builtin_mod {
|
2021-02-28 20:24:29 +01:00
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <- TODO: remove; this prevents a failure in the `performance-regressions` CI job
|
|
|
|
c.warn('string errors are deprecated; use `err.msg` instead',
|
|
|
|
ident.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
// if typ == ast.t_type {
|
2020-05-25 05:32:33 +02:00
|
|
|
// sym := c.table.get_type_symbol(c.cur_generic_type)
|
2020-12-06 04:55:08 +01:00
|
|
|
// println('IDENT T unresolved $ident.name typ=$sym.name')
|
2020-05-25 05:32:33 +02:00
|
|
|
// 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-11-11 09:18:15 +01:00
|
|
|
if !is_sum_type_cast {
|
|
|
|
obj.typ = typ
|
|
|
|
}
|
2020-11-24 13:58:29 +01:00
|
|
|
ident.obj = obj
|
2020-05-10 16:13:30 +02:00
|
|
|
// unwrap optional (`println(x)`)
|
|
|
|
if is_optional {
|
2020-06-04 14:38:54 +02:00
|
|
|
return typ.clear_flag(.optional)
|
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
|
|
|
mut name := ident.name
|
2020-12-07 18:13:03 +01:00
|
|
|
// check for imported symbol
|
|
|
|
if name in c.file.imported_symbols {
|
|
|
|
name = c.file.imported_symbols[name]
|
|
|
|
}
|
|
|
|
// prepend mod to look for fn call or const
|
|
|
|
else if !name.contains('.') && ident.mod != 'builtin' {
|
2020-05-10 16:13:30 +02:00
|
|
|
name = '${ident.mod}.$ident.name'
|
|
|
|
}
|
2020-11-24 13:58:29 +01:00
|
|
|
if obj := c.file.global_scope.find(name) {
|
2020-11-25 12:09:40 +01:00
|
|
|
match mut obj {
|
2020-04-04 05:14:40 +02:00
|
|
|
ast.ConstField {
|
2021-02-03 09:17:13 +01:00
|
|
|
if !(obj.is_pub || obj.mod == c.mod || c.pref.is_test) {
|
|
|
|
c.error('constant `$obj.name` is private', ident.pos)
|
|
|
|
}
|
2020-06-18 20:38:59 +02:00
|
|
|
mut typ := obj.typ
|
2020-04-04 05:14:40 +02:00
|
|
|
if typ == 0 {
|
2021-01-30 16:53:31 +01:00
|
|
|
c.inside_const = true
|
2020-06-18 20:38:59 +02:00
|
|
|
typ = c.expr(obj.expr)
|
2021-01-30 16:53:31 +01:00
|
|
|
c.inside_const = false
|
2021-01-30 11:46:36 +01:00
|
|
|
if obj.expr is ast.CallExpr {
|
|
|
|
if obj.expr.or_block.kind != .absent {
|
|
|
|
typ = typ.clear_flag(.optional)
|
|
|
|
}
|
|
|
|
}
|
2020-04-04 05:14:40 +02:00
|
|
|
}
|
|
|
|
ident.name = name
|
|
|
|
ident.kind = .constant
|
|
|
|
ident.info = ast.IdentVar{
|
|
|
|
typ: typ
|
|
|
|
}
|
2020-06-18 20:38:59 +02:00
|
|
|
obj.typ = typ
|
2020-11-24 13:58:29 +01:00
|
|
|
ident.obj = obj
|
2020-04-04 05:14:40 +02:00
|
|
|
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) {
|
2021-04-02 00:57:09 +02:00
|
|
|
fn_type := ast.new_type(c.table.find_or_register_fn_type(ident.mod, func,
|
2020-06-24 14:44:06 +02:00
|
|
|
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-12-12 10:42:07 +01:00
|
|
|
if ident.name == 'C.NULL' {
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.voidptr_type
|
2020-12-12 10:42:07 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.int_type
|
2020-02-18 18:13:34 +01:00
|
|
|
}
|
2020-09-18 23:47:50 +02:00
|
|
|
if c.inside_sql {
|
2021-01-23 07:57:17 +01:00
|
|
|
if field := c.table.find_field(c.cur_orm_ts, ident.name) {
|
2020-09-18 23:47:50 +02:00
|
|
|
return field.typ
|
2020-07-01 00:53:53 +02:00
|
|
|
}
|
2020-09-18 23:47:50 +02:00
|
|
|
}
|
|
|
|
if ident.kind == .unresolved && ident.mod != 'builtin' {
|
|
|
|
// search in the `builtin` idents, for example
|
|
|
|
// main.compare_f32 may actually be builtin.compare_f32
|
|
|
|
saved_mod := ident.mod
|
|
|
|
ident.mod = 'builtin'
|
2020-09-27 03:14:24 +02:00
|
|
|
builtin_type := c.ident(mut ident)
|
2021-04-02 00:57:09 +02:00
|
|
|
if builtin_type != ast.void_type {
|
2020-09-18 23:47:50 +02:00
|
|
|
return builtin_type
|
2020-07-21 18:49:53 +02:00
|
|
|
}
|
2020-09-18 23:47:50 +02:00
|
|
|
ident.mod = saved_mod
|
|
|
|
}
|
|
|
|
if ident.tok_kind == .assign {
|
|
|
|
c.error('undefined ident: `$ident.name` (use `:=` to declare a variable)', ident.pos)
|
2021-02-28 20:24:29 +01:00
|
|
|
} else if ident.name == 'errcode' {
|
|
|
|
c.error('undefined ident: `errcode`; did you mean `err.code`?', ident.pos)
|
2020-09-18 23:47:50 +02:00
|
|
|
} else {
|
|
|
|
c.error('undefined ident: `$ident.name`', ident.pos)
|
2020-05-01 12:22:39 +02:00
|
|
|
}
|
2020-05-01 12:02:31 +02:00
|
|
|
if c.table.known_type(ident.name) {
|
|
|
|
// e.g. `User` in `json.decode(User, '...')`
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-05-01 12:02:31 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-02-17 12:25:18 +01:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) concat_expr(mut concat_expr ast.ConcatExpr) ast.Type {
|
|
|
|
mut mr_types := []ast.Type{}
|
2020-05-15 23:14:53 +02:00
|
|
|
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)
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.new_type(typ)
|
2020-05-15 23:14:53 +02:00
|
|
|
concat_expr.return_type = typ
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
|
|
|
|
node.is_expr = c.expected_type != ast.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)
|
2020-08-17 20:19:21 +02:00
|
|
|
// we setting this here rather than at the end of the method
|
|
|
|
// since it is used in c.match_exprs() it saves checking twice
|
2021-02-19 10:23:13 +01:00
|
|
|
node.cond_type = c.table.mktyp(cond_type)
|
2021-04-02 00:57:09 +02:00
|
|
|
c.ensure_type_exists(node.cond_type, node.pos) or { return ast.void_type }
|
2020-05-31 10:22:18 +02:00
|
|
|
cond_type_sym := c.table.get_type_symbol(cond_type)
|
2020-11-25 12:09:40 +01:00
|
|
|
if cond_type_sym.kind !in [.interface_, .sum_type] {
|
2020-04-15 23:32:56 +02:00
|
|
|
node.is_sum_type = false
|
|
|
|
}
|
2020-05-31 10:22:18 +02:00
|
|
|
c.match_exprs(mut node, cond_type_sym)
|
2020-03-18 12:23:32 +01:00
|
|
|
c.expected_type = cond_type
|
2021-04-02 00:57:09 +02:00
|
|
|
mut ret_type := ast.void_type
|
2020-11-11 16:23:57 +01:00
|
|
|
mut nbranches_with_return := 0
|
|
|
|
mut nbranches_without_return := 0
|
2020-03-04 11:59:45 +01:00
|
|
|
for branch in node.branches {
|
|
|
|
c.stmts(branch.stmts)
|
2020-10-10 15:42:40 +02:00
|
|
|
if node.is_expr && branch.stmts.len > 0 {
|
2020-10-02 09:33:04 +02:00
|
|
|
// ignore last statement - workaround
|
|
|
|
// currently the last statement in a match branch does not have an
|
|
|
|
// expected value set, so e.g. IfExpr.is_expr is not set.
|
|
|
|
// probably any mismatch will be caught by not producing a value instead
|
|
|
|
for st in branch.stmts[0..branch.stmts.len - 1] {
|
2020-10-10 15:42:40 +02:00
|
|
|
// must not contain C statements
|
2021-03-13 18:13:50 +01:00
|
|
|
st.check_c_expr() or { c.error('`match` expression branch has $err.msg', st.pos) }
|
2020-09-30 16:06:22 +02:00
|
|
|
}
|
|
|
|
}
|
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 {
|
2020-11-24 13:58:29 +01:00
|
|
|
mut stmt := branch.stmts[branch.stmts.len - 1]
|
2020-11-25 12:09:40 +01:00
|
|
|
match mut stmt {
|
2020-03-03 17:29:16 +01:00
|
|
|
ast.ExprStmt {
|
2020-12-05 15:26:28 +01:00
|
|
|
expr_type := c.expr(stmt.expr)
|
2021-04-02 00:57:09 +02:00
|
|
|
if ret_type == ast.void_type {
|
2020-12-05 15:26:28 +01:00
|
|
|
ret_type = expr_type
|
2020-12-03 20:14:23 +01:00
|
|
|
stmt.typ = ret_type
|
2020-12-05 15:26:28 +01:00
|
|
|
} else if node.is_expr && ret_type != expr_type {
|
2021-02-01 19:57:38 +01:00
|
|
|
if !c.check_types(ret_type, expr_type) {
|
|
|
|
ret_sym := c.table.get_type_symbol(ret_type)
|
|
|
|
c.error('return type mismatch, it should be `$ret_sym.name`',
|
|
|
|
stmt.expr.position())
|
|
|
|
}
|
2020-12-03 20:14:23 +01:00
|
|
|
}
|
2020-03-03 17:29:16 +01:00
|
|
|
}
|
2020-04-07 15:15:45 +02:00
|
|
|
else {
|
|
|
|
// TODO: ask alex about this
|
2020-07-04 11:23:41 +02:00
|
|
|
// typ := c.expr(stmt.expr)
|
2020-04-07 15:15:45 +02:00
|
|
|
// type_sym := c.table.get_type_symbol(typ)
|
2020-12-06 04:55:08 +01:00
|
|
|
// p.warn('match expr ret $type_sym.name')
|
2020-04-07 15:15:45 +02:00
|
|
|
// node.typ = typ
|
|
|
|
// return typ
|
|
|
|
}
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
2020-07-09 22:38:43 +02:00
|
|
|
if has_return := c.has_return(branch.stmts) {
|
|
|
|
if has_return {
|
2020-11-11 16:23:57 +01:00
|
|
|
nbranches_with_return++
|
2020-07-09 22:38:43 +02:00
|
|
|
} else {
|
2020-11-11 16:23:57 +01:00
|
|
|
nbranches_without_return++
|
2020-07-09 22:38:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-11 16:23:57 +01:00
|
|
|
if nbranches_with_return > 0 {
|
|
|
|
if nbranches_with_return == node.branches.len {
|
|
|
|
// an exhaustive match, and all branches returned
|
|
|
|
c.returns = true
|
|
|
|
}
|
|
|
|
if nbranches_without_return > 0 {
|
|
|
|
// some of the branches did not return
|
|
|
|
c.returns = false
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
// if ret_type != ast.void_type {
|
|
|
|
// node.is_expr = c.expected_type != ast.void_type
|
2020-03-18 08:41:49 +01:00
|
|
|
// node.expected_type = c.expected_type
|
|
|
|
// }
|
|
|
|
node.return_type = ret_type
|
2021-04-25 20:40:38 +02:00
|
|
|
cond_var := c.get_base_name(&node.cond)
|
|
|
|
if cond_var != '' {
|
|
|
|
mut cond_is_auto_heap := false
|
|
|
|
for branch in node.branches {
|
|
|
|
if v := branch.scope.find_var(cond_var) {
|
|
|
|
if v.is_auto_heap {
|
|
|
|
cond_is_auto_heap = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if cond_is_auto_heap {
|
|
|
|
for branch in node.branches {
|
|
|
|
mut v := branch.scope.find_var(cond_var) or { continue }
|
|
|
|
v.is_auto_heap = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-03 17:29:16 +01:00
|
|
|
return ret_type
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.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-12-12 09:01:12 +01:00
|
|
|
for branch_i, _ in node.branches {
|
|
|
|
mut branch := node.branches[branch_i]
|
2021-04-02 00:57:09 +02:00
|
|
|
mut expr_types := []ast.TypeNode{}
|
2020-04-24 16:04:39 +02:00
|
|
|
for expr in branch.exprs {
|
|
|
|
mut key := ''
|
2020-07-16 15:41:18 +02:00
|
|
|
if expr is ast.RangeExpr {
|
2021-03-01 00:18:02 +01:00
|
|
|
mut low := i64(0)
|
|
|
|
mut high := i64(0)
|
2020-07-16 15:41:18 +02:00
|
|
|
c.expected_type = node.expected_type
|
2020-07-16 19:40:14 +02:00
|
|
|
low_expr := expr.low
|
|
|
|
high_expr := expr.high
|
|
|
|
if low_expr is ast.IntegerLiteral {
|
|
|
|
if high_expr is ast.IntegerLiteral {
|
2021-03-01 00:18:02 +01:00
|
|
|
low = low_expr.val.i64()
|
|
|
|
high = high_expr.val.i64()
|
2020-07-16 15:41:18 +02:00
|
|
|
} else {
|
|
|
|
c.error('mismatched range types', low_expr.pos)
|
|
|
|
}
|
2020-07-16 19:40:14 +02:00
|
|
|
} else if low_expr is ast.CharLiteral {
|
|
|
|
if high_expr is ast.CharLiteral {
|
2020-07-16 15:41:18 +02:00
|
|
|
low = low_expr.val[0]
|
|
|
|
high = high_expr.val[0]
|
|
|
|
} else {
|
|
|
|
c.error('mismatched range types', low_expr.pos)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
typ := c.table.type_to_str(c.expr(expr.low))
|
|
|
|
c.error('cannot use type `$typ` in match range', branch.pos)
|
|
|
|
}
|
2021-03-01 00:18:02 +01:00
|
|
|
high_low_cutoff := 1000
|
|
|
|
if high - low > high_low_cutoff {
|
|
|
|
c.warn('more than $high_low_cutoff possibilities ($low ... $high) in match range',
|
|
|
|
branch.pos)
|
|
|
|
}
|
2020-07-17 19:13:22 +02:00
|
|
|
for i in low .. high + 1 {
|
2020-07-16 15:41:18 +02:00
|
|
|
key = i.str()
|
|
|
|
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
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2020-11-25 12:09:40 +01:00
|
|
|
match expr {
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.TypeNode {
|
2020-11-11 09:18:15 +01:00
|
|
|
key = c.table.type_to_str(expr.typ)
|
2020-11-18 20:52:00 +01:00
|
|
|
expr_types << expr
|
2020-11-11 09:18:15 +01:00
|
|
|
}
|
|
|
|
ast.EnumVal {
|
|
|
|
key = expr.val
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
key = expr.str()
|
|
|
|
}
|
2020-04-24 16:04:39 +02:00
|
|
|
}
|
2020-06-25 17:12:32 +02:00
|
|
|
val := if key in branch_exprs { branch_exprs[key] } else { 0 }
|
2020-04-24 16:04:39 +02:00
|
|
|
if val == 1 {
|
|
|
|
c.error('match case `$key` is handled more than once', branch.pos)
|
|
|
|
}
|
2020-08-17 20:19:21 +02:00
|
|
|
c.expected_type = node.cond_type
|
|
|
|
expr_type := c.expr(expr)
|
2020-12-12 13:52:22 +01:00
|
|
|
if expr_type.idx() == 0 {
|
|
|
|
// parser failed, stop checking
|
|
|
|
return
|
|
|
|
}
|
2020-09-18 01:01:05 +02:00
|
|
|
if cond_type_sym.kind == .interface_ {
|
2020-11-11 09:18:15 +01:00
|
|
|
// TODO
|
|
|
|
// This generates a memory issue with TCC
|
|
|
|
// Needs to be checked later when TCC errors are fixed
|
|
|
|
// Current solution is to move expr.position() to its own statement
|
|
|
|
// c.type_implements(expr_type, c.expected_type, expr.position())
|
|
|
|
expr_pos := expr.position()
|
|
|
|
c.type_implements(expr_type, c.expected_type, expr_pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if mut cond_type_sym.info is ast.SumType {
|
2020-11-24 13:58:29 +01:00
|
|
|
if expr_type !in cond_type_sym.info.variants {
|
2020-11-11 09:18:15 +01:00
|
|
|
expr_str := c.table.type_to_str(expr_type)
|
2020-12-05 15:26:28 +01:00
|
|
|
expect_str := c.table.type_to_str(node.cond_type)
|
2020-11-11 09:18:15 +01:00
|
|
|
c.error('`$expect_str` has no variant `$expr_str`', expr.position())
|
|
|
|
}
|
2020-12-05 15:26:28 +01:00
|
|
|
} else if !c.check_types(expr_type, node.cond_type) {
|
2020-08-17 20:19:21 +02:00
|
|
|
expr_str := c.table.type_to_str(expr_type)
|
2020-12-05 15:26:28 +01:00
|
|
|
expect_str := c.table.type_to_str(node.cond_type)
|
2020-11-11 09:18:15 +01:00
|
|
|
c.error('cannot match `$expr_str` with `$expect_str` condition', expr.position())
|
2020-08-17 20:19:21 +02:00
|
|
|
}
|
2020-04-24 16:04:39 +02:00
|
|
|
branch_exprs[key] = val + 1
|
|
|
|
}
|
2021-04-09 10:00:05 +02:00
|
|
|
// when match is type matching, then register smart cast for every branch
|
2020-11-18 20:52:00 +01:00
|
|
|
if expr_types.len > 0 {
|
2021-04-09 10:00:05 +02:00
|
|
|
if cond_type_sym.kind in [.sum_type, .interface_] {
|
2021-04-02 00:57:09 +02:00
|
|
|
mut expr_type := ast.Type(0)
|
2020-11-18 20:52:00 +01:00
|
|
|
if expr_types.len > 1 {
|
|
|
|
mut agg_name := strings.new_builder(20)
|
2020-11-29 14:10:45 +01:00
|
|
|
mut agg_cname := strings.new_builder(20)
|
2021-02-22 12:18:11 +01:00
|
|
|
agg_name.write_string('(')
|
2020-11-18 20:52:00 +01:00
|
|
|
for i, expr in expr_types {
|
|
|
|
if i > 0 {
|
2021-02-22 12:18:11 +01:00
|
|
|
agg_name.write_string(' | ')
|
|
|
|
agg_cname.write_string('___')
|
2020-11-18 20:52:00 +01:00
|
|
|
}
|
|
|
|
type_str := c.table.type_to_str(expr.typ)
|
2020-11-29 14:10:45 +01:00
|
|
|
name := if c.is_builtin_mod { type_str } else { '${c.mod}.$type_str' }
|
2021-02-22 12:18:11 +01:00
|
|
|
agg_name.write_string(name)
|
|
|
|
agg_cname.write_string(util.no_dots(name))
|
2020-11-18 20:52:00 +01:00
|
|
|
}
|
2021-02-22 12:18:11 +01:00
|
|
|
agg_name.write_string(')')
|
2020-11-18 20:52:00 +01:00
|
|
|
name := agg_name.str()
|
2020-12-11 07:39:51 +01:00
|
|
|
existing_idx := c.table.type_idxs[name]
|
|
|
|
if existing_idx > 0 {
|
|
|
|
expr_type = existing_idx
|
|
|
|
} else {
|
2021-04-02 00:57:09 +02:00
|
|
|
expr_type = c.table.register_type_symbol(ast.TypeSymbol{
|
2020-12-11 07:39:51 +01:00
|
|
|
name: name
|
|
|
|
cname: agg_cname.str()
|
|
|
|
kind: .aggregate
|
|
|
|
mod: c.mod
|
2021-04-02 00:57:09 +02:00
|
|
|
info: ast.Aggregate{
|
2020-12-11 07:39:51 +01:00
|
|
|
types: expr_types.map(it.typ)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2020-11-18 20:52:00 +01:00
|
|
|
} else {
|
|
|
|
expr_type = expr_types[0].typ
|
|
|
|
}
|
2021-04-09 10:00:05 +02:00
|
|
|
c.smartcast(node.cond, node.cond_type, expr_type, mut branch.scope)
|
2020-11-18 20:52:00 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-24 16:04:39 +02:00
|
|
|
}
|
|
|
|
// 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{}
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.cond_type == ast.bool_type {
|
2021-01-01 12:28:23 +01:00
|
|
|
variants := ['true', 'false']
|
|
|
|
for v in variants {
|
|
|
|
if v !in branch_exprs {
|
|
|
|
is_exhaustive = false
|
|
|
|
unhandled << '`$v`'
|
2020-11-11 09:18:15 +01:00
|
|
|
}
|
|
|
|
}
|
2021-01-01 12:28:23 +01:00
|
|
|
} else {
|
|
|
|
match mut cond_type_sym.info {
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.SumType {
|
2021-01-01 12:28:23 +01:00
|
|
|
for v in cond_type_sym.info.variants {
|
|
|
|
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-10-15 22:12:59 +02:00
|
|
|
}
|
2021-01-01 12:28:23 +01:00
|
|
|
//
|
2021-04-02 00:57:09 +02:00
|
|
|
ast.Enum {
|
2021-01-01 12:28:23 +01:00
|
|
|
for v in cond_type_sym.info.vals {
|
|
|
|
if v !in branch_exprs {
|
|
|
|
is_exhaustive = false
|
|
|
|
unhandled << '`.$v`'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
is_exhaustive = false
|
|
|
|
}
|
2020-10-15 22:12:59 +02:00
|
|
|
}
|
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 {
|
2020-07-10 14:45:55 +02:00
|
|
|
err_details += ' (add match branches for: '
|
2021-03-15 05:22:22 +01:00
|
|
|
if unhandled.len < c.match_exhaustive_cutoff_limit {
|
2020-07-10 14:45:55 +02:00
|
|
|
err_details += unhandled.join(', ')
|
|
|
|
} else {
|
2021-03-15 05:22:22 +01:00
|
|
|
remaining := unhandled.len - c.match_exhaustive_cutoff_limit
|
|
|
|
err_details += unhandled[0..c.match_exhaustive_cutoff_limit].join(', ')
|
2020-07-14 17:18:10 +02:00
|
|
|
err_details += ', and $remaining others ...'
|
2020-07-10 14:45:55 +02:00
|
|
|
}
|
|
|
|
err_details += ' or `else {}` at the end)'
|
2020-04-25 21:51:44 +02:00
|
|
|
} else {
|
|
|
|
err_details += ' (add `else {}` at the end)'
|
|
|
|
}
|
|
|
|
c.error(err_details, node.pos)
|
2020-04-24 16:04:39 +02:00
|
|
|
}
|
|
|
|
|
2021-01-07 21:35:32 +01:00
|
|
|
// smartcast takes the expression with the current type which should be smartcasted to the target type in the given scope
|
2021-04-09 10:00:05 +02:00
|
|
|
fn (c Checker) smartcast(expr ast.Expr, cur_type ast.Type, to_type_ ast.Type, mut scope ast.Scope) {
|
|
|
|
sym := c.table.get_type_symbol(cur_type)
|
|
|
|
to_type := if sym.kind == .interface_ { to_type_.to_ptr() } else { to_type_ }
|
|
|
|
match expr {
|
2021-01-07 21:35:32 +01:00
|
|
|
ast.SelectorExpr {
|
|
|
|
mut is_mut := false
|
2021-04-09 10:00:05 +02:00
|
|
|
mut smartcasts := []ast.Type{}
|
2021-01-07 21:35:32 +01:00
|
|
|
expr_sym := c.table.get_type_symbol(expr.expr_type)
|
2021-01-21 21:31:25 +01:00
|
|
|
mut orig_type := 0
|
2021-01-23 07:57:17 +01:00
|
|
|
if field := c.table.find_field(expr_sym, expr.field_name) {
|
2021-01-07 21:35:32 +01:00
|
|
|
if field.is_mut {
|
2021-04-09 18:06:40 +02:00
|
|
|
if root_ident := expr.root_ident() {
|
|
|
|
if v := scope.find_var(root_ident.name) {
|
|
|
|
is_mut = v.is_mut
|
|
|
|
}
|
2021-01-07 21:35:32 +01:00
|
|
|
}
|
|
|
|
}
|
2021-01-21 21:31:25 +01:00
|
|
|
if orig_type == 0 {
|
|
|
|
orig_type = field.typ
|
|
|
|
}
|
2021-01-07 21:35:32 +01:00
|
|
|
}
|
|
|
|
if field := scope.find_struct_field(expr.expr_type, expr.field_name) {
|
2021-04-09 10:00:05 +02:00
|
|
|
smartcasts << field.smartcasts
|
2021-01-07 21:35:32 +01:00
|
|
|
}
|
|
|
|
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
|
|
|
if !is_mut || expr.is_mut {
|
2021-04-09 10:00:05 +02:00
|
|
|
smartcasts << to_type
|
2021-01-07 21:35:32 +01:00
|
|
|
scope.register_struct_field(ast.ScopeStructField{
|
|
|
|
struct_type: expr.expr_type
|
|
|
|
name: expr.field_name
|
|
|
|
typ: cur_type
|
2021-04-09 10:00:05 +02:00
|
|
|
smartcasts: smartcasts
|
2021-01-07 21:35:32 +01:00
|
|
|
pos: expr.pos
|
2021-01-21 21:31:25 +01:00
|
|
|
orig_type: orig_type
|
2021-01-07 21:35:32 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.Ident {
|
|
|
|
mut is_mut := false
|
2021-04-09 10:00:05 +02:00
|
|
|
mut smartcasts := []ast.Type{}
|
2021-01-07 21:35:32 +01:00
|
|
|
mut is_already_casted := false
|
2021-01-21 21:31:25 +01:00
|
|
|
mut orig_type := 0
|
|
|
|
if mut expr.obj is ast.Var {
|
|
|
|
is_mut = expr.obj.is_mut
|
2021-04-09 10:00:05 +02:00
|
|
|
smartcasts << expr.obj.smartcasts
|
2021-01-21 21:31:25 +01:00
|
|
|
is_already_casted = expr.obj.pos.pos == expr.pos.pos
|
|
|
|
if orig_type == 0 {
|
|
|
|
orig_type = expr.obj.typ
|
|
|
|
}
|
2021-01-07 21:35:32 +01:00
|
|
|
}
|
|
|
|
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
|
|
|
if (!is_mut || expr.is_mut) && !is_already_casted {
|
2021-04-09 10:00:05 +02:00
|
|
|
smartcasts << to_type
|
2021-01-07 21:35:32 +01:00
|
|
|
scope.register(ast.Var{
|
|
|
|
name: expr.name
|
|
|
|
typ: cur_type
|
|
|
|
pos: expr.pos
|
|
|
|
is_used: true
|
|
|
|
is_mut: expr.is_mut
|
2021-04-09 10:00:05 +02:00
|
|
|
smartcasts: smartcasts
|
2021-01-21 21:31:25 +01:00
|
|
|
orig_type: orig_type
|
2021-01-07 21:35:32 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) ast.Type {
|
|
|
|
node.is_expr = c.expected_type != ast.void_type
|
2020-09-19 02:14:35 +02:00
|
|
|
node.expected_type = c.expected_type
|
|
|
|
for branch in node.branches {
|
|
|
|
c.stmt(branch.stmt)
|
2020-11-25 12:09:40 +01:00
|
|
|
match branch.stmt {
|
2020-09-20 03:50:09 +02:00
|
|
|
ast.ExprStmt {
|
|
|
|
if branch.is_timeout {
|
2020-11-24 13:58:29 +01:00
|
|
|
if !branch.stmt.typ.is_int() {
|
|
|
|
tsym := c.table.get_type_symbol(branch.stmt.typ)
|
2020-09-20 03:50:09 +02:00
|
|
|
c.error('invalid type `$tsym.name` for timeout - expected integer type aka `time.Duration`',
|
2020-11-24 13:58:29 +01:00
|
|
|
branch.stmt.pos)
|
2020-09-20 03:50:09 +02:00
|
|
|
}
|
|
|
|
} else {
|
2020-11-24 13:58:29 +01:00
|
|
|
if branch.stmt.expr is ast.InfixExpr {
|
2021-01-23 09:33:22 +01:00
|
|
|
if branch.stmt.expr.left !is ast.Ident
|
|
|
|
&& branch.stmt.expr.left !is ast.SelectorExpr
|
|
|
|
&& branch.stmt.expr.left !is ast.IndexExpr {
|
2020-11-24 13:58:29 +01:00
|
|
|
c.error('channel in `select` key must be predefined', branch.stmt.expr.left.position())
|
2020-09-20 03:50:09 +02:00
|
|
|
}
|
|
|
|
} else {
|
2020-11-24 13:58:29 +01:00
|
|
|
c.error('invalid expression for `select` key', branch.stmt.expr.position())
|
2020-09-20 03:50:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.AssignStmt {
|
2020-11-24 13:58:29 +01:00
|
|
|
expr := branch.stmt.right[0]
|
2020-11-25 12:09:40 +01:00
|
|
|
match expr {
|
2020-09-20 03:50:09 +02:00
|
|
|
ast.PrefixExpr {
|
2021-01-23 09:33:22 +01:00
|
|
|
if expr.right !is ast.Ident && expr.right !is ast.SelectorExpr
|
|
|
|
&& expr.right !is ast.IndexExpr {
|
2020-09-20 03:50:09 +02:00
|
|
|
c.error('channel in `select` key must be predefined', expr.right.position())
|
|
|
|
}
|
|
|
|
if expr.or_block.kind != .absent {
|
2020-12-27 14:20:30 +01:00
|
|
|
err_prefix := if expr.or_block.kind == .block {
|
|
|
|
'or block'
|
|
|
|
} else {
|
|
|
|
'error propagation'
|
|
|
|
}
|
2020-09-20 03:50:09 +02:00
|
|
|
c.error('$err_prefix not allowed in `select` key', expr.or_block.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-11-24 13:58:29 +01:00
|
|
|
c.error('`<-` receive expression expected', branch.stmt.right[0].position())
|
2020-09-20 03:50:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if !branch.is_else {
|
2021-03-11 13:50:02 +01:00
|
|
|
c.error('receive or send statement expected as `select` key', branch.stmt.pos)
|
2020-09-20 03:50:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-19 02:14:35 +02:00
|
|
|
c.stmts(branch.stmts)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.bool_type
|
2020-09-19 02:14:35 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) ast.Type {
|
2021-02-03 15:16:52 +01:00
|
|
|
if c.rlocked_names.len > 0 || c.locked_names.len > 0 {
|
|
|
|
c.error('nested `lock`/`rlock` not allowed', node.pos)
|
|
|
|
}
|
2020-09-27 16:08:11 +02:00
|
|
|
for i in 0 .. node.lockeds.len {
|
2020-09-27 03:14:24 +02:00
|
|
|
c.ident(mut node.lockeds[i])
|
2020-09-27 15:32:46 +02:00
|
|
|
id := node.lockeds[i]
|
2020-11-24 13:58:29 +01:00
|
|
|
if mut id.obj is ast.Var {
|
|
|
|
if id.obj.typ.share() != .shared_t {
|
2020-07-13 12:19:28 +02:00
|
|
|
c.error('`$id.name` must be declared `shared` to be locked', id.pos)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
c.error('`$id.name` is not a variable and cannot be locked', id.pos)
|
|
|
|
}
|
|
|
|
if id.name in c.locked_names {
|
|
|
|
c.error('`$id.name` is already locked', id.pos)
|
|
|
|
} else if id.name in c.rlocked_names {
|
|
|
|
c.error('`$id.name` is already read-locked', id.pos)
|
|
|
|
}
|
2021-02-03 15:16:52 +01:00
|
|
|
if node.is_rlock[i] {
|
2020-07-13 12:19:28 +02:00
|
|
|
c.rlocked_names << id.name
|
|
|
|
} else {
|
|
|
|
c.locked_names << id.name
|
|
|
|
}
|
2020-07-04 12:44:25 +02:00
|
|
|
}
|
|
|
|
c.stmts(node.stmts)
|
2021-02-03 15:16:52 +01:00
|
|
|
c.rlocked_names = []
|
|
|
|
c.locked_names = []
|
2021-02-03 23:56:58 +01:00
|
|
|
// handle `x := rlock a { a.getval() }`
|
2021-04-02 00:57:09 +02:00
|
|
|
mut ret_type := ast.void_type
|
2021-02-03 23:56:58 +01:00
|
|
|
if node.stmts.len > 0 {
|
|
|
|
last_stmt := node.stmts[node.stmts.len - 1]
|
|
|
|
if last_stmt is ast.ExprStmt {
|
|
|
|
ret_type = last_stmt.typ
|
|
|
|
}
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if ret_type != ast.void_type {
|
2021-02-03 23:56:58 +01:00
|
|
|
node.is_expr = true
|
|
|
|
}
|
|
|
|
node.typ = ret_type
|
|
|
|
return ret_type
|
2020-07-04 12:44:25 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) unsafe_expr(mut node ast.UnsafeExpr) ast.Type {
|
2020-07-12 12:58:33 +02:00
|
|
|
c.inside_unsafe = true
|
2020-09-19 18:18:36 +02:00
|
|
|
t := c.expr(node.expr)
|
|
|
|
c.inside_unsafe = false
|
|
|
|
return t
|
2020-07-12 12:58:33 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
2020-10-18 21:22:37 +02:00
|
|
|
if_kind := if node.is_comptime { '\$if' } else { 'if' }
|
2021-04-02 00:57:09 +02:00
|
|
|
expr_required := c.expected_type != ast.void_type
|
2020-05-14 17:15:25 +02:00
|
|
|
former_expected_type := c.expected_type
|
2021-04-02 00:57:09 +02:00
|
|
|
node.typ = ast.void_type
|
2020-11-11 16:23:57 +01:00
|
|
|
mut nbranches_with_return := 0
|
|
|
|
mut nbranches_without_return := 0
|
2020-09-18 00:58:54 +02:00
|
|
|
mut should_skip := false // Whether the current branch should be skipped
|
|
|
|
mut found_branch := false // Whether a matching branch was found- skip the rest
|
2021-01-25 17:08:02 +01:00
|
|
|
mut is_comptime_type_is_expr := false // if `$if T is string`
|
2020-09-18 00:58:54 +02:00
|
|
|
for i in 0 .. node.branches.len {
|
|
|
|
mut branch := node.branches[i]
|
2020-04-24 20:27:18 +02:00
|
|
|
if branch.cond is ast.ParExpr {
|
2020-09-18 00:58:54 +02:00
|
|
|
c.error('unnecessary `()` in `$if_kind` condition, use `$if_kind expr {` instead of `$if_kind (expr) {`.',
|
2020-04-25 17:49:16 +02:00
|
|
|
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 {
|
2020-10-18 21:22:37 +02:00
|
|
|
if node.is_comptime {
|
2020-09-18 00:58:54 +02:00
|
|
|
should_skip = c.comp_if_branch(branch.cond, branch.pos)
|
|
|
|
} else {
|
|
|
|
// check condition type is boolean
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_type = ast.bool_type
|
2020-09-18 00:58:54 +02:00
|
|
|
cond_typ := c.expr(branch.cond)
|
2021-04-02 00:57:09 +02:00
|
|
|
if cond_typ.idx() != ast.bool_type_idx && !c.pref.translated {
|
2020-09-18 00:58:54 +02:00
|
|
|
typ_sym := c.table.get_type_symbol(cond_typ)
|
2021-02-14 19:19:41 +01:00
|
|
|
c.error('non-bool type `$typ_sym.name` used as if condition', branch.cond.position())
|
2020-09-18 00:58:54 +02:00
|
|
|
}
|
2020-04-30 09:32:29 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-18 21:22:37 +02:00
|
|
|
if node.is_comptime { // Skip checking if needed
|
2021-01-05 15:11:43 +01:00
|
|
|
// smartcast field type on comptime if
|
2021-01-29 00:45:00 +01:00
|
|
|
mut comptime_field_name := ''
|
2021-01-05 15:11:43 +01:00
|
|
|
if branch.cond is ast.InfixExpr {
|
|
|
|
if branch.cond.op == .key_is {
|
2021-04-02 00:57:09 +02:00
|
|
|
if branch.cond.right !is ast.TypeNode {
|
2021-02-26 21:51:35 +01:00
|
|
|
c.error('invalid `\$if` condition: expected a type', branch.cond.right.position())
|
|
|
|
return 0
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
got_type := c.unwrap_generic((branch.cond.right as ast.TypeNode).typ)
|
2021-02-26 15:26:50 +01:00
|
|
|
sym := c.table.get_type_symbol(got_type)
|
|
|
|
if sym.kind == .placeholder || got_type.has_flag(.generic) {
|
|
|
|
c.error('unknown type `$sym.name`', branch.cond.right.position())
|
|
|
|
}
|
2021-02-26 21:51:35 +01:00
|
|
|
left := branch.cond.left
|
2021-01-06 18:58:53 +01:00
|
|
|
if left is ast.SelectorExpr {
|
2021-01-29 00:45:00 +01:00
|
|
|
comptime_field_name = left.expr.str()
|
|
|
|
c.comptime_fields_type[comptime_field_name] = got_type
|
2021-01-25 17:08:02 +01:00
|
|
|
is_comptime_type_is_expr = true
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if left is ast.TypeNode {
|
2021-01-25 17:08:02 +01:00
|
|
|
is_comptime_type_is_expr = true
|
2021-01-06 18:58:53 +01:00
|
|
|
left_type := c.unwrap_generic(left.typ)
|
|
|
|
if left_type != got_type {
|
|
|
|
should_skip = true
|
|
|
|
}
|
2021-01-05 17:14:55 +01:00
|
|
|
}
|
2021-01-05 15:11:43 +01:00
|
|
|
}
|
|
|
|
}
|
2020-09-18 00:58:54 +02:00
|
|
|
cur_skip_flags := c.skip_flags
|
|
|
|
if found_branch {
|
|
|
|
c.skip_flags = true
|
|
|
|
} else if should_skip {
|
|
|
|
c.skip_flags = true
|
|
|
|
should_skip = false // Reset the value of `should_skip` for the next branch
|
2021-01-25 17:08:02 +01:00
|
|
|
} else if !is_comptime_type_is_expr {
|
2020-09-18 00:58:54 +02:00
|
|
|
found_branch = true // If a branch wasn't skipped, the rest must be
|
|
|
|
}
|
2021-04-14 07:50:50 +02:00
|
|
|
if !c.skip_flags {
|
|
|
|
c.stmts(branch.stmts)
|
|
|
|
} else if c.pref.output_cross_c {
|
|
|
|
mut is_freestanding_block := false
|
|
|
|
if branch.cond is ast.Ident {
|
|
|
|
if branch.cond.name == 'freestanding' {
|
|
|
|
is_freestanding_block = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if is_freestanding_block {
|
|
|
|
branch.stmts = []
|
|
|
|
node.branches[i].stmts = []
|
|
|
|
}
|
2020-09-18 00:58:54 +02:00
|
|
|
c.stmts(branch.stmts)
|
2021-01-25 17:08:02 +01:00
|
|
|
} else if !is_comptime_type_is_expr {
|
2020-09-18 00:58:54 +02:00
|
|
|
node.branches[i].stmts = []
|
|
|
|
}
|
2021-01-29 00:45:00 +01:00
|
|
|
if comptime_field_name.len > 0 {
|
|
|
|
c.comptime_fields_type.delete(comptime_field_name)
|
|
|
|
}
|
2020-09-18 00:58:54 +02:00
|
|
|
c.skip_flags = cur_skip_flags
|
|
|
|
} else {
|
2021-03-12 12:40:05 +01:00
|
|
|
// smartcast sumtypes and interfaces when using `is`
|
|
|
|
pos := branch.cond.position()
|
|
|
|
if branch.cond is ast.InfixExpr {
|
|
|
|
if branch.cond.op == .key_is {
|
2021-04-18 15:28:39 +02:00
|
|
|
right_expr := branch.cond.right
|
|
|
|
right_type := match right_expr {
|
|
|
|
ast.TypeNode {
|
|
|
|
right_expr.typ
|
|
|
|
}
|
|
|
|
ast.None {
|
|
|
|
ast.none_type_idx
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
c.error('invalid type `$right_expr`', right_expr.position())
|
|
|
|
ast.Type(0)
|
|
|
|
}
|
2021-03-12 12:40:05 +01:00
|
|
|
}
|
2021-04-18 15:28:39 +02:00
|
|
|
if right_type != ast.Type(0) {
|
|
|
|
left_sym := c.table.get_type_symbol(branch.cond.left_type)
|
|
|
|
expr_type := c.expr(branch.cond.left)
|
|
|
|
if left_sym.kind == .interface_ {
|
|
|
|
c.type_implements(right_type, expr_type, pos)
|
|
|
|
} else if !c.check_types(right_type, expr_type) {
|
|
|
|
expect_str := c.table.type_to_str(right_type)
|
|
|
|
expr_str := c.table.type_to_str(expr_type)
|
|
|
|
c.error('cannot use type `$expect_str` as type `$expr_str`',
|
|
|
|
pos)
|
2021-03-12 12:40:05 +01:00
|
|
|
}
|
2021-04-18 15:28:39 +02:00
|
|
|
if (branch.cond.left is ast.Ident || branch.cond.left is ast.SelectorExpr)
|
|
|
|
&& branch.cond.right is ast.TypeNode {
|
|
|
|
is_variable := if mut branch.cond.left is ast.Ident {
|
|
|
|
branch.cond.left.kind == .variable
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
if is_variable {
|
|
|
|
if left_sym.kind in [.interface_, .sum_type] {
|
|
|
|
c.smartcast(branch.cond.left, branch.cond.left_type,
|
|
|
|
right_type, mut branch.scope)
|
|
|
|
}
|
2021-03-12 12:40:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-18 00:58:54 +02: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 {
|
2020-07-23 23:16:36 +02:00
|
|
|
mut last_expr := branch.stmts[branch.stmts.len - 1] as ast.ExprStmt
|
2020-05-14 17:15:25 +02:00
|
|
|
c.expected_type = former_expected_type
|
2021-03-01 13:56:07 +01:00
|
|
|
if c.expected_type.has_flag(.optional) {
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.typ == ast.void_type {
|
2021-03-01 13:56:07 +01:00
|
|
|
node.is_expr = true
|
|
|
|
node.typ = c.expected_type
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2021-03-14 18:01:32 +01:00
|
|
|
if c.expected_type.has_flag(.generic) {
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.typ == ast.void_type {
|
2021-03-14 18:01:32 +01:00
|
|
|
node.is_expr = true
|
|
|
|
node.typ = c.unwrap_generic(c.expected_type)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2020-05-21 22:35:43 +02:00
|
|
|
last_expr.typ = c.expr(last_expr.expr)
|
2020-08-27 06:46:18 +02:00
|
|
|
if !c.check_types(last_expr.typ, node.typ) {
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.typ == ast.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-27 05:42:48 +02:00
|
|
|
continue
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if node.typ in [ast.float_literal_type, ast.int_literal_type] {
|
|
|
|
if node.typ == ast.int_literal_type {
|
2020-05-27 05:42:48 +02:00
|
|
|
if last_expr.typ.is_int() || last_expr.typ.is_float() {
|
|
|
|
node.typ = last_expr.typ
|
|
|
|
continue
|
2020-05-27 15:56:21 +02:00
|
|
|
}
|
2021-01-11 22:58:15 +01:00
|
|
|
} else { // node.typ == float_literal
|
2020-05-27 05:42:48 +02:00
|
|
|
if last_expr.typ.is_float() {
|
|
|
|
node.typ = last_expr.typ
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2020-04-30 12:17:31 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if last_expr.typ in [ast.float_literal_type, ast.int_literal_type] {
|
|
|
|
if last_expr.typ == ast.int_literal_type {
|
2020-05-27 05:42:48 +02:00
|
|
|
if node.typ.is_int() || node.typ.is_float() {
|
|
|
|
continue
|
2020-05-27 15:56:21 +02:00
|
|
|
}
|
2021-01-11 22:58:15 +01:00
|
|
|
} else { // expr_type == float_literal
|
2020-05-27 05:42:48 +02:00
|
|
|
if node.typ.is_float() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(last_expr.typ)}`',
|
2020-05-28 05:50:57 +02:00
|
|
|
node.pos)
|
2020-03-20 14:39:56 +01:00
|
|
|
}
|
2020-05-14 17:15:25 +02:00
|
|
|
} else {
|
2020-09-18 00:58:54 +02:00
|
|
|
c.error('`$if_kind` expression requires an expression as the last statement of every branch',
|
2020-05-14 17:15:25 +02:00
|
|
|
branch.pos)
|
2020-04-07 15:15:45 +02:00
|
|
|
}
|
2020-09-30 16:06:22 +02:00
|
|
|
for st in branch.stmts {
|
2020-10-10 15:42:40 +02:00
|
|
|
// must not contain C statements
|
2021-03-13 18:13:50 +01:00
|
|
|
st.check_c_expr() or { c.error('`if` expression branch has $err.msg', st.pos) }
|
2020-09-30 16:06:22 +02:00
|
|
|
}
|
2020-03-20 14:39:56 +01:00
|
|
|
}
|
2020-09-18 00:58:54 +02:00
|
|
|
// Also check for returns inside a comp.if's statements, even if its contents aren't parsed
|
2020-07-09 22:38:43 +02:00
|
|
|
if has_return := c.has_return(branch.stmts) {
|
|
|
|
if has_return {
|
2020-11-11 16:23:57 +01:00
|
|
|
nbranches_with_return++
|
2020-07-09 22:38:43 +02:00
|
|
|
} else {
|
2020-11-11 16:23:57 +01:00
|
|
|
nbranches_without_return++
|
2020-07-09 22:38:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-11 16:23:57 +01:00
|
|
|
if nbranches_with_return > 0 {
|
|
|
|
if nbranches_with_return == node.branches.len {
|
|
|
|
// if/else... where all branches returned
|
|
|
|
c.returns = true
|
|
|
|
}
|
|
|
|
if !node.has_else {
|
|
|
|
// `if cond { return ... }` means that when cond is false, execution continues
|
|
|
|
c.returns = false
|
|
|
|
}
|
|
|
|
if nbranches_without_return > 0 {
|
|
|
|
// some of the branches did not return
|
|
|
|
c.returns = false
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
// if only untyped literals were given default to int/f64
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.typ == ast.int_literal_type {
|
|
|
|
node.typ = ast.int_type
|
|
|
|
} else if node.typ == ast.float_literal_type {
|
|
|
|
node.typ = ast.f64_type
|
2020-05-27 05:42:48 +02:00
|
|
|
}
|
2020-12-03 20:14:23 +01:00
|
|
|
if expr_required && !node.has_else {
|
|
|
|
d := if node.is_comptime { '$' } else { '' }
|
|
|
|
c.error('`$if_kind` expression needs `${d}else` clause', node.pos)
|
2020-05-11 14:38:25 +02:00
|
|
|
}
|
2020-12-03 20:14:23 +01:00
|
|
|
return node.typ
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
|
|
|
|
2020-09-18 00:58:54 +02:00
|
|
|
// comp_if_branch checks the condition of a compile-time `if` branch. It returns a `bool` that
|
|
|
|
// saying whether that branch's contents should be skipped (targets a different os for example)
|
|
|
|
fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
|
|
|
|
// TODO: better error messages here
|
2020-11-25 12:09:40 +01:00
|
|
|
match cond {
|
2020-12-11 04:46:06 +01:00
|
|
|
ast.BoolLiteral {
|
|
|
|
return !cond.val
|
|
|
|
}
|
2020-09-18 00:58:54 +02:00
|
|
|
ast.ParExpr {
|
|
|
|
return c.comp_if_branch(cond.expr, pos)
|
|
|
|
}
|
|
|
|
ast.PrefixExpr {
|
|
|
|
if cond.op != .not {
|
|
|
|
c.error('invalid `\$if` condition', cond.pos)
|
|
|
|
}
|
|
|
|
return !c.comp_if_branch(cond.right, cond.pos)
|
|
|
|
}
|
|
|
|
ast.PostfixExpr {
|
|
|
|
if cond.op != .question {
|
|
|
|
c.error('invalid \$if postfix operator', cond.pos)
|
2020-11-21 00:05:57 +01:00
|
|
|
} else if cond.expr is ast.Ident {
|
|
|
|
return cond.expr.name !in c.pref.compile_defines_all
|
2020-09-18 00:58:54 +02:00
|
|
|
} else {
|
|
|
|
c.error('invalid `\$if` condition', cond.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.InfixExpr {
|
|
|
|
match cond.op {
|
|
|
|
.and {
|
|
|
|
l := c.comp_if_branch(cond.left, cond.pos)
|
|
|
|
r := c.comp_if_branch(cond.right, cond.pos)
|
2020-09-25 11:48:27 +02:00
|
|
|
return l || r // skip (return true) if at least one should be skipped
|
2020-09-18 00:58:54 +02:00
|
|
|
}
|
|
|
|
.logical_or {
|
|
|
|
l := c.comp_if_branch(cond.left, cond.pos)
|
|
|
|
r := c.comp_if_branch(cond.right, cond.pos)
|
2020-09-25 11:48:27 +02:00
|
|
|
return l && r // skip (return true) only if both should be skipped
|
2020-09-18 00:58:54 +02:00
|
|
|
}
|
|
|
|
.key_is, .not_is {
|
2021-04-02 00:57:09 +02:00
|
|
|
if cond.left is ast.SelectorExpr || cond.left is ast.TypeNode {
|
2020-12-11 04:46:06 +01:00
|
|
|
// $if method.@type is string
|
2021-02-26 21:51:35 +01:00
|
|
|
c.expr(cond.left)
|
|
|
|
return false
|
2020-12-11 04:46:06 +01:00
|
|
|
} else {
|
2021-02-26 21:51:35 +01:00
|
|
|
c.error('invalid `\$if` condition: expected a type or selector expression',
|
|
|
|
cond.left.position())
|
2020-09-18 00:58:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
.eq, .ne {
|
2020-12-11 04:46:06 +01:00
|
|
|
if cond.left is ast.SelectorExpr && cond.right is ast.IntegerLiteral {
|
|
|
|
// $if method.args.len == 1
|
|
|
|
} else if cond.left is ast.Ident {
|
|
|
|
// $if version == 2
|
|
|
|
left_type := c.expr(cond.left)
|
|
|
|
right_type := c.expr(cond.right)
|
|
|
|
expr := c.find_definition(cond.left) or {
|
2021-02-28 21:20:21 +01:00
|
|
|
c.error(err.msg, cond.left.pos)
|
2020-12-11 04:46:06 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !c.check_types(right_type, left_type) {
|
|
|
|
left_name := c.table.type_to_str(left_type)
|
|
|
|
right_name := c.table.type_to_str(right_type)
|
|
|
|
c.error('mismatched types `$left_name` and `$right_name`',
|
|
|
|
cond.pos)
|
|
|
|
}
|
|
|
|
// :)
|
|
|
|
// until `v.eval` is stable, I can't think of a better way to do this
|
|
|
|
different := expr.str() != cond.right.str()
|
2021-02-08 00:28:46 +01:00
|
|
|
return if cond.op == .eq { different } else { !different }
|
2020-12-11 04:46:06 +01:00
|
|
|
} else {
|
2021-01-22 13:49:56 +01:00
|
|
|
c.error('invalid `\$if` condition: ${cond.left.type_name()}1',
|
|
|
|
cond.pos)
|
2020-09-18 00:58:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
c.error('invalid `\$if` condition', cond.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.Ident {
|
2021-01-25 10:26:20 +01:00
|
|
|
if cond.name in checker.valid_comp_if_os {
|
2020-09-18 00:58:54 +02:00
|
|
|
return cond.name != c.pref.os.str().to_lower() // TODO hack
|
2021-01-25 10:26:20 +01:00
|
|
|
} else if cond.name in checker.valid_comp_if_compilers {
|
2020-09-18 00:58:54 +02:00
|
|
|
return pref.cc_from_string(cond.name) != c.pref.ccompiler_type
|
2021-01-25 10:26:20 +01:00
|
|
|
} else if cond.name in checker.valid_comp_if_platforms {
|
2020-09-18 00:58:54 +02:00
|
|
|
return false // TODO
|
2021-01-25 10:26:20 +01:00
|
|
|
} else if cond.name in checker.valid_comp_if_other {
|
2020-09-18 00:58:54 +02:00
|
|
|
// TODO: This should probably be moved
|
2020-11-24 17:55:24 +01:00
|
|
|
match cond.name {
|
2020-09-18 00:58:54 +02:00
|
|
|
'js' { return c.pref.backend != .js }
|
|
|
|
'debug' { return !c.pref.is_debug }
|
2021-03-10 18:26:34 +01:00
|
|
|
'prod' { return !c.pref.is_prod }
|
2020-09-18 00:58:54 +02:00
|
|
|
'test' { return !c.pref.is_test }
|
|
|
|
'glibc' { return false } // TODO
|
|
|
|
'prealloc' { return !c.pref.prealloc }
|
|
|
|
'no_bounds_checking' { return cond.name !in c.pref.compile_defines_all }
|
2021-04-14 07:50:50 +02:00
|
|
|
'freestanding' { return !c.pref.is_bare || c.pref.output_cross_c }
|
2020-09-18 00:58:54 +02:00
|
|
|
else { return false }
|
|
|
|
}
|
2020-12-11 04:46:06 +01:00
|
|
|
} else if cond.name !in c.pref.compile_defines_all {
|
|
|
|
// `$if some_var {}`
|
|
|
|
typ := c.expr(cond)
|
2021-01-23 09:33:22 +01:00
|
|
|
if cond.obj !is ast.Var && cond.obj !is ast.ConstField
|
|
|
|
&& cond.obj !is ast.GlobalField {
|
2020-12-11 04:46:06 +01:00
|
|
|
c.error('unknown var: `$cond.name`', pos)
|
|
|
|
return false
|
|
|
|
}
|
2020-12-12 09:01:12 +01:00
|
|
|
expr := c.find_obj_definition(cond.obj) or {
|
2021-02-28 21:20:21 +01:00
|
|
|
c.error(err.msg, cond.pos)
|
2020-12-11 04:46:06 +01:00
|
|
|
return false
|
2020-10-24 09:13:58 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if !c.check_types(typ, ast.bool_type) {
|
2020-12-11 04:46:06 +01:00
|
|
|
type_name := c.table.type_to_str(typ)
|
|
|
|
c.error('non-bool type `$type_name` used as \$if condition', cond.pos)
|
|
|
|
}
|
|
|
|
// :)
|
|
|
|
// until `v.eval` is stable, I can't think of a better way to do this
|
|
|
|
return !(expr as ast.BoolLiteral).val
|
2020-09-18 00:58:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
c.error('invalid `\$if` condition', pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-12-11 04:46:06 +01:00
|
|
|
fn (mut c Checker) find_definition(ident ast.Ident) ?ast.Expr {
|
|
|
|
match ident.kind {
|
|
|
|
.unresolved, .blank_ident { return none }
|
|
|
|
.variable, .constant { return c.find_obj_definition(ident.obj) }
|
|
|
|
.global { return error('$ident.name is a global variable') }
|
|
|
|
.function { return error('$ident.name is a function') }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) find_obj_definition(obj ast.ScopeObject) ?ast.Expr {
|
|
|
|
// TODO: remove once we have better type inference
|
|
|
|
mut name := ''
|
|
|
|
match obj {
|
2021-03-17 01:43:17 +01:00
|
|
|
ast.Var, ast.ConstField, ast.GlobalField, ast.AsmRegister { name = obj.name }
|
2020-12-11 04:46:06 +01:00
|
|
|
}
|
2021-03-31 10:13:15 +02:00
|
|
|
mut expr := ast.empty_expr()
|
2020-12-11 04:46:06 +01:00
|
|
|
if obj is ast.Var {
|
|
|
|
if obj.is_mut {
|
|
|
|
return error('`$name` is mut and may have changed since its definition')
|
|
|
|
}
|
|
|
|
expr = obj.expr
|
|
|
|
} else if obj is ast.ConstField {
|
|
|
|
expr = obj.expr
|
|
|
|
} else {
|
|
|
|
return error('`$name` is a global variable and is unknown at compile time')
|
|
|
|
}
|
|
|
|
if expr is ast.Ident {
|
|
|
|
return c.find_definition(expr as ast.Ident) // TODO: smartcast
|
|
|
|
}
|
|
|
|
if !expr.is_lit() {
|
|
|
|
return error('definition of `$name` is unknown at compile time')
|
|
|
|
}
|
|
|
|
return expr
|
|
|
|
}
|
|
|
|
|
2020-08-10 23:59:38 +02:00
|
|
|
fn (c &Checker) has_return(stmts []ast.Stmt) ?bool {
|
2020-07-09 22:38:43 +02:00
|
|
|
// complexity means either more match or ifs
|
2020-08-10 23:59:08 +02:00
|
|
|
mut has_complexity := false
|
|
|
|
for s in stmts {
|
|
|
|
if s is ast.ExprStmt {
|
|
|
|
if s.expr is ast.IfExpr || s.expr is ast.MatchExpr {
|
|
|
|
has_complexity = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-09 22:38:43 +02:00
|
|
|
// if the inner complexity covers all paths with returns there is no need for further checks
|
2020-08-10 23:59:08 +02:00
|
|
|
if !has_complexity || !c.returns {
|
2020-07-09 22:38:43 +02:00
|
|
|
return has_top_return(stmts)
|
|
|
|
}
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) ast.Type {
|
2021-04-26 18:57:05 +02:00
|
|
|
typ := c.unwrap_generic(c.expr(node.expr))
|
2020-02-27 17:31:10 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2021-01-17 17:09:17 +01:00
|
|
|
is_non_void_pointer := (typ.is_ptr() || typ.is_pointer()) && typ_sym.kind != .voidptr
|
2021-02-21 11:15:36 +01:00
|
|
|
if !c.inside_unsafe && is_non_void_pointer && !node.expr.is_auto_deref_var() {
|
2021-01-17 17:09:17 +01:00
|
|
|
c.warn('pointer arithmetic is only allowed in `unsafe` blocks', node.pos)
|
|
|
|
}
|
|
|
|
if !(typ_sym.is_number() || (c.inside_unsafe && is_non_void_pointer)) {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('invalid operation: $node.op.str() (non-numeric type `$typ_sym.name`)',
|
2020-06-24 14:44:06 +02:00
|
|
|
node.pos)
|
2020-07-14 18:11:16 +02:00
|
|
|
} else {
|
2020-07-13 14:01:32 +02:00
|
|
|
node.auto_locked, _ = c.fail_if_immutable(node.expr)
|
2020-02-04 07:37:38 +01:00
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
2021-04-25 20:40:38 +02:00
|
|
|
pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr) {
|
|
|
|
match mut node {
|
|
|
|
ast.Ident {
|
|
|
|
if mut node.obj is ast.Var {
|
|
|
|
mut obj := unsafe { &node.obj }
|
|
|
|
if c.fn_scope != voidptr(0) {
|
|
|
|
obj = c.fn_scope.find_var(node.obj.name) or { unsafe { &node.obj } }
|
|
|
|
}
|
|
|
|
type_sym := c.table.get_type_symbol(obj.typ)
|
|
|
|
if obj.is_stack_obj {
|
|
|
|
c.error('`$node.name` cannot be referenced outside `unsafe` blocks as it might be stored on stack. Consider declaring `$type_sym.name` as `[heap]`.',
|
|
|
|
node.pos)
|
|
|
|
} else if type_sym.kind == .array_fixed {
|
|
|
|
c.error('cannot reference fixed array `$node.name` outside `unsafe` blocks as it is supposed to be stored on stack',
|
|
|
|
node.pos)
|
|
|
|
} else {
|
|
|
|
node.obj.is_auto_heap = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.SelectorExpr {
|
2021-04-28 06:47:00 +02:00
|
|
|
if !node.expr_type.is_ptr() {
|
|
|
|
c.mark_as_referenced(mut &node.expr)
|
|
|
|
}
|
2021-04-25 20:40:38 +02:00
|
|
|
}
|
|
|
|
ast.IndexExpr {
|
|
|
|
c.mark_as_referenced(mut &node.left)
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (mut c Checker) get_base_name(node &ast.Expr) string {
|
|
|
|
match node {
|
|
|
|
ast.Ident {
|
|
|
|
return node.name
|
|
|
|
}
|
|
|
|
ast.SelectorExpr {
|
|
|
|
return c.get_base_name(&node.expr)
|
|
|
|
}
|
|
|
|
ast.IndexExpr {
|
|
|
|
return c.get_base_name(&node.left)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return ''
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type {
|
2021-02-13 00:47:37 +01:00
|
|
|
old_inside_ref_lit := c.inside_ref_lit
|
|
|
|
c.inside_ref_lit = c.inside_ref_lit || node.op == .amp
|
2020-12-20 07:55:23 +01:00
|
|
|
right_type := c.expr(node.right)
|
2021-02-13 00:47:37 +01:00
|
|
|
c.inside_ref_lit = old_inside_ref_lit
|
2020-12-20 07:55:23 +01:00
|
|
|
node.right_type = right_type
|
|
|
|
// TODO: testing ref/deref strategy
|
|
|
|
if node.op == .amp && !right_type.is_ptr() {
|
2021-02-12 16:49:22 +01:00
|
|
|
mut expr := node.right
|
|
|
|
// if ParExpr get the innermost expr
|
|
|
|
for mut expr is ast.ParExpr {
|
|
|
|
expr = expr.expr
|
|
|
|
}
|
|
|
|
match expr {
|
|
|
|
ast.BoolLiteral, ast.CallExpr, ast.CharLiteral, ast.FloatLiteral, ast.IntegerLiteral,
|
|
|
|
ast.InfixExpr, ast.StringLiteral, ast.StringInterLiteral {
|
|
|
|
c.error('cannot take the address of $expr', node.pos)
|
2021-01-30 11:57:57 +01:00
|
|
|
}
|
2020-12-30 17:21:15 +01:00
|
|
|
else {}
|
2020-12-20 07:55:23 +01:00
|
|
|
}
|
|
|
|
if mut node.right is ast.IndexExpr {
|
|
|
|
typ_sym := c.table.get_type_symbol(node.right.left_type)
|
|
|
|
mut is_mut := false
|
|
|
|
if mut node.right.left is ast.Ident {
|
|
|
|
ident := node.right.left
|
|
|
|
// TODO: temporary, remove this
|
|
|
|
ident_obj := ident.obj
|
|
|
|
if ident_obj is ast.Var {
|
|
|
|
is_mut = ident_obj.is_mut
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if typ_sym.kind == .map {
|
|
|
|
c.error('cannot take the address of map values', node.right.pos)
|
|
|
|
}
|
|
|
|
if !c.inside_unsafe {
|
|
|
|
if typ_sym.kind == .array && is_mut {
|
|
|
|
c.error('cannot take the address of mutable array elements outside unsafe blocks',
|
|
|
|
node.right.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-25 20:40:38 +02:00
|
|
|
if !c.inside_fn_arg && !c.inside_unsafe {
|
|
|
|
c.mark_as_referenced(mut &node.right)
|
|
|
|
}
|
2020-12-20 07:55:23 +01:00
|
|
|
return right_type.to_ptr()
|
|
|
|
} else if node.op == .amp && node.right !is ast.CastExpr {
|
2021-04-25 20:40:38 +02:00
|
|
|
if !c.inside_fn_arg && !c.inside_unsafe {
|
|
|
|
c.mark_as_referenced(mut &node.right)
|
|
|
|
}
|
|
|
|
if node.right.is_auto_deref_var() {
|
|
|
|
return right_type
|
|
|
|
} else {
|
|
|
|
return right_type.to_ptr()
|
|
|
|
}
|
2020-12-20 07:55:23 +01:00
|
|
|
}
|
|
|
|
if node.op == .mul {
|
|
|
|
if right_type.is_ptr() {
|
|
|
|
return right_type.deref()
|
|
|
|
}
|
|
|
|
if !right_type.is_pointer() {
|
|
|
|
s := c.table.type_to_str(right_type)
|
|
|
|
c.error('invalid indirect of `$s`', node.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if node.op == .bit_not && !right_type.is_int() && !c.pref.translated {
|
|
|
|
c.error('operator ~ only defined on int types', node.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.op == .not && right_type != ast.bool_type_idx && !c.pref.translated {
|
2020-12-20 07:55:23 +01:00
|
|
|
c.error('! operator can only be used with bool types', node.pos)
|
|
|
|
}
|
2021-04-23 14:18:56 +02:00
|
|
|
// FIXME
|
|
|
|
// there are currently other issues to investigate if right_type
|
|
|
|
// is unwraped directly as initialization, so do it here
|
|
|
|
right_sym := c.table.get_final_type_symbol(c.unwrap_generic(right_type))
|
|
|
|
if node.op == .minus && !right_sym.is_number() {
|
|
|
|
c.error('- operator can only be used with numeric types', node.pos)
|
|
|
|
}
|
2020-12-20 07:55:23 +01:00
|
|
|
if node.op == .arrow {
|
2021-04-23 14:18:56 +02:00
|
|
|
if right_sym.kind == .chan {
|
2020-12-20 07:55:23 +01:00
|
|
|
c.stmts(node.or_block.stmts)
|
2021-04-23 14:18:56 +02:00
|
|
|
return right_sym.chan_info().elem_type
|
2020-12-20 07:55:23 +01:00
|
|
|
}
|
2021-04-23 14:18:56 +02:00
|
|
|
c.error('<- operator can only be used with `chan` types', node.pos)
|
2020-12-20 07:55:23 +01:00
|
|
|
}
|
|
|
|
return right_type
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) check_index(typ_sym &ast.TypeSymbol, index ast.Expr, index_type ast.Type, pos token.Position, range_index bool) {
|
2020-07-20 18:52:03 +02:00
|
|
|
index_type_sym := c.table.get_type_symbol(index_type)
|
2020-12-06 04:55:08 +01:00
|
|
|
// println('index expr left=$typ_sym.name $node.pos.line_nr')
|
2021-04-02 00:57:09 +02:00
|
|
|
// if typ_sym.kind == .array && (!(ast.type_idx(index_type) in ast.number_type_idxs) &&
|
2020-07-20 18:52:03 +02:00
|
|
|
// index_type_sym.kind != .enum_) {
|
2020-11-07 14:55:05 +01:00
|
|
|
if typ_sym.kind in [.array, .array_fixed, .string, .ustring] {
|
2021-02-20 19:18:47 +01:00
|
|
|
if !(index_type.is_int() || index_type_sym.kind == .enum_) {
|
2020-12-27 14:20:30 +01:00
|
|
|
type_str := if typ_sym.kind in [.string, .ustring] {
|
|
|
|
'non-integer string index `$index_type_sym.name`'
|
|
|
|
} else {
|
|
|
|
'non-integer index `$index_type_sym.name` (array type `$typ_sym.name`)'
|
|
|
|
}
|
2020-11-07 14:55:05 +01:00
|
|
|
c.error('$type_str', pos)
|
|
|
|
}
|
2021-01-24 22:09:51 +01:00
|
|
|
if index is ast.IntegerLiteral {
|
2021-02-20 19:18:47 +01:00
|
|
|
if index.val[0] == `-` {
|
|
|
|
c.error('negative index `$index.val`', index.pos)
|
|
|
|
} else if typ_sym.kind == .array_fixed {
|
|
|
|
i := index.val.int()
|
2021-04-02 00:57:09 +02:00
|
|
|
info := typ_sym.info as ast.ArrayFixed
|
2021-02-20 19:18:47 +01:00
|
|
|
if (!range_index && i >= info.size) || (range_index && i > info.size) {
|
|
|
|
c.error('index out of range (index: $i, len: $info.size)', index.pos)
|
|
|
|
}
|
2021-01-21 12:27:16 +01:00
|
|
|
}
|
|
|
|
}
|
2020-11-07 14:55:05 +01:00
|
|
|
if index_type.has_flag(.optional) {
|
2020-12-27 14:20:30 +01:00
|
|
|
type_str := if typ_sym.kind in [.string, .ustring] {
|
|
|
|
'(type `$typ_sym.name`)'
|
|
|
|
} else {
|
|
|
|
'(array type `$typ_sym.name`)'
|
|
|
|
}
|
2020-11-07 14:55:05 +01:00
|
|
|
c.error('cannot use optional as index $type_str', pos)
|
|
|
|
}
|
2020-07-20 18:52:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
|
2021-01-19 06:06:57 +01:00
|
|
|
mut typ := c.expr(node.left)
|
2020-04-03 10:41:01 +02:00
|
|
|
node.left_type = typ
|
2021-01-05 19:17:18 +01:00
|
|
|
typ_sym := c.table.get_final_type_symbol(typ)
|
2021-02-16 14:08:01 +01:00
|
|
|
match typ_sym.kind {
|
|
|
|
.map {
|
|
|
|
node.is_map = true
|
|
|
|
}
|
|
|
|
.array {
|
|
|
|
node.is_array = true
|
|
|
|
}
|
|
|
|
.array_fixed {
|
|
|
|
node.is_farray = true
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
if typ_sym.kind !in [.array, .array_fixed, .string, .map] && !typ.is_ptr()
|
2021-04-02 00:57:09 +02:00
|
|
|
&& typ !in [ast.byteptr_type, ast.charptr_type] && !typ.has_flag(.variadic) {
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('type `$typ_sym.name` does not support indexing', node.pos)
|
2020-06-19 12:53:53 +02:00
|
|
|
}
|
2020-08-11 17:41:54 +02:00
|
|
|
if typ_sym.kind == .string && !typ.is_ptr() && node.is_setter {
|
2020-08-13 23:21:11 +02:00
|
|
|
c.error('cannot assign to s[i] since V strings are immutable\n' +
|
|
|
|
'(note, that variables may be mutable but string values are always immutable, like in Go and Java)',
|
|
|
|
node.pos)
|
2020-08-11 17:41:54 +02:00
|
|
|
}
|
2021-02-21 11:15:36 +01:00
|
|
|
if !c.inside_unsafe && ((typ.is_ptr() && !node.left.is_auto_deref_var()) || typ.is_pointer()) {
|
2020-07-22 19:28:53 +02:00
|
|
|
mut is_ok := false
|
2020-11-21 00:05:57 +01:00
|
|
|
if mut node.left is ast.Ident {
|
2020-12-12 09:01:12 +01:00
|
|
|
if node.left.obj is ast.Var {
|
|
|
|
v := node.left.obj as ast.Var
|
2020-07-22 19:28:53 +02:00
|
|
|
// `mut param []T` function parameter
|
2021-01-23 13:33:32 +01:00
|
|
|
is_ok = ((v.is_mut && v.is_arg) || v.share == .shared_t) && !typ.deref().is_ptr()
|
2020-07-22 19:28:53 +02:00
|
|
|
}
|
|
|
|
}
|
2020-08-09 03:57:54 +02:00
|
|
|
if !is_ok && !c.pref.translated {
|
2020-07-22 19:28:53 +02:00
|
|
|
c.warn('pointer indexing is only allowed in `unsafe` blocks', node.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-11-21 00:05:57 +01:00
|
|
|
if mut node.index is ast.RangeExpr { // [1..2]
|
|
|
|
if node.index.has_low {
|
|
|
|
index_type := c.expr(node.index.low)
|
2021-02-20 19:18:47 +01:00
|
|
|
c.check_index(typ_sym, node.index.low, index_type, node.pos, true)
|
2020-07-20 18:52:03 +02:00
|
|
|
}
|
2020-11-21 00:05:57 +01:00
|
|
|
if node.index.has_high {
|
|
|
|
index_type := c.expr(node.index.high)
|
2021-02-20 19:18:47 +01:00
|
|
|
c.check_index(typ_sym, node.index.high, index_type, node.pos, true)
|
2020-07-20 18:52:03 +02:00
|
|
|
}
|
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)
|
2021-01-13 23:43:19 +01:00
|
|
|
idx := c.table.find_or_register_array(elem_type)
|
2021-04-02 00:57:09 +02:00
|
|
|
typ = ast.new_type(idx)
|
2021-01-19 06:06:57 +01:00
|
|
|
} else {
|
|
|
|
typ = typ.set_nr_muls(0)
|
2020-03-31 16:47:55 +02:00
|
|
|
}
|
2020-10-10 12:03:23 +02:00
|
|
|
} else { // [1]
|
2020-12-27 14:18:46 +01:00
|
|
|
if typ_sym.kind == .map {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := typ_sym.info as ast.Map
|
2021-04-29 07:33:41 +02:00
|
|
|
c.expected_type = info.key_type
|
|
|
|
index_type := c.expr(node.index)
|
2020-12-27 14:18:46 +01:00
|
|
|
if !c.check_types(index_type, info.key_type) {
|
|
|
|
err := c.expected_msg(index_type, info.key_type)
|
|
|
|
c.error('invalid key: $err', node.pos)
|
|
|
|
}
|
|
|
|
} else {
|
2021-04-29 07:33:41 +02:00
|
|
|
index_type := c.expr(node.index)
|
2021-02-20 19:18:47 +01:00
|
|
|
c.check_index(typ_sym, node.index, index_type, node.pos, false)
|
2020-10-10 12:03:23 +02:00
|
|
|
}
|
|
|
|
value_type := c.table.value_type(typ)
|
2021-04-02 00:57:09 +02:00
|
|
|
if value_type != ast.void_type {
|
2021-01-19 06:06:57 +01:00
|
|
|
typ = value_type
|
2020-10-10 12:03:23 +02:00
|
|
|
}
|
2020-03-31 16:47:55 +02:00
|
|
|
}
|
2021-01-19 06:06:57 +01:00
|
|
|
c.stmts(node.or_expr.stmts)
|
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.
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) enum_val(mut node ast.EnumVal) ast.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)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-02-29 18:34:25 +01:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
mut typ := ast.new_type(typ_idx)
|
2020-08-11 16:26:49 +02:00
|
|
|
if c.pref.translated {
|
|
|
|
// TODO make more strict
|
|
|
|
node.typ = typ
|
|
|
|
return typ
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if typ == ast.void_type {
|
2020-04-26 19:59:03 +02:00
|
|
|
c.error('not an enum', node.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-04-26 19:59:03 +02:00
|
|
|
}
|
2020-05-25 20:48:43 +02:00
|
|
|
mut typ_sym := c.table.get_type_symbol(typ)
|
2020-12-06 04:55:08 +01:00
|
|
|
// println('tname=$typ_sym.name $node.pos.line_nr $c.file.path')
|
2020-05-25 20:48:43 +02:00
|
|
|
if typ_sym.kind == .array && node.enum_name.len == 0 {
|
2021-04-02 00:57:09 +02:00
|
|
|
array_info := typ_sym.info as ast.Array
|
2020-05-25 20:48:43 +02:00
|
|
|
typ = array_info.elem_type
|
|
|
|
typ_sym = c.table.get_type_symbol(typ)
|
|
|
|
}
|
2020-08-11 16:26:49 +02:00
|
|
|
if typ_sym.kind != .enum_ && !c.pref.translated {
|
|
|
|
// TODO in C int fields can be compared to enums, need to handle that in C2V
|
2020-12-06 04:55:08 +01:00
|
|
|
c.error('expected type is not an enum (`$typ_sym.name`)', node.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-05-08 15:00:04 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if typ_sym.info !is ast.Enum {
|
2020-05-08 15:00:04 +02:00
|
|
|
c.error('not an enum', node.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-02-29 18:25:38 +01:00
|
|
|
}
|
2021-04-22 06:07:56 +02:00
|
|
|
if !(typ_sym.is_public || typ_sym.mod == c.mod) {
|
|
|
|
c.error('enum `$typ_sym.name` is private', node.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
// info := typ_sym.info as ast.Enum
|
2020-03-15 02:51:31 +01:00
|
|
|
info := typ_sym.enum_info()
|
2020-12-06 04:55:08 +01:00
|
|
|
// 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 {
|
2021-03-05 12:19:39 +01:00
|
|
|
suggestion := util.new_suggestion(node.val, info.vals)
|
|
|
|
c.error(suggestion.say('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
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) chan_init(mut node ast.ChanInit) ast.Type {
|
2020-08-14 21:18:42 +02:00
|
|
|
if node.typ != 0 {
|
|
|
|
info := c.table.get_type_symbol(node.typ).chan_info()
|
|
|
|
node.elem_type = info.elem_type
|
2020-08-26 06:41:51 +02:00
|
|
|
if node.has_cap {
|
|
|
|
c.check_array_init_para_type('cap', node.cap_expr, node.pos)
|
|
|
|
}
|
2020-08-14 21:18:42 +02:00
|
|
|
return node.typ
|
|
|
|
} else {
|
|
|
|
c.error('`chan` of unknown type', node.pos)
|
|
|
|
return node.typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) offset_of(node ast.OffsetOf) ast.Type {
|
2021-01-30 12:57:09 +01:00
|
|
|
sym := c.table.get_final_type_symbol(node.struct_type)
|
|
|
|
if sym.kind != .struct_ {
|
|
|
|
c.error('first argument of __offsetof must be struct', node.pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.u32_type
|
2021-01-30 12:57:09 +01:00
|
|
|
}
|
2021-02-19 13:14:40 +01:00
|
|
|
if !c.table.struct_has_field(sym, node.field) {
|
2021-01-30 12:57:09 +01:00
|
|
|
c.error('struct `$sym.name` has no field called `$node.field`', node.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.u32_type
|
2021-01-30 12:57:09 +01:00
|
|
|
}
|
|
|
|
|
2021-01-01 17:23:32 +01:00
|
|
|
pub fn (mut c Checker) check_dup_keys(node &ast.MapInit, i int) {
|
|
|
|
key_i := node.keys[i]
|
|
|
|
if key_i is ast.StringLiteral {
|
|
|
|
for j in 0 .. i {
|
2021-02-06 22:13:24 +01:00
|
|
|
key_j := node.keys[j]
|
|
|
|
if key_j is ast.StringLiteral {
|
|
|
|
if key_i.val == key_j.val {
|
|
|
|
c.error('duplicate key "$key_i.val" in map literal', key_i.pos)
|
|
|
|
}
|
2021-01-01 17:23:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if key_i is ast.IntegerLiteral {
|
|
|
|
for j in 0 .. i {
|
2021-02-06 22:13:24 +01:00
|
|
|
key_j := node.keys[j]
|
|
|
|
if key_j is ast.IntegerLiteral {
|
|
|
|
if key_i.val == key_j.val {
|
|
|
|
c.error('duplicate key "$key_i.val" in map literal', key_i.pos)
|
|
|
|
}
|
2021-01-01 17:23:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
pub fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type {
|
2021-01-30 11:55:10 +01:00
|
|
|
// `map = {}`
|
|
|
|
if node.keys.len == 0 && node.vals.len == 0 && node.typ == 0 {
|
|
|
|
sym := c.table.get_type_symbol(c.expected_type)
|
|
|
|
if sym.kind == .map {
|
|
|
|
info := sym.map_info()
|
|
|
|
node.typ = c.expected_type
|
|
|
|
node.key_type = info.key_type
|
|
|
|
node.value_type = info.value_type
|
|
|
|
return node.typ
|
2021-03-08 18:48:17 +01:00
|
|
|
} else {
|
|
|
|
c.error('invalid empty map initilization syntax, use e.g. map[string]int{} instead',
|
|
|
|
node.pos)
|
2021-01-30 11:55:10 +01:00
|
|
|
}
|
|
|
|
}
|
2020-10-03 06:59:30 +02:00
|
|
|
// `x := map[string]string` - set in parser
|
2020-03-07 08:13:00 +01:00
|
|
|
if node.typ != 0 {
|
|
|
|
info := c.table.get_type_symbol(node.typ).map_info()
|
2021-03-06 20:04:51 +01:00
|
|
|
c.ensure_type_exists(info.key_type, node.pos) or {}
|
|
|
|
c.ensure_type_exists(info.value_type, node.pos) or {}
|
2021-04-17 07:29:06 +02:00
|
|
|
node.key_type = c.unwrap_generic(info.key_type)
|
|
|
|
node.value_type = c.unwrap_generic(info.value_type)
|
2020-03-07 08:13:00 +01:00
|
|
|
return node.typ
|
|
|
|
}
|
2021-03-08 18:48:17 +01:00
|
|
|
if node.keys.len > 0 && node.vals.len > 0 {
|
|
|
|
// `{'age': 20}`
|
|
|
|
mut key0_type := c.table.mktyp(c.expr(node.keys[0]))
|
|
|
|
if node.keys[0].is_auto_deref_var() {
|
|
|
|
key0_type = key0_type.deref()
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2021-03-08 18:48:17 +01:00
|
|
|
mut val0_type := c.table.mktyp(c.expr(node.vals[0]))
|
|
|
|
if node.vals[0].is_auto_deref_var() {
|
|
|
|
val0_type = val0_type.deref()
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2021-03-08 18:48:17 +01:00
|
|
|
mut same_key_type := true
|
|
|
|
for i, key in node.keys {
|
|
|
|
if i == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
val := node.vals[i]
|
|
|
|
key_type := c.expr(key)
|
|
|
|
c.expected_type = val0_type
|
|
|
|
val_type := c.expr(val)
|
|
|
|
if !c.check_types(key_type, key0_type) {
|
|
|
|
msg := c.expected_msg(key_type, key0_type)
|
|
|
|
c.error('invalid map key: $msg', key.position())
|
|
|
|
same_key_type = false
|
|
|
|
}
|
|
|
|
if !c.check_types(val_type, val0_type) {
|
|
|
|
msg := c.expected_msg(val_type, val0_type)
|
|
|
|
c.error('invalid map value: $msg', val.position())
|
|
|
|
}
|
2021-01-01 17:23:32 +01:00
|
|
|
}
|
2021-03-08 18:48:17 +01:00
|
|
|
if same_key_type {
|
|
|
|
for i in 1 .. node.keys.len {
|
|
|
|
c.check_dup_keys(node, i)
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2021-04-17 07:29:06 +02:00
|
|
|
key0_type = c.unwrap_generic(key0_type)
|
|
|
|
val0_type = c.unwrap_generic(val0_type)
|
2021-04-15 01:44:11 +02:00
|
|
|
mut map_type := ast.new_type(c.table.find_or_register_map(key0_type, val0_type))
|
2021-03-08 18:48:17 +01:00
|
|
|
node.typ = map_type
|
|
|
|
node.key_type = key0_type
|
|
|
|
node.value_type = val0_type
|
|
|
|
return map_type
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2021-03-08 18:48:17 +01:00
|
|
|
return node.typ
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
|
2021-01-14 19:50:06 +01:00
|
|
|
// call this *before* calling error or warn
|
2020-07-04 21:24:44 +02:00
|
|
|
pub fn (mut c Checker) add_error_detail(s string) {
|
|
|
|
c.error_details << s
|
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
pub fn (mut c Checker) warn(s string, pos token.Position) {
|
2020-10-24 19:41:47 +02:00
|
|
|
allow_warnings := !(c.pref.is_prod || c.pref.warns_are_errors) // allow warnings only in dev builds
|
2021-03-22 18:43:06 +01:00
|
|
|
c.warn_or_error(s, pos, allow_warnings)
|
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-08-11 16:26:49 +02:00
|
|
|
if c.pref.translated && message.starts_with('mismatched types') {
|
|
|
|
// TODO move this
|
|
|
|
return
|
|
|
|
}
|
2020-04-16 15:32:11 +02:00
|
|
|
if c.pref.is_verbose {
|
|
|
|
print_backtrace()
|
|
|
|
}
|
2021-02-15 14:51:57 +01:00
|
|
|
msg := message.replace('`Array_', '`[]')
|
2020-08-12 03:54:14 +02:00
|
|
|
c.warn_or_error(msg, pos, false)
|
2020-04-03 20:17:53 +02:00
|
|
|
}
|
|
|
|
|
2021-02-01 20:08:25 +01:00
|
|
|
// check `to` has all fields of `from`
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (c &Checker) check_struct_signature(from ast.Struct, to ast.Struct) bool {
|
2021-02-01 20:08:25 +01:00
|
|
|
// Note: `to` can have extra fields
|
|
|
|
if from.fields.len == 0 {
|
2020-08-14 14:57:08 +02:00
|
|
|
return false
|
|
|
|
}
|
2021-02-18 10:30:43 +01:00
|
|
|
for field in from.fields {
|
2020-08-14 14:57:08 +02:00
|
|
|
filtered := to.fields.filter(it.name == field.name)
|
|
|
|
if filtered.len != 1 {
|
|
|
|
// field doesn't exist
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
counterpart := filtered[0]
|
|
|
|
if field.typ != counterpart.typ {
|
|
|
|
// field has different tye
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if field.is_pub != counterpart.is_pub {
|
|
|
|
// field is not public while the other one is
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if field.is_mut != counterpart.is_mut {
|
|
|
|
// field is not mutable while the other one is
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-03-22 18:43:06 +01:00
|
|
|
pub fn (mut c Checker) note(message string, pos token.Position) {
|
|
|
|
mut details := ''
|
|
|
|
if c.error_details.len > 0 {
|
|
|
|
details = c.error_details.join('\n')
|
|
|
|
c.error_details = []
|
|
|
|
}
|
|
|
|
wrn := errors.Notice{
|
|
|
|
reporter: errors.Reporter.checker
|
|
|
|
pos: pos
|
|
|
|
file_path: c.file.path
|
|
|
|
message: message
|
|
|
|
details: details
|
|
|
|
}
|
|
|
|
c.file.notices << wrn
|
|
|
|
c.notices << wrn
|
|
|
|
c.nr_notices++
|
|
|
|
}
|
|
|
|
|
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-07-04 21:24:44 +02:00
|
|
|
mut details := ''
|
2020-07-04 23:37:41 +02:00
|
|
|
if c.error_details.len > 0 {
|
2020-07-04 21:24:44 +02:00
|
|
|
details = c.error_details.join('\n')
|
2020-07-04 23:37:41 +02:00
|
|
|
c.error_details = []
|
2020-07-04 21:24:44 +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-07-04 21:24:44 +02:00
|
|
|
details: details
|
2020-04-17 17:16:14 +02:00
|
|
|
}
|
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-12-04 19:34:05 +01:00
|
|
|
if c.pref.fatal_errors {
|
|
|
|
exit(1)
|
|
|
|
}
|
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-07-04 21:24:44 +02:00
|
|
|
details: details
|
2020-04-13 01:56:01 +02:00
|
|
|
}
|
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
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
|
2020-06-28 18:56:11 +02:00
|
|
|
c.inside_sql = true
|
|
|
|
defer {
|
|
|
|
c.inside_sql = false
|
|
|
|
}
|
2021-01-22 23:28:26 +01:00
|
|
|
sym := c.table.get_type_symbol(node.table_expr.typ)
|
2021-04-02 00:57:09 +02:00
|
|
|
c.ensure_type_exists(node.table_expr.typ, node.pos) or { return ast.void_type }
|
2020-06-28 18:56:11 +02:00
|
|
|
c.cur_orm_ts = sym
|
2021-04-02 00:57:09 +02:00
|
|
|
info := sym.info as ast.Struct
|
2021-01-22 23:28:26 +01:00
|
|
|
fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, sym.name)
|
2021-02-04 20:28:33 +01:00
|
|
|
mut sub_structs := map[int]ast.SqlExpr{}
|
2021-03-19 21:51:52 +01:00
|
|
|
for f in fields.filter(c.table.type_symbols[int(it.typ)].kind == .struct_) {
|
2021-02-04 20:28:33 +01:00
|
|
|
mut n := ast.SqlExpr{
|
|
|
|
pos: node.pos
|
|
|
|
has_where: true
|
|
|
|
typ: f.typ
|
|
|
|
db_expr: node.db_expr
|
2021-04-02 00:57:09 +02:00
|
|
|
table_expr: ast.TypeNode{
|
2021-02-04 20:28:33 +01:00
|
|
|
pos: node.table_expr.pos
|
|
|
|
typ: f.typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tmp_inside_sql := c.inside_sql
|
|
|
|
c.sql_expr(mut n)
|
|
|
|
c.inside_sql = tmp_inside_sql
|
|
|
|
n.where_expr = ast.InfixExpr{
|
|
|
|
op: .eq
|
|
|
|
pos: n.pos
|
|
|
|
left: ast.Ident{
|
|
|
|
language: .v
|
|
|
|
tok_kind: .eq
|
|
|
|
scope: c.fn_scope
|
|
|
|
obj: ast.Var{}
|
|
|
|
mod: 'main'
|
|
|
|
name: 'id'
|
|
|
|
is_mut: false
|
|
|
|
kind: .unresolved
|
|
|
|
info: ast.IdentVar{}
|
|
|
|
}
|
|
|
|
right: ast.Ident{
|
|
|
|
language: .c
|
|
|
|
mod: 'main'
|
|
|
|
tok_kind: .eq
|
|
|
|
obj: ast.Var{}
|
|
|
|
is_mut: false
|
|
|
|
scope: c.fn_scope
|
|
|
|
info: ast.IdentVar{
|
2021-04-02 00:57:09 +02:00
|
|
|
typ: ast.int_type
|
2021-02-04 20:28:33 +01:00
|
|
|
}
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
left_type: ast.int_type
|
|
|
|
right_type: ast.int_type
|
2021-02-04 20:28:33 +01:00
|
|
|
auto_locked: ''
|
|
|
|
or_block: ast.OrExpr{}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub_structs[int(f.typ)] = n
|
|
|
|
}
|
2020-06-25 12:05:24 +02:00
|
|
|
node.fields = fields
|
2021-02-16 12:46:12 +01:00
|
|
|
node.sub_structs = sub_structs.move()
|
2020-06-17 04:05:13 +02:00
|
|
|
if node.has_where {
|
|
|
|
c.expr(node.where_expr)
|
|
|
|
}
|
2020-06-27 16:41:29 +02:00
|
|
|
if node.has_offset {
|
|
|
|
c.expr(node.offset_expr)
|
|
|
|
}
|
|
|
|
if node.has_limit {
|
|
|
|
c.expr(node.limit_expr)
|
|
|
|
}
|
2020-07-02 19:29:22 +02:00
|
|
|
if node.has_order {
|
|
|
|
c.expr(node.order_expr)
|
|
|
|
}
|
2020-06-21 23:09:17 +02:00
|
|
|
c.expr(node.db_expr)
|
2020-06-17 04:05:13 +02:00
|
|
|
return node.typ
|
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) ast.Type {
|
2021-04-27 14:28:57 +02:00
|
|
|
c.expr(node.db_expr)
|
|
|
|
mut typ := ast.void_type
|
|
|
|
for mut line in node.lines {
|
|
|
|
a := c.sql_stmt_line(mut line)
|
|
|
|
if a != ast.void_type {
|
|
|
|
typ = a
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type {
|
2020-06-28 18:56:11 +02:00
|
|
|
c.inside_sql = true
|
|
|
|
defer {
|
|
|
|
c.inside_sql = false
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
c.ensure_type_exists(node.table_expr.typ, node.pos) or { return ast.void_type }
|
2021-01-22 23:28:26 +01:00
|
|
|
table_sym := c.table.get_type_symbol(node.table_expr.typ)
|
2021-02-19 10:23:13 +01:00
|
|
|
c.cur_orm_ts = table_sym
|
2021-04-15 02:11:24 +02:00
|
|
|
if table_sym.info !is ast.Struct {
|
|
|
|
c.error('unknown type `$table_sym.name`', node.pos)
|
|
|
|
return ast.void_type
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
info := table_sym.info as ast.Struct
|
2021-01-22 23:28:26 +01:00
|
|
|
fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, table_sym.name)
|
2021-04-27 14:28:57 +02:00
|
|
|
mut sub_structs := map[int]ast.SqlStmtLine{}
|
2021-03-19 21:51:52 +01:00
|
|
|
for f in fields.filter(c.table.type_symbols[int(it.typ)].kind == .struct_) {
|
2021-04-27 14:28:57 +02:00
|
|
|
mut n := ast.SqlStmtLine{
|
2021-02-04 20:28:33 +01:00
|
|
|
pos: node.pos
|
|
|
|
kind: node.kind
|
2021-04-02 00:57:09 +02:00
|
|
|
table_expr: ast.TypeNode{
|
2021-02-04 20:28:33 +01:00
|
|
|
pos: node.table_expr.pos
|
|
|
|
typ: f.typ
|
|
|
|
}
|
|
|
|
object_var_name: '${node.object_var_name}.$f.name'
|
|
|
|
}
|
|
|
|
tmp_inside_sql := c.inside_sql
|
2021-04-27 14:28:57 +02:00
|
|
|
c.sql_stmt_line(mut n)
|
2021-02-04 20:28:33 +01:00
|
|
|
c.inside_sql = tmp_inside_sql
|
|
|
|
sub_structs[int(f.typ)] = n
|
|
|
|
}
|
2020-06-25 14:43:07 +02:00
|
|
|
node.fields = fields
|
2021-02-16 12:46:12 +01:00
|
|
|
node.sub_structs = sub_structs.move()
|
2020-06-27 16:41:29 +02:00
|
|
|
if node.kind == .update {
|
2020-06-25 22:23:19 +02:00
|
|
|
for expr in node.update_exprs {
|
|
|
|
c.expr(expr)
|
|
|
|
}
|
|
|
|
}
|
2021-03-31 10:13:15 +02:00
|
|
|
if node.where_expr !is ast.EmptyExpr {
|
|
|
|
c.expr(node.where_expr)
|
|
|
|
}
|
2021-02-04 20:28:33 +01:00
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
return ast.void_type
|
2020-06-19 16:43:32 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) fetch_and_verify_orm_fields(info ast.Struct, pos token.Position, table_name string) []ast.StructField {
|
|
|
|
fields := info.fields.filter((it.typ in [ast.string_type, ast.int_type, ast.bool_type]
|
2021-03-19 21:51:52 +01:00
|
|
|
|| c.table.type_symbols[int(it.typ)].kind == .struct_) && !it.attrs.contains('skip'))
|
2020-06-25 14:43:07 +02:00
|
|
|
if fields.len == 0 {
|
|
|
|
c.error('V orm: select: empty fields in `$table_name`', pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
return []ast.StructField{}
|
2020-06-25 14:43:07 +02:00
|
|
|
}
|
|
|
|
if fields[0].name != 'id' {
|
|
|
|
c.error('V orm: `id int` must be the first field in `$table_name`', pos)
|
|
|
|
}
|
|
|
|
return fields
|
|
|
|
}
|
|
|
|
|
2020-12-01 06:43:10 +01:00
|
|
|
fn (mut c Checker) post_process_generic_fns() {
|
2020-10-06 15:34:02 +02:00
|
|
|
// Loop thru each generic function concrete type.
|
|
|
|
// Check each specific fn instantiation.
|
2021-04-17 07:29:06 +02:00
|
|
|
// Check 2 times (in order to check nested generics fn)
|
|
|
|
for _ in 0 .. 2 {
|
|
|
|
for i in 0 .. c.file.generic_fns.len {
|
|
|
|
if c.table.fn_generic_types.len == 0 {
|
|
|
|
// no concrete types, so just skip:
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
mut node := c.file.generic_fns[i]
|
|
|
|
c.mod = node.mod
|
2021-04-22 17:21:01 +02:00
|
|
|
for generic_types in c.table.fn_generic_types[node.name] {
|
|
|
|
node.cur_generic_types = generic_types
|
2021-04-17 07:29:06 +02:00
|
|
|
c.fn_decl(mut node)
|
|
|
|
if node.name in ['vweb.run_app', 'vweb.run'] {
|
2021-04-22 17:21:01 +02:00
|
|
|
c.vweb_gen_types << generic_types
|
2021-04-17 07:29:06 +02:00
|
|
|
}
|
2020-11-08 09:14:24 +01:00
|
|
|
}
|
2021-04-17 07:29:06 +02:00
|
|
|
node.cur_generic_types = []
|
2020-05-21 21:51:49 +02:00
|
|
|
}
|
2020-10-06 15:34:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
|
|
|
c.returns = false
|
2021-04-16 10:53:20 +02:00
|
|
|
if node.generic_names.len > 0 && node.cur_generic_types.len == 0 {
|
2020-10-06 15:34:02 +02:00
|
|
|
// Just remember the generic function for now.
|
|
|
|
// It will be processed later in c.post_process_generic_fns,
|
|
|
|
// after all other normal functions are processed.
|
|
|
|
// This is done so that all generic function calls can
|
2021-04-15 10:00:23 +02:00
|
|
|
// have a chance to populate c.table.fn_generic_types with
|
2020-10-06 15:34:02 +02:00
|
|
|
// the correct concrete types.
|
2020-12-01 06:43:10 +01:00
|
|
|
c.file.generic_fns << node
|
2020-05-21 21:51:49 +02:00
|
|
|
return
|
|
|
|
}
|
2020-07-04 11:23:41 +02:00
|
|
|
if node.language == .v && !c.is_builtin_mod {
|
|
|
|
c.check_valid_snake_case(node.name, 'function name', node.pos)
|
2020-05-18 22:54:08 +02:00
|
|
|
}
|
2021-02-05 08:05:13 +01:00
|
|
|
if node.name == 'main.main' {
|
|
|
|
c.main_fn_decl_node = node
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.return_type != ast.void_type {
|
2021-04-23 14:51:52 +02:00
|
|
|
if ct_name := node.attrs.find_comptime_define() {
|
|
|
|
c.error('only functions that do NOT return values can have `[if $ct_name]` tags',
|
|
|
|
node.pos)
|
2021-01-27 13:54:33 +01:00
|
|
|
}
|
|
|
|
}
|
2020-07-04 11:23:41 +02:00
|
|
|
if node.is_method {
|
2020-07-23 23:16:36 +02:00
|
|
|
mut sym := c.table.get_type_symbol(node.receiver.typ)
|
2021-01-26 11:56:17 +01:00
|
|
|
if sym.kind == .array && !c.is_builtin_mod && node.name == 'map' {
|
2021-01-03 16:57:29 +01:00
|
|
|
// TODO `node.map in array_builtin_methods`
|
|
|
|
c.error('method overrides built-in array method', node.pos)
|
|
|
|
} else if sym.kind == .sum_type && node.name == 'type_name' {
|
2020-11-14 12:59:03 +01:00
|
|
|
c.error('method overrides built-in sum type method', node.pos)
|
2021-02-15 16:55:54 +01:00
|
|
|
} else if sym.kind == .multi_return {
|
|
|
|
c.error('cannot define method on multi-value', node.method_type_pos)
|
2020-11-14 12:59:03 +01:00
|
|
|
}
|
2021-01-28 11:05:37 +01:00
|
|
|
if sym.name.len == 1 {
|
|
|
|
// One letter types are reserved for generics.
|
2021-01-28 14:20:07 +01:00
|
|
|
c.error('unknown type `$sym.name`', node.receiver_pos)
|
2021-01-28 11:05:37 +01:00
|
|
|
return
|
|
|
|
}
|
2021-03-03 05:02:10 +01:00
|
|
|
// make sure interface does not implement its own interface methods
|
|
|
|
if sym.kind == .interface_ && sym.has_method(node.name) {
|
2021-04-02 00:57:09 +02:00
|
|
|
if sym.info is ast.Interface {
|
|
|
|
info := sym.info as ast.Interface
|
2021-03-03 05:02:10 +01:00
|
|
|
// if the method is in info.methods then it is an interface method
|
|
|
|
if info.has_method(node.name) {
|
|
|
|
c.error('interface `$sym.name` cannot implement its own interface method `$node.name`',
|
|
|
|
node.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-08 09:14:24 +01:00
|
|
|
// needed for proper error reporting during vweb route checking
|
|
|
|
sym.methods[node.method_idx].source_fn = voidptr(node)
|
2020-05-18 22:54:08 +02:00
|
|
|
}
|
2020-07-04 11:23:41 +02:00
|
|
|
if node.language == .v {
|
2020-05-18 22:54:08 +02:00
|
|
|
// Make sure all types are valid
|
2020-09-27 03:32:56 +02:00
|
|
|
for arg in node.params {
|
2021-02-19 10:23:13 +01:00
|
|
|
c.ensure_type_exists(arg.typ, node.pos) or { return }
|
2020-05-18 22:54:08 +02:00
|
|
|
}
|
|
|
|
}
|
2021-02-27 10:11:20 +01:00
|
|
|
if node.language == .v && node.name.after_char(`.`) == 'init' && !node.is_method
|
|
|
|
&& node.params.len == 0 {
|
|
|
|
if node.is_pub {
|
|
|
|
c.error('fn `init` must not be public', node.pos)
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.return_type != ast.void_type {
|
2021-02-27 10:11:20 +01:00
|
|
|
c.error('fn `init` cannot have a return type', node.pos)
|
|
|
|
}
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.return_type != ast.Type(0) {
|
2021-02-19 10:23:13 +01:00
|
|
|
c.ensure_type_exists(node.return_type, node.pos) or { return }
|
2021-02-13 01:21:05 +01:00
|
|
|
if node.language == .v && node.is_method && node.name == 'str' {
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.return_type != ast.string_type {
|
2021-02-13 01:21:05 +01:00
|
|
|
c.error('.str() methods should return `string`', node.pos)
|
|
|
|
}
|
|
|
|
if node.params.len != 1 {
|
|
|
|
c.error('.str() methods should have 0 arguments', node.pos)
|
|
|
|
}
|
2020-07-04 13:14:24 +02:00
|
|
|
}
|
2021-02-13 01:21:05 +01:00
|
|
|
if node.language == .v && node.is_method
|
|
|
|
&& node.name in ['+', '-', '*', '%', '/', '<', '=='] {
|
|
|
|
if node.params.len != 2 {
|
|
|
|
c.error('operator methods should have exactly 1 argument', node.pos)
|
2020-12-31 12:38:10 +01:00
|
|
|
} else {
|
2021-02-13 01:21:05 +01:00
|
|
|
receiver_sym := c.table.get_type_symbol(node.receiver.typ)
|
|
|
|
param_sym := c.table.get_type_symbol(node.params[1].typ)
|
|
|
|
if param_sym.kind !in [.struct_, .alias] || receiver_sym.kind !in [.struct_, .alias] {
|
|
|
|
c.error('operator methods are only allowed for struct and type alias',
|
2021-01-15 02:27:19 +01:00
|
|
|
node.pos)
|
2021-02-13 01:21:05 +01:00
|
|
|
} else {
|
|
|
|
parent_sym := c.table.get_final_type_symbol(node.receiver.typ)
|
|
|
|
if node.rec_mut {
|
|
|
|
c.error('receiver cannot be `mut` for operator overloading', node.receiver_pos)
|
|
|
|
} else if node.params[1].is_mut {
|
|
|
|
c.error('argument cannot be `mut` for operator overloading', node.pos)
|
|
|
|
} else if node.receiver.typ != node.params[1].typ {
|
|
|
|
c.error('expected `$receiver_sym.name` not `$param_sym.name` - both operands must be the same type for operator overloading',
|
|
|
|
node.params[1].type_pos)
|
2021-04-02 00:57:09 +02:00
|
|
|
} else if node.name in ['<', '=='] && node.return_type != ast.bool_type {
|
2021-02-13 01:21:05 +01:00
|
|
|
c.error('operator comparison methods should return `bool`', node.pos)
|
|
|
|
} else if parent_sym.is_primitive() {
|
|
|
|
c.error('cannot define operator methods on type alias for `$parent_sym.name`',
|
|
|
|
node.pos)
|
|
|
|
}
|
2020-12-31 12:38:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-25 18:26:11 +02:00
|
|
|
// TODO c.pref.is_vet
|
2021-03-05 12:19:39 +01:00
|
|
|
if node.language == .v && !node.is_method && node.params.len == 0 && node.is_test {
|
2021-01-30 09:42:18 +01:00
|
|
|
if !c.pref.is_test {
|
2020-11-20 12:32:46 +01:00
|
|
|
// simple heuristic
|
|
|
|
for st in node.stmts {
|
|
|
|
if st is ast.AssertStmt {
|
|
|
|
c.warn('tests will not be run, because filename does not end with `_test.v`',
|
|
|
|
node.pos)
|
|
|
|
break
|
|
|
|
}
|
2020-09-25 18:26:11 +02:00
|
|
|
}
|
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.return_type != ast.void_type_idx
|
|
|
|
&& node.return_type.clear_flag(.optional) != ast.void_type_idx {
|
2021-03-05 12:19:39 +01:00
|
|
|
c.error('test functions should either return nothing at all, or be marked to return `?`',
|
|
|
|
node.pos)
|
2020-11-20 12:32:46 +01:00
|
|
|
}
|
2020-09-25 18:26:11 +02:00
|
|
|
}
|
2021-04-02 00:57:09 +02:00
|
|
|
c.expected_type = ast.void_type
|
2020-07-08 16:01:17 +02:00
|
|
|
c.cur_fn = node
|
|
|
|
// Add return if `fn(...) ? {...}` have no return at end
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.return_type != ast.void_type && node.return_type.has_flag(.optional)
|
2021-01-23 09:33:22 +01:00
|
|
|
&& (node.stmts.len == 0 || node.stmts[node.stmts.len - 1] !is ast.Return) {
|
2020-07-08 16:01:17 +02:00
|
|
|
sym := c.table.get_type_symbol(node.return_type)
|
|
|
|
if sym.kind == .void {
|
|
|
|
node.stmts << ast.Return{
|
|
|
|
pos: node.pos
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-21 11:09:19 +01:00
|
|
|
c.fn_scope = node.scope
|
2020-07-04 11:23:41 +02:00
|
|
|
c.stmts(node.stmts)
|
2021-02-03 10:40:21 +01:00
|
|
|
node.has_return = c.returns || has_top_return(node.stmts)
|
2021-04-02 00:57:09 +02:00
|
|
|
if node.language == .v && !node.no_body && node.return_type != ast.void_type && !node.has_return
|
2021-04-20 10:49:06 +02:00
|
|
|
&& (node.is_method || node.name !in ['panic', 'exit']) {
|
2021-01-29 22:11:05 +01:00
|
|
|
if c.inside_anon_fn {
|
|
|
|
c.error('missing return at the end of an anonymous function', node.pos)
|
|
|
|
} else {
|
|
|
|
c.error('missing return at end of function `$node.name`', node.pos)
|
|
|
|
}
|
2020-05-18 22:54:08 +02:00
|
|
|
}
|
|
|
|
c.returns = false
|
2020-11-08 09:14:24 +01:00
|
|
|
node.source_file = c.file
|
2020-05-18 22:54:08 +02:00
|
|
|
}
|
2020-07-09 22:38:43 +02:00
|
|
|
|
|
|
|
fn has_top_return(stmts []ast.Stmt) bool {
|
2020-08-02 13:06:44 +02:00
|
|
|
for stmt in stmts {
|
2020-08-10 23:59:08 +02:00
|
|
|
if stmt is ast.Return {
|
|
|
|
return true
|
|
|
|
} else if stmt is ast.Block {
|
2020-08-02 13:06:44 +02:00
|
|
|
if has_top_return(stmt.stmts) {
|
|
|
|
return true
|
2020-07-09 22:38:43 +02:00
|
|
|
}
|
2020-08-10 23:59:08 +02:00
|
|
|
} else if stmt is ast.ExprStmt {
|
2020-11-21 00:05:57 +01:00
|
|
|
if stmt.expr is ast.CallExpr {
|
2021-04-20 10:49:06 +02:00
|
|
|
if !stmt.expr.is_method && stmt.expr.name in ['panic', 'exit'] {
|
2020-08-10 23:59:08 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
2020-07-09 22:38:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2020-11-08 09:14:24 +01:00
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) verify_vweb_params_for_method(m ast.Fn) (bool, int, int) {
|
2020-11-08 09:14:24 +01:00
|
|
|
margs := m.params.len - 1 // first arg is the receiver/this
|
|
|
|
if m.attrs.len == 0 {
|
|
|
|
// allow non custom routed methods, with 1:1 mapping
|
|
|
|
return true, -1, margs
|
|
|
|
}
|
|
|
|
mut route_attributes := 0
|
|
|
|
for a in m.attrs {
|
|
|
|
if a.name.starts_with('/') {
|
|
|
|
route_attributes += a.name.count(':')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return route_attributes == margs, route_attributes, margs
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) verify_all_vweb_routes() {
|
|
|
|
if c.vweb_gen_types.len == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
typ_vweb_result := c.table.find_type_idx('vweb.Result')
|
2021-04-19 18:01:47 +02:00
|
|
|
old_file := c.file
|
2020-11-08 09:14:24 +01:00
|
|
|
for vgt in c.vweb_gen_types {
|
|
|
|
sym_app := c.table.get_type_symbol(vgt)
|
|
|
|
for m in sym_app.methods {
|
2020-12-06 04:55:08 +01:00
|
|
|
if m.return_type == typ_vweb_result {
|
2020-11-08 09:14:24 +01:00
|
|
|
is_ok, nroute_attributes, nargs := c.verify_vweb_params_for_method(m)
|
|
|
|
if !is_ok {
|
2021-01-05 19:26:48 +01:00
|
|
|
f := &ast.FnDecl(m.source_fn)
|
2020-11-08 09:14:24 +01:00
|
|
|
if isnil(f) {
|
|
|
|
continue
|
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
if f.return_type == typ_vweb_result && f.receiver.typ == m.params[0].typ
|
|
|
|
&& f.name == m.name {
|
2021-04-19 18:01:47 +02:00
|
|
|
c.change_current_file(f.source_file) // setup of file path for the warning
|
2020-11-08 09:14:24 +01:00
|
|
|
c.warn('mismatched parameters count between vweb method `${sym_app.name}.$m.name` ($nargs) and route attribute $m.attrs ($nroute_attributes)',
|
|
|
|
f.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-19 18:01:47 +02:00
|
|
|
c.change_current_file(old_file)
|
2020-11-08 09:14:24 +01:00
|
|
|
}
|
2021-01-15 19:28:18 +01:00
|
|
|
|
|
|
|
fn (mut c Checker) trace(fbase string, message string) {
|
|
|
|
if c.file.path_base == fbase {
|
|
|
|
println('> c.trace | ${fbase:-10s} | $message')
|
|
|
|
}
|
|
|
|
}
|
2021-02-19 10:23:13 +01:00
|
|
|
|
2021-04-02 00:57:09 +02:00
|
|
|
fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Position) ? {
|
2021-02-19 10:23:13 +01:00
|
|
|
if typ == 0 {
|
|
|
|
c.error('unknown type', pos)
|
2021-04-20 11:11:41 +02:00
|
|
|
return none
|
2021-02-19 10:23:13 +01:00
|
|
|
}
|
|
|
|
sym := c.table.get_type_symbol(typ)
|
|
|
|
match sym.kind {
|
|
|
|
.placeholder {
|
|
|
|
if sym.language == .v && !sym.name.starts_with('C.') {
|
|
|
|
c.error(util.new_suggestion(sym.name, c.table.known_type_names()).say('unknown type `$sym.name`'),
|
|
|
|
pos)
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.int_literal, .float_literal {
|
|
|
|
// Separate error condition for `int_literal` and `float_literal` because `util.suggestion` may give different
|
|
|
|
// suggestions due to f32 comparision issue.
|
|
|
|
if !c.is_builtin_mod {
|
|
|
|
msg := if sym.kind == .int_literal {
|
|
|
|
'unknown type `$sym.name`.\nDid you mean `int`?'
|
|
|
|
} else {
|
|
|
|
'unknown type `$sym.name`.\nDid you mean `f64`?'
|
|
|
|
}
|
|
|
|
c.error(msg, pos)
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.array {
|
2021-04-02 00:57:09 +02:00
|
|
|
c.ensure_type_exists((sym.info as ast.Array).elem_type, pos) ?
|
2021-02-19 10:23:13 +01:00
|
|
|
}
|
|
|
|
.map {
|
2021-04-02 00:57:09 +02:00
|
|
|
info := sym.info as ast.Map
|
2021-02-19 10:23:13 +01:00
|
|
|
c.ensure_type_exists(info.key_type, pos) ?
|
|
|
|
c.ensure_type_exists(info.value_type, pos) ?
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|