parent
9d0a5942ac
commit
7178367de0
|
@ -534,6 +534,7 @@ pub mut:
|
||||||
should_be_skipped bool // true for calls to `[if someflag?]` functions, when there is no `-d someflag`
|
should_be_skipped bool // true for calls to `[if someflag?]` functions, when there is no `-d someflag`
|
||||||
concrete_types []Type // concrete types, e.g. <int, string>
|
concrete_types []Type // concrete types, e.g. <int, string>
|
||||||
concrete_list_pos token.Pos
|
concrete_list_pos token.Pos
|
||||||
|
raw_concrete_types []Type
|
||||||
free_receiver bool // true if the receiver expression needs to be freed
|
free_receiver bool // true if the receiver expression needs to be freed
|
||||||
scope &Scope
|
scope &Scope
|
||||||
from_embed_types []Type // holds the type of the embed that the method is called from
|
from_embed_types []Type // holds the type of the embed that the method is called from
|
||||||
|
|
|
@ -135,6 +135,8 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if right is ast.ComptimeSelector {
|
||||||
|
right_type = c.comptime_fields_default_type
|
||||||
}
|
}
|
||||||
if is_decl {
|
if is_decl {
|
||||||
// check generic struct init and return unwrap generic struct type
|
// check generic struct init and return unwrap generic struct type
|
||||||
|
|
|
@ -70,21 +70,22 @@ pub mut:
|
||||||
rlocked_names []string // vars that are currently read-locked
|
rlocked_names []string // vars that are currently read-locked
|
||||||
in_for_count int // if checker is currently in a for loop
|
in_for_count int // if checker is currently in a for loop
|
||||||
// checked_ident string // to avoid infinite checker loops
|
// checked_ident string // to avoid infinite checker loops
|
||||||
should_abort bool // when too many errors/warnings/notices are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time.
|
should_abort bool // when too many errors/warnings/notices are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time.
|
||||||
returns bool
|
returns bool
|
||||||
scope_returns bool
|
scope_returns bool
|
||||||
is_builtin_mod bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this
|
is_builtin_mod bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this
|
||||||
is_generated bool // true for `[generated] module xyz` .v files
|
is_generated bool // true for `[generated] module xyz` .v files
|
||||||
inside_unsafe bool // true inside `unsafe {}` blocks
|
inside_unsafe bool // true inside `unsafe {}` blocks
|
||||||
inside_const bool // true inside `const ( ... )` blocks
|
inside_const bool // true inside `const ( ... )` blocks
|
||||||
inside_anon_fn bool // true inside `fn() { ... }()`
|
inside_anon_fn bool // true inside `fn() { ... }()`
|
||||||
inside_ref_lit bool // true inside `a := &something`
|
inside_ref_lit bool // true inside `a := &something`
|
||||||
inside_defer bool // true inside `defer {}` blocks
|
inside_defer bool // true inside `defer {}` blocks
|
||||||
inside_fn_arg bool // `a`, `b` in `a.f(b)`
|
inside_fn_arg bool // `a`, `b` in `a.f(b)`
|
||||||
inside_ct_attr bool // true inside `[if expr]`
|
inside_ct_attr bool // true inside `[if expr]`
|
||||||
skip_flags bool // should `#flag` and `#include` be skipped
|
inside_comptime_for_field bool
|
||||||
fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
|
skip_flags bool // should `#flag` and `#include` be skipped
|
||||||
ct_cond_stack []ast.Expr
|
fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
|
||||||
|
ct_cond_stack []ast.Expr
|
||||||
mut:
|
mut:
|
||||||
stmt_level int // the nesting level inside each stmts list;
|
stmt_level int // the nesting level inside each stmts list;
|
||||||
// .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1`
|
// .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1`
|
||||||
|
|
|
@ -109,10 +109,19 @@ fn (mut c Checker) comptime_for(node ast.ComptimeFor) {
|
||||||
c.error('unknown type `$sym.name`', node.typ_pos)
|
c.error('unknown type `$sym.name`', node.typ_pos)
|
||||||
}
|
}
|
||||||
if node.kind == .fields {
|
if node.kind == .fields {
|
||||||
c.comptime_fields_type[node.val_var] = node.typ
|
if sym.kind == .struct_ {
|
||||||
c.comptime_fields_default_type = node.typ
|
sym_info := sym.info as ast.Struct
|
||||||
|
c.inside_comptime_for_field = true
|
||||||
|
for field in sym_info.fields {
|
||||||
|
c.comptime_fields_type[node.val_var] = node.typ
|
||||||
|
c.comptime_fields_default_type = field.typ
|
||||||
|
c.stmts(node.stmts)
|
||||||
|
}
|
||||||
|
c.inside_comptime_for_field = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.stmts(node.stmts)
|
||||||
}
|
}
|
||||||
c.stmts(node.stmts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// comptime const eval
|
// comptime const eval
|
||||||
|
|
|
@ -428,6 +428,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
||||||
}
|
}
|
||||||
mut has_generic := false // foo<T>() instead of foo<int>()
|
mut has_generic := false // foo<T>() instead of foo<int>()
|
||||||
mut concrete_types := []ast.Type{}
|
mut concrete_types := []ast.Type{}
|
||||||
|
node.concrete_types = node.raw_concrete_types
|
||||||
for concrete_type in node.concrete_types {
|
for concrete_type in node.concrete_types {
|
||||||
if concrete_type.has_flag(.generic) {
|
if concrete_type.has_flag(.generic) {
|
||||||
has_generic = true
|
has_generic = true
|
||||||
|
@ -777,6 +778,13 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
||||||
|
|
||||||
typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr))
|
typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr))
|
||||||
node.args[i].typ = typ
|
node.args[i].typ = typ
|
||||||
|
if c.inside_comptime_for_field {
|
||||||
|
if mut call_arg.expr is ast.Ident {
|
||||||
|
if mut call_arg.expr.obj is ast.Var {
|
||||||
|
node.args[i].typ = call_arg.expr.obj.typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
typ_sym := c.table.sym(typ)
|
typ_sym := c.table.sym(typ)
|
||||||
param_typ_sym := c.table.sym(param.typ)
|
param_typ_sym := c.table.sym(param.typ)
|
||||||
if func.is_variadic && typ.has_flag(.variadic) && node.args.len - 1 > i {
|
if func.is_variadic && typ.has_flag(.variadic) && node.args.len - 1 > i {
|
||||||
|
@ -936,6 +944,9 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.check_expected_call_arg(utyp, unwrap_typ, node.language, call_arg) or {
|
c.check_expected_call_arg(utyp, unwrap_typ, node.language, call_arg) or {
|
||||||
|
if c.comptime_fields_type.len > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
c.error('$err.msg() in argument ${i + 1} to `$fn_name`', call_arg.pos)
|
c.error('$err.msg() in argument ${i + 1} to `$fn_name`', call_arg.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,20 +18,6 @@ vlib/v/checker/tests/comptime_for.vv:4:12: error: unknown type `T`
|
||||||
| ^
|
| ^
|
||||||
5 | $if f.typ is Huh {}
|
5 | $if f.typ is Huh {}
|
||||||
6 | $if f.typ is T {}
|
6 | $if f.typ is T {}
|
||||||
vlib/v/checker/tests/comptime_for.vv:5:16: error: unknown type `Huh`
|
|
||||||
3 | $for f in Huh.fields {}
|
|
||||||
4 | $for f in T.fields {
|
|
||||||
5 | $if f.typ is Huh {}
|
|
||||||
| ~~~
|
|
||||||
6 | $if f.typ is T {}
|
|
||||||
7 | }
|
|
||||||
vlib/v/checker/tests/comptime_for.vv:6:16: error: unknown type `T`
|
|
||||||
4 | $for f in T.fields {
|
|
||||||
5 | $if f.typ is Huh {}
|
|
||||||
6 | $if f.typ is T {}
|
|
||||||
| ^
|
|
||||||
7 | }
|
|
||||||
8 | _ = m
|
|
||||||
vlib/v/checker/tests/comptime_for.vv:8:6: error: undefined ident: `m`
|
vlib/v/checker/tests/comptime_for.vv:8:6: error: undefined ident: `m`
|
||||||
6 | $if f.typ is T {}
|
6 | $if f.typ is T {}
|
||||||
7 | }
|
7 | }
|
||||||
|
|
|
@ -20,4 +20,6 @@ struct S1 {
|
||||||
i int
|
i int
|
||||||
}
|
}
|
||||||
|
|
||||||
gf<S1>()
|
fn main() {
|
||||||
|
gf<S1>()
|
||||||
|
}
|
||||||
|
|
|
@ -46,122 +46,123 @@ struct Gen {
|
||||||
timers_should_print bool
|
timers_should_print bool
|
||||||
table &ast.Table
|
table &ast.Table
|
||||||
mut:
|
mut:
|
||||||
out strings.Builder
|
out strings.Builder
|
||||||
cheaders strings.Builder
|
cheaders strings.Builder
|
||||||
includes strings.Builder // all C #includes required by V modules
|
includes strings.Builder // all C #includes required by V modules
|
||||||
typedefs strings.Builder
|
typedefs strings.Builder
|
||||||
enum_typedefs strings.Builder // enum types
|
enum_typedefs strings.Builder // enum types
|
||||||
definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
|
definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
|
||||||
type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
|
type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
|
||||||
alias_definitions strings.Builder // alias fixed array of non-builtin
|
alias_definitions strings.Builder // alias fixed array of non-builtin
|
||||||
hotcode_definitions strings.Builder // -live declarations & functions
|
hotcode_definitions strings.Builder // -live declarations & functions
|
||||||
channel_definitions strings.Builder // channel related code
|
channel_definitions strings.Builder // channel related code
|
||||||
comptime_definitions strings.Builder // custom defines, given by -d/-define flags on the CLI
|
comptime_definitions strings.Builder // custom defines, given by -d/-define flags on the CLI
|
||||||
global_inits map[string]strings.Builder // default initializers for globals (goes in _vinit())
|
global_inits map[string]strings.Builder // default initializers for globals (goes in _vinit())
|
||||||
global_init strings.Builder // thread local of the above
|
global_init strings.Builder // thread local of the above
|
||||||
inits map[string]strings.Builder // contents of `void _vinit/2{}`
|
inits map[string]strings.Builder // contents of `void _vinit/2{}`
|
||||||
init strings.Builder
|
init strings.Builder
|
||||||
cleanup strings.Builder
|
cleanup strings.Builder
|
||||||
cleanups map[string]strings.Builder // contents of `void _vcleanup(){}`
|
cleanups map[string]strings.Builder // contents of `void _vcleanup(){}`
|
||||||
gowrappers strings.Builder // all go callsite wrappers
|
gowrappers strings.Builder // all go callsite wrappers
|
||||||
stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined
|
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
|
auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs
|
||||||
dump_funcs strings.Builder // function bodies of all auto generated _str funcs
|
dump_funcs strings.Builder // function bodies of all auto generated _str funcs
|
||||||
pcs_declarations strings.Builder // -prof profile counter declarations for each function
|
pcs_declarations strings.Builder // -prof profile counter declarations for each function
|
||||||
embedded_data strings.Builder // data to embed in the executable/binary
|
embedded_data strings.Builder // data to embed in the executable/binary
|
||||||
shared_types strings.Builder // shared/lock types
|
shared_types strings.Builder // shared/lock types
|
||||||
shared_functions strings.Builder // shared constructors
|
shared_functions strings.Builder // shared constructors
|
||||||
options strings.Builder // `Option_xxxx` types
|
options strings.Builder // `Option_xxxx` types
|
||||||
json_forward_decls strings.Builder // json type forward decls
|
json_forward_decls strings.Builder // json type forward decls
|
||||||
sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
|
sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
|
||||||
file &ast.File
|
file &ast.File
|
||||||
unique_file_path_hash u64 // a hash of file.path, used for making auxilary fn generation unique (like `compare_xyz`)
|
unique_file_path_hash u64 // a hash of file.path, used for making auxilary fn generation unique (like `compare_xyz`)
|
||||||
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
|
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
|
||||||
last_fn_c_name string
|
last_fn_c_name string
|
||||||
tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn.
|
tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn.
|
||||||
tmp_count_af int // a separate tmp var counter for autofree fn calls
|
tmp_count_af int // a separate tmp var counter for autofree fn calls
|
||||||
tmp_count_declarations int // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations
|
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
|
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
|
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_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
|
||||||
is_void_expr_stmt bool // ExprStmt whos result is discarded
|
is_void_expr_stmt bool // ExprStmt whos result is discarded
|
||||||
is_arraymap_set bool // map or array set value state
|
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_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_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_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_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_autofree bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree
|
||||||
is_builtin_mod bool
|
is_builtin_mod bool
|
||||||
is_json_fn bool // inside json.encode()
|
is_json_fn bool // inside json.encode()
|
||||||
is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)`
|
is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)`
|
||||||
is_fn_index_call bool
|
is_fn_index_call bool
|
||||||
vlines_path string // set to the proper path for generating #line directives
|
vlines_path string // set to the proper path for generating #line directives
|
||||||
optionals map[string]string // to avoid duplicates
|
optionals map[string]string // to avoid duplicates
|
||||||
done_optionals shared []string // to avoid duplicates
|
done_optionals shared []string // to avoid duplicates
|
||||||
chan_pop_optionals map[string]string // types for `x := <-ch or {...}`
|
chan_pop_optionals map[string]string // types for `x := <-ch or {...}`
|
||||||
chan_push_optionals map[string]string // types for `ch <- x or {...}`
|
chan_push_optionals map[string]string // types for `ch <- x or {...}`
|
||||||
mtxs string // array of mutexes if the `lock` has multiple variables
|
mtxs string // array of mutexes if the `lock` has multiple variables
|
||||||
labeled_loops map[string]&ast.Stmt
|
labeled_loops map[string]&ast.Stmt
|
||||||
inner_loop &ast.Stmt
|
inner_loop &ast.Stmt
|
||||||
shareds map[int]string // types with hidden mutex for which decl has been emitted
|
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_ternary int // ?: comma separated statements on a single line
|
||||||
inside_map_postfix bool // inside map++/-- postfix expr
|
inside_map_postfix bool // inside map++/-- postfix expr
|
||||||
inside_map_infix bool // inside map<</+=/-= infix expr
|
inside_map_infix bool // inside map<</+=/-= infix expr
|
||||||
inside_map_index bool
|
inside_map_index bool
|
||||||
inside_opt_data bool
|
inside_opt_data bool
|
||||||
inside_if_optional bool
|
inside_if_optional bool
|
||||||
inside_match_optional bool
|
inside_match_optional bool
|
||||||
inside_vweb_tmpl bool
|
inside_vweb_tmpl bool
|
||||||
inside_return bool
|
inside_return bool
|
||||||
inside_struct_init bool
|
inside_struct_init bool
|
||||||
inside_or_block bool
|
inside_or_block bool
|
||||||
inside_call bool
|
inside_call bool
|
||||||
inside_for_c_stmt bool
|
inside_for_c_stmt bool
|
||||||
inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls)
|
inside_comptime_for_field bool
|
||||||
inside_const bool
|
inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls)
|
||||||
inside_lambda bool
|
inside_const bool
|
||||||
loop_depth int
|
inside_lambda bool
|
||||||
ternary_names map[string]string
|
loop_depth int
|
||||||
ternary_level_names map[string][]string
|
ternary_names map[string]string
|
||||||
arraymap_set_pos int // map or array set value position
|
ternary_level_names map[string][]string
|
||||||
stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement
|
arraymap_set_pos int // map or array set value position
|
||||||
skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements)
|
stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement
|
||||||
right_is_opt bool
|
skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements)
|
||||||
indent int
|
right_is_opt bool
|
||||||
empty_line bool
|
indent int
|
||||||
assign_op token.Kind // *=, =, etc (for array_set)
|
empty_line bool
|
||||||
defer_stmts []ast.DeferStmt
|
assign_op token.Kind // *=, =, etc (for array_set)
|
||||||
defer_ifdef string
|
defer_stmts []ast.DeferStmt
|
||||||
defer_profile_code string
|
defer_ifdef string
|
||||||
defer_vars []string
|
defer_profile_code string
|
||||||
str_types []StrType // types that need automatic str() generation
|
defer_vars []string
|
||||||
generated_str_fns []StrType // types that already have a str() function
|
str_types []StrType // types that need automatic str() generation
|
||||||
threaded_fns shared []string // for generating unique wrapper types and fns for `go xxx()`
|
generated_str_fns []StrType // types that already have a str() function
|
||||||
waiter_fns shared []string // functions that wait for `go xxx()` to finish
|
threaded_fns shared []string // for generating unique wrapper types and fns for `go xxx()`
|
||||||
needed_equality_fns []ast.Type
|
waiter_fns shared []string // functions that wait for `go xxx()` to finish
|
||||||
generated_eq_fns []ast.Type
|
needed_equality_fns []ast.Type
|
||||||
array_sort_fn shared []string
|
generated_eq_fns []ast.Type
|
||||||
array_contains_types []ast.Type
|
array_sort_fn shared []string
|
||||||
array_index_types []ast.Type
|
array_contains_types []ast.Type
|
||||||
auto_fn_definitions []string // auto generated functions defination list
|
array_index_types []ast.Type
|
||||||
sumtype_casting_fns []SumtypeCastingFn
|
auto_fn_definitions []string // auto generated functions defination list
|
||||||
anon_fn_definitions []string // anon generated functions defination list
|
sumtype_casting_fns []SumtypeCastingFn
|
||||||
sumtype_definitions map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated
|
anon_fn_definitions []string // anon generated functions defination list
|
||||||
json_types []ast.Type // to avoid json gen duplicates
|
sumtype_definitions map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated
|
||||||
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
|
json_types []ast.Type // to avoid json gen duplicates
|
||||||
hotcode_fn_names []string
|
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
|
||||||
embedded_files []ast.EmbeddedFile
|
hotcode_fn_names []string
|
||||||
sql_i int
|
embedded_files []ast.EmbeddedFile
|
||||||
sql_stmt_name string
|
sql_i int
|
||||||
sql_bind_name string
|
sql_stmt_name string
|
||||||
sql_idents []string
|
sql_bind_name string
|
||||||
sql_idents_types []ast.Type
|
sql_idents []string
|
||||||
sql_left_type ast.Type
|
sql_idents_types []ast.Type
|
||||||
sql_table_name string
|
sql_left_type ast.Type
|
||||||
sql_fkey string
|
sql_table_name string
|
||||||
sql_parent_id string
|
sql_fkey string
|
||||||
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
sql_parent_id string
|
||||||
strs_to_free0 []string // strings.Builder
|
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
||||||
|
strs_to_free0 []string // strings.Builder
|
||||||
// strs_to_free []string // strings.Builder
|
// strs_to_free []string // strings.Builder
|
||||||
// tmp_arg_vars_to_free []string
|
// tmp_arg_vars_to_free []string
|
||||||
// autofree_pregen map[string]string
|
// autofree_pregen map[string]string
|
||||||
|
|
|
@ -503,13 +503,16 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
||||||
}
|
}
|
||||||
} else if node.kind == .fields {
|
} else if node.kind == .fields {
|
||||||
// TODO add fields
|
// TODO add fields
|
||||||
if sym.info is ast.Struct {
|
if sym.kind == .struct_ {
|
||||||
if sym.info.fields.len > 0 {
|
sym_info := sym.info as ast.Struct
|
||||||
|
if sym_info.fields.len > 0 {
|
||||||
g.writeln('\tFieldData $node.val_var = {0};')
|
g.writeln('\tFieldData $node.val_var = {0};')
|
||||||
}
|
}
|
||||||
for field in sym.info.fields {
|
g.inside_comptime_for_field = true
|
||||||
|
for field in sym_info.fields {
|
||||||
g.comptime_for_field_var = node.val_var
|
g.comptime_for_field_var = node.val_var
|
||||||
g.comptime_for_field_value = field
|
g.comptime_for_field_value = field
|
||||||
|
g.comptime_for_field_type = field.typ
|
||||||
g.writeln('/* field $i */ {')
|
g.writeln('/* field $i */ {')
|
||||||
g.writeln('\t${node.val_var}.name = _SLIT("$field.name");')
|
g.writeln('\t${node.val_var}.name = _SLIT("$field.name");')
|
||||||
if field.attrs.len == 0 {
|
if field.attrs.len == 0 {
|
||||||
|
@ -531,7 +534,9 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
||||||
g.stmts(node.stmts)
|
g.stmts(node.stmts)
|
||||||
i++
|
i++
|
||||||
g.writeln('}')
|
g.writeln('}')
|
||||||
|
g.comptime_for_field_type = 0
|
||||||
}
|
}
|
||||||
|
g.inside_comptime_for_field = false
|
||||||
g.comptime_var_type_map.delete(node.val_var)
|
g.comptime_var_type_map.delete(node.val_var)
|
||||||
}
|
}
|
||||||
} else if node.kind == .attributes {
|
} else if node.kind == .attributes {
|
||||||
|
|
|
@ -1120,6 +1120,16 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
is_selector_call = true
|
is_selector_call = true
|
||||||
}
|
}
|
||||||
|
if g.inside_comptime_for_field {
|
||||||
|
mut node_ := unsafe { node }
|
||||||
|
for i, mut call_arg in node_.args {
|
||||||
|
if mut call_arg.expr is ast.Ident {
|
||||||
|
if mut call_arg.expr.obj is ast.Var {
|
||||||
|
node_.args[i].typ = call_arg.expr.obj.typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
mut name := node.name
|
mut name := node.name
|
||||||
is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic']
|
is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic']
|
||||||
print_method := name
|
print_method := name
|
||||||
|
@ -1201,14 +1211,22 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !is_selector_call {
|
if !is_selector_call {
|
||||||
name = g.generic_fn_name(node.concrete_types, name, false)
|
if func := g.table.find_fn(node.name) {
|
||||||
|
if func.generic_names.len > 0 {
|
||||||
|
if g.comptime_for_field_type != 0 && g.inside_comptime_for_field {
|
||||||
|
name = g.generic_fn_name([g.comptime_for_field_type], name, false)
|
||||||
|
} else {
|
||||||
|
name = g.generic_fn_name(node.concrete_types, name, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO2
|
// TODO2
|
||||||
// cgen shouldn't modify ast nodes, this should be moved
|
// cgen shouldn't modify ast nodes, this should be moved
|
||||||
// g.generate_tmp_autofree_arg_vars(node, name)
|
// g.generate_tmp_autofree_arg_vars(node, name)
|
||||||
// Handle `print(x)`
|
// Handle `print(x)`
|
||||||
mut print_auto_str := false
|
mut print_auto_str := false
|
||||||
if is_print && (node.args[0].typ != ast.string_type || g.comptime_for_method.len > 0) { // && !free_tmp_arg_vars {
|
if is_print && (node.args[0].typ != ast.string_type || g.comptime_for_method.len > 0) {
|
||||||
mut typ := node.args[0].typ
|
mut typ := node.args[0].typ
|
||||||
if typ == 0 {
|
if typ == 0 {
|
||||||
g.checker_bug('print arg.typ is 0', node.pos)
|
g.checker_bug('print arg.typ is 0', node.pos)
|
||||||
|
|
|
@ -197,6 +197,16 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int) {
|
||||||
|
|
||||||
fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
||||||
// fn (mut g Gen) str_int2(node ast.StringInterLiteral) {
|
// fn (mut g Gen) str_int2(node ast.StringInterLiteral) {
|
||||||
|
if g.inside_comptime_for_field {
|
||||||
|
mut node_ := unsafe { node }
|
||||||
|
for i, expr in node_.exprs {
|
||||||
|
if mut expr is ast.Ident {
|
||||||
|
if mut expr.obj is ast.Var {
|
||||||
|
node_.expr_types[i] = expr.obj.typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
g.write(' str_intp($node.vals.len, ')
|
g.write(' str_intp($node.vals.len, ')
|
||||||
g.write('_MOV((StrIntpData[]){')
|
g.write('_MOV((StrIntpData[]){')
|
||||||
for i, val in node.vals {
|
for i, val in node.vals {
|
||||||
|
|
|
@ -92,6 +92,7 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr
|
||||||
language: language
|
language: language
|
||||||
concrete_types: concrete_types
|
concrete_types: concrete_types
|
||||||
concrete_list_pos: concrete_list_pos
|
concrete_list_pos: concrete_list_pos
|
||||||
|
raw_concrete_types: concrete_types
|
||||||
or_block: ast.OrExpr{
|
or_block: ast.OrExpr{
|
||||||
stmts: or_stmts
|
stmts: or_stmts
|
||||||
kind: or_kind
|
kind: or_kind
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
// exploring `decode` options with nested structs
|
||||||
|
|
||||||
|
struct Parent {
|
||||||
|
name string
|
||||||
|
age int
|
||||||
|
child Child
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Child {
|
||||||
|
name string
|
||||||
|
age int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inspect<T>(t T) string {
|
||||||
|
mut output_str := ''
|
||||||
|
println('$T.name')
|
||||||
|
$for field in T.fields {
|
||||||
|
val := t.$(field.name)
|
||||||
|
$if field.typ is string {
|
||||||
|
println(' $field.name = $val')
|
||||||
|
output_str += ' $field.name = $val\n'
|
||||||
|
} $else $if field.typ is int {
|
||||||
|
println(' $field.name = $val')
|
||||||
|
output_str += ' $field.name = $val\n'
|
||||||
|
} $else {
|
||||||
|
str := inspect(val)
|
||||||
|
output_str += str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output_str
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_comptime_for_in_field_with_generic_fn() {
|
||||||
|
p := Parent{
|
||||||
|
name: 'parent'
|
||||||
|
age: 30
|
||||||
|
child: Child{
|
||||||
|
name: 'child'
|
||||||
|
age: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret := inspect(p)
|
||||||
|
assert ret.contains('name = parent')
|
||||||
|
assert ret.contains('age = 30')
|
||||||
|
assert ret.contains('name = child')
|
||||||
|
assert ret.contains('age = 5')
|
||||||
|
}
|
|
@ -39,7 +39,7 @@ fn decode2<T>() T {
|
||||||
x.$(field.name) = byte(-1)
|
x.$(field.name) = byte(-1)
|
||||||
} $else $if field.typ is int {
|
} $else $if field.typ is int {
|
||||||
x.$(field.name) = int(-1)
|
x.$(field.name) = int(-1)
|
||||||
} $else {
|
} $else $if field.typ is string {
|
||||||
x.$(field.name) = 'hi'
|
x.$(field.name) = 'hi'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue