checker: check array and fields mutability

pull/4627/head
Enzo Baldisserri 2020-04-27 22:53:26 +02:00 committed by GitHub
parent ce1215f507
commit 682838a0cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 340 additions and 86 deletions

View File

@ -24,8 +24,8 @@ const (
'vlib/v/tests/live_test.v', // Linux & Solaris only; since live does not actually work for now with v2, just skip 'vlib/v/tests/live_test.v', // Linux & Solaris only; since live does not actually work for now with v2, just skip
'vlib/v/tests/asm_test.v', // skip everywhere for now, works on linux with cc != tcc 'vlib/v/tests/asm_test.v', // skip everywhere for now, works on linux with cc != tcc
] ]
skip_on_linux = []string skip_on_linux = []string{}
skip_on_non_linux = []string skip_on_non_linux = []string{}
) )
fn main() { fn main() {

View File

@ -141,7 +141,7 @@ fn test_bf_from_str() {
rand.seed(time.now().unix) rand.seed(time.now().unix)
len := 80 len := 80
mut input := '' mut input := ''
for i in 0..len { for _ in 0..len {
if rand.next(2) == 1 { if rand.next(2) == 1 {
input = input + '1' input = input + '1'
} }
@ -244,7 +244,7 @@ fn test_bf_resize() {
rand.seed(time.now().unix) rand.seed(time.now().unix)
len := 80 len := 80
mut input := bitfield.new(rand.next(len) + 1) mut input := bitfield.new(rand.next(len) + 1)
for i in 0..100 { for _ in 0..100 {
input.resize(rand.next(len) + 1) input.resize(rand.next(len) + 1)
input.setbit(input.getsize() - 1) input.setbit(input.getsize() - 1)
} }

View File

@ -7,11 +7,12 @@ import strings
pub struct array { pub struct array {
pub: pub:
element_size int
pub mut:
data voidptr// Using a void pointer allows to implement arrays without generics and without generating data voidptr// Using a void pointer allows to implement arrays without generics and without generating
// extra code for every type. // extra code for every type.
len int len int
cap int cap int
element_size int
} }
// Internal function, used by V (`nums := []int`) // Internal function, used by V (`nums := []int`)

View File

@ -33,6 +33,7 @@ pub mut:
} }
pub struct Line64 { pub struct Line64 {
pub mut:
f_size_of_struct u32 f_size_of_struct u32
f_key voidptr f_key voidptr
f_line_number u32 f_line_number u32

View File

@ -5,6 +5,7 @@ struct User {
} }
struct A { struct A {
mut:
m map[string]int m map[string]int
users map[string]User users map[string]User
} }
@ -24,7 +25,7 @@ fn test_map() {
assert 'hi' in m assert 'hi' in m
mut sum := 0 mut sum := 0
// Test `for in` // Test `for in`
for key, val in m { for _, val in m {
sum += val sum += val
} }
assert sum == 80 + 101 assert sum == 80 + 101

View File

@ -51,7 +51,7 @@ pub:
// hash_cache int // hash_cache int
pub struct ustring { pub struct ustring {
pub: pub mut:
s string s string
runes []int runes []int
len int len int

View File

@ -435,7 +435,7 @@ pub fn (fs FlagParser) usage() string {
if fs.flags.len > 0 { if fs.flags.len > 0 {
use += 'Options:\n' use += 'Options:\n'
for f in fs.flags { for f in fs.flags {
mut onames:=[]string mut onames := []string{}
if f.abbr != 0 { if f.abbr != 0 {
onames << '-${f.abbr.str()}' onames << '-${f.abbr.str()}'
} }

View File

@ -28,7 +28,7 @@ fn test_rand_r_seed_update() {
for _ in 0..rnd_count { for _ in 0..rnd_count {
prev_seed := seed prev_seed := seed
res := rand.rand_r(&seed) _ := rand.rand_r(&seed)
assert prev_seed != seed assert prev_seed != seed
} }

View File

@ -4,8 +4,9 @@
module time module time
pub struct StopWatch { pub struct StopWatch {
mut:
pause_time u64 pause_time u64
pub: pub mut:
start u64 start u64
end u64 end u64
} }

View File

@ -134,9 +134,10 @@ mut:
pub struct ConstDecl { pub struct ConstDecl {
pub: pub:
fields []ConstField
is_pub bool is_pub bool
pos token.Position pos token.Position
pub mut:
fields []ConstField
} }
pub struct StructDecl { pub struct StructDecl {
@ -162,10 +163,10 @@ pub:
pub struct StructInitField { pub struct StructInitField {
pub: pub:
name string
expr Expr expr Expr
pos token.Position pos token.Position
mut: mut:
name string
typ table.Type typ table.Type
expected_type table.Type expected_type table.Type
} }
@ -173,10 +174,10 @@ mut:
pub struct StructInit { pub struct StructInit {
pub: pub:
pos token.Position pos token.Position
fields []StructInitField
is_short bool is_short bool
mut: mut:
typ table.Type typ table.Type
fields []StructInitField
} }
// import statement // import statement
@ -198,7 +199,6 @@ pub struct FnDecl {
pub: pub:
name string name string
stmts []Stmt stmts []Stmt
return_type table.Type
args []table.Arg args []table.Arg
is_deprecated bool is_deprecated bool
is_pub bool is_pub bool
@ -213,6 +213,8 @@ pub:
is_builtin bool // this function is defined in builtin/strconv is_builtin bool // this function is defined in builtin/strconv
ctdefine string // has [if myflag] tag ctdefine string // has [if myflag] tag
pos token.Position pos token.Position
pub mut:
return_type table.Type
} }
pub struct BranchStmt { pub struct BranchStmt {
@ -224,10 +226,10 @@ pub struct CallExpr {
pub: pub:
pos token.Position pos token.Position
left Expr // `user` in `user.register()` left Expr // `user` in `user.register()`
is_method bool
mod string mod string
mut: mut:
name string name string
is_method bool
args []CallArg args []CallArg
expected_arg_types []table.Type expected_arg_types []table.Type
is_c bool is_c bool
@ -292,10 +294,11 @@ pub struct File {
pub: pub:
path string path string
mod Module mod Module
imports []Import
stmts []Stmt stmts []Stmt
scope &Scope scope &Scope
global_scope &Scope global_scope &Scope
mut:
imports []Import
} }
pub struct IdentFn { pub struct IdentFn {
@ -331,11 +334,11 @@ pub:
tok_kind token.Kind tok_kind token.Kind
mod string mod string
pos token.Position pos token.Position
is_mut bool
mut: mut:
name string name string
kind IdentKind kind IdentKind
info IdentInfo info IdentInfo
is_mut bool
} }
pub fn (i &Ident) var_info() IdentVar { pub fn (i &Ident) var_info() IdentVar {
@ -508,11 +511,11 @@ pub:
pub struct AssignStmt { pub struct AssignStmt {
pub: pub:
left []Ident
right []Expr right []Expr
op token.Kind op token.Kind
pos token.Position pos token.Position
mut: pub mut:
left []Ident
left_types []table.Type left_types []table.Type
right_types []table.Type right_types []table.Type
is_static bool // for translated code only is_static bool // for translated code only
@ -670,8 +673,8 @@ pub:
expr Expr // `buf` expr Expr // `buf`
arg Expr // `n` in `string(buf, n)` arg Expr // `n` in `string(buf, n)`
typ table.Type // `string` typ table.Type // `string`
typname string
mut: mut:
typname string
expr_type table.Type // `byteptr` expr_type table.Type // `byteptr`
has_arg bool has_arg bool
} }

View File

@ -121,7 +121,7 @@ pub fn (x Expr) str() string {
return '${it.expr.str()}.${it.field}' return '${it.expr.str()}.${it.field}'
} }
StringInterLiteral { StringInterLiteral {
res := []string{} mut res := []string{}
res << "'" res << "'"
for i, val in it.vals { for i, val in it.vals {
res << val res << val

View File

@ -17,12 +17,12 @@ import v.depgraph
pub struct Builder { pub struct Builder {
pub: pub:
pref &pref.Preferences
table &table.Table table &table.Table
checker checker.Checker checker checker.Checker
compiled_dir string // contains os.real_path() of the dir of the final file beeing compiled, or the dir itself when doing `v .` compiled_dir string // contains os.real_path() of the dir of the final file beeing compiled, or the dir itself when doing `v .`
module_path string module_path string
mut: mut:
pref &pref.Preferences
module_search_paths []string module_search_paths []string
parsed_files []ast.File parsed_files []ast.File
global_scope &ast.Scope global_scope &ast.Scope

View File

@ -364,11 +364,7 @@ pub fn (mut c Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type {
.left_shift { .left_shift {
if left.kind == .array { if left.kind == .array {
// `array << elm` // `array << elm`
match infix_expr.left { c.fail_if_immutable(infix_expr.left)
ast.Ident {}
ast.SelectorExpr {}
else { println('typeof: ${typeof(infix_expr.left)}') }
}
// the expressions have different types (array_x and x) // the expressions have different types (array_x and x)
if c.table.check(c.table.value_type(left_type), right_type) { if c.table.check(c.table.value_type(left_type), right_type) {
// []T << T // []T << T
@ -446,6 +442,58 @@ pub fn (mut c Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type {
} }
} }
fn (mut c Checker) fail_if_immutable(expr ast.Expr) {
match expr {
ast.Ident {
scope := c.file.scope.innermost(expr.position().pos)
if v := scope.find_var(it.name) {
if !v.is_mut && !v.typ.is_ptr() {
c.error('`$it.name` is immutable, declare it with `mut` to make it mutable',
it.pos)
}
}
}
ast.IndexExpr {
c.fail_if_immutable(it.left)
}
ast.ParExpr {
c.fail_if_immutable(it.expr)
}
ast.PrefixExpr {
c.fail_if_immutable(it.right)
}
ast.SelectorExpr {
// retrieve table.Field
typ_sym := c.table.get_type_symbol(it.expr_type)
match typ_sym.kind {
.struct_ {
struct_info := typ_sym.info as table.Struct
field_info := struct_info.get_field(it.field)
if !field_info.is_mut {
type_str := c.table.type_to_str(it.expr_type)
c.error('field `$it.field` of struct `${type_str}` is immutable', it.pos)
}
c.fail_if_immutable(it.expr)
}
.array, .string {
// This should only happen in `builtin`
// TODO Remove `crypto.rand` when possible (see vlib/crypto/rand/rand.v,
// if `c_array_to_bytes_tmp` doesn't exist, then it's safe to remove it)
if c.file.mod.name !in ['builtin', 'crypto.rand'] {
c.error('`$typ_sym.kind` can not be modified', expr.position())
}
}
else {
c.error('unexpected symbol `${typ_sym.kind}`', expr.position())
}
}
}
else {
c.error('unexpected expression `${typeof(expr)}`', expr.position())
}
}
}
fn (mut c Checker) assign_expr(assign_expr mut ast.AssignExpr) { fn (mut c Checker) assign_expr(assign_expr mut ast.AssignExpr) {
c.expected_type = table.void_type c.expected_type = table.void_type
left_type := c.expr(assign_expr.left) left_type := c.expr(assign_expr.left)
@ -460,32 +508,7 @@ fn (mut c Checker) assign_expr(assign_expr mut ast.AssignExpr) {
return return
} }
// Make sure the variable is mutable // Make sure the variable is mutable
match assign_expr.left { c.fail_if_immutable(assign_expr.left)
ast.Ident {
scope := c.file.scope.innermost(assign_expr.pos.pos)
if v := scope.find_var(it.name) {
if !v.is_mut {
c.error('`$it.name` is immutable, declare it with `mut` to assign to it',
assign_expr.pos)
}
}
}
ast.IndexExpr {
// `m[key] = val`
if it.left is ast.Ident {
ident := it.left as ast.Ident
// TODO copy pasta
scope := c.file.scope.innermost(assign_expr.pos.pos)
if v := scope.find_var(ident.name) {
if !v.is_mut {
c.error('`$ident.name` is immutable, declare it with `mut` to assign to it',
assign_expr.pos)
}
}
}
}
else {}
}
// Single side check // Single side check
match assign_expr.op { match assign_expr.op {
.assign {} // No need to do single side check for =. But here put it first for speed. .assign {} // No need to do single side check for =. But here put it first for speed.

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/immutable_array_field_assign.v:9:4: error: field `i` of struct `A` is immutable
7| i: [0]
8| }
9| a.i[0] = 3
^
10| }

View File

@ -0,0 +1,10 @@
struct A {
i []int
}
fn main() {
mut a := A{
i: [0]
}
a.i[0] = 3
}

View File

@ -0,0 +1,10 @@
struct A {
i []int
}
fn main() {
mut a := A{
i: [0]
}
a.i[0] = 3
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/immutable_array_field_shift.v:14:4: error: field `a` of struct `B` is immutable
12| a: A{}
13| }
14| b.a.i << 3
^
15| }

View File

@ -0,0 +1,15 @@
struct A {
mut:
i []int
}
struct B {
a A
}
fn main() {
mut b := B{
a: A{}
}
b.a.i << 3
}

View File

@ -0,0 +1,15 @@
struct A {
mut:
i []int
}
struct B {
a A
}
fn main() {
mut b := B{
a: A{}
}
b.a.i << 3
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/immutable_array_struct_assign.v:8:2: error: `a` is immutable, declare it with `mut` to make it mutable
6| fn main() {
7| a := A{}
8| a.i[0] += 3
^
9| }

View File

@ -0,0 +1,9 @@
struct A {
pub mut:
i []int
}
fn main() {
a := A{}
a.i[0] += 3
}

View File

@ -0,0 +1,9 @@
struct A {
pub mut:
i []int
}
fn main() {
a := A{}
a.i[0] += 3
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/immutable_array_struct_shift.v:8:2: error: `a` is immutable, declare it with `mut` to make it mutable
6| fn main() {
7| a := []A{}
8| a[0].i << 3
^
9| }

View File

@ -0,0 +1,9 @@
struct A {
pub mut:
i []int
}
fn main() {
a := []A{}
a[0].i << 3
}

View File

@ -0,0 +1,9 @@
struct A {
pub mut:
i []int
}
fn main() {
a := []A{}
a[0].i << 3
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/immutable_array_var.v:3:2: error: `a` is immutable, declare it with `mut` to make it mutable
1| fn main() {
2| a := [1, 2]
3| a << 3
^
4| }

View File

@ -0,0 +1,4 @@
fn main() {
a := [1, 2]
a << 3
}

View File

@ -0,0 +1,4 @@
fn main() {
a := [1, 2]
a << 3
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/immutable_field.v:8:4: error: field `i1` of struct `A` is immutable
6| fn main() {
7| a := A{1}
8| a.i1 = 2
~~
9| }

View File

@ -0,0 +1,9 @@
struct A {
pub:
i1 int
}
fn main() {
a := A{1}
a.i1 = 2
}

View File

@ -0,0 +1,9 @@
struct A {
pub:
i1 int
}
fn main() {
a := A{1}
a.i1 = 2
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/immutable_var.v:3:2: error: `a` is immutable, declare it with `mut` to make it mutable
1| fn main() {
2| a := 1
3| a = 2
^
4| }

View File

@ -0,0 +1,4 @@
fn main() {
a := 1
a = 2
}

View File

@ -0,0 +1,4 @@
fn main() {
a := 1
a = 2
}

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/left_shift_err.v:3:7: error: cannot shift type string into array_int vlib/v/checker/tests/left_shift_err.v:3:7: error: cannot shift type string into array_int
1| fn main() { 1| fn main() {
2| l := []int{} 2| mut l := []int{}
3| l << 'test' 3| l << 'test'
~~~~~~ ~~~~~~
4| } 4| }

View File

@ -1,4 +1,4 @@
fn main() { fn main() {
l := []int{} mut l := []int{}
l << 'test' l << 'test'
} }

View File

@ -1,4 +1,4 @@
fn main() { fn main() {
l := []int{} mut l := []int{}
l << 'test' l << 'test'
} }

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/struct_pub_field.v:9:4: error: field `i` of struct `A` is immutable
7| i: 1
8| }
9| a.i = 2
^
10| }

View File

@ -0,0 +1,10 @@
struct A {
i int
}
fn main() {
a := A{
i: 1
}
a.i = 2
}

View File

@ -0,0 +1,10 @@
struct A {
i int
}
fn main() {
a := A{
i: 1
}
a.i = 2
}

View File

@ -56,7 +56,6 @@ struct Gen {
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
comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI
pcs_declarations strings.Builder // -prof profile counter declarations for each function pcs_declarations strings.Builder // -prof profile counter declarations for each function
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
table &table.Table table &table.Table
pref &pref.Preferences pref &pref.Preferences
mut: mut:
@ -86,6 +85,7 @@ mut:
threaded_fns []string // for generating unique wrapper types and fns for `go xxx()` threaded_fns []string // for generating unique wrapper types and fns for `go xxx()`
array_fn_definitions []string // array equality functions that have been defined array_fn_definitions []string // array equality functions that have been defined
is_json_fn bool // inside json.encode() is_json_fn bool // inside json.encode()
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
} }
const ( const (

View File

@ -16,15 +16,15 @@ const (
) )
struct JsGen { struct JsGen {
table &table.Table
definitions strings.Builder
pref &pref.Preferences
mut:
out strings.Builder out strings.Builder
namespaces map[string]strings.Builder namespaces map[string]strings.Builder
namespaces_pub map[string][]string namespaces_pub map[string][]string
namespace string namespace string
table &table.Table
definitions strings.Builder
pref &pref.Preferences
doc &JsDoc doc &JsDoc
mut:
constants strings.Builder // all global V constants constants strings.Builder // all global V constants
file ast.File file ast.File
tmp_count int tmp_count int
@ -110,7 +110,7 @@ pub fn (g mut JsGen) escape_namespace() {
pub fn (g mut JsGen) push_pub_var(s string) { pub fn (g mut JsGen) push_pub_var(s string) {
// Workaround until `m[key]<<val` works. // Workaround until `m[key]<<val` works.
arr := g.namespaces_pub[g.namespace] mut arr := g.namespaces_pub[g.namespace]
arr << s arr << s
g.namespaces_pub[g.namespace] = arr g.namespaces_pub[g.namespace] = arr
} }
@ -123,7 +123,7 @@ pub fn (g mut JsGen) find_class_methods(stmts []ast.Stmt) {
// Found struct method, store it to be generated along with the class. // Found struct method, store it to be generated along with the class.
class_name := g.table.get_type_symbol(it.receiver.typ).name class_name := g.table.get_type_symbol(it.receiver.typ).name
// Workaround until `map[key] << val` works. // Workaround until `map[key] << val` works.
arr := g.method_fn_decls[class_name] mut arr := g.method_fn_decls[class_name]
arr << stmt arr << stmt
g.method_fn_decls[class_name] = arr g.method_fn_decls[class_name] = arr
} }

View File

@ -26,7 +26,6 @@ fn (mut g Gen) profile_fn(fn_name string, is_main bool){
} }
} }
pub fn (mut g Gen) gen_vprint_profile_stats() { pub fn (mut g Gen) gen_vprint_profile_stats() {
g.pcs_declarations.writeln('void vprint_profile_stats(){') g.pcs_declarations.writeln('void vprint_profile_stats(){')
fstring := '"%14llu %14.3fms %14.0fns %s \\n"' fstring := '"%14llu %14.3fms %14.0fns %s \\n"'

View File

@ -284,7 +284,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
stmts = p.parse_block_no_scope() stmts = p.parse_block_no_scope()
} }
p.close_scope() p.close_scope()
func := table.Fn{ mut func := table.Fn{
args: args args: args
is_variadic: is_variadic is_variadic: is_variadic
return_type: return_type return_type: return_type

View File

@ -109,21 +109,16 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
} else if p.tok.kind == .name && (p.tok.lit in table.builtin_type_names || (p.tok.lit[0].is_capital() && } else if p.tok.kind == .name && (p.tok.lit in table.builtin_type_names || (p.tok.lit[0].is_capital() &&
!p.tok.lit.is_upper()) || p.peek_tok.kind == .dot) { !p.tok.lit.is_upper()) || p.peek_tok.kind == .dot) {
// Sum type match // Sum type match
// if sym.kind == .sum_type {
// p.warn('is sum')
// TODO `exprs << ast.Type{...}
typ := p.parse_type() typ := p.parse_type()
x := ast.Type{ exprs << ast.Type{
typ: typ typ: typ
} }
mut expr := ast.Expr{}
expr = x
exprs << expr
p.scope.register('it', ast.Var{ p.scope.register('it', ast.Var{
name: 'it' name: 'it'
typ: typ.to_ptr() typ: typ.to_ptr()
pos: cond_pos pos: cond_pos
is_used: true is_used: true
is_mut: is_mut
}) })
// TODO // TODO
if p.tok.kind == .comma { if p.tok.kind == .comma {

View File

@ -565,7 +565,7 @@ pub fn (mut p Parser) parse_ident(is_c, is_js bool) ast.Ident {
if p.expr_mod.len > 0 { if p.expr_mod.len > 0 {
name = '${p.expr_mod}.$name' name = '${p.expr_mod}.$name'
} }
mut ident := ast.Ident{ return ast.Ident{
kind: .unresolved kind: .unresolved
name: name name: name
is_c: is_c is_c: is_c
@ -573,7 +573,6 @@ pub fn (mut p Parser) parse_ident(is_c, is_js bool) ast.Ident {
mod: p.mod mod: p.mod
pos: pos pos: pos
} }
return ident
} }
pub fn (mut p Parser) name_expr() ast.Expr { pub fn (mut p Parser) name_expr() ast.Expr {
@ -694,9 +693,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
mod: mod mod: mod
} }
} else { } else {
mut ident := ast.Ident{} node = p.parse_ident(is_c, is_js)
ident = p.parse_ident(is_c, is_js)
node = ident
} }
p.expr_mod = '' p.expr_mod = ''
return node return node

View File

@ -193,14 +193,12 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr {
p.inside_is = true p.inside_is = true
} }
right = p.expr(precedence) right = p.expr(precedence)
mut expr := ast.Expr{} return ast.InfixExpr{
expr = ast.InfixExpr{
left: left left: left
right: right right: right
op: op op: op
pos: pos pos: pos
} }
return expr
} }
fn (mut p Parser) prefix_expr() ast.PrefixExpr { fn (mut p Parser) prefix_expr() ast.PrefixExpr {

View File

@ -38,6 +38,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
mut mut_pos := -1 mut mut_pos := -1
mut pub_pos := -1 mut pub_pos := -1
mut pub_mut_pos := -1 mut pub_mut_pos := -1
mut is_field_mut := false
mut is_field_pub := false
mut is_field_global := false
if !no_body { if !no_body {
p.check(.lcbr) p.check(.lcbr)
for p.tok.kind != .rcbr { for p.tok.kind != .rcbr {
@ -50,17 +53,29 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
if p.tok.kind == .key_mut { if p.tok.kind == .key_mut {
p.check(.key_mut) p.check(.key_mut)
pub_mut_pos = fields.len pub_mut_pos = fields.len
is_field_pub = true
is_field_mut = true
is_field_global = false
} else { } else {
pub_pos = fields.len pub_pos = fields.len
is_field_pub = true
is_field_mut = false
is_field_global = false
} }
p.check(.colon) p.check(.colon)
} else if p.tok.kind == .key_mut { } else if p.tok.kind == .key_mut {
p.check(.key_mut) p.check(.key_mut)
p.check(.colon) p.check(.colon)
mut_pos = fields.len mut_pos = fields.len
is_field_pub = false
is_field_mut = true
is_field_global = false
} else if p.tok.kind == .key_global { } else if p.tok.kind == .key_global {
p.check(.key_global) p.check(.key_global)
p.check(.colon) p.check(.colon)
is_field_pub = true
is_field_mut = true
is_field_global = true
} }
field_name := p.check_name() field_name := p.check_name()
field_pos := p.tok.position() field_pos := p.tok.position()
@ -108,6 +123,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
typ: typ typ: typ
default_expr: default_expr default_expr: default_expr
has_default_expr: has_default_expr has_default_expr: has_default_expr
is_pub: is_field_pub
is_mut: is_field_mut
is_global: is_field_global
} }
// println('struct field $ti.name $field_name') // println('struct field $ti.name $field_name')
} }

View File

@ -311,6 +311,14 @@ pub fn (t &TypeSymbol) map_info() Map {
} }
} }
[inline]
pub fn (t &TypeSymbol) struct_info() Struct {
match t.info {
Struct { return it }
else { panic('TypeSymbol.struct_info(): no struct info for type: $t.name') }
}
}
/* /*
pub fn (t TypeSymbol) str() string { pub fn (t TypeSymbol) str() string {
return t.name return t.name
@ -499,6 +507,7 @@ pub mut:
} }
pub struct Interface { pub struct Interface {
mut:
gen_types []string gen_types []string
foo string foo string
} }
@ -522,6 +531,9 @@ mut:
has_default_expr bool has_default_expr bool
default_val string default_val string
attr string attr string
is_pub bool
is_mut bool
is_global bool
} }
pub struct Array { pub struct Array {
@ -594,3 +606,19 @@ pub fn (table &Table) type_to_str(t Type) string {
*/ */
return res return res
} }
pub fn (s Struct) find_field(name string) ?Field {
for field in s.fields {
if field.name == name {
return field
}
}
return none
}
pub fn (s Struct) get_field(name string) Field {
if field := s.find_field(name) {
return field
}
panic('unknown field `$name`')
}

View File

@ -3,7 +3,7 @@ import v.cflag
const ( const (
module_name = 'main' module_name = 'main'
cdefines = []string cdefines = []string{}
no_name = '' no_name = ''
no_flag = '' no_flag = ''
no_os = '' no_os = ''

View File

@ -18,7 +18,6 @@ pub mut:
pub struct Fn { pub struct Fn {
pub: pub:
name string
args []Arg args []Arg
return_type Type return_type Type
is_variadic bool is_variadic bool
@ -28,6 +27,8 @@ pub:
is_pub bool is_pub bool
mod string mod string
ctdefine string // compile time define. myflag, when [if myflag] tag ctdefine string // compile time define. myflag, when [if myflag] tag
pub mut:
name string
} }
pub struct Arg { pub struct Arg {
@ -486,7 +487,7 @@ pub fn (t &Table) check(got, expected Type) bool {
exp_type_sym := t.get_type_symbol(expected) exp_type_sym := t.get_type_symbol(expected)
// //
if exp_type_sym.kind == .interface_ { if exp_type_sym.kind == .interface_ {
info := exp_type_sym.info as Interface mut info := exp_type_sym.info as Interface
// println('gen_types before') // println('gen_types before')
// println(info.gen_types) // println(info.gen_types)
info.gen_types << got_type_sym.name info.gen_types << got_type_sym.name