2022-01-04 10:21:08 +01:00
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
2020-04-05 18:03:36 +02:00
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
2021-02-02 15:41:51 +01:00
module c
2019-12-24 18:54:43 +01:00
2020-12-17 06:08:28 +01:00
import os
2020-04-14 19:32:23 +02:00
import strings
2021-12-29 19:15:51 +01:00
import hash . fnv1a
2020-04-14 19:32:23 +02:00
import v . ast
import v . pref
import v . token
import v . util
2021-07-27 11:35:54 +02:00
import v . util . version
2020-04-14 19:32:23 +02:00
import v . depgraph
2021-09-28 09:28:04 +02:00
import sync . pool
2019-12-24 18:54:43 +01:00
2020-03-23 03:36:50 +01:00
const (
2021-02-26 09:22:35 +01:00
// NB: some of the words in c_reserved, are not reserved in C,
// but are in C++, or have special meaning in V, thus need escaping too.
2021-12-11 18:14:26 +01:00
// `small` should not be needed, but see: https://stackoverflow.com/questions/5874215/what-is-rpcndr-h
2021-12-19 08:19:47 +01:00
c_reserved = [ ' a r r a y ' , ' a u t o ' , ' b o o l ' , ' b r e a k ' , ' c a l l o c ' , ' c a s e ' , ' c h a r ' , ' c l a s s ' , ' c o m p l e x ' ,
' c o n s t ' , ' c o n t i n u e ' , ' d e f a u l t ' , ' d e l e t e ' , ' d o ' , ' d o u b l e ' , ' e l s e ' , ' e n u m ' , ' e r r o r ' , ' e x i t ' ,
' e x p o r t ' , ' e x t e r n ' , ' f l o a t ' , ' f o r ' , ' f r e e ' , ' g o t o ' , ' i f ' , ' i n l i n e ' , ' i n t ' , ' l i n k ' , ' l o n g ' ,
' m a l l o c ' , ' n a m e s p a c e ' , ' n e w ' , ' p a n i c ' , ' r e g i s t e r ' , ' r e s t r i c t ' , ' r e t u r n ' , ' s h o r t ' , ' s i g n e d ' ,
' s i z e o f ' , ' s t a t i c ' , ' s t r i n g ' , ' s t r u c t ' , ' s w i t c h ' , ' t y p e d e f ' , ' t y p e n a m e ' , ' u n i o n ' , ' u n i x ' ,
' u n s i g n e d ' , ' v o i d ' , ' v o l a t i l e ' , ' w h i l e ' , ' t e m p l a t e ' , ' s m a l l ' , ' s t d o u t ' , ' s t d i n ' , ' s t d e r r ' ]
2021-05-03 11:58:40 +02:00
c_reserved_map = string_array_to_map ( c_reserved )
2020-06-18 20:21:08 +02:00
// same order as in token.Kind
2021-05-03 11:58:40 +02:00
cmp_str = [ ' e q ' , ' n e ' , ' g t ' , ' l t ' , ' g e ' , ' l e ' ]
2020-06-18 20:21:08 +02:00
// when operands are switched
2021-05-03 11:58:40 +02:00
cmp_rev = [ ' e q ' , ' n e ' , ' l t ' , ' g t ' , ' l e ' , ' g e ' ]
2020-03-23 03:36:50 +01:00
)
2021-05-03 11:58:40 +02:00
fn string_array_to_map ( a [ ] string ) map [ string ] bool {
mut res := map [ string ] bool { }
for x in a {
res [ x ] = true
}
return res
}
2019-12-24 18:54:43 +01:00
struct Gen {
2021-09-28 09:28:04 +02:00
pref & pref . Preferences
field_data_type ast . Type // cache her to avoid map lookups
module_built string
timers_should_print bool
table & ast . Table
2020-06-01 15:43:54 +02:00
mut :
2021-03-22 06:39:07 +01:00
out strings . Builder
cheaders strings . Builder
includes strings . Builder // all C #includes required by V modules
typedefs strings . Builder
2021-12-19 13:31:04 +01:00
enum_typedefs strings . Builder // enum types
2021-03-22 06:39:07 +01:00
definitions strings . Builder // typedefs, defines etc (everything that goes to the top of the file)
2021-12-19 13:31:04 +01:00
type_definitions strings . Builder // typedefs, defines etc (everything that goes to the top of the file)
2022-01-06 10:54:22 +01:00
alias_definitions strings . Builder // alias fixed array of non-builtin
2021-12-19 13:31:04 +01:00
hotcode_definitions strings . Builder // -live declarations & functions
channel_definitions strings . Builder // channel related code
comptime_definitions strings . Builder // custom defines, given by -d/-define flags on the CLI
2021-07-29 09:57:31 +02:00
global_inits map [ string ] strings . Builder // default initializers for globals (goes in _vinit())
2021-09-28 09:28:04 +02:00
global_init strings . Builder // thread local of the above
2021-03-22 06:39:07 +01:00
inits map [ string ] strings . Builder // contents of `void _vinit/2{}`
2021-09-28 09:28:04 +02:00
init strings . Builder
cleanup strings . Builder
2021-03-22 06:39:07 +01:00
cleanups map [ string ] strings . Builder // contents of `void _vcleanup(){}`
gowrappers strings . Builder // all go callsite wrappers
stringliterals strings . Builder // all string literals (they depend on tos3() beeing defined
auto_str_funcs strings . Builder // function bodies of all auto generated _str funcs
2022-01-06 10:54:22 +01:00
dump_funcs strings . Builder // function bodies of all auto generated _str funcs
2021-03-22 06:39:07 +01:00
pcs_declarations strings . Builder // -prof profile counter declarations for each function
embedded_data strings . Builder // data to embed in the executable/binary
shared_types strings . Builder // shared/lock types
shared_functions strings . Builder // shared constructors
options strings . Builder // `Option_xxxx` types
json_forward_decls strings . Builder // json type forward decls
sql_buf strings . Builder // for writing exprs to args via `sqlite3_bind_int()` etc
2021-05-22 17:59:17 +02:00
file & ast . File
2021-12-29 19:15:51 +01:00
unique_file_path_hash u64 // a hash of file.path, used for making auxilary fn generation unique (like `compare_xyz`)
2021-03-22 06:39:07 +01:00
fn_decl & ast . FnDecl // pointer to the FnDecl we are currently inside otherwise 0
last_fn_c_name string
2021-12-18 16:52:33 +01:00
tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn.
2021-12-19 13:31:04 +01:00
tmp_count_af int // a separate tmp var counter for autofree fn calls
2021-12-18 16:52:33 +01:00
tmp_count_declarations int // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations
global_tmp_count int // like tmp_count but global and not resetted in each function
discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
is_void_expr_stmt bool // ExprStmt whos result is discarded
is_arraymap_set bool // map or array set value state
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
is_shared bool // for initialization of hidden mutex in `[rw]shared` literals
is_vlines_enabled bool // is it safe to generate #line directives when -g is passed
is_autofree bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree
is_builtin_mod bool
is_json_fn bool // inside json.encode()
is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)`
is_fn_index_call bool
2021-09-28 09:28:04 +02:00
vlines_path string // set to the proper path for generating #line directives
optionals map [ string ] string // to avoid duplicates
done_optionals shared [ ] string // to avoid duplicates
chan_pop_optionals map [ string ] string // types for `x := <-ch or {...}`
chan_push_optionals map [ string ] string // types for `ch <- x or {...}`
2021-05-12 10:44:47 +02:00
mtxs string // array of mutexes if the `lock` has multiple variables
labeled_loops map [ string ] & ast . Stmt
inner_loop & ast . Stmt
2021-09-28 09:28:04 +02:00
shareds map [ int ] string // types with hidden mutex for which decl has been emitted
inside_ternary int // ?: comma separated statements on a single line
inside_map_postfix bool // inside map++/-- postfix expr
inside_map_infix bool // inside map<</+=/-= infix expr
2021-03-22 06:39:07 +01:00
inside_map_index bool
inside_opt_data bool
inside_if_optional bool
2021-12-02 11:22:48 +01:00
inside_match_optional bool
2021-12-18 16:52:33 +01:00
inside_vweb_tmpl bool
inside_return bool
inside_or_block bool
inside_call bool
2021-12-22 08:34:26 +01:00
inside_for_c_stmt bool
2021-12-18 16:52:33 +01:00
inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls)
inside_const bool
inside_lambda bool
2021-10-18 09:59:52 +02:00
loop_depth int
2021-03-22 06:39:07 +01:00
ternary_names map [ string ] string
ternary_level_names map [ string ] [ ] string
2021-12-18 16:52:33 +01:00
arraymap_set_pos int // map or array set value position
2021-03-22 06:39:07 +01:00
stmt_path_pos [ ] int // positions of each statement start, for inserting C statements before the current statement
skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements)
right_is_opt bool
indent int
empty_line bool
assign_op token . Kind // *=, =, etc (for array_set)
defer_stmts [ ] ast . DeferStmt
defer_ifdef string
defer_profile_code string
2021-12-18 16:52:33 +01:00
defer_vars [ ] string
2021-10-06 21:12:14 +02:00
str_types [ ] StrType // types that need automatic str() generation
generated_str_fns [ ] StrType // types that already have a str() function
threaded_fns shared [ ] string // for generating unique wrapper types and fns for `go xxx()`
2021-12-19 12:38:48 +01:00
waiter_fns shared [ ] string // functions that wait for `go xxx()` to finish
2021-12-18 16:52:33 +01:00
needed_equality_fns [ ] ast . Type
generated_eq_fns [ ] ast . Type
array_sort_fn shared [ ] string
array_contains_types [ ] ast . Type
array_index_types [ ] ast . Type
auto_fn_definitions [ ] string // auto generated functions defination list
2021-09-28 09:28:04 +02:00
sumtype_casting_fns [ ] SumtypeCastingFn
2021-03-22 06:39:07 +01:00
anon_fn_definitions [ ] string // anon generated functions defination list
sumtype_definitions map [ int ] bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated
2021-12-18 16:52:33 +01:00
json_types [ ] ast . Type // to avoid json gen duplicates
2021-03-22 06:39:07 +01:00
pcs [ ] ProfileCounterMeta // -prof profile counter fn_names => fn counter name
hotcode_fn_names [ ] string
embedded_files [ ] ast . EmbeddedFile
2021-05-31 16:14:45 +02:00
sql_i int
sql_stmt_name string
sql_bind_name string
sql_idents [ ] string
sql_idents_types [ ] ast . Type
sql_left_type ast . Type
sql_table_name string
sql_fkey string
sql_parent_id string
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
2021-12-18 16:52:33 +01:00
strs_to_free0 [ ] string // strings.Builder
2020-10-29 01:09:38 +01:00
// strs_to_free []string // strings.Builder
2020-09-05 12:00:35 +02:00
// tmp_arg_vars_to_free []string
2020-09-30 07:27:24 +02:00
// autofree_pregen map[string]string
2020-10-18 00:48:06 +02:00
// autofree_pregen_buf strings.Builder
2020-10-15 16:26:59 +02:00
// autofree_tmp_vars []string // to avoid redefining the same tmp vars in a single function
2020-11-05 08:44:34 +01:00
// nr_vars_to_free int
2020-11-09 14:24:46 +01:00
// doing_autofree_tmp bool
2021-12-18 16:52:33 +01:00
comptime_for_method string // $for method in T.methods {}
comptime_for_field_var string // $for field in T.fields {}; the variable name
comptime_for_field_value ast . StructField // value of the field variable
comptime_for_field_type ast . Type // type of the field variable inferred from `$if field.typ is T {}`
comptime_var_type_map map [ string ] ast . Type
2020-11-11 09:18:15 +01:00
prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type
2020-11-18 20:52:00 +01:00
// used in match multi branch
// TypeOne, TypeTwo {}
// where an aggregate (at least two types) is generated
// sum type deref needs to know which index to deref because unions take care of the correct field
2021-08-16 10:23:19 +02:00
aggregate_type_idx int
branch_parent_pos int // used in BranchStmt (continue/break) for autofree stop position
2021-12-18 16:52:33 +01:00
returned_var_name string // to detect that a var doesn't need to be freed since it's being returned
2021-08-16 10:23:19 +02:00
infix_left_var_name string // a && if expr
2021-12-18 16:52:33 +01:00
called_fn_name string
2021-12-05 10:55:41 +01:00
timers & util . Timers = util . get_timers ( )
2021-08-16 10:23:19 +02:00
force_main_console bool // true when [console] used on fn main()
as_cast_type_names map [ string ] string // table for type name lookup in runtime (for __as_cast)
obf_table map [ string ] string
2021-12-26 19:01:00 +01:00
referenced_fns shared map [ string ] bool // functions that have been referenced
2021-12-18 16:52:33 +01:00
nr_closures int
expected_cast_type ast . Type // for match expr of sumtypes
anon_fn bool
tests_inited bool
has_main bool
2021-02-05 08:05:13 +01:00
// main_fn_decl_node ast.FnDecl
2021-12-18 16:52:33 +01:00
cur_mod ast . Module
cur_concrete_types [ ] ast . Type // do not use table.cur_concrete_types because table is global, so should not be accessed by different threads
cur_fn & ast . FnDecl = 0 // same here
cur_lock ast . LockExpr
2019-12-24 18:54:43 +01:00
}
2021-05-22 17:59:17 +02:00
pub fn gen ( files [ ] & ast . File , table & ast . Table , pref & pref . Preferences ) string {
2020-03-22 12:06:33 +01:00
// println('start cgen2')
2020-12-17 06:08:28 +01:00
mut module_built := ' '
if pref . build_mode == . build_module {
2021-01-20 06:04:59 +01:00
for file in files {
2021-03-23 09:38:56 +01:00
if file . path . contains ( pref . path )
2021-01-23 09:33:22 +01:00
&& file . mod . short_name == pref . path . all_after_last ( os . path_separator ) . trim_right ( os . path_separator ) {
2021-01-20 06:04:59 +01:00
module_built = file . mod . name
2020-12-22 09:21:05 +01:00
break
}
2020-12-17 06:08:28 +01:00
}
}
2020-12-19 12:16:18 +01:00
mut timers_should_print := false
$ if time_cgening ? {
timers_should_print = true
}
2021-09-28 09:28:04 +02:00
mut global_g := Gen {
2021-05-22 17:59:17 +02:00
file : 0
2021-03-06 11:03:38 +01:00
out : strings . new_builder ( 512000 )
cheaders : strings . new_builder ( 15000 )
2020-04-16 11:29:36 +02:00
includes : strings . new_builder ( 100 )
2020-03-04 17:08:28 +01:00
typedefs : strings . new_builder ( 100 )
2021-12-19 13:31:04 +01:00
enum_typedefs : strings . new_builder ( 100 )
2020-05-31 12:57:26 +02:00
type_definitions : strings . new_builder ( 100 )
2022-01-06 10:54:22 +01:00
alias_definitions : strings . new_builder ( 100 )
2021-12-19 13:31:04 +01:00
hotcode_definitions : strings . new_builder ( 100 )
channel_definitions : strings . new_builder ( 100 )
comptime_definitions : strings . new_builder ( 100 )
2020-01-02 08:30:15 +01:00
definitions : strings . new_builder ( 100 )
2020-04-03 18:44:49 +02:00
gowrappers : strings . new_builder ( 100 )
2020-04-04 22:06:47 +02:00
stringliterals : strings . new_builder ( 100 )
2020-04-21 12:26:46 +02:00
auto_str_funcs : strings . new_builder ( 100 )
2022-01-06 10:54:22 +01:00
dump_funcs : strings . new_builder ( 100 )
2020-04-25 12:05:31 +02:00
pcs_declarations : strings . new_builder ( 100 )
2021-01-14 15:20:11 +01:00
embedded_data : strings . new_builder ( 1000 )
2020-05-31 12:57:26 +02:00
options : strings . new_builder ( 100 )
2020-08-18 10:45:36 +02:00
shared_types : strings . new_builder ( 100 )
2021-02-06 03:07:05 +01:00
shared_functions : strings . new_builder ( 100 )
2020-06-01 18:36:07 +02:00
json_forward_decls : strings . new_builder ( 100 )
2020-06-17 00:59:33 +02:00
sql_buf : strings . new_builder ( 100 )
2020-01-02 20:09:15 +01:00
table : table
2020-03-30 17:21:32 +02:00
pref : pref
2020-01-07 12:10:07 +01:00
fn_decl : 0
2021-09-28 09:28:04 +02:00
is_autofree : pref . autofree
2020-03-22 10:12:43 +01:00
indent : - 1
2020-12-17 06:08:28 +01:00
module_built : module_built
2021-09-28 09:28:04 +02:00
timers_should_print : timers_should_print
2021-12-05 10:55:41 +01:00
timers : util . new_timers ( should_print : timers_should_print , label : ' g l o b a l _ c g e n ' )
2021-05-12 10:44:47 +02:00
inner_loop : & ast . EmptyStmt { }
2021-07-21 22:43:30 +02:00
field_data_type : ast . Type ( table . find_type_idx ( ' F i e l d D a t a ' ) )
2021-09-28 09:28:04 +02:00
init : strings . new_builder ( 100 )
}
// anon fn may include assert and thus this needs
// to be included before any test contents are written
if pref . is_test {
global_g . write_tests_definitions ( )
}
global_g . timers . start ( ' c g e n i n i t ' )
for mod in global_g . table . modules {
global_g . inits [ mod ] = strings . new_builder ( 200 )
global_g . global_inits [ mod ] = strings . new_builder ( 100 )
global_g . cleanups [ mod ] = strings . new_builder ( 100 )
}
global_g . init ( )
global_g . timers . show ( ' c g e n i n i t ' )
global_g . tests_inited = false
if ! pref . no_parallel {
2021-10-29 20:01:07 +02:00
mut pp := pool . new_pool_processor ( callback : cgen_process_one_file_cb )
2021-09-28 09:28:04 +02:00
pp . set_shared_context ( global_g ) // TODO: make global_g shared
pp . work_on_items ( files )
global_g . timers . start ( ' c g e n u n i f i c a t i o n ' )
// tg = thread gen
2021-10-29 20:01:07 +02:00
for g in pp . get_results_ref < Gen > ( ) {
2021-10-05 14:06:17 +02:00
global_g . embedded_files << g . embedded_files
2021-09-28 09:28:04 +02:00
global_g . out . write ( g . out ) or { panic ( err ) }
global_g . cheaders . write ( g . cheaders ) or { panic ( err ) }
global_g . includes . write ( g . includes ) or { panic ( err ) }
global_g . typedefs . write ( g . typedefs ) or { panic ( err ) }
global_g . type_definitions . write ( g . type_definitions ) or { panic ( err ) }
2022-01-06 10:54:22 +01:00
global_g . alias_definitions . write ( g . alias_definitions ) or { panic ( err ) }
2021-09-28 09:28:04 +02:00
global_g . definitions . write ( g . definitions ) or { panic ( err ) }
global_g . gowrappers . write ( g . gowrappers ) or { panic ( err ) }
global_g . stringliterals . write ( g . stringliterals ) or { panic ( err ) }
global_g . auto_str_funcs . write ( g . auto_str_funcs ) or { panic ( err ) }
2022-01-06 10:54:22 +01:00
global_g . dump_funcs . write ( g . auto_str_funcs ) or { panic ( err ) }
2021-12-19 13:31:04 +01:00
global_g . comptime_definitions . write ( g . comptime_definitions ) or { panic ( err ) }
2021-09-28 09:28:04 +02:00
global_g . pcs_declarations . write ( g . pcs_declarations ) or { panic ( err ) }
global_g . hotcode_definitions . write ( g . hotcode_definitions ) or { panic ( err ) }
global_g . embedded_data . write ( g . embedded_data ) or { panic ( err ) }
global_g . shared_types . write ( g . shared_types ) or { panic ( err ) }
global_g . shared_functions . write ( g . channel_definitions ) or { panic ( err ) }
2021-10-05 22:57:14 +02:00
global_g . force_main_console = global_g . force_main_console || g . force_main_console
2021-09-28 09:28:04 +02:00
// merge maps
for k , v in g . shareds {
global_g . shareds [ k ] = v
}
for k , v in g . chan_pop_optionals {
global_g . chan_pop_optionals [ k ] = v
}
for k , v in g . chan_push_optionals {
global_g . chan_push_optionals [ k ] = v
}
for k , v in g . optionals {
global_g . optionals [ k ] = v
}
for k , v in g . as_cast_type_names {
global_g . as_cast_type_names [ k ] = v
}
for k , v in g . sumtype_definitions {
global_g . sumtype_definitions [ k ] = v
}
global_g . json_forward_decls . write ( g . json_forward_decls ) or { panic ( err ) }
global_g . enum_typedefs . write ( g . enum_typedefs ) or { panic ( err ) }
global_g . channel_definitions . write ( g . channel_definitions ) or { panic ( err ) }
global_g . sql_buf . write ( g . sql_buf ) or { panic ( err ) }
global_g . cleanups [ g . file . mod . name ] . write ( g . cleanup ) or { panic ( err ) } // strings.Builder.write never fails; it is like that in the source
global_g . inits [ g . file . mod . name ] . write ( g . init ) or { panic ( err ) }
global_g . global_inits [ g . file . mod . name ] . write ( g . global_init ) or { panic ( err ) }
for str_type in g . str_types {
global_g . str_types << str_type
}
for scf in g . sumtype_casting_fns {
if scf ! in global_g . sumtype_casting_fns {
global_g . sumtype_casting_fns << scf
}
}
global_g . nr_closures += g . nr_closures
global_g . has_main = global_g . has_main || g . has_main
global_g . auto_fn_definitions << g . auto_fn_definitions
global_g . anon_fn_definitions << g . anon_fn_definitions
global_g . needed_equality_fns << g . needed_equality_fns // duplicates are resolved later in gen_equality_fns
global_g . array_contains_types << g . array_contains_types
global_g . array_index_types << g . array_index_types
global_g . pcs << g . pcs
global_g . json_types << g . json_types
global_g . hotcode_fn_names << g . hotcode_fn_names
2021-10-29 20:01:07 +02:00
unsafe { g . free_builders ( ) }
2020-05-07 15:36:04 +02:00
}
2021-09-28 09:28:04 +02:00
} else {
for file in files {
global_g . file = file
2021-10-05 14:06:17 +02:00
global_g . gen_file ( )
2021-12-05 10:59:18 +01:00
global_g . inits [ file . mod . name ] . drain_builder ( mut global_g . init , 100 )
global_g . cleanups [ file . mod . name ] . drain_builder ( mut global_g . cleanup , 100 )
global_g . global_inits [ file . mod . name ] . drain_builder ( mut global_g . global_init ,
100 )
2021-09-28 09:28:04 +02:00
}
global_g . timers . start ( ' c g e n u n i f i c a t i o n ' )
}
global_g . gen_jsons ( )
global_g . write_optionals ( )
global_g . dump_expr_definitions ( ) // this uses global_g.get_str_fn, so it has to go before the below for loop
for i := 0 ; i < global_g . str_types . len ; i ++ {
global_g . final_gen_str ( global_g . str_types [ i ] )
}
for sumtype_casting_fn in global_g . sumtype_casting_fns {
global_g . write_sumtype_casting_fn ( sumtype_casting_fn )
}
global_g . write_shareds ( )
global_g . write_chan_pop_optional_fns ( )
global_g . write_chan_push_optional_fns ( )
global_g . gen_array_contains_methods ( )
global_g . gen_array_index_methods ( )
global_g . gen_equality_fns ( )
global_g . timers . show ( ' c g e n u n i f i c a t i o n ' )
mut g := global_g
2020-12-19 11:49:28 +01:00
g . timers . start ( ' c g e n c o m m o n ' )
2020-11-29 14:10:45 +01:00
// to make sure type idx's are the same in cached mods
if g . pref . build_mode == . build_module {
2021-12-01 08:50:53 +01:00
for idx , sym in g . table . type_symbols {
2020-11-29 14:10:45 +01:00
if idx == 0 {
continue
}
2021-12-01 08:50:53 +01:00
g . definitions . writeln ( ' i n t _ v _ t y p e _ i d x _ $ { sym . cname } ( ) ; ' )
2020-11-29 14:10:45 +01:00
}
} else if g . pref . use_cache {
2021-12-01 08:50:53 +01:00
for idx , sym in g . table . type_symbols {
2020-11-29 14:10:45 +01:00
if idx == 0 {
continue
}
2021-12-01 08:50:53 +01:00
g . definitions . writeln ( ' i n t _ v _ t y p e _ i d x _ $ { sym . cname } ( ) { r e t u r n $ idx ; } ; ' )
2020-11-29 14:10:45 +01:00
}
}
2021-03-06 18:09:28 +01:00
//
2020-08-11 23:08:19 +02:00
// v files are finished, what remains is pure C code
g . gen_vlines_reset ( )
2020-04-10 18:11:43 +02:00
if g . pref . build_mode != . build_module {
// no init in builtin.o
g . write_init_function ( )
}
2021-03-06 11:03:38 +01:00
2020-04-04 22:06:47 +02:00
g . finish ( )
2021-03-06 11:03:38 +01:00
mut b := strings . new_builder ( 640000 )
2021-02-22 12:18:11 +01:00
b . write_string ( g . hashes ( ) )
2021-12-19 13:31:04 +01:00
b . writeln ( ' \n / / V c o m p t i m e _ d e f i n i t i o n s : ' )
b . write_string ( g . comptime_definitions . str ( ) )
2020-04-25 12:05:31 +02:00
b . writeln ( ' \n / / V t y p e d e f s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . typedefs . str ( ) )
2020-04-25 12:05:31 +02:00
b . writeln ( ' \n / / V c h e a d e r s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . cheaders . str ( ) )
2020-12-25 13:11:21 +01:00
if g . pcs_declarations . len > 0 {
b . writeln ( ' \n / / V p r o f i l e c o u n t e r s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . pcs_declarations . str ( ) )
2020-12-25 13:11:21 +01:00
}
2020-04-25 12:05:31 +02:00
b . writeln ( ' \n / / V i n c l u d e s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . includes . str ( ) )
2020-06-10 17:01:55 +02:00
b . writeln ( ' \n / / E n u m d e f i n i t i o n s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . enum_typedefs . str ( ) )
2020-05-31 12:57:26 +02:00
b . writeln ( ' \n / / V t y p e d e f i n i t i o n s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . type_definitions . str ( ) )
2022-01-06 10:54:22 +01:00
b . writeln ( ' \n / / V a l i a s d e f i n i t i o n s : ' )
b . write_string ( g . alias_definitions . str ( ) )
2021-02-06 03:07:05 +01:00
b . writeln ( ' \n / / V s h a r e d t y p e s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . shared_types . str ( ) )
2020-05-31 12:57:26 +02:00
b . writeln ( ' \n / / V O p t i o n _ x x x d e f i n i t i o n s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . options . str ( ) )
2020-06-01 18:36:07 +02:00
b . writeln ( ' \n / / V j s o n f o r w a r d d e c l s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . json_forward_decls . str ( ) )
2020-04-25 12:05:31 +02:00
b . writeln ( ' \n / / V d e f i n i t i o n s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . definitions . str ( ) )
2020-07-06 12:33:57 +02:00
interface_table := g . interface_table ( )
if interface_table . len > 0 {
b . writeln ( ' \n / / V i n t e r f a c e t a b l e : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( interface_table )
2020-07-06 12:33:57 +02:00
}
if g . gowrappers . len > 0 {
b . writeln ( ' \n / / V g o w r a p p e r s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . gowrappers . str ( ) )
2020-07-06 12:33:57 +02:00
}
if g . hotcode_definitions . len > 0 {
b . writeln ( ' \n / / V h o t c o d e d e f i n i t i o n s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . hotcode_definitions . str ( ) )
2020-07-06 12:33:57 +02:00
}
2021-01-14 15:20:11 +01:00
if g . embedded_data . len > 0 {
b . writeln ( ' \n / / V e m b e d d e d d a t a : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . embedded_data . str ( ) )
2021-01-14 15:20:11 +01:00
}
2021-02-06 03:07:05 +01:00
if g . shared_functions . len > 0 {
b . writeln ( ' \n / / V s h a r e d t y p e f u n c t i o n s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . shared_functions . str ( ) )
b . write_string ( c_concurrency_helpers )
2020-08-18 10:45:36 +02:00
}
if g . channel_definitions . len > 0 {
b . writeln ( ' \n / / V c h a n n e l c o d e : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . channel_definitions . str ( ) )
2020-08-18 10:45:36 +02:00
}
2020-07-06 12:33:57 +02:00
if g . stringliterals . len > 0 {
b . writeln ( ' \n / / V s t r i n g l i t e r a l s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . stringliterals . str ( ) )
2020-07-06 12:33:57 +02:00
}
if g . auto_str_funcs . len > 0 {
2020-11-29 14:10:45 +01:00
// if g.pref.build_mode != .build_module {
b . writeln ( ' \n / / V a u t o s t r f u n c t i o n s : ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . auto_str_funcs . str ( ) )
2020-11-29 14:10:45 +01:00
// }
2020-07-06 12:33:57 +02:00
}
2022-01-06 10:54:22 +01:00
if g . dump_funcs . len > 0 {
b . writeln ( ' \n / / V d u m p f u n c t i o n s : ' )
b . write_string ( g . dump_funcs . str ( ) )
}
2020-12-23 02:32:19 +01:00
if g . auto_fn_definitions . len > 0 {
for fn_def in g . auto_fn_definitions {
b . writeln ( fn_def )
}
}
2021-01-15 10:25:30 +01:00
if g . anon_fn_definitions . len > 0 {
2021-08-10 20:27:15 +02:00
if g . nr_closures > 0 {
b . writeln ( ' \n / / V c l o s u r e h e l p e r s ' )
2021-08-11 13:09:40 +02:00
b . writeln ( c_closure_helpers ( g . pref ) )
2021-08-10 20:27:15 +02:00
}
2021-01-15 10:25:30 +01:00
for fn_def in g . anon_fn_definitions {
b . writeln ( fn_def )
}
}
2020-04-25 12:05:31 +02:00
b . writeln ( ' \n / / V o u t ' )
2021-02-22 12:18:11 +01:00
b . write_string ( g . out . str ( ) )
2020-04-25 12:05:31 +02:00
b . writeln ( ' \n / / T H E E N D . ' )
2020-12-19 11:24:29 +01:00
g . timers . show ( ' c g e n c o m m o n ' )
2021-10-29 20:01:07 +02:00
res := b . str ( )
2021-12-28 18:42:31 +01:00
$ if trace_all_generic_fn_keys ? {
gkeys := g . table . fn_generic_types . keys ( )
for gkey in gkeys {
eprintln ( ' > > g . t a b l e . f n _ g e n e r i c _ t y p e s k e y : $ gkey ' )
}
}
2021-10-29 20:01:07 +02:00
unsafe { b . free ( ) }
unsafe { g . free_builders ( ) }
return res
}
fn cgen_process_one_file_cb ( p & pool . PoolProcessor , idx int , wid int ) & Gen {
file := p . get_item < & ast . File > ( idx )
mut global_g := & Gen ( p . get_shared_context ( ) )
mut g := & Gen {
file : file
out : strings . new_builder ( 512000 )
cheaders : strings . new_builder ( 15000 )
includes : strings . new_builder ( 100 )
typedefs : strings . new_builder ( 100 )
type_definitions : strings . new_builder ( 100 )
2022-01-06 10:54:22 +01:00
alias_definitions : strings . new_builder ( 100 )
2021-10-29 20:01:07 +02:00
definitions : strings . new_builder ( 100 )
gowrappers : strings . new_builder ( 100 )
stringliterals : strings . new_builder ( 100 )
auto_str_funcs : strings . new_builder ( 100 )
2021-12-19 13:31:04 +01:00
comptime_definitions : strings . new_builder ( 100 )
2021-10-29 20:01:07 +02:00
pcs_declarations : strings . new_builder ( 100 )
hotcode_definitions : strings . new_builder ( 100 )
embedded_data : strings . new_builder ( 1000 )
options : strings . new_builder ( 100 )
shared_types : strings . new_builder ( 100 )
shared_functions : strings . new_builder ( 100 )
channel_definitions : strings . new_builder ( 100 )
json_forward_decls : strings . new_builder ( 100 )
enum_typedefs : strings . new_builder ( 100 )
sql_buf : strings . new_builder ( 100 )
init : strings . new_builder ( 100 )
global_init : strings . new_builder ( 0 )
cleanup : strings . new_builder ( 100 )
table : global_g . table
pref : global_g . pref
fn_decl : 0
indent : - 1
module_built : global_g . module_built
2021-12-05 10:55:41 +01:00
timers : util . new_timers (
should_print : global_g . timers_should_print
label : ' c g e n _ p r o c e s s _ o n e _ f i l e _ c b i d x : $ idx , w i d : $ wid '
)
2021-10-29 20:01:07 +02:00
inner_loop : & ast . EmptyStmt { }
field_data_type : ast . Type ( global_g . table . find_type_idx ( ' F i e l d D a t a ' ) )
array_sort_fn : global_g . array_sort_fn
2021-12-19 12:38:48 +01:00
waiter_fns : global_g . waiter_fns
2021-10-29 20:01:07 +02:00
threaded_fns : global_g . threaded_fns
done_optionals : global_g . done_optionals
is_autofree : global_g . pref . autofree
2021-12-26 19:01:00 +01:00
referenced_fns : global_g . referenced_fns
2021-10-29 20:01:07 +02:00
}
g . gen_file ( )
return g
}
// free_builders should be called only when a Gen would NOT be used anymore
// it frees the bulk of the memory that is private to the Gen instance
// (the various string builders)
[ unsafe ]
pub fn ( mut g Gen ) free_builders ( ) {
unsafe {
g . out . free ( )
g . cheaders . free ( )
g . includes . free ( )
g . typedefs . free ( )
g . type_definitions . free ( )
2022-01-06 10:54:22 +01:00
g . alias_definitions . free ( )
2021-10-29 20:01:07 +02:00
g . definitions . free ( )
g . global_init . free ( )
g . init . free ( )
g . cleanup . free ( )
g . gowrappers . free ( )
g . stringliterals . free ( )
g . auto_str_funcs . free ( )
2022-01-06 10:54:22 +01:00
g . dump_funcs . free ( )
2021-12-19 13:31:04 +01:00
g . comptime_definitions . free ( )
2021-10-29 20:01:07 +02:00
g . pcs_declarations . free ( )
g . hotcode_definitions . free ( )
g . embedded_data . free ( )
g . shared_types . free ( )
g . shared_functions . free ( )
g . channel_definitions . free ( )
g . options . free ( )
g . json_forward_decls . free ( )
g . enum_typedefs . free ( )
g . sql_buf . free ( )
for _ , mut v in g . global_inits {
v . free ( )
}
for _ , mut v in g . inits {
v . free ( )
}
for _ , mut v in g . cleanups {
v . free ( )
}
}
2020-04-02 13:49:59 +02:00
}
2021-10-05 14:06:17 +02:00
pub fn ( mut g Gen ) gen_file ( ) {
2021-09-28 09:28:04 +02:00
g . timers . start ( ' c g e n _ f i l e $ g . file . path ' )
2021-12-29 19:15:51 +01:00
g . unique_file_path_hash = fnv1a . sum64_string ( g . file . path )
2021-09-28 09:28:04 +02:00
if g . pref . is_vlines {
g . vlines_path = util . vlines_escape_path ( g . file . path , g . pref . ccompiler )
2021-10-05 20:44:26 +02:00
g . is_vlines_enabled = true
g . inside_ternary = 0
2021-09-28 09:28:04 +02:00
}
2021-10-05 20:44:26 +02:00
2021-09-28 09:28:04 +02:00
g . stmts ( g . file . stmts )
// Transfer embedded files
for path in g . file . embedded_files {
if path ! in g . embedded_files {
g . embedded_files << path
}
}
g . timers . show ( ' c g e n _ f i l e $ g . file . path ' )
}
2020-08-11 23:08:19 +02:00
pub fn ( g & Gen ) hashes ( ) string {
2021-07-27 11:35:54 +02:00
mut res := c_commit_hash_default . replace ( ' @ @ @ ' , version . vhash ( ) )
res += c_current_commit_hash_default . replace ( ' @ @ @ ' , version . githash ( g . pref . building_v ) )
2020-04-02 13:49:59 +02:00
return res
2020-03-04 17:08:28 +01:00
}
2020-04-21 05:11:50 +02:00
pub fn ( mut g Gen ) init ( ) {
2020-05-26 14:12:18 +02:00
if g . pref . custom_prelude != ' ' {
g . cheaders . writeln ( g . pref . custom_prelude )
} else if ! g . pref . no_preludes {
g . cheaders . writeln ( ' / / G e n e r a t e d b y t h e V c o m p i l e r ' )
2022-01-08 10:06:32 +01:00
if g . pref . os == . wasm32 {
g . cheaders . writeln ( ' # d e f i n e V W A S M 1 ' )
// Include <stdint.h> instead of <inttypes.h> for WASM target
g . cheaders . writeln ( ' # i n c l u d e < s t d i n t . h > ' )
2021-09-23 12:48:41 +02:00
g . cheaders . writeln ( ' # i n c l u d e < s t d d e f . h > ' )
} else {
2022-01-08 10:06:32 +01:00
tcc_undef_has_include := '
#if defined(__TINYC__) && defined(__has_include)
// tcc does not support has_include properly yet, turn it off completely
#undef __has_include
#endif'
g . cheaders . writeln ( tcc_undef_has_include )
g . includes . writeln ( tcc_undef_has_include )
if g . pref . os == . freebsd {
g . cheaders . writeln ( ' # i n c l u d e < i n t t y p e s . h > ' )
g . cheaders . writeln ( ' # i n c l u d e < s t d d e f . h > ' )
} else {
g . cheaders . writeln ( get_guarded_include_text ( ' < i n t t y p e s . h > ' , ' T h e C c o m p i l e r c a n n o t f i n d < i n t t y p e s . h > . P l e a s e i n s t a l l b u i l d - e s s e n t i a l s ' ) ) // int64_t etc
g . cheaders . writeln ( get_guarded_include_text ( ' < s t d d e f . h > ' , ' T h e C c o m p i l e r c a n n o t f i n d < s t d d e f . h > . P l e a s e i n s t a l l b u i l d - e s s e n t i a l s ' ) ) // size_t, ptrdiff_t
}
2021-09-23 12:48:41 +02:00
}
2022-01-10 11:42:41 +01:00
if g . pref . nofloat {
g . cheaders . writeln ( ' # d e f i n e V N O F L O A T 1 ' )
}
2020-05-26 14:12:18 +02:00
g . cheaders . writeln ( c_builtin_types )
if g . pref . is_bare {
2021-06-05 16:29:23 +02:00
g . cheaders . writeln ( c_bare_headers )
2020-05-26 14:12:18 +02:00
} else {
g . cheaders . writeln ( c_headers )
}
2021-06-05 16:29:23 +02:00
if ! g . pref . skip_unused || g . table . used_maps > 0 {
g . cheaders . writeln ( c_wyhash_headers )
}
2020-05-26 14:12:18 +02:00
}
2020-07-23 06:58:44 +02:00
if g . pref . os == . ios {
g . cheaders . writeln ( ' # d e f i n e _ _ T A R G E T _ I O S _ _ 1 ' )
2020-07-24 09:17:32 +02:00
g . cheaders . writeln ( ' # i n c l u d e < s p a w n . h > ' )
2020-07-23 06:58:44 +02:00
}
2020-03-10 23:21:26 +01:00
g . write_builtin_types ( )
2020-03-12 07:56:44 +01:00
g . write_typedef_types ( )
2020-03-28 17:37:22 +01:00
g . write_typeof_functions ( )
2020-03-05 23:27:21 +01:00
g . write_sorted_types ( )
2020-12-31 21:45:03 +01:00
g . write_multi_return_types ( )
2020-03-06 12:06:41 +01:00
g . definitions . writeln ( ' / / e n d o f d e f i n i t i o n s # e n d i f ' )
2020-04-08 13:53:11 +02:00
//
2021-10-23 11:53:53 +02:00
if ! g . pref . no_builtin {
g . stringliterals . writeln ( ' ' )
g . stringliterals . writeln ( ' / / > > s t r i n g l i t e r a l c o n s t s ' )
if g . pref . build_mode != . build_module {
2021-10-24 14:29:36 +02:00
g . stringliterals . writeln ( ' v o i d v i n i t _ s t r i n g _ l i t e r a l s ( v o i d ) { ' )
2021-10-23 11:53:53 +02:00
}
2020-04-10 18:11:43 +02:00
}
2020-04-23 16:52:44 +02:00
if g . pref . compile_defines_all . len > 0 {
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' / / V c o m p i l e t i m e d e f i n e s b y - d o r - d e f i n e f l a g s : ' )
g . comptime_definitions . writeln ( ' / / A l l c u s t o m d e f i n e s : ' +
2021-01-23 09:33:22 +01:00
g . pref . compile_defines_all . join ( ' , ' ) )
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' / / T u r n e d O N c u s t o m d e f i n e s : ' +
2021-01-23 09:33:22 +01:00
g . pref . compile_defines . join ( ' , ' ) )
2020-04-23 16:52:44 +02:00
for cdefine in g . pref . compile_defines {
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' # d e f i n e C U S T O M _ D E F I N E _ $ cdefine ' )
2020-04-23 16:52:44 +02:00
}
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' ' )
2020-04-23 16:52:44 +02:00
}
2021-05-27 17:36:07 +02:00
if g . table . gostmts > 0 {
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' # d e f i n e _ _ V T H R E A D S _ _ ( 1 ) ' )
2021-05-27 17:36:07 +02:00
}
2021-06-15 14:43:00 +02:00
if g . pref . gc_mode in [ . boehm_full , . boehm_incr , . boehm_full_opt , . boehm_incr_opt , . boehm_leak ] {
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' # d e f i n e _ V G C B O E H M ( 1 ) ' )
2021-03-20 14:16:36 +01:00
}
2020-04-23 17:03:07 +02:00
if g . pref . is_debug || ' d e b u g ' in g . pref . compile_defines {
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' # d e f i n e _ V D E B U G ( 1 ) ' )
2020-04-23 17:03:07 +02:00
}
2021-03-10 18:26:34 +01:00
if g . pref . is_prod || ' p r o d ' in g . pref . compile_defines {
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' # d e f i n e _ V P R O D ( 1 ) ' )
2021-03-10 18:26:34 +01:00
}
2020-06-03 09:16:08 +02:00
if g . pref . is_test || ' t e s t ' in g . pref . compile_defines {
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' # d e f i n e _ V T E S T ( 1 ) ' )
2020-06-03 09:16:08 +02:00
}
2020-06-29 17:47:01 +02:00
if g . pref . autofree {
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' # d e f i n e _ V A U T O F R E E ( 1 ) ' )
// g.comptime_definitions.writeln('unsigned char* g_cur_str;')
2020-06-29 17:47:01 +02:00
}
2020-07-11 11:25:53 +02:00
if g . pref . prealloc {
2021-12-19 13:31:04 +01:00
g . comptime_definitions . writeln ( ' # d e f i n e _ V P R E A L L O C ( 1 ) ' )
2020-07-11 11:25:53 +02:00
}
2020-05-01 19:34:27 +02:00
if g . pref . is_livemain || g . pref . is_liveshared {
g . generate_hotcode_reloading_declarations ( )
}
2021-02-02 09:14:34 +01:00
// Obfuscate only functions in the main module for now.
2021-04-02 00:57:09 +02:00
// Generate the obf_ast.
2021-02-02 09:14:34 +01:00
if g . pref . obfuscate {
mut i := 0
// fns
for key , f in g . table . fns {
if f . mod != ' m a i n ' && key != ' m a i n ' { // !key.starts_with('main.') {
continue
}
g . obf_table [ key ] = ' _ f $ i '
i ++
}
// methods
2021-03-19 21:51:52 +01:00
for type_sym in g . table . type_symbols {
2021-02-02 09:14:34 +01:00
if type_sym . mod != ' m a i n ' {
continue
}
for method in type_sym . methods {
g . obf_table [ type_sym . name + ' . ' + method . name ] = ' _ f $ i '
i ++
}
}
}
2021-09-28 09:28:04 +02:00
// we know that this is being called before the multi-threading starts
// and this is being called in the main thread, so we can mutate the table
mut muttable := unsafe { & ast . Table ( g . table ) }
muttable . used_fns [ ' v _ s e g m e n t a t i o n _ f a u l t _ h a n d l e r ' ] = true
muttable . used_fns [ ' e p r i n t l n ' ] = true
muttable . used_fns [ ' p r i n t _ b a c k t r a c e ' ] = true
muttable . used_fns [ ' e x i t ' ] = true
2020-04-04 22:06:47 +02:00
}
2020-04-21 05:11:50 +02:00
pub fn ( mut g Gen ) finish ( ) {
2021-10-23 11:53:53 +02:00
if ! g . pref . no_builtin {
if g . pref . build_mode != . build_module {
g . stringliterals . writeln ( ' } ' )
}
g . stringliterals . writeln ( ' / / < < s t r i n g l i t e r a l c o n s t s ' )
g . stringliterals . writeln ( ' ' )
2020-04-10 18:11:43 +02:00
}
2021-01-20 06:04:59 +01:00
if g . pref . is_prof && g . pref . build_mode != . build_module {
2020-04-27 08:38:47 +02:00
g . gen_vprint_profile_stats ( )
}
2020-05-01 19:34:27 +02:00
if g . pref . is_livemain || g . pref . is_liveshared {
g . generate_hotcode_reloader_code ( )
}
2021-01-16 19:03:07 +01:00
if g . embed_file_is_prod_mode ( ) && g . embedded_files . len > 0 {
2021-01-14 15:20:11 +01:00
g . gen_embedded_data ( )
}
2021-01-10 13:31:20 +01:00
if g . pref . is_test {
g . gen_c_main_for_tests ( )
} else {
2020-07-01 00:53:53 +02:00
g . gen_c_main ( )
2020-05-01 19:34:27 +02:00
}
2020-03-05 23:27:21 +01:00
}
2020-04-21 05:11:50 +02:00
pub fn ( mut g Gen ) write_typeof_functions ( ) {
2020-04-04 22:06:47 +02:00
g . writeln ( ' ' )
2021-02-15 14:29:44 +01:00
g . writeln ( ' / / > > t y p e o f ( ) s u p p o r t f o r s u m t y p e s / i n t e r f a c e s ' )
2021-12-01 08:50:53 +01:00
for ityp , sym in g . table . type_symbols {
if sym . kind == . sum_type {
sum_info := sym . info as ast . SumType
2021-07-15 07:29:13 +02:00
if sum_info . is_generic {
continue
}
2021-12-01 08:50:53 +01:00
g . writeln ( ' s t a t i c c h a r * v _ t y p e o f _ s u m t y p e _ $ { sym . cname } ( i n t s i d x ) { / * $ sym . name * / ' )
2021-01-20 06:04:59 +01:00
if g . pref . build_mode == . build_module {
2021-12-01 08:50:53 +01:00
g . writeln ( ' \t \t i f ( s i d x = = _ v _ t y p e _ i d x _ $ { sym . cname } ( ) ) r e t u r n " $ { util . strip_main_name ( sym . name ) } " ; ' )
2021-01-20 06:04:59 +01:00
for v in sum_info . variants {
2021-12-19 17:25:18 +01:00
subtype := g . table . sym ( v )
2021-01-20 06:04:59 +01:00
g . writeln ( ' \t i f ( s i d x = = _ v _ t y p e _ i d x _ $ { subtype . cname } ( ) ) r e t u r n " $ { util . strip_main_name ( subtype . name ) } " ; ' )
}
2021-12-01 08:50:53 +01:00
g . writeln ( ' \t r e t u r n " u n k n o w n $ { util . strip_main_name ( sym . name ) } " ; ' )
2021-01-20 06:04:59 +01:00
} else {
2021-12-01 08:50:53 +01:00
tidx := g . table . find_type_idx ( sym . name )
2021-01-20 06:04:59 +01:00
g . writeln ( ' \t s w i t c h ( s i d x ) { ' )
2021-12-01 08:50:53 +01:00
g . writeln ( ' \t \t c a s e $ tidx : r e t u r n " $ { util . strip_main_name ( sym . name ) } " ; ' )
2021-01-20 06:04:59 +01:00
for v in sum_info . variants {
2021-12-19 17:25:18 +01:00
subtype := g . table . sym ( v )
2021-01-20 06:04:59 +01:00
g . writeln ( ' \t \t c a s e $ v : r e t u r n " $ { util . strip_main_name ( subtype . name ) } " ; ' )
}
2021-12-01 08:50:53 +01:00
g . writeln ( ' \t \t d e f a u l t : r e t u r n " u n k n o w n $ { util . strip_main_name ( sym . name ) } " ; ' )
2021-01-20 06:04:59 +01:00
g . writeln ( ' \t } ' )
}
2020-11-12 12:27:54 +01:00
g . writeln ( ' } ' )
2021-08-14 16:22:25 +02:00
g . writeln ( ' ' )
2021-12-01 08:50:53 +01:00
g . writeln ( ' i n t v _ t y p e o f _ s u m t y p e _ i d x _ $ { sym . cname } ( i n t s i d x ) { / * $ sym . name * / ' )
2021-08-14 16:22:25 +02:00
if g . pref . build_mode == . build_module {
2021-12-01 08:50:53 +01:00
g . writeln ( ' \t \t i f ( s i d x = = _ v _ t y p e _ i d x _ $ { sym . cname } ( ) ) r e t u r n $ { int ( ityp ) } ; ' )
2021-08-14 16:22:25 +02:00
for v in sum_info . variants {
2021-12-19 17:25:18 +01:00
subtype := g . table . sym ( v )
2021-08-14 16:22:25 +02:00
g . writeln ( ' \t i f ( s i d x = = _ v _ t y p e _ i d x _ $ { subtype . cname } ( ) ) r e t u r n $ { int ( v ) } ; ' )
}
g . writeln ( ' \t r e t u r n $ { int ( ityp ) } ; ' )
} else {
2021-12-01 08:50:53 +01:00
tidx := g . table . find_type_idx ( sym . name )
2021-08-14 16:22:25 +02:00
g . writeln ( ' \t s w i t c h ( s i d x ) { ' )
g . writeln ( ' \t \t c a s e $ tidx : r e t u r n $ { int ( ityp ) } ; ' )
for v in sum_info . variants {
g . writeln ( ' \t \t c a s e $ v : r e t u r n $ { int ( v ) } ; ' )
}
g . writeln ( ' \t \t d e f a u l t : r e t u r n $ { int ( ityp ) } ; ' )
g . writeln ( ' \t } ' )
}
g . writeln ( ' } ' )
2021-12-01 08:50:53 +01:00
} else if sym . kind == . interface_ {
if sym . info ! is ast . Interface {
2021-10-23 11:53:53 +02:00
continue
}
2021-12-01 08:50:53 +01:00
inter_info := sym . info as ast . Interface
2021-07-15 07:29:13 +02:00
if inter_info . is_generic {
continue
}
2021-12-01 08:50:53 +01:00
g . definitions . writeln ( ' s t a t i c c h a r * v _ t y p e o f _ i n t e r f a c e _ $ { sym . cname } ( i n t s i d x ) ; ' )
g . writeln ( ' s t a t i c c h a r * v _ t y p e o f _ i n t e r f a c e _ $ { sym . cname } ( i n t s i d x ) { / * $ sym . name * / ' )
2021-02-15 14:29:44 +01:00
for t in inter_info . types {
2021-12-19 17:25:18 +01:00
subtype := g . table . sym ( t )
2021-12-01 08:50:53 +01:00
g . writeln ( ' \t i f ( s i d x = = _ $ { sym . cname } _ $ { subtype . cname } _ i n d e x ) r e t u r n " $ { util . strip_main_name ( subtype . name ) } " ; ' )
2021-02-15 14:29:44 +01:00
}
2021-12-01 08:50:53 +01:00
g . writeln ( ' \t r e t u r n " u n k n o w n $ { util . strip_main_name ( sym . name ) } " ; ' )
2021-02-15 14:29:44 +01:00
g . writeln ( ' } ' )
2021-08-14 16:22:25 +02:00
g . writeln ( ' ' )
2021-12-01 08:50:53 +01:00
g . writeln ( ' s t a t i c i n t v _ t y p e o f _ i n t e r f a c e _ i d x _ $ { sym . cname } ( i n t s i d x ) { / * $ sym . name * / ' )
2021-08-14 16:22:25 +02:00
for t in inter_info . types {
2021-12-19 17:25:18 +01:00
subtype := g . table . sym ( t )
2021-12-01 08:50:53 +01:00
g . writeln ( ' \t i f ( s i d x = = _ $ { sym . cname } _ $ { subtype . cname } _ i n d e x ) r e t u r n $ { int ( t ) } ; ' )
2021-08-14 16:22:25 +02:00
}
g . writeln ( ' \t r e t u r n $ { int ( ityp ) } ; ' )
g . writeln ( ' } ' )
2020-11-12 12:27:54 +01:00
}
}
2020-03-28 17:37:22 +01:00
g . writeln ( ' / / < < t y p e o f ( ) s u p p o r t f o r s u m t y p e s ' )
2020-04-04 22:06:47 +02:00
g . writeln ( ' ' )
2020-03-28 17:37:22 +01:00
}
2021-01-20 06:04:59 +01:00
// V type to C typecc
2021-04-02 00:57:09 +02:00
fn ( mut g Gen ) typ ( t ast . Type ) string {
2020-06-04 14:38:54 +02:00
if t . has_flag ( . optional ) {
2020-05-13 21:59:05 +02:00
// Register an optional if it's not registered yet
2020-05-31 12:57:26 +02:00
return g . register_optional ( t )
2021-09-28 09:28:04 +02:00
} else {
return g . base_type ( t )
2020-04-22 20:12:30 +02:00
}
}
2021-09-28 09:28:04 +02:00
fn ( mut g Gen ) base_type ( _t ast . Type ) string {
t := g . unwrap_generic ( _t )
2021-09-10 19:54:41 +02:00
if g . pref . nofloat {
// todo compile time if for perf?
if t == ast . f32_type {
return ' u 3 2 '
} else if t == ast . f64_type {
return ' u 6 4 '
}
}
2020-07-04 12:44:25 +02:00
share := t . share ( )
2021-03-01 21:36:58 +01:00
mut styp := if share == . atomic_t { t . atomic_typename ( ) } else { g . cc_type ( t , true ) }
2020-07-04 12:44:25 +02:00
if t . has_flag ( . shared_f ) {
styp = g . find_or_register_shared ( t , styp )
}
2021-08-10 09:36:11 +02:00
if ! t . has_flag ( . variadic ) {
nr_muls := g . unwrap_generic ( t ) . nr_muls ( )
if nr_muls > 0 {
styp += strings . repeat ( ` * ` , nr_muls )
}
2020-05-19 14:04:51 +02:00
}
2020-05-04 00:14:59 +02:00
return styp
}
2021-07-07 10:51:05 +02:00
fn ( mut g Gen ) generic_fn_name ( types [ ] ast . Type , before string , is_decl bool ) string {
if types . len == 0 {
return before
}
// Using _T_ to differentiate between get<string> and get_string
// `foo<int>()` => `foo_T_int()`
mut name := before + ' _ T '
for typ in types {
2021-07-17 10:51:42 +02:00
name += ' _ ' + strings . repeat_string ( ' _ _ p t r _ _ ' , typ . nr_muls ( ) ) + g . typ ( typ . set_nr_muls ( 0 ) )
2021-07-07 10:51:05 +02:00
}
return name
}
2021-03-08 11:46:39 +01:00
fn ( mut g Gen ) expr_string ( expr ast . Expr ) string {
pos := g . out . len
g . expr ( expr )
2021-08-10 20:27:15 +02:00
return g . out . cut_to ( pos ) . trim_space ( )
2021-03-08 11:46:39 +01:00
}
2021-12-13 11:55:46 +01:00
fn ( mut g Gen ) expr_string_with_cast ( expr ast . Expr , typ ast . Type , exp ast . Type ) string {
pos := g . out . len
g . expr_with_cast ( expr , typ , exp )
return g . out . cut_to ( pos ) . trim_space ( )
}
2021-05-09 02:25:44 +02:00
// Surround a potentially multi-statement expression safely with `prepend` and `append`.
// (and create a statement)
fn ( mut g Gen ) expr_string_surround ( prepend string , expr ast . Expr , append string ) string {
pos := g . out . len
g . stmt_path_pos << pos
2021-08-10 20:27:15 +02:00
defer {
g . stmt_path_pos . delete_last ( )
}
2021-05-09 02:25:44 +02:00
g . write ( prepend )
g . expr ( expr )
g . write ( append )
2021-08-10 20:27:15 +02:00
return g . out . cut_to ( pos )
2021-05-09 02:25:44 +02:00
}
2020-05-31 12:57:26 +02:00
// TODO this really shouldnt be seperate from typ
2020-06-01 15:43:54 +02:00
// but I(emily) would rather have this generation
2020-05-31 12:57:26 +02:00
// all unified in one place so that it doesnt break
// if one location changes
2021-04-02 00:57:09 +02:00
fn ( mut g Gen ) optional_type_name ( t ast . Type ) ( string , string ) {
2020-05-31 12:57:26 +02:00
base := g . base_type ( t )
2021-03-14 01:54:46 +01:00
mut styp := ' O p t i o n _ $ base '
2020-05-31 12:57:26 +02:00
if t . is_ptr ( ) {
styp = styp . replace ( ' * ' , ' _ p t r ' )
}
return styp , base
}
2021-09-28 09:28:04 +02:00
fn ( g Gen ) optional_type_text ( styp string , base string ) string {
2020-05-31 12:57:26 +02:00
// replace void with something else
2021-02-28 20:24:29 +01:00
size := if base == ' v o i d ' { ' b y t e ' } else { base }
ret := ' s t r u c t $ styp {
byte state ;
2021-03-13 18:13:50 +01:00
IError err ;
2020-06-24 12:45:15 +02:00
byte data [ sizeof ( $ size ) ] ;
} '
2020-05-31 12:57:26 +02:00
return ret
}
2021-04-02 00:57:09 +02:00
fn ( mut g Gen ) register_optional ( t ast . Type ) string {
2020-05-31 12:57:26 +02:00
styp , base := g . optional_type_name ( t )
2021-09-28 09:28:04 +02:00
g . optionals [ base ] = styp
return styp
}
fn ( mut g Gen ) write_optionals ( ) {
mut done := g . done_optionals . clone ( )
for base , styp in g . optionals {
if base in done {
continue
}
done << base
2021-12-19 13:31:04 +01:00
g . typedefs . writeln ( ' t y p e d e f s t r u c t $ styp $ styp ; ' )
2021-09-28 09:28:04 +02:00
g . options . write_string ( g . optional_type_text ( styp , base ) + ' ; \n \n ' )
2020-05-31 12:57:26 +02:00
}
2020-05-13 21:59:05 +02:00
}
2021-04-02 00:57:09 +02:00
fn ( mut g Gen ) find_or_register_shared ( t ast . Type , base string ) string {
2021-09-28 09:28:04 +02:00
g . shareds [ t . idx ( ) ] = base
return ' _ _ s h a r e d _ _ $ base '
}
fn ( mut g Gen ) write_shareds ( ) {
mut done_types := [ ] int { }
for typ , base in g . shareds {
if typ in done_types {
continue
}
done_types << typ
sh_typ := ' _ _ s h a r e d _ _ $ base '
mtx_typ := ' s y n c _ _ R w M u t e x '
2021-10-04 17:27:38 +02:00
g . shared_types . writeln ( ' s t r u c t $ sh_typ { ' )
g . shared_types . writeln ( ' \t $ mtx_typ m t x ; ' )
g . shared_types . writeln ( ' \t $ base v a l ; ' )
g . shared_types . writeln ( ' } ; ' )
2021-09-28 09:28:04 +02:00
g . shared_functions . writeln ( ' s t a t i c i n l i n e v o i d p t r _ _ d u p $ { sh_typ } ( v o i d p t r s r c , i n t s z ) { ' )
g . shared_functions . writeln ( ' \t $ sh_typ * d e s t = m e m d u p ( s r c , s z ) ; ' )
g . shared_functions . writeln ( ' \t s y n c _ _ R w M u t e x _ i n i t ( & d e s t - > m t x ) ; ' )
g . shared_functions . writeln ( ' \t r e t u r n d e s t ; ' )
g . shared_functions . writeln ( ' } ' )
2021-12-19 13:31:04 +01:00
g . typedefs . writeln ( ' t y p e d e f s t r u c t $ sh_typ $ sh_typ ; ' )
2021-09-28 09:28:04 +02:00
}
2020-07-04 12:44:25 +02:00
}
2021-10-03 17:24:44 +02:00
fn ( mut g Gen ) register_thread_void_wait_call ( ) {
2021-12-19 12:38:48 +01:00
lock g . waiter_fns {
if ' _ _ v _ t h r e a d _ w a i t ' in g . waiter_fns {
return
2021-10-03 17:24:44 +02:00
}
g . waiter_fns << ' _ _ v _ t h r e a d _ w a i t '
}
2021-12-19 12:38:48 +01:00
g . gowrappers . writeln ( ' v o i d _ _ v _ t h r e a d _ w a i t ( _ _ v _ t h r e a d t h r e a d ) { ' )
if g . pref . os == . windows {
g . gowrappers . writeln ( ' \t u 3 2 s t a t = W a i t F o r S i n g l e O b j e c t ( t h r e a d , I N F I N I T E ) ; ' )
} else {
g . gowrappers . writeln ( ' \t i n t s t a t = p t h r e a d _ j o i n ( t h r e a d , ( v o i d * * ) N U L L ) ; ' )
}
g . gowrappers . writeln ( ' \t i f ( s t a t ! = 0 ) { _ v _ p a n i c ( _ S L I T ( " u n a b l e t o j o i n t h r e a d " ) ) ; } ' )
if g . pref . os == . windows {
g . gowrappers . writeln ( ' \t C l o s e H a n d l e ( t h r e a d ) ; ' )
}
g . gowrappers . writeln ( ' } ' )
2021-10-03 17:24:44 +02:00
}
2021-02-22 14:08:52 +01:00
fn ( mut g Gen ) register_thread_array_wait_call ( eltyp string ) string {
2021-02-23 08:37:29 +01:00
is_void := eltyp == ' v o i d '
thread_typ := if is_void { ' _ _ v _ t h r e a d ' } else { ' _ _ v _ t h r e a d _ $ eltyp ' }
ret_typ := if is_void { ' v o i d ' } else { ' A r r a y _ $ eltyp ' }
2021-02-22 14:08:52 +01:00
thread_arr_typ := ' A r r a y _ $ thread_typ '
fn_name := ' $ { thread_arr_typ } _ w a i t '
2021-12-19 12:38:48 +01:00
mut should_register := false
lock g . waiter_fns {
if fn_name ! in g . waiter_fns {
g . waiter_fns << fn_name
should_register = true
}
}
if should_register {
2021-02-23 08:37:29 +01:00
if is_void {
2021-10-03 17:24:44 +02:00
g . register_thread_void_wait_call ( )
2021-02-22 14:08:52 +01:00
g . gowrappers . writeln ( '
void $ { fn_name } ( $ thread_arr_typ a ) {
for ( int i = 0 ; i < a . len ; ++ i ) {
$ thread_typ t = ( ( $ thread_typ * ) a . data ) [ i ] ;
2021-11-04 13:24:58 +01:00
if ( t == 0 ) continue ;
2021-02-23 08:37:29 +01:00
__v_thread_wait ( t ) ;
2021-02-22 14:08:52 +01:00
}
} ' )
} else {
g . gowrappers . writeln ( '
$ ret_typ $ { fn_name } ( $ thread_arr_typ a ) {
$ ret_typ res = __new_array_with_default ( a . len , a . len , sizeof ( $ eltyp ) , 0 ) ;
for ( int i = 0 ; i < a . len ; ++ i ) {
2021-11-04 13:24:58 +01:00
$ thread_typ t = ( ( $ thread_typ * ) a . data ) [ i ] ; ' )
if g . pref . os == . windows {
g . gowrappers . writeln ( ' \t \t i f ( t . h a n d l e = = 0 ) c o n t i n u e ; ' )
} else {
g . gowrappers . writeln ( ' \t \t i f ( t = = 0 ) c o n t i n u e ; ' )
}
g . gowrappers . writeln ( ' \t \t ( ( $ eltyp * ) r e s . d a t a ) [ i ] = _ _ v _ t h r e a d _ $ { eltyp } _ w a i t ( t ) ;
2021-02-22 14:08:52 +01:00
}
return res ;
} ' )
}
}
return fn_name
}
2020-10-15 12:00:46 +02:00
fn ( mut g Gen ) register_chan_pop_optional_call ( opt_el_type string , styp string ) {
2021-09-28 09:28:04 +02:00
g . chan_pop_optionals [ opt_el_type ] = styp
}
fn ( mut g Gen ) write_chan_pop_optional_fns ( ) {
mut done := [ ] string { }
for opt_el_type , styp in g . chan_pop_optionals {
if opt_el_type in done {
continue
}
done << opt_el_type
2020-08-23 02:12:05 +02:00
g . channel_definitions . writeln ( '
2021-03-14 01:54:46 +01:00
static inline $ opt_el_type __Option_ $ { styp } _popval ( $ styp ch ) {
2021-02-28 20:24:29 +01:00
$ opt_el_type _tmp = { 0 } ;
2020-08-23 02:12:05 +02:00
if ( sync__Channel_try_pop_priv ( ch , _tmp . data , false ) ) {
2021-07-21 10:46:04 +02:00
return ( $ opt_el_type ) { . state = 2 , . err = _v_error ( _SLIT ( " c h a n n e l c l o s e d " ) ) , . data = { EMPTY_STRUCT_INITIALIZATION } } ;
2020-08-23 02:12:05 +02:00
}
return _tmp ;
} ' )
}
}
2021-09-28 09:28:04 +02:00
fn ( mut g Gen ) register_chan_push_optional_fn ( el_type string , styp string ) {
g . chan_push_optionals [ styp ] = el_type
}
fn ( mut g Gen ) write_chan_push_optional_fns ( ) {
mut done := [ ] string { }
for styp , el_type in g . chan_push_optionals {
if styp in done {
continue
}
done << styp
2021-04-09 17:18:02 +02:00
g . register_optional ( ast . void_type . set_flag ( . optional ) )
2021-01-06 21:19:40 +01:00
g . channel_definitions . writeln ( '
2021-03-14 01:54:46 +01:00
static inline Option_void __Option_ $ { styp } _pushval ( $ styp ch , $ el_type e ) {
2021-01-06 21:19:40 +01:00
if ( sync__Channel_try_push_priv ( ch , & e , false ) ) {
2021-07-21 10:46:04 +02:00
return ( Option_void ) { . state = 2 , . err = _v_error ( _SLIT ( " c h a n n e l c l o s e d " ) ) , . data = { EMPTY_STRUCT_INITIALIZATION } } ;
2021-01-06 21:19:40 +01:00
}
2021-03-14 01:54:46 +01:00
return ( Option_void ) { 0 } ;
2021-01-06 21:19:40 +01:00
} ' )
}
}
2021-03-01 21:36:58 +01:00
// cc_type whether to prefix 'struct' or not (C__Foo -> struct Foo)
2021-04-11 08:02:57 +02:00
fn ( mut g Gen ) cc_type ( typ ast . Type , is_prefix_struct bool ) string {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( g . unwrap_generic ( typ ) )
2020-11-29 14:10:45 +01:00
mut styp := sym . cname
2021-01-23 22:41:32 +01:00
// TODO: this needs to be removed; cgen shouldn't resolve generic types (job of checker)
2021-07-15 07:29:13 +02:00
match mut sym . info {
ast . Struct , ast . Interface , ast . SumType {
if sym . info . is_generic {
mut sgtyps := ' _ T '
for gt in sym . info . generic_types {
2021-12-19 17:25:18 +01:00
gts := g . table . sym ( g . unwrap_generic ( gt ) )
2021-07-15 07:29:13 +02:00
sgtyps += ' _ $ gts . cname '
}
styp += sgtyps
2020-06-29 20:09:09 +02:00
}
}
2021-07-15 07:29:13 +02:00
else { }
2020-06-29 20:09:09 +02:00
}
2021-08-31 22:55:37 +02:00
if is_prefix_struct && sym . language == . c {
2020-03-13 01:43:30 +01:00
styp = styp [ 3 .. ]
2020-03-31 15:18:25 +02:00
if sym . kind == . struct_ {
2021-04-02 00:57:09 +02:00
info := sym . info as ast . Struct
2020-04-04 14:32:42 +02:00
if ! info . is_typedef {
styp = ' s t r u c t $ styp '
}
2020-03-31 15:18:25 +02:00
}
2020-03-13 01:43:30 +01:00
}
2020-03-06 22:36:51 +01:00
return styp
}
2020-11-29 14:10:45 +01:00
[ inline ]
2021-04-02 00:57:09 +02:00
fn ( g & Gen ) type_sidx ( t ast . Type ) string {
2020-11-29 14:10:45 +01:00
if g . pref . build_mode == . build_module {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( t )
2020-11-29 14:10:45 +01:00
return ' _ v _ t y p e _ i d x _ $ { sym . cname } ( ) '
}
2021-09-28 09:28:04 +02:00
return t . idx ( ) . str ( )
2020-11-29 14:10:45 +01:00
}
2020-04-08 13:53:11 +02:00
//
2020-04-21 05:11:50 +02:00
pub fn ( mut g Gen ) write_typedef_types ( ) {
2021-12-01 08:50:53 +01:00
for sym in g . table . type_symbols {
if sym . name in c . builtins {
2021-03-12 19:05:05 +01:00
continue
}
2021-12-01 08:50:53 +01:00
match sym . kind {
2020-03-12 07:56:44 +01:00
. array {
2021-12-01 08:50:53 +01:00
info := sym . info as ast . Array
2021-12-19 17:25:18 +01:00
elem_sym := g . table . sym ( info . elem_type )
2021-11-07 20:24:30 +01:00
if elem_sym . kind != . placeholder && ! info . elem_type . has_flag ( . generic ) {
2021-12-01 08:50:53 +01:00
g . type_definitions . writeln ( ' t y p e d e f a r r a y $ sym . cname ; ' )
2021-07-07 11:42:53 +02:00
}
2020-03-12 07:56:44 +01:00
}
2021-03-31 18:51:03 +02:00
. array_fixed {
2021-12-01 08:50:53 +01:00
info := sym . info as ast . ArrayFixed
2021-12-19 17:25:18 +01:00
elem_sym := g . table . sym ( info . elem_type )
2021-03-31 18:51:03 +02:00
if elem_sym . is_builtin ( ) {
// .array_fixed {
2021-12-01 08:50:53 +01:00
styp := sym . cname
2021-03-31 18:51:03 +02:00
// array_fixed_char_300 => char x[300]
len := styp . after ( ' _ ' )
2022-01-04 10:34:07 +01:00
mut fixed := g . typ ( info . elem_type )
2021-04-02 00:57:09 +02:00
if elem_sym . info is ast . FnType {
2021-03-31 18:51:03 +02:00
pos := g . out . len
g . write_fn_ptr_decl ( & elem_sym . info , ' ' )
2021-08-10 20:27:15 +02:00
fixed = g . out . cut_to ( pos )
2021-03-31 18:51:03 +02:00
mut def_str := ' t y p e d e f $ fixed ; '
def_str = def_str . replace_once ( ' ( * ) ' , ' ( * $ styp [ $ len ] ) ' )
g . type_definitions . writeln ( def_str )
} else {
g . type_definitions . writeln ( ' t y p e d e f $ fixed $ styp [ $ len ] ; ' )
}
}
}
2020-08-14 21:18:42 +02:00
. chan {
2021-12-01 08:50:53 +01:00
if sym . name != ' c h a n ' {
g . type_definitions . writeln ( ' t y p e d e f c h a n $ sym . cname ; ' )
chan_inf := sym . chan_info ( )
2021-01-18 18:37:45 +01:00
chan_elem_type := chan_inf . elem_type
if ! chan_elem_type . has_flag ( . generic ) {
el_stype := g . typ ( chan_elem_type )
g . channel_definitions . writeln ( '
2021-12-01 08:50:53 +01:00
static inline $ el_stype __ $ { sym . cname } _popval ( $ sym . cname ch ) {
2020-08-17 23:35:36 +02:00
$ el_stype val ;
sync__Channel_try_pop_priv ( ch , & val , false ) ;
return val ;
} ' )
2021-01-18 18:37:45 +01:00
g . channel_definitions . writeln ( '
2021-12-01 08:50:53 +01:00
static inline void __ $ { sym . cname } _pushval ( $ sym . cname ch , $ el_stype val ) {
2020-08-17 23:35:36 +02:00
sync__Channel_try_push_priv ( ch , & val , false ) ;
} ' )
2021-01-18 18:37:45 +01:00
}
2020-08-17 23:35:36 +02:00
}
2020-08-14 21:18:42 +02:00
}
2020-03-12 07:56:44 +01:00
. map {
2021-12-01 08:50:53 +01:00
g . type_definitions . writeln ( ' t y p e d e f m a p $ sym . cname ; ' )
2020-03-12 07:56:44 +01:00
}
else {
continue
}
2020-04-05 18:03:36 +02:00
}
2020-03-06 13:43:22 +01:00
}
2021-12-01 08:50:53 +01:00
for sym in g . table . type_symbols {
if sym . kind == . alias && sym . name ! in c . builtins {
g . write_alias_typesymbol_declaration ( sym )
2021-05-31 11:26:06 +02:00
}
}
2021-12-01 08:50:53 +01:00
for sym in g . table . type_symbols {
if sym . kind == . function && sym . name ! in c . builtins {
g . write_fn_typesymbol_declaration ( sym )
2021-06-01 11:37:27 +02:00
}
}
2021-04-11 23:43:19 +02:00
// Generating interfaces after all the common types have been defined
// to prevent generating interface struct before definition of field types
2021-12-01 08:50:53 +01:00
for sym in g . table . type_symbols {
if sym . kind == . interface_ && sym . name ! in c . builtins {
g . write_interface_typedef ( sym )
2021-07-19 18:57:12 +02:00
}
}
2021-12-01 08:50:53 +01:00
for sym in g . table . type_symbols {
if sym . kind == . interface_ && sym . name ! in c . builtins {
g . write_interface_typesymbol_declaration ( sym )
2021-04-11 23:43:19 +02:00
}
}
2020-03-06 13:43:22 +01:00
}
2021-05-31 11:26:06 +02:00
pub fn ( mut g Gen ) write_alias_typesymbol_declaration ( sym ast . TypeSymbol ) {
2021-12-01 08:50:53 +01:00
parent := g . table . type_symbols [ sym . parent_idx ]
2021-05-31 11:26:06 +02:00
is_c_parent := parent . name . len > 2 && parent . name [ 0 ] == ` C ` && parent . name [ 1 ] == ` . `
mut is_typedef := false
2022-01-06 10:54:22 +01:00
mut is_fixed_array_of_non_builtin := false
2021-05-31 11:26:06 +02:00
if parent . info is ast . Struct {
is_typedef = parent . info . is_typedef
}
mut parent_styp := parent . cname
if is_c_parent {
if ! is_typedef {
parent_styp = ' s t r u c t ' + parent . cname [ 3 .. ]
} else {
parent_styp = parent . cname [ 3 .. ]
}
} else {
if sym . info is ast . Alias {
parent_styp = g . typ ( sym . info . parent_type )
2022-01-06 10:54:22 +01:00
parent_sym := g . table . sym ( sym . info . parent_type )
if parent_sym . info is ast . ArrayFixed {
elem_sym := g . table . sym ( parent_sym . info . elem_type )
if ! elem_sym . is_builtin ( ) {
is_fixed_array_of_non_builtin = true
}
}
2021-05-31 11:26:06 +02:00
}
}
2021-07-29 08:54:52 +02:00
if parent_styp == ' b y t e ' && sym . cname == ' u 8 ' {
// TODO: remove this check; it is here just to fix V rebuilding in -cstrict mode with clang-12
return
}
2022-01-06 10:54:22 +01:00
if is_fixed_array_of_non_builtin {
g . alias_definitions . writeln ( ' t y p e d e f $ parent_styp $ sym . cname ; ' )
} else {
g . type_definitions . writeln ( ' t y p e d e f $ parent_styp $ sym . cname ; ' )
}
2021-05-31 11:26:06 +02:00
}
2021-07-19 18:57:12 +02:00
pub fn ( mut g Gen ) write_interface_typedef ( sym ast . TypeSymbol ) {
struct_name := c_name ( sym . cname )
2021-08-20 00:12:31 +02:00
g . typedefs . writeln ( ' t y p e d e f s t r u c t $ struct_name $ struct_name ; ' )
2021-07-19 18:57:12 +02:00
}
2021-04-02 00:57:09 +02:00
pub fn ( mut g Gen ) write_interface_typesymbol_declaration ( sym ast . TypeSymbol ) {
2021-10-23 11:53:53 +02:00
if sym . info ! is ast . Interface {
return
}
2021-04-02 00:57:09 +02:00
info := sym . info as ast . Interface
2021-07-15 07:29:13 +02:00
if info . is_generic {
return
}
struct_name := c_name ( sym . cname )
2021-06-28 09:24:05 +02:00
g . type_definitions . writeln ( ' s t r u c t $ struct_name { ' )
2021-04-09 10:00:05 +02:00
g . type_definitions . writeln ( ' \t u n i o n { ' )
g . type_definitions . writeln ( ' \t \t v o i d * _ o b j e c t ; ' )
for variant in info . types {
2021-12-19 17:25:18 +01:00
vcname := g . table . sym ( variant ) . cname
2021-04-09 10:00:05 +02:00
g . type_definitions . writeln ( ' \t \t $ vcname * _ $ vcname ; ' )
}
g . type_definitions . writeln ( ' \t } ; ' )
g . type_definitions . writeln ( ' \t i n t _ t y p ; ' )
2021-03-12 19:05:05 +01:00
for field in info . fields {
styp := g . typ ( field . typ )
cname := c_name ( field . name )
g . type_definitions . writeln ( ' \t $ styp * $ cname ; ' )
}
2021-06-28 09:24:05 +02:00
g . type_definitions . writeln ( ' } ; ' )
2021-03-12 19:05:05 +01:00
}
2021-04-02 00:57:09 +02:00
pub fn ( mut g Gen ) write_fn_typesymbol_declaration ( sym ast . TypeSymbol ) {
info := sym . info as ast . FnType
2020-10-11 08:53:35 +02:00
func := info . func
is_fn_sig := func . name == ' '
not_anon := ! info . is_anon
2021-04-27 14:25:42 +02:00
mut has_generic_arg := false
for param in func . params {
if param . typ . has_flag ( . generic ) {
has_generic_arg = true
break
}
}
if ! info . has_decl && ( not_anon || is_fn_sig ) && ! func . return_type . has_flag ( . generic )
&& ! has_generic_arg {
2020-12-06 04:55:08 +01:00
fn_name := sym . cname
2021-02-22 12:18:11 +01:00
g . type_definitions . write_string ( ' t y p e d e f $ { g . typ ( func . return_type ) } ( * $ fn_name ) ( ' )
2020-10-11 08:53:35 +02:00
for i , param in func . params {
2021-02-22 12:18:11 +01:00
g . type_definitions . write_string ( g . typ ( param . typ ) )
2020-10-11 08:53:35 +02:00
if i < func . params . len - 1 {
2021-02-22 12:18:11 +01:00
g . type_definitions . write_string ( ' , ' )
2020-10-11 08:53:35 +02:00
}
}
g . type_definitions . writeln ( ' ) ; ' )
}
}
2020-12-31 21:45:03 +01:00
pub fn ( mut g Gen ) write_multi_return_types ( ) {
g . typedefs . writeln ( ' \n / / B E G I N _ m u l t i _ r e t u r n _ t y p e d e f s ' )
2020-10-11 08:53:35 +02:00
g . type_definitions . writeln ( ' \n / / B E G I N _ m u l t i _ r e t u r n _ s t r u c t s ' )
2021-03-19 21:49:46 +01:00
for sym in g . table . type_symbols {
2020-12-31 11:55:21 +01:00
if sym . kind != . multi_return {
2020-03-04 20:17:29 +01:00
continue
}
2021-01-23 22:41:32 +01:00
info := sym . mr_info ( )
if info . types . filter ( it . has_flag ( . generic ) ) . len > 0 {
continue
}
2020-12-31 21:45:03 +01:00
g . typedefs . writeln ( ' t y p e d e f s t r u c t $ sym . cname $ sym . cname ; ' )
2020-12-31 11:55:21 +01:00
g . type_definitions . writeln ( ' s t r u c t $ sym . cname { ' )
for i , mr_typ in info . types {
type_name := g . typ ( mr_typ )
g . type_definitions . writeln ( ' \t $ type_name a r g $ i ; ' )
}
g . type_definitions . writeln ( ' } ; \n ' )
2020-03-04 20:17:29 +01:00
}
2020-12-31 21:45:03 +01:00
g . typedefs . writeln ( ' / / E N D _ m u l t i _ r e t u r n _ t y p e d e f s \n ' )
2020-10-11 08:53:35 +02:00
g . type_definitions . writeln ( ' / / E N D _ m u l t i _ r e t u r n _ s t r u c t s \n ' )
2019-12-24 18:54:43 +01:00
}
2020-04-21 05:11:50 +02:00
pub fn ( mut g Gen ) write ( s string ) {
2020-07-20 11:36:27 +02:00
$ if trace_gen ? {
eprintln ( ' g e n f i l e : $ { g . file . path : - 30 } | l a s t _ f n _ c _ n a m e : $ { g . last_fn_c_name : - 45 } | w r i t e : $ s ' )
}
2020-03-22 10:12:43 +01:00
if g . indent > 0 && g . empty_line {
2021-03-09 12:03:25 +01:00
g . out . write_string ( util . tabs ( g . indent ) )
2020-03-22 10:12:43 +01:00
}
2021-02-22 12:18:11 +01:00
g . out . write_string ( s )
2020-03-22 10:12:43 +01:00
g . empty_line = false
2019-12-24 18:54:43 +01:00
}
2020-04-21 05:11:50 +02:00
pub fn ( mut g Gen ) writeln ( s string ) {
2020-07-20 11:36:27 +02:00
$ if trace_gen ? {
eprintln ( ' g e n f i l e : $ { g . file . path : - 30 } | l a s t _ f n _ c _ n a m e : $ { g . last_fn_c_name : - 45 } | w r i t e l n : $ s ' )
}
2020-03-22 10:12:43 +01:00
if g . indent > 0 && g . empty_line {
2021-03-09 12:03:25 +01:00
g . out . write_string ( util . tabs ( g . indent ) )
2020-03-22 10:12:43 +01:00
}
2019-12-24 18:54:43 +01:00
g . out . writeln ( s )
2020-03-22 10:12:43 +01:00
g . empty_line = true
2019-12-24 18:54:43 +01:00
}
2020-04-21 05:11:50 +02:00
pub fn ( mut g Gen ) new_tmp_var ( ) string {
2020-02-18 03:17:21 +01:00
g . tmp_count ++
2020-05-16 16:12:23 +02:00
return ' _ t $ g . tmp_count '
2020-02-18 03:17:21 +01:00
}
2021-07-23 07:55:55 +02:00
pub fn ( mut g Gen ) new_global_tmp_var ( ) string {
g . global_tmp_count ++
return ' _ t $ g . global_tmp_count '
}
2021-07-06 13:40:20 +02:00
pub fn ( mut g Gen ) new_tmp_declaration_name ( ) string {
g . tmp_count_declarations ++
return ' _ d $ g . tmp_count_declarations '
}
2020-12-04 11:44:16 +01:00
pub fn ( mut g Gen ) current_tmp_var ( ) string {
return ' _ t $ g . tmp_count '
}
2020-09-05 12:00:35 +02:00
/ *
pub fn ( mut g Gen ) new_tmp_var2 ( ) string {
2021-12-19 13:31:04 +01:00
g . tmp_count_af ++
return ' _ t t $ g . tmp_count_af '
2020-09-05 12:00:35 +02:00
}
* /
2020-04-21 05:11:50 +02:00
pub fn ( mut g Gen ) reset_tmp_count ( ) {
2020-02-18 03:17:21 +01:00
g . tmp_count = 0
}
2020-05-14 17:15:25 +02:00
fn ( mut g Gen ) decrement_inside_ternary ( ) {
key := g . inside_ternary . str ( )
for name in g . ternary_level_names [ key ] {
g . ternary_names . delete ( name )
}
g . ternary_level_names . delete ( key )
g . inside_ternary --
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) stmts ( stmts [ ] ast . Stmt ) {
2020-10-18 15:55:04 +02:00
g . stmts_with_tmp_var ( stmts , ' ' )
}
2021-08-29 10:57:11 +02:00
fn is_noreturn_callexpr ( expr ast . Expr ) bool {
if expr is ast . CallExpr {
return expr . is_noreturn
}
return false
}
2020-10-29 01:09:38 +01:00
// tmp_var is used in `if` expressions only
2020-10-18 15:55:04 +02:00
fn ( mut g Gen ) stmts_with_tmp_var ( stmts [ ] ast . Stmt , tmp_var string ) {
2020-03-22 10:12:43 +01:00
g . indent ++
2020-05-11 15:11:48 +02:00
if g . inside_ternary > 0 {
2020-06-05 08:30:03 +02:00
g . write ( ' ( ' )
2020-04-13 12:05:38 +02:00
}
for i , stmt in stmts {
2020-10-18 15:55:04 +02:00
if i == stmts . len - 1 && tmp_var != ' ' {
2020-10-18 21:22:37 +02:00
// Handle if expressions, set the value of the last expression to the temp var.
2021-12-02 11:22:48 +01:00
if g . inside_if_optional || g . inside_match_optional {
2021-09-26 14:51:18 +02:00
g . set_current_pos_as_last_stmt_pos ( )
2021-03-01 13:56:07 +01:00
g . skip_stmt_pos = true
if stmt is ast . ExprStmt {
2021-04-02 00:57:09 +02:00
if stmt . typ == ast . error_type_idx || stmt . expr is ast . None {
2021-03-23 21:45:08 +01:00
g . writeln ( ' $ { tmp_var } . s t a t e = 2 ; ' )
g . write ( ' $ { tmp_var } . e r r = ' )
2021-03-01 13:56:07 +01:00
g . expr ( stmt . expr )
2021-03-23 21:45:08 +01:00
g . writeln ( ' ; ' )
2021-03-01 13:56:07 +01:00
} else {
mut styp := g . base_type ( stmt . typ )
$ if tinyc && x32 && windows {
2021-04-02 00:57:09 +02:00
if stmt . typ == ast . int_literal_type {
2021-03-01 13:56:07 +01:00
styp = ' i n t '
2021-04-02 00:57:09 +02:00
} else if stmt . typ == ast . float_literal_type {
2021-03-01 13:56:07 +01:00
styp = ' f 6 4 '
}
}
2021-03-14 01:54:46 +01:00
g . write ( ' o p t _ o k ( & ( $ styp [ ] ) { ' )
2021-03-01 13:56:07 +01:00
g . stmt ( stmt )
2021-03-14 01:54:46 +01:00
g . writeln ( ' } , ( O p t i o n * ) ( & $ tmp_var ) , s i z e o f ( $ styp ) ) ; ' )
2021-03-01 13:56:07 +01:00
}
}
} else {
2021-09-26 14:51:18 +02:00
g . set_current_pos_as_last_stmt_pos ( )
2021-03-01 13:56:07 +01:00
g . skip_stmt_pos = true
2021-08-29 10:57:11 +02:00
mut is_noreturn := false
if stmt is ast . ExprStmt {
is_noreturn = is_noreturn_callexpr ( stmt . expr )
}
if ! is_noreturn {
g . write ( ' $ tmp_var = ' )
}
2021-03-01 13:56:07 +01:00
g . stmt ( stmt )
2021-06-12 20:14:08 +02:00
if ! g . out . last_n ( 2 ) . contains ( ' ; ' ) {
g . writeln ( ' ; ' )
}
2021-03-01 13:56:07 +01:00
}
} else {
g . stmt ( stmt )
2021-12-02 11:22:48 +01:00
if ( g . inside_if_optional || g . inside_match_optional ) && stmt is ast . ExprStmt {
2021-03-01 21:39:04 +01:00
g . writeln ( ' ; ' )
}
2020-10-18 15:55:04 +02:00
}
2020-10-24 19:06:16 +02:00
g . skip_stmt_pos = false
2020-05-11 15:11:48 +02:00
if g . inside_ternary > 0 && i < stmts . len - 1 {
2020-06-05 08:30:03 +02:00
g . write ( ' , ' )
2020-04-13 12:05:38 +02:00
}
}
2020-05-14 17:15:25 +02:00
g . indent --
2020-05-11 15:11:48 +02:00
if g . inside_ternary > 0 {
2020-06-05 08:30:03 +02:00
g . write ( ' ' )
2020-05-14 17:15:25 +02:00
g . write ( ' ) ' )
2020-02-07 14:49:14 +01:00
}
2021-01-08 13:56:55 +01:00
if g . is_autofree && ! g . inside_vweb_tmpl && stmts . len > 0 {
2020-07-16 12:06:50 +02:00
// use the first stmt to get the scope
stmt := stmts [ 0 ]
// stmt := stmts[stmts.len-1]
2020-11-16 17:26:54 +01:00
if stmt ! is ast . FnDecl && g . inside_ternary == 0 {
2021-05-11 18:29:06 +02:00
// g.trace_autofree('// autofree scope')
// g.trace_autofree('// autofree_scope_vars($stmt.pos.pos) | ${typeof(stmt)}')
2020-07-16 19:40:14 +02:00
// go back 1 position is important so we dont get the
2020-07-16 12:06:50 +02:00
// internal scope of for loops and possibly other nodes
2021-03-11 13:50:02 +01:00
// g.autofree_scope_vars(stmt.pos.pos - 1)
mut stmt_pos := stmt . pos
2020-12-01 18:58:16 +01:00
if stmt_pos . pos == 0 {
// Do not autofree if the position is 0, since the correct scope won't be found.
// Report a bug, since position shouldn't be 0 for most nodes.
if stmt is ast . Module {
return
}
if stmt is ast . ExprStmt {
// For some reason ExprStmt.pos is 0 when ExprStmt.expr is comp if expr
// Extract the pos. TODO figure out why and fix.
stmt_pos = stmt . expr . position ( )
}
if stmt_pos . pos == 0 {
2021-03-17 11:31:36 +01:00
$ if trace_autofree ? {
println ( ' a u t o f r e e : f i r s t s t m t p o s = 0 . $ stmt . type_name ( ) ' )
}
2020-12-01 18:58:16 +01:00
return
}
}
2020-12-02 03:45:08 +01:00
g . autofree_scope_vars ( stmt_pos . pos - 1 , stmt_pos . line_nr , false )
2020-07-14 14:50:51 +02:00
}
2020-07-14 13:56:50 +02:00
}
2020-02-07 14:49:14 +01:00
}
2020-08-11 23:08:19 +02:00
[ inline ]
fn ( mut g Gen ) write_v_source_line_info ( pos token . Position ) {
if g . inside_ternary == 0 && g . pref . is_vlines && g . is_vlines_enabled {
nline := pos . line_nr + 1
lineinfo := ' \n # l i n e $ nline " $ g . vlines_path " '
g . writeln ( lineinfo )
}
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) stmt ( node ast . Stmt ) {
2020-10-24 19:06:16 +02:00
if ! g . skip_stmt_pos {
2021-09-26 14:51:18 +02:00
g . set_current_pos_as_last_stmt_pos ( )
2020-10-24 19:06:16 +02:00
}
2020-06-29 17:47:01 +02:00
defer {
}
2021-02-02 15:41:51 +01:00
// println('g.stmt()')
2020-01-06 16:13:12 +01:00
// g.writeln('//// stmt start')
2020-11-25 12:09:40 +01:00
match node {
2021-03-31 10:13:15 +02:00
ast . EmptyStmt { }
2021-03-17 01:43:17 +01:00
ast . AsmStmt {
g . write_v_source_line_info ( node . pos )
g . gen_asm_stmt ( node )
}
2020-03-02 18:43:41 +01:00
ast . AssertStmt {
2020-08-11 23:08:19 +02:00
g . write_v_source_line_info ( node . pos )
2020-06-18 20:21:08 +02:00
g . gen_assert_stmt ( node )
2020-03-26 10:27:46 +01:00
}
ast . AssignStmt {
2020-08-11 23:08:19 +02:00
g . write_v_source_line_info ( node . pos )
2020-06-18 20:07:48 +02:00
g . gen_assign_stmt ( node )
2020-03-02 18:43:41 +01:00
}
2020-03-25 14:24:48 +01:00
ast . Block {
2021-04-16 18:13:35 +02:00
g . write_v_source_line_info ( node . pos )
2020-08-02 13:06:44 +02:00
if node . is_unsafe {
g . writeln ( ' { / / U n s a f e b l o c k ' )
} else {
g . writeln ( ' { ' )
}
2020-06-18 20:21:08 +02:00
g . stmts ( node . stmts )
2020-03-25 14:24:48 +01:00
g . writeln ( ' } ' )
}
2020-03-02 18:43:41 +01:00
ast . BranchStmt {
2020-11-04 12:34:12 +01:00
g . write_v_source_line_info ( node . pos )
2021-05-12 10:44:47 +02:00
2020-12-05 05:06:47 +01:00
if node . label != ' ' {
2021-05-12 10:44:47 +02:00
x := g . labeled_loops [ node . label ] or {
panic ( ' $ node . label d o e s n \' t e x i s t $ g . file . path , $ node . pos ' )
}
match x {
ast . ForCStmt {
if x . scope . contains ( g . cur_lock . pos . pos ) {
g . unlock_locks ( )
}
}
ast . ForInStmt {
if x . scope . contains ( g . cur_lock . pos . pos ) {
g . unlock_locks ( )
}
}
ast . ForStmt {
if x . scope . contains ( g . cur_lock . pos . pos ) {
g . unlock_locks ( )
}
}
else { }
}
2020-11-20 12:23:48 +01:00
if node . kind == . key_break {
2020-11-27 03:17:07 +01:00
g . writeln ( ' g o t o $ { node . label } _ _ b r e a k ; ' )
2020-11-20 12:23:48 +01:00
} else {
// assert node.kind == .key_continue
2020-11-27 03:17:07 +01:00
g . writeln ( ' g o t o $ { node . label } _ _ c o n t i n u e ; ' )
2020-11-20 12:23:48 +01:00
}
} else {
2021-05-12 10:44:47 +02:00
inner_loop := g . inner_loop
match inner_loop {
ast . ForCStmt {
if inner_loop . scope . contains ( g . cur_lock . pos . pos ) {
g . unlock_locks ( )
}
}
ast . ForInStmt {
if inner_loop . scope . contains ( g . cur_lock . pos . pos ) {
g . unlock_locks ( )
}
}
ast . ForStmt {
if inner_loop . scope . contains ( g . cur_lock . pos . pos ) {
g . unlock_locks ( )
}
}
else { }
}
2020-11-20 12:23:48 +01:00
// continue or break
2021-01-08 13:56:55 +01:00
if g . is_autofree && ! g . is_builtin_mod {
2021-05-11 18:29:06 +02:00
g . trace_autofree ( ' / / f r e e b e f o r e c o n t i n u e / b r e a k ' )
2020-12-05 16:36:27 +01:00
g . autofree_scope_vars_stop ( node . pos . pos - 1 , node . pos . line_nr , true ,
2020-12-06 08:02:36 +01:00
g . branch_parent_pos )
2020-12-05 05:06:47 +01:00
}
2020-11-20 12:23:48 +01:00
g . writeln ( ' $ node . kind ; ' )
}
2020-03-02 18:43:41 +01:00
}
2020-02-03 07:02:54 +01:00
ast . ConstDecl {
2020-08-11 23:08:19 +02:00
g . write_v_source_line_info ( node . pos )
2020-04-10 18:11:43 +02:00
// if g.pref.build_mode != .build_module {
2020-06-18 20:21:08 +02:00
g . const_decl ( node )
2020-04-10 18:11:43 +02:00
// }
2020-02-03 07:02:54 +01:00
}
2021-11-17 07:29:43 +01:00
ast . ComptimeFor {
2021-11-15 14:47:29 +01:00
g . comptime_for ( node )
2020-07-03 15:10:39 +02:00
}
2020-03-04 15:48:43 +01:00
ast . DeferStmt {
2020-11-24 13:58:29 +01:00
mut defer_stmt := node
2020-03-27 07:21:22 +01:00
defer_stmt . ifdef = g . defer_ifdef
2021-02-06 17:40:39 +01:00
g . writeln ( ' $ { g . defer_flag_var ( defer_stmt ) } = t r u e ; ' )
2020-03-27 07:21:22 +01:00
g . defer_stmts << defer_stmt
2020-03-04 15:48:43 +01:00
}
2020-02-25 11:52:41 +01:00
ast . EnumDecl {
2020-07-01 00:53:53 +02:00
enum_name := util . no_dots ( node . name )
2020-11-27 22:09:14 +01:00
is_flag := node . is_flag
2020-06-10 17:01:55 +02:00
g . enum_typedefs . writeln ( ' t y p e d e f e n u m { ' )
2020-04-21 12:26:46 +02:00
mut cur_enum_expr := ' '
mut cur_enum_offset := 0
2020-11-27 22:09:14 +01:00
for i , field in node . fields {
2021-06-27 18:05:32 +02:00
g . enum_typedefs . write_string ( ' \t $ { enum_name } _ _ $ field . name ' )
2020-04-10 14:44:01 +02:00
if field . has_expr {
2021-02-22 12:18:11 +01:00
g . enum_typedefs . write_string ( ' = ' )
2021-03-08 11:46:39 +01:00
expr_str := g . expr_string ( field . expr )
2021-02-22 12:18:11 +01:00
g . enum_typedefs . write_string ( expr_str )
2020-04-21 12:26:46 +02:00
cur_enum_expr = expr_str
cur_enum_offset = 0
2020-11-27 22:09:14 +01:00
} else if is_flag {
2021-02-22 12:18:11 +01:00
g . enum_typedefs . write_string ( ' = ' )
2020-11-27 22:09:14 +01:00
cur_enum_expr = ' 1 < < $ i '
2021-02-22 12:18:11 +01:00
g . enum_typedefs . write_string ( ( 1 << i ) . str ( ) )
2020-11-27 22:09:14 +01:00
cur_enum_offset = 0
2020-03-31 19:43:11 +02:00
}
2020-12-27 14:20:30 +01:00
cur_value := if cur_enum_offset > 0 {
' $ cur_enum_expr + $ cur_enum_offset '
} else {
cur_enum_expr
}
2020-06-18 20:21:08 +02:00
g . enum_typedefs . writeln ( ' , / / $ cur_value ' )
2020-04-21 12:26:46 +02:00
cur_enum_offset ++
2020-02-25 11:52:41 +01:00
}
2020-06-18 20:21:08 +02:00
g . enum_typedefs . writeln ( ' } $ enum_name ; \n ' )
2020-02-25 11:52:41 +01:00
}
2020-03-02 18:43:41 +01:00
ast . ExprStmt {
2020-08-11 23:08:19 +02:00
g . write_v_source_line_info ( node . pos )
2021-01-08 13:56:55 +01:00
// af := g.autofree && node.expr is ast.CallExpr && !g.is_builtin_mod
2020-10-18 00:48:06 +02:00
// if af {
// g.autofree_call_pregen(node.expr as ast.CallExpr)
// }
2021-02-28 23:21:03 +01:00
old_is_void_expr_stmt := g . is_void_expr_stmt
g . is_void_expr_stmt = ! node . is_expr
2021-10-29 16:14:49 +02:00
if node . typ != ast . void_type && g . expected_cast_type != 0 && node . expr ! is ast . MatchExpr {
2021-05-27 09:13:50 +02:00
g . expr_with_cast ( node . expr , node . typ , g . expected_cast_type )
} else {
g . expr ( node . expr )
}
2021-02-28 23:21:03 +01:00
g . is_void_expr_stmt = old_is_void_expr_stmt
2020-10-18 00:48:06 +02:00
// if af {
// g.autofree_call_postgen()
// }
2021-12-02 11:22:48 +01:00
if g . inside_ternary == 0 && ! g . inside_if_optional && ! g . inside_match_optional
&& ! node . is_expr && node . expr ! is ast . IfExpr {
2020-05-14 17:15:25 +02:00
g . writeln ( ' ; ' )
2020-04-05 18:03:36 +02:00
}
2020-03-02 18:43:41 +01:00
}
2019-12-28 14:11:05 +01:00
ast . FnDecl {
2021-09-30 11:51:32 +02:00
g . fn_decl ( node )
2019-12-28 14:11:05 +01:00
}
2020-03-02 18:43:41 +01:00
ast . ForCStmt {
2020-12-07 02:08:09 +01:00
prev_branch_parent_pos := g . branch_parent_pos
g . branch_parent_pos = node . pos . pos
2021-05-12 10:44:47 +02:00
save_inner_loop := g . inner_loop
g . inner_loop = unsafe { & node }
if node . label != ' ' {
g . labeled_loops [ node . label ] = unsafe { & node }
}
2020-08-11 23:08:19 +02:00
g . write_v_source_line_info ( node . pos )
2021-02-24 13:58:45 +01:00
g . for_c_stmt ( node )
2020-12-07 02:08:09 +01:00
g . branch_parent_pos = prev_branch_parent_pos
2021-05-12 10:44:47 +02:00
g . labeled_loops . delete ( node . label )
g . inner_loop = save_inner_loop
2020-03-02 18:43:41 +01:00
}
ast . ForInStmt {
2020-12-07 02:08:09 +01:00
prev_branch_parent_pos := g . branch_parent_pos
g . branch_parent_pos = node . pos . pos
2021-05-12 10:44:47 +02:00
save_inner_loop := g . inner_loop
g . inner_loop = unsafe { & node }
if node . label != ' ' {
g . labeled_loops [ node . label ] = unsafe { & node }
}
2020-08-11 23:08:19 +02:00
g . write_v_source_line_info ( node . pos )
2021-02-24 13:58:45 +01:00
g . for_in_stmt ( node )
2020-12-07 02:08:09 +01:00
g . branch_parent_pos = prev_branch_parent_pos
2021-05-12 10:44:47 +02:00
g . labeled_loops . delete ( node . label )
g . inner_loop = save_inner_loop
2020-03-02 18:43:41 +01:00
}
ast . ForStmt {
2020-12-06 08:02:36 +01:00
prev_branch_parent_pos := g . branch_parent_pos
g . branch_parent_pos = node . pos . pos
2021-05-12 10:44:47 +02:00
save_inner_loop := g . inner_loop
g . inner_loop = unsafe { & node }
if node . label != ' ' {
g . labeled_loops [ node . label ] = unsafe { & node }
}
2020-08-11 23:08:19 +02:00
g . write_v_source_line_info ( node . pos )
2021-02-24 13:58:45 +01:00
g . for_stmt ( node )
2020-12-06 08:02:36 +01:00
g . branch_parent_pos = prev_branch_parent_pos
2021-05-12 10:44:47 +02:00
g . labeled_loops . delete ( node . label )
g . inner_loop = save_inner_loop
2020-03-02 18:43:41 +01:00
}
ast . GlobalDecl {
2020-10-03 07:03:44 +02:00
g . global_decl ( node )
2020-03-02 18:43:41 +01:00
}
2020-03-04 15:48:43 +01:00
ast . GotoLabel {
2021-12-11 18:41:44 +01:00
g . writeln ( ' $ { c_name ( node . name ) } : { } ' )
2020-03-04 15:48:43 +01:00
}
2020-03-25 17:24:55 +01:00
ast . GotoStmt {
2021-04-16 18:13:35 +02:00
g . write_v_source_line_info ( node . pos )
2021-12-11 18:41:44 +01:00
g . writeln ( ' g o t o $ { c_name ( node . name ) } ; ' )
2020-03-25 17:24:55 +01:00
}
2020-03-02 18:43:41 +01:00
ast . HashStmt {
2021-07-23 14:30:51 +02:00
mut ct_condition := ' '
if node . ct_conds . len > 0 {
ct_condition_start := g . out . len
for idx , ct_expr in node . ct_conds {
2021-11-15 14:47:29 +01:00
g . comptime_if_cond ( ct_expr , false )
2021-07-23 14:30:51 +02:00
if idx < node . ct_conds . len - 1 {
g . write ( ' & & ' )
}
}
ct_condition = g . out . cut_to ( ct_condition_start ) . trim_space ( )
// dump(node)
// dump(ct_condition)
}
2020-03-05 00:43:02 +01:00
// #include etc
2020-10-15 10:58:01 +02:00
if node . kind == ' i n c l u d e ' {
2020-10-17 17:27:06 +02:00
mut missing_message := ' H e a d e r f i l e $ node . main , n e e d e d f o r m o d u l e ` $ node . mod ` w a s n o t f o u n d . '
if node . msg != ' ' {
missing_message += ' $ { node . msg } . '
} else {
missing_message += ' P l e a s e i n s t a l l t h e c o r r e s p o n d i n g d e v e l o p m e n t h e a d e r s . '
}
2020-10-19 11:18:22 +02:00
mut guarded_include := get_guarded_include_text ( node . main , missing_message )
2020-10-17 17:27:06 +02:00
if node . main == ' < e r r n o . h > ' {
// fails with musl-gcc and msvc; but an unguarded include works:
guarded_include = ' # i n c l u d e $ node . main '
}
if node . main . contains ( ' . m ' ) {
2021-07-23 14:30:51 +02:00
g . definitions . writeln ( ' \n ' )
if ct_condition . len > 0 {
g . definitions . writeln ( ' # i f $ ct_condition ' )
}
2020-08-16 19:16:59 +02:00
// Objective C code import, include it after V types, so that e.g. `string` is
// available there
2021-07-23 14:30:51 +02:00
g . definitions . writeln ( ' / / a d d e d b y m o d u l e ` $ node . mod ` ' )
2020-10-17 17:27:06 +02:00
g . definitions . writeln ( guarded_include )
2021-07-23 14:30:51 +02:00
if ct_condition . len > 0 {
g . definitions . writeln ( ' # e n d i f / / \$ i f $ ct_condition ' )
}
g . definitions . writeln ( ' \n ' )
2020-08-16 19:16:59 +02:00
} else {
2021-07-23 14:30:51 +02:00
g . includes . writeln ( ' \n ' )
if ct_condition . len > 0 {
g . includes . writeln ( ' # i f $ ct_condition ' )
}
g . includes . writeln ( ' / / a d d e d b y m o d u l e ` $ node . mod ` ' )
2020-10-17 17:27:06 +02:00
g . includes . writeln ( guarded_include )
2021-07-23 14:30:51 +02:00
if ct_condition . len > 0 {
g . includes . writeln ( ' # e n d i f / / \$ i f $ ct_condition ' )
}
g . includes . writeln ( ' \n ' )
2020-08-16 19:16:59 +02:00
}
2020-10-15 10:58:01 +02:00
} else if node . kind == ' d e f i n e ' {
2021-07-23 14:30:51 +02:00
if ct_condition . len > 0 {
g . includes . writeln ( ' # i f $ ct_condition ' )
}
g . includes . writeln ( ' / / d e f i n e d b y m o d u l e ` $ node . mod ` ' )
2020-10-17 17:27:06 +02:00
g . includes . writeln ( ' # d e f i n e $ node . main ' )
2021-07-23 14:30:51 +02:00
if ct_condition . len > 0 {
g . includes . writeln ( ' # e n d i f / / \$ i f $ ct_condition ' )
}
2020-03-15 00:46:08 +01:00
}
2020-03-02 18:43:41 +01:00
}
ast . Import { }
2020-03-31 19:59:38 +02:00
ast . InterfaceDecl {
2020-04-29 13:08:39 +02:00
// definitions are sorted and added in write_types
2021-12-19 17:25:18 +01:00
ts := g . table . sym ( node . typ )
2021-07-15 21:15:25 +02:00
if ! ( ts . info as ast . Interface ) . is_generic {
for method in node . methods {
if method . return_type . has_flag ( . optional ) {
// Register an optional if it's not registered yet
g . register_optional ( method . return_type )
}
2021-06-23 14:14:08 +02:00
}
}
2020-03-31 19:59:38 +02:00
}
2020-04-28 13:15:37 +02:00
ast . Module {
2020-09-05 12:00:35 +02:00
// g.is_builtin_mod = node.name == 'builtin'
2022-01-06 13:10:37 +01:00
g . is_builtin_mod = node . name in [ ' b u i l t i n ' , ' s t r c o n v ' , ' s t r i n g s ' , ' d l m a l l o c ' ]
2020-12-08 19:52:29 +01:00
// g.cur_mod = node.name
g . cur_mod = node
2020-04-28 13:15:37 +02:00
}
2021-03-30 09:33:29 +02:00
ast . NodeError { }
2019-12-28 14:11:05 +01:00
ast . Return {
2021-04-10 10:55:34 +02:00
g . return_stmt ( node )
2019-12-28 14:11:05 +01:00
}
2020-06-24 14:44:06 +02:00
ast . SqlStmt {
2020-06-25 14:43:07 +02:00
g . sql_stmt ( node )
2020-06-24 14:44:06 +02:00
}
2019-12-30 06:16:59 +01:00
ast . StructDecl {
2021-09-04 18:51:45 +02:00
name := if node . language == . c {
util . no_dots ( node . name )
} else if node . name in [ ' a r r a y ' , ' s t r i n g ' ] {
node . name
} else {
c_name ( node . name )
}
2020-08-04 20:10:22 +02:00
// TODO For some reason, build fails with autofree with this line
// as it's only informative, comment it for now
// g.gen_attrs(node.attrs)
2020-03-05 23:27:21 +01:00
// g.writeln('typedef struct {')
// for field in it.fields {
2021-12-19 17:25:18 +01:00
// field_type_sym := g.table.sym(field.typ)
2020-03-05 23:27:21 +01:00
// g.writeln('\t$field_type_sym.name $field.name;')
// }
// g.writeln('} $name;')
2020-06-18 20:21:08 +02:00
if node . language == . c {
2020-04-08 01:20:55 +02:00
return
}
2020-06-18 20:21:08 +02:00
if node . is_union {
2020-04-08 01:20:55 +02:00
g . typedefs . writeln ( ' t y p e d e f u n i o n $ name $ name ; ' )
} else {
2021-04-05 07:02:19 +02:00
/ *
2021-04-05 05:49:43 +02:00
attrs := if node . attrs . contains ( ' p a c k e d ' ) {
' _ _ a t t r i b u t e _ _ ( ( _ _ p a c k e d _ _ ) ) '
} else {
' '
}
2021-04-05 07:02:19 +02:00
* /
g . typedefs . writeln ( ' t y p e d e f s t r u c t $ name $ name ; ' )
2020-03-13 01:43:30 +01:00
}
2019-12-30 06:16:59 +01:00
}
2020-03-04 15:48:43 +01:00
ast . TypeDecl {
2021-04-09 16:56:36 +02:00
if ! g . pref . skip_unused {
g . writeln ( ' / / T y p e D e c l ' )
}
2020-03-04 15:48:43 +01:00
}
2019-12-28 14:11:05 +01:00
}
2020-10-24 19:06:16 +02:00
if ! g . skip_stmt_pos { // && g.stmt_path_pos.len > 0 {
g . stmt_path_pos . delete_last ( )
}
2020-10-29 01:09:38 +01:00
// If we have temporary string exprs to free after this statement, do it. e.g.:
// `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);`
2021-01-08 13:56:55 +01:00
if g . is_autofree {
2020-11-05 08:44:34 +01:00
// if node is ast.ExprStmt {&& node.expr is ast.CallExpr {
if node ! is ast . FnDecl {
2020-11-09 14:24:46 +01:00
// p := node.position()
// g.autofree_call_postgen(p.pos)
2020-11-05 08:44:34 +01:00
}
2020-10-29 01:09:38 +01:00
}
2019-12-28 14:11:05 +01:00
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) write_defer_stmts ( ) {
2021-03-30 09:35:19 +02:00
for i := g . defer_stmts . len - 1 ; i >= 0 ; i -- {
defer_stmt := g . defer_stmts [ i ]
2020-07-08 07:39:11 +02:00
g . writeln ( ' / / D e f e r b e g i n ' )
2021-04-19 14:38:48 +02:00
g . writeln ( ' i f ( $ { g . defer_flag_var ( defer_stmt ) } ) { ' )
2021-02-06 17:40:39 +01:00
g . indent ++
2020-03-27 11:21:00 +01:00
if defer_stmt . ifdef . len > 0 {
g . writeln ( defer_stmt . ifdef )
g . stmts ( defer_stmt . stmts )
2020-04-11 11:41:48 +02:00
g . writeln ( ' ' )
2020-03-27 11:21:00 +01:00
g . writeln ( ' # e n d i f ' )
2020-04-05 18:03:36 +02:00
} else {
2020-07-08 07:39:11 +02:00
g . indent --
2020-03-27 11:21:00 +01:00
g . stmts ( defer_stmt . stmts )
2020-07-08 07:39:11 +02:00
g . indent ++
2020-03-27 11:21:00 +01:00
}
2021-02-06 17:40:39 +01:00
g . indent --
g . writeln ( ' } ' )
2020-07-08 07:39:11 +02:00
g . writeln ( ' / / D e f e r e n d ' )
2020-03-27 11:21:00 +01:00
}
}
2021-02-24 13:58:45 +01:00
fn ( mut g Gen ) for_c_stmt ( node ast . ForCStmt ) {
2021-10-18 09:59:52 +02:00
g . loop_depth ++
2021-02-24 13:58:45 +01:00
if node . is_multi {
g . is_vlines_enabled = false
2021-12-22 08:34:26 +01:00
g . inside_for_c_stmt = true
2021-02-24 13:58:45 +01:00
if node . label . len > 0 {
g . writeln ( ' $ node . label : ' )
}
g . writeln ( ' { ' )
g . indent ++
if node . has_init {
g . stmt ( node . init )
}
g . writeln ( ' b o o l _ i s _ f i r s t = t r u e ; ' )
g . writeln ( ' w h i l e ( t r u e ) { ' )
g . writeln ( ' \t i f ( _ i s _ f i r s t ) { ' )
g . writeln ( ' \t \t _ i s _ f i r s t = f a l s e ; ' )
g . writeln ( ' \t } e l s e { ' )
if node . has_inc {
g . indent ++
g . stmt ( node . inc )
g . writeln ( ' ; ' )
g . indent --
}
g . writeln ( ' } ' )
if node . has_cond {
g . write ( ' i f ( ! ( ' )
g . expr ( node . cond )
g . writeln ( ' ) ) b r e a k ; ' )
}
g . is_vlines_enabled = true
2021-12-22 08:34:26 +01:00
g . inside_for_c_stmt = false
2021-02-24 13:58:45 +01:00
g . stmts ( node . stmts )
if node . label . len > 0 {
g . writeln ( ' $ { node . label } _ _ c o n t i n u e : { } ' )
}
g . writeln ( ' } ' )
g . indent --
g . writeln ( ' } ' )
if node . label . len > 0 {
g . writeln ( ' $ { node . label } _ _ b r e a k : { } ' )
}
} else {
g . is_vlines_enabled = false
2021-12-22 08:34:26 +01:00
g . inside_for_c_stmt = true
2021-02-24 13:58:45 +01:00
if node . label . len > 0 {
g . writeln ( ' $ node . label : ' )
}
g . write ( ' f o r ( ' )
if ! node . has_init {
g . write ( ' ; ' )
} else {
g . stmt ( node . init )
// Remove excess return and add space
if g . out . last_n ( 1 ) == ' \n ' {
g . out . go_back ( 1 )
g . empty_line = false
g . write ( ' ' )
}
}
if node . has_cond {
g . expr ( node . cond )
}
g . write ( ' ; ' )
if node . has_inc {
g . stmt ( node . inc )
}
g . writeln ( ' ) { ' )
g . is_vlines_enabled = true
2021-12-22 08:34:26 +01:00
g . inside_for_c_stmt = false
2021-02-24 13:58:45 +01:00
g . stmts ( node . stmts )
if node . label . len > 0 {
g . writeln ( ' $ { node . label } _ _ c o n t i n u e : { } ' )
}
g . writeln ( ' } ' )
if node . label . len > 0 {
g . writeln ( ' $ { node . label } _ _ b r e a k : { } ' )
}
}
2021-10-18 09:59:52 +02:00
g . loop_depth --
2021-02-24 13:58:45 +01:00
}
fn ( mut g Gen ) for_stmt ( node ast . ForStmt ) {
2021-10-18 09:59:52 +02:00
g . loop_depth ++
2021-02-24 13:58:45 +01:00
g . is_vlines_enabled = false
if node . label . len > 0 {
g . writeln ( ' $ node . label : ' )
}
g . writeln ( ' f o r ( ; ; ) { ' )
if ! node . is_inf {
g . indent ++
2021-09-26 14:51:18 +02:00
g . set_current_pos_as_last_stmt_pos ( )
2021-02-24 13:58:45 +01:00
g . write ( ' i f ( ! ( ' )
g . expr ( node . cond )
g . writeln ( ' ) ) b r e a k ; ' )
g . indent --
}
g . is_vlines_enabled = true
g . stmts ( node . stmts )
if node . label . len > 0 {
g . writeln ( ' \t $ { node . label } _ _ c o n t i n u e : { } ' )
}
g . writeln ( ' } ' )
if node . label . len > 0 {
g . writeln ( ' $ { node . label } _ _ b r e a k : { } ' )
}
2021-10-18 09:59:52 +02:00
g . loop_depth --
2021-02-24 13:58:45 +01:00
}
2021-02-25 13:24:42 +01:00
fn ( mut g Gen ) for_in_stmt ( node ast . ForInStmt ) {
2021-10-18 09:59:52 +02:00
g . loop_depth ++
2021-02-25 13:24:42 +01:00
if node . label . len > 0 {
g . writeln ( ' \t $ node . label : { } ' )
2020-11-20 12:23:48 +01:00
}
2021-02-25 13:24:42 +01:00
if node . is_range {
2020-03-26 17:03:14 +01:00
// `for x in 1..10 {`
2021-02-25 13:24:42 +01:00
i := if node . val_var == ' _ ' { g . new_tmp_var ( ) } else { c_name ( node . val_var ) }
2022-01-04 17:37:18 +01:00
val_typ := ast . mktyp ( node . val_type )
2021-04-19 14:38:48 +02:00
g . write ( ' f o r ( $ { g . typ ( val_typ ) } $ i = ' )
2021-02-25 13:24:42 +01:00
g . expr ( node . cond )
2020-03-26 17:03:14 +01:00
g . write ( ' ; $ i < ' )
2021-02-25 13:24:42 +01:00
g . expr ( node . high )
2020-06-30 14:14:48 +02:00
g . writeln ( ' ; + + $ i ) { ' )
2021-02-25 13:24:42 +01:00
} else if node . kind == . array {
2020-03-26 17:03:14 +01:00
// `for num in nums {`
2021-06-26 01:05:53 +02:00
// g.writeln('// FOR IN array')
2021-02-25 13:24:42 +01:00
styp := g . typ ( node . val_type )
2021-12-19 17:25:18 +01:00
val_sym := g . table . sym ( node . val_type )
2021-03-05 06:58:09 +01:00
mut cond_var := ' '
if node . cond is ast . Ident || node . cond is ast . SelectorExpr {
2021-03-08 11:46:39 +01:00
cond_var = g . expr_string ( node . cond )
2021-03-05 06:58:09 +01:00
} else {
cond_var = g . new_tmp_var ( )
g . write ( g . typ ( node . cond_type ) )
g . write ( ' $ cond_var = ' )
g . expr ( node . cond )
g . writeln ( ' ; ' )
}
2021-02-25 13:24:42 +01:00
i := if node . key_var in [ ' ' , ' _ ' ] { g . new_tmp_var ( ) } else { node . key_var }
2021-03-14 08:37:38 +01:00
field_accessor := if node . cond_type . is_ptr ( ) { ' - > ' } else { ' . ' }
share_accessor := if node . cond_type . share ( ) == . shared_t { ' v a l . ' } else { ' ' }
op_field := field_accessor + share_accessor
2021-03-05 06:58:09 +01:00
g . empty_line = true
g . writeln ( ' f o r ( i n t $ i = 0 ; $ i < $ cond_var $ { op_field } l e n ; + + $ i ) { ' )
2021-02-25 13:24:42 +01:00
if node . val_var != ' _ ' {
2020-07-22 02:30:44 +02:00
if val_sym . kind == . function {
g . write ( ' \t ' )
2021-04-02 00:57:09 +02:00
g . write_fn_ptr_decl ( val_sym . info as ast . FnType , c_name ( node . val_var ) )
2021-03-05 06:58:09 +01:00
g . writeln ( ' = ( ( v o i d p t r * ) $ cond_var $ { op_field } d a t a ) [ $ i ] ; ' )
2021-03-04 11:39:39 +01:00
} else if val_sym . kind == . array_fixed && ! node . val_is_mut {
2021-03-05 06:58:09 +01:00
right := ' ( ( $ styp * ) $ cond_var $ { op_field } d a t a ) [ $ i ] '
2021-03-04 11:39:39 +01:00
g . writeln ( ' \t $ styp $ { c_name ( node . val_var ) } ; ' )
g . writeln ( ' \t m e m c p y ( * ( $ styp * ) $ { c_name ( node . val_var ) } , ( b y t e * ) $ right , s i z e o f ( $ styp ) ) ; ' )
2020-07-22 02:30:44 +02:00
} else {
2020-10-14 12:49:55 +02:00
// If val is mutable (pointer behind the scenes), we need to generate
2020-10-14 13:34:49 +02:00
// `int* val = ((int*)arr.data) + i;`
2020-10-14 12:49:55 +02:00
// instead of
// `int* val = ((int**)arr.data)[i];`
2021-02-25 13:24:42 +01:00
// right := if node.val_is_mut { styp } else { styp + '*' }
right := if node . val_is_mut {
2021-03-05 06:58:09 +01:00
' ( ( $ styp ) $ cond_var $ { op_field } d a t a ) + $ i '
2020-12-27 14:20:30 +01:00
} else {
2021-03-05 06:58:09 +01:00
' ( ( $ styp * ) $ cond_var $ { op_field } d a t a ) [ $ i ] '
2020-12-27 14:20:30 +01:00
}
2021-02-25 13:24:42 +01:00
g . writeln ( ' \t $ styp $ { c_name ( node . val_var ) } = $ right ; ' )
2020-07-22 02:30:44 +02:00
}
2020-04-05 18:15:12 +02:00
}
2021-02-25 13:24:42 +01:00
} else if node . kind == . array_fixed {
2021-03-05 13:35:09 +01:00
mut cond_var := ' '
2021-03-09 15:18:07 +01:00
cond_type_is_ptr := node . cond_type . is_ptr ( )
cond_is_literal := node . cond is ast . ArrayInit
if cond_is_literal {
cond_var = g . new_tmp_var ( )
g . write ( g . typ ( node . cond_type ) )
g . write ( ' $ cond_var = ' )
g . expr ( node . cond )
g . writeln ( ' ; ' )
} else if cond_type_is_ptr {
2021-03-05 13:35:09 +01:00
cond_var = g . new_tmp_var ( )
cond_var_type := g . typ ( node . cond_type ) . trim ( ' * ' )
2021-02-25 13:24:42 +01:00
if ! node . cond . is_lvalue ( ) {
2021-03-05 13:35:09 +01:00
g . write ( ' $ cond_var_type * $ cond_var = ( ( $ cond_var_type ) ' )
2021-01-19 05:50:23 +01:00
} else {
2021-03-05 13:35:09 +01:00
g . write ( ' $ cond_var_type * $ cond_var = ( ' )
2021-01-18 06:05:38 +01:00
}
2021-02-25 13:24:42 +01:00
g . expr ( node . cond )
2021-01-19 05:50:23 +01:00
g . writeln ( ' ) ; ' )
2021-03-05 13:35:09 +01:00
} else {
2021-03-08 11:46:39 +01:00
cond_var = g . expr_string ( node . cond )
2020-08-17 21:10:47 +02:00
}
2021-03-05 13:35:09 +01:00
idx := if node . key_var in [ ' ' , ' _ ' ] { g . new_tmp_var ( ) } else { node . key_var }
2021-12-19 17:25:18 +01:00
cond_sym := g . table . sym ( node . cond_type )
2021-04-02 00:57:09 +02:00
info := cond_sym . info as ast . ArrayFixed
2021-03-05 13:35:09 +01:00
g . writeln ( ' f o r ( i n t $ idx = 0 ; $ idx ! = $ info . size ; + + $ idx ) { ' )
2021-02-25 13:24:42 +01:00
if node . val_var != ' _ ' {
2021-12-19 17:25:18 +01:00
val_sym := g . table . sym ( node . val_type )
2021-03-05 13:35:09 +01:00
is_fixed_array := val_sym . kind == . array_fixed && ! node . val_is_mut
2020-08-17 21:10:47 +02:00
if val_sym . kind == . function {
g . write ( ' \t ' )
2021-04-02 00:57:09 +02:00
g . write_fn_ptr_decl ( val_sym . info as ast . FnType , c_name ( node . val_var ) )
2021-03-05 13:35:09 +01:00
} else if is_fixed_array {
styp := g . typ ( node . val_type )
g . writeln ( ' \t $ styp $ { c_name ( node . val_var ) } ; ' )
g . writeln ( ' \t m e m c p y ( * ( $ styp * ) $ { c_name ( node . val_var ) } , ( b y t e * ) $ cond_var [ $ idx ] , s i z e o f ( $ styp ) ) ; ' )
2020-08-17 21:10:47 +02:00
} else {
2021-02-25 13:24:42 +01:00
styp := g . typ ( node . val_type )
g . write ( ' \t $ styp $ { c_name ( node . val_var ) } ' )
2020-08-17 21:10:47 +02:00
}
2021-03-05 13:35:09 +01:00
if ! is_fixed_array {
addr := if node . val_is_mut { ' & ' } else { ' ' }
2021-03-09 15:18:07 +01:00
if cond_type_is_ptr {
2021-03-05 13:35:09 +01:00
g . writeln ( ' = $ { addr } ( * $ cond_var ) [ $ idx ] ; ' )
2021-03-09 15:18:07 +01:00
} else if cond_is_literal {
g . writeln ( ' = $ addr $ cond_var [ $ idx ] ; ' )
2021-03-05 13:35:09 +01:00
} else {
g . write ( ' = $ addr ' )
g . expr ( node . cond )
g . writeln ( ' [ $ idx ] ; ' )
}
2021-01-18 06:05:38 +01:00
}
2020-08-17 21:10:47 +02:00
}
2021-02-25 13:24:42 +01:00
} else if node . kind == . map {
2020-11-29 20:23:37 +01:00
// `for key, val in map {
2021-06-26 01:05:53 +02:00
// g.writeln('// FOR IN map')
2021-03-07 10:41:08 +01:00
mut cond_var := ' '
if node . cond is ast . Ident {
2021-03-08 11:46:39 +01:00
cond_var = g . expr_string ( node . cond )
2021-03-07 10:41:08 +01:00
} else {
cond_var = g . new_tmp_var ( )
g . write ( g . typ ( node . cond_type ) )
g . write ( ' $ cond_var = ' )
g . expr ( node . cond )
g . writeln ( ' ; ' )
}
2021-04-16 13:49:14 +02:00
mut arw_or_pt := if node . cond_type . is_ptr ( ) { ' - > ' } else { ' . ' }
if node . cond_type . has_flag ( . shared_f ) {
arw_or_pt = ' - > v a l . '
}
2021-03-07 10:41:08 +01:00
idx := g . new_tmp_var ( )
2021-03-17 01:42:33 +01:00
map_len := g . new_tmp_var ( )
2021-03-07 10:41:08 +01:00
g . empty_line = true
2021-03-17 01:42:33 +01:00
g . writeln ( ' i n t $ map_len = $ cond_var $ { arw_or_pt } k e y _ v a l u e s . l e n ; ' )
g . writeln ( ' f o r ( i n t $ idx = 0 ; $ idx < $ map_len ; + + $ idx ) { ' )
2020-12-06 00:24:24 +01:00
// TODO: don't have this check when the map has no deleted elements
2021-03-17 01:42:33 +01:00
g . indent ++
diff := g . new_tmp_var ( )
g . writeln ( ' i n t $ diff = $ cond_var $ { arw_or_pt } k e y _ v a l u e s . l e n - $ map_len ; ' )
g . writeln ( ' $ map_len = $ cond_var $ { arw_or_pt } k e y _ v a l u e s . l e n ; ' )
// TODO: optimize this
g . writeln ( ' i f ( $ diff < 0 ) { ' )
g . writeln ( ' \t $ idx = - 1 ; ' )
g . writeln ( ' \t c o n t i n u e ; ' )
g . writeln ( ' } ' )
g . writeln ( ' i f ( ! D e n s e A r r a y _ h a s _ i n d e x ( & $ cond_var $ { arw_or_pt } k e y _ v a l u e s , $ idx ) ) { c o n t i n u e ; } ' )
2021-02-25 13:24:42 +01:00
if node . key_var != ' _ ' {
key_styp := g . typ ( node . key_type )
key := c_name ( node . key_var )
2021-03-17 01:42:33 +01:00
g . writeln ( ' $ key_styp $ key = / * k e y * / * ( $ key_styp * ) D e n s e A r r a y _ k e y ( & $ cond_var $ { arw_or_pt } k e y _ v a l u e s , $ idx ) ; ' )
2021-02-25 13:24:42 +01:00
// TODO: analyze whether node.key_type has a .clone() method and call .clone() for all types:
2021-04-02 00:57:09 +02:00
if node . key_type == ast . string_type {
2021-03-17 01:42:33 +01:00
g . writeln ( ' $ key = s t r i n g _ c l o n e ( $ key ) ; ' )
2020-11-26 23:22:14 +01:00
}
2020-07-01 10:48:46 +02:00
}
2021-02-25 13:24:42 +01:00
if node . val_var != ' _ ' {
2021-12-19 17:25:18 +01:00
val_sym := g . table . sym ( node . val_type )
2020-07-22 02:30:44 +02:00
if val_sym . kind == . function {
2021-04-02 00:57:09 +02:00
g . write_fn_ptr_decl ( val_sym . info as ast . FnType , c_name ( node . val_var ) )
2020-12-03 20:12:53 +01:00
g . write ( ' = ( * ( v o i d p t r * ) ' )
2021-03-07 10:41:08 +01:00
g . writeln ( ' D e n s e A r r a y _ v a l u e ( & $ cond_var $ { arw_or_pt } k e y _ v a l u e s , $ idx ) ) ; ' )
} else if val_sym . kind == . array_fixed && ! node . val_is_mut {
val_styp := g . typ ( node . val_type )
2021-03-17 01:42:33 +01:00
g . writeln ( ' $ val_styp $ { c_name ( node . val_var ) } ; ' )
g . writeln ( ' m e m c p y ( * ( $ val_styp * ) $ { c_name ( node . val_var ) } , ( b y t e * ) D e n s e A r r a y _ v a l u e ( & $ cond_var $ { arw_or_pt } k e y _ v a l u e s , $ idx ) , s i z e o f ( $ val_styp ) ) ; ' )
2020-07-22 02:30:44 +02:00
} else {
2021-02-25 13:24:42 +01:00
val_styp := g . typ ( node . val_type )
if node . val_type . is_ptr ( ) {
2021-11-16 15:07:39 +01:00
if node . val_is_mut {
g . write ( ' $ val_styp $ { c_name ( node . val_var ) } = & ( * ( $ val_styp ) ' )
} else {
g . write ( ' $ val_styp $ { c_name ( node . val_var ) } = ( * ( $ val_styp * ) ' )
}
2021-01-21 12:44:22 +01:00
} else {
2021-03-17 01:42:33 +01:00
g . write ( ' $ val_styp $ { c_name ( node . val_var ) } = ( * ( $ val_styp * ) ' )
2021-01-21 12:44:22 +01:00
}
2021-03-07 10:41:08 +01:00
g . writeln ( ' D e n s e A r r a y _ v a l u e ( & $ cond_var $ { arw_or_pt } k e y _ v a l u e s , $ idx ) ) ; ' )
2020-07-22 02:30:44 +02:00
}
2020-11-20 12:23:48 +01:00
}
2021-03-17 01:42:33 +01:00
g . indent --
2021-02-25 13:24:42 +01:00
} else if node . kind == . string {
2021-03-11 14:01:53 +01:00
cond := if node . cond is ast . StringLiteral || node . cond is ast . StringInterLiteral {
2021-04-02 00:57:09 +02:00
ast . Expr ( g . new_ctemp_var_then_gen ( node . cond , ast . string_type ) )
2021-03-11 14:01:53 +01:00
} else {
node . cond
}
2021-02-25 13:24:42 +01:00
i := if node . key_var in [ ' ' , ' _ ' ] { g . new_tmp_var ( ) } else { node . key_var }
2020-03-26 17:03:14 +01:00
g . write ( ' f o r ( i n t $ i = 0 ; $ i < ' )
2021-03-11 14:01:53 +01:00
g . expr ( cond )
2020-06-30 14:14:48 +02:00
g . writeln ( ' . l e n ; + + $ i ) { ' )
2021-02-25 13:24:42 +01:00
if node . val_var != ' _ ' {
g . write ( ' \t b y t e $ { c_name ( node . val_var ) } = ' )
2021-03-11 14:01:53 +01:00
g . expr ( cond )
2020-04-23 12:00:51 +02:00
g . writeln ( ' . s t r [ $ i ] ; ' )
}
2021-02-25 13:24:42 +01:00
} else if node . kind == . struct_ {
2021-12-19 17:25:18 +01:00
cond_type_sym := g . table . sym ( node . cond_type )
2021-11-12 13:29:01 +01:00
next_fn := cond_type_sym . find_method_with_generic_parent ( ' n e x t ' ) or {
2021-01-05 01:06:44 +01:00
verror ( ' ` n e x t ` m e t h o d n o t f o u n d ' )
return
}
ret_typ := next_fn . return_type
2021-02-26 21:51:01 +01:00
t_expr := g . new_tmp_var ( )
g . write ( ' $ { g . typ ( node . cond_type ) } $ t_expr = ' )
g . expr ( node . cond )
g . writeln ( ' ; ' )
2021-09-28 18:35:07 +02:00
if node . key_var in [ ' ' , ' _ ' ] {
g . writeln ( ' w h i l e ( 1 ) { ' )
2021-09-29 07:58:27 +02:00
} else {
2021-09-28 18:35:07 +02:00
g . writeln ( ' f o r ( s i z e _ t $ node . key_var = 0 ; ; + + $ node . key_var ) { ' )
}
2021-03-26 07:21:01 +01:00
t_var := g . new_tmp_var ( )
2021-02-26 21:51:01 +01:00
receiver_typ := next_fn . params [ 0 ] . typ
receiver_styp := g . typ ( receiver_typ )
2021-11-12 13:29:01 +01:00
mut fn_name := receiver_styp . replace_each ( [ ' * ' , ' ' , ' . ' , ' _ _ ' ] ) + ' _ n e x t '
2021-12-19 17:25:18 +01:00
receiver_sym := g . table . sym ( receiver_typ )
2021-11-12 13:29:01 +01:00
if receiver_sym . info is ast . Struct {
if receiver_sym . info . concrete_types . len > 0 {
fn_name = g . generic_fn_name ( receiver_sym . info . concrete_types , fn_name ,
false )
}
}
2021-03-26 07:21:01 +01:00
g . write ( ' \t $ { g . typ ( ret_typ ) } $ t_var = $ { fn_name } ( ' )
2021-02-26 21:51:01 +01:00
if ! node . cond_type . is_ptr ( ) && receiver_typ . is_ptr ( ) {
2021-01-05 01:06:44 +01:00
g . write ( ' & ' )
}
2021-02-26 21:51:01 +01:00
g . writeln ( ' $ t_expr ) ; ' )
2021-03-26 07:21:01 +01:00
g . writeln ( ' \t i f ( $ { t_var } . s t a t e ! = 0 ) b r e a k ; ' )
2021-02-25 13:24:42 +01:00
val := if node . val_var in [ ' ' , ' _ ' ] { g . new_tmp_var ( ) } else { node . val_var }
val_styp := g . typ ( node . val_type )
2021-11-02 16:40:13 +01:00
if node . val_is_mut {
2021-11-24 13:43:37 +01:00
g . writeln ( ' \t $ val_styp $ val = ( $ val_styp ) $ { t_var } . d a t a ; ' )
2021-11-02 16:40:13 +01:00
} else {
g . writeln ( ' \t $ val_styp $ val = * ( $ val_styp * ) $ { t_var } . d a t a ; ' )
}
2020-08-14 21:01:43 +02:00
} else {
2021-03-26 07:21:01 +01:00
typ_str := g . table . type_to_str ( node . cond_type )
g . error ( ' f o r i n : u n h a n d l e d s y m b o l ` $ node . cond ` o f t y p e ` $ typ_str ` ' , node . pos )
2020-03-26 17:03:14 +01:00
}
2021-02-25 13:24:42 +01:00
g . stmts ( node . stmts )
if node . label . len > 0 {
g . writeln ( ' \t $ { node . label } _ _ c o n t i n u e : { } ' )
2020-11-20 12:23:48 +01:00
}
2021-03-17 01:42:33 +01:00
if node . kind == . map {
// diff := g.new_tmp_var()
// g.writeln('int $diff = $cond_var${arw_or_pt}key_values.len - $map_len;')
// g.writeln('if ($diff < 0) {')
// g.writeln('\t$idx = -1;')
// g.writeln('\t$map_len = $cond_var${arw_or_pt}key_values.len;')
// g.writeln('}')
}
2020-11-20 12:23:48 +01:00
g . writeln ( ' } ' )
2021-02-25 13:24:42 +01:00
if node . label . len > 0 {
g . writeln ( ' \t $ { node . label } _ _ b r e a k : { } ' )
2020-11-20 12:23:48 +01:00
}
2021-10-18 09:59:52 +02:00
g . loop_depth --
2020-03-26 17:03:14 +01:00
}
2021-09-28 09:28:04 +02:00
struct SumtypeCastingFn {
fn_name string
got ast . Type
exp ast . Type
}
fn ( mut g Gen ) get_sumtype_casting_fn ( got_ ast . Type , exp_ ast . Type ) string {
2021-03-09 18:16:18 +01:00
got , exp := got_ . idx ( ) , exp_ . idx ( )
2021-11-29 01:48:49 +01:00
i := got | int ( u32 ( exp ) << 16 )
2021-12-19 17:25:18 +01:00
got_cname , exp_cname := g . table . sym ( got ) . cname , g . table . sym ( exp ) . cname
2021-09-28 09:28:04 +02:00
fn_name := ' $ { got_cname } _ t o _ s u m t y p e _ $ exp_cname '
2021-03-09 18:16:18 +01:00
if got == exp || g . sumtype_definitions [ i ] {
2021-09-28 09:28:04 +02:00
return fn_name
2021-03-09 18:16:18 +01:00
}
g . sumtype_definitions [ i ] = true
2021-09-28 09:28:04 +02:00
g . sumtype_casting_fns << SumtypeCastingFn {
fn_name : fn_name
got : got
exp : exp
}
return fn_name
}
fn ( mut g Gen ) write_sumtype_casting_fn ( fun SumtypeCastingFn ) {
got , exp := fun . got , fun . exp
2021-12-19 17:25:18 +01:00
got_sym , exp_sym := g . table . sym ( got ) , g . table . sym ( exp )
2021-03-09 18:16:18 +01:00
got_cname , exp_cname := got_sym . cname , exp_sym . cname
2021-09-28 09:28:04 +02:00
mut sb := strings . new_builder ( 128 )
sb . writeln ( ' s t a t i c i n l i n e $ exp_cname $ { fun . fn_name } ( $ got_cname * x ) { ' )
2021-03-09 18:16:18 +01:00
sb . writeln ( ' \t $ got_cname * p t r = m e m d u p ( x , s i z e o f ( $ got_cname ) ) ; ' )
2021-08-06 20:26:19 +02:00
for embed_hierarchy in g . table . get_embeds ( got_sym ) {
// last embed in the hierarchy
mut embed_cname := ' '
mut embed_name := ' '
mut accessor := ' & x - > '
for j , embed in embed_hierarchy {
2021-12-19 17:25:18 +01:00
embed_sym := g . table . sym ( embed )
2021-08-06 20:26:19 +02:00
embed_cname = embed_sym . cname
embed_name = embed_sym . embed_name ( )
if j > 0 {
accessor += ' . '
}
accessor += embed_name
}
// if the variable is not used, the C compiler will optimize it away
sb . writeln ( ' \t $ embed_cname * $ { embed_name } _ p t r = m e m d u p ( $ accessor , s i z e o f ( $ embed_cname ) ) ; ' )
}
2021-03-09 18:16:18 +01:00
sb . write_string ( ' \t r e t u r n ( $ exp_cname ) { . _ $ got_cname = p t r , . _ t y p = $ { g . type_sidx ( got ) } ' )
2021-04-02 00:57:09 +02:00
for field in ( exp_sym . info as ast . SumType ) . fields {
2021-08-06 20:26:19 +02:00
mut ptr := ' p t r '
mut type_cname := got_cname
2021-12-04 17:46:41 +01:00
_ , embed_types := g . table . find_field_from_embeds ( got_sym , field . name ) or {
2021-08-06 20:26:19 +02:00
ast . StructField { } , [ ] ast . Type { }
}
if embed_types . len > 0 {
2021-12-19 17:25:18 +01:00
embed_sym := g . table . sym ( embed_types . last ( ) )
2021-08-06 20:26:19 +02:00
ptr = ' $ { embed_sym . embed_name ( ) } _ p t r '
type_cname = embed_sym . cname
}
2021-03-09 18:16:18 +01:00
field_styp := g . typ ( field . typ )
if got_sym . kind in [ . sum_type , . interface_ ] {
// the field is already a wrapped pointer; we shouldn't wrap it once again
sb . write_string ( ' , . $ field . name = p t r - > $ field . name ' )
} else {
2021-08-06 20:26:19 +02:00
sb . write_string ( ' , . $ field . name = ( $ field_styp * ) ( ( c h a r * ) $ ptr + _ _ o f f s e t o f _ p t r ( $ ptr , $ type_cname , $ field . name ) ) ' )
2021-03-09 18:16:18 +01:00
}
}
sb . writeln ( ' } ; \n } ' )
g . auto_fn_definitions << sb . str ( )
}
2021-05-13 16:54:48 +02:00
fn ( mut g Gen ) call_cfn_for_casting_expr ( fname string , expr ast . Expr , exp_is_ptr bool , exp_styp string , got_is_ptr bool , got_styp string ) {
mut rparen_n := 1
if exp_is_ptr {
g . write ( ' H E A P ( $ exp_styp , ' )
rparen_n ++
}
g . write ( ' $ { fname } ( ' )
if ! got_is_ptr {
if ! expr . is_lvalue ( )
2021-07-19 07:20:09 +02:00
|| ( expr is ast . Ident && ( expr as ast . Ident ) . obj . is_simple_define_const ( ) ) {
2021-05-13 16:54:48 +02:00
g . write ( ' A D D R ( $ got_styp , ( ' )
rparen_n += 2
} else {
g . write ( ' & ' )
}
}
g . expr ( expr )
g . write ( ' ) ' . repeat ( rparen_n ) )
}
2021-01-31 18:24:33 +01:00
// use instead of expr() when you need to cast to a different type
2021-04-02 00:57:09 +02:00
fn ( mut g Gen ) expr_with_cast ( expr ast . Expr , got_type_raw ast . Type , expected_type ast . Type ) {
2022-01-04 17:37:18 +01:00
got_type := ast . mktyp ( got_type_raw )
2021-12-19 17:25:18 +01:00
exp_sym := g . table . sym ( expected_type )
got_sym := g . table . sym ( got_type )
2021-02-08 00:28:29 +01:00
expected_is_ptr := expected_type . is_ptr ( )
got_is_ptr := got_type . is_ptr ( )
2021-02-28 20:24:29 +01:00
// allow using the new Error struct as a string, to avoid a breaking change
// TODO: temporary to allow people to migrate their code; remove soon
2021-04-02 00:57:09 +02:00
if got_type == ast . error_type_idx && expected_type == ast . string_type_idx {
2021-03-13 18:13:50 +01:00
g . write ( ' ( * ( ' )
2021-02-28 20:24:29 +01:00
g . expr ( expr )
2021-03-13 18:13:50 +01:00
g . write ( ' . m s g ) ) ' )
2021-02-28 20:24:29 +01:00
return
}
2021-07-30 02:18:20 +02:00
if got_sym . kind == . none_ && exp_sym . name == ' I E r r o r ' {
g . expr ( expr )
return
}
2021-07-15 07:29:13 +02:00
if exp_sym . info is ast . Interface && got_type_raw . idx ( ) ! = expected_type . idx ( )
2021-01-31 18:24:33 +01:00
&& ! expected_type . has_flag ( . optional ) {
2021-07-06 15:33:26 +02:00
if expr is ast . StructInit && ! got_type . is_ptr ( ) {
g . inside_cast_in_heap ++
2021-10-06 19:49:39 +02:00
got_styp := g . cc_type ( got_type . ref ( ) , true )
2021-07-15 07:29:13 +02:00
// TODO: why does cc_type even add this in the first place?
exp_styp := exp_sym . cname
mut fname := ' I _ $ { got_styp } _ t o _ I n t e r f a c e _ $ exp_styp '
if exp_sym . info . is_generic {
fname = g . generic_fn_name ( exp_sym . info . concrete_types , fname , false )
}
2021-07-06 15:33:26 +02:00
g . call_cfn_for_casting_expr ( fname , expr , expected_is_ptr , exp_styp , true ,
got_styp )
g . inside_cast_in_heap --
} else {
2021-10-05 08:53:05 +02:00
mut got_styp := g . cc_type ( got_type , true )
got_styp = match got_styp {
' i n t ' { ' i n t _ l i t e r a l ' }
' f 6 4 ' { ' f l o a t _ l i t e r a l ' }
else { got_styp }
}
2021-12-26 19:01:00 +01:00
got_is_shared := got_type . has_flag ( . shared_f )
exp_styp := if got_is_shared { ' _ _ s h a r e d _ _ $ exp_sym . cname ' } else { exp_sym . cname }
// If it's shared, we need to use the other caster:
mut fname := if got_is_shared {
' I _ _ _ s h a r e d _ _ $ { got_styp } _ t o _ s h a r e d _ I n t e r f a c e _ $ exp_styp '
} else {
' I _ $ { got_styp } _ t o _ I n t e r f a c e _ $ exp_styp '
}
lock g . referenced_fns {
g . referenced_fns [ fname ] = true
}
fname = ' / * $ exp_sym * / $ fname '
2021-07-15 07:29:13 +02:00
if exp_sym . info . is_generic {
fname = g . generic_fn_name ( exp_sym . info . concrete_types , fname , false )
}
2021-07-06 15:33:26 +02:00
g . call_cfn_for_casting_expr ( fname , expr , expected_is_ptr , exp_styp , got_is_ptr ,
got_styp )
}
2021-01-31 18:24:33 +01:00
return
}
2020-11-11 09:18:15 +01:00
// cast to sum type
2021-02-08 00:28:29 +01:00
exp_styp := g . typ ( expected_type )
got_styp := g . typ ( got_type )
2021-04-02 00:57:09 +02:00
if expected_type != ast . void_type {
2021-09-06 02:15:53 +02:00
unwrapped_expected_type := g . unwrap_generic ( expected_type )
unwrapped_got_type := g . unwrap_generic ( got_type )
2021-12-19 17:25:18 +01:00
unwrapped_exp_sym := g . table . sym ( unwrapped_expected_type )
unwrapped_got_sym := g . table . sym ( unwrapped_got_type )
2021-09-06 02:15:53 +02:00
expected_deref_type := if expected_is_ptr {
unwrapped_expected_type . deref ( )
} else {
unwrapped_expected_type
}
got_deref_type := if got_is_ptr { unwrapped_got_type . deref ( ) } else { unwrapped_got_type }
2021-12-06 23:31:47 +01:00
if g . table . sumtype_has_variant ( expected_deref_type , got_deref_type , false ) {
2021-03-09 18:16:18 +01:00
mut is_already_sum_type := false
scope := g . file . scope . innermost ( expr . position ( ) . pos )
if expr is ast . Ident {
if v := scope . find_var ( expr . name ) {
2021-04-09 10:00:05 +02:00
if v . smartcasts . len > 0 {
2020-11-11 09:18:15 +01:00
is_already_sum_type = true
}
}
2021-03-09 18:16:18 +01:00
} else if expr is ast . SelectorExpr {
2021-06-18 13:49:15 +02:00
if _ := scope . find_struct_field ( expr . expr . str ( ) , expr . expr_type , expr . field_name ) {
2021-03-09 18:16:18 +01:00
is_already_sum_type = true
}
}
if is_already_sum_type {
// Don't create a new sum type wrapper if there is already one
g . prevent_sum_type_unwrapping_once = true
g . expr ( expr )
} else {
2021-09-28 09:28:04 +02:00
g . get_sumtype_casting_fn ( unwrapped_got_type , unwrapped_expected_type )
2021-09-06 02:15:53 +02:00
fname := ' $ { unwrapped_got_sym . cname } _ t o _ s u m t y p e _ $ unwrapped_exp_sym . cname '
g . call_cfn_for_casting_expr ( fname , expr , expected_is_ptr , unwrapped_exp_sym . cname ,
2021-05-13 16:54:48 +02:00
got_is_ptr , got_styp )
2020-11-11 09:18:15 +01:00
}
return
}
}
// Generic dereferencing logic
2021-04-02 00:57:09 +02:00
neither_void := ast . voidptr_type ! in [ got_type , expected_type ]
2021-02-08 00:28:29 +01:00
to_shared := expected_type . has_flag ( . shared_f ) && ! got_type_raw . has_flag ( . shared_f )
&& ! expected_type . has_flag ( . optional )
// from_shared := got_type_raw.has_flag(.shared_f) && !expected_type.has_flag(.shared_f)
if to_shared {
shared_styp := exp_styp [ 0 .. exp_styp . len - 1 ] // `shared` implies ptr, so eat one `*`
if got_type_raw . is_ptr ( ) {
g . error ( ' c a n n o t c o n v e r t r e f e r e n c e t o ` s h a r e d ` ' , expr . position ( ) )
}
if exp_sym . kind == . array {
2021-04-19 14:38:48 +02:00
g . writeln ( ' ( $ shared_styp * ) _ _ d u p _ s h a r e d _ a r r a y ( & ( $ shared_styp ) { . m t x = { 0 } , . v a l = ' )
2021-02-08 00:28:29 +01:00
} else if exp_sym . kind == . map {
2021-04-19 14:38:48 +02:00
g . writeln ( ' ( $ shared_styp * ) _ _ d u p _ s h a r e d _ m a p ( & ( $ shared_styp ) { . m t x = { 0 } , . v a l = ' )
2021-02-08 00:28:29 +01:00
} else {
2021-04-19 14:38:48 +02:00
g . writeln ( ' ( $ shared_styp * ) _ _ d u p $ { shared_styp } ( & ( $ shared_styp ) { . m t x = { 0 } , . v a l = ' )
2021-02-08 00:28:29 +01:00
}
2021-08-06 04:11:03 +02:00
old_is_shared := g . is_shared
g . is_shared = false
2021-02-08 00:28:29 +01:00
g . expr ( expr )
2021-08-06 04:11:03 +02:00
g . is_shared = old_is_shared
2021-02-08 00:28:29 +01:00
g . writeln ( ' } , s i z e o f ( $ shared_styp ) ) ' )
return
}
2021-09-29 12:54:23 +02:00
if got_is_ptr && ! expected_is_ptr && neither_void && exp_sym . kind != . placeholder
&& expr ! is ast . InfixExpr {
2020-11-11 09:18:15 +01:00
got_deref_type := got_type . deref ( )
2021-12-19 17:25:18 +01:00
deref_sym := g . table . sym ( got_deref_type )
2020-11-11 09:18:15 +01:00
deref_will_match := expected_type in [ got_type , got_deref_type , deref_sym . parent_idx ]
got_is_opt := got_type . has_flag ( . optional )
2021-09-29 12:54:23 +02:00
if deref_will_match || got_is_opt || expr . is_auto_deref_var ( ) {
2020-11-11 09:18:15 +01:00
g . write ( ' * ' )
}
}
2021-04-02 16:34:48 +02:00
if expected_type . has_flag ( . optional ) && expr is ast . None {
g . gen_optional_error ( expected_type , expr )
return
}
2021-04-19 14:38:48 +02:00
if expr is ast . IntegerLiteral {
if expected_type in [ ast . u64_type , ast . u32_type , ast . u16_type ] && expr . val [ 0 ] != ` - ` {
g . expr ( expr )
g . write ( ' U ' )
return
}
}
if exp_sym . kind == . function {
g . write ( ' ( v o i d p t r ) ' )
}
2020-11-11 09:18:15 +01:00
// no cast
g . expr ( expr )
}
2022-01-11 21:36:18 +01:00
fn cescape_nonascii ( original string ) string {
mut b := strings . new_builder ( original . len )
for c in original {
if c < 32 || c > 126 {
2022-01-12 09:58:37 +01:00
// Encode with a 3 digit octal escape code, which has the
// advantage to be limited/non dependant on what character
// will follow next, unlike hex escapes:
b . write_b ( 92 ) // \
b . write_b ( 48 + ( c >> 6 ) ) // oct digit 2
b . write_b ( 48 + ( c >> 3 ) & 7 ) // oct digit 1
b . write_b ( 48 + c & 7 ) // oct digit 0
2022-01-11 21:36:18 +01:00
continue
}
b . write_b ( c )
}
res := b . str ( )
return res
}
2020-06-01 13:43:31 +02:00
// cestring returns a V string, properly escaped for embeddeding in a C string literal.
fn cestring ( s string ) string {
2021-01-13 06:05:27 +01:00
return s . replace ( ' \\ ' , ' \\ \\ ' ) . replace ( ' " ' , " ' " )
2020-06-01 13:43:31 +02:00
}
2020-06-02 15:49:43 +02:00
2020-12-03 16:02:48 +01:00
// ctoslit returns a '_SLIT("$s")' call, where s is properly escaped.
2020-06-01 13:43:31 +02:00
fn ctoslit ( s string ) string {
2022-01-11 21:36:18 +01:00
return ' _ S L I T ( " ' + cescape_nonascii ( cestring ( s ) ) + ' " ) '
2020-06-01 13:43:31 +02:00
}
2020-06-02 15:49:43 +02:00
2021-04-02 00:57:09 +02:00
fn ( mut g Gen ) gen_attrs ( attrs [ ] ast . Attr ) {
2021-04-09 16:56:36 +02:00
if g . pref . skip_unused {
return
}
2020-08-04 20:10:22 +02:00
for attr in attrs {
g . writeln ( ' / / A t t r : [ $ attr . name ] ' )
}
}
2021-03-17 01:43:17 +01:00
fn ( mut g Gen ) gen_asm_stmt ( stmt ast . AsmStmt ) {
g . write ( ' _ _ a s m _ _ ' )
if stmt . is_volatile {
g . write ( ' v o l a t i l e ' )
}
if stmt . is_goto {
g . write ( ' g o t o ' )
}
g . writeln ( ' ( ' )
g . indent ++
2021-06-07 09:53:33 +02:00
for template_tmp in stmt . templates {
mut template := template_tmp
2021-03-17 01:43:17 +01:00
g . write ( ' " ' )
if template . is_directive {
g . write ( ' . ' )
}
g . write ( template . name )
if template . is_label {
g . write ( ' : ' )
} else {
g . write ( ' ' )
}
2021-03-18 03:41:50 +01:00
// swap destionation and operands for att syntax
2021-06-18 00:20:46 +02:00
if template . args . len != 0 && ! template . is_directive {
2021-03-17 01:43:17 +01:00
template . args . prepend ( template . args [ template . args . len - 1 ] )
template . args . delete ( template . args . len - 1 )
}
for i , arg in template . args {
2021-06-23 13:20:07 +02:00
if stmt . arch == . amd64 && ( template . name == ' c a l l ' || template . name [ 0 ] == ` j ` )
&& arg is ast . AsmRegister {
g . write ( ' * ' ) // indirect branching
}
2021-03-17 01:43:17 +01:00
g . asm_arg ( arg , stmt )
if i + 1 < template . args . len {
g . write ( ' , ' )
}
}
if ! template . is_label {
g . write ( ' ; ' )
}
g . writeln ( ' " ' )
}
2021-06-23 13:20:07 +02:00
2021-06-18 12:03:38 +02:00
if stmt . output . len != 0 || stmt . input . len != 0 || stmt . clobbered . len != 0 || stmt . is_goto {
2021-03-17 01:43:17 +01:00
g . write ( ' : ' )
}
g . gen_asm_ios ( stmt . output )
if stmt . input . len != 0 || stmt . clobbered . len != 0 || stmt . is_goto {
g . write ( ' : ' )
}
g . gen_asm_ios ( stmt . input )
if stmt . clobbered . len != 0 || stmt . is_goto {
g . write ( ' : ' )
}
for i , clob in stmt . clobbered {
g . write ( ' " ' )
g . write ( clob . reg . name )
g . write ( ' " ' )
if i + 1 < stmt . clobbered . len {
g . writeln ( ' , ' )
} else {
g . writeln ( ' ' )
}
}
if stmt . is_goto {
g . write ( ' : ' )
}
for i , label in stmt . global_labels {
g . write ( label )
if i + 1 < stmt . clobbered . len {
g . writeln ( ' , ' )
} else {
g . writeln ( ' ' )
}
}
g . indent --
g . writeln ( ' ) ; ' )
}
fn ( mut g Gen ) asm_arg ( arg ast . AsmArg , stmt ast . AsmStmt ) {
match arg {
ast . AsmAlias {
name := arg . name
2021-04-01 08:58:33 +02:00
if name in stmt . local_labels || name in stmt . global_labels
2021-06-23 13:20:07 +02:00
|| name in g . file . global_labels || stmt . is_basic
2021-06-18 12:03:38 +02:00
|| ( name ! in stmt . input . map ( it . alias ) && name ! in stmt . output . map ( it . alias ) ) {
2021-04-01 08:58:33 +02:00
asm_formatted_name := if name in stmt . global_labels { ' % l [ $ name ] ' } else { name }
2021-03-17 08:48:56 +01:00
g . write ( asm_formatted_name )
2021-03-17 01:43:17 +01:00
} else {
g . write ( ' % [ $ name ] ' )
}
}
ast . CharLiteral {
g . write ( " ' $ arg . val ' " )
}
2021-09-13 18:06:19 +02:00
ast . IntegerLiteral {
2021-03-17 01:43:17 +01:00
g . write ( ' \$ $ arg . val ' )
}
2021-09-13 18:06:19 +02:00
ast . FloatLiteral {
if g . pref . nofloat {
g . write ( ' \$ $ arg . val . int ( ) ' )
} else {
g . write ( ' \$ $ arg . val ' )
}
}
2021-03-17 01:43:17 +01:00
ast . BoolLiteral {
g . write ( ' \$ $ arg . val . str ( ) ' )
}
ast . AsmRegister {
2021-06-23 13:20:07 +02:00
if ! stmt . is_basic {
g . write ( ' % ' ) // escape percent with percent in extended assembly
2021-03-17 01:43:17 +01:00
}
g . write ( ' % $ arg . name ' )
}
ast . AsmAddressing {
2021-10-05 17:57:15 +02:00
if arg . segment != ' ' {
g . write ( ' % % $ arg . segment : ' )
}
2021-03-17 01:43:17 +01:00
base := arg . base
index := arg . index
displacement := arg . displacement
scale := arg . scale
match arg . mode {
. base {
g . write ( ' ( ' )
g . asm_arg ( base , stmt )
2021-04-01 08:58:33 +02:00
g . write ( ' ) ' )
2021-03-17 01:43:17 +01:00
}
. displacement {
2021-04-01 08:58:33 +02:00
g . asm_arg ( displacement , stmt )
2021-03-17 01:43:17 +01:00
}
. base_plus_displacement {
2021-04-01 08:58:33 +02:00
g . asm_arg ( displacement , stmt )
g . write ( ' ( ' )
2021-03-17 01:43:17 +01:00
g . asm_arg ( base , stmt )
2021-04-01 08:58:33 +02:00
g . write ( ' ) ' )
2021-03-17 01:43:17 +01:00
}
. index_times_scale_plus_displacement {
2021-06-18 00:20:46 +02:00
if displacement is ast . AsmDisp {
g . asm_arg ( displacement , stmt )
g . write ( ' ( , ' )
} else if displacement is ast . AsmRegister {
g . write ( ' ( ' )
g . asm_arg ( displacement , stmt )
g . write ( ' , ' )
} else {
panic ( ' u n e x p e c t e d $ displacement . type_name ( ) ' )
}
2021-03-17 01:43:17 +01:00
g . asm_arg ( index , stmt )
2021-04-01 08:58:33 +02:00
g . write ( ' , $ scale ) ' )
2021-03-17 01:43:17 +01:00
}
. base_plus_index_plus_displacement {
2021-04-01 08:58:33 +02:00
g . asm_arg ( displacement , stmt )
g . write ( ' ( ' )
2021-03-17 01:43:17 +01:00
g . asm_arg ( base , stmt )
g . write ( ' , ' )
g . asm_arg ( index , stmt )
2021-04-01 08:58:33 +02:00
g . write ( ' , 1 ) ' )
2021-03-17 01:43:17 +01:00
}
. base_plus_index_times_scale_plus_displacement {
2021-04-01 08:58:33 +02:00
g . asm_arg ( displacement , stmt )
g . write ( ' ( ' )
2021-03-17 01:43:17 +01:00
g . asm_arg ( base , stmt )
g . write ( ' , ' )
g . asm_arg ( index , stmt )
2021-04-01 08:58:33 +02:00
g . write ( ' , $ scale ) ' )
2021-03-17 01:43:17 +01:00
}
. rip_plus_displacement {
2021-04-01 08:58:33 +02:00
g . asm_arg ( displacement , stmt )
g . write ( ' ( ' )
2021-03-17 01:43:17 +01:00
g . asm_arg ( base , stmt )
2021-04-01 08:58:33 +02:00
g . write ( ' ) ' )
2021-03-17 01:43:17 +01:00
}
. invalid {
g . error ( ' i n v a l i d a d d r e s s i n g m o d e ' , arg . pos )
}
}
2021-04-01 08:58:33 +02:00
}
ast . AsmDisp {
2021-06-23 13:20:07 +02:00
g . write ( arg . val )
2021-03-17 01:43:17 +01:00
}
string {
2021-06-23 13:20:07 +02:00
g . write ( arg )
2021-03-17 01:43:17 +01:00
}
}
}
fn ( mut g Gen ) gen_asm_ios ( ios [ ] ast . AsmIO ) {
for i , io in ios {
if io . alias != ' ' {
g . write ( ' [ $ io . alias ] ' )
}
2021-04-01 08:58:33 +02:00
g . write ( ' " $ io . constraint " ( ' )
g . expr ( io . expr )
g . write ( ' ) ' )
2021-03-17 01:43:17 +01:00
if i + 1 < ios . len {
g . writeln ( ' , ' )
} else {
g . writeln ( ' ' )
}
}
}
2020-11-01 15:59:26 +01:00
fn cnewlines ( s string ) string {
return s . replace ( ' \n ' , r '\n' )
}
2021-04-02 00:57:09 +02:00
fn ( mut g Gen ) write_fn_ptr_decl ( func & ast . FnType , ptr_name string ) {
2020-07-22 02:30:44 +02:00
ret_styp := g . typ ( func . func . return_type )
g . write ( ' $ ret_styp ( * $ ptr_name ) ( ' )
2020-09-27 03:32:56 +02:00
arg_len := func . func . params . len
for i , arg in func . func . params {
2020-07-26 14:22:02 +02:00
arg_styp := g . typ ( arg . typ )
g . write ( ' $ arg_styp $ arg . name ' )
2020-07-22 02:30:44 +02:00
if i < arg_len - 1 {
g . write ( ' , ' )
}
}
g . write ( ' ) ' )
}
2020-05-14 17:15:25 +02:00
fn ( mut g Gen ) register_ternary_name ( name string ) {
level_key := g . inside_ternary . str ( )
if level_key ! in g . ternary_level_names {
g . ternary_level_names [ level_key ] = [ ] string { }
}
new_name := g . new_tmp_var ( )
g . ternary_names [ name ] = new_name
g . ternary_level_names [ level_key ] << name
}
fn ( mut g Gen ) get_ternary_name ( name string ) string {
if g . inside_ternary == 0 {
return name
}
if name ! in g . ternary_names {
return name
}
return g . ternary_names [ name ]
}
2021-09-28 09:28:04 +02:00
fn ( mut g Gen ) gen_clone_assignment ( val ast . Expr , typ ast . Type , add_eq bool ) bool {
2020-07-19 19:58:34 +02:00
if val ! is ast . Ident && val ! is ast . SelectorExpr {
return false
2020-03-31 14:33:16 +02:00
}
2021-12-19 17:25:18 +01:00
right_sym := g . table . sym ( typ )
2021-09-28 09:28:04 +02:00
if g . is_autofree {
2020-03-31 14:33:16 +02:00
if add_eq {
g . write ( ' = ' )
}
2021-09-28 09:28:04 +02:00
if right_sym . kind == . array {
// `arr1 = arr2` => `arr1 = arr2.clone()`
shared_styp := g . typ ( typ . set_nr_muls ( 0 ) )
if typ . share ( ) == . shared_t {
g . write ( ' ( $ shared_styp * ) _ _ d u p _ s h a r e d _ a r r a y ( & ( $ shared_styp ) { . m t x = { 0 } , . v a l = ' )
}
g . write ( ' a r r a y _ c l o n e _ s t a t i c _ t o _ d e p t h ( ' )
g . expr ( val )
if typ . share ( ) == . shared_t {
g . write ( ' - > v a l ' )
}
elem_type := ( right_sym . info as ast . Array ) . elem_type
array_depth := g . get_array_depth ( elem_type )
g . write ( ' , $ array_depth ) ' )
if typ . share ( ) == . shared_t {
g . write ( ' } , s i z e o f ( $ shared_styp ) ) ' )
}
} else if right_sym . kind == . string {
// `str1 = str2` => `str1 = str2.clone()`
g . write ( ' s t r i n g _ c l o n e _ s t a t i c ( ' )
g . expr ( val )
g . write ( ' ) ' )
2020-03-31 14:33:16 +02:00
}
}
return true
}
2020-11-21 18:23:50 +01:00
fn ( mut g Gen ) autofree_scope_vars ( pos int , line_nr int , free_parent_scopes bool ) {
2020-12-05 16:36:27 +01:00
g . autofree_scope_vars_stop ( pos , line_nr , free_parent_scopes , - 1 )
}
fn ( mut g Gen ) autofree_scope_vars_stop ( pos int , line_nr int , free_parent_scopes bool , stop_pos int ) {
2020-11-06 10:13:05 +01:00
if g . is_builtin_mod {
// In `builtin` everything is freed manually.
return
}
2020-11-21 19:07:47 +01:00
if pos == - 1 {
// TODO why can pos be -1?
return
}
2020-05-07 13:00:42 +02:00
// eprintln('> free_scope_vars($pos)')
2020-03-25 10:26:54 +01:00
scope := g . file . scope . innermost ( pos )
2020-11-21 19:07:47 +01:00
if scope . start_pos == 0 {
// TODO why can scope.pos be 0? (only outside fns?)
return
}
2021-05-11 18:29:06 +02:00
g . trace_autofree ( ' / / a u t o f r e e _ s c o p e _ v a r s ( p o s = $ pos l i n e _ n r = $ line_nr s c o p e . p o s = $ scope . start_pos s c o p e . e n d _ p o s = $ scope . end_pos ) ' )
2020-12-05 16:36:27 +01:00
g . autofree_scope_vars2 ( scope , scope . start_pos , scope . end_pos , line_nr , free_parent_scopes ,
stop_pos )
2020-11-09 15:56:20 +01:00
}
2021-06-22 09:30:14 +02:00
[ if trace_autofree ? ]
2021-05-11 18:29:06 +02:00
fn ( mut g Gen ) trace_autofree ( line string ) {
g . writeln ( line )
}
2020-11-16 17:26:54 +01:00
// fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, end_pos int) {
2020-12-05 16:36:27 +01:00
fn ( mut g Gen ) autofree_scope_vars2 ( scope & ast . Scope , start_pos int , end_pos int , line_nr int , free_parent_scopes bool , stop_pos int ) {
2021-07-01 05:21:03 +02:00
if isnil ( scope ) {
2020-11-09 15:56:20 +01:00
return
}
2020-04-04 05:14:40 +02:00
for _ , obj in scope . objects {
2020-11-25 12:09:40 +01:00
match obj {
2020-04-04 05:14:40 +02:00
ast . Var {
2021-05-11 18:29:06 +02:00
g . trace_autofree ( ' / / v a r " $ obj . name " v a r . p o s = $ obj . pos . pos v a r . l i n e _ n r = $ obj . pos . line_nr ' )
2020-12-04 10:10:02 +01:00
if obj . name == g . returned_var_name {
2021-05-11 18:29:06 +02:00
g . trace_autofree ( ' / / s k i p p i n g r e t u r n e d v a r ' )
2020-12-04 10:10:02 +01:00
continue
}
2020-12-04 18:06:53 +01:00
if obj . is_or {
// Skip vars inited with the `or {}`, since they are generated
// after the or block in C.
2021-05-11 18:29:06 +02:00
g . trace_autofree ( ' / / s k i p p i n g ` o r { } ` v a r " $ obj . name " ' )
2020-12-06 08:38:21 +01:00
continue
}
if obj . is_tmp {
// Skip for loop vars
2021-05-11 18:29:06 +02:00
g . trace_autofree ( ' / / s k i p p i n g t m p v a r " $ obj . name " ' )
2020-12-04 18:06:53 +01:00
continue
}
2021-08-10 20:27:15 +02:00
if obj . is_inherited {
g . trace_autofree ( ' / / s k i p p i n g i n h e r i t e d v a r " $ obj . name " ' )
continue
}
2020-04-04 05:14:40 +02:00
// if var.typ == 0 {
// // TODO why 0?
// continue
// }
2020-11-16 17:26:54 +01:00
// if v.pos.pos > end_pos {
2020-11-24 13:58:29 +01:00
if obj . pos . pos > end_pos || ( obj . pos . pos < start_pos && obj . pos . line_nr == line_nr ) {
2020-11-09 15:56:20 +01:00
// Do not free vars that were declared after this scope
continue
}
2020-11-24 13:58:29 +01:00
is_optional := obj . typ . has_flag ( . optional )
2020-05-06 18:03:44 +02:00
if is_optional {
// TODO: free optionals
continue
2020-03-25 10:26:54 +01:00
}
2020-11-24 13:58:29 +01:00
g . autofree_variable ( obj )
2020-04-04 05:14:40 +02:00
}
else { }
2020-03-25 10:26:54 +01:00
}
}
2020-11-09 15:56:20 +01:00
// Free all vars in parent scopes as well:
// ```
// s := ...
// if ... {
// s.free()
// return
// }
// ```
2020-11-16 17:26:54 +01:00
// if !isnil(scope.parent) && line_nr > 0 {
2021-09-28 09:28:04 +02:00
if free_parent_scopes && ! isnil ( scope . parent ) && ! scope . detached_from_parent
2021-01-23 09:33:22 +01:00
&& ( stop_pos == - 1 || scope . parent . start_pos >= stop_pos ) {
2021-05-11 18:29:06 +02:00
g . trace_autofree ( ' / / a f p a r e n t s c o p e : ' )
2020-12-05 16:36:27 +01:00
g . autofree_scope_vars2 ( scope . parent , start_pos , end_pos , line_nr , true , stop_pos )
2020-11-09 15:56:20 +01:00
}
2020-05-06 18:03:44 +02:00
}
2020-07-23 23:16:36 +02:00
fn ( mut g Gen ) autofree_variable ( v ast . Var ) {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( v . typ )
2020-05-07 17:37:10 +02:00
// if v.name.contains('output2') {
2021-09-18 03:19:16 +02:00
if g . is_autofree {
// eprintln(' > var name: ${v.name:-20s} | is_arg: ${v.is_arg.str():6} | var type: ${int(v.typ):8} | type_name: ${sym.name:-33s}')
}
2020-05-07 17:37:10 +02:00
// }
2021-09-28 09:28:04 +02:00
free_fn := g . typ ( v . typ . set_nr_muls ( 0 ) ) + ' _ f r e e '
2020-05-06 18:03:44 +02:00
if sym . kind == . array {
2021-03-18 20:10:42 +01:00
if sym . has_method ( ' f r e e ' ) {
2021-09-28 09:28:04 +02:00
g . autofree_var_call ( free_fn , v )
2021-03-18 20:10:42 +01:00
return
}
2020-07-14 13:56:50 +02:00
g . autofree_var_call ( ' a r r a y _ f r e e ' , v )
return
2020-05-06 18:03:44 +02:00
}
if sym . kind == . string {
// Don't free simple string literals.
match v . expr {
ast . StringLiteral {
2021-05-11 18:29:06 +02:00
g . trace_autofree ( ' / / s t r l i t e r a l ' )
2020-05-06 18:03:44 +02:00
}
else {
// NOTE/TODO: assign_stmt multi returns variables have no expr
// since the type comes from the called fns return type
2020-05-25 08:17:36 +02:00
/ *
f := v . name [ 0 ]
if
//!(f >= `a` && f <= `d`) {
//f != `c` {
v . name != ' c v a r _ n a m e ' {
t := typeof ( v . expr )
2020-05-07 17:51:30 +02:00
return ' / / o t h e r ' + t + ' \n '
2020-05-25 08:17:36 +02:00
}
* /
2020-05-06 18:03:44 +02:00
}
}
2020-07-14 13:56:50 +02:00
g . autofree_var_call ( ' s t r i n g _ f r e e ' , v )
return
2020-05-06 18:03:44 +02:00
}
if sym . has_method ( ' f r e e ' ) {
2021-09-28 09:28:04 +02:00
g . autofree_var_call ( free_fn , v )
2021-09-23 03:35:09 +02:00
} else if g . pref . experimental && v . typ . is_ptr ( ) && sym . name . after ( ' . ' ) [ 0 ] . is_capital ( ) {
2021-09-18 05:32:25 +02:00
// Free user reference types
2021-09-18 03:19:16 +02:00
g . autofree_var_call ( ' f r e e ' , v )
2020-05-06 18:03:44 +02:00
}
}
2020-07-23 23:16:36 +02:00
fn ( mut g Gen ) autofree_var_call ( free_fn_name string , v ast . Var ) {
2020-05-06 18:03:44 +02:00
if v . is_arg {
// fn args should not be autofreed
2020-07-14 13:56:50 +02:00
return
2020-05-06 18:03:44 +02:00
}
2020-10-29 01:09:38 +01:00
if v . is_used && v . is_autofree_tmp {
// tmp expr vars do not need to be freed again here
return
}
2020-12-04 10:10:02 +01:00
if g . is_builtin_mod {
return
}
2021-03-13 16:43:02 +01:00
if ! g . is_autofree {
return
}
2020-11-09 14:24:46 +01:00
// if v.is_autofree_tmp && !g.doing_autofree_tmp {
// return
// }
2021-02-22 16:00:48 +01:00
if v . name . contains ( ' e x p r _ w r i t e _ s t r i n g _ 1 _ ' ) {
2020-11-06 10:13:05 +01:00
// TODO remove this temporary hack
return
}
2020-05-06 18:03:44 +02:00
if v . typ . is_ptr ( ) {
2021-09-28 09:28:04 +02:00
g . write ( ' \t ' )
if v . typ . share ( ) == . shared_t {
g . write ( free_fn_name . replace_each ( [ ' _ _ s h a r e d _ _ ' , ' ' ] ) )
} else {
g . write ( free_fn_name )
}
g . write ( ' ( ' )
if v . typ . share ( ) == . shared_t {
g . write ( ' & ' )
}
g . write ( strings . repeat ( ` * ` , v . typ . nr_muls ( ) - 1 ) ) // dereference if it is a pointer to a pointer
g . write ( c_name ( v . name ) )
if v . typ . share ( ) == . shared_t {
g . write ( ' - > v a l ' )
}
g . writeln ( ' ) ; / / a u t o f r e e d p t r v a r ' )
2020-05-07 13:00:42 +02:00
} else {
2021-04-02 00:57:09 +02:00
if v . typ == ast . error_type && ! v . is_autofree_tmp {
2021-03-17 13:44:45 +01:00
return
}
2020-12-08 19:52:29 +01:00
g . writeln ( ' \t $ { free_fn_name } ( & $ { c_name ( v . name ) } ) ; / / a u t o f r e e d v a r $ g . cur_mod . name $ g . is_builtin_mod ' )
2020-05-06 18:03:44 +02:00
}
2020-03-25 10:26:54 +01:00
}
2021-04-02 00:57:09 +02:00
fn ( mut g Gen ) map_fn_ptrs ( key_typ ast . TypeSymbol ) ( string , string , string , string ) {
2020-12-27 18:31:50 +01:00
mut hash_fn := ' '
mut key_eq_fn := ' '
mut clone_fn := ' '
mut free_fn := ' & m a p _ f r e e _ n o p '
match key_typ . kind {
2021-01-05 02:59:01 +01:00
. byte , . i8 , . char {
2020-12-29 20:02:28 +01:00
hash_fn = ' & m a p _ h a s h _ i n t _ 1 '
key_eq_fn = ' & m a p _ e q _ i n t _ 1 '
clone_fn = ' & m a p _ c l o n e _ i n t _ 1 '
2020-12-27 18:31:50 +01:00
}
. i16 , . u16 {
hash_fn = ' & m a p _ h a s h _ i n t _ 2 '
key_eq_fn = ' & m a p _ e q _ i n t _ 2 '
clone_fn = ' & m a p _ c l o n e _ i n t _ 2 '
}
2021-02-20 18:39:25 +01:00
. int , . u32 , . rune , . f32 , . enum_ {
2020-12-27 18:31:50 +01:00
hash_fn = ' & m a p _ h a s h _ i n t _ 4 '
key_eq_fn = ' & m a p _ e q _ i n t _ 4 '
clone_fn = ' & m a p _ c l o n e _ i n t _ 4 '
}
2021-01-05 02:59:01 +01:00
. voidptr {
ts := if g . pref . m64 {
2021-12-19 17:25:18 +01:00
unsafe { g . table . sym_by_idx ( ast . u64_type_idx ) }
2021-01-05 02:59:01 +01:00
} else {
2021-12-19 17:25:18 +01:00
unsafe { g . table . sym_by_idx ( ast . u32_type_idx ) }
2021-01-05 02:59:01 +01:00
}
return g . map_fn_ptrs ( ts )
}
2021-02-04 23:59:49 +01:00
. u64 , . i64 , . f64 {
2020-12-27 18:31:50 +01:00
hash_fn = ' & m a p _ h a s h _ i n t _ 8 '
key_eq_fn = ' & m a p _ e q _ i n t _ 8 '
clone_fn = ' & m a p _ c l o n e _ i n t _ 8 '
}
. string {
hash_fn = ' & m a p _ h a s h _ s t r i n g '
key_eq_fn = ' & m a p _ e q _ s t r i n g '
clone_fn = ' & m a p _ c l o n e _ s t r i n g '
free_fn = ' & m a p _ f r e e _ s t r i n g '
}
else {
verror ( ' m a p k e y t y p e n o t s u p p o r t e d ' )
}
}
return hash_fn , key_eq_fn , clone_fn , free_fn
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) expr ( node ast . Expr ) {
2020-03-04 15:48:43 +01:00
// println('cgen expr() line_nr=$node.pos.line_nr')
2021-02-28 23:21:03 +01:00
old_discard_or_result := g . discard_or_result
old_is_void_expr_stmt := g . is_void_expr_stmt
if g . is_void_expr_stmt {
g . discard_or_result = true
g . is_void_expr_stmt = false
} else {
g . discard_or_result = false
}
2020-10-21 21:58:40 +02:00
// NB: please keep the type names in the match here in alphabetical order:
2021-02-25 12:16:35 +01:00
match mut node {
2021-03-31 10:13:15 +02:00
ast . EmptyExpr {
g . error ( ' g . e x p r ( ) : u n h a n d l e d E m p t y E x p r ' , token . Position { } )
}
2020-05-15 23:14:53 +02:00
ast . AnonFn {
2021-08-10 20:27:15 +02:00
g . gen_anon_fn ( mut node )
2020-05-15 23:14:53 +02:00
}
2020-12-29 16:14:08 +01:00
ast . ArrayDecompose {
g . expr ( node . expr )
}
2020-02-17 22:50:04 +01:00
ast . ArrayInit {
2020-06-18 20:21:08 +02:00
g . array_init ( node )
2020-02-17 22:50:04 +01:00
}
2020-03-04 15:48:43 +01:00
ast . AsCast {
2020-06-18 20:21:08 +02:00
g . as_cast ( node )
2020-03-04 15:48:43 +01:00
}
ast . Assoc {
2020-06-18 20:21:08 +02:00
g . assoc ( node )
2020-03-04 15:48:43 +01:00
}
2021-10-06 13:32:42 +02:00
ast . AtExpr {
2021-11-15 14:47:29 +01:00
g . comptime_at ( node )
2021-10-06 13:32:42 +02:00
}
2020-02-17 22:50:04 +01:00
ast . BoolLiteral {
2020-06-18 20:21:08 +02:00
g . write ( node . val . str ( ) )
2020-02-17 22:50:04 +01:00
}
2019-12-29 07:24:17 +01:00
ast . CallExpr {
2020-09-27 03:14:24 +02:00
// if g.fileis('1.strings') {
// println('\ncall_expr()()')
// }
2021-02-06 03:07:05 +01:00
ret_type := if node . or_block . kind == . absent {
node . return_type
} else {
node . return_type . clear_flag ( . optional )
}
2021-02-02 13:13:13 +01:00
mut shared_styp := ' '
2021-02-06 19:41:52 +01:00
if g . is_shared && ! ret_type . has_flag ( . shared_f ) {
2021-12-19 17:25:18 +01:00
ret_sym := g . table . sym ( ret_type )
2021-02-06 03:07:05 +01:00
shared_typ := ret_type . set_flag ( . shared_f )
2021-02-02 13:13:13 +01:00
shared_styp = g . typ ( shared_typ )
if ret_sym . kind == . array {
2021-04-19 14:38:48 +02:00
g . writeln ( ' ( $ shared_styp * ) _ _ d u p _ s h a r e d _ a r r a y ( & ( $ shared_styp ) { . m t x = { 0 } , . v a l = ' )
2021-02-02 13:13:13 +01:00
} else if ret_sym . kind == . map {
2021-04-19 14:38:48 +02:00
g . writeln ( ' ( $ shared_styp * ) _ _ d u p _ s h a r e d _ m a p ( & ( $ shared_styp ) { . m t x = { 0 } , . v a l = ' )
2021-02-02 13:13:13 +01:00
} else {
2021-04-19 14:38:48 +02:00
g . writeln ( ' ( $ shared_styp * ) _ _ d u p $ { shared_styp } ( & ( $ shared_styp ) { . m t x = { 0 } , . v a l = ' )
2021-02-02 13:13:13 +01:00
}
}
2021-08-06 02:57:34 +02:00
last_stmt_pos := g . stmt_path_pos . last ( )
2020-06-18 20:21:08 +02:00
g . call_expr ( node )
2020-09-27 03:14:24 +02:00
// if g.fileis('1.strings') {
// println('before:' + node.autofree_pregen)
// }
2021-01-23 09:33:22 +01:00
if g . is_autofree && ! g . is_builtin_mod && ! g . is_js_call && g . strs_to_free0 . len == 0
&& ! g . inside_lambda { // && g.inside_ternary ==
2020-10-18 00:48:06 +02:00
// if len != 0, that means we are handling call expr inside call expr (arg)
// and it'll get messed up here, since it's handled recursively in autofree_call_pregen()
// so just skip it
g . autofree_call_pregen ( node )
if g . strs_to_free0 . len > 0 {
2021-08-06 02:57:34 +02:00
g . insert_at ( last_stmt_pos , g . strs_to_free0 . join ( ' \n ' ) + ' / * i n s e r t e d b e f o r e * / ' )
2020-10-18 00:48:06 +02:00
}
g . strs_to_free0 = [ ]
2020-09-30 07:27:24 +02:00
// println('pos=$node.pos.pos')
}
2021-02-06 19:41:52 +01:00
if g . is_shared && ! ret_type . has_flag ( . shared_f ) {
2021-02-02 13:13:13 +01:00
g . writeln ( ' } , s i z e o f ( $ shared_styp ) ) ' )
}
2021-01-08 13:56:55 +01:00
// if g.autofree && node.autofree_pregen != '' { // g.strs_to_free0.len != 0 {
2020-09-30 07:27:24 +02:00
/ *
2021-01-08 13:56:55 +01:00
if g . autofree {
2020-09-30 07:27:24 +02:00
s := g . autofree_pregen [ node . pos . pos . str ( ) ]
if s != ' ' {
// g.insert_before_stmt('/*START2*/' + g.strs_to_free0.join('\n') + '/*END*/')
// g.insert_before_stmt('/*START3*/' + node.autofree_pregen + '/*END*/')
g . insert_before_stmt ( ' / * S T A R T 3 * / ' + s + ' / * E N D * / ' )
// for s in g.strs_to_free0 {
}
2020-09-27 03:14:24 +02:00
// //g.writeln(s)
// }
g . strs_to_free0 = [ ]
}
2020-09-30 07:27:24 +02:00
* /
2020-03-01 13:07:51 +01:00
}
2020-03-03 18:37:38 +01:00
ast . CastExpr {
2021-04-02 16:34:48 +02:00
g . cast_expr ( node )
2019-12-29 07:24:17 +01:00
}
2020-08-14 21:18:42 +02:00
ast . ChanInit {
elem_typ_str := g . typ ( node . elem_type )
2021-06-14 17:12:47 +02:00
noscan := g . check_noscan ( node . elem_type )
g . write ( ' s y n c _ _ n e w _ c h a n n e l _ s t $ { noscan } ( ' )
2020-08-14 21:18:42 +02:00
if node . has_cap {
g . expr ( node . cap_expr )
} else {
g . write ( ' 0 ' )
}
g . write ( ' , s i z e o f ( ' )
g . write ( elem_typ_str )
g . write ( ' ) ) ' )
}
2020-03-03 18:37:38 +01:00
ast . CharLiteral {
2020-11-06 15:26:59 +01:00
if node . val == r '\`' {
g . write ( " ' ` ' " )
} else {
2021-06-30 08:17:38 +02:00
// TODO: optimize use L-char instead of u32 when possible
2021-05-15 09:36:26 +02:00
if utf8_str_len ( node . val ) < node . val . len {
2021-06-30 21:30:28 +02:00
g . write ( ' ( ( r u n e ) 0 x $ node . val . utf32_code ( ) . hex ( ) / * ` $ node . val ` * / ) ' )
2021-05-15 09:36:26 +02:00
} else {
g . write ( " ' $ node . val ' " )
}
2020-11-06 15:26:59 +01:00
}
2020-03-03 18:37:38 +01:00
}
2021-10-06 13:32:42 +02:00
ast . Comment { }
2020-05-29 04:30:00 +02:00
ast . ComptimeCall {
2021-12-04 18:43:19 +01:00
g . comptime_call ( mut node )
2020-05-29 04:30:00 +02:00
}
2021-01-05 15:11:43 +01:00
ast . ComptimeSelector {
g . comptime_selector ( node )
}
2020-05-15 23:14:53 +02:00
ast . ConcatExpr {
2020-06-18 20:21:08 +02:00
g . concat_expr ( node )
2020-05-15 23:14:53 +02:00
}
2020-10-21 21:58:40 +02:00
ast . CTempVar {
2021-03-09 18:16:18 +01:00
// g.write('/*ctmp .orig: $node.orig.str() , ._typ: $node.typ, .is_ptr: $node.is_ptr */ ')
2020-10-21 21:58:40 +02:00
g . write ( node . name )
}
2021-10-06 13:32:42 +02:00
ast . DumpExpr {
g . dump_expr ( node )
}
2020-03-03 18:37:38 +01:00
ast . EnumVal {
2020-04-12 12:35:54 +02:00
// g.write('${it.mod}${it.enum_name}_$it.val')
2021-05-28 16:59:26 +02:00
// g.enum_expr(node)
2021-12-30 12:42:06 +01:00
styp := g . typ ( g . table . unaliased_type ( node . typ ) )
2021-06-27 18:05:32 +02:00
g . write ( ' $ { styp } _ _ $ node . val ' )
2020-03-03 18:37:38 +01:00
}
ast . FloatLiteral {
2021-09-13 18:06:19 +02:00
if g . pref . nofloat {
g . write ( node . val . int ( ) . str ( ) )
} else {
g . write ( node . val )
}
2020-03-03 18:37:38 +01:00
}
2021-01-15 13:45:26 +01:00
ast . GoExpr {
g . go_expr ( node )
}
2019-12-28 14:11:05 +01:00
ast . Ident {
2020-06-18 20:21:08 +02:00
g . ident ( node )
2019-12-24 18:54:43 +01:00
}
2019-12-29 08:51:55 +01:00
ast . IfExpr {
2020-06-18 20:21:08 +02:00
g . if_expr ( node )
2019-12-29 08:51:55 +01:00
}
2020-03-04 15:48:43 +01:00
ast . IfGuardExpr {
g . write ( ' / * g u a r d * / ' )
}
2020-03-03 18:37:38 +01:00
ast . IndexExpr {
2020-06-18 20:21:08 +02:00
g . index_expr ( node )
2020-03-03 18:37:38 +01:00
}
ast . InfixExpr {
2020-06-24 20:41:26 +02:00
if node . op in [ . left_shift , . plus_assign , . minus_assign ] {
g . inside_map_infix = true
g . infix_expr ( node )
g . inside_map_infix = false
} else {
g . infix_expr ( node )
}
2020-03-03 18:37:38 +01:00
}
ast . IntegerLiteral {
2020-06-18 20:21:08 +02:00
if node . val . starts_with ( ' 0 o ' ) {
2020-03-26 17:14:24 +01:00
g . write ( ' 0 ' )
2020-06-18 20:21:08 +02:00
g . write ( node . val [ 2 .. ] )
2021-01-24 22:09:51 +01:00
} else if node . val . starts_with ( ' - 0 o ' ) {
g . write ( ' - 0 ' )
g . write ( node . val [ 3 .. ] )
2020-04-05 18:03:36 +02:00
} else {
2020-06-18 20:21:08 +02:00
g . write ( node . val ) // .int().str())
2020-03-26 17:14:24 +01:00
}
2020-03-03 18:37:38 +01:00
}
2021-10-06 13:32:42 +02:00
ast . IsRefType {
2021-11-15 14:47:29 +01:00
typ := if node . typ == g . field_data_type {
g . comptime_for_field_value . typ
} else {
node . typ
}
2021-10-06 13:32:42 +02:00
node_typ := g . unwrap_generic ( typ )
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( node_typ )
2021-10-06 13:32:42 +02:00
if sym . language == . v && sym . kind in [ . placeholder , . any ] {
g . error ( ' u n k n o w n t y p e ` $ sym . name ` ' , node . pos )
}
is_ref_type := g . contains_ptr ( node_typ )
g . write ( ' / * I s R e f T y p e * / $ is_ref_type ' )
}
ast . Likely {
if node . is_likely {
g . write ( ' _ l i k e l y _ ' )
} else {
g . write ( ' _ u n l i k e l y _ ' )
}
g . write ( ' ( ' )
g . expr ( node . expr )
g . write ( ' ) ' )
}
2020-07-04 12:44:25 +02:00
ast . LockExpr {
g . lock_expr ( node )
}
2020-03-07 08:13:00 +01:00
ast . MapInit {
2021-02-02 14:42:00 +01:00
g . map_init ( node )
2020-03-07 08:13:00 +01:00
}
2021-10-06 13:32:42 +02:00
ast . MatchExpr {
g . match_expr ( node )
}
2021-03-30 09:33:29 +02:00
ast . NodeError { }
2020-03-04 15:48:43 +01:00
ast . None {
2021-03-13 18:13:50 +01:00
g . write ( ' _ c o n s t _ n o n e _ _ ' )
2020-03-04 15:48:43 +01:00
}
2021-10-06 13:32:42 +02:00
ast . OffsetOf {
styp := g . typ ( node . struct_type )
g . write ( ' / * O f f s e t O f * / ( u 3 2 ) ( _ _ o f f s e t o f ( $ { util . no_dots ( styp ) } , $ node . field ) ) ' )
}
2020-06-16 12:14:22 +02:00
ast . OrExpr {
// this should never appear here
}
2020-03-03 18:37:38 +01:00
ast . ParExpr {
g . write ( ' ( ' )
2020-06-18 20:21:08 +02:00
g . expr ( node . expr )
2020-03-03 18:37:38 +01:00
g . write ( ' ) ' )
}
ast . PostfixExpr {
2020-07-13 14:01:32 +02:00
if node . auto_locked != ' ' {
2021-01-30 15:23:55 +01:00
g . writeln ( ' s y n c _ _ R w M u t e x _ l o c k ( & $ node . auto_locked - > m t x ) ; ' )
2020-07-13 14:01:32 +02:00
}
2020-06-24 20:41:26 +02:00
g . inside_map_postfix = true
2021-02-21 11:15:36 +01:00
if node . expr . is_auto_deref_var ( ) {
2021-02-16 13:24:19 +01:00
g . write ( ' ( * ' )
g . expr ( node . expr )
g . write ( ' ) ' )
} else {
g . expr ( node . expr )
}
2020-06-24 20:41:26 +02:00
g . inside_map_postfix = false
2020-06-18 20:21:08 +02:00
g . write ( node . op . str ( ) )
2020-07-13 14:01:32 +02:00
if node . auto_locked != ' ' {
g . writeln ( ' ; ' )
2021-01-30 15:23:55 +01:00
g . write ( ' s y n c _ _ R w M u t e x _ u n l o c k ( & $ node . auto_locked - > m t x ) ' )
2020-07-13 14:01:32 +02:00
}
2020-03-03 18:37:38 +01:00
}
ast . PrefixExpr {
2021-03-03 09:10:38 +01:00
gen_or := node . op == . arrow && ( node . or_block . kind != . absent || node . is_option )
2020-06-18 20:21:08 +02:00
if node . op == . amp {
2020-03-10 23:21:26 +01:00
g . is_amp = true
}
2020-08-17 23:35:36 +02:00
if node . op == . arrow {
2020-08-23 02:12:05 +02:00
styp := g . typ ( node . right_type )
2021-12-19 17:25:18 +01:00
right_sym := g . table . sym ( node . right_type )
2021-04-02 00:57:09 +02:00
mut right_inf := right_sym . info as ast . Chan
2020-08-23 02:12:05 +02:00
elem_type := right_inf . elem_type
2021-02-28 23:21:03 +01:00
is_gen_or_and_assign_rhs := gen_or && ! g . discard_or_result
2020-08-23 02:12:05 +02:00
cur_line := if is_gen_or_and_assign_rhs {
line := g . go_before_stmt ( 0 )
2021-03-09 12:03:25 +01:00
g . out . write_string ( util . tabs ( g . indent ) )
2020-08-23 02:12:05 +02:00
line
} else {
' '
}
tmp_opt := if gen_or { g . new_tmp_var ( ) } else { ' ' }
if gen_or {
opt_elem_type := g . typ ( elem_type . set_flag ( . optional ) )
g . register_chan_pop_optional_call ( opt_elem_type , styp )
2021-03-14 01:54:46 +01:00
g . write ( ' $ opt_elem_type $ tmp_opt = _ _ O p t i o n _ $ { styp } _ p o p v a l ( ' )
2020-08-23 02:12:05 +02:00
} else {
g . write ( ' _ _ $ { styp } _ p o p v a l ( ' )
}
g . expr ( node . right )
g . write ( ' ) ' )
if gen_or {
2021-03-03 09:10:38 +01:00
if ! node . is_option {
g . or_block ( tmp_opt , node . or_block , elem_type )
}
2020-08-23 02:12:05 +02:00
if is_gen_or_and_assign_rhs {
elem_styp := g . typ ( elem_type )
2021-03-14 01:54:05 +01:00
g . write ( ' ; \n $ cur_line * ( $ elem_styp * ) $ { tmp_opt } . d a t a ' )
2020-08-23 02:12:05 +02:00
}
}
2020-08-17 23:35:36 +02:00
} else {
// g.write('/*pref*/')
2021-04-25 20:40:38 +02:00
if ! ( g . is_amp && node . right . is_auto_deref_var ( ) ) {
g . write ( node . op . str ( ) )
}
2020-08-17 23:35:36 +02:00
// g.write('(')
2020-08-23 02:12:05 +02:00
g . expr ( node . right )
2020-08-17 23:35:36 +02:00
}
2020-03-10 23:21:26 +01:00
g . is_amp = false
2020-03-03 18:37:38 +01:00
}
2020-06-16 12:14:22 +02:00
ast . RangeExpr {
// Only used in IndexExpr
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
g . select_expr ( node )
2020-09-16 15:34:57 +02:00
}
2021-10-06 13:32:42 +02:00
ast . SelectorExpr {
g . selector_expr ( node )
}
2020-03-03 18:37:38 +01:00
ast . SizeOf {
2021-12-28 05:37:03 +01:00
g . size_of ( node )
2020-03-03 18:37:38 +01:00
}
2020-06-16 12:14:22 +02:00
ast . SqlExpr {
2021-07-23 11:33:55 +02:00
g . sql_select_expr ( node )
2020-06-16 12:14:22 +02:00
}
2020-03-03 18:37:38 +01:00
ast . StringLiteral {
2020-06-29 17:47:01 +02:00
g . string_literal ( node )
2020-03-03 18:37:38 +01:00
}
2020-03-21 07:01:06 +01:00
ast . StringInterLiteral {
2020-06-18 20:21:08 +02:00
g . string_inter_literal ( node )
2020-03-21 07:01:06 +01:00
}
2020-03-03 18:37:38 +01:00
ast . StructInit {
2021-02-02 14:42:00 +01:00
if node . unresolved {
g . expr ( ast . resolve_init ( node , g . unwrap_generic ( node . typ ) , g . table ) )
} else {
// `user := User{name: 'Bob'}`
g . struct_init ( node )
}
2020-03-03 18:37:38 +01:00
}
2021-04-02 00:57:09 +02:00
ast . TypeNode {
2020-03-16 03:19:26 +01:00
// match sum Type
// g.write('/* Type */')
2020-11-29 14:10:45 +01:00
// type_idx := node.typ.idx()
2021-07-15 07:29:13 +02:00
typ := g . unwrap_generic ( node . typ )
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( typ )
2021-07-15 07:29:13 +02:00
sidx := g . type_sidx ( typ )
2020-11-29 14:10:45 +01:00
// g.write('$type_idx /* $sym.name */')
g . write ( ' $ sidx / * $ sym . name * / ' )
2020-03-04 15:48:43 +01:00
}
2020-03-19 12:15:39 +01:00
ast . TypeOf {
2020-06-18 20:21:08 +02:00
g . typeof_expr ( node )
2020-03-19 12:15:39 +01:00
}
2020-07-12 12:58:33 +02:00
ast . UnsafeExpr {
2020-09-19 18:18:36 +02:00
g . expr ( node . expr )
2020-07-12 12:58:33 +02:00
}
2019-12-24 18:54:43 +01:00
}
2021-02-28 23:21:03 +01:00
g . discard_or_result = old_discard_or_result
g . is_void_expr_stmt = old_is_void_expr_stmt
2019-12-26 11:27:35 +01:00
}
2020-11-09 14:35:26 +01:00
// T.name, typeof(expr).name
2021-07-21 22:43:30 +02:00
fn ( mut g Gen ) type_name ( raw_type ast . Type ) {
2021-11-15 14:47:29 +01:00
typ := if raw_type == g . field_data_type { g . comptime_for_field_value . typ } else { raw_type }
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( typ )
2021-04-30 12:40:36 +02:00
mut s := ' '
if sym . kind == . function {
if typ . is_ptr ( ) {
s = ' & ' + g . fn_decl_str ( sym . info as ast . FnType )
} else {
s = g . fn_decl_str ( sym . info as ast . FnType )
}
} else {
s = g . table . type_to_str ( g . unwrap_generic ( typ ) )
2020-11-02 22:59:48 +01:00
}
2020-12-03 16:02:48 +01:00
g . write ( ' _ S L I T ( " $ { util . strip_main_name ( s ) } " ) ' )
2020-08-30 08:56:18 +02:00
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) typeof_expr ( node ast . TypeOf ) {
2021-07-21 22:43:30 +02:00
typ := if node . expr_type == g . field_data_type {
2021-11-15 14:47:29 +01:00
g . comptime_for_field_value . typ
2021-07-21 22:43:30 +02:00
} else {
node . expr_type
}
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( typ )
2020-11-25 12:09:40 +01:00
if sym . kind == . sum_type {
2020-11-12 12:27:54 +01:00
// When encountering a .sum_type, typeof() should be done at runtime,
// because the subtype of the expression may change:
2021-08-13 17:37:34 +02:00
g . write ( ' c h a r p t r _ v s t r i n g _ l i t e r a l ( / * $ sym . name * / v _ t y p e o f _ s u m t y p e _ $ { sym . cname } ( ( ' )
2020-11-12 12:27:54 +01:00
g . expr ( node . expr )
2021-03-09 18:16:18 +01:00
g . write ( ' ) . _ t y p ) ) ' )
2020-04-05 18:03:36 +02:00
} else if sym . kind == . array_fixed {
2021-04-02 00:57:09 +02:00
fixed_info := sym . info as ast . ArrayFixed
2020-04-16 21:04:27 +02:00
typ_name := g . table . get_type_name ( fixed_info . elem_type )
2020-12-03 16:02:48 +01:00
g . write ( ' _ S L I T ( " [ $ fixed_info . size ] $ { util . strip_main_name ( typ_name ) } " ) ' )
2020-04-16 21:04:27 +02:00
} else if sym . kind == . function {
2021-04-02 00:57:09 +02:00
info := sym . info as ast . FnType
2020-12-16 10:07:58 +01:00
g . write ( ' _ S L I T ( " $ { g . fn_decl_str ( info ) } " ) ' )
2021-07-21 22:43:30 +02:00
} else if typ . has_flag ( . variadic ) {
2021-12-19 17:25:18 +01:00
varg_elem_type_sym := g . table . sym ( g . table . value_type ( typ ) )
2020-12-29 16:14:08 +01:00
g . write ( ' _ S L I T ( " . . . $ { util . strip_main_name ( varg_elem_type_sym . name ) } " ) ' )
2020-04-05 18:03:36 +02:00
} else {
2021-07-21 22:43:30 +02:00
x := g . table . type_to_str ( typ )
2021-04-06 14:21:11 +02:00
y := util . strip_main_name ( x )
g . write ( ' _ S L I T ( " $ y " ) ' )
2020-03-28 17:37:22 +01:00
}
}
2020-12-23 19:12:49 +01:00
fn ( mut g Gen ) selector_expr ( node ast . SelectorExpr ) {
prevent_sum_type_unwrapping_once := g . prevent_sum_type_unwrapping_once
g . prevent_sum_type_unwrapping_once = false
if node . name_type > 0 {
2021-08-14 07:48:25 +02:00
match node . gkind_field {
. name {
g . type_name ( node . name_type )
return
}
. typ {
g . write ( int ( g . unwrap_generic ( node . name_type ) ) . str ( ) )
return
}
. unknown {
if node . field_name == ' n a m e ' {
// typeof(expr).name
2021-11-15 09:23:49 +01:00
mut name_type := node . name_type
if node . expr is ast . TypeOf {
if node . expr . expr is ast . ComptimeSelector {
if node . expr . expr . field_expr is ast . SelectorExpr {
if node . expr . expr . field_expr . expr is ast . Ident {
key_str := ' $ { node . expr . expr . field_expr . expr . name } . t y p '
name_type = g . comptime_var_type_map [ key_str ] or { name_type }
}
}
}
}
g . type_name ( name_type )
2021-08-14 07:48:25 +02:00
return
2021-08-14 16:22:25 +02:00
} else if node . field_name == ' i d x ' {
// typeof(expr).idx
g . write ( int ( g . unwrap_generic ( node . name_type ) ) . str ( ) )
return
2021-08-14 07:48:25 +02:00
}
g . error ( ' u n k n o w n g e n e r i c f i e l d ' , node . pos )
}
}
2020-12-23 19:12:49 +01:00
}
if node . expr_type == 0 {
g . checker_bug ( ' u n e x p e c t e d S e l e c t o r E x p r . e x p r _ t y p e = 0 ' , node . pos )
}
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( g . unwrap_generic ( node . expr_type ) )
2020-12-23 19:12:49 +01:00
// if node expr is a root ident and an optional
mut is_optional := node . expr is ast . Ident && node . expr_type . has_flag ( . optional )
if is_optional {
opt_base_typ := g . base_type ( node . expr_type )
g . writeln ( ' ( * ( $ opt_base_typ * ) ' )
}
2021-03-09 18:16:18 +01:00
if sym . kind in [ . interface_ , . sum_type ] {
2021-01-26 11:56:17 +01:00
g . write ( ' ( * ( ' )
2021-01-23 07:57:17 +01:00
}
2020-12-23 19:12:49 +01:00
if sym . kind == . array_fixed {
2021-03-06 18:09:28 +01:00
if node . field_name != ' l e n ' {
g . error ( ' f i e l d _ n a m e s h o u l d b e ` l e n ` ' , node . pos )
}
2021-04-02 00:57:09 +02:00
info := sym . info as ast . ArrayFixed
2020-12-23 19:12:49 +01:00
g . write ( ' $ info . size ' )
return
}
2021-01-22 08:37:29 +01:00
if sym . kind == . chan && ( node . field_name == ' l e n ' || node . field_name == ' c l o s e d ' ) {
g . write ( ' s y n c _ _ C h a n n e l _ $ { node . field_name } ( ' )
2020-12-23 19:12:49 +01:00
g . expr ( node . expr )
g . write ( ' ) ' )
return
}
mut sum_type_deref_field := ' '
2021-01-21 21:31:25 +01:00
mut sum_type_dot := ' . '
2021-01-23 07:57:17 +01:00
if f := g . table . find_field ( sym , node . field_name ) {
2021-12-19 17:25:18 +01:00
field_sym := g . table . sym ( f . typ )
2021-04-09 10:00:05 +02:00
if field_sym . kind in [ . sum_type , . interface_ ] {
2020-12-23 19:12:49 +01:00
if ! prevent_sum_type_unwrapping_once {
// check first if field is sum type because scope searching is expensive
scope := g . file . scope . innermost ( node . pos . pos )
2021-06-18 13:49:15 +02:00
if field := scope . find_struct_field ( node . expr . str ( ) , node . expr_type , node . field_name ) {
2021-01-21 21:31:25 +01:00
if field . orig_type . is_ptr ( ) {
sum_type_dot = ' - > '
}
2021-04-09 10:00:05 +02:00
for i , typ in field . smartcasts {
g . write ( ' ( ' )
if field_sym . kind == . sum_type {
g . write ( ' * ' )
}
2021-12-19 17:25:18 +01:00
cast_sym := g . table . sym ( typ )
2020-12-23 19:12:49 +01:00
if i != 0 {
dot := if field . typ . is_ptr ( ) { ' - > ' } else { ' . ' }
sum_type_deref_field += ' ) $ dot '
}
2021-04-02 00:57:09 +02:00
if mut cast_sym . info is ast . Aggregate {
2021-12-19 17:25:18 +01:00
agg_sym := g . table . sym ( cast_sym . info . types [ g . aggregate_type_idx ] )
2020-12-23 19:12:49 +01:00
sum_type_deref_field += ' _ $ agg_sym . cname '
} else {
sum_type_deref_field += ' _ $ cast_sym . cname '
}
}
}
}
}
}
2021-06-21 18:25:18 +02:00
n_ptr := node . expr_type . nr_muls ( ) - 1
if n_ptr > 0 {
g . write ( ' ( ' )
g . write ( ' * ' . repeat ( n_ptr ) )
g . expr ( node . expr )
g . write ( ' ) ' )
} else {
g . expr ( node . expr )
}
2020-12-23 19:12:49 +01:00
if is_optional {
g . write ( ' . d a t a ) ' )
}
// struct embedding
2021-09-16 18:26:53 +02:00
if sym . info in [ ast . Struct , ast . Aggregate ] {
2021-12-04 09:19:19 +01:00
for embed in node . from_embed_types {
2021-12-19 17:25:18 +01:00
embed_sym := g . table . sym ( embed )
2020-12-23 19:12:49 +01:00
embed_name := embed_sym . embed_name ( )
2020-12-30 21:50:58 +01:00
if node . expr_type . is_ptr ( ) {
g . write ( ' - > ' )
} else {
g . write ( ' . ' )
}
g . write ( embed_name )
2020-12-23 19:12:49 +01:00
}
}
2021-12-04 09:19:19 +01:00
if ( node . expr_type . is_ptr ( ) || sym . kind == . chan ) && node . from_embed_types . len == 0 {
2020-12-23 19:12:49 +01:00
g . write ( ' - > ' )
} else {
// g.write('. /*typ= $it.expr_type */') // ${g.typ(it.expr_type)} /')
g . write ( ' . ' )
}
if node . expr_type . has_flag ( . shared_f ) {
g . write ( ' v a l . ' )
}
if node . expr_type == 0 {
verror ( ' c g e n : S e l e c t o r E x p r | e x p r _ t y p e : 0 | i t . e x p r : ` $ node . expr ` | f i e l d : ` $ node . field_name ` | f i l e : $ g . file . path | l i n e : $ node . pos . line_nr ' )
}
2021-07-22 00:37:49 +02:00
field_name := if sym . language == . v { c_name ( node . field_name ) } else { node . field_name }
g . write ( field_name )
2020-12-23 19:12:49 +01:00
if sum_type_deref_field != ' ' {
2021-01-21 21:31:25 +01:00
g . write ( ' $ sum_type_dot $ sum_type_deref_field ) ' )
2020-12-23 19:12:49 +01:00
}
2021-03-09 18:16:18 +01:00
if sym . kind in [ . interface_ , . sum_type ] {
2021-01-26 11:56:17 +01:00
g . write ( ' ) ) ' )
2021-01-23 07:57:17 +01:00
}
2020-12-23 19:12:49 +01:00
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) enum_expr ( node ast . Expr ) {
2020-11-25 12:09:40 +01:00
match node {
2021-05-28 16:59:26 +02:00
ast . EnumVal {
g . write ( node . val )
}
else {
g . expr ( node )
}
2020-04-02 03:27:06 +02:00
}
}
2020-07-04 12:44:25 +02:00
fn ( mut g Gen ) lock_expr ( node ast . LockExpr ) {
2021-05-12 10:44:47 +02:00
g . cur_lock = unsafe { node } // is ok because it is discarded at end of fn
defer {
g . cur_lock = ast . LockExpr {
scope : 0
}
}
2021-02-03 23:56:58 +01:00
tmp_result := if node . is_expr { g . new_tmp_var ( ) } else { ' ' }
mut cur_line := ' '
if node . is_expr {
styp := g . typ ( node . typ )
cur_line = g . go_before_stmt ( 0 )
g . writeln ( ' $ styp $ tmp_result ; ' )
}
2021-02-03 15:16:52 +01:00
mut mtxs := ' '
if node . lockeds . len == 0 {
// this should not happen
} else if node . lockeds . len == 1 {
lock_prefix := if node . is_rlock [ 0 ] { ' r ' } else { ' ' }
2021-05-15 03:34:27 +02:00
g . write ( ' s y n c _ _ R w M u t e x _ $ { lock_prefix } l o c k ( & ' )
g . expr ( node . lockeds [ 0 ] )
g . writeln ( ' - > m t x ) ; ' )
2021-02-03 15:16:52 +01:00
} else {
mtxs = g . new_tmp_var ( )
g . writeln ( ' u i n t p t r _ t _ a r r _ $ mtxs [ $ node . lockeds . len ] ; ' )
g . writeln ( ' b o o l _ i s r l c k _ $ mtxs [ $ node . lockeds . len ] ; ' )
2021-02-03 17:33:18 +01:00
mut j := 0
2021-05-15 03:34:27 +02:00
for i , is_rlock in node . is_rlock {
if ! is_rlock {
g . write ( ' _ a r r _ $ mtxs [ $ j ] = ( u i n t p t r _ t ) & ' )
g . expr ( node . lockeds [ i ] )
g . writeln ( ' - > m t x ; ' )
2021-02-03 17:33:18 +01:00
g . writeln ( ' _ i s r l c k _ $ mtxs [ $ j ] = f a l s e ; ' )
j ++
}
}
2021-05-15 03:34:27 +02:00
for i , is_rlock in node . is_rlock {
if is_rlock {
g . write ( ' _ a r r _ $ mtxs [ $ j ] = ( u i n t p t r _ t ) & ' )
g . expr ( node . lockeds [ i ] )
g . writeln ( ' - > m t x ; ' )
2021-02-03 17:33:18 +01:00
g . writeln ( ' _ i s r l c k _ $ mtxs [ $ j ] = t r u e ; ' )
j ++
}
2021-02-03 15:16:52 +01:00
}
2021-05-15 03:34:27 +02:00
if node . lockeds . len == 2 {
g . writeln ( ' i f ( _ a r r _ $ mtxs [ 0 ] > _ a r r _ $ mtxs [ 1 ] ) { ' )
g . writeln ( ' \t u i n t p t r _ t _ p t r _ $ mtxs = _ a r r _ $ mtxs [ 0 ] ; ' )
g . writeln ( ' \t _ a r r _ $ mtxs [ 0 ] = _ a r r _ $ mtxs [ 1 ] ; ' )
g . writeln ( ' \t _ a r r _ $ mtxs [ 1 ] = _ p t r _ $ mtxs ; ' )
g . writeln ( ' \t b o o l _ b o o l _ $ mtxs = _ i s r l c k _ $ mtxs [ 0 ] ; ' )
g . writeln ( ' \t _ i s r l c k _ $ mtxs [ 0 ] = _ i s r l c k _ $ mtxs [ 1 ] ; ' )
g . writeln ( ' \t _ i s r l c k _ $ mtxs [ 1 ] = _ b o o l _ $ mtxs ; ' )
g . writeln ( ' } ' )
} else {
g . writeln ( ' _ _ s o r t _ p t r ( _ a r r _ $ mtxs , _ i s r l c k _ $ mtxs , $ node . lockeds . len ) ; ' )
}
2021-02-03 15:16:52 +01:00
g . writeln ( ' f o r ( i n t $ mtxs = 0 ; $ mtxs < $ node . lockeds . len ; $ mtxs + + ) { ' )
g . writeln ( ' \t i f ( $ mtxs & & _ a r r _ $ mtxs [ $ mtxs ] = = _ a r r _ $ mtxs [ $ mtxs - 1 ] ) c o n t i n u e ; ' )
g . writeln ( ' \t i f ( _ i s r l c k _ $ mtxs [ $ mtxs ] ) ' )
g . writeln ( ' \t \t s y n c _ _ R w M u t e x _ r l o c k ( ( s y n c _ _ R w M u t e x * ) _ a r r _ $ mtxs [ $ mtxs ] ) ; ' )
g . writeln ( ' \t e l s e ' )
g . writeln ( ' \t \t s y n c _ _ R w M u t e x _ l o c k ( ( s y n c _ _ R w M u t e x * ) _ a r r _ $ mtxs [ $ mtxs ] ) ; ' )
g . writeln ( ' } ' )
2020-07-04 12:44:25 +02:00
}
2021-05-12 10:44:47 +02:00
g . mtxs = mtxs
defer {
g . mtxs = ' '
}
2021-02-03 23:25:01 +01:00
g . writeln ( ' / * l o c k * / { ' )
2021-02-03 23:56:58 +01:00
g . stmts_with_tmp_var ( node . stmts , tmp_result )
2021-04-10 16:57:18 +02:00
if node . is_expr {
g . writeln ( ' ; ' )
}
2021-02-03 23:25:01 +01:00
g . writeln ( ' } ' )
2021-05-12 10:44:47 +02:00
g . unlock_locks ( )
if node . is_expr {
g . writeln ( ' ' )
g . write ( cur_line )
g . write ( ' $ tmp_result ' )
}
}
fn ( mut g Gen ) unlock_locks ( ) {
if g . cur_lock . lockeds . len == 0 {
} else if g . cur_lock . lockeds . len == 1 {
lock_prefix := if g . cur_lock . is_rlock [ 0 ] { ' r ' } else { ' ' }
2021-05-15 03:34:27 +02:00
g . write ( ' s y n c _ _ R w M u t e x _ $ { lock_prefix } u n l o c k ( & ' )
g . expr ( g . cur_lock . lockeds [ 0 ] )
g . write ( ' - > m t x ) ; ' )
2021-02-03 15:16:52 +01:00
} else {
2021-05-12 10:44:47 +02:00
g . writeln ( ' f o r ( i n t $ g . mtxs = $ { g . cur_lock . lockeds . len - 1 } ; $ g . mtxs > = 0 ; $ g . mtxs - - ) { ' )
g . writeln ( ' \t i f ( $ g . mtxs & & _ a r r _ $ g . mtxs [ $ g . mtxs ] = = _ a r r _ $ g . mtxs [ $ g . mtxs - 1 ] ) c o n t i n u e ; ' )
g . writeln ( ' \t i f ( _ i s r l c k _ $ g . mtxs [ $ g . mtxs ] ) ' )
g . writeln ( ' \t \t s y n c _ _ R w M u t e x _ r u n l o c k ( ( s y n c _ _ R w M u t e x * ) _ a r r _ $ g . mtxs [ $ g . mtxs ] ) ; ' )
2021-02-03 15:16:52 +01:00
g . writeln ( ' \t e l s e ' )
2021-05-12 10:44:47 +02:00
g . writeln ( ' \t \t s y n c _ _ R w M u t e x _ u n l o c k ( ( s y n c _ _ R w M u t e x * ) _ a r r _ $ g . mtxs [ $ g . mtxs ] ) ; ' )
2021-02-03 23:25:01 +01:00
g . write ( ' } ' )
2020-07-04 12:44:25 +02:00
}
}
2021-02-21 10:47:46 +01:00
fn ( mut g Gen ) need_tmp_var_in_match ( node ast . MatchExpr ) bool {
2021-04-02 00:57:09 +02:00
if node . is_expr && node . return_type != ast . void_type && node . return_type != 0 {
2021-12-19 17:25:18 +01:00
cond_sym := g . table . final_sym ( node . cond_type )
sym := g . table . sym ( node . return_type )
2021-10-20 10:52:11 +02:00
if g . table . type_kind ( node . return_type ) == . sum_type {
return true
}
2021-12-02 11:22:48 +01:00
if node . return_type . has_flag ( . optional ) {
return true
}
2021-02-21 10:47:46 +01:00
if sym . kind == . multi_return {
return false
}
2021-10-18 09:59:52 +02:00
if cond_sym . kind == . enum_ && node . branches . len > 5 {
return true
}
2021-02-21 10:47:46 +01:00
for branch in node . branches {
if branch . stmts . len > 1 {
return true
}
if branch . stmts . len == 1 {
if branch . stmts [ 0 ] is ast . ExprStmt {
stmt := branch . stmts [ 0 ] as ast . ExprStmt
2021-09-16 18:26:53 +02:00
if stmt . expr in [ ast . CallExpr , ast . IfExpr , ast . MatchExpr ]
|| ( stmt . expr is ast . IndexExpr
2021-05-31 16:10:33 +02:00
&& ( stmt . expr as ast . IndexExpr ) . or_expr . kind != . absent ) {
2021-02-21 10:47:46 +01:00
return true
}
}
}
}
}
return false
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) match_expr ( node ast . MatchExpr ) {
2020-03-16 03:56:38 +01:00
// println('match expr typ=$it.expr_type')
// TODO
2020-03-18 12:23:32 +01:00
if node . cond_type == 0 {
2020-03-16 03:56:38 +01:00
g . writeln ( ' / / m a t c h 0 ' )
return
}
2021-02-21 10:47:46 +01:00
need_tmp_var := g . need_tmp_var_in_match ( node )
2021-04-02 00:57:09 +02:00
is_expr := ( node . is_expr && node . return_type != ast . void_type ) || g . inside_ternary > 0
2021-10-20 10:52:11 +02:00
2021-01-29 14:51:17 +01:00
mut cond_var := ' '
2021-02-21 10:47:46 +01:00
mut tmp_var := ' '
mut cur_line := ' '
if is_expr && ! need_tmp_var {
2020-05-11 15:11:48 +02:00
g . inside_ternary ++
2020-03-18 08:41:49 +01:00
}
2021-12-02 11:22:48 +01:00
if is_expr && node . return_type . has_flag ( . optional ) {
old := g . inside_match_optional
defer {
g . inside_match_optional = old
}
g . inside_match_optional = true
}
2021-09-16 18:26:53 +02:00
if node . cond in [ ast . Ident , ast . SelectorExpr , ast . IntegerLiteral , ast . StringLiteral ,
2021-09-21 15:20:09 +02:00
ast . FloatLiteral ] {
2021-03-08 11:46:39 +01:00
cond_var = g . expr_string ( node . cond )
2020-09-08 00:38:24 +02:00
} else {
2021-02-21 10:47:46 +01:00
line := if is_expr {
2021-01-29 14:51:17 +01:00
g . empty_line = true
g . go_before_stmt ( 0 )
} else {
' '
}
cond_var = g . new_tmp_var ( )
g . write ( ' $ { g . typ ( node . cond_type ) } $ cond_var = ' )
g . expr ( node . cond )
2021-02-23 08:53:07 +01:00
g . writeln ( ' ; ' )
2021-09-26 14:51:18 +02:00
g . set_current_pos_as_last_stmt_pos ( )
2021-02-21 10:47:46 +01:00
g . write ( line )
}
if need_tmp_var {
g . empty_line = true
2021-06-12 20:14:08 +02:00
cur_line = g . go_before_stmt ( 0 ) . trim_left ( ' \t ' )
2021-02-21 10:47:46 +01:00
tmp_var = g . new_tmp_var ( )
2021-07-20 15:09:47 +02:00
g . writeln ( ' $ { g . typ ( node . return_type ) } $ tmp_var = $ { g . type_default ( node . return_type ) } ; ' )
2020-09-08 00:38:24 +02:00
}
2021-01-29 14:51:17 +01:00
2021-02-21 10:47:46 +01:00
if is_expr && ! need_tmp_var {
2020-11-07 14:47:27 +01:00
// brackets needed otherwise '?' will apply to everything on the left
g . write ( ' ( ' )
}
2021-12-19 17:25:18 +01:00
typ := g . table . final_sym ( node . cond_type )
2020-10-04 19:50:23 +02:00
if node . is_sum_type {
2021-02-21 10:47:46 +01:00
g . match_expr_sumtype ( node , is_expr , cond_var , tmp_var )
2021-10-19 16:02:22 +02:00
} else if typ . kind == . enum_ && g . loop_depth == 0 && node . branches . len > 5 && g . fn_decl != 0 { // do not optimize while in top-level
2021-10-18 09:59:52 +02:00
g . match_expr_switch ( node , is_expr , cond_var , tmp_var , typ )
2020-10-04 19:50:23 +02:00
} else {
2021-02-21 10:47:46 +01:00
g . match_expr_classic ( node , is_expr , cond_var , tmp_var )
}
2021-11-19 12:52:28 +01:00
g . set_current_pos_as_last_stmt_pos ( )
2021-02-21 10:47:46 +01:00
g . write ( cur_line )
if need_tmp_var {
g . write ( ' $ tmp_var ' )
2020-10-04 19:50:23 +02:00
}
2021-02-21 10:47:46 +01:00
if is_expr && ! need_tmp_var {
2020-11-07 14:47:27 +01:00
g . write ( ' ) ' )
2020-10-04 19:50:23 +02:00
g . decrement_inside_ternary ( )
}
}
2021-02-21 10:47:46 +01:00
fn ( mut g Gen ) match_expr_sumtype ( node ast . MatchExpr , is_expr bool , cond_var string , tmp_var string ) {
2020-03-16 03:56:38 +01:00
for j , branch in node . branches {
2020-10-03 11:19:43 +02:00
mut sumtype_index := 0
// iterates through all types in sumtype branches
for {
2020-11-18 20:52:00 +01:00
g . aggregate_type_idx = sumtype_index
2020-10-03 11:19:43 +02:00
is_last := j == node . branches . len - 1
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( node . cond_type )
2021-02-21 10:47:46 +01:00
if branch . is_else || ( node . is_expr && is_last && tmp_var . len == 0 ) {
if is_expr && tmp_var . len == 0 {
2020-10-04 19:50:23 +02:00
// TODO too many branches. maybe separate ?: matches
g . write ( ' : ' )
} else {
2020-12-12 23:08:45 +01:00
g . writeln ( ' ' )
g . write_v_source_line_info ( branch . pos )
g . writeln ( ' e l s e { ' )
2020-10-03 11:19:43 +02:00
}
} else {
2020-10-04 19:50:23 +02:00
if j > 0 || sumtype_index > 0 {
2021-02-21 10:47:46 +01:00
if is_expr && tmp_var . len == 0 {
2020-10-03 11:19:43 +02:00
g . write ( ' : ' )
} else {
2020-12-12 23:08:45 +01:00
g . write_v_source_line_info ( branch . pos )
g . write ( ' e l s e ' )
2020-10-03 11:19:43 +02:00
}
2020-04-13 01:53:26 +02:00
}
2021-02-21 10:47:46 +01:00
if is_expr && tmp_var . len == 0 {
2020-10-03 11:19:43 +02:00
g . write ( ' ( ' )
2020-04-05 18:15:12 +02:00
} else {
2021-02-12 03:37:31 +01:00
if j == 0 && sumtype_index == 0 {
2021-03-14 01:54:05 +01:00
g . empty_line = true
2021-02-12 03:37:31 +01:00
}
2020-12-12 23:08:45 +01:00
g . write_v_source_line_info ( branch . pos )
2020-10-03 11:19:43 +02:00
g . write ( ' i f ( ' )
2020-04-05 18:15:12 +02:00
}
2020-10-04 19:50:23 +02:00
g . write ( cond_var )
2021-02-22 13:55:43 +01:00
dot_or_ptr := if node . cond_type . is_ptr ( ) { ' - > ' } else { ' . ' }
2020-11-25 12:09:40 +01:00
if sym . kind == . sum_type {
2021-03-09 18:16:18 +01:00
g . write ( ' $ { dot_or_ptr } _ t y p = = ' )
2021-02-22 13:55:43 +01:00
g . expr ( branch . exprs [ sumtype_index ] )
2020-10-04 19:50:23 +02:00
} else if sym . kind == . interface_ {
2021-04-02 00:57:09 +02:00
if branch . exprs [ sumtype_index ] is ast . TypeNode {
typ := branch . exprs [ sumtype_index ] as ast . TypeNode
2021-12-19 17:25:18 +01:00
branch_sym := g . table . sym ( g . unwrap_generic ( typ . typ ) )
2021-04-09 10:00:05 +02:00
g . write ( ' $ { dot_or_ptr } _ t y p = = _ $ { sym . cname } _ $ { branch_sym . cname } _ i n d e x ' )
2021-03-13 18:13:50 +01:00
} else if branch . exprs [ sumtype_index ] is ast . None && sym . name == ' I E r r o r ' {
2021-04-09 10:00:05 +02:00
g . write ( ' $ { dot_or_ptr } _ t y p = = _ I E r r o r _ N o n e _ _ _ i n d e x ' )
2021-03-13 18:13:50 +01:00
}
2020-10-04 19:50:23 +02:00
}
2021-02-21 10:47:46 +01:00
if is_expr && tmp_var . len == 0 {
2020-10-03 11:19:43 +02:00
g . write ( ' ) ? ' )
} else {
g . writeln ( ' ) { ' )
}
}
2021-12-19 17:25:18 +01:00
if is_expr && tmp_var . len > 0 && g . table . sym ( node . return_type ) . kind == . sum_type {
2021-05-27 09:13:50 +02:00
g . expected_cast_type = node . return_type
}
2021-02-21 10:47:46 +01:00
g . stmts_with_tmp_var ( branch . stmts , tmp_var )
2021-05-27 09:13:50 +02:00
g . expected_cast_type = 0
2020-10-04 19:50:23 +02:00
if g . inside_ternary == 0 {
2021-06-07 13:16:30 +02:00
g . writeln ( ' } ' )
2021-09-26 14:51:18 +02:00
g . set_current_pos_as_last_stmt_pos ( )
2020-10-03 11:19:43 +02:00
}
2020-10-04 19:50:23 +02:00
sumtype_index ++
if branch . exprs . len == 0 || sumtype_index == branch . exprs . len {
2020-10-03 11:19:43 +02:00
break
2020-04-05 18:03:36 +02:00
}
2020-03-18 08:41:49 +01:00
}
2020-11-18 20:52:00 +01:00
// reset global field for next use
g . aggregate_type_idx = 0
2020-03-16 03:56:38 +01:00
}
2020-10-04 19:50:23 +02:00
}
2021-10-18 09:59:52 +02:00
fn ( mut g Gen ) match_expr_switch ( node ast . MatchExpr , is_expr bool , cond_var string , tmp_var string , enum_typ ast . TypeSymbol ) {
cname := ' $ { enum_typ . cname } _ _ '
2021-10-19 16:02:22 +02:00
mut covered_enum := [ ] string { cap : ( enum_typ . info as ast . Enum ) . vals . len } // collects missing enum variant branches to avoid cstrict errors
mut range_branches := [ ] ast . MatchBranch { cap : node . branches . len } // branches have RangeExpr cannot emit as switch case branch, we handle it in default branch
mut default_generated := false
2021-10-18 09:59:52 +02:00
g . empty_line = true
g . writeln ( ' s w i t c h ( $ cond_var ) { ' )
g . indent ++
for branch in node . branches {
if branch . is_else {
for val in ( enum_typ . info as ast . Enum ) . vals {
if val ! in covered_enum {
g . writeln ( ' c a s e $ cname $ val : ' )
}
}
g . writeln ( ' d e f a u l t : ' )
2021-10-19 16:02:22 +02:00
default_generated = true
if range_branches . len > 0 {
g . indent ++
for range_branch in range_branches {
g . write ( ' i f ( ' )
for i , expr in range_branch . exprs {
if i > 0 {
g . write ( ' | | ' )
}
if expr is ast . RangeExpr {
// if type is unsigned and low is 0, check is unneeded
mut skip_low := false
if expr . low is ast . IntegerLiteral {
if node . cond_type in [ ast . u16_type , ast . u32_type , ast . u64_type ]
&& expr . low . val == ' 0 ' {
skip_low = true
}
}
g . write ( ' ( ' )
if ! skip_low {
g . write ( ' $ cond_var > = ' )
g . expr ( expr . low )
g . write ( ' & & ' )
}
g . write ( ' $ cond_var < = ' )
g . expr ( expr . high )
g . write ( ' ) ' )
} else {
g . write ( ' $ cond_var = = ( ' )
g . expr ( expr )
g . write ( ' ) ' )
}
}
g . writeln ( ' ) { ' )
g . stmts_with_tmp_var ( range_branch . stmts , tmp_var )
2021-10-21 21:30:05 +02:00
g . writeln ( ' b r e a k ; ' )
2021-10-19 16:02:22 +02:00
g . writeln ( ' } ' )
}
g . indent --
}
2021-10-18 09:59:52 +02:00
} else {
2021-10-19 16:02:22 +02:00
if branch . exprs . any ( it is ast . RangeExpr ) {
range_branches << branch
continue
}
2021-10-18 09:59:52 +02:00
for expr in branch . exprs {
if expr is ast . EnumVal {
2021-10-19 16:02:22 +02:00
covered_enum << expr . val
2021-10-18 09:59:52 +02:00
g . write ( ' c a s e ' )
g . expr ( expr )
g . writeln ( ' : ' )
}
}
}
g . indent ++
g . writeln ( ' { ' )
2021-12-19 17:25:18 +01:00
if is_expr && tmp_var . len > 0 && g . table . sym ( node . return_type ) . kind == . sum_type {
2021-10-20 10:52:11 +02:00
g . expected_cast_type = node . return_type
}
2021-10-18 09:59:52 +02:00
g . stmts_with_tmp_var ( branch . stmts , tmp_var )
2021-10-20 10:52:11 +02:00
g . expected_cast_type = 0
2021-10-18 09:59:52 +02:00
g . writeln ( ' } b r e a k ; ' )
g . indent --
}
2021-10-19 16:02:22 +02:00
if range_branches . len > 0 && ! default_generated {
g . writeln ( ' d e f a u l t : ' )
g . indent ++
for range_branch in range_branches {
g . write ( ' i f ( ' )
for i , expr in range_branch . exprs {
if i > 0 {
g . write ( ' | | ' )
}
if expr is ast . RangeExpr {
// if type is unsigned and low is 0, check is unneeded
mut skip_low := false
if expr . low is ast . IntegerLiteral {
if node . cond_type in [ ast . u16_type , ast . u32_type , ast . u64_type ]
&& expr . low . val == ' 0 ' {
skip_low = true
}
}
g . write ( ' ( ' )
if ! skip_low {
g . write ( ' $ cond_var > = ' )
g . expr ( expr . low )
g . write ( ' & & ' )
}
g . write ( ' $ cond_var < = ' )
g . expr ( expr . high )
g . write ( ' ) ' )
} else {
g . write ( ' $ cond_var = = ( ' )
g . expr ( expr )
g . write ( ' ) ' )
}
}
g . writeln ( ' ) { ' )
g . stmts_with_tmp_var ( range_branch . stmts , tmp_var )
2021-10-21 21:30:05 +02:00
g . writeln ( ' b r e a k ; ' )
2021-10-19 16:02:22 +02:00
g . writeln ( ' } ' )
}
g . indent --
}
2021-10-18 09:59:52 +02:00
g . indent --
g . writeln ( ' } ' )
}
2021-02-21 10:47:46 +01:00
fn ( mut g Gen ) match_expr_classic ( node ast . MatchExpr , is_expr bool , cond_var string , tmp_var string ) {
2021-12-19 17:25:18 +01:00
type_sym := g . table . sym ( node . cond_type )
2020-10-04 19:50:23 +02:00
for j , branch in node . branches {
is_last := j == node . branches . len - 1
2021-02-21 10:47:46 +01:00
if branch . is_else || ( node . is_expr && is_last && tmp_var . len == 0 ) {
2020-10-04 19:50:23 +02:00
if node . branches . len > 1 {
2021-02-21 10:47:46 +01:00
if is_expr && tmp_var . len == 0 {
2020-10-04 19:50:23 +02:00
// TODO too many branches. maybe separate ?: matches
g . write ( ' : ' )
} else {
2020-12-12 23:08:45 +01:00
g . writeln ( ' ' )
g . write_v_source_line_info ( branch . pos )
g . writeln ( ' e l s e { ' )
2020-10-04 19:50:23 +02:00
}
}
} else {
if j > 0 {
2021-02-21 10:47:46 +01:00
if is_expr && tmp_var . len == 0 {
2020-10-04 19:50:23 +02:00
g . write ( ' : ' )
} else {
2020-12-12 23:08:45 +01:00
g . writeln ( ' ' )
g . write_v_source_line_info ( branch . pos )
g . write ( ' e l s e ' )
2020-10-04 19:50:23 +02:00
}
}
2021-02-21 10:47:46 +01:00
if is_expr && tmp_var . len == 0 {
2020-10-04 19:50:23 +02:00
g . write ( ' ( ' )
} else {
2021-02-12 03:37:31 +01:00
if j == 0 {
g . writeln ( ' ' )
}
2020-12-12 23:08:45 +01:00
g . write_v_source_line_info ( branch . pos )
2020-10-04 19:50:23 +02:00
g . write ( ' i f ( ' )
}
for i , expr in branch . exprs {
if i > 0 {
g . write ( ' | | ' )
}
2021-06-13 05:27:31 +02:00
match type_sym . kind {
. array {
2021-09-28 09:28:04 +02:00
ptr_typ := g . equality_fn ( node . cond_type )
2021-06-13 05:27:31 +02:00
g . write ( ' $ { ptr_typ } _ a r r _ e q ( $ cond_var , ' )
2020-10-04 19:50:23 +02:00
g . expr ( expr )
g . write ( ' ) ' )
}
2021-06-13 05:27:31 +02:00
. array_fixed {
2021-09-28 09:28:04 +02:00
ptr_typ := g . equality_fn ( node . cond_type )
2021-06-13 05:27:31 +02:00
g . write ( ' $ { ptr_typ } _ a r r _ e q ( $ cond_var , ' )
g . expr ( expr )
g . write ( ' ) ' )
2020-10-04 19:50:23 +02:00
}
2021-06-13 05:27:31 +02:00
. map {
2021-09-28 09:28:04 +02:00
ptr_typ := g . equality_fn ( node . cond_type )
2021-06-13 05:27:31 +02:00
g . write ( ' $ { ptr_typ } _ m a p _ e q ( $ cond_var , ' )
g . expr ( expr )
g . write ( ' ) ' )
}
. string {
g . write ( ' s t r i n g _ _ e q ( $ cond_var , ' )
g . expr ( expr )
g . write ( ' ) ' )
}
. struct_ {
2021-09-28 09:28:04 +02:00
ptr_typ := g . equality_fn ( node . cond_type )
2021-06-13 05:27:31 +02:00
g . write ( ' $ { ptr_typ } _ s t r u c t _ e q ( $ cond_var , ' )
g . expr ( expr )
g . write ( ' ) ' )
}
else {
if expr is ast . RangeExpr {
// if type is unsigned and low is 0, check is unneeded
mut skip_low := false
if expr . low is ast . IntegerLiteral {
if node . cond_type in [ ast . u16_type , ast . u32_type , ast . u64_type ]
&& expr . low . val == ' 0 ' {
skip_low = true
}
}
g . write ( ' ( ' )
if ! skip_low {
g . write ( ' $ cond_var > = ' )
g . expr ( expr . low )
g . write ( ' & & ' )
}
g . write ( ' $ cond_var < = ' )
g . expr ( expr . high )
g . write ( ' ) ' )
} else {
g . write ( ' $ cond_var = = ( ' )
g . expr ( expr )
g . write ( ' ) ' )
}
2020-10-04 19:50:23 +02:00
}
}
}
2021-02-21 10:47:46 +01:00
if is_expr && tmp_var . len == 0 {
2020-10-04 19:50:23 +02:00
g . write ( ' ) ? ' )
} else {
g . writeln ( ' ) { ' )
}
}
2021-12-19 17:25:18 +01:00
if is_expr && tmp_var . len > 0 && g . table . sym ( node . return_type ) . kind == . sum_type {
2021-10-20 10:52:11 +02:00
g . expected_cast_type = node . return_type
}
2021-02-21 10:47:46 +01:00
g . stmts_with_tmp_var ( branch . stmts , tmp_var )
2021-10-20 10:52:11 +02:00
g . expected_cast_type = 0
2021-03-10 17:44:32 +01:00
if g . inside_ternary == 0 && node . branches . len >= 1 {
2020-10-04 19:50:23 +02:00
g . write ( ' } ' )
}
2020-05-11 15:11:48 +02:00
}
2020-03-16 03:56:38 +01:00
}
2021-02-02 14:42:00 +01:00
fn ( mut g Gen ) map_init ( node ast . MapInit ) {
2021-12-16 08:53:05 +01:00
unwrap_key_typ := g . unwrap_generic ( node . key_type )
unwrap_val_typ := g . unwrap_generic ( node . value_type )
key_typ_str := g . typ ( unwrap_key_typ )
value_typ_str := g . typ ( unwrap_val_typ )
2021-12-19 17:25:18 +01:00
value_typ := g . table . sym ( unwrap_val_typ )
key_typ := g . table . final_sym ( unwrap_key_typ )
2021-02-02 14:42:00 +01:00
hash_fn , key_eq_fn , clone_fn , free_fn := g . map_fn_ptrs ( key_typ )
size := node . vals . len
mut shared_styp := ' ' // only needed for shared &[]{...}
mut styp := ' '
is_amp := g . is_amp
g . is_amp = false
if is_amp {
g . out . go_back ( 1 ) // delete the `&` already generated in `prefix_expr()
}
if g . is_shared {
mut shared_typ := node . typ . set_flag ( . shared_f )
shared_styp = g . typ ( shared_typ )
2021-04-19 14:38:48 +02:00
g . writeln ( ' ( $ shared_styp * ) _ _ d u p _ s h a r e d _ m a p ( & ( $ shared_styp ) { . m t x = { 0 } , . v a l = ' )
2021-02-02 14:42:00 +01:00
} else if is_amp {
styp = g . typ ( node . typ )
g . write ( ' ( $ styp * ) m e m d u p ( A D D R ( $ styp , ' )
}
2021-06-13 05:26:43 +02:00
noscan_key := g . check_noscan ( node . key_type )
noscan_value := g . check_noscan ( node . value_type )
mut noscan := if noscan_key . len != 0 || noscan_value . len != 0 { ' _ n o s c a n ' } else { ' ' }
if noscan . len != 0 {
if noscan_key . len != 0 {
noscan += ' _ k e y '
}
if noscan_value . len != 0 {
noscan += ' _ v a l u e '
}
}
2021-02-02 14:42:00 +01:00
if size > 0 {
if value_typ . kind == . function {
2021-06-13 05:26:43 +02:00
g . write ( ' n e w _ m a p _ i n i t $ { noscan } ( $ hash_fn , $ key_eq_fn , $ clone_fn , $ free_fn , $ size , s i z e o f ( $ key_typ_str ) , s i z e o f ( v o i d p t r ) , _ M O V ( ( $ key_typ_str [ $ size ] ) { ' )
2021-02-02 14:42:00 +01:00
} else {
2021-06-13 05:26:43 +02:00
g . write ( ' n e w _ m a p _ i n i t $ { noscan } ( $ hash_fn , $ key_eq_fn , $ clone_fn , $ free_fn , $ size , s i z e o f ( $ key_typ_str ) , s i z e o f ( $ value_typ_str ) , _ M O V ( ( $ key_typ_str [ $ size ] ) { ' )
2021-02-02 14:42:00 +01:00
}
for expr in node . keys {
g . expr ( expr )
g . write ( ' , ' )
}
if value_typ . kind == . function {
g . write ( ' } ) , _ M O V ( ( v o i d p t r [ $ size ] ) { ' )
} else {
g . write ( ' } ) , _ M O V ( ( $ value_typ_str [ $ size ] ) { ' )
}
for expr in node . vals {
2021-02-21 11:15:36 +01:00
if expr . is_auto_deref_var ( ) {
2021-02-16 13:24:19 +01:00
g . write ( ' * ' )
}
2021-02-02 14:42:00 +01:00
g . expr ( expr )
g . write ( ' , ' )
}
g . write ( ' } ) ) ' )
} else {
2021-06-13 05:26:43 +02:00
g . write ( ' n e w _ m a p $ { noscan } ( s i z e o f ( $ key_typ_str ) , s i z e o f ( $ value_typ_str ) , $ hash_fn , $ key_eq_fn , $ clone_fn , $ free_fn ) ' )
2021-02-02 14:42:00 +01:00
}
if g . is_shared {
g . write ( ' } , s i z e o f ( $ shared_styp ) ) ' )
} else if is_amp {
g . write ( ' ) , s i z e o f ( $ styp ) ) ' )
}
}
2020-09-19 02:14:35 +02:00
fn ( mut g Gen ) select_expr ( node ast . SelectExpr ) {
is_expr := node . is_expr || g . inside_ternary > 0
cur_line := if is_expr {
g . empty_line = true
g . go_before_stmt ( 0 )
} else {
' '
}
n_channels := if node . has_exception { node . branches . len - 1 } else { node . branches . len }
mut channels := [ ] ast . Expr { cap : n_channels }
mut objs := [ ] ast . Expr { cap : n_channels }
mut tmp_objs := [ ] string { cap : n_channels }
mut elem_types := [ ] string { cap : n_channels }
mut is_push := [ ] bool { cap : n_channels }
mut has_else := false
mut has_timeout := false
2021-03-31 10:13:15 +02:00
mut timeout_expr := ast . empty_expr ( )
2020-09-19 02:14:35 +02:00
mut exception_branch := - 1
for j , branch in node . branches {
if branch . is_else {
has_else = true
exception_branch = j
} else if branch . is_timeout {
has_timeout = true
exception_branch = j
timeout_expr = ( branch . stmt as ast . ExprStmt ) . expr
} else {
2020-11-25 12:09:40 +01:00
match branch . stmt {
2020-09-19 02:14:35 +02:00
ast . ExprStmt {
2020-09-20 03:50:09 +02:00
// send expression
2020-11-24 13:58:29 +01:00
expr := branch . stmt . expr as ast . InfixExpr
2020-09-19 02:14:35 +02:00
channels << expr . left
2021-09-16 18:26:53 +02:00
if expr . right in [ ast . Ident , ast . IndexExpr , ast . SelectorExpr , ast . StructInit ] {
2020-09-20 03:50:09 +02:00
// addressable objects in the `C` output
objs << expr . right
tmp_objs << ' '
elem_types << ' '
} else {
// must be evaluated to tmp var before real `select` is performed
2021-03-31 10:13:15 +02:00
objs << ast . empty_expr ( )
2020-09-20 03:50:09 +02:00
tmp_obj := g . new_tmp_var ( )
tmp_objs << tmp_obj
2022-01-04 17:37:18 +01:00
el_stype := g . typ ( ast . mktyp ( expr . right_type ) )
2020-09-20 03:50:09 +02:00
g . writeln ( ' $ el_stype $ tmp_obj ; ' )
}
2020-09-19 02:14:35 +02:00
is_push << true
}
ast . AssignStmt {
2020-11-24 13:58:29 +01:00
rec_expr := branch . stmt . right [ 0 ] as ast . PrefixExpr
2020-09-19 02:14:35 +02:00
channels << rec_expr . right
is_push << false
// create tmp unless the object with *exactly* the type we need exists already
2021-01-23 09:33:22 +01:00
if branch . stmt . op == . decl_assign
|| branch . stmt . right_types [ 0 ] != branch . stmt . left_types [ 0 ] {
2020-09-19 02:14:35 +02:00
tmp_obj := g . new_tmp_var ( )
tmp_objs << tmp_obj
2020-11-24 13:58:29 +01:00
el_stype := g . typ ( branch . stmt . right_types [ 0 ] )
elem_types << if branch . stmt . op == . decl_assign {
2020-09-19 02:14:35 +02:00
el_stype + ' '
} else {
' '
}
g . writeln ( ' $ el_stype $ tmp_obj ; ' )
} else {
tmp_objs << ' '
elem_types << ' '
}
2020-11-24 13:58:29 +01:00
objs << branch . stmt . left [ 0 ]
2020-09-19 02:14:35 +02:00
}
else { }
}
}
}
chan_array := g . new_tmp_var ( )
2021-02-15 14:51:57 +01:00
g . write ( ' A r r a y _ s y n c _ _ C h a n n e l _ p t r $ chan_array = n e w _ a r r a y _ f r o m _ c _ a r r a y ( $ n_channels , $ n_channels , s i z e o f ( s y n c _ _ C h a n n e l * ) , _ M O V ( ( s y n c _ _ C h a n n e l * [ $ n_channels ] ) { ' )
2020-09-19 02:14:35 +02:00
for i in 0 .. n_channels {
if i > 0 {
g . write ( ' , ' )
}
g . write ( ' ( s y n c _ _ C h a n n e l * ) ( ' )
g . expr ( channels [ i ] )
g . write ( ' ) ' )
}
2021-05-24 04:20:45 +02:00
g . writeln ( ' } ) ) ; \n ' )
2020-09-19 02:14:35 +02:00
directions_array := g . new_tmp_var ( )
2021-02-15 14:51:57 +01:00
g . write ( ' A r r a y _ s y n c _ _ D i r e c t i o n $ directions_array = n e w _ a r r a y _ f r o m _ c _ a r r a y ( $ n_channels , $ n_channels , s i z e o f ( s y n c _ _ D i r e c t i o n ) , _ M O V ( ( s y n c _ _ D i r e c t i o n [ $ n_channels ] ) { ' )
2020-09-19 02:14:35 +02:00
for i in 0 .. n_channels {
if i > 0 {
g . write ( ' , ' )
}
if is_push [ i ] {
2021-06-27 18:05:32 +02:00
g . write ( ' s y n c _ _ D i r e c t i o n _ _ p u s h ' )
2020-09-19 02:14:35 +02:00
} else {
2021-06-27 18:05:32 +02:00
g . write ( ' s y n c _ _ D i r e c t i o n _ _ p o p ' )
2020-09-19 02:14:35 +02:00
}
}
2021-05-24 04:20:45 +02:00
g . writeln ( ' } ) ) ; \n ' )
2020-09-19 02:14:35 +02:00
objs_array := g . new_tmp_var ( )
2021-02-15 14:51:57 +01:00
g . write ( ' A r r a y _ v o i d p t r $ objs_array = n e w _ a r r a y _ f r o m _ c _ a r r a y ( $ n_channels , $ n_channels , s i z e o f ( v o i d p t r ) , _ M O V ( ( v o i d p t r [ $ n_channels ] ) { ' )
2020-09-19 02:14:35 +02:00
for i in 0 .. n_channels {
2021-03-14 08:37:38 +01:00
if i > 0 {
g . write ( ' , & ' )
} else {
g . write ( ' & ' )
}
2020-09-19 02:14:35 +02:00
if tmp_objs [ i ] == ' ' {
g . expr ( objs [ i ] )
} else {
g . write ( tmp_objs [ i ] )
}
}
2021-05-24 04:20:45 +02:00
g . writeln ( ' } ) ) ; \n ' )
2020-09-19 02:14:35 +02:00
select_result := g . new_tmp_var ( )
g . write ( ' i n t $ select_result = s y n c _ _ c h a n n e l _ s e l e c t ( & / * a r r * / $ chan_array , $ directions_array , & / * a r r * / $ objs_array , ' )
if has_timeout {
g . expr ( timeout_expr )
} else if has_else {
g . write ( ' 0 ' )
} else {
2021-06-22 11:17:44 +02:00
g . write ( ' _ c o n s t _ t i m e _ _ i n f i n i t e ' )
2020-09-19 02:14:35 +02:00
}
g . writeln ( ' ) ; ' )
2021-01-24 20:02:17 +01:00
// free the temps that were created
g . writeln ( ' a r r a y _ f r e e ( & $ objs_array ) ; ' )
g . writeln ( ' a r r a y _ f r e e ( & $ directions_array ) ; ' )
g . writeln ( ' a r r a y _ f r e e ( & $ chan_array ) ; ' )
2020-09-19 02:14:35 +02:00
mut i := 0
for j in 0 .. node . branches . len {
if j > 0 {
g . write ( ' } e l s e ' )
}
g . write ( ' i f ( $ select_result = = ' )
if j == exception_branch {
g . writeln ( ' - 1 ) { ' )
} else {
g . writeln ( ' $ i ) { ' )
2020-09-20 03:50:09 +02:00
if ! is_push [ i ] && tmp_objs [ i ] != ' ' {
2020-09-19 02:14:35 +02:00
g . write ( ' \t $ { elem_types [ i ] } ' )
g . expr ( objs [ i ] )
g . writeln ( ' = $ { tmp_objs [ i ] } ; ' )
}
i ++
}
g . stmts ( node . branches [ j ] . stmts )
}
g . writeln ( ' } ' )
if is_expr {
2020-09-23 06:05:12 +02:00
g . empty_line = false
2020-09-19 02:14:35 +02:00
g . write ( cur_line )
g . write ( ' ( $ select_result ! = - 2 ) ' )
}
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) ident ( node ast . Ident ) {
2020-11-11 09:18:15 +01:00
prevent_sum_type_unwrapping_once := g . prevent_sum_type_unwrapping_once
g . prevent_sum_type_unwrapping_once = false
2020-03-23 03:36:50 +01:00
if node . name == ' l l d ' {
2020-03-20 20:46:42 +01:00
return
}
2020-03-23 03:36:50 +01:00
if node . name . starts_with ( ' C . ' ) {
2020-07-01 00:53:53 +02:00
g . write ( util . no_dots ( node . name [ 2 .. ] ) )
2020-03-24 22:23:59 +01:00
return
2020-03-13 05:57:51 +01:00
}
2020-06-02 15:49:43 +02:00
if node . kind == . constant { // && !node.name.starts_with('g_') {
2020-03-24 22:23:59 +01:00
// TODO globals hack
g . write ( ' _ c o n s t _ ' )
}
2020-05-14 17:15:25 +02:00
mut name := c_name ( node . name )
2020-12-10 00:59:39 +01:00
// TODO: temporary, remove this
node_info := node . info
2021-04-25 20:40:38 +02:00
mut is_auto_heap := false
2020-12-10 00:59:39 +01:00
if node_info is ast . IdentVar {
2020-04-22 20:12:30 +02:00
// x ?int
// `x = 10` => `x.data = 10` (g.right_is_opt == false)
// `x = new_opt()` => `x = new_opt()` (g.right_is_opt == true)
// `println(x)` => `println(*(int*)x.data)`
2020-12-10 00:59:39 +01:00
if node_info . is_optional && ! ( g . is_assign_lhs && g . right_is_opt ) {
2020-04-22 20:12:30 +02:00
g . write ( ' / * o p t * / ' )
2020-12-10 00:59:39 +01:00
styp := g . base_type ( node_info . typ )
2020-04-22 20:12:30 +02:00
g . write ( ' ( * ( $ styp * ) $ { name } . d a t a ) ' )
return
2020-03-24 22:23:59 +01:00
}
2020-12-10 00:59:39 +01:00
if ! g . is_assign_lhs && node_info . share == . shared_t {
2020-07-04 12:44:25 +02:00
g . write ( ' $ { name } . v a l ' )
return
}
2020-11-11 09:18:15 +01:00
scope := g . file . scope . innermost ( node . pos . pos )
if v := scope . find_var ( node . name ) {
2021-04-25 20:40:38 +02:00
is_auto_heap = v . is_auto_heap && ( ! g . is_assign_lhs || g . assign_op != . decl_assign )
if is_auto_heap {
g . write ( ' ( * ( ' )
}
2021-04-09 10:00:05 +02:00
if v . smartcasts . len > 0 {
2021-12-19 17:25:18 +01:00
v_sym := g . table . sym ( v . typ )
2020-11-11 09:18:15 +01:00
if ! prevent_sum_type_unwrapping_once {
2021-04-09 10:00:05 +02:00
for _ in v . smartcasts {
g . write ( ' ( ' )
2021-04-25 20:40:38 +02:00
if v_sym . kind == . sum_type && ! is_auto_heap {
2021-04-09 10:00:05 +02:00
g . write ( ' * ' )
}
2020-11-23 16:16:13 +01:00
}
2021-04-09 10:00:05 +02:00
for i , typ in v . smartcasts {
2021-12-19 17:25:18 +01:00
cast_sym := g . table . sym ( g . unwrap_generic ( typ ) )
2021-01-21 21:31:25 +01:00
mut is_ptr := false
2020-11-23 16:16:13 +01:00
if i == 0 {
g . write ( name )
2021-01-21 21:31:25 +01:00
if v . orig_type . is_ptr ( ) {
is_ptr = true
}
2020-11-23 16:16:13 +01:00
}
2021-04-25 20:40:38 +02:00
dot := if is_ptr || is_auto_heap { ' - > ' } else { ' . ' }
2021-04-02 00:57:09 +02:00
if mut cast_sym . info is ast . Aggregate {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( cast_sym . info . types [ g . aggregate_type_idx ] )
2020-12-08 06:08:10 +01:00
g . write ( ' $ { dot } _ $ sym . cname ' )
2020-11-23 16:16:13 +01:00
} else {
2020-12-08 06:08:10 +01:00
g . write ( ' $ { dot } _ $ cast_sym . cname ' )
2020-11-23 16:16:13 +01:00
}
g . write ( ' ) ' )
2020-11-18 20:52:00 +01:00
}
2021-04-25 20:40:38 +02:00
if is_auto_heap {
g . write ( ' ) ) ' )
}
2020-11-11 09:18:15 +01:00
return
}
}
2021-08-10 20:27:15 +02:00
if v . is_inherited {
g . write ( closure_ctx + ' - > ' )
}
2020-11-11 09:18:15 +01:00
}
2021-02-02 09:14:34 +01:00
} else if node_info is ast . IdentFn {
2021-11-18 09:57:57 +01:00
if g . pref . translated {
// `p_mobjthinker` => `P_MobjThinker`
if f := g . table . find_fn ( node . name ) {
// TODO PERF fn lookup for each fn call in translated mode
if f . attrs . contains ( ' c ' ) {
name = f . attrs [ 0 ] . arg
}
}
}
2021-02-02 09:14:34 +01:00
if g . pref . obfuscate && g . cur_mod . name == ' m a i n ' && name . starts_with ( ' m a i n _ _ ' ) {
key := node . name
g . write ( ' / * o b f i d e n t f n : $ key * / ' )
name = g . obf_table [ key ] or {
panic ( ' c g e n : o b f n a m e " $ key " n o t f o u n d , t h i s s h o u l d n e v e r h a p p e n ' )
}
}
2020-03-13 05:57:51 +01:00
}
2020-05-14 17:15:25 +02:00
g . write ( g . get_ternary_name ( name ) )
2021-04-25 20:40:38 +02:00
if is_auto_heap {
g . write ( ' ) ) ' )
}
2020-03-13 05:57:51 +01:00
}
2021-04-02 16:34:48 +02:00
fn ( mut g Gen ) cast_expr ( node ast . CastExpr ) {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( node . typ )
2021-12-01 08:50:53 +01:00
if sym . kind in [ . sum_type , . interface_ ] {
2021-04-02 16:34:48 +02:00
g . expr_with_cast ( node . expr , node . expr_type , node . typ )
} else if sym . kind == . struct_ && ! node . typ . is_ptr ( ) && ! ( sym . info as ast . Struct ) . is_typedef {
// deprecated, replaced by Struct{...exr}
styp := g . typ ( node . typ )
g . write ( ' * ( ( $ styp * ) ( & ' )
g . expr ( node . expr )
g . write ( ' ) ) ' )
2021-12-19 17:25:18 +01:00
} else if sym . kind == . alias && g . table . final_sym ( node . typ ) . kind == . array_fixed {
2021-07-13 09:51:54 +02:00
g . expr ( node . expr )
2021-04-02 16:34:48 +02:00
} else {
styp := g . typ ( node . typ )
2021-11-18 09:57:57 +01:00
if g . pref . translated && sym . kind == . function {
// TODO handle the type in fn casts, not just exprs
/ *
info := sym . info as ast . FnType
if info . func . attrs . contains ( ' c ' ) {
// name = f.attrs[0].arg
}
* /
}
2021-04-02 16:34:48 +02:00
mut cast_label := ' '
// `ast.string_type` is done for MSVC's bug
if sym . kind != . alias
|| ( sym . info as ast . Alias ) . parent_type ! in [ node . expr_type , ast . string_type ] {
cast_label = ' ( $ styp ) '
}
if node . typ . has_flag ( . optional ) && node . expr is ast . None {
g . gen_optional_error ( node . typ , node . expr )
} else {
g . write ( ' ( $ { cast_label } ( ' )
g . expr ( node . expr )
if node . expr is ast . IntegerLiteral {
if node . typ in [ ast . u64_type , ast . u32_type , ast . u16_type ] {
if ! node . expr . val . starts_with ( ' - ' ) {
g . write ( ' U ' )
}
}
}
g . write ( ' ) ) ' )
}
}
}
2020-05-15 23:14:53 +02:00
fn ( mut g Gen ) concat_expr ( node ast . ConcatExpr ) {
2021-10-11 01:45:01 +02:00
mut styp := g . typ ( node . return_type )
if g . inside_return {
styp = g . typ ( g . fn_decl . return_type )
}
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( node . return_type )
2020-05-15 23:14:53 +02:00
is_multi := sym . kind == . multi_return
if ! is_multi {
g . expr ( node . vals [ 0 ] )
} else {
g . write ( ' ( $ styp ) { ' )
for i , expr in node . vals {
g . write ( ' . a r g $ i = ' )
g . expr ( expr )
if i < node . vals . len - 1 {
g . write ( ' , ' )
}
}
g . write ( ' } ' )
}
}
2021-02-24 14:06:29 +01:00
fn ( mut g Gen ) need_tmp_var_in_if ( node ast . IfExpr ) bool {
if node . is_expr && g . inside_ternary == 0 {
2021-03-01 13:56:07 +01:00
if g . is_autofree || node . typ . has_flag ( . optional ) {
2021-02-24 14:06:29 +01:00
return true
}
for branch in node . branches {
2021-08-16 20:48:58 +02:00
if branch . cond is ast . IfGuardExpr || branch . stmts . len > 1 {
2021-03-20 03:53:47 +01:00
return true
}
2021-02-24 14:06:29 +01:00
if branch . stmts . len == 1 {
if branch . stmts [ 0 ] is ast . ExprStmt {
stmt := branch . stmts [ 0 ] as ast . ExprStmt
2021-11-15 09:29:58 +01:00
if is_noreturn_callexpr ( stmt . expr ) {
return true
}
2021-02-24 14:06:29 +01:00
if stmt . expr is ast . CallExpr {
if stmt . expr . is_method {
2021-12-19 17:25:18 +01:00
left_sym := g . table . sym ( stmt . expr . receiver_type )
2021-02-24 14:06:29 +01:00
if left_sym . kind in [ . array , . array_fixed , . map ] {
return true
}
2021-10-02 13:30:04 +02:00
} else if stmt . expr . or_block . kind != . absent {
return true
2021-02-24 14:06:29 +01:00
}
}
}
}
}
}
return false
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) if_expr ( node ast . IfExpr ) {
2020-09-18 00:58:54 +02:00
if node . is_comptime {
2021-11-15 14:47:29 +01:00
g . comptime_if ( node )
2020-09-18 00:58:54 +02:00
return
}
2020-10-18 20:19:52 +02:00
// For simpe if expressions we can use C's `?:`
2020-10-18 15:55:04 +02:00
// `if x > 0 { 1 } else { 2 }` => `(x > 0) ? (1) : (2)`
// For if expressions with multiple statements or another if expression inside, it's much
// easier to use a temp var, than do C tricks with commas, introduce special vars etc
// (as it used to be done).
2020-11-21 19:07:47 +01:00
// Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
2021-02-24 14:06:29 +01:00
needs_tmp_var := g . need_tmp_var_in_if ( node )
2020-10-18 15:55:04 +02:00
tmp := if needs_tmp_var { g . new_tmp_var ( ) } else { ' ' }
mut cur_line := ' '
if needs_tmp_var {
2021-03-01 13:56:07 +01:00
if node . typ . has_flag ( . optional ) {
g . inside_if_optional = true
}
2020-10-18 15:55:04 +02:00
styp := g . typ ( node . typ )
cur_line = g . go_before_stmt ( 0 )
2021-03-03 13:33:00 +01:00
g . empty_line = true
2020-10-23 22:50:22 +02:00
g . writeln ( ' $ styp $ tmp ; / * i f p r e p e n d * / ' )
2021-08-16 10:23:19 +02:00
if g . infix_left_var_name . len > 0 {
g . writeln ( ' i f ( $ g . infix_left_var_name ) { ' )
g . indent ++
}
2020-10-18 15:55:04 +02:00
} else if node . is_expr || g . inside_ternary != 0 {
2020-05-11 15:11:48 +02:00
g . inside_ternary ++
2020-03-27 07:21:22 +01:00
g . write ( ' ( ' )
2020-03-20 14:39:56 +01:00
for i , branch in node . branches {
if i > 0 {
2020-03-17 16:40:41 +01:00
g . write ( ' : ' )
}
2020-03-20 14:46:00 +01:00
if i < node . branches . len - 1 || ! node . has_else {
2020-03-20 14:39:56 +01:00
g . expr ( branch . cond )
g . write ( ' ? ' )
}
g . stmts ( branch . stmts )
}
2020-05-14 17:15:25 +02:00
if node . branches . len == 1 {
g . write ( ' : 0 ' )
}
2020-03-27 07:21:22 +01:00
g . write ( ' ) ' )
2020-05-14 17:15:25 +02:00
g . decrement_inside_ternary ( )
return
}
mut is_guard := false
2020-07-17 19:10:01 +02:00
mut guard_idx := 0
mut guard_vars := [ ] string { }
2020-05-14 17:15:25 +02:00
for i , branch in node . branches {
2020-07-16 19:40:14 +02:00
cond := branch . cond
if cond is ast . IfGuardExpr {
2020-07-14 17:19:55 +02:00
if ! is_guard {
is_guard = true
2020-07-17 19:10:01 +02:00
guard_idx = i
2020-07-18 11:14:03 +02:00
guard_vars = [ ] string { len : node . branches . len }
2020-07-14 17:19:55 +02:00
}
2021-03-03 09:10:38 +01:00
if cond . expr ! is ast . IndexExpr && cond . expr ! is ast . PrefixExpr {
var_name := g . new_tmp_var ( )
guard_vars [ i ] = var_name
g . writeln ( ' $ { g . typ ( cond . expr_type ) } $ var_name ; ' )
} else {
guard_vars [ i ] = ' '
}
2020-07-14 17:19:55 +02:00
}
}
for i , branch in node . branches {
if i > 0 {
g . write ( ' } e l s e ' )
}
2020-07-17 19:10:01 +02:00
// if last branch is `else {`
2020-07-14 17:19:55 +02:00
if i == node . branches . len - 1 && node . has_else {
g . writeln ( ' { ' )
2020-07-17 19:10:01 +02:00
// define `err` only for simple `if val := opt {...} else {`
if is_guard && guard_idx == i - 1 {
cvar_name := guard_vars [ guard_idx ]
2021-03-13 18:13:50 +01:00
g . writeln ( ' \t I E r r o r e r r = $ { cvar_name } . e r r ; ' )
2020-07-17 19:10:01 +02:00
}
2020-07-14 17:19:55 +02:00
} else {
2020-11-25 12:09:40 +01:00
match branch . cond {
2020-05-14 17:15:25 +02:00
ast . IfGuardExpr {
2021-03-03 09:10:38 +01:00
mut var_name := guard_vars [ i ]
mut short_opt := false
if var_name == ' ' {
short_opt = true // we don't need a further tmp, so use the one we'll get later
var_name = g . new_tmp_var ( )
guard_vars [ i ] = var_name // for `else`
g . tmp_count --
g . writeln ( ' i f ( $ { var_name } . s t a t e = = 0 ) { ' )
} else {
g . write ( ' i f ( $ var_name = ' )
g . expr ( branch . cond . expr )
g . writeln ( ' , $ { var_name } . s t a t e = = 0 ) { ' )
}
2021-04-09 17:18:36 +02:00
if short_opt || branch . cond . var_name != ' _ ' {
2020-11-21 22:40:05 +01:00
base_type := g . base_type ( branch . cond . expr_type )
2021-03-03 09:10:38 +01:00
if short_opt {
2021-04-09 17:18:36 +02:00
cond_var_name := if branch . cond . var_name == ' _ ' {
' _ d u m m y _ $ { g . tmp_count + 1 } '
} else {
branch . cond . var_name
}
g . write ( ' \t $ base_type $ cond_var_name = ' )
2021-03-03 09:10:38 +01:00
g . expr ( branch . cond . expr )
g . writeln ( ' ; ' )
} else {
2021-04-25 20:40:38 +02:00
mut is_auto_heap := false
if branch . stmts . len > 0 {
scope := g . file . scope . innermost ( ast . Node ( branch . stmts [ branch . stmts . len - 1 ] ) . position ( ) . pos )
if v := scope . find_var ( branch . cond . var_name ) {
is_auto_heap = v . is_auto_heap
}
}
2021-12-17 03:50:52 +01:00
left_var_name := c_name ( branch . cond . var_name )
2021-04-25 20:40:38 +02:00
if is_auto_heap {
2021-12-17 03:50:52 +01:00
g . writeln ( ' \t $ base_type * $ left_var_name = H E A P ( $ base_type , * ( $ base_type * ) $ { var_name } . d a t a ) ; ' )
2021-04-25 20:40:38 +02:00
} else {
2021-12-17 03:50:52 +01:00
g . writeln ( ' \t $ base_type $ left_var_name = * ( $ base_type * ) $ { var_name } . d a t a ; ' )
2021-04-25 20:40:38 +02:00
}
2021-03-03 09:10:38 +01:00
}
2020-07-14 17:19:55 +02:00
}
2020-05-14 17:15:25 +02:00
}
else {
2021-10-14 10:38:16 +02:00
mut no_needs_par := false
if branch . cond is ast . InfixExpr {
if branch . cond . op == . key_in && branch . cond . left ! is ast . InfixExpr
&& branch . cond . right is ast . ArrayInit {
no_needs_par = true
}
}
if no_needs_par {
g . write ( ' i f ' )
} else {
g . write ( ' i f ( ' )
}
2020-05-14 17:15:25 +02:00
g . expr ( branch . cond )
2021-10-14 10:38:16 +02:00
if no_needs_par {
g . writeln ( ' { ' )
} else {
g . writeln ( ' ) { ' )
}
2020-04-05 18:03:36 +02:00
}
2020-03-17 16:40:41 +01:00
}
}
2020-10-18 15:55:04 +02:00
if needs_tmp_var {
g . stmts_with_tmp_var ( branch . stmts , tmp )
} else {
2021-07-13 18:51:49 +02:00
// restore if_expr stmt header pos
stmt_pos := g . nth_stmt_pos ( 0 )
2020-10-18 15:55:04 +02:00
g . stmts ( branch . stmts )
2021-07-13 18:51:49 +02:00
g . stmt_path_pos << stmt_pos
2020-10-18 15:55:04 +02:00
}
2020-05-14 17:15:25 +02:00
}
2021-10-11 13:29:17 +02:00
if node . branches . len > 0 {
g . writeln ( ' } ' )
}
2021-09-26 14:51:18 +02:00
g . set_current_pos_as_last_stmt_pos ( )
2020-10-18 15:55:04 +02:00
if needs_tmp_var {
2021-08-16 10:23:19 +02:00
if g . infix_left_var_name . len > 0 {
g . indent --
g . writeln ( ' } ' )
}
2021-03-03 13:33:00 +01:00
g . empty_line = false
g . write ( ' $ cur_line $ tmp ' )
2020-10-18 15:55:04 +02:00
}
2021-03-01 13:56:07 +01:00
if node . typ . has_flag ( . optional ) {
g . inside_if_optional = false
}
2020-03-17 16:40:41 +01:00
}
2020-05-27 14:47:38 +02:00
[ inline ]
2020-08-11 23:08:19 +02:00
fn ( g & Gen ) expr_is_multi_return_call ( expr ast . Expr ) bool {
2020-11-25 12:09:40 +01:00
match expr {
2021-12-19 17:25:18 +01:00
ast . CallExpr { return g . table . sym ( expr . return_type ) . kind == . multi_return }
2020-05-29 04:30:00 +02:00
else { return false }
2020-05-28 01:22:09 +02:00
}
2020-05-27 14:47:38 +02:00
}
2021-04-02 16:34:48 +02:00
fn ( mut g Gen ) gen_optional_error ( target_type ast . Type , expr ast . Expr ) {
styp := g . typ ( target_type )
g . write ( ' ( $ styp ) { . s t a t e = 2 , . e r r = ' )
g . expr ( expr )
2021-07-03 19:11:56 +02:00
g . write ( ' , . d a t a = { E M P T Y _ S T R U C T _ I N I T I A L I Z A T I O N } } ' )
2021-04-02 16:34:48 +02:00
}
2021-04-10 10:55:34 +02:00
fn ( mut g Gen ) return_stmt ( node ast . Return ) {
2020-08-11 23:08:19 +02:00
g . write_v_source_line_info ( node . pos )
2021-09-19 22:22:26 +02:00
g . inside_return = true
defer {
g . inside_return = false
}
2020-06-20 03:12:35 +02:00
if node . exprs . len > 0 {
2021-04-27 00:42:16 +02:00
// skip `return $vweb.html()`
2020-06-20 03:12:35 +02:00
if node . exprs [ 0 ] is ast . ComptimeCall {
g . expr ( node . exprs [ 0 ] )
g . writeln ( ' ; ' )
return
}
}
2021-05-12 10:44:47 +02:00
2020-05-05 16:21:21 +02:00
// got to do a correct check for multireturn
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( g . fn_decl . return_type )
2020-05-05 16:21:21 +02:00
fn_return_is_multi := sym . kind == . multi_return
2020-06-04 14:38:54 +02:00
fn_return_is_optional := g . fn_decl . return_type . has_flag ( . optional )
2021-03-14 13:58:17 +01:00
mut has_semicolon := false
2020-06-24 12:45:15 +02:00
if node . exprs . len == 0 {
2021-04-27 00:42:16 +02:00
g . write_defer_stmts_when_needed ( )
2020-06-24 12:45:15 +02:00
if fn_return_is_optional {
styp := g . typ ( g . fn_decl . return_type )
2021-02-28 20:24:29 +01:00
g . writeln ( ' r e t u r n ( $ styp ) { 0 } ; ' )
2020-06-24 12:45:15 +02:00
} else {
2021-03-18 03:41:50 +01:00
if g . is_autofree {
2021-05-11 18:29:06 +02:00
g . trace_autofree ( ' / / f r e e b e f o r e r e t u r n ( n o v a l u e s r e t u r n e d ) ' )
2020-12-04 18:06:53 +01:00
g . autofree_scope_vars ( node . pos . pos - 1 , node . pos . line_nr , true )
2020-12-04 10:10:02 +01:00
}
2020-06-24 12:45:15 +02:00
g . writeln ( ' r e t u r n ; ' )
}
return
}
2021-04-27 00:42:16 +02:00
tmpvar := g . new_tmp_var ( )
ret_typ := g . typ ( g . fn_decl . return_type )
mut use_tmp_var := g . defer_stmts . len > 0 || g . defer_profile_code . len > 0
2020-05-31 12:57:26 +02:00
// handle promoting none/error/function returning 'Option'
2020-05-07 15:36:04 +02:00
if fn_return_is_optional {
optional_none := node . exprs [ 0 ] is ast . None
2021-02-28 20:24:29 +01:00
ftyp := g . typ ( node . types [ 0 ] )
2021-06-02 18:14:37 +02:00
mut is_regular_option := ftyp == ' O p t i o n '
2021-04-02 16:34:48 +02:00
if optional_none || is_regular_option || node . types [ 0 ] == ast . error_type_idx {
2021-06-02 18:14:37 +02:00
if ! isnil ( g . fn_decl ) && g . fn_decl . is_test {
test_error_var := g . new_tmp_var ( )
g . write ( ' $ ret_typ $ test_error_var = ' )
g . gen_optional_error ( g . fn_decl . return_type , node . exprs [ 0 ] )
g . writeln ( ' ; ' )
g . write_defer_stmts_when_needed ( )
g . gen_failing_return_error_for_test_fn ( node , test_error_var )
return
}
2021-04-27 00:42:16 +02:00
if use_tmp_var {
g . write ( ' $ ret_typ $ tmpvar = ' )
} else {
g . write ( ' r e t u r n ' )
}
2021-04-02 16:34:48 +02:00
g . gen_optional_error ( g . fn_decl . return_type , node . exprs [ 0 ] )
g . writeln ( ' ; ' )
2021-04-27 00:42:16 +02:00
if use_tmp_var {
g . write_defer_stmts_when_needed ( )
g . writeln ( ' r e t u r n $ tmpvar ; ' )
}
2020-05-07 15:36:04 +02:00
return
}
}
// regular cases
2021-04-27 00:42:16 +02:00
if fn_return_is_multi && node . exprs . len > 0 && ! g . expr_is_multi_return_call ( node . exprs [ 0 ] ) {
2021-10-11 01:45:01 +02:00
if node . exprs . len == 1 && ( node . exprs [ 0 ] is ast . IfExpr || node . exprs [ 0 ] is ast . MatchExpr ) {
// use a temporary for `return if cond { x,y } else { a,b }` or `return match expr { abc { x, y } else { z, w } }`
2021-04-27 00:42:16 +02:00
g . write ( ' $ ret_typ $ tmpvar = ' )
2021-04-13 10:31:40 +02:00
g . expr ( node . exprs [ 0 ] )
g . writeln ( ' ; ' )
2021-04-27 00:42:16 +02:00
g . write_defer_stmts_when_needed ( )
2021-04-13 10:31:40 +02:00
g . writeln ( ' r e t u r n $ tmpvar ; ' )
return
}
2021-12-22 11:09:08 +01:00
typ_sym := g . table . sym ( g . fn_decl . return_type )
mr_info := typ_sym . info as ast . MultiReturn
2020-04-22 20:12:30 +02:00
mut styp := ' '
2020-05-07 15:36:04 +02:00
if fn_return_is_optional {
2021-04-27 00:42:16 +02:00
g . writeln ( ' $ ret_typ $ tmpvar ; ' )
2020-05-04 00:14:59 +02:00
styp = g . base_type ( g . fn_decl . return_type )
2021-03-14 01:54:46 +01:00
g . write ( ' o p t _ o k ( & ( $ styp / * X * / [ ] ) { ' )
2020-04-22 20:12:30 +02:00
} else {
2021-04-27 00:42:16 +02:00
if use_tmp_var {
g . write ( ' $ ret_typ $ tmpvar = ' )
} else {
g . write ( ' r e t u r n ' )
}
2020-04-22 20:12:30 +02:00
styp = g . typ ( g . fn_decl . return_type )
2020-03-19 12:13:17 +01:00
}
2020-05-27 14:47:38 +02:00
// Use this to keep the tmp assignments in order
mut multi_unpack := ' '
2020-03-19 12:13:17 +01:00
g . write ( ' ( $ styp ) { ' )
2020-05-27 14:47:38 +02:00
mut arg_idx := 0
2020-03-30 12:39:20 +02:00
for i , expr in node . exprs {
2020-05-27 14:47:38 +02:00
// Check if we are dealing with a multi return and handle it seperately
if g . expr_is_multi_return_call ( expr ) {
c := expr as ast . CallExpr
2021-12-19 17:25:18 +01:00
expr_sym := g . table . sym ( c . return_type )
2020-05-27 14:47:38 +02:00
// Create a tmp for this call
2020-07-23 00:39:19 +02:00
mut tmp := g . new_tmp_var ( )
if ! c . return_type . has_flag ( . optional ) {
s := g . go_before_stmt ( 0 )
expr_styp := g . typ ( c . return_type )
g . write ( ' $ expr_styp $ tmp = ' )
g . expr ( expr )
g . writeln ( ' ; ' )
multi_unpack += g . go_before_stmt ( 0 )
g . write ( s )
} else {
s := g . go_before_stmt ( 0 )
// TODO
// I (emily) am sorry for doing this
// I cant find another way to do this so right now
// this will have to do.
g . tmp_count --
g . expr ( expr )
multi_unpack += g . go_before_stmt ( 0 )
g . write ( s )
// modify tmp so that it is the opt deref
// TODO copy-paste from cgen.v:2397
expr_styp := g . base_type ( c . return_type )
tmp = ( ' / * o p t * / ( * ( $ expr_styp * ) $ { tmp } . d a t a ) ' )
}
2020-05-27 14:47:38 +02:00
expr_types := expr_sym . mr_info ( ) . types
for j , _ in expr_types {
g . write ( ' . a r g $ arg_idx = $ { tmp } . a r g $ j ' )
if j < expr_types . len || i < node . exprs . len - 1 {
g . write ( ' , ' )
}
arg_idx ++
}
continue
}
g . write ( ' . a r g $ arg_idx = ' )
2021-07-04 17:36:37 +02:00
if expr . is_auto_deref_var ( ) {
g . write ( ' * ' )
}
2021-12-22 11:09:08 +01:00
if g . table . sym ( mr_info . types [ i ] ) . kind in [ . sum_type , . interface_ ] {
g . expr_with_cast ( expr , node . types [ i ] , mr_info . types [ i ] )
} else {
g . expr ( expr )
}
2020-05-27 14:47:38 +02:00
arg_idx ++
2020-03-30 12:39:20 +02:00
if i < node . exprs . len - 1 {
2020-07-02 11:08:27 +02:00
g . write ( ' , ' )
2020-03-13 05:57:51 +01:00
}
}
g . write ( ' } ' )
2020-03-19 12:13:17 +01:00
if fn_return_is_optional {
2021-04-27 00:42:16 +02:00
g . writeln ( ' } , ( O p t i o n * ) ( & $ tmpvar ) , s i z e o f ( $ styp ) ) ; ' )
g . write_defer_stmts_when_needed ( )
g . write ( ' r e t u r n $ tmpvar ' )
2020-03-19 12:13:17 +01:00
}
2020-05-27 14:47:38 +02:00
// Make sure to add our unpacks
2020-07-02 11:08:27 +02:00
if multi_unpack . len > 0 {
g . insert_before_stmt ( multi_unpack )
}
2021-04-27 00:42:16 +02:00
if use_tmp_var && ! fn_return_is_optional {
if ! has_semicolon {
g . writeln ( ' ; ' )
}
g . write_defer_stmts_when_needed ( )
g . writeln ( ' r e t u r n $ tmpvar ; ' )
has_semicolon = true
}
2020-05-05 16:21:21 +02:00
} else if node . exprs . len >= 1 {
2021-12-22 15:16:10 +01:00
if node . types . len == 0 {
g . checker_bug ( ' n o d e . e x p r s . l e n = = $ node . exprs . len & & n o d e . t y p e s . l e n = = 0 ' ,
node . pos )
}
2020-04-05 02:08:10 +02:00
// normal return
2021-12-19 17:25:18 +01:00
return_sym := g . table . sym ( node . types [ 0 ] )
2020-12-28 10:09:43 +01:00
expr0 := node . exprs [ 0 ]
2021-02-28 23:21:03 +01:00
// `return opt_ok(expr)` for functions that expect an optional
expr_type_is_opt := match expr0 {
ast . CallExpr {
expr0 . return_type . has_flag ( . optional ) && expr0 . or_block . kind == . absent
}
else {
node . types [ 0 ] . has_flag ( . optional )
}
2020-12-28 10:09:43 +01:00
}
2021-06-02 18:14:37 +02:00
if fn_return_is_optional && ! expr_type_is_opt && return_sym . name != ' O p t i o n ' {
2020-05-07 15:36:04 +02:00
styp := g . base_type ( g . fn_decl . return_type )
2021-04-27 00:42:16 +02:00
g . writeln ( ' $ ret_typ $ tmpvar ; ' )
2021-03-14 01:54:46 +01:00
g . write ( ' o p t _ o k ( & ( $ styp [ ] ) { ' )
2020-05-07 15:36:04 +02:00
if ! g . fn_decl . return_type . is_ptr ( ) && node . types [ 0 ] . is_ptr ( ) {
2021-01-31 18:24:33 +01:00
if ! ( node . exprs [ 0 ] is ast . Ident && ! g . is_amp ) {
g . write ( ' * ' )
2021-01-04 20:54:13 +01:00
}
2020-04-05 18:03:36 +02:00
}
2020-05-07 15:36:04 +02:00
for i , expr in node . exprs {
2021-01-24 20:56:44 +01:00
g . expr_with_cast ( expr , node . types [ i ] , g . fn_decl . return_type . clear_flag ( . optional ) )
2020-05-07 15:36:04 +02:00
if i < node . exprs . len - 1 {
g . write ( ' , ' )
2020-04-28 07:59:41 +02:00
}
2020-03-13 05:57:51 +01:00
}
2021-04-27 00:42:16 +02:00
g . writeln ( ' } , ( O p t i o n * ) ( & $ tmpvar ) , s i z e o f ( $ styp ) ) ; ' )
g . write_defer_stmts_when_needed ( )
2021-04-06 16:14:07 +02:00
g . autofree_scope_vars ( node . pos . pos - 1 , node . pos . line_nr , true )
2021-04-27 00:42:16 +02:00
g . writeln ( ' r e t u r n $ tmpvar ; ' )
2020-05-07 15:36:04 +02:00
return
2020-03-13 05:57:51 +01:00
}
2021-03-18 03:41:50 +01:00
// autofree before `return`
// set free_parent_scopes to true, since all variables defined in parent
// scopes need to be freed before the return
if g . is_autofree {
expr := node . exprs [ 0 ]
if expr is ast . Ident {
g . returned_var_name = expr . name
}
}
2021-02-27 18:00:19 +01:00
// free := g.is_autofree && !g.is_builtin_mod // node.exprs[0] is ast.CallExpr
2021-03-18 03:41:50 +01:00
// Create a temporary variable for the return expression
2021-04-27 00:42:16 +02:00
use_tmp_var = use_tmp_var || ! g . is_builtin_mod // node.exprs[0] is ast.CallExpr
if use_tmp_var {
2020-07-01 14:34:14 +02:00
// `return foo(a, b, c)`
// `tmp := foo(a, b, c); free(a); free(b); free(c); return tmp;`
2021-03-18 03:41:50 +01:00
// Save return value in a temp var so that all args (a,b,c) can be freed
2020-12-04 20:44:33 +01:00
// Don't use a tmp var if a variable is simply returned: `return x`
2021-07-30 19:29:06 +02:00
// Just in case of defer statements exists, that the return values cannot
// be modified.
if node . exprs [ 0 ] ! is ast . Ident || use_tmp_var {
2021-04-27 00:42:16 +02:00
g . write ( ' $ ret_typ $ tmpvar = ' )
2020-12-04 20:44:33 +01:00
} else {
2021-04-27 00:42:16 +02:00
use_tmp_var = false
g . write_defer_stmts_when_needed ( )
if ! g . is_builtin_mod {
g . autofree_scope_vars ( node . pos . pos - 1 , node . pos . line_nr , true )
}
2020-12-04 20:44:33 +01:00
g . write ( ' r e t u r n ' )
}
2020-07-01 14:34:14 +02:00
} else {
2021-03-23 09:03:34 +01:00
g . autofree_scope_vars ( node . pos . pos - 1 , node . pos . line_nr , true )
2020-07-01 14:34:14 +02:00
g . write ( ' r e t u r n ' )
}
2021-03-14 18:11:21 +01:00
if expr0 . is_auto_deref_var ( ) {
if g . fn_decl . return_type . is_ptr ( ) {
2021-03-15 11:22:52 +01:00
var_str := g . expr_string ( expr0 )
g . write ( var_str . trim ( ' & ' ) )
2021-03-14 18:11:21 +01:00
} else {
g . write ( ' * ' )
g . expr ( expr0 )
}
} else {
2021-07-06 15:33:26 +02:00
g . expr_with_cast ( node . exprs [ 0 ] , node . types [ 0 ] , g . fn_decl . return_type )
2021-03-14 18:11:21 +01:00
}
2021-04-27 00:42:16 +02:00
if use_tmp_var {
2020-12-04 20:44:33 +01:00
g . writeln ( ' ; ' )
2021-03-14 13:58:17 +01:00
has_semicolon = true
2021-04-27 00:42:16 +02:00
g . write_defer_stmts_when_needed ( )
if ! g . is_builtin_mod {
2021-03-23 09:03:34 +01:00
g . autofree_scope_vars ( node . pos . pos - 1 , node . pos . line_nr , true )
2020-12-04 20:44:33 +01:00
}
2021-04-27 00:42:16 +02:00
g . write ( ' r e t u r n $ tmpvar ' )
has_semicolon = false
2020-07-01 14:34:14 +02:00
}
2020-12-04 10:10:02 +01:00
} else { // if node.exprs.len == 0 {
println ( ' t h i s s h o u l d n e v e r h a p p e n ' )
g . write ( ' / * F * / r e t u r n ' )
2020-03-13 05:57:51 +01:00
}
2021-03-14 13:58:17 +01:00
if ! has_semicolon {
g . writeln ( ' ; ' )
}
2020-03-13 05:57:51 +01:00
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) const_decl ( node ast . ConstDecl ) {
2020-07-01 16:40:04 +02:00
g . inside_const = true
defer {
g . inside_const = false
}
2020-04-27 15:16:31 +02:00
for field in node . fields {
2021-02-07 00:01:37 +01:00
if g . pref . skip_unused {
if field . name ! in g . table . used_consts {
$ if trace_skip_unused_consts ? {
eprintln ( ' > > s k i p p i n g u n u s e d c o n s t n a m e : $ field . name ' )
}
continue
}
}
2020-03-23 03:36:50 +01:00
name := c_name ( field . name )
2021-05-09 02:25:44 +02:00
field_expr := field . expr
2020-11-25 12:09:40 +01:00
match field . expr {
2020-05-02 15:18:49 +02:00
ast . ArrayInit {
2020-11-21 00:05:57 +01:00
if field . expr . is_fixed {
styp := g . typ ( field . expr . typ )
2021-01-20 06:04:59 +01:00
if g . pref . build_mode != . build_module {
2021-05-09 02:25:44 +02:00
val := g . expr_string ( field . expr )
2021-01-20 06:04:59 +01:00
g . definitions . writeln ( ' $ styp _ c o n s t _ $ name = $ val ; / / f i x e d a r r a y c o n s t ' )
} else {
g . definitions . writeln ( ' $ styp _ c o n s t _ $ name ; / / f i x e d a r r a y c o n s t ' )
}
2020-05-02 15:18:49 +02:00
} else {
2021-05-09 02:25:44 +02:00
g . const_decl_init_later ( field . mod , name , field . expr , field . typ , false )
2020-05-02 15:18:49 +02:00
}
}
2020-04-04 22:06:47 +02:00
ast . StringLiteral {
2020-08-18 10:46:12 +02:00
g . definitions . writeln ( ' s t r i n g _ c o n s t _ $ name ; / / a s t r i n g l i t e r a l , i n i t e d l a t e r ' )
2020-04-10 18:11:43 +02:00
if g . pref . build_mode != . build_module {
2021-05-09 02:25:44 +02:00
val := g . expr_string ( field . expr )
2020-04-10 18:11:43 +02:00
g . stringliterals . writeln ( ' \t _ c o n s t _ $ name = $ val ; ' )
}
2020-03-06 14:03:35 +01:00
}
2020-12-04 11:44:16 +01:00
ast . CallExpr {
2021-05-09 02:25:44 +02:00
if field . expr . return_type . has_flag ( . optional ) {
2021-01-30 11:46:36 +01:00
unwrap_option := field . expr . or_block . kind != . absent
2021-05-09 02:25:44 +02:00
g . const_decl_init_later ( field . mod , name , field . expr , field . typ , unwrap_option )
2020-12-04 11:44:16 +01:00
} else {
2021-05-09 02:25:44 +02:00
g . const_decl_init_later ( field . mod , name , field . expr , field . typ , false )
2020-12-04 11:44:16 +01:00
}
}
2020-03-06 14:03:35 +01:00
else {
2021-12-29 16:57:04 +01:00
// NB: -usecache uses prebuilt modules, each compiled with:
// `v build-module vlib/module`
// combined with a top level program, that is compiled with:
// `v -usecache toplevel`
// For it to work, the consts optimisations should be identical, because
// only the top level program will have the const initialisation code for
// all the modules.
// TODO: encapsulate const initialisation for each module in a separate function,
// that is just called by the top level program in _vinit, instead of generating
// all the code inside _vinit for each module.
use_cache_mode := g . pref . build_mode == . build_module || g . pref . use_cache
if ! use_cache_mode {
2021-07-20 16:13:01 +02:00
if ct_value := field . comptime_expr_value ( ) {
if g . const_decl_precomputed ( field . mod , name , ct_value , field . typ ) {
continue
}
2021-07-18 18:41:39 +02:00
}
}
2021-07-19 07:20:09 +02:00
if field . is_simple_define_const ( ) {
2021-05-13 16:54:48 +02:00
// "Simple" expressions are not going to need multiple statements,
// only the ones which are inited later, so it's safe to use expr_string
g . const_decl_simple_define ( name , g . expr_string ( field_expr ) )
} else {
g . const_decl_init_later ( field . mod , name , field . expr , field . typ , false )
}
2020-03-06 14:03:35 +01:00
}
2020-04-05 18:03:36 +02:00
}
2020-03-06 14:03:35 +01:00
}
}
2021-07-18 18:41:39 +02:00
fn ( mut g Gen ) const_decl_precomputed ( mod string , name string , ct_value ast . ComptTimeConstValue , typ ast . Type ) bool {
mut styp := g . typ ( typ )
cname := ' _ c o n s t _ $ name '
2021-07-26 07:06:59 +02:00
$ if trace_const_precomputed ? {
eprintln ( ' > s t y p : $ styp | c n a m e : $ cname | c t _ v a l u e : $ ct_value | $ ct_value . type_name ( ) ' )
}
2021-07-18 18:41:39 +02:00
match ct_value {
2021-07-30 16:22:27 +02:00
i8 {
2021-07-18 18:41:39 +02:00
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) )
}
2021-07-30 16:22:27 +02:00
i16 {
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) )
}
int {
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) )
2021-07-18 18:41:39 +02:00
}
i64 {
2021-07-30 16:56:46 +02:00
if typ == ast . i64_type {
return false
}
2021-07-18 21:59:52 +02:00
if typ == ast . int_type {
// TODO: use g.const_decl_write_precomputed here too.
// For now, use #define macros, so existing code compiles
// with -cstrict. Add checker errors for overflows instead,
// so V can catch them earlier, instead of relying on the
// C compiler for that.
g . const_decl_simple_define ( name , ct_value . str ( ) )
return true
}
2021-07-26 07:06:59 +02:00
if typ == ast . u64_type {
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) + ' U ' )
} else {
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) )
}
2021-07-18 18:41:39 +02:00
}
2021-07-30 16:22:27 +02:00
byte {
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) )
}
u16 {
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) )
}
u32 {
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) )
}
2021-07-18 21:59:52 +02:00
u64 {
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) + ' U ' )
}
2021-07-30 16:22:27 +02:00
f32 {
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) )
}
2021-07-18 18:41:39 +02:00
f64 {
g . const_decl_write_precomputed ( styp , cname , ct_value . str ( ) )
}
2021-07-30 16:22:27 +02:00
rune {
rune_code := u32 ( ct_value )
2021-09-20 09:45:42 +02:00
if rune_code <= 127 {
2021-08-06 05:21:28 +02:00
if rune_code in [ ` " ` , ` \\ ` , ` ' ` ] {
2021-07-30 16:22:27 +02:00
return false
}
escval := util . smart_quote ( byte ( rune_code ) . ascii_str ( ) , false )
g . const_decl_write_precomputed ( styp , cname , " ' $ escval ' " )
} else {
g . const_decl_write_precomputed ( styp , cname , u32 ( ct_value ) . str ( ) )
}
}
2021-07-18 18:41:39 +02:00
string {
escaped_val := util . smart_quote ( ct_value , false )
2021-07-19 07:20:09 +02:00
// g.const_decl_write_precomputed(styp, cname, '_SLIT("$escaped_val")')
// TODO: ^ the above for strings, cause:
// `error C2099: initializer is not a constant` errors in MSVC,
// so fall back to the delayed initialisation scheme:
g . definitions . writeln ( ' $ styp $ cname ; / / i n i t e d l a t e r ' )
2021-09-28 09:28:04 +02:00
g . init . writeln ( ' \t $ cname = _ S L I T ( " $ escaped_val " ) ; ' )
2021-07-19 07:20:09 +02:00
if g . is_autofree {
g . cleanups [ mod ] . writeln ( ' \t s t r i n g _ f r e e ( & $ cname ) ; ' )
}
2021-07-18 18:41:39 +02:00
}
ast . EmptyExpr {
return false
}
}
return true
}
fn ( mut g Gen ) const_decl_write_precomputed ( styp string , cname string , ct_value string ) {
g . definitions . writeln ( ' $ styp $ cname = $ ct_value ; / / p r e c o m p u t e d ' )
}
2020-10-15 12:00:46 +02:00
fn ( mut g Gen ) const_decl_simple_define ( name string , val string ) {
2020-04-04 22:06:47 +02:00
// Simple expressions should use a #define
// so that we don't pollute the binary with unnecessary global vars
// Do not do this when building a module, otherwise the consts
// will not be accessible.
2021-02-22 12:18:11 +01:00
g . definitions . write_string ( ' # d e f i n e _ c o n s t _ $ name ' )
2020-04-04 22:06:47 +02:00
g . definitions . writeln ( val )
}
2021-05-09 02:25:44 +02:00
fn ( mut g Gen ) const_decl_init_later ( mod string , name string , expr ast . Expr , typ ast . Type , unwrap_option bool ) {
2021-01-10 13:31:20 +01:00
// Initialize more complex consts in `void _vinit/2{}`
2020-05-02 15:18:49 +02:00
// (C doesn't allow init expressions that can't be resolved at compile time).
2021-02-28 20:24:29 +01:00
mut styp := g . typ ( typ )
2020-05-25 09:17:06 +02:00
cname := ' _ c o n s t _ $ name '
2020-08-18 10:46:12 +02:00
g . definitions . writeln ( ' $ styp $ cname ; / / i n i t e d l a t e r ' )
2021-01-10 13:31:20 +01:00
if cname == ' _ c o n s t _ o s _ _ a r g s ' {
if g . pref . os == . windows {
2021-09-28 09:28:04 +02:00
g . init . writeln ( ' \t _ c o n s t _ o s _ _ a r g s = o s _ _ i n i t _ o s _ a r g s _ w i d e ( _ _ _ a r g c , ( b y t e p t r * ) _ _ _ a r g v ) ; ' )
2021-01-10 13:31:20 +01:00
} else {
2021-09-28 09:28:04 +02:00
g . init . writeln ( ' \t _ c o n s t _ o s _ _ a r g s = o s _ _ i n i t _ o s _ a r g s ( _ _ _ a r g c , ( b y t e * * ) _ _ _ a r g v ) ; ' )
2021-01-10 13:31:20 +01:00
}
} else {
2021-01-30 11:46:36 +01:00
if unwrap_option {
2021-11-19 18:02:33 +01:00
g . init . writeln ( ' { ' )
2021-09-28 09:28:04 +02:00
g . init . writeln ( g . expr_string_surround ( ' \t $ cname = * ( $ styp * ) ' , expr , ' . d a t a ; ' ) )
2021-11-19 18:02:33 +01:00
g . init . writeln ( ' } ' )
2021-01-30 11:46:36 +01:00
} else {
2021-09-28 09:28:04 +02:00
g . init . writeln ( g . expr_string_surround ( ' \t $ cname = ' , expr , ' ; ' ) )
2021-01-30 11:46:36 +01:00
}
2021-01-10 13:31:20 +01:00
}
2021-01-08 13:56:55 +01:00
if g . is_autofree {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( typ )
2021-02-15 14:51:57 +01:00
if styp . starts_with ( ' A r r a y _ ' ) {
2021-11-21 19:53:42 +01:00
if sym . has_method_with_generic_parent ( ' f r e e ' ) {
g . cleanup . writeln ( ' \t $ { styp } _ f r e e ( & $ cname ) ; ' )
} else {
g . cleanup . writeln ( ' \t a r r a y _ f r e e ( & $ cname ) ; ' )
}
2021-03-17 01:41:52 +01:00
} else if styp == ' s t r i n g ' {
2021-09-28 09:28:04 +02:00
g . cleanup . writeln ( ' \t s t r i n g _ f r e e ( & $ cname ) ; ' )
2021-03-17 01:41:52 +01:00
} else if sym . kind == . map {
2021-09-28 09:28:04 +02:00
g . cleanup . writeln ( ' \t m a p _ f r e e ( & $ cname ) ; ' )
2021-03-17 11:31:36 +01:00
} else if styp == ' I E r r o r ' {
2021-09-28 09:28:04 +02:00
g . cleanup . writeln ( ' \t I E r r o r _ f r e e ( & $ cname ) ; ' )
2021-03-17 01:33:31 +01:00
}
2020-05-25 09:17:06 +02:00
}
2020-05-02 15:18:49 +02:00
}
2020-10-03 07:03:44 +02:00
fn ( mut g Gen ) global_decl ( node ast . GlobalDecl ) {
2020-11-29 19:30:35 +01:00
mod := if g . pref . build_mode == . build_module && g . is_builtin_mod { ' s t a t i c ' } else { ' ' }
2021-11-18 17:09:31 +01:00
mut attributes := ' '
if node . attrs . contains ( ' w e a k ' ) {
attributes += ' V W E A K '
}
2020-10-03 07:03:44 +02:00
for field in node . fields {
2021-07-28 07:11:55 +02:00
if g . pref . skip_unused {
if field . name ! in g . table . used_globals {
$ if trace_skip_unused_globals ? {
eprintln ( ' > > s k i p p i n g u n u s e d g l o b a l n a m e : $ field . name ' )
}
continue
}
}
2020-10-03 07:03:44 +02:00
styp := g . typ ( field . typ )
if field . has_expr {
2021-11-18 17:09:31 +01:00
g . definitions . write_string ( ' $ mod $ styp $ attributes $ field . name ' )
2021-07-30 18:20:45 +02:00
if field . expr . is_literal ( ) {
g . definitions . writeln ( ' = $ { g . expr_string ( field . expr ) } ; / / g l o b a l ' )
} else {
g . definitions . writeln ( ' ; ' )
2021-09-28 09:28:04 +02:00
g . global_init . writeln ( ' \t $ field . name = $ { g . expr_string ( field . expr ) } ; / / g l o b a l ' )
2021-07-30 18:20:45 +02:00
}
2020-10-03 07:03:44 +02:00
} else {
2021-07-27 19:14:30 +02:00
default_initializer := g . type_default ( field . typ )
if default_initializer == ' { 0 } ' {
2021-11-18 17:09:31 +01:00
g . definitions . writeln ( ' $ mod $ styp $ attributes $ field . name = { 0 } ; / / g l o b a l ' )
2021-07-27 19:14:30 +02:00
} else {
2021-11-18 17:09:31 +01:00
g . definitions . writeln ( ' $ mod $ styp $ attributes $ field . name ; / / g l o b a l ' )
2022-01-06 13:10:37 +01:00
if field . name ! in [ ' a s _ c a s t _ t y p e _ i n d e x e s ' , ' g _ m e m o r y _ b l o c k ' , ' g l o b a l _ a l l o c a t o r ' ] {
2021-09-28 09:28:04 +02:00
g . global_init . writeln ( ' \t $ field . name = * ( $ styp * ) & ( ( $ styp [ ] ) { $ { g . type_default ( field . typ ) } } [ 0 ] ) ; / / g l o b a l ' )
2021-07-27 19:14:30 +02:00
}
}
2020-10-03 07:03:44 +02:00
}
}
}
2020-05-18 21:38:06 +02:00
fn ( mut g Gen ) go_back_out ( n int ) {
g . out . go_back ( n )
}
2020-06-24 12:53:36 +02:00
const (
2020-09-18 15:36:40 +02:00
skip_struct_init = [ ' s t r u c t s t a t ' , ' s t r u c t a d d r i n f o ' ]
2020-06-24 12:53:36 +02:00
)
2021-12-13 12:01:36 +01:00
fn ( mut g Gen ) struct_init ( node ast . StructInit ) {
styp := g . typ ( node . typ )
2021-02-08 00:28:29 +01:00
mut shared_styp := ' ' // only needed for shared x := St{...
2021-02-02 15:41:51 +01:00
if styp in c . skip_struct_init {
2020-09-18 15:36:40 +02:00
// needed for c++ compilers
2020-05-18 21:38:06 +02:00
g . go_back_out ( 3 )
return
}
2021-12-19 17:25:18 +01:00
mut sym := g . table . final_sym ( g . unwrap_generic ( node . typ ) )
2020-04-04 17:59:49 +02:00
is_amp := g . is_amp
2021-12-13 12:01:36 +01:00
is_multiline := node . fields . len > 5
2020-05-09 15:23:48 +02:00
g . is_amp = false // reset the flag immediately so that other struct inits in this expr are handled correctly
2020-04-04 17:59:49 +02:00
if is_amp {
2020-05-07 13:11:24 +02:00
g . out . go_back ( 1 ) // delete the `&` already generated in `prefix_expr()
2021-02-01 14:39:36 +01:00
}
2021-03-04 13:40:57 +01:00
if g . is_shared && ! g . inside_opt_data && ! g . is_arraymap_set {
2021-12-13 12:01:36 +01:00
mut shared_typ := node . typ . set_flag ( . shared_f )
2021-02-01 14:39:36 +01:00
shared_styp = g . typ ( shared_typ )
2021-04-19 14:38:48 +02:00
g . writeln ( ' ( $ shared_styp * ) _ _ d u p $ { shared_styp } ( & ( $ shared_styp ) { . m t x = { 0 } , . v a l = ( $ styp ) { ' )
2021-07-06 15:33:26 +02:00
} else if is_amp || g . inside_cast_in_heap > 0 {
2021-02-01 14:39:36 +01:00
g . write ( ' ( $ styp * ) m e m d u p ( & ( $ styp ) { ' )
2021-12-13 12:01:36 +01:00
} else if node . typ . is_ptr ( ) {
basetyp := g . typ ( node . typ . set_nr_muls ( 0 ) )
2021-01-28 09:05:09 +01:00
if is_multiline {
g . writeln ( ' & ( $ basetyp ) { ' )
} else {
g . write ( ' & ( $ basetyp ) { ' )
}
2020-04-05 18:03:36 +02:00
} else {
2021-02-01 14:39:36 +01:00
if is_multiline {
2020-07-09 17:25:48 +02:00
g . writeln ( ' ( $ styp ) { ' )
2020-07-04 12:44:25 +02:00
} else {
2020-07-04 21:49:04 +02:00
g . write ( ' ( $ styp ) { ' )
2020-07-04 12:44:25 +02:00
}
2020-03-23 08:59:34 +01:00
}
2020-04-26 13:49:31 +02:00
// mut fields := []string{}
2020-05-21 03:58:50 +02:00
mut inited_fields := map [ string ] int { } // TODO this is done in checker, move to ast node
2020-04-20 03:54:35 +02:00
/ *
2021-12-13 12:01:36 +01:00
if node . fields . len == 0 && node . exprs . len > 0 {
2020-03-26 17:03:14 +01:00
// Get fields for {a,b} short syntax. Fields array wasn't set in the parser.
for f in info . fields {
fields << f . name
}
2020-04-05 18:03:36 +02:00
} else {
2021-12-13 12:01:36 +01:00
fields = node . fields
2020-04-20 03:54:35 +02:00
}
2020-04-25 17:49:16 +02:00
* /
2020-07-11 11:25:53 +02:00
if is_multiline {
g . indent ++
}
2020-04-12 12:35:54 +02:00
// User set fields
2020-05-18 21:38:06 +02:00
mut initialized := false
2021-08-06 04:11:03 +02:00
mut old_is_shared := g . is_shared
2021-12-13 12:01:36 +01:00
for i , field in node . fields {
2021-08-06 04:11:03 +02:00
if ! field . typ . has_flag ( . shared_f ) {
g . is_shared = false
}
2020-05-18 21:38:06 +02:00
inited_fields [ field . name ] = i
if sym . kind != . struct_ {
2021-07-22 00:37:49 +02:00
field_name := if sym . language == . v { c_name ( field . name ) } else { field . name }
2020-07-04 21:49:04 +02:00
g . write ( ' . $ field_name = ' )
2020-10-30 19:26:08 +01:00
if field . typ == 0 {
g . checker_bug ( ' s t r u c t i n i t , f i e l d . t y p i s 0 ' , field . pos )
}
2021-12-19 17:25:18 +01:00
field_type_sym := g . table . sym ( field . typ )
2020-05-18 21:38:06 +02:00
mut cloned := false
2021-01-08 13:56:55 +01:00
if g . is_autofree && ! field . typ . is_ptr ( ) && field_type_sym . kind in [ . array , . string ] {
2020-05-18 21:38:06 +02:00
g . write ( ' / * c l o n e 1 * / ' )
2021-09-28 09:28:04 +02:00
if g . gen_clone_assignment ( field . expr , field . typ , false ) {
2020-05-18 21:38:06 +02:00
cloned = true
}
2020-05-08 15:07:50 +02:00
}
2020-05-18 21:38:06 +02:00
if ! cloned {
2021-02-08 00:28:29 +01:00
if ( field . expected_type . is_ptr ( ) && ! field . expected_type . has_flag ( . shared_f ) )
&& ! ( field . typ . is_ptr ( ) || field . typ . is_pointer ( ) ) && ! field . typ . is_number ( ) {
2020-05-18 21:38:06 +02:00
g . write ( ' / * a u t o r e f * / & ' )
}
g . expr_with_cast ( field . expr , field . typ , field . expected_type )
2020-05-18 05:10:56 +02:00
}
2021-12-13 12:01:36 +01:00
if i != node . fields . len - 1 {
2020-07-09 17:25:48 +02:00
if is_multiline {
g . writeln ( ' , ' )
} else {
g . write ( ' , ' )
}
2020-07-04 21:49:04 +02:00
}
2020-05-18 21:38:06 +02:00
initialized = true
2020-05-08 15:07:50 +02:00
}
2021-08-06 04:11:03 +02:00
g . is_shared = old_is_shared
2020-03-23 08:59:34 +01:00
}
2021-08-06 04:11:03 +02:00
g . is_shared = old_is_shared
2020-03-23 08:59:34 +01:00
// The rest of the fields are zeroed.
2020-06-24 12:53:36 +02:00
// `inited_fields` is a list of fields that have been init'ed, they are skipped
2021-04-18 15:34:25 +02:00
mut nr_fields := 1
2020-05-07 13:00:42 +02:00
if sym . kind == . struct_ {
2021-10-17 05:41:39 +02:00
mut info := sym . info as ast . Struct
2021-04-18 15:34:25 +02:00
nr_fields = info . fields . len
2021-12-13 12:01:36 +01:00
if info . is_union && node . fields . len > 1 {
2020-05-18 21:38:06 +02:00
verror ( ' u n i o n m u s t n o t h a v e m o r e t h a n 1 i n i t i a l i z e r ' )
}
2021-06-13 22:53:38 +02:00
if ! info . is_union {
2021-08-06 04:11:03 +02:00
old_is_shared2 := g . is_shared
2021-07-20 14:06:06 +02:00
mut used_embed_fields := [ ] string { }
init_field_names := info . fields . map ( it . name )
// fields that are initialized but belong to the embedding
2021-12-13 12:01:36 +01:00
init_fields_to_embed := node . fields . filter ( it . name ! in init_field_names )
2021-06-13 22:53:38 +02:00
for embed in info . embeds {
2021-12-19 17:25:18 +01:00
embed_sym := g . table . sym ( embed )
2021-06-13 22:53:38 +02:00
embed_name := embed_sym . embed_name ( )
if embed_name ! in inited_fields {
2021-07-20 14:06:06 +02:00
embed_info := embed_sym . info as ast . Struct
embed_field_names := embed_info . fields . map ( it . name )
fields_to_embed := init_fields_to_embed . filter ( it . name ! in used_embed_fields
&& it . name in embed_field_names )
used_embed_fields << fields_to_embed . map ( it . name )
2021-06-13 22:53:38 +02:00
default_init := ast . StructInit {
2021-12-13 12:01:36 +01:00
... node
2021-06-13 22:53:38 +02:00
typ : embed
2021-12-12 18:54:29 +01:00
fields : init_fields_to_embed
2021-06-13 22:53:38 +02:00
}
2021-12-17 14:32:31 +01:00
inside_cast_in_heap := g . inside_cast_in_heap
g . inside_cast_in_heap = 0 // prevent use of pointers in child structs
2021-06-13 22:53:38 +02:00
g . write ( ' . $ embed_name = ' )
g . struct_init ( default_init )
2021-12-17 14:32:31 +01:00
g . inside_cast_in_heap = inside_cast_in_heap // restore value for further struct inits
2021-06-13 22:53:38 +02:00
if is_multiline {
g . writeln ( ' , ' )
} else {
g . write ( ' , ' )
}
initialized = true
2020-12-23 19:12:49 +01:00
}
}
2021-08-06 04:11:03 +02:00
g . is_shared = old_is_shared2
2020-12-23 19:12:49 +01:00
}
2020-06-24 14:44:06 +02:00
// g.zero_struct_fields(info, inited_fields)
// nr_fields = info.fields.len
2021-05-06 10:44:48 +02:00
for mut field in info . fields {
2021-08-06 04:11:03 +02:00
if ! field . typ . has_flag ( . shared_f ) {
g . is_shared = false
}
2021-04-02 00:57:09 +02:00
if mut sym . info is ast . Struct {
2021-05-06 10:44:48 +02:00
mut found_equal_fields := 0
for mut sifield in sym . info . fields {
if sifield . name == field . name {
found_equal_fields ++
break
}
}
if found_equal_fields == 0 {
2020-10-30 19:26:08 +01:00
continue
}
2020-10-30 07:09:26 +01:00
}
2020-03-23 08:59:34 +01:00
if field . name in inited_fields {
2021-12-13 12:01:36 +01:00
sfield := node . fields [ inited_fields [ field . name ] ]
2021-07-22 00:37:49 +02:00
field_name := if sym . language == . v { c_name ( field . name ) } else { field . name }
2020-12-15 04:24:57 +01:00
if sfield . typ == 0 {
continue
}
2020-07-04 21:49:04 +02:00
g . write ( ' . $ field_name = ' )
2021-12-19 17:25:18 +01:00
field_type_sym := g . table . sym ( sfield . typ )
2020-05-18 21:38:06 +02:00
mut cloned := false
2021-01-08 13:56:55 +01:00
if g . is_autofree && ! sfield . typ . is_ptr ( ) && field_type_sym . kind in [ . array , . string ] {
2020-05-18 21:38:06 +02:00
g . write ( ' / * c l o n e 1 * / ' )
2021-09-28 09:28:04 +02:00
if g . gen_clone_assignment ( sfield . expr , sfield . typ , false ) {
2020-05-18 21:38:06 +02:00
cloned = true
}
}
if ! cloned {
2021-08-08 09:54:52 +02:00
if field_type_sym . kind == . array_fixed && sfield . expr is ast . Ident {
fixed_array_info := field_type_sym . info as ast . ArrayFixed
g . write ( ' { ' )
for i in 0 .. fixed_array_info . size {
g . expr ( sfield . expr )
g . write ( ' [ $ i ] ' )
if i != fixed_array_info . size - 1 {
g . write ( ' , ' )
}
}
g . write ( ' } ' )
} else {
if ( sfield . expected_type . is_ptr ( )
&& ! sfield . expected_type . has_flag ( . shared_f ) ) && ! ( sfield . typ . is_ptr ( )
|| sfield . typ . is_pointer ( ) ) && ! sfield . typ . is_number ( ) {
g . write ( ' / * a u t o r e f * / & ' )
}
g . expr_with_cast ( sfield . expr , sfield . typ , sfield . expected_type )
2020-05-18 21:38:06 +02:00
}
}
2020-07-09 17:25:48 +02:00
if is_multiline {
g . writeln ( ' , ' )
} else {
g . write ( ' , ' )
2020-07-04 21:49:04 +02:00
}
2020-05-18 21:38:06 +02:00
initialized = true
continue
}
if info . is_union {
// unions thould have exactly one explicit initializer
2020-03-23 08:59:34 +01:00
continue
}
2020-06-04 14:38:54 +02:00
if field . typ . has_flag ( . optional ) {
2021-04-27 07:18:48 +02:00
field_name := c_name ( field . name )
2021-07-03 19:11:56 +02:00
g . write ( ' . $ field_name = { E M P T Y _ S T R U C T _ I N I T I A L I Z A T I O N } , ' )
2021-04-27 07:18:48 +02:00
initialized = true
2020-04-06 18:46:46 +02:00
continue
}
2020-12-25 21:46:42 +01:00
if field . typ in info . embeds {
continue
}
2021-12-13 12:01:36 +01:00
if node . has_update_expr {
g . expr ( node . update_expr )
if node . update_expr_type . is_ptr ( ) {
2021-01-04 19:19:03 +01:00
g . write ( ' - > ' )
} else {
g . write ( ' . ' )
}
g . write ( field . name )
} else {
2021-04-18 15:34:25 +02:00
if ! g . zero_struct_field ( field ) {
nr_fields --
continue
}
2021-01-04 19:19:03 +01:00
}
2020-07-09 17:25:48 +02:00
if is_multiline {
g . writeln ( ' , ' )
} else {
g . write ( ' , ' )
}
2020-05-18 21:38:06 +02:00
initialized = true
2021-08-06 04:11:03 +02:00
g . is_shared = old_is_shared
2020-03-23 08:59:34 +01:00
}
2021-08-06 04:11:03 +02:00
g . is_shared = old_is_shared
2020-05-07 13:11:24 +02:00
}
2020-07-11 11:25:53 +02:00
if is_multiline {
g . indent --
}
2021-02-16 23:40:10 +01:00
2021-04-17 18:19:25 +02:00
if ! initialized {
2021-04-18 15:34:25 +02:00
if nr_fields > 0 {
g . write ( ' 0 ' )
} else {
g . write ( ' E M P T Y _ S T R U C T _ I N I T I A L I Z A T I O N ' )
}
2020-03-23 08:59:34 +01:00
}
2021-02-16 23:40:10 +01:00
2020-03-23 08:59:34 +01:00
g . write ( ' } ' )
2021-03-04 13:40:57 +01:00
if g . is_shared && ! g . inside_opt_data && ! g . is_arraymap_set {
2021-02-01 14:39:36 +01:00
g . write ( ' } , s i z e o f ( $ shared_styp ) ) ' )
2021-07-06 15:33:26 +02:00
} else if is_amp || g . inside_cast_in_heap > 0 {
2020-03-23 08:59:34 +01:00
g . write ( ' , s i z e o f ( $ styp ) ) ' )
}
}
2021-04-18 15:34:25 +02:00
fn ( mut g Gen ) zero_struct_field ( field ast . StructField ) bool {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( field . typ )
2021-04-18 15:34:25 +02:00
if sym . kind == . struct_ {
info := sym . info as ast . Struct
if info . fields . len == 0 {
return false
}
}
2021-07-22 00:37:49 +02:00
field_name := if sym . language == . v { c_name ( field . name ) } else { field . name }
2020-07-09 17:25:48 +02:00
g . write ( ' . $ field_name = ' )
2020-06-24 12:53:36 +02:00
if field . has_default_expr {
2021-02-08 16:33:05 +01:00
if sym . kind in [ . sum_type , . interface_ ] {
2021-04-02 00:57:09 +02:00
g . expr_with_cast ( field . default_expr , field . default_expr_typ , field . typ )
2021-04-18 15:34:25 +02:00
return true
2021-02-08 16:33:05 +01:00
}
2021-04-02 00:57:09 +02:00
g . expr ( field . default_expr )
2020-06-24 12:53:36 +02:00
} else {
g . write ( g . type_default ( field . typ ) )
}
2021-04-18 15:34:25 +02:00
return true
2020-06-24 12:53:36 +02:00
}
2021-04-02 00:57:09 +02:00
// fn (mut g Gen) zero_struct_fields(info ast.Struct, inited_fields map[string]int) {
2020-06-24 14:44:06 +02:00
// }
2020-03-19 07:59:01 +01:00
// { user | name: 'new name' }
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) assoc ( node ast . Assoc ) {
2020-03-19 08:49:47 +01:00
g . writeln ( ' / / a s s o c ' )
2020-03-19 08:14:09 +01:00
if node . typ == 0 {
return
}
styp := g . typ ( node . typ )
g . writeln ( ' ( $ styp ) { ' )
2020-05-21 03:58:50 +02:00
mut inited_fields := map [ string ] int { }
2020-03-19 08:14:09 +01:00
for i , field in node . fields {
2020-05-18 21:38:06 +02:00
inited_fields [ field ] = i
2020-03-19 08:14:09 +01:00
}
2020-05-18 21:38:06 +02:00
// Merge inited_fields in the rest of the fields.
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( node . typ )
2021-04-02 00:57:09 +02:00
info := sym . info as ast . Struct
2020-03-19 08:14:09 +01:00
for field in info . fields {
2020-05-18 17:05:48 +02:00
field_name := c_name ( field . name )
2020-05-18 21:38:06 +02:00
if field . name in inited_fields {
g . write ( ' \t . $ field_name = ' )
g . expr ( node . exprs [ inited_fields [ field . name ] ] )
g . writeln ( ' , ' )
} else {
g . writeln ( ' \t . $ field_name = $ { node . var_name } . $ field_name , ' )
}
2020-03-19 08:14:09 +01:00
}
g . write ( ' } ' )
if g . is_amp {
g . write ( ' , s i z e o f ( $ styp ) ) ' )
}
}
2020-03-19 07:59:01 +01:00
2021-07-04 19:24:19 +02:00
[ noreturn ]
2019-12-26 11:27:35 +01:00
fn verror ( s string ) {
2020-04-03 17:38:41 +02:00
util . verror ( ' c g e n e r r o r ' , s )
2019-12-24 18:54:43 +01:00
}
2020-03-10 23:21:26 +01:00
2021-07-04 19:24:19 +02:00
[ noreturn ]
2020-08-14 21:01:43 +02:00
fn ( g & Gen ) error ( s string , pos token . Position ) {
2020-10-30 19:26:08 +01:00
ferror := util . formatted_error ( ' c g e n e r r o r : ' , s , g . file . path , pos )
eprintln ( ferror )
exit ( 1 )
}
fn ( g & Gen ) checker_bug ( s string , pos token . Position ) {
g . error ( ' c h e c k e r b u g ; $ s ' , pos )
2020-08-14 21:01:43 +02:00
}
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) write_init_function ( ) {
2021-10-23 11:53:53 +02:00
if g . pref . no_builtin {
return
}
2021-09-30 19:49:09 +02:00
util . timing_start ( @METHOD )
defer {
util . timing_measure ( @METHOD )
}
2020-05-01 19:34:27 +02:00
if g . pref . is_liveshared {
return
}
2020-05-25 08:47:00 +02:00
fn_vinit_start_pos := g . out . len
2021-09-28 09:28:04 +02:00
2021-01-10 13:31:20 +01:00
// ___argv is declared as voidptr here, because that unifies the windows/unix logic
g . writeln ( ' v o i d _ v i n i t ( i n t _ _ _ a r g c , v o i d p t r _ _ _ a r g v ) { ' )
2021-09-28 09:28:04 +02:00
2022-01-08 10:06:32 +01:00
if ' n o _ s e g f a u l t _ h a n d l e r ' ! in g . pref . compile_defines || g . pref . os == . wasm32 {
2021-12-03 18:01:45 +01:00
// 11 is SIGSEGV. It is hardcoded here, to avoid FreeBSD compilation errors for trivial examples.
g . writeln ( ' # i f _ _ S T D C _ H O S T E D _ _ = = 1 \n \t s i g n a l ( 1 1 , v _ s e g m e n t a t i o n _ f a u l t _ h a n d l e r ) ; \n # e n d i f ' )
}
2022-01-06 13:10:37 +01:00
if g . pref . is_bare {
g . writeln ( ' i n i t _ g l o b a l _ a l l o c a t o r ( ) ; ' )
}
2020-07-11 11:25:53 +02:00
if g . pref . prealloc {
2021-05-19 09:35:56 +02:00
g . writeln ( ' p r e a l l o c _ v i n i t ( ) ; ' )
2020-07-11 11:25:53 +02:00
}
2021-01-30 17:54:05 +01:00
// NB: the as_cast table should be *before* the other constant initialize calls,
// because it may be needed during const initialization of builtin and during
// calling module init functions too, just in case they do fail...
g . write ( ' \t a s _ c a s t _ t y p e _ i n d e x e s = ' )
g . writeln ( g . as_cast_name_table ( ) )
//
2020-04-05 16:08:16 +02:00
g . writeln ( ' \t b u i l t i n _ i n i t ( ) ; ' )
2020-04-04 22:06:47 +02:00
g . writeln ( ' \t v i n i t _ s t r i n g _ l i t e r a l s ( ) ; ' )
2020-07-08 13:19:58 +02:00
//
for mod_name in g . table . modules {
2021-11-19 17:15:06 +01:00
g . writeln ( ' \t { / / I n i t i a l i z a t i o n s f o r m o d u l e $ mod_name : ' )
2020-07-08 13:19:58 +02:00
g . write ( g . inits [ mod_name ] . str ( ) )
2021-07-29 09:57:31 +02:00
g . write ( g . global_inits [ mod_name ] . str ( ) )
2020-04-11 18:38:51 +02:00
init_fn_name := ' $ { mod_name } . i n i t '
2020-07-08 13:19:58 +02:00
if initfn := g . table . find_fn ( init_fn_name ) {
2021-04-02 00:57:09 +02:00
if initfn . return_type == ast . void_type && initfn . params . len == 0 {
2020-07-08 13:19:58 +02:00
mod_c_name := util . no_dots ( mod_name )
init_fn_c_name := ' $ { mod_c_name } _ _ i n i t '
g . writeln ( ' \t $ { init_fn_c_name } ( ) ; ' )
}
2020-04-11 18:38:51 +02:00
}
2021-11-19 17:15:06 +01:00
g . writeln ( ' \t } ' )
2020-04-11 18:38:51 +02:00
}
2020-03-21 09:29:16 +01:00
g . writeln ( ' } ' )
2020-05-25 08:47:00 +02:00
if g . pref . printfn_list . len > 0 && ' _ v i n i t ' in g . pref . printfn_list {
println ( g . out . after ( fn_vinit_start_pos ) )
}
2021-01-31 11:10:49 +01:00
//
fn_vcleanup_start_pos := g . out . len
2021-10-24 14:29:36 +02:00
g . writeln ( ' v o i d _ v c l e a n u p ( v o i d ) { ' )
2021-01-08 13:56:55 +01:00
if g . is_autofree {
2020-03-31 20:26:15 +02:00
// g.writeln('puts("cleaning up...");')
2020-07-08 13:19:58 +02:00
reversed_table_modules := g . table . modules . reverse ( )
for mod_name in reversed_table_modules {
g . writeln ( ' \t / / C l e a n u p s f o r m o d u l e $ mod_name : ' )
g . writeln ( g . cleanups [ mod_name ] . str ( ) )
}
2021-01-30 17:54:05 +01:00
g . writeln ( ' \t a r r a y _ f r e e ( & a s _ c a s t _ t y p e _ i n d e x e s ) ; ' )
2020-03-21 19:52:19 +01:00
}
2021-01-31 11:10:49 +01:00
g . writeln ( ' } ' )
if g . pref . printfn_list . len > 0 && ' _ v c l e a n u p ' in g . pref . printfn_list {
println ( g . out . after ( fn_vcleanup_start_pos ) )
}
//
2021-01-10 13:31:20 +01:00
needs_constructor := g . pref . is_shared && g . pref . os != . windows
if needs_constructor {
// shared libraries need a way to call _vinit/2. For that purpose,
2021-01-31 11:10:49 +01:00
// provide a constructor/destructor pair, ensuring that all constants
// are initialized just once, and that they will be freed too.
2021-01-10 13:31:20 +01:00
// NB: os.args in this case will be [].
g . writeln ( ' _ _ a t t r i b u t e _ _ ( ( c o n s t r u c t o r ) ) ' )
g . writeln ( ' v o i d _ v i n i t _ c a l l e r ( ) { ' )
g . writeln ( ' \t s t a t i c b o o l o n c e = f a l s e ; i f ( o n c e ) { r e t u r n ; } o n c e = t r u e ; ' )
g . writeln ( ' \t _ v i n i t ( 0 , 0 ) ; ' )
g . writeln ( ' } ' )
2021-01-31 11:10:49 +01:00
g . writeln ( ' _ _ a t t r i b u t e _ _ ( ( d e s t r u c t o r ) ) ' )
g . writeln ( ' v o i d _ v c l e a n u p _ c a l l e r ( ) { ' )
g . writeln ( ' \t s t a t i c b o o l o n c e = f a l s e ; i f ( o n c e ) { r e t u r n ; } o n c e = t r u e ; ' )
g . writeln ( ' \t _ v c l e a n u p ( ) ; ' )
g . writeln ( ' } ' )
2021-01-10 13:31:20 +01:00
}
2020-03-21 09:29:16 +01:00
}
2020-03-10 23:21:26 +01:00
const (
2021-06-02 18:14:37 +02:00
builtins = [ ' s t r i n g ' , ' a r r a y ' , ' D e n s e A r r a y ' , ' m a p ' , ' E r r o r ' , ' I E r r o r ' , ' O p t i o n ' ]
2020-03-10 23:21:26 +01:00
)
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) write_builtin_types ( ) {
2021-10-23 11:53:53 +02:00
if g . pref . no_builtin {
return
}
2021-12-01 08:50:53 +01:00
mut builtin_types := [ ] & ast . TypeSymbol { } // builtin types
2020-03-05 23:27:21 +01:00
// builtin types need to be on top
// everything except builtin will get sorted
2021-02-02 15:41:51 +01:00
for builtin_name in c . builtins {
2021-12-19 17:25:18 +01:00
sym := g . table . sym_by_idx ( g . table . type_idxs [ builtin_name ] )
2021-03-12 19:05:05 +01:00
if sym . kind == . interface_ {
2021-07-19 18:57:12 +02:00
g . write_interface_typedef ( sym )
2021-03-12 19:05:05 +01:00
g . write_interface_typesymbol_declaration ( sym )
} else {
builtin_types << sym
}
2020-03-06 16:57:27 +01:00
}
2020-03-10 23:21:26 +01:00
g . write_types ( builtin_types )
}
// C struct definitions, ordered
// Sort the types, make sure types that are referenced by other types
// are added before them.
2020-04-21 05:11:50 +02:00
fn ( mut g Gen ) write_sorted_types ( ) {
2021-12-01 08:50:53 +01:00
mut symbols := [ ] & ast . TypeSymbol { cap : g . table . type_symbols . len } // structs that need to be sorted
for sym in g . table . type_symbols {
if sym . name ! in c . builtins {
symbols << sym
2020-03-05 23:27:21 +01:00
}
}
// sort structs
2021-12-01 08:50:53 +01:00
sorted_symbols := g . sort_structs ( symbols )
2020-03-05 23:27:21 +01:00
// Generate C code
2020-05-31 12:57:26 +02:00
g . type_definitions . writeln ( ' / / b u i l t i n t y p e s : ' )
g . type_definitions . writeln ( ' / / - - - - - - - - - - - - - - - - - - # e n d b u i l t i n ' )
2021-12-01 08:50:53 +01:00
g . write_types ( sorted_symbols )
2020-03-05 23:27:21 +01:00
}
2021-12-01 08:50:53 +01:00
fn ( mut g Gen ) write_types ( symbols [ ] & ast . TypeSymbol ) {
for sym in symbols {
if sym . name . starts_with ( ' C . ' ) {
2020-03-13 01:43:30 +01:00
continue
}
2021-12-01 08:50:53 +01:00
if sym . kind == . none_ {
2021-10-09 02:32:37 +02:00
g . type_definitions . writeln ( ' s t r u c t n o n e { ' )
g . type_definitions . writeln ( ' \t E M P T Y _ S T R U C T _ D E C L A R A T I O N ; ' )
g . type_definitions . writeln ( ' } ; ' )
2021-12-19 13:31:04 +01:00
g . typedefs . writeln ( ' t y p e d e f s t r u c t n o n e n o n e ; ' )
2021-10-09 02:32:37 +02:00
}
2021-12-19 17:25:18 +01:00
// sym := g.table.sym(typ)
2021-12-01 08:50:53 +01:00
mut name := sym . cname
match mut sym . info {
2021-04-02 00:57:09 +02:00
ast . Struct {
2021-12-01 08:50:53 +01:00
if sym . info . is_generic {
2020-06-29 20:09:09 +02:00
continue
}
2020-12-01 11:17:19 +01:00
if name . contains ( ' _ T _ ' ) {
2020-06-29 20:09:09 +02:00
g . typedefs . writeln ( ' t y p e d e f s t r u c t $ name $ name ; ' )
}
2020-05-31 12:57:26 +02:00
// TODO avoid buffer manip
start_pos := g . type_definitions . len
2021-06-13 22:53:38 +02:00
mut pre_pragma := ' '
mut post_pragma := ' '
2021-12-01 08:50:53 +01:00
for attr in sym . info . attrs {
2021-06-13 22:53:38 +02:00
match attr . name {
' _ p a c k ' {
pre_pragma += ' # p r a g m a p a c k ( p u s h , $ attr . arg ) \n '
post_pragma += ' # p r a g m a p a c k ( p o p ) '
}
else { }
}
}
g . type_definitions . writeln ( pre_pragma )
2021-12-01 08:50:53 +01:00
if sym . info . is_union {
2020-05-31 12:57:26 +02:00
g . type_definitions . writeln ( ' u n i o n $ name { ' )
2020-04-08 01:20:55 +02:00
} else {
2020-05-31 12:57:26 +02:00
g . type_definitions . writeln ( ' s t r u c t $ name { ' )
2020-04-08 01:20:55 +02:00
}
2021-12-01 08:50:53 +01:00
if sym . info . fields . len > 0 || sym . info . embeds . len > 0 {
for field in sym . info . fields {
2020-05-31 12:57:26 +02:00
// Some of these structs may want to contain
// optionals that may not be defined at this point
2020-06-01 15:43:54 +02:00
// if this is the case then we are going to
2020-05-31 12:57:26 +02:00
// buffer manip out in front of the struct
// write the optional in and then continue
2021-09-28 09:28:04 +02:00
// FIXME: for parallel cgen (two different files using the same optional in struct fields)
2020-06-04 14:38:54 +02:00
if field . typ . has_flag ( . optional ) {
2020-05-31 12:57:26 +02:00
// Dont use g.typ() here becuase it will register
// optional and we dont want that
styp , base := g . optional_type_name ( field . typ )
2021-09-28 09:28:04 +02:00
lock g . done_optionals {
if base ! in g . done_optionals {
g . done_optionals << base
last_text := g . type_definitions . after ( start_pos ) . clone ( )
g . type_definitions . go_back_to ( start_pos )
2021-12-19 13:31:04 +01:00
g . typedefs . writeln ( ' t y p e d e f s t r u c t $ styp $ styp ; ' )
2021-09-28 09:28:04 +02:00
g . type_definitions . writeln ( ' $ { g . optional_type_text ( styp ,
base ) } ; ' )
g . type_definitions . write_string ( last_text )
}
2021-02-21 14:00:39 +01:00
}
2020-05-31 12:57:26 +02:00
}
2020-04-05 12:31:25 +02:00
type_name := g . typ ( field . typ )
field_name := c_name ( field . name )
2021-11-04 08:31:40 +01:00
volatile_prefix := if field . is_volatile { ' v o l a t i l e ' } else { ' ' }
g . type_definitions . writeln ( ' \t $ volatile_prefix $ type_name $ field_name ; ' )
2020-04-05 18:15:12 +02:00
}
} else {
2021-05-10 19:27:52 +02:00
g . type_definitions . writeln ( ' \t E M P T Y _ S T R U C T _ D E C L A R A T I O N ; ' )
2020-04-05 18:15:12 +02:00
}
2020-05-31 12:57:26 +02:00
// g.type_definitions.writeln('} $name;\n')
2020-04-08 13:53:11 +02:00
//
2021-12-01 08:50:53 +01:00
ti_attrs := if sym . info . attrs . contains ( ' p a c k e d ' ) {
2021-04-05 07:02:19 +02:00
' _ _ a t t r i b u t e _ _ ( ( _ _ p a c k e d _ _ ) ) '
} else {
' '
}
2021-06-13 22:53:38 +02:00
g . type_definitions . writeln ( ' } $ ti_attrs ; \n ' )
g . type_definitions . writeln ( post_pragma )
2020-03-05 23:27:21 +01:00
}
2021-04-02 00:57:09 +02:00
ast . Alias {
// ast.Alias { TODO
2020-04-01 17:13:21 +02:00
}
2021-04-02 00:57:09 +02:00
ast . Thread {
2021-01-15 13:45:26 +01:00
if g . pref . os == . windows {
2021-02-23 08:37:29 +01:00
if name == ' _ _ v _ t h r e a d ' {
2021-01-15 13:45:26 +01:00
g . type_definitions . writeln ( ' t y p e d e f H A N D L E $ name ; ' )
} else {
// Windows can only return `u32` (no void*) from a thread, so the
// V gohandle must maintain a pointer to the return value
g . type_definitions . writeln ( ' t y p e d e f s t r u c t { ' )
2021-02-22 14:08:52 +01:00
g . type_definitions . writeln ( ' \t v o i d * r e t _ p t r ; ' )
2021-01-15 13:45:26 +01:00
g . type_definitions . writeln ( ' \t H A N D L E h a n d l e ; ' )
g . type_definitions . writeln ( ' } $ name ; ' )
}
} else {
2021-10-23 11:53:53 +02:00
if ! g . pref . is_bare && ! g . pref . no_builtin {
2021-04-14 07:50:50 +02:00
g . type_definitions . writeln ( ' t y p e d e f p t h r e a d _ t $ name ; ' )
}
2021-01-15 13:45:26 +01:00
}
}
2021-04-02 00:57:09 +02:00
ast . SumType {
2021-12-01 08:50:53 +01:00
if sym . info . is_generic {
2021-07-15 07:29:13 +02:00
continue
}
2020-11-24 12:53:39 +01:00
g . typedefs . writeln ( ' t y p e d e f s t r u c t $ name $ name ; ' )
2020-11-11 09:18:15 +01:00
g . type_definitions . writeln ( ' ' )
g . type_definitions . writeln ( ' / / U n i o n s u m t y p e $ name = ' )
2021-12-01 08:50:53 +01:00
for variant in sym . info . variants {
2020-11-11 09:18:15 +01:00
g . type_definitions . writeln ( ' / / | $ { variant : 4 d } = $ { g . typ ( variant . idx ( ) ) : - 20 s } ' )
}
2020-11-24 12:53:39 +01:00
g . type_definitions . writeln ( ' s t r u c t $ name { ' )
2021-03-09 18:16:18 +01:00
g . type_definitions . writeln ( ' \t u n i o n { ' )
2021-12-01 08:50:53 +01:00
for variant in sym . info . variants {
2021-12-19 17:25:18 +01:00
variant_sym := g . table . sym ( variant )
2021-10-06 19:49:39 +02:00
g . type_definitions . writeln ( ' \t \t $ { g . typ ( variant . ref ( ) ) } _ $ variant_sym . cname ; ' )
2021-03-09 18:16:18 +01:00
}
g . type_definitions . writeln ( ' \t } ; ' )
g . type_definitions . writeln ( ' \t i n t _ t y p ; ' )
2021-12-01 08:50:53 +01:00
if sym . info . fields . len > 0 {
2021-03-09 18:16:18 +01:00
g . writeln ( ' \t / / p o i n t e r s t o c o m m o n s u m t y p e f i e l d s ' )
2021-12-01 08:50:53 +01:00
for field in sym . info . fields {
2021-10-06 19:49:39 +02:00
g . type_definitions . writeln ( ' \t $ { g . typ ( field . typ . ref ( ) ) } $ field . name ; ' )
2021-03-09 18:16:18 +01:00
}
2020-11-11 09:18:15 +01:00
}
2020-11-24 12:53:39 +01:00
g . type_definitions . writeln ( ' } ; ' )
2020-11-11 09:18:15 +01:00
g . type_definitions . writeln ( ' ' )
}
2021-04-02 00:57:09 +02:00
ast . ArrayFixed {
2021-12-19 17:25:18 +01:00
elem_sym := g . table . sym ( sym . info . elem_type )
2021-12-01 08:50:53 +01:00
if ! elem_sym . is_builtin ( ) && ! sym . info . elem_type . has_flag ( . generic ) {
2021-03-31 18:51:03 +02:00
// .array_fixed {
2021-12-01 08:50:53 +01:00
styp := sym . cname
2021-03-31 18:51:03 +02:00
// array_fixed_char_300 => char x[300]
2021-05-20 14:59:02 +02:00
// [16]&&&EventListener{} => Array_fixed_main__EventListener_16_ptr3
// => typedef main__EventListener*** Array_fixed_main__EventListener_16_ptr3 [16]
2021-12-01 08:50:53 +01:00
mut fixed_elem_name := g . typ ( sym . info . elem_type . set_nr_muls ( 0 ) )
if sym . info . elem_type . is_ptr ( ) {
fixed_elem_name += ' * ' . repeat ( sym . info . elem_type . nr_muls ( ) )
2021-05-20 14:59:02 +02:00
}
2021-12-01 08:50:53 +01:00
len := sym . info . size
2021-05-20 14:59:02 +02:00
if fixed_elem_name . starts_with ( ' C _ _ ' ) {
fixed_elem_name = fixed_elem_name [ 3 .. ]
2021-03-31 18:51:03 +02:00
}
2021-04-02 00:57:09 +02:00
if elem_sym . info is ast . FnType {
2021-03-31 18:51:03 +02:00
pos := g . out . len
g . write_fn_ptr_decl ( & elem_sym . info , ' ' )
2021-08-10 20:27:15 +02:00
fixed_elem_name = g . out . cut_to ( pos )
2021-05-20 14:59:02 +02:00
mut def_str := ' t y p e d e f $ fixed_elem_name ; '
2021-03-31 18:51:03 +02:00
def_str = def_str . replace_once ( ' ( * ) ' , ' ( * $ styp [ $ len ] ) ' )
g . type_definitions . writeln ( def_str )
} else {
2021-05-20 14:59:02 +02:00
g . type_definitions . writeln ( ' t y p e d e f $ fixed_elem_name $ styp [ $ len ] ; ' )
2021-03-31 18:51:03 +02:00
}
2021-02-01 14:50:10 +01:00
}
2020-03-14 05:20:12 +01:00
}
2020-03-22 14:28:11 +01:00
else { }
2020-04-05 18:03:36 +02:00
}
2020-03-05 23:27:21 +01:00
}
}
// sort structs by dependant fields
2021-12-01 08:50:53 +01:00
fn ( g & Gen ) sort_structs ( typesa [ ] & ast . TypeSymbol ) [ ] & ast . TypeSymbol {
2021-09-30 19:49:09 +02:00
util . timing_start ( @METHOD )
defer {
util . timing_measure ( @METHOD )
}
2020-04-21 05:11:50 +02:00
mut dep_graph := depgraph . new_dep_graph ( )
2020-03-05 23:27:21 +01:00
// types name list
2020-04-26 09:17:13 +02:00
mut type_names := [ ] string { }
2021-12-01 08:50:53 +01:00
for sym in typesa {
type_names << sym . name
2020-03-05 23:27:21 +01:00
}
// loop over types
2021-12-01 08:50:53 +01:00
for sym in typesa {
if sym . kind == . interface_ {
dep_graph . add ( sym . name , [ ] )
2020-04-22 20:20:49 +02:00
continue
}
2020-03-06 12:01:56 +01:00
// create list of deps
2020-04-26 09:17:13 +02:00
mut field_deps := [ ] string { }
2021-12-01 08:50:53 +01:00
match mut sym . info {
2021-04-02 00:57:09 +02:00
ast . ArrayFixed {
2021-12-19 17:25:18 +01:00
dep := g . table . sym ( sym . info . elem_type ) . name
2020-04-01 17:13:21 +02:00
if dep in type_names {
field_deps << dep
}
}
2021-04-02 00:57:09 +02:00
ast . Struct {
2021-12-01 08:50:53 +01:00
for embed in sym . info . embeds {
2021-12-19 17:25:18 +01:00
dep := g . table . sym ( embed ) . name
2020-12-23 19:12:49 +01:00
// skip if not in types list or already in deps
if dep ! in type_names || dep in field_deps {
continue
}
field_deps << dep
}
2021-12-01 08:50:53 +01:00
for field in sym . info . fields {
2021-12-19 17:25:18 +01:00
dep := g . table . sym ( field . typ ) . name
2020-03-05 23:27:21 +01:00
// skip if not in types list or already in deps
2020-04-26 06:39:23 +02:00
if dep ! in type_names || dep in field_deps || field . typ . is_ptr ( ) {
2020-03-06 12:01:56 +01:00
continue
}
field_deps << dep
2020-03-05 23:27:21 +01:00
}
}
2021-04-02 00:57:09 +02:00
// ast.Interface {}
2020-03-05 23:27:21 +01:00
else { }
2020-04-05 18:03:36 +02:00
}
2020-03-06 12:01:56 +01:00
// add type and dependant types to graph
2021-12-01 08:50:53 +01:00
dep_graph . add ( sym . name , field_deps )
2020-03-05 23:27:21 +01:00
}
// sort graph
dep_graph_sorted := dep_graph . resolve ( )
if ! dep_graph_sorted . acyclic {
2021-01-09 01:33:36 +01:00
// this should no longer be called since it's catched in the parser
// TODO: should it be removed?
2021-01-23 09:33:22 +01:00
verror ( ' c g e n . s o r t _ s t r u c t s ( ) : t h e f o l l o w i n g s t r u c t s f o r m a d e p e n d e n c y c y c l e : \n ' +
dep_graph_sorted . display_cycles ( ) +
2020-04-08 00:59:28 +02:00
' \n y o u c a n s o l v e t h i s b y m a k i n g o n e o r b o t h o f t h e d e p e n d a n t s t r u c t f i e l d s r e f e r e n c e s , e g : f i e l d & M y S t r u c t ' +
2020-04-05 18:03:36 +02:00
' \n i f y o u f e e l t h i s i s a n e r r o r , p l e a s e c r e a t e a n e w i s s u e h e r e : h t t p s : / / g i t h u b . c o m / v l a n g / v / i s s u e s a n d t a g @ j o e - c o n i g l i a r o ' )
2020-03-05 23:27:21 +01:00
}
// sort types
2021-12-01 08:50:53 +01:00
mut sorted_symbols := [ ] & ast . TypeSymbol { cap : dep_graph_sorted . nodes . len }
2020-03-05 23:27:21 +01:00
for node in dep_graph_sorted . nodes {
2021-12-19 17:25:18 +01:00
sorted_symbols << g . table . sym_by_idx ( g . table . type_idxs [ node . name ] )
2020-03-05 23:27:21 +01:00
}
2021-12-01 08:50:53 +01:00
return sorted_symbols
2020-03-05 23:27:21 +01:00
}
2020-03-10 14:40:30 +01:00
2020-05-14 17:15:25 +02:00
[ inline ]
fn ( g & Gen ) nth_stmt_pos ( n int ) int {
return g . stmt_path_pos [ g . stmt_path_pos . len - ( 1 + n ) ]
}
2021-09-26 14:51:18 +02:00
[ inline ]
fn ( mut g Gen ) set_current_pos_as_last_stmt_pos ( ) {
g . stmt_path_pos << g . out . len
}
2020-05-14 17:15:25 +02:00
fn ( mut g Gen ) go_before_stmt ( n int ) string {
stmt_pos := g . nth_stmt_pos ( n )
2021-08-10 20:27:15 +02:00
return g . out . cut_to ( stmt_pos )
2020-05-14 17:15:25 +02:00
}
[ inline ]
fn ( mut g Gen ) go_before_ternary ( ) string {
return g . go_before_stmt ( g . inside_ternary )
}
fn ( mut g Gen ) insert_before_stmt ( s string ) {
cur_line := g . go_before_stmt ( 0 )
2020-03-25 23:57:22 +01:00
g . writeln ( s )
g . write ( cur_line )
}
2021-08-06 02:57:34 +02:00
fn ( mut g Gen ) insert_at ( pos int , s string ) {
cur_line := g . out . cut_to ( pos )
g . writeln ( s )
g . write ( cur_line )
}
2020-07-11 12:03:24 +02:00
// fn (mut g Gen) start_tmp() {
// }
2020-03-31 06:34:59 +02:00
// If user is accessing the return value eg. in assigment, pass the variable name.
// If the user is not using the optional return value. We need to pass a temp var
// to access its fields (`.ok`, `.error` etc)
2021-02-28 20:24:29 +01:00
// `os.cp(...)` => `Option bool tmp = os__cp(...); if (tmp.state != 0) { ... }`
2020-05-21 22:35:43 +02:00
// Returns the type of the last stmt
2021-04-02 00:57:09 +02:00
fn ( mut g Gen ) or_block ( var_name string , or_block ast . OrExpr , return_type ast . Type ) {
2020-05-05 18:28:25 +02:00
cvar_name := c_name ( var_name )
2020-05-04 00:14:59 +02:00
mr_styp := g . base_type ( return_type )
2021-06-13 22:53:38 +02:00
is_none_ok := return_type == ast . ovoid_type
2021-02-28 20:24:29 +01:00
g . writeln ( ' ; ' )
2020-06-10 17:01:55 +02:00
if is_none_ok {
2021-04-09 10:00:05 +02:00
g . writeln ( ' i f ( $ { cvar_name } . s t a t e ! = 0 & & $ { cvar_name } . e r r . _ t y p ! = _ I E r r o r _ N o n e _ _ _ i n d e x ) { ' )
2020-06-10 17:01:55 +02:00
} else {
2021-02-28 20:24:29 +01:00
g . writeln ( ' i f ( $ { cvar_name } . s t a t e ! = 0 ) { / * o r b l o c k * / ' )
2020-06-10 17:01:55 +02:00
}
2020-05-23 08:51:15 +02:00
if or_block . kind == . block {
2020-12-12 23:06:02 +01:00
if g . inside_or_block {
2021-02-28 20:24:29 +01:00
g . writeln ( ' \t e r r = $ { cvar_name } . e r r ; ' )
2020-12-12 23:06:02 +01:00
} else {
2021-03-13 18:13:50 +01:00
g . writeln ( ' \t I E r r o r e r r = $ { cvar_name } . e r r ; ' )
2020-12-12 23:06:02 +01:00
}
g . inside_or_block = true
defer {
g . inside_or_block = false
}
2020-05-23 08:51:15 +02:00
stmts := or_block . stmts
2021-01-23 09:33:22 +01:00
if stmts . len > 0 && stmts [ or_block . stmts . len - 1 ] is ast . ExprStmt
2021-04-02 00:57:09 +02:00
&& ( stmts [ stmts . len - 1 ] as ast . ExprStmt ) . typ != ast . void_type {
2020-05-23 08:51:15 +02:00
g . indent ++
for i , stmt in stmts {
if i == stmts . len - 1 {
expr_stmt := stmt as ast . ExprStmt
2021-09-26 14:51:18 +02:00
g . set_current_pos_as_last_stmt_pos ( )
2020-06-18 20:21:08 +02:00
g . write ( ' * ( $ mr_styp * ) $ { cvar_name } . d a t a = ' )
2021-02-06 03:07:05 +01:00
old_inside_opt_data := g . inside_opt_data
g . inside_opt_data = true
2021-01-24 20:56:44 +01:00
g . expr_with_cast ( expr_stmt . expr , expr_stmt . typ , return_type . clear_flag ( . optional ) )
2021-02-06 03:07:05 +01:00
g . inside_opt_data = old_inside_opt_data
2021-05-29 09:00:12 +02:00
if g . inside_ternary == 0 {
2020-05-23 08:51:15 +02:00
g . writeln ( ' ; ' )
}
2020-10-23 23:04:22 +02:00
g . stmt_path_pos . delete_last ( )
2020-05-23 08:51:15 +02:00
} else {
g . stmt ( stmt )
2020-05-21 22:35:43 +02:00
}
2020-04-05 10:37:14 +02:00
}
2020-05-23 08:51:15 +02:00
g . indent --
} else {
g . stmts ( stmts )
2021-06-03 00:18:52 +02:00
if stmts . len > 0 && stmts [ or_block . stmts . len - 1 ] is ast . ExprStmt {
g . writeln ( ' ; ' )
}
2020-05-23 08:51:15 +02:00
}
} else if or_block . kind == . propagate {
2021-03-05 12:19:39 +01:00
if g . file . mod . name == ' m a i n ' && ( isnil ( g . fn_decl ) || g . fn_decl . is_main ) {
2021-03-01 00:18:14 +01:00
// In main(), an `opt()?` call is sugar for `opt() or { panic(err) }`
2020-05-23 10:43:20 +02:00
if g . pref . is_debug {
paline , pafile , pamod , pafn := g . panic_debug_info ( or_block . pos )
2021-03-13 18:13:50 +01:00
g . writeln ( ' p a n i c _ d e b u g ( $ paline , t o s 3 ( " $ pafile " ) , t o s 3 ( " $ pamod " ) , t o s 3 ( " $ pafn " ) , * $ { cvar_name } . e r r . m s g ) ; ' )
2020-05-24 04:43:00 +02:00
} else {
2021-03-24 12:16:36 +01:00
g . writeln ( ' \t p a n i c _ o p t i o n a l _ n o t _ s e t ( * $ { cvar_name } . e r r . m s g ) ; ' )
2020-05-23 10:43:20 +02:00
}
2021-03-05 12:19:39 +01:00
} else if ! isnil ( g . fn_decl ) && g . fn_decl . is_test {
g . gen_failing_error_propagation_for_test_fn ( or_block , cvar_name )
2020-05-23 08:51:15 +02:00
} else {
2020-06-10 17:43:23 +02:00
// In ordinary functions, `opt()?` call is sugar for:
2021-02-28 21:20:21 +01:00
// `opt() or { return err }`
2020-06-12 01:24:25 +02:00
// Since we *do* return, first we have to ensure that
2020-06-10 17:43:23 +02:00
// the defered statements are generated.
g . write_defer_stmts ( )
2020-05-31 12:57:26 +02:00
// Now that option types are distinct we need a cast here
2021-04-02 00:57:09 +02:00
if g . fn_decl . return_type == ast . void_type {
2021-01-30 11:46:36 +01:00
g . writeln ( ' \t r e t u r n ; ' )
} else {
styp := g . typ ( g . fn_decl . return_type )
err_obj := g . new_tmp_var ( )
g . writeln ( ' \t $ styp $ err_obj ; ' )
2021-03-14 01:54:46 +01:00
g . writeln ( ' \t m e m c p y ( & $ err_obj , & $ cvar_name , s i z e o f ( O p t i o n ) ) ; ' )
2021-01-30 11:46:36 +01:00
g . writeln ( ' \t r e t u r n $ err_obj ; ' )
}
2020-04-05 10:37:14 +02:00
}
}
2021-06-03 00:18:52 +02:00
g . writeln ( ' } ' )
2021-09-26 14:51:18 +02:00
g . set_current_pos_as_last_stmt_pos ( )
2020-03-26 14:42:14 +01:00
}
2020-03-23 03:36:50 +01:00
[ inline ]
fn c_name ( name_ string ) string {
2020-07-01 00:53:53 +02:00
name := util . no_dots ( name_ )
2021-05-03 11:58:40 +02:00
if name in c . c_reserved_map {
2021-07-21 10:46:04 +02:00
return ' _ v _ $ name '
2020-03-23 03:36:50 +01:00
}
return name
}
2020-03-23 08:59:34 +01:00
2021-04-02 00:57:09 +02:00
fn ( mut g Gen ) type_default ( typ_ ast . Type ) string {
2020-11-06 15:26:59 +01:00
typ := g . unwrap_generic ( typ_ )
if typ . has_flag ( . optional ) {
return ' { 0 } '
}
// Always set pointers to 0
2021-04-11 09:30:23 +02:00
if typ . is_ptr ( ) && ! typ . has_flag ( . shared_f ) {
2020-12-06 05:18:24 +01:00
return ' 0 '
2020-11-06 15:26:59 +01:00
}
2021-04-26 10:54:32 +02:00
if typ . idx ( ) < ast . string_type_idx {
// Default values for other types are not needed because of mandatory initialization
return ' 0 '
}
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( typ )
2021-04-26 10:54:32 +02:00
match sym . kind {
. string {
return ' ( s t r i n g ) { . s t r = ( b y t e p t r ) " " , . i s _ l i t = 1 } '
2021-04-11 09:30:23 +02:00
}
2021-04-26 10:54:32 +02:00
. interface_ , . sum_type , . array_fixed , . multi_return {
return ' { 0 } '
2021-04-11 09:30:23 +02:00
}
2021-04-26 10:54:32 +02:00
. alias {
return g . type_default ( ( sym . info as ast . Alias ) . parent_type )
}
. chan {
2021-06-14 17:12:47 +02:00
elem_type := sym . chan_info ( ) . elem_type
elemtypstr := g . typ ( elem_type )
noscan := g . check_noscan ( elem_type )
return ' s y n c _ _ n e w _ c h a n n e l _ s t $ { noscan } ( 0 , s i z e o f ( $ elemtypstr ) ) '
2021-04-26 10:54:32 +02:00
}
. array {
elem_typ := sym . array_info ( ) . elem_type
elem_sym := g . typ ( elem_typ )
mut elem_type_str := util . no_dots ( elem_sym )
if elem_type_str . starts_with ( ' C _ _ ' ) {
elem_type_str = elem_type_str [ 3 .. ]
}
noscan := g . check_noscan ( elem_typ )
2021-05-26 06:51:20 +02:00
init_str := ' _ _ n e w _ a r r a y $ { noscan } ( 0 , 0 , s i z e o f ( $ elem_type_str ) ) '
2021-04-26 10:54:32 +02:00
if typ . has_flag ( . shared_f ) {
2021-12-19 17:25:18 +01:00
atyp := ' _ _ s h a r e d _ _ A r r a y _ $ { g . table . sym ( elem_typ ) . cname } '
2021-04-26 10:54:32 +02:00
return ' ( $ atyp * ) _ _ d u p _ s h a r e d _ a r r a y ( & ( $ atyp ) { . m t x = { 0 } , . v a l = $ init_str } , s i z e o f ( $ atyp ) ) '
} else {
return init_str
2020-12-19 06:55:13 +01:00
}
}
2021-04-26 10:54:32 +02:00
. map {
info := sym . map_info ( )
2021-12-19 17:25:18 +01:00
key_typ := g . table . sym ( info . key_type )
2021-04-26 10:54:32 +02:00
hash_fn , key_eq_fn , clone_fn , free_fn := g . map_fn_ptrs ( key_typ )
2021-06-13 05:26:43 +02:00
noscan_key := g . check_noscan ( info . key_type )
noscan_value := g . check_noscan ( info . value_type )
mut noscan := if noscan_key . len != 0 || noscan_value . len != 0 { ' _ n o s c a n ' } else { ' ' }
if noscan . len != 0 {
if noscan_key . len != 0 {
noscan += ' _ k e y '
}
if noscan_value . len != 0 {
noscan += ' _ v a l u e '
}
}
init_str := ' n e w _ m a p $ { noscan } ( s i z e o f ( $ { g . typ ( info . key_type ) } ) , s i z e o f ( $ { g . typ ( info . value_type ) } ) , $ hash_fn , $ key_eq_fn , $ clone_fn , $ free_fn ) '
2021-04-26 10:54:32 +02:00
if typ . has_flag ( . shared_f ) {
2021-12-19 17:25:18 +01:00
mtyp := ' _ _ s h a r e d _ _ M a p _ $ { key_typ . cname } _ $ { g . table . sym ( info . value_type ) . cname } '
2021-04-26 10:54:32 +02:00
return ' ( $ mtyp * ) _ _ d u p _ s h a r e d _ m a p ( & ( $ mtyp ) { . m t x = { 0 } , . v a l = $ init_str } , s i z e o f ( $ mtyp ) ) '
} else {
return init_str
}
2020-12-19 06:55:13 +01:00
}
2021-04-26 10:54:32 +02:00
. struct_ {
mut has_none_zero := false
mut init_str := ' { '
info := sym . info as ast . Struct
2021-04-27 07:18:48 +02:00
typ_is_shared_f := typ . has_flag ( . shared_f )
if sym . language == . v && ! typ_is_shared_f {
for field in info . fields {
2021-12-19 17:25:18 +01:00
field_sym := g . table . sym ( field . typ )
2021-04-27 07:18:48 +02:00
if field . has_default_expr
2021-09-21 01:12:38 +02:00
|| field_sym . kind in [ . array , . map , . string , . bool , . alias , . i8 , . i16 , . int , . i64 , . byte , . u16 , . u32 , . u64 , . char , . voidptr , . byteptr , . charptr , . struct_ ] {
2021-04-27 07:18:48 +02:00
field_name := c_name ( field . name )
if field . has_default_expr {
2021-12-14 13:55:58 +01:00
mut expr_str := ' '
2021-12-19 17:25:18 +01:00
if g . table . sym ( field . typ ) . kind in [ . sum_type , . interface_ ] {
2021-12-14 13:55:58 +01:00
expr_str = g . expr_string_with_cast ( field . default_expr ,
field . default_expr_typ , field . typ )
} else {
expr_str = g . expr_string ( field . default_expr )
}
2021-04-27 07:18:48 +02:00
init_str += ' . $ field_name = $ expr_str , '
} else {
2021-12-15 07:49:30 +01:00
mut zero_str := g . type_default ( field . typ )
if zero_str == ' { 0 } ' {
if field_sym . info is ast . Struct && field_sym . language == . v {
if field_sym . info . fields . len == 0
&& field_sym . info . embeds . len == 0 {
zero_str = ' { E M P T Y _ S T R U C T _ I N I T I A L I Z A T I O N } '
}
}
}
2021-12-14 13:55:58 +01:00
init_str += ' . $ field_name = $ zero_str , '
2021-04-27 07:18:48 +02:00
}
has_none_zero = true
2021-04-26 10:54:32 +02:00
}
}
}
if has_none_zero {
init_str += ' } '
type_name := g . typ ( typ )
init_str = ' ( $ type_name ) ' + init_str
} else {
init_str += ' 0 } '
}
if typ . has_flag ( . shared_f ) {
2021-12-19 17:25:18 +01:00
styp := ' _ _ s h a r e d _ _ $ { g . table . sym ( typ ) . cname } '
2021-04-26 10:54:32 +02:00
return ' ( $ styp * ) _ _ d u p $ { styp } ( & ( $ styp ) { . m t x = { 0 } , . v a l = $ init_str } , s i z e o f ( $ styp ) ) '
} else {
return init_str
}
2021-04-11 09:30:23 +02:00
}
2021-04-26 10:54:32 +02:00
else {
2020-04-05 19:35:10 +02:00
return ' 0 '
}
2020-07-14 00:16:31 +02:00
}
2020-03-23 08:59:34 +01:00
}
2020-03-24 22:23:59 +01:00
2020-08-11 23:08:19 +02:00
fn ( g & Gen ) get_all_test_function_names ( ) [ ] string {
2020-04-26 09:17:13 +02:00
mut tfuncs := [ ] string { }
2020-04-21 05:11:50 +02:00
mut tsuite_begin := ' '
mut tsuite_end := ' '
2020-03-24 22:23:59 +01:00
for _ , f in g . table . fns {
2021-01-30 09:20:09 +01:00
if f . name . ends_with ( ' . t e s t s u i t e _ b e g i n ' ) {
2020-03-26 19:17:14 +01:00
tsuite_begin = f . name
2020-03-31 23:42:30 +02:00
continue
2020-03-26 19:17:14 +01:00
}
2020-03-31 23:42:30 +02:00
if f . name . contains ( ' . t e s t _ ' ) {
tfuncs << f . name
continue
}
if f . name . ends_with ( ' . t e s t s u i t e _ e n d ' ) {
tsuite_end = f . name
2020-03-24 22:23:59 +01:00
continue
}
2020-03-26 19:17:14 +01:00
}
2020-04-26 09:17:13 +02:00
mut all_tfuncs := [ ] string { }
2020-03-26 19:17:14 +01:00
if tsuite_begin . len > 0 {
2020-03-30 17:21:32 +02:00
all_tfuncs << tsuite_begin
2020-03-26 19:17:14 +01:00
}
2020-03-30 17:21:32 +02:00
all_tfuncs << tfuncs
2020-03-26 19:17:14 +01:00
if tsuite_end . len > 0 {
2020-03-30 17:21:32 +02:00
all_tfuncs << tsuite_end
2020-03-26 19:17:14 +01:00
}
2021-03-05 12:19:39 +01:00
return all_tfuncs
2020-03-24 22:23:59 +01:00
}
2020-03-26 09:20:55 +01:00
2021-12-28 05:37:03 +01:00
fn ( mut g Gen ) size_of ( node ast . SizeOf ) {
typ := if node . typ == g . field_data_type { g . comptime_for_field_value . typ } else { node . typ }
node_typ := g . unwrap_generic ( typ )
sym := g . table . sym ( node_typ )
if sym . language == . v && sym . kind in [ . placeholder , . any ] {
g . error ( ' u n k n o w n t y p e ` $ sym . name ` ' , node . pos )
}
styp := g . typ ( node_typ )
g . write ( ' s i z e o f ( $ { util . no_dots ( styp ) } ) ' )
}
2020-08-11 23:08:19 +02:00
fn ( g & Gen ) is_importing_os ( ) bool {
2020-03-26 09:20:55 +01:00
return ' o s ' in g . table . imports
}
2020-03-27 14:44:30 +01:00
2021-01-15 13:45:26 +01:00
fn ( mut g Gen ) go_expr ( node ast . GoExpr ) {
line := g . go_before_stmt ( 0 )
mut handle := ' '
2020-04-03 15:18:17 +02:00
tmp := g . new_tmp_var ( )
2021-02-25 12:16:35 +01:00
mut expr := node . call_expr
2020-07-01 00:53:53 +02:00
mut name := expr . name // util.no_dots(expr.name)
2021-01-13 16:35:50 +01:00
// TODO: fn call is duplicated. merge with fn_call().
2021-04-24 08:44:15 +02:00
for i , concrete_type in expr . concrete_types {
if concrete_type != ast . void_type && concrete_type != 0 {
2021-01-22 13:49:56 +01:00
// Using _T_ to differentiate between get<string> and get_string
// `foo<int>()` => `foo_T_int()`
if i == 0 {
name += ' _ T '
}
2021-04-24 08:44:15 +02:00
name += ' _ ' + g . typ ( concrete_type )
2021-01-22 13:49:56 +01:00
}
2021-01-13 16:35:50 +01:00
}
2020-06-19 11:46:08 +02:00
if expr . is_method {
2021-12-19 17:25:18 +01:00
receiver_sym := g . table . sym ( expr . receiver_type )
2020-06-19 11:46:08 +02:00
name = receiver_sym . name + ' _ ' + name
2021-02-25 12:16:35 +01:00
} else if mut expr . left is ast . AnonFn {
g . gen_anon_fn_decl ( mut expr . left )
2021-08-10 20:27:15 +02:00
name = expr . left . decl . name
2020-06-19 11:46:08 +02:00
}
2020-07-01 00:53:53 +02:00
name = util . no_dots ( name )
2021-02-02 09:14:34 +01:00
if g . pref . obfuscate && g . cur_mod . name == ' m a i n ' && name . starts_with ( ' m a i n _ _ ' ) {
mut key := expr . name
if expr . is_method {
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( expr . receiver_type )
2021-02-02 09:14:34 +01:00
key = sym . name + ' . ' + expr . name
}
g . write ( ' / * o b f g o : $ key * / ' )
name = g . obf_table [ key ] or {
panic ( ' c g e n : o b f n a m e " $ key " n o t f o u n d , t h i s s h o u l d n e v e r h a p p e n ' )
}
}
2020-06-19 11:46:08 +02:00
g . writeln ( ' / / g o ' )
wrapper_struct_name := ' t h r e a d _ a r g _ ' + name
wrapper_fn_name := name + ' _ t h r e a d _ w r a p p e r '
arg_tmp_var := ' a r g _ ' + tmp
g . writeln ( ' $ wrapper_struct_name * $ arg_tmp_var = m a l l o c ( s i z e o f ( t h r e a d _ a r g _ $ name ) ) ; ' )
if expr . is_method {
g . write ( ' $ arg_tmp_var - > a r g 0 = ' )
2020-06-26 20:38:17 +02:00
// TODO is this needed?
/ *
if false && ! expr . return_type . is_ptr ( ) {
g . write ( ' & ' )
}
* /
2020-06-19 11:46:08 +02:00
g . expr ( expr . left )
g . writeln ( ' ; ' )
}
for i , arg in expr . args {
2020-10-15 22:12:59 +02:00
g . write ( ' $ arg_tmp_var - > a r g $ { i + 1 } = ' )
2020-06-19 11:46:08 +02:00
g . expr ( arg . expr )
g . writeln ( ' ; ' )
}
2021-01-15 13:45:26 +01:00
s_ret_typ := g . typ ( node . call_expr . return_type )
2021-04-02 00:57:09 +02:00
if g . pref . os == . windows && node . call_expr . return_type != ast . void_type {
2021-01-15 13:45:26 +01:00
g . writeln ( ' $ arg_tmp_var - > r e t _ p t r = m a l l o c ( s i z e o f ( $ s_ret_typ ) ) ; ' )
}
2021-02-27 09:16:55 +01:00
is_opt := node . call_expr . return_type . has_flag ( . optional )
2021-03-14 08:37:38 +01:00
mut gohandle_name := ' '
2021-04-02 00:57:09 +02:00
if node . call_expr . return_type == ast . void_type {
2021-03-14 08:37:38 +01:00
gohandle_name = if is_opt { ' _ _ v _ t h r e a d _ O p t i o n _ v o i d ' } else { ' _ _ v _ t h r e a d ' }
2021-02-23 08:37:29 +01:00
} else {
2021-02-27 09:16:55 +01:00
opt := if is_opt { ' O p t i o n _ ' } else { ' ' }
2021-12-19 17:25:18 +01:00
gohandle_name = ' _ _ v _ t h r e a d _ $ opt $ { g . table . sym ( g . unwrap_generic ( node . call_expr . return_type ) ) . cname } '
2021-02-23 08:37:29 +01:00
}
2020-06-19 11:46:08 +02:00
if g . pref . os == . windows {
2021-04-11 23:56:25 +02:00
simple_handle := if node . is_expr && node . call_expr . return_type != ast . void_type {
2021-01-15 13:45:26 +01:00
' t h r e a d _ h a n d l e _ $ tmp '
} else {
' t h r e a d _ $ tmp '
}
g . writeln ( ' H A N D L E $ simple_handle = C r e a t e T h r e a d ( 0 , 0 , ( L P T H R E A D _ S T A R T _ R O U T I N E ) $ wrapper_fn_name , $ arg_tmp_var , 0 , 0 ) ; ' )
2021-07-25 00:13:34 +02:00
g . writeln ( ' i f ( ! $ simple_handle ) p a n i c _ l a s t e r r ( t o s 3 ( " ` g o $ { name } ( ) ` : " ) ) ; ' )
2021-04-11 23:56:25 +02:00
if node . is_expr && node . call_expr . return_type != ast . void_type {
2021-01-15 13:45:26 +01:00
g . writeln ( ' $ gohandle_name t h r e a d _ $ tmp = { ' )
g . writeln ( ' \t . r e t _ p t r = $ arg_tmp_var - > r e t _ p t r , ' )
g . writeln ( ' \t . h a n d l e = t h r e a d _ h a n d l e _ $ tmp ' )
g . writeln ( ' } ; ' )
}
2021-04-11 23:56:25 +02:00
if ! node . is_expr {
2021-01-15 13:45:26 +01:00
g . writeln ( ' C l o s e H a n d l e ( t h r e a d _ $ tmp ) ; ' )
}
2020-06-19 11:46:08 +02:00
} else {
g . writeln ( ' p t h r e a d _ t t h r e a d _ $ tmp ; ' )
2021-07-25 00:13:34 +02:00
g . writeln ( ' i n t $ { tmp } _ t h r _ r e s = p t h r e a d _ c r e a t e ( & t h r e a d _ $ tmp , N U L L , ( v o i d * ) $ wrapper_fn_name , $ arg_tmp_var ) ; ' )
g . writeln ( ' i f ( $ { tmp } _ t h r _ r e s ) p a n i c _ e r r o r _ n u m b e r ( t o s 3 ( " ` g o $ { name } ( ) ` : " ) , $ { tmp } _ t h r _ r e s ) ; ' )
2021-04-11 23:56:25 +02:00
if ! node . is_expr {
2021-01-15 13:45:26 +01:00
g . writeln ( ' p t h r e a d _ d e t a c h ( t h r e a d _ $ tmp ) ; ' )
}
2020-06-19 11:46:08 +02:00
}
g . writeln ( ' / / e n d g o \n ' )
2021-04-11 23:56:25 +02:00
if node . is_expr {
2021-01-15 13:45:26 +01:00
handle = ' t h r e a d _ $ tmp '
// create wait handler for this return type if none exists
waiter_fn_name := gohandle_name + ' _ w a i t '
2021-12-19 12:38:48 +01:00
mut should_register := false
lock g . waiter_fns {
if waiter_fn_name ! in g . waiter_fns {
g . waiter_fns << waiter_fn_name
should_register = true
}
}
if should_register {
2021-01-15 13:45:26 +01:00
g . gowrappers . writeln ( ' \n $ s_ret_typ $ { waiter_fn_name } ( $ gohandle_name t h r e a d ) { ' )
mut c_ret_ptr_ptr := ' N U L L '
2021-04-02 00:57:09 +02:00
if node . call_expr . return_type != ast . void_type {
2021-01-15 13:45:26 +01:00
g . gowrappers . writeln ( ' \t $ s_ret_typ * r e t _ p t r ; ' )
c_ret_ptr_ptr = ' & r e t _ p t r '
}
if g . pref . os == . windows {
2021-04-02 00:57:09 +02:00
if node . call_expr . return_type == ast . void_type {
2021-01-15 13:45:26 +01:00
g . gowrappers . writeln ( ' \t u 3 2 s t a t = W a i t F o r S i n g l e O b j e c t ( t h r e a d , I N F I N I T E ) ; ' )
} else {
g . gowrappers . writeln ( ' \t u 3 2 s t a t = W a i t F o r S i n g l e O b j e c t ( t h r e a d . h a n d l e , I N F I N I T E ) ; ' )
g . gowrappers . writeln ( ' \t r e t _ p t r = t h r e a d . r e t _ p t r ; ' )
}
} else {
2021-02-09 13:22:51 +01:00
g . gowrappers . writeln ( ' \t i n t s t a t = p t h r e a d _ j o i n ( t h r e a d , ( v o i d * * ) $ c_ret_ptr_ptr ) ; ' )
2021-01-15 13:45:26 +01:00
}
2021-07-21 10:46:04 +02:00
g . gowrappers . writeln ( ' \t i f ( s t a t ! = 0 ) { _ v _ p a n i c ( _ S L I T ( " u n a b l e t o j o i n t h r e a d " ) ) ; } ' )
2021-01-15 13:45:26 +01:00
if g . pref . os == . windows {
2021-04-02 00:57:09 +02:00
if node . call_expr . return_type == ast . void_type {
2021-01-15 13:45:26 +01:00
g . gowrappers . writeln ( ' \t C l o s e H a n d l e ( t h r e a d ) ; ' )
} else {
g . gowrappers . writeln ( ' \t C l o s e H a n d l e ( t h r e a d . h a n d l e ) ; ' )
}
}
2021-04-02 00:57:09 +02:00
if node . call_expr . return_type != ast . void_type {
2021-01-15 13:45:26 +01:00
g . gowrappers . writeln ( ' \t $ s_ret_typ r e t = * r e t _ p t r ; ' )
g . gowrappers . writeln ( ' \t f r e e ( r e t _ p t r ) ; ' )
g . gowrappers . writeln ( ' \t r e t u r n r e t ; ' )
}
g . gowrappers . writeln ( ' } ' )
}
}
2020-06-19 11:46:08 +02:00
// Register the wrapper type and function
2021-10-06 21:12:14 +02:00
mut should_register := false
lock g . threaded_fns {
if name ! in g . threaded_fns {
g . threaded_fns << name
should_register = true
}
}
if should_register {
2021-04-11 23:56:25 +02:00
g . type_definitions . writeln ( ' \n t y p e d e f s t r u c t $ wrapper_struct_name { ' )
if expr . is_method {
styp := g . typ ( expr . receiver_type )
g . type_definitions . writeln ( ' \t $ styp a r g 0 ; ' )
2020-06-19 11:46:08 +02:00
}
2021-04-11 23:56:25 +02:00
need_return_ptr := g . pref . os == . windows && node . call_expr . return_type != ast . void_type
if expr . args . len == 0 && ! need_return_ptr {
g . type_definitions . writeln ( ' E M P T Y _ S T R U C T _ D E C L A R A T I O N ; ' )
2021-01-15 13:45:26 +01:00
} else {
2021-04-11 23:56:25 +02:00
for i , arg in expr . args {
styp := g . typ ( arg . typ )
g . type_definitions . writeln ( ' \t $ styp a r g $ { i + 1 } ; ' )
}
2021-01-15 13:45:26 +01:00
}
2021-04-11 23:56:25 +02:00
if need_return_ptr {
g . type_definitions . writeln ( ' \t v o i d * r e t _ p t r ; ' )
2020-06-19 11:46:08 +02:00
}
2021-04-11 23:56:25 +02:00
g . type_definitions . writeln ( ' } $ wrapper_struct_name ; ' )
thread_ret_type := if g . pref . os == . windows { ' u 3 2 ' } else { ' v o i d * ' }
g . type_definitions . writeln ( ' $ thread_ret_type $ { wrapper_fn_name } ( $ wrapper_struct_name * a r g ) ; ' )
g . gowrappers . writeln ( ' $ thread_ret_type $ { wrapper_fn_name } ( $ wrapper_struct_name * a r g ) { ' )
if node . call_expr . return_type != ast . void_type {
if g . pref . os == . windows {
g . gowrappers . write_string ( ' \t * ( ( $ s_ret_typ * ) ( a r g - > r e t _ p t r ) ) = ' )
} else {
g . gowrappers . writeln ( ' \t $ s_ret_typ * r e t _ p t r = m a l l o c ( s i z e o f ( $ s_ret_typ ) ) ; ' )
g . gowrappers . write_string ( ' \t * r e t _ p t r = ' )
}
} else {
g . gowrappers . write_string ( ' \t ' )
}
if expr . is_method {
2021-06-29 10:16:15 +02:00
unwrapped_rec_type := g . unwrap_generic ( expr . receiver_type )
2021-12-19 17:25:18 +01:00
typ_sym := g . table . sym ( unwrapped_rec_type )
2021-06-29 10:16:15 +02:00
if typ_sym . kind == . interface_
&& ( typ_sym . info as ast . Interface ) . defines_method ( expr . name ) {
rec_cc_type := g . cc_type ( unwrapped_rec_type , false )
receiver_type_name := util . no_dots ( rec_cc_type )
g . gowrappers . write_string ( ' $ { c_name ( receiver_type_name ) } _ n a m e _ t a b l e [ ' )
g . gowrappers . write_string ( ' a r g - > a r g 0 ' )
dot := if expr . left_type . is_ptr ( ) { ' - > ' } else { ' . ' }
mname := c_name ( expr . name )
g . gowrappers . write_string ( ' $ { dot } _ t y p ] . _ m e t h o d _ $ { mname } ( ' )
g . gowrappers . write_string ( ' a r g - > a r g 0 ' )
g . gowrappers . write_string ( ' $ { dot } _ o b j e c t ' )
} else {
g . gowrappers . write_string ( ' $ { name } ( ' )
g . gowrappers . write_string ( ' a r g - > a r g 0 ' )
}
2021-04-11 23:56:25 +02:00
if expr . args . len > 0 {
g . gowrappers . write_string ( ' , ' )
}
2021-06-29 10:16:15 +02:00
} else {
g . gowrappers . write_string ( ' $ { name } ( ' )
2020-04-03 15:18:17 +02:00
}
2021-06-28 15:31:11 +02:00
if expr . args . len > 0 {
mut has_cast := false
for i in 0 .. expr . args . len {
2021-12-19 17:25:18 +01:00
if g . table . sym ( expr . expected_arg_types [ i ] ) . kind == . interface_
&& g . table . sym ( expr . args [ i ] . typ ) . kind != . interface_ {
2021-06-28 15:31:11 +02:00
has_cast = true
break
}
}
if has_cast {
pos := g . out . len
g . call_args ( expr )
mut call_args_str := g . out . after ( pos )
g . out . go_back ( call_args_str . len )
mut rep_group := [ ] string { cap : 2 * expr . args . len }
for i in 0 .. expr . args . len {
rep_group << g . expr_string ( expr . args [ i ] . expr )
rep_group << ' a r g - > a r g $ { i + 1 } '
}
call_args_str = call_args_str . replace_each ( rep_group )
g . gowrappers . write_string ( call_args_str )
} else {
for i in 0 .. expr . args . len {
g . gowrappers . write_string ( ' a r g - > a r g $ { i + 1 } ' )
if i != expr . args . len - 1 {
g . gowrappers . write_string ( ' , ' )
}
}
2021-04-11 23:56:25 +02:00
}
}
g . gowrappers . writeln ( ' ) ; ' )
g . gowrappers . writeln ( ' \t f r e e ( a r g ) ; ' )
if g . pref . os != . windows && node . call_expr . return_type != ast . void_type {
g . gowrappers . writeln ( ' \t r e t u r n r e t _ p t r ; ' )
} else {
g . gowrappers . writeln ( ' \t r e t u r n 0 ; ' )
}
g . gowrappers . writeln ( ' } ' )
2020-04-03 15:18:17 +02:00
}
2021-04-11 23:56:25 +02:00
if node . is_expr {
g . empty_line = false
g . write ( line )
g . write ( handle )
2021-01-15 13:45:26 +01:00
}
2020-04-03 15:18:17 +02:00
}
2020-04-25 08:36:53 +02:00
fn ( mut g Gen ) as_cast ( node ast . AsCast ) {
// Make sure the sum type can be cast to this type (the types
// are the same), otherwise panic.
// g.insert_before('
styp := g . typ ( node . typ )
2021-12-19 17:25:18 +01:00
sym := g . table . sym ( node . typ )
mut expr_type_sym := g . table . sym ( node . expr_type )
2021-08-17 20:00:27 +02:00
if mut expr_type_sym . info is ast . SumType {
2020-11-11 09:18:15 +01:00
dot := if node . expr_type . is_ptr ( ) { ' - > ' } else { ' . ' }
2021-01-30 17:54:05 +01:00
g . write ( ' / * a s * / * ( $ styp * ) _ _ a s _ c a s t ( ' )
g . write ( ' ( ' )
2020-11-11 09:18:15 +01:00
g . expr ( node . expr )
g . write ( ' ) ' )
g . write ( dot )
2021-01-30 17:54:05 +01:00
g . write ( ' _ $ sym . cname , ' )
g . write ( ' ( ' )
2020-11-11 09:18:15 +01:00
g . expr ( node . expr )
g . write ( ' ) ' )
g . write ( dot )
2020-11-29 14:10:45 +01:00
// g.write('typ, /*expected:*/$node.typ)')
sidx := g . type_sidx ( node . typ )
2021-12-19 17:25:18 +01:00
expected_sym := g . table . sym ( node . typ )
2021-03-09 18:16:18 +01:00
g . write ( ' _ t y p , $ sidx ) / * e x p e c t e d i d x : $ sidx , n a m e : $ expected_sym . name * / ' )
2021-01-30 17:54:05 +01:00
// fill as cast name table
for variant in expr_type_sym . info . variants {
2021-07-17 13:44:38 +02:00
idx := u32 ( variant ) . str ( )
2021-01-30 17:54:05 +01:00
if idx in g . as_cast_type_names {
continue
}
2021-12-19 17:25:18 +01:00
variant_sym := g . table . sym ( variant )
2021-01-30 17:54:05 +01:00
g . as_cast_type_names [ idx ] = variant_sym . name
}
2021-08-17 20:00:27 +02:00
} else if expr_type_sym . kind == . interface_ && sym . kind == . interface_ {
g . write ( ' I _ $ { expr_type_sym . cname } _ a s _ I _ $ { sym . cname } ( ' )
2021-08-18 12:47:57 +02:00
if node . expr_type . is_ptr ( ) {
g . write ( ' * ' )
}
2021-08-17 20:00:27 +02:00
g . expr ( node . expr )
g . write ( ' ) ' )
mut info := expr_type_sym . info as ast . Interface
if node . typ ! in info . conversions {
left_variants := g . table . iface_types [ expr_type_sym . name ]
right_variants := g . table . iface_types [ sym . name ]
info . conversions [ node . typ ] = left_variants . filter ( it in right_variants )
}
expr_type_sym . info = info
2021-06-16 18:16:15 +02:00
} else {
g . expr ( node . expr )
2020-04-25 08:36:53 +02:00
}
}
2021-01-30 17:54:05 +01:00
fn ( g Gen ) as_cast_name_table ( ) string {
if g . as_cast_type_names . len == 0 {
2021-05-24 04:20:45 +02:00
return ' n e w _ a r r a y _ f r o m _ c _ a r r a y ( 1 , 1 , s i z e o f ( V C a s t T y p e I n d e x N a m e ) , _ M O V ( ( V C a s t T y p e I n d e x N a m e [ 1 ] ) { ( V C a s t T y p e I n d e x N a m e ) { . t i n d e x = 0 , . t n a m e = _ S L I T ( " u n k n o w n " ) } } ) ) ; \n '
2021-01-30 17:54:05 +01:00
}
2021-04-02 00:57:09 +02:00
mut name_ast := strings . new_builder ( 1024 )
2021-01-30 17:54:05 +01:00
casts_len := g . as_cast_type_names . len + 1
2021-04-02 00:57:09 +02:00
name_ast . writeln ( ' n e w _ a r r a y _ f r o m _ c _ a r r a y ( $ casts_len , $ casts_len , s i z e o f ( V C a s t T y p e I n d e x N a m e ) , _ M O V ( ( V C a s t T y p e I n d e x N a m e [ $ casts_len ] ) { ' )
name_ast . writeln ( ' \t \t ( V C a s t T y p e I n d e x N a m e ) { . t i n d e x = 0 , . t n a m e = _ S L I T ( " u n k n o w n " ) } ' )
2021-01-30 17:54:05 +01:00
for key , value in g . as_cast_type_names {
2021-04-02 00:57:09 +02:00
name_ast . writeln ( ' \t \t , ( V C a s t T y p e I n d e x N a m e ) { . t i n d e x = $ key , . t n a m e = _ S L I T ( " $ value " ) } ' )
2021-01-30 17:54:05 +01:00
}
2021-05-24 04:20:45 +02:00
name_ast . writeln ( ' \t } ) ) ; \n ' )
2021-04-02 00:57:09 +02:00
return name_ast . str ( )
2021-01-30 17:54:05 +01:00
}
2021-12-26 19:01:00 +01:00
fn ( g Gen ) has_been_referenced ( fn_name string ) bool {
mut referenced := false
lock g . referenced_fns {
referenced = g . referenced_fns [ fn_name ]
}
return referenced
}
2020-04-22 20:20:49 +02:00
// Generates interface table and interface indexes
2020-07-23 23:16:36 +02:00
fn ( mut g Gen ) interface_table ( ) string {
2020-04-22 20:20:49 +02:00
mut sb := strings . new_builder ( 100 )
2021-08-17 20:00:27 +02:00
mut conversion_functions := strings . new_builder ( 100 )
2021-12-01 08:50:53 +01:00
for isym in g . table . type_symbols {
if isym . kind != . interface_ {
2020-05-04 00:14:59 +02:00
continue
}
2021-12-15 11:47:28 +01:00
if isym . info ! is ast . Interface {
// Do not remove this check, `isym.info` could be `&IError`.
// dump(isym)
continue
}
2021-12-01 08:50:53 +01:00
inter_info := isym . info as ast . Interface
2021-07-15 07:29:13 +02:00
if inter_info . is_generic {
continue
}
2020-04-22 20:20:49 +02:00
// interface_name is for example Speaker
2021-12-01 08:50:53 +01:00
interface_name := isym . cname
2020-05-04 00:14:59 +02:00
// generate a struct that references interface methods
methods_struct_name := ' s t r u c t _ $ { interface_name } _ i n t e r f a c e _ m e t h o d s '
mut methods_struct_def := strings . new_builder ( 100 )
methods_struct_def . writeln ( ' $ methods_struct_name { ' )
2020-05-21 03:58:50 +02:00
mut methodidx := map [ string ] int { }
2021-01-26 11:56:17 +01:00
for k , method in inter_info . methods {
2020-05-18 21:38:06 +02:00
methodidx [ method . name ] = k
2020-05-04 00:14:59 +02:00
ret_styp := g . typ ( method . return_type )
2021-06-09 21:44:18 +02:00
methods_struct_def . write_string ( ' \t $ ret_styp ( * _ m e t h o d _ $ { c_name ( method . name ) } ) ( v o i d * _ ' )
2020-05-04 00:14:59 +02:00
// the first param is the receiver, it's handled by `void*` above
2020-09-27 03:32:56 +02:00
for i in 1 .. method . params . len {
arg := method . params [ i ]
2021-06-09 21:44:18 +02:00
methods_struct_def . write_string ( ' , $ { g . typ ( arg . typ ) } $ arg . name ' )
2020-05-04 00:14:59 +02:00
}
2021-08-10 20:27:15 +02:00
// TODO g.fn_args(method.args[1..])
2021-06-09 21:44:18 +02:00
methods_struct_def . writeln ( ' ) ; ' )
2020-05-04 00:14:59 +02:00
}
methods_struct_def . writeln ( ' } ; ' )
// generate an array of the interface methods for the structs using the interface
// as well as case functions from the struct to the interface
mut methods_struct := strings . new_builder ( 100 )
2020-12-09 19:18:48 +01:00
//
2020-12-09 19:08:51 +01:00
iname_table_length := inter_info . types . len
2020-12-09 19:18:48 +01:00
if iname_table_length == 0 {
// msvc can not process `static struct x[0] = {};`
2021-01-20 06:04:59 +01:00
methods_struct . writeln ( ' $ methods_struct_name $ { interface_name } _ n a m e _ t a b l e [ 1 ] ; ' )
2020-12-09 19:08:51 +01:00
} else {
2021-01-20 06:04:59 +01:00
if g . pref . build_mode != . build_module {
methods_struct . writeln ( ' $ methods_struct_name $ { interface_name } _ n a m e _ t a b l e [ $ iname_table_length ] = { ' )
} else {
methods_struct . writeln ( ' $ methods_struct_name $ { interface_name } _ n a m e _ t a b l e [ $ iname_table_length ] ; ' )
}
2020-12-09 18:22:10 +01:00
}
2020-05-04 00:14:59 +02:00
mut cast_functions := strings . new_builder ( 100 )
mut methods_wrapper := strings . new_builder ( 100 )
2020-06-18 20:21:08 +02:00
methods_wrapper . writeln ( ' / / M e t h o d s w r a p p e r f o r i n t e r f a c e " $ interface_name " ' )
2020-10-16 14:16:36 +02:00
mut already_generated_mwrappers := map [ string ] int { }
iinidx_minimum_base := 1000 // NB: NOT 0, to avoid map entries set to 0 later, so `if already_generated_mwrappers[name] > 0 {` works.
mut current_iinidx := iinidx_minimum_base
for st in inter_info . types {
2021-12-19 17:25:18 +01:00
st_sym := g . table . sym ( st )
2020-04-22 20:20:49 +02:00
// cctype is the Cleaned Concrete Type name, *without ptr*,
// i.e. cctype is always just Cat, not Cat_ptr:
2021-03-01 21:36:58 +01:00
cctype := g . cc_type ( st , true )
2020-12-09 17:51:37 +01:00
$ if debug _interface_table ? {
2021-12-17 14:32:31 +01:00
eprintln ( ' > > i n t e r f a c e n a m e : $ isym . name | c o n c r e t e t y p e : $ st . debug ( ) | s t s y m n a m e : $ st_sym . name ' )
2020-12-09 17:51:37 +01:00
}
2020-04-22 20:20:49 +02:00
// Speaker_Cat_index = 0
2020-05-04 00:14:59 +02:00
interface_index_name := ' _ $ { interface_name } _ $ { cctype } _ i n d e x '
2020-10-16 14:16:36 +02:00
if already_generated_mwrappers [ interface_index_name ] > 0 {
2020-08-13 18:56:58 +02:00
continue
}
2020-10-16 14:16:36 +02:00
already_generated_mwrappers [ interface_index_name ] = current_iinidx
current_iinidx ++
2021-12-01 08:50:53 +01:00
if isym . name != ' v w e b . D b I n t e r f a c e ' { // TODO remove this
2021-05-11 08:30:01 +02:00
// eprintln('>>> current_iinidx: ${current_iinidx-iinidx_minimum_base} | interface_index_name: $interface_index_name')
2021-06-09 21:44:18 +02:00
sb . writeln ( ' s t a t i c $ interface_name I _ $ { cctype } _ t o _ I n t e r f a c e _ $ { interface_name } ( $ cctype * x ) ; ' )
2021-05-11 08:30:01 +02:00
mut cast_struct := strings . new_builder ( 100 )
cast_struct . writeln ( ' ( $ interface_name ) { ' )
cast_struct . writeln ( ' \t \t . _ $ cctype = x , ' )
cast_struct . writeln ( ' \t \t . _ t y p = $ interface_index_name , ' )
for field in inter_info . fields {
cname := c_name ( field . name )
field_styp := g . typ ( field . typ )
if _ := st_sym . find_field ( field . name ) {
cast_struct . writeln ( ' \t \t . $ cname = ( $ field_styp * ) ( ( c h a r * ) x + _ _ o f f s e t o f _ p t r ( x , $ cctype , $ cname ) ) , ' )
} else {
// the field is embedded in another struct
cast_struct . write_string ( ' \t \t . $ cname = ( $ field_styp * ) ( ( c h a r * ) x ' )
2021-06-16 11:15:16 +02:00
if st == ast . voidptr_type {
cast_struct . write_string ( ' / * . . . . a s t . v o i d p t r _ t y p e * / ' )
} else {
2021-06-16 09:48:50 +02:00
for embed_type in st_sym . struct_info ( ) . embeds {
2021-12-19 17:25:18 +01:00
embed_sym := g . table . sym ( embed_type )
2021-06-16 09:48:50 +02:00
if _ := embed_sym . find_field ( field . name ) {
cast_struct . write_string ( ' + _ _ o f f s e t o f _ p t r ( x , $ cctype , $ embed_sym . embed_name ( ) ) + _ _ o f f s e t o f _ p t r ( x , $ embed_sym . cname , $ cname ) ' )
break
}
2021-05-11 08:30:01 +02:00
}
2021-03-01 21:47:00 +01:00
}
2021-05-11 08:30:01 +02:00
cast_struct . writeln ( ' ) , ' )
2021-03-01 21:47:00 +01:00
}
}
2021-05-11 08:30:01 +02:00
cast_struct . write_string ( ' \t } ' )
cast_struct_str := cast_struct . str ( )
2021-01-23 07:57:17 +01:00
2021-05-11 08:30:01 +02:00
cast_functions . writeln ( '
2021-01-23 07:57:17 +01:00
// Casting functions for converting "$cctype" to interface "$interface_name"
2021-06-09 21:44:18 +02:00
static inline $ interface_name I_ $ { cctype } _to_Interface_ $ { interface_name } ( $ cctype * x ) {
2021-01-23 07:57:17 +01:00
return $ cast_struct_str ;
2020-05-04 00:14:59 +02:00
} ' )
2021-12-26 19:01:00 +01:00
shared_fn_name := ' I _ _ _ s h a r e d _ _ $ { cctype } _ t o _ s h a r e d _ I n t e r f a c e _ _ _ s h a r e d _ _ $ interface_name '
// Avoid undefined types errors by only generating the converters that are referenced:
if g . has_been_referenced ( shared_fn_name ) {
mut cast_shared_struct := strings . new_builder ( 100 )
cast_shared_struct . writeln ( ' ( _ _ s h a r e d _ _ $ interface_name ) { ' )
cast_shared_struct . writeln ( ' \t \t . m t x = { 0 } , ' )
cast_shared_struct . writeln ( ' \t \t . v a l = { ' )
cast_shared_struct . writeln ( ' \t \t \t . _ $ cctype = & x - > v a l , ' )
cast_shared_struct . writeln ( ' \t \t \t . _ t y p = $ interface_index_name , ' )
cast_shared_struct . writeln ( ' \t \t } ' )
cast_shared_struct . write_string ( ' \t } ' )
cast_shared_struct_str := cast_shared_struct . str ( )
cast_functions . writeln ( '
// Casting functions for converting "__shared__$cctype" to interface "__shared__$interface_name"
static inline __shared__ $ interface_name $ { shared_fn_name } ( __shared__ $ cctype * x ) {
return $ cast_shared_struct_str ;
} ' )
}
2021-05-11 08:30:01 +02:00
}
2021-01-23 07:57:17 +01:00
2021-01-20 06:04:59 +01:00
if g . pref . build_mode != . build_module {
methods_struct . writeln ( ' \t { ' )
}
2021-06-16 11:15:16 +02:00
if st == ast . voidptr_type {
for mname , _ in methodidx {
if g . pref . build_mode != . build_module {
methods_struct . writeln ( ' \t \t . _ m e t h o d _ $ { c_name ( mname ) } = ( v o i d * ) 0 , ' )
}
}
}
2021-07-15 07:29:13 +02:00
mut methods := st_sym . methods
2021-12-17 14:32:31 +01:00
method_names := methods . map ( it . name )
2021-07-15 07:29:13 +02:00
match st_sym . info {
ast . Struct , ast . Interface , ast . SumType {
if st_sym . info . parent_type . has_flag ( . generic ) {
2021-12-19 17:25:18 +01:00
parent_sym := g . table . sym ( st_sym . info . parent_type )
2021-07-15 07:29:13 +02:00
for method in parent_sym . methods {
if method . name in methodidx {
methods << st_sym . find_method_with_generic_parent ( method . name ) or {
continue
}
}
}
}
}
else { }
}
2021-12-10 13:56:13 +01:00
if st_sym . info is ast . Struct {
for embed in st_sym . info . embeds {
2021-12-19 17:25:18 +01:00
embed_sym := g . table . sym ( embed )
2021-12-15 12:21:21 +01:00
for embed_method in embed_sym . methods {
if embed_method . name ! in method_names {
methods << embed_method
}
}
2021-12-10 13:56:13 +01:00
}
}
2021-07-15 07:29:13 +02:00
for method in methods {
mut name := method . name
if inter_info . parent_type . has_flag ( . generic ) {
2021-12-19 17:25:18 +01:00
parent_sym := g . table . sym ( inter_info . parent_type )
2021-07-15 07:29:13 +02:00
match mut parent_sym . info {
ast . Struct , ast . Interface , ast . SumType {
name = g . generic_fn_name ( parent_sym . info . concrete_types , method . name ,
false )
}
else { }
}
}
2021-06-09 21:44:18 +02:00
if method . name ! in methodidx {
2020-05-04 21:40:33 +02:00
// a method that is not part of the interface should be just skipped
continue
}
2020-05-04 00:14:59 +02:00
// .speak = Cat_speak
2021-08-20 00:13:48 +02:00
if st_sym . info is ast . Struct {
if st_sym . info . parent_type . has_flag ( . generic ) {
name = g . generic_fn_name ( st_sym . info . concrete_types , method . name ,
false )
}
}
2021-12-21 18:16:05 +01:00
styp := g . cc_type ( method . params [ 0 ] . typ , true )
mut method_call := ' $ { styp } _ $ name '
2020-09-27 03:32:56 +02:00
if ! method . params [ 0 ] . typ . is_ptr ( ) {
2021-05-02 02:00:47 +02:00
// inline void Cat_speak_Interface_Animal_method_wrapper(Cat c) { return Cat_speak(*c); }
iwpostfix := ' _ I n t e r f a c e _ $ { interface_name } _ m e t h o d _ w r a p p e r '
2021-12-21 18:16:05 +01:00
methods_wrapper . write_string ( ' s t a t i c i n l i n e $ { g . typ ( method . return_type ) } $ { cctype } _ $ name $ { iwpostfix } ( ' )
2021-01-31 18:44:55 +01:00
//
params_start_pos := g . out . len
mut params := method . params . clone ( )
2021-02-08 16:03:05 +01:00
// hack to mutate typ
2021-07-20 10:17:08 +02:00
params [ 0 ] = ast . Param {
2021-02-08 16:03:05 +01:00
... params [ 0 ]
2021-12-10 13:56:13 +01:00
typ : st . set_nr_muls ( 1 )
2020-05-04 00:14:59 +02:00
}
2021-08-10 20:27:15 +02:00
fargs , _ , _ := g . fn_args ( params , voidptr ( 0 ) )
2021-12-26 19:01:00 +01:00
mut parameter_name := g . out . cut_last ( g . out . len - params_start_pos )
if st . is_ptr ( ) {
2022-01-05 12:06:06 +01:00
parameter_name = parameter_name . trim_string_left ( ' _ _ s h a r e d _ _ ' )
2021-12-26 19:01:00 +01:00
}
methods_wrapper . write_string ( parameter_name )
2020-05-04 00:14:59 +02:00
methods_wrapper . writeln ( ' ) { ' )
2021-02-22 12:18:11 +01:00
methods_wrapper . write_string ( ' \t ' )
2021-04-02 00:57:09 +02:00
if method . return_type != ast . void_type {
2021-02-22 12:18:11 +01:00
methods_wrapper . write_string ( ' r e t u r n ' )
2020-05-04 00:14:59 +02:00
}
2021-12-10 13:56:13 +01:00
_ , embed_types := g . table . find_method_from_embeds ( st_sym , method . name ) or {
ast . Fn { } , [ ] ast . Type { }
}
2021-12-17 14:32:31 +01:00
if embed_types . len > 0 && method . name ! in method_names {
2021-12-19 17:25:18 +01:00
embed_sym := g . table . sym ( embed_types . last ( ) )
2021-12-10 13:56:13 +01:00
method_name := ' $ { embed_sym . cname } _ $ method . name '
methods_wrapper . write_string ( ' $ { method_name } ( $ { fargs [ 0 ] } ' )
for embed in embed_types {
2021-12-19 17:25:18 +01:00
esym := g . table . sym ( embed )
2021-12-10 13:56:13 +01:00
methods_wrapper . write_string ( ' - > $ esym . embed_name ( ) ' )
}
methods_wrapper . writeln ( ' $ { fargs [ 1 .. ] . join ( ' , ' ) } ) ; ' )
} else {
2021-12-26 19:01:00 +01:00
if parameter_name . starts_with ( ' _ _ s h a r e d _ _ ' ) {
methods_wrapper . writeln ( ' $ { method_call } ( $ { fargs . join ( ' , ' ) } - > v a l ) ; ' )
} else {
methods_wrapper . writeln ( ' $ { method_call } ( * $ { fargs . join ( ' , ' ) } ) ; ' )
}
2021-12-10 13:56:13 +01:00
}
2020-05-04 00:14:59 +02:00
methods_wrapper . writeln ( ' } ' )
2021-05-02 02:00:47 +02:00
// .speak = Cat_speak_Interface_Animal_method_wrapper
method_call += iwpostfix
2020-04-22 20:20:49 +02:00
}
2021-11-30 14:03:44 +01:00
if g . pref . build_mode != . build_module && st != ast . voidptr_type {
2021-02-16 22:36:44 +01:00
methods_struct . writeln ( ' \t \t . _ m e t h o d _ $ { c_name ( method . name ) } = ( v o i d * ) $ method_call , ' )
2021-01-20 06:04:59 +01:00
}
}
if g . pref . build_mode != . build_module {
methods_struct . writeln ( ' \t } , ' )
2020-04-22 20:20:49 +02:00
}
2020-10-16 14:16:36 +02:00
iin_idx := already_generated_mwrappers [ interface_index_name ] - iinidx_minimum_base
2021-01-20 06:04:59 +01:00
if g . pref . build_mode != . build_module {
2021-08-17 20:00:27 +02:00
sb . writeln ( ' c o n s t i n t $ interface_index_name = $ iin_idx ; ' )
} else {
sb . writeln ( ' e x t e r n c o n s t i n t $ interface_index_name ; ' )
}
}
for vtyp , variants in inter_info . conversions {
2021-12-19 17:25:18 +01:00
vsym := g . table . sym ( vtyp )
2021-08-17 20:00:27 +02:00
conversion_functions . write_string ( ' s t a t i c i n l i n e b o o l I _ $ { interface_name } _ i s _ I _ $ { vsym . cname } ( $ interface_name x ) { \n \t r e t u r n ' )
for i , variant in variants {
2021-12-19 17:25:18 +01:00
variant_sym := g . table . sym ( variant )
2021-08-17 20:00:27 +02:00
if i > 0 {
conversion_functions . write_string ( ' | | ' )
}
conversion_functions . write_string ( ' ( x . _ t y p = = _ $ { interface_name } _ $ { variant_sym . cname } _ i n d e x ) ' )
}
conversion_functions . writeln ( ' ; \n } ' )
conversion_functions . writeln ( ' s t a t i c i n l i n e $ vsym . cname I _ $ { interface_name } _ a s _ I _ $ { vsym . cname } ( $ interface_name x ) { ' )
for variant in variants {
2021-12-19 17:25:18 +01:00
variant_sym := g . table . sym ( variant )
2021-08-17 20:00:27 +02:00
conversion_functions . writeln ( ' \t i f ( x . _ t y p = = _ $ { interface_name } _ $ { variant_sym . cname } _ i n d e x ) r e t u r n I _ $ { variant_sym . cname } _ t o _ I n t e r f a c e _ $ { vsym . cname } ( x . _ $ variant_sym . cname ) ; ' )
}
pmessage := ' s t r i n g _ _ p l u s ( s t r i n g _ _ p l u s ( t o s 3 ( " ` a s _ c a s t ` : c a n n o t c o n v e r t " ) , t o s 3 ( v _ t y p e o f _ i n t e r f a c e _ $ { interface_name } ( x . _ t y p ) ) ) , t o s 3 ( " t o $ { util . strip_main_name ( vsym . name ) } " ) ) '
if g . pref . is_debug {
// TODO: actually return a valid position here
conversion_functions . write_string ( ' \t p a n i c _ d e b u g ( 1 , t o s 3 ( " b u i l t i n . v " ) , t o s 3 ( " b u i l t i n " ) , t o s 3 ( " _ _ a s _ c a s t " ) , ' )
conversion_functions . write_string ( pmessage )
conversion_functions . writeln ( ' ) ; ' )
2021-01-20 06:04:59 +01:00
} else {
2021-08-17 20:00:27 +02:00
conversion_functions . write_string ( ' \t _ v _ p a n i c ( ' )
conversion_functions . write_string ( pmessage )
conversion_functions . writeln ( ' ) ; ' )
2021-01-20 06:04:59 +01:00
}
2021-08-17 20:00:27 +02:00
conversion_functions . writeln ( ' \t r e t u r n ( $ vsym . cname ) { 0 } ; ' )
conversion_functions . writeln ( ' } ' )
2020-04-22 20:20:49 +02:00
}
2020-10-16 14:16:36 +02:00
sb . writeln ( ' / / ^ ^ ^ n u m b e r o f t y p e s f o r i n t e r f a c e $ interface_name : $ { current_iinidx - iinidx_minimum_base } ' )
2020-12-09 19:18:48 +01:00
if iname_table_length == 0 {
2020-12-09 19:08:51 +01:00
methods_struct . writeln ( ' ' )
2020-12-09 19:12:47 +01:00
} else {
2021-01-20 06:04:59 +01:00
if g . pref . build_mode != . build_module {
methods_struct . writeln ( ' } ; ' )
}
2020-12-09 19:08:51 +01:00
}
2020-05-04 00:14:59 +02:00
// add line return after interface index declarations
sb . writeln ( ' ' )
2021-01-26 11:56:17 +01:00
if inter_info . methods . len > 0 {
2021-01-23 07:57:17 +01:00
sb . writeln ( methods_wrapper . str ( ) )
sb . writeln ( methods_struct_def . str ( ) )
sb . writeln ( methods_struct . str ( ) )
}
2020-05-04 00:14:59 +02:00
sb . writeln ( cast_functions . str ( ) )
2020-04-22 20:20:49 +02:00
}
2021-08-17 20:00:27 +02:00
sb . writeln ( conversion_functions . str ( ) )
2020-04-22 20:20:49 +02:00
return sb . str ( )
}
2020-04-26 17:28:43 +02:00
2020-05-23 10:43:20 +02:00
fn ( mut g Gen ) panic_debug_info ( pos token . Position ) ( int , string , string , string ) {
paline := pos . line_nr + 1
2020-12-04 11:44:16 +01:00
if isnil ( g . fn_decl ) {
return paline , ' ' , ' m a i n ' , ' C . _ v i n i t '
}
2020-05-23 10:43:20 +02:00
pafile := g . fn_decl . file . replace ( ' \\ ' , ' / ' )
pafn := g . fn_decl . name . after ( ' . ' )
2020-06-11 20:02:27 +02:00
pamod := g . fn_decl . modname ( )
2020-05-23 10:43:20 +02:00
return paline , pafile , pamod , pafn
}
2020-10-19 11:18:22 +02:00
pub fn get_guarded_include_text ( iname string , imessage string ) string {
res := '
| #if defined(__has_include)
|
| #if __has_include($iname)
| # include $iname
| #else
| #error VERROR_MESSAGE $imessage
| #endif
|
| #else
| # include $iname
| #endif
' . s t r i p _ m a r g i n ( )
2020-10-19 11:44:53 +02:00
return res
}
2021-01-15 19:28:18 +01:00
fn ( mut g Gen ) trace ( fbase string , message string ) {
if g . file . path_base == fbase {
println ( ' > g . t r a c e | $ { fbase : - 10 s } | $ message ' )
}
}
2021-04-14 00:40:26 +02:00
2021-06-08 22:23:28 +02:00
pub fn ( mut g Gen ) get_array_depth ( el_typ ast . Type ) int {
typ := g . unwrap_generic ( el_typ )
2021-12-19 17:25:18 +01:00
sym := g . table . final_sym ( typ )
2021-06-08 22:23:28 +02:00
if sym . kind == . array {
info := sym . info as ast . Array
return 1 + g . get_array_depth ( info . elem_type )
} else {
return 0
}
}
2021-04-14 00:40:26 +02:00
// returns true if `t` includes any pointer(s) - during garbage collection heap regions
// that contain no pointers do not have to be scanned
pub fn ( mut g Gen ) contains_ptr ( el_typ ast . Type ) bool {
if el_typ . is_ptr ( ) || el_typ . is_pointer ( ) {
return true
}
typ := g . unwrap_generic ( el_typ )
if typ . is_ptr ( ) {
return true
}
2021-12-19 17:25:18 +01:00
sym := g . table . final_sym ( typ )
2021-04-14 00:40:26 +02:00
if sym . language != . v {
return true
}
match sym . kind {
2021-09-21 01:12:38 +02:00
. i8 , . i16 , . int , . i64 , . byte , . u16 , . u32 , . u64 , . f32 , . f64 , . char , . rune , . bool , . enum_ {
2021-04-14 00:40:26 +02:00
return false
}
. array_fixed {
info := sym . info as ast . ArrayFixed
return g . contains_ptr ( info . elem_type )
}
. struct_ {
info := sym . info as ast . Struct
for embed in info . embeds {
if g . contains_ptr ( embed ) {
return true
}
}
for field in info . fields {
if g . contains_ptr ( field . typ ) {
return true
}
}
return false
}
. aggregate {
info := sym . info as ast . Aggregate
for atyp in info . types {
if g . contains_ptr ( atyp ) {
return true
}
}
return false
}
2021-06-10 20:26:17 +02:00
. multi_return {
info := sym . info as ast . MultiReturn
for mrtyp in info . types {
if g . contains_ptr ( mrtyp ) {
return true
}
}
return false
}
2021-04-14 00:40:26 +02:00
else {
return true
}
}
}
fn ( mut g Gen ) check_noscan ( elem_typ ast . Type ) string {
if g . pref . gc_mode in [ . boehm_full_opt , . boehm_incr_opt ] {
if ! g . contains_ptr ( elem_typ ) {
return ' _ n o s c a n '
}
}
return ' '
}