2022-01-04 10:21:08 +01:00
// Copyright (c) 2019-2022 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
2022-01-27 10:50:26 +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
2021-07-27 11:35:54 +02:00
import v . util . version
2020-04-27 15:08:04 +02:00
import v . errors
2020-10-29 10:57:23 +01:00
import v . pkgconfig
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
2022-02-17 09:04:01 +01:00
// prevent stack overflows by restricting too deep recursion:
const expr_level_cutoff_limit = 40
const stmt_level_cutoff_limit = 40
2022-02-17 10:46:04 +01:00
const iface_level_cutoff_limit = 100
2022-02-17 09:04:01 +01:00
2022-02-19 14:22:33 +01:00
pub const (
2021-12-04 18:43:19 +01:00
valid_comptime_if_os = [ ' w i n d o w s ' , ' i o s ' , ' m a c o s ' , ' m a c h ' , ' d a r w i n ' , ' h p u x ' , ' g n u ' ,
2021-04-19 18:01:47 +02:00
' q n x ' , ' l i n u x ' , ' f r e e b s d ' , ' o p e n b s d ' , ' n e t b s d ' , ' b s d ' , ' d r a g o n f l y ' , ' a n d r o i d ' , ' s o l a r i s ' ,
2021-07-13 10:09:32 +02:00
' h a i k u ' , ' s e r e n i t y ' , ' v i n i x ' ]
2021-12-04 18:43:19 +01:00
valid_comptime_compression_types = [ ' n o n e ' , ' z l i b ' ]
valid_comptime_if_compilers = [ ' g c c ' , ' t i n y c ' , ' c l a n g ' , ' m i n g w ' , ' m s v c ' , ' c p l u s p l u s ' ]
valid_comptime_if_platforms = [ ' a m d 6 4 ' , ' i 3 8 6 ' , ' a a r c h 6 4 ' , ' a r m 6 4 ' , ' a r m 3 2 ' , ' r v 6 4 ' , ' r v 3 2 ' ]
valid_comptime_if_cpu_features = [ ' x 6 4 ' , ' x 3 2 ' , ' l i t t l e _ e n d i a n ' , ' b i g _ e n d i a n ' ]
2022-01-07 12:38:21 +01:00
valid_comptime_if_other = [ ' a p k ' , ' j s ' , ' d e b u g ' , ' p r o d ' , ' t e s t ' , ' g l i b c ' , ' p r e a l l o c ' ,
2021-12-08 09:09:10 +01:00
' n o _ b o u n d s _ c h e c k i n g ' , ' f r e e s t a n d i n g ' , ' t h r e a d s ' , ' j s _ n o d e ' , ' j s _ b r o w s e r ' , ' j s _ f r e e s t a n d i n g ' ,
2022-01-21 02:26:05 +01:00
' i n t e r p r e t e r ' , ' e s 5 ' , ' p r o f i l e ' ]
2021-12-04 18:43:19 +01:00
valid_comptime_not_user_defined = all_valid_comptime_idents ( )
array_builtin_methods = [ ' f i l t e r ' , ' c l o n e ' , ' r e p e a t ' , ' r e v e r s e ' , ' m a p ' , ' s l i c e ' ,
2021-11-15 14:47:29 +01:00
' s o r t ' , ' c o n t a i n s ' , ' i n d e x ' , ' w a i t ' , ' a n y ' , ' a l l ' , ' f i r s t ' , ' l a s t ' , ' p o p ' ]
2021-12-04 18:43:19 +01:00
reserved_type_names = [ ' b o o l ' , ' c h a r ' , ' i 8 ' , ' i 1 6 ' , ' i n t ' , ' i 6 4 ' , ' b y t e ' , ' u 1 6 ' ,
2021-11-22 15:51:58 +01:00
' u 3 2 ' , ' u 6 4 ' , ' f 3 2 ' , ' f 6 4 ' , ' m a p ' , ' s t r i n g ' , ' r u n e ' ]
2021-12-04 18:43:19 +01:00
vroot_is_deprecated_message = ' @ V R O O T i s d e p r e c a t e d , u s e @ V M O D R O O T o r @ V E X E R O O T i n s t e a d '
2020-09-18 00:58:54 +02:00
)
2021-06-17 10:38:42 +02:00
fn all_valid_comptime_idents ( ) [ ] string {
mut res := [ ] string { }
2021-11-15 14:47:29 +01:00
res << checker . valid_comptime_if_os
res << checker . valid_comptime_if_compilers
res << checker . valid_comptime_if_platforms
res << checker . valid_comptime_if_cpu_features
res << checker . valid_comptime_if_other
2021-06-17 10:38:42 +02:00
return res
}
2021-05-22 17:59:17 +02:00
[ heap ]
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
2021-05-11 08:30:01 +02:00
expected_or_type ast . Type // fn() or { 'this type' } eg. string. expected or block type
2021-12-23 14:02:09 +01:00
mod string // current module name
2021-05-31 16:14:45 +02:00
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
2022-02-11 21:00:13 +01:00
should_abort bool // when too many errors/warnings/notices are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time.
returns bool
scope_returns bool
is_builtin_mod bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this
is_generated bool // true for `[generated] module xyz` .v files
inside_unsafe bool // true inside `unsafe {}` blocks
inside_const bool // true inside `const ( ... )` blocks
inside_anon_fn bool // true inside `fn() { ... }()`
inside_ref_lit bool // true inside `a := &something`
inside_defer bool // true inside `defer {}` blocks
inside_fn_arg bool // `a`, `b` in `a.f(b)`
inside_ct_attr bool // true inside `[if expr]`
inside_comptime_for_field bool
skip_flags bool // should `#flag` and `#include` be skipped
fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
2022-03-03 15:36:40 +01:00
smartcast_mut_pos token . Pos
2022-02-11 21:00:13 +01:00
ct_cond_stack [ ] ast . Expr
2020-06-08 13:10:47 +02:00
mut :
2021-11-27 06:38:35 +01:00
stmt_level int // the nesting level inside each stmts list;
// .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1`
// 1 for statements directly at each inner scope level;
// increases for `x := if cond { statement_list1} else {statement_list2}`;
// increases for `x := optfn() or { statement_list3 }`;
2021-02-05 08:05:13 +01:00
files [ ] ast . File
2021-12-23 14:02:09 +01:00
expr_level int // to avoid infinite recursion segfaults due to compiler bugs
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**
loop_label string // set when inside a labelled for loop
2021-12-23 14:02:09 +01:00
vweb_gen_types [ ] ast . Type // vweb route checks
2021-12-05 10:55:41 +01:00
timers & util . Timers = util . get_timers ( )
2021-12-12 02:18:29 +01:00
comptime_fields_default_type ast . Type
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
main_fn_decl_node ast . FnDecl
2021-03-15 05:22:22 +01:00
match_exhaustive_cutoff_limit int = 10
2021-12-23 14:02:09 +01:00
is_last_stmt bool
prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type, stopping unwrapping then
using_new_err_struct bool
need_recheck_generic_fns bool // need recheck generic fns because there are cascaded nested generic fn
inside_sql bool // to handle sql table fields pseudo variables
inside_selector_expr bool
inside_println_arg bool
inside_decl_rhs bool
inside_if_guard bool // true inside the guard condition of `if x := opt() {}`
2020-01-18 23:26:14 +01:00
}
2021-05-22 17:59:17 +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
}
2021-05-22 17:59:17 +02:00
return & Checker {
2020-01-18 23:26:14 +01:00
table : table
2020-04-03 11:01:09 +02:00
pref : pref
2021-12-05 10:55:41 +01:00
timers : util . new_timers ( should_print : timers_should_print , label : ' c h e c k e r ' )
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
}
}
2021-11-27 06:38:35 +01:00
fn ( mut c Checker ) reset_checker_state_at_start_of_new_file ( ) {
c . expected_type = ast . void_type
c . expected_or_type = ast . void_type
c . const_decl = ' '
c . in_for_count = 0
c . returns = false
c . scope_returns = false
c . mod = ' '
c . is_builtin_mod = false
c . inside_unsafe = false
c . inside_const = false
c . inside_anon_fn = false
c . inside_ref_lit = false
c . inside_defer = false
c . inside_fn_arg = false
c . inside_ct_attr = false
c . skip_flags = false
c . fn_level = 0
c . expr_level = 0
c . stmt_level = 0
c . inside_sql = false
c . cur_orm_ts = ast . TypeSymbol { }
c . prevent_sum_type_unwrapping_once = false
c . loop_label = ' '
c . using_new_err_struct = false
c . inside_selector_expr = false
c . inside_println_arg = false
c . inside_decl_rhs = false
c . inside_if_guard = false
}
2021-10-17 05:41:39 +02:00
pub fn ( mut c Checker ) check ( ast_file_ & ast . File ) {
mut ast_file := ast_file_
2021-11-27 06:38:35 +01:00
c . reset_checker_state_at_start_of_new_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 {
2021-06-26 17:09:52 +02:00
for sym in ast_import . syms {
full_name := ast_import . mod + ' . ' + sym . name
if full_name in c . const_names {
c . error ( ' c a n n o t s e l e c t i v e l y i m p o r t c o n s t a n t ` $ sym . name ` f r o m ` $ ast_import . mod ` , i m p o r t ` $ ast_import . mod ` a n d u s e ` $ full_name ` i n s t e a d ' ,
sym . pos )
}
}
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-05-21 05:12:38 +02:00
c . error ( ' ` $ ast_import . mod ` w a s a l r e a d y i m p o r t e d o n l i n e $ {
ast_file . imports [ j ] . mod_pos . line_nr + 1 } ' , a s t _ i m p o r t . m o d _ p o s )
2020-04-30 09:33:12 +02:00
}
}
}
2021-11-27 06:38:35 +01:00
c . stmt_level = 0
2021-07-31 15:35:19 +02:00
for mut stmt in ast_file . stmts {
2021-09-16 06:08:14 +02:00
if stmt in [ ast . ConstDecl , ast . ExprStmt ] {
2021-07-31 15:35:19 +02:00
c . expr_level = 0
c . stmt ( stmt )
}
2021-08-15 12:41:51 +02:00
if c . should_abort {
return
}
2021-07-31 15:35:19 +02:00
}
2021-11-27 06:38:35 +01:00
//
c . stmt_level = 0
2021-07-31 15:35:19 +02:00
for mut stmt in ast_file . stmts {
if stmt is ast . GlobalDecl {
c . expr_level = 0
c . stmt ( stmt )
}
2021-08-15 12:41:51 +02:00
if c . should_abort {
return
}
2021-07-31 15:35:19 +02:00
}
2021-11-27 06:38:35 +01:00
//
c . stmt_level = 0
2021-07-31 15:35:19 +02:00
for mut stmt in ast_file . stmts {
if stmt ! is ast . ConstDecl && stmt ! is ast . GlobalDecl && stmt ! is ast . ExprStmt {
c . expr_level = 0
c . stmt ( stmt )
}
2021-08-15 12:41:51 +02:00
if c . should_abort {
return
}
2020-01-18 23:26:14 +01:00
}
2021-11-27 06:38:35 +01:00
//
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 ) {
2021-05-12 17:03:01 +02:00
if ! c . pref . is_repl && ! c . file . is_test {
for _ , obj in sc . objects {
match obj {
ast . Var {
if ! obj . is_used && obj . name [ 0 ] != ` _ ` {
2020-10-24 19:29:24 +02:00
c . warn ( ' u n u s e d v a r i a b l e : ` $ obj . name ` ' , obj . pos )
2020-06-20 04:42:08 +02:00
}
2021-05-12 17:03:01 +02:00
if obj . is_mut && ! obj . is_changed && ! c . is_builtin_mod && obj . name != ' i t ' {
// 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
}
2021-05-12 17:03:01 +02:00
else { }
2020-06-02 09:00:51 +02:00
}
}
}
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 ) {
2021-05-07 14:58:48 +02:00
c . file = unsafe { file }
2021-04-19 18:01:47 +02:00
c . vmod_file_content = ' '
c . mod = file . mod . name
2021-12-11 23:58:38 +01:00
c . is_generated = file . is_generated
2021-04-19 18:01:47 +02:00
}
2021-05-22 17:59:17 +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 {
2021-10-17 05:41:39 +02:00
mut file := unsafe { ast_files [ i ] }
2020-12-19 11:24:29 +01:00
c . timers . start ( ' c h e c k e r _ c h e c k $ file . path ' )
2020-10-05 17:40:51 +02:00
c . check ( file )
if file . mod . name == ' m a i n ' {
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 ( ' c h e c k e r _ c h e c k $ 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 : ' m a i n . m a i n '
mod : ' m a i n '
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 ( ' c h e c k e r _ p o s t _ p r o c e s s _ g e n e r i c _ f n s ' )
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
2021-12-28 18:42:31 +01:00
// checked, to ensure all generic calls are processed, as this information
// is needed when the generic type is auto inferred from the call argument.
// we may have to loop several times, if there were more concrete types found.
mut post_process_generic_fns_iterations := 0
2021-05-02 18:30:57 +02:00
for {
2021-12-28 18:42:31 +01:00
$ if trace_post_process_generic_fns_loop ? {
eprintln ( ' > > > > > > > > > r e c h e c k _ g e n e r i c _ f n s l o o p i t e r a t i o n : $ post_process_generic_fns_iterations ' )
}
2021-05-11 02:43:26 +02:00
for file in ast_files {
2021-04-29 16:37:54 +02:00
if file . generic_fns . len > 0 {
2021-12-28 18:42:31 +01:00
$ if trace_post_process_generic_fns_loop ? {
eprintln ( ' > > f i l e . p a t h : $ { file . path : - 40 } | f i l e . g e n e r i c _ f n s : ' +
file . generic_fns . map ( it . name ) . str ( ) )
}
2021-05-22 17:59:17 +02:00
c . change_current_file ( file )
2021-04-29 16:37:54 +02:00
c . post_process_generic_fns ( )
}
2020-12-01 05:10:06 +01:00
}
2021-05-11 02:43:26 +02:00
if ! c . need_recheck_generic_fns {
2021-05-02 18:30:57 +02:00
break
}
2021-05-11 02:43:26 +02:00
c . need_recheck_generic_fns = false
2021-12-28 18:42:31 +01:00
post_process_generic_fns_iterations ++
}
$ if trace_post_process_generic_fns_loop ? {
eprintln ( ' > > > > > > > > > r e c h e c k _ g e n e r i c _ f n s l o o p d o n e , i t e r a t i o n : $ post_process_generic_fns_iterations ' )
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 ( ' c h e c k e r _ p o s t _ p r o c e s s _ g e n e r i c _ f n s ' )
2021-05-11 02:43:26 +02:00
2020-12-19 11:24:29 +01:00
c . timers . start ( ' c h e c k e r _ v e r i f y _ a l l _ v w e b _ r o u t e s ' )
2020-11-08 09:14:24 +01:00
c . verify_all_vweb_routes ( )
2020-12-19 11:24:29 +01:00
c . timers . show ( ' c h e c k e r _ v e r i f y _ a l l _ v w e b _ r o u t e s ' )
2021-05-11 02:43:26 +02:00
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 ( ' T h e n a m e o f a t e s t f u n c t i o n i n V , s h o u l d s t a r t w i t h ` t e s t _ ` . ' )
c . add_error_detail ( ' T h e t e s t f u n c t i o n s h o u l d t a k e 0 p a r a m e t e r s , a n d n o r e t u r n t y p e . E x a m p l e : ' )
c . add_error_detail ( ' f n t e s t _ x y z ( ) { a s s e r t 2 + 2 = = 4 } ' )
2022-01-26 11:36:28 +01:00
c . error ( ' a _ t e s t . v f i l e s h o u l d h a v e * a t l e a s t * o n e ` t e s t _ ` f u n c t i o n ' , token . Pos { } )
2021-01-30 09:19:29 +01:00
}
}
2020-04-10 22:27:51 +02:00
// Make sure fn main is defined in non lib builds
2020-04-10 22:32:52 +02:00
if c . pref . build_mode == . build_module || c . pref . is_test {
2020-04-10 22:27:51 +02:00
return
}
2020-04-18 17:46:23 +02:00
if c . pref . is_shared {
2020-04-16 15:30:19 +02:00
// shared libs do not need to have a main
2020-04-11 02:24:00 +02:00
return
}
2021-10-23 11:53:53 +02:00
if c . pref . no_builtin {
// `v -no-builtin module/` do not necessarily need to have a `main` function
// This is useful for compiling linux kernel modules for example.
return
}
2020-05-06 00:09:46 +02:00
if ! has_main_mod_file {
2020-05-06 12:57:40 +02:00
c . error ( ' p r o j e c t m u s t i n c l u d e a ` m a i n ` m o d u l e o r b e a s h a r e d l i b r a r y ( c o m p i l e w i t h ` v - s h a r e d ` ) ' ,
2022-01-26 11:36:28 +01:00
token . Pos { } )
2020-05-06 00:09:46 +02:00
} else if ! has_main_fn {
2022-01-26 11:36:28 +01:00
c . error ( ' f u n c t i o n ` m a i n ` m u s t b e d e c l a r e d i n t h e m a i n m o d u l e ' , token . Pos { } )
2020-04-18 00:20:38 +02:00
}
}
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-05-21 05:14:57 +02: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 == ' m a i n . m a i n ' {
if has_main_fn {
c . error ( ' f u n c t i o n ` m a i n ` i s a l r e a d y d e f i n e d ' , stmt . pos )
}
has_main_fn = true
if stmt . params . len > 0 {
c . error ( ' f u n c t i o n ` m a i n ` c a n n o t h a v e a r g u m e n t s ' , 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 ( ' f u n c t i o n ` m a i n ` c a n n o t r e t u r n v a l u e s ' , stmt . pos )
}
if stmt . no_body {
c . error ( ' f u n c t i o n ` m a i n ` m u s t d e c l a r e a b o d y ' , stmt . pos )
2020-04-25 15:57:11 +02:00
}
2021-01-27 13:54:33 +01:00
} else if stmt . attrs . contains ( ' c o n s o l e ' ) {
c . error ( ' o n l y ` m a i n ` c a n h a v e t h e ` [ c o n s o l e ] ` a t t r i b u t e ' , 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
}
2022-01-26 11:36:28 +01:00
fn ( mut c Checker ) check_valid_snake_case ( name string , identifier string , pos token . Pos ) {
2022-02-05 23:16:02 +01:00
if c . pref . translated || c . file . is_translated {
return
}
if ! c . pref . is_vweb && name . len > 0 && ( name [ 0 ] == ` _ ` || name . contains ( ' . _ ' ) ) {
2020-05-16 16:12:23 +02:00
c . error ( ' $ identifier ` $ name ` c a n n o t s t a r t w i t h ` _ ` ' , pos )
}
2022-02-05 23:16:02 +01:00
if ! c . pref . experimental && util . contains_capital ( name ) {
2020-05-16 16:12:23 +02:00
c . error ( ' $ identifier ` $ name ` c a n n o t c o n t a i n u p p e r c a s e l e t t e r s , u s e s n a k e _ c a s e i n s t e a d ' ,
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 ) .. ]
}
2022-01-26 11:36:28 +01:00
fn ( mut c Checker ) check_valid_pascal_case ( name string , identifier string , pos token . Pos ) {
2020-07-01 00:53:53 +02:00
sname := stripped_name ( name )
2022-02-05 23:16:02 +01:00
if sname . len > 0 && ! sname [ 0 ] . is_capital ( ) && ! c . pref . translated && ! c . file . is_translated {
2020-05-16 16:12:23 +02:00
c . error ( ' $ identifier ` $ name ` m u s t b e g i n w i t h c a p i t a l l e t t e r ' , 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 ) {
2021-05-27 21:40:32 +02:00
// TODO Remove when `u8` isn't an alias in builtin anymore
if c . file . mod . name != ' b u i l t i n ' {
2021-03-10 19:18:48 +01:00
c . check_valid_pascal_case ( node . name , ' t y p e a l i a s ' , node . pos )
}
2021-05-16 03:51:23 +02:00
c . ensure_type_exists ( node . parent_type , node . type_pos ) or { return }
2021-12-19 17:25:18 +01:00
typ_sym := c . table . sym ( node . parent_type )
2021-03-10 19:18:48 +01:00
if typ_sym . kind in [ . placeholder , . int_literal , . float_literal ] {
2021-05-16 03:51:23 +02:00
c . error ( ' u n k n o w n t y p e ` $ typ_sym . name ` ' , node . type_pos )
2021-03-10 19:18:48 +01:00
} else if typ_sym . kind == . alias {
2021-12-19 17:25:18 +01:00
orig_sym := c . table . sym ( ( typ_sym . info as ast . Alias ) . parent_type )
2021-03-10 19:18:48 +01:00
c . error ( ' t y p e ` $ typ_sym . str ( ) ` i s a n a l i a s , u s e t h e o r i g i n a l a l i a s t y p e ` $ orig_sym . name ` i n s t e a d ' ,
2021-05-16 03:51:23 +02:00
node . type_pos )
2021-03-10 19:18:48 +01:00
} else if typ_sym . kind == . chan {
2021-05-16 03:51:23 +02:00
c . error ( ' a l i a s e s o f ` c h a n ` t y p e s a r e n o t a l l o w e d . ' , node . type_pos )
2021-03-10 19:18:48 +01:00
}
}
pub fn ( mut c Checker ) fn_type_decl ( node ast . FnTypeDecl ) {
c . check_valid_pascal_case ( node . name , ' f n t y p e ' , node . pos )
2021-12-19 17:25:18 +01:00
typ_sym := c . table . sym ( 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
2021-05-16 03:51:23 +02:00
c . ensure_type_exists ( fn_info . return_type , fn_info . return_type_pos ) or { }
2021-12-19 17:25:18 +01:00
ret_sym := c . table . sym ( fn_info . return_type )
2021-03-10 19:18:48 +01:00
if ret_sym . kind == . placeholder {
2021-05-16 03:51:23 +02:00
c . error ( ' u n k n o w n t y p e ` $ ret_sym . name ` ' , fn_info . return_type_pos )
2021-03-10 19:18:48 +01:00
}
for arg in fn_info . params {
2021-05-16 03:51:23 +02:00
c . ensure_type_exists ( arg . typ , arg . type_pos ) or { return }
2021-12-19 17:25:18 +01:00
arg_sym := c . table . sym ( arg . typ )
2021-03-10 19:18:48 +01:00
if arg_sym . kind == . placeholder {
2021-05-16 03:51:23 +02:00
c . error ( ' u n k n o w n t y p e ` $ arg_sym . name ` ' , arg . type_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 , ' s u m t y p e ' , node . pos )
mut names_used := [ ] string { }
for variant in node . variants {
if variant . typ . is_ptr ( ) {
c . error ( ' s u m t y p e c a n n o t h o l d a r e f e r e n c e t y p e ' , variant . pos )
2020-04-26 06:40:54 +02:00
}
2021-05-16 03:51:23 +02:00
c . ensure_type_exists ( variant . typ , variant . pos ) or { }
2021-12-19 17:25:18 +01:00
mut sym := c . table . sym ( variant . typ )
2021-03-10 19:18:48 +01:00
if sym . name in names_used {
c . error ( ' s u m t y p e $ node . name c a n n o t h o l d t h e t y p e ` $ sym . name ` m o r e t h a n o n c e ' ,
variant . pos )
} else if sym . kind in [ . placeholder , . int_literal , . float_literal ] {
2021-05-16 03:51:23 +02:00
c . error ( ' u n k n o w n t y p e ` $ sym . name ` ' , variant . pos )
2021-11-20 20:28:11 +01:00
} else if sym . kind == . interface_ && sym . language != . js {
2021-03-10 19:18:48 +01:00
c . error ( ' s u m t y p e c a n n o t h o l d a n i n t e r f a c e ' , variant . pos )
2021-11-20 20:28:11 +01:00
} else if sym . kind == . struct_ && sym . language == . js {
c . error ( ' s u m t y p e c a n n o t h o l d a n J S s t r u c t ' , variant . pos )
2020-11-11 09:18:15 +01:00
}
2022-01-05 11:49:22 +01:00
if sym . name . trim_string_left ( sym . mod + ' . ' ) == node . name {
2021-04-29 08:04:02 +02:00
c . error ( ' s u m t y p e c a n n o t h o l d i t s e l f ' , variant . pos )
}
2021-03-10 19:18:48 +01:00
names_used << sym . name
2020-04-26 06:40:54 +02:00
}
}
2021-05-02 02:00:47 +02:00
pub fn ( mut c Checker ) expand_iface_embeds ( idecl & ast . InterfaceDecl , level int , iface_embeds [ ] ast . InterfaceEmbedding ) [ ] ast . InterfaceEmbedding {
// eprintln('> expand_iface_embeds: idecl.name: $idecl.name | level: $level | iface_embeds.len: $iface_embeds.len')
2022-02-17 09:04:01 +01:00
if level > checker . iface_level_cutoff_limit {
2021-05-02 02:00:47 +02:00
c . error ( ' t o o m a n y i n t e r f a c e e m b e d d i n g l e v e l s : $ level , f o r i n t e r f a c e ` $ idecl . name ` ' ,
idecl . pos )
return [ ]
}
if iface_embeds . len == 0 {
return [ ]
}
mut res := map [ int ] ast . InterfaceEmbedding { }
mut ares := [ ] ast . InterfaceEmbedding { }
for ie in iface_embeds {
if iface_decl := c . table . interfaces [ ie . typ ] {
2022-02-15 10:17:39 +01:00
mut list := iface_decl . embeds
if ! iface_decl . are_embeds_expanded {
list = c . expand_iface_embeds ( idecl , level + 1 , iface_decl . embeds )
c . table . interfaces [ ie . typ ] . embeds = list
c . table . interfaces [ ie . typ ] . are_embeds_expanded = true
2021-05-02 02:00:47 +02:00
}
for partial in list {
res [ partial . typ ] = partial
}
2021-02-19 10:23:13 +01:00
}
2021-05-02 02:00:47 +02:00
res [ ie . typ ] = ie
2020-05-16 16:12:23 +02:00
}
2021-05-02 02:00:47 +02:00
for _ , v in res {
ares << v
}
return ares
}
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 { ' d i v i s i o n ' } else { ' m o d u l o ' }
c . error ( ' $ oper b y z e r o ' , expr . pos )
}
}
ast . IntegerLiteral {
if expr . val . int ( ) == 0 {
oper := if op_kind == . div { ' d i v i s i o n ' } else { ' m o d u l o ' }
c . error ( ' $ oper b y z e r o ' , expr . pos )
}
}
ast . CastExpr {
c . check_div_mod_by_zero ( expr . expr , op_kind )
}
else { }
}
}
2021-06-08 23:06:29 +02:00
pub fn ( mut c Checker ) infix_expr ( mut node ast . InfixExpr ) ast . Type {
2020-05-14 17:15:25 +02:00
former_expected_type := c . expected_type
defer {
c . expected_type = former_expected_type
}
2022-02-04 23:11:24 +01:00
mut left_type := c . expr ( node . left )
2021-06-08 23:06:29 +02:00
node . left_type = left_type
2020-02-29 18:25:38 +01:00
c . expected_type = left_type
2022-02-04 23:11:24 +01:00
mut right_type := c . expr ( node . right )
2021-06-08 23:06:29 +02:00
node . 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 ] {
2021-06-08 23:06:29 +02:00
node . right_type = left_type
2021-04-19 14:38:48 +02:00
}
if right_type . is_number ( ) && ! right_type . is_ptr ( )
&& left_type in [ ast . int_literal_type , ast . float_literal_type ] {
2021-06-08 23:06:29 +02:00
node . left_type = right_type
2021-04-19 14:38:48 +02:00
}
2021-12-19 17:25:18 +01:00
mut right_sym := c . table . sym ( right_type )
right_final := c . table . final_sym ( right_type )
mut left_sym := c . table . sym ( left_type )
left_final := c . table . final_sym ( left_type )
2022-01-26 11:36:28 +01:00
left_pos := node . left . pos ( )
right_pos := node . right . pos ( )
2021-02-19 11:38:41 +01:00
left_right_pos := left_pos . extend ( right_pos )
2021-07-21 22:39:49 +02:00
if left_type . is_any_kind_of_pointer ( )
&& node . op in [ . plus , . minus , . mul , . div , . mod , . xor , . amp , . pipe ] {
if ( right_type . is_any_kind_of_pointer ( ) && node . op != . minus )
|| ( ! right_type . is_any_kind_of_pointer ( ) && node . op ! in [ . plus , . minus ] ) {
left_name := c . table . type_to_str ( left_type )
right_name := c . table . type_to_str ( right_type )
c . error ( ' i n v a l i d o p e r a t o r ` $ node . op ` t o ` $ left_name ` a n d ` $ right_name ` ' , left_right_pos )
} else if node . op in [ . plus , . minus ] {
if ! c . inside_unsafe && ! node . left . is_auto_deref_var ( ) && ! node . right . is_auto_deref_var ( ) {
c . warn ( ' p o i n t e r a r i t h m e t i c i s o n l y a l l o w e d i n ` u n s a f e ` b l o c k s ' , left_right_pos )
}
if left_type == ast . voidptr_type {
c . error ( ' ` $ node . op ` c a n n o t b e u s e d w i t h ` v o i d p t r ` ' , left_pos )
}
2020-12-07 21:43:38 +01:00
}
2020-07-03 18:10:10 +02:00
}
2020-05-27 05:42:48 +02:00
mut return_type := left_type
2021-06-08 23:06:29 +02:00
if node . op != . key_is {
match mut node . left {
2020-11-27 03:08:42 +01:00
ast . Ident , ast . SelectorExpr {
2021-06-08 23:06:29 +02:00
if node . left . is_mut {
c . error ( ' r e m o v e u n n e c e s s a r y ` m u t ` ' , node . left . mut_pos )
2020-11-27 03:08:42 +01:00
}
}
else { }
}
}
2021-06-08 23:06:29 +02:00
eq_ne := node . 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.
2021-06-08 23:06:29 +02:00
match node . 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-06-08 23:06:29 +02:00
is_mismatch :=
( left_sym . kind == . alias && right_sym . kind in [ . struct_ , . array , . sum_type ] )
|| ( right_sym . kind == . alias && left_sym . kind in [ . struct_ , . array , . sum_type ] )
2021-02-05 16:49:40 +01:00
if is_mismatch {
2021-06-08 23:06:29 +02:00
c . error ( ' p o s s i b l e t y p e m i s m a t c h o f c o m p a r e d v a l u e s o f ` $ node . op ` o p e r a t i o n ' ,
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-09-14 14:56:12 +02:00
if left_sym . kind ! in [ . sum_type , . interface_ ] {
elem_type := right_final . array_info ( ) . elem_type
c . check_expected ( left_type , elem_type ) or {
2022-02-11 14:52:33 +01:00
c . error ( ' l e f t o p e r a n d t o ` $ node . op ` d o e s n o t m a t c h t h e a r r a y e l e m e n t t y p e : $ err . msg ( ) ' ,
2021-09-14 14:56:12 +02: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 {
2022-02-11 14:52:33 +01:00
c . error ( ' l e f t o p e r a n d t o ` $ node . op ` d o e s n o t m a t c h t h e m a p k e y t y p e : $ err . msg ( ) ' ,
2021-02-19 11:38:41 +01:00
left_right_pos )
2020-04-25 21:51:44 +02:00
}
2021-06-08 23:06:29 +02:00
node . left_type = map_info . key_type
2020-04-25 21:51:44 +02:00
}
2020-04-25 20:28:49 +02:00
else {
2022-01-05 11:21:07 +01:00
c . error ( ' ` $ node . op . str ( ) ` c a n o n l y b e u s e d w i t h a r r a y s a n d m a p s ' ,
2021-06-08 23:06:29 +02:00
node . 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-06-08 23:06:29 +02:00
if right_sym . info is ast . Alias && ( right_sym . 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-12-19 17:25:18 +01:00
&& c . table . sym ( ( right_sym . info as ast . Alias ) . parent_type ) . is_primitive ( ) {
right_sym = c . table . sym ( ( right_sym . info as ast . Alias ) . parent_type )
2020-09-20 19:51:14 +02:00
}
2021-06-08 23:06:29 +02:00
if left_sym . info is ast . Alias && ( left_sym . 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-12-19 17:25:18 +01:00
&& c . table . sym ( ( left_sym . info as ast . Alias ) . parent_type ) . is_primitive ( ) {
left_sym = c . table . sym ( ( left_sym . 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-06-08 23:06:29 +02:00
if left_sym . kind == . alias && left_sym . info is ast . Alias
2021-12-19 17:25:18 +01:00
&& ! ( c . table . sym ( ( left_sym . info as ast . Alias ) . parent_type ) . is_primitive ( ) ) {
2021-06-08 23:06:29 +02:00
if left_sym . has_method ( node . op . str ( ) ) {
if method := left_sym . find_method ( node . op . str ( ) ) {
2021-03-30 11:39:54 +02:00
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 {
2021-06-08 23:06:29 +02:00
c . error ( ' u n d e f i n e d o p e r a t i o n ` $ left_name ` $ node . op . str ( ) ` $ right_name ` ' ,
2021-03-30 11:39:54 +02:00
left_right_pos )
} else {
c . error ( ' m i s m a t c h e d t y p e s ` $ left_name ` a n d ` $ right_name ` ' , left_right_pos )
}
}
2021-06-08 23:06:29 +02:00
} else if right_sym . kind == . alias && right_sym . info is ast . Alias
2021-12-19 17:25:18 +01:00
&& ! ( c . table . sym ( ( right_sym . info as ast . Alias ) . parent_type ) . is_primitive ( ) ) {
2021-06-08 23:06:29 +02:00
if right_sym . has_method ( node . op . str ( ) ) {
if method := right_sym . find_method ( node . op . str ( ) ) {
2021-03-30 11:39:54 +02:00
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 {
2021-06-08 23:06:29 +02:00
c . error ( ' u n d e f i n e d o p e r a t i o n ` $ left_name ` $ node . op . str ( ) ` $ right_name ` ' ,
2021-03-30 11:39:54 +02:00
left_right_pos )
} else {
c . error ( ' m i s m a t c h e d t y p e s ` $ left_name ` a n d ` $ right_name ` ' , left_right_pos )
}
}
}
2021-06-08 23:06:29 +02:00
if left_sym . kind in [ . array , . array_fixed , . map , . struct_ ] {
2022-01-20 07:57:25 +01:00
if left_sym . has_method_with_generic_parent ( node . op . str ( ) ) {
if method := left_sym . find_method_with_generic_parent ( node . op . str ( ) ) {
2020-09-20 19:51:14 +02:00
return_type = method . return_type
} else {
return_type = left_type
}
2020-05-27 05:42:48 +02:00
} else {
2022-01-20 07:57:25 +01:00
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 ( ' u n d e f i n e d o p e r a t i o n ` $ left_name ` $ node . op . str ( ) ` $ right_name ` ' ,
left_right_pos )
2020-09-20 19:51:14 +02:00
} else {
2022-01-20 07:57:25 +01:00
c . error ( ' m i s m a t c h e d t y p e s ` $ left_name ` a n d ` $ right_name ` ' , left_right_pos )
2020-09-20 19:51:14 +02:00
}
2020-05-27 05:42:48 +02:00
}
2021-06-08 23:06:29 +02:00
} else if right_sym . kind in [ . array , . array_fixed , . map , . struct_ ] {
2022-01-20 07:57:25 +01:00
if right_sym . has_method_with_generic_parent ( node . op . str ( ) ) {
if method := right_sym . find_method_with_generic_parent ( node . op . str ( ) ) {
2020-09-20 19:51:14 +02:00
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-06-08 23:06:29 +02:00
c . error ( ' u n d e f i n e d o p e r a t i o n ` $ left_name ` $ node . 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 ( ' m i s m a t c h e d t y p e s ` $ left_name ` a n d ` $ right_name ` ' , left_right_pos )
2020-09-20 19:51:14 +02:00
}
2020-05-27 05:42:48 +02:00
}
2021-06-16 03:04:31 +02:00
} else if node . left . is_auto_deref_var ( ) || node . right . is_auto_deref_var ( ) {
2021-06-15 03:43:10 +02:00
deref_left_type := if node . left . is_auto_deref_var ( ) {
left_type . deref ( )
} else {
left_type
}
deref_right_type := if node . right . is_auto_deref_var ( ) {
right_type . deref ( )
} else {
right_type
}
2022-01-04 17:37:18 +01:00
left_name := c . table . type_to_str ( ast . mktyp ( deref_left_type ) )
right_name := c . table . type_to_str ( ast . mktyp ( deref_right_type ) )
2021-06-15 03:43:10 +02:00
if left_name != right_name {
c . error ( ' m i s m a t c h e d t y p e s ` $ left_name ` a n d ` $ right_name ` ' , left_right_pos )
}
2020-05-27 05:42:48 +02:00
} else {
2021-07-21 22:39:49 +02:00
unaliased_left_type := c . table . unalias_num_type ( left_type )
unalias_right_type := c . table . unalias_num_type ( right_type )
mut promoted_type := c . promote ( unaliased_left_type , unalias_right_type )
// substract pointers is allowed in unsafe block
is_allowed_pointer_arithmetic := left_type . is_any_kind_of_pointer ( )
&& right_type . is_any_kind_of_pointer ( ) && node . op == . minus
if is_allowed_pointer_arithmetic {
promoted_type = ast . int_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 ( ' m i s m a t c h e d t y p e s ` $ left_name ` a n d ` $ 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 )
2021-06-08 23:06:29 +02:00
c . error ( ' ` $ node . op ` c a n n o t b e u s e d w i t h ` $ s ` ' , node . pos )
2020-05-27 05:42:48 +02:00
} else if promoted_type . is_float ( ) {
2021-06-08 23:06:29 +02:00
if node . op in [ . mod , . xor , . amp , . pipe ] {
2020-05-27 05:42:48 +02:00
side := if left_type == promoted_type { ' l e f t ' } else { ' r i g h t ' }
pos := if left_type == promoted_type { left_pos } else { right_pos }
2021-06-08 23:06:29 +02:00
name := if left_type == promoted_type {
left_sym . name
} else {
right_sym . name
}
if node . op == . mod {
2020-06-24 14:44:06 +02:00
c . error ( ' f l o a t m o d u l o n o t a l l o w e d , u s e m a t h . f m o d ( ) i n s t e a d ' ,
pos )
2020-05-27 05:42:48 +02:00
} else {
2021-06-08 23:06:29 +02:00
c . error ( ' $ side t y p e o f ` $ node . op . str ( ) ` c a n n o t b e n o n - i n t e g e r t y p e ` $ name ` ' ,
2020-05-28 05:50:57 +02:00
pos )
2020-05-27 05:42:48 +02:00
}
}
}
2021-06-08 23:06:29 +02:00
if node . op in [ . div , . mod ] {
c . check_div_mod_by_zero ( node . right , node . 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 {
2021-06-08 23:06:29 +02:00
if left_sym . kind in [ . array , . array_fixed ] && right_sym . kind in [ . array , . array_fixed ] {
c . error ( ' o n l y ` = = ` a n d ` ! = ` a r e d e f i n e d o n a r r a y s ' , node . pos )
2021-09-14 15:20:02 +02:00
} else if left_sym . kind == . struct_
&& ( left_sym . info as ast . Struct ) . generic_types . len > 0 {
return ast . bool_type
2021-06-08 23:06:29 +02:00
} else if left_sym . kind == . struct_ && right_sym . kind == . struct_
&& node . op in [ . eq , . lt ] {
if ! ( left_sym . has_method ( node . op . str ( ) ) && right_sym . has_method ( node . op . str ( ) ) ) {
2021-01-10 11:24:46 +01:00
left_name := c . table . type_to_str ( left_type )
right_name := c . table . type_to_str ( right_type )
if left_name == right_name {
2021-06-08 23:06:29 +02:00
c . error ( ' u n d e f i n e d o p e r a t i o n ` $ left_name ` $ node . 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 ( ' m i s m a t c h e d t y p e s ` $ left_name ` a n d ` $ right_name ` ' , left_right_pos )
2021-01-10 11:24:46 +01:00
}
}
2020-08-28 10:08:07 +02:00
}
2021-06-08 23:06:29 +02:00
if left_sym . kind == . struct_ && right_sym . kind == . struct_ {
if ! left_sym . has_method ( ' < ' ) && node . op in [ . ge , . le ] {
c . error ( ' c a n n o t u s e ` $ node . op ` a s ` < ` o p e r a t o r m e t h o d i s n o t d e f i n e d ' ,
2021-02-19 11:38:41 +01:00
left_right_pos )
2021-06-08 23:06:29 +02:00
} else if ! left_sym . has_method ( ' < ' ) && node . op == . gt {
2021-02-19 11:38:41 +01:00
c . error ( ' c a n n o t u s e ` > ` a s ` < = ` o p e r a t o r m e t h o d i s n o t d e f i n e d ' , left_right_pos )
2021-02-03 15:18:38 +01:00
}
2022-02-04 23:11:24 +01:00
} else if left_type . has_flag ( . generic ) && right_type . has_flag ( . generic ) {
// Try to unwrap the generic type to make sure that
// the below check works as expected
left_gen_type := c . unwrap_generic ( left_type )
gen_sym := c . table . sym ( left_gen_type )
need_overload := gen_sym . kind in [ . struct_ , . interface_ ]
if need_overload && ! gen_sym . has_method ( ' < ' ) && node . op in [ . ge , . le ] {
c . error ( ' c a n n o t u s e ` $ node . op ` a s ` < ` o p e r a t o r m e t h o d i s n o t d e f i n e d ' ,
left_right_pos )
} else if need_overload && ! gen_sym . has_method ( ' < ' ) && node . op == . gt {
c . error ( ' c a n n o t u s e ` > ` a s ` < = ` o p e r a t o r m e t h o d i s n o t d e f i n e d ' , 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-06-08 23:06:29 +02:00
if ! node . is_stmt {
c . error ( ' a r r a y a p p e n d c a n n o t b e u s e d i n a n e x p r e s s i o n ' , node . pos )
2021-03-12 16:09:01 +01:00
}
2020-04-25 20:28:49 +02:00
// `array << elm`
2021-06-08 23:06:29 +02:00
c . check_expr_opt_call ( node . right , right_type )
node . auto_locked , _ = c . fail_if_immutable ( node . left )
2021-07-19 12:29:46 +02:00
left_value_type := c . table . value_type ( c . unwrap_generic ( left_type ) )
2021-12-19 17:25:18 +01:00
left_value_sym := c . table . sym ( c . unwrap_generic ( left_value_type ) )
2020-05-06 11:29:37 +02:00
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
2021-06-21 06:10:10 +02:00
if c . type_implements ( right_type , left_value_type , right_pos ) {
if ! right_type . is_ptr ( ) && ! right_type . is_pointer ( ) && ! c . inside_unsafe
&& right_sym . kind != . interface_ {
c . mark_as_referenced ( mut & node . right , true )
}
}
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
2022-02-24 10:07:03 +01:00
} else if left_value_sym . kind == . sum_type {
if right_final . kind != . array {
2022-02-28 09:38:20 +01:00
if ! c . table . is_sumtype_or_in_variant ( left_value_type , right_type ) {
2022-02-24 10:07:03 +01:00
c . error ( ' c a n n o t a p p e n d ` $ right_sym . name ` t o ` $ left_sym . name ` ' ,
right_pos )
}
} else {
right_value_type := c . table . value_type ( right_type )
2022-02-28 09:38:20 +01:00
if ! c . table . is_sumtype_or_in_variant ( left_value_type , right_value_type ) {
2022-02-24 10:07:03 +01:00
c . error ( ' c a n n o t a p p e n d ` $ right_sym . name ` t o ` $ left_sym . name ` ' ,
right_pos )
}
}
return ast . void_type
2020-05-06 11:29:37 +02:00
}
2021-06-13 05:29:40 +02:00
// []T << T or []T << []T
2021-07-19 12:29:46 +02:00
unwrapped_right_type := c . unwrap_generic ( right_type )
2021-09-23 10:59:43 +02:00
if c . check_types ( unwrapped_right_type , left_value_type ) {
// []&T << T is wrong: we check for that, !(T.is_ptr()) && ?(&T).is_ptr()
if ! ( ! unwrapped_right_type . is_ptr ( ) && left_value_type . is_ptr ( )
&& left_value_type . share ( ) == . mut_t ) {
return ast . void_type
}
} else if c . check_types ( unwrapped_right_type , c . unwrap_generic ( left_type ) ) {
2021-04-02 00:57:09 +02:00
return ast . void_type
2020-04-25 20:28:49 +02:00
}
2021-06-08 23:06:29 +02:00
c . error ( ' c a n n o t a p p e n d ` $ right_sym . name ` t o ` $ left_sym . name ` ' , right_pos )
2021-04-02 00:57:09 +02:00
return ast . void_type
2020-05-31 12:58:13 +02:00
} else {
2021-11-29 01:48:49 +01:00
return c . check_shift ( mut node , left_type , right_type )
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 {
2021-11-29 01:48:49 +01:00
return c . check_shift ( mut node , left_type , right_type )
2020-05-31 12:58:13 +02:00
}
2021-09-21 13:02:17 +02:00
. unsigned_right_shift {
modified_left_type := if ! left_type . is_int ( ) {
2021-12-19 17:25:18 +01:00
c . error ( ' i n v a l i d o p e r a t i o n : s h i f t o n t y p e ` $ { c . table . sym ( left_type ) . name } ` ' ,
2021-09-21 13:02:17 +02:00
left_pos )
ast . void_type_idx
} else if left_type . is_int_literal ( ) {
// int literal => i64
ast . u32_type_idx
} else if left_type . is_unsigned ( ) {
left_type
} else {
// signed types' idx adds with 5 will get correct relative unsigned type
// i8 => byte
// i16 => u16
// int => u32
// i64 => u64
// isize => usize
// i128 => u128 NOT IMPLEMENTED YET
left_type . idx ( ) + ast . u32_type_idx - ast . int_type_idx
}
if modified_left_type == 0 {
return ast . void_type
}
node = ast . InfixExpr {
left : ast . CastExpr {
expr : node . left
typ : modified_left_type
typname : c . table . type_str ( modified_left_type )
pos : node . pos
}
left_type : left_type
op : . right_shift
right : node . right
right_type : right_type
is_stmt : false
pos : node . pos
auto_locked : node . auto_locked
or_block : node . or_block
}
2021-11-29 01:48:49 +01:00
return c . check_shift ( mut node , left_type , right_type )
2021-09-21 13:02:17 +02:00
}
2020-06-02 16:18:12 +02:00
. key_is , . not_is {
2021-06-08 23:06:29 +02:00
right_expr := node . right
2021-03-13 18:13:50 +01:00
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 {
2022-01-26 11:36:28 +01:00
c . error ( ' i n v a l i d t y p e ` $ right_expr ` ' , right_expr . pos ( ) )
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 ) {
2021-12-19 17:25:18 +01:00
typ_sym := c . table . sym ( typ )
2021-06-08 23:06:29 +02:00
op := node . op . str ( )
2021-04-18 15:28:39 +02:00
if typ_sym . kind == . placeholder {
2022-01-26 11:36:28 +01:00
c . error ( ' $ op : t y p e ` $ typ_sym . name ` d o e s n o t e x i s t ' , right_expr . pos ( ) )
2021-04-18 15:28:39 +02:00
}
2021-06-08 23:06:29 +02:00
if left_sym . kind ! in [ . interface_ , . sum_type ] {
c . error ( ' ` $ op ` c a n o n l y b e u s e d w i t h i n t e r f a c e s a n d s u m t y p e s ' , node . pos )
} else if mut left_sym . info is ast . SumType {
if typ ! in left_sym . info . variants {
c . error ( ' ` $ left_sym . name ` h a s n o v a r i a n t ` $ right_sym . name ` ' , node . pos )
2021-04-18 15:28:39 +02:00
}
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`
2021-06-08 23:06:29 +02:00
if left_sym . kind == . chan {
chan_info := left_sym . chan_info ( )
2020-09-06 21:24:41 +02:00
elem_type := chan_info . elem_type
2020-08-17 20:12:00 +02:00
if ! c . check_types ( right_type , elem_type ) {
2021-06-08 23:06:29 +02:00
c . error ( ' c a n n o t p u s h ` $ right_sym . name ` o n ` $ left_sym . name ` ' , right_pos )
2020-08-17 20:12:00 +02:00
}
2020-09-06 21:24:41 +02:00
if chan_info . is_mut {
// TODO: The error message of the following could be more specific...
2021-06-08 23:06:29 +02:00
c . fail_if_immutable ( node . right )
2020-09-06 21:24:41 +02:00
}
if elem_type . is_ptr ( ) && ! right_type . is_ptr ( ) {
2021-06-08 23:06:29 +02:00
c . error ( ' c a n n o t p u s h n o n - r e f e r e n c e ` $ right_sym . name ` o n ` $ left_sym . name ` ' ,
2020-09-06 21:24:41 +02:00
right_pos )
}
2021-11-27 07:09:36 +01:00
c . stmts_ending_with_expression ( node . or_block . stmts )
2020-08-17 20:12:00 +02:00
} else {
2021-06-08 23:06:29 +02:00
c . error ( ' c a n n o t p u s h o n n o n - c h a n n e l ` $ left_sym . name ` ' , left_pos )
2020-08-17 20:12:00 +02:00
}
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 {
2022-02-05 23:16:02 +01:00
if ! c . pref . translated && ! c . file . is_translated {
2021-06-08 23:06:29 +02:00
if node . left_type != ast . bool_type_idx {
2022-01-26 11:36:28 +01:00
c . error ( ' l e f t o p e r a n d f o r ` $ node . op ` i s n o t a b o o l e a n ' , node . left . pos ( ) )
2021-05-28 16:59:26 +02:00
}
2021-06-08 23:06:29 +02:00
if node . right_type != ast . bool_type_idx {
2022-01-26 11:36:28 +01:00
c . error ( ' r i g h t o p e r a n d f o r ` $ node . op ` i s n o t a b o o l e a n ' , node . right . pos ( ) )
2021-05-28 16:59:26 +02:00
}
2020-09-23 20:48:25 +02:00
}
2021-06-08 23:06:29 +02:00
if mut node . left is ast . InfixExpr {
if node . left . op != node . op && node . 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 ( ' a m b i g u o u s b o o l e a n e x p r e s s i o n . u s e ` ( ) ` t o e n s u r e c o r r e c t o r d e r o f o p e r a t i o n s ' ,
2021-06-08 23:06:29 +02:00
node . 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-06-08 23:06:29 +02:00
if left_type == ast . bool_type && node . op ! in [ . eq , . ne , . logical_or , . and ] {
2020-04-25 21:51:44 +02:00
c . error ( ' b o o l t y p e s o n l y h a v e t h e f o l l o w i n g o p e r a t o r s d e f i n e d : ` = = ` , ` ! = ` , ` | | ` , a n d ` & & ` ' ,
2021-06-08 23:06:29 +02:00
node . pos )
} else if left_type == ast . string_type && node . 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 ( ' s t r i n g t y p e s o n l y h a v e t h e f o l l o w i n g o p e r a t o r s d e f i n e d : ` = = ` , ` ! = ` , ` < ` , ` > ` , ` < = ` , ` > = ` , a n d ` + ` ' ,
2021-06-08 23:06:29 +02:00
node . pos )
} else if left_sym . kind == . enum_ && right_sym . kind == . enum_ && ! eq_ne {
left_enum := left_sym . info as ast . Enum
right_enum := right_sym . 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
2021-06-08 23:06:29 +02:00
if node . op ! in [ . pipe , . amp ] {
2021-02-20 20:51:54 +01:00
c . error ( ' o n l y ` = = ` , ` ! = ` , ` | ` a n d ` & ` a r e d e f i n e d o n ` [ f l a g ] ` t a g g e d ` e n u m ` , u s e a n e x p l i c i t c a s t t o ` i n t ` i f n e e d e d ' ,
2021-06-08 23:06:29 +02:00
node . pos )
2021-02-20 20:51:54 +01:00
}
2022-02-05 23:16:02 +01:00
} else if ! c . pref . translated && ! c . file . is_translated {
2021-02-20 20:51:54 +01:00
// Regular enums
c . error ( ' o n l y ` = = ` a n d ` ! = ` a r e d e f i n e d o n ` e n u m ` , u s e a n e x p l i c i t c a s t t o ` i n t ` i f n e e d e d ' ,
2021-06-08 23:06:29 +02:00
node . pos )
2021-02-20 20:51:54 +01:00
}
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 {
2021-06-08 23:06:29 +02:00
c . error ( ' c a n n o t u s e o p e r a t o r ` $ node . op ` w i t h ` $ left_sym . name ` ' , node . pos )
2021-03-22 06:39:07 +01:00
} else if c . table . type_kind ( right_type ) == . sum_type && ! eq_ne {
2021-06-08 23:06:29 +02:00
c . error ( ' c a n n o t u s e o p e r a t o r ` $ node . op ` w i t h ` $ right_sym . name ` ' , node . pos )
2020-11-17 21:26:40 +01:00
}
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 ( ' u n w r a p p e d o p t i o n a l c a n n o t b e u s e d i n a n i n f i x e x p r e s s i o n ' , left_right_pos )
2020-12-29 13:49:43 +01:00
}
2020-04-25 20:28:49 +02:00
// Dual sides check (compatibility check)
2021-04-29 08:04:02 +02:00
if ! ( c . symmetric_check ( left_type , right_type ) && c . symmetric_check ( right_type , left_type ) )
2022-02-05 23:16:02 +01:00
&& ! c . pref . translated && ! c . file . is_translated && ! node . left . is_auto_deref_var ( )
&& ! node . right . is_auto_deref_var ( ) {
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
}
2021-06-08 23:06:29 +02:00
c . error ( ' i n f i x e x p r : c a n n o t u s e ` $ right_sym . name ` ( r i g h t e x p r e s s i o n ) a s ` $ left_sym . 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
/ *
2021-06-08 23:06:29 +02:00
if ( node . left is ast . InfixExpr &&
( node . left as ast . InfixExpr ) . op == . inc ) ||
( node . right is ast . InfixExpr && ( node . right as ast . InfixExpr ) . op == . inc ) {
c . warn ( ' ` + + ` a n d ` - - ` a r e s t a t e m e n t s , n o t e x p r e s s i o n s ' , node . pos )
2020-07-16 19:40:14 +02:00
}
* /
2021-06-08 23:06:29 +02:00
return if node . 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?)
2022-01-26 11:36:28 +01:00
fn ( mut c Checker ) fail_if_immutable ( expr ast . Expr ) ( string , token . Pos ) {
2020-07-13 12:19:28 +02:00
mut to_lock := ' ' // name of variable that needs lock
2022-01-26 11:36:28 +01:00
mut pos := token . Pos { } // and its position
2020-07-13 12:19:28 +02:00
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 {
2022-02-28 16:06:37 +01:00
if mut expr . obj is ast . Var {
if ! expr . obj . is_mut && ! c . pref . translated && ! c . file . is_translated
&& ! c . inside_unsafe {
2020-09-27 09:36:26 +02:00
c . error ( ' ` $ expr . name ` i s i m m u t a b l e , d e c l a r e i t w i t h ` m u t ` t o m a k e i t m u t a b l e ' ,
expr . pos )
2020-04-27 22:53:26 +02:00
}
2022-02-28 16:06:37 +01:00
expr . obj . is_changed = true
if expr . obj . typ . share ( ) == . shared_t {
2020-07-13 12:19:28 +02:00
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 h a s a n ` r l o c k ` b u t n e e d s a ` l o c k ` ' ,
expr . pos )
} else {
c . error ( ' $ expr . name m u s t b e a d d e d t o t h e ` l o c k ` l i s t a b o v e ' ,
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 {
2021-12-16 14:59:46 +01:00
if ! c . inside_unsafe {
c . error ( ' c a n n o t m o d i f y c o n s t a n t ` $ expr . name ` ' , expr . pos )
}
2020-04-27 22:53:26 +02:00
}
}
ast . IndexExpr {
2021-12-19 17:25:18 +01:00
left_sym := c . table . sym ( 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 , ' a r r a y '
}
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 , ' f i x e d a r r a y '
}
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 , ' m a p '
}
else { }
}
if elem_type . has_flag ( . shared_f ) {
c . error ( ' y o u h a v e t o c r e a t e a h a n d l e a n d ` l o c k ` i t t o m o d i f y ` s h a r e d ` $ kind e l e m e n t ' ,
2022-01-26 11:36:28 +01:00
expr . left . pos ( ) . extend ( expr . pos ) )
2021-03-15 14:54:06 +01:00
}
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 }
2021-12-19 17:25:18 +01:00
mut typ_sym := c . table . final_sym ( c . unwrap_generic ( expr . expr_type ) )
2020-04-27 22:53:26 +02:00
match typ_sym . kind {
. struct_ {
2020-12-23 19:12:49 +01:00
mut has_field := true
2021-11-28 20:31:19 +01:00
mut field_info := c . table . find_field_with_embeds ( typ_sym , expr . field_name ) or {
2020-12-23 19:12:49 +01:00
has_field = false
2021-04-02 00:57:09 +02:00
ast . StructField { }
2020-12-23 19:12:49 +01:00
}
if ! has_field {
2020-06-18 20:38:59 +02:00
type_str := c . table . type_to_str ( expr . expr_type )
c . error ( ' u n k n o w n f i e l d ` $ { 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
}
2021-03-15 14:54:06 +01:00
if field_info . typ . has_flag ( . shared_f ) {
2021-05-15 03:34:27 +02:00
expr_name := ' $ { expr . expr } . $ expr . field_name '
if expr_name ! in c . locked_names {
if c . locked_names . len > 0 || c . rlocked_names . len > 0 {
if expr_name in c . rlocked_names {
c . error ( ' $ expr_name h a s a n ` r l o c k ` b u t n e e d s a ` l o c k ` ' ,
expr . pos )
} else {
c . error ( ' $ expr_name m u s t b e a d d e d t o t h e ` l o c k ` l i s t a b o v e ' ,
expr . pos )
}
2021-09-03 12:21:35 +02:00
return ' ' , expr . pos
2021-05-15 03:34:27 +02:00
}
to_lock = expr_name
pos = expr . pos
}
} else {
2022-02-05 23:16:02 +01:00
if ! field_info . is_mut && ! c . pref . translated && ! c . file . is_translated {
2021-05-15 03:34:27 +02:00
type_str := c . table . type_to_str ( expr . expr_type )
c . error ( ' f i e l d ` $ expr . field_name ` o f s t r u c t ` $ type_str ` i s i m m u t a b l e ' ,
expr . pos )
}
to_lock , pos = c . fail_if_immutable ( expr . expr )
2021-03-15 14:54:06 +01:00
}
2020-07-13 12:19:28 +02:00
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 ( ' u n k n o w n f i e l d ` $ { 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 ( ' f i e l d ` $ expr . field_name ` o f i n t e r f a c e ` $ type_str ` i s i m m u t a b l e ' ,
expr . pos )
2021-09-03 12:21:35 +02:00
return ' ' , expr . pos
}
c . fail_if_immutable ( expr . expr )
}
. sum_type {
sumtype_info := typ_sym . info as ast . SumType
mut field_info := sumtype_info . find_field ( expr . field_name ) or {
type_str := c . table . type_to_str ( expr . expr_type )
c . error ( ' u n k n o w n f i e l d ` $ { type_str } . $ expr . field_name ` ' , expr . pos )
return ' ' , pos
}
if ! field_info . is_mut {
type_str := c . table . type_to_str ( expr . expr_type )
c . error ( ' f i e l d ` $ expr . field_name ` o f s u m t y p e ` $ type_str ` i s i m m u t a b l e ' ,
expr . pos )
return ' ' , expr . pos
2021-01-24 22:11:17 +01:00
}
c . fail_if_immutable ( expr . expr )
2021-01-23 07:57:17 +01:00
}
2020-04-27 22:53:26 +02:00
. array , . string {
2021-10-30 12:30:14 +02:00
// should only happen in `builtin` and unsafe blocks
inside_builtin := c . file . mod . name == ' b u i l t i n '
if ! inside_builtin && ! c . inside_unsafe {
2020-06-18 20:38:59 +02:00
c . error ( ' ` $ typ_sym . kind ` c a n n o t b e m o d i f i e d ' , expr . pos )
2021-09-03 12:21:35 +02:00
return ' ' , expr . pos
2020-04-27 22:53:26 +02:00
}
}
2021-07-19 08:16:15 +02:00
. aggregate , . placeholder {
2021-02-10 10:59:56 +01:00
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 ( ' u n e x p e c t e d s y m b o l ` $ typ_sym . kind ` ' , expr . pos )
2021-09-03 12:21:35 +02:00
return ' ' , 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 == ' s l i c e ' {
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 ( ' a r r a y l i t e r a l c a n n o t b e m o d i f i e d ' , 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
}
2021-06-24 11:39:42 +02:00
ast . InfixExpr {
return ' ' , pos
}
2020-04-27 22:53:26 +02:00
else {
2021-06-04 22:18:11 +02:00
if ! expr . is_lit ( ) {
2022-01-26 11:36:28 +01:00
c . error ( ' u n e x p e c t e d e x p r e s s i o n ` $ expr . type_name ( ) ` ' , expr . pos ( ) )
2021-09-03 12:21:35 +02:00
return ' ' , pos
2021-06-04 22:18:11 +02:00
}
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 ` i s ` s h a r e d ` a n d n e e d s e x p l i c i t l o c k f o r ` $ 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
}
2022-01-26 11:36:28 +01:00
fn ( mut c Checker ) type_implements ( typ ast . Type , interface_type ast . Type , pos token . Pos ) bool {
2021-12-30 18:28:42 +01:00
if typ == interface_type {
return true
}
2021-12-11 08:47:57 +01:00
$ if debug _interface_type_implements ? {
eprintln ( ' > t y p e _ i m p l e m e n t s t y p : $ typ . debug ( ) ( ` $ { c . table . type_to_str ( typ ) } ` ) | i n t e r _ t y p : $ interface_type . debug ( ) ( ` $ { c . table . type_to_str ( interface_type ) } ` ) ' )
}
utyp := c . unwrap_generic ( typ )
2021-12-19 17:25:18 +01:00
typ_sym := c . table . sym ( utyp )
mut inter_sym := c . table . sym ( interface_type )
2021-12-11 08:47:57 +01:00
// small hack for JS.Any type. Since `any` in regular V is getting deprecated we have our own JS.Any type for JS backend.
if typ_sym . name == ' J S . A n y ' {
return true
}
if mut inter_sym . info is ast . Interface {
mut generic_type := interface_type
mut generic_info := inter_sym . info
if inter_sym . info . parent_type . has_flag ( . generic ) {
2021-12-19 17:25:18 +01:00
parent_sym := c . table . sym ( inter_sym . info . parent_type )
2021-12-11 08:47:57 +01:00
if parent_sym . info is ast . Interface {
generic_type = inter_sym . info . parent_type
generic_info = parent_sym . info
2020-06-11 13:14:07 +02:00
}
}
2021-12-11 08:47:57 +01:00
mut inferred_type := interface_type
if generic_info . is_generic {
inferred_type = c . resolve_generic_interface ( typ , generic_type , pos )
if inferred_type == 0 {
return false
2020-06-11 13:14:07 +02:00
}
}
2021-12-11 08:47:57 +01:00
if inter_sym . info . is_generic {
2021-12-30 18:28:42 +01:00
if inferred_type == interface_type {
// terminate early, since otherwise we get an infinite recursion/segfault:
return false
}
2021-12-11 08:47:57 +01:00
return c . type_implements ( typ , inferred_type , pos )
2021-04-02 01:56:51 +02:00
}
2020-06-11 13:14:07 +02:00
}
2021-12-11 08:47:57 +01:00
// do not check the same type more than once
if mut inter_sym . info is ast . Interface {
for t in inter_sym . info . types {
if t . idx ( ) == utyp . idx ( ) {
return true
2021-07-17 14:44:44 +02:00
}
}
2021-07-15 16:39:55 +02:00
}
2021-12-11 08:47:57 +01:00
styp := c . table . type_to_str ( utyp )
if utyp . idx ( ) == interface_type . idx ( ) {
// same type -> already casted to the interface
return true
2020-11-14 12:59:03 +01:00
}
2021-12-11 08:47:57 +01:00
if interface_type . idx ( ) == ast . error_type_idx && utyp . idx ( ) == ast . none_type_idx {
// `none` "implements" the Error interface
return true
2021-06-24 06:33:12 +02:00
}
2021-12-11 08:47:57 +01:00
if typ_sym . kind == . interface_ && inter_sym . kind == . interface_ && ! styp . starts_with ( ' J S . ' )
&& ! inter_sym . name . starts_with ( ' J S . ' ) {
c . error ( ' c a n n o t i m p l e m e n t i n t e r f a c e ` $ inter_sym . name ` w i t h a d i f f e r e n t i n t e r f a c e ` $ styp ` ' ,
pos )
2021-01-22 13:49:56 +01:00
}
2021-12-11 08:47:57 +01:00
imethods := if inter_sym . kind == . interface_ {
( inter_sym . info as ast . Interface ) . methods
} else {
inter_sym . methods
2020-12-31 18:00:22 +01:00
}
2021-12-11 08:47:57 +01:00
// voidptr is an escape hatch, it should be allowed to be passed
if utyp != ast . voidptr_type {
// Verify methods
for imethod in imethods {
method := c . table . find_method_with_embeds ( typ_sym , imethod . name ) or {
2022-02-11 14:52:33 +01:00
// >> Hack to allow old style custom error implementations
// TODO: remove once deprecation period for `IError` methods has ended
if inter_sym . name == ' I E r r o r ' && ( imethod . name == ' m s g ' || imethod . name == ' c o d e ' ) {
c . note ( " ` $ styp ` d o e s n ' t i m p l e m e n t m e t h o d ` $ imethod . name ` o f i n t e r f a c e ` $ inter_sym . name ` . T h e u s a g e o f f i e l d s i s b e i n g d e p r e c a t e d i n f a v o r o f m e t h o d s . " ,
pos )
continue
}
// <<
2021-12-11 08:47:57 +01:00
typ_sym . find_method_with_generic_parent ( imethod . name ) or {
c . error ( " ` $ styp ` d o e s n ' t i m p l e m e n t m e t h o d ` $ imethod . name ` o f i n t e r f a c e ` $ inter_sym . name ` " ,
pos )
continue
2021-08-24 19:28:05 +02:00
}
}
2021-12-11 08:47:57 +01:00
msg := c . table . is_same_method ( imethod , method )
if msg . len > 0 {
sig := c . table . fn_signature ( imethod , skip_receiver : false )
typ_sig := c . table . fn_signature ( method , skip_receiver : false )
c . add_error_detail ( ' $ inter_sym . name h a s ` $ sig ` ' )
c . add_error_detail ( ' $ typ_sym . name h a s ` $ typ_sig ` ' )
c . error ( ' ` $ styp ` i n c o r r e c t l y i m p l e m e n t s m e t h o d ` $ imethod . name ` o f i n t e r f a c e ` $ inter_sym . name ` : $ msg ' ,
pos )
return false
2021-08-24 19:28:05 +02:00
}
}
2020-04-12 17:45:04 +02:00
}
2021-12-11 08:47:57 +01:00
// Verify fields
if mut inter_sym . info is ast . Interface {
for ifield in inter_sym . info . fields {
if field := c . table . find_field_with_embeds ( typ_sym , ifield . name ) {
if ifield . typ != field . typ {
exp := c . table . type_to_str ( ifield . typ )
got := c . table . type_to_str ( field . typ )
c . error ( ' ` $ styp ` i n c o r r e c t l y i m p l e m e n t s f i e l d ` $ ifield . name ` o f i n t e r f a c e ` $ inter_sym . name ` , e x p e c t e d ` $ exp ` , g o t ` $ got ` ' ,
pos )
return false
} else if ifield . is_mut && ! ( field . is_mut || field . is_global ) {
c . error ( ' ` $ styp ` i n c o r r e c t l y i m p l e m e n t s i n t e r f a c e ` $ inter_sym . name ` , f i e l d ` $ ifield . name ` m u s t b e m u t a b l e ' ,
pos )
return false
2021-01-01 14:36:07 +01:00
}
2021-12-11 08:47:57 +01:00
continue
2021-07-23 00:14:39 +02:00
}
2021-12-11 08:47:57 +01:00
// voidptr is an escape hatch, it should be allowed to be passed
if utyp != ast . voidptr_type {
2022-02-11 14:52:33 +01:00
// >> Hack to allow old style custom error implementations
// TODO: remove once deprecation period for `IError` methods has ended
if inter_sym . name == ' I E r r o r ' && ( ifield . name == ' m s g ' || ifield . name == ' c o d e ' ) {
// do nothing, necessary warnings are already printed
} else {
// <<
c . error ( " ` $ styp ` d o e s n ' t i m p l e m e n t f i e l d ` $ ifield . name ` o f i n t e r f a c e ` $ inter_sym . name ` " ,
pos )
}
2021-01-01 14:36:07 +01:00
}
}
2021-12-11 08:47:57 +01:00
inter_sym . info . types << utyp
2020-12-23 19:12:49 +01:00
}
2021-12-11 08:47:57 +01:00
return true
}
// return the actual type of the expression, once the optional is handled
pub fn ( mut c Checker ) check_expr_opt_call ( expr ast . Expr , ret_type ast . Type ) ast . Type {
if expr is ast . CallExpr {
if expr . return_type . has_flag ( . optional ) {
if expr . or_block . kind == . absent {
if c . inside_defer {
c . error ( ' $ { expr . name } ( ) r e t u r n s a n o p t i o n , s o i t s h o u l d h a v e a n ` o r { } ` b l o c k a t t h e e n d ' ,
expr . pos )
2021-02-03 00:20:19 +01:00
} else {
2021-12-11 08:47:57 +01:00
c . error ( ' $ { expr . name } ( ) r e t u r n s a n o p t i o n , s o i t s h o u l d h a v e e i t h e r a n ` o r { } ` b l o c k , o r ` ? ` a t t h e e n d ' ,
expr . pos )
2020-09-27 03:14:24 +02:00
}
} else {
2021-12-11 08:47:57 +01:00
c . check_or_expr ( expr . or_block , ret_type , expr . return_type . clear_flag ( . optional ) )
2020-12-31 18:00:22 +01:00
}
2021-12-11 08:47:57 +01:00
return ret_type . clear_flag ( . optional )
} else if expr . or_block . kind == . block {
c . error ( ' u n e x p e c t e d ` o r ` b l o c k , t h e f u n c t i o n ` $ expr . name ` d o e s n o t r e t u r n a n o p t i o n a l ' ,
expr . or_block . pos )
} else if expr . or_block . kind == . propagate {
c . error ( ' u n e x p e c t e d ` ? ` , t h e f u n c t i o n ` $ expr . name ` d o e s n o t r e t u r n a n o p t i o n a l ' ,
expr . or_block . pos )
2020-12-31 18:00:22 +01:00
}
2021-12-11 08:47:57 +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 )
2021-01-19 08:28:34 +01:00
}
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-08-20 09:25:16 +02:00
pub fn ( mut c Checker ) check_or_expr ( node ast . OrExpr , ret_type ast . Type , expr_return_type ast . Type ) {
if node . kind == . propagate {
2021-05-11 08:30:01 +02:00
if ! c . table . cur_fn . return_type . has_flag ( . optional ) && c . table . cur_fn . name != ' m a i n . m a i n '
2021-04-01 11:49:13 +02:00
&& ! c . inside_const {
2021-05-11 08:30:01 +02:00
c . error ( ' t o p r o p a g a t e t h e o p t i o n a l c a l l , ` $ c . table . cur_fn . name ` m u s t r e t u r n a n o p t i o n a l ' ,
2021-08-20 09:25:16 +02:00
node . pos )
2020-05-23 08:51:15 +02:00
}
return
}
2021-08-20 09:25:16 +02:00
stmts_len := node . 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 {}
2021-08-20 09:25:16 +02:00
c . error ( ' a s s i g n m e n t r e q u i r e s a n o n e m p t y ` o r { } ` b l o c k ' , node . pos )
2020-04-08 01:19:24 +02:00
}
// allow `f() or {}`
return
}
2021-08-20 09:25:16 +02:00
last_stmt := node . stmts [ stmts_len - 1 ]
2022-03-05 12:06:08 +01:00
c . check_or_last_stmt ( last_stmt , ret_type , expr_return_type )
}
fn ( mut c Checker ) check_or_last_stmt ( stmt ast . Stmt , ret_type ast . Type , expr_return_type ast . Type ) {
2021-04-02 00:57:09 +02:00
if ret_type != ast . void_type {
2022-03-05 12:06:08 +01:00
match 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 )
2022-03-05 12:06:08 +01:00
last_stmt_typ := c . expr ( 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 ( )
2022-03-05 12:06:08 +01:00
is_noreturn := is_noreturn_callexpr ( stmt . expr )
2021-07-04 19:24:19 +02:00
if type_fits || is_noreturn {
2020-04-08 01:19:24 +02:00
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 ) )
2022-03-05 12:06:08 +01:00
if stmt . typ == ast . void_type {
if stmt . expr is ast . IfExpr {
for branch in stmt . expr . branches {
last_stmt := branch . stmts [ branch . stmts . len - 1 ]
c . check_or_last_stmt ( last_stmt , ret_type , expr_return_type )
}
return
} else if stmt . expr is ast . MatchExpr {
for branch in stmt . expr . branches {
last_stmt := branch . stmts [ branch . stmts . len - 1 ]
c . check_or_last_stmt ( last_stmt , ret_type , expr_return_type )
}
return
}
2021-07-04 19:24:19 +02:00
c . error ( ' ` o r ` b l o c k m u s t p r o v i d e a d e f a u l t v a l u e o f t y p e ` $ expected_type_name ` , o r r e t u r n / c o n t i n u e / b r e a k o r c a l l a [ n o r e t u r n ] f u n c t i o n l i k e p a n i c ( e r r ) o r e x i t ( 1 ) ' ,
2022-03-05 12:06:08 +01:00
stmt . expr . pos ( ) )
2020-11-02 03:52:35 +01:00
} 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 ( ' w r o n g r e t u r n t y p e ` $ type_name ` i n t h e ` o r { } ` b l o c k , e x p e c t e d ` $ expected_type_name ` ' ,
2022-03-05 12:06:08 +01:00
stmt . expr . pos ( ) )
2020-11-02 03:52:35 +01:00
}
2020-04-07 16:36:00 +02:00
}
2020-04-08 01:19:24 +02:00
ast . BranchStmt {
2022-03-05 12:06:08 +01:00
if stmt . kind ! in [ . key_continue , . key_break ] {
2020-04-08 14:19:13 +02:00
c . error ( ' o n l y b r e a k / c o n t i n u e i s a l l o w e d a s a b r a n c h s t a t e m e n t i n t h e e n d o f a n ` o r { } ` b l o c k ' ,
2022-03-05 12:06:08 +01:00
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 ( ' l a s t s t a t e m e n t i n t h e ` o r { } ` b l o c k s h o u l d b e a n e x p r e s s i o n o f t y p e ` $ expected_type_name ` o r e x i t p a r e n t s c o p e ' ,
2022-03-05 12:06:08 +01:00
stmt . pos )
2020-09-18 01:09:00 +02:00
}
2020-04-07 16:36:00 +02:00
}
2020-11-02 04:23:58 +01:00
} else {
2022-03-05 12:06:08 +01:00
match stmt {
2020-11-02 04:23:58 +01:00
ast . ExprStmt {
2022-03-05 12:06:08 +01:00
match stmt . expr {
ast . IfExpr {
for branch in stmt . expr . branches {
last_stmt := branch . stmts [ branch . stmts . len - 1 ]
c . check_or_last_stmt ( last_stmt , ret_type , expr_return_type )
}
}
ast . MatchExpr {
for branch in stmt . expr . branches {
last_stmt := branch . stmts [ branch . stmts . len - 1 ]
c . check_or_last_stmt ( last_stmt , ret_type , expr_return_type )
}
}
else {
if stmt . typ == ast . void_type {
return
}
if is_noreturn_callexpr ( stmt . expr ) {
return
}
if c . check_types ( stmt . typ , expr_return_type ) {
return
}
// opt_returning_string() or { ... 123 }
type_name := c . table . type_to_str ( stmt . typ )
expr_return_type_name := c . table . type_to_str ( expr_return_type )
c . error ( ' t h e d e f a u l t e x p r e s s i o n t y p e i n t h e ` o r ` b l o c k s h o u l d b e ` $ expr_return_type_name ` , i n s t e a d y o u g a v e a v a l u e o f t y p e ` $ type_name ` ' ,
stmt . expr . pos ( ) )
}
2020-11-02 04:23:58 +01:00
}
}
else { }
}
2020-04-07 16:36:00 +02:00
}
}
2021-06-03 15:49:50 +02:00
pub fn ( mut c Checker ) selector_expr ( mut node 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
2021-06-03 15:49:50 +02:00
if ' $ node . expr ' == ' e r r ' {
2021-02-28 20:24:29 +01:00
c . using_new_err_struct = true
}
2020-11-09 14:35:26 +01:00
// T.name, typeof(expr).name
mut name_type := 0
2021-06-03 15:49:50 +02:00
match mut node . expr {
2020-11-09 14:35:26 +01:00
ast . Ident {
2021-06-03 15:49:50 +02:00
name := node . expr . name
2021-05-11 08:30:01 +02:00
valid_generic := util . is_generic_type_name ( name ) && name in c . table . 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 {
2021-06-03 15:49:50 +02:00
name_type = c . expr ( node . expr . expr )
2020-11-09 14:35:26 +01:00
}
else { }
}
if name_type > 0 {
2021-06-03 15:49:50 +02:00
node . name_type = name_type
2021-08-14 07:48:25 +02:00
match node . gkind_field {
. name {
return ast . string_type
}
. typ {
return ast . int_type
}
else {
2021-08-14 16:22:25 +02:00
if node . field_name == ' n a m e ' {
return ast . string_type
} else if node . field_name == ' i d x ' {
return ast . int_type
2021-08-14 07:48:25 +02:00
}
2021-08-14 16:22:25 +02:00
c . error ( ' i n v a l i d f i e l d ` . $ node . field_name ` f o r t y p e ` $ node . expr ` ' , node . pos )
2021-08-14 07:48:25 +02:00
return ast . string_type
}
}
2020-11-09 14:35:26 +01:00
}
2021-06-24 06:33:12 +02:00
2021-02-28 20:24:29 +01:00
old_selector_expr := c . inside_selector_expr
c . inside_selector_expr = true
2021-11-24 13:43:37 +01:00
mut typ := c . expr ( node . expr )
if node . expr . is_auto_deref_var ( ) {
if mut node . expr is ast . Ident {
if mut node . expr . obj is ast . Var {
typ = node . expr . obj . typ
}
}
}
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-06-24 06:33:12 +02:00
c . error ( ' ` v o i d ` t y p e h a s n o f i e l d s ' , node . pos )
2021-04-02 00:57:09 +02:00
return ast . void_type
2020-03-11 05:56:15 +01:00
}
2021-06-03 15:49:50 +02:00
node . expr_type = typ
2021-08-18 12:49:50 +02:00
if node . expr_type . has_flag ( . optional ) && ! ( node . expr is ast . Ident
&& ( node . expr as ast . Ident ) . kind == . constant ) {
2021-01-27 13:53:20 +01:00
c . error ( ' c a n n o t a c c e s s f i e l d s o f a n o p t i o n a l , h a n d l e t h e e r r o r w i t h ` o r { . . . } ` o r p r o p a g a t e i t w i t h ` ? ` ' ,
2021-06-03 15:49:50 +02:00
node . pos )
2021-01-27 13:53:20 +01:00
}
2021-06-03 15:49:50 +02:00
field_name := node . field_name
2021-12-19 17:25:18 +01:00
sym := c . table . sym ( typ )
2021-04-19 14:38:48 +02:00
if ( typ . has_flag ( . variadic ) || sym . kind == . array_fixed ) && field_name == ' l e n ' {
2021-06-03 15:49:50 +02:00
node . typ = ast . int_type
2021-04-19 14:38:48 +02:00
return ast . int_type
}
if sym . kind == . chan {
if field_name == ' c l o s e d ' {
2021-06-03 15:49:50 +02:00
node . typ = ast . bool_type
2021-04-02 00:57:09 +02:00
return ast . bool_type
2021-04-19 14:38:48 +02:00
} else if field_name in [ ' l e n ' , ' c a p ' ] {
2021-06-03 15:49:50 +02:00
node . typ = ast . u32_type
2021-04-19 14:38:48 +02:00
return ast . u32_type
2021-01-22 08:37:29 +01:00
}
2020-02-29 09:04:47 +01:00
}
2021-06-12 14:15:25 +02:00
mut unknown_field_msg := ' t y p e ` $ sym . name ` h a s n o f i e l d n a m e d ` $ 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-06-17 11:27:31 +02:00
for embed in sym . info . embeds {
2021-12-19 17:25:18 +01:00
embed_sym := c . table . sym ( embed )
2020-12-23 19:12:49 +01:00
if embed_sym . embed_name ( ) == field_name {
2021-06-03 15:49:50 +02:00
node . typ = embed
2020-12-23 19:12:49 +01:00
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-07-23 00:14:39 +02:00
has_field = true
2021-12-04 09:19:19 +01:00
mut embed_types := [ ] ast . Type { }
2021-12-04 17:46:41 +01:00
field , embed_types = c . table . find_field_from_embeds ( sym , field_name ) or {
2022-02-11 14:52:33 +01:00
if err . msg ( ) ! = ' ' {
c . error ( err . msg ( ) , node . pos )
2020-12-23 19:12:49 +01:00
}
2021-07-23 00:14:39 +02:00
has_field = false
2021-12-04 09:19:19 +01:00
ast . StructField { } , [ ] ast . Type { }
2020-12-23 19:12:49 +01:00
}
2021-12-04 09:19:19 +01:00
node . from_embed_types = embed_types
2021-03-09 18:16:18 +01:00
if sym . kind in [ . aggregate , . sum_type ] {
2022-02-11 14:52:33 +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-06-03 15:49:50 +02:00
if sym . info . is_union && node . next_token ! in token . assign_tokens {
2021-01-09 01:32:33 +01:00
c . warn ( ' r e a d i n g a u n i o n f i e l d ( o r i t s a d d r e s s ) r e q u i r e s ` u n s a f e ` ' ,
2021-06-03 15:49:50 +02:00
node . pos )
2021-01-09 01:32:33 +01:00
}
}
}
2021-07-18 07:55:19 +02:00
if typ . has_flag ( . generic ) && ! has_field {
2021-12-19 17:25:18 +01:00
gs := c . table . sym ( c . unwrap_generic ( typ ) )
2021-07-18 07:55:19 +02:00
if f := c . table . find_field ( gs , field_name ) {
has_field = true
field = f
} else {
// look for embedded field
2021-07-23 00:14:39 +02:00
has_field = true
2021-12-04 09:19:19 +01:00
mut embed_types := [ ] ast . Type { }
2021-12-04 17:46:41 +01:00
field , embed_types = c . table . find_field_from_embeds ( gs , field_name ) or {
2022-02-11 14:52:33 +01:00
if err . msg ( ) ! = ' ' {
c . error ( err . msg ( ) , node . pos )
2021-07-18 07:55:19 +02:00
}
2021-07-23 00:14:39 +02:00
has_field = false
2021-12-04 09:19:19 +01:00
ast . StructField { } , [ ] ast . Type { }
2021-07-18 07:55:19 +02:00
}
2021-12-04 09:19:19 +01:00
node . from_embed_types = embed_types
2021-07-18 07:55:19 +02:00
}
}
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 {
2021-12-19 17:25:18 +01:00
unwrapped_sym := c . table . sym ( c . unwrap_generic ( typ ) )
2021-08-29 11:08:57 +02:00
c . error ( ' f i e l d ` $ { unwrapped_sym . name } . $ field_name ` i s n o t p u b l i c ' , node . pos )
2020-05-11 14:38:25 +02:00
}
2021-12-19 17:25:18 +01:00
field_sym := c . table . sym ( 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 {
2021-08-11 15:59:13 +02:00
if scope_field := node . scope . find_struct_field ( node . expr . str ( ) , typ , field_name ) {
2021-04-09 10:00:05 +02:00
return scope_field . smartcasts . last ( )
2020-11-11 09:18:15 +01:00
}
}
}
2021-06-03 15:49:50 +02:00
node . 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-12-19 17:25:18 +01:00
unwrapped_sym := c . table . sym ( c . unwrap_generic ( typ ) )
2022-03-02 19:46:18 +01:00
if unwrapped_sym . kind == . array_fixed && node . field_name == ' l e n ' {
node . typ = ast . int_type
return ast . int_type
}
2021-08-29 11:08:57 +02:00
c . error ( ' ` $ unwrapped_sym . name ` h a s n o p r o p e r t y ` $ node . field_name ` ' , node . 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 {
2022-03-03 15:36:40 +01:00
if c . smartcast_mut_pos != token . Pos { } {
c . note ( ' s m a r t c a s t i n g r e q u i r e s e i t h e r a n i m m u t a b l e v a l u e , o r a n e x p l i c i t m u t k e y w o r d b e f o r e t h e v a l u e ' ,
c . smartcast_mut_pos )
}
2020-12-23 19:12:49 +01:00
suggestion := util . new_suggestion ( field_name , sym . info . fields . map ( it . name ) )
2021-06-03 15:49:50 +02:00
c . error ( suggestion . say ( unknown_field_msg ) , node . pos )
2022-03-03 15:36:40 +01:00
return ast . void_type
2020-10-01 14:36:47 +02:00
}
2022-02-11 14:52:33 +01:00
// >> Hack to allow old style custom error implementations
// TODO: remove once deprecation period for `IError` methods has ended
if sym . name == ' I E r r o r ' && ( field_name == ' m s g ' || field_name == ' c o d e ' ) {
method := c . table . find_method ( sym , field_name ) or {
c . error ( ' i n v a l i d ` I E r r o r ` i n t e r f a c e i m p l e m e n t a t i o n : $ err ' , node . pos )
return ast . void_type
}
c . note ( ' t h e ` . $ field_name ` f i e l d o n ` I E r r o r ` i s d e p r e c a t e d , u s e ` . $ { field_name } ( ) ` i n s t e a d . ' ,
node . pos )
return method . return_type
}
// <<<
2022-03-03 15:36:40 +01:00
if c . smartcast_mut_pos != token . Pos { } {
c . note ( ' s m a r t c a s t i n g r e q u i r e s e i t h e r a n i m m u t a b l e v a l u e , o r a n e x p l i c i t m u t k e y w o r d b e f o r e t h e v a l u e ' ,
c . smartcast_mut_pos )
}
2021-06-03 15:49:50 +02:00
c . error ( unknown_field_msg , node . 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
}
2020-11-21 19:07:47 +01:00
pub fn ( mut c Checker ) const_decl ( mut node ast . ConstDecl ) {
2021-03-11 21:43:04 +01:00
if node . fields . len == 0 {
c . warn ( ' c o n s t b l o c k m u s t h a v e a t l e a s t 1 d e c l a r a t i o n ' , node . pos )
}
2021-11-26 19:36:27 +01:00
for field in node . fields {
2020-11-21 19:07:47 +01:00
// TODO Check const name once the syntax is decided
if field . name in c . const_names {
2022-01-26 11:36:28 +01:00
name_pos := token . Pos {
2021-03-03 08:23:11 +01:00
... field . pos
len : util . no_cur_mod ( field . name , c . mod ) . len
}
c . error ( ' d u p l i c a t e c o n s t ` $ field . name ` ' , name_pos )
2020-11-21 19:07:47 +01:00
}
c . const_names << field . name
}
2021-07-18 18:41:39 +02:00
for i , mut field in node . fields {
2020-11-21 19:07:47 +01:00
c . const_decl = field . name
c . const_deps << field . name
2021-07-18 21:59:52 +02:00
mut typ := c . check_expr_opt_call ( field . expr , c . expr ( field . expr ) )
2021-11-29 01:48:49 +01:00
if ct_value := c . eval_comptime_const_expr ( field . expr , 0 ) {
2021-07-18 18:41:39 +02:00
field . comptime_expr_value = ct_value
2021-07-18 21:59:52 +02:00
if ct_value is u64 {
typ = ast . u64_type
}
2021-07-18 18:41:39 +02:00
}
2022-01-04 17:37:18 +01:00
node . fields [ i ] . typ = ast . mktyp ( typ )
2020-11-21 19:07:47 +01:00
c . const_deps = [ ]
}
}
2021-12-03 05:45:37 +01:00
pub fn ( mut c Checker ) enum_decl ( mut node ast . EnumDecl ) {
2021-08-20 09:25:16 +02:00
c . check_valid_pascal_case ( node . name , ' e n u m n a m e ' , node . pos )
2021-12-03 05:45:37 +01:00
mut seen := [ ] i64 { cap : node . fields . len }
2021-08-20 09:25:16 +02:00
if node . fields . len == 0 {
c . error ( ' e n u m c a n n o t b e e m p t y ' , node . pos )
2021-01-05 01:46:32 +01:00
}
2021-05-16 03:28:11 +02:00
/ *
2021-08-20 09:25:16 +02:00
if node . is_pub && c . mod == ' b u i l t i n ' {
c . error ( ' ` b u i l t i n ` m o d u l e c a n n o t h a v e e n u m s ' , node . pos )
2021-05-16 03:28:11 +02:00
}
* /
2021-12-03 05:45:37 +01:00
for i , mut field in node . 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 ( ' f i e l d n a m e ` $ field . name ` c a n n o t c o n t a i n u p p e r c a s e l e t t e r s , u s e s n a k e _ c a s e i n s t e a d ' ,
field . pos )
}
2020-04-30 18:06:14 +02:00
for j in 0 .. i {
2021-08-20 09:25:16 +02:00
if field . name == node . fields [ j ] . name {
2020-04-30 09:34:18 +02:00
c . error ( ' f i e l d n a m e ` $ field . name ` d u p l i c a t e ' , field . pos )
}
}
2020-04-10 14:44:01 +02:00
if field . has_expr {
2021-12-03 05:45:37 +01:00
match mut 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 ( ' e n u m v a l u e ` $ val ` o v e r f l o w s i n t ' , field . expr . pos )
2022-02-05 23:16:02 +01:00
} else if ! c . pref . translated && ! c . file . is_translated && ! node . is_multi_allowed
&& i64 ( val ) in seen {
2020-11-21 00:05:57 +01:00
c . error ( ' e n u m v a l u e ` $ val ` a l r e a d y e x i s t s ' , 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 { }
2021-12-03 05:45:37 +01:00
ast . InfixExpr {
// Handle `enum Foo { x = 1 + 2 }`
c . infix_expr ( mut field . expr )
}
// ast.ParExpr {} // TODO allow `.x = (1+2)`
2020-04-09 19:23:49 +02:00
else {
2022-02-28 16:06:37 +01:00
if mut field . expr is ast . Ident {
if field . expr . language == . c {
2020-04-19 22:26:04 +02:00
continue
}
}
2022-01-26 11:36:28 +01:00
mut pos := field . expr . pos ( )
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 ( ' d e f a u l t v a l u e f o r e n u m h a s t o b e a n i n t e g e r ' , 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 ( ' e n u m v a l u e o v e r f l o w s ' , field . pos )
2022-02-05 23:16:02 +01:00
} else if ! c . pref . translated && ! c . file . is_translated && ! node . is_multi_allowed
&& last + 1 in seen {
2021-06-05 11:28:17 +02:00
c . error ( ' e n u m v a l u e ` $ { last + 1 } ` a l r e a d y e x i s t s ' , field . pos )
2020-07-04 17:28:01 +02:00
}
seen << last + 1
} else {
seen << 0
}
2020-04-09 19:23:49 +02:00
}
}
}
2020-11-22 20:51:07 +01:00
[ inline ]
2022-01-26 11:36:28 +01:00
fn ( mut c Checker ) check_loop_label ( label string , pos token . Pos ) {
2020-11-22 20:51:07 +01:00
if label . len == 0 {
// ignore
return
}
if c . loop_label . len != 0 {
c . error ( ' n e s t i n g o f l a b e l l e d ` f o r ` l o o p s i s n o t s u p p o r t e d ' , 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-12-06 08:44:52 +01:00
ntype := typeof ( node ) . replace ( ' v . a s t . ' , ' ' )
eprintln ( ' c h e c k i n g : $ { c . file . path : - 30 } | p o s : $ { node . pos . line_str ( ) : - 39 } | n o d e : $ ntype | $ node ' )
2020-07-12 11:56:01 +02:00
}
2021-11-01 08:41:04 +01: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 ( ' C h e c k e r . s t m t ( ) E m p t y S t m t ' )
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
}
2021-11-17 07:29:43 +01:00
ast . ComptimeFor {
2021-11-15 14:47:29 +01:00
c . comptime_for ( node )
2020-07-03 15:10:39 +02:00
}
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 {
2021-05-11 08:30:01 +02:00
node . idx_in_fn = c . table . cur_fn . defer_stmts . len
c . table . cur_fn . defer_stmts << unsafe { & node }
2021-02-06 17:40:39 +01:00
}
2021-05-12 10:44:47 +02:00
if c . locked_names . len != 0 || c . rlocked_names . len != 0 {
c . error ( ' d e f e r s a r e n o t a l l o w e d i n l o c k s t a t e m e n t s ' , node . pos )
}
2021-05-31 13:43:44 +02:00
for i , ident in node . defer_vars {
mut id := ident
2022-02-28 16:06:37 +01:00
if mut id . info is ast . IdentVar {
2021-11-15 14:47:29 +01:00
if id . comptime && id . name in checker . valid_comptime_not_user_defined {
2021-05-31 13:43:44 +02:00
node . defer_vars [ i ] = ast . Ident {
scope : 0
name : ' '
}
continue
}
typ := c . ident ( mut id )
if typ == ast . error_type_idx {
continue
}
2022-02-28 16:06:37 +01:00
id . info . typ = typ
2021-05-31 13:43:44 +02:00
node . defer_vars [ i ] = id
}
}
2021-07-23 21:35:05 +02:00
c . inside_defer = true
2021-11-27 07:09:36 +01:00
c . stmts ( node . stmts )
2021-07-23 21:35:05 +02:00
c . inside_defer = false
2020-04-09 19:23:49 +02:00
}
ast . EnumDecl {
2021-12-03 05:45:37 +01:00
c . enum_decl ( mut 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
2022-01-20 07:40:16 +01:00
match mut node . expr {
2021-04-10 16:57:18 +02:00
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 { }
}
2021-11-27 06:38:35 +01:00
if ! c . pref . is_repl && ( c . stmt_level == 1 || ( c . stmt_level > 1 && ! c . is_last_stmt ) ) {
2022-01-20 07:40:16 +01:00
if mut node . expr is ast . InfixExpr {
2021-11-27 06:38:35 +01:00
if node . expr . op == . left_shift {
2021-12-19 17:25:18 +01:00
left_sym := c . table . final_sym ( node . expr . left_type )
2021-11-27 06:38:35 +01:00
if left_sym . kind != . array {
c . error ( ' u n u s e d e x p r e s s i o n ' , node . pos )
}
}
}
}
2021-04-10 16:57:18 +02:00
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-07-28 07:11:55 +02:00
c . global_decl ( mut 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-07-23 21:35:05 +02:00
if c . inside_defer {
c . error ( ' g o t o i s n o t a l l o w e d i n d e f e r s t a t e m e n t s ' , node . pos )
}
2021-02-15 14:48:24 +01:00
if ! c . inside_unsafe {
c . warn ( ' ` g o t o ` r e q u i r e s ` u n s a f e ` ( c o n s i d e r u s i n g l a b e l l e d b r e a k / c o n t i n u e ) ' ,
node . pos )
}
2021-05-11 08:30:01 +02:00
if node . name ! in c . table . cur_fn . label_names {
2021-02-03 15:20:10 +01:00
c . error ( ' u n k n o w n l a b e l ` $ 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 {
2021-05-02 02:00:47 +02:00
c . interface_decl ( mut 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 [ ' b u i l t i n ' , ' o s ' , ' s t r c o n v ' ]
2020-07-04 11:23:41 +02:00
c . check_valid_snake_case ( node . name , ' m o d u l e n a m e ' , 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-06-02 19:38:26 +02:00
if assert_type != ast . bool_type_idx {
2021-12-19 17:25:18 +01:00
atype_name := c . table . sym ( assert_type ) . name
2021-02-10 17:51:41 +01:00
c . error ( ' a s s e r t c a n b e u s e d o n l y w i t h ` b o o l ` e x p r e s s i o n s , b u t f o u n d ` $ atype_name ` i n s t e a d ' ,
node . pos )
}
c . expected_type = cur_exp_typ
}
fn ( mut c Checker ) block ( node ast . Block ) {
if node . is_unsafe {
c . inside_unsafe = true
2021-11-27 07:09:36 +01:00
c . stmts ( node . stmts )
2021-02-10 17:51:41 +01:00
c . inside_unsafe = false
} else {
2021-11-27 07:09:36 +01:00
c . stmts ( node . stmts )
2021-02-10 17:51:41 +01:00
}
}
fn ( mut c Checker ) branch_stmt ( node ast . BranchStmt ) {
2021-07-23 21:35:05 +02:00
if c . inside_defer {
c . error ( ' ` $ node . kind . str ( ) ` i s n o t a l l o w e d i n d e f e r s t a t e m e n t s ' , node . pos )
}
2021-07-25 09:38:37 +02:00
if c . in_for_count == 0 {
2021-02-10 17:51:41 +01:00
c . error ( ' $ node . kind . str ( ) s t a t e m e n t n o t w i t h i n a l o o p ' , node . pos )
}
if node . label . len > 0 {
if node . label != c . loop_label {
c . error ( ' i n v a l i d l a b e l n a m e ` $ node . label ` ' , node . pos )
}
}
}
2021-07-28 07:11:55 +02:00
fn ( mut c Checker ) global_decl ( mut node ast . GlobalDecl ) {
for mut field in node . fields {
2021-02-10 17:51:41 +01:00
c . check_valid_snake_case ( field . name , ' g l o b a l n a m e ' , field . pos )
if field . name in c . global_names {
c . error ( ' d u p l i c a t e g l o b a l ` $ field . name ` ' , field . pos )
}
2021-12-19 17:25:18 +01:00
sym := c . table . sym ( field . typ )
2021-04-23 11:28:08 +02:00
if sym . kind == . placeholder {
c . error ( ' u n k n o w n t y p e ` $ sym . name ` ' , field . typ_pos )
}
2021-07-31 15:35:19 +02:00
if field . has_expr {
field . typ = c . expr ( field . expr )
mut v := c . file . global_scope . find_global ( field . name ) or {
panic ( ' i n t e r n a l c o m p i l e r e r r o r - c o u l d n o t f i n d g l o b a l i n s c o p e ' )
2021-07-28 07:11:55 +02:00
}
2022-01-04 17:37:18 +01:00
v . typ = ast . mktyp ( field . typ )
2021-07-28 07:11:55 +02:00
}
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 ( ' o p t i o n a l h a n d l i n g c a n n o t b e d o n e i n ` g o ` c a l l . D o i t w h e n c a l l i n g ` . w a i t ( ) ` ' ,
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 ( ' f u n c t i o n i n ` g o ` s t a t e m e n t c a n n o t c o n t a i n m u t a b l e n o n - r e f e r e n c e a r g u m e n t s ' ,
2022-01-26 11:36:28 +01:00
arg . expr . pos ( ) )
2021-02-10 17:51:41 +01:00
}
}
if node . call_expr . is_method && node . call_expr . receiver_type . is_ptr ( )
&& ! node . call_expr . left_type . is_ptr ( ) {
c . error ( ' m e t h o d i n ` g o ` s t a t e m e n t c a n n o t h a v e n o n - r e f e r e n c e m u t a b l e r e c e i v e r ' ,
2022-01-26 11:36:28 +01:00
node . call_expr . left . pos ( ) )
2021-02-10 17:51:41 +01:00
}
2021-12-07 10:11:54 +01:00
if c . pref . backend . is_js ( ) {
return c . table . find_or_register_promise ( ret_type )
} else {
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 ( ' i n l i n e a s s e m b l y g o t o i s n o t s u p p o r t e d , i t w i l l m o s t l i k e l y n o t w o r k ' ,
stmt . pos )
}
2021-07-19 14:55:03 +02:00
if c . pref . backend . is_js ( ) {
2021-04-14 07:50:50 +02:00
c . error ( ' i n l i n e a s s e m b l y i s n o t s u p p o r t e d i n t h e j s b a c k e n d ' , stmt . pos )
2021-03-17 01:43:17 +01:00
}
if c . pref . backend == . c && c . pref . ccompiler_type == . msvc {
c . error ( ' m s v c c o m p i l e r d o e s n o t s u p p o r t i n l i n e a s s e m b l y ' , 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
2021-10-17 05:41:39 +02:00
for mut template in stmt . templates {
2021-03-17 01:43:17 +01:00
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 [ ' s k i p ' , ' s p a c e ' , ' b y t e ' , ' w o r d ' , ' s h o r t ' , ' i n t ' , ' l o n g ' , ' q u a d ' ,
' g l o b l ' , ' g l o b a l ' , ' s e c t i o n ' , ' t e x t ' , ' d a t a ' , ' b s s ' , ' f i l l ' , ' o r g ' , ' p r e v i o u s ' ,
2021-04-15 01:30:23 +02:00
' s t r i n g ' , ' a s c i z ' , ' a s c i i ' ] { // all tcc-supported assembler directives
2021-03-17 01:43:17 +01:00
c . error ( ' u n k n o w n a s s e m b l e r d i r e c t i v e : ` $ 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 ( ' s c a l e m u s t b e o n e o f 1 , 2 , 4 , o r 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
}
2021-07-23 14:30:51 +02:00
if c . ct_cond_stack . len > 0 {
node . ct_conds = c . ct_cond_stack . clone ( )
}
2021-07-19 14:55:03 +02:00
if c . pref . backend . is_js ( ) {
2020-08-28 08:24:04 +02:00
if ! c . file . path . ends_with ( ' . j s . v ' ) {
2021-03-11 21:43:42 +01:00
c . error ( ' h a s h s t a t e m e n t s a r e o n l y a l l o w e d i n b a c k e n d s p e c i f i c f i l e s s u c h " x . j s . v " ' ,
2020-08-28 08:24:04 +02:00
node . pos )
}
if c . mod == ' m a i n ' {
2021-05-08 20:25:39 +02:00
c . error ( ' h a s h s t a t e m e n t s a r e n o t a l l o w e d i n t h e m a i n m o d u l e . P l a c e t h e m i n a s e p a r a t e m o d u l e . ' ,
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 {
' i n c l u d e ' {
mut flag := node . main
if flag . contains ( ' @ V R O O T ' ) {
2021-04-19 18:01:47 +02:00
// c.note(checker.vroot_is_deprecated_message, node.pos)
vroot := util . resolve_vmodroot ( flag . replace ( ' @ V R O O T ' , ' @ V M O D R O O T ' ) , c . file . path ) or {
2022-02-11 14:52:33 +01:00
c . error ( err . msg ( ) , node . pos )
2021-04-19 18:01:47 +02:00
return
}
node . val = ' i n c l u d e $ vroot '
node . main = vroot
flag = vroot
}
if flag . contains ( ' @ V E X E R O O T ' ) {
vroot := flag . replace ( ' @ V E X E R O O T ' , os . dir ( pref . vexe_path ( ) ) )
node . val = ' i n c l u d e $ vroot '
node . main = vroot
flag = vroot
}
if flag . contains ( ' @ V M O D R O O T ' ) {
vroot := util . resolve_vmodroot ( flag , c . file . path ) or {
2022-02-11 14:52:33 +01:00
c . error ( err . msg ( ) , node . pos )
2021-03-11 21:43:42 +01:00
return
}
node . val = ' i n c l u d e $ vroot '
node . main = vroot
flag = vroot
}
if flag . contains ( ' \$ e n v ( ' ) {
env := util . resolve_env_value ( flag , true ) or {
2022-02-11 14:52:33 +01:00
c . error ( err . msg ( ) , node . pos )
2021-03-11 21:43:42 +01:00
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 ( ' i n c l u d i n g C f i l e s s h o u l d u s e e i t h e r ` " h e a d e r _ f i l e . h " ` o r ` < h e a d e r _ f i l e . h > ` q u o t i n g ' ,
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
' p k g c o n f i g ' {
args := if node . main . contains ( ' - - ' ) {
node . main . split ( ' ' )
} else {
' - - c f l a g s - - l i b s $ node . main ' . split ( ' ' )
}
mut m := pkgconfig . main ( args ) or {
2022-02-11 14:52:33 +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 {
2022-02-11 14:52:33 +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 {
2022-02-11 14:52:33 +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
' f l a g ' {
// #flag linux -lm
mut flag := node . main
if flag . contains ( ' @ V R O O T ' ) {
2021-04-19 18:01:47 +02:00
// c.note(checker.vroot_is_deprecated_message, node.pos)
flag = util . resolve_vmodroot ( flag . replace ( ' @ V R O O T ' , ' @ V M O D R O O T ' ) , c . file . path ) or {
2022-02-11 14:52:33 +01:00
c . error ( err . msg ( ) , node . pos )
2021-04-19 18:01:47 +02:00
return
}
}
if flag . contains ( ' @ V E X E R O O T ' ) {
// expand `@VEXEROOT` to its absolute path
flag = flag . replace ( ' @ V E X E R O O T ' , os . dir ( pref . vexe_path ( ) ) )
}
if flag . contains ( ' @ V M O D R O O T ' ) {
flag = util . resolve_vmodroot ( flag , c . file . path ) or {
2022-02-11 14:52:33 +01:00
c . error ( err . msg ( ) , node . pos )
2021-03-11 21:43:42 +01:00
return
}
}
if flag . contains ( ' \$ e n v ( ' ) {
flag = util . resolve_env_value ( flag , true ) or {
2022-02-11 14:52:33 +01:00
c . error ( err . msg ( ) , node . pos )
2021-03-11 21:43:42 +01:00
return
}
}
for deprecated in [ ' @ V M O D ' , ' @ V M O D U L E ' , ' @ V P A T H ' , ' @ V L I B _ P A T H ' ] {
if flag . contains ( deprecated ) {
2021-04-19 18:01:47 +02:00
if ! flag . contains ( ' @ V M O D R O O T ' ) {
c . error ( ' $ deprecated h a d b e e n d e p r e c a t e d , u s e @ V M O D R O O T i n s t e a d . ' ,
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 {
2022-02-11 14:52:33 +01:00
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 != ' d e f i n e ' {
c . error ( ' e x p e c t e d ` # d e f i n e ` , ` # f l a g ` , ` # i n c l u d e ` o r ` # p k g c o n f i g ` n o t $ node . val ' ,
node . pos )
}
2020-10-15 10:58:01 +02:00
}
2020-08-28 08:24:04 +02:00
}
}
2021-08-20 09:25:16 +02:00
fn ( mut c Checker ) import_stmt ( node ast . Import ) {
c . check_valid_snake_case ( node . alias , ' m o d u l e a l i a s ' , node . pos )
for sym in node . syms {
name := ' $ { node . mod } . $ sym . name '
2020-12-07 18:13:03 +01:00
if sym . name [ 0 ] . is_capital ( ) {
2022-01-02 14:47:58 +01:00
if type_sym := c . table . find_sym ( name ) {
2020-12-07 18:13:03 +01:00
if type_sym . kind != . placeholder {
if ! type_sym . is_public {
2021-08-20 09:25:16 +02:00
c . error ( ' m o d u l e ` $ node . mod ` t y p e ` $ sym . name ` i s p r i v a t e ' , sym . pos )
2020-12-07 18:13:03 +01:00
}
continue
2020-07-18 21:34:38 +02:00
}
}
2021-08-20 09:25:16 +02:00
c . error ( ' m o d u l e ` $ node . mod ` h a s n o t y p e ` $ sym . name ` ' , sym . pos )
2020-12-07 18:13:03 +01:00
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 {
2021-08-20 09:25:16 +02:00
c . error ( ' m o d u l e ` $ node . mod ` f u n c t i o n ` $ { sym . name } ( ) ` i s p r i v a t e ' , sym . pos )
2020-12-07 18:13:03 +01:00
}
continue
}
if _ := c . file . global_scope . find_const ( name ) {
continue
}
2021-08-20 09:25:16 +02:00
c . error ( ' m o d u l e ` $ node . mod ` h a s n o c o n s t a n t o r f u n c t i o n ` $ sym . name ` ' , sym . pos )
2020-07-18 21:34:38 +02:00
}
2022-01-27 10:50:26 +01:00
if after_time := c . table . mdeprecated_after [ node . mod ] {
now := time . now ( )
deprecation_message := c . table . mdeprecated_msg [ node . mod ]
c . deprecate ( ' m o d u l e ' , node . mod , deprecation_message , now , after_time , node . pos )
}
2020-07-18 21:34:38 +02:00
}
2021-11-27 07:09:36 +01:00
// stmts should be used for processing normal statement lists (fn bodies, for loop bodies etc).
fn ( mut c Checker ) stmts ( stmts [ ] ast . Stmt ) {
2021-11-27 06:38:35 +01:00
old_stmt_level := c . stmt_level
c . stmt_level = 0
2021-11-27 07:09:36 +01:00
c . stmts_ending_with_expression ( stmts )
2021-11-27 06:38:35 +01:00
c . stmt_level = old_stmt_level
}
2021-11-27 07:09:36 +01:00
// stmts_ending_with_expression, should be used for processing list of statements, that can end with an expression.
// Examples for such lists are the bodies of `or` blocks, `if` expressions and `match` expressions:
// `x := opt() or { stmt1 stmt2 ExprStmt }`,
// `x := if cond { stmt1 stmt2 ExprStmt } else { stmt2 stmt3 ExprStmt }`,
// `x := match expr { Type1 { stmt1 stmt2 ExprStmt } else { stmt2 stmt3 ExprStmt }`.
fn ( mut c Checker ) stmts_ending_with_expression ( stmts [ ] ast . Stmt ) {
2022-02-17 09:04:01 +01:00
if stmts . len == 0 {
c . scope_returns = false
c . expected_type = ast . void_type
return
}
if c . stmt_level > checker . stmt_level_cutoff_limit {
c . scope_returns = false
c . expected_type = ast . void_type
c . error ( ' c h e c k e r : t o o m a n y s t m t l e v e l s : $ c . stmt_level ' , stmts [ 0 ] . pos )
return
}
2022-01-26 11:36:28 +01:00
mut unreachable := token . Pos {
2020-04-30 18:06:14 +02:00
line_nr : - 1
}
2021-04-02 00:57:09 +02:00
c . expected_type = ast . void_type
2021-11-27 06:38:35 +01:00
c . stmt_level ++
for i , stmt in stmts {
c . is_last_stmt = i == stmts . len - 1
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 )
2021-10-28 09:40:18 +02:00
if stmt is ast . GotoLabel {
2022-01-26 11:36:28 +01:00
unreachable = token . Pos {
2021-10-28 09:40:18 +02:00
line_nr : - 1
}
c . scope_returns = false
}
2021-11-29 01:48:49 +01:00
if c . should_abort {
return
}
2020-02-29 18:34:25 +01:00
}
2021-11-27 06:38:35 +01:00
c . stmt_level --
2020-04-29 12:31:18 +02:00
if unreachable . line_nr >= 0 {
2020-06-15 07:10:45 +02:00
c . error ( ' u n r e a c h a b l e c o d e ' , unreachable )
2020-04-29 12:31:18 +02:00
}
2021-07-05 04:05:37 +02:00
c . find_unreachable_statements_after_noreturn_calls ( stmts )
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-05-11 08:30:01 +02:00
if t_typ := c . table . resolve_generic_to_concrete ( typ , c . table . cur_fn . generic_names ,
2021-06-24 11:47:36 +02:00
c . table . cur_concrete_types )
2021-04-26 19:05:10 +02:00
{
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 --
}
2021-11-20 20:28:11 +01:00
2022-02-17 09:04:01 +01:00
if c . expr_level > checker . expr_level_cutoff_limit {
2022-01-26 11:36:28 +01:00
c . error ( ' c h e c k e r : t o o m a n y e x p r l e v e l s : $ c . expr_level ' , node . pos ( ) )
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 { }
2022-02-21 16:42:54 +01:00
ast . ComptimeType {
c . error ( ' i n c o r r e c t u s e o f c o m p i l e - t i m e t y p e ' , node . pos )
}
2021-03-31 10:13:15 +02:00
ast . EmptyExpr {
2022-01-26 11:36:28 +01:00
c . error ( ' c h e c k e r . e x p r ( ) : u n h a n d l e d E m p t y E x p r ' , token . Pos { } )
2021-03-31 10:13:15 +02:00
}
2020-10-21 21:58:40 +02:00
ast . CTempVar {
return node . typ
}
2020-05-15 23:14:53 +02:00
ast . AnonFn {
2021-08-10 20:27:15 +02:00
return c . anon_fn ( mut node )
2020-05-15 23:14:53 +02:00
}
2020-12-29 16:14:08 +01:00
ast . ArrayDecompose {
typ := c . expr ( node . expr )
2021-12-19 17:25:18 +01:00
type_sym := c . table . sym ( typ )
2020-12-29 16:14:08 +01:00
if type_sym . kind != . array {
2022-01-26 11:36:28 +01:00
c . error ( ' d e c o m p o s i t i o n c a n o n l y b e u s e d o n a r r a y s ' , node . expr . pos ( ) )
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 )
2021-12-19 17:25:18 +01:00
expr_type_sym := c . table . sym ( node . expr_type )
type_sym := c . table . sym ( 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 { }
2021-12-06 23:31:47 +01:00
if ! c . table . sumtype_has_variant ( node . expr_type , node . typ , true ) {
addr := ' & ' . repeat ( node . typ . nr_muls ( ) )
c . error ( ' c a n n o t c a s t ` $ expr_type_sym . name ` t o ` $ addr $ type_sym . name ` ' ,
node . pos )
2020-03-18 14:50:21 +01:00
}
2021-08-17 20:00:27 +02:00
} else if expr_type_sym . kind == . interface_ && type_sym . kind == . interface_ {
c . ensure_type_exists ( node . typ , node . pos ) or { }
2021-06-17 11:27:31 +02:00
} else if node . expr_type != node . typ {
mut s := ' c a n n o t c a s t n o n - s u m t y p e ` $ expr_type_sym . name ` u s i n g ` a s ` '
if type_sym . kind == . sum_type {
s += ' - u s e e . g . ` $ { type_sym . name } ( s o m e _ e x p r ) ` i n s t e a d . '
}
c . error ( s , node . pos )
2020-11-11 09:18:15 +01:00
}
2021-06-16 18:16:15 +02:00
return node . typ
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 ( ' u n e x p e c t e d ` o r ` b l o c k , t h e f u n c t i o n ` $ node . name ` d o e s n o t r e t u r n a n o p t i o n a l ' ,
node . or_block . pos )
} else if node . or_block . kind == . propagate {
c . error ( ' u n e x p e c t e d ` ? ` , t h e f u n c t i o n ` $ node . name ` d o e s n o t r e t u r n a n o p t i o n a l ' ,
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 ) )
2021-12-19 17:25:18 +01:00
expr_sym := c . table . sym ( 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 ( ' e x p e c t e d ` s t r i n g ` i n s t e a d o f ` $ expr_sym . name ` ( e . g . ` f i e l d . n a m e ` ) ' ,
2022-01-26 11:36:28 +01:00
node . field_expr . pos ( ) )
2021-01-05 15:11:43 +01:00
}
2022-01-20 07:40:16 +01:00
if mut node . field_expr is ast . SelectorExpr {
2022-01-26 11:36:28 +01:00
left_pos := node . field_expr . expr . pos ( )
2021-01-29 00:45:00 +01:00
if c . comptime_fields_type . len == 0 {
c . error ( ' c o m p i l e t i m e f i e l d a c c e s s c a n o n l y b e u s e d w h e n i t e r a t i n g o v e r ` T . f i e l d s ` ' ,
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 ( ' u n k n o w n ` \$ f o r ` v a r i a b l e ` $ expr_name ` ' , left_pos )
} else {
2022-01-26 11:36:28 +01:00
c . error ( ' e x p e c t e d s e l e c t o r e x p r e s s i o n e . g . ` $ ( f i e l d . n a m e ) ` ' , node . field_expr . pos ( ) )
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 {
2022-01-26 11:36:28 +01:00
c . error ( ' d u m p e x p r e s s i o n c a n n o t b e v o i d ' , node . expr . pos ( ) )
2021-04-02 00:57:09 +02:00
return ast . void_type
2021-03-06 18:09:28 +01:00
}
2021-12-19 17:25:18 +01:00
tsym := c . table . sym ( node . expr_type )
2021-03-06 18:09:28 +01:00
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 {
2021-09-15 14:42:28 +02:00
old_inside_if_guard := c . inside_if_guard
c . inside_if_guard = true
2020-06-18 20:38:59 +02:00
node . expr_type = c . expr ( node . expr )
2021-09-15 14:42:28 +02:00
c . inside_if_guard = old_inside_if_guard
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 {
2022-01-26 11:36:28 +01:00
c . error ( ' e x p r e s s i o n s h o u l d r e t u r n a n o p t i o n ' , node . expr . pos ( ) )
2021-03-03 09:10:38 +01:00
}
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-07-09 13:02:10 +02:00
return c . int_lit ( mut node )
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 {
2021-08-18 12:49:50 +02:00
if node . expr is ast . ParExpr {
c . warn ( ' r e d u n d a n t p a r e n t h e s e s a r e u s e d ' , node . pos )
}
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-06-13 05:26:13 +02:00
ast . IsRefType {
if ! node . is_type {
node . typ = c . expr ( node . expr )
}
return ast . bool_type
}
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-07-12 21:37:31 +02:00
return c . string_lit ( mut node )
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 ) {
2021-12-19 17:25:18 +01:00
ltype_sym := c . table . sym ( ltype )
2020-06-18 20:38:59 +02:00
lname := if node . is_likely { ' _ l i k e l y _ ' } else { ' _ u n l i k e l y _ ' }
2020-12-06 04:55:08 +01:00
c . error ( ' ` $ { lname } ( ) ` e x p e c t s a b o o l e a n e x p r e s s i o n , i n s t e a d i t g o t ` $ 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 {
2021-12-01 08:50:53 +01:00
// Given: `Outside( Inside(xyz) )`,
// node.expr_type: `Inside`
// node.typ: `Outside`
2020-12-16 17:50:39 +01:00
node . expr_type = c . expr ( node . expr ) // type to be casted
2021-12-29 20:48:23 +01:00
2022-01-05 08:27:10 +01:00
mut from_type := c . unwrap_generic ( node . expr_type )
2021-12-29 20:48:23 +01:00
from_sym := c . table . sym ( from_type )
final_from_sym := c . table . final_sym ( from_type )
2021-12-30 20:19:24 +01:00
mut to_type := node . typ
mut to_sym := c . table . sym ( to_type ) // type to be used as cast
mut final_to_sym := c . table . final_sym ( to_type )
2021-12-29 20:48:23 +01:00
if ( to_sym . is_number ( ) && from_sym . name == ' J S . N u m b e r ' )
|| ( to_sym . is_number ( ) && from_sym . name == ' J S . B i g I n t ' )
|| ( to_sym . is_string ( ) && from_sym . name == ' J S . S t r i n g ' )
|| ( to_type . is_bool ( ) && from_sym . name == ' J S . B o o l e a n ' )
|| ( from_type . is_bool ( ) && to_sym . name == ' J S . B o o l e a n ' )
|| ( from_sym . is_number ( ) && to_sym . name == ' J S . N u m b e r ' )
|| ( from_sym . is_number ( ) && to_sym . name == ' J S . B i g I n t ' )
|| ( from_sym . is_string ( ) && to_sym . name == ' J S . S t r i n g ' ) {
2021-12-01 08:50:53 +01:00
return to_type
2021-11-10 10:37:16 +01:00
}
2021-12-29 20:48:23 +01:00
if to_sym . language != . c {
2021-12-01 08:50:53 +01:00
c . ensure_type_exists ( to_type , node . pos ) or { }
2021-02-26 07:26:36 +01:00
}
2021-12-29 20:48:23 +01:00
if from_sym . kind == . byte && from_type . is_ptr ( ) && to_sym . kind == . string && ! to_type . is_ptr ( ) {
2021-07-13 11:01:24 +02:00
c . error ( ' t o c o n v e r t a C s t r i n g b u f f e r p o i n t e r t o a V s t r i n g , u s e x . v s t r i n g ( ) i n s t e a d o f s t r i n g ( x ) ' ,
node . pos )
}
2021-12-01 08:50:53 +01:00
if from_type == ast . void_type {
2022-01-26 11:36:28 +01:00
c . error ( ' e x p r e s s i o n d o e s n o t r e t u r n a v a l u e s o i t c a n n o t b e c a s t ' , node . expr . pos ( ) )
2021-07-07 20:59:58 +02:00
}
2021-12-29 20:48:23 +01:00
if to_sym . kind == . sum_type {
2021-12-01 08:50:53 +01:00
if from_type in [ ast . int_literal_type , ast . float_literal_type ] {
xx := if from_type == ast . int_literal_type { ast . int_type } else { ast . f64_type }
node . expr_type = c . promote_num ( node . expr_type , xx )
from_type = node . expr_type
2020-11-02 01:17:35 +01:00
}
2021-12-06 23:31:47 +01:00
if ! c . table . sumtype_has_variant ( to_type , from_type , false ) && ! to_type . has_flag ( . optional ) {
2022-02-03 11:24:29 +01:00
ft := c . table . type_to_str ( from_type )
tt := c . table . type_to_str ( to_type )
c . error ( ' c a n n o t c a s t ` $ ft ` t o ` $ tt ` ' , node . pos )
2020-11-02 01:17:35 +01:00
}
2022-01-26 11:32:14 +01:00
} else if mut to_sym . info is ast . Alias && ! ( final_to_sym . kind == . struct_ && to_type . is_ptr ( ) ) {
2021-12-29 20:48:23 +01:00
if ! c . check_types ( from_type , to_sym . info . parent_type ) && ! ( final_to_sym . is_int ( )
2022-01-07 11:54:11 +01:00
&& final_from_sym . kind in [ . enum_ , . bool , . i8 , . char ] ) {
2022-02-03 11:24:29 +01:00
ft := c . table . type_to_str ( from_type )
tt := c . table . type_to_str ( to_type )
c . error ( ' c a n n o t c a s t ` $ ft ` t o ` $ tt ` ( a l i a s t o ` $ final_to_sym . name ` ) ' , node . pos )
2020-11-02 01:17:35 +01:00
}
2021-12-29 20:48:23 +01:00
} else if to_sym . kind == . struct_ && ! to_type . is_ptr ( )
&& ! ( to_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
2021-12-29 20:48:23 +01:00
if from_sym . kind == . struct_ && ! from_type . is_ptr ( ) {
2021-02-01 20:08:25 +01:00
c . warn ( ' c a s t i n g t o s t r u c t i s d e p r e c a t e d , u s e e . g . ` S t r u c t { . . . e x p r } ` i n s t e a d ' ,
node . pos )
2021-12-29 20:48:23 +01:00
from_type_info := from_sym . info as ast . Struct
to_type_info := to_sym . info as ast . Struct
2020-11-02 01:17:35 +01:00
if ! c . check_struct_signature ( from_type_info , to_type_info ) {
2021-12-29 20:48:23 +01:00
c . error ( ' c a n n o t c o n v e r t s t r u c t ` $ from_sym . name ` t o s t r u c t ` $ to_sym . name ` ' ,
2020-11-02 01:17:35 +01:00
node . pos )
}
} else {
2022-02-03 11:24:29 +01:00
ft := c . table . type_to_str ( from_type )
c . error ( ' c a n n o t c a s t ` $ ft ` t o s t r u c t ' , node . pos )
2020-11-02 01:17:35 +01:00
}
2021-12-29 20:48:23 +01:00
} else if to_sym . kind == . interface_ {
2021-12-01 08:50:53 +01:00
if c . type_implements ( from_type , to_type , node . pos ) {
2021-12-29 20:48:23 +01:00
if ! from_type . is_ptr ( ) && ! from_type . is_pointer ( ) && from_sym . kind != . interface_
2021-12-01 08:50:53 +01:00
&& ! c . inside_unsafe {
2021-06-21 06:10:10 +02:00
c . mark_as_referenced ( mut & node . expr , true )
}
2021-12-30 20:19:24 +01:00
if ( to_sym . info as ast . Interface ) . is_generic {
inferred_type := c . resolve_generic_interface ( from_type , to_type , node . pos )
if inferred_type != 0 {
to_type = inferred_type
to_sym = c . table . sym ( to_type )
final_to_sym = c . table . final_sym ( to_type )
}
}
2021-06-21 06:10:10 +02:00
}
2022-01-19 13:26:24 +01:00
} else if to_type == ast . bool_type && from_type != ast . bool_type && ! c . inside_unsafe
2022-02-05 23:16:02 +01:00
&& ! c . pref . translated && ! c . file . is_translated {
2020-11-02 01:17:35 +01:00
c . error ( ' c a n n o t c a s t t o b o o l - u s e e . g . ` s o m e _ i n t ! = 0 ` i n s t e a d ' , node . pos )
2021-12-01 08:50:53 +01:00
} else if from_type == ast . none_type && ! to_type . has_flag ( . optional ) {
type_name := c . table . type_to_str ( to_type )
2020-11-02 01:17:35 +01:00
c . error ( ' c a n n o t c a s t ` n o n e ` t o ` $ type_name ` ' , node . pos )
2021-12-29 20:48:23 +01:00
} else if from_sym . kind == . struct_ && ! from_type . is_ptr ( ) {
if ( to_type . is_ptr ( ) || to_sym . kind ! in [ . sum_type , . interface_ ] ) && ! c . is_builtin_mod {
2021-12-01 08:50:53 +01:00
from_type_name := c . table . type_to_str ( from_type )
type_name := c . table . type_to_str ( to_type )
2021-12-25 10:34:15 +01:00
c . error ( ' c a n n o t c a s t s t r u c t ` $ from_type_name ` t o ` $ type_name ` ' , node . pos )
2021-01-15 01:20:58 +01:00
}
2021-12-29 20:48:23 +01:00
} else if to_sym . kind == . byte && ! final_from_sym . is_number ( ) && ! final_from_sym . is_pointer ( )
&& ! from_type . is_ptr ( ) && final_from_sym . kind ! in [ . char , . enum_ , . bool ] {
2022-02-03 11:24:29 +01:00
ft := c . table . type_to_str ( from_type )
tt := c . table . type_to_str ( to_type )
c . error ( ' c a n n o t c a s t t y p e ` $ ft ` t o ` $ tt ` ' , node . pos )
2021-12-01 08:50:53 +01:00
} else if from_type . has_flag ( . optional ) || from_type . has_flag ( . variadic ) {
2020-12-16 17:50:39 +01:00
// variadic case can happen when arrays are converted into variadic
2021-12-01 08:50:53 +01:00
msg := if from_type . has_flag ( . optional ) { ' a n o p t i o n a l ' } else { ' a v a r i a d i c ' }
2020-12-16 17:50:39 +01:00
c . error ( ' c a n n o t t y p e c a s t $ msg ' , node . pos )
2021-12-01 08:50:53 +01:00
} else if ! c . inside_unsafe && to_type . is_ptr ( ) && from_type . is_ptr ( )
&& to_type . deref ( ) ! = ast . char_type && from_type . deref ( ) ! = ast . char_type {
ft := c . table . type_to_str ( from_type )
tt := c . table . type_to_str ( to_type )
2021-01-05 19:26:48 +01:00
c . warn ( ' c a s t i n g ` $ ft ` t o ` $ tt ` i s o n l y a l l o w e d i n ` u n s a f e ` c o d e ' , node . pos )
2021-12-29 20:48:23 +01:00
} else if from_sym . kind == . array_fixed && ! from_type . is_ptr ( ) {
2021-02-17 20:45:11 +01:00
c . warn ( ' c a n n o t c a s t a f i x e d a r r a y ( u s e e . g . ` & a r r [ 0 ] ` i n s t e a d ) ' , node . pos )
2021-12-29 20:48:23 +01:00
} else if final_from_sym . kind == . string && final_to_sym . is_number ( )
&& final_to_sym . kind != . rune {
2021-12-28 06:55:39 +01:00
snexpr := node . expr . str ( )
2022-02-03 11:24:29 +01:00
tt := c . table . type_to_str ( to_type )
c . error ( ' c a n n o t c a s t s t r i n g t o ` $ tt ` , u s e ` $ { snexpr } . $ { final_to_sym . name } ( ) ` i n s t e a d . ' ,
2021-12-28 06:55:39 +01:00
node . pos )
2020-11-02 01:17:35 +01:00
}
2021-12-01 10:25:53 +01:00
2022-01-24 11:13:22 +01:00
if to_sym . kind == . rune && from_sym . is_string ( ) {
snexpr := node . expr . str ( )
2022-02-03 11:24:29 +01:00
ft := c . table . type_to_str ( from_type )
c . error ( ' c a n n o t c a s t ` $ ft ` t o r u n e , u s e ` $ { snexpr } . r u n e s ( ) ` i n s t e a d . ' , node . pos )
2022-01-24 11:13:22 +01:00
}
2021-12-01 10:25:53 +01:00
if to_type == ast . string_type {
if from_type in [ ast . byte_type , ast . bool_type ] {
snexpr := node . expr . str ( )
2022-02-03 11:24:29 +01:00
ft := c . table . type_to_str ( from_type )
c . error ( ' c a n n o t c a s t t y p e ` $ ft ` t o s t r i n g , u s e ` $ { snexpr } . s t r ( ) ` i n s t e a d . ' ,
2021-12-01 10:25:53 +01:00
node . pos )
} else if from_type . is_real_pointer ( ) {
snexpr := node . expr . str ( )
2022-02-03 11:24:29 +01:00
ft := c . table . type_to_str ( from_type )
c . error ( ' c a n n o t c a s t p o i n t e r t y p e ` $ ft ` t o s t r i n g , u s e ` & b y t e ( $ snexpr ) . v s t r i n g ( ) ` o r ` c s t r i n g _ t o _ v s t r i n g ( $ snexpr ) ` i n s t e a d . ' ,
2021-12-01 10:25:53 +01:00
node . pos )
} else if from_type . is_number ( ) {
snexpr := node . expr . str ( )
c . error ( ' c a n n o t c a s t n u m b e r t o s t r i n g , u s e ` $ { snexpr } . s t r ( ) ` i n s t e a d . ' , node . pos )
2021-12-29 20:48:23 +01:00
} else if from_sym . kind == . alias && final_from_sym . name != ' s t r i n g ' {
2022-02-03 11:24:29 +01:00
ft := c . table . type_to_str ( from_type )
c . error ( ' c a n n o t c a s t t y p e ` $ ft ` t o s t r i n g , u s e ` x . s t r ( ) ` i n s t e a d . ' , node . pos )
2021-12-29 20:48:23 +01:00
} else if final_from_sym . kind == . array {
2021-12-01 10:25:53 +01:00
snexpr := node . expr . str ( )
2021-12-29 20:48:23 +01:00
if final_from_sym . name == ' [ ] b y t e ' {
2021-12-01 10:25:53 +01:00
c . error ( ' c a n n o t c a s t [ ] b y t e t o s t r i n g , u s e ` $ { snexpr } . b y t e s t r ( ) ` o r ` $ { snexpr } . s t r ( ) ` i n s t e a d . ' ,
node . pos )
} else {
first_elem_idx := ' [ 0 ] '
c . error ( ' c a n n o t c a s t a r r a y t o s t r i n g , u s e ` $ snexpr $ { first_elem_idx } . s t r ( ) ` i n s t e a d . ' ,
node . pos )
}
2021-12-29 20:48:23 +01:00
} else if final_from_sym . kind == . enum_ {
2021-12-01 10:25:53 +01:00
snexpr := node . expr . str ( )
c . error ( ' c a n n o t c a s t e n u m t o s t r i n g , u s e $ { snexpr } . s t r ( ) i n s t e a d . ' , node . pos )
2021-12-29 20:48:23 +01:00
} else if final_from_sym . kind == . map {
2021-12-01 10:25:53 +01:00
c . error ( ' c a n n o t c a s t m a p t o s t r i n g . ' , node . pos )
2021-12-29 20:48:23 +01:00
} else if final_from_sym . kind == . sum_type {
2021-12-01 10:25:53 +01:00
snexpr := node . expr . str ( )
2022-02-03 11:24:29 +01:00
ft := c . table . type_to_str ( from_type )
c . error ( ' c a n n o t c a s t s u m t y p e ` $ ft ` t o s t r i n g , u s e ` $ { snexpr } . s t r ( ) ` i n s t e a d . ' ,
2021-12-01 10:25:53 +01:00
node . pos )
} else if to_type != ast . string_type && from_type == ast . string_type
2021-12-29 20:48:23 +01:00
&& ( ! ( to_sym . kind == . alias && final_to_sym . name == ' s t r i n g ' ) ) {
mut error_msg := ' c a n n o t c a s t a s t r i n g t o a t y p e ` $ final_to_sym . name ` , t h a t i s n o t a n a l i a s o f s t r i n g '
2021-12-01 10:25:53 +01:00
if mut node . expr is ast . StringLiteral {
if node . expr . val . len == 1 {
error_msg += " , f o r d e n o t i n g c h a r a c t e r s u s e ` $ node . expr . val ` i n s t e a d o f ' $ node . expr . val ' "
}
}
c . error ( error_msg , node . pos )
}
}
2020-11-02 01:17:35 +01:00
if node . has_arg {
c . expr ( node . arg )
}
2021-08-02 06:12:29 +02:00
// checks on int literal to enum cast if the value represents a value on the enum
2021-12-29 20:48:23 +01:00
if to_sym . kind == . enum_ {
2022-02-28 16:06:37 +01:00
if mut node . expr is ast . IntegerLiteral {
2021-12-01 08:50:53 +01:00
enum_typ_name := c . table . get_type_name ( to_type )
2022-02-28 16:06:37 +01:00
node_val := node . expr . val . int ( )
2021-08-02 06:12:29 +02:00
2021-12-29 20:48:23 +01:00
if enum_decl := c . table . enum_decls [ to_sym . name ] {
2021-08-02 06:12:29 +02:00
mut in_range := false
2021-12-15 15:58:25 +01:00
if enum_decl . is_flag {
// if a flag enum has 4 variants, the maximum possible value would have all 4 flags set (0b1111)
max_val := ( 1 << enum_decl . fields . len ) - 1
in_range = node_val >= 0 && node_val <= max_val
} else {
mut enum_val := 0
2021-08-02 06:12:29 +02:00
2021-12-15 15:58:25 +01:00
for enum_field in enum_decl . fields {
// check if the field of the enum value is an integer literal
if enum_field . expr is ast . IntegerLiteral {
enum_val = enum_field . expr . val . int ( )
}
if node_val == enum_val {
in_range = true
break
}
enum_val += 1
}
2021-08-02 06:12:29 +02:00
}
if ! in_range {
2021-12-15 15:58:25 +01:00
c . warn ( ' $ node_val d o e s n o t r e p r e s e n t a v a l u e o f e n u m $ enum_typ_name ' ,
2021-08-02 06:12:29 +02:00
node . pos )
}
}
}
}
2021-12-19 17:25:18 +01:00
node . typname = c . table . sym ( to_type ) . name
2021-12-01 08:50:53 +01:00
return to_type
2020-11-02 01:17:35 +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 {
2021-05-11 08:30:01 +02:00
node . val = c . table . cur_fn . name . all_after_last ( ' . ' )
2020-11-05 09:12:32 +01:00
}
2021-02-05 15:30:58 +01:00
. method_name {
2021-05-11 08:30:01 +02:00
fname := c . table . cur_fn . name . all_after_last ( ' . ' )
if c . table . cur_fn . is_method {
node . val = c . table . type_to_str ( c . table . cur_fn . receiver . typ ) . all_after_last ( ' . ' ) +
' . ' + fname
2021-02-05 15:30:58 +01:00
} else {
node . val = fname
}
}
2020-11-05 09:12:32 +01:00
. mod_name {
2021-05-11 08:30:01 +02:00
node . val = c . table . cur_fn . mod
2020-11-05 09:12:32 +01:00
}
. struct_name {
2021-05-11 08:30:01 +02:00
if c . table . cur_fn . is_method {
node . val = c . table . type_to_str ( c . table . cur_fn . receiver . typ ) . all_after_last ( ' . ' )
2020-11-05 09:12:32 +01:00
} 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 {
2021-07-27 11:35:54 +02:00
node . val = version . vhash ( )
2020-11-05 09:12:32 +01:00
}
. 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 ( ' @ V M O D _ F I L E c a n b e u s e d o n l y i n p r o j e c t s , t h a t h a v e v . m o d f i l e ' ,
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 ( ' u n k n o w n @ i d e n t i f i e r : $ { node . name } . A v a i l a b l e i d e n t i f i e r s : $ 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-08-20 09:25:16 +02:00
pub fn ( mut c Checker ) ident ( mut node ast . Ident ) ast . Type {
2020-04-04 05:14:40 +02:00
// TODO: move this
if c . const_deps . len > 0 {
2021-08-20 09:25:16 +02:00
mut name := node . name
if ! name . contains ( ' . ' ) && node . mod != ' b u i l t i n ' {
name = ' $ { node . mod } . $ node . name '
2020-04-04 05:14:40 +02:00
}
2020-04-04 08:05:26 +02:00
if name == c . const_decl {
2021-08-20 09:25:16 +02:00
c . error ( ' c y c l e i n c o n s t a n t ` $ c . const_decl ` ' , node . 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
}
2021-08-20 09:25:16 +02:00
if node . kind == . blank_ident {
if node . tok_kind ! in [ . assign , . decl_assign ] {
c . error ( ' u n d e f i n e d i d e n t : ` _ ` ( m a y o n l y b e u s e d i n a s s i g n m e n t s ) ' , node . pos )
2020-09-18 23:47:50 +02:00
}
2021-04-02 00:57:09 +02:00
return ast . void_type
2020-04-04 05:14:40 +02:00
}
// second use
2021-08-20 09:25:16 +02:00
if node . kind in [ . constant , . global , . variable ] {
info := node . 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
2021-08-20 09:25:16 +02:00
} else if node . kind == . function {
info := node . info as ast . IdentFn
2020-04-04 05:14:40 +02:00
return info . typ
2021-08-20 09:25:16 +02:00
} else if node . kind == . unresolved {
2020-04-07 15:15:45 +02:00
// first use
2021-08-20 09:25:16 +02:00
if node . tok_kind == . assign && node . is_mut {
c . error ( ' ` m u t ` n o t a l l o w e d w i t h ` = ` ( u s e ` : = ` t o d e c l a r e a v a r i a b l e ) ' , node . pos )
2020-08-28 19:07:32 +02:00
}
2021-08-20 09:25:16 +02:00
if obj := node . scope . find ( node . name ) {
2020-11-25 12:09:40 +01:00
match mut obj {
2020-10-03 07:03:44 +02:00
ast . GlobalField {
2021-08-20 09:25:16 +02:00
node . kind = . global
node . info = ast . IdentVar {
2020-06-18 20:38:59 +02:00
typ : obj . typ
2020-06-09 09:08:11 +02:00
}
2021-08-20 09:25:16 +02:00
node . 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
2021-08-20 09:25:16 +02:00
if node . pos . pos < obj . pos . pos {
c . error ( ' u n d e f i n e d v a r i a b l e ` $ node . name ` ( u s e d b e f o r e d e c l a r a t i o n ) ' ,
node . 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 {
2021-08-20 09:25:16 +02:00
c . error ( ' u n r e s o l v e d v a r i a b l e : ` $ node . name ` ' , node . 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
2022-01-25 13:36:33 +01:00
sym := c . table . sym ( obj . expr . expr_type )
if sym . kind == . multi_return {
mr_info := sym . info as ast . MultiReturn
if mr_info . types . len == obj . expr . vars . len {
for vi , var in obj . expr . vars {
if var . name == node . name {
typ = mr_info . types [ vi ]
}
}
}
} else {
typ = obj . expr . expr_type . clear_flag ( . optional )
}
2020-11-21 22:40:05 +01:00
} 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 )
2021-08-20 09:25:16 +02:00
node . kind = . variable
node . info = ast . IdentVar {
2020-05-10 16:13:30 +02:00
typ : typ
is_optional : is_optional
}
2021-04-02 00:57:09 +02:00
// if typ == ast.t_type {
2021-12-19 17:25:18 +01:00
// sym := c.table.sym(c.cur_generic_type)
2021-08-20 09:25:16 +02:00
// println('IDENT T unresolved $node.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
}
2021-08-20 09:25:16 +02:00
node . 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
}
2021-08-20 09:25:16 +02:00
mut name := node . 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
2021-08-20 09:25:16 +02:00
else if ! name . contains ( ' . ' ) && node . mod != ' b u i l t i n ' {
name = ' $ { node . mod } . $ node . name '
2020-05-10 16:13:30 +02:00
}
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 ) {
2021-08-20 09:25:16 +02:00
c . error ( ' c o n s t a n t ` $ obj . name ` i s p r i v a t e ' , node . pos )
2021-02-03 09:17:13 +01:00
}
2020-06-18 20:38:59 +02:00
mut typ := obj . typ
2020-04-04 05:14:40 +02:00
if typ == 0 {
2021-12-16 11:03:49 +01:00
old_c_mod := c . mod
c . mod = obj . mod
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-12-16 11:03:49 +01:00
c . mod = old_c_mod
2022-01-20 07:40:16 +01:00
if mut obj . expr is ast . CallExpr {
2021-01-30 11:46:36 +01:00
if obj . expr . or_block . kind != . absent {
typ = typ . clear_flag ( . optional )
}
}
2020-04-04 05:14:40 +02:00
}
2021-08-20 09:25:16 +02:00
node . name = name
node . kind = . constant
2021-12-11 08:47:57 +01:00
node . info = ast . IdentVar {
typ : typ
2021-01-01 12:28:23 +01:00
}
2021-12-11 08:47:57 +01:00
obj . typ = typ
node . obj = obj
return typ
2021-01-01 12:28:23 +01:00
}
2021-12-11 08:47:57 +01:00
else { }
2021-01-01 12:28:23 +01:00
}
2021-12-11 08:47:57 +01:00
}
// Non-anon-function object (not a call), e.g. `onclick(my_click)`
if func := c . table . find_fn ( name ) {
fn_type := ast . new_type ( c . table . find_or_register_fn_type ( node . mod , func , false ,
true ) )
node . name = name
node . kind = . function
node . info = ast . IdentFn {
typ : fn_type
2021-01-01 12:28:23 +01:00
}
2021-12-11 08:47:57 +01:00
return fn_type
2020-10-15 22:12:59 +02:00
}
2020-04-25 21:51:44 +02:00
}
2021-12-11 08:47:57 +01:00
if node . language == . c {
if node . name == ' C . N U L L ' {
return ast . voidptr_type
2020-04-24 16:04:39 +02:00
}
2021-12-11 08:47:57 +01:00
return ast . int_type
2020-04-24 16:04:39 +02:00
}
2021-12-11 08:47:57 +01:00
if c . inside_sql {
if field := c . table . find_field ( c . cur_orm_ts , node . name ) {
return field . typ
2020-04-25 21:51:44 +02:00
}
}
2021-12-11 08:47:57 +01:00
if node . kind == . unresolved && node . mod != ' b u i l t i n ' {
// search in the `builtin` idents, for example
// main.compare_f32 may actually be builtin.compare_f32
saved_mod := node . mod
node . mod = ' b u i l t i n '
builtin_type := c . ident ( mut node )
if builtin_type != ast . void_type {
return builtin_type
}
node . mod = saved_mod
2020-04-25 21:51:44 +02:00
}
2021-12-11 08:47:57 +01:00
if node . tok_kind == . assign {
c . error ( ' u n d e f i n e d i d e n t : ` $ node . name ` ( u s e ` : = ` t o d e c l a r e a v a r i a b l e ) ' , node . pos )
} else if node . name == ' e r r c o d e ' {
c . error ( ' u n d e f i n e d i d e n t : ` e r r c o d e ` ; d i d y o u m e a n ` e r r . c o d e ` ? ' , node . pos )
} else {
if c . inside_ct_attr {
c . note ( ' ` [ i f $ node . name ] ` i s d e p r e c a t e d . U s e ` [ i f $ node . name ? ] ` i n s t e a d ' ,
node . pos )
2020-07-10 14:45:55 +02:00
} else {
2021-12-11 08:47:57 +01:00
c . error ( ' u n d e f i n e d i d e n t : ` $ node . name ` ' , node . pos )
2020-07-10 14:45:55 +02:00
}
2021-12-11 08:47:57 +01:00
}
if c . table . known_type ( node . name ) {
// e.g. `User` in `json.decode(User, '...')`
return ast . void_type
}
return ast . void_type
}
pub fn ( mut c Checker ) concat_expr ( mut node ast . ConcatExpr ) ast . Type {
mut mr_types := [ ] ast . Type { }
for expr in node . vals {
mr_types << c . expr ( expr )
}
if node . vals . len == 1 {
typ := mr_types [ 0 ]
node . return_type = typ
return typ
2020-04-25 21:51:44 +02:00
} else {
2021-12-11 08:47:57 +01:00
typ := c . table . find_or_register_multi_return ( mr_types )
ast . new_type ( typ )
node . return_type = typ
return typ
2020-04-25 21:51:44 +02:00
}
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
2022-03-03 15:36:40 +01:00
fn ( mut c Checker ) smartcast ( expr ast . Expr , cur_type ast . Type , to_type_ ast . Type , mut scope ast . Scope ) {
2021-12-19 17:25:18 +01:00
sym := c . table . sym ( cur_type )
2021-10-06 19:49:39 +02:00
to_type := if sym . kind == . interface_ { to_type_ . ref ( ) } else { to_type_ }
2021-04-09 10:00:05 +02:00
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-12-19 17:25:18 +01:00
expr_sym := c . table . sym ( 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
}
2021-06-18 13:49:15 +02:00
if field := scope . find_struct_field ( expr . expr . str ( ) , 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-06-18 13:49:15 +02:00
scope . register_struct_field ( expr . expr . str ( ) , ast . ScopeStructField {
2021-01-07 21:35:32 +01:00
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
} )
2022-03-03 15:36:40 +01:00
} else {
c . smartcast_mut_pos = expr . pos
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
} )
2022-03-03 15:36:40 +01:00
} else if is_mut && ! expr . is_mut {
c . smartcast_mut_pos = expr . pos
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 ( ) {
2021-12-19 17:25:18 +01:00
tsym := c . table . sym ( branch . stmt . typ )
2021-07-23 22:24:27 +02:00
c . error ( ' i n v a l i d t y p e ` $ tsym . name ` f o r t i m e o u t - e x p e c t e d i n t e g e r n u m b e r o f n a n o s e c o n d s a k a ` t i m e . D u r a t i o n ` ' ,
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 {
2022-01-26 11:36:28 +01:00
c . error ( ' c h a n n e l i n ` s e l e c t ` k e y m u s t b e p r e d e f i n e d ' , branch . stmt . expr . left . pos ( ) )
2020-09-20 03:50:09 +02:00
}
} else {
2022-01-26 11:36:28 +01:00
c . error ( ' i n v a l i d e x p r e s s i o n f o r ` s e l e c t ` k e y ' , branch . stmt . expr . pos ( ) )
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 {
2022-01-26 11:36:28 +01:00
c . error ( ' c h a n n e l i n ` s e l e c t ` k e y m u s t b e p r e d e f i n e d ' , expr . right . pos ( ) )
2020-09-20 03:50:09 +02:00
}
if expr . or_block . kind != . absent {
2020-12-27 14:20:30 +01:00
err_prefix := if expr . or_block . kind == . block {
' o r b l o c k '
} else {
' e r r o r p r o p a g a t i o n '
}
2020-09-20 03:50:09 +02:00
c . error ( ' $ err_prefix n o t a l l o w e d i n ` s e l e c t ` k e y ' , expr . or_block . pos )
}
}
else {
2022-01-26 11:36:28 +01:00
c . error ( ' ` < - ` r e c e i v e e x p r e s s i o n e x p e c t e d ' , branch . stmt . right [ 0 ] . pos ( ) )
2020-09-20 03:50:09 +02:00
}
}
}
else {
if ! branch . is_else {
2021-03-11 13:50:02 +01:00
c . error ( ' r e c e i v e o r s e n d s t a t e m e n t e x p e c t e d a s ` s e l e c t ` k e y ' , branch . stmt . pos )
2020-09-20 03:50:09 +02:00
}
}
}
2021-11-27 07:09:36 +01:00
c . stmts ( branch . stmts )
2020-09-19 02:14:35 +02:00
}
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 ( ' n e s t e d ` l o c k ` / ` r l o c k ` n o t a l l o w e d ' , node . pos )
}
2020-09-27 16:08:11 +02:00
for i in 0 .. node . lockeds . len {
2021-05-15 03:34:27 +02:00
e_typ := c . expr ( node . lockeds [ i ] )
id_name := node . lockeds [ i ] . str ( )
if ! e_typ . has_flag ( . shared_f ) {
obj_type := if node . lockeds [ i ] is ast . Ident { ' v a r i a b l e ' } else { ' s t r u c t e l e m e n t ' }
c . error ( ' ` $ id_name ` m u s t b e d e c l a r e d a s ` s h a r e d ` $ obj_type t o b e l o c k e d ' ,
2022-01-26 11:36:28 +01:00
node . lockeds [ i ] . pos ( ) )
2021-05-15 03:34:27 +02:00
}
if id_name in c . locked_names {
2022-01-26 11:36:28 +01:00
c . error ( ' ` $ id_name ` i s a l r e a d y l o c k e d ' , node . lockeds [ i ] . pos ( ) )
2021-05-15 03:34:27 +02:00
} else if id_name in c . rlocked_names {
2022-01-26 11:36:28 +01:00
c . error ( ' ` $ id_name ` i s a l r e a d y r e a d - l o c k e d ' , node . lockeds [ i ] . pos ( ) )
2020-07-13 12:19:28 +02:00
}
2021-02-03 15:16:52 +01:00
if node . is_rlock [ i ] {
2021-05-15 03:34:27 +02:00
c . rlocked_names << id_name
2020-07-13 12:19:28 +02:00
} else {
2021-05-15 03:34:27 +02:00
c . locked_names << id_name
2020-07-13 12:19:28 +02:00
}
2020-07-04 12:44:25 +02:00
}
2021-11-27 07:09:36 +01: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
}
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 i s a g l o b a l v a r i a b l e ' ) }
. function { return error ( ' $ ident . name i s a f u n c t i o n ' ) }
}
}
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 ` i s m u t a n d m a y h a v e c h a n g e d s i n c e i t s d e f i n i t i o n ' )
}
expr = obj . expr
} else if obj is ast . ConstField {
expr = obj . expr
} else {
return error ( ' ` $ name ` i s a g l o b a l v a r i a b l e a n d i s u n k n o w n a t c o m p i l e t i m e ' )
}
2022-02-28 16:06:37 +01:00
if mut expr is ast . Ident {
return c . find_definition ( expr )
2020-12-11 04:46:06 +01:00
}
if ! expr . is_lit ( ) {
return error ( ' d e f i n i t i o n o f ` $ name ` i s u n k n o w n a t c o m p i l e t i m e ' )
}
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 {
2021-09-16 06:08:14 +02:00
if s . expr in [ ast . IfExpr , ast . MatchExpr ] {
2020-08-10 23:59:08 +02:00
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 ) )
2021-12-19 17:25:18 +01:00
typ_sym := c . table . sym ( 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 ( ' p o i n t e r a r i t h m e t i c i s o n l y a l l o w e d i n ` u n s a f e ` b l o c k s ' , node . pos )
}
if ! ( typ_sym . is_number ( ) || ( c . inside_unsafe && is_non_void_pointer ) ) {
2020-12-06 04:55:08 +01:00
c . error ( ' i n v a l i d o p e r a t i o n : $ node . op . str ( ) ( n o n - n u m e r i c t y p e ` $ 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-06-21 06:10:10 +02:00
pub fn ( mut c Checker ) mark_as_referenced ( mut node ast . Expr , as_interface bool ) {
2021-04-25 20:40:38 +02:00
match mut node {
ast . Ident {
if mut node . obj is ast . Var {
mut obj := unsafe { & node . obj }
if c . fn_scope != voidptr ( 0 ) {
2021-05-07 14:58:48 +02:00
obj = c . fn_scope . find_var ( node . obj . name ) or { obj }
2021-04-25 20:40:38 +02:00
}
2021-12-19 17:25:18 +01:00
type_sym := c . table . sym ( obj . typ . set_nr_muls ( 0 ) )
2022-02-05 23:16:02 +01:00
if obj . is_stack_obj && ! type_sym . is_heap ( ) && ! c . pref . translated
&& ! c . file . is_translated {
2021-05-07 14:58:48 +02:00
suggestion := if type_sym . kind == . struct_ {
' d e c l a r i n g ` $ type_sym . name ` a s ` [ h e a p ] ` '
} else {
' w r a p p i n g t h e ` $ type_sym . name ` o b j e c t i n a ` s t r u c t ` d e c l a r e d a s ` [ h e a p ] ` '
}
2022-02-05 23:16:02 +01:00
mischief := if as_interface { ' u s e d a s i n t e r f a c e o b j e c t ' } else { ' r e f e r e n c e d ' }
c . error ( ' ` $ node . name ` c a n n o t b e $ mischief o u t s i d e ` u n s a f e ` b l o c k s a s i t m i g h t b e s t o r e d o n s t a c k . C o n s i d e r $ { suggestion } . ' ,
node . pos )
2021-04-25 20:40:38 +02:00
} else if type_sym . kind == . array_fixed {
c . error ( ' c a n n o t r e f e r e n c e f i x e d a r r a y ` $ node . name ` o u t s i d e ` u n s a f e ` b l o c k s a s i t i s s u p p o s e d t o b e s t o r e d o n s t a c k ' ,
node . pos )
} else {
2021-06-20 17:40:24 +02:00
match type_sym . kind {
. struct_ {
info := type_sym . info as ast . Struct
if ! info . is_heap {
node . obj . is_auto_heap = true
}
}
else {
2021-05-07 14:58:48 +02:00
node . obj . is_auto_heap = true
}
}
2021-04-25 20:40:38 +02:00
}
}
}
ast . SelectorExpr {
2021-04-28 06:47:00 +02:00
if ! node . expr_type . is_ptr ( ) {
2021-06-21 06:10:10 +02:00
c . mark_as_referenced ( mut & node . expr , as_interface )
2021-04-28 06:47:00 +02:00
}
2021-04-25 20:40:38 +02:00
}
ast . IndexExpr {
2021-06-21 06:10:10 +02:00
c . mark_as_referenced ( mut & node . left , as_interface )
2021-04-25 20:40:38 +02:00
}
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
2021-08-09 01:11:53 +02:00
if node . op == . amp {
if mut node . right is ast . PrefixExpr {
if node . right . op == . amp {
c . error ( ' u n e x p e c t e d ` & ` , e x p e c t i n g e x p r e s s i o n ' , node . right . pos )
}
}
}
2020-12-20 07:55:23 +01:00
// 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
}
2022-03-03 09:48:31 +01:00
if expr in [ ast . BoolLiteral , ast . CallExpr , ast . CharLiteral , ast . FloatLiteral ,
ast . IntegerLiteral , ast . InfixExpr , ast . StringLiteral , ast . StringInterLiteral ] {
c . error ( ' c a n n o t t a k e t h e a d d r e s s o f $ expr ' , node . pos )
2020-12-20 07:55:23 +01:00
}
if mut node . right is ast . IndexExpr {
2021-12-19 17:25:18 +01:00
typ_sym := c . table . sym ( node . right . left_type )
2020-12-20 07:55:23 +01:00
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 ( ' c a n n o t t a k e t h e a d d r e s s o f m a p v a l u e s ' , node . right . pos )
}
if ! c . inside_unsafe {
if typ_sym . kind == . array && is_mut {
c . error ( ' c a n n o t t a k e t h e a d d r e s s o f m u t a b l e a r r a y e l e m e n t s o u t s i d e u n s a f e b l o c k s ' ,
node . right . pos )
}
}
}
2021-04-25 20:40:38 +02:00
if ! c . inside_fn_arg && ! c . inside_unsafe {
2021-06-21 06:10:10 +02:00
c . mark_as_referenced ( mut & node . right , false )
2021-04-25 20:40:38 +02:00
}
2021-10-06 19:49:39 +02:00
return right_type . ref ( )
2020-12-20 07:55:23 +01:00
} 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 {
2021-06-21 06:10:10 +02:00
c . mark_as_referenced ( mut & node . right , false )
2021-04-25 20:40:38 +02:00
}
if node . right . is_auto_deref_var ( ) {
return right_type
} else {
2021-10-06 19:49:39 +02:00
return right_type . ref ( )
2021-04-25 20:40:38 +02:00
}
2020-12-20 07:55:23 +01:00
}
if node . op == . mul {
if right_type . is_ptr ( ) {
return right_type . deref ( )
}
2022-02-05 23:16:02 +01:00
if ! right_type . is_pointer ( ) && ! c . pref . translated && ! c . file . is_translated {
2020-12-20 07:55:23 +01:00
s := c . table . type_to_str ( right_type )
c . error ( ' i n v a l i d i n d i r e c t o f ` $ s ` ' , node . pos )
}
}
2022-02-05 23:16:02 +01:00
if node . op == . bit_not && ! right_type . is_int ( ) && ! c . pref . translated && ! c . file . is_translated {
2020-12-20 07:55:23 +01:00
c . error ( ' o p e r a t o r ~ o n l y d e f i n e d o n i n t t y p e s ' , node . pos )
}
2022-02-05 23:16:02 +01:00
if node . op == . not && right_type != ast . bool_type_idx && ! c . pref . translated
&& ! c . file . is_translated {
2020-12-20 07:55:23 +01:00
c . error ( ' ! o p e r a t o r c a n o n l y b e u s e d w i t h b o o l t y p e s ' , 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
2021-12-19 17:25:18 +01:00
right_sym := c . table . final_sym ( c . unwrap_generic ( right_type ) )
2021-04-23 14:18:56 +02:00
if node . op == . minus && ! right_sym . is_number ( ) {
c . error ( ' - o p e r a t o r c a n o n l y b e u s e d w i t h n u m e r i c t y p e s ' , 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 {
2021-11-27 07:09:36 +01:00
c . stmts_ending_with_expression ( 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 ( ' < - o p e r a t o r c a n o n l y b e u s e d w i t h ` c h a n ` t y p e s ' , node . pos )
2020-12-20 07:55:23 +01:00
}
return right_type
}
2022-01-26 11:36:28 +01:00
fn ( mut c Checker ) check_index ( typ_sym & ast . TypeSymbol , index ast . Expr , index_type ast . Type , pos token . Pos , range_index bool , is_gated bool ) {
2021-12-19 17:25:18 +01:00
index_type_sym := c . table . sym ( 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_) {
2021-07-03 19:14:09 +02:00
if typ_sym . kind in [ . array , . array_fixed , . string ] {
2021-02-20 19:18:47 +01:00
if ! ( index_type . is_int ( ) || index_type_sym . kind == . enum_ ) {
2021-07-03 19:14:09 +02:00
type_str := if typ_sym . kind == . string {
2020-12-27 14:20:30 +01:00
' n o n - i n t e g e r s t r i n g i n d e x ` $ index_type_sym . name ` '
} else {
' n o n - i n t e g e r i n d e x ` $ index_type_sym . name ` ( a r r a y t y p e ` $ typ_sym . name ` ) '
}
2020-11-07 14:55:05 +01:00
c . error ( ' $ type_str ' , pos )
}
2021-12-22 14:34:02 +01:00
if index is ast . IntegerLiteral && ! is_gated {
2021-02-20 19:18:47 +01:00
if index . val [ 0 ] == ` - ` {
c . error ( ' n e g a t i v e i n d e x ` $ 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 ( ' i n d e x o u t o f r a n g e ( i n d e x : $ i , l e n : $ 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 ) {
2021-07-03 19:14:09 +02:00
type_str := if typ_sym . kind == . string {
2020-12-27 14:20:30 +01:00
' ( t y p e ` $ typ_sym . name ` ) '
} else {
' ( a r r a y t y p e ` $ typ_sym . name ` ) '
}
2020-11-07 14:55:05 +01:00
c . error ( ' c a n n o t u s e o p t i o n a l a s i n d e x $ 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 )
2021-12-19 17:25:18 +01:00
mut typ_sym := c . table . final_sym ( typ )
2020-04-03 10:41:01 +02:00
node . left_type = typ
2021-06-09 10:46:17 +02:00
for {
match typ_sym . kind {
. map {
node . is_map = true
break
}
. array {
node . is_array = true
2022-01-17 11:03:10 +01:00
if node . or_expr . kind != . absent && node . index is ast . RangeExpr {
c . error ( ' c u s t o m e r r o r h a n d l i n g o n r a n g e e x p r e s s i o n s f o r a r r a y s i s n o t s u p p o r t e d y e t . ' ,
node . or_expr . pos )
}
2021-06-09 10:46:17 +02:00
break
}
. array_fixed {
node . is_farray = true
break
}
. any {
2021-06-09 11:05:46 +02:00
gname := typ_sym . name
2021-06-09 10:46:17 +02:00
typ = c . unwrap_generic ( typ )
node . left_type = typ
2021-12-19 17:25:18 +01:00
typ_sym = c . table . final_sym ( typ )
2021-06-09 11:05:46 +02:00
if typ . is_ptr ( ) {
continue
} else {
c . error ( ' g e n e r i c t y p e $ gname d o e s n o t s u p p o r t i n d e x i n g , p a s s a n a r r a y , o r a r e f e r e n c e i n s t e a d , e . g . [ ] $ gname o r & $ gname ' ,
node . pos )
}
2021-06-09 10:46:17 +02:00
}
else {
break
}
2021-06-08 16:23:44 +02:00
}
2021-02-16 14:08:01 +01:00
}
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 ( ' t y p e ` $ typ_sym . name ` d o e s n o t s u p p o r t i n d e x i n g ' , 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 ( ' c a n n o t a s s i g n t o s [ i ] s i n c e V s t r i n g s a r e i m m u t a b l e \n ' +
' ( n o t e , t h a t v a r i a b l e s m a y b e m u t a b l e b u t s t r i n g v a l u e s a r e a l w a y s i m m u t a b l e , l i k e i n G o a n d J a v a ) ' ,
node . pos )
2020-08-11 17:41:54 +02:00
}
2021-07-29 09:57:31 +02:00
if ! c . inside_unsafe && ( ( typ . is_ptr ( ) && ! typ . has_flag ( . shared_f )
&& ! 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 {
2022-02-28 16:06:37 +01:00
if mut node . left . obj is ast . Var {
2020-07-22 19:28:53 +02:00
// `mut param []T` function parameter
2022-02-28 16:06:37 +01:00
is_ok = node . left . obj . is_mut && node . left . obj . is_arg && ! typ . deref ( ) . is_ptr ( )
2020-07-22 19:28:53 +02:00
}
}
2022-02-05 23:16:02 +01:00
if ! is_ok && ! c . pref . translated && ! c . file . is_translated {
2020-07-22 19:28:53 +02:00
c . warn ( ' p o i n t e r i n d e x i n g i s o n l y a l l o w e d i n ` u n s a f e ` b l o c k s ' , 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-12-22 14:34:02 +01:00
c . check_index ( typ_sym , node . index . low , index_type , node . pos , true , node . is_gated )
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-12-22 14:34:02 +01:00
c . check_index ( typ_sym , node . index . high , index_type , node . pos , true , node . is_gated )
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 ( ' i n v a l i d k e y : $ err ' , node . pos )
}
2021-12-19 17:25:18 +01:00
value_sym := c . table . sym ( info . value_type )
2021-09-10 15:07:39 +02:00
if ! node . is_setter && value_sym . kind == . sum_type && node . or_expr . kind == . absent
2021-09-15 14:42:28 +02:00
&& ! c . inside_unsafe && ! c . inside_if_guard {
2021-09-10 15:07:39 +02:00
c . warn ( ' ` o r { } ` b l o c k r e q u i r e d w h e n i n d e x i n g a m a p w i t h s u m t y p e v a l u e ' ,
node . pos )
}
2020-12-27 14:18:46 +01:00
} else {
2021-04-29 07:33:41 +02:00
index_type := c . expr ( node . index )
2021-12-22 14:34:02 +01:00
// for [1] case #[1] is not allowed!
if node . is_gated == true {
c . error ( ' ` # [ ] ` a l l o w e d o n l y f o r r a n g e s ' , node . pos )
}
c . check_index ( typ_sym , node . index , index_type , node . pos , false , 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-11-27 07:09:36 +01:00
c . stmts_ending_with_expression ( node . or_expr . stmts )
2021-05-28 10:40:59 +02:00
c . check_expr_opt_call ( node , typ )
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 {
2021-05-20 02:14:27 +02:00
mut typ_idx := if node . enum_name == ' ' {
2020-04-25 09:08:53 +02:00
c . expected_type . idx ( )
2021-06-06 15:27:46 +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
if typ_idx == 0 {
2021-05-20 02:14:27 +02:00
// Handle `builtin` enums like `ChanState`, so that `x := ChanState.closed` works.
// In the checker the name for such enums was set to `main.ChanState` instead of
// just `ChanState`.
2021-06-06 15:27:46 +02:00
if node . enum_name . starts_with ( ' $ { c . mod } . ' ) {
typ_idx = c . table . find_type_idx ( node . enum_name [ ' $ { c . mod } . ' . len .. ] )
2021-05-20 02:14:27 +02:00
if typ_idx == 0 {
c . error ( ' u n k n o w n e n u m ` $ node . enum_name ` ( t y p e _ i d x = 0 ) ' , node . pos )
return ast . void_type
}
2022-01-28 09:27:37 +01:00
}
if typ_idx == 0 {
// the actual type is still unknown, produce an error, instead of panic:
2022-01-28 10:02:51 +01:00
c . error ( ' u n k n o w n e n u m ` $ node . enum_name ` ( t y p e _ i d x = 0 ) ' , node . pos )
return ast . void_type
2021-05-20 02:14:27 +02:00
}
2020-02-29 18:34:25 +01:00
}
2021-04-02 00:57:09 +02:00
mut typ := ast . new_type ( typ_idx )
2022-02-05 23:16:02 +01:00
if c . pref . translated || c . file . is_translated {
2020-08-11 16:26:49 +02:00
// 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 ( ' n o t a n e n u m ' , node . pos )
2021-04-02 00:57:09 +02:00
return ast . void_type
2020-04-26 19:59:03 +02:00
}
2021-12-19 17:25:18 +01:00
mut typ_sym := c . table . sym ( typ )
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
2021-12-19 17:25:18 +01:00
typ_sym = c . table . sym ( typ )
2020-05-25 20:48:43 +02:00
}
2021-12-30 12:42:06 +01:00
fsym := c . table . final_sym ( typ )
2022-02-05 23:16:02 +01:00
if fsym . kind != . enum_ && ! c . pref . translated && ! c . file . is_translated {
2020-08-11 16:26:49 +02:00
// 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 ( ' e x p e c t e d t y p e i s n o t a n e n u m ( ` $ 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-12-30 12:42:06 +01:00
if fsym . info ! is ast . Enum {
2020-05-08 15:00:04 +02:00
c . error ( ' n o t a n e n u m ' , 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 ( ' e n u m ` $ typ_sym . name ` i s p r i v a t e ' , node . pos )
}
2020-03-15 02:51:31 +01:00
info := typ_sym . enum_info ( )
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 ( ' e n u m ` $ typ_sym . name ` d o e s n o t h a v e a v a l u e ` $ 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 {
2021-12-19 17:25:18 +01:00
info := c . table . sym ( node . typ ) . chan_info ( )
2020-08-14 21:18:42 +02:00
node . elem_type = info . elem_type
2020-08-26 06:41:51 +02:00
if node . has_cap {
c . check_array_init_para_type ( ' c a p ' , node . cap_expr , node . pos )
}
2020-08-14 21:18:42 +02:00
return node . typ
} else {
c . error ( ' ` c h a n ` o f u n k n o w n t y p e ' , 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-12-19 17:25:18 +01:00
sym := c . table . final_sym ( node . struct_type )
2021-01-30 12:57:09 +01:00
if sym . kind != . struct_ {
c . error ( ' f i r s t a r g u m e n t o f _ _ o f f s e t o f m u s t b e s t r u c t ' , 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 ( ' s t r u c t ` $ sym . name ` h a s n o f i e l d c a l l e d ` $ 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 ( ' d u p l i c a t e k e y " $ key_i . val " i n m a p l i t e r a l ' , 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 ( ' d u p l i c a t e k e y " $ key_i . val " i n m a p l i t e r a l ' , key_i . pos )
}
2021-01-01 17:23:32 +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
}
2022-01-26 11:36:28 +01:00
pub fn ( mut c Checker ) warn ( s string , pos token . Pos ) {
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
}
2022-01-26 11:36:28 +01:00
pub fn ( mut c Checker ) error ( message string , pos token . Pos ) {
2021-05-02 02:00:47 +02:00
$ if checker_exit_on_first_error ? {
eprintln ( ' \n \n > > c h e c k e r e r r o r : $ message , p o s : $ pos ' )
print_backtrace ( )
exit ( 1 )
}
2022-02-05 23:16:02 +01:00
if ( c . pref . translated || c . file . is_translated ) && message . starts_with ( ' m i s m a t c h e d t y p e s ' ) {
2020-08-11 16:26:49 +02:00
// 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 ( ' ` A r r a y _ ' , ' ` [ ] ' )
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
}
2022-01-26 11:36:28 +01:00
pub fn ( mut c Checker ) note ( message string , pos token . Pos ) {
2021-08-15 12:41:51 +02:00
if c . pref . message_limit >= 0 && c . nr_notices >= c . pref . message_limit {
c . should_abort = true
2021-08-14 21:14:13 +02:00
return
}
2021-12-11 23:58:38 +01:00
if c . is_generated {
return
}
2021-03-22 18:43:06 +01:00
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 ++
}
2022-01-26 11:36:28 +01:00
fn ( mut c Checker ) warn_or_error ( message string , pos token . Pos , 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 ++
2021-08-15 12:41:51 +02:00
if c . pref . message_limit >= 0 && c . nr_warnings >= c . pref . message_limit {
c . should_abort = true
2021-08-14 18:49:21 +02:00
return
}
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 ++
2021-08-15 12:41:51 +02:00
if c . pref . message_limit >= 0 && c . errors . len >= c . pref . message_limit {
c . should_abort = true
return
}
if pos . line_nr ! in c . error_lines {
err := errors . Error {
reporter : errors . Reporter . checker
pos : pos
file_path : c . file . path
message : message
details : details
}
c . file . errors << err
c . errors << err
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-07-23 11:33:55 +02:00
fn ( mut c Checker ) fetch_field_name ( field ast . StructField ) string {
mut name := field . name
for attr in field . attrs {
if attr . kind == . string && attr . name == ' s q l ' && attr . arg != ' ' {
name = attr . arg
break
}
}
2021-12-19 17:25:18 +01:00
sym := c . table . sym ( field . typ )
2021-10-28 21:31:41 +02:00
if sym . kind == . struct_ && sym . name != ' t i m e . T i m e ' {
2021-07-23 11:33:55 +02:00
name = ' $ { name } _ i d '
}
return name
}
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 . t r a c e | $ { fbase : - 10 s } | $ message ' )
}
}
2021-02-19 10:23:13 +01:00
2022-01-26 11:36:28 +01:00
fn ( mut c Checker ) ensure_type_exists ( typ ast . Type , pos token . Pos ) ? {
2021-02-19 10:23:13 +01:00
if typ == 0 {
c . error ( ' u n k n o w n t y p e ' , pos )
2021-06-13 22:53:38 +02:00
return
2021-02-19 10:23:13 +01:00
}
2021-12-19 17:25:18 +01:00
sym := c . table . sym ( typ )
2021-02-19 10:23:13 +01:00
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 ( ' u n k n o w n t y p e ` $ sym . name ` ' ) ,
pos )
2021-06-13 22:53:38 +02:00
return
2021-02-19 10:23:13 +01:00
}
}
. 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 {
' u n k n o w n t y p e ` $ sym . name ` . \n D i d y o u m e a n ` i n t ` ? '
} else {
' u n k n o w n t y p e ` $ sym . name ` . \n D i d y o u m e a n ` f 6 4 ` ? '
}
c . error ( msg , pos )
2021-06-13 22:53:38 +02:00
return
2021-02-19 10:23:13 +01:00
}
}
. 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
}
2021-05-16 03:51:23 +02:00
. array_fixed {
c . ensure_type_exists ( ( sym . info as ast . ArrayFixed ) . 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 { }
}
}