Compare commits
25 Commits
9a0f499506
...
f9079b6700
Author | SHA1 | Date |
---|---|---|
Delyan Angelov | f9079b6700 | |
yuyi | 7c7d59acee | |
Delyan Angelov | c5933aa3c5 | |
Delyan Angelov | 990a540851 | |
Delyan Angelov | 366ff17bff | |
Delyan Angelov | bdc1375d00 | |
yuyi | 5acc130c60 | |
Delyan Angelov | 6a93b3df2f | |
yuyi | 4549afd642 | |
yuyi | 09886f78d3 | |
Delyan Angelov | a1bd9acf82 | |
Delyan Angelov | 888c85dedc | |
Larpon | 21308289ad | |
Adam Oates | 88c4e8db29 | |
yuyi | b37130e664 | |
Larpon | c5d93afdc1 | |
Delyan Angelov | 3bae3a23df | |
Delyan Angelov | 055c33e2ca | |
playX | 84c1f642f5 | |
spaceface | a2985d652a | |
Delyan Angelov | c78022c583 | |
Delyan Angelov | 68569ae598 | |
Delyan Angelov | 4858a113a7 | |
Alexander Medvednikov | f66e514430 | |
Ned | 5ada79c629 |
|
@ -60,7 +60,7 @@ jobs:
|
|||
- name: Self tests
|
||||
run: ./v test-self
|
||||
# - name: Self tests (-cstrict)
|
||||
# run: ./v -cstrict test-self
|
||||
# run: V_CI_CSTRICT=1 ./v -cstrict test-self
|
||||
- name: Test time functions in a timezone UTC-12
|
||||
run: TZ=Etc/GMT+12 ./v test vlib/time/
|
||||
- name: Test time functions in a timezone UTC-3
|
||||
|
@ -194,7 +194,7 @@ jobs:
|
|||
./v cmd/tools/test_if_v_test_system_works.v
|
||||
./cmd/tools/test_if_v_test_system_works
|
||||
- name: All code is formatted
|
||||
run: ./v test-cleancode
|
||||
run: VJOBS=1 ./v test-cleancode
|
||||
- name: Self tests
|
||||
run: VJOBS=1 ./v test-self
|
||||
- name: Build examples
|
||||
|
@ -294,7 +294,7 @@ jobs:
|
|||
- name: Self tests (-prod)
|
||||
run: ./v -o vprod -prod cmd/v && ./vprod test-self
|
||||
- name: Self tests (-cstrict)
|
||||
run: ./v -cc gcc -cstrict test-self
|
||||
run: V_CI_CSTRICT=1 ./v -cc gcc -cstrict test-self
|
||||
- name: Build examples
|
||||
run: ./v build-examples
|
||||
- name: Build tetris.v with -autofree
|
||||
|
@ -383,12 +383,16 @@ jobs:
|
|||
./cmd/tools/test_if_v_test_system_works
|
||||
- name: All code is formatted
|
||||
run: ./v test-cleancode
|
||||
|
||||
- name: Self tests
|
||||
run: ./v test-self
|
||||
- name: Self tests (-prod)
|
||||
run: ./v -o vprod -prod cmd/v && ./vprod test-self
|
||||
- name: Self tests (vprod)
|
||||
run: |
|
||||
./v -o vprod -prod cmd/v
|
||||
./vprod test-self
|
||||
- name: Self tests (-cstrict)
|
||||
run: ./v -cstrict test-self
|
||||
run: V_CI_CSTRICT=1 ./vprod -cstrict test-self
|
||||
|
||||
- name: Build examples
|
||||
run: ./v build-examples
|
||||
- name: Build examples with -autofree
|
||||
|
|
|
@ -2,57 +2,56 @@ name: Periodic
|
|||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 */2 * * *'
|
||||
- cron: '0 */6 * * *'
|
||||
|
||||
jobs:
|
||||
network-tests-ubuntu:
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
V_CI_PERIODIC: 1
|
||||
V_CI_PERIODIC: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install --quiet -y libssl-dev sqlite3 libsqlite3-dev valgrind
|
||||
- name: Build v
|
||||
run: make -j4
|
||||
- name: Symlink V
|
||||
run: sudo ./v symlink
|
||||
## - name: Run network tests
|
||||
## run: ./v -d network test vlib/net
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies 1
|
||||
run: sudo apt-get install --quiet -y libssl-dev sqlite3 libsqlite3-dev
|
||||
- name: Build v
|
||||
run: make
|
||||
- name: Symlink V
|
||||
run: sudo ./v symlink
|
||||
## - name: Run network tests
|
||||
## run: ./v -d network test vlib/net
|
||||
|
||||
|
||||
network-tests-macos:
|
||||
runs-on: macOS-latest
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
V_CI_PERIODIC: 1
|
||||
V_CI_PERIODIC: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup openssl library path
|
||||
run: export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/opt/openssl/lib/"
|
||||
- name: Build V
|
||||
run: make -j4
|
||||
- name: Symlink V
|
||||
run: sudo ./v symlink
|
||||
- name: Ensure thirdparty/cJSON/cJSON.o is compiled, before running tests.
|
||||
run: ./v examples/json.v
|
||||
## - name: Run network tests
|
||||
## run: ./v -d network test vlib/net
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup openssl library path
|
||||
run: export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/opt/openssl/lib/"
|
||||
- name: Build V
|
||||
run: make
|
||||
- name: Symlink V
|
||||
run: sudo ./v symlink
|
||||
- name: Ensure thirdparty/cJSON/cJSON.o is compiled, before running tests.
|
||||
run: ./v examples/json.v
|
||||
## - name: Run network tests
|
||||
## run: ./v -d network test vlib/net
|
||||
|
||||
network-windows-msvc:
|
||||
runs-on: windows-2019
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
V_CI_PERIODIC: 1
|
||||
VFLAGS: -cc msvc
|
||||
V_CI_PERIODIC: 1
|
||||
VFLAGS: -cc msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: |
|
||||
echo %VFLAGS%
|
||||
echo $VFLAGS
|
||||
.\make.bat -msvc
|
||||
## - name: Run network tests
|
||||
## run: .\v.exe -d network test vlib/net
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: |
|
||||
echo %VFLAGS%
|
||||
echo $VFLAGS
|
||||
.\make.bat -msvc
|
||||
## - name: Run network tests
|
||||
## run: .\v.exe -d network test vlib/net
|
||||
|
|
|
@ -76,7 +76,7 @@ SUBCMD:
|
|||
|
||||
// Snooped from cmd/v/v.v, vlib/v/pref/pref.v
|
||||
const (
|
||||
auto_complete_commands = [
|
||||
auto_complete_commands = [
|
||||
// simple_cmd
|
||||
'ast',
|
||||
'doc',
|
||||
|
@ -114,7 +114,6 @@ const (
|
|||
'help',
|
||||
'new',
|
||||
'init',
|
||||
'complete',
|
||||
'translate',
|
||||
'self',
|
||||
'search',
|
||||
|
@ -130,8 +129,13 @@ const (
|
|||
'run',
|
||||
'build',
|
||||
'build-module',
|
||||
'missdoc',
|
||||
]
|
||||
auto_complete_flags = [
|
||||
// Entries in the flag arrays below should be entered as is:
|
||||
// * Short flags, e.g.: "-v", should be entered: '-v'
|
||||
// * Long flags, e.g.: "--version", should be entered: '--version'
|
||||
// * Single-dash flags, e.g.: "-version", should be entered: '-version'
|
||||
auto_complete_flags = [
|
||||
'-apk',
|
||||
'-show-timings',
|
||||
'-check-syntax',
|
||||
|
@ -190,7 +194,7 @@ const (
|
|||
'-version',
|
||||
'--version',
|
||||
]
|
||||
auto_complete_flags_doc = [
|
||||
auto_complete_flags_doc = [
|
||||
'-all',
|
||||
'-f',
|
||||
'-h',
|
||||
|
@ -209,7 +213,7 @@ const (
|
|||
'-s',
|
||||
'-l',
|
||||
]
|
||||
auto_complete_flags_fmt = [
|
||||
auto_complete_flags_fmt = [
|
||||
'-c',
|
||||
'-diff',
|
||||
'-l',
|
||||
|
@ -217,7 +221,7 @@ const (
|
|||
'-debug',
|
||||
'-verify',
|
||||
]
|
||||
auto_complete_flags_bin2v = [
|
||||
auto_complete_flags_bin2v = [
|
||||
'-h',
|
||||
'--help',
|
||||
'-m',
|
||||
|
@ -227,22 +231,39 @@ const (
|
|||
'-w',
|
||||
'--write',
|
||||
]
|
||||
auto_complete_flags_shader = [
|
||||
'help',
|
||||
'h',
|
||||
'force-update',
|
||||
'u',
|
||||
'verbose',
|
||||
'v',
|
||||
'slang',
|
||||
'l',
|
||||
'output',
|
||||
'o',
|
||||
auto_complete_flags_shader = [
|
||||
'--help',
|
||||
'-h',
|
||||
'--force-update',
|
||||
'-u',
|
||||
'--verbose',
|
||||
'-v',
|
||||
'--slang',
|
||||
'-l',
|
||||
'--output',
|
||||
'-o',
|
||||
]
|
||||
auto_complete_flags_self = [
|
||||
auto_complete_flags_missdoc = [
|
||||
'--help',
|
||||
'-h',
|
||||
'--tags',
|
||||
'-t',
|
||||
'--deprecated',
|
||||
'-d',
|
||||
'--private',
|
||||
'-p',
|
||||
'--no-line-numbers',
|
||||
'-n',
|
||||
'--exclude',
|
||||
'-e',
|
||||
'--relative-paths',
|
||||
'-r',
|
||||
'--js',
|
||||
]
|
||||
auto_complete_flags_self = [
|
||||
'-prod',
|
||||
]
|
||||
auto_complete_compilers = [
|
||||
auto_complete_compilers = [
|
||||
'cc',
|
||||
'gcc',
|
||||
'tcc',
|
||||
|
@ -372,12 +393,17 @@ fn auto_complete_request(args []string) []string {
|
|||
parent_command = parts[i]
|
||||
break
|
||||
}
|
||||
get_flags := fn (base []string, flag string) []string {
|
||||
if flag.len == 1 { return base
|
||||
} else { return base.filter(it.starts_with(flag))
|
||||
}
|
||||
}
|
||||
if part.starts_with('-') { // 'v -<tab>' -> flags.
|
||||
if part.starts_with('-') { // 'v [subcmd] -<tab>' or 'v [subcmd] --<tab>'-> flags.
|
||||
get_flags := fn (base []string, flag string) []string {
|
||||
mut results := []string{}
|
||||
for entry in base {
|
||||
if entry.starts_with(flag) {
|
||||
results << entry
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
match parent_command {
|
||||
'bin2v' { // 'v bin2v -<tab>'
|
||||
list = get_flags(auto_complete_flags_bin2v, part)
|
||||
|
@ -397,6 +423,9 @@ fn auto_complete_request(args []string) []string {
|
|||
'shader' { // 'v shader -<tab>' -> flags.
|
||||
list = get_flags(auto_complete_flags_shader, part)
|
||||
}
|
||||
'missdoc' { // 'v missdoc -<tab>' -> flags.
|
||||
list = get_flags(auto_complete_flags_missdoc, part)
|
||||
}
|
||||
else {
|
||||
for flag in auto_complete_flags {
|
||||
if flag == part {
|
||||
|
@ -414,6 +443,11 @@ fn auto_complete_request(args []string) []string {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Clear the list if the result is identical to the part examined
|
||||
// (the flag must have already been completed)
|
||||
if list.len == 1 && part == list[0] {
|
||||
list.clear()
|
||||
}
|
||||
} else {
|
||||
match part {
|
||||
'help' { // 'v help <tab>' -> top level commands except "help".
|
||||
|
|
|
@ -5,52 +5,67 @@ import testing
|
|||
import v.util
|
||||
import arrays
|
||||
|
||||
const (
|
||||
vet_known_failing_exceptions = []string{}
|
||||
vet_folders = [
|
||||
'vlib/sqlite',
|
||||
'vlib/v',
|
||||
'vlib/x/json2',
|
||||
'vlib/x/ttf',
|
||||
'cmd/v',
|
||||
'cmd/tools',
|
||||
'examples/2048',
|
||||
'examples/tetris',
|
||||
'examples/term.ui',
|
||||
]
|
||||
verify_known_failing_exceptions = [
|
||||
// Handcrafted meaningful formatting of code parts (mostly arrays)
|
||||
'examples/sokol/02_cubes_glsl/cube_glsl.v',
|
||||
'examples/sokol/03_march_tracing_glsl/rt_glsl.v',
|
||||
'examples/sokol/04_multi_shader_glsl/rt_glsl.v',
|
||||
'examples/sokol/05_instancing_glsl/rt_glsl.v',
|
||||
'examples/sokol/06_obj_viewer/show_obj.v',
|
||||
'vlib/v/checker/tests/modules/deprecated_module/main.v' /* adds deprecated_module. module prefix to imports, even though the folder has v.mod */,
|
||||
'vlib/gg/m4/graphic.v',
|
||||
'vlib/gg/m4/m4_test.v',
|
||||
'vlib/gg/m4/matrix.v',
|
||||
'vlib/builtin/int_test.v' /* special number formatting that should be tested */,
|
||||
// TODOs and unfixed vfmt bugs
|
||||
'vlib/v/gen/js/tests/js.v', /* local `hello` fn, gets replaced with module `hello` aliased as `hl` */
|
||||
]
|
||||
vfmt_verify_list = [
|
||||
'cmd/',
|
||||
'examples/',
|
||||
'tutorials/',
|
||||
'vlib/',
|
||||
]
|
||||
vfmt_known_failing_exceptions = arrays.merge(verify_known_failing_exceptions, [
|
||||
'vlib/regex/regex_test.v' /* contains meaningfull formatting of the test case data */,
|
||||
'vlib/crypto/sha512/sha512block_generic.v' /* formatting of large constant arrays wraps to too many lines */,
|
||||
'vlib/crypto/aes/const.v' /* formatting of large constant arrays wraps to too many lines */,
|
||||
])
|
||||
)
|
||||
const vet_known_failing = [
|
||||
'do_not_delete_this',
|
||||
]
|
||||
|
||||
const (
|
||||
vexe = os.getenv('VEXE')
|
||||
vroot = os.dir(vexe)
|
||||
is_fix = '-fix' in os.args
|
||||
)
|
||||
const vet_known_failing_windows = [
|
||||
'do_not_delete_this',
|
||||
'vlib/v/gen/js/tests/testdata/byte_is_space.v',
|
||||
'vlib/v/gen/js/tests/testdata/compare_ints.v',
|
||||
'vlib/v/gen/js/tests/testdata/hw.v',
|
||||
'vlib/v/gen/js/tests/testdata/string_methods.v',
|
||||
'vlib/v/tests/project_with_modules_having_submodules/bin/main.vsh',
|
||||
'vlib/v/tests/valgrind/simple_interpolation_script_mode.v',
|
||||
'vlib/v/tests/valgrind/simple_interpolation_script_mode_more_scopes.v',
|
||||
]
|
||||
|
||||
const vet_folders = [
|
||||
'vlib/sqlite',
|
||||
'vlib/v',
|
||||
'vlib/x/json2',
|
||||
'vlib/x/ttf',
|
||||
'cmd/v',
|
||||
'cmd/tools',
|
||||
'examples/2048',
|
||||
'examples/tetris',
|
||||
'examples/term.ui',
|
||||
]
|
||||
|
||||
const verify_known_failing_exceptions = [
|
||||
// Handcrafted meaningful formatting of code parts (mostly arrays)
|
||||
'examples/sokol/02_cubes_glsl/cube_glsl.v',
|
||||
'examples/sokol/03_march_tracing_glsl/rt_glsl.v',
|
||||
'examples/sokol/04_multi_shader_glsl/rt_glsl.v',
|
||||
'examples/sokol/05_instancing_glsl/rt_glsl.v',
|
||||
'examples/sokol/06_obj_viewer/show_obj.v',
|
||||
'vlib/v/checker/tests/modules/deprecated_module/main.v' /* adds deprecated_module. module prefix to imports, even though the folder has v.mod */,
|
||||
'vlib/gg/m4/graphic.v',
|
||||
'vlib/gg/m4/m4_test.v',
|
||||
'vlib/gg/m4/matrix.v',
|
||||
'vlib/builtin/int_test.v' /* special number formatting that should be tested */,
|
||||
// TODOs and unfixed vfmt bugs
|
||||
'vlib/v/gen/js/tests/js.v', /* local `hello` fn, gets replaced with module `hello` aliased as `hl` */
|
||||
]
|
||||
|
||||
const vfmt_verify_list = [
|
||||
'cmd/',
|
||||
'examples/',
|
||||
'tutorials/',
|
||||
'vlib/',
|
||||
]
|
||||
|
||||
const vfmt_known_failing_exceptions = arrays.merge(verify_known_failing_exceptions, [
|
||||
'vlib/regex/regex_test.v' /* contains meaningfull formatting of the test case data */,
|
||||
'vlib/crypto/sha512/sha512block_generic.v' /* formatting of large constant arrays wraps to too many lines */,
|
||||
'vlib/crypto/aes/const.v' /* formatting of large constant arrays wraps to too many lines */,
|
||||
])
|
||||
|
||||
const vexe = os.getenv('VEXE')
|
||||
|
||||
const vroot = os.dir(vexe)
|
||||
|
||||
const is_fix = '-fix' in os.args
|
||||
|
||||
fn main() {
|
||||
args_string := os.args[1..].join(' ')
|
||||
|
@ -76,8 +91,12 @@ fn tsession(vargs string, tool_source string, tool_cmd string, tool_args string,
|
|||
|
||||
fn v_test_vetting(vargs string) {
|
||||
expanded_vet_list := util.find_all_v_files(vet_folders) or { return }
|
||||
mut vet_known_exceptions := vet_known_failing.clone()
|
||||
if os.user_os() == 'windows' {
|
||||
vet_known_exceptions << vet_known_failing_windows
|
||||
}
|
||||
vet_session := tsession(vargs, 'vvet', '${os.quoted_path(vexe)} vet', 'vet', expanded_vet_list,
|
||||
vet_known_failing_exceptions)
|
||||
vet_known_exceptions)
|
||||
//
|
||||
fmt_cmd, fmt_args := if is_fix {
|
||||
'${os.quoted_path(vexe)} fmt -w', 'fmt -w'
|
||||
|
|
|
@ -55,7 +55,7 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) {
|
|||
C.exit(1)
|
||||
}
|
||||
$if use_libbacktrace ? {
|
||||
print_libbacktrace(1)
|
||||
eprint_libbacktrace(1)
|
||||
} $else {
|
||||
print_backtrace_skipping_top_frames(1)
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ pub fn panic(s string) {
|
|||
C.exit(1)
|
||||
}
|
||||
$if use_libbacktrace ? {
|
||||
print_libbacktrace(1)
|
||||
eprint_libbacktrace(1)
|
||||
} $else {
|
||||
print_backtrace_skipping_top_frames(1)
|
||||
}
|
||||
|
|
|
@ -130,6 +130,10 @@ pub:
|
|||
[markused]
|
||||
fn v_segmentation_fault_handler(signal int) {
|
||||
eprintln('signal 11: segmentation fault')
|
||||
print_backtrace()
|
||||
$if use_libbacktrace ? {
|
||||
eprint_libbacktrace(1)
|
||||
} $else {
|
||||
print_backtrace()
|
||||
}
|
||||
exit(128 + 11)
|
||||
}
|
||||
|
|
|
@ -33,11 +33,11 @@ fn init_bt_state() &C.backtrace_state {
|
|||
}
|
||||
|
||||
// for bt_error_callback
|
||||
// struct BacktraceData {
|
||||
// state &C.backtrace_state
|
||||
// }
|
||||
struct BacktraceOptions {
|
||||
stdin bool = true
|
||||
}
|
||||
|
||||
fn bt_print_callback(data voidptr, pc voidptr, filename_ptr &char, line int, fn_name_ptr &char) int {
|
||||
fn bt_print_callback(data &BacktraceOptions, pc voidptr, filename_ptr &char, line int, fn_name_ptr &char) int {
|
||||
filename := if isnil(filename_ptr) { '???' } else { unsafe { filename_ptr.vstring() } }
|
||||
fn_name := if isnil(fn_name_ptr) {
|
||||
'???'
|
||||
|
@ -46,7 +46,12 @@ fn bt_print_callback(data voidptr, pc voidptr, filename_ptr &char, line int, fn_
|
|||
}
|
||||
// keep it for later
|
||||
// pc_64 := u64(pc)
|
||||
println('$filename:$line: by $fn_name')
|
||||
bt_str := '$filename:$line: by $fn_name'
|
||||
if data.stdin {
|
||||
println(bt_str)
|
||||
} else {
|
||||
eprintln(bt_str)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -81,6 +86,17 @@ fn print_libbacktrace(frames_to_skip int) {
|
|||
$if no_backtrace ? {
|
||||
return
|
||||
}
|
||||
// data := &BacktraceData{bt_state}
|
||||
C.backtrace_full(bt_state, frames_to_skip, bt_print_callback, bt_error_callback, 0)
|
||||
data := &BacktraceOptions{}
|
||||
C.backtrace_full(bt_state, frames_to_skip, bt_print_callback, bt_error_callback, data)
|
||||
}
|
||||
|
||||
[noinline]
|
||||
fn eprint_libbacktrace(frames_to_skip int) {
|
||||
$if no_backtrace ? {
|
||||
return
|
||||
}
|
||||
data := &BacktraceOptions{
|
||||
stdin: false
|
||||
}
|
||||
C.backtrace_full(bt_state, frames_to_skip, bt_print_callback, bt_error_callback, data)
|
||||
}
|
||||
|
|
|
@ -2,3 +2,7 @@ module builtin
|
|||
|
||||
fn print_libbacktrace(frames_to_skip int) {
|
||||
}
|
||||
|
||||
[noinline]
|
||||
fn eprint_libbacktrace(frames_to_skip int) {
|
||||
}
|
||||
|
|
|
@ -433,7 +433,7 @@ fn test_set_bit() {
|
|||
assert a == b
|
||||
}
|
||||
|
||||
fn test_bit_len() {
|
||||
fn test_bit_len() ? {
|
||||
assert big.zero_int.bit_len() == 0
|
||||
assert big.one_int.bit_len() == 1
|
||||
|
||||
|
@ -441,7 +441,10 @@ fn test_bit_len() {
|
|||
|
||||
assert big.one_int.lshift(1239).bit_len() == 1240
|
||||
|
||||
assert big.integer_from_string('4338476092346017364013796407961305761039463198075691378460917856') or {
|
||||
panic('Could not read from decimal')
|
||||
}.bit_len() == 212
|
||||
mut num := big.integer_from_string('4338476092346017364013796407961305761039463198075691378460917856')?
|
||||
assert num.bit_len() == 212
|
||||
for num.bit_len() > 0 {
|
||||
num = num.rshift(8)
|
||||
assert true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -960,6 +960,8 @@ pub fn (x Integer) bit_len() int {
|
|||
if x.signum == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if x.digits.len == 0 {
|
||||
return 0
|
||||
}
|
||||
return x.digits.len * 32 - bits.leading_zeros_32(x.digits.last())
|
||||
}
|
||||
|
|
22
vlib/os/os.v
22
vlib/os/os.v
|
@ -343,6 +343,28 @@ pub fn user_os() string {
|
|||
return 'unknown'
|
||||
}
|
||||
|
||||
// user_names returns an array of the name of every user on the system.
|
||||
pub fn user_names() ?[]string {
|
||||
$if windows {
|
||||
result := execute('wmic useraccount get name')
|
||||
if result.exit_code != 0 {
|
||||
return error('Failed to get user names. Exited with code $result.exit_code: $result.output')
|
||||
}
|
||||
mut users := result.output.split_into_lines()
|
||||
// windows command prints an empty line at the end of output
|
||||
users.delete(users.len - 1)
|
||||
return users
|
||||
} $else {
|
||||
lines := read_lines('/etc/passwd')?
|
||||
mut users := []string{cap: lines.len}
|
||||
for line in lines {
|
||||
end_name := line.index(':') or { line.len }
|
||||
users << line[0..end_name]
|
||||
}
|
||||
return users
|
||||
}
|
||||
}
|
||||
|
||||
// home_dir returns path to the user's home directory.
|
||||
pub fn home_dir() string {
|
||||
$if windows {
|
||||
|
|
|
@ -56,6 +56,9 @@ pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
|||
&& (expected_sym.info as ast.ArrayFixed).elem_type.is_any_kind_of_pointer() {
|
||||
return true
|
||||
}
|
||||
if c.check_types((got_sym.info as ast.ArrayFixed).elem_type, (expected_sym.info as ast.ArrayFixed).elem_type) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if got_sym.kind == .enum_ {
|
||||
|
|
|
@ -555,626 +555,6 @@ pub fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int,
|
|||
return ares
|
||||
}
|
||||
|
||||
fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) {
|
||||
match expr {
|
||||
ast.FloatLiteral {
|
||||
if expr.val.f64() == 0.0 {
|
||||
oper := if op_kind == .div { 'division' } else { 'modulo' }
|
||||
c.error('$oper by zero', expr.pos)
|
||||
}
|
||||
}
|
||||
ast.IntegerLiteral {
|
||||
if expr.val.int() == 0 {
|
||||
oper := if op_kind == .div { 'division' } else { 'modulo' }
|
||||
c.error('$oper by zero', expr.pos)
|
||||
}
|
||||
}
|
||||
ast.CastExpr {
|
||||
c.check_div_mod_by_zero(expr.expr, op_kind)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
||||
former_expected_type := c.expected_type
|
||||
defer {
|
||||
c.expected_type = former_expected_type
|
||||
}
|
||||
mut left_type := c.expr(node.left)
|
||||
node.left_type = left_type
|
||||
c.expected_type = left_type
|
||||
mut right_type := c.expr(node.right)
|
||||
node.right_type = right_type
|
||||
if left_type.is_number() && !left_type.is_ptr()
|
||||
&& right_type in [ast.int_literal_type, ast.float_literal_type] {
|
||||
node.right_type = left_type
|
||||
}
|
||||
if right_type.is_number() && !right_type.is_ptr()
|
||||
&& left_type in [ast.int_literal_type, ast.float_literal_type] {
|
||||
node.left_type = right_type
|
||||
}
|
||||
mut right_sym := c.table.sym(right_type)
|
||||
right_final := c.table.final_sym(right_type)
|
||||
mut left_sym := c.table.sym(left_type)
|
||||
left_final := c.table.final_sym(left_type)
|
||||
left_pos := node.left.pos()
|
||||
right_pos := node.right.pos()
|
||||
left_right_pos := left_pos.extend(right_pos)
|
||||
if left_type.is_any_kind_of_pointer()
|
||||
&& node.op in [.plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe] {
|
||||
if !c.pref.translated && ((right_type.is_any_kind_of_pointer() && node.op != .minus)
|
||||
|| (!right_type.is_any_kind_of_pointer() && node.op !in [.plus, .minus])) {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
c.error('invalid operator `$node.op` to `$left_name` and `$right_name`', left_right_pos)
|
||||
} else if node.op in [.plus, .minus] {
|
||||
if !c.inside_unsafe && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() {
|
||||
c.warn('pointer arithmetic is only allowed in `unsafe` blocks', left_right_pos)
|
||||
}
|
||||
if left_type == ast.voidptr_type && !c.pref.translated {
|
||||
c.error('`$node.op` cannot be used with `voidptr`', left_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
mut return_type := left_type
|
||||
|
||||
if node.op != .key_is {
|
||||
match mut node.left {
|
||||
ast.Ident, ast.SelectorExpr {
|
||||
if node.left.is_mut {
|
||||
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.
|
||||
// TODO: First branch includes ops where single side check is not needed, or needed but hasn't been implemented.
|
||||
// TODO: Some of the checks are not single side. Should find a better way to organize them.
|
||||
match node.op {
|
||||
// .eq, .ne, .gt, .lt, .ge, .le, .and, .logical_or, .dot, .key_as, .right_shift {}
|
||||
.eq, .ne {
|
||||
is_mismatch :=
|
||||
(left_sym.kind == .alias && right_sym.kind in [.struct_, .array, .sum_type])
|
||||
|| (right_sym.kind == .alias && left_sym.kind in [.struct_, .array, .sum_type])
|
||||
if is_mismatch {
|
||||
c.error('possible type mismatch of compared values of `$node.op` operation',
|
||||
left_right_pos)
|
||||
} else if left_type in ast.integer_type_idxs && right_type in ast.integer_type_idxs {
|
||||
is_left_type_signed := left_type in ast.signed_integer_type_idxs
|
||||
is_right_type_signed := right_type in ast.signed_integer_type_idxs
|
||||
if !is_left_type_signed && mut node.right is ast.IntegerLiteral {
|
||||
if node.right.val.int() < 0 && left_type in ast.int_promoted_type_idxs {
|
||||
lt := c.table.sym(left_type).name
|
||||
c.error('`$lt` cannot be compared with negative value', node.right.pos)
|
||||
}
|
||||
} else if !is_right_type_signed && mut node.left is ast.IntegerLiteral {
|
||||
if node.left.val.int() < 0 && right_type in ast.int_promoted_type_idxs {
|
||||
rt := c.table.sym(right_type).name
|
||||
c.error('negative value cannot be compared with `$rt`', node.left.pos)
|
||||
}
|
||||
} else if is_left_type_signed != is_right_type_signed
|
||||
&& left_type != ast.int_literal_type_idx
|
||||
&& right_type != ast.int_literal_type_idx {
|
||||
ls, _ := c.table.type_size(left_type)
|
||||
rs, _ := c.table.type_size(right_type)
|
||||
// prevent e.g. `u32 == i16` but not `u16 == i32` as max_u16 fits in i32
|
||||
// TODO u32 == i32, change < to <=
|
||||
if !c.pref.translated && ((is_left_type_signed && ls < rs)
|
||||
|| (is_right_type_signed && rs < ls)) {
|
||||
lt := c.table.sym(left_type).name
|
||||
rt := c.table.sym(right_type).name
|
||||
c.error('`$lt` cannot be compared with `$rt`', node.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.key_in, .not_in {
|
||||
match right_final.kind {
|
||||
.array {
|
||||
if left_sym.kind !in [.sum_type, .interface_] {
|
||||
elem_type := right_final.array_info().elem_type
|
||||
c.check_expected(left_type, elem_type) or {
|
||||
c.error('left operand to `$node.op` does not match the array element type: $err.msg()',
|
||||
left_right_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
.map {
|
||||
map_info := right_final.map_info()
|
||||
c.check_expected(left_type, map_info.key_type) or {
|
||||
c.error('left operand to `$node.op` does not match the map key type: $err.msg()',
|
||||
left_right_pos)
|
||||
}
|
||||
node.left_type = map_info.key_type
|
||||
}
|
||||
.array_fixed {
|
||||
if left_sym.kind !in [.sum_type, .interface_] {
|
||||
elem_type := right_final.array_fixed_info().elem_type
|
||||
c.check_expected(left_type, elem_type) or {
|
||||
c.error('left operand to `$node.op` does not match the fixed array element type: $err.msg()',
|
||||
left_right_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
c.error('`$node.op.str()` can only be used with arrays and maps',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
return ast.bool_type
|
||||
}
|
||||
.plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe {
|
||||
// binary operators that expect matching types
|
||||
if right_sym.info is ast.Alias && (right_sym.info as ast.Alias).language != .c
|
||||
&& c.mod == c.table.type_to_str(right_type).split('.')[0]
|
||||
&& c.table.sym((right_sym.info as ast.Alias).parent_type).is_primitive() {
|
||||
right_sym = c.table.sym((right_sym.info as ast.Alias).parent_type)
|
||||
}
|
||||
if left_sym.info is ast.Alias && (left_sym.info as ast.Alias).language != .c
|
||||
&& c.mod == c.table.type_to_str(left_type).split('.')[0]
|
||||
&& c.table.sym((left_sym.info as ast.Alias).parent_type).is_primitive() {
|
||||
left_sym = c.table.sym((left_sym.info as ast.Alias).parent_type)
|
||||
}
|
||||
|
||||
if c.pref.translated && node.op in [.plus, .minus, .mul]
|
||||
&& left_type.is_any_kind_of_pointer() && right_type.is_any_kind_of_pointer() {
|
||||
return_type = left_type
|
||||
} else if !c.pref.translated && left_sym.kind == .alias && left_sym.info is ast.Alias
|
||||
&& !(c.table.sym((left_sym.info as ast.Alias).parent_type).is_primitive()) {
|
||||
if left_sym.has_method(node.op.str()) {
|
||||
if method := left_sym.find_method(node.op.str()) {
|
||||
return_type = method.return_type
|
||||
} else {
|
||||
return_type = left_type
|
||||
}
|
||||
} else {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
if left_name == right_name {
|
||||
c.error('undefined operation `$left_name` $node.op.str() `$right_name`',
|
||||
left_right_pos)
|
||||
} else {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
}
|
||||
} else if !c.pref.translated && right_sym.kind == .alias && right_sym.info is ast.Alias
|
||||
&& !(c.table.sym((right_sym.info as ast.Alias).parent_type).is_primitive()) {
|
||||
if right_sym.has_method(node.op.str()) {
|
||||
if method := right_sym.find_method(node.op.str()) {
|
||||
return_type = method.return_type
|
||||
} else {
|
||||
return_type = right_type
|
||||
}
|
||||
} else {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
if left_name == right_name {
|
||||
c.error('undefined operation `$left_name` $node.op.str() `$right_name`',
|
||||
left_right_pos)
|
||||
} else {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !c.pref.translated && left_sym.kind in [.array, .array_fixed, .map, .struct_] {
|
||||
if left_sym.has_method_with_generic_parent(node.op.str()) {
|
||||
if method := left_sym.find_method_with_generic_parent(node.op.str()) {
|
||||
return_type = method.return_type
|
||||
} else {
|
||||
return_type = left_type
|
||||
}
|
||||
} else {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
if left_name == right_name {
|
||||
c.error('undefined operation `$left_name` $node.op.str() `$right_name`',
|
||||
left_right_pos)
|
||||
} else {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
}
|
||||
} else if !c.pref.translated && right_sym.kind in [.array, .array_fixed, .map, .struct_] {
|
||||
if right_sym.has_method_with_generic_parent(node.op.str()) {
|
||||
if method := right_sym.find_method_with_generic_parent(node.op.str()) {
|
||||
return_type = method.return_type
|
||||
} else {
|
||||
return_type = right_type
|
||||
}
|
||||
} else {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
if left_name == right_name {
|
||||
c.error('undefined operation `$left_name` $node.op.str() `$right_name`',
|
||||
left_right_pos)
|
||||
} else {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
}
|
||||
} else if node.left.is_auto_deref_var() || node.right.is_auto_deref_var() {
|
||||
deref_left_type := if node.left.is_auto_deref_var() {
|
||||
left_type.deref()
|
||||
} else {
|
||||
left_type
|
||||
}
|
||||
deref_right_type := if node.right.is_auto_deref_var() {
|
||||
right_type.deref()
|
||||
} else {
|
||||
right_type
|
||||
}
|
||||
left_name := c.table.type_to_str(ast.mktyp(deref_left_type))
|
||||
right_name := c.table.type_to_str(ast.mktyp(deref_right_type))
|
||||
if left_name != right_name {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
} else {
|
||||
unaliased_left_type := c.table.unalias_num_type(left_type)
|
||||
unalias_right_type := c.table.unalias_num_type(right_type)
|
||||
mut promoted_type := c.promote(unaliased_left_type, unalias_right_type)
|
||||
// substract pointers is allowed in unsafe block
|
||||
is_allowed_pointer_arithmetic := left_type.is_any_kind_of_pointer()
|
||||
&& right_type.is_any_kind_of_pointer() && node.op == .minus
|
||||
if is_allowed_pointer_arithmetic {
|
||||
promoted_type = ast.int_type
|
||||
}
|
||||
if promoted_type.idx() == ast.void_type_idx {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
} else if promoted_type.has_flag(.optional) {
|
||||
s := c.table.type_to_str(promoted_type)
|
||||
c.error('`$node.op` cannot be used with `$s`', node.pos)
|
||||
} else if promoted_type.is_float() {
|
||||
if node.op in [.mod, .xor, .amp, .pipe] {
|
||||
side := if left_type == promoted_type { 'left' } else { 'right' }
|
||||
pos := if left_type == promoted_type { left_pos } else { right_pos }
|
||||
name := if left_type == promoted_type {
|
||||
left_sym.name
|
||||
} else {
|
||||
right_sym.name
|
||||
}
|
||||
if node.op == .mod {
|
||||
c.error('float modulo not allowed, use math.fmod() instead',
|
||||
pos)
|
||||
} else {
|
||||
c.error('$side type of `$node.op.str()` cannot be non-integer type `$name`',
|
||||
pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.op in [.div, .mod] {
|
||||
c.check_div_mod_by_zero(node.right, node.op)
|
||||
}
|
||||
|
||||
return_type = promoted_type
|
||||
}
|
||||
}
|
||||
.gt, .lt, .ge, .le {
|
||||
if left_sym.kind in [.array, .array_fixed] && right_sym.kind in [.array, .array_fixed] {
|
||||
c.error('only `==` and `!=` are defined on arrays', node.pos)
|
||||
} else if left_sym.kind == .struct_
|
||||
&& (left_sym.info as ast.Struct).generic_types.len > 0 {
|
||||
return ast.bool_type
|
||||
} else if left_sym.kind == .struct_ && right_sym.kind == .struct_
|
||||
&& node.op in [.eq, .lt] {
|
||||
if !(left_sym.has_method(node.op.str()) && right_sym.has_method(node.op.str())) {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
if left_name == right_name {
|
||||
if !(node.op == .lt && c.pref.translated) {
|
||||
// Allow `&Foo < &Foo` in translated code.
|
||||
// TODO maybe in unsafe as well?
|
||||
c.error('undefined operation `$left_name` $node.op.str() `$right_name`',
|
||||
left_right_pos)
|
||||
}
|
||||
} else {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
if left_sym.kind == .struct_ && right_sym.kind == .struct_ {
|
||||
if !left_sym.has_method('<') && node.op in [.ge, .le] {
|
||||
c.error('cannot use `$node.op` as `<` operator method is not defined',
|
||||
left_right_pos)
|
||||
} else if !left_sym.has_method('<') && node.op == .gt {
|
||||
c.error('cannot use `>` as `<=` operator method is not defined', left_right_pos)
|
||||
}
|
||||
} else if left_type.has_flag(.generic) && right_type.has_flag(.generic) {
|
||||
// Try to unwrap the generic type to make sure that
|
||||
// the below check works as expected
|
||||
left_gen_type := c.unwrap_generic(left_type)
|
||||
gen_sym := c.table.sym(left_gen_type)
|
||||
need_overload := gen_sym.kind in [.struct_, .interface_]
|
||||
if need_overload && !gen_sym.has_method_with_generic_parent('<')
|
||||
&& node.op in [.ge, .le] {
|
||||
c.error('cannot use `$node.op` as `<` operator method is not defined',
|
||||
left_right_pos)
|
||||
} else if need_overload && !gen_sym.has_method_with_generic_parent('<')
|
||||
&& node.op == .gt {
|
||||
c.error('cannot use `>` as `<=` operator method is not defined', left_right_pos)
|
||||
}
|
||||
} else if left_type in ast.integer_type_idxs && right_type in ast.integer_type_idxs {
|
||||
is_left_type_signed := left_type in ast.signed_integer_type_idxs
|
||||
|| left_type == ast.int_literal_type_idx
|
||||
is_right_type_signed := right_type in ast.signed_integer_type_idxs
|
||||
|| right_type == ast.int_literal_type_idx
|
||||
if is_left_type_signed != is_right_type_signed {
|
||||
if is_right_type_signed {
|
||||
if mut node.right is ast.IntegerLiteral {
|
||||
if node.right.val.int() < 0 {
|
||||
c.error('unsigned integer cannot be compared with negative value',
|
||||
node.right.pos)
|
||||
}
|
||||
}
|
||||
} else if is_left_type_signed {
|
||||
if mut node.left is ast.IntegerLiteral {
|
||||
if node.left.val.int() < 0 {
|
||||
c.error('unsigned integer cannot be compared with negative value',
|
||||
node.left.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if left_type.has_flag(.optional) || right_type.has_flag(.optional) {
|
||||
opt_comp_pos := if left_type.has_flag(.optional) { left_pos } else { right_pos }
|
||||
c.error('unwrapped optional cannot be compared in an infix expression',
|
||||
opt_comp_pos)
|
||||
}
|
||||
}
|
||||
.left_shift {
|
||||
if left_final.kind == .array {
|
||||
if !node.is_stmt {
|
||||
c.error('array append cannot be used in an expression', node.pos)
|
||||
}
|
||||
// `array << elm`
|
||||
c.check_expr_opt_call(node.right, right_type)
|
||||
node.auto_locked, _ = c.fail_if_immutable(node.left)
|
||||
left_value_type := c.table.value_type(c.unwrap_generic(left_type))
|
||||
left_value_sym := c.table.sym(c.unwrap_generic(left_value_type))
|
||||
if left_value_sym.kind == .interface_ {
|
||||
if right_final.kind != .array {
|
||||
// []Animal << Cat
|
||||
if c.type_implements(right_type, left_value_type, right_pos) {
|
||||
if !right_type.is_ptr() && !right_type.is_pointer() && !c.inside_unsafe
|
||||
&& right_sym.kind != .interface_ {
|
||||
c.mark_as_referenced(mut &node.right, true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// []Animal << []Cat
|
||||
c.type_implements(c.table.value_type(right_type), left_value_type,
|
||||
right_pos)
|
||||
}
|
||||
return ast.void_type
|
||||
} else if left_value_sym.kind == .sum_type {
|
||||
if right_final.kind != .array {
|
||||
if !c.table.is_sumtype_or_in_variant(left_value_type, ast.mktyp(right_type)) {
|
||||
c.error('cannot append `$right_sym.name` to `$left_sym.name`',
|
||||
right_pos)
|
||||
}
|
||||
} else {
|
||||
right_value_type := c.table.value_type(right_type)
|
||||
if !c.table.is_sumtype_or_in_variant(left_value_type, ast.mktyp(right_value_type)) {
|
||||
c.error('cannot append `$right_sym.name` to `$left_sym.name`',
|
||||
right_pos)
|
||||
}
|
||||
}
|
||||
return ast.void_type
|
||||
}
|
||||
// []T << T or []T << []T
|
||||
unwrapped_right_type := c.unwrap_generic(right_type)
|
||||
if c.check_types(unwrapped_right_type, left_value_type) {
|
||||
// []&T << T is wrong: we check for that, !(T.is_ptr()) && ?(&T).is_ptr()
|
||||
if !(!unwrapped_right_type.is_ptr() && left_value_type.is_ptr()
|
||||
&& left_value_type.share() == .mut_t) {
|
||||
return ast.void_type
|
||||
}
|
||||
} else if c.check_types(unwrapped_right_type, c.unwrap_generic(left_type)) {
|
||||
return ast.void_type
|
||||
}
|
||||
c.error('cannot append `$right_sym.name` to `$left_sym.name`', right_pos)
|
||||
return ast.void_type
|
||||
} else {
|
||||
return c.check_shift(mut node, left_type, right_type)
|
||||
}
|
||||
}
|
||||
.right_shift {
|
||||
return c.check_shift(mut node, left_type, right_type)
|
||||
}
|
||||
.unsigned_right_shift {
|
||||
modified_left_type := if !left_type.is_int() {
|
||||
c.error('invalid operation: shift on type `${c.table.sym(left_type).name}`',
|
||||
left_pos)
|
||||
ast.void_type_idx
|
||||
} else if left_type.is_int_literal() {
|
||||
// int literal => i64
|
||||
ast.u32_type_idx
|
||||
} else if left_type.is_unsigned() {
|
||||
left_type
|
||||
} else {
|
||||
// signed types' idx adds with 5 will get correct relative unsigned type
|
||||
// i8 => byte
|
||||
// i16 => u16
|
||||
// int => u32
|
||||
// i64 => u64
|
||||
// isize => usize
|
||||
// i128 => u128 NOT IMPLEMENTED YET
|
||||
left_type.idx() + ast.u32_type_idx - ast.int_type_idx
|
||||
}
|
||||
|
||||
if modified_left_type == 0 {
|
||||
return ast.void_type
|
||||
}
|
||||
|
||||
node = ast.InfixExpr{
|
||||
left: ast.CastExpr{
|
||||
expr: node.left
|
||||
typ: modified_left_type
|
||||
typname: c.table.type_str(modified_left_type)
|
||||
pos: node.pos
|
||||
}
|
||||
left_type: left_type
|
||||
op: .right_shift
|
||||
right: node.right
|
||||
right_type: right_type
|
||||
is_stmt: false
|
||||
pos: node.pos
|
||||
auto_locked: node.auto_locked
|
||||
or_block: node.or_block
|
||||
}
|
||||
|
||||
return c.check_shift(mut node, left_type, right_type)
|
||||
}
|
||||
.key_is, .not_is {
|
||||
right_expr := node.right
|
||||
mut typ := match right_expr {
|
||||
ast.TypeNode {
|
||||
right_expr.typ
|
||||
}
|
||||
ast.None {
|
||||
ast.none_type_idx
|
||||
}
|
||||
else {
|
||||
c.error('invalid type `$right_expr`', right_expr.pos())
|
||||
ast.Type(0)
|
||||
}
|
||||
}
|
||||
if typ != ast.Type(0) {
|
||||
typ_sym := c.table.sym(typ)
|
||||
op := node.op.str()
|
||||
if typ_sym.kind == .placeholder {
|
||||
c.error('$op: type `$typ_sym.name` does not exist', right_expr.pos())
|
||||
}
|
||||
if left_sym.kind == .aggregate {
|
||||
parent_left_type := (left_sym.info as ast.Aggregate).sum_type
|
||||
left_sym = c.table.sym(parent_left_type)
|
||||
}
|
||||
if left_sym.kind !in [.interface_, .sum_type] {
|
||||
c.error('`$op` can only be used with interfaces and sum types', node.pos)
|
||||
} else if mut left_sym.info is ast.SumType {
|
||||
if typ !in left_sym.info.variants {
|
||||
c.error('`$left_sym.name` has no variant `$right_sym.name`', node.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ast.bool_type
|
||||
}
|
||||
.arrow { // `chan <- elem`
|
||||
if left_sym.kind == .chan {
|
||||
chan_info := left_sym.chan_info()
|
||||
elem_type := chan_info.elem_type
|
||||
if !c.check_types(right_type, elem_type) {
|
||||
c.error('cannot push `$right_sym.name` on `$left_sym.name`', right_pos)
|
||||
}
|
||||
if chan_info.is_mut {
|
||||
// TODO: The error message of the following could be more specific...
|
||||
c.fail_if_immutable(node.right)
|
||||
}
|
||||
if elem_type.is_ptr() && !right_type.is_ptr() {
|
||||
c.error('cannot push non-reference `$right_sym.name` on `$left_sym.name`',
|
||||
right_pos)
|
||||
}
|
||||
c.stmts_ending_with_expression(node.or_block.stmts)
|
||||
} else {
|
||||
c.error('cannot push on non-channel `$left_sym.name`', left_pos)
|
||||
}
|
||||
return ast.void_type
|
||||
}
|
||||
.and, .logical_or {
|
||||
if !c.pref.translated && !c.file.is_translated {
|
||||
if node.left_type != ast.bool_type_idx {
|
||||
c.error('left operand for `$node.op` is not a boolean', node.left.pos())
|
||||
}
|
||||
if node.right_type != ast.bool_type_idx {
|
||||
c.error('right operand for `$node.op` is not a boolean', node.right.pos())
|
||||
}
|
||||
}
|
||||
if mut node.left is ast.InfixExpr {
|
||||
if node.left.op != node.op && node.left.op in [.logical_or, .and] {
|
||||
// for example: `(a && b) || c` instead of `a && b || c`
|
||||
c.error('ambiguous boolean expression. use `()` to ensure correct order of operations',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
// TODO: Absorb this block into the above single side check block to accelerate.
|
||||
if left_type == ast.bool_type && node.op !in [.eq, .ne, .logical_or, .and] {
|
||||
c.error('bool types only have the following operators defined: `==`, `!=`, `||`, and `&&`',
|
||||
node.pos)
|
||||
} else if left_type == ast.string_type && node.op !in [.plus, .eq, .ne, .lt, .gt, .le, .ge] {
|
||||
// TODO broken !in
|
||||
c.error('string types only have the following operators defined: `==`, `!=`, `<`, `>`, `<=`, `>=`, and `+`',
|
||||
node.pos)
|
||||
} else if left_sym.kind == .enum_ && right_sym.kind == .enum_ && !eq_ne {
|
||||
left_enum := left_sym.info as ast.Enum
|
||||
right_enum := right_sym.info as ast.Enum
|
||||
if left_enum.is_flag && right_enum.is_flag {
|
||||
// `[flag]` tagged enums are a special case that allow also `|` and `&` binary operators
|
||||
if node.op !in [.pipe, .amp] {
|
||||
c.error('only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed',
|
||||
node.pos)
|
||||
}
|
||||
} else if !c.pref.translated && !c.file.is_translated {
|
||||
// Regular enums
|
||||
c.error('only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
// sum types can't have any infix operation except of `is`, `eq`, `ne`.
|
||||
// `is` is checked before and doesn't reach this.
|
||||
if c.table.type_kind(left_type) == .sum_type && !eq_ne {
|
||||
c.error('cannot use operator `$node.op` with `$left_sym.name`', node.pos)
|
||||
} else if c.table.type_kind(right_type) == .sum_type && !eq_ne {
|
||||
c.error('cannot use operator `$node.op` with `$right_sym.name`', node.pos)
|
||||
}
|
||||
// TODO move this to symmetric_check? Right now it would break `return 0` for `fn()?int `
|
||||
left_is_optional := left_type.has_flag(.optional)
|
||||
right_is_optional := right_type.has_flag(.optional)
|
||||
if left_is_optional && right_is_optional {
|
||||
c.error('unwrapped optionals cannot be used in an infix expression', left_right_pos)
|
||||
} else if left_is_optional || right_is_optional {
|
||||
opt_infix_pos := if left_is_optional { left_pos } else { right_pos }
|
||||
c.error('unwrapped optional cannot be used in an infix expression', opt_infix_pos)
|
||||
}
|
||||
// Dual sides check (compatibility check)
|
||||
if !(c.symmetric_check(left_type, right_type) && c.symmetric_check(right_type, left_type))
|
||||
&& !c.pref.translated && !c.file.is_translated && !node.left.is_auto_deref_var()
|
||||
&& !node.right.is_auto_deref_var() {
|
||||
// for type-unresolved consts
|
||||
if left_type == ast.void_type || right_type == ast.void_type {
|
||||
return ast.void_type
|
||||
}
|
||||
if left_type.nr_muls() > 0 && right_type.is_int() {
|
||||
// pointer arithmetic is fine, it is checked in other places
|
||||
return return_type
|
||||
}
|
||||
c.error('infix expr: cannot use `$right_sym.name` (right expression) as `$left_sym.name`',
|
||||
left_right_pos)
|
||||
}
|
||||
/*
|
||||
if (node.left is ast.InfixExpr &&
|
||||
(node.left as ast.InfixExpr).op == .inc) ||
|
||||
(node.right is ast.InfixExpr && (node.right as ast.InfixExpr).op == .inc) {
|
||||
c.warn('`++` and `--` are statements, not expressions', node.pos)
|
||||
}
|
||||
*/
|
||||
return if node.op.is_relational() { ast.bool_type } else { return_type }
|
||||
}
|
||||
|
||||
// returns name and position of variable that needs write lock
|
||||
// also sets `is_changed` to true (TODO update the name to reflect this?)
|
||||
fn (mut c Checker) fail_if_immutable(expr_ ast.Expr) (string, token.Pos) {
|
||||
|
@ -2460,6 +1840,9 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
|||
'flag' {
|
||||
// #flag linux -lm
|
||||
mut flag := node.main
|
||||
if flag == 'flag' { // Checks for empty flag
|
||||
c.error('no argument(s) provided for #flag', node.pos)
|
||||
}
|
||||
if flag.contains('@VROOT') {
|
||||
// c.note(checker.vroot_is_deprecated_message, node.pos)
|
||||
flag = util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or {
|
||||
|
|
|
@ -0,0 +1,625 @@
|
|||
module checker
|
||||
|
||||
import v.ast
|
||||
import v.pref
|
||||
import v.token
|
||||
|
||||
pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
||||
former_expected_type := c.expected_type
|
||||
defer {
|
||||
c.expected_type = former_expected_type
|
||||
}
|
||||
mut left_type := c.expr(node.left)
|
||||
node.left_type = left_type
|
||||
c.expected_type = left_type
|
||||
mut right_type := c.expr(node.right)
|
||||
node.right_type = right_type
|
||||
if left_type.is_number() && !left_type.is_ptr()
|
||||
&& right_type in [ast.int_literal_type, ast.float_literal_type] {
|
||||
node.right_type = left_type
|
||||
}
|
||||
if right_type.is_number() && !right_type.is_ptr()
|
||||
&& left_type in [ast.int_literal_type, ast.float_literal_type] {
|
||||
node.left_type = right_type
|
||||
}
|
||||
mut right_sym := c.table.sym(right_type)
|
||||
right_final := c.table.final_sym(right_type)
|
||||
mut left_sym := c.table.sym(left_type)
|
||||
left_final := c.table.final_sym(left_type)
|
||||
left_pos := node.left.pos()
|
||||
right_pos := node.right.pos()
|
||||
left_right_pos := left_pos.extend(right_pos)
|
||||
if left_type.is_any_kind_of_pointer()
|
||||
&& node.op in [.plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe] {
|
||||
if !c.pref.translated && ((right_type.is_any_kind_of_pointer() && node.op != .minus)
|
||||
|| (!right_type.is_any_kind_of_pointer() && node.op !in [.plus, .minus])) {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
c.error('invalid operator `$node.op` to `$left_name` and `$right_name`', left_right_pos)
|
||||
} else if node.op in [.plus, .minus] {
|
||||
if !c.inside_unsafe && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() {
|
||||
c.warn('pointer arithmetic is only allowed in `unsafe` blocks', left_right_pos)
|
||||
}
|
||||
if left_type == ast.voidptr_type && !c.pref.translated {
|
||||
c.error('`$node.op` cannot be used with `voidptr`', left_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
mut return_type := left_type
|
||||
|
||||
if node.op != .key_is {
|
||||
match mut node.left {
|
||||
ast.Ident, ast.SelectorExpr {
|
||||
if node.left.is_mut {
|
||||
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.
|
||||
// TODO: First branch includes ops where single side check is not needed, or needed but hasn't been implemented.
|
||||
// TODO: Some of the checks are not single side. Should find a better way to organize them.
|
||||
match node.op {
|
||||
// .eq, .ne, .gt, .lt, .ge, .le, .and, .logical_or, .dot, .key_as, .right_shift {}
|
||||
.eq, .ne {
|
||||
is_mismatch :=
|
||||
(left_sym.kind == .alias && right_sym.kind in [.struct_, .array, .sum_type])
|
||||
|| (right_sym.kind == .alias && left_sym.kind in [.struct_, .array, .sum_type])
|
||||
if is_mismatch {
|
||||
c.error('possible type mismatch of compared values of `$node.op` operation',
|
||||
left_right_pos)
|
||||
} else if left_type in ast.integer_type_idxs && right_type in ast.integer_type_idxs {
|
||||
is_left_type_signed := left_type in ast.signed_integer_type_idxs
|
||||
is_right_type_signed := right_type in ast.signed_integer_type_idxs
|
||||
if !is_left_type_signed && mut node.right is ast.IntegerLiteral {
|
||||
if node.right.val.int() < 0 && left_type in ast.int_promoted_type_idxs {
|
||||
lt := c.table.sym(left_type).name
|
||||
c.error('`$lt` cannot be compared with negative value', node.right.pos)
|
||||
}
|
||||
} else if !is_right_type_signed && mut node.left is ast.IntegerLiteral {
|
||||
if node.left.val.int() < 0 && right_type in ast.int_promoted_type_idxs {
|
||||
rt := c.table.sym(right_type).name
|
||||
c.error('negative value cannot be compared with `$rt`', node.left.pos)
|
||||
}
|
||||
} else if is_left_type_signed != is_right_type_signed
|
||||
&& left_type != ast.int_literal_type_idx
|
||||
&& right_type != ast.int_literal_type_idx {
|
||||
ls, _ := c.table.type_size(left_type)
|
||||
rs, _ := c.table.type_size(right_type)
|
||||
// prevent e.g. `u32 == i16` but not `u16 == i32` as max_u16 fits in i32
|
||||
// TODO u32 == i32, change < to <=
|
||||
if !c.pref.translated && ((is_left_type_signed && ls < rs)
|
||||
|| (is_right_type_signed && rs < ls)) {
|
||||
lt := c.table.sym(left_type).name
|
||||
rt := c.table.sym(right_type).name
|
||||
c.error('`$lt` cannot be compared with `$rt`', node.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.key_in, .not_in {
|
||||
match right_final.kind {
|
||||
.array {
|
||||
if left_sym.kind !in [.sum_type, .interface_] {
|
||||
elem_type := right_final.array_info().elem_type
|
||||
c.check_expected(left_type, elem_type) or {
|
||||
c.error('left operand to `$node.op` does not match the array element type: $err.msg()',
|
||||
left_right_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
.map {
|
||||
map_info := right_final.map_info()
|
||||
c.check_expected(left_type, map_info.key_type) or {
|
||||
c.error('left operand to `$node.op` does not match the map key type: $err.msg()',
|
||||
left_right_pos)
|
||||
}
|
||||
node.left_type = map_info.key_type
|
||||
}
|
||||
.array_fixed {
|
||||
if left_sym.kind !in [.sum_type, .interface_] {
|
||||
elem_type := right_final.array_fixed_info().elem_type
|
||||
c.check_expected(left_type, elem_type) or {
|
||||
c.error('left operand to `$node.op` does not match the fixed array element type: $err.msg()',
|
||||
left_right_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
c.error('`$node.op.str()` can only be used with arrays and maps',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
return ast.bool_type
|
||||
}
|
||||
.plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe {
|
||||
// binary operators that expect matching types
|
||||
if right_sym.info is ast.Alias && (right_sym.info as ast.Alias).language != .c
|
||||
&& c.mod == c.table.type_to_str(right_type).split('.')[0]
|
||||
&& c.table.sym((right_sym.info as ast.Alias).parent_type).is_primitive() {
|
||||
right_sym = c.table.sym((right_sym.info as ast.Alias).parent_type)
|
||||
}
|
||||
if left_sym.info is ast.Alias && (left_sym.info as ast.Alias).language != .c
|
||||
&& c.mod == c.table.type_to_str(left_type).split('.')[0]
|
||||
&& c.table.sym((left_sym.info as ast.Alias).parent_type).is_primitive() {
|
||||
left_sym = c.table.sym((left_sym.info as ast.Alias).parent_type)
|
||||
}
|
||||
|
||||
if c.pref.translated && node.op in [.plus, .minus, .mul]
|
||||
&& left_type.is_any_kind_of_pointer() && right_type.is_any_kind_of_pointer() {
|
||||
return_type = left_type
|
||||
} else if !c.pref.translated && left_sym.kind == .alias && left_sym.info is ast.Alias
|
||||
&& !(c.table.sym((left_sym.info as ast.Alias).parent_type).is_primitive()) {
|
||||
if left_sym.has_method(node.op.str()) {
|
||||
if method := left_sym.find_method(node.op.str()) {
|
||||
return_type = method.return_type
|
||||
} else {
|
||||
return_type = left_type
|
||||
}
|
||||
} else {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
if left_name == right_name {
|
||||
c.error('undefined operation `$left_name` $node.op.str() `$right_name`',
|
||||
left_right_pos)
|
||||
} else {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
}
|
||||
} else if !c.pref.translated && right_sym.kind == .alias && right_sym.info is ast.Alias
|
||||
&& !(c.table.sym((right_sym.info as ast.Alias).parent_type).is_primitive()) {
|
||||
if right_sym.has_method(node.op.str()) {
|
||||
if method := right_sym.find_method(node.op.str()) {
|
||||
return_type = method.return_type
|
||||
} else {
|
||||
return_type = right_type
|
||||
}
|
||||
} else {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
if left_name == right_name {
|
||||
c.error('undefined operation `$left_name` $node.op.str() `$right_name`',
|
||||
left_right_pos)
|
||||
} else {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !c.pref.translated && left_sym.kind in [.array, .array_fixed, .map, .struct_] {
|
||||
if left_sym.has_method_with_generic_parent(node.op.str()) {
|
||||
if method := left_sym.find_method_with_generic_parent(node.op.str()) {
|
||||
return_type = method.return_type
|
||||
} else {
|
||||
return_type = left_type
|
||||
}
|
||||
} else {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
if left_name == right_name {
|
||||
c.error('undefined operation `$left_name` $node.op.str() `$right_name`',
|
||||
left_right_pos)
|
||||
} else {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
}
|
||||
} else if !c.pref.translated && right_sym.kind in [.array, .array_fixed, .map, .struct_] {
|
||||
if right_sym.has_method_with_generic_parent(node.op.str()) {
|
||||
if method := right_sym.find_method_with_generic_parent(node.op.str()) {
|
||||
return_type = method.return_type
|
||||
} else {
|
||||
return_type = right_type
|
||||
}
|
||||
} else {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
if left_name == right_name {
|
||||
c.error('undefined operation `$left_name` $node.op.str() `$right_name`',
|
||||
left_right_pos)
|
||||
} else {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
}
|
||||
} else if node.left.is_auto_deref_var() || node.right.is_auto_deref_var() {
|
||||
deref_left_type := if node.left.is_auto_deref_var() {
|
||||
left_type.deref()
|
||||
} else {
|
||||
left_type
|
||||
}
|
||||
deref_right_type := if node.right.is_auto_deref_var() {
|
||||
right_type.deref()
|
||||
} else {
|
||||
right_type
|
||||
}
|
||||
left_name := c.table.type_to_str(ast.mktyp(deref_left_type))
|
||||
right_name := c.table.type_to_str(ast.mktyp(deref_right_type))
|
||||
if left_name != right_name {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
} else {
|
||||
unaliased_left_type := c.table.unalias_num_type(left_type)
|
||||
unalias_right_type := c.table.unalias_num_type(right_type)
|
||||
mut promoted_type := c.promote(unaliased_left_type, unalias_right_type)
|
||||
// substract pointers is allowed in unsafe block
|
||||
is_allowed_pointer_arithmetic := left_type.is_any_kind_of_pointer()
|
||||
&& right_type.is_any_kind_of_pointer() && node.op == .minus
|
||||
if is_allowed_pointer_arithmetic {
|
||||
promoted_type = ast.int_type
|
||||
}
|
||||
if promoted_type.idx() == ast.void_type_idx {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
} else if promoted_type.has_flag(.optional) {
|
||||
s := c.table.type_to_str(promoted_type)
|
||||
c.error('`$node.op` cannot be used with `$s`', node.pos)
|
||||
} else if promoted_type.is_float() {
|
||||
if node.op in [.mod, .xor, .amp, .pipe] {
|
||||
side := if left_type == promoted_type { 'left' } else { 'right' }
|
||||
pos := if left_type == promoted_type { left_pos } else { right_pos }
|
||||
name := if left_type == promoted_type {
|
||||
left_sym.name
|
||||
} else {
|
||||
right_sym.name
|
||||
}
|
||||
if node.op == .mod {
|
||||
c.error('float modulo not allowed, use math.fmod() instead',
|
||||
pos)
|
||||
} else {
|
||||
c.error('$side type of `$node.op.str()` cannot be non-integer type `$name`',
|
||||
pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.op in [.div, .mod] {
|
||||
c.check_div_mod_by_zero(node.right, node.op)
|
||||
}
|
||||
|
||||
return_type = promoted_type
|
||||
}
|
||||
}
|
||||
.gt, .lt, .ge, .le {
|
||||
if left_sym.kind in [.array, .array_fixed] && right_sym.kind in [.array, .array_fixed] {
|
||||
c.error('only `==` and `!=` are defined on arrays', node.pos)
|
||||
} else if left_sym.kind == .struct_
|
||||
&& (left_sym.info as ast.Struct).generic_types.len > 0 {
|
||||
return ast.bool_type
|
||||
} else if left_sym.kind == .struct_ && right_sym.kind == .struct_
|
||||
&& node.op in [.eq, .lt] {
|
||||
if !(left_sym.has_method(node.op.str()) && right_sym.has_method(node.op.str())) {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
right_name := c.table.type_to_str(right_type)
|
||||
if left_name == right_name {
|
||||
if !(node.op == .lt && c.pref.translated) {
|
||||
// Allow `&Foo < &Foo` in translated code.
|
||||
// TODO maybe in unsafe as well?
|
||||
c.error('undefined operation `$left_name` $node.op.str() `$right_name`',
|
||||
left_right_pos)
|
||||
}
|
||||
} else {
|
||||
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
if left_sym.kind == .struct_ && right_sym.kind == .struct_ {
|
||||
if !left_sym.has_method('<') && node.op in [.ge, .le] {
|
||||
c.error('cannot use `$node.op` as `<` operator method is not defined',
|
||||
left_right_pos)
|
||||
} else if !left_sym.has_method('<') && node.op == .gt {
|
||||
c.error('cannot use `>` as `<=` operator method is not defined', left_right_pos)
|
||||
}
|
||||
} else if left_type.has_flag(.generic) && right_type.has_flag(.generic) {
|
||||
// Try to unwrap the generic type to make sure that
|
||||
// the below check works as expected
|
||||
left_gen_type := c.unwrap_generic(left_type)
|
||||
gen_sym := c.table.sym(left_gen_type)
|
||||
need_overload := gen_sym.kind in [.struct_, .interface_]
|
||||
if need_overload && !gen_sym.has_method_with_generic_parent('<')
|
||||
&& node.op in [.ge, .le] {
|
||||
c.error('cannot use `$node.op` as `<` operator method is not defined',
|
||||
left_right_pos)
|
||||
} else if need_overload && !gen_sym.has_method_with_generic_parent('<')
|
||||
&& node.op == .gt {
|
||||
c.error('cannot use `>` as `<=` operator method is not defined', left_right_pos)
|
||||
}
|
||||
} else if left_type in ast.integer_type_idxs && right_type in ast.integer_type_idxs {
|
||||
is_left_type_signed := left_type in ast.signed_integer_type_idxs
|
||||
|| left_type == ast.int_literal_type_idx
|
||||
is_right_type_signed := right_type in ast.signed_integer_type_idxs
|
||||
|| right_type == ast.int_literal_type_idx
|
||||
if is_left_type_signed != is_right_type_signed {
|
||||
if is_right_type_signed {
|
||||
if mut node.right is ast.IntegerLiteral {
|
||||
if node.right.val.int() < 0 {
|
||||
c.error('unsigned integer cannot be compared with negative value',
|
||||
node.right.pos)
|
||||
}
|
||||
}
|
||||
} else if is_left_type_signed {
|
||||
if mut node.left is ast.IntegerLiteral {
|
||||
if node.left.val.int() < 0 {
|
||||
c.error('unsigned integer cannot be compared with negative value',
|
||||
node.left.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if left_type.has_flag(.optional) || right_type.has_flag(.optional) {
|
||||
opt_comp_pos := if left_type.has_flag(.optional) { left_pos } else { right_pos }
|
||||
c.error('unwrapped optional cannot be compared in an infix expression',
|
||||
opt_comp_pos)
|
||||
}
|
||||
}
|
||||
.left_shift {
|
||||
if left_final.kind == .array {
|
||||
if !node.is_stmt {
|
||||
c.error('array append cannot be used in an expression', node.pos)
|
||||
}
|
||||
// `array << elm`
|
||||
c.check_expr_opt_call(node.right, right_type)
|
||||
node.auto_locked, _ = c.fail_if_immutable(node.left)
|
||||
left_value_type := c.table.value_type(c.unwrap_generic(left_type))
|
||||
left_value_sym := c.table.sym(c.unwrap_generic(left_value_type))
|
||||
if left_value_sym.kind == .interface_ {
|
||||
if right_final.kind != .array {
|
||||
// []Animal << Cat
|
||||
if c.type_implements(right_type, left_value_type, right_pos) {
|
||||
if !right_type.is_ptr() && !right_type.is_pointer() && !c.inside_unsafe
|
||||
&& right_sym.kind != .interface_ {
|
||||
c.mark_as_referenced(mut &node.right, true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// []Animal << []Cat
|
||||
c.type_implements(c.table.value_type(right_type), left_value_type,
|
||||
right_pos)
|
||||
}
|
||||
return ast.void_type
|
||||
} else if left_value_sym.kind == .sum_type {
|
||||
if right_final.kind != .array {
|
||||
if !c.table.is_sumtype_or_in_variant(left_value_type, ast.mktyp(right_type)) {
|
||||
c.error('cannot append `$right_sym.name` to `$left_sym.name`',
|
||||
right_pos)
|
||||
}
|
||||
} else {
|
||||
right_value_type := c.table.value_type(right_type)
|
||||
if !c.table.is_sumtype_or_in_variant(left_value_type, ast.mktyp(right_value_type)) {
|
||||
c.error('cannot append `$right_sym.name` to `$left_sym.name`',
|
||||
right_pos)
|
||||
}
|
||||
}
|
||||
return ast.void_type
|
||||
}
|
||||
// []T << T or []T << []T
|
||||
unwrapped_right_type := c.unwrap_generic(right_type)
|
||||
if c.check_types(unwrapped_right_type, left_value_type) {
|
||||
// []&T << T is wrong: we check for that, !(T.is_ptr()) && ?(&T).is_ptr()
|
||||
if !(!unwrapped_right_type.is_ptr() && left_value_type.is_ptr()
|
||||
&& left_value_type.share() == .mut_t) {
|
||||
return ast.void_type
|
||||
}
|
||||
} else if c.check_types(unwrapped_right_type, c.unwrap_generic(left_type)) {
|
||||
return ast.void_type
|
||||
}
|
||||
c.error('cannot append `$right_sym.name` to `$left_sym.name`', right_pos)
|
||||
return ast.void_type
|
||||
} else {
|
||||
return c.check_shift(mut node, left_type, right_type)
|
||||
}
|
||||
}
|
||||
.right_shift {
|
||||
return c.check_shift(mut node, left_type, right_type)
|
||||
}
|
||||
.unsigned_right_shift {
|
||||
modified_left_type := if !left_type.is_int() {
|
||||
c.error('invalid operation: shift on type `${c.table.sym(left_type).name}`',
|
||||
left_pos)
|
||||
ast.void_type_idx
|
||||
} else if left_type.is_int_literal() {
|
||||
// int literal => i64
|
||||
ast.u32_type_idx
|
||||
} else if left_type.is_unsigned() {
|
||||
left_type
|
||||
} else {
|
||||
// signed types' idx adds with 5 will get correct relative unsigned type
|
||||
// i8 => byte
|
||||
// i16 => u16
|
||||
// int => u32
|
||||
// i64 => u64
|
||||
// isize => usize
|
||||
// i128 => u128 NOT IMPLEMENTED YET
|
||||
left_type.idx() + ast.u32_type_idx - ast.int_type_idx
|
||||
}
|
||||
|
||||
if modified_left_type == 0 {
|
||||
return ast.void_type
|
||||
}
|
||||
|
||||
node = ast.InfixExpr{
|
||||
left: ast.CastExpr{
|
||||
expr: node.left
|
||||
typ: modified_left_type
|
||||
typname: c.table.type_str(modified_left_type)
|
||||
pos: node.pos
|
||||
}
|
||||
left_type: left_type
|
||||
op: .right_shift
|
||||
right: node.right
|
||||
right_type: right_type
|
||||
is_stmt: false
|
||||
pos: node.pos
|
||||
auto_locked: node.auto_locked
|
||||
or_block: node.or_block
|
||||
}
|
||||
|
||||
return c.check_shift(mut node, left_type, right_type)
|
||||
}
|
||||
.key_is, .not_is {
|
||||
right_expr := node.right
|
||||
mut typ := match right_expr {
|
||||
ast.TypeNode {
|
||||
right_expr.typ
|
||||
}
|
||||
ast.None {
|
||||
ast.none_type_idx
|
||||
}
|
||||
else {
|
||||
c.error('invalid type `$right_expr`', right_expr.pos())
|
||||
ast.Type(0)
|
||||
}
|
||||
}
|
||||
if typ != ast.Type(0) {
|
||||
typ_sym := c.table.sym(typ)
|
||||
op := node.op.str()
|
||||
if typ_sym.kind == .placeholder {
|
||||
c.error('$op: type `$typ_sym.name` does not exist', right_expr.pos())
|
||||
}
|
||||
if left_sym.kind == .aggregate {
|
||||
parent_left_type := (left_sym.info as ast.Aggregate).sum_type
|
||||
left_sym = c.table.sym(parent_left_type)
|
||||
}
|
||||
if left_sym.kind !in [.interface_, .sum_type] {
|
||||
c.error('`$op` can only be used with interfaces and sum types', node.pos)
|
||||
} else if mut left_sym.info is ast.SumType {
|
||||
if typ !in left_sym.info.variants {
|
||||
c.error('`$left_sym.name` has no variant `$right_sym.name`', node.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ast.bool_type
|
||||
}
|
||||
.arrow { // `chan <- elem`
|
||||
if left_sym.kind == .chan {
|
||||
chan_info := left_sym.chan_info()
|
||||
elem_type := chan_info.elem_type
|
||||
if !c.check_types(right_type, elem_type) {
|
||||
c.error('cannot push `$right_sym.name` on `$left_sym.name`', right_pos)
|
||||
}
|
||||
if chan_info.is_mut {
|
||||
// TODO: The error message of the following could be more specific...
|
||||
c.fail_if_immutable(node.right)
|
||||
}
|
||||
if elem_type.is_ptr() && !right_type.is_ptr() {
|
||||
c.error('cannot push non-reference `$right_sym.name` on `$left_sym.name`',
|
||||
right_pos)
|
||||
}
|
||||
c.stmts_ending_with_expression(node.or_block.stmts)
|
||||
} else {
|
||||
c.error('cannot push on non-channel `$left_sym.name`', left_pos)
|
||||
}
|
||||
return ast.void_type
|
||||
}
|
||||
.and, .logical_or {
|
||||
if !c.pref.translated && !c.file.is_translated {
|
||||
if node.left_type != ast.bool_type_idx {
|
||||
c.error('left operand for `$node.op` is not a boolean', node.left.pos())
|
||||
}
|
||||
if node.right_type != ast.bool_type_idx {
|
||||
c.error('right operand for `$node.op` is not a boolean', node.right.pos())
|
||||
}
|
||||
}
|
||||
if mut node.left is ast.InfixExpr {
|
||||
if node.left.op != node.op && node.left.op in [.logical_or, .and] {
|
||||
// for example: `(a && b) || c` instead of `a && b || c`
|
||||
c.error('ambiguous boolean expression. use `()` to ensure correct order of operations',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
// TODO: Absorb this block into the above single side check block to accelerate.
|
||||
if left_type == ast.bool_type && node.op !in [.eq, .ne, .logical_or, .and] {
|
||||
c.error('bool types only have the following operators defined: `==`, `!=`, `||`, and `&&`',
|
||||
node.pos)
|
||||
} else if left_type == ast.string_type && node.op !in [.plus, .eq, .ne, .lt, .gt, .le, .ge] {
|
||||
// TODO broken !in
|
||||
c.error('string types only have the following operators defined: `==`, `!=`, `<`, `>`, `<=`, `>=`, and `+`',
|
||||
node.pos)
|
||||
} else if left_sym.kind == .enum_ && right_sym.kind == .enum_ && !eq_ne {
|
||||
left_enum := left_sym.info as ast.Enum
|
||||
right_enum := right_sym.info as ast.Enum
|
||||
if left_enum.is_flag && right_enum.is_flag {
|
||||
// `[flag]` tagged enums are a special case that allow also `|` and `&` binary operators
|
||||
if node.op !in [.pipe, .amp] {
|
||||
c.error('only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed',
|
||||
node.pos)
|
||||
}
|
||||
} else if !c.pref.translated && !c.file.is_translated {
|
||||
// Regular enums
|
||||
c.error('only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
// sum types can't have any infix operation except of `is`, `eq`, `ne`.
|
||||
// `is` is checked before and doesn't reach this.
|
||||
if c.table.type_kind(left_type) == .sum_type && !eq_ne {
|
||||
c.error('cannot use operator `$node.op` with `$left_sym.name`', node.pos)
|
||||
} else if c.table.type_kind(right_type) == .sum_type && !eq_ne {
|
||||
c.error('cannot use operator `$node.op` with `$right_sym.name`', node.pos)
|
||||
}
|
||||
// TODO move this to symmetric_check? Right now it would break `return 0` for `fn()?int `
|
||||
left_is_optional := left_type.has_flag(.optional)
|
||||
right_is_optional := right_type.has_flag(.optional)
|
||||
if left_is_optional && right_is_optional {
|
||||
c.error('unwrapped optionals cannot be used in an infix expression', left_right_pos)
|
||||
} else if left_is_optional || right_is_optional {
|
||||
opt_infix_pos := if left_is_optional { left_pos } else { right_pos }
|
||||
c.error('unwrapped optional cannot be used in an infix expression', opt_infix_pos)
|
||||
}
|
||||
// Dual sides check (compatibility check)
|
||||
if !(c.symmetric_check(left_type, right_type) && c.symmetric_check(right_type, left_type))
|
||||
&& !c.pref.translated && !c.file.is_translated && !node.left.is_auto_deref_var()
|
||||
&& !node.right.is_auto_deref_var() {
|
||||
// for type-unresolved consts
|
||||
if left_type == ast.void_type || right_type == ast.void_type {
|
||||
return ast.void_type
|
||||
}
|
||||
if left_type.nr_muls() > 0 && right_type.is_int() {
|
||||
// pointer arithmetic is fine, it is checked in other places
|
||||
return return_type
|
||||
}
|
||||
c.error('infix expr: cannot use `$right_sym.name` (right expression) as `$left_sym.name`',
|
||||
left_right_pos)
|
||||
}
|
||||
/*
|
||||
if (node.left is ast.InfixExpr &&
|
||||
(node.left as ast.InfixExpr).op == .inc) ||
|
||||
(node.right is ast.InfixExpr && (node.right as ast.InfixExpr).op == .inc) {
|
||||
c.warn('`++` and `--` are statements, not expressions', node.pos)
|
||||
}
|
||||
*/
|
||||
return if node.op.is_relational() { ast.bool_type } else { return_type }
|
||||
}
|
||||
|
||||
fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) {
|
||||
match expr {
|
||||
ast.FloatLiteral {
|
||||
if expr.val.f64() == 0.0 {
|
||||
oper := if op_kind == .div { 'division' } else { 'modulo' }
|
||||
c.error('$oper by zero', expr.pos)
|
||||
}
|
||||
}
|
||||
ast.IntegerLiteral {
|
||||
if expr.val.int() == 0 {
|
||||
oper := if op_kind == .div { 'division' } else { 'modulo' }
|
||||
c.error('$oper by zero', expr.pos)
|
||||
}
|
||||
}
|
||||
ast.CastExpr {
|
||||
c.check_div_mod_by_zero(expr.expr, op_kind)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
*.v
|
||||
*.c
|
||||
require_or_block_sumtype_map.err
|
||||
!*_test.v
|
||||
!modules/**/*.v
|
||||
!modules/**/*.v
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
vlib/v/checker/tests/ambiguous_field_method_err.vv:23:2: warning: unused variable: `n`
|
||||
21 | b := Bar{}
|
||||
22 | b.test()
|
||||
23 | n := b.name
|
||||
| ^
|
||||
24 | }
|
||||
vlib/v/checker/tests/ambiguous_field_method_err.vv:22:4: error: ambiguous method `test`
|
||||
20 | fn main() {
|
||||
21 | b := Bar{}
|
||||
|
@ -10,4 +16,4 @@ vlib/v/checker/tests/ambiguous_field_method_err.vv:23:9: error: ambiguous field
|
|||
22 | b.test()
|
||||
23 | n := b.name
|
||||
| ~~~~
|
||||
24 | }
|
||||
24 | }
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
vlib/v/checker/tests/array_of_generic_struct_init_err.vv:6:6: warning: unused variable: `arr`
|
||||
4 |
|
||||
5 | fn main() {
|
||||
6 | mut arr := []Item{}
|
||||
| ~~~
|
||||
7 | }
|
||||
vlib/v/checker/tests/array_of_generic_struct_init_err.vv:6:15: error: generic struct must specify type parameter, e.g. Foo<int>
|
||||
4 |
|
||||
4 |
|
||||
5 | fn main() {
|
||||
6 | mut arr := []Item{}
|
||||
| ~~~~
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
vlib/v/checker/tests/assign_to_typeless_variable_err.vv:2:3: warning: unused variable: `val`
|
||||
1 | fn main() {
|
||||
2 | val := {}
|
||||
| ~~~
|
||||
3 | val = 1
|
||||
4 | }
|
||||
vlib/v/checker/tests/assign_to_typeless_variable_err.vv:2:10: error: invalid empty map initialisation syntax, use e.g. map[string]int{} instead
|
||||
1 | fn main() {
|
||||
2 | val := {}
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
vlib/v/checker/tests/cannot_cast_to_struct.vv:10:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
vlib/v/checker/tests/cannot_cast_to_struct.vv:10:7: warning: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
8 |
|
||||
9 | fn main() {
|
||||
10 | _ := Test(Abc{})
|
||||
| ~~~~~~~~~~~
|
||||
11 | sum := Alphabet(Xyz{})
|
||||
12 | _ = Xyz(sum)
|
||||
vlib/v/checker/tests/cannot_cast_to_struct.vv:10:7: error: cannot convert struct `Abc` to struct `Test`
|
||||
8 |
|
||||
9 | fn main() {
|
||||
10 | _ := Test(Abc{})
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
vlib/v/checker/tests/compile_error.vv:8:5: warning: On non Vinix this warning should be shown
|
||||
6 |
|
||||
7 | $if !vinix {
|
||||
8 | $compile_warn('On non Vinix this warning should be shown')
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
9 | }
|
||||
10 |
|
||||
vlib/v/checker/tests/compile_error.vv:4:5: error: Only Serenity is supported
|
||||
2 |
|
||||
3 | $if !serenity {
|
||||
|
@ -5,10 +12,3 @@ vlib/v/checker/tests/compile_error.vv:4:5: error: Only Serenity is supported
|
|||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
5 | }
|
||||
6 |
|
||||
vlib/v/checker/tests/compile_error.vv:8:5: error: On non Vinix this warning should be shown
|
||||
6 |
|
||||
7 | $if !vinix {
|
||||
8 | $compile_warn('On non Vinix this warning should be shown')
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
9 | }
|
||||
10 |
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
vlib/v/checker/tests/comptime_for.vv:2:7: warning: unused variable: `m`
|
||||
1 | fn unknown() {
|
||||
2 | $for m in Huh.methods {}
|
||||
| ^
|
||||
3 | $for f in Huh.fields {}
|
||||
4 | $for f in T.fields {
|
||||
vlib/v/checker/tests/comptime_for.vv:3:7: warning: unused variable: `f`
|
||||
1 | fn unknown() {
|
||||
2 | $for m in Huh.methods {}
|
||||
3 | $for f in Huh.fields {}
|
||||
| ^
|
||||
4 | $for f in T.fields {
|
||||
5 | $if f.typ is Huh {}
|
||||
vlib/v/checker/tests/comptime_for.vv:2:12: error: unknown type `Huh`
|
||||
1 | fn unknown() {
|
||||
2 | $for m in Huh.methods {}
|
||||
|
|
|
@ -12,6 +12,26 @@ vlib/v/checker/tests/deprecations.vv:67:4: notice: method `Abc.future` will be d
|
|||
| ~~~~~~~~
|
||||
68 | a.past()
|
||||
69 | a.simply_deprecated()
|
||||
vlib/v/checker/tests/deprecations.vv:62:2: warning: function `simply_deprecated` has been deprecated; custom message 7
|
||||
60 | future()
|
||||
61 | past()
|
||||
62 | simply_deprecated()
|
||||
| ~~~~~~~~~~~~~~~~~~~
|
||||
63 | just_deprecated()
|
||||
64 | ancient()
|
||||
vlib/v/checker/tests/deprecations.vv:63:2: warning: function `just_deprecated` has been deprecated
|
||||
61 | past()
|
||||
62 | simply_deprecated()
|
||||
63 | just_deprecated()
|
||||
| ~~~~~~~~~~~~~~~~~
|
||||
64 | ancient()
|
||||
65 | //
|
||||
vlib/v/checker/tests/deprecations.vv:69:4: warning: method `Abc.simply_deprecated` has been deprecated; custom message 3
|
||||
67 | a.future()
|
||||
68 | a.past()
|
||||
69 | a.simply_deprecated()
|
||||
| ~~~~~~~~~~~~~~~~~~~
|
||||
70 | }
|
||||
vlib/v/checker/tests/deprecations.vv:61:2: error: function `past` has been deprecated since 2021-03-01; custom message 5
|
||||
59 | fn main() {
|
||||
60 | future()
|
||||
|
@ -19,20 +39,6 @@ vlib/v/checker/tests/deprecations.vv:61:2: error: function `past` has been depre
|
|||
| ~~~~~~
|
||||
62 | simply_deprecated()
|
||||
63 | just_deprecated()
|
||||
vlib/v/checker/tests/deprecations.vv:62:2: error: function `simply_deprecated` has been deprecated; custom message 7
|
||||
60 | future()
|
||||
61 | past()
|
||||
62 | simply_deprecated()
|
||||
| ~~~~~~~~~~~~~~~~~~~
|
||||
63 | just_deprecated()
|
||||
64 | ancient()
|
||||
vlib/v/checker/tests/deprecations.vv:63:2: error: function `just_deprecated` has been deprecated
|
||||
61 | past()
|
||||
62 | simply_deprecated()
|
||||
63 | just_deprecated()
|
||||
| ~~~~~~~~~~~~~~~~~
|
||||
64 | ancient()
|
||||
65 | //
|
||||
vlib/v/checker/tests/deprecations.vv:64:2: error: function `ancient` has been deprecated since 1990-03-01; custom message 6
|
||||
62 | simply_deprecated()
|
||||
63 | just_deprecated()
|
||||
|
@ -47,9 +53,3 @@ vlib/v/checker/tests/deprecations.vv:68:4: error: method `Abc.past` has been dep
|
|||
| ~~~~~~
|
||||
69 | a.simply_deprecated()
|
||||
70 | }
|
||||
vlib/v/checker/tests/deprecations.vv:69:4: error: method `Abc.simply_deprecated` has been deprecated; custom message 3
|
||||
67 | a.future()
|
||||
68 | a.past()
|
||||
69 | a.simply_deprecated()
|
||||
| ~~~~~~~~~~~~~~~~~~~
|
||||
70 | }
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:7:7: warning: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
5 | _ := p * p //should be error
|
||||
6 | _ := p * 2 //should be error
|
||||
7 | _ := p + 5 //OK but only in unsafe block, r is *int
|
||||
| ~~~~~
|
||||
8 | _ := p - p //OK even in safe code, but n should be isize
|
||||
9 | }
|
||||
vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:8:7: warning: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
6 | _ := p * 2 //should be error
|
||||
7 | _ := p + 5 //OK but only in unsafe block, r is *int
|
||||
8 | _ := p - p //OK even in safe code, but n should be isize
|
||||
| ~~~~~
|
||||
9 | }
|
||||
vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:4:7: error: invalid operator `+` to `&int` and `&int`
|
||||
2 | x := 5
|
||||
3 | p := &x
|
||||
|
@ -19,16 +32,3 @@ vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:6:7: error: invalid oper
|
|||
| ~~~~~
|
||||
7 | _ := p + 5 //OK but only in unsafe block, r is *int
|
||||
8 | _ := p - p //OK even in safe code, but n should be isize
|
||||
vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:7:7: error: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
5 | _ := p * p //should be error
|
||||
6 | _ := p * 2 //should be error
|
||||
7 | _ := p + 5 //OK but only in unsafe block, r is *int
|
||||
| ~~~~~
|
||||
8 | _ := p - p //OK even in safe code, but n should be isize
|
||||
9 | }
|
||||
vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:8:7: error: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
6 | _ := p * 2 //should be error
|
||||
7 | _ := p + 5 //OK but only in unsafe block, r is *int
|
||||
8 | _ := p - p //OK even in safe code, but n should be isize
|
||||
| ~~~~~
|
||||
9 | }
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
vlib/v/checker/tests/enum_cast.vv:9:13: error: 12 does not represent a value of enum Color
|
||||
vlib/v/checker/tests/enum_cast.vv:9:13: warning: 12 does not represent a value of enum Color
|
||||
7 | println(Color(0))
|
||||
8 | println(Color(10))
|
||||
9 | println(Color(12))
|
||||
| ~~~~~~~~~
|
||||
10 | println(Color(-10))
|
||||
11 |
|
||||
vlib/v/checker/tests/enum_cast.vv:10:13: error: -10 does not represent a value of enum Color
|
||||
vlib/v/checker/tests/enum_cast.vv:10:13: warning: -10 does not represent a value of enum Color
|
||||
8 | println(Color(10))
|
||||
9 | println(Color(12))
|
||||
10 | println(Color(-10))
|
||||
| ~~~~~~~~~~
|
||||
11 |
|
||||
12 | println(Permissions(0b101))
|
||||
vlib/v/checker/tests/enum_cast.vv:13:13: error: 10 does not represent a value of enum Permissions
|
||||
vlib/v/checker/tests/enum_cast.vv:13:13: warning: 10 does not represent a value of enum Permissions
|
||||
11 |
|
||||
12 | println(Permissions(0b101))
|
||||
13 | println(Permissions(0b1010))
|
||||
| ~~~~~~~~~~~~~~~~~~~
|
||||
14 | println(Permissions(-1))
|
||||
15 | }
|
||||
vlib/v/checker/tests/enum_cast.vv:14:13: error: -1 does not represent a value of enum Permissions
|
||||
vlib/v/checker/tests/enum_cast.vv:14:13: warning: -1 does not represent a value of enum Permissions
|
||||
12 | println(Permissions(0b101))
|
||||
13 | println(Permissions(0b1010))
|
||||
14 | println(Permissions(-1))
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
vlib/v/checker/tests/filter_func_return_nonbool_err.vv:2:2: warning: unused variable: `list`
|
||||
1 | fn main() {
|
||||
2 | list := [1,2,3].filter(stringsss(it))
|
||||
| ~~~~
|
||||
3 | }
|
||||
4 |
|
||||
vlib/v/checker/tests/filter_func_return_nonbool_err.vv:2:25: error: type mismatch, `stringsss` must return a bool
|
||||
1 | fn main() {
|
||||
2 | list := [1,2,3].filter(stringsss(it))
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
vlib/v/checker/tests/fixed_array_conv.vv:6:5: warning: cannot cast a fixed array (use e.g. `&arr[0]` instead)
|
||||
4 | mut ip := &int(0)
|
||||
5 | ip = arr
|
||||
6 | _ = &int(arr)
|
||||
| ~~~~~~~~~
|
||||
7 | _ = p
|
||||
8 | _ = ip
|
||||
vlib/v/checker/tests/fixed_array_conv.vv:3:3: error: mismatched types `voidptr` and `[2]int`
|
||||
1 | arr := [2,3]!
|
||||
2 | mut p := voidptr(0)
|
||||
|
@ -12,13 +19,6 @@ vlib/v/checker/tests/fixed_array_conv.vv:5:4: error: mismatched types `&int` and
|
|||
| ^
|
||||
6 | _ = &int(arr)
|
||||
7 | _ = p
|
||||
vlib/v/checker/tests/fixed_array_conv.vv:6:5: error: cannot cast a fixed array (use e.g. `&arr[0]` instead)
|
||||
4 | mut ip := &int(0)
|
||||
5 | ip = arr
|
||||
6 | _ = &int(arr)
|
||||
| ~~~~~~~~~
|
||||
7 | _ = p
|
||||
8 | _ = ip
|
||||
vlib/v/checker/tests/fixed_array_conv.vv:11:13: error: cannot use `[2]int` as `voidptr` in argument 1 to `memdup`
|
||||
9 |
|
||||
10 | unsafe {
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
vlib/v/checker/tests/generic_interface_err.vv:10:1: warning: unused variable: `i`
|
||||
8 |
|
||||
9 | s := Struct{7}
|
||||
10 | i := Interface(s)
|
||||
| ^
|
||||
vlib/v/checker/tests/generic_interface_err.vv:9:6: error: generic struct init must specify type parameter, e.g. Foo<int>
|
||||
7 | }
|
||||
8 |
|
||||
|
|
|
@ -1,3 +1,31 @@
|
|||
vlib/v/checker/tests/goto_label.vv:7:8: warning: `goto` requires `unsafe` (consider using labelled break/continue)
|
||||
5 | goto a1
|
||||
6 | _ = fn(){
|
||||
7 | goto f1
|
||||
| ~~
|
||||
8 | goto f2
|
||||
9 | goto a1
|
||||
vlib/v/checker/tests/goto_label.vv:8:8: warning: `goto` requires `unsafe` (consider using labelled break/continue)
|
||||
6 | _ = fn(){
|
||||
7 | goto f1
|
||||
8 | goto f2
|
||||
| ~~
|
||||
9 | goto a1
|
||||
10 | a1:
|
||||
vlib/v/checker/tests/goto_label.vv:9:8: warning: `goto` requires `unsafe` (consider using labelled break/continue)
|
||||
7 | goto f1
|
||||
8 | goto f2
|
||||
9 | goto a1
|
||||
| ~~
|
||||
10 | a1:
|
||||
11 | goto a1
|
||||
vlib/v/checker/tests/goto_label.vv:11:8: warning: `goto` requires `unsafe` (consider using labelled break/continue)
|
||||
9 | goto a1
|
||||
10 | a1:
|
||||
11 | goto a1
|
||||
| ~~
|
||||
12 | }
|
||||
13 | f2:
|
||||
vlib/v/checker/tests/goto_label.vv:5:7: error: unknown label `a1`
|
||||
3 | goto f2
|
||||
4 | f1:
|
||||
|
@ -19,20 +47,6 @@ vlib/v/checker/tests/goto_label.vv:8:8: error: unknown label `f2`
|
|||
| ~~
|
||||
9 | goto a1
|
||||
10 | a1:
|
||||
vlib/v/checker/tests/goto_label.vv:9:8: error: `goto` requires `unsafe` (consider using labelled break/continue)
|
||||
7 | goto f1
|
||||
8 | goto f2
|
||||
9 | goto a1
|
||||
| ~~
|
||||
10 | a1:
|
||||
11 | goto a1
|
||||
vlib/v/checker/tests/goto_label.vv:11:8: error: `goto` requires `unsafe` (consider using labelled break/continue)
|
||||
9 | goto a1
|
||||
10 | a1:
|
||||
11 | goto a1
|
||||
| ~~
|
||||
12 | }
|
||||
13 | f2:
|
||||
vlib/v/checker/tests/goto_label.vv:14:7: error: unknown label `a1`
|
||||
12 | }
|
||||
13 | f2:
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
vlib/v/checker/tests/interface_generic_err.vv:8:1: warning: unused variable: `why`
|
||||
6 | // no segfault without generic
|
||||
7 | what := What{}
|
||||
8 | why := Why(what)
|
||||
| ~~~
|
||||
vlib/v/checker/tests/interface_generic_err.vv:7:9: error: generic struct init must specify type parameter, e.g. Foo<int>
|
||||
5 |
|
||||
6 | // no segfault without generic
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
vlib/v/checker/tests/map_func_void_return_err.vv:2:2: warning: unused variable: `list`
|
||||
1 | fn main() {
|
||||
2 | list := [1,2,3].map(voids(it))
|
||||
| ~~~~
|
||||
3 | }
|
||||
4 |
|
||||
vlib/v/checker/tests/map_func_void_return_err.vv:2:22: error: type mismatch, `voids` does not return anything
|
||||
1 | fn main() {
|
||||
2 | list := [1,2,3].map(voids(it))
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
vlib/v/checker/tests/map_of_generic_struct_init_err.vv:6:6: warning: unused variable: `m`
|
||||
4 |
|
||||
5 | fn main() {
|
||||
6 | mut m := map[string]Item{}
|
||||
| ^
|
||||
7 | }
|
||||
vlib/v/checker/tests/map_of_generic_struct_init_err.vv:6:11: error: generic struct `Item` must specify type parameter, e.g. Foo<int>
|
||||
4 |
|
||||
4 |
|
||||
5 | fn main() {
|
||||
6 | mut m := map[string]Item{}
|
||||
| ~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
vlib/v/checker/tests/mut_arg.vv:6:3: warning: automatic referencing/dereferencing is deprecated and will be removed soon (got: 0 references, expected: 1 references)
|
||||
4 | }
|
||||
5 |
|
||||
6 | f([3,4])
|
||||
| ~~~~~
|
||||
7 | mut a := [1,2]
|
||||
8 | f(a)
|
||||
vlib/v/checker/tests/mut_arg.vv:8:3: warning: automatic referencing/dereferencing is deprecated and will be removed soon (got: 0 references, expected: 1 references)
|
||||
6 | f([3,4])
|
||||
7 | mut a := [1,2]
|
||||
8 | f(a)
|
||||
| ^
|
||||
9 |
|
||||
10 | g(mut [3,4])
|
||||
vlib/v/checker/tests/mut_arg.vv:6:3: error: function `f` parameter `par` is `mut`, so use `mut [3, 4]` instead
|
||||
4 | }
|
||||
5 |
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
vlib/v/checker/tests/redundant_parentheses_warning.vv:3:7: error: redundant parentheses are used
|
||||
vlib/v/checker/tests/redundant_parentheses_warning.vv:3:7: warning: redundant parentheses are used
|
||||
1 | fn main() {
|
||||
2 | a := 2
|
||||
3 | b := ((a + 2))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
vlib/v/checker/tests/require_or_block_sumtype_map.err.vv:8:8: error: `or {}` block required when indexing a map with sum type value
|
||||
vlib/v/checker/tests/require_or_block_sumtype_map.err.vv:8:8: warning: `or {}` block required when indexing a map with sum type value
|
||||
6 | println(y)
|
||||
7 | }
|
||||
8 | _ := x['nonexisting']
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv:11:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv:11:7: warning: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
9 | fn main() {
|
||||
10 | abc := Abc<int>{}
|
||||
11 | _ := Xyz(abc)
|
||||
| ~~~~~~~~
|
||||
12 | }
|
||||
vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv:11:7: error: cannot convert struct `Abc<int>` to struct `Xyz`
|
||||
9 | fn main() {
|
||||
10 | abc := Abc<int>{}
|
||||
11 | _ := Xyz(abc)
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv:12:7: warning: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
10 | fn main() {
|
||||
11 | abc := Abc{}
|
||||
12 | _ := Xyz(abc)
|
||||
| ~~~~~~~~
|
||||
13 | }
|
||||
vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv:12:7: error: cannot convert struct `Abc` to struct `Xyz`
|
||||
10 | fn main() {
|
||||
11 | abc := Abc{}
|
||||
12 | _ := Xyz(abc)
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv:12:7: warning: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
10 | fn main() {
|
||||
11 | abc := Abc{}
|
||||
12 | _ := Xyz(abc)
|
||||
| ~~~~~~~~
|
||||
13 | }
|
||||
vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv:12:7: error: cannot convert struct `Abc` to struct `Xyz`
|
||||
10 | fn main() {
|
||||
11 | abc := Abc{}
|
||||
12 | _ := Xyz(abc)
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv:12:7: warning: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
10 | fn main() {
|
||||
11 | abc := Abc{}
|
||||
12 | _ := Xyz(abc)
|
||||
| ~~~~~~~~
|
||||
13 | }
|
||||
vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv:12:7: error: cannot convert struct `Abc` to struct `Xyz`
|
||||
10 | fn main() {
|
||||
11 | abc := Abc{}
|
||||
12 | _ := Xyz(abc)
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv:12:7: warning: casting to struct is deprecated, use e.g. `Struct{...expr}` instead
|
||||
10 | fn main() {
|
||||
11 | abc := Abc{}
|
||||
12 | _ := Xyz(abc)
|
||||
| ~~~~~~~~
|
||||
13 | }
|
||||
vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv:12:7: error: cannot convert struct `Abc` to struct `Xyz`
|
||||
10 | fn main() {
|
||||
11 | abc := Abc{}
|
||||
12 | _ := Xyz(abc)
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
vlib/v/checker/tests/struct_unneeded_default.vv:2:10: error: unnecessary default value of `0`: struct fields are zeroed by default
|
||||
vlib/v/checker/tests/struct_unneeded_default.vv:2:10: warning: unnecessary default value of `0`: struct fields are zeroed by default
|
||||
1 | struct Test {
|
||||
2 | n int = 0
|
||||
| ^
|
||||
3 | s string = ''
|
||||
4 | b bool = false
|
||||
vlib/v/checker/tests/struct_unneeded_default.vv:3:13: error: unnecessary default value of '': struct fields are zeroed by default
|
||||
vlib/v/checker/tests/struct_unneeded_default.vv:3:13: warning: unnecessary default value of '': struct fields are zeroed by default
|
||||
1 | struct Test {
|
||||
2 | n int = 0
|
||||
3 | s string = ''
|
||||
| ~~
|
||||
4 | b bool = false
|
||||
5 | }
|
||||
vlib/v/checker/tests/struct_unneeded_default.vv:4:11: error: unnecessary default value `false`: struct fields are zeroed by default
|
||||
vlib/v/checker/tests/struct_unneeded_default.vv:4:11: warning: unnecessary default value `false`: struct fields are zeroed by default
|
||||
2 | n int = 0
|
||||
3 | s string = ''
|
||||
4 | b bool = false
|
||||
| ~~~~~
|
||||
5 | }
|
||||
6 |
|
||||
6 |
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
vlib/v/checker/tests/undefined_type_on_sumtype.vv:1:17: error: unknown type `Token`.
|
||||
Did you mean `Ok<[]Token>`?
|
||||
1 | type ParseRes = Result<[]Token, ParseErr>
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
2 |
|
||||
3 | // Token type is unknown
|
||||
vlib/v/checker/tests/undefined_type_on_sumtype.vv:30:4: error: unused variable: `rx`
|
||||
vlib/v/checker/tests/undefined_type_on_sumtype.vv:30:4: warning: unused variable: `rx`
|
||||
28 | match r {
|
||||
29 | Some<ParseRes> {
|
||||
30 | rx := r.value
|
||||
| ~~
|
||||
31 | }
|
||||
32 | None<ParseRes> {}
|
||||
vlib/v/checker/tests/undefined_type_on_sumtype.vv:1:17: error: unknown type `Token`.
|
||||
Did you mean `Ok<[]Token>`?
|
||||
1 | type ParseRes = Result<[]Token, ParseErr>
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
2 |
|
||||
3 | // Token type is unknown
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
vlib/v/checker/tests/union_unsafe_fields.vv:10:9: error: reading a union field (or its address) requires `unsafe`
|
||||
vlib/v/checker/tests/union_unsafe_fields.vv:10:9: warning: reading a union field (or its address) requires `unsafe`
|
||||
8 | mut u := Uf32{u: 3}
|
||||
9 | u.f = 3.3 // ok
|
||||
10 | _ := u.u
|
||||
| ^
|
||||
11 | return &u.f
|
||||
12 | }
|
||||
vlib/v/checker/tests/union_unsafe_fields.vv:11:12: error: reading a union field (or its address) requires `unsafe`
|
||||
vlib/v/checker/tests/union_unsafe_fields.vv:11:12: warning: reading a union field (or its address) requires `unsafe`
|
||||
9 | u.f = 3.3 // ok
|
||||
10 | _ := u.u
|
||||
11 | return &u.f
|
||||
| ^
|
||||
12 | }
|
||||
vlib/v/checker/tests/union_unsafe_fields.vv:11:9: error: fn `f` expects you to return a non reference type `f32`, but you are returning `&f32` instead
|
||||
9 | u.f = 3.3 // ok
|
||||
10 | _ := u.u
|
||||
11 | return &u.f
|
||||
| ^
|
||||
12 | }
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv:3:16: error: function `C.malloc` must be called from an `unsafe` block
|
||||
1 |
|
||||
vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv:3:16: warning: function `C.malloc` must be called from an `unsafe` block
|
||||
1 |
|
||||
2 | fn test_c() {
|
||||
3 | mut p := C.malloc(4)
|
||||
| ~~~~~~~~~
|
||||
4 | s := 'hope'
|
||||
5 | C.memcpy(p, s.str, 4)
|
||||
vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv:5:7: error: function `C.memcpy` must be called from an `unsafe` block
|
||||
vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv:5:7: warning: function `C.memcpy` must be called from an `unsafe` block
|
||||
3 | mut p := C.malloc(4)
|
||||
4 | s := 'hope'
|
||||
5 | C.memcpy(p, s.str, 4)
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:4:6: error: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:4:6: warning: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
2 | mut v := 5
|
||||
3 | mut p := &v
|
||||
4 | p++
|
||||
| ~~
|
||||
5 | p += 2
|
||||
6 | _ := v
|
||||
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:5:7: error: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:5:7: warning: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
3 | mut p := &v
|
||||
4 | p++
|
||||
5 | p += 2
|
||||
| ~~
|
||||
6 | _ := v
|
||||
7 | }
|
||||
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:11:14: error: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:11:14: warning: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
9 | fn test_ptr_infix() {
|
||||
10 | v := 4
|
||||
11 | mut q := &v - 1
|
||||
| ~~~~~~
|
||||
12 | q = q + 3
|
||||
13 | _ := q
|
||||
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:12:9: error: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:12:9: warning: pointer arithmetic is only allowed in `unsafe` blocks
|
||||
10 | v := 4
|
||||
11 | mut q := &v - 1
|
||||
12 | q = q + 3
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
vlib/v/checker/tests/unsafe_required.vv:8:7: error: method `S1.f` must be called from an `unsafe` block
|
||||
vlib/v/checker/tests/unsafe_required.vv:8:7: warning: method `S1.f` must be called from an `unsafe` block
|
||||
6 | fn test_funcs() {
|
||||
7 | s := S1{}
|
||||
8 | s.f()
|
||||
| ~~~
|
||||
9 | }
|
||||
10 |
|
||||
vlib/v/checker/tests/unsafe_required.vv:16:7: error: pointer indexing is only allowed in `unsafe` blocks
|
||||
vlib/v/checker/tests/unsafe_required.vv:16:7: warning: pointer indexing is only allowed in `unsafe` blocks
|
||||
14 | _ = b[0] // OK
|
||||
15 | c := &b
|
||||
16 | _ = c[0]
|
||||
| ~~~
|
||||
17 |
|
||||
18 | v := 4
|
||||
vlib/v/checker/tests/unsafe_required.vv:20:10: error: pointer indexing is only allowed in `unsafe` blocks
|
||||
vlib/v/checker/tests/unsafe_required.vv:20:10: warning: pointer indexing is only allowed in `unsafe` blocks
|
||||
18 | v := 4
|
||||
19 | p := &v
|
||||
20 | _ = p[0]
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
vlib/v/checker/tests/use_deprecated_function_warning.vv:12:2: error: function `xyz` has been deprecated
|
||||
vlib/v/checker/tests/use_deprecated_function_warning.vv:12:2: warning: function `xyz` has been deprecated
|
||||
10 |
|
||||
11 | fn main() {
|
||||
12 | xyz()
|
||||
| ~~~~~
|
||||
13 | abc()
|
||||
14 | }
|
||||
vlib/v/checker/tests/use_deprecated_function_warning.vv:13:2: error: function `abc` has been deprecated; use foo2 instead
|
||||
vlib/v/checker/tests/use_deprecated_function_warning.vv:13:2: warning: function `abc` has been deprecated; use foo2 instead
|
||||
11 | fn main() {
|
||||
12 | xyz()
|
||||
13 | abc()
|
||||
| ~~~~~
|
||||
14 | }
|
||||
15 |
|
||||
vlib/v/checker/tests/use_deprecated_function_warning.vv:23:4: error: method `S1.m` has been deprecated; use bar instead
|
||||
vlib/v/checker/tests/use_deprecated_function_warning.vv:23:4: warning: method `S1.m` has been deprecated; use bar instead
|
||||
21 | fn method() {
|
||||
22 | s := S1{}
|
||||
23 | s.m()
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
vlib/v/checker/tests/var_duplicate_const.vv:4:5: error: duplicate of a const name `size`
|
||||
2 |
|
||||
vlib/v/checker/tests/var_duplicate_const.vv:4:5: warning: duplicate of a const name `size`
|
||||
2 |
|
||||
3 | fn main() {
|
||||
4 | size := 11
|
||||
| ~~~~
|
||||
5 | println(main.size)
|
||||
6 | }
|
||||
vlib/v/checker/tests/var_duplicate_const.vv:4:5: warning: unused variable: `size`
|
||||
2 |
|
||||
3 | fn main() {
|
||||
4 | size := 11
|
||||
| ~~~~
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
vlib/v/checker/tests/vweb_routing_checks.vv:20:1: error: mismatched parameters count between vweb method `App.bar` (1) and route attribute ['/bar'] (0)
|
||||
vlib/v/checker/tests/vweb_routing_checks.vv:20:1: warning: mismatched parameters count between vweb method `App.bar` (1) and route attribute ['/bar'] (0)
|
||||
18 | // segfault because path taks 0 vars and fcn takes 1 arg
|
||||
19 | ['/bar']
|
||||
20 | pub fn (mut app App) bar(a string) vweb.Result {
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
21 | return app.html('works')
|
||||
22 | }
|
||||
vlib/v/checker/tests/vweb_routing_checks.vv:26:1: error: mismatched parameters count between vweb method `App.cow` (0) and route attribute ['/cow/:low'] (1)
|
||||
vlib/v/checker/tests/vweb_routing_checks.vv:26:1: warning: mismatched parameters count between vweb method `App.cow` (0) and route attribute ['/cow/:low'] (1)
|
||||
24 | // no segfault, but it shouldnt compile
|
||||
25 | ['/cow/:low']
|
||||
26 | pub fn (mut app App) cow() vweb.Result {
|
||||
|
|
|
@ -4,7 +4,6 @@ import term
|
|||
import v.util.diff
|
||||
import v.util.vtest
|
||||
import time
|
||||
import sync
|
||||
import runtime
|
||||
import benchmark
|
||||
|
||||
|
@ -12,12 +11,22 @@ const skip_files = [
|
|||
'non_existing.vv', // minimize commit diff churn, do not remove
|
||||
]
|
||||
|
||||
const skip_on_ubuntu_musl = [
|
||||
'vlib/v/checker/tests/vweb_tmpl_used_var.vv',
|
||||
const skip_on_cstrict = [
|
||||
'vlib/v/checker/tests/missing_c_lib_header_1.vv',
|
||||
'vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv',
|
||||
]
|
||||
|
||||
const skip_on_ubuntu_musl = [
|
||||
'vlib/v/checker/tests/vweb_tmpl_used_var.vv',
|
||||
'vlib/v/checker/tests/vweb_routing_checks.vv',
|
||||
]
|
||||
|
||||
const vexe = os.getenv('VEXE')
|
||||
|
||||
const turn_off_vcolors = os.setenv('VCOLORS', 'never', true)
|
||||
|
||||
const show_cmd = os.getenv('VTEST_SHOW_CMD') != ''
|
||||
|
||||
// This is needed, because some of the .vv files are tests, and we do need stable
|
||||
// output from them, that can be compared against their .out files:
|
||||
const turn_on_normal_test_runner = os.setenv('VTEST_RUNNER', 'normal', true)
|
||||
|
@ -26,6 +35,10 @@ const should_autofix = os.getenv('VAUTOFIX') != ''
|
|||
|
||||
const github_job = os.getenv('GITHUB_JOB')
|
||||
|
||||
const v_ci_ubuntu_musl = os.getenv('V_CI_UBUNTU_MUSL').len > 0
|
||||
|
||||
const v_ci_cstrict = os.getenv('V_CI_CSTRICT').len > 0
|
||||
|
||||
struct TaskDescription {
|
||||
vexe string
|
||||
evars string
|
||||
|
@ -54,7 +67,6 @@ mut:
|
|||
}
|
||||
|
||||
fn test_all() {
|
||||
vexe := os.getenv('VEXE')
|
||||
vroot := os.dir(vexe)
|
||||
os.chdir(vroot) or {}
|
||||
checker_dir := 'vlib/v/checker/tests'
|
||||
|
@ -74,14 +86,13 @@ fn test_all() {
|
|||
module_tests := get_tests_in_dir(module_dir, true)
|
||||
run_tests := get_tests_in_dir(run_dir, false)
|
||||
skip_unused_dir_tests := get_tests_in_dir(skip_unused_dir, false)
|
||||
// -prod is used for the parser and checker tests, so that warns are errors
|
||||
mut tasks := Tasks{
|
||||
vexe: vexe
|
||||
label: 'all tests'
|
||||
}
|
||||
tasks.add('', parser_dir, '-prod', '.out', parser_tests, false)
|
||||
tasks.add('', checker_dir, '-prod', '.out', checker_tests, false)
|
||||
tasks.add('', scanner_dir, '-prod', '.out', scanner_tests, false)
|
||||
tasks.add('', parser_dir, '', '.out', parser_tests, false)
|
||||
tasks.add('', checker_dir, '', '.out', checker_tests, false)
|
||||
tasks.add('', scanner_dir, '', '.out', scanner_tests, false)
|
||||
tasks.add('', checker_dir, '-enable-globals run', '.run.out', ['globals_error.vv'],
|
||||
false)
|
||||
tasks.add('', global_run_dir, '-enable-globals run', '.run.out', global_run_tests,
|
||||
|
@ -162,15 +173,11 @@ fn (mut tasks Tasks) add(custom_vexe string, dir string, voptions string, result
|
|||
}
|
||||
|
||||
fn (mut tasks Tasks) add_evars(evars string, custom_vexe string, dir string, voptions string, result_extension string, tests []string, is_module bool) {
|
||||
mut vexe := tasks.vexe
|
||||
if custom_vexe != '' {
|
||||
vexe = custom_vexe
|
||||
}
|
||||
paths := vtest.filter_vtest_only(tests, basepath: dir)
|
||||
for path in paths {
|
||||
tasks.all << TaskDescription{
|
||||
evars: evars
|
||||
vexe: vexe
|
||||
vexe: if custom_vexe != '' { custom_vexe } else { tasks.vexe }
|
||||
dir: dir
|
||||
voptions: voptions
|
||||
result_extension: result_extension
|
||||
|
@ -186,16 +193,22 @@ fn bstep_message(mut bench benchmark.Benchmark, label string, msg string, sdurat
|
|||
|
||||
// process an array of tasks in parallel, using no more than vjobs worker threads
|
||||
fn (mut tasks Tasks) run() {
|
||||
tasks.show_cmd = os.getenv('VTEST_SHOW_CMD') != ''
|
||||
if tasks.all.len == 0 {
|
||||
return
|
||||
}
|
||||
tasks.show_cmd = show_cmd
|
||||
vjobs := if tasks.parallel_jobs > 0 { tasks.parallel_jobs } else { runtime.nr_jobs() }
|
||||
mut bench := benchmark.new_benchmark()
|
||||
bench.set_total_expected_steps(tasks.all.len)
|
||||
mut work := sync.new_channel<TaskDescription>(u32(tasks.all.len))
|
||||
mut results := sync.new_channel<TaskDescription>(u32(tasks.all.len))
|
||||
mut work := chan TaskDescription{cap: tasks.all.len}
|
||||
mut results := chan TaskDescription{cap: tasks.all.len}
|
||||
mut m_skip_files := skip_files.clone()
|
||||
if os.getenv('V_CI_UBUNTU_MUSL').len > 0 {
|
||||
if v_ci_ubuntu_musl {
|
||||
m_skip_files << skip_on_ubuntu_musl
|
||||
}
|
||||
if v_ci_cstrict {
|
||||
m_skip_files << skip_on_cstrict
|
||||
}
|
||||
$if noskip ? {
|
||||
m_skip_files = []
|
||||
}
|
||||
|
@ -220,11 +233,11 @@ fn (mut tasks Tasks) run() {
|
|||
if tasks.all[i].path in m_skip_files {
|
||||
tasks.all[i].is_skipped = true
|
||||
}
|
||||
unsafe { work.push(&tasks.all[i]) }
|
||||
work <- tasks.all[i]
|
||||
}
|
||||
work.close()
|
||||
for _ in 0 .. vjobs {
|
||||
go work_processor(mut work, mut results)
|
||||
go work_processor(work, results)
|
||||
}
|
||||
if github_job == '' {
|
||||
println('')
|
||||
|
@ -233,7 +246,7 @@ fn (mut tasks Tasks) run() {
|
|||
mut total_errors := 0
|
||||
for _ in 0 .. tasks.all.len {
|
||||
mut task := TaskDescription{}
|
||||
results.pop(&task)
|
||||
task = <-results
|
||||
bench.step()
|
||||
if task.is_skipped {
|
||||
bench.skip()
|
||||
|
@ -261,8 +274,7 @@ fn (mut tasks Tasks) run() {
|
|||
bench.ok()
|
||||
assert true
|
||||
if tasks.show_cmd {
|
||||
eprintln(bstep_message(mut bench, benchmark.b_ok, '$task.cli_cmd $task.path',
|
||||
task.took))
|
||||
eprintln(bstep_message(mut bench, benchmark.b_ok, '$task.cli_cmd', task.took))
|
||||
} else {
|
||||
if github_job == '' {
|
||||
// local mode:
|
||||
|
@ -285,16 +297,13 @@ fn (mut tasks Tasks) run() {
|
|||
|
||||
// a single worker thread spends its time getting work from the `work` channel,
|
||||
// processing the task, and then putting the task in the `results` channel
|
||||
fn work_processor(mut work sync.Channel, mut results sync.Channel) {
|
||||
fn work_processor(work chan TaskDescription, results chan TaskDescription) {
|
||||
for {
|
||||
mut task := TaskDescription{}
|
||||
if !work.pop(&task) {
|
||||
break
|
||||
}
|
||||
mut task := <-work or { break }
|
||||
sw := time.new_stopwatch()
|
||||
task.execute()
|
||||
task.took = sw.elapsed()
|
||||
results.push(&task)
|
||||
results <- task
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1694,6 +1694,7 @@ fn (mut f Fmt) write_generic_call_if_require(node ast.CallExpr) {
|
|||
name = 'JS.' + name
|
||||
}
|
||||
f.write(name)
|
||||
f.mark_import_as_used(name)
|
||||
if i != node.concrete_types.len - 1 {
|
||||
f.write(', ')
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import math.complex { Complex }
|
||||
import gg { MouseButton }
|
||||
import time { Duration }
|
||||
import modtest { Test }
|
||||
|
||||
fn keep_imported_enum_map_key() {
|
||||
bm := map[MouseButton]string{}
|
||||
}
|
||||
|
||||
fn generic<T>() {}
|
||||
|
||||
fn main() {
|
||||
_ := Duration(10) // keep cast type
|
||||
assert *(&f64(&byte(&num) + __offsetof(Complex, re))) == 1.0
|
||||
generic<Test>()
|
||||
}
|
||||
|
|
|
@ -4756,6 +4756,9 @@ fn (mut g Gen) write_init_function() {
|
|||
g.writeln('\tbuiltin_init();')
|
||||
g.writeln('\tvinit_string_literals();')
|
||||
//
|
||||
if g.nr_closures > 0 {
|
||||
g.writeln('\t_closure_mtx_init();')
|
||||
}
|
||||
for mod_name in g.table.modules {
|
||||
g.writeln('\t{ // Initializations for module $mod_name :')
|
||||
g.write(g.inits[mod_name].str())
|
||||
|
|
|
@ -76,86 +76,135 @@ fn c_closure_helpers(pref &pref.Preferences) string {
|
|||
#define __RETURN_ADDRESS() ((char*)__builtin_extract_return_addr(__builtin_return_address(0)))
|
||||
#endif
|
||||
|
||||
#define ASSUMED_PAGE_SIZE 0x4000 // 16K
|
||||
#define _CLOSURE_SIZE (((3*sizeof(void*) > sizeof(__closure_thunk) ? 3*sizeof(void*) : sizeof(__closure_thunk)) + sizeof(void*) - 1) & ~(sizeof(void*) - 1))
|
||||
// equal to `max(3*sizeof(void*), sizeof(__closure_thunk))`, rounded up to the next multiple of `sizeof(void*)`
|
||||
|
||||
// refer to https://godbolt.org/z/r7P3EYv6c for a complete assembly
|
||||
#ifdef __V_amd64
|
||||
static const char __closure_thunk[] = {
|
||||
0x8f, 0x05, 0xda, 0xff, 0xff, 0xff, // pop QWORD PTR [rip - 0x26] # <_orig_rbp>
|
||||
0xff, 0x15, 0xe4, 0xff, 0xff, 0xff, // call QWORD PTR [rip - 0x1C] # <fn>
|
||||
0xff, 0x25, 0xce, 0xff, 0xff, 0xff, // jmp QWORD PTR [rip - 0x32] # <orig_rbp>
|
||||
0x8f, 0x05, 0x0a, 0xc0, 0xff, 0xff, // pop QWORD PTR [rip - return_addr]
|
||||
0xff, 0x15, 0xfc, 0xbf, 0xff, 0xff, // call QWORD PTR [rip - fn]
|
||||
0xff, 0x25, 0xfe, 0xbf, 0xff, 0xff // jmp QWORD PTR [rip - return_addr]
|
||||
};
|
||||
#define __CLOSURE_DATA_OFFSET 20
|
||||
#define __CLOSURE_DATA_OFFSET 0x400C
|
||||
#elif defined(__V_x86)
|
||||
static char __closure_thunk[] = {
|
||||
0xe8, 0x00, 0x00, 0x00, 0x00, // call 4
|
||||
0x59, // pop ecx
|
||||
0x8f, 0x41, 0xeb, // pop DWORD PTR [ecx - 21] # <_orig_rbp>
|
||||
0xff, 0x51, 0xf3, // call DWORD PTR [ecx - 13] # <fn>
|
||||
0xe8, 0x00, 0x00, 0x00, 0x00, // call 4
|
||||
0x59, // pop ecx
|
||||
0xff, 0x61, 0xdf, // jmp DWORD PTR [ecx - 33] # <_orig_rbp>
|
||||
0xe8, 0x00, 0x00, 0x00, 0x00, // call 4
|
||||
0x59, // pop ecx
|
||||
0x8f, 0x81, 0x03, 0xc0, 0xff, 0xff, // pop DWORD PTR [ecx - 0x3ffd] # <return_addr>
|
||||
0xff, 0x91, 0xff, 0xbf, 0xff, 0xff, // call DWORD PTR [ecx - 0x4001] # <fn>
|
||||
0xe8, 0x00, 0x00, 0x00, 0x00, // call 4
|
||||
0x59, // pop ecx
|
||||
0xff, 0xa1, 0xf1, 0xbf, 0xff, 0xff // jmp DWORD PTR [ecx - 0x400f] # <return_addr>
|
||||
};
|
||||
|
||||
#define __CLOSURE_DATA_OFFSET 16
|
||||
#define __CLOSURE_DATA_OFFSET 0x4012
|
||||
#elif defined(__V_arm64)
|
||||
static char __closure_thunk[] = {
|
||||
0x10, 0x00, 0x00, 0x10, // adr x16, start
|
||||
0x1e, 0x02, 0x1e, 0xf8, // str x30, _orig_x30
|
||||
0x50, 0xff, 0xff, 0x58, // ldr x16, fn
|
||||
0x90, 0x00, 0xfe, 0x10, // adr x16, return_addr
|
||||
0x1e, 0x02, 0x00, 0xf9, // str x30, [x16]
|
||||
0x10, 0x00, 0xfe, 0x58, // ldr x16, fn
|
||||
0x00, 0x02, 0x3f, 0xd6, // blr x16
|
||||
0x9e, 0xfe, 0xff, 0x58, // ldr x30, _orig_x30
|
||||
0x1e, 0x00, 0xfe, 0x58, // ldr x30, return_addr
|
||||
0xc0, 0x03, 0x5f, 0xd6 // ret
|
||||
};
|
||||
#define __CLOSURE_DATA_OFFSET 24
|
||||
#define __CLOSURE_DATA_OFFSET 0x4010
|
||||
#elif defined(__V_arm32)
|
||||
// arm32 needs a small page size because its pc-relative addressing range is just ±4095 bytes
|
||||
#undef ASSUMED_PAGE_SIZE
|
||||
#define ASSUMED_PAGE_SIZE 4080
|
||||
#undef _CLOSURE_SIZE
|
||||
#define _CLOSURE_SIZE 28
|
||||
static char __closure_thunk[] = {
|
||||
0x18, 0xe0, 0x0f, 0xe5, // str lr, orig_lr
|
||||
0x14, 0xc0, 0x1f, 0xe5, // ldr ip, fn
|
||||
0xf0, 0xef, 0x0f, 0xe5, // str lr, return_addr
|
||||
0xf8, 0xcf, 0x1f, 0xe5, // ldr ip, fn
|
||||
0x3c, 0xff, 0x2f, 0xe1, // blx ip
|
||||
0x24, 0xe0, 0x1f, 0xe5, // ldr lr, orig_lr
|
||||
0xfc, 0xef, 0x1f, 0xe5, // ldr lr, return_addr
|
||||
0x1e, 0xff, 0x2f, 0xe1 // bx lr
|
||||
};
|
||||
#define __CLOSURE_DATA_OFFSET 16
|
||||
#define __CLOSURE_DATA_OFFSET 0xFFC
|
||||
#endif
|
||||
|
||||
static int _V_PAGE_SIZE = 4096; // pre-initialized to the most common value, in case _vinit is not called (in a DLL, for example)
|
||||
|
||||
static inline void __closure_set_data(void* closure, void* data) {
|
||||
void** p = closure;
|
||||
p[-1] = data;
|
||||
static inline void __closure_set_data(char* closure, void* data) {
|
||||
void** p = (void**)(closure - ASSUMED_PAGE_SIZE);
|
||||
p[0] = data;
|
||||
}
|
||||
|
||||
static inline void __closure_set_function(void* closure, void* f) {
|
||||
void** p = closure;
|
||||
p[-2] = f;
|
||||
static inline void __closure_set_function(char* closure, void* f) {
|
||||
void** p = (void**)(closure - ASSUMED_PAGE_SIZE);
|
||||
p[1] = f;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <synchapi.h>
|
||||
static SRWLOCK _closure_mtx;
|
||||
#define _closure_mtx_init() InitializeSRWLock(&_closure_mtx)
|
||||
#define _closure_mtx_lock() AcquireSRWLockExclusive(&_closure_mtx)
|
||||
#define _closure_mtx_unlock() ReleaseSRWLockExclusive(&_closure_mtx)
|
||||
#else
|
||||
static pthread_mutex_t _closure_mtx;
|
||||
#define _closure_mtx_init() pthread_mutex_init(&_closure_mtx, 0)
|
||||
#define _closure_mtx_lock() pthread_mutex_lock(&_closure_mtx)
|
||||
#define _closure_mtx_unlock() pthread_mutex_unlock(&_closure_mtx)
|
||||
#endif
|
||||
static char* _closure_ptr = 0;
|
||||
static int _closure_cap = 0;
|
||||
|
||||
static void* __closure_create(void* fn, void* data) {
|
||||
_closure_mtx_lock();
|
||||
if (_closure_cap == 0) {
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO si;
|
||||
GetNativeSystemInfo(&si);
|
||||
uint32_t page_size = si.dwPageSize;
|
||||
page_size = page_size * (((ASSUMED_PAGE_SIZE - 1) / page_size) + 1);
|
||||
char* p = VirtualAlloc(NULL, page_size * 2, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (p == NULL) return 0;
|
||||
#else
|
||||
uint32_t page_size = sysconf(_SC_PAGESIZE);
|
||||
page_size = page_size * (((ASSUMED_PAGE_SIZE - 1) / page_size) + 1);
|
||||
int prot = PROT_READ | PROT_WRITE;
|
||||
int flags = MAP_ANONYMOUS | MAP_PRIVATE;
|
||||
char* p = mmap(0, page_size * 2, prot, flags, -1, 0);
|
||||
if (p == MAP_FAILED) return 0;
|
||||
#endif
|
||||
char* x = p + page_size;
|
||||
int remaining = page_size / _CLOSURE_SIZE;
|
||||
_closure_ptr = x;
|
||||
_closure_cap = remaining;
|
||||
while (remaining > 0) {
|
||||
memcpy(x, __closure_thunk, sizeof(__closure_thunk));
|
||||
remaining--;
|
||||
x += _CLOSURE_SIZE;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
DWORD _tmp;
|
||||
VirtualProtect(_closure_ptr, page_size, PAGE_EXECUTE_READ, &_tmp);
|
||||
#else
|
||||
mprotect(_closure_ptr, page_size, PROT_READ | PROT_EXEC);
|
||||
#endif
|
||||
}
|
||||
_closure_cap--;
|
||||
void* closure = _closure_ptr;
|
||||
_closure_ptr += _CLOSURE_SIZE;
|
||||
__closure_set_data(closure, data);
|
||||
__closure_set_function(closure, fn);
|
||||
_closure_mtx_unlock();
|
||||
return closure;
|
||||
}
|
||||
|
||||
static void __closure_destroy(void *closure) {
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO si;
|
||||
GetNativeSystemInfo(&si);
|
||||
uint32_t page_size = si.dwPageSize;
|
||||
char* p = VirtualAlloc(NULL, page_size * 2, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (p == NULL) return 0;
|
||||
page_size = page_size * (((ASSUMED_PAGE_SIZE - 1) / page_size) + 1);
|
||||
VirtualFree(closure, page_size * 2, MEM_RELEASE);
|
||||
#else
|
||||
uint32_t page_size = sysconf(_SC_PAGESIZE);
|
||||
int prot = PROT_READ | PROT_WRITE;
|
||||
int flags = MAP_ANONYMOUS | MAP_PRIVATE;
|
||||
char* p = mmap(0, page_size * 2, prot, flags, -1, 0);
|
||||
if (p == MAP_FAILED) return 0;
|
||||
long page_size = sysconf(_SC_PAGESIZE);
|
||||
page_size = page_size * (((ASSUMED_PAGE_SIZE - 1) / page_size) + 1);
|
||||
munmap((char*)closure - page_size, page_size * 2);
|
||||
#endif
|
||||
|
||||
void* closure = p + page_size;
|
||||
memcpy(closure, __closure_thunk, sizeof(__closure_thunk));
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD _tmp;
|
||||
VirtualProtect(closure, page_size, PAGE_EXECUTE_READ, &_tmp);
|
||||
#else
|
||||
mprotect(closure, page_size, PROT_READ | PROT_EXEC);
|
||||
#endif
|
||||
|
||||
__closure_set_data(closure, data);
|
||||
__closure_set_function(closure, fn);
|
||||
return closure;
|
||||
}
|
||||
')
|
||||
return builder.str()
|
||||
|
|
|
@ -116,6 +116,10 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
|
|||
g.write(')')
|
||||
} else if left.typ.idx() == right.typ.idx()
|
||||
&& left.sym.kind in [.array, .array_fixed, .alias, .map, .struct_, .sum_type, .interface_] {
|
||||
if g.pref.translated && !g.is_builtin_mod {
|
||||
g.gen_plain_infix_expr(node)
|
||||
return
|
||||
}
|
||||
match left.sym.kind {
|
||||
.alias {
|
||||
ptr_typ := g.equality_fn(left.typ)
|
||||
|
@ -201,25 +205,25 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
|
|||
g.write(')')
|
||||
}
|
||||
.struct_ {
|
||||
if g.pref.translated {
|
||||
g.gen_plain_infix_expr(node)
|
||||
} else {
|
||||
ptr_typ := g.equality_fn(left.unaliased)
|
||||
if node.op == .ne {
|
||||
g.write('!')
|
||||
}
|
||||
g.write('${ptr_typ}_struct_eq(')
|
||||
if left.typ.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
g.write(', ')
|
||||
if right.typ.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.right)
|
||||
g.write(')')
|
||||
// if g.pref.translated {
|
||||
// g.gen_plain_infix_expr(node)
|
||||
//} else {
|
||||
ptr_typ := g.equality_fn(left.unaliased)
|
||||
if node.op == .ne {
|
||||
g.write('!')
|
||||
}
|
||||
g.write('${ptr_typ}_struct_eq(')
|
||||
if left.typ.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
g.write(', ')
|
||||
if right.typ.is_ptr() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.right)
|
||||
g.write(')')
|
||||
//}
|
||||
}
|
||||
.sum_type {
|
||||
ptr_typ := g.equality_fn(left.unaliased)
|
||||
|
@ -378,7 +382,8 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) {
|
|||
if right.unaliased_sym.kind == .array {
|
||||
if left.sym.kind in [.sum_type, .interface_] {
|
||||
if node.right is ast.ArrayInit {
|
||||
if node.right.exprs.len > 0 {
|
||||
if node.right.exprs.len > 0
|
||||
&& g.table.sym(node.right.expr_types[0]).kind !in [.sum_type, .interface_] {
|
||||
mut infix_exprs := []ast.InfixExpr{}
|
||||
for i in 0 .. node.right.exprs.len {
|
||||
infix_exprs << ast.InfixExpr{
|
||||
|
@ -397,11 +402,25 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) {
|
|||
}
|
||||
}
|
||||
if node.right is ast.ArrayInit {
|
||||
elem_type := node.right.elem_type
|
||||
elem_sym := g.table.sym(elem_type)
|
||||
if node.right.exprs.len > 0 {
|
||||
// `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3`
|
||||
// avoids an allocation
|
||||
g.write('(')
|
||||
g.infix_expr_in_optimization(node.left, node.right)
|
||||
if elem_sym.kind == .sum_type && left.sym.kind != .sum_type {
|
||||
if node.left_type in elem_sym.sumtype_info().variants {
|
||||
new_node_left := ast.CastExpr{
|
||||
arg: ast.EmptyExpr{}
|
||||
typ: elem_type
|
||||
expr: node.left
|
||||
expr_type: node.left_type
|
||||
}
|
||||
g.infix_expr_in_optimization(new_node_left, node.right)
|
||||
}
|
||||
} else {
|
||||
g.infix_expr_in_optimization(node.left, node.right)
|
||||
}
|
||||
g.write(')')
|
||||
return
|
||||
}
|
||||
|
|
|
@ -125,7 +125,8 @@ fn (mut p Parser) for_stmt() ast.Stmt {
|
|||
is_stack_obj: true
|
||||
})
|
||||
} else if p.scope.known_var(val_var_name) {
|
||||
return p.error('redefinition of value iteration variable `$val_var_name`')
|
||||
return p.error_with_pos('redefinition of value iteration variable `$val_var_name`, use `for ($val_var_name in array) {` if you want to check for a condition instead',
|
||||
val_var_pos)
|
||||
}
|
||||
p.check(.key_in)
|
||||
if p.tok.kind == .name && p.tok.lit in [key_var_name, val_var_name] {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
vlib/v/parser/tests/closure_not_used.vv:3:11: error: unused variable: `a`
|
||||
vlib/v/parser/tests/closure_not_used.vv:3:11: warning: unused variable: `a`
|
||||
1 | fn my_fn() {
|
||||
2 | a := 1
|
||||
3 | f := fn [a] () {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/parser/tests/for_val_in_array_err.vv:5:6: error: redefinition of value iteration variable `val`, use `for (val in array) {` if you want to check for a condition instead
|
||||
3 | fn main() {
|
||||
4 | val := `+`
|
||||
5 | for val in [TokenValue(`+`), TokenValue(`-`)] {
|
||||
| ~~~
|
||||
6 | println("ok")
|
||||
7 | break
|
|
@ -0,0 +1,9 @@
|
|||
type TokenValue = rune | u64
|
||||
|
||||
fn main() {
|
||||
val := `+`
|
||||
for val in [TokenValue(`+`), TokenValue(`-`)] {
|
||||
println("ok")
|
||||
break
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
vlib/v/parser/tests/hash_empty_flag_value.vv:1:1: error: no argument(s) provided for #flag
|
||||
1 | #flag
|
||||
| ~~~~~
|
|
@ -0,0 +1 @@
|
|||
#flag
|
|
@ -237,7 +237,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
|||
}
|
||||
res.run_only = os.getenv('VTEST_ONLY_FN').split_any(',')
|
||||
mut command := ''
|
||||
mut command_pos := 0
|
||||
mut command_pos := -1
|
||||
// for i, arg in args {
|
||||
for i := 0; i < args.len; i++ {
|
||||
arg := args[i]
|
||||
|
@ -289,6 +289,10 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
|||
res.is_help = true
|
||||
}
|
||||
'-v' {
|
||||
if command_pos != -1 {
|
||||
// a -v flag after the command, is intended for the command, not for V itself
|
||||
continue
|
||||
}
|
||||
// `-v` flag is for setting verbosity, but without any args it prints the version, like Clang
|
||||
if args.len > 1 {
|
||||
res.is_verbose = true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
vlib/v/scanner/tests/position_0_err.vv:1:1: error: unused variable: `i`
|
||||
vlib/v/scanner/tests/position_0_err.vv:1:1: warning: unused variable: `i`
|
||||
1 | i := 'hello'
|
||||
| ^
|
||||
2 | x := 3
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
type TokenValue = rune | u64
|
||||
|
||||
fn test_for_cond() {
|
||||
val := `+`
|
||||
for (val in [TokenValue(`+`), TokenValue(`-`)]) {
|
||||
println('ok')
|
||||
break
|
||||
}
|
||||
assert true
|
||||
}
|
|
@ -309,3 +309,13 @@ fn test_in_alias_array() {
|
|||
assert Str('') in [Str(''), Str('a')]
|
||||
assert Struct{} == Struct{}
|
||||
}
|
||||
|
||||
type TokenValue = rune | u64
|
||||
|
||||
fn test_in_array_literal_of_sumtype() {
|
||||
val1 := TokenValue(`+`)
|
||||
assert val1 in [TokenValue(`+`), TokenValue(`-`)]
|
||||
|
||||
val2 := `+`
|
||||
assert val2 in [TokenValue(`+`), TokenValue(`-`)]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
> main start
|
||||
> work started
|
||||
> work x: 0
|
||||
> work x: 1
|
||||
> work x: 2
|
||||
> work x: 3
|
||||
> work x: 4
|
||||
> work x: 5
|
||||
> work x: 6
|
||||
> work x: 7
|
||||
> work x: 8
|
||||
> work x: 9
|
||||
> work ended
|
||||
> main task was finished
|
|
@ -0,0 +1,29 @@
|
|||
import time
|
||||
|
||||
fn work(input chan u32, started chan bool) {
|
||||
println('> work started')
|
||||
started <- true
|
||||
for {
|
||||
x := <-input or { break }
|
||||
println('> work x: $x')
|
||||
time.sleep(50 * time.millisecond)
|
||||
}
|
||||
println('> work ended')
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println('> main start')
|
||||
ch := chan u32{cap: 100}
|
||||
work_started := chan bool{}
|
||||
for x in 0 .. 10 {
|
||||
ch <- x
|
||||
}
|
||||
task := go work(ch, work_started)
|
||||
_ := <-work_started
|
||||
|
||||
ch.close()
|
||||
// println('> main ch.close called') // the position of this is not deterministic
|
||||
|
||||
task.wait()
|
||||
println('> main task was finished')
|
||||
}
|
Loading…
Reference in New Issue