cgen: parallelize (#10844)

pull/12012/head
crthpl 2021-09-28 00:28:04 -07:00 committed by GitHub
parent a17b943e87
commit 85b58b03a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1003 additions and 580 deletions

View File

@ -191,7 +191,7 @@ jobs:
- name: v.c can be compiled and run with -os cross
run: |
./v -os cross -o /tmp/v.c cmd/v
gcc -g -std=gnu11 -w -o v_from_vc /tmp/v.c -lm -lpthread
gcc -g -std=gnu11 -I ./thirdparty/stdatomic/nix -w -o v_from_vc /tmp/v.c -lm -lpthread
ls -lart v_from_vc
./v_from_vc version
@ -235,7 +235,7 @@ jobs:
- name: g++ version
run: g++-9 --version
- name: V self compilation with g++
run: ./v -cc g++-9 -o v2 cmd/v && ./v2 -cc g++-9 -o v3 cmd/v
run: ./v -cc g++-9 -no-std -cflags -std=c++11 -o v2 cmd/v && ./v2 -cc g++-9 -no-std -cflags -std=c++11 -o v3 cmd/v
## - name: Running tests with g++
## run: ./v -cc g++-9 -silent test-self
@ -323,7 +323,7 @@ jobs:
- name: v.c can be compiled and run with -os cross
run: |
./v -os cross -o /tmp/v.c cmd/v
cc -g -std=gnu11 -w -o v_from_vc /tmp/v.c -lm -lpthread
cc -g -std=gnu11 -w -o v_from_vc /tmp/v.c -I ./thirdparty/stdatomic/nix -lm -lpthread
ls -lart v_from_vc
./v_from_vc version
@ -627,7 +627,7 @@ jobs:
gcc --version
.\make.bat -gcc
- name: Test new v.c
run: .\v.exe -o v.c cmd/v && gcc -Werror -municode -w v.c
run: .\v.exe -o v.c cmd/v && gcc -Werror -I ./thirdparty/stdatomic/win -municode -w v.c
- name: Install dependencies
run: |
.\v.exe setup-freetype
@ -715,7 +715,7 @@ jobs:
run: |
.\make.bat -tcc --verbose
- name: Test new v.c
run: .\v.exe -o v.c cmd/v && .\thirdparty\tcc\tcc.exe -Werror -w -ladvapi32 -bt10 v.c
run: .\v.exe -o v.c cmd/v && .\thirdparty\tcc\tcc.exe -I ./thirdparty/stdatomic/win -Werror -w -ladvapi32 -bt10 v.c
- name: Install dependencies
run: |
.\v.exe setup-freetype
@ -764,7 +764,7 @@ jobs:
.\v.exe wipe-cache
.\make.bat -tcc32 --verbose
- name: Test new v.c
run: .\v.exe -o v.c cmd/v && .\thirdparty\tcc\tcc.exe -Werror -w -ladvapi32 -bt10 v.c
run: .\v.exe -o v.c cmd/v && .\thirdparty\tcc\tcc.exe -I ./thirdparty/stdatomic/win -Werror -w -ladvapi32 -bt10 v.c
- name: v doctor
run: ./v doctor

View File

@ -64,14 +64,14 @@ jobs:
- name: v.c can be compiled and run with -os cross
run: |
./v -os cross -o /tmp/v.c cmd/v
gcc -g -std=gnu11 -w -o v_from_vc /tmp/v.c -lm -lpthread
gcc -g -std=gnu11 -I ./thirdparty/stdatomic/nix -w -o v_from_vc /tmp/v.c -lm -lpthread
ls -lart v_from_vc
./v_from_vc version
- name: v_win.c can be compiled and run with -os windows
run: |
./v -os windows -o /tmp/v_win.c cmd/v
x86_64-w64-mingw32-gcc /tmp/v_win.c -std=c99 -w -municode -o v_from_vc.exe
x86_64-w64-mingw32-gcc -I ./thirdparty/stdatomic/win /tmp/v_win.c -std=c99 -w -municode -o v_from_vc.exe
ls -lart v_from_vc.exe
wine v_from_vc.exe version

2
.gitignore vendored
View File

@ -99,3 +99,5 @@ shell.nix
default.nix
flake.nix
.envrc
thirdparty/stdatomic/nix/cpp/*.h

View File

@ -4,7 +4,7 @@ LABEL maintainer="Vitaly Takmazov <vitalyster@gmail.com>"
COPY . .
RUN make
RUN ./v -os windows -o v.c cmd/v
RUN x86_64-w64-mingw32-gcc v.c -std=c99 -w -municode -o v.exe
RUN x86_64-w64-mingw32-gcc v.c -std=c99 -I ./thirdparty/stdatomic/win -w -municode -o v.exe
RUN file v.exe
CMD [ "bash" ]

View File

@ -3,6 +3,6 @@ CC ?= cc
all:
rm -rf vc/
git clone --depth 1 --quiet https://github.com/vlang/vc
$(CC) -std=gnu11 -w -o v vc/v.c -lm -lexecinfo
$(CC) -std=gnu11 -w -o v vc/v.c -lm -lexecinfo -I ./thirdparty/stdatomic/nix
rm -rf vc/
@echo "V has been successfully built"

View File

@ -21,11 +21,13 @@ fn main() {
println('fast.html generator needs to be located in `v/cmd/tools/fast`')
}
println('fast.html generator\n')
println('Fetching updates...')
ret := os.system('$vdir/v up')
if ret != 0 {
println('failed to update V')
return
if !os.args.contains('-noupdate') {
println('Fetching updates...')
ret := os.system('$vdir/v up')
if ret != 0 {
println('failed to update V')
return
}
}
// Fetch the last commit's hash
commit := exec('git rev-parse HEAD')[..8]
@ -55,7 +57,6 @@ fn main() {
} else {
exec('./v -o vprod -prod -prealloc cmd/v')
}
// println('cur vdir="$vdir"')
// cache vlib modules
exec('$vdir/v wipe-cache')
exec('$vdir/v -o v2 -prod cmd/v')

View File

@ -111,10 +111,10 @@ pub fn (mut vgit_context VGitContext) compile_oldv_if_needed() {
mut command_for_building_v_from_c_source := ''
mut command_for_selfbuilding := ''
if 'windows' == os.user_os() {
command_for_building_v_from_c_source = '$vgit_context.cc -std=c99 -municode -w -o cv.exe "$vgit_context.path_vc/v_win.c" '
command_for_building_v_from_c_source = '$vgit_context.cc -std=c99 -I ./thirdparty/stdatomic/win -municode -w -o cv.exe "$vgit_context.path_vc/v_win.c" '
command_for_selfbuilding = './cv.exe -o $vgit_context.vexename {SOURCE}'
} else {
command_for_building_v_from_c_source = '$vgit_context.cc -std=gnu11 -w -o cv "$vgit_context.path_vc/v.c" -lm -lpthread'
command_for_building_v_from_c_source = '$vgit_context.cc -std=gnu11 -I ./thirdparty/stdatomic/nix -w -o cv "$vgit_context.path_vc/v.c" -lm -lpthread'
command_for_selfbuilding = './cv -o $vgit_context.vexename {SOURCE}'
}
scripting.chdir(vgit_context.workdir)

View File

@ -8,6 +8,7 @@ import v.parser
import v.ast
import v.pref
import v.errors
import strings
struct Context {
mut:
@ -208,7 +209,7 @@ fn (t Tree) type_node(typ ast.Type) &Node {
return create_null()
} else {
type_name := t.table.get_type_name(typ)
return create_string(type_name)
return create_string(strings.repeat(`&`, typ.nr_muls()) + type_name)
}
}

View File

@ -148,7 +148,7 @@ fn get_all_commands() []Command {
}
$if macos || linux {
res << Command{
line: '$vexe -o v.c cmd/v && cc -Werror v.c && rm -rf a.out'
line: '$vexe -o v.c cmd/v && cc -I "$vroot/thirdparty/stdatomic/nix" -lpthread v.c && rm -rf a.out'
label: 'v.c should be buildable with no warnings...'
okmsg: 'v.c can be compiled without warnings. This is good :)'
rmfile: 'v.c'

View File

@ -90,6 +90,9 @@ NB: the build flags are shared with the run command too:
The checker will abort prematurely once this limit has been reached.
Setting this to 0 or a negative value, will disable the limit.
-no-parallel
Do not run the compiler in parallel (currently only the cgen stage has parallelization).
-profile-no-inline
Skip [inline] functions when profiling.

View File

@ -175,10 +175,10 @@ REM By default, use tcc, since we have it prebuilt:
:tcc32_strap
echo ^> Attempting to build v_win.c with TCC
if !flag_verbose! EQU 1 (
echo [Debug] "!tcc_exe!" -ladvapi32 -bt10 -w -o v.exe vc\v_win.c>>"!log_file!"
echo "!tcc_exe!" -ladvapi32 -bt10 -w -o v.exe vc\v_win.c
echo [Debug] "!tcc_exe!" -ladvapi32 -I .\thirdparty\stdatomic\win -bt10 -w -o v.exe vc\v_win.c>>"!log_file!"
echo "!tcc_exe!" -ladvapi32 -I .\thirdparty\stdatomic\win -bt10 -w -o v.exe vc\v_win.c
)
"!tcc_exe!" -ladvapi32 -bt10 -w -o v.exe vc\v_win.c>>"!log_file!"
"!tcc_exe!" -ladvapi32 -I .\thirdparty\stdatomic\win -bt10 -w -o v.exe vc\v_win.c>>"!log_file!"
if %ERRORLEVEL% NEQ 0 goto :compile_error
echo ^> Compiling with .\v.exe self
@ -202,8 +202,8 @@ if %ERRORLEVEL% NEQ 0 (
echo ^> Attempting to build v_win.c with Clang
if !flag_verbose! EQU 1 (
echo [Debug] clang -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_File!"
echo clang -std=c99 -municode -w -o v.exe .\vc\v_win.c
echo [Debug] clang -std=c99 -municode -I .\thirdparty\stdatomic\win -w -o v.exe .\vc\v_win.c>>"!log_File!"
echo clang -std=c99 -municode -I .\thirdparty\stdatomic\win -w -o v.exe .\vc\v_win.c
)
clang -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_file!"
if %ERRORLEVEL% NEQ 0 (
@ -231,10 +231,10 @@ if %ERRORLEVEL% NEQ 0 (
echo ^> Attempting to build v_win.c with GCC
if !flag_verbose! EQU 1 (
echo [Debug] gcc -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_File!"
echo gcc -std=c99 -municode -w -o v.exe .\vc\v_win.c
echo [Debug] gcc -std=c99 -municode -I .\thirdparty\stdatomic\win -w -o v.exe .\vc\v_win.c>>"!log_File!"
echo gcc -std=c99 -municode -I .\thirdparty\stdatomic\win -w -o v.exe .\vc\v_win.c
)
gcc -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_File!"
gcc -std=c99 -municode -I .\thirdparty\stdatomic\win -w -o v.exe .\vc\v_win.c>>"!log_File!"
if %ERRORLEVEL% NEQ 0 (
REM In most cases, compile errors happen because the version of GCC installed is too old
gcc --version>>"!log_File!"
@ -279,8 +279,8 @@ set ObjFile=.v.c.obj
echo ^> Attempting to build v_win.c with MSVC
if !flag_verbose! EQU 1 (
echo [Debug] cl.exe /volatile:ms /Fo%ObjFile% /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no>>"!log_file!"
echo cl.exe /volatile:ms /Fo%ObjFile% /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no
echo [Debug] cl.exe /volatile:ms /Fo%ObjFile% /I .\thirdparty\stdatomic\win /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no>>"!log_file!"
echo cl.exe /volatile:ms /Fo%ObjFile% /I .\thirdparty\stdatomic\win /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no
)
cl.exe /volatile:ms /Fo%ObjFile% /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no>>"!log_file!"
if %ERRORLEVEL% NEQ 0 (

View File

@ -275,160 +275,160 @@ static inline unsigned char atomic_fetch_xor_byte(unsigned char* x, unsigned cha
// Since V might be confused with "generic" C functions either we provide special versions
// for gcc/clang, too
static inline unsigned long long atomic_load_u64(unsigned long long* x) {
return atomic_load_explicit((_Atomic unsigned long long*)x, memory_order_seq_cst);
return atomic_load_explicit((_Atomic (unsigned long long)*)x, memory_order_seq_cst);
}
static inline void atomic_store_u64(unsigned long long* x, unsigned long long y) {
atomic_store_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst);
atomic_store_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
}
static inline int atomic_compare_exchange_weak_u64(unsigned long long* x, unsigned long long* expected, unsigned long long y) {
return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned long long*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
return (int)atomic_compare_exchange_weak_explicit((_Atomic(unsigned long long)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
}
static inline int atomic_compare_exchange_strong_u64(unsigned long long* x, unsigned long long* expected, unsigned long long y) {
return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned long long*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
return (int)atomic_compare_exchange_strong_explicit((_Atomic(unsigned long long)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
}
static inline unsigned long long atomic_exchange_u64(unsigned long long* x, unsigned long long y) {
return atomic_exchange_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst);
return atomic_exchange_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
}
static inline unsigned long long atomic_fetch_add_u64(unsigned long long* x, unsigned long long y) {
return atomic_fetch_add_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst);
return atomic_fetch_add_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
}
static inline unsigned long long atomic_fetch_sub_u64(unsigned long long* x, unsigned long long y) {
return atomic_fetch_sub_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst);
return atomic_fetch_sub_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
}
static inline unsigned long long atomic_fetch_and_u64(unsigned long long* x, unsigned long long y) {
return atomic_fetch_and_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst);
return atomic_fetch_and_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
}
static inline unsigned long long atomic_fetch_or_u64(unsigned long long* x, unsigned long long y) {
return atomic_fetch_or_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst);
return atomic_fetch_or_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
}
static inline unsigned long long atomic_fetch_xor_u64(unsigned long long* x, unsigned long long y) {
return atomic_fetch_xor_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst);
return atomic_fetch_xor_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst);
}
static inline void* atomic_load_ptr(void** x) {
return (void*)atomic_load_explicit((_Atomic uintptr_t*)x, memory_order_seq_cst);
return (void*)atomic_load_explicit((_Atomic(uintptr_t)*)x, memory_order_seq_cst);
}
static inline void atomic_store_ptr(void** x, void* y) {
atomic_store_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst);
atomic_store_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst);
}
static inline int atomic_compare_exchange_weak_ptr(void** x, void** expected, void* y) {
return (int)atomic_compare_exchange_weak_explicit((_Atomic uintptr_t*)x, (unsigned long *)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst);
return (int)atomic_compare_exchange_weak_explicit((_Atomic(uintptr_t)*)x, (unsigned long *)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst);
}
static inline int atomic_compare_exchange_strong_ptr(void** x, void** expected, void* y) {
return (int)atomic_compare_exchange_strong_explicit((_Atomic uintptr_t*)x, (unsigned long *)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst);
return (int)atomic_compare_exchange_strong_explicit((_Atomic(uintptr_t)*)x, (unsigned long *)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst);
}
static inline void* atomic_exchange_ptr(void** x, void* y) {
return (void*)atomic_exchange_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst);
return (void*)atomic_exchange_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst);
}
static inline void* atomic_fetch_add_ptr(void** x, void* y) {
return (void*)atomic_fetch_add_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst);
return (void*)atomic_fetch_add_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst);
}
static inline void* atomic_fetch_sub_ptr(void** x, void* y) {
return (void*)atomic_fetch_sub_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst);
return (void*)atomic_fetch_sub_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst);
}
static inline void* atomic_fetch_and_ptr(void** x, void* y) {
return (void*)atomic_fetch_and_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst);
return (void*)atomic_fetch_and_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst);
}
static inline void* atomic_fetch_or_ptr(void** x, void* y) {
return (void*)atomic_fetch_or_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst);
return (void*)atomic_fetch_or_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst);
}
static inline void* atomic_fetch_xor_ptr(void** x, void* y) {
return (void*)atomic_fetch_xor_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst);
return (void*)atomic_fetch_xor_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst);
}
static inline unsigned atomic_load_u32(unsigned* x) {
return atomic_load_explicit((_Atomic unsigned*)x, memory_order_seq_cst);
return atomic_load_explicit((_Atomic(unsigned)*)x, memory_order_seq_cst);
}
static inline void atomic_store_u32(unsigned* x, unsigned y) {
atomic_store_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst);
atomic_store_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst);
}
static inline int atomic_compare_exchange_weak_u32(unsigned* x, unsigned* expected, unsigned y) {
return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
return (int)atomic_compare_exchange_weak_explicit((_Atomic(unsigned)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
}
static inline int atomic_compare_exchange_strong_u32(unsigned* x, unsigned* expected, unsigned y) {
return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
return (int)atomic_compare_exchange_strong_explicit((_Atomic(unsigned)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
}
static inline unsigned atomic_exchange_u32(unsigned* x, unsigned y) {
return atomic_exchange_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst);
return atomic_exchange_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst);
}
static inline unsigned atomic_fetch_add_u32(unsigned* x, unsigned y) {
return atomic_fetch_add_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst);
return atomic_fetch_add_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst);
}
static inline unsigned atomic_fetch_sub_u32(unsigned* x, unsigned y) {
return atomic_fetch_sub_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst);
return atomic_fetch_sub_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst);
}
static inline unsigned atomic_fetch_and_u32(unsigned* x, unsigned y) {
return atomic_fetch_and_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst);
return atomic_fetch_and_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst);
}
static inline unsigned atomic_fetch_or_u32(unsigned* x, unsigned y) {
return atomic_fetch_or_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst);
return atomic_fetch_or_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst);
}
static inline unsigned atomic_fetch_xor_u32(unsigned* x, unsigned y) {
return atomic_fetch_xor_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst);
return atomic_fetch_xor_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst);
}
static inline unsigned short atomic_load_u16(unsigned short* x) {
return atomic_load_explicit((_Atomic unsigned short*)x, memory_order_seq_cst);
return atomic_load_explicit((_Atomic(unsigned short)*)x, memory_order_seq_cst);
}
static inline void atomic_store_u16(void* x, unsigned short y) {
atomic_store_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst);
atomic_store_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst);
}
static inline int atomic_compare_exchange_weak_u16(void* x, unsigned short* expected, unsigned short y) {
return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned short*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
return (int)atomic_compare_exchange_weak_explicit((_Atomic(unsigned short)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
}
static inline int atomic_compare_exchange_strong_u16(unsigned short* x, unsigned short* expected, unsigned short y) {
return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned short*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
return (int)atomic_compare_exchange_strong_explicit((_Atomic(unsigned short)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
}
static inline unsigned short atomic_exchange_u16(unsigned short* x, unsigned short y) {
return atomic_exchange_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst);
return atomic_exchange_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst);
}
static inline unsigned short atomic_fetch_add_u16(unsigned short* x, unsigned short y) {
return atomic_fetch_add_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst);
return atomic_fetch_add_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst);
}
static inline unsigned short atomic_fetch_sub_u16(unsigned short* x, unsigned short y) {
return atomic_fetch_sub_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst);
return atomic_fetch_sub_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst);
}
static inline unsigned short atomic_fetch_and_u16(unsigned short* x, unsigned short y) {
return atomic_fetch_and_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst);
return atomic_fetch_and_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst);
}
static inline unsigned short atomic_fetch_or_u16(unsigned short* x, unsigned short y) {
return atomic_fetch_or_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst);
return atomic_fetch_or_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst);
}
static inline unsigned short atomic_fetch_xor_u16(unsigned short* x, unsigned short y) {
return atomic_fetch_xor_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst);
return atomic_fetch_xor_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst);
}
static inline unsigned char atomic_load_byte(unsigned char* x) {
return atomic_load_explicit((_Atomic unsigned char*)x, memory_order_seq_cst);
return atomic_load_explicit((_Atomic(unsigned char)*)x, memory_order_seq_cst);
}
static inline void atomic_store_byte(unsigned char* x, unsigned char y) {
atomic_store_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst);
atomic_store_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst);
}
static inline int atomic_compare_exchange_weak_byte(unsigned char* x, unsigned char* expected, unsigned char y) {
return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned char*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
return (int)atomic_compare_exchange_weak_explicit((_Atomic(unsigned char)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
}
static inline int atomic_compare_exchange_strong_byte(unsigned char* x, unsigned char* expected, unsigned char y) {
return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned char*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
return (int)atomic_compare_exchange_strong_explicit((_Atomic(unsigned char)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst);
}
static inline unsigned char atomic_exchange_byte(unsigned char* x, unsigned char y) {
return atomic_exchange_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst);
return atomic_exchange_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst);
}
static inline unsigned char atomic_fetch_add_byte(unsigned char* x, unsigned char y) {
return atomic_fetch_add_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst);
return atomic_fetch_add_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst);
}
static inline unsigned char atomic_fetch_sub_byte(unsigned char* x, unsigned char y) {
return atomic_fetch_sub_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst);
return atomic_fetch_sub_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst);
}
static inline unsigned char atomic_fetch_and_byte(unsigned char* x, unsigned char y) {
return atomic_fetch_and_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst);
return atomic_fetch_and_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst);
}
static inline unsigned char atomic_fetch_or_byte(unsigned char* x, unsigned char y) {
return atomic_fetch_or_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst);
return atomic_fetch_or_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst);
}
static inline unsigned char atomic_fetch_xor_byte(unsigned char* x, unsigned char y) {
return atomic_fetch_xor_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst);
return atomic_fetch_xor_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst);
}
#endif

View File

@ -30,7 +30,7 @@
#ifndef _STDATOMIC_H_
#define _STDATOMIC_H_
#include <sys/cdefs.h>
#if defined(__cplusplus) && defined(_USING_LIBCXX)
#if defined(__cplusplus)
#ifdef __clang__
#if __has_feature(cxx_atomic)
#define _STDATOMIC_HAVE_ATOMIC
@ -42,8 +42,9 @@
#endif
#endif
#ifdef _STDATOMIC_HAVE_ATOMIC
/* We have a usable C++ <atomic>; use it instead. */
#include <atomic>
#include "cpp/atomic.h"
#undef _Atomic
/* Also defined by <atomic> for gcc. But not used in macros. */
/* Also a clang intrinsic. */

View File

@ -0,0 +1,99 @@
import os
fn main() {
if os.args.len <= 1 {
eprintln('please specify a C++ compiler')
exit(1)
}
cc := os.args[1]
if os.execute('$cc -v').exit_code != 0 {
eprintln('please specify a valid C++ compiler')
exit(1)
}
cc_type, cc_version, cc_os := get_cc_info(cc)
triple := '$cc_type-$cc_version-$cc_os'
println('compiler: $triple')
search_paths := get_search_paths(cc)
atomic_path := find_file(search_paths, 'atomic') or {
eprintln(err)
exit(2)
}
bitsatomicbase_path := find_file(search_paths, 'bits/atomic_base.h') or {
if cc_os == 'linux' {
eprintln(err)
exit(2)
}
'no_file' // bits/atomic_base.h is only used on linux
}
patch_atomic(os.join_path(os.dir(@FILE), 'atomic.h'), atomic_path) or {
eprintln(err)
exit(2)
}
if bitsatomicbase_path != 'no_file' {
patch_bitsatomicbase(os.join_path(os.dir(@FILE), 'bitsatomicbase.h'), bitsatomicbase_path) or {
eprintln(err)
exit(2)
}
}
println('$atomic_path:::$bitsatomicbase_path')
}
fn get_cc_info(cc string) (string, string, string) {
cc_type := if cc.contains('clang') {
'clang'
} else if cc.contains('g') {
'gcc'
} else {
eprintln('only gcc and clang are supported')
exit(1)
'none'
}
lines := os.execute('$cc -v').output.split('\n')
// gcc and clang both have the same way way to say what version they have and what the host target triple is
cc_version := lines.filter(it.contains('$cc_type version '))[0].all_after('$cc_type version ').all_before('.')
cc_os := lines.filter(it.starts_with('Target: '))[0].all_after('Target: ').split('-')[2]
return cc_type, cc_version, if cc_os.contains('darwin') {
'darwin' // remove 20.6.0 from darwin20.6.0
} else {
cc_os
}
}
fn get_search_paths(cc string) []string {
result := os.execute('$cc -v -x c++ /dev/null').output
lines := result.split('\n')
search_path := lines[lines.index('#include <...> search starts here:') + 1..lines.index('End of search list.')]
return search_path.map(os.real_path(it.all_before('(').trim_space()))
}
fn find_file(search_paths []string, file string) ?string {
for search_path in search_paths {
if os.exists(os.join_path(search_path, file)) {
return os.join_path(search_path, file)
}
}
return error('$file not found')
}
fn patch_atomic(outfile string, infile string) ? {
lines := os.read_file(infile) ?.split('\n')
outlines := lines.filter(!it.contains('atomic(const atomic&) = delete;'))
outtext := outlines.join('\n').replace('#include <bits/atomic_base.h>', '#include "bitsatomicbase.h"')
os.write_file(outfile, outtext) ?
}
fn patch_bitsatomicbase(outfile string, infile string) ? {
lines := os.read_file(infile) ?.split('\n')
outlines := lines.filter(!it.contains('__atomic_base(const __atomic_base&) = delete;'))
outtext := outlines.join('\n').replace('#include <bits/atomic_base.h>', '#include "bitsatomicbase.h"')
os.write_file(outfile, outtext) ?
}

View File

@ -126,3 +126,10 @@ pub:
arg string
kind AttributeKind
}
[used]
fn v_segmentation_fault_handler(signal int) {
eprintln('signal 11: segmentation fault')
print_backtrace()
exit(128 + 11)
}

View File

@ -623,8 +623,8 @@ pub fn channel_select(mut channels []&Channel, dir []Direction, mut objrefs []vo
}
subscr[i].prev = unsafe { &ch.write_subscriber }
unsafe {
subscr[i].nxt = C.atomic_exchange_ptr(&voidptr(&ch.write_subscriber),
&subscr[i])
subscr[i].nxt = &Subscription(C.atomic_exchange_ptr(&voidptr(&ch.write_subscriber),
&subscr[i]))
}
if voidptr(subscr[i].nxt) != voidptr(0) {
subscr[i].nxt.prev = unsafe { &subscr[i].nxt }
@ -637,7 +637,8 @@ pub fn channel_select(mut channels []&Channel, dir []Direction, mut objrefs []vo
}
subscr[i].prev = unsafe { &ch.read_subscriber }
unsafe {
subscr[i].nxt = C.atomic_exchange_ptr(&voidptr(&ch.read_subscriber), &subscr[i])
subscr[i].nxt = &Subscription(C.atomic_exchange_ptr(&voidptr(&ch.read_subscriber),
&subscr[i]))
}
if voidptr(subscr[i].nxt) != voidptr(0) {
subscr[i].nxt.prev = unsafe { &subscr[i].nxt }

View File

@ -32,6 +32,18 @@ fn C.sem_trywait(voidptr) int
fn C.sem_timedwait(voidptr, voidptr) int
fn C.sem_destroy(voidptr) int
[typedef]
struct C.pthread_mutex_t {}
[typedef]
struct C.pthread_rwlock_t {}
[typedef]
struct C.pthread_rwlockattr_t {}
[typedef]
struct C.sem_t {}
// [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
[heap]
pub struct Mutex {

View File

@ -1259,7 +1259,7 @@ pub fn (mut t Table) bitsize_to_type(bit_size int) Type {
}
}
pub fn (mut t Table) does_type_implement_interface(typ Type, inter_typ Type) bool {
pub fn (t Table) does_type_implement_interface(typ Type, inter_typ Type) bool {
if typ.idx() == inter_typ.idx() {
// same type -> already casted to the interface
return true

View File

@ -556,7 +556,7 @@ pub enum Kind {
thread
}
pub fn (t &TypeSymbol) str() string {
pub fn (t TypeSymbol) str() string {
return t.name
}
@ -641,11 +641,6 @@ pub fn (t &TypeSymbol) is_heap() bool {
}
}
/*
pub fn (t TypeSymbol) str() string {
return t.name
}
*/
pub fn (mut t Table) register_builtin_type_symbols() {
// reserve index 0 so nothing can go there
// save index check, 0 will mean not found

View File

@ -550,6 +550,18 @@ fn (mut v Builder) cc() {
v.setup_ccompiler_options(ccompiler)
v.build_thirdparty_obj_files()
v.setup_output_name()
if v.pref.os != .windows && ccompiler.contains('++') {
for file in v.parsed_files {
if file.imports.any(it.mod.contains('sync')) {
x := @VEXE + ' run ' +
os.join_path(@VEXEROOT, 'thirdparty', 'stdatomic', 'nix', 'cpp', 'gen.v') +
' ' + ccompiler
os.execute(x)
break
}
}
}
//
mut libs := []string{} // builtin.o os.o http.o etc
if v.pref.build_mode == .build_module {

View File

@ -418,7 +418,9 @@ pub fn (mut c Checker) fail_if_unreadable(expr ast.Expr, typ ast.Type, what stri
pos = expr.left.position().extend(expr.pos)
c.fail_if_unreadable(expr.left, expr.left_type, what)
}
else {}
else {
pos = expr.position()
}
}
if typ.has_flag(.shared_f) {
c.error('you have to create a handle and `rlock` it to use a `shared` element as non-mut $what',

View File

@ -226,7 +226,15 @@ pub fn (mut f Fmt) short_module(name string) string {
idx := vals.len - 1
mname, tprefix := f.get_modname_prefix(vals[..idx].join('.'))
symname := vals[vals.len - 1]
aname := f.mod2alias[mname]
mut aname := f.mod2alias[mname]
if aname == '' {
for _, v in f.mod2alias {
if v == mname {
aname = mname
break
}
}
}
if aname == '' {
return symname
}

View File

@ -254,7 +254,9 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) {
// the only argument can only be an infix expression like `a < b` or `b.field > a.field`
if node.args.len == 0 {
comparison_type = g.unwrap(info.elem_type.set_nr_muls(0))
if compare_fn in g.array_sort_fn {
shared a := g.array_sort_fn
array_sort_fn := a.clone()
if compare_fn in array_sort_fn {
g.gen_array_sort_call(node, compare_fn)
return
}
@ -273,7 +275,9 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) {
if is_reverse {
compare_fn += '_reverse'
}
if compare_fn in g.array_sort_fn {
shared a := g.array_sort_fn
array_sort_fn := a.clone()
if compare_fn in array_sort_fn {
g.gen_array_sort_call(node, compare_fn)
return
}
@ -300,8 +304,9 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) {
// Register a new custom `compare_xxx` function for qsort()
// TODO: move to checker
g.table.register_fn(name: compare_fn, return_type: ast.int_type)
g.array_sort_fn[compare_fn] = true
lock g.array_sort_fn {
g.array_sort_fn << compare_fn
}
stype_arg := g.typ(info.elem_type)
g.definitions.writeln('int ${compare_fn}($stype_arg* a, $stype_arg* b) {')
@ -485,16 +490,22 @@ fn (mut g Gen) gen_array_prepend(node ast.CallExpr) {
}
}
fn (mut g Gen) gen_array_contains_method(left_type ast.Type) string {
mut unwrap_left_type := g.unwrap_generic(left_type)
if unwrap_left_type.share() == .shared_t {
unwrap_left_type = unwrap_left_type.clear_flag(.shared_f)
}
mut left_sym := g.table.get_type_symbol(unwrap_left_type)
left_final_sym := g.table.get_final_type_symbol(unwrap_left_type)
mut left_type_str := g.typ(unwrap_left_type).replace('*', '')
fn_name := '${left_type_str}_contains'
if !left_sym.has_method('contains') {
fn (mut g Gen) get_array_contains_method(typ ast.Type) string {
t := g.table.get_final_type_symbol(g.unwrap_generic(typ).set_nr_muls(0)).idx
g.array_contains_types << t
return g.typ(t) + '_contains'
}
fn (mut g Gen) gen_array_contains_methods() {
mut done := []ast.Type{}
for t in g.array_contains_types {
left_final_sym := g.table.get_final_type_symbol(t)
if left_final_sym.idx in done || g.table.get_type_symbol(t).has_method('contains') {
continue
}
done << t
mut left_type_str := g.typ(t)
fn_name := '${left_type_str}_contains'
left_info := left_final_sym.info as ast.Array
mut elem_type_str := g.typ(left_info.elem_type)
elem_sym := g.table.get_type_symbol(left_info.elem_type)
@ -509,15 +520,15 @@ fn (mut g Gen) gen_array_contains_method(left_type ast.Type) string {
if elem_sym.kind == .string {
fn_builder.writeln('\t\tif (fast_string_eq(((string*)a.data)[i], v)) {')
} else if elem_sym.kind == .array && left_info.elem_type.nr_muls() == 0 {
ptr_typ := g.gen_array_equality_fn(left_info.elem_type)
ptr_typ := g.equality_fn(left_info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq((($elem_type_str*)a.data)[i], v)) {')
} else if elem_sym.kind == .function {
fn_builder.writeln('\t\tif (((voidptr*)a.data)[i] == v) {')
} else if elem_sym.kind == .map && left_info.elem_type.nr_muls() == 0 {
ptr_typ := g.gen_map_equality_fn(left_info.elem_type)
ptr_typ := g.equality_fn(left_info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_map_eq((($elem_type_str*)a.data)[i], v)) {')
} else if elem_sym.kind == .struct_ && left_info.elem_type.nr_muls() == 0 {
ptr_typ := g.gen_struct_equality_fn(left_info.elem_type)
ptr_typ := g.equality_fn(left_info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq((($elem_type_str*)a.data)[i], v)) {')
} else {
fn_builder.writeln('\t\tif ((($elem_type_str*)a.data)[i] == v) {')
@ -528,41 +539,43 @@ fn (mut g Gen) gen_array_contains_method(left_type ast.Type) string {
fn_builder.writeln('\treturn false;')
fn_builder.writeln('}')
g.auto_fn_definitions << fn_builder.str()
left_sym.register_method(&ast.Fn{
name: 'contains'
params: [ast.Param{
typ: unwrap_left_type
}, ast.Param{
typ: left_info.elem_type
}]
})
}
return fn_name
}
// `nums.contains(2)`
fn (mut g Gen) gen_array_contains(node ast.CallExpr) {
fn_name := g.gen_array_contains_method(node.left_type)
fn (mut g Gen) gen_array_contains(typ ast.Type, left ast.Expr, right ast.Expr) {
fn_name := g.get_array_contains_method(typ)
g.write('${fn_name}(')
if node.left_type.is_ptr() && node.left_type.share() != .shared_t {
g.write('*')
g.write(strings.repeat(`*`, typ.nr_muls()))
if typ.share() == .shared_t {
g.out.go_back(1)
}
g.expr(node.left)
if node.left_type.share() == .shared_t {
g.expr(left)
if typ.share() == .shared_t {
g.write('->val')
}
g.write(', ')
g.expr(node.args[0].expr)
g.expr(right)
g.write(')')
}
fn (mut g Gen) gen_array_index_method(left_type ast.Type) string {
unwrap_left_type := g.unwrap_generic(left_type)
mut left_sym := g.table.get_type_symbol(unwrap_left_type)
mut left_type_str := g.typ(unwrap_left_type).trim('*')
fn_name := '${left_type_str}_index'
if !left_sym.has_method('index') {
info := left_sym.info as ast.Array
fn (mut g Gen) get_array_index_method(typ ast.Type) string {
t := g.unwrap_generic(typ).set_nr_muls(0)
g.array_index_types << t
return g.typ(t) + '_index'
}
fn (mut g Gen) gen_array_index_methods() {
mut done := []ast.Type{}
for t in g.array_index_types {
if t in done || g.table.get_type_symbol(t).has_method('index') {
continue
}
done << t
final_left_sym := g.table.get_final_type_symbol(t)
mut left_type_str := g.typ(t)
fn_name := '${left_type_str}_index'
info := final_left_sym.info as ast.Array
mut elem_type_str := g.typ(info.elem_type)
elem_sym := g.table.get_type_symbol(info.elem_type)
if elem_sym.kind == .function {
@ -577,15 +590,15 @@ fn (mut g Gen) gen_array_index_method(left_type ast.Type) string {
if elem_sym.kind == .string {
fn_builder.writeln('\t\tif (fast_string_eq(*pelem, v)) {')
} else if elem_sym.kind == .array && !info.elem_type.is_ptr() {
ptr_typ := g.gen_array_equality_fn(info.elem_type)
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq( *pelem, v)) {')
} else if elem_sym.kind == .function && !info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif ( pelem == v) {')
} else if elem_sym.kind == .map && !info.elem_type.is_ptr() {
ptr_typ := g.gen_map_equality_fn(info.elem_type)
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(( *pelem, v))) {')
} else if elem_sym.kind == .struct_ && !info.elem_type.is_ptr() {
ptr_typ := g.gen_struct_equality_fn(info.elem_type)
ptr_typ := g.equality_fn(info.elem_type)
fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq( *pelem, v)) {')
} else {
fn_builder.writeln('\t\tif (*pelem == v) {')
@ -596,21 +609,12 @@ fn (mut g Gen) gen_array_index_method(left_type ast.Type) string {
fn_builder.writeln('\treturn -1;')
fn_builder.writeln('}')
g.auto_fn_definitions << fn_builder.str()
left_sym.register_method(&ast.Fn{
name: 'index'
params: [ast.Param{
typ: unwrap_left_type
}, ast.Param{
typ: info.elem_type
}]
})
}
return fn_name
}
// `nums.index(2)`
fn (mut g Gen) gen_array_index(node ast.CallExpr) {
fn_name := g.gen_array_index_method(node.left_type)
fn_name := g.get_array_index_method(node.left_type)
g.write('${fn_name}(')
if node.left_type.is_ptr() {
g.write('*')

View File

@ -20,7 +20,7 @@ fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) {
}
}
g.inside_ternary++
if g.is_test {
if g.pref.is_test {
g.write('if (')
g.expr(node.expr)
g.write(')')

View File

@ -5,13 +5,52 @@ module c
import strings
import v.ast
fn (mut g Gen) equality_fn(typ ast.Type) string {
g.needed_equality_fns << typ.set_nr_muls(0)
return g.typ(g.unwrap_generic(typ).set_nr_muls(0))
}
fn (mut g Gen) gen_equality_fns() {
for needed_typ in g.needed_equality_fns {
if needed_typ in g.generated_eq_fns {
continue
}
sym := g.table.get_type_symbol(needed_typ)
match sym.kind {
.sum_type {
g.gen_sumtype_equality_fn(needed_typ)
}
.struct_ {
g.gen_struct_equality_fn(needed_typ)
}
.array {
g.gen_array_equality_fn(needed_typ)
}
.array_fixed {
g.gen_fixed_array_equality_fn(needed_typ)
}
.map {
g.gen_map_equality_fn(needed_typ)
}
.alias {
g.gen_alias_equality_fn(needed_typ)
}
else {
verror('could not generate equality function for type $sym.kind')
}
}
}
}
fn (mut g Gen) gen_sumtype_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0))
if ptr_styp in g.sumtype_fn_definitions {
if left_type in g.generated_eq_fns {
return ptr_styp
}
g.sumtype_fn_definitions << ptr_styp
g.generated_eq_fns << left_type
info := left.sym.sumtype_info()
g.type_definitions.writeln('static bool ${ptr_styp}_sumtype_eq($ptr_styp a, $ptr_styp b); // auto')
@ -59,10 +98,10 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0))
fn_name := ptr_styp.replace('struct ', '')
if fn_name in g.struct_fn_definitions {
if left_type in g.generated_eq_fns {
return fn_name
}
g.struct_fn_definitions << fn_name
g.generated_eq_fns << left_type
info := left.sym.struct_info()
g.type_definitions.writeln('static bool ${fn_name}_struct_eq($ptr_styp a, $ptr_styp b); // auto')
@ -124,10 +163,10 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string {
fn (mut g Gen) gen_alias_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0))
if ptr_styp in g.alias_fn_definitions {
if left_type in g.generated_eq_fns {
return ptr_styp
}
g.alias_fn_definitions << ptr_styp
g.generated_eq_fns << left_type
info := left.sym.info as ast.Alias
g.type_definitions.writeln('static bool ${ptr_styp}_alias_eq($ptr_styp a, $ptr_styp b); // auto')
@ -164,10 +203,10 @@ fn (mut g Gen) gen_alias_equality_fn(left_type ast.Type) string {
fn (mut g Gen) gen_array_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0))
if ptr_styp in g.array_fn_definitions {
if left_type in g.generated_eq_fns {
return ptr_styp
}
g.array_fn_definitions << ptr_styp
g.generated_eq_fns << left_type
elem := g.unwrap(left.sym.array_info().elem_type)
ptr_elem_styp := g.typ(elem.typ)
g.type_definitions.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b); // auto')
@ -216,10 +255,10 @@ fn (mut g Gen) gen_array_equality_fn(left_type ast.Type) string {
fn (mut g Gen) gen_fixed_array_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0))
if ptr_styp in g.array_fn_definitions {
if left_type in g.generated_eq_fns {
return ptr_styp
}
g.array_fn_definitions << ptr_styp
g.generated_eq_fns << left_type
elem_info := left.sym.array_fixed_info()
elem := g.unwrap(elem_info.elem_type)
size := elem_info.size
@ -266,10 +305,10 @@ fn (mut g Gen) gen_fixed_array_equality_fn(left_type ast.Type) string {
fn (mut g Gen) gen_map_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0))
if ptr_styp in g.map_fn_definitions {
if left_type in g.generated_eq_fns {
return ptr_styp
}
g.map_fn_definitions << ptr_styp
g.generated_eq_fns << left_type
value := g.unwrap(left.sym.map_info().value_type)
ptr_value_styp := g.typ(value.typ)
g.type_definitions.writeln('static bool ${ptr_styp}_map_eq($ptr_styp a, $ptr_styp b); // auto')

View File

@ -117,9 +117,26 @@ fn (mut g Gen) gen_str_default(sym ast.TypeSymbol, styp string, str_fn_name stri
g.auto_str_funcs.writeln('}')
}
fn (mut g Gen) gen_str_method_for_type(typ ast.Type) string {
styp := g.typ(typ).replace('*', '')
mut sym := g.table.get_type_symbol(g.unwrap_generic(typ))
struct StrType {
styp string
mut:
typ ast.Type
}
fn (mut g Gen) get_str_fn(typ ast.Type) string {
mut unwrapped := g.unwrap_generic(typ).set_nr_muls(0).clear_flag(.variadic)
if g.pref.nofloat {
if typ == ast.f32_type {
unwrapped = ast.u32_type
} else if typ == ast.f64_type {
unwrapped = ast.u64_type
}
}
if typ.has_flag(.optional) {
unwrapped.set_flag(.optional)
}
styp := g.typ(unwrapped)
mut sym := g.table.get_type_symbol(unwrapped)
mut str_fn_name := styp_to_str_fn_name(styp)
if mut sym.info is ast.Alias {
if sym.info.is_import {
@ -127,85 +144,80 @@ fn (mut g Gen) gen_str_method_for_type(typ ast.Type) string {
str_fn_name = styp_to_str_fn_name(sym.name)
}
}
sym_has_str_method, str_method_expects_ptr, str_nr_args := sym.str_method_info()
already_generated_key := '$styp:$str_fn_name'
if !sym_has_str_method && already_generated_key !in g.str_types && !typ.has_flag(.optional) {
$if debugautostr ? {
eprintln('> gen_str_for_type: |typ: ${typ:5}, ${sym.name:20}|has_str: ${sym_has_str_method:5}|expects_ptr: ${str_method_expects_ptr:5}|nr_args: ${str_nr_args:1}|fn_name: ${str_fn_name:20}')
}
g.str_types << already_generated_key
if g.pref.nofloat {
if sym.name == 'f32' {
return g.gen_str_method_for_type(ast.u32_type)
// return ''
} else if sym.name == 'f64' {
return g.gen_str_method_for_type(ast.u64_type)
// return ''
}
}
match mut sym.info {
ast.Alias {
if sym.info.is_import {
g.gen_str_default(sym, styp, str_fn_name)
} else {
g.gen_str_for_alias(sym.info, styp, str_fn_name)
}
}
ast.Array {
g.gen_str_for_array(sym.info, styp, str_fn_name)
}
ast.ArrayFixed {
g.gen_str_for_array_fixed(sym.info, styp, str_fn_name)
}
ast.Enum {
g.gen_str_for_enum(sym.info, styp, str_fn_name)
}
ast.FnType {
g.gen_str_for_fn_type(sym.info, styp, str_fn_name)
}
ast.Struct {
g.gen_str_for_struct(sym.info, styp, str_fn_name)
}
ast.Map {
g.gen_str_for_map(sym.info, styp, str_fn_name)
}
ast.MultiReturn {
g.gen_str_for_multi_return(sym.info, styp, str_fn_name)
}
ast.SumType {
g.gen_str_for_union_sum_type(sym.info, styp, str_fn_name)
}
ast.Interface {
g.gen_str_for_interface(sym.info, styp, str_fn_name)
}
ast.Chan {
g.gen_str_for_chan(sym.info, styp, str_fn_name)
}
ast.Thread {
g.gen_str_for_thread(sym.info, styp, str_fn_name)
}
else {
println(g.table.type_str(typ))
verror("1could not generate string method '$str_fn_name' for type '$styp'")
}
}
}
if typ.has_flag(.optional) {
option_already_generated_key := 'option_$already_generated_key'
if option_already_generated_key !in g.str_types {
g.gen_str_for_option(typ, styp, str_fn_name)
g.str_types << option_already_generated_key
}
return str_fn_name
g.str_types << StrType{
typ: unwrapped
styp: styp
}
return str_fn_name
}
fn (mut g Gen) final_gen_str(typ StrType) {
if typ in g.generated_str_fns {
return
}
g.generated_str_fns << typ
sym := g.table.get_type_symbol(typ.typ)
if sym.has_method('str') && !typ.typ.has_flag(.optional) {
return
}
styp := typ.styp
str_fn_name := styp_to_str_fn_name(styp)
if typ.typ.has_flag(.optional) {
g.gen_str_for_option(typ.typ, styp, str_fn_name)
return
}
match mut sym.info {
ast.Alias {
if sym.info.is_import {
g.gen_str_default(sym, styp, str_fn_name)
} else {
g.gen_str_for_alias(sym.info, styp, str_fn_name)
}
}
ast.Array {
g.gen_str_for_array(sym.info, styp, str_fn_name)
}
ast.ArrayFixed {
g.gen_str_for_array_fixed(sym.info, styp, str_fn_name)
}
ast.Enum {
g.gen_str_for_enum(sym.info, styp, str_fn_name)
}
ast.FnType {
g.gen_str_for_fn_type(sym.info, styp, str_fn_name)
}
ast.Struct {
g.gen_str_for_struct(sym.info, styp, str_fn_name)
}
ast.Map {
g.gen_str_for_map(sym.info, styp, str_fn_name)
}
ast.MultiReturn {
g.gen_str_for_multi_return(sym.info, styp, str_fn_name)
}
ast.SumType {
g.gen_str_for_union_sum_type(sym.info, styp, str_fn_name)
}
ast.Interface {
g.gen_str_for_interface(sym.info, styp, str_fn_name)
}
ast.Chan {
g.gen_str_for_chan(sym.info, styp, str_fn_name)
}
ast.Thread {
g.gen_str_for_thread(sym.info, styp, str_fn_name)
}
else {
verror("could not generate string method $str_fn_name for type '$styp'")
}
}
}
fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string) {
parent_type := typ.clear_flag(.optional)
sym := g.table.get_type_symbol(parent_type)
sym_has_str_method, _, _ := sym.str_method_info()
parent_str_fn_name := g.gen_str_method_for_type(parent_type)
parent_str_fn_name := g.get_str_fn(parent_type)
g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }')
@ -232,7 +244,7 @@ fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string)
}
fn (mut g Gen) gen_str_for_alias(info ast.Alias, styp string, str_fn_name string) {
parent_str_fn_name := g.gen_str_method_for_type(info.parent_type)
parent_str_fn_name := g.get_str_fn(info.parent_type)
mut clean_type_v_type_name := util.strip_main_name(styp.replace('__', '.'))
g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }')
@ -261,7 +273,7 @@ fn (mut g Gen) gen_str_for_multi_return(info ast.MultiReturn, styp string, str_f
sym := g.table.get_type_symbol(typ)
is_arg_ptr := typ.is_ptr()
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
arg_str_fn_name := g.gen_str_method_for_type(typ)
arg_str_fn_name := g.get_str_fn(typ)
if should_use_indent_func(sym.kind) && !sym_has_str_method {
fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}(a.arg$i));')
@ -346,7 +358,7 @@ fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, str_fn_nam
fn_builder.writeln('static string indent_${str_fn_name}($styp x, int indent_count) { /* gen_str_for_interface */')
for typ in info.types {
subtype := g.table.get_type_symbol(typ)
mut func_name := g.gen_str_method_for_type(typ)
mut func_name := g.get_str_fn(typ)
sym_has_str_method, str_method_expects_ptr, _ := subtype.str_method_info()
if should_use_indent_func(subtype.kind) && !sym_has_str_method {
func_name = 'indent_$func_name'
@ -405,7 +417,7 @@ fn (mut g Gen) gen_str_for_union_sum_type(info ast.SumType, styp string, str_fn_
fn_builder.writeln('\tswitch(x._typ) {')
for typ in info.variants {
typ_str := g.typ(typ)
mut func_name := g.gen_str_method_for_type(typ)
mut func_name := g.get_str_fn(typ)
sym := g.table.get_type_symbol(typ)
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' }
@ -512,7 +524,7 @@ fn (mut g Gen) gen_str_for_array(info ast.Array, styp string, str_fn_name string
field_styp := g.typ(typ)
is_elem_ptr := typ.is_ptr()
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
mut elem_str_fn_name := g.gen_str_method_for_type(typ)
mut elem_str_fn_name := g.get_str_fn(typ)
if sym.kind == .byte {
elem_str_fn_name = elem_str_fn_name + '_escaped'
}
@ -584,7 +596,7 @@ fn (mut g Gen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_fn_
}
is_elem_ptr := typ.is_ptr()
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
elem_str_fn_name := g.gen_str_method_for_type(typ)
elem_str_fn_name := g.get_str_fn(typ)
g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto')
g.auto_str_funcs.writeln('static string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}')
@ -645,7 +657,7 @@ fn (mut g Gen) gen_str_for_map(info ast.Map, styp string, str_fn_name string) {
key_styp := g.typ(key_typ)
key_str_fn_name := key_styp.replace('*', '') + '_str'
if !key_sym.has_method('str') {
g.gen_str_method_for_type(key_typ)
g.get_str_fn(key_typ)
}
mut val_typ := info.value_type
@ -657,7 +669,7 @@ fn (mut g Gen) gen_str_for_map(info ast.Map, styp string, str_fn_name string) {
val_styp := g.typ(val_typ)
elem_str_fn_name := val_styp.replace('*', '') + '_str'
if !val_sym.has_method('str') {
g.gen_str_method_for_type(val_typ)
g.get_str_fn(val_typ)
}
g.type_definitions.writeln('static string ${str_fn_name}($styp m); // auto')
@ -813,7 +825,7 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, styp string, str_fn_name stri
field_styp_fn_name := if has_custom_str {
'${field_styp}_str'
} else {
g.gen_str_method_for_type(field.typ)
g.get_str_fn(field.typ)
}
// manage the fact hat with float we use always the g representation

View File

@ -11,6 +11,7 @@ import v.token
import v.util
import v.util.version
import v.depgraph
import sync.pool
const (
// NB: some of the words in c_reserved, are not reserved in C,
@ -37,11 +38,12 @@ fn string_array_to_map(a []string) map[string]bool {
}
struct Gen {
pref &pref.Preferences
module_built string
field_data_type ast.Type // cache her to avoid map lookups
pref &pref.Preferences
field_data_type ast.Type // cache her to avoid map lookups
module_built string
timers_should_print bool
table &ast.Table
mut:
table &ast.Table
out strings.Builder
cheaders strings.Builder
includes strings.Builder // all C #includes required by V modules
@ -50,7 +52,10 @@ mut:
type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
global_inits map[string]strings.Builder // default initializers for globals (goes in _vinit())
global_init strings.Builder // thread local of the above
inits map[string]strings.Builder // contents of `void _vinit/2{}`
init strings.Builder
cleanup strings.Builder
cleanups map[string]strings.Builder // contents of `void _vcleanup(){}`
gowrappers strings.Builder // all go callsite wrappers
stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined
@ -62,7 +67,6 @@ mut:
shared_types strings.Builder // shared/lock types
shared_functions strings.Builder // shared constructors
channel_definitions strings.Builder // channel related code
options_typedefs strings.Builder // Option typedefs
options strings.Builder // `Option_xxxx` types
json_forward_decls strings.Builder // json type forward decls
enum_typedefs strings.Builder // enum types
@ -70,32 +74,33 @@ mut:
file &ast.File
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
last_fn_c_name string
tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn.
tmp_count2 int // a separate tmp var counter for autofree fn calls
tmp_count_declarations int // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations
global_tmp_count int // like tmp_count but global and not resetted in each function
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage
is_void_expr_stmt bool // ExprStmt whos result is discarded
is_arraymap_set bool // map or array set value state
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
is_shared bool // for initialization of hidden mutex in `[rw]shared` literals
is_vlines_enabled bool // is it safe to generate #line directives when -g is passed
inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls)
arraymap_set_pos int // map or array set value position
vlines_path string // set to the proper path for generating #line directives
optionals []string // to avoid duplicates TODO perf, use map
chan_pop_optionals []string // types for `x := <-ch or {...}`
chan_push_optionals []string // types for `ch <- x or {...}`
tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn.
tmp_count2 int // a separate tmp var counter for autofree fn calls
tmp_count_declarations int // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations
global_tmp_count int // like tmp_count but global and not resetted in each function
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage
is_void_expr_stmt bool // ExprStmt whos result is discarded
is_arraymap_set bool // map or array set value state
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
is_shared bool // for initialization of hidden mutex in `[rw]shared` literals
is_vlines_enabled bool // is it safe to generate #line directives when -g is passed
inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls)
arraymap_set_pos int // map or array set value position
vlines_path string // set to the proper path for generating #line directives
optionals map[string]string // to avoid duplicates
done_optionals shared []string // to avoid duplicates
chan_pop_optionals map[string]string // types for `x := <-ch or {...}`
chan_push_optionals map[string]string // types for `ch <- x or {...}`
cur_lock ast.LockExpr
mtxs string // array of mutexes if the `lock` has multiple variables
labeled_loops map[string]&ast.Stmt
inner_loop &ast.Stmt
shareds []int // types with hidden mutex for which decl has been emitted
inside_ternary int // ?: comma separated statements on a single line
inside_map_postfix bool // inside map++/-- postfix expr
inside_map_infix bool // inside map<</+=/-= infix expr
shareds map[int]string // types with hidden mutex for which decl has been emitted
inside_ternary int // ?: comma separated statements on a single line
inside_map_postfix bool // inside map++/-- postfix expr
inside_map_infix bool // inside map<</+=/-= infix expr
inside_map_index bool
inside_opt_data bool
inside_if_optional bool
@ -107,24 +112,20 @@ mut:
is_autofree bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree
indent int
empty_line bool
is_test bool
assign_op token.Kind // *=, =, etc (for array_set)
defer_stmts []ast.DeferStmt
defer_ifdef string
defer_profile_code string
str_types []string // types that need automatic str() generation
threaded_fns []string // for generating unique wrapper types and fns for `go xxx()`
waiter_fns []string // functions that wait for `go xxx()` to finish
array_fn_definitions []string // array equality functions that have been defined
map_fn_definitions []string // map equality functions that have been defined
struct_fn_definitions []string // struct equality functions that have been defined
sumtype_fn_definitions []string // sumtype equality functions that have been defined
alias_fn_definitions []string // alias equality functions that have been defined
auto_fn_definitions []string // auto generated functions defination list
str_types []StrType // types that need automatic str() generation
generated_str_fns []StrType // types that already have a str() function
threaded_fns []string // for generating unique wrapper types and fns for `go xxx()`
waiter_fns []string // functions that wait for `go xxx()` to finish
auto_fn_definitions []string // auto generated functions defination list
sumtype_casting_fns []SumtypeCastingFn
anon_fn_definitions []string // anon generated functions defination list
sumtype_definitions map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated
is_json_fn bool // inside json.encode()
json_types []string // to avoid json gen duplicates
is_json_fn bool // inside json.encode()
json_types []ast.Type // to avoid json gen duplicates
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
is_builtin_mod bool
hotcode_fn_names []string
@ -177,11 +178,18 @@ mut:
as_cast_type_names map[string]string // table for type name lookup in runtime (for __as_cast)
obf_table map[string]string
// main_fn_decl_node ast.FnDecl
expected_cast_type ast.Type // for match expr of sumtypes
defer_vars []string
anon_fn bool
array_sort_fn map[string]bool
nr_closures int
nr_closures int
array_sort_fn shared []string
expected_cast_type ast.Type // for match expr of sumtypes
defer_vars []string
anon_fn bool
tests_inited bool
cur_concrete_types []ast.Type // do not use table.cur_concrete_types because table is global, so should not be accessed by different threads
cur_fn &ast.FnDecl = 0 // same here
needed_equality_fns []ast.Type
generated_eq_fns []ast.Type
array_contains_types []ast.Type
array_index_types []ast.Type
}
pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
@ -200,7 +208,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
$if time_cgening ? {
timers_should_print = true
}
mut g := Gen{
mut global_g := Gen{
file: 0
out: strings.new_builder(512000)
cheaders: strings.new_builder(15000)
@ -216,7 +224,6 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
pcs_declarations: strings.new_builder(100)
hotcode_definitions: strings.new_builder(100)
embedded_data: strings.new_builder(1000)
options_typedefs: strings.new_builder(100)
options: strings.new_builder(100)
shared_types: strings.new_builder(100)
shared_functions: strings.new_builder(100)
@ -227,61 +234,182 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
table: table
pref: pref
fn_decl: 0
is_autofree: true
is_autofree: pref.autofree
indent: -1
module_built: module_built
timers_should_print: timers_should_print
timers: util.new_timers(timers_should_print)
inner_loop: &ast.EmptyStmt{}
field_data_type: ast.Type(table.find_type_idx('FieldData'))
init: strings.new_builder(100)
}
g.timers.start('cgen init')
for mod in g.table.modules {
g.inits[mod] = strings.new_builder(100)
g.global_inits[mod] = strings.new_builder(100)
g.cleanups[mod] = strings.new_builder(100)
// anon fn may include assert and thus this needs
// to be included before any test contents are written
if pref.is_test {
global_g.write_tests_definitions()
}
g.init()
g.timers.show('cgen init')
mut tests_inited := false
mut autofree_used := false
for file in files {
g.timers.start('cgen_file $file.path')
g.file = file
if g.pref.is_vlines {
g.vlines_path = util.vlines_escape_path(file.path, g.pref.ccompiler)
}
// println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len')
// building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v'))
g.is_test = g.pref.is_test
if g.file.path == '' || !g.pref.autofree {
// cgen test or building V
// println('autofree=false')
g.is_autofree = false
} else {
g.is_autofree = true
autofree_used = true
}
// anon fn may include assert and thus this needs
// to be included before any test contents are written
if g.is_test && !tests_inited {
g.write_tests_definitions()
tests_inited = true
}
g.stmts(file.stmts)
// Transfer embedded files
if file.embedded_files.len > 0 {
for path in file.embedded_files {
if path !in g.embedded_files {
g.embedded_files << path
global_g.timers.start('cgen init')
for mod in global_g.table.modules {
global_g.inits[mod] = strings.new_builder(200)
global_g.global_inits[mod] = strings.new_builder(100)
global_g.cleanups[mod] = strings.new_builder(100)
}
global_g.init()
global_g.timers.show('cgen init')
global_g.tests_inited = false
if !pref.no_parallel {
mut pp := pool.new_pool_processor(
callback: fn (p &pool.PoolProcessor, idx int, wid int) &Gen {
file := p.get_item<&ast.File>(idx)
mut global_g := &Gen(p.get_shared_context())
mut g := &Gen{
file: file
out: strings.new_builder(512000)
cheaders: strings.new_builder(15000)
includes: strings.new_builder(100)
typedefs: strings.new_builder(100)
typedefs2: strings.new_builder(100)
type_definitions: strings.new_builder(100)
definitions: strings.new_builder(100)
gowrappers: strings.new_builder(100)
stringliterals: strings.new_builder(100)
auto_str_funcs: strings.new_builder(100)
comptime_defines: strings.new_builder(100)
pcs_declarations: strings.new_builder(100)
hotcode_definitions: strings.new_builder(100)
embedded_data: strings.new_builder(1000)
options: strings.new_builder(100)
shared_types: strings.new_builder(100)
shared_functions: strings.new_builder(100)
channel_definitions: strings.new_builder(100)
json_forward_decls: strings.new_builder(100)
enum_typedefs: strings.new_builder(100)
sql_buf: strings.new_builder(100)
init: strings.new_builder(100)
global_init: strings.new_builder(0)
cleanup: strings.new_builder(100)
table: global_g.table
pref: global_g.pref
fn_decl: 0
indent: -1
module_built: global_g.module_built
timers: util.new_timers(global_g.timers_should_print)
inner_loop: &ast.EmptyStmt{}
field_data_type: ast.Type(global_g.table.find_type_idx('FieldData'))
array_sort_fn: global_g.array_sort_fn
done_optionals: global_g.done_optionals
is_autofree: global_g.pref.autofree
}
g.gen()
return g
}
)
pp.set_shared_context(global_g) // TODO: make global_g shared
pp.work_on_items(files)
global_g.timers.start('cgen unification')
// tg = thread gen
for g in pp.get_results<Gen>() {
global_g.out.write(g.out) or { panic(err) }
global_g.cheaders.write(g.cheaders) or { panic(err) }
global_g.includes.write(g.includes) or { panic(err) }
global_g.typedefs.write(g.typedefs) or { panic(err) }
global_g.typedefs2.write(g.typedefs2) or { panic(err) }
global_g.type_definitions.write(g.type_definitions) or { panic(err) }
global_g.definitions.write(g.definitions) or { panic(err) }
global_g.gowrappers.write(g.gowrappers) or { panic(err) }
global_g.stringliterals.write(g.stringliterals) or { panic(err) }
global_g.auto_str_funcs.write(g.auto_str_funcs) or { panic(err) }
global_g.comptime_defines.write(g.comptime_defines) or { panic(err) }
global_g.pcs_declarations.write(g.pcs_declarations) or { panic(err) }
global_g.hotcode_definitions.write(g.hotcode_definitions) or { panic(err) }
global_g.embedded_data.write(g.embedded_data) or { panic(err) }
global_g.shared_types.write(g.shared_types) or { panic(err) }
global_g.shared_functions.write(g.channel_definitions) or { panic(err) }
// merge maps
for k, v in g.shareds {
global_g.shareds[k] = v
}
for k, v in g.chan_pop_optionals {
global_g.chan_pop_optionals[k] = v
}
for k, v in g.chan_push_optionals {
global_g.chan_push_optionals[k] = v
}
for k, v in g.optionals {
global_g.optionals[k] = v
}
for k, v in g.as_cast_type_names {
global_g.as_cast_type_names[k] = v
}
for k, v in g.sumtype_definitions {
global_g.sumtype_definitions[k] = v
}
global_g.json_forward_decls.write(g.json_forward_decls) or { panic(err) }
global_g.enum_typedefs.write(g.enum_typedefs) or { panic(err) }
global_g.channel_definitions.write(g.channel_definitions) or { panic(err) }
global_g.sql_buf.write(g.sql_buf) or { panic(err) }
global_g.cleanups[g.file.mod.name].write(g.cleanup) or { panic(err) } // strings.Builder.write never fails; it is like that in the source
global_g.inits[g.file.mod.name].write(g.init) or { panic(err) }
global_g.global_inits[g.file.mod.name].write(g.global_init) or { panic(err) }
for str_type in g.str_types {
global_g.str_types << str_type
}
for scf in g.sumtype_casting_fns {
if scf !in global_g.sumtype_casting_fns {
global_g.sumtype_casting_fns << scf
}
}
global_g.nr_closures += g.nr_closures
global_g.has_main = global_g.has_main || g.has_main
global_g.threaded_fns << g.threaded_fns
global_g.waiter_fns << g.waiter_fns
global_g.auto_fn_definitions << g.auto_fn_definitions
global_g.anon_fn_definitions << g.anon_fn_definitions
global_g.needed_equality_fns << g.needed_equality_fns // duplicates are resolved later in gen_equality_fns
global_g.array_contains_types << g.array_contains_types
global_g.array_index_types << g.array_index_types
global_g.pcs << g.pcs
global_g.json_types << g.json_types
global_g.hotcode_fn_names << g.hotcode_fn_names
}
g.timers.show('cgen_file $file.path')
} else {
for file in files {
global_g.file = file
global_g.gen()
global_g.inits[file.mod.name].write(global_g.init) or { panic(err) }
global_g.init = strings.new_builder(100)
global_g.cleanups[file.mod.name].write(global_g.cleanup) or { panic(err) }
global_g.cleanup = strings.new_builder(100)
global_g.global_inits[file.mod.name].write(global_g.global_init) or { panic(err) }
global_g.global_init = strings.new_builder(100)
}
global_g.timers.start('cgen unification')
}
global_g.gen_jsons()
global_g.write_optionals()
global_g.dump_expr_definitions() // this uses global_g.get_str_fn, so it has to go before the below for loop
for i := 0; i < global_g.str_types.len; i++ {
global_g.final_gen_str(global_g.str_types[i])
}
for sumtype_casting_fn in global_g.sumtype_casting_fns {
global_g.write_sumtype_casting_fn(sumtype_casting_fn)
}
global_g.write_shareds()
global_g.write_chan_pop_optional_fns()
global_g.write_chan_push_optional_fns()
global_g.gen_array_contains_methods()
global_g.gen_array_index_methods()
global_g.gen_equality_fns()
global_g.timers.show('cgen unification')
mut g := global_g
g.timers.start('cgen common')
if autofree_used {
g.is_autofree = true // so that void _vcleanup is generated
}
// to make sure type idx's are the same in cached mods
if g.pref.build_mode == .build_module {
for idx, typ in g.table.type_symbols {
@ -299,7 +427,6 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
}
}
//
g.dump_expr_definitions()
// v files are finished, what remains is pure C code
g.gen_vlines_reset()
if g.pref.build_mode != .build_module {
@ -354,10 +481,6 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
b.writeln('\n// V embedded data:')
b.write_string(g.embedded_data.str())
}
if g.options_typedefs.len > 0 {
b.writeln('\n// V option typedefs:')
b.write_string(g.options_typedefs.str())
}
if g.shared_functions.len > 0 {
b.writeln('\n// V shared type functions:')
b.write_string(g.shared_functions.str())
@ -398,6 +521,22 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
return b.str()
}
pub fn (mut g Gen) gen() {
g.timers.start('cgen_file $g.file.path')
if g.pref.is_vlines {
g.vlines_path = util.vlines_escape_path(g.file.path, g.pref.ccompiler)
}
g.stmts(g.file.stmts)
// Transfer embedded files
for path in g.file.embedded_files {
if path !in g.embedded_files {
g.embedded_files << path
}
}
g.timers.show('cgen_file $g.file.path')
}
pub fn (g &Gen) hashes() string {
mut res := c_commit_hash_default.replace('@@@', version.vhash())
res += c_current_commit_hash_default.replace('@@@', version.githash(g.pref.building_v))
@ -508,6 +647,13 @@ pub fn (mut g Gen) init() {
}
}
}
// we know that this is being called before the multi-threading starts
// and this is being called in the main thread, so we can mutate the table
mut muttable := unsafe { &ast.Table(g.table) }
muttable.used_fns['v_segmentation_fault_handler'] = true
muttable.used_fns['eprintln'] = true
muttable.used_fns['print_backtrace'] = true
muttable.used_fns['exit'] = true
}
pub fn (mut g Gen) finish() {
@ -610,20 +756,16 @@ pub fn (mut g Gen) write_typeof_functions() {
// V type to C typecc
fn (mut g Gen) typ(t ast.Type) string {
styp := g.base_type(t)
if t.has_flag(.optional) {
// Register an optional if it's not registered yet
return g.register_optional(t)
} else {
return g.base_type(t)
}
/*
if styp.starts_with('C__') {
return styp[3..]
}
*/
return styp
}
fn (mut g Gen) base_type(t ast.Type) string {
fn (mut g Gen) base_type(_t ast.Type) string {
t := g.unwrap_generic(_t)
if g.pref.nofloat {
// todo compile time if for perf?
if t == ast.f32_type {
@ -692,7 +834,7 @@ fn (mut g Gen) optional_type_name(t ast.Type) (string, string) {
return styp, base
}
fn (g &Gen) optional_type_text(styp string, base string) string {
fn (g Gen) optional_type_text(styp string, base string) string {
// replace void with something else
size := if base == 'void' { 'byte' } else { base }
ret := 'struct $styp {
@ -705,32 +847,44 @@ fn (g &Gen) optional_type_text(styp string, base string) string {
fn (mut g Gen) register_optional(t ast.Type) string {
styp, base := g.optional_type_name(t)
if styp !in g.optionals {
g.typedefs2.writeln('typedef struct $styp $styp;')
g.options.write_string(g.optional_type_text(styp, base))
g.options.writeln(';\n')
g.optionals << styp.clone()
}
g.optionals[base] = styp
return styp
}
fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string {
sh_typ := '__shared__$base'
t_idx := t.idx()
if t_idx in g.shareds {
return sh_typ
fn (mut g Gen) write_optionals() {
mut done := g.done_optionals.clone()
for base, styp in g.optionals {
if base in done {
continue
}
done << base
g.typedefs2.writeln('typedef struct $styp $styp;')
g.options.write_string(g.optional_type_text(styp, base) + ';\n\n')
}
}
fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string {
g.shareds[t.idx()] = base
return '__shared__$base'
}
fn (mut g Gen) write_shareds() {
mut done_types := []int{}
for typ, base in g.shareds {
if typ in done_types {
continue
}
done_types << typ
sh_typ := '__shared__$base'
mtx_typ := 'sync__RwMutex'
g.shared_types.writeln('struct $sh_typ { $mtx_typ mtx; $base val; };')
g.shared_functions.writeln('static inline voidptr __dup${sh_typ}(voidptr src, int sz) {')
g.shared_functions.writeln('\t$sh_typ* dest = memdup(src, sz);')
g.shared_functions.writeln('\tsync__RwMutex_init(&dest->mtx);')
g.shared_functions.writeln('\treturn dest;')
g.shared_functions.writeln('}')
g.typedefs2.writeln('typedef struct $sh_typ $sh_typ;')
}
mtx_typ := 'sync__RwMutex'
g.shared_types.writeln('struct $sh_typ { $base val; $mtx_typ mtx; };')
g.shared_functions.writeln('static inline voidptr __dup${sh_typ}(voidptr src, int sz) {')
g.shared_functions.writeln('\t$sh_typ* dest = memdup(src, sz);')
g.shared_functions.writeln('\tsync__RwMutex_init(&dest->mtx);')
g.shared_functions.writeln('\treturn dest;')
g.shared_functions.writeln('}')
g.typedefs2.writeln('typedef struct $sh_typ $sh_typ;')
// println('registered shared type $sh_typ')
g.shareds << t_idx
return sh_typ
}
fn (mut g Gen) register_thread_array_wait_call(eltyp string) string {
@ -765,8 +919,16 @@ $ret_typ ${fn_name}($thread_arr_typ a) {
}
fn (mut g Gen) register_chan_pop_optional_call(opt_el_type string, styp string) {
if opt_el_type !in g.chan_pop_optionals {
g.chan_pop_optionals << opt_el_type
g.chan_pop_optionals[opt_el_type] = styp
}
fn (mut g Gen) write_chan_pop_optional_fns() {
mut done := []string{}
for opt_el_type, styp in g.chan_pop_optionals {
if opt_el_type in done {
continue
}
done << opt_el_type
g.channel_definitions.writeln('
static inline $opt_el_type __Option_${styp}_popval($styp ch) {
$opt_el_type _tmp = {0};
@ -778,9 +940,17 @@ static inline $opt_el_type __Option_${styp}_popval($styp ch) {
}
}
fn (mut g Gen) register_chan_push_optional_call(el_type string, styp string) {
if styp !in g.chan_push_optionals {
g.chan_push_optionals << styp
fn (mut g Gen) register_chan_push_optional_fn(el_type string, styp string) {
g.chan_push_optionals[styp] = el_type
}
fn (mut g Gen) write_chan_push_optional_fns() {
mut done := []string{}
for styp, el_type in g.chan_push_optionals {
if styp in done {
continue
}
done << styp
g.register_optional(ast.void_type.set_flag(.optional))
g.channel_definitions.writeln('
static inline Option_void __Option_${styp}_pushval($styp ch, $el_type e) {
@ -828,7 +998,7 @@ fn (g &Gen) type_sidx(t ast.Type) string {
sym := g.table.get_type_symbol(t)
return '_v_type_idx_${sym.cname}()'
}
return '$t.idx()'
return t.idx().str()
}
//
@ -1920,18 +2090,35 @@ fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
}
}
fn (mut g Gen) write_sumtype_casting_fn(got_ ast.Type, exp_ ast.Type) {
struct SumtypeCastingFn {
fn_name string
got ast.Type
exp ast.Type
}
fn (mut g Gen) get_sumtype_casting_fn(got_ ast.Type, exp_ ast.Type) string {
got, exp := got_.idx(), exp_.idx()
i := got | (exp << 16)
got_cname, exp_cname := g.table.get_type_symbol(got).cname, g.table.get_type_symbol(exp).cname
fn_name := '${got_cname}_to_sumtype_$exp_cname'
if got == exp || g.sumtype_definitions[i] {
return
return fn_name
}
g.sumtype_definitions[i] = true
got_sym := g.table.get_type_symbol(got)
exp_sym := g.table.get_type_symbol(exp)
mut sb := strings.new_builder(128)
g.sumtype_casting_fns << SumtypeCastingFn{
fn_name: fn_name
got: got
exp: exp
}
return fn_name
}
fn (mut g Gen) write_sumtype_casting_fn(fun SumtypeCastingFn) {
got, exp := fun.got, fun.exp
got_sym, exp_sym := g.table.get_type_symbol(got), g.table.get_type_symbol(exp)
got_cname, exp_cname := got_sym.cname, exp_sym.cname
sb.writeln('static inline $exp_cname ${got_cname}_to_sumtype_${exp_cname}($got_cname* x) {')
mut sb := strings.new_builder(128)
sb.writeln('static inline $exp_cname ${fun.fn_name}($got_cname* x) {')
sb.writeln('\t$got_cname* ptr = memdup(x, sizeof($got_cname));')
for embed_hierarchy in g.table.get_embeds(got_sym) {
// last embed in the hierarchy
@ -1998,9 +2185,9 @@ fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp_is_ptr
fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) {
got_type := g.table.mktyp(got_type_raw)
exp_sym := g.table.get_type_symbol(expected_type)
got_sym := g.table.get_type_symbol(got_type)
expected_is_ptr := expected_type.is_ptr()
got_is_ptr := got_type.is_ptr()
got_sym := g.table.get_type_symbol(got_type)
// allow using the new Error struct as a string, to avoid a breaking change
// TODO: temporary to allow people to migrate their code; remove soon
if got_type == ast.error_type_idx && expected_type == ast.string_type_idx {
@ -2073,7 +2260,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
g.prevent_sum_type_unwrapping_once = true
g.expr(expr)
} else {
g.write_sumtype_casting_fn(unwrapped_got_type, unwrapped_expected_type)
g.get_sumtype_casting_fn(unwrapped_got_type, unwrapped_expected_type)
fname := '${unwrapped_got_sym.cname}_to_sumtype_$unwrapped_exp_sym.cname'
g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, unwrapped_exp_sym.cname,
got_is_ptr, got_styp)
@ -2916,7 +3103,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
}
mut cloned := false
if g.is_autofree && right_sym.kind in [.array, .string] {
if g.gen_clone_assignment(val, right_sym, false) {
if g.gen_clone_assignment(val, unwrapped_val_type, false) {
cloned = true
}
}
@ -3092,28 +3279,38 @@ fn (mut g Gen) get_ternary_name(name string) string {
return g.ternary_names[name]
}
fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym ast.TypeSymbol, add_eq bool) bool {
fn (mut g Gen) gen_clone_assignment(val ast.Expr, typ ast.Type, add_eq bool) bool {
if val !is ast.Ident && val !is ast.SelectorExpr {
return false
}
if g.is_autofree && right_sym.kind == .array {
// `arr1 = arr2` => `arr1 = arr2.clone()`
right_sym := g.table.get_type_symbol(typ)
if g.is_autofree {
if add_eq {
g.write('=')
}
g.write(' array_clone_static_to_depth(')
g.expr(val)
elem_type := (right_sym.info as ast.Array).elem_type
array_depth := g.get_array_depth(elem_type)
g.write(', $array_depth)')
} else if g.is_autofree && right_sym.kind == .string {
if add_eq {
g.write('=')
if right_sym.kind == .array {
// `arr1 = arr2` => `arr1 = arr2.clone()`
shared_styp := g.typ(typ.set_nr_muls(0))
if typ.share() == .shared_t {
g.write('($shared_styp*)__dup_shared_array(&($shared_styp){.mtx = {0}, .val =')
}
g.write(' array_clone_static_to_depth(')
g.expr(val)
if typ.share() == .shared_t {
g.write('->val')
}
elem_type := (right_sym.info as ast.Array).elem_type
array_depth := g.get_array_depth(elem_type)
g.write(', $array_depth)')
if typ.share() == .shared_t {
g.write('}, sizeof($shared_styp))')
}
} else if right_sym.kind == .string {
// `str1 = str2` => `str1 = str2.clone()`
g.write(' string_clone_static(')
g.expr(val)
g.write(')')
}
// `str1 = str2` => `str1 = str2.clone()`
g.write(' string_clone_static(')
g.expr(val)
g.write(')')
}
return true
}
@ -3203,7 +3400,7 @@ fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, start_pos int, end_pos int
// }
// ```
// if !isnil(scope.parent) && line_nr > 0 {
if free_parent_scopes && !isnil(scope.parent)
if free_parent_scopes && !isnil(scope.parent) && !scope.detached_from_parent
&& (stop_pos == -1 || scope.parent.start_pos >= stop_pos) {
g.trace_autofree('// af parent scope:')
g.autofree_scope_vars2(scope.parent, start_pos, end_pos, line_nr, true, stop_pos)
@ -3217,10 +3414,10 @@ fn (mut g Gen) autofree_variable(v ast.Var) {
// eprintln(' > var name: ${v.name:-20s} | is_arg: ${v.is_arg.str():6} | var type: ${int(v.typ):8} | type_name: ${sym.name:-33s}')
}
// }
free_fn := g.typ(v.typ.set_nr_muls(0)) + '_free'
if sym.kind == .array {
if sym.has_method('free') {
free_method_name := g.typ(v.typ) + '_free'
g.autofree_var_call(free_method_name, v)
g.autofree_var_call(free_fn, v)
return
}
g.autofree_var_call('array_free', v)
@ -3251,7 +3448,7 @@ fn (mut g Gen) autofree_variable(v ast.Var) {
return
}
if sym.has_method('free') {
g.autofree_var_call(c_name(sym.name) + '_free', v)
g.autofree_var_call(free_fn, v)
} else if g.pref.experimental && v.typ.is_ptr() && sym.name.after('.')[0].is_capital() {
// Free user reference types
g.autofree_var_call('free', v)
@ -3281,7 +3478,23 @@ fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) {
return
}
if v.typ.is_ptr() {
g.writeln('\t${free_fn_name}(${c_name(v.name)}); // autofreed ptr var')
g.write('\t')
if v.typ.share() == .shared_t {
g.write(free_fn_name.replace_each(['__shared__', '']))
} else {
g.write(free_fn_name)
}
g.write('(')
if v.typ.share() == .shared_t {
g.write('&')
}
g.write(strings.repeat(`*`, v.typ.nr_muls() - 1)) // dereference if it is a pointer to a pointer
g.write(c_name(v.name))
if v.typ.share() == .shared_t {
g.write('->val')
}
g.writeln('); // autofreed ptr var')
} else {
if v.typ == ast.error_type && !v.is_autofree_tmp {
return
@ -4183,19 +4396,19 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
}
match type_sym.kind {
.array {
ptr_typ := g.gen_array_equality_fn(node.cond_type)
ptr_typ := g.equality_fn(node.cond_type)
g.write('${ptr_typ}_arr_eq($cond_var, ')
g.expr(expr)
g.write(')')
}
.array_fixed {
ptr_typ := g.gen_fixed_array_equality_fn(node.cond_type)
ptr_typ := g.equality_fn(node.cond_type)
g.write('${ptr_typ}_arr_eq($cond_var, ')
g.expr(expr)
g.write(')')
}
.map {
ptr_typ := g.gen_map_equality_fn(node.cond_type)
ptr_typ := g.equality_fn(node.cond_type)
g.write('${ptr_typ}_map_eq($cond_var, ')
g.expr(expr)
g.write(')')
@ -4206,7 +4419,7 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
g.write(')')
}
.struct_ {
ptr_typ := g.gen_struct_equality_fn(node.cond_type)
ptr_typ := g.equality_fn(node.cond_type)
g.write('${ptr_typ}_struct_eq($cond_var, ')
g.expr(expr)
g.write(')')
@ -5236,7 +5449,7 @@ fn (mut g Gen) const_decl_precomputed(mod string, name string, ct_value ast.Comp
// `error C2099: initializer is not a constant` errors in MSVC,
// so fall back to the delayed initialisation scheme:
g.definitions.writeln('$styp $cname; // inited later')
g.inits[mod].writeln('\t$cname = _SLIT("$escaped_val");')
g.init.writeln('\t$cname = _SLIT("$escaped_val");')
if g.is_autofree {
g.cleanups[mod].writeln('\tstring_free(&$cname);')
}
@ -5269,35 +5482,33 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ
g.definitions.writeln('$styp $cname; // inited later')
if cname == '_const_os__args' {
if g.pref.os == .windows {
g.inits[mod].writeln('\t_const_os__args = os__init_os_args_wide(___argc, (byteptr*)___argv);')
g.init.writeln('\t_const_os__args = os__init_os_args_wide(___argc, (byteptr*)___argv);')
} else {
g.inits[mod].writeln('\t_const_os__args = os__init_os_args(___argc, (byte**)___argv);')
g.init.writeln('\t_const_os__args = os__init_os_args(___argc, (byte**)___argv);')
}
} else {
if unwrap_option {
g.inits[mod].writeln(g.expr_string_surround('\t$cname = *($styp*)', expr,
'.data;'))
g.init.writeln(g.expr_string_surround('\t$cname = *($styp*)', expr, '.data;'))
} else {
g.inits[mod].writeln(g.expr_string_surround('\t$cname = ', expr, ';'))
g.init.writeln(g.expr_string_surround('\t$cname = ', expr, ';'))
}
}
if g.is_autofree {
sym := g.table.get_type_symbol(typ)
if styp.starts_with('Array_') {
g.cleanups[mod].writeln('\tarray_free(&$cname);')
g.cleanup.writeln('\tarray_free(&$cname);')
} else if styp == 'string' {
g.cleanups[mod].writeln('\tstring_free(&$cname);')
g.cleanup.writeln('\tstring_free(&$cname);')
} else if sym.kind == .map {
g.cleanups[mod].writeln('\tmap_free(&$cname);')
g.cleanup.writeln('\tmap_free(&$cname);')
} else if styp == 'IError' {
g.cleanups[mod].writeln('\tIError_free(&$cname);')
g.cleanup.writeln('\tIError_free(&$cname);')
}
}
}
fn (mut g Gen) global_decl(node ast.GlobalDecl) {
mod := if g.pref.build_mode == .build_module && g.is_builtin_mod { 'static ' } else { '' }
key := node.mod // module name
for field in node.fields {
if g.pref.skip_unused {
if field.name !in g.table.used_globals {
@ -5314,7 +5525,7 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
g.definitions.writeln(' = ${g.expr_string(field.expr)}; // global')
} else {
g.definitions.writeln(';')
g.global_inits[key].writeln('\t$field.name = ${g.expr_string(field.expr)}; // global')
g.global_init.writeln('\t$field.name = ${g.expr_string(field.expr)}; // global')
}
} else {
default_initializer := g.type_default(field.typ)
@ -5323,7 +5534,7 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
} else {
g.definitions.writeln('$mod$styp $field.name; // global')
if field.name !in ['as_cast_type_indexes', 'g_memory_block'] {
g.global_inits[key].writeln('\t$field.name = *($styp*)&(($styp[]){${g.type_default(field.typ)}}[0]); // global')
g.global_init.writeln('\t$field.name = *($styp*)&(($styp[]){${g.type_default(field.typ)}}[0]); // global')
}
}
}
@ -5406,7 +5617,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
mut cloned := false
if g.is_autofree && !field.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
g.write('/*clone1*/')
if g.gen_clone_assignment(field.expr, field_type_sym, false) {
if g.gen_clone_assignment(field.expr, field.typ, false) {
cloned = true
}
}
@ -5498,7 +5709,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
mut cloned := false
if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
g.write('/*clone1*/')
if g.gen_clone_assignment(sfield.expr, field_type_sym, false) {
if g.gen_clone_assignment(sfield.expr, sfield.typ, false) {
cloned = true
}
}
@ -5664,8 +5875,11 @@ fn (mut g Gen) write_init_function() {
return
}
fn_vinit_start_pos := g.out.len
// ___argv is declared as voidptr here, because that unifies the windows/unix logic
g.writeln('void _vinit(int ___argc, voidptr ___argv) {')
g.writeln('#if __STDC_HOSTED__ == 1\n\tsignal(SIGSEGV, v_segmentation_fault_handler);\n#endif')
if g.pref.prealloc {
g.writeln('prealloc_vinit();')
}
@ -5815,17 +6029,21 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) {
// if this is the case then we are going to
// buffer manip out in front of the struct
// write the optional in and then continue
// FIXME: for parallel cgen (two different files using the same optional in struct fields)
if field.typ.has_flag(.optional) {
// Dont use g.typ() here becuase it will register
// optional and we dont want that
styp, base := g.optional_type_name(field.typ)
if styp !in g.optionals {
last_text := g.type_definitions.cut_to(start_pos).clone()
g.optionals << styp
g.typedefs2.writeln('typedef struct $styp $styp;')
g.type_definitions.writeln('${g.optional_type_text(styp,
base)};')
g.type_definitions.write_string(last_text)
lock g.done_optionals {
if base !in g.done_optionals {
g.done_optionals << base
last_text := g.type_definitions.after(start_pos).clone()
g.type_definitions.go_back_to(start_pos)
g.typedefs2.writeln('typedef struct $styp $styp;')
g.type_definitions.writeln('${g.optional_type_text(styp,
base)};')
g.type_definitions.write_string(last_text)
}
}
}
type_name := g.typ(field.typ)

View File

@ -23,14 +23,14 @@ const c_current_commit_hash_default = '
const c_concurrency_helpers = '
typedef struct __shared_map __shared_map;
struct __shared_map { map val; sync__RwMutex mtx; };
struct __shared_map { sync__RwMutex mtx; map val; };
static inline voidptr __dup_shared_map(voidptr src, int sz) {
__shared_map* dest = memdup(src, sz);
sync__RwMutex_init(&dest->mtx);
return dest;
}
typedef struct __shared_array __shared_array;
struct __shared_array { array val; sync__RwMutex mtx; };
struct __shared_array { sync__RwMutex mtx; array val; };
static inline voidptr __dup_shared_array(voidptr src, int sz) {
__shared_array* dest = memdup(src, sz);
sync__RwMutex_init(&dest->mtx);
@ -451,6 +451,7 @@ voidptr memdup(voidptr src, int sz);
#include <io.h> // _waccess
#include <direct.h> // _wgetcwd
#include <signal.h> // signal and SIGSEGV for segmentation fault handler
#ifdef _MSC_VER
// On MSVC these are the same (as long as /volatile:ms is passed)

View File

@ -14,7 +14,6 @@ pub fn (mut g Gen) gen_c_main() {
main_fn_start_pos := g.out.len
is_sokol := 'sokol' in g.table.imports
if (g.pref.os == .android && g.pref.is_apk) || (g.pref.os == .ios && is_sokol) {
g.gen_c_android_sokol_main()
} else {

View File

@ -364,7 +364,6 @@ fn (mut g Gen) comp_if_cond(cond ast.Expr, pkg_exist bool) bool {
name = '${left.expr}.$left.field_name'
exp_type = g.comptime_var_type_map[name]
} else if left is ast.TypeNode {
name = left.str()
// this is only allowed for generics currently, otherwise blocked by checker
exp_type = g.unwrap_generic(left.typ)
}

View File

@ -21,7 +21,7 @@ fn (mut g Gen) dump_expr_definitions() {
mut dump_typedefs := map[string]bool{}
mut dump_fns := strings.new_builder(100)
for dump_type, cname in g.table.dumps {
to_string_fn_name := g.gen_str_method_for_type(dump_type)
to_string_fn_name := g.get_str_fn(dump_type)
is_ptr := ast.Type(dump_type).is_ptr()
ptr_asterisk := if is_ptr { '*' } else { '' }
dump_sym := g.table.get_type_symbol(dump_type)

View File

@ -143,7 +143,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
// if g.fileis('vweb.v') {
// println('\ngen_fn_decl() $node.name $node.is_generic $g.cur_generic_type')
// }
if node.generic_names.len > 0 && g.table.cur_concrete_types.len == 0 { // need the cur_concrete_type check to avoid inf. recursion
if node.generic_names.len > 0 && g.cur_concrete_types.len == 0 { // need the cur_concrete_type check to avoid inf. recursion
// loop thru each generic type and generate a function
for concrete_types in g.table.fn_generic_types[node.name] {
if g.pref.is_verbose {
@ -151,19 +151,19 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
the_type := syms.map(it.name).join(', ')
println('gen fn `$node.name` for type `$the_type`')
}
g.table.cur_concrete_types = concrete_types
g.cur_concrete_types = concrete_types
g.gen_fn_decl(node, skip)
}
g.table.cur_concrete_types = []
g.cur_concrete_types = []
return
}
cur_fn_save := g.table.cur_fn
cur_fn_save := g.cur_fn
defer {
g.table.cur_fn = cur_fn_save
g.cur_fn = cur_fn_save
}
unsafe {
// TODO remove unsafe
g.table.cur_fn = node
g.cur_fn = node
}
fn_start_pos := g.out.len
is_closure := node.scope.has_inherited_vars()
@ -206,7 +206,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
}
mut type_name := g.typ(node.return_type)
name = g.generic_fn_name(g.table.cur_concrete_types, name, true)
name = g.generic_fn_name(g.cur_concrete_types, name, true)
if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') && !node.is_main
&& node.name != 'str' {
@ -288,7 +288,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
}
arg_str := g.out.after(arg_start_pos)
if node.no_body || ((g.pref.use_cache && g.pref.build_mode != .build_module) && node.is_builtin
&& !g.is_test) || skip {
&& !g.pref.is_test) || skip {
// Just a function header. Builtin function bodies are defined in builtin.o
g.definitions.writeln(');') // // NO BODY')
g.writeln(');')
@ -644,14 +644,16 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
g.checker_bug('CallExpr.receiver_type is 0 in method_call', node.pos)
}
mut unwrapped_rec_type := node.receiver_type
if g.table.cur_fn.generic_names.len > 0 { // in generic fn
if g.cur_fn != 0 && g.cur_fn.generic_names.len > 0 { // in generic fn
unwrapped_rec_type = g.unwrap_generic(node.receiver_type)
} else { // in non-generic fn
sym := g.table.get_type_symbol(node.receiver_type)
match sym.info {
ast.Struct, ast.Interface, ast.SumType {
generic_names := sym.info.generic_types.map(g.table.get_type_symbol(it).name)
if utyp := g.table.resolve_generic_to_concrete(node.receiver_type, generic_names,
// see comment at top of vlib/v/gen/c/utils.v
mut muttable := unsafe { &ast.Table(g.table) }
if utyp := muttable.resolve_generic_to_concrete(node.receiver_type, generic_names,
sym.info.concrete_types)
{
unwrapped_rec_type = utyp
@ -671,7 +673,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
if typ_sym.kind == .interface_ && (typ_sym.info as ast.Interface).defines_method(node.name) {
// Speaker_name_table[s._interface_idx].speak(s._object)
$if debug_interface_method_call ? {
eprintln('>>> interface typ_sym.name: $typ_sym.name | receiver_type_name: $receiver_type_name')
eprintln('>>> interface typ_sym.name: $typ_sym.name | receiver_type_name: $receiver_type_name | pos: $node.pos')
}
g.write('${c_name(receiver_type_name)}_name_table[')
g.expr(node.left)
@ -712,7 +714,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
return
}
'contains' {
g.gen_array_contains(node)
g.gen_array_contains(node.left_type, node.left, node.args[0].expr)
return
}
'index' {
@ -795,7 +797,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
if rec_type.has_flag(.shared_f) {
rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0)
}
g.gen_str_method_for_type(rec_type)
g.get_str_fn(rec_type)
} else if node.name == 'free' {
mut rec_type := node.receiver_type
if rec_type.has_flag(.shared_f) {
@ -1333,7 +1335,8 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
mut arr_info := arr_sym.info as ast.Array
if varg_type.has_flag(.generic) {
if fn_def := g.table.find_fn(node.name) {
if utyp := g.table.resolve_generic_to_concrete(arr_info.elem_type, fn_def.generic_names,
mut muttable := unsafe { &ast.Table(g.table) }
if utyp := muttable.resolve_generic_to_concrete(arr_info.elem_type, fn_def.generic_names,
node.concrete_types)
{
arr_info.elem_type = utyp

View File

@ -347,11 +347,7 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
g.expr(node.left)
}
if node.left_type.has_flag(.shared_f) {
if left_is_ptr {
g.write('->val')
} else {
g.write('.val')
}
g.write('->val')
}
g.write(', &($key_type_str[]){')
g.expr(node.index)
@ -374,10 +370,13 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
} else {
g.write('(*($elem_type_str*)map_get((map*)')
}
if !left_is_ptr {
if !left_is_ptr || node.left_type.has_flag(.shared_f) {
g.write('&')
}
g.expr(node.left)
if node.left_type.has_flag(.shared_f) {
g.write('->val')
}
g.write(', &($key_type_str[]){')
g.expr(node.index)
g.write('}, &($elem_type_str[]){ $zero }))')

View File

@ -63,7 +63,7 @@ fn (mut g Gen) infix_expr_arrow_op(node ast.InfixExpr) {
tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
if gen_or {
elem_styp := g.typ(elem_type)
g.register_chan_push_optional_call(elem_styp, styp)
g.register_chan_push_optional_fn(elem_styp, styp)
g.write('Option_void $tmp_opt = __Option_${styp}_pushval(')
} else {
g.write('__${styp}_pushval(')
@ -114,7 +114,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
&& left.sym.kind in [.array, .array_fixed, .alias, .map, .struct_, .sum_type] {
match left.sym.kind {
.alias {
ptr_typ := g.gen_alias_equality_fn(left.typ)
ptr_typ := g.equality_fn(left.typ)
if node.op == .ne {
g.write('!')
}
@ -131,7 +131,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.write(')')
}
.array {
ptr_typ := g.gen_array_equality_fn(left.unaliased.clear_flag(.shared_f))
ptr_typ := g.equality_fn(left.unaliased.clear_flag(.shared_f))
if node.op == .ne {
g.write('!')
}
@ -141,11 +141,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
}
g.expr(node.left)
if left.typ.has_flag(.shared_f) {
if left.typ.is_ptr() {
g.write('->val')
} else {
g.write('.val')
}
g.write('->val')
}
g.write(', ')
if right.typ.is_ptr() && !right.typ.has_flag(.shared_f) {
@ -153,16 +149,12 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
}
g.expr(node.right)
if right.typ.has_flag(.shared_f) {
if right.typ.is_ptr() {
g.write('->val')
} else {
g.write('.val')
}
g.write('->val')
}
g.write(')')
}
.array_fixed {
ptr_typ := g.gen_fixed_array_equality_fn(left.unaliased)
ptr_typ := g.equality_fn(left.unaliased)
if node.op == .ne {
g.write('!')
}
@ -184,7 +176,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.write(')')
}
.map {
ptr_typ := g.gen_map_equality_fn(left.unaliased)
ptr_typ := g.equality_fn(left.unaliased)
if node.op == .ne {
g.write('!')
}
@ -201,7 +193,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.write(')')
}
.struct_ {
ptr_typ := g.gen_struct_equality_fn(left.unaliased)
ptr_typ := g.equality_fn(left.unaliased)
if node.op == .ne {
g.write('!')
}
@ -218,7 +210,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.write(')')
}
.sum_type {
ptr_typ := g.gen_sumtype_equality_fn(left.unaliased)
ptr_typ := g.equality_fn(left.unaliased)
if node.op == .ne {
g.write('!')
}
@ -385,19 +377,7 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) {
return
}
}
fn_name := g.gen_array_contains_method(node.right_type)
g.write('(${fn_name}(')
if right.typ.is_ptr() && right.typ.share() != .shared_t {
g.write('*')
}
g.expr(node.right)
if right.typ.share() == .shared_t {
g.write('->val')
}
g.write(', ')
g.expr(node.left)
g.write('))')
return
g.gen_array_contains(node.right_type, node.right, node.left)
} else if right.unaliased_sym.kind == .map {
g.write('_IN_MAP(')
if !left.typ.is_ptr() {
@ -437,7 +417,7 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) {
if is_str {
g.write('string__eq(')
} else if is_array {
ptr_typ := g.gen_array_equality_fn(right.elem_type)
ptr_typ := g.equality_fn(right.elem_type)
g.write('${ptr_typ}_arr_eq(')
}
g.expr(left)
@ -580,10 +560,16 @@ fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) {
elem_type_str := g.typ(array_info.elem_type)
elem_sym := g.table.get_type_symbol(array_info.elem_type)
g.write('array_push${noscan}((array*)')
if !left.typ.is_ptr() {
if node.left_type.has_flag(.shared_f) && !node.left_type.deref().is_ptr() {
}
if !left.typ.is_ptr()
|| (node.left_type.has_flag(.shared_f) && !node.left_type.deref().is_ptr()) {
g.write('&')
}
g.expr(node.left)
if node.left_type.has_flag(.shared_f) {
g.write('->val')
}
if elem_sym.kind == .function {
g.write(', _MOV((voidptr[]){ ')
} else {

View File

@ -19,29 +19,34 @@ import strings
// }
// Codegen json_decode/encode funcs
fn (mut g Gen) gen_json_for_type(typ ast.Type) {
utyp := g.unwrap_generic(typ)
mut dec := strings.new_builder(100)
mut enc := strings.new_builder(100)
utyp := g.unwrap_generic(typ).set_nr_muls(0)
sym := g.table.get_type_symbol(utyp)
styp := g.typ(utyp)
g.register_optional(utyp)
if is_js_prim(sym.name) || sym.kind == .enum_ {
return
}
if sym.kind == .array {
// return
}
if sym.name in g.json_types {
return
}
g.json_types << sym.name
// println('gen_json_for_type($sym.name)')
// decode_TYPE funcs receive an actual cJSON* object to decode
// cJSON_Parse(str) call is added by the compiler
// Code gen decoder
dec_fn_name := js_dec_name(styp)
dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)'
dec.writeln('
g.json_types << utyp
}
fn (mut g Gen) gen_jsons() {
mut done := []ast.Type{}
for i := 0; i < g.json_types.len; i++ {
utyp := g.json_types[i]
if utyp in done {
continue
}
done << utyp
mut dec := strings.new_builder(100)
mut enc := strings.new_builder(100)
sym := g.table.get_type_symbol(utyp)
styp := g.typ(utyp)
g.register_optional(utyp)
// println('gen_json_for_type($sym.name)')
// decode_TYPE funcs receive an actual cJSON* object to decode
// cJSON_Parse(str) call is added by the compiler
// Code gen decoder
dec_fn_name := js_dec_name(styp)
dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)'
dec.writeln('
$dec_fn_dec {
$styp res;
if (!root) {
@ -53,69 +58,70 @@ $dec_fn_dec {
}
}
')
g.json_forward_decls.writeln('$dec_fn_dec;')
// Code gen encoder
// encode_TYPE funcs receive an object to encode
enc_fn_name := js_enc_name(styp)
enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)'
g.json_forward_decls.writeln('$enc_fn_dec;\n')
enc.writeln('
g.json_forward_decls.writeln('$dec_fn_dec;')
// Code gen encoder
// encode_TYPE funcs receive an object to encode
enc_fn_name := js_enc_name(styp)
enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)'
g.json_forward_decls.writeln('$enc_fn_dec;\n')
enc.writeln('
$enc_fn_dec {
\tcJSON *o;')
if sym.kind == .array {
// Handle arrays
value_type := g.table.value_type(utyp)
// If we have `[]Profile`, have to register a Profile en(de)coder first
g.gen_json_for_type(value_type)
dec.writeln(g.decode_array(value_type))
enc.writeln(g.encode_array(value_type))
// enc += g.encode_array(t)
} else if sym.kind == .map {
// Handle maps
m := sym.info as ast.Map
g.gen_json_for_type(m.key_type)
g.gen_json_for_type(m.value_type)
dec.writeln(g.decode_map(m.key_type, m.value_type))
enc.writeln(g.encode_map(m.key_type, m.value_type))
} else if sym.kind == .alias {
a := sym.info as ast.Alias
parent_typ := a.parent_type
psym := g.table.get_type_symbol(parent_typ)
if is_js_prim(g.typ(parent_typ)) {
g.gen_json_for_type(parent_typ)
return
}
enc.writeln('\to = cJSON_CreateObject();')
if psym.info is ast.Struct {
g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec)
} else if psym.kind == .sum_type {
verror('json: $sym.name aliased sumtypes does not work at the moment')
if sym.kind == .array {
// Handle arrays
value_type := g.table.value_type(utyp)
// If we have `[]Profile`, have to register a Profile en(de)coder first
g.gen_json_for_type(value_type)
dec.writeln(g.decode_array(value_type))
enc.writeln(g.encode_array(value_type))
// enc += g.encode_array(t)
} else if sym.kind == .map {
// Handle maps
m := sym.info as ast.Map
g.gen_json_for_type(m.key_type)
g.gen_json_for_type(m.value_type)
dec.writeln(g.decode_map(m.key_type, m.value_type))
enc.writeln(g.encode_map(m.key_type, m.value_type))
} else if sym.kind == .alias {
a := sym.info as ast.Alias
parent_typ := a.parent_type
psym := g.table.get_type_symbol(parent_typ)
if is_js_prim(g.typ(parent_typ)) {
g.gen_json_for_type(parent_typ)
continue
}
enc.writeln('\to = cJSON_CreateObject();')
if psym.info is ast.Struct {
g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec)
} else if psym.kind == .sum_type {
verror('json: $sym.name aliased sumtypes does not work at the moment')
} else {
verror('json: $sym.name is not struct')
}
} else if sym.kind == .sum_type {
enc.writeln('\to = cJSON_CreateObject();')
// Sumtypes. Range through variants of sumtype
if sym.info !is ast.SumType {
verror('json: $sym.name is not a sumtype')
}
g.gen_sumtype_enc_dec(sym, mut enc, mut dec)
} else {
verror('json: $sym.name is not struct')
enc.writeln('\to = cJSON_CreateObject();')
// Structs. Range through fields
if sym.info !is ast.Struct {
verror('json: $sym.name is not struct')
}
g.gen_struct_enc_dec(sym.info, styp, mut enc, mut dec)
}
} else if sym.kind == .sum_type {
enc.writeln('\to = cJSON_CreateObject();')
// Sumtypes. Range through variants of sumtype
if sym.info !is ast.SumType {
verror('json: $sym.name is not a sumtype')
}
g.gen_sumtype_enc_dec(sym, mut enc, mut dec)
} else {
enc.writeln('\to = cJSON_CreateObject();')
// Structs. Range through fields
if sym.info !is ast.Struct {
verror('json: $sym.name is not struct')
}
g.gen_struct_enc_dec(sym.info, styp, mut enc, mut dec)
// cJSON_delete
// p.cgen.fns << '$dec return opt_ok(res); \n}'
dec.writeln('\tOption_$styp ret;')
dec.writeln('\topt_ok(&res, (Option*)&ret, sizeof(res));')
dec.writeln('\treturn ret;\n}')
enc.writeln('\treturn o;\n}')
g.definitions.writeln(dec.str())
g.gowrappers.writeln(enc.str())
}
// cJSON_delete
// p.cgen.fns << '$dec return opt_ok(res); \n}'
dec.writeln('\tOption_$styp ret;')
dec.writeln('\topt_ok(&res, (Option*)&ret, sizeof(res));')
dec.writeln('\treturn ret;\n}')
enc.writeln('\treturn o;\n}')
g.definitions.writeln(dec.str())
g.gowrappers.writeln(enc.str())
}
[inline]
@ -150,7 +156,7 @@ fn (mut g Gen) gen_sumtype_enc_dec(sym ast.TypeSymbol, mut enc strings.Builder,
g.gen_json_for_type(variant)
// Helpers for decoding
g.write_sumtype_casting_fn(variant, typ)
g.get_sumtype_casting_fn(variant, typ)
g.definitions.writeln('static inline $sym.cname ${variant_typ}_to_sumtype_${sym.cname}($variant_typ* x);')
// ENCODING

View File

@ -80,7 +80,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
}
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
if typ.has_flag(.variadic) {
str_fn_name := g.gen_str_method_for_type(typ)
str_fn_name := g.get_str_fn(typ)
g.write('${str_fn_name}(')
g.expr(expr)
g.write(')')
@ -93,7 +93,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
g.write('_SLIT("<none>")')
} else if sym.kind == .enum_ {
if expr !is ast.EnumVal {
str_fn_name := g.gen_str_method_for_type(typ)
str_fn_name := g.get_str_fn(typ)
g.write('${str_fn_name}(')
g.enum_expr(expr)
g.write(')')
@ -106,7 +106,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|| sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] {
is_ptr := typ.is_ptr()
is_var_mut := expr.is_auto_deref_var()
str_fn_name := g.gen_str_method_for_type(typ)
str_fn_name := g.get_str_fn(typ)
if is_ptr && !is_var_mut {
g.write('str_intp(1, _MOV((StrIntpData[]){{_SLIT("&"), $si_s_code ,{.d_s=')
}
@ -132,7 +132,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
// g.write(')')
}
} else {
str_fn_name := g.gen_str_method_for_type(typ)
str_fn_name := g.get_str_fn(typ)
g.write('${str_fn_name}(')
if expr.is_auto_deref_var() {
g.write('*')

View File

@ -7,8 +7,20 @@ import v.ast
fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
if typ.has_flag(.generic) {
if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names,
g.table.cur_concrete_types)
/*
resolve_generic_to_concrete should not mutate the table.
It mutates if the generic type is for example []T and the
concrete type is an array type that has not been registered
yet. This should have already happened in the checker, since
it also calls resolve_generic_to_concrete. g.table is made
non-mut to make sure no one else can accidentally mutates the table.
*/
mut muttable := unsafe { &ast.Table(g.table) }
if t_typ := muttable.resolve_generic_to_concrete(typ, if g.cur_fn != 0 {
g.cur_fn.generic_names
} else {
[]string{}
}, g.cur_concrete_types)
{
return t_typ
}

View File

@ -129,7 +129,6 @@ fn (mut g JsGen) comp_if_cond(cond ast.Expr, pkg_exist bool) bool {
name = '${left.expr}.$left.field_name'
exp_type = g.comptime_var_type_map[name]
} else if left is ast.TypeNode {
name = left.str()
// this is only allowed for generics currently, otherwise blocked by checker
exp_type = g.unwrap_generic(left.typ)
}

View File

@ -346,7 +346,7 @@ pub fn parse_files(paths []string, table &ast.Table, pref &pref.Preferences) []&
}
$if macos {
/*
if pref.is_parallel && paths[0].contains('/array.v') {
if !pref.no_parallel && paths[0].contains('/array.v') {
println('\n\n\nparse_files() nr_files=$paths.len')
println(paths)
nr_cpus := runtime.nr_cpus()

View File

@ -136,6 +136,10 @@ pub fn (mut p Preferences) fill_with_defaults() {
if p.bare_builtin_dir == '' {
p.bare_builtin_dir = os.join_path(p.vroot, 'vlib', 'builtin', 'linux_bare')
}
$if prealloc {
eprintln('disabling parallel cgen, since V was built with -prealloc')
p.no_parallel = true
}
}
pub const cc_to_windows = 'x86_64-w64-mingw32-gcc'

View File

@ -175,7 +175,7 @@ pub mut:
no_rsp bool // when true, pass C backend options directly on the CLI (do not use `.rsp` files for them, some older C compilers do not support them)
no_std bool // when true, do not pass -std=c99 to the C backend
use_color ColorOutput // whether the warnings/errors should use ANSI color escapes.
is_parallel bool
no_parallel bool
is_vweb bool // skip _ var warning in templates
only_check_syntax bool // when true, just parse the files, then stop, before running checker
check_only bool // same as only_check_syntax, but also runs the checker
@ -474,8 +474,8 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
res.prealloc = true
res.build_options << arg
}
'-parallel' {
res.is_parallel = true
'-no-parallel' {
res.no_parallel = true
}
'-native' {
res.backend = .native

View File

@ -85,12 +85,10 @@ fn test_comptime_for_fields() {
if field.name == 'f' {
assert sizeof(field) == 8
assert isreftype(field) == false
// assert typeof(field) == 'u64'
assert typeof(field).name == 'u64'
fields_found++
}
if field.name == 'g' {
// assert typeof(field) == 'string'
assert typeof(field).name == 'string'
assert isreftype(field) == true
fields_found++

View File

@ -7,7 +7,7 @@ fn inc_array_elem(shared b []int, i int) {
}
fn test_autolock_array() {
shared a := &[1, 2, 7, 5]
shared a := [1, 2, 7, 5]
t := go inc_array_elem(shared a, 2)
for _ in 0 .. iterations {
a[2]++
@ -23,7 +23,7 @@ fn inc_map_elem(shared b map[string]int, k string) {
}
fn test_autolock_map() {
shared m := &{
shared m := {
'xy': 1
'qwe': 2
'asd': 7