diff --git a/.github/workflows/sdl_ci.yml b/.github/workflows/sdl_ci.yml
index aadad3af22..f7f14ef780 100644
--- a/.github/workflows/sdl_ci.yml
+++ b/.github/workflows/sdl_ci.yml
@@ -40,6 +40,7 @@ jobs:
- name: Build sdl examples
run: |
+ v shader sdl/examples/sdl_opengl_and_sokol
for example in sdl/examples/*; do
echo "v $example"
v "$example";
diff --git a/README.md b/README.md
index 0689012a35..2bea39eac9 100644
--- a/README.md
+++ b/README.md
@@ -60,23 +60,40 @@ Unlike many other languages, V is not going to be always changing, with new feat
being introduced and old features modified. It is always going to be a small and simple
language, very similar to the way it is right now.
-## Installing V from source
+## Installing V - from source *(preferred method)*
-### Linux, macOS, Windows, *BSD, Solaris, WSL, Android, Raspbian
+### Linux, macOS, Windows, *BSD, Solaris, WSL, Android, etc.
+Usually installing V is quite simple if you have an environment that already has a
+functional `git` installation.
+
+* *(* ***PLEASE NOTE:*** *If you run into any trouble or you have a different operating
+system or Linux distribution that doesn't install or work immediately, please see
+[Installation Issues](https://github.com/vlang/v/discussions/categories/installation-issues)
+and search for your OS and problem. If you can't find your problem, please add it to an
+existing discussion if one exists for your OS, or create a new one if a main discussion
+doesn't yet exist for your OS.)*
+
+
+To get started, simply try to execute the following in your terminal/shell:
```bash
git clone https://github.com/vlang/v
cd v
make
+# HINT: Using Windows?: run make.bat in the cmd.exe shell
```
-That's it! Now you have a V executable at `[path to V repo]/v`.
+That should be it and you should find your V executable at `[path to V repo]/v`.
`[path to V repo]` can be anywhere.
-(On Windows `make` means running `make.bat`, so make sure you use `cmd.exe`)
+(As in the hint above, on Windows `make` means running `make.bat`, so make sure you use
+the `cmd.exe` terminal.)
Now you can try `./v run examples/hello_world.v` (`v.exe` on Windows).
+* *Trouble? Please see the note above and link to
+[Installation Issues](https://github.com/vlang/v/discussions/categories/installation-issues) for help.*
+
V is constantly being updated. To update V, simply run:
```bash
diff --git a/cmd/tools/vbug.v b/cmd/tools/vbug.v
index 4d91cb6e6c..f5cd708452 100644
--- a/cmd/tools/vbug.v
+++ b/cmd/tools/vbug.v
@@ -118,7 +118,7 @@ fn main() {
build_output := get_v_build_output(is_verbose, is_yes, file_path)
// ask the user if he wants to submit even after an error
if !is_yes && (vdoctor_output == '' || file_content == '' || build_output == '') {
- confirm_or_exit('An error occured retrieving the information, do you want to continue?')
+ confirm_or_exit('An error occurred retrieving the information, do you want to continue?')
}
expected_result := readline.read_line('What did you expect to see? ') or {
diff --git a/cmd/tools/vfmt.v b/cmd/tools/vfmt.v
index c8cecaf07b..7412bfd21b 100644
--- a/cmd/tools/vfmt.v
+++ b/cmd/tools/vfmt.v
@@ -27,6 +27,8 @@ struct FormatOptions {
is_verify bool // exit(1) if the file is not vfmt'ed
is_worker bool // true *only* in the worker processes. Note: workers can crash.
is_backup bool // make a `file.v.bak` copy *before* overwriting a `file.v` in place with `-w`
+mut:
+ diff_cmd string // filled in when -diff or -verify is passed
}
const (
@@ -201,36 +203,23 @@ fn print_compiler_options(compiler_params &pref.Preferences) {
eprintln(' is_script: $compiler_params.is_script ')
}
-fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path string) ? {
+fn (mut foptions FormatOptions) find_diff_cmd() string {
+ if foptions.diff_cmd != '' {
+ return foptions.diff_cmd
+ }
+ if foptions.is_verify || foptions.is_diff {
+ foptions.diff_cmd = diff.find_working_diff_command() or {
+ eprintln(err)
+ exit(1)
+ }
+ }
+ return foptions.diff_cmd
+}
+
+fn (mut foptions FormatOptions) post_process_file(file string, formatted_file_path string) ? {
if formatted_file_path.len == 0 {
return
}
- if foptions.is_diff {
- diff_cmd := diff.find_working_diff_command() or {
- eprintln(err)
- return
- }
- if foptions.is_verbose {
- eprintln('Using diff command: $diff_cmd')
- }
- diff := diff.color_compare_files(diff_cmd, file, formatted_file_path)
- if diff.len > 0 {
- println(diff)
- }
- return
- }
- if foptions.is_verify {
- diff_cmd := diff.find_working_diff_command() or {
- eprintln(err)
- return
- }
- x := diff.color_compare_files(diff_cmd, file, formatted_file_path)
- if x.len != 0 {
- println("$file is not vfmt'ed")
- return error('')
- }
- return
- }
fc := os.read_file(file) or {
eprintln('File $file could not be read')
return
@@ -240,6 +229,31 @@ fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path
return
}
is_formatted_different := fc != formatted_fc
+ if foptions.is_diff {
+ if !is_formatted_different {
+ return
+ }
+ diff_cmd := foptions.find_diff_cmd()
+ if foptions.is_verbose {
+ eprintln('Using diff command: $diff_cmd')
+ }
+ diff := diff.color_compare_files(diff_cmd, file, formatted_file_path)
+ if diff.len > 0 {
+ println(diff)
+ }
+ return
+ }
+ if foptions.is_verify {
+ if !is_formatted_different {
+ return
+ }
+ x := diff.color_compare_files(foptions.find_diff_cmd(), file, formatted_file_path)
+ if x.len != 0 {
+ println("$file is not vfmt'ed")
+ return error('')
+ }
+ return
+ }
if foptions.is_c {
if is_formatted_different {
eprintln('File is not formatted: $file')
diff --git a/cmd/tools/vgret.v b/cmd/tools/vgret.v
index c74f8e955c..b9f575ec62 100644
--- a/cmd/tools/vgret.v
+++ b/cmd/tools/vgret.v
@@ -354,7 +354,11 @@ fn vexe() string {
}
fn new_config(root_path string, toml_config string) ?Config {
- doc := toml.parse(toml_config) ?
+ doc := if os.is_file(toml_config) {
+ toml.parse_file(toml_config) ?
+ } else {
+ toml.parse_text(toml_config) ?
+ }
path := os.real_path(root_path).trim_right('/')
diff --git a/cmd/tools/vrepl.v b/cmd/tools/vrepl.v
index f3a5571138..bbbd7bcb38 100644
--- a/cmd/tools/vrepl.v
+++ b/cmd/tools/vrepl.v
@@ -208,11 +208,21 @@ fn (mut r Repl) parse_import(line string) {
}
}
+fn highlight_console_command(command string) string {
+ return term.bright_white(term.bright_bg_black(' $command '))
+}
+
+fn highlight_repl_command(command string) string {
+ return term.bright_white(term.bg_blue(' $command '))
+}
+
fn print_welcome_screen() {
- cmd_exit := term.highlight_command('exit')
- cmd_help := term.highlight_command('v help')
- file_main := term.highlight_command('main.v')
- cmd_run := term.highlight_command('v run main.v')
+ cmd_exit := highlight_repl_command('exit')
+ cmd_list := highlight_repl_command('list')
+ cmd_help := highlight_repl_command('help')
+ cmd_v_help := highlight_console_command('v help')
+ cmd_v_run := highlight_console_command('v run main.v')
+ file_main := highlight_console_command('main.v')
vbar := term.bright_green('|')
width, _ := term.get_terminal_size() // get the size of the terminal
vlogo := [
@@ -224,11 +234,11 @@ fn print_welcome_screen() {
term.bright_blue(r' \__/ '),
]
help_text := [
- 'Welcome to the V REPL (for help with V itself, type $cmd_exit, then run $cmd_help).',
+ 'Welcome to the V REPL (for help with V itself, type $cmd_exit, then run $cmd_v_help).',
'Note: the REPL is highly experimental. For best V experience, use a text editor, ',
- 'save your code in a $file_main file and execute: $cmd_run',
- version.full_v_version(false),
- 'Use Ctrl-C or ${term.highlight_command('exit')} to exit, or ${term.highlight_command('help')} to see other available commands',
+ 'save your code in a $file_main file and execute: $cmd_v_run',
+ '${version.full_v_version(false)} . Use $cmd_list to see the accumulated program so far.',
+ 'Use Ctrl-C or $cmd_exit to exit, or $cmd_help to see other available commands.',
]
if width >= 97 {
eprintln('${vlogo[0]}')
@@ -480,6 +490,9 @@ fn main() {
println(' ... where vexepath is the full path to the v executable file')
return
}
+ if !is_stdin_a_pipe {
+ os.setenv('VCOLORS', 'always', true)
+ }
run_repl(replfolder, replprefix)
}
diff --git a/cmd/v/help/build-c.txt b/cmd/v/help/build-c.txt
index 11c7504037..8627eb48d7 100644
--- a/cmd/v/help/build-c.txt
+++ b/cmd/v/help/build-c.txt
@@ -25,7 +25,7 @@ see also `v help build`.
-cstrict
Turn on additional C warnings. This slows down compilation
- slightly (~10-10% for gcc), but sometimes provides better diagnosis.
+ slightly (~10% for gcc), but sometimes provides better diagnosis.
-showcc
Prints the C command that is used to build the program.
@@ -97,7 +97,8 @@ see also `v help build`.
systems also (although we do not test it as regularly as for the above):
`android`, `ios`,
`freebsd`, `openbsd`, `netbsd`, `dragonfly`,
- `solaris`, `serenity`, `haiku`, `vinix`
+ `solaris`, `serenity`, `haiku`, `vinix`,
+ `wasm32`, `wasm32-wasi`, `wasm32-emscripten`
Note that V has the concept of platform files, i.e. files ending
with `_platform.c.v`, and usually only the matching files are used in
diff --git a/doc/docs.md b/doc/docs.md
index 94133b2a7d..3629d8ff8f 100644
--- a/doc/docs.md
+++ b/doc/docs.md
@@ -624,6 +624,9 @@ println('[${int(x):010}]') // pad with zeros on the left => [0000000123]
println('[${int(x):b}]') // output as binary => [1111011]
println('[${int(x):o}]') // output as octal => [173]
println('[${int(x):X}]') // output as uppercase hex => [7B]
+
+println('[${10.0000:.2}]') // remove insignificant 0s at the end => [10]
+println('[${10.0000:.2f}]') // do show the 0s at the end, even though they do not change the number => [10.00]
```
### String operators
diff --git a/examples/toml.v b/examples/toml.v
index 303aa5ede1..a245cf3510 100644
--- a/examples/toml.v
+++ b/examples/toml.v
@@ -38,7 +38,7 @@ hosts = [
]'
fn main() {
- doc := toml.parse(toml_text) or { panic(err) }
+ doc := toml.parse_text(toml_text) or { panic(err) }
title := doc.value('title').string()
println('title: "$title"')
ip := doc.value('servers.alpha.ip').string()
diff --git a/examples/ttf_font/example_ttf.v b/examples/ttf_font/example_ttf.v
index d22ea84e62..2edb417f20 100644
--- a/examples/ttf_font/example_ttf.v
+++ b/examples/ttf_font/example_ttf.v
@@ -144,7 +144,7 @@ fn main() {
// TTF render 0 Frame counter
app.ttf_render << &ttf.TTF_render_Sokol{
bmp: &ttf.BitMap{
- tf: &(app.tf[0])
+ tf: &app.tf[0]
buf: unsafe { malloc_noscan(32000000) }
buf_size: (32000000)
color: 0xFF0000FF
@@ -155,7 +155,7 @@ fn main() {
// TTF render 1 Text Block
app.ttf_render << &ttf.TTF_render_Sokol{
bmp: &ttf.BitMap{
- tf: &(app.tf[1])
+ tf: &app.tf[1]
// color : 0xFF0000_10
// style: .raw
// use_font_metrics: true
@@ -164,7 +164,7 @@ fn main() {
// TTF mouse position render
app.ttf_render << &ttf.TTF_render_Sokol{
bmp: &ttf.BitMap{
- tf: &(app.tf[0])
+ tf: &app.tf[0]
}
}
// setup sokol_gfx
diff --git a/examples/vweb/vweb_assets/assets/index.css b/examples/vweb/vweb_assets/assets/index.css
index 4ad9eb895f..f5b7ea6dd5 100644
--- a/examples/vweb/vweb_assets/assets/index.css
+++ b/examples/vweb/vweb_assets/assets/index.css
@@ -17,3 +17,9 @@ img.logo {
float: left;
width: 10em;
}
+
+html {
+ display:block;
+}
+
+
diff --git a/examples/vweb/vweb_assets/index.html b/examples/vweb/vweb_assets/index.html
index 6635329ad0..668bdb3d49 100644
--- a/examples/vweb/vweb_assets/index.html
+++ b/examples/vweb/vweb_assets/index.html
@@ -1,6 +1,7 @@
@title
+
@css 'index.css'
diff --git a/examples/vweb/vweb_assets/vweb_assets.v b/examples/vweb/vweb_assets/vweb_assets.v
index 058f45922b..1033f7ea8f 100644
--- a/examples/vweb/vweb_assets/vweb_assets.v
+++ b/examples/vweb/vweb_assets/vweb_assets.v
@@ -1,5 +1,6 @@
module main
+import os
import vweb
// import vweb.assets
import time
@@ -16,6 +17,7 @@ fn main() {
mut app := &App{}
app.serve_static('/favicon.ico', 'favicon.ico')
// Automatically make available known static mime types found in given directory.
+ os.chdir(os.dir(os.executable())) ?
app.handle_static('assets', true)
vweb.run(app, port)
}
diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v
index 74f942cfc0..e29e50d478 100644
--- a/vlib/builtin/array.v
+++ b/vlib/builtin/array.v
@@ -124,7 +124,7 @@ fn (mut a array) ensure_cap(required int) {
cap *= 2
}
new_size := cap * a.element_size
- new_data := vcalloc(new_size)
+ new_data := unsafe { malloc(new_size) }
if a.data != voidptr(0) {
unsafe { vmemcpy(new_data, a.data, a.len * a.element_size) }
// TODO: the old data may be leaked when no GC is used (ref-counting?)
@@ -402,12 +402,13 @@ pub fn (mut a array) pop() voidptr {
a.len = new_len
// Note: a.cap is not changed here *on purpose*, so that
// further << ops on that array will be more efficient.
- return unsafe { memdup(last_elem, a.element_size) }
+ return last_elem
}
// delete_last efficiently deletes the last element of the array.
// It does it simply by reducing the length of the array by 1.
// If the array is empty, this will panic.
+// See also: [trim](#array.trim)
pub fn (mut a array) delete_last() {
// copy pasting code for performance
$if !no_bounds_checking ? {
diff --git a/vlib/builtin/string_interpolation.v b/vlib/builtin/string_interpolation.v
index b71e380c2f..ec396e2664 100644
--- a/vlib/builtin/string_interpolation.v
+++ b/vlib/builtin/string_interpolation.v
@@ -660,7 +660,7 @@ pub fn str_intp(data_len int, in_data voidptr) string {
mut res := strings.new_builder(256)
input_base := &StrIntpData(in_data)
for i := 0; i < data_len; i++ {
- data := unsafe { &(input_base[i]) }
+ data := unsafe { &input_base[i] }
// avoid empty strings
if data.str.len != 0 {
res.write_string(data.str)
diff --git a/vlib/builtin/wasm_bare/libc_impl.v b/vlib/builtin/wasm_bare/libc_impl.v
index bd94945fbd..26f17bd156 100644
--- a/vlib/builtin/wasm_bare/libc_impl.v
+++ b/vlib/builtin/wasm_bare/libc_impl.v
@@ -5,7 +5,7 @@ module builtin
[unsafe]
pub fn __malloc(size usize) voidptr {
unsafe {
- return malloc(int(size))
+ return C.malloc(int(size))
}
}
diff --git a/vlib/rand/rand.v b/vlib/rand/rand.v
index 944776ad79..bf2abc0641 100644
--- a/vlib/rand/rand.v
+++ b/vlib/rand/rand.v
@@ -467,3 +467,20 @@ pub fn hex(len int) string {
pub fn ascii(len int) string {
return string_from_set(rand.ascii_chars, len)
}
+
+// shuffle randomly permutates the elements in `a`.
+pub fn shuffle(mut a []T) {
+ len := a.len
+ for i in 0 .. len {
+ si := i + intn(len - i) or { len }
+ a[si], a[i] = a[i], a[si]
+ }
+}
+
+// shuffle_clone returns a random permutation of the elements in `a`.
+// The permutation is done on a fresh clone of `a`, so `a` remains unchanged.
+pub fn shuffle_clone(a []T) []T {
+ mut res := a.clone()
+ shuffle(mut res)
+ return res
+}
diff --git a/vlib/rand/random_numbers_test.v b/vlib/rand/random_numbers_test.v
index f5f1d61697..e2735339c5 100644
--- a/vlib/rand/random_numbers_test.v
+++ b/vlib/rand/random_numbers_test.v
@@ -317,3 +317,55 @@ fn test_new_global_rng() {
rand.set_rng(old)
}
+
+fn test_shuffle() {
+ mut arrays := [][]int{}
+ arrays << [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ arrays << [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
+ for seed in seeds {
+ a := get_n_random_ints(seed, 10)
+ arrays << a
+ }
+ //
+ mut digits := []map[int]int{len: 10}
+ for digit in 0 .. 10 {
+ digits[digit] = {}
+ for idx in 0 .. 10 {
+ digits[digit][idx] = 0
+ }
+ }
+ for mut a in arrays {
+ o := a.clone()
+ for _ in 0 .. 100 {
+ rand.shuffle(mut a)
+ assert *a != o
+ for idx in 0 .. 10 {
+ digits[idx][a[idx]]++
+ }
+ }
+ }
+ for digit in 1 .. 10 {
+ assert digits[0] != digits[digit]
+ }
+ for digit in 0 .. 10 {
+ for idx in 0 .. 10 {
+ assert digits[digit][idx] > 10
+ }
+ // eprintln('digits[$digit]: ${digits[digit]}')
+ }
+}
+
+fn test_shuffle_clone() {
+ original := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ mut a := original.clone()
+ mut results := [][]int{}
+ for _ in 0 .. 10 {
+ results << rand.shuffle_clone(a)
+ }
+ assert original == a
+ for idx in 1 .. 10 {
+ assert results[idx].len == 10
+ assert results[idx] != results[0]
+ assert results[idx] != original
+ }
+}
diff --git a/vlib/strconv/format_mem.c.v b/vlib/strconv/format_mem.c.v
index c2229d534d..6ef5762d03 100644
--- a/vlib/strconv/format_mem.c.v
+++ b/vlib/strconv/format_mem.c.v
@@ -158,29 +158,36 @@ pub fn f64_to_str_lnd1(f f64, dec_digit int) string {
// get sign and decimal parts
for c in s {
- if c == `-` {
- sgn = -1
- i++
- } else if c == `+` {
- sgn = 1
- i++
- } else if c >= `0` && c <= `9` {
- b[i1] = c
- i1++
- i++
- } else if c == `.` {
- if sgn > 0 {
- d_pos = i
- } else {
- d_pos = i - 1
+ match c {
+ `-` {
+ sgn = -1
+ i++
+ }
+ `+` {
+ sgn = 1
+ i++
+ }
+ `0`...`9` {
+ b[i1] = c
+ i1++
+ i++
+ }
+ `.` {
+ if sgn > 0 {
+ d_pos = i
+ } else {
+ d_pos = i - 1
+ }
+ i++
+ }
+ `e` {
+ i++
+ break
+ }
+ else {
+ s.free()
+ return '[Float conversion error!!]'
}
- i++
- } else if c == `e` {
- i++
- break
- } else {
- s.free()
- return '[Float conversion error!!]'
}
}
b[i1] = 0
@@ -274,10 +281,13 @@ pub fn f64_to_str_lnd1(f f64, dec_digit int) string {
// println("r_i-d_pos: ${r_i - d_pos}")
if dot_res_sp >= 0 {
- if (r_i - dot_res_sp) > dec_digit {
- r_i = dot_res_sp + dec_digit + 1
- }
+ r_i = dot_res_sp + dec_digit + 1
res[r_i] = 0
+ for c1 in 1 .. dec_digit + 1 {
+ if res[r_i - c1] == 0 {
+ res[r_i - c1] = `0`
+ }
+ }
// println("result: [${tos(&res[0],r_i)}]")
tmp_res := tos(res.data, r_i).clone()
res.free()
diff --git a/vlib/strings/builder.c.v b/vlib/strings/builder.c.v
index 5271d46c8c..2e7e7b1bb0 100644
--- a/vlib/strings/builder.c.v
+++ b/vlib/strings/builder.c.v
@@ -176,6 +176,28 @@ pub fn (mut b Builder) str() string {
return s
}
+// ensure_cap ensures that the buffer has enough space for at least `n` bytes by growing the buffer if necessary
+pub fn (mut b Builder) ensure_cap(n int) {
+ // code adapted from vlib/builtin/array.v
+ if n <= b.cap {
+ return
+ }
+
+ new_data := vcalloc(n * b.element_size)
+ if b.data != voidptr(0) {
+ unsafe { vmemcpy(new_data, b.data, b.len * b.element_size) }
+ // TODO: the old data may be leaked when no GC is used (ref-counting?)
+ if b.flags.has(.noslices) {
+ unsafe { free(b.data) }
+ }
+ }
+ unsafe {
+ b.data = new_data
+ b.offset = 0
+ b.cap = n
+ }
+}
+
// free is for manually freeing the contents of the buffer
[unsafe]
pub fn (mut b Builder) free() {
diff --git a/vlib/strings/builder_test.v b/vlib/strings/builder_test.v
index 1bcc54a058..151e1b989a 100644
--- a/vlib/strings/builder_test.v
+++ b/vlib/strings/builder_test.v
@@ -112,3 +112,18 @@ fn test_write_runes() {
x := sb.str()
assert x == 'hello world'
}
+
+fn test_ensure_cap() {
+ mut sb := strings.new_builder(0)
+ assert sb.cap == 0
+ sb.ensure_cap(10)
+ assert sb.cap == 10
+ sb.ensure_cap(10)
+ assert sb.cap == 10
+ sb.ensure_cap(15)
+ assert sb.cap == 15
+ sb.ensure_cap(10)
+ assert sb.cap == 15
+ sb.ensure_cap(-1)
+ assert sb.cap == 15
+}
diff --git a/vlib/sync/thread_default.c.v b/vlib/sync/thread_default.c.v
new file mode 100644
index 0000000000..81bcf105e8
--- /dev/null
+++ b/vlib/sync/thread_default.c.v
@@ -0,0 +1,15 @@
+module sync
+
+fn C.pthread_self() usize
+
+// thread_id returns a unique identifier for the caller thread.
+// All *currently* running threads in the same process, will have *different* thread identifiers.
+// Note: if a thread finishes, and another starts, the identifier of the old thread may be
+// reused for the newly started thread.
+// In other words, thread IDs are guaranteed to be unique only within a process.
+// A thread ID may be reused after a terminated thread has been joined (with `t.wait()`),
+// or when the thread has terminated.
+
+pub fn thread_id() u64 {
+ return u64(C.pthread_self())
+}
diff --git a/vlib/sync/thread_test.v b/vlib/sync/thread_test.v
new file mode 100644
index 0000000000..797d7e9ea6
--- /dev/null
+++ b/vlib/sync/thread_test.v
@@ -0,0 +1,22 @@
+import sync
+
+fn simple_thread() u64 {
+ tid := sync.thread_id()
+ eprintln('simple_thread thread_id: $tid.hex()')
+ return tid
+}
+
+fn test_sync_thread_id() {
+ mtid := sync.thread_id()
+ eprintln('main thread_id: $sync.thread_id().hex()')
+ x := go simple_thread()
+ y := go simple_thread()
+ xtid := x.wait()
+ ytid := y.wait()
+ eprintln('main thread_id: $sync.thread_id().hex()')
+ dump(xtid.hex())
+ dump(ytid.hex())
+ assert mtid != xtid
+ assert mtid != ytid
+ assert xtid != ytid
+}
diff --git a/vlib/sync/thread_windows.c.v b/vlib/sync/thread_windows.c.v
new file mode 100644
index 0000000000..33241d5a0f
--- /dev/null
+++ b/vlib/sync/thread_windows.c.v
@@ -0,0 +1,7 @@
+module sync
+
+fn C.GetCurrentThreadId() u32
+
+pub fn thread_id() u64 {
+ return u64(C.GetCurrentThreadId())
+}
diff --git a/vlib/toml/README.md b/vlib/toml/README.md
index cdcc475039..8fd07c838f 100644
--- a/vlib/toml/README.md
+++ b/vlib/toml/README.md
@@ -8,7 +8,8 @@ Parsing files or `string`s containing TOML is easy.
Simply import the `toml` module and do:
```v ignore
-doc := toml.parse() or { panic(err) }
+doc1 := toml.parse_text() or { panic(err) }
+doc2 := toml.parse_file() or { panic(err) }
```
## Example
@@ -54,7 +55,7 @@ hosts = [
]'
fn main() {
- doc := toml.parse(toml_text) or { panic(err) }
+ doc := toml.parse_text(toml_text) or { panic(err) }
title := doc.value('title').string()
println('title: "$title"')
ip := doc.value('servers.alpha.ip').string()
@@ -91,7 +92,7 @@ array = [
]
'
-doc := toml.parse(toml_text) or { panic(err) }
+doc := toml.parse_text(toml_text) or { panic(err) }
assert doc.value('val').bool() == true
assert doc.value('table.array[0].a').string() == 'A'
@@ -142,6 +143,6 @@ array = [
]
'
-doc := toml.parse(toml_text) or { panic(err) }
+doc := toml.parse_text(toml_text) or { panic(err) }
assert to.json(doc) == '{ "val": true, "table": { "array": [ { "a": "A" }, { "b": "B" } ] } }'
```
diff --git a/vlib/toml/checker/checker.v b/vlib/toml/checker/checker.v
index 7c25c12ec7..0f22274bcf 100644
--- a/vlib/toml/checker/checker.v
+++ b/vlib/toml/checker/checker.v
@@ -415,7 +415,7 @@ pub fn (c Checker) check_quoted(q ast.Quoted) ? {
// \UXXXXXXXX - Unicode (U+XXXXXXXX)
fn (c Checker) check_quoted_escapes(q ast.Quoted) ? {
// Setup a scanner in stack memory for easier navigation.
- mut s := scanner.new_simple(q.text) ?
+ mut s := scanner.new_simple_text(q.text) ?
// See https://toml.io/en/v1.0.0#string for more info on string types.
is_basic := q.quote == `\"`
@@ -552,7 +552,7 @@ fn (c Checker) check_unicode_escape(esc_unicode string) ? {
pub fn (c Checker) check_comment(comment ast.Comment) ? {
lit := comment.text
// Setup a scanner in stack memory for easier navigation.
- mut s := scanner.new_simple(lit) ?
+ mut s := scanner.new_simple_text(lit) ?
for {
ch := s.next()
if ch == scanner.end_of_text {
diff --git a/vlib/toml/decoder/decoder.v b/vlib/toml/decoder/decoder.v
index cfe19e33b4..2a5e47b166 100644
--- a/vlib/toml/decoder/decoder.v
+++ b/vlib/toml/decoder/decoder.v
@@ -84,7 +84,7 @@ pub fn decode_quoted_escapes(mut q ast.Quoted) ? {
return
}
- mut s := scanner.new_simple(q.text) ?
+ mut s := scanner.new_simple_text(q.text) ?
q.text = q.text.replace('\\"', '"')
for {
diff --git a/vlib/toml/input/input.v b/vlib/toml/input/input.v
index c2490c0cce..cdce8227c1 100644
--- a/vlib/toml/input/input.v
+++ b/vlib/toml/input/input.v
@@ -15,6 +15,10 @@ pub:
// auto_config returns an, automatic determined, input Config based on heuristics
// found in `toml`
+// One example of several of why it's deprecated:
+// https://discord.com/channels/592103645835821068/592114487759470596/954101934988615721
+[deprecated: 'will be removed and not replaced due to flaky heuristics that leads to hard to find bugs']
+[deprecated_after: '2022-06-18']
pub fn auto_config(toml string) ?Config {
mut config := Config{}
if !toml.contains('\n') && os.is_file(toml) {
@@ -32,7 +36,7 @@ pub fn auto_config(toml string) ?Config {
// validate returns an optional error if more than one of the fields
// in `Config` has a non-default value (empty string).
-pub fn (c Config) validate() ? {
+fn (c Config) validate() ? {
if c.file_path != '' && c.text != '' {
error(@MOD + '.' + @FN +
' ${typeof(c).name} should contain only one of the fields `file_path` OR `text` filled out')
@@ -42,9 +46,12 @@ pub fn (c Config) validate() ? {
}
}
+// read_input returns either Config.text or the read file contents of Config.file_path
+// depending on which one is not empty.
pub fn (c Config) read_input() ?string {
+ c.validate() ?
mut text := c.text
- if os.is_file(c.file_path) {
+ if text == '' && os.is_file(c.file_path) {
text = os.read_file(c.file_path) or {
return error(@MOD + '.' + @STRUCT + '.' + @FN +
' Could not read "$c.file_path": "$err.msg()"')
diff --git a/vlib/toml/scanner/scanner.v b/vlib/toml/scanner/scanner.v
index 0bda8081f0..31bc4fdc3d 100644
--- a/vlib/toml/scanner/scanner.v
+++ b/vlib/toml/scanner/scanner.v
@@ -3,7 +3,6 @@
// that can be found in the LICENSE file.
module scanner
-import os
import math
import toml.input
import toml.token
@@ -47,28 +46,47 @@ pub:
tokenize_formatting bool = true // if true, generate tokens for `\n`, ` `, `\t`, `\r` etc.
}
-// new_scanner returns a new *heap* allocated `Scanner` instance.
+// new_scanner returns a new *heap* allocated `Scanner` instance, based on the file in config.input.file_path,
+// or based on the text in config.input.text .
pub fn new_scanner(config Config) ?&Scanner {
- config.input.validate() ?
- mut text := config.input.text
- file_path := config.input.file_path
- if os.is_file(file_path) {
- text = os.read_file(file_path) or {
- return error(@MOD + '.' + @STRUCT + '.' + @FN +
- ' Could not read "$file_path": "$err.msg()"')
- }
- }
mut s := &Scanner{
config: config
- text: text
+ text: config.input.read_input() ?
}
return s
}
-// returns a new *stack* allocated `Scanner` instance.
-pub fn new_simple(toml_input string) ?Scanner {
+// new_simple returns a new *stack* allocated `Scanner` instance.
+pub fn new_simple(config Config) ?Scanner {
+ return Scanner{
+ config: config
+ text: config.input.read_input() ?
+ }
+}
+
+// new_simple_text returns a new *stack* allocated `Scanner` instance
+// ready for parsing TOML in `text`.
+pub fn new_simple_text(text string) ?Scanner {
+ in_config := input.Config{
+ text: text
+ }
config := Config{
- input: input.auto_config(toml_input) ?
+ input: in_config
+ }
+ return Scanner{
+ config: config
+ text: config.input.read_input() ?
+ }
+}
+
+// new_simple_file returns a new *stack* allocated `Scanner` instance
+// ready for parsing TOML in file read from `path`.
+pub fn new_simple_file(path string) ?Scanner {
+ in_config := input.Config{
+ file_path: path
+ }
+ config := Config{
+ input: in_config
}
return Scanner{
config: config
diff --git a/vlib/toml/tests/array_of_tables_1_level_test.v b/vlib/toml/tests/array_of_tables_1_level_test.v
index 5a0495d4ab..971db6fb3e 100644
--- a/vlib/toml/tests/array_of_tables_1_level_test.v
+++ b/vlib/toml/tests/array_of_tables_1_level_test.v
@@ -18,7 +18,7 @@ color = "gray"'
)
fn test_tables() {
- mut toml_doc := toml.parse(toml_table_text) or { panic(err) }
+ mut toml_doc := toml.parse_text(toml_table_text) or { panic(err) }
toml_json := to.json(toml_doc)
diff --git a/vlib/toml/tests/array_of_tables_2_level_test.v b/vlib/toml/tests/array_of_tables_2_level_test.v
index ae73b8cfe9..4ad2e94837 100644
--- a/vlib/toml/tests/array_of_tables_2_level_test.v
+++ b/vlib/toml/tests/array_of_tables_2_level_test.v
@@ -22,13 +22,13 @@ name = "Born in the USA"
name = "Dancing in the Dark"'
)
-fn test_nested_array_of_tables() {
- mut toml_doc := toml.parse(toml_text) or { panic(err) }
+const fprefix = os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))
+
+fn test_nested_array_of_tables() ? {
+ mut toml_doc := toml.parse_text(toml_text) ?
toml_json := to.json(toml_doc)
-
eprintln(toml_json)
- assert toml_json == os.read_file(
- os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
- '.out') or { panic(err) }
+
+ assert toml_json == os.read_file(fprefix + '.out') ?
}
diff --git a/vlib/toml/tests/array_of_tables_array_test.v b/vlib/toml/tests/array_of_tables_array_test.v
index 5a005c9884..32671dc000 100644
--- a/vlib/toml/tests/array_of_tables_array_test.v
+++ b/vlib/toml/tests/array_of_tables_array_test.v
@@ -14,7 +14,7 @@ const (
)
fn test_nested_array_of_tables() {
- mut toml_doc := toml.parse(toml_text) or { panic(err) }
+ mut toml_doc := toml.parse_text(toml_text) or { panic(err) }
toml_json := to.json(toml_doc)
diff --git a/vlib/toml/tests/array_of_tables_edge_case_1_test.v b/vlib/toml/tests/array_of_tables_edge_case_1_test.v
index f4f18dac29..92e5e66235 100644
--- a/vlib/toml/tests/array_of_tables_edge_case_1_test.v
+++ b/vlib/toml/tests/array_of_tables_edge_case_1_test.v
@@ -6,7 +6,7 @@ fn test_array_of_tables_edge_case_file() {
toml_file :=
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
'.toml'
- toml_doc := toml.parse(toml_file) or { panic(err) }
+ toml_doc := toml.parse_file(toml_file) or { panic(err) }
toml_json := to.json(toml_doc)
out_file :=
diff --git a/vlib/toml/tests/array_of_tables_edge_case_2_test.v b/vlib/toml/tests/array_of_tables_edge_case_2_test.v
index f4f18dac29..0c30e8bfc9 100644
--- a/vlib/toml/tests/array_of_tables_edge_case_2_test.v
+++ b/vlib/toml/tests/array_of_tables_edge_case_2_test.v
@@ -2,17 +2,14 @@ import os
import toml
import toml.to
-fn test_array_of_tables_edge_case_file() {
- toml_file :=
- os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
- '.toml'
- toml_doc := toml.parse(toml_file) or { panic(err) }
+const fprefix = os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))
+
+fn test_array_of_tables_edge_case_file() ? {
+ toml_doc := toml.parse_file(os.real_path(fprefix + '.toml')) ?
toml_json := to.json(toml_doc)
- out_file :=
- os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
- '.out'
- out_file_json := os.read_file(out_file) or { panic(err) }
+
+ out_file_json := os.read_file(os.real_path(fprefix + '.out')) ?
println(toml_json)
assert toml_json == out_file_json
}
diff --git a/vlib/toml/tests/compact_test.v b/vlib/toml/tests/compact_test.v
index c9ebc5b7f1..d662749261 100644
--- a/vlib/toml/tests/compact_test.v
+++ b/vlib/toml/tests/compact_test.v
@@ -29,7 +29,7 @@ hosts = [
]'
fn test_parse_compact_text() {
- toml_doc := toml.parse(toml_text) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text) or { panic(err) }
title := toml_doc.value('title')
assert title == toml.Any('TOML Example')
diff --git a/vlib/toml/tests/crlf_test.v b/vlib/toml/tests/crlf_test.v
index ac95c8f9d3..07960c9385 100644
--- a/vlib/toml/tests/crlf_test.v
+++ b/vlib/toml/tests/crlf_test.v
@@ -4,7 +4,7 @@ fn test_crlf() {
str_value := 'test string'
mut toml_txt := 'crlf_string = "test string"\r\n
# Comment with CRLF is not allowed'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
value := toml_doc.value('crlf_string')
assert value == toml.Any(str_value)
@@ -16,7 +16,7 @@ fn test_crlf_is_parsable_just_like_lf() ? {
crlf_content := '# a comment\r\ntitle = "TOML Example"\r\n[database]\r\nserver = "192.168.1.1"\r\nports = [ 8000, 8001, 8002 ]\r\n'
all := [crlf_content, crlf_content.replace('\r\n', '\n')]
for content in all {
- res := toml.parse(content) ?
+ res := toml.parse_text(content) ?
assert res.value('title') == toml.Any('TOML Example')
assert (res.value('database') as map[string]toml.Any)['server'] ? == toml.Any('192.168.1.1')
}
diff --git a/vlib/toml/tests/datetime_test.v b/vlib/toml/tests/datetime_test.v
index 61d0269e06..4d656566c3 100644
--- a/vlib/toml/tests/datetime_test.v
+++ b/vlib/toml/tests/datetime_test.v
@@ -16,7 +16,7 @@ fn test_dates() {
lt1 = 07:32:00
lt2 = 00:32:00.999999
'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
// Re-use vars
mut odt_time := toml.DateTime{'1979-05-27T07:32:00Z'}
diff --git a/vlib/toml/tests/default_to_test.v b/vlib/toml/tests/default_to_test.v
index 2bf2283f43..d4634d1925 100644
--- a/vlib/toml/tests/default_to_test.v
+++ b/vlib/toml/tests/default_to_test.v
@@ -3,7 +3,7 @@ import toml
fn test_default_to() {
default_value := 4321
mut toml_txt := 'var = 1234'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
value := toml_doc.value('tar').default_to(default_value).int()
assert value == default_value
}
diff --git a/vlib/toml/tests/json_encoding_test.v b/vlib/toml/tests/json_encoding_test.v
index bc58383c74..9368820348 100644
--- a/vlib/toml/tests/json_encoding_test.v
+++ b/vlib/toml/tests/json_encoding_test.v
@@ -2,17 +2,14 @@ import os
import toml
import toml.to
-fn test_parse() {
- toml_file :=
- os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
- '.toml'
- toml_doc := toml.parse(toml_file) or { panic(err) }
+const fprefix = os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))
+
+fn test_parse() ? {
+ toml_doc := toml.parse_file(os.real_path(fprefix + '.toml')) ?
toml_json := to.json(toml_doc)
- out_file :=
- os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
- '.out'
- out_file_json := os.read_file(out_file) or { panic(err) }
println(toml_json)
+
+ out_file_json := os.read_file(os.real_path(fprefix + '.out')) ?
assert toml_json == out_file_json
}
diff --git a/vlib/toml/tests/json_test.v b/vlib/toml/tests/json_test.v
index bc58383c74..15b4190328 100644
--- a/vlib/toml/tests/json_test.v
+++ b/vlib/toml/tests/json_test.v
@@ -6,7 +6,7 @@ fn test_parse() {
toml_file :=
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
'.toml'
- toml_doc := toml.parse(toml_file) or { panic(err) }
+ toml_doc := toml.parse_file(toml_file) or { panic(err) }
toml_json := to.json(toml_doc)
out_file :=
diff --git a/vlib/toml/tests/key_test.v b/vlib/toml/tests/key_test.v
index d624c4d4fd..ceb3c8906b 100644
--- a/vlib/toml/tests/key_test.v
+++ b/vlib/toml/tests/key_test.v
@@ -6,7 +6,7 @@ fn test_keys() {
toml_file :=
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
'.toml'
- toml_doc := toml.parse(toml_file) or { panic(err) }
+ toml_doc := toml.parse_file(toml_file) or { panic(err) }
mut value := toml_doc.value('34-11')
assert value.int() == 23
diff --git a/vlib/toml/tests/large_toml_file_test.v b/vlib/toml/tests/large_toml_file_test.v
index 51daa01dc3..3bb4c30440 100644
--- a/vlib/toml/tests/large_toml_file_test.v
+++ b/vlib/toml/tests/large_toml_file_test.v
@@ -15,7 +15,7 @@ fn test_large_file() {
'.toml'
if os.exists(toml_file) {
println('Testing parsing of large (${os.file_size(toml_file)} bytes) "$toml_file"...')
- toml_doc := toml.parse(toml_file) or { panic(err) }
+ toml_doc := toml.parse_file(toml_file) or { panic(err) }
println('OK [1/1] "$toml_file"...') // So it can be checked with `v -stats test ...`
}
}
diff --git a/vlib/toml/tests/nested_test.v b/vlib/toml/tests/nested_test.v
index dd0390e415..82950ef6de 100644
--- a/vlib/toml/tests/nested_test.v
+++ b/vlib/toml/tests/nested_test.v
@@ -25,7 +25,7 @@ enabled = true
'
fn test_parse() {
- toml_doc := toml.parse(toml_text) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text) or { panic(err) }
// dump(toml_doc.ast)
// assert false
diff --git a/vlib/toml/tests/quoted_keys_test.v b/vlib/toml/tests/quoted_keys_test.v
index 506668eada..3625e6e2fc 100644
--- a/vlib/toml/tests/quoted_keys_test.v
+++ b/vlib/toml/tests/quoted_keys_test.v
@@ -3,7 +3,7 @@ import toml
fn test_quoted_keys() {
str_value := 'V rocks!'
toml_txt := 'a."b.c" = "V rocks!"'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
value := toml_doc.value('a."b.c"')
assert value == toml.Any(str_value)
diff --git a/vlib/toml/tests/reflect_test.v b/vlib/toml/tests/reflect_test.v
index d166a637dd..e2a1bd1562 100644
--- a/vlib/toml/tests/reflect_test.v
+++ b/vlib/toml/tests/reflect_test.v
@@ -50,7 +50,7 @@ mut:
}
fn test_reflect() {
- toml_doc := toml.parse(toml_text) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text) or { panic(err) }
mut user := toml_doc.reflect()
user.bio = toml_doc.value('bio').reflect()
diff --git a/vlib/toml/tests/spaced_keys_test.v b/vlib/toml/tests/spaced_keys_test.v
index f76efdf3d5..b9ea4f4685 100644
--- a/vlib/toml/tests/spaced_keys_test.v
+++ b/vlib/toml/tests/spaced_keys_test.v
@@ -12,7 +12,7 @@ fn test_spaced_keys() {
[ tube . test . "test.test" ]
h . "i.j." . "k" = "Cryptic"
'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
mut value := toml_doc.value('a."b.c"[0].d.e')
assert value == toml.Any(str_value)
assert value as string == str_value
diff --git a/vlib/toml/tests/strings_test.v b/vlib/toml/tests/strings_test.v
index a96b2e6574..76548de0ac 100644
--- a/vlib/toml/tests/strings_test.v
+++ b/vlib/toml/tests/strings_test.v
@@ -33,7 +33,7 @@ long = "\U000003B4"'
)
fn test_multiline_strings() {
- mut toml_doc := toml.parse(toml_multiline_text_1) or { panic(err) }
+ mut toml_doc := toml.parse_text(toml_multiline_text_1) or { panic(err) }
mut value := toml_doc.value('multi1')
assert value.string() == 'one'
@@ -44,7 +44,7 @@ fn test_multiline_strings() {
value = toml_doc.value('multi4')
assert value.string() == 'one\ntwo\nthree\nfour\n'
- toml_doc = toml.parse(toml_multiline_text_2) or { panic(err) }
+ toml_doc = toml.parse_text(toml_multiline_text_2) or { panic(err) }
value = toml_doc.value('multi1')
assert value.string() == 'one'
value = toml_doc.value('multi2')
@@ -57,7 +57,7 @@ fn test_multiline_strings() {
toml_file :=
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
'.toml'
- toml_doc = toml.parse(toml_file) or { panic(err) }
+ toml_doc = toml.parse_file(toml_file) or { panic(err) }
value = toml_doc.value('lit_one')
assert value.string() == "'one quote'"
value = toml_doc.value('lit_two')
@@ -69,7 +69,7 @@ fn test_multiline_strings() {
}
fn test_unicode_escapes() {
- mut toml_doc := toml.parse(toml_unicode_escapes) or { panic(err) }
+ mut toml_doc := toml.parse_text(toml_unicode_escapes) or { panic(err) }
mut value := toml_doc.value('short')
assert value.string() == '\u03B4' // <- This escape is handled by V
@@ -81,7 +81,7 @@ fn test_literal_strings() {
toml_file :=
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
'.toml'
- toml_doc := toml.parse(toml_file) or { panic(err) }
+ toml_doc := toml.parse_file(toml_file) or { panic(err) }
assert toml_doc.value('lit1').string() == r'\' // '\'
assert toml_doc.value('lit2').string() == r'\\' // '\\'
diff --git a/vlib/toml/tests/table_test.v b/vlib/toml/tests/table_test.v
index 43ade350b4..c10d6a1167 100644
--- a/vlib/toml/tests/table_test.v
+++ b/vlib/toml/tests/table_test.v
@@ -27,7 +27,7 @@ T = {a.b=2}'
)
fn test_tables() {
- mut toml_doc := toml.parse(toml_table_text) or { panic(err) }
+ mut toml_doc := toml.parse_text(toml_table_text) or { panic(err) }
mut value := toml_doc.value('inline.a.b')
assert value.int() == 42
diff --git a/vlib/toml/tests/toml_bom_test.v b/vlib/toml/tests/toml_bom_test.v
index fda33f63a1..9ccf1fc614 100644
--- a/vlib/toml/tests/toml_bom_test.v
+++ b/vlib/toml/tests/toml_bom_test.v
@@ -17,7 +17,7 @@ const (
)
fn test_toml_with_bom() {
- toml_doc := toml.parse(toml_text_with_utf8_bom) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text_with_utf8_bom) or { panic(err) }
toml_json := to.json(toml_doc)
title := toml_doc.value('title')
@@ -36,13 +36,13 @@ fn test_toml_with_bom() {
// Re-cycle bad_toml_doc
mut bad_toml_doc := empty_toml_document
- bad_toml_doc = toml.parse(toml_text_with_utf16_bom) or {
+ bad_toml_doc = toml.parse_text(toml_text_with_utf16_bom) or {
println(' $err.msg()')
assert true
empty_toml_document
}
- bad_toml_doc = toml.parse(toml_text_with_utf32_bom) or {
+ bad_toml_doc = toml.parse_text(toml_text_with_utf32_bom) or {
println(' $err.msg()')
assert true
empty_toml_document
diff --git a/vlib/toml/tests/toml_memory_corruption_test.v b/vlib/toml/tests/toml_memory_corruption_test.v
index 9e0c518e79..b54e9398bb 100644
--- a/vlib/toml/tests/toml_memory_corruption_test.v
+++ b/vlib/toml/tests/toml_memory_corruption_test.v
@@ -7,7 +7,7 @@ const toml_text = os.read_file(os.real_path(os.join_path(os.dir(@FILE), 'testdat
'.toml') or { panic(err) }
fn test_toml_known_memory_corruption() {
- toml_doc := toml.parse(toml_text) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text) or { panic(err) }
owner := toml_doc.value('owner') as map[string]toml.Any
any_name := owner.value('name')
@@ -34,7 +34,7 @@ fn test_toml_known_memory_corruption_2() {
lt1 = 07:32:00
lt2 = 00:32:00.999999
'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
// ldt1 test section
odt_time := toml.DateTime{'1979-05-27T07:32:00'}
diff --git a/vlib/toml/tests/toml_test.v b/vlib/toml/tests/toml_test.v
index 51f1dd4310..ceb08f475d 100644
--- a/vlib/toml/tests/toml_test.v
+++ b/vlib/toml/tests/toml_test.v
@@ -9,7 +9,7 @@ const toml_text = os.read_file(
fn test_toml() {
// File containing the complete text from the example in the official TOML project README.md:
// https://github.com/toml-lang/toml/blob/3b11f6921da7b6f5db37af039aa021fee450c091/README.md#Example
- toml_doc := toml.parse(toml_text) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text) or { panic(err) }
toml_json := to.json(toml_doc)
// NOTE Kept for easier debugging:
@@ -98,7 +98,7 @@ fn test_toml_parse_text() {
}
fn test_toml_parse() {
- toml_doc := toml.parse(toml_text) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text) or { panic(err) }
toml_json := to.json(toml_doc)
assert toml_json == os.read_file(
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
diff --git a/vlib/toml/tests/toml_types_test.v b/vlib/toml/tests/toml_types_test.v
index c870d6970d..2564299ac6 100644
--- a/vlib/toml/tests/toml_types_test.v
+++ b/vlib/toml/tests/toml_types_test.v
@@ -4,7 +4,7 @@ import strconv
fn test_string() {
str_value := 'test string'
toml_txt := 'string = "test string"'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
value := toml_doc.value('string')
assert value == toml.Any(str_value)
@@ -14,7 +14,7 @@ fn test_string() {
fn test_i64() {
toml_txt := 'i64 = 120'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
value := toml_doc.value('i64')
assert value == toml.Any(i64(120))
@@ -26,7 +26,7 @@ fn test_bool() {
toml_txt := '
bool_true = true
bool_false = false'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
value_true := toml_doc.value('bool_true')
assert value_true == toml.Any(true)
@@ -46,7 +46,7 @@ bool_false = false'
fn test_bool_key_is_not_value() {
toml_txt := 'true = true
false = false'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
value_true := toml_doc.value('true')
assert value_true == toml.Any(true)
@@ -64,7 +64,7 @@ false = false'
fn test_single_letter_key() {
toml_txt := '[v]
open_sourced = "Jun 22 2019 20:20:28"'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
value := toml_doc.value('v.open_sourced').string()
assert value == 'Jun 22 2019 20:20:28'
@@ -74,7 +74,7 @@ fn test_hex_values() {
// Regression test
// '0xb' is carefully chosen to include the 'b' character that also denotes binary via 0b prefix.
toml_txt := 'hex = 0xb'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
value := toml_doc.value('hex')
assert value as i64 == 11
@@ -85,7 +85,7 @@ fn test_comment_as_last_value() {
toml_txt := '
test = 42
# this line has comment as last thing'
- toml_doc := toml.parse(toml_txt) or { panic(err) }
+ toml_doc := toml.parse_text(toml_txt) or { panic(err) }
value := toml_doc.value('test')
assert value as i64 == 42
@@ -93,31 +93,31 @@ test = 42
}
fn test_nan_and_inf_values() {
- mut toml_doc := toml.parse('nan = nan') or { panic(err) }
+ mut toml_doc := toml.parse_text('nan = nan') or { panic(err) }
mut value := toml_doc.value('nan')
assert value.string() == 'nan'
- toml_doc = toml.parse('nan = nan#comment') or { panic(err) }
+ toml_doc = toml.parse_text('nan = nan#comment') or { panic(err) }
value = toml_doc.value('nan')
assert value.string() == 'nan'
- toml_doc = toml.parse('nan = -nan') or { panic(err) }
+ toml_doc = toml.parse_text('nan = -nan') or { panic(err) }
value = toml_doc.value('nan')
assert value.string() == 'nan'
- toml_doc = toml.parse('nan = +nan') or { panic(err) }
+ toml_doc = toml.parse_text('nan = +nan') or { panic(err) }
value = toml_doc.value('nan')
assert value.string() == 'nan'
- toml_doc = toml.parse('inf = inf') or { panic(err) }
+ toml_doc = toml.parse_text('inf = inf') or { panic(err) }
value = toml_doc.value('inf')
assert value.u64() == strconv.double_plus_infinity
- toml_doc = toml.parse('inf = +inf') or { panic(err) }
+ toml_doc = toml.parse_text('inf = +inf') or { panic(err) }
value = toml_doc.value('inf')
assert value.u64() == strconv.double_plus_infinity
- toml_doc = toml.parse('inf = -inf') or { panic(err) }
+ toml_doc = toml.parse_text('inf = -inf') or { panic(err) }
value = toml_doc.value('inf')
assert value.u64() == strconv.double_minus_infinity
}
diff --git a/vlib/toml/tests/value_query_test.v b/vlib/toml/tests/value_query_test.v
index 1c8312368a..b7d6d76bea 100644
--- a/vlib/toml/tests/value_query_test.v
+++ b/vlib/toml/tests/value_query_test.v
@@ -50,7 +50,7 @@ colors = [
)
fn test_value_query_in_array() {
- toml_doc := toml.parse(toml_text) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text) or { panic(err) }
mut value := toml_doc.value('themes[0].colors[1]').string()
assert value == 'black'
value = toml_doc.value('themes[1].colors[0]').string()
@@ -67,7 +67,7 @@ fn test_value_query_in_array() {
}
fn test_any_value_query() {
- toml_doc := toml.parse(toml_text) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text) or { panic(err) }
themes := toml_doc.value('themes')
assert themes.value('[0].colors[0]').string() == 'red'
@@ -94,7 +94,7 @@ fn test_any_value_query() {
}
fn test_inf_and_nan_query() {
- toml_doc := toml.parse(toml_text) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text) or { panic(err) }
value := toml_doc.value('values.nan').string()
assert value == 'nan'
@@ -106,7 +106,7 @@ fn test_inf_and_nan_query() {
}
fn test_any_value_query_2() {
- toml_doc := toml.parse(toml_text_2) or { panic(err) }
+ toml_doc := toml.parse_text(toml_text_2) or { panic(err) }
defaults := toml_doc.value('defaults')
assert defaults.value('run.flags[0]').string() == '-f 1'
assert defaults.value('env[0].RUN_TIME').int() == 5
diff --git a/vlib/toml/toml.v b/vlib/toml/toml.v
index 5259c2f149..43cd4eb4f5 100644
--- a/vlib/toml/toml.v
+++ b/vlib/toml/toml.v
@@ -108,6 +108,8 @@ pub fn parse_text(text string) ?Doc {
// parse parses the TOML document provided in `toml`.
// parse automatically try to determine if the type of `toml` is a file or text.
// For explicit parsing of input types see `parse_file` or `parse_text`.
+[deprecated: 'use parse_file or parse_text instead']
+[deprecated_after: '2022-06-18']
pub fn parse(toml string) ?Doc {
mut input_config := input.auto_config(toml) ?
scanner_config := scanner.Config{
diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v
index b0a97f7b6a..a983a61e28 100644
--- a/vlib/v/ast/ast.v
+++ b/vlib/v/ast/ast.v
@@ -557,6 +557,7 @@ pub mut:
name string // left.name()
is_method bool
is_field bool // temp hack, remove ASAP when re-impl CallExpr / Selector (joe)
+ is_fn_var bool // fn variable
is_keep_alive bool // GC must not free arguments before fn returns
is_noreturn bool // whether the function/method is marked as [noreturn]
is_ctor_new bool // if JS ctor calls requires `new` before call, marked as `[use_new]` in V
@@ -568,6 +569,7 @@ pub mut:
left_type Type // type of `user`
receiver_type Type // User
return_type Type
+ fn_var_type Type // fn variable type
should_be_skipped bool // true for calls to `[if someflag?]` functions, when there is no `-d someflag`
concrete_types []Type // concrete types, e.g.
concrete_list_pos token.Pos
@@ -1692,10 +1694,10 @@ pub:
[inline]
pub fn (expr Expr) is_blank_ident() bool {
- match expr {
- Ident { return expr.kind == .blank_ident }
- else { return false }
+ if expr is Ident {
+ return expr.kind == .blank_ident
}
+ return false
}
pub fn (expr Expr) pos() token.Pos {
diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v
index b213a5fb43..51932ead65 100644
--- a/vlib/v/ast/types.v
+++ b/vlib/v/ast/types.v
@@ -257,10 +257,6 @@ fn (ts TypeSymbol) dbg_common(mut res []string) {
res << 'language: $ts.language'
}
-pub fn (t Type) str() string {
- return 'ast.Type(0x$t.hex() = ${u32(t)})'
-}
-
pub fn (t &Table) type_str(typ Type) string {
sym := t.sym(typ)
return sym.name
@@ -980,6 +976,12 @@ pub fn (mytable &Table) type_to_code(t Type) string {
}
}
+// clean type name from generics form. From Type -> Type
+pub fn (t &Table) clean_generics_type_str(typ Type) string {
+ result := t.type_to_str(typ)
+ return result.all_before('<')
+}
+
// import_aliases is a map of imported symbol aliases 'module.Type' => 'Type'
pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]string) string {
sym := t.sym(typ)
@@ -1186,7 +1188,7 @@ pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string
// TODO write receiver
}
if !opts.type_only {
- sb.write_string('$func.name')
+ sb.write_string(func.name)
}
sb.write_string('(')
start := int(opts.skip_receiver)
@@ -1203,22 +1205,38 @@ pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string
sb.write_string('mut ')
}
if !opts.type_only {
- sb.write_string('$param.name ')
+ sb.write_string(param.name)
+ sb.write_string(' ')
}
styp := t.type_to_str_using_aliases(typ, import_aliases)
if i == func.params.len - 1 && func.is_variadic {
- sb.write_string('...$styp')
+ sb.write_string('...')
+ sb.write_string(styp)
} else {
- sb.write_string('$styp')
+ sb.write_string(styp)
}
}
sb.write_string(')')
if func.return_type != ast.void_type {
- sb.write_string(' ${t.type_to_str_using_aliases(func.return_type, import_aliases)}')
+ sb.write_string(' ')
+ sb.write_string(t.type_to_str_using_aliases(func.return_type, import_aliases))
}
return sb.str()
}
+// Get the name of the complete quanlified name of the type
+// without the generic parts.
+pub fn (t &TypeSymbol) symbol_name_except_generic() string {
+ // main.Abc
+ mut embed_name := t.name
+ // remove generic part from name
+ // main.Abc => main.Abc
+ if embed_name.contains('<') {
+ embed_name = embed_name.all_before('<')
+ }
+ return embed_name
+}
+
pub fn (t &TypeSymbol) embed_name() string {
// main.Abc => Abc
mut embed_name := t.name.split('.').last()
diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v
index 355a89d86d..48cc1a5a1a 100644
--- a/vlib/v/checker/check_types.v
+++ b/vlib/v/checker/check_types.v
@@ -188,7 +188,39 @@ pub fn (mut c Checker) check_expected_call_arg(got ast.Type, expected_ ast.Type,
return
}
}
- return error('cannot use `${c.table.type_to_str(got.clear_flag(.variadic))}` as `${c.table.type_to_str(expected.clear_flag(.variadic))}`')
+
+ // Check on Generics types, there are some case where we have the following case
+ // `&Type == &Type<>`. This is a common case we are implementing a function
+ // with generic parameters like `compare(bst Bst node) {}`
+ got_typ_sym := c.table.sym(got)
+ got_typ_str := c.table.type_to_str(got.clear_flag(.variadic))
+ expected_typ_sym := c.table.sym(expected_)
+ expected_typ_str := c.table.type_to_str(expected.clear_flag(.variadic))
+
+ if got_typ_sym.symbol_name_except_generic() == expected_typ_sym.symbol_name_except_generic() {
+ // Check if we are making a comparison between two different types of
+ // the same type like `Type and &Type<>`
+ if (got.is_ptr() != expected.is_ptr()) || !c.check_same_module(got, expected) {
+ return error('cannot use `$got_typ_str` as `$expected_typ_str`')
+ }
+ return
+ }
+ return error('cannot use `$got_typ_str` as `$expected_typ_str`')
+}
+
+// helper method to check if the type is of the same module.
+// FIXME(vincenzopalazzo) This is a work around to the issue
+// explained in the https://github.com/vlang/v/pull/13718#issuecomment-1074517800
+fn (c Checker) check_same_module(got ast.Type, expected ast.Type) bool {
+ clean_got_typ := c.table.clean_generics_type_str(got.clear_flag(.variadic)).all_before('<')
+ clean_expected_typ := c.table.clean_generics_type_str(expected.clear_flag(.variadic)).all_before('<')
+ if clean_got_typ == clean_expected_typ {
+ return true
+ // The following if confition should catch the bugs descripted in the issue
+ } else if clean_expected_typ.all_after('.') == clean_got_typ.all_after('.') {
+ return true
+ }
+ return false
}
pub fn (mut c Checker) check_basic(got ast.Type, expected ast.Type) bool {
@@ -580,7 +612,12 @@ pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Typ
}
c.fail_if_unreadable(expr, ftyp, 'interpolation object')
node.expr_types << ftyp
- typ := c.table.unalias_num_type(ftyp)
+ ftyp_sym := c.table.sym(ftyp)
+ typ := if ftyp_sym.kind == .alias && !ftyp_sym.has_method('str') {
+ c.table.unalias_num_type(ftyp)
+ } else {
+ ftyp
+ }
mut fmt := node.fmts[i]
// analyze and validate format specifier
if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `S`, `p`,
diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v
index 1d0d4fcded..bb8f1b453e 100644
--- a/vlib/v/checker/checker.v
+++ b/vlib/v/checker/checker.v
@@ -91,9 +91,10 @@ pub mut:
inside_fn_arg bool // `a`, `b` in `a.f(b)`
inside_ct_attr bool // true inside `[if expr]`
inside_comptime_for_field bool
- skip_flags bool // should `#flag` and `#include` be skipped
- fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
- smartcast_mut_pos token.Pos
+ skip_flags bool // should `#flag` and `#include` be skipped
+ fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
+ smartcast_mut_pos token.Pos // match mut foo, if mut foo is Foo
+ smartcast_cond_pos token.Pos // match cond
ct_cond_stack []ast.Expr
mut:
stmt_level int // the nesting level inside each stmts list;
@@ -594,12 +595,20 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
match mut node.left {
ast.Ident, ast.SelectorExpr {
if node.left.is_mut {
- c.error('remove unnecessary `mut`', node.left.mut_pos)
+ c.error('the `mut` keyword is invalid here', node.left.mut_pos)
}
}
else {}
}
}
+ match mut node.right {
+ ast.Ident, ast.SelectorExpr {
+ if node.right.is_mut {
+ c.error('the `mut` keyword is invalid here', node.right.mut_pos)
+ }
+ }
+ else {}
+ }
eq_ne := node.op in [.eq, .ne]
// Single side check
// Place these branches according to ops' usage frequency to accelerate.
@@ -1522,41 +1531,36 @@ fn (mut c Checker) check_or_last_stmt(stmt ast.Stmt, ret_type ast.Type, expr_ret
stmt.pos)
}
}
- } else {
- match stmt {
- ast.ExprStmt {
- match stmt.expr {
- ast.IfExpr {
- for branch in stmt.expr.branches {
- last_stmt := branch.stmts[branch.stmts.len - 1]
- c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
- }
- }
- ast.MatchExpr {
- for branch in stmt.expr.branches {
- last_stmt := branch.stmts[branch.stmts.len - 1]
- c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
- }
- }
- else {
- if stmt.typ == ast.void_type {
- return
- }
- if is_noreturn_callexpr(stmt.expr) {
- return
- }
- if c.check_types(stmt.typ, expr_return_type) {
- return
- }
- // opt_returning_string() or { ... 123 }
- type_name := c.table.type_to_str(stmt.typ)
- expr_return_type_name := c.table.type_to_str(expr_return_type)
- c.error('the default expression type in the `or` block should be `$expr_return_type_name`, instead you gave a value of type `$type_name`',
- stmt.expr.pos())
- }
+ } else if stmt is ast.ExprStmt {
+ match stmt.expr {
+ ast.IfExpr {
+ for branch in stmt.expr.branches {
+ last_stmt := branch.stmts[branch.stmts.len - 1]
+ c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
}
}
- else {}
+ ast.MatchExpr {
+ for branch in stmt.expr.branches {
+ last_stmt := branch.stmts[branch.stmts.len - 1]
+ c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
+ }
+ }
+ else {
+ if stmt.typ == ast.void_type {
+ return
+ }
+ if is_noreturn_callexpr(stmt.expr) {
+ return
+ }
+ if c.check_types(stmt.typ, expr_return_type) {
+ return
+ }
+ // opt_returning_string() or { ... 123 }
+ type_name := c.table.type_to_str(stmt.typ)
+ expr_return_type_name := c.table.type_to_str(expr_return_type)
+ c.error('the default expression type in the `or` block should be `$expr_return_type_name`, instead you gave a value of type `$type_name`',
+ stmt.expr.pos())
+ }
}
}
}
@@ -1761,6 +1765,10 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
c.note('smartcasting requires either an immutable value, or an explicit mut keyword before the value',
c.smartcast_mut_pos)
}
+ if c.smartcast_cond_pos != token.Pos{} {
+ c.note('smartcast can only be used on the ident or selector, e.g. match foo, match foo.bar',
+ c.smartcast_cond_pos)
+ }
c.error(unknown_field_msg, node.pos)
}
return ast.void_type
@@ -2089,6 +2097,10 @@ fn (mut c Checker) global_decl(mut node ast.GlobalDecl) {
c.error('unknown type `$sym.name`', field.typ_pos)
}
if field.has_expr {
+ if field.expr is ast.AnonFn && field.name == 'main' {
+ c.error('the `main` function is the program entry point, cannot redefine it',
+ field.pos)
+ }
field.typ = c.expr(field.expr)
mut v := c.file.global_scope.find_global(field.name) or {
panic('internal compiler error - could not find global in scope')
@@ -3349,7 +3361,9 @@ fn (mut c Checker) smartcast(expr_ ast.Expr, cur_type ast.Type, to_type_ ast.Typ
c.smartcast_mut_pos = expr.pos
}
}
- else {}
+ else {
+ c.smartcast_cond_pos = expr.pos()
+ }
}
}
@@ -3765,6 +3779,9 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
&& typ !in [ast.byteptr_type, ast.charptr_type] && !typ.has_flag(.variadic) {
c.error('type `$typ_sym.name` does not support indexing', node.pos)
}
+ if typ.has_flag(.optional) {
+ c.error('type `?$typ_sym.name` is optional, it does not support indexing', node.left.pos())
+ }
if typ_sym.kind == .string && !typ.is_ptr() && node.is_setter {
c.error('cannot assign to s[i] since V strings are immutable\n' +
'(note, that variables may be mutable but string values are always immutable, like in Go and Java)',
diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v
index 15177508fe..355be70ecf 100644
--- a/vlib/v/checker/fn.v
+++ b/vlib/v/checker/fn.v
@@ -352,32 +352,12 @@ fn (mut c Checker) anon_fn(mut node ast.AnonFn) ast.Type {
}
pub fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
- // First check everything that applies to both fns and methods
// TODO merge logic from method_call and fn_call
- /*
- for i, call_arg in node.args {
- if call_arg.is_mut {
- c.fail_if_immutable(call_arg.expr)
- if !arg.is_mut {
- tok := call_arg.share.str()
- c.error('`$node.name` parameter `$arg.name` is not `$tok`, `$tok` is not needed`',
- call_arg.expr.pos())
- } else if arg.typ.share() != call_arg.share {
- c.error('wrong shared type', call_arg.expr.pos())
- }
- } else {
- if arg.is_mut && (!call_arg.is_mut || arg.typ.share() != call_arg.share) {
- tok := call_arg.share.str()
- c.error('`$node.name` parameter `$arg.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${i+1}`',
- call_arg.expr.pos())
- }
- }
- }
- */
- // Now call `method_call` or `fn_call` for specific checks.
+ // First check everything that applies to both fns and methods
old_inside_fn_arg := c.inside_fn_arg
c.inside_fn_arg = true
mut continue_check := true
+ // Now call `method_call` or `fn_call` for specific checks.
typ := if node.is_method {
c.method_call(mut node)
} else {
@@ -620,9 +600,13 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
match obj {
ast.GlobalField {
typ = obj.typ
+ node.is_fn_var = true
+ node.fn_var_type = typ
}
ast.Var {
typ = if obj.smartcasts.len != 0 { obj.smartcasts.last() } else { obj.typ }
+ node.is_fn_var = true
+ node.fn_var_type = typ
}
else {}
}
@@ -810,7 +794,8 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
call_arg.expr.pos())
} else {
if param.typ.share() != call_arg.share {
- c.error('wrong shared type', call_arg.expr.pos())
+ c.error('wrong shared type `$call_arg.share.str()`, expected: `$param.typ.share().str()`',
+ call_arg.expr.pos())
}
if to_lock != '' && !param.typ.has_flag(.shared_f) {
c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`',
@@ -1258,10 +1243,6 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
final_arg_sym = c.table.sym(final_arg_typ)
}
if exp_arg_typ.has_flag(.generic) {
- if concrete_types.len == 0 {
- continue
- }
-
if exp_utyp := c.table.resolve_generic_to_concrete(exp_arg_typ, method.generic_names,
concrete_types)
{
@@ -1299,7 +1280,8 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
arg.expr.pos())
} else {
if param_share != arg.share {
- c.error('wrong shared type', arg.expr.pos())
+ c.error('wrong shared type `$arg.share.str()`, expected: `$param_share.str()`',
+ arg.expr.pos())
}
if to_lock != '' && param_share != .shared_t {
c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`',
diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v
index 4660ac3287..f50fa265af 100644
--- a/vlib/v/checker/if.v
+++ b/vlib/v/checker/if.v
@@ -152,9 +152,8 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
} else {
c.stmts(branch.stmts)
}
- if c.smartcast_mut_pos != token.Pos{} {
- c.smartcast_mut_pos = token.Pos{}
- }
+ c.smartcast_mut_pos = token.Pos{}
+ c.smartcast_cond_pos = token.Pos{}
}
if expr_required {
if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt {
diff --git a/vlib/v/checker/match.v b/vlib/v/checker/match.v
index 81e9ef937f..b3eccd7374 100644
--- a/vlib/v/checker/match.v
+++ b/vlib/v/checker/match.v
@@ -43,9 +43,8 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
} else {
c.stmts(branch.stmts)
}
- if c.smartcast_mut_pos != token.Pos{} {
- c.smartcast_mut_pos = token.Pos{}
- }
+ c.smartcast_mut_pos = token.Pos{}
+ c.smartcast_cond_pos = token.Pos{}
if node.is_expr {
if branch.stmts.len > 0 {
// ignore last statement - workaround
@@ -66,37 +65,36 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
// If the last statement is an expression, return its type
if branch.stmts.len > 0 {
mut stmt := branch.stmts[branch.stmts.len - 1]
- match mut stmt {
- ast.ExprStmt {
- if node.is_expr {
- c.expected_type = node.expected_type
+ if mut stmt is ast.ExprStmt {
+ if node.is_expr {
+ c.expected_type = node.expected_type
+ }
+ expr_type := c.expr(stmt.expr)
+ if first_iteration {
+ if node.is_expr && (node.expected_type.has_flag(.optional)
+ || c.table.type_kind(node.expected_type) == .sum_type) {
+ ret_type = node.expected_type
+ } else {
+ ret_type = expr_type
}
- expr_type := c.expr(stmt.expr)
- if first_iteration {
- if node.is_expr && (node.expected_type.has_flag(.optional)
- || c.table.type_kind(node.expected_type) == .sum_type) {
- ret_type = node.expected_type
- } else {
- ret_type = expr_type
- }
- stmt.typ = expr_type
- } else if node.is_expr && ret_type.idx() != expr_type.idx() {
- if !c.check_types(ret_type, expr_type)
- && !c.check_types(expr_type, ret_type) {
- ret_sym := c.table.sym(ret_type)
- is_noreturn := is_noreturn_callexpr(stmt.expr)
- if !(node.is_expr && ret_sym.kind == .sum_type) && !is_noreturn {
- c.error('return type mismatch, it should be `$ret_sym.name`',
- stmt.expr.pos())
- }
+ stmt.typ = expr_type
+ } else if node.is_expr && ret_type.idx() != expr_type.idx() {
+ if !c.check_types(ret_type, expr_type) && !c.check_types(expr_type, ret_type) {
+ ret_sym := c.table.sym(ret_type)
+ is_noreturn := is_noreturn_callexpr(stmt.expr)
+ if !(node.is_expr && ret_sym.kind == .sum_type
+ && (ret_type.has_flag(.generic)
+ || c.table.is_sumtype_or_in_variant(ret_type, expr_type)))
+ && !is_noreturn {
+ c.error('return type mismatch, it should be `$ret_sym.name`',
+ stmt.expr.pos())
}
}
}
- else {
- if node.is_expr && ret_type != ast.void_type {
- c.error('`match` expression requires an expression as the last statement of every branch',
- stmt.pos)
- }
+ } else {
+ if node.is_expr && ret_type != ast.void_type {
+ c.error('`match` expression requires an expression as the last statement of every branch',
+ stmt.pos)
}
}
}
@@ -119,10 +117,6 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
c.returns = false
}
}
- // if ret_type != ast.void_type {
- // node.is_expr = c.expected_type != ast.void_type
- // node.expected_type = c.expected_type
- // }
node.return_type = ret_type
cond_var := c.get_base_name(&node.cond)
if cond_var != '' {
@@ -241,7 +235,11 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
if expr_type !in cond_type_sym.info.variants {
expr_str := c.table.type_to_str(expr_type)
expect_str := c.table.type_to_str(node.cond_type)
- c.error('`$expect_str` has no variant `$expr_str`', expr.pos())
+ sumtype_variant_names := cond_type_sym.info.variants.map(c.table.type_to_str_using_aliases(it,
+ {}))
+ suggestion := util.new_suggestion(expr_str, sumtype_variant_names)
+ c.error(suggestion.say('`$expect_str` has no variant `$expr_str`'),
+ expr.pos())
}
} else if cond_type_sym.info is ast.Alias && expr_type_sym.info is ast.Struct {
expr_str := c.table.type_to_str(expr_type)
diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v
index acd2f69c36..bca2af63e8 100644
--- a/vlib/v/checker/struct.v
+++ b/vlib/v/checker/struct.v
@@ -55,6 +55,16 @@ pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
field.type_pos)
}
}
+ field_sym := c.table.sym(field.typ)
+ if field_sym.kind == .function {
+ fn_info := field_sym.info as ast.FnType
+ c.ensure_type_exists(fn_info.func.return_type, fn_info.func.return_type_pos) or {
+ return
+ }
+ for param in fn_info.func.params {
+ c.ensure_type_exists(param.typ, param.type_pos) or { return }
+ }
+ }
}
if sym.kind == .struct_ {
info := sym.info as ast.Struct
@@ -324,6 +334,15 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
field.pos)
}
}
+ if field_type_sym.kind == .function && field_type_sym.language == .v {
+ pos := field.expr.pos()
+ if mut field.expr is ast.AnonFn {
+ if field.expr.decl.no_body {
+ c.error('cannot initialize the fn field with anonymous fn that does not have a body',
+ pos)
+ }
+ }
+ }
node.fields[i].typ = expr_type
node.fields[i].expected_type = field_info.typ
diff --git a/vlib/v/checker/tests/amod/amod.v b/vlib/v/checker/tests/amod/amod.v
new file mode 100644
index 0000000000..5392fc0f2b
--- /dev/null
+++ b/vlib/v/checker/tests/amod/amod.v
@@ -0,0 +1,5 @@
+module amod
+
+pub struct Xyz {}
+
+pub struct Bcg {}
diff --git a/vlib/v/checker/tests/generic_parameter_on_method.out b/vlib/v/checker/tests/generic_parameter_on_method.out
new file mode 100644
index 0000000000..4d82cccbdf
--- /dev/null
+++ b/vlib/v/checker/tests/generic_parameter_on_method.out
@@ -0,0 +1,7 @@
+vlib/v/checker/tests/generic_parameter_on_method.vv:15:15: error: cannot use `&Type` as `Type<>` in argument 1 to `ContainerType.contains`
+ 13 | fn main() {
+ 14 | con := ContainerType{typ: &Type{0}}
+ 15 | con.contains(con.typ)
+ | ~~~~~~~
+ 16 | println(con)
+ 17 | }
diff --git a/vlib/v/checker/tests/generic_parameter_on_method.vv b/vlib/v/checker/tests/generic_parameter_on_method.vv
new file mode 100644
index 0000000000..719dbe892e
--- /dev/null
+++ b/vlib/v/checker/tests/generic_parameter_on_method.vv
@@ -0,0 +1,17 @@
+struct Type {
+ value T
+}
+
+struct ContainerType {
+ typ &Type
+}
+
+fn (instance &ContainerType) contains(typ Type) {
+ println(typ)
+}
+
+fn main() {
+ con := ContainerType{typ: &Type{0}}
+ con.contains(con.typ)
+ println(con)
+}
diff --git a/vlib/v/checker/tests/globals/redefine_main.out b/vlib/v/checker/tests/globals/redefine_main.out
new file mode 100644
index 0000000000..f7a2d410dc
--- /dev/null
+++ b/vlib/v/checker/tests/globals/redefine_main.out
@@ -0,0 +1,3 @@
+vlib/v/checker/tests/globals/redefine_main.vv:1:10: error: the `main` function is the program entry point, cannot redefine it
+ 1 | __global main = fn () int { return 22 }
+ | ~~~~
diff --git a/vlib/v/checker/tests/globals/redefine_main.vv b/vlib/v/checker/tests/globals/redefine_main.vv
new file mode 100644
index 0000000000..c8786bc304
--- /dev/null
+++ b/vlib/v/checker/tests/globals/redefine_main.vv
@@ -0,0 +1 @@
+__global main = fn () int { return 22 }
diff --git a/vlib/v/checker/tests/incorrect_smartcast2_err.out b/vlib/v/checker/tests/incorrect_smartcast2_err.out
new file mode 100644
index 0000000000..c3e813e244
--- /dev/null
+++ b/vlib/v/checker/tests/incorrect_smartcast2_err.out
@@ -0,0 +1,14 @@
+vlib/v/checker/tests/incorrect_smartcast2_err.vv:24:9: notice: smartcast can only be used on the ident or selector, e.g. match foo, match foo.bar
+ 22 |
+ 23 | fn doesntwork(v []Either) {
+ 24 | match v[0] {
+ | ~~~
+ 25 | Left {
+ 26 | println(v[0].error)
+vlib/v/checker/tests/incorrect_smartcast2_err.vv:26:17: error: field `error` does not exist or have the same type in all sumtype variants
+ 24 | match v[0] {
+ 25 | Left {
+ 26 | println(v[0].error)
+ | ~~~~~
+ 27 | }
+ 28 | else {}
diff --git a/vlib/v/checker/tests/incorrect_smartcast2_err.vv b/vlib/v/checker/tests/incorrect_smartcast2_err.vv
new file mode 100644
index 0000000000..65b4c5b5d3
--- /dev/null
+++ b/vlib/v/checker/tests/incorrect_smartcast2_err.vv
@@ -0,0 +1,32 @@
+struct Left {
+ error E
+}
+
+struct Right {
+ inner T
+}
+
+type Either =
+ Left |
+ Right
+
+fn works(v []Either) {
+ first := v[0]
+ match first {
+ Left {
+ println(first.error)
+ }
+ else {}
+ }
+}
+
+fn doesntwork(v []Either) {
+ match v[0] {
+ Left {
+ println(v[0].error)
+ }
+ else {}
+ }
+}
+
+fn main() {}
diff --git a/vlib/v/checker/tests/index_of_optional_err.out b/vlib/v/checker/tests/index_of_optional_err.out
new file mode 100644
index 0000000000..a3628501d8
--- /dev/null
+++ b/vlib/v/checker/tests/index_of_optional_err.out
@@ -0,0 +1,7 @@
+vlib/v/checker/tests/index_of_optional_err.vv:6:7: error: type `?[]int` is optional, it does not support indexing
+ 4 |
+ 5 | fn main() {
+ 6 | a := abc()[0] or { 5 }
+ | ~~~~~
+ 7 | dump(a)
+ 8 | }
diff --git a/vlib/v/checker/tests/index_of_optional_err.vv b/vlib/v/checker/tests/index_of_optional_err.vv
new file mode 100644
index 0000000000..06bc943aae
--- /dev/null
+++ b/vlib/v/checker/tests/index_of_optional_err.vv
@@ -0,0 +1,8 @@
+fn abc() ?[]int {
+ return [1, 2, 3]
+}
+
+fn main() {
+ a := abc()[0] or { 5 }
+ dump(a)
+}
diff --git a/vlib/v/checker/tests/invalid_mut.out b/vlib/v/checker/tests/invalid_mut.out
new file mode 100644
index 0000000000..9747d4e444
--- /dev/null
+++ b/vlib/v/checker/tests/invalid_mut.out
@@ -0,0 +1,14 @@
+vlib/v/checker/tests/invalid_mut.vv:3:5: error: the `mut` keyword is invalid here
+ 1 | fn main() {
+ 2 | mut x := 0
+ 3 | if mut x == 0 {
+ | ~~~
+ 4 | println(true)
+ 5 | }
+vlib/v/checker/tests/invalid_mut.vv:6:10: error: the `mut` keyword is invalid here
+ 4 | println(true)
+ 5 | }
+ 6 | if 0 == mut x {
+ | ~~~
+ 7 | println(true)
+ 8 | }
diff --git a/vlib/v/parser/tests/unnecessary_mut.vv b/vlib/v/checker/tests/invalid_mut.vv
similarity index 65%
rename from vlib/v/parser/tests/unnecessary_mut.vv
rename to vlib/v/checker/tests/invalid_mut.vv
index 6c7a470ed5..d4b27b2ca9 100644
--- a/vlib/v/parser/tests/unnecessary_mut.vv
+++ b/vlib/v/checker/tests/invalid_mut.vv
@@ -3,5 +3,8 @@ fn main() {
if mut x == 0 {
println(true)
}
+ if 0 == mut x {
+ println(true)
+ }
_ = x
}
diff --git a/vlib/v/checker/tests/match_invalid_type.out b/vlib/v/checker/tests/match_invalid_type.out
index 4c6b98b836..fc7f5f8a57 100644
--- a/vlib/v/checker/tests/match_invalid_type.out
+++ b/vlib/v/checker/tests/match_invalid_type.out
@@ -1,4 +1,5 @@
-vlib/v/checker/tests/match_invalid_type.vv:5:3: error: `IoS` has no variant `byte`
+vlib/v/checker/tests/match_invalid_type.vv:5:3: error: `IoS` has no variant `byte`.
+2 possibilities: `int`, `string`.
3 | fn sum() {
4 | match IoS(1) {
5 | byte {
diff --git a/vlib/v/checker/tests/match_return_sumtype_mismatch_err.out b/vlib/v/checker/tests/match_return_sumtype_mismatch_err.out
new file mode 100644
index 0000000000..25086c8629
--- /dev/null
+++ b/vlib/v/checker/tests/match_return_sumtype_mismatch_err.out
@@ -0,0 +1,7 @@
+vlib/v/checker/tests/match_return_sumtype_mismatch_err.vv:15:11: error: return type mismatch, it should be `Myt`
+ 13 | return match b {
+ 14 | true { St('TRUE') }
+ 15 | false { `F` }
+ | ~~~
+ 16 | }
+ 17 | }
diff --git a/vlib/v/checker/tests/match_return_sumtype_mismatch_err.vv b/vlib/v/checker/tests/match_return_sumtype_mismatch_err.vv
new file mode 100644
index 0000000000..3f5a1a1d47
--- /dev/null
+++ b/vlib/v/checker/tests/match_return_sumtype_mismatch_err.vv
@@ -0,0 +1,19 @@
+type St = string
+type Ru = rune
+type Myt = Ru | St
+
+fn myt_t1(b bool) Myt {
+ match b {
+ true { return St('TRUE') }
+ false { return Ru(`F`) }
+ }
+}
+
+fn myt_t2(b bool) Myt {
+ return match b {
+ true { St('TRUE') }
+ false { `F` }
+ }
+}
+
+fn main() {}
diff --git a/vlib/v/checker/tests/struct_field_init_with_nobody_anon_fn_err.out b/vlib/v/checker/tests/struct_field_init_with_nobody_anon_fn_err.out
new file mode 100644
index 0000000000..3602a1fcc2
--- /dev/null
+++ b/vlib/v/checker/tests/struct_field_init_with_nobody_anon_fn_err.out
@@ -0,0 +1,7 @@
+vlib/v/checker/tests/struct_field_init_with_nobody_anon_fn_err.vv:7:7: error: cannot initialize the fn field with anonymous fn that does not have a body
+ 5 | fn main() {
+ 6 | _ = App{
+ 7 | cb: fn(x int) // Note the missing `{}` (the function body) here
+ | ~~~~~~~~~
+ 8 | }
+ 9 | }
diff --git a/vlib/v/checker/tests/struct_field_init_with_nobody_anon_fn_err.vv b/vlib/v/checker/tests/struct_field_init_with_nobody_anon_fn_err.vv
new file mode 100644
index 0000000000..fabdfce29e
--- /dev/null
+++ b/vlib/v/checker/tests/struct_field_init_with_nobody_anon_fn_err.vv
@@ -0,0 +1,9 @@
+struct App {
+ cb fn(x int) // the function signature doesn't make a difference
+}
+
+fn main() {
+ _ = App{
+ cb: fn(x int) // Note the missing `{}` (the function body) here
+ }
+}
diff --git a/vlib/v/checker/tests/sumtype_has_no_variant_suggestion.out b/vlib/v/checker/tests/sumtype_has_no_variant_suggestion.out
new file mode 100644
index 0000000000..6697412807
--- /dev/null
+++ b/vlib/v/checker/tests/sumtype_has_no_variant_suggestion.out
@@ -0,0 +1,8 @@
+vlib/v/checker/tests/sumtype_has_no_variant_suggestion.vv:14:5: error: `Abc` has no variant `amod.NonExisting`.
+5 possibilities: `amod.Bcg`, `amod.Xyz`, `AnotherStruct`, `Struct1`, `ThirdStruct`.
+ 12 | a := Abc(Struct1{})
+ 13 | match a {
+ 14 | x.NonExisting { println('----') }
+ | ~~~~~~~~~~~
+ 15 | else {}
+ 16 | }
diff --git a/vlib/v/checker/tests/sumtype_has_no_variant_suggestion.vv b/vlib/v/checker/tests/sumtype_has_no_variant_suggestion.vv
new file mode 100644
index 0000000000..240c1fa415
--- /dev/null
+++ b/vlib/v/checker/tests/sumtype_has_no_variant_suggestion.vv
@@ -0,0 +1,17 @@
+import v.checker.tests.amod as x
+
+struct Struct1 {}
+
+struct AnotherStruct {}
+
+struct ThirdStruct {}
+
+type Abc = AnotherStruct | Struct1 | ThirdStruct | x.Bcg | x.Xyz
+
+fn main() {
+ a := Abc(Struct1{})
+ match a {
+ x.NonExisting { println('----') }
+ else {}
+ }
+}
diff --git a/vlib/v/checker/tests/unknown_type_in_anon_fn.out b/vlib/v/checker/tests/unknown_type_in_anon_fn.out
new file mode 100644
index 0000000000..2045ddbba6
--- /dev/null
+++ b/vlib/v/checker/tests/unknown_type_in_anon_fn.out
@@ -0,0 +1,7 @@
+vlib/v/checker/tests/unknown_type_in_anon_fn.vv:5:10: error: unknown type `Another`
+ 3 | struct Struc{
+ 4 | mut:
+ 5 | f fn (s Another, i int) ?
+ | ~~~~~~~
+ 6 | }
+ 7 |
diff --git a/vlib/v/checker/tests/unknown_type_in_anon_fn.vv b/vlib/v/checker/tests/unknown_type_in_anon_fn.vv
new file mode 100644
index 0000000000..f03742cc4d
--- /dev/null
+++ b/vlib/v/checker/tests/unknown_type_in_anon_fn.vv
@@ -0,0 +1,8 @@
+module main
+
+struct Struc{
+mut:
+ f fn (s Another, i int) ?
+}
+
+fn main() {}
diff --git a/vlib/v/checker/tests/unnecessary_parenthesis_of_reference.out b/vlib/v/checker/tests/unnecessary_parenthesis_of_reference.out
new file mode 100644
index 0000000000..3aa279de02
--- /dev/null
+++ b/vlib/v/checker/tests/unnecessary_parenthesis_of_reference.out
@@ -0,0 +1,35 @@
+vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv:29:10: notice: unnecessary `()`, use `&Quad{....}` instead of `&(Quad{....})`
+ 27 | // ritorna una nuova Quadrica somma del ricevente e di un'altra
+ 28 | fn (q &Quad) add(other &Quad) &Quad {
+ 29 | return &(Quad{q.x + other.x, q.y + other.y, q.z + other.z, q.w + other.w})
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 30 | }
+ 31 |
+vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv:34:10: notice: unnecessary `()`, use `&Quad{....}` instead of `&(Quad{....})`
+ 32 | // ritorna una nuova Quadrica differenza tra il ricevente e un'altra
+ 33 | fn (q &Quad) sub(other &Quad) &Quad {
+ 34 | return &(Quad{q.x - other.x, q.y - other.y, q.z - other.z, q.w - other.w})
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 35 | }
+ 36 |
+vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv:39:10: notice: unnecessary `()`, use `&Quad{....}` instead of `&(Quad{....})`
+ 37 | // ritorna una nuova Quadrica ottenuta negando il ricevente
+ 38 | fn (q &Quad) neg() &Quad {
+ 39 | return &(Quad{-q.x, -q.y, -q.z, -q.w})
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 40 | }
+ 41 |
+vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv:44:10: notice: unnecessary `()`, use `&Quad{....}` instead of `&(Quad{....})`
+ 42 | // ritorna una nuova Quadrica ottenuta moltiplicando il ricevente per una costante
+ 43 | fn (q &Quad) mult(factor f64) &Quad {
+ 44 | return &(Quad{q.x * factor, q.y * factor, q.z * factor, q.w * factor})
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 45 | }
+ 46 |
+vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv:49:10: notice: unnecessary `()`, use `&Quad{....}` instead of `&(Quad{....})`
+ 47 | // ritorna una nuova Quadrica ottenuta dividendo il ricevente per una costante
+ 48 | fn (q &Quad) div(factor f64) &Quad {
+ 49 | return &(Quad{q.x / factor, q.y / factor, q.z / factor, q.w / factor})
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 50 | }
+ 51 |
diff --git a/vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv b/vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv
new file mode 100644
index 0000000000..1fe1a36986
--- /dev/null
+++ b/vlib/v/checker/tests/unnecessary_parenthesis_of_reference.vv
@@ -0,0 +1,69 @@
+struct Quad {
+mut:
+ x f64
+ y f64
+ z f64
+ w f64
+}
+
+fn (q Quad) get(i int) f64 {
+ return match i {
+ 0 { q.x }
+ 1 { q.y }
+ 2 { q.z }
+ else { q.w }
+ }
+}
+
+fn (mut q Quad) set(i int, v f64) {
+ match i {
+ 0 { q.x = v }
+ 1 { q.y = v }
+ 2 { q.z = v }
+ else { q.w = v }
+ }
+}
+
+// ritorna una nuova Quadrica somma del ricevente e di un'altra
+fn (q &Quad) add(other &Quad) &Quad {
+ return &(Quad{q.x + other.x, q.y + other.y, q.z + other.z, q.w + other.w})
+}
+
+// ritorna una nuova Quadrica differenza tra il ricevente e un'altra
+fn (q &Quad) sub(other &Quad) &Quad {
+ return &(Quad{q.x - other.x, q.y - other.y, q.z - other.z, q.w - other.w})
+}
+
+// ritorna una nuova Quadrica ottenuta negando il ricevente
+fn (q &Quad) neg() &Quad {
+ return &(Quad{-q.x, -q.y, -q.z, -q.w})
+}
+
+// ritorna una nuova Quadrica ottenuta moltiplicando il ricevente per una costante
+fn (q &Quad) mult(factor f64) &Quad {
+ return &(Quad{q.x * factor, q.y * factor, q.z * factor, q.w * factor})
+}
+
+// ritorna una nuova Quadrica ottenuta dividendo il ricevente per una costante
+fn (q &Quad) div(factor f64) &Quad {
+ return &(Quad{q.x / factor, q.y / factor, q.z / factor, q.w / factor})
+}
+
+fn main() {
+ mut n := Quad{1, 2, 3, 4}
+
+ println(n)
+ println(n.get(0))
+ println(n.get(1))
+ println(n.get(2))
+ println(n.get(3))
+ n.set(0, 5)
+ n.set(1, 6)
+ n.set(2, 7)
+ n.set(3, 8)
+ println(n)
+ println(n.get(0))
+ println(n.get(1))
+ println(n.get(2))
+ println(n.get(3))
+}
diff --git a/vlib/v/gen/c/auto_free_methods.v b/vlib/v/gen/c/auto_free_methods.v
index a30c6175ac..695e230143 100644
--- a/vlib/v/gen/c/auto_free_methods.v
+++ b/vlib/v/gen/c/auto_free_methods.v
@@ -7,15 +7,14 @@ import strings
fn (mut g Gen) get_free_method(typ ast.Type) string {
g.autofree_methods[typ] = true
- styp := g.typ(typ).replace('*', '')
mut sym := g.table.sym(g.unwrap_generic(typ))
- mut fn_name := styp_to_free_fn_name(styp)
if mut sym.info is ast.Alias {
if sym.info.is_import {
sym = g.table.sym(sym.info.parent_type)
}
}
-
+ styp := g.typ(typ).replace('*', '')
+ fn_name := styp_to_free_fn_name(styp)
if sym.has_method_with_generic_parent('free') {
return fn_name
}
@@ -30,21 +29,23 @@ fn (mut g Gen) gen_free_methods() {
fn (mut g Gen) gen_free_method(typ ast.Type) string {
styp := g.typ(typ).replace('*', '')
- mut sym := g.table.sym(g.unwrap_generic(typ))
mut fn_name := styp_to_free_fn_name(styp)
- if typ in g.generated_free_methods {
+ deref_typ := typ.set_nr_muls(0)
+ if deref_typ in g.generated_free_methods {
return fn_name
}
- g.generated_free_methods[typ] = true
+ g.generated_free_methods[deref_typ] = true
+
+ mut sym := g.table.sym(g.unwrap_generic(typ))
if mut sym.info is ast.Alias {
if sym.info.is_import {
sym = g.table.sym(sym.info.parent_type)
}
}
-
if sym.has_method_with_generic_parent('free') {
return fn_name
}
+
match mut sym.info {
ast.Struct {
g.gen_free_for_struct(sym.info, styp, fn_name)
diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v
index 982a3cf3e8..98c1d10869 100644
--- a/vlib/v/gen/c/auto_str_methods.v
+++ b/vlib/v/gen/c/auto_str_methods.v
@@ -144,7 +144,7 @@ fn (mut g Gen) get_str_fn(typ ast.Type) string {
styp := g.typ(unwrapped)
mut sym := g.table.sym(unwrapped)
mut str_fn_name := styp_to_str_fn_name(styp)
- if mut sym.info is ast.Alias {
+ if mut sym.info is ast.Alias && !sym.has_method('str') {
if sym.info.is_import {
sym = g.table.sym(sym.info.parent_type)
str_fn_name = styp_to_str_fn_name(sym.name)
diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v
index d576ede838..a0c13cdf91 100644
--- a/vlib/v/gen/c/cgen.v
+++ b/vlib/v/gen/c/cgen.v
@@ -20,10 +20,11 @@ const (
// `small` should not be needed, but see: https://stackoverflow.com/questions/5874215/what-is-rpcndr-h
c_reserved = ['array', 'auto', 'bool', 'break', 'calloc', 'case', 'char', 'class', 'complex',
'const', 'continue', 'default', 'delete', 'do', 'double', 'else', 'enum', 'error', 'exit',
- 'export', 'extern', 'float', 'for', 'free', 'goto', 'if', 'inline', 'int', 'link', 'long',
- 'malloc', 'namespace', 'new', 'panic', 'register', 'restrict', 'return', 'short', 'signed',
- 'sizeof', 'static', 'string', 'struct', 'switch', 'typedef', 'typename', 'union', 'unix',
- 'unsigned', 'void', 'volatile', 'while', 'template', 'small', 'stdout', 'stdin', 'stderr']
+ 'export', 'extern', 'false', 'float', 'for', 'free', 'goto', 'if', 'inline', 'int', 'link',
+ 'long', 'malloc', 'namespace', 'new', 'panic', 'register', 'restrict', 'return', 'short',
+ 'signed', 'sizeof', 'static', 'string', 'struct', 'switch', 'typedef', 'typename', 'union',
+ 'unix', 'unsigned', 'void', 'volatile', 'while', 'template', 'true', 'small', 'stdout',
+ 'stdin', 'stderr']
c_reserved_map = string_array_to_map(c_reserved)
// same order as in token.Kind
cmp_str = ['eq', 'ne', 'gt', 'lt', 'ge', 'le']
@@ -955,7 +956,13 @@ fn (mut g Gen) optional_type_name(t ast.Type) (string, string) {
fn (g Gen) optional_type_text(styp string, base string) string {
// replace void with something else
- size := if base == 'void' { 'byte' } else { base }
+ size := if base == 'void' {
+ 'byte'
+ } else if base.starts_with('anon_fn') {
+ 'void*'
+ } else {
+ base
+ }
ret := 'struct $styp {
byte state;
IError err;
@@ -1834,7 +1841,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
}
ast.Module {
// g.is_builtin_mod = node.name == 'builtin'
- g.is_builtin_mod = node.name in ['builtin', 'strconv', 'strings', 'dlmalloc']
+ // g.is_builtin_mod = node.name in ['builtin', 'strconv', 'strings', 'dlmalloc']
+ g.is_builtin_mod = util.module_is_builtin(node.name)
// g.cur_mod = node.name
g.cur_mod = node
}
@@ -2037,7 +2045,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
&& !expected_type.has_flag(.optional) {
if expr is ast.StructInit && !got_type.is_ptr() {
g.inside_cast_in_heap++
- got_styp := g.cc_type(got_type.ref(), true)
+ got_styp := g.cc_type(got_type_raw.ref(), true)
// TODO: why does cc_type even add this in the first place?
exp_styp := exp_sym.cname
mut fname := 'I_${got_styp}_to_Interface_$exp_styp'
@@ -2048,12 +2056,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
got_styp)
g.inside_cast_in_heap--
} else {
- mut got_styp := g.cc_type(got_type, true)
- got_styp = match got_styp {
- 'int' { 'int_literal' }
- 'f64' { 'float_literal' }
- else { got_styp }
- }
+ got_styp := g.cc_type(got_type_raw, true)
got_is_shared := got_type.has_flag(.shared_f)
exp_styp := if got_is_shared { '__shared__$exp_sym.cname' } else { exp_sym.cname }
// If it's shared, we need to use the other caster:
@@ -3645,11 +3648,23 @@ fn (mut g Gen) ident(node ast.Ident) {
g.write(util.no_dots(node.name[2..]))
return
}
- if node.kind == .constant { // && !node.name.starts_with('g_') {
- // TODO globals hack
- g.write('_const_')
- }
mut name := c_name(node.name)
+ if node.kind == .constant { // && !node.name.starts_with('g_') {
+ if g.pref.translated && !g.is_builtin_mod
+ && !util.module_is_builtin(node.name.all_before_last('.')) {
+ // Don't prepend "_const" to translated C consts,
+ // but only in user code, continue prepending "_const" to builtin consts.
+ mut x := util.no_dots(node.name)
+ if x.starts_with('main__') {
+ x = x['main__'.len..]
+ }
+ g.write(x)
+ return
+ } else {
+ // TODO globals hack
+ g.write('_const_')
+ }
+ }
// TODO: temporary, remove this
node_info := node.info
mut is_auto_heap := false
@@ -4114,6 +4129,12 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
}
}
name := c_name(field.name)
+ const_name := if node.attrs.contains('export') && !g.is_builtin_mod {
+ // TODO this only works for the first const in the group for now
+ node.attrs[0].arg
+ } else {
+ '_const_' + name
+ }
field_expr := field.expr
match field.expr {
ast.ArrayInit {
@@ -4121,19 +4142,19 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
styp := g.typ(field.expr.typ)
if g.pref.build_mode != .build_module {
val := g.expr_string(field.expr)
- g.definitions.writeln('$styp _const_$name = $val; // fixed array const')
+ g.definitions.writeln('$styp $const_name = $val; // fixed array const')
} else {
- g.definitions.writeln('$styp _const_$name; // fixed array const')
+ g.definitions.writeln('$styp $const_name; // fixed array const')
}
} else {
g.const_decl_init_later(field.mod, name, field.expr, field.typ, false)
}
}
ast.StringLiteral {
- g.definitions.writeln('string _const_$name; // a string literal, inited later')
+ g.definitions.writeln('string $const_name; // a string literal, inited later')
if g.pref.build_mode != .build_module {
val := g.expr_string(field.expr)
- g.stringliterals.writeln('\t_const_$name = $val;')
+ g.stringliterals.writeln('\t$const_name = $val;')
}
}
ast.CallExpr {
@@ -4177,7 +4198,7 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
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 := '_const_$name'
+ cname := if g.pref.translated && !g.is_builtin_mod { name } else { '_const_$name' }
$if trace_const_precomputed ? {
eprintln('> styp: $styp | cname: $cname | ct_value: $ct_value | $ct_value.type_name()')
}
@@ -4246,7 +4267,7 @@ fn (mut g Gen) const_decl_precomputed(mod string, name string, ct_value ast.Comp
// 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; // inited later')
+ g.definitions.writeln('$styp $cname; // str inited later')
g.init.writeln('\t$cname = _SLIT("$escaped_val");')
if g.is_autofree {
g.cleanups[mod].writeln('\tstring_free(&$cname);')
@@ -4276,7 +4297,7 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ
// Initialize more complex consts in `void _vinit/2{}`
// (C doesn't allow init expressions that can't be resolved at compile time).
mut styp := g.typ(typ)
- cname := '_const_$name'
+ cname := if g.pref.translated && !g.is_builtin_mod { name } else { '_const_$name' }
g.definitions.writeln('$styp $cname; // inited later')
if cname == '_const_os__args' {
if g.pref.os == .windows {
@@ -4339,6 +4360,13 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
}
}
styp := g.typ(field.typ)
+ mut anon_fn_expr := unsafe { field.expr }
+ if field.has_expr && mut anon_fn_expr is ast.AnonFn {
+ g.gen_anon_fn_decl(mut anon_fn_expr)
+ fn_type_name := g.get_anon_fn_type_name(mut anon_fn_expr, field.name)
+ g.definitions.writeln('$fn_type_name = ${g.table.sym(field.typ).name}; // global')
+ continue
+ }
g.definitions.write_string('$visibility_kw$styp $attributes $field.name')
if field.has_expr {
if field.expr.is_literal() && should_init {
@@ -4524,17 +4552,17 @@ fn (mut g Gen) write_builtin_types() {
// Sort the types, make sure types that are referenced by other types
// are added before them.
fn (mut g Gen) write_sorted_types() {
+ g.type_definitions.writeln('// #start sorted_symbols')
+ defer {
+ g.type_definitions.writeln('// #end sorted_symbols')
+ }
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
}
}
- // sort structs
sorted_symbols := g.sort_structs(symbols)
- // Generate C code
- g.type_definitions.writeln('// builtin types:')
- g.type_definitions.writeln('//------------------ #endbuiltin')
g.write_types(sorted_symbols)
}
@@ -4706,7 +4734,7 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
}
// sort structs by dependant fields
-fn (g &Gen) sort_structs(typesa []&ast.TypeSymbol) []&ast.TypeSymbol {
+fn (mut g Gen) sort_structs(typesa []&ast.TypeSymbol) []&ast.TypeSymbol {
util.timing_start(@METHOD)
defer {
util.timing_measure(@METHOD)
@@ -4742,12 +4770,23 @@ fn (g &Gen) sort_structs(typesa []&ast.TypeSymbol) []&ast.TypeSymbol {
field_deps << dep
}
for field in sym.info.fields {
- dep := g.table.sym(field.typ).name
+ if field.typ.is_ptr() {
+ continue
+ }
+ fsym := g.table.sym(field.typ)
+ dep := fsym.name
// skip if not in types list or already in deps
- if dep !in type_names || dep in field_deps || field.typ.is_ptr() {
+ if dep !in type_names || dep in field_deps {
continue
}
field_deps << dep
+ if fsym.info is ast.Alias {
+ xdep := g.table.sym(fsym.info.parent_type).name
+ if xdep !in type_names || xdep in field_deps {
+ continue
+ }
+ field_deps << xdep
+ }
}
}
// ast.Interface {}
@@ -4795,7 +4834,7 @@ fn (mut g Gen) go_before_ternary() string {
}
fn (mut g Gen) insert_before_stmt(s string) {
- cur_line := g.go_before_stmt(0)
+ cur_line := g.go_before_stmt(g.inside_ternary)
g.writeln(s)
g.write(cur_line)
}
@@ -4815,12 +4854,15 @@ fn (mut g Gen) insert_at(pos int, s string) {
// Returns the type of the last stmt
fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Type) {
cvar_name := c_name(var_name)
- mr_styp := g.base_type(return_type)
+ mut mr_styp := g.base_type(return_type)
is_none_ok := return_type == ast.ovoid_type
g.writeln(';')
if is_none_ok {
g.writeln('if (${cvar_name}.state != 0 && ${cvar_name}.err._typ != _IError_None___index) {')
} else {
+ if return_type != 0 && g.table.sym(return_type).kind == .function {
+ mr_styp = 'voidptr'
+ }
g.writeln('if (${cvar_name}.state != 0) { /*or block*/ ')
}
if or_block.kind == .block {
@@ -5106,6 +5148,8 @@ fn (mut g Gen) go_expr(node ast.GoExpr) {
} else if mut expr.left is ast.AnonFn {
g.gen_anon_fn_decl(mut expr.left)
name = expr.left.decl.name
+ } else if expr.is_fn_var {
+ name = g.table.sym(expr.fn_var_type).name
}
name = util.no_dots(name)
if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
@@ -5119,7 +5163,8 @@ fn (mut g Gen) go_expr(node ast.GoExpr) {
panic('cgen: obf name "$key" not found, this should never happen')
}
}
- g.writeln('// go')
+ g.empty_line = true
+ g.writeln('// start go')
wrapper_struct_name := 'thread_arg_' + name
wrapper_fn_name := name + '_thread_wrapper'
arg_tmp_var := 'arg_' + tmp
@@ -5158,7 +5203,7 @@ fn (mut g Gen) go_expr(node ast.GoExpr) {
} else {
'thread_$tmp'
}
- g.writeln('HANDLE $simple_handle = CreateThread(0,0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0,0);')
+ g.writeln('HANDLE $simple_handle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0, 0);')
g.writeln('if (!$simple_handle) panic_lasterr(tos3("`go ${name}()`: "));')
if node.is_expr && node.call_expr.return_type != ast.void_type {
g.writeln('$gohandle_name thread_$tmp = {')
@@ -5177,7 +5222,7 @@ fn (mut g Gen) go_expr(node ast.GoExpr) {
g.writeln('pthread_detach(thread_$tmp);')
}
}
- g.writeln('// endgo\n')
+ g.writeln('// end go')
if node.is_expr {
handle = 'thread_$tmp'
// create wait handler for this return type if none exists
@@ -5643,7 +5688,8 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
if fargs.len > 1 {
methods_wrapper.write_string(', ')
}
- methods_wrapper.writeln('${fargs[1..].join(', ')});')
+ args := fargs[1..].join(', ')
+ methods_wrapper.writeln('$args);')
} else {
if parameter_name.starts_with('__shared__') {
methods_wrapper.writeln('${method_call}(${fargs.join(', ')}->val);')
diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v
index 9237cc16e8..29c07d8978 100644
--- a/vlib/v/gen/c/comptime.v
+++ b/vlib/v/gen/c/comptime.v
@@ -203,7 +203,7 @@ fn cgen_attrs(attrs []ast.Attr) []string {
fn (mut g Gen) comptime_at(node ast.AtExpr) {
if node.kind == .vmod_file {
- val := cnewlines(node.val.replace('\r', ''))
+ val := cescape_nonascii(util.smart_quote(node.val, false))
g.write('_SLIT("$val")')
} else {
val := node.val.replace('\\', '\\\\')
diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v
index 445da57c66..15ddbc5b97 100644
--- a/vlib/v/gen/c/fn.v
+++ b/vlib/v/gen/c/fn.v
@@ -612,6 +612,25 @@ fn (mut g Gen) fn_decl_params(params []ast.Param, scope &ast.Scope, is_variadic
return fargs, fargtypes, heap_promoted
}
+fn (mut g Gen) get_anon_fn_type_name(mut node ast.AnonFn, var_name string) string {
+ mut builder := strings.new_builder(64)
+ return_styp := g.typ(node.decl.return_type)
+ builder.write_string('$return_styp (*$var_name) (')
+ if node.decl.params.len == 0 {
+ builder.write_string('void)')
+ } else {
+ for i, param in node.decl.params {
+ param_styp := g.typ(param.typ)
+ builder.write_string('$param_styp $param.name')
+ if i != node.decl.params.len - 1 {
+ builder.write_string(', ')
+ }
+ }
+ builder.write_string(')')
+ }
+ return builder.str()
+}
+
fn (mut g Gen) call_expr(node ast.CallExpr) {
// g.write('/*call expr*/')
// NOTE: everything could be done this way
diff --git a/vlib/v/gen/c/if.v b/vlib/v/gen/c/if.v
index 9ae3015754..bf1c31ca57 100644
--- a/vlib/v/gen/c/if.v
+++ b/vlib/v/gen/c/if.v
@@ -115,92 +115,91 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
cvar_name := guard_vars[guard_idx]
g.writeln('\tIError err = ${cvar_name}.err;')
}
- } else {
- match branch.cond {
- ast.IfGuardExpr {
- 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('if (${var_name}.state == 0) {')
+ } else if branch.cond is ast.IfGuardExpr {
+ 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('if (${var_name}.state == 0) {')
+ } else {
+ g.write('if ($var_name = ')
+ g.expr(branch.cond.expr)
+ g.writeln(', ${var_name}.state == 0) {')
+ }
+ if short_opt || branch.cond.vars[0].name != '_' {
+ base_type := g.base_type(branch.cond.expr_type)
+ if short_opt {
+ cond_var_name := if branch.cond.vars[0].name == '_' {
+ '_dummy_${g.tmp_count + 1}'
} else {
- g.write('if ($var_name = ')
- g.expr(branch.cond.expr)
- g.writeln(', ${var_name}.state == 0) {')
+ branch.cond.vars[0].name
}
- if short_opt || branch.cond.vars[0].name != '_' {
- base_type := g.base_type(branch.cond.expr_type)
- if short_opt {
- cond_var_name := if branch.cond.vars[0].name == '_' {
- '_dummy_${g.tmp_count + 1}'
- } else {
- branch.cond.vars[0].name
- }
- g.write('\t$base_type $cond_var_name = ')
- g.expr(branch.cond.expr)
- g.writeln(';')
+ g.write('\t$base_type $cond_var_name = ')
+ g.expr(branch.cond.expr)
+ g.writeln(';')
+ } else {
+ mut is_auto_heap := false
+ if branch.stmts.len > 0 {
+ scope := g.file.scope.innermost(ast.Node(branch.stmts[branch.stmts.len - 1]).pos().pos)
+ if v := scope.find_var(branch.cond.vars[0].name) {
+ is_auto_heap = v.is_auto_heap
+ }
+ }
+ if branch.cond.vars.len == 1 {
+ left_var_name := c_name(branch.cond.vars[0].name)
+ if is_auto_heap {
+ g.writeln('\t$base_type* $left_var_name = HEAP($base_type, *($base_type*)${var_name}.data);')
} else {
- mut is_auto_heap := false
- if branch.stmts.len > 0 {
- scope := g.file.scope.innermost(ast.Node(branch.stmts[branch.stmts.len - 1]).pos().pos)
- if v := scope.find_var(branch.cond.vars[0].name) {
- is_auto_heap = v.is_auto_heap
- }
- }
- if branch.cond.vars.len == 1 {
- left_var_name := c_name(branch.cond.vars[0].name)
- if is_auto_heap {
- g.writeln('\t$base_type* $left_var_name = HEAP($base_type, *($base_type*)${var_name}.data);')
- } else {
- g.writeln('\t$base_type $left_var_name = *($base_type*)${var_name}.data;')
- }
- } else if branch.cond.vars.len > 1 {
- for vi, var in branch.cond.vars {
- left_var_name := c_name(var.name)
- sym := g.table.sym(branch.cond.expr_type)
- if sym.kind == .multi_return {
- mr_info := sym.info as ast.MultiReturn
- if mr_info.types.len == branch.cond.vars.len {
- var_typ := g.typ(mr_info.types[vi])
- if is_auto_heap {
- g.writeln('\t$var_typ* $left_var_name = (HEAP($base_type, *($base_type*)${var_name}.data).arg$vi);')
- } else {
- g.writeln('\t$var_typ $left_var_name = (*($base_type*)${var_name}.data).arg$vi;')
- }
- }
+ g.writeln('\t$base_type $left_var_name = *($base_type*)${var_name}.data;')
+ }
+ } else if branch.cond.vars.len > 1 {
+ for vi, var in branch.cond.vars {
+ left_var_name := c_name(var.name)
+ sym := g.table.sym(branch.cond.expr_type)
+ if sym.kind == .multi_return {
+ mr_info := sym.info as ast.MultiReturn
+ if mr_info.types.len == branch.cond.vars.len {
+ var_typ := g.typ(mr_info.types[vi])
+ if is_auto_heap {
+ g.writeln('\t$var_typ* $left_var_name = (HEAP($base_type, *($base_type*)${var_name}.data).arg$vi);')
+ } else {
+ g.writeln('\t$var_typ $left_var_name = (*($base_type*)${var_name}.data).arg$vi;')
}
}
}
}
}
}
- else {
- 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('if ')
- } else {
- g.write('if (')
- }
- g.expr(branch.cond)
- if no_needs_par {
- g.writeln(' {')
- } else {
- g.writeln(') {')
- }
+ }
+ } else {
+ 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('if ')
+ } else {
+ g.write('if (')
+ }
+ g.expr(branch.cond)
+ if no_needs_par {
+ g.writeln(' {')
+ } else {
+ g.writeln(') {')
+ }
}
if needs_tmp_var {
+ if node.is_expr && g.table.sym(node.typ).kind == .sum_type {
+ g.expected_cast_type = node.typ
+ }
g.stmts_with_tmp_var(branch.stmts, tmp)
+ g.expected_cast_type = 0
} else {
// restore if_expr stmt header pos
stmt_pos := g.nth_stmt_pos(0)
diff --git a/vlib/v/gen/c/index.v b/vlib/v/gen/c/index.v
index 08a041d9d5..8434eda7cc 100644
--- a/vlib/v/gen/c/index.v
+++ b/vlib/v/gen/c/index.v
@@ -62,6 +62,7 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
mut tmp_opt := ''
mut cur_line := ''
mut gen_or := node.or_expr.kind != .absent || node.is_option
+ mut tmp_left := ''
if sym.kind == .string {
if node.is_gated {
@@ -82,6 +83,15 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
}
g.expr(node.left)
} else if sym.kind == .array {
+ if !range.has_high {
+ tmp_left = g.new_tmp_var()
+ tmp_type := g.typ(node.left_type)
+ g.insert_before_stmt('${util.tabs(g.indent)}$tmp_type $tmp_left;')
+ // (tmp = expr, array_slice(...))
+ g.write('($tmp_left = ')
+ g.expr(node.left)
+ g.write(', ')
+ }
if node.is_gated {
g.write('array_slice_ni(')
} else {
@@ -90,7 +100,11 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
if node.left_type.is_ptr() {
g.write('*')
}
- g.expr(node.left)
+ if range.has_high {
+ g.expr(node.left)
+ } else {
+ g.write(tmp_left)
+ }
} else if sym.kind == .array_fixed {
// Convert a fixed array to V array when doing `fixed_arr[start..end]`
info := sym.info as ast.ArrayFixed
@@ -101,17 +115,8 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
g.write('array_slice(')
}
g.write('new_array_from_c_array${noscan}(')
- g.write('$info.size')
- g.write(', $info.size')
- g.write(', sizeof(')
- if node.left_type.is_ptr() {
- g.write('(*')
- }
- g.expr(node.left)
- if node.left_type.is_ptr() {
- g.write(')')
- }
- g.write('[0]), ')
+ ctype := g.typ(info.elem_type)
+ g.write('$info.size, $info.size, sizeof($ctype), ')
if node.left_type.is_ptr() {
g.write('*')
}
@@ -132,15 +137,17 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
} else if sym.kind == .array_fixed {
info := sym.info as ast.ArrayFixed
g.write('$info.size')
- } else if node.left_type.is_ptr() {
- g.write('(')
- g.write('*')
- g.expr(node.left)
- g.write(')')
- g.write('.len')
+ } else if sym.kind == .array {
+ if node.left_type.is_ptr() {
+ g.write('$tmp_left->')
+ } else {
+ g.write('${tmp_left}.')
+ }
+ g.write('len)')
} else {
+ g.write('(')
g.expr(node.left)
- g.write('.len')
+ g.write(').len')
}
g.write(')')
@@ -157,23 +164,22 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
gen_or := node.or_expr.kind != .absent || node.is_option
left_is_ptr := node.left_type.is_ptr()
info := sym.info as ast.Array
- elem_type_str := g.typ(info.elem_type)
+ mut elem_type_str := g.typ(info.elem_type)
elem_type := info.elem_type
- elem_typ := g.table.sym(elem_type)
+ elem_sym := g.table.sym(elem_type)
+ if elem_sym.kind == .function {
+ elem_type_str = 'voidptr'
+ }
// `vals[i].field = x` is an exception and requires `array_get`:
// `(*(Val*)array_get(vals, i)).field = x;`
is_selector := node.left is ast.SelectorExpr
if g.is_assign_lhs && !is_selector && node.is_setter {
is_direct_array_access := (g.fn_decl != 0 && g.fn_decl.is_direct_arr) || node.is_direct
is_op_assign := g.assign_op != .assign && info.elem_type != ast.string_type
- array_ptr_type_str := match elem_typ.kind {
- .function { 'voidptr*' }
- else { '$elem_type_str*' }
- }
if is_direct_array_access {
- g.write('(($array_ptr_type_str)')
+ g.write('(($elem_type_str*)')
} else if is_op_assign {
- g.write('(*($array_ptr_type_str)array_get(')
+ g.write('(*($elem_type_str*)array_get(')
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
g.write('*')
}
@@ -217,11 +223,7 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
}
*/
if need_wrapper {
- if elem_typ.kind == .function {
- g.write(', &(voidptr[]) { ')
- } else {
- g.write(', &($elem_type_str[]) { ')
- }
+ g.write(', &($elem_type_str[]) { ')
} else {
g.write(', &')
}
@@ -232,10 +234,6 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
}
} else {
is_direct_array_access := (g.fn_decl != 0 && g.fn_decl.is_direct_arr) || node.is_direct
- array_ptr_type_str := match elem_typ.kind {
- .function { 'voidptr*' }
- else { '$elem_type_str*' }
- }
// do not clone inside `opt_ok(opt_ok(&(string[]) {..})` before returns
needs_clone := info.elem_type == ast.string_type_idx && g.is_autofree && !(g.inside_return
&& g.fn_decl.return_type.has_flag(.optional)) && !g.is_assign_lhs
@@ -250,24 +248,24 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
tmp_opt_ptr := if gen_or { g.new_tmp_var() } else { '' }
if gen_or {
- g.write('$array_ptr_type_str $tmp_opt_ptr = ($array_ptr_type_str)/*ee elem_ptr_typ */(array_get_with_check(')
+ g.write('$elem_type_str* $tmp_opt_ptr = ($elem_type_str*)/*ee elem_ptr_typ */(array_get_with_check(')
} else {
if needs_clone {
g.write('/*2*/string_clone(')
}
if g.is_fn_index_call {
- if elem_typ.info is ast.FnType {
+ if elem_sym.info is ast.FnType {
g.write('((')
- g.write_fn_ptr_decl(&elem_typ.info, '')
- g.write(')(*($array_ptr_type_str)/*ee elem_typ */array_get(')
+ g.write_fn_ptr_decl(&elem_sym.info, '')
+ g.write(')(*($elem_type_str*)/*ee elem_sym */array_get(')
}
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
g.write('*')
}
} else if is_direct_array_access {
- g.write('(($array_ptr_type_str)')
+ g.write('(($elem_type_str*)')
} else {
- g.write('(*($array_ptr_type_str)/*ee elem_typ */array_get(')
+ g.write('(*($elem_type_str*)/*ee elem_sym */array_get(')
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
g.write('*')
}
@@ -358,9 +356,12 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
info := sym.info as ast.Map
key_type_str := g.typ(info.key_type)
elem_type := info.value_type
- elem_type_str := g.typ(elem_type)
- elem_typ := g.table.sym(elem_type)
- get_and_set_types := elem_typ.kind in [.struct_, .map]
+ mut elem_type_str := g.typ(elem_type)
+ elem_sym := g.table.sym(elem_type)
+ if elem_sym.kind == .function {
+ elem_type_str = 'voidptr'
+ }
+ get_and_set_types := elem_sym.kind in [.struct_, .map]
if g.is_assign_lhs && !g.is_arraymap_set && !get_and_set_types {
if g.assign_op == .assign || info.value_type == ast.string_type {
g.is_arraymap_set = true
@@ -394,12 +395,8 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
g.is_arraymap_set = old_is_arraymap_set
g.is_assign_lhs = old_is_assign_lhs
g.write('}')
- if elem_typ.kind == .function {
- g.write(', &(voidptr[]) { ')
- } else {
- g.arraymap_set_pos = g.out.len
- g.write(', &($elem_type_str[]) { ')
- }
+ g.arraymap_set_pos = g.out.len
+ g.write(', &($elem_type_str[]) { ')
if g.assign_op != .assign && info.value_type != ast.string_type {
zero := g.type_default(info.value_type)
g.write('$zero })))')
@@ -438,13 +435,11 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
g.write('$elem_type_str* $tmp_opt_ptr = ($elem_type_str*)/*ee elem_ptr_typ */(map_get_check(')
} else {
if g.is_fn_index_call {
- if elem_typ.info is ast.FnType {
+ if elem_sym.info is ast.FnType {
g.write('((')
- g.write_fn_ptr_decl(&elem_typ.info, '')
+ g.write_fn_ptr_decl(&elem_sym.info, '')
g.write(')(*(voidptr*)map_get(')
}
- } else if elem_typ.kind == .function {
- g.write('(*(voidptr*)map_get(')
} else {
g.write('(*($elem_type_str*)map_get(')
}
@@ -470,8 +465,6 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
g.write('))')
} else if g.is_fn_index_call {
g.write(', &(voidptr[]){ $zero })))')
- } else if elem_typ.kind == .function {
- g.write(', &(voidptr[]){ $zero }))')
} else {
g.write(', &($elem_type_str[]){ $zero }))')
}
diff --git a/vlib/v/gen/c/infix_expr.v b/vlib/v/gen/c/infix_expr.v
index af061cf77d..61a47508da 100644
--- a/vlib/v/gen/c/infix_expr.v
+++ b/vlib/v/gen/c/infix_expr.v
@@ -507,7 +507,7 @@ fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
else { ast.Type(0) }
}
sub_sym := g.table.sym(sub_type)
- g.write('_${c_name(sym.name)}_${c_name(sub_sym.name)}_index')
+ g.write('_${sym.cname}_${sub_sym.cname}_index')
return
} else if sym.kind == .sum_type {
g.write('_typ $cmp_op ')
diff --git a/vlib/v/gen/c/match.v b/vlib/v/gen/c/match.v
index 7dd059723d..bb07b2ce4e 100644
--- a/vlib/v/gen/c/match.v
+++ b/vlib/v/gen/c/match.v
@@ -233,7 +233,7 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
}
g.writeln(') {')
g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
- g.writeln('break;')
+ g.writeln('\tbreak;')
g.writeln('}')
}
g.indent--
@@ -259,7 +259,8 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
}
g.stmts_with_tmp_var(branch.stmts, tmp_var)
g.expected_cast_type = 0
- g.writeln('} break;')
+ g.writeln('\tbreak;')
+ g.writeln('}')
g.indent--
}
if range_branches.len > 0 && !default_generated {
@@ -297,7 +298,7 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
}
g.writeln(') {')
g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
- g.writeln('break;')
+ g.writeln('\tbreak;')
g.writeln('}')
}
g.indent--
diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v
index fc9f1090e5..4640078364 100644
--- a/vlib/v/gen/c/str.v
+++ b/vlib/v/gen/c/str.v
@@ -70,8 +70,8 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
typ = typ.clear_flag(.shared_f).set_nr_muls(0)
}
mut sym := g.table.sym(typ)
- // when type is alias, print the aliased value
- if mut sym.info is ast.Alias {
+ // when type is alias and doesn't has `str()`, print the aliased value
+ if mut sym.info is ast.Alias && !sym.has_method('str') {
parent_sym := g.table.sym(sym.info.parent_type)
if parent_sym.has_method('str') {
typ = sym.info.parent_type
diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v
index 92cfa0074c..6b5d793c99 100644
--- a/vlib/v/parser/expr.v
+++ b/vlib/v/parser/expr.v
@@ -604,6 +604,13 @@ fn (mut p Parser) prefix_expr() ast.Expr {
return right
}
}
+ if mut right is ast.ParExpr {
+ if right.expr is ast.StructInit {
+ p.note_with_pos('unnecessary `()`, use `&$right.expr` instead of `&($right.expr)`',
+ right.pos)
+ right = right.expr
+ }
+ }
}
mut or_stmts := []ast.Stmt{}
mut or_kind := ast.OrKind.absent
diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v
index 0bad3f9a63..0a993d033f 100644
--- a/vlib/v/parser/if_match.v
+++ b/vlib/v/parser/if_match.v
@@ -107,6 +107,10 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
p.check(.decl_assign)
comments << p.eat_comments()
expr := p.expr(0)
+ if expr !in [ast.CallExpr, ast.IndexExpr, ast.PrefixExpr] {
+ p.error_with_pos('if guard condition expression is illegal, it should return optional',
+ expr.pos())
+ }
cond = ast.IfGuardExpr{
vars: vars
diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v
index c4468d242b..5608996559 100644
--- a/vlib/v/parser/parser.v
+++ b/vlib/v/parser/parser.v
@@ -1943,6 +1943,7 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
p.register_auto_import('sync')
}
mut_pos := p.tok.pos()
+ modifier_kind := p.tok.kind
is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
if is_mut {
p.next()
@@ -1956,7 +1957,11 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
p.next()
}
if p.tok.kind != .name {
- p.error('unexpected token `$p.tok.lit`')
+ if is_mut || is_static || is_volatile {
+ p.error_with_pos('the `$modifier_kind` keyword is invalid here', mut_pos)
+ } else {
+ p.error('unexpected token `$p.tok.lit`')
+ }
return ast.Ident{
scope: p.scope
}
@@ -2190,7 +2195,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
}
// Raw string (`s := r'hello \n ')
if p.peek_tok.kind == .string && !p.inside_str_interp && p.peek_token(2).kind != .colon {
- if p.tok.lit in ['r', 'c', 'js'] && p.tok.kind == .name {
+ if p.tok.kind == .name && p.tok.lit in ['r', 'c', 'js'] {
return p.string_expr()
} else {
// don't allow any other string prefix except `r`, `js` and `c`
@@ -2198,7 +2203,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
}
}
// don't allow r`byte` and c`byte`
- if p.tok.lit in ['r', 'c'] && p.peek_tok.kind == .chartoken {
+ if p.peek_tok.kind == .chartoken && p.tok.lit.len == 1 && p.tok.lit[0] in [`r`, `c`] {
opt := if p.tok.lit == 'r' { '`r` (raw string)' } else { '`c` (c string)' }
return p.error('cannot use $opt with `byte` and `rune`')
}
diff --git a/vlib/v/parser/tests/if_guard_cond_err.out b/vlib/v/parser/tests/if_guard_cond_err.out
new file mode 100644
index 0000000000..70a11b41f5
--- /dev/null
+++ b/vlib/v/parser/tests/if_guard_cond_err.out
@@ -0,0 +1,7 @@
+vlib/v/parser/tests/if_guard_cond_err.vv:16:16: error: if guard condition expression is illegal, it should return optional
+ 14 | fp.usage_example('GOOG AAPL')
+ 15 | _ := fp.bool('version', `v`, false, 'version information.')
+ 16 | if args := fp.finalize() && args.len > 0 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 17 | return args
+ 18 | } else {
diff --git a/vlib/v/parser/tests/if_guard_cond_err.vv b/vlib/v/parser/tests/if_guard_cond_err.vv
new file mode 100644
index 0000000000..69de3f19ec
--- /dev/null
+++ b/vlib/v/parser/tests/if_guard_cond_err.vv
@@ -0,0 +1,28 @@
+import os
+import flag
+
+const version = "v0.1.0"
+
+// getting command line options and arguments
+// returns the arguments
+fn get_args() ?[]string {
+ mut fp := flag.new_flag_parser(os.args)
+ fp.application('ticker')
+ fp.version(version)
+ fp.description('A CLI yahoo ticker app')
+ fp.skip_executable()
+ fp.usage_example('GOOG AAPL')
+ _ := fp.bool('version', `v`, false, 'version information.')
+ if args := fp.finalize() && args.len > 0 {
+ return args
+ } else {
+ eprintln(err.msg())
+ println(fp.usage())
+ return none
+ }
+}
+
+fn main() {
+ tickers := get_args() or { return }
+ println(tickers)
+}
diff --git a/vlib/v/parser/tests/invalid_using_atomic.out b/vlib/v/parser/tests/invalid_using_atomic.out
new file mode 100644
index 0000000000..37b5c030f4
--- /dev/null
+++ b/vlib/v/parser/tests/invalid_using_atomic.out
@@ -0,0 +1,6 @@
+vlib/v/parser/tests/invalid_using_atomic.vv:2:5: error: the `atomic` keyword is invalid here
+ 1 | fn main() {
+ 2 | if atomic true {
+ | ~~~~~~
+ 3 | println(true)
+ 4 | }
diff --git a/vlib/v/parser/tests/invalid_using_atomic.vv b/vlib/v/parser/tests/invalid_using_atomic.vv
new file mode 100644
index 0000000000..5d9b58aaad
--- /dev/null
+++ b/vlib/v/parser/tests/invalid_using_atomic.vv
@@ -0,0 +1,5 @@
+fn main() {
+ if atomic true {
+ println(true)
+ }
+}
diff --git a/vlib/v/parser/tests/invalid_using_mut.out b/vlib/v/parser/tests/invalid_using_mut.out
new file mode 100644
index 0000000000..fff45d7a9d
--- /dev/null
+++ b/vlib/v/parser/tests/invalid_using_mut.out
@@ -0,0 +1,6 @@
+vlib/v/parser/tests/invalid_using_mut.vv:2:5: error: the `mut` keyword is invalid here
+ 1 | fn main() {
+ 2 | if mut true {
+ | ~~~
+ 3 | println(true)
+ 4 | }
diff --git a/vlib/v/parser/tests/unnecessary_mut_2.vv b/vlib/v/parser/tests/invalid_using_mut.vv
similarity index 100%
rename from vlib/v/parser/tests/unnecessary_mut_2.vv
rename to vlib/v/parser/tests/invalid_using_mut.vv
diff --git a/vlib/v/parser/tests/invalid_using_shared.out b/vlib/v/parser/tests/invalid_using_shared.out
new file mode 100644
index 0000000000..318569239c
--- /dev/null
+++ b/vlib/v/parser/tests/invalid_using_shared.out
@@ -0,0 +1,6 @@
+vlib/v/parser/tests/invalid_using_shared.vv:2:5: error: the `shared` keyword is invalid here
+ 1 | fn main() {
+ 2 | if shared true {
+ | ~~~~~~
+ 3 | println(true)
+ 4 | }
diff --git a/vlib/v/parser/tests/invalid_using_shared.vv b/vlib/v/parser/tests/invalid_using_shared.vv
new file mode 100644
index 0000000000..3c30c4e471
--- /dev/null
+++ b/vlib/v/parser/tests/invalid_using_shared.vv
@@ -0,0 +1,5 @@
+fn main() {
+ if shared true {
+ println(true)
+ }
+}
diff --git a/vlib/v/parser/tests/invalid_using_static.out b/vlib/v/parser/tests/invalid_using_static.out
new file mode 100644
index 0000000000..75bae06cfe
--- /dev/null
+++ b/vlib/v/parser/tests/invalid_using_static.out
@@ -0,0 +1,6 @@
+vlib/v/parser/tests/invalid_using_static.vv:2:5: error: the `static` keyword is invalid here
+ 1 | fn main() {
+ 2 | if static true {
+ | ~~~~~~
+ 3 | println(true)
+ 4 | }
diff --git a/vlib/v/parser/tests/invalid_using_static.vv b/vlib/v/parser/tests/invalid_using_static.vv
new file mode 100644
index 0000000000..c6a1ebc677
--- /dev/null
+++ b/vlib/v/parser/tests/invalid_using_static.vv
@@ -0,0 +1,5 @@
+fn main() {
+ if static true {
+ println(true)
+ }
+}
diff --git a/vlib/v/parser/tests/invalid_using_volatile.out b/vlib/v/parser/tests/invalid_using_volatile.out
new file mode 100644
index 0000000000..6b5f37bd80
--- /dev/null
+++ b/vlib/v/parser/tests/invalid_using_volatile.out
@@ -0,0 +1,6 @@
+vlib/v/parser/tests/invalid_using_volatile.vv:2:5: error: the `volatile` keyword is invalid here
+ 1 | fn main() {
+ 2 | if volatile true {
+ | ~~~~~~~~
+ 3 | println(true)
+ 4 | }
diff --git a/vlib/v/parser/tests/invalid_using_volatile.vv b/vlib/v/parser/tests/invalid_using_volatile.vv
new file mode 100644
index 0000000000..e5dfdece79
--- /dev/null
+++ b/vlib/v/parser/tests/invalid_using_volatile.vv
@@ -0,0 +1,5 @@
+fn main() {
+ if volatile true {
+ println(true)
+ }
+}
diff --git a/vlib/v/parser/tests/unnecessary_mut.out b/vlib/v/parser/tests/unnecessary_mut.out
deleted file mode 100644
index 4399c9f164..0000000000
--- a/vlib/v/parser/tests/unnecessary_mut.out
+++ /dev/null
@@ -1,7 +0,0 @@
-vlib/v/parser/tests/unnecessary_mut.vv:3:5: error: remove unnecessary `mut`
- 1 | fn main() {
- 2 | mut x := 0
- 3 | if mut x == 0 {
- | ~~~
- 4 | println(true)
- 5 | }
\ No newline at end of file
diff --git a/vlib/v/parser/tests/unnecessary_mut_2.out b/vlib/v/parser/tests/unnecessary_mut_2.out
deleted file mode 100644
index 5f7dfee46b..0000000000
--- a/vlib/v/parser/tests/unnecessary_mut_2.out
+++ /dev/null
@@ -1,6 +0,0 @@
-vlib/v/parser/tests/unnecessary_mut_2.vv:2:9: error: unexpected token `true`
- 1 | fn main() {
- 2 | if mut true {
- | ~~~~
- 3 | println(true)
- 4 | }
\ No newline at end of file
diff --git a/vlib/v/tests/array_get_anon_fn_value_test.v b/vlib/v/tests/array_get_anon_fn_value_test.v
new file mode 100644
index 0000000000..0af940aabc
--- /dev/null
+++ b/vlib/v/tests/array_get_anon_fn_value_test.v
@@ -0,0 +1,20 @@
+const numbers = [
+ fn () int {
+ return 1
+ },
+ fn () int {
+ return 2
+ },
+]
+
+fn test_array_get_anon_fn_value() {
+ num1 := numbers[0]
+ ret1 := num1()
+ println(ret1)
+ assert ret1 == 1
+
+ num2 := numbers[1]
+ ret2 := num2()
+ println(ret2)
+ assert ret2 == 2
+}
diff --git a/vlib/v/tests/cast_int_to_interface_test.v b/vlib/v/tests/cast_int_to_interface_test.v
new file mode 100644
index 0000000000..34b22a523f
--- /dev/null
+++ b/vlib/v/tests/cast_int_to_interface_test.v
@@ -0,0 +1,19 @@
+interface Any {}
+
+fn return_any(val Any) ?Any {
+ return val
+}
+
+fn test_cast_int_to_interface() {
+ code := 200
+ if an := return_any(code) {
+ if an is int {
+ println('an is an int!')
+ } else {
+ println('an is not an int!')
+ }
+ assert '$an' == 'Any(200)'
+ } else {
+ assert false
+ }
+}
diff --git a/vlib/v/tests/empty_interface_test.v b/vlib/v/tests/empty_interface_test.v
new file mode 100644
index 0000000000..ded28b335e
--- /dev/null
+++ b/vlib/v/tests/empty_interface_test.v
@@ -0,0 +1,14 @@
+interface Any {}
+
+fn print_out(x Any) string {
+ if x is string {
+ println(x)
+ return '$x'
+ }
+ return ''
+}
+
+fn test_empty_interface() {
+ ret := print_out('12345')
+ assert ret == '&12345'
+}
diff --git a/vlib/v/tests/go_anon_fn_variable_call_test.v b/vlib/v/tests/go_anon_fn_variable_call_test.v
new file mode 100644
index 0000000000..e5f199c1fb
--- /dev/null
+++ b/vlib/v/tests/go_anon_fn_variable_call_test.v
@@ -0,0 +1,35 @@
+fn sum1(a int, b int) int {
+ sum_func1 := fn (a int, b int) int {
+ return a + b
+ }
+ sum_func2 := sum_func1
+
+ g := go sum_func2(a, b)
+
+ result := g.wait()
+ return result
+}
+
+fn add(a int, b int) int {
+ return a + b
+}
+
+fn sum2(a int, b int) int {
+ sum_func1 := add
+ sum_func2 := sum_func1
+
+ g := go sum_func2(a, b)
+
+ result := g.wait()
+ return result
+}
+
+fn test_go_anon_fn_variable_call() {
+ ret1 := sum1(22, 33)
+ println(ret1)
+ assert ret1 == 55
+
+ ret2 := sum2(2, 3)
+ println(ret2)
+ assert ret2 == 5
+}
diff --git a/vlib/v/tests/go_array_wait_test.v b/vlib/v/tests/go_array_wait_test.v
index d5513ca53c..cdf1686b3c 100644
--- a/vlib/v/tests/go_array_wait_test.v
+++ b/vlib/v/tests/go_array_wait_test.v
@@ -1,3 +1,5 @@
+// vtest retry: 3
+
fn f(x f64) f64 {
y := x * x
return y
diff --git a/vlib/v/tests/if_expr_with_sumtype_test.v b/vlib/v/tests/if_expr_with_sumtype_test.v
new file mode 100644
index 0000000000..81bc07c7ea
--- /dev/null
+++ b/vlib/v/tests/if_expr_with_sumtype_test.v
@@ -0,0 +1,23 @@
+struct A_str {}
+
+struct B_str {}
+
+type Token = A_str | B_str
+
+fn next(mut v []Token) Token {
+ return if v.len > 0 { v.pop() } else { A_str{} }
+}
+
+fn test_if_expr_with_sumtype() {
+ mut arr := []Token{}
+ ret1 := next(mut arr)
+ println(ret1)
+ assert '$ret1' == 'Token(A_str{})'
+
+ arr << A_str{}
+ arr << B_str{}
+
+ ret2 := next(mut arr)
+ println(ret2)
+ assert '$ret2' == 'Token(B_str{})'
+}
diff --git a/vlib/v/tests/init_global_test.v b/vlib/v/tests/init_global_test.v
index b39d4d468b..4dae83c8f1 100644
--- a/vlib/v/tests/init_global_test.v
+++ b/vlib/v/tests/init_global_test.v
@@ -53,6 +53,11 @@ fn test_no_type() {
assert testmap['asd'] == -7.25
}
+fn test_fn_type() {
+ assert func2(22) == 22
+ assert func3(22) == '22'
+}
+
__global (
intmap map[string]int
numberfns map[string]fn () int
@@ -75,6 +80,13 @@ __global (
'asd': -7.25
'yxc': 3.125
}
+ func1 = fn () {}
+ func2 = fn (n int) int {
+ return n
+ }
+ func3 = fn (n int) string {
+ return '$n'
+ }
)
fn init() {
diff --git a/vlib/v/tests/inout/printing_alias_has_str_method.out b/vlib/v/tests/inout/printing_alias_has_str_method.out
new file mode 100644
index 0000000000..640bb73cf1
--- /dev/null
+++ b/vlib/v/tests/inout/printing_alias_has_str_method.out
@@ -0,0 +1,3 @@
+hello
+hello
+hello
diff --git a/vlib/v/tests/inout/printing_alias_has_str_method.vv b/vlib/v/tests/inout/printing_alias_has_str_method.vv
new file mode 100644
index 0000000000..3b79901af9
--- /dev/null
+++ b/vlib/v/tests/inout/printing_alias_has_str_method.vv
@@ -0,0 +1,12 @@
+type Byte = byte
+
+fn (b Byte) str() string {
+ return 'hello'
+}
+
+fn main() {
+ b := Byte(`a`)
+ println(b)
+ println(b.str())
+ println('$b')
+}
diff --git a/vlib/v/tests/keyword_escaping_test.v b/vlib/v/tests/keyword_escaping_test.v
new file mode 100644
index 0000000000..c96c5e47d4
--- /dev/null
+++ b/vlib/v/tests/keyword_escaping_test.v
@@ -0,0 +1,11 @@
+fn test_escapes() {
+ @if := 0
+ @for := 0
+ auto := 0
+ calloc := 0
+ stdout := 0
+ signed := 0
+ @true := 0
+ @false := 0
+ assert true
+}
diff --git a/vlib/v/tests/map_get_anon_fn_value_test.v b/vlib/v/tests/map_get_anon_fn_value_test.v
new file mode 100644
index 0000000000..6b0c10249c
--- /dev/null
+++ b/vlib/v/tests/map_get_anon_fn_value_test.v
@@ -0,0 +1,25 @@
+const numbers = {
+ 'one': fn () int {
+ return 1
+ }
+}
+
+fn test_map_get_anon_fn_value() {
+ num1 := numbers['one'] or {
+ fn () int {
+ return 2
+ }
+ }
+ ret1 := num1()
+ println(ret1)
+ assert ret1 == 1
+
+ num2 := numbers['two'] or {
+ fn () int {
+ return 2
+ }
+ }
+ ret2 := num2()
+ println(ret2)
+ assert ret2 == 2
+}
diff --git a/vlib/v/tests/modules/alias_to_another_module/alias.v b/vlib/v/tests/modules/alias_to_another_module/alias.v
new file mode 100644
index 0000000000..d92a17ea62
--- /dev/null
+++ b/vlib/v/tests/modules/alias_to_another_module/alias.v
@@ -0,0 +1,9 @@
+module alias_to_another_module
+
+import another_module
+
+pub type MyAlias = another_module.SomeStruct
+
+pub fn (m MyAlias) alias_method() int {
+ return 42
+}
diff --git a/vlib/v/tests/modules/another_module/module.v b/vlib/v/tests/modules/another_module/module.v
new file mode 100644
index 0000000000..a21270ea7c
--- /dev/null
+++ b/vlib/v/tests/modules/another_module/module.v
@@ -0,0 +1,12 @@
+module another_module
+
+pub struct SomeStruct {
+pub mut:
+ x int
+ y int
+ z int
+}
+
+pub fn (s SomeStruct) some_method() int {
+ return 999 + s.x + s.y + s.z
+}
diff --git a/vlib/v/tests/slice_rval_test.v b/vlib/v/tests/slice_rval_test.v
new file mode 100644
index 0000000000..2078f5b353
--- /dev/null
+++ b/vlib/v/tests/slice_rval_test.v
@@ -0,0 +1,8 @@
+fn test_arr_rval() {
+ a := [1, 2]
+ s := (*&a)[..]
+ assert s == a
+
+ b := unsafe { *&[]int(&a) }[..]
+ assert b == a
+}
diff --git a/vlib/v/tests/string_interpolation_float_fmt_test.v b/vlib/v/tests/string_interpolation_float_fmt_test.v
new file mode 100644
index 0000000000..37c4fe960a
--- /dev/null
+++ b/vlib/v/tests/string_interpolation_float_fmt_test.v
@@ -0,0 +1,13 @@
+fn test_string_interpolation_float_fmt() {
+ mut a := 76.295
+ eprintln('${a:8.2}')
+ assert '${a:8.2}' == ' 76.30'
+ eprintln('${a:8.2f}')
+ assert '${a:8.2f}' == ' 76.30'
+
+ a = 76.296
+ eprintln('${a:8.2}')
+ assert '${a:8.2}' == ' 76.30'
+ eprintln('${a:8.2f}')
+ assert '${a:8.2f}' == ' 76.30'
+}
diff --git a/vlib/v/tests/use_alias_from_another_module_in_struct_field_test.v b/vlib/v/tests/use_alias_from_another_module_in_struct_field_test.v
new file mode 100644
index 0000000000..ed4d194279
--- /dev/null
+++ b/vlib/v/tests/use_alias_from_another_module_in_struct_field_test.v
@@ -0,0 +1,14 @@
+import alias_to_another_module
+
+struct MyStruct {
+ myfield alias_to_another_module.MyAlias
+}
+
+fn test_using_struct_with_alias() {
+ m := MyStruct{
+ myfield: alias_to_another_module.MyAlias{1, 2, 3}
+ }
+ dump(m)
+ assert m.myfield.alias_method() == 42
+ assert m.myfield.some_method() == 1005
+}
diff --git a/vlib/v/tests/valgrind/struct_of_array_of_same_struct.v b/vlib/v/tests/valgrind/struct_of_array_of_same_struct.v
new file mode 100644
index 0000000000..7112117eb0
--- /dev/null
+++ b/vlib/v/tests/valgrind/struct_of_array_of_same_struct.v
@@ -0,0 +1,38 @@
+struct Abc {
+mut:
+ name string
+ children []Abc
+}
+
+[manualfree]
+fn main() {
+ mut a := &Abc{}
+ a.name = 'aaa'
+ a.children = [
+ Abc{
+ name: 'xyz'
+ children: [
+ Abc{
+ name: 'xx'
+ },
+ Abc{
+ name: 'yy'
+ },
+ ]
+ },
+ Abc{
+ name: 'def'
+ children: [
+ Abc{
+ name: 'dd'
+ },
+ Abc{
+ name: 'ee'
+ },
+ ]
+ },
+ ]
+ dump(a)
+ unsafe { a.free() }
+ unsafe { free(a) }
+}
diff --git a/vlib/v/util/util.v b/vlib/v/util/util.v
index b3affaf60d..41f0da4fca 100644
--- a/vlib/v/util/util.v
+++ b/vlib/v/util/util.v
@@ -36,6 +36,12 @@ const (
]
)
+const builtin_module_names = ['builtin', 'strconv', 'strings', 'dlmalloc']
+
+pub fn module_is_builtin(mod string) bool {
+ return mod in util.builtin_module_names
+}
+
pub fn tabs(n int) string {
return if n < util.const_tabs.len { util.const_tabs[n] } else { '\t'.repeat(n) }
}
diff --git a/vlib/v/vmod/parser.v b/vlib/v/vmod/parser.v
index 0eeb580e95..b0bd2e9a9d 100644
--- a/vlib/v/vmod/parser.v
+++ b/vlib/v/vmod/parser.v
@@ -2,6 +2,8 @@ module vmod
import os
+const err_label = 'vmod:'
+
enum TokenKind {
module_keyword
field_key
@@ -32,6 +34,7 @@ pub mut:
struct Scanner {
mut:
pos int
+ line int = 1
text string
inside_text bool
tokens []Token
@@ -44,8 +47,9 @@ mut:
}
struct Token {
- typ TokenKind
- val string
+ typ TokenKind
+ val string
+ line int
}
pub fn from_file(vmod_path string) ?Manifest {
@@ -67,7 +71,7 @@ pub fn decode(contents string) ?Manifest {
}
fn (mut s Scanner) tokenize(t_type TokenKind, val string) {
- s.tokens << Token{t_type, val}
+ s.tokens << Token{t_type, val, s.line}
}
fn (mut s Scanner) skip_whitespace() {
@@ -82,7 +86,7 @@ fn is_name_alpha(chr byte) bool {
fn (mut s Scanner) create_string(q byte) string {
mut str := ''
- for s.text[s.pos] != q {
+ for s.pos < s.text.len && s.text[s.pos] != q {
if s.text[s.pos] == `\\` && s.text[s.pos + 1] == q {
str += s.text[s.pos..s.pos + 1]
s.pos += 2
@@ -96,7 +100,7 @@ fn (mut s Scanner) create_string(q byte) string {
fn (mut s Scanner) create_ident() string {
mut text := ''
- for is_name_alpha(s.text[s.pos]) {
+ for s.pos < s.text.len && is_name_alpha(s.text[s.pos]) {
text += s.text[s.pos].ascii_str()
s.pos++
}
@@ -112,6 +116,9 @@ fn (mut s Scanner) scan_all() {
c := s.text[s.pos]
if c.is_space() || c == `\\` {
s.pos++
+ if c == `\n` {
+ s.line++
+ }
continue
}
if is_name_alpha(c) {
@@ -120,7 +127,7 @@ fn (mut s Scanner) scan_all() {
s.tokenize(.module_keyword, name)
s.pos++
continue
- } else if s.text[s.pos] == `:` {
+ } else if s.pos < s.text.len && s.text[s.pos] == `:` {
s.tokenize(.field_key, name + ':')
s.pos += 2
continue
@@ -155,7 +162,7 @@ fn get_array_content(tokens []Token, st_idx int) ?([]string, int) {
mut vals := []string{}
mut idx := st_idx
if tokens[idx].typ != .labr {
- return error('vmod: not a valid array')
+ return error('$vmod.err_label not a valid array, at line ${tokens[idx].line}')
}
idx++
for {
@@ -164,7 +171,7 @@ fn get_array_content(tokens []Token, st_idx int) ?([]string, int) {
.str {
vals << tok.val
if tokens[idx + 1].typ !in [.comma, .rabr] {
- return error('vmod: invalid separator "${tokens[idx + 1].val}"')
+ return error('$vmod.err_label invalid separator "${tokens[idx + 1].val}", at line $tok.line')
}
idx += if tokens[idx + 1].typ == .comma { 2 } else { 1 }
}
@@ -173,7 +180,7 @@ fn get_array_content(tokens []Token, st_idx int) ?([]string, int) {
break
}
else {
- return error('vmod: invalid token "$tok.val"')
+ return error('$vmod.err_label invalid token "$tok.val", at line $tok.line')
}
}
}
@@ -181,15 +188,14 @@ fn get_array_content(tokens []Token, st_idx int) ?([]string, int) {
}
fn (mut p Parser) parse() ?Manifest {
- err_label := 'vmod:'
if p.scanner.text.len == 0 {
- return error('$err_label no content.')
+ return error('$vmod.err_label no content.')
}
p.scanner.scan_all()
tokens := p.scanner.tokens
mut mn := Manifest{}
if tokens[0].typ != .module_keyword {
- return error('vmod: v.mod files should start with Module')
+ return error('$vmod.err_label v.mod files should start with Module, at line ${tokens[0].line}')
}
mut i := 1
for i < tokens.len {
@@ -197,7 +203,7 @@ fn (mut p Parser) parse() ?Manifest {
match tok.typ {
.lcbr {
if tokens[i + 1].typ !in [.field_key, .rcbr] {
- return error('$err_label invalid content after opening brace')
+ return error('$vmod.err_label invalid content after opening brace, at line $tok.line')
}
i++
continue
@@ -208,7 +214,7 @@ fn (mut p Parser) parse() ?Manifest {
.field_key {
field_name := tok.val.trim_right(':')
if tokens[i + 1].typ !in [.str, .labr] {
- return error('$err_label value of field "$field_name" must be either string or an array of strings')
+ return error('$vmod.err_label value of field "$field_name" must be either string or an array of strings, at line $tok.line')
}
field_value := tokens[i + 1].val
match field_name {
@@ -251,13 +257,13 @@ fn (mut p Parser) parse() ?Manifest {
}
.comma {
if tokens[i - 1].typ !in [.str, .rabr] || tokens[i + 1].typ != .field_key {
- return error('$err_label invalid comma placement')
+ return error('$vmod.err_label invalid comma placement, at line $tok.line')
}
i++
continue
}
else {
- return error('$err_label invalid token "$tok.val"')
+ return error('$vmod.err_label invalid token "$tok.val", at line $tok.line')
}
}
}
diff --git a/vlib/v/vmod/parser_test.v b/vlib/v/vmod/parser_test.v
new file mode 100644
index 0000000000..3bd8b6e188
--- /dev/null
+++ b/vlib/v/vmod/parser_test.v
@@ -0,0 +1,51 @@
+import v.vmod
+
+const quote = '\x22'
+
+const apos = '\x27'
+
+fn test_ok() ? {
+ ok_source := "Module {
+ name: 'V'
+ description: 'The V programming language.'
+ version: '0.2.4'
+ license: 'MIT'
+ repo_url: 'https://github.com/vlang/v'
+ dependencies: []
+}"
+ for s in [ok_source, ok_source.replace(apos, quote), ok_source.replace('\n', '\r\n'),
+ ok_source.replace('\n', '\r\n '), ok_source.replace('\n', '\n ')] {
+ content := vmod.decode(s) ?
+ assert content.name == 'V'
+ assert content.description == 'The V programming language.'
+ assert content.version == '0.2.4'
+ assert content.license == 'MIT'
+ assert content.repo_url == 'https://github.com/vlang/v'
+ assert content.dependencies == []
+ assert content.unknown == {}
+ }
+ e := vmod.decode('Module{}') ?
+ assert e.name == ''
+ assert e.description == ''
+ assert e.version == ''
+ assert e.license == ''
+ assert e.repo_url == ''
+ assert e.dependencies == []
+ assert e.unknown == {}
+}
+
+fn test_invalid_start() ? {
+ vmod.decode('\n\nXYZ') or {
+ assert err.msg() == 'vmod: v.mod files should start with Module, at line 3'
+ return
+ }
+ assert false
+}
+
+fn test_invalid_end() ? {
+ vmod.decode('\nModule{\n \nname: ${quote}zzzz}') or {
+ assert err.msg() == 'vmod: invalid token ${quote}eof$quote, at line 4'
+ return
+ }
+ assert false
+}
diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v
index ad11e36b55..03551e9dcb 100644
--- a/vlib/vweb/vweb.v
+++ b/vlib/vweb/vweb.v
@@ -459,7 +459,12 @@ fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route) {
}
return
}
-
+ $if trace_request ? {
+ dump(req)
+ }
+ $if trace_request_url ? {
+ dump(req.url)
+ }
// URL Parse
url := urllib.parse(req.url) or {
eprintln('error parsing path: $err')
@@ -628,8 +633,14 @@ fn (mut ctx Context) scan_static_directory(directory_path string, mount_path str
}
}
-// Handles a directory static
+// handle_static is used to mark a folder (relative to the current working folder)
+// as one that contains only static resources (css files, images etc).
// If `root` is set the mount path for the dir will be in '/'
+// Usage:
+// ```v
+// os.chdir( os.executable() ) ?
+// app.handle_static('assets', true)
+// ```
pub fn (mut ctx Context) handle_static(directory_path string, root bool) bool {
if ctx.done || !os.exists(directory_path) {
return false
@@ -696,6 +707,9 @@ pub fn not_found() Result {
}
fn send_string(mut conn net.TcpConn, s string) ? {
+ $if trace_response ? {
+ eprintln('> send_string:\n$s\n')
+ }
conn.write(s.bytes()) ?
}
diff --git a/vlib/x/ttf/README.md b/vlib/x/ttf/README.md
index 811f281a55..2a1062332c 100644
--- a/vlib/x/ttf/README.md
+++ b/vlib/x/ttf/README.md
@@ -298,7 +298,7 @@ fn main() {
// TTF render 0 Frame counter
app.ttf_render << &ttf.TTF_render_Sokol{
bmp: &ttf.BitMap{
- tf: &(app.tf[0])
+ tf: &app.tf[0]
buf: unsafe { malloc(32000000) }
buf_size: (32000000)
color: 0xFF0000FF
diff --git a/vlib/x/x.v b/vlib/x/x.v
new file mode 100644
index 0000000000..10a4b5ea01
--- /dev/null
+++ b/vlib/x/x.v
@@ -0,0 +1,3 @@
+module x
+
+pub const description = 'an empty module, used as a placeholder, for other modules'