Compare commits

...

33 Commits

Author SHA1 Message Date
Jef Roosens 88d69dca77
ci: Added docker workflow & Dockerfile
ci/woodpecker/push/vc Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
2022-06-10 08:15:48 +02:00
Delyan Angelov b4b58d7d7d
examples: speed up mandelbrot.v a little, increase iterations/details too 2022-06-10 08:14:54 +02:00
yuyi 04f8b0cb1e
cgen: fix fixed array global variable (fix #14712) (#14730) 2022-06-10 08:14:54 +02:00
Larpon e22c3d2a64
ci: remove `VFLAGS: -gc none` from vab runs (#14731) 2022-06-10 08:14:54 +02:00
yuyi eb674411c7
checker: fix generic method on aliases receiver type (#14729) 2022-06-10 08:14:53 +02:00
Delyan Angelov 048431f586
ci: add `VTEST_JUST_ESSENTIAL=1 ./v test-self` mode. Use it for alpine-docker-musl-gcc . 2022-06-10 08:14:53 +02:00
Ben 824c7e42f3
os: correct description of windows_volume function (#14726) 2022-06-10 08:14:53 +02:00
spaceface 086d1ded18
all: enable `-gc boehm` by default (#14577) 2022-06-10 08:14:53 +02:00
yuyi a8843ae549
ast: cleanup in generic_insts_to_concrete() (#14724) 2022-06-10 08:14:53 +02:00
Spydr 8d9ef9a7f3
native: added new helper functions (#14723) 2022-06-10 08:14:52 +02:00
Larpon d908613aa5
gg: fire resize event before init if necessary on Android (#14725) 2022-06-10 08:14:52 +02:00
Ben d65fb03cab
os: add windows_volume function (#14721) 2022-06-10 08:14:52 +02:00
Mikey 10b9dcb06e
term.ui: don't print event data in readme example (vlang#14719) (#14720) 2022-06-10 08:14:52 +02:00
Delyan Angelov 726938ca57
checker: add error for `if c >= A && c <= Z {` in non generic functions 2022-06-10 08:14:52 +02:00
Larpon 701526e310
strings: add split_capital (#14716) 2022-06-10 08:14:52 +02:00
David Valdespino Pavon 1fb9253c0e
net.http: cookie parsing fixes (#14420) 2022-06-10 08:14:51 +02:00
Larpon c897cc15ef
ci: update vab install (vlang/vab#176) (#14713) 2022-06-10 08:14:51 +02:00
Delyan Angelov cb5278f77b
v.vcache: improve the output of `-d trace_usecache_n` 2022-06-10 08:14:51 +02:00
Delyan Angelov 65dfe199c9
v.vcache: support `-d trace_usecache_n` too (less verbose tracing for just the initialisation of CacheManager) 2022-06-10 08:14:51 +02:00
Delyan Angelov 5a5f432588
builtin: add flush_stdout and flush_stderr to builtin.js.v, for feature parity with builtin.c.v 2022-06-10 08:14:51 +02:00
yuyi 44b15c0b93
cgen: fix cast to generic interface (#14708) 2022-06-10 08:14:50 +02:00
ChAoS_UnItY 3ceadec7ad
cgen: fix none literal str() function calling (#14704) 2022-06-10 08:14:50 +02:00
yuyi 24ae92cd0d
checker: fix json decoder with generic struct (#14700) 2022-06-10 08:14:50 +02:00
ChAoS_UnItY 8241713fe4
cgen: fix mutable receiver type calling mapping function causes C error (fix #14230) (#14696) 2022-06-10 08:14:50 +02:00
Delyan Angelov 724f989c2d
ast: use `[direct_array_access]` for `attrs []Attr` lookup methods 2022-06-10 08:14:50 +02:00
Delyan Angelov 54152c9555
cgen: fix missing function names in declarations on `[c2v_variadic][c: xyz]fn deh_fprintf(fstream &C.FILE, fmt &i8)` 2022-06-10 08:14:50 +02:00
Alexander Medvednikov 1157ccc073
checker: allow literal args as references in translated code 2022-06-10 08:14:49 +02:00
yuyi cb4fdc3037
cgen: fix generic interface with non-generic method (#14694) 2022-06-10 08:14:49 +02:00
Alexander Medvednikov 972190dcc0
checker: do not require fn main when building an object file 2022-06-10 08:14:49 +02:00
yuyi d28840036c
cgen: fix nested map index check (fix #14683) (#14687) 2022-06-10 08:14:49 +02:00
ChAoS_UnItY c2315b6c86
cgen: fix array init with it (fix #14679) (#14680) 2022-06-10 08:14:49 +02:00
Leo Developer e5f922df1f
compress: add a new module `compress.gzip` too (#14686) 2022-06-10 08:14:49 +02:00
Ikko Ashimine 633f93d500
builtin: fix typo in array.v (#14688) 2022-06-10 08:14:46 +02:00
66 changed files with 1169 additions and 172 deletions

View File

@ -45,7 +45,7 @@ jobs:
- name: Compile to raw Android (non-graphic) compatible
run: |
# Test that V can compile non-graphic app to Android compatible code *without* using the -apk flag
./v -os android examples/toml.v
./v -os android -gc none examples/toml.v
linux-cross:
runs-on: ubuntu-20.04
@ -98,7 +98,7 @@ jobs:
- name: toml.v can be compiled to raw Android C
run: |
# Test that V can compile non-graphic app to Android compatible code *without* using the -apk flag
./v -os android examples/toml.v
./v -os android -gc none examples/toml.v
windows-cross:

View File

@ -228,7 +228,7 @@ jobs:
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 180
env:
VFLAGS: -cc clang
VFLAGS: -cc clang -gc none
VJOBS: 1
VTEST_SHOW_START: 1
steps:

View File

@ -17,7 +17,7 @@ jobs:
alpine-docker-musl-gcc:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
timeout-minutes: 121
timeout-minutes: 181
container:
# Alpine docker pre-built container
image: thevlang/vlang:alpine-build
@ -46,8 +46,8 @@ jobs:
- name: All code is formatted
run: ./v test-cleancode
- name: Test V fixed tests
run: ./v test-self
- name: Run only essential tests
run: VTEST_JUST_ESSENTIAL=1 ./v test-self
ubuntu-docker-musl:
runs-on: ubuntu-20.04
@ -58,7 +58,7 @@ jobs:
env:
V_CI_MUSL: 1
V_CI_UBUNTU_MUSL: 1
VFLAGS: -cc musl-gcc
VFLAGS: -cc musl-gcc -gc none
volumes:
- ${{github.workspace}}:/opt/vlang

View File

@ -25,20 +25,14 @@ jobs:
- name: Build V
run: make && sudo ./v symlink
- name: Checkout vab
uses: actions/checkout@v2
with:
repository: vlang/vab
path: vab
- name: Build vab
- name: Install vab
run: |
cd vab
v -g vab.v
sudo ln -s $(pwd)/vab /usr/local/bin/vab
v install vab
v -g ~/.vmodules/vab
sudo ln -s ~/.vmodules/vab/vab /usr/local/bin/vab
- name: Run tests
run: v test vab
run: v test ~/.vmodules/vab
- name: Run vab --help
run: vab --help

View File

@ -14,6 +14,8 @@ jobs:
vinix-build:
runs-on: ubuntu-20.04
if: github.event_name != 'push' || github.event.ref == 'refs/heads/master' || github.event.repository.full_name != 'vlang/v'
env:
VFLAGS: -gc none
steps:
- uses: actions/checkout@v2

61
.woodpecker.yml 100644
View File

@ -0,0 +1,61 @@
platform: 'linux/amd64'
branches: ['master']
pipeline:
gen-vc:
# This is what the official CI uses as well
image: 'ubuntu:latest'
secrets:
- deploy_key
commands:
# Install necessary dependencies
- apt-get update -y && apt-get install openssh-client git build-essential -y
# Build the compiler
- make
# Run ssh-agent
- eval $(ssh-agent -s)
# Add ssh key
- echo "$DEPLOY_KEY" | tr -d '\r' | ssh-add -
# Create ssh dir with proper permissions
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
# Configure git credentials
- git config --global user.email 'vbot@rustybever.be'
- git config --global user.name 'vbot'
# Verify SSH keys
- ssh-keyscan git.rustybever.be > ~/.ssh/known_hosts
# The following is copied over from the official repo's CI
# https://github.com/vlang/v/blob/master/.github/workflows/gen_vc.yml
- export "COMMIT_HASH=$(git rev-parse --short HEAD)"
- export "COMMIT_MSG=$(git log -1 --oneline --pretty='%s' HEAD)"
- rm -rf vc
- git clone --depth=1 'git@git.rustybever.be:vieter/vc.git'
- rm -rf vc/v.c vc/v_win.c
- ./v -o vc/v.c -os cross cmd/v
- ./v -o vc/v_win.c -os windows -cc msvc cmd/v
- sed -i "1s/^/#define V_COMMIT_HASH \"$COMMIT_HASH\"\n/" vc/v.c
- sed -i "1s/^/#define V_COMMIT_HASH \"$COMMIT_HASH\"\n/" vc/v_win.c
# ensure the C files are over 5000 lines long, as a safety measure
- '[ $(wc -l < vc/v.c) -gt 5000 ]'
- '[ $(wc -l < vc/v_win.c) -gt 5000 ]'
- git -C vc add v.c v_win.c
- 'git -C vc commit -m "[v:master] $COMMIT_HASH - $COMMIT_MSG"'
# in case there are recent commits:
- git -C vc pull --rebase origin main
- git -C vc push
when:
event: push
publish:
image: woodpeckerci/plugin-docker-buildx
secrets: [ docker_username, docker_password ]
settings:
repo: chewingbever/vlang
tag: latest
dockerfile: Dockerfile.builder
platforms: [ linux/arm64/v8, linux/amd64 ]
# The build can run every time, because we should only push when there's
# actual changes
when:
event: push

View File

@ -28,6 +28,9 @@ endif
ifeq ($(_SYS),Linux)
LINUX := 1
TCCOS := linux
ifneq ($(shell ldd /bin/ls | grep musl),)
TCCOS := linuxmusl
endif
endif
ifeq ($(_SYS),Darwin)

View File

@ -6,7 +6,83 @@ import v.pref
const github_job = os.getenv('GITHUB_JOB')
const just_essential = os.getenv('VTEST_JUST_ESSENTIAL') != ''
const (
essential_list = [
'cmd/tools/vvet/vet_test.v',
'vlib/arrays/arrays_test.v',
'vlib/bitfield/bitfield_test.v',
//
'vlib/builtin/int_test.v',
'vlib/builtin/array_test.v',
'vlib/builtin/float_test.v',
'vlib/builtin/byte_test.v',
'vlib/builtin/rune_test.v',
'vlib/builtin/builtin_test.v',
'vlib/builtin/map_of_floats_test.v',
'vlib/builtin/string_int_test.v',
'vlib/builtin/utf8_test.v',
'vlib/builtin/map_test.v',
'vlib/builtin/string_test.v',
'vlib/builtin/sorting_test.v',
'vlib/builtin/gated_array_string_test.v',
'vlib/builtin/array_shrinkage_test.v',
'vlib/builtin/isnil_test.v',
'vlib/builtin/string_match_glob_test.v',
'vlib/builtin/string_strip_margin_test.v',
//
'vlib/cli/command_test.v',
'vlib/crypto/md5/md5_test.v',
'vlib/dl/dl_test.v',
'vlib/encoding/base64/base64_test.v',
'vlib/encoding/utf8/encoding_utf8_test.v',
'vlib/encoding/utf8/utf8_util_test.v',
'vlib/flag/flag_test.v',
'vlib/json/json_decode_test.v',
'vlib/math/math_test.v',
'vlib/net/tcp_test.v',
'vlib/net/http/http_test.v',
'vlib/net/http/server_test.v',
'vlib/net/http/request_test.v',
'vlib/io/io_test.v',
'vlib/io/os_file_reader_test.v',
'vlib/os/process_test.v',
'vlib/os/file_test.v',
'vlib/os/notify/notify_test.v',
'vlib/os/filepath_test.v',
'vlib/os/environment_test.v',
'vlib/os/glob_test.v',
'vlib/os/os_test.v',
'vlib/rand/random_numbers_test.v',
'vlib/rand/wyrand/wyrand_test.v',
'vlib/runtime/runtime_test.v',
'vlib/semver/semver_test.v',
'vlib/sync/stdatomic/atomic_test.v',
'vlib/sync/thread_test.v',
'vlib/sync/waitgroup_test.v',
'vlib/sync/pool/pool_test.v',
'vlib/strings/builder_test.v',
'vlib/strconv/atof_test.v',
'vlib/strconv/atoi_test.v',
'vlib/strconv/f32_f64_to_string_test.v',
'vlib/strconv/format_test.v',
'vlib/strconv/number_to_base_test.v',
'vlib/time/time_test.v',
'vlib/toml/tests/toml_test.v',
'vlib/v/compiler_errors_test.v',
'vlib/v/doc/doc_test.v',
'vlib/v/eval/interpret_test.v',
'vlib/v/fmt/fmt_keep_test.v',
'vlib/v/fmt/fmt_test.v',
'vlib/v/gen/c/coutput_test.v',
'vlib/v/gen/js/program_test.v',
'vlib/v/gen/native/macho_test.v',
'vlib/v/gen/native/tests/native_test.v',
'vlib/v/pkgconfig/pkgconfig_test.v',
'vlib/v/tests/inout/compiler_test.v',
'vlib/x/json2/json2_test.v',
]
skip_test_files = [
'cmd/tools/vdoc/html_tag_escape_test.v', /* can't locate local module: markdown */
'cmd/tools/vdoc/tests/vdoc_file_test.v', /* fails on Windows; order of output is not as expected */
@ -177,6 +253,11 @@ fn main() {
all_test_files << os.walk_ext(os.join_path(vroot, 'cmd'), '_test.v')
test_js_files := os.walk_ext(os.join_path(vroot, 'vlib'), '_test.js.v')
all_test_files << test_js_files
if just_essential {
rooted_essential_list := essential_list.map(os.join_path(vroot, it))
all_test_files = all_test_files.filter(rooted_essential_list.contains(it))
}
testing.eheader(title)
mut tsession := testing.new_test_session(cmd_prefix, true)
tsession.files << all_test_files.filter(!it.contains('testdata' + os.path_separator))

View File

@ -11,6 +11,8 @@ const chunk_height = 2 // the image is recalculated in chunks, each chunk proces
const zoom_factor = 1.1
const max_iterations = 255
struct ViewRect {
mut:
x_min f64
@ -33,12 +35,13 @@ mut:
iidx int
pixels &u32 = unsafe { vcalloc(pwidth * pheight * sizeof(u32)) }
npixels &u32 = unsafe { vcalloc(pwidth * pheight * sizeof(u32)) } // all drawing happens here, results are swapped at the end
view ViewRect = ViewRect{-2.7610033817025625, 1.1788897130338223, -1.824584023871934, 2.1153096311072788}
view ViewRect = ViewRect{-3.0773593290970673, 1.4952456603855397, -2.019938598189011, 2.3106642054225945}
scale int = 1
ntasks int = runtime.nr_jobs()
}
const colors = [gx.black, gx.blue, gx.red, gx.green, gx.yellow, gx.orange, gx.purple, gx.white,
gx.indigo, gx.violet, gx.black].map(u32(it.abgr8()))
gx.indigo, gx.violet, gx.black, gx.blue, gx.orange, gx.yellow, gx.green].map(u32(it.abgr8()))
struct MandelChunk {
cview ViewRect
@ -83,30 +86,35 @@ fn (mut state AppState) update() {
}
// everything is done, swap the buffer pointers
state.pixels, state.npixels = state.npixels, state.pixels
println('$state.ntasks threads; $sw.elapsed().milliseconds() ms / frame')
println('${state.ntasks:2} threads; ${sw.elapsed().milliseconds():3} ms / frame; scale: ${state.scale:4}')
oview = cview
}
}
[direct_array_access]
fn (mut state AppState) worker(id int, input chan MandelChunk, ready chan bool) {
for {
chunk := <-input or { break }
yscale := chunk.cview.height() / pheight
xscale := chunk.cview.width() / pwidth
mut x, mut y, mut iter := 0.0, 0.0, 0
mut y0 := chunk.ymin * yscale + chunk.cview.y_min
mut x0 := chunk.cview.x_min
for y_pixel := chunk.ymin; y_pixel < chunk.ymax && y_pixel < pheight; y_pixel++ {
y0 := y_pixel * yscale + chunk.cview.y_min
for x_pixel := 0.0; x_pixel < pwidth; x_pixel++ {
x0 := x_pixel * xscale + chunk.cview.x_min
mut x, mut y := x0, y0
mut iter := 0
for ; iter < 80; iter++ {
yrow := unsafe { &state.npixels[int(y_pixel * pwidth)] }
y0 += yscale
x0 = chunk.cview.x_min
for x_pixel := 0; x_pixel < pwidth; x_pixel++ {
x0 += xscale
x, y = x0, y0
for iter = 0; iter < max_iterations; iter++ {
x, y = x * x - y * y + x0, 2 * x * y + y0
if x * x + y * y > 4 {
break
}
}
unsafe {
state.npixels[int(y_pixel * pwidth) + int(x_pixel)] = colors[iter & 7]
yrow[x_pixel] = colors[iter & 15]
}
}
}
@ -128,6 +136,7 @@ fn (mut state AppState) zoom(zoom_factor f64) {
state.view.x_max = c_x + zoom_factor * d_x
state.view.y_min = c_y - zoom_factor * d_y
state.view.y_max = c_y + zoom_factor * d_y
state.scale += if zoom_factor < 1 { 1 } else { -1 }
}
fn (mut state AppState) center(s_x f64, s_y f64) {
@ -182,7 +191,7 @@ fn graphics_keydown(code gg.KeyCode, mod gg.Modifier, mut state AppState) {
// movement
mut d_x, mut d_y := 0.0, 0.0
if code == .enter {
println('> $state.view.x_min | $state.view.x_max | $state.view.y_min | $state.view.y_max')
println('> ViewRect{$state.view.x_min, $state.view.x_max, $state.view.y_min, $state.view.y_max}')
}
if state.gg.pressed_keys[int(gg.KeyCode.left)] {
d_x -= s_x

View File

@ -614,7 +614,7 @@ fn (mut a array) set_unsafe(i int, val voidptr) {
unsafe { vmemcpy(&u8(a.data) + u64(a.element_size) * u64(i), val, a.element_size) }
}
// Private function. Used to implement assigment to the array element.
// Private function. Used to implement assignment to the array element.
fn (mut a array) set(i int, val voidptr) {
$if !no_bounds_checking ? {
if i < 0 || i >= a.len {

View File

@ -1590,3 +1590,14 @@ fn test_inline_array_element_access() {
a2 := [1][0]
assert a2 == 1
}
//
fn f(x int, y int) []int {
return [x, y]
}
fn test_2d_array_init_with_it() {
a := [][]int{len: 6, init: f(it, 2 * it)}
assert a == [[0, 0], [1, 2], [2, 4], [3, 6], [4, 8], [5, 10]]
}

View File

@ -3,16 +3,16 @@ module builtin
$if dynamic_boehm ? {
$if windows {
$if tinyc {
#flag -I@VEXEROOT/thirdparty/libgc/include
#flag -L@VEXEROOT/thirdparty/tcc/lib
#flag -I @VEXEROOT/thirdparty/libgc/include
#flag -L @VEXEROOT/thirdparty/tcc/lib
#flag -lgc
} $else $if msvc {
#flag -DGC_BUILTIN_ATOMIC=1
#flag -I@VEXEROOT/thirdparty/libgc/include
#flag -I @VEXEROOT/thirdparty/libgc/include
} $else {
#flag -DGC_WIN32_THREADS=1
#flag -DGC_BUILTIN_ATOMIC=1
#flag -I@VEXEROOT/thirdparty/libgc
#flag -I @VEXEROOT/thirdparty/libgc
#flag @VEXEROOT/thirdparty/libgc/gc.o
}
} $else {
@ -31,7 +31,7 @@ $if dynamic_boehm ? {
#flag -DGC_BUILTIN_ATOMIC=1
$if macos || linux {
#flag -DGC_PTHREADS=1
#flag -I@VEXEROOT/thirdparty/libgc/include
#flag -I @VEXEROOT/thirdparty/libgc/include
$if (prod && !tinyc && !debug) || !(amd64 || arm64 || i386 || arm32) {
// TODO: replace the architecture check with a `!$exists("@VEXEROOT/thirdparty/tcc/lib/libgc.a")` comptime call
#flag @VEXEROOT/thirdparty/libgc/gc.o
@ -45,7 +45,7 @@ $if dynamic_boehm ? {
#flag -DBUS_PAGE_FAULT=T_PAGEFLT
#flag -DGC_PTHREADS=1
$if !tinyc {
#flag -I@VEXEROOT/thirdparty/libgc/include
#flag -I @VEXEROOT/thirdparty/libgc/include
#flag @VEXEROOT/thirdparty/libgc/gc.o
}
$if tinyc {
@ -62,11 +62,11 @@ $if dynamic_boehm ? {
#flag -DGC_NOT_DLL=1
#flag -DGC_WIN32_THREADS=1
$if tinyc {
#flag -I@VEXEROOT/thirdparty/libgc/include
#flag -I @VEXEROOT/thirdparty/libgc/include
#flag @VEXEROOT/thirdparty/tcc/lib/libgc.a
#flag -luser32
} $else {
#flag -I@VEXEROOT/thirdparty/libgc/include
#flag -I @VEXEROOT/thirdparty/libgc/include
#flag @VEXEROOT/thirdparty/libgc/gc.o
}
} $else $if $pkgconfig('bdw-gc') {

View File

@ -14,6 +14,14 @@ $if js_freestanding {
#globalPrint = globalThis.print
}
pub fn flush_stdout() {
// needed for parity with builtin.c.v
}
pub fn flush_stderr() {
// needed for parity with builtin.c.v
}
pub fn println(s string) {
$if js_freestanding {
#globalPrint(s.str)

View File

@ -1,4 +1,8 @@
## Description:
`compress` is a namespace for (multiple) compression algorithms supported by V.
At the moment, only `compress.zlib` and `compress.deflate` are implemented.
At the moment, the following compression algorithms are implemented:
- `compress.deflate`
- `compress.gzip`
- `compress.zlib`

View File

@ -9,7 +9,7 @@ fn C.tdefl_compress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_le
fn C.tinfl_decompress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags int) voidptr
// compresses an array of bytes based on providing flags and returns the compressed bytes in a new array
// see `gzip.compress([]u8)` and `zlib.compress([]u8)` for default implementations.
// NB: this is a low level api, a high level implementation like zlib/gzip should be preferred
[manualfree]
pub fn compress(data []u8, flags int) ?[]u8 {
if u64(data.len) > compress.max_size {
@ -28,7 +28,7 @@ pub fn compress(data []u8, flags int) ?[]u8 {
}
// decompresses an array of bytes based on providing flags and returns the decompressed bytes in a new array
// see `gzip.decompress([]u8)` and `zlib.decompress([]u8)` for default implementations.
// NB: this is a low level api, a high level implementation like zlib/gzip should be preferred
[manualfree]
pub fn decompress(data []u8, flags int) ?[]u8 {
mut out_len := usize(0)

View File

@ -3,9 +3,6 @@
`compress.deflate` is a module that assists in the compression and
decompression of binary data using `deflate` compression
NOTE: To decompress gzip, discard first 10 bytes of compressed bytes
then use `compress.deflate.decompress`. (Header validation won't be
performed in this case)
## Examples:

View File

@ -2,15 +2,14 @@ module deflate
import compress
// compresses an array of bytes using gzip and returns the compressed bytes in a new array
// Example: compressed := gzip.compress(b)?
// compresses an array of bytes using deflate and returns the compressed bytes in a new array
// Example: compressed := deflate.compress(b)?
pub fn compress(data []u8) ?[]u8 {
return compress.compress(data, 0)
}
// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array
// Example: decompressed := zlib.decompress(b)?
[manualfree]
// decompresses an array of bytes using deflate and returns the decompressed bytes in a new array
// Example: decompressed := deflate.decompress(b)?
pub fn decompress(data []u8) ?[]u8 {
return compress.decompress(data, 0)
}

View File

@ -0,0 +1,18 @@
## Description:
`compress.gzip` is a module that assists in the compression and
decompression of binary data using `gzip` compression
## Examples:
```v
import compress.gzip
fn main() {
uncompressed := 'Hello world!'
compressed := gzip.compress(uncompressed.bytes())?
decompressed := gzip.decompress(compressed)?
assert decompressed == uncompressed.bytes()
}
```

View File

@ -0,0 +1,114 @@
// [rfc1952](https://datatracker.ietf.org/doc/html/rfc1952) compliant
// gzip compression/decompression
module gzip
import compress
import hash.crc32
// compresses an array of bytes using gzip and returns the compressed bytes in a new array
// Example: compressed := gzip.compress(b)?
pub fn compress(data []u8) ?[]u8 {
compressed := compress.compress(data, 0)?
// header
mut result := [
u8(0x1f), // magic numbers (1F 8B)
0x8b,
0x08, // deflate
0x00, // header flags
0x00, // 4-byte timestamp, 0 = no timestamp (00 00 00 00)
0x00,
0x00,
0x00,
0x00, // extra flags
0xff, // operating system id (0xff = unknown)
] // 10 bytes
result << compressed
// trailer
checksum := crc32.sum(data)
length := data.len
result << [
u8(checksum >> 24),
u8(checksum >> 16),
u8(checksum >> 8),
u8(checksum),
u8(length >> 24),
u8(length >> 16),
u8(length >> 8),
u8(length),
] // 8 bytes
return result
}
// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array
// Example: decompressed := gzip.decompress(b)?
pub fn decompress(data []u8) ?[]u8 {
if data.len < 18 {
return error('data is too short, not gzip compressed?')
} else if data[0] != 0x1f || data[1] != 0x8b {
return error('wrong magic numbers, not gzip compressed?')
} else if data[2] != 0x08 {
return error('gzip data is not compressed with DEFLATE')
}
mut header_length := 10
// parse flags, we ignore most of them, but we still need to parse them
// correctly, so we dont accidently decompress something that belongs
// to the header
if data[4] & 0b1110_0000 > 0 { // reserved bits
// rfc 1952 2.3.1.2 Compliance
// A compliant decompressor must give an error indication if any
// reserved bit is non-zero, since such a bit could indicate the
// presence of a new field that would cause subsequent data to be
// interpreted incorrectly.
return error('reserved flags are set, unsupported field detected')
}
// if data[4] & 0b0000_0001 {} // FTEXT
if data[4] & 0b0000_0100 > 0 { // FEXTRA, extra data
xlen := data[header_length]
header_length += xlen + 1
}
if data[4] & 0b0000_1000 > 0 { // FNAME, file name
// filename is zero-terminated, so skip until we hit a zero byte
for header_length < data.len && data[header_length] != 0x00 {
header_length++
}
header_length++
}
if data[4] & 0b0001_0000 > 0 { // FCOMMENT
// comment is zero-terminated, so skip until we hit a zero byte
for header_length < data.len && data[header_length] != 0x00 {
header_length++
}
header_length++
}
if data[4] & 0b0000_0010 > 0 { // FHCRC, flag header crc
if header_length + 12 > data.len {
return error('data too short')
}
checksum_header := crc32.sum(data[..header_length])
checksum_header_expected := (u32(data[header_length]) << 24) | (u32(data[header_length + 1]) << 16) | (u32(data[
header_length + 2]) << 8) | data[header_length + 3]
if checksum_header != checksum_header_expected {
return error('header checksum verification failed')
}
header_length += 4
}
if header_length + 8 > data.len {
return error('data too short')
}
decompressed := compress.decompress(data[header_length..data.len - 8], 0)?
length_expected := (u32(data[data.len - 4]) << 24) | (u32(data[data.len - 3]) << 16) | (u32(data[data.len - 2]) << 8) | data[data.len - 1]
if decompressed.len != length_expected {
return error('length verification failed, got $decompressed.len, expected $length_expected')
}
checksum := crc32.sum(decompressed)
checksum_expected := (u32(data[data.len - 8]) << 24) | (u32(data[data.len - 7]) << 16) | (u32(data[data.len - 6]) << 8) | data[data.len - 5]
if checksum != checksum_expected {
return error('checksum verification failed')
}
return decompressed
}

View File

@ -0,0 +1,134 @@
module gzip
import hash.crc32
fn test_gzip() ? {
uncompressed := 'Hello world!'
compressed := compress(uncompressed.bytes())?
decompressed := decompress(compressed)?
assert decompressed == uncompressed.bytes()
}
fn assert_decompress_error(data []u8, reason string) ? {
decompress(data) or {
assert err.msg() == reason
return
}
return error('did not error')
}
fn test_gzip_invalid_too_short() ? {
assert_decompress_error([]u8{}, 'data is too short, not gzip compressed?')?
}
fn test_gzip_invalid_magic_numbers() ? {
assert_decompress_error([]u8{len: 100}, 'wrong magic numbers, not gzip compressed?')?
}
fn test_gzip_invalid_compression() ? {
mut data := []u8{len: 100}
data[0] = 0x1f
data[1] = 0x8b
assert_decompress_error(data, 'gzip data is not compressed with DEFLATE')?
}
fn test_gzip_with_ftext() ? {
uncompressed := 'Hello world!'
mut compressed := compress(uncompressed.bytes())?
compressed[4] |= 0b0000_0001 // FTEXT
decompressed := decompress(compressed)?
assert decompressed == uncompressed.bytes()
}
fn test_gzip_with_fname() ? {
uncompressed := 'Hello world!'
mut compressed := compress(uncompressed.bytes())?
compressed[4] |= 0b0000_1000
compressed.insert(10, `h`)
compressed.insert(11, `i`)
compressed.insert(12, 0x00)
decompressed := decompress(compressed)?
assert decompressed == uncompressed.bytes()
}
fn test_gzip_with_fcomment() ? {
uncompressed := 'Hello world!'
mut compressed := compress(uncompressed.bytes())?
compressed[4] |= 0b0001_0000
compressed.insert(10, `h`)
compressed.insert(11, `i`)
compressed.insert(12, 0x00)
decompressed := decompress(compressed)?
assert decompressed == uncompressed.bytes()
}
fn test_gzip_with_fname_fcomment() ? {
uncompressed := 'Hello world!'
mut compressed := compress(uncompressed.bytes())?
compressed[4] |= 0b0001_1000
compressed.insert(10, `h`)
compressed.insert(11, `i`)
compressed.insert(12, 0x00)
compressed.insert(10, `h`)
compressed.insert(11, `i`)
compressed.insert(12, 0x00)
decompressed := decompress(compressed)?
assert decompressed == uncompressed.bytes()
}
fn test_gzip_with_fextra() ? {
uncompressed := 'Hello world!'
mut compressed := compress(uncompressed.bytes())?
compressed[4] |= 0b0000_0100
compressed.insert(10, 2)
compressed.insert(11, `h`)
compressed.insert(12, `i`)
decompressed := decompress(compressed)?
assert decompressed == uncompressed.bytes()
}
fn test_gzip_with_hcrc() ? {
uncompressed := 'Hello world!'
mut compressed := compress(uncompressed.bytes())?
compressed[4] |= 0b0000_0010
checksum := crc32.sum(compressed[..10])
compressed.insert(10, u8(checksum >> 24))
compressed.insert(11, u8(checksum >> 16))
compressed.insert(12, u8(checksum >> 8))
compressed.insert(13, u8(checksum))
decompressed := decompress(compressed)?
assert decompressed == uncompressed.bytes()
}
fn test_gzip_with_invalid_hcrc() ? {
uncompressed := 'Hello world!'
mut compressed := compress(uncompressed.bytes())?
compressed[4] |= 0b0000_0010
checksum := crc32.sum(compressed[..10])
compressed.insert(10, u8(checksum >> 24))
compressed.insert(11, u8(checksum >> 16))
compressed.insert(12, u8(checksum >> 8))
compressed.insert(13, u8(checksum + 1))
assert_decompress_error(compressed, 'header checksum verification failed')?
}
fn test_gzip_with_invalid_checksum() ? {
uncompressed := 'Hello world!'
mut compressed := compress(uncompressed.bytes())?
compressed[compressed.len - 5] += 1
assert_decompress_error(compressed, 'checksum verification failed')?
}
fn test_gzip_with_invalid_length() ? {
uncompressed := 'Hello world!'
mut compressed := compress(uncompressed.bytes())?
compressed[compressed.len - 1] += 1
assert_decompress_error(compressed, 'length verification failed, got 12, expected 13')?
}
fn test_gzip_with_invalid_flags() ? {
uncompressed := 'Hello world!'
mut compressed := compress(uncompressed.bytes())?
compressed[4] |= 0b1000_0000
assert_decompress_error(compressed, 'reserved flags are set, unsupported field detected')?
}

View File

@ -4,7 +4,6 @@ import compress
// compresses an array of bytes using zlib and returns the compressed bytes in a new array
// Example: compressed := zlib.compress(b)?
[manualfree]
pub fn compress(data []u8) ?[]u8 {
// flags = TDEFL_WRITE_ZLIB_HEADER (0x01000)
return compress.compress(data, 0x01000)
@ -12,7 +11,6 @@ pub fn compress(data []u8) ?[]u8 {
// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array
// Example: decompressed := zlib.decompress(b)?
[manualfree]
pub fn decompress(data []u8) ?[]u8 {
// flags = TINFL_FLAG_PARSE_ZLIB_HEADER (0x1)
return compress.decompress(data, 0x1)

View File

@ -222,6 +222,26 @@ fn gg_init_sokol_window(user_data voidptr) {
ctx.timage_pip = sgl.make_pipeline(&pipdesc)
//
if ctx.config.init_fn != voidptr(0) {
$if android {
// NOTE on Android sokol can emit resize events *before* the init function is
// called (Android has to initialize a lot more through the Activity system to
// reach a valid coontext) and thus the user's code will miss the resize event.
// To prevent this we emit a custom window resize event, if the screen size has
// changed meanwhile.
win_size := ctx.window_size()
if ctx.width != win_size.width || ctx.height != win_size.height {
ctx.width = win_size.width
ctx.height = win_size.height
if ctx.config.resized_fn != voidptr(0) {
e := Event{
typ: .resized
window_width: ctx.width
window_height: ctx.height
}
ctx.config.resized_fn(&e, ctx.user_data)
}
}
}
ctx.config.init_fn(ctx.user_data)
}
// Create images now that we can do that after sg is inited

View File

@ -0,0 +1,24 @@
import json
struct Result<T> {
ok bool
result T
}
struct User {
id int
username string
}
fn func<T>() ?T {
text := '{"ok": true, "result":{"id":37467243, "username": "ciao"}}'
a := json.decode(Result<T>, text)?
return a.result
}
fn test_decode_with_generic_struct() ? {
ret := func<User>()?
println(ret)
assert ret.id == 37467243
assert ret.username == 'ciao'
}

View File

@ -68,10 +68,11 @@ pub fn read_cookies(h map[string][]string, filter string) []&Cookie {
mut line := line_.trim_space()
mut part := ''
for line.len > 0 {
if line.index_any(';') > 0 {
line_parts := line.split(';')
mut semicolon_position := line.index_any(';') // Store the position of the next semicolon
if semicolon_position > 0 { // So, there is a semicolon, let's parse until that position
line_parts := line[..semicolon_position].split(';') // split the line only until that semicolon
line = line[(semicolon_position + 1)..] // and then skip everything before the semicolon
part = line_parts[0]
line = line_parts[1]
} else {
part = line
line = ''

View File

@ -214,11 +214,17 @@ pub fn parse_request_head(mut reader io.BufferedReader) ?Request {
}
header.coerce(canonicalize: true)
mut request_cookies := map[string]string{}
for _, cookie in read_cookies(header.data, '') {
request_cookies[cookie.name] = cookie.value
}
return Request{
method: method
url: target.str()
header: header
version: version
cookies: request_cookies
}
}

View File

@ -8,13 +8,14 @@ import strings.textscanner
// therefore results may be different for certain operating systems.
const (
fslash = `/`
bslash = `\\`
dot = `.`
qmark = `?`
dot_dot = '..'
empty_str = ''
dot_str = '.'
fslash = `/`
bslash = `\\`
dot = `.`
qmark = `?`
fslash_str = '/'
dot_dot = '..'
empty_str = ''
dot_str = '.'
)
// is_abs_path returns `true` if the given `path` is absolute.
@ -62,8 +63,13 @@ pub fn norm_path(path string) string {
return os.dot_str
}
rooted := is_abs_path(path)
volume := get_volume(path)
volume_len := volume.len
// get the volume name from the path
// if the current operating system is Windows
volume_len := win_volume_len(path)
mut volume := path[..volume_len]
if volume_len != 0 && volume.contains(os.fslash_str) {
volume = volume.replace(os.fslash_str, path_separator)
}
cpath := clean_path(path[volume_len..])
if cpath.len == 0 && volume_len == 0 {
return os.dot_str
@ -217,6 +223,9 @@ fn clean_path(path string) string {
// win_volume_len returns the length of the
// Windows volume/drive from the given `path`.
fn win_volume_len(path string) int {
$if !windows {
return 0
}
plen := path.len
if plen < 2 {
return 0
@ -244,20 +253,6 @@ fn win_volume_len(path string) int {
return 0
}
fn get_volume(path string) string {
$if !windows {
return os.empty_str
}
volume := path[..win_volume_len(path)]
if volume.len == 0 {
return os.empty_str
}
if volume[0] == os.fslash {
return volume.replace('/', '\\')
}
return volume
}
fn is_slash(b u8) bool {
$if windows {
return b == os.bslash || b == os.fslash

View File

@ -152,3 +152,20 @@ fn test_existing_path() {
assert existing_path('$wd//././/.//') or { '' } == '$wd//././/.//'
assert existing_path('$wd//././/.//oh') or { '' } == '$wd//././/.//'
}
fn test_windows_volume() {
$if windows {
assert windows_volume('C:/path\\to/file.v') == 'C:'
assert windows_volume('D:\\.\\') == 'D:'
assert windows_volume('G:') == 'G:'
assert windows_volume('G') == ''
assert windows_volume(r'\\Host\share\files\file.v') == r'\\Host\share'
assert windows_volume('\\\\Host\\') == ''
assert windows_volume(r'\\.\BootPartition2\\files\.\\') == r'\\.\BootPartition2'
assert windows_volume(r'\/.\BootPartition2\\files\.\\') == r'\/.\BootPartition2'
assert windows_volume(r'\\\.\BootPartition2\\files\.\\') == ''
assert windows_volume('') == ''
assert windows_volume('\\') == ''
assert windows_volume('/') == ''
}
}

View File

@ -0,0 +1,17 @@
module os
// windows_volume returns the volume name from the given `path` on a Windows system.
// An empty string is returned if no Windows volume is present.
// Examples (on a Windows system):
// ```v
// assert os.windows_volume(r'C:\path\to\file.v') == 'C:'
// assert os.windows_volume('D:') == 'D:'
// assert os.windows_volume(r'\\Host\share\files\file.v') == r'\\Host\share'
// ```
pub fn windows_volume(path string) string {
volume_len := win_volume_len(path)
if volume_len == 0 {
return empty_str
}
return path[..volume_len]
}

View File

@ -126,3 +126,24 @@ pub fn find_between_pair_string(input string, start string, end string) string {
}
return ''
}
// split_capital returns an array containing the contents of `s` split by capital letters.
// Example: assert strings.split_capital('XYZ') == ['X', 'Y', 'Z']
// Example: assert strings.split_capital('XYStar') == ['X', 'Y', 'Star']
pub fn split_capital(s string) []string {
mut res := []string{}
mut word_start := 0
for idx, c in s {
if c >= `A` && c <= `Z` {
if word_start != idx {
res << s#[word_start..idx]
}
word_start = idx
continue
}
}
if word_start != s.len {
res << s#[word_start..]
}
return res
}

View File

@ -94,3 +94,14 @@ fn test_find_between_pair_family() {
assert '$e1' == '$e2'
}
}
fn test_split_capital() {
assert strings.split_capital('') == []
assert strings.split_capital('abc') == ['abc']
assert strings.split_capital('X') == ['X']
assert strings.split_capital('XX') == ['X', 'X']
assert strings.split_capital('XYZ') == ['X', 'Y', 'Z']
assert strings.split_capital('JohnWilliams') == ['John', 'Williams']
assert strings.split_capital('JDStar') == ['J', 'D', 'Star']
assert strings.split_capital('cpDumpRotarySpring') == ['cp', 'Dump', 'Rotary', 'Spring']
}

View File

@ -13,7 +13,6 @@ mut:
}
fn event(e &tui.Event, x voidptr) {
println(e)
if e.typ == .key_down && e.code == .escape {
exit(0)
}

View File

@ -54,6 +54,28 @@ pub fn (attrs []Attr) contains(str string) bool {
return attrs.any(it.name == str)
}
[direct_array_access]
pub fn (attrs []Attr) find_first(aname string) ?Attr {
for a in attrs {
if a.name == aname {
return a
}
}
return none
}
[direct_array_access]
pub fn (attrs []Attr) find_last(aname string) ?Attr {
for idx := attrs.len - 1; idx > -1; idx-- {
a := attrs[idx]
if a.name == aname {
return a
}
}
return none
}
[direct_array_access]
pub fn (attrs []Attr) find_comptime_define() ?int {
for idx in 0 .. attrs.len {
if attrs[idx].kind == .comptime_define {

View File

@ -1947,12 +1947,12 @@ pub fn (mut t Table) replace_generic_type(typ Type, generic_types []Type) {
// generic struct instantiations to concrete types
pub fn (mut t Table) generic_insts_to_concrete() {
for mut typ in t.type_symbols {
if typ.kind == .generic_inst {
info := typ.info as GenericInst
for mut sym in t.type_symbols {
if sym.kind == .generic_inst {
info := sym.info as GenericInst
parent := t.type_symbols[info.parent_idx]
if parent.kind == .placeholder {
typ.kind = .placeholder
sym.kind = .placeholder
continue
}
match parent.info {
@ -1982,15 +1982,15 @@ pub fn (mut t Table) generic_insts_to_concrete() {
parent_info.concrete_types = info.concrete_types.clone()
parent_info.fields = fields
parent_info.parent_type = new_type(info.parent_idx).set_flag(.generic)
typ.info = Struct{
sym.info = Struct{
...parent_info
is_generic: false
concrete_types: info.concrete_types.clone()
fields: fields
parent_type: new_type(info.parent_idx).set_flag(.generic)
}
typ.is_pub = true
typ.kind = parent.kind
sym.is_pub = true
sym.kind = parent.kind
parent_sym := t.sym(parent_info.parent_type)
for method in parent_sym.methods {
@ -2034,7 +2034,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
param.typ = pt
}
}
typ.register_method(method)
sym.register_method(method)
}
mut all_methods := parent.methods
for imethod in imethods {
@ -2044,7 +2044,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
}
}
}
typ.info = Interface{
sym.info = Interface{
...parent_info
is_generic: false
concrete_types: info.concrete_types.clone()
@ -2052,9 +2052,9 @@ pub fn (mut t Table) generic_insts_to_concrete() {
methods: imethods
parent_type: new_type(info.parent_idx).set_flag(.generic)
}
typ.is_pub = true
typ.kind = parent.kind
typ.methods = all_methods
sym.is_pub = true
sym.kind = parent.kind
sym.methods = all_methods
} else {
util.verror('generic error', 'the number of generic types of interface `$parent.name` is inconsistent with the concrete types')
}
@ -2078,8 +2078,8 @@ pub fn (mut t Table) generic_insts_to_concrete() {
}
for i in 0 .. variants.len {
if variants[i].has_flag(.generic) {
sym := t.sym(variants[i])
if sym.kind == .struct_ && variants[i].idx() != info.parent_idx {
t_sym := t.sym(variants[i])
if t_sym.kind == .struct_ && variants[i].idx() != info.parent_idx {
variants[i] = t.unwrap_generic_type(variants[i], generic_names,
info.concrete_types)
} else {
@ -2091,7 +2091,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
}
}
}
typ.info = SumType{
sym.info = SumType{
...parent_info
is_generic: false
concrete_types: info.concrete_types.clone()
@ -2099,8 +2099,8 @@ pub fn (mut t Table) generic_insts_to_concrete() {
variants: variants
parent_type: new_type(info.parent_idx).set_flag(.generic)
}
typ.is_pub = true
typ.kind = parent.kind
sym.is_pub = true
sym.kind = parent.kind
} else {
util.verror('generic error', 'the number of generic types of sumtype `$parent.name` is inconsistent with the concrete types')
}

View File

@ -93,6 +93,13 @@ fn (mut v Builder) post_process_c_compiler_output(res os.Result) {
}
return
}
if res.exit_code != 0 && v.pref.gc_mode != .no_gc && res.output.contains('libgc.a') {
$if windows {
verror(r'Your V installation may be out-of-date. Try removing `thirdparty\tcc\` and running `.\make.bat`')
} $else {
verror('Your V installation may be out-of-date. Try removing `thirdparty/tcc/` and running `make`')
}
}
for emsg_marker in [builder.c_verror_message_marker, 'error: include file '] {
if res.output.contains(emsg_marker) {
emessage := res.output.all_after(emsg_marker).all_before('\n').all_before('\r').trim_right('\r\n')

View File

@ -117,10 +117,14 @@ pub fn (cflags []CFlag) defines_others_libs() ([]string, []string, []string) {
mut others := []string{}
mut libs := []string{}
for copt in copts_without_obj_files {
if copt.starts_with('-l') || copt.ends_with('.a') {
if copt.starts_with('-l') {
libs << copt
continue
}
if copt.ends_with('.a') {
libs << '"$copt"'
continue
}
if copt.starts_with('-D') {
defines << copt
continue

View File

@ -619,7 +619,7 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
mut to_set := ast.void_type
// resolve generic struct receiver
if node.is_method && param.typ.has_flag(.generic) {
sym := c.table.sym(node.receiver_type)
sym := c.table.final_sym(node.receiver_type)
match sym.info {
ast.Struct, ast.Interface, ast.SumType {
if !isnil(c.table.cur_fn) && c.table.cur_fn.generic_names.len > 0 { // in generic fn

View File

@ -89,6 +89,7 @@ pub mut:
inside_defer bool // true inside `defer {}` blocks
inside_fn_arg bool // `a`, `b` in `a.f(b)`
inside_ct_attr bool // true inside `[if expr]`
inside_x_is_type bool // true inside the Type expression of `if x is Type {`
inside_comptime_for_field bool
skip_flags bool // should `#flag` and `#include` be skipped
fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
@ -156,6 +157,7 @@ fn (mut c Checker) reset_checker_state_at_start_of_new_file() {
c.inside_defer = false
c.inside_fn_arg = false
c.inside_ct_attr = false
c.inside_x_is_type = false
c.skip_flags = false
c.fn_level = 0
c.expr_level = 0
@ -368,7 +370,7 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) {
if !has_main_mod_file {
c.error('project must include a `main` module or be a shared library (compile with `v -shared`)',
token.Pos{})
} else if !has_main_fn {
} else if !has_main_fn && !c.pref.is_o {
c.error('function `main` must be declared in the main module', token.Pos{})
}
}
@ -1605,9 +1607,10 @@ fn (mut c Checker) assert_stmt(node ast.AssertStmt) {
fn (mut c Checker) block(node ast.Block) {
if node.is_unsafe {
prev_unsafe := c.inside_unsafe
c.inside_unsafe = true
c.stmts(node.stmts)
c.inside_unsafe = false
c.inside_unsafe = prev_unsafe
} else {
c.stmts(node.stmts)
}
@ -2021,7 +2024,9 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
c.error('incorrect use of compile-time type', node.pos)
}
ast.EmptyExpr {
print_backtrace()
c.error('checker.expr(): unhandled EmptyExpr', token.Pos{})
return ast.void_type
}
ast.CTempVar {
return node.typ
@ -2269,6 +2274,11 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
return c.struct_init(mut node)
}
ast.TypeNode {
if !c.inside_x_is_type && node.typ.has_flag(.generic) && unsafe { c.table.cur_fn != 0 }
&& c.table.cur_fn.generic_names.len == 0 {
c.error('unexpected generic variable in non-generic function `$c.table.cur_fn.name`',
node.pos)
}
return node.typ
}
ast.TypeOf {

View File

@ -9,7 +9,9 @@ import v.util
import v.pkgconfig
fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
node.left_type = c.expr(node.left)
if node.left !is ast.EmptyExpr {
node.left_type = c.expr(node.left)
}
if node.method_name == 'compile_error' {
c.error(node.args_var, node.pos)
return ast.void_type

View File

@ -506,7 +506,12 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
}
expr := node.args[0].expr
if expr is ast.TypeNode {
sym := c.table.sym(c.unwrap_generic(expr.typ))
mut unwrapped_typ := c.unwrap_generic(expr.typ)
if c.table.sym(expr.typ).kind == .struct_ && expr.typ.has_flag(.generic) {
unwrapped_typ = c.table.unwrap_generic_type(expr.typ, c.table.cur_fn.generic_names,
c.table.cur_concrete_types)
}
sym := c.table.sym(unwrapped_typ)
if c.table.known_type(sym.name) && sym.kind != .placeholder {
mut kind := sym.kind
if sym.info is ast.Alias {
@ -898,7 +903,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
continue
}
if param.typ.is_ptr() && !param.is_mut && !call_arg.typ.is_real_pointer()
&& call_arg.expr.is_literal() && func.language == .v {
&& call_arg.expr.is_literal() && func.language == .v && !c.pref.translated {
c.error('literal argument cannot be passed as reference parameter `${c.table.type_to_str(param.typ)}`',
call_arg.pos)
}
@ -1188,14 +1193,14 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
method = m
has_method = true
} else {
if left_sym.kind in [.struct_, .sum_type, .interface_] {
if final_left_sym.kind in [.struct_, .sum_type, .interface_] {
mut parent_type := ast.void_type
if left_sym.info is ast.Struct {
parent_type = left_sym.info.parent_type
} else if left_sym.info is ast.SumType {
parent_type = left_sym.info.parent_type
} else if left_sym.info is ast.Interface {
parent_type = left_sym.info.parent_type
if final_left_sym.info is ast.Struct {
parent_type = final_left_sym.info.parent_type
} else if final_left_sym.info is ast.SumType {
parent_type = final_left_sym.info.parent_type
} else if final_left_sym.info is ast.Interface {
parent_type = final_left_sym.info.parent_type
}
if parent_type != 0 {
type_sym := c.table.sym(parent_type)
@ -1209,7 +1214,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
if !has_method {
has_method = true
mut embed_types := []ast.Type{}
method, embed_types = c.table.find_method_from_embeds(left_sym, method_name) or {
method, embed_types = c.table.find_method_from_embeds(final_left_sym, method_name) or {
if err.msg() != '' {
c.error(err.msg(), node.pos)
}
@ -1221,14 +1226,14 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
node.from_embed_types = embed_types
}
}
if left_sym.kind == .aggregate {
if final_left_sym.kind == .aggregate {
// the error message contains the problematic type
unknown_method_msg = err.msg()
}
}
if has_method {
// x is Bar<T>, x.foo() -> x.foo<T>()
rec_sym := c.table.sym(node.left_type)
rec_sym := c.table.final_sym(node.left_type)
rec_is_generic := left_type.has_flag(.generic)
mut rec_concrete_types := []ast.Type{}
if rec_sym.info is ast.Struct {
@ -1438,7 +1443,8 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
}
continue
}
if param.typ.is_ptr() && !arg.typ.is_real_pointer() && arg.expr.is_literal() {
if param.typ.is_ptr() && !arg.typ.is_real_pointer() && arg.expr.is_literal()
&& !c.pref.translated {
c.error('literal argument cannot be passed as reference parameter `${c.table.type_to_str(param.typ)}`',
arg.pos)
}

View File

@ -165,9 +165,12 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
c.in_for_count++
prev_loop_label := c.loop_label
c.expected_type = ast.bool_type
typ := c.expr(node.cond)
if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated && !c.file.is_translated {
c.error('non-bool used as for condition', node.pos)
if node.cond !is ast.EmptyExpr {
typ := c.expr(node.cond)
if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated
&& !c.file.is_translated {
c.error('non-bool used as for condition', node.pos)
}
}
if mut node.cond is ast.InfixExpr {
if node.cond.op == .key_is {

View File

@ -12,7 +12,14 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
mut left_type := c.expr(node.left)
node.left_type = left_type
c.expected_type = left_type
if node.op == .key_is {
c.inside_x_is_type = true
}
mut right_type := c.expr(node.right)
if node.op == .key_is {
c.inside_x_is_type = false
}
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] {

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/generic_type_name_in_non_generic_function.vv:3:10: error: unexpected generic variable in non-generic function `main`
1 | fn main() {
2 | c := u8(`D`)
3 | if c >= A && c <= Z {
| ^
4 | println('yes')
5 | } else {

View File

@ -0,0 +1,8 @@
fn main() {
c := u8(`D`)
if c >= A && c <= Z {
println('yes')
} else {
println('no')
}
}

View File

@ -0,0 +1 @@
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

View File

@ -0,0 +1,9 @@
module main
__global (
heap = [10]u8{}
)
fn main() {
println(heap)
}

View File

@ -223,7 +223,7 @@ fn (mut g Gen) array_init_with_fields(node ast.ArrayInit, elem_type Type, is_amp
}
if is_default_array {
g.write('($elem_styp[]){')
g.expr(node.default_expr)
g.write(g.type_default(node.elem_type))
g.write('}[0])')
} else if node.has_len && node.elem_type == ast.string_type {
g.write('&($elem_styp[]){')
@ -345,10 +345,15 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
}
left_type := if node.left_type.has_flag(.shared_f) {
node.left_type.clear_flag(.shared_f).deref()
} else if node.left_type.is_ptr() {
node.left_type.deref()
} else {
node.left_type
}
g.write('${g.typ(left_type)} ${tmp}_orig = ')
if !node.left_type.has_flag(.shared_f) && node.left_type.is_ptr() {
g.write('*')
}
g.expr(node.left)
if node.left_type.has_flag(.shared_f) {
g.write('->val')

View File

@ -2126,8 +2126,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
g.expr(expr)
return
}
if exp_sym.info is ast.Interface && got_type.idx() != expected_type.idx()
&& !expected_type.has_flag(.optional) {
if got_sym.info !is ast.Interface && exp_sym.info is ast.Interface
&& got_type.idx() != expected_type.idx() && !expected_type.has_flag(.optional) {
if expr is ast.StructInit && !got_type.is_ptr() {
g.inside_cast_in_heap++
got_styp := g.cc_type(got_type.ref(), true)
@ -3925,8 +3925,8 @@ fn (mut g Gen) ident(node ast.Ident) {
// `p_mobjthinker` => `P_MobjThinker`
if f := g.table.find_fn(node.name) {
// TODO PERF fn lookup for each fn call in translated mode
if f.attrs.contains('c') {
name = f.attrs[0].arg
if cattr := f.attrs.find_first('c') {
name = cattr.arg
}
}
}
@ -3970,8 +3970,8 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) {
// TODO handle the type in fn casts, not just exprs
/*
info := sym.info as ast.FnType
if info.func.attrs.contains('c') {
// name = f.attrs[0].arg
if cattr := info.func.attrs.find_first('c') {
name = cattr.arg
}
*/
}
@ -4398,11 +4398,11 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
}
}
name := c_name(field.name)
const_name := if node.attrs.contains('export') && !g.is_builtin_mod {
// TODO this only works for the first const in the group for now
node.attrs[0].arg
} else {
'_const_' + name
mut const_name := '_const_' + name
if !g.is_builtin_mod {
if cattr := node.attrs.find_first('export') {
const_name = cattr.arg
}
}
field_expr := field.expr
match field.expr {
@ -4667,7 +4667,8 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
if field.has_expr || cinit {
if g.pref.translated {
g.definitions.write_string(' = ${g.expr_string(field.expr)}')
} else if (field.expr.is_literal() && should_init) || cinit {
} else if (field.expr.is_literal() && should_init) || cinit
|| (field.expr is ast.ArrayInit && (field.expr as ast.ArrayInit).is_fixed) {
// Simple literals can be initialized right away in global scope in C.
// e.g. `int myglobal = 10;`
g.definitions.write_string(' = ${g.expr_string(field.expr)}')
@ -5486,8 +5487,9 @@ fn (mut g Gen) enum_val(node ast.EnumVal) {
// && g.inside_switch
if g.pref.translated && node.typ.is_number() {
// Mostly in translated code, when C enums are used as ints in switches
sym := g.table.sym(node.typ)
g.write('/* $node enum val is_number $node.mod styp=$styp sym=$sym*/_const_main__$node.val')
// sym := g.table.sym(node.typ)
// g.write('/* $node enum val is_number $node.mod styp=$styp sym=$sym*/_const_main__$node.val')
g.write('_const_main__$node.val')
} else {
g.write('${styp}__$node.val')
}
@ -5742,7 +5744,7 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
for method in methods {
mut name := method.name
if inter_info.parent_type.has_flag(.generic) {
if method.generic_names.len > 0 && inter_info.parent_type.has_flag(.generic) {
parent_sym := g.table.sym(inter_info.parent_type)
match parent_sym.info {
ast.Struct, ast.Interface, ast.SumType {
@ -5759,7 +5761,7 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
}
// .speak = Cat_speak
if st_sym.info is ast.Struct {
if st_sym.info.parent_type.has_flag(.generic) {
if method.generic_names.len > 0 && st_sym.info.parent_type.has_flag(.generic) {
name = g.generic_fn_name(st_sym.info.concrete_types, method.name,
false)
}

View File

@ -446,20 +446,16 @@ fn (mut g Gen) c_fn_name(node &ast.FnDecl) ?string {
name = g.generic_fn_name(g.cur_concrete_types, name, true)
}
if (g.pref.translated || g.file.is_translated) && node.attrs.contains('c') {
// This fixes unknown symbols errors when building separate .c => .v files
// into .o files
//
// example:
// [c: 'P_TryMove']
// fn p_trymove(thing &Mobj_t, x int, y int) bool
//
// =>
//
// bool P_TryMove(main__Mobj_t* thing, int x, int y);
//
// In fn_call every time `p_trymove` is called, `P_TryMove` will be generated instead.
name = node.attrs[0].arg
if g.pref.translated || g.file.is_translated {
if cattr := node.attrs.find_first('c') {
// This fixes unknown symbols errors when building separate .c => .v files into .o files
// example:
// [c: 'P_TryMove'] fn p_trymove(thing &Mobj_t, x int, y int) bool
// translates to:
// bool P_TryMove(main__Mobj_t* thing, int x, int y);
// In fn_call every time `p_trymove` is called, `P_TryMove` will be generated instead.
name = cattr.arg
}
}
return name
}
@ -932,6 +928,10 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
return
}
}
} else if node.left is ast.None {
// none.str()
g.gen_expr_to_string(node.left, ast.none_type)
return
}
g.get_str_fn(rec_type)
} else if node.name == 'free' {
@ -1239,8 +1239,8 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
// every time `p_trymove` is called, `P_TryMove` must be generated instead.
if f := g.table.find_fn(node.name) {
// TODO PERF fn lookup for each fn call in translated mode
if f.attrs.contains('c') {
name = f.attrs[0].arg
if cattr := f.attrs.find_first('c') {
name = cattr.arg
}
}
}

View File

@ -484,7 +484,7 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
if !node.is_option {
g.or_block(tmp_opt, node.or_expr, elem_type)
}
g.write('\n$cur_line*($elem_type_str*)${tmp_opt}.data')
g.write('\n${cur_line}(*($elem_type_str*)${tmp_opt}.data)')
}
}
}

View File

View File

@ -0,0 +1,11 @@
// From issue #14679
fn iterate_linear(value1 u32, value2 u32, length u32) []u32 {
step := u32((value2 - value1) / (length - 1))
return []u32{len: int(length), init: value1 + step * u32(it + 1)}
}
pub fn iterate_rect_single(val1 u32, val2 u32, val3 u32, val4 u32, width u32, height u32) [][]u32 {
left := iterate_linear(val1, val3, height)
right := iterate_linear(val2, val4, height)
return [][]u32{len: int(width), init: iterate_linear(left[it], right[it], width)}
}

View File

@ -0,0 +1,3 @@
[1, 0, 0]
[0, 1, 0]
[0, 0, 1]

View File

@ -0,0 +1,18 @@
fn (mut a [][]f32) str() string {
return a.map(it.str()).join('\n')
}
fn identity(n int) [][]f32 {
mut res := [][]f32{len: n, init: []f32{len: n, init: 0.0}}
for i in 0 .. n {
res[i][i] = 1.0
}
return res
}
fn main() {
a := identity(3)
println(a)
}

View File

@ -0,0 +1 @@
<none>

View File

@ -0,0 +1 @@
println(none.str())

View File

@ -135,6 +135,24 @@ fn (mut g Gen) cmp_reg(reg Register, reg2 Register) {
g.println('cmp $reg, $reg2')
}
// cmp $reg, 0
fn (mut g Gen) cmp_zero(reg Register) {
g.write8(0x48)
g.write8(0x39)
match reg {
.rax {
g.write8(0x04)
g.write8(0x25)
}
else {
panic('unhandled cmp $reg, 0')
}
}
g.write32(0)
}
fn (mut g Gen) cmp_var_reg(var_name string, reg Register) {
g.write8(0x48) // 83 for 1 byte?
g.write8(0x39)
@ -225,6 +243,26 @@ fn (mut g Gen) jmp(addr i64) {
g.write8(offset)
}
*/
fn (mut g Gen) mov32(reg Register, val int) {
match reg {
.rax {
g.write8(0xb8)
}
.rdi {
g.write8(0xbf)
}
.rcx {
g.write8(0xb9)
}
else {
panic('unhandled mov32 $reg')
}
}
g.write32(val)
g.println('mov32 $reg, $val')
}
fn (mut g Gen) mov64(reg Register, val i64) {
match reg {
.eax {
@ -272,6 +310,21 @@ fn (mut g Gen) mov64(reg Register, val i64) {
g.println('mov64 $reg, $val')
}
fn (mut g Gen) movabs(reg Register, val i64) {
match reg {
.rsi {
g.write8(0x48)
g.write8(0xbe)
}
else {
panic('unhandled movabs $reg, $val')
}
}
g.write64(val)
g.println('movabs $reg, $val')
}
fn (mut g Gen) mov_reg_to_var(var_offset int, reg Register) {
// 89 7d fc mov DWORD PTR [rbp-0x4],edi
match reg {
@ -293,6 +346,27 @@ fn (mut g Gen) mov_reg_to_var(var_offset int, reg Register) {
g.println('mov DWORD PTR[rbp-$var_offset.hex2()],$reg')
}
fn (mut g Gen) lea_var_to_reg(reg Register, var_offset int) {
match reg {
.rax, .rbx, .rsi {
g.write8(0x48)
}
else {}
}
g.write8(0x8d)
match reg {
.eax, .rax { g.write8(0x45) }
.edi, .rdi { g.write8(0x7d) }
.rsi { g.write8(0x75) }
.rdx { g.write8(0x55) }
.rbx { g.write8(0x5d) }
.rcx { g.write8(0x4d) }
else { g.n_error('lea_var_to_reg $reg') }
}
g.write8(0xff - var_offset + 1)
g.println('lea $reg, [rbp-$var_offset.hex2()]')
}
fn (mut g Gen) mov_var_to_reg(reg Register, var_offset int) {
// 8b 7d f8 mov edi,DWORD PTR [rbp-0x8]
match reg {
@ -473,6 +547,21 @@ pub fn (mut g Gen) allocate_string(s string, opsize int, typ RelocType) int {
return str_pos
}
pub fn (mut g Gen) var_zero(vo int, size int) {
g.mov32(.rcx, size)
g.lea_var_to_reg(.rdi, vo)
g.write8(0xb0)
g.write8(0x00)
g.println('mov al, 0')
g.rep_stosb()
}
pub fn (mut g Gen) rep_stosb() {
g.write8(0xf3)
g.write8(0xaa)
g.println('rep stosb')
}
pub fn (mut g Gen) cld_repne_scasb() {
g.write8(0xfc)
g.println('cld')
@ -500,6 +589,20 @@ pub fn (mut g Gen) xor(r Register, v int) {
}
}
pub fn (mut g Gen) test_reg(r Register) {
match r {
.rdi {
g.write8(0x48)
g.write8(0x85)
g.write8(0xff)
g.println('test rdi, rdi')
}
else {
panic('unhandled test $r, $r')
}
}
}
// return length in .rax of string pointed by given register
pub fn (mut g Gen) inline_strlen(r Register) {
g.mov_reg(.rdi, r)
@ -676,8 +779,11 @@ fn (mut g Gen) learel(reg Register, val int) {
.rsi {
g.write8(0x35)
}
.rcx {
g.write8(0x0d)
}
else {
g.n_error('learel must use rsi or rax')
g.n_error('learel must use rsi, rcx or rax')
}
}
g.write32(val)
@ -823,6 +929,20 @@ fn (mut g Gen) mul_reg(a Register, b Register) {
g.println('mul $a')
}
fn (mut g Gen) imul_reg(r Register) {
match r {
.rsi {
g.write8(0x48)
g.write8(0xf7)
g.write8(0xee)
g.println('imul $r')
}
else {
panic('unhandled imul $r')
}
}
}
fn (mut g Gen) div_reg(a Register, b Register) {
if a != .rax {
panic('div always operates on rax')
@ -846,11 +966,24 @@ fn (mut g Gen) div_reg(a Register, b Register) {
g.println('div $a')
}
fn (mut g Gen) mod_reg(a Register, b Register) {
g.div_reg(a, b)
g.mov_reg(.rdx, .rax)
}
fn (mut g Gen) sub_reg(a Register, b Register) {
if a == .rax && b == .rbx {
g.write8(0x48)
g.write8(0x29)
g.write8(0xd8)
} else if a == .rdx && b == .rax {
g.write8(0x48)
g.write8(0x29)
g.write8(0xc2)
} else if a == .rdi && b == .rax {
g.write8(0x48)
g.write8(0x29)
g.write8(0xc7)
} else {
panic('unhandled add $a, $b')
}
@ -866,6 +999,10 @@ fn (mut g Gen) add_reg(a Register, b Register) {
g.write8(0x48)
g.write8(0x01)
g.write8(0xf8)
} else if a == .rax && b == .rax {
g.write8(0x48)
g.write8(0x01)
g.write8(0xc0)
} else {
panic('unhandled add $a, $b')
}
@ -906,12 +1043,39 @@ fn (mut g Gen) mov_reg(a Register, b Register) {
g.write8(0x48)
g.write8(0x89)
g.write8(0xc6)
} else if a == .rdi && b == .rdx {
g.write8(0x48)
g.write8(0x89)
g.write8(0xd7)
} else if a == .rdi && b == .rax {
g.write8(0x48)
g.write8(0x89)
g.write8(0xc7)
} else {
g.n_error('unhandled mov_reg combination for $a $b')
}
g.println('mov $a, $b')
}
fn (mut g Gen) sar8(r Register, val u8) {
g.write8(0x48)
g.write8(0xc1)
match r {
.rax {
g.write8(0xf8)
}
.rdx {
g.write8(0xfa)
}
else {
panic('unhandled sar $r, $val')
}
}
g.write8(val)
g.println('sar $r, $val')
}
// generates `mov rbp, rsp`
fn (mut g Gen) mov_rbp_rsp() {
g.write8(0x48)
@ -1668,9 +1832,32 @@ pub fn (mut g Gen) allocate_var(name string, size int, initial_val int) int {
g.write8(0xff - n + 1)
g.stack_var_pos += size
g.var_offset[name] = g.stack_var_pos
// Generate the value assigned to the variable
g.write32(initial_val)
match size {
1 {
g.write8(initial_val)
}
4 {
g.write32(initial_val)
}
8 {
g.write32(initial_val) // fixme: 64-bit segfaulting
}
else {
g.n_error('allocate_var: bad size $size')
}
}
// println('allocate_var(size=$size, initial_val=$initial_val)')
g.println('mov [rbp-$n.hex2()], $initial_val ; Allocate var `$name`')
return g.stack_var_pos
}
fn (mut g Gen) convert_int_to_string(r Register, buffer int) {
if r != .rax {
g.mov_reg(.rax, r)
}
// TODO
}

View File

@ -299,6 +299,12 @@ fn (mut g Gen) gen_typeof_expr(it ast.TypeOf, newline bool) {
g.learel(.rax, g.allocate_string('$r$nl', 3, .rel32))
}
fn (mut g Gen) gen_var_to_string(reg Register, vo int) {
buffer := g.allocate_array('itoa-buffer', 1, 32) // 32 characters should be enough
g.convert_int_to_string(reg, buffer)
g.lea_var_to_reg(reg, buffer)
}
pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, name string) {
newline := name in ['println', 'eprintln']
fd := if name in ['eprint', 'eprintln'] { 2 } else { 1 }
@ -316,16 +322,15 @@ pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, name string) {
}
ast.Ident {
vo := g.try_var_offset(expr.name)
if vo != -1 {
g.n_error('Printing idents is not yet supported in the native backend')
// g.mov_var_to_reg(.rsi, vo)
// g.mov_reg(.rax, .rsi)
// g.learel(.rax, vo * 8)
// g.relpc(.rax, .rsi)
// g.learel(.rax, g.allocate_string('$vo\n', 3, .rel32))
// g.expr(expr)
g.gen_var_to_string(.rax, vo)
if newline {
g.gen_print('\n', fd)
}
} else {
g.gen_print_reg(.rax, 3, fd)
}
g.gen_print_reg(.rax, 3, fd)
}
ast.IntegerLiteral {
g.learel(.rax, g.allocate_string('$expr.val\n', 3, .rel32))
@ -775,7 +780,9 @@ fn (mut g Gen) expr(node ast.Expr) {
ast.PostfixExpr {
g.postfix_expr(node)
}
ast.StringLiteral {}
ast.StringLiteral {
g.allocate_string(node.val, 3, .rel32)
}
ast.StructInit {}
ast.GoExpr {
g.v_error('native backend doesnt support threads yet', node.pos)

View File

@ -29,9 +29,15 @@ fn test_stderr() {
eprintln('2(World)')
}
fn test_idents() {
x := 5
// println(x) uncomment again when int-to-string conversion is working
}
fn main() {
test_stdout()
test_stderr()
test_numbers()
test_oof()
test_idents()
}

View File

@ -85,6 +85,18 @@ pub fn (mut p Preferences) fill_with_defaults() {
}
rpath_name := os.file_name(rpath)
p.building_v = !p.is_repl && (rpath_name == 'v' || rpath_name == 'vfmt.v')
if p.gc_mode == .unknown {
if p.backend != .c || p.building_v || p.is_bare || p.ccompiler == 'msvc' {
p.gc_mode = .no_gc
p.build_options << ['-gc', 'none']
} else {
// enable the GC by default
p.gc_mode = .boehm_full_opt
p.parse_define('gcboehm')
p.parse_define('gcboehm_full')
p.parse_define('gcboehm_opt')
}
}
if p.os == ._auto {
// No OS specifed? Use current system
p.os = get_host_os()

View File

@ -24,6 +24,7 @@ pub enum AssertFailureMode {
}
pub enum GarbageCollectionMode {
unknown
no_gc
boehm_full // full garbage collection mode
boehm_incr // incremental garbage colletion mode
@ -204,7 +205,7 @@ pub mut:
cleanup_files []string // list of temporary *.tmp.c and *.tmp.c.rsp files. Cleaned up on successfull builds.
build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache
cache_manager vcache.CacheManager
gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm, .boehm_leak, ...
gc_mode GarbageCollectionMode = .unknown // .no_gc, .boehm, .boehm_leak, ...
assert_failure_mode AssertFailureMode // whether to call abort() or print_backtrace() after an assertion failure
message_limit int = 100 // the maximum amount of warnings/errors/notices that will be accumulated
nofloat bool // for low level code, like kernels: replaces f32 with u32 and f64 with u64
@ -327,9 +328,15 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
'-gc' {
gc_mode := cmdline.option(current_args, '-gc', '')
match gc_mode {
'', 'none' {
'none' {
res.gc_mode = .no_gc
}
'', 'boehm' {
res.gc_mode = .boehm_full_opt // default mode
res.parse_define('gcboehm')
res.parse_define('gcboehm_full')
res.parse_define('gcboehm_opt')
}
'boehm_full' {
res.gc_mode = .boehm_full
res.parse_define('gcboehm')
@ -352,12 +359,6 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
res.parse_define('gcboehm_incr')
res.parse_define('gcboehm_opt')
}
'boehm' {
res.gc_mode = .boehm_full_opt // default mode
res.parse_define('gcboehm')
res.parse_define('gcboehm_full')
res.parse_define('gcboehm_opt')
}
'boehm_leak' {
res.gc_mode = .boehm_leak
res.parse_define('gcboehm')

View File

@ -0,0 +1,55 @@
module main
interface TypeFactory<T> {
get_type(type_name string) T
}
enum NodeType {
unknown
expression
statement
literal
}
struct EnumTypeFactory {}
fn (f &EnumTypeFactory) get_type(type_name string) NodeType {
return match type_name {
'expression' { NodeType.expression }
'statement' { NodeType.statement }
'literal' { NodeType.literal }
else { NodeType.unknown }
}
}
struct RawNode {
type_name string
}
struct Node<T> {
factory TypeFactory<T>
type_name NodeType
raw_node RawNode
}
fn new_node<T>(factory TypeFactory<T>, raw_node RawNode) ?Node<T> {
return Node<T>{
factory: factory
type_name: factory.get_type(raw_node.type_name)
raw_node: raw_node
}
}
fn program<T>(factory TypeFactory<T>) ? {
root1 := new_node<T>(factory, RawNode{'literal'})?
println(root1)
assert root1.type_name == .literal
root2 := new_node<T>(root1.factory, RawNode{'expression'})?
println(root2)
assert root2.type_name == .expression
}
fn test_generic_interface_with_non_generic_method() ? {
program<NodeType>(&EnumTypeFactory{})?
}

View File

@ -0,0 +1,17 @@
module main
struct Container<T> {
value T
}
fn (c Container<T>) id() int {
return 1
}
type Text = Container<string>
fn test_generic_method_on_receiver_aliases_type() {
t := Text{'test'}
println(t.id())
assert t.id() == 1
}

View File

@ -0,0 +1,24 @@
struct Foo {
mut:
foo map[int]Bar
}
struct Bar {
mut:
bar map[int]string
}
fn test_nested_map_index() ? {
f := Foo{
foo: {
11: Bar{
bar: {
22: 'hello'
}
}
}
}
ret := f.foo[11]?.bar[22]?
println(ret)
assert ret == 'hello'
}

View File

@ -86,7 +86,7 @@ fn test_all() {
base_filename := os.file_name(test).replace('.v', '')
exe_filename := '$wrkdir/$base_filename'
full_path_to_source_file := os.join_path(vroot, test)
compile_cmd := '${os.quoted_path(vexe)} -o ${os.quoted_path(exe_filename)} -cg -cflags "-w" -experimental -autofree ${os.quoted_path(full_path_to_source_file)}'
compile_cmd := '${os.quoted_path(vexe)} -o ${os.quoted_path(exe_filename)} -cg -cflags "-w" -experimental -gc none -autofree ${os.quoted_path(full_path_to_source_file)}'
vprintln('compile cmd: ${bold(compile_cmd)}')
res := os.execute(compile_cmd)
if res.exit_code != 0 {

View File

@ -39,6 +39,7 @@ pub fn new_cache_manager(opts []string) CacheManager {
if vcache_basepath == '' {
vcache_basepath = os.join_path(os.vmodules_dir(), 'cache')
}
nlog(@FN, 'vcache_basepath: $vcache_basepath\n opts: $opts\n os.args: ${os.args.join(' ')}')
dlog(@FN, 'vcache_basepath: $vcache_basepath | opts:\n $opts')
if !os.is_dir(vcache_basepath) {
os.mkdir_all(vcache_basepath) or { panic(err) }
@ -54,7 +55,14 @@ pub fn new_cache_manager(opts []string) CacheManager {
os.write_file(readme_file, readme_content) or { panic(err) }
dlog(@FN, 'created readme_file:\n $readme_file')
}
original_vopts := opts.join('|')
mut deduped_opts := map[string]bool{}
for o in opts {
deduped_opts[o] = true
}
deduped_opts_keys := deduped_opts.keys().filter(it != '' && !it.starts_with("['gcboehm', "))
// TODO: do not filter the gcboehm options here, instead just start `v build-module vlib/builtin` without the -d gcboehm etc.
// Note: the current approach of filtering the gcboehm keys may interfere with (potential) other gc modes.
original_vopts := deduped_opts_keys.join('|')
return CacheManager{
basepath: vcache_basepath
vopts: original_vopts
@ -125,6 +133,15 @@ pub fn (mut cm CacheManager) load(postfix string, key string) ?string {
[if trace_usecache ?]
pub fn dlog(fname string, s string) {
xlog(fname, s)
}
[if trace_usecache_n ?]
fn nlog(fname string, s string) {
xlog(fname, s)
}
fn xlog(fname string, s string) {
pid := unsafe { mypid() }
if fname[0] != `|` {
eprintln('> VCache | pid: $pid | CacheManager.$fname $s')

View File

@ -252,7 +252,7 @@ pub fn (mut ctx Context) file(f_path string) Result {
return Result{}
}
content_type := vweb.mime_types[ext]
if content_type.len == O {
if content_type.len == 0 {
eprintln('no MIME type found for extension $ext')
ctx.server_error(500)
} else {