checker: check array and fields mutability
parent
ce1215f507
commit
682838a0cf
|
@ -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() {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()}'
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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| }
|
|
@ -0,0 +1,10 @@
|
||||||
|
struct A {
|
||||||
|
i []int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut a := A{
|
||||||
|
i: [0]
|
||||||
|
}
|
||||||
|
a.i[0] = 3
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
struct A {
|
||||||
|
i []int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut a := A{
|
||||||
|
i: [0]
|
||||||
|
}
|
||||||
|
a.i[0] = 3
|
||||||
|
}
|
|
@ -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| }
|
|
@ -0,0 +1,15 @@
|
||||||
|
struct A {
|
||||||
|
mut:
|
||||||
|
i []int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
a A
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut b := B{
|
||||||
|
a: A{}
|
||||||
|
}
|
||||||
|
b.a.i << 3
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
struct A {
|
||||||
|
mut:
|
||||||
|
i []int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
a A
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut b := B{
|
||||||
|
a: A{}
|
||||||
|
}
|
||||||
|
b.a.i << 3
|
||||||
|
}
|
|
@ -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| }
|
|
@ -0,0 +1,9 @@
|
||||||
|
struct A {
|
||||||
|
pub mut:
|
||||||
|
i []int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := A{}
|
||||||
|
a.i[0] += 3
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
struct A {
|
||||||
|
pub mut:
|
||||||
|
i []int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := A{}
|
||||||
|
a.i[0] += 3
|
||||||
|
}
|
|
@ -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| }
|
|
@ -0,0 +1,9 @@
|
||||||
|
struct A {
|
||||||
|
pub mut:
|
||||||
|
i []int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := []A{}
|
||||||
|
a[0].i << 3
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
struct A {
|
||||||
|
pub mut:
|
||||||
|
i []int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := []A{}
|
||||||
|
a[0].i << 3
|
||||||
|
}
|
|
@ -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| }
|
|
@ -0,0 +1,4 @@
|
||||||
|
fn main() {
|
||||||
|
a := [1, 2]
|
||||||
|
a << 3
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
fn main() {
|
||||||
|
a := [1, 2]
|
||||||
|
a << 3
|
||||||
|
}
|
|
@ -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| }
|
|
@ -0,0 +1,9 @@
|
||||||
|
struct A {
|
||||||
|
pub:
|
||||||
|
i1 int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := A{1}
|
||||||
|
a.i1 = 2
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
struct A {
|
||||||
|
pub:
|
||||||
|
i1 int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := A{1}
|
||||||
|
a.i1 = 2
|
||||||
|
}
|
|
@ -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| }
|
|
@ -0,0 +1,4 @@
|
||||||
|
fn main() {
|
||||||
|
a := 1
|
||||||
|
a = 2
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
fn main() {
|
||||||
|
a := 1
|
||||||
|
a = 2
|
||||||
|
}
|
|
@ -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| }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
l := []int{}
|
mut l := []int{}
|
||||||
l << 'test'
|
l << 'test'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
l := []int{}
|
mut l := []int{}
|
||||||
l << 'test'
|
l << 'test'
|
||||||
}
|
}
|
||||||
|
|
|
@ -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| }
|
|
@ -0,0 +1,10 @@
|
||||||
|
struct A {
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := A{
|
||||||
|
i: 1
|
||||||
|
}
|
||||||
|
a.i = 2
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
struct A {
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := A{
|
||||||
|
i: 1
|
||||||
|
}
|
||||||
|
a.i = 2
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"'
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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')
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`')
|
||||||
|
}
|
||||||
|
|
|
@ -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 = ''
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue