1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

cgen: parallelize (#10844)

This commit is contained in:
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 - name: v.c can be compiled and run with -os cross
run: | run: |
./v -os cross -o /tmp/v.c cmd/v ./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 ls -lart v_from_vc
./v_from_vc version ./v_from_vc version
@ -235,7 +235,7 @@ jobs:
- name: g++ version - name: g++ version
run: g++-9 --version run: g++-9 --version
- name: V self compilation with g++ - 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++ ## - name: Running tests with g++
## run: ./v -cc g++-9 -silent test-self ## run: ./v -cc g++-9 -silent test-self
@ -323,7 +323,7 @@ jobs:
- name: v.c can be compiled and run with -os cross - name: v.c can be compiled and run with -os cross
run: | run: |
./v -os cross -o /tmp/v.c cmd/v ./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 ls -lart v_from_vc
./v_from_vc version ./v_from_vc version
@ -627,7 +627,7 @@ jobs:
gcc --version gcc --version
.\make.bat -gcc .\make.bat -gcc
- name: Test new v.c - 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 - name: Install dependencies
run: | run: |
.\v.exe setup-freetype .\v.exe setup-freetype
@ -715,7 +715,7 @@ jobs:
run: | run: |
.\make.bat -tcc --verbose .\make.bat -tcc --verbose
- name: Test new v.c - 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 - name: Install dependencies
run: | run: |
.\v.exe setup-freetype .\v.exe setup-freetype
@ -764,7 +764,7 @@ jobs:
.\v.exe wipe-cache .\v.exe wipe-cache
.\make.bat -tcc32 --verbose .\make.bat -tcc32 --verbose
- name: Test new v.c - 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 - name: v doctor
run: ./v doctor run: ./v doctor

View File

@ -64,14 +64,14 @@ jobs:
- name: v.c can be compiled and run with -os cross - name: v.c can be compiled and run with -os cross
run: | run: |
./v -os cross -o /tmp/v.c cmd/v ./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 ls -lart v_from_vc
./v_from_vc version ./v_from_vc version
- name: v_win.c can be compiled and run with -os windows - name: v_win.c can be compiled and run with -os windows
run: | run: |
./v -os windows -o /tmp/v_win.c cmd/v ./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 ls -lart v_from_vc.exe
wine v_from_vc.exe version wine v_from_vc.exe version

2
.gitignore vendored
View File

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

View File

@ -4,7 +4,7 @@ LABEL maintainer="Vitaly Takmazov <vitalyster@gmail.com>"
COPY . . COPY . .
RUN make RUN make
RUN ./v -os windows -o v.c cmd/v 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 RUN file v.exe
CMD [ "bash" ] CMD [ "bash" ]

View File

@ -3,6 +3,6 @@ CC ?= cc
all: all:
rm -rf vc/ rm -rf vc/
git clone --depth 1 --quiet https://github.com/vlang/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/ rm -rf vc/
@echo "V has been successfully built" @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 needs to be located in `v/cmd/tools/fast`')
} }
println('fast.html generator\n') println('fast.html generator\n')
println('Fetching updates...') if !os.args.contains('-noupdate') {
ret := os.system('$vdir/v up') println('Fetching updates...')
if ret != 0 { ret := os.system('$vdir/v up')
println('failed to update V') if ret != 0 {
return println('failed to update V')
return
}
} }
// Fetch the last commit's hash // Fetch the last commit's hash
commit := exec('git rev-parse HEAD')[..8] commit := exec('git rev-parse HEAD')[..8]
@ -55,7 +57,6 @@ fn main() {
} else { } else {
exec('./v -o vprod -prod -prealloc cmd/v') exec('./v -o vprod -prod -prealloc cmd/v')
} }
// println('cur vdir="$vdir"')
// cache vlib modules // cache vlib modules
exec('$vdir/v wipe-cache') exec('$vdir/v wipe-cache')
exec('$vdir/v -o v2 -prod cmd/v') 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_building_v_from_c_source := ''
mut command_for_selfbuilding := '' mut command_for_selfbuilding := ''
if 'windows' == os.user_os() { 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}' command_for_selfbuilding = './cv.exe -o $vgit_context.vexename {SOURCE}'
} else { } 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}' command_for_selfbuilding = './cv -o $vgit_context.vexename {SOURCE}'
} }
scripting.chdir(vgit_context.workdir) scripting.chdir(vgit_context.workdir)

View File

@ -8,6 +8,7 @@ import v.parser
import v.ast import v.ast
import v.pref import v.pref
import v.errors import v.errors
import strings
struct Context { struct Context {
mut: mut:
@ -208,7 +209,7 @@ fn (t Tree) type_node(typ ast.Type) &Node {
return create_null() return create_null()
} else { } else {
type_name := t.table.get_type_name(typ) 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 { $if macos || linux {
res << Command{ 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...' label: 'v.c should be buildable with no warnings...'
okmsg: 'v.c can be compiled without warnings. This is good :)' okmsg: 'v.c can be compiled without warnings. This is good :)'
rmfile: 'v.c' 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. The checker will abort prematurely once this limit has been reached.
Setting this to 0 or a negative value, will disable the limit. 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 -profile-no-inline
Skip [inline] functions when profiling. Skip [inline] functions when profiling.

View File

@ -175,10 +175,10 @@ REM By default, use tcc, since we have it prebuilt:
:tcc32_strap :tcc32_strap
echo ^> Attempting to build v_win.c with TCC echo ^> Attempting to build v_win.c with TCC
if !flag_verbose! EQU 1 ( if !flag_verbose! EQU 1 (
echo [Debug] "!tcc_exe!" -ladvapi32 -bt10 -w -o v.exe vc\v_win.c>>"!log_file!" echo [Debug] "!tcc_exe!" -ladvapi32 -I .\thirdparty\stdatomic\win -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 "!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 if %ERRORLEVEL% NEQ 0 goto :compile_error
echo ^> Compiling with .\v.exe self echo ^> Compiling with .\v.exe self
@ -202,8 +202,8 @@ if %ERRORLEVEL% NEQ 0 (
echo ^> Attempting to build v_win.c with Clang echo ^> Attempting to build v_win.c with Clang
if !flag_verbose! EQU 1 ( if !flag_verbose! EQU 1 (
echo [Debug] clang -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_File!" 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 -w -o v.exe .\vc\v_win.c 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!" clang -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_file!"
if %ERRORLEVEL% NEQ 0 ( if %ERRORLEVEL% NEQ 0 (
@ -231,10 +231,10 @@ if %ERRORLEVEL% NEQ 0 (
echo ^> Attempting to build v_win.c with GCC echo ^> Attempting to build v_win.c with GCC
if !flag_verbose! EQU 1 ( if !flag_verbose! EQU 1 (
echo [Debug] gcc -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_File!" 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 -w -o v.exe .\vc\v_win.c 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 ( if %ERRORLEVEL% NEQ 0 (
REM In most cases, compile errors happen because the version of GCC installed is too old REM In most cases, compile errors happen because the version of GCC installed is too old
gcc --version>>"!log_File!" gcc --version>>"!log_File!"
@ -279,8 +279,8 @@ set ObjFile=.v.c.obj
echo ^> Attempting to build v_win.c with MSVC echo ^> Attempting to build v_win.c with MSVC
if !flag_verbose! EQU 1 ( 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 [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% /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no 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!" 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 ( 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 // Since V might be confused with "generic" C functions either we provide special versions
// for gcc/clang, too // for gcc/clang, too
static inline unsigned long long atomic_load_u64(unsigned long long* x) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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 #endif

View File

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

99
thirdparty/stdatomic/nix/cpp/gen.v vendored Normal file
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 arg string
kind AttributeKind 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 } subscr[i].prev = unsafe { &ch.write_subscriber }
unsafe { unsafe {
subscr[i].nxt = C.atomic_exchange_ptr(&voidptr(&ch.write_subscriber), subscr[i].nxt = &Subscription(C.atomic_exchange_ptr(&voidptr(&ch.write_subscriber),
&subscr[i]) &subscr[i]))
} }
if voidptr(subscr[i].nxt) != voidptr(0) { if voidptr(subscr[i].nxt) != voidptr(0) {
subscr[i].nxt.prev = unsafe { &subscr[i].nxt } 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 } subscr[i].prev = unsafe { &ch.read_subscriber }
unsafe { 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) { if voidptr(subscr[i].nxt) != voidptr(0) {
subscr[i].nxt.prev = unsafe { &subscr[i].nxt } 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_timedwait(voidptr, voidptr) int
fn C.sem_destroy(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. // [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
[heap] [heap]
pub struct Mutex { 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() { if typ.idx() == inter_typ.idx() {
// same type -> already casted to the interface // same type -> already casted to the interface
return true return true

View File

@ -556,7 +556,7 @@ pub enum Kind {
thread thread
} }
pub fn (t &TypeSymbol) str() string { pub fn (t TypeSymbol) str() string {
return t.name 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() { pub fn (mut t Table) register_builtin_type_symbols() {
// reserve index 0 so nothing can go there // reserve index 0 so nothing can go there
// save index check, 0 will mean not found // 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.setup_ccompiler_options(ccompiler)
v.build_thirdparty_obj_files() v.build_thirdparty_obj_files()
v.setup_output_name() 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 mut libs := []string{} // builtin.o os.o http.o etc
if v.pref.build_mode == .build_module { 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) pos = expr.left.position().extend(expr.pos)
c.fail_if_unreadable(expr.left, expr.left_type, what) c.fail_if_unreadable(expr.left, expr.left_type, what)
} }
else {} else {
pos = expr.position()
}
} }
if typ.has_flag(.shared_f) { 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', 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 idx := vals.len - 1
mname, tprefix := f.get_modname_prefix(vals[..idx].join('.')) mname, tprefix := f.get_modname_prefix(vals[..idx].join('.'))
symname := vals[vals.len - 1] 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 == '' { if aname == '' {
return symname 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` // the only argument can only be an infix expression like `a < b` or `b.field > a.field`
if node.args.len == 0 { if node.args.len == 0 {
comparison_type = g.unwrap(info.elem_type.set_nr_muls(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) g.gen_array_sort_call(node, compare_fn)
return return
} }
@ -273,7 +275,9 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) {
if is_reverse { if is_reverse {
compare_fn += '_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) g.gen_array_sort_call(node, compare_fn)
return return
} }
@ -300,8 +304,9 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) {
// Register a new custom `compare_xxx` function for qsort() // Register a new custom `compare_xxx` function for qsort()
// TODO: move to checker // TODO: move to checker
g.table.register_fn(name: compare_fn, return_type: ast.int_type) lock g.array_sort_fn {
g.array_sort_fn[compare_fn] = true g.array_sort_fn << compare_fn
}
stype_arg := g.typ(info.elem_type) stype_arg := g.typ(info.elem_type)
g.definitions.writeln('int ${compare_fn}($stype_arg* a, $stype_arg* b) {') 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 { fn (mut g Gen) get_array_contains_method(typ ast.Type) string {
mut unwrap_left_type := g.unwrap_generic(left_type) t := g.table.get_final_type_symbol(g.unwrap_generic(typ).set_nr_muls(0)).idx
if unwrap_left_type.share() == .shared_t { g.array_contains_types << t
unwrap_left_type = unwrap_left_type.clear_flag(.shared_f) return g.typ(t) + '_contains'
} }
mut left_sym := g.table.get_type_symbol(unwrap_left_type)
left_final_sym := g.table.get_final_type_symbol(unwrap_left_type) fn (mut g Gen) gen_array_contains_methods() {
mut left_type_str := g.typ(unwrap_left_type).replace('*', '') mut done := []ast.Type{}
fn_name := '${left_type_str}_contains' for t in g.array_contains_types {
if !left_sym.has_method('contains') { 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 left_info := left_final_sym.info as ast.Array
mut elem_type_str := g.typ(left_info.elem_type) mut elem_type_str := g.typ(left_info.elem_type)
elem_sym := g.table.get_type_symbol(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 { if elem_sym.kind == .string {
fn_builder.writeln('\t\tif (fast_string_eq(((string*)a.data)[i], v)) {') 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 { } 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)) {') fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq((($elem_type_str*)a.data)[i], v)) {')
} else if elem_sym.kind == .function { } else if elem_sym.kind == .function {
fn_builder.writeln('\t\tif (((voidptr*)a.data)[i] == v) {') fn_builder.writeln('\t\tif (((voidptr*)a.data)[i] == v) {')
} else if elem_sym.kind == .map && left_info.elem_type.nr_muls() == 0 { } 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)) {') 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 { } 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)) {') fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq((($elem_type_str*)a.data)[i], v)) {')
} else { } else {
fn_builder.writeln('\t\tif ((($elem_type_str*)a.data)[i] == v) {') 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('\treturn false;')
fn_builder.writeln('}') fn_builder.writeln('}')
g.auto_fn_definitions << fn_builder.str() 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)` // `nums.contains(2)`
fn (mut g Gen) gen_array_contains(node ast.CallExpr) { fn (mut g Gen) gen_array_contains(typ ast.Type, left ast.Expr, right ast.Expr) {
fn_name := g.gen_array_contains_method(node.left_type) fn_name := g.get_array_contains_method(typ)
g.write('${fn_name}(') g.write('${fn_name}(')
if node.left_type.is_ptr() && node.left_type.share() != .shared_t { g.write(strings.repeat(`*`, typ.nr_muls()))
g.write('*') if typ.share() == .shared_t {
g.out.go_back(1)
} }
g.expr(node.left) g.expr(left)
if node.left_type.share() == .shared_t { if typ.share() == .shared_t {
g.write('->val') g.write('->val')
} }
g.write(', ') g.write(', ')
g.expr(node.args[0].expr) g.expr(right)
g.write(')') g.write(')')
} }
fn (mut g Gen) gen_array_index_method(left_type ast.Type) string { fn (mut g Gen) get_array_index_method(typ ast.Type) string {
unwrap_left_type := g.unwrap_generic(left_type) t := g.unwrap_generic(typ).set_nr_muls(0)
mut left_sym := g.table.get_type_symbol(unwrap_left_type) g.array_index_types << t
mut left_type_str := g.typ(unwrap_left_type).trim('*') return g.typ(t) + '_index'
fn_name := '${left_type_str}_index' }
if !left_sym.has_method('index') {
info := left_sym.info as ast.Array 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) mut elem_type_str := g.typ(info.elem_type)
elem_sym := g.table.get_type_symbol(info.elem_type) elem_sym := g.table.get_type_symbol(info.elem_type)
if elem_sym.kind == .function { 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 { if elem_sym.kind == .string {
fn_builder.writeln('\t\tif (fast_string_eq(*pelem, v)) {') fn_builder.writeln('\t\tif (fast_string_eq(*pelem, v)) {')
} else if elem_sym.kind == .array && !info.elem_type.is_ptr() { } 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)) {') fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq( *pelem, v)) {')
} else if elem_sym.kind == .function && !info.elem_type.is_ptr() { } else if elem_sym.kind == .function && !info.elem_type.is_ptr() {
fn_builder.writeln('\t\tif ( pelem == v) {') fn_builder.writeln('\t\tif ( pelem == v) {')
} else if elem_sym.kind == .map && !info.elem_type.is_ptr() { } 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))) {') fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(( *pelem, v))) {')
} else if elem_sym.kind == .struct_ && !info.elem_type.is_ptr() { } 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)) {') fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq( *pelem, v)) {')
} else { } else {
fn_builder.writeln('\t\tif (*pelem == v) {') 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('\treturn -1;')
fn_builder.writeln('}') fn_builder.writeln('}')
g.auto_fn_definitions << fn_builder.str() 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)` // `nums.index(2)`
fn (mut g Gen) gen_array_index(node ast.CallExpr) { 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}(') g.write('${fn_name}(')
if node.left_type.is_ptr() { if node.left_type.is_ptr() {
g.write('*') g.write('*')

View File

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

View File

@ -5,13 +5,52 @@ module c
import strings import strings
import v.ast 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 { fn (mut g Gen) gen_sumtype_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type) left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0)) 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 return ptr_styp
} }
g.sumtype_fn_definitions << ptr_styp g.generated_eq_fns << left_type
info := left.sym.sumtype_info() info := left.sym.sumtype_info()
g.type_definitions.writeln('static bool ${ptr_styp}_sumtype_eq($ptr_styp a, $ptr_styp b); // auto') 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) left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0)) ptr_styp := g.typ(left.typ.set_nr_muls(0))
fn_name := ptr_styp.replace('struct ', '') 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 return fn_name
} }
g.struct_fn_definitions << fn_name g.generated_eq_fns << left_type
info := left.sym.struct_info() info := left.sym.struct_info()
g.type_definitions.writeln('static bool ${fn_name}_struct_eq($ptr_styp a, $ptr_styp b); // auto') 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 { fn (mut g Gen) gen_alias_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type) left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0)) 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 return ptr_styp
} }
g.alias_fn_definitions << ptr_styp g.generated_eq_fns << left_type
info := left.sym.info as ast.Alias info := left.sym.info as ast.Alias
g.type_definitions.writeln('static bool ${ptr_styp}_alias_eq($ptr_styp a, $ptr_styp b); // auto') 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 { fn (mut g Gen) gen_array_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type) left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0)) 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 return ptr_styp
} }
g.array_fn_definitions << ptr_styp g.generated_eq_fns << left_type
elem := g.unwrap(left.sym.array_info().elem_type) elem := g.unwrap(left.sym.array_info().elem_type)
ptr_elem_styp := g.typ(elem.typ) ptr_elem_styp := g.typ(elem.typ)
g.type_definitions.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b); // auto') 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 { fn (mut g Gen) gen_fixed_array_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type) left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0)) 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 return ptr_styp
} }
g.array_fn_definitions << ptr_styp g.generated_eq_fns << left_type
elem_info := left.sym.array_fixed_info() elem_info := left.sym.array_fixed_info()
elem := g.unwrap(elem_info.elem_type) elem := g.unwrap(elem_info.elem_type)
size := elem_info.size 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 { fn (mut g Gen) gen_map_equality_fn(left_type ast.Type) string {
left := g.unwrap(left_type) left := g.unwrap(left_type)
ptr_styp := g.typ(left.typ.set_nr_muls(0)) 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 return ptr_styp
} }
g.map_fn_definitions << ptr_styp g.generated_eq_fns << left_type
value := g.unwrap(left.sym.map_info().value_type) value := g.unwrap(left.sym.map_info().value_type)
ptr_value_styp := g.typ(value.typ) ptr_value_styp := g.typ(value.typ)
g.type_definitions.writeln('static bool ${ptr_styp}_map_eq($ptr_styp a, $ptr_styp b); // auto') 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('}') g.auto_str_funcs.writeln('}')
} }
fn (mut g Gen) gen_str_method_for_type(typ ast.Type) string { struct StrType {
styp := g.typ(typ).replace('*', '') styp string
mut sym := g.table.get_type_symbol(g.unwrap_generic(typ)) 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) mut str_fn_name := styp_to_str_fn_name(styp)
if mut sym.info is ast.Alias { if mut sym.info is ast.Alias {
if sym.info.is_import { 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) 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() g.str_types << StrType{
already_generated_key := '$styp:$str_fn_name' typ: unwrapped
if !sym_has_str_method && already_generated_key !in g.str_types && !typ.has_flag(.optional) { styp: styp
$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
} }
return str_fn_name 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) { fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string) {
parent_type := typ.clear_flag(.optional) parent_type := typ.clear_flag(.optional)
sym := g.table.get_type_symbol(parent_type) sym := g.table.get_type_symbol(parent_type)
sym_has_str_method, _, _ := sym.str_method_info() 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.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); }') 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) { 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('__', '.')) 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.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); }') 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) sym := g.table.get_type_symbol(typ)
is_arg_ptr := typ.is_ptr() is_arg_ptr := typ.is_ptr()
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() 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 { 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));') 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 */') fn_builder.writeln('static string indent_${str_fn_name}($styp x, int indent_count) { /* gen_str_for_interface */')
for typ in info.types { for typ in info.types {
subtype := g.table.get_type_symbol(typ) 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() sym_has_str_method, str_method_expects_ptr, _ := subtype.str_method_info()
if should_use_indent_func(subtype.kind) && !sym_has_str_method { if should_use_indent_func(subtype.kind) && !sym_has_str_method {
func_name = 'indent_$func_name' 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) {') fn_builder.writeln('\tswitch(x._typ) {')
for typ in info.variants { for typ in info.variants {
typ_str := g.typ(typ) 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 := g.table.get_type_symbol(typ)
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' } 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) field_styp := g.typ(typ)
is_elem_ptr := typ.is_ptr() is_elem_ptr := typ.is_ptr()
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() 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 { if sym.kind == .byte {
elem_str_fn_name = elem_str_fn_name + '_escaped' 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() is_elem_ptr := typ.is_ptr()
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() 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.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);}') 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_styp := g.typ(key_typ)
key_str_fn_name := key_styp.replace('*', '') + '_str' key_str_fn_name := key_styp.replace('*', '') + '_str'
if !key_sym.has_method('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 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) val_styp := g.typ(val_typ)
elem_str_fn_name := val_styp.replace('*', '') + '_str' elem_str_fn_name := val_styp.replace('*', '') + '_str'
if !val_sym.has_method('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') 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_fn_name := if has_custom_str {
'${field_styp}_str' '${field_styp}_str'
} else { } 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 // 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
import v.util.version import v.util.version
import v.depgraph import v.depgraph
import sync.pool
const ( const (
// NB: some of the words in c_reserved, are not reserved in C, // 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 { struct Gen {
pref &pref.Preferences pref &pref.Preferences
module_built string field_data_type ast.Type // cache her to avoid map lookups
field_data_type ast.Type // cache her to avoid map lookups module_built string
timers_should_print bool
table &ast.Table
mut: mut:
table &ast.Table
out strings.Builder out strings.Builder
cheaders strings.Builder cheaders strings.Builder
includes strings.Builder // all C #includes required by V modules 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) 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) 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_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{}` 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(){}` cleanups map[string]strings.Builder // contents of `void _vcleanup(){}`
gowrappers strings.Builder // all go callsite wrappers gowrappers strings.Builder // all go callsite wrappers
stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined 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_types strings.Builder // shared/lock types
shared_functions strings.Builder // shared constructors shared_functions strings.Builder // shared constructors
channel_definitions strings.Builder // channel related code channel_definitions strings.Builder // channel related code
options_typedefs strings.Builder // Option typedefs
options strings.Builder // `Option_xxxx` types options strings.Builder // `Option_xxxx` types
json_forward_decls strings.Builder // json type forward decls json_forward_decls strings.Builder // json type forward decls
enum_typedefs strings.Builder // enum types enum_typedefs strings.Builder // enum types
@ -70,32 +74,33 @@ mut:
file &ast.File file &ast.File
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
last_fn_c_name string last_fn_c_name string
tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn. 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_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 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 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) 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 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_void_expr_stmt bool // ExprStmt whos result is discarded
is_arraymap_set bool // map or array set value state 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_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_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_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 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) 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 arraymap_set_pos int // map or array set value position
vlines_path string // set to the proper path for generating #line directives vlines_path string // set to the proper path for generating #line directives
optionals []string // to avoid duplicates TODO perf, use map optionals map[string]string // to avoid duplicates
chan_pop_optionals []string // types for `x := <-ch or {...}` done_optionals shared []string // to avoid duplicates
chan_push_optionals []string // types for `ch <- x or {...}` 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 cur_lock ast.LockExpr
mtxs string // array of mutexes if the `lock` has multiple variables mtxs string // array of mutexes if the `lock` has multiple variables
labeled_loops map[string]&ast.Stmt labeled_loops map[string]&ast.Stmt
inner_loop &ast.Stmt inner_loop &ast.Stmt
shareds []int // types with hidden mutex for which decl has been emitted 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_ternary int // ?: comma separated statements on a single line
inside_map_postfix bool // inside map++/-- postfix expr inside_map_postfix bool // inside map++/-- postfix expr
inside_map_infix bool // inside map<</+=/-= infix expr inside_map_infix bool // inside map<</+=/-= infix expr
inside_map_index bool inside_map_index bool
inside_opt_data bool inside_opt_data bool
inside_if_optional 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 is_autofree bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree
indent int indent int
empty_line bool empty_line bool
is_test bool
assign_op token.Kind // *=, =, etc (for array_set) assign_op token.Kind // *=, =, etc (for array_set)
defer_stmts []ast.DeferStmt defer_stmts []ast.DeferStmt
defer_ifdef string defer_ifdef string
defer_profile_code string defer_profile_code string
str_types []string // types that need automatic str() generation str_types []StrType // types that need automatic str() generation
threaded_fns []string // for generating unique wrapper types and fns for `go xxx()` generated_str_fns []StrType // types that already have a str() function
waiter_fns []string // functions that wait for `go xxx()` to finish threaded_fns []string // for generating unique wrapper types and fns for `go xxx()`
array_fn_definitions []string // array equality functions that have been defined waiter_fns []string // functions that wait for `go xxx()` to finish
map_fn_definitions []string // map equality functions that have been defined auto_fn_definitions []string // auto generated functions defination list
struct_fn_definitions []string // struct equality functions that have been defined sumtype_casting_fns []SumtypeCastingFn
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
anon_fn_definitions []string // anon generated functions defination list anon_fn_definitions []string // anon generated functions defination list
sumtype_definitions map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated sumtype_definitions map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated
is_json_fn bool // inside json.encode() is_json_fn bool // inside json.encode()
json_types []string // to avoid json gen duplicates json_types []ast.Type // to avoid json gen duplicates
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
is_builtin_mod bool is_builtin_mod bool
hotcode_fn_names []string 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) as_cast_type_names map[string]string // table for type name lookup in runtime (for __as_cast)
obf_table map[string]string obf_table map[string]string
// main_fn_decl_node ast.FnDecl // main_fn_decl_node ast.FnDecl
expected_cast_type ast.Type // for match expr of sumtypes nr_closures int
defer_vars []string array_sort_fn shared []string
anon_fn bool expected_cast_type ast.Type // for match expr of sumtypes
array_sort_fn map[string]bool defer_vars []string
nr_closures int 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 { 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 ? { $if time_cgening ? {
timers_should_print = true timers_should_print = true
} }
mut g := Gen{ mut global_g := Gen{
file: 0 file: 0
out: strings.new_builder(512000) out: strings.new_builder(512000)
cheaders: strings.new_builder(15000) 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) pcs_declarations: strings.new_builder(100)
hotcode_definitions: strings.new_builder(100) hotcode_definitions: strings.new_builder(100)
embedded_data: strings.new_builder(1000) embedded_data: strings.new_builder(1000)
options_typedefs: strings.new_builder(100)
options: strings.new_builder(100) options: strings.new_builder(100)
shared_types: strings.new_builder(100) shared_types: strings.new_builder(100)
shared_functions: 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 table: table
pref: pref pref: pref
fn_decl: 0 fn_decl: 0
is_autofree: true is_autofree: pref.autofree
indent: -1 indent: -1
module_built: module_built module_built: module_built
timers_should_print: timers_should_print
timers: util.new_timers(timers_should_print) timers: util.new_timers(timers_should_print)
inner_loop: &ast.EmptyStmt{} inner_loop: &ast.EmptyStmt{}
field_data_type: ast.Type(table.find_type_idx('FieldData')) field_data_type: ast.Type(table.find_type_idx('FieldData'))
init: strings.new_builder(100)
} }
g.timers.start('cgen init') // anon fn may include assert and thus this needs
for mod in g.table.modules { // to be included before any test contents are written
g.inits[mod] = strings.new_builder(100) if pref.is_test {
g.global_inits[mod] = strings.new_builder(100) global_g.write_tests_definitions()
g.cleanups[mod] = strings.new_builder(100)
} }
g.init()
g.timers.show('cgen init') global_g.timers.start('cgen init')
mut tests_inited := false for mod in global_g.table.modules {
mut autofree_used := false global_g.inits[mod] = strings.new_builder(200)
for file in files { global_g.global_inits[mod] = strings.new_builder(100)
g.timers.start('cgen_file $file.path') global_g.cleanups[mod] = strings.new_builder(100)
g.file = file }
if g.pref.is_vlines { global_g.init()
g.vlines_path = util.vlines_escape_path(file.path, g.pref.ccompiler) global_g.timers.show('cgen init')
} global_g.tests_inited = false
// println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len') if !pref.no_parallel {
// building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v')) mut pp := pool.new_pool_processor(
g.is_test = g.pref.is_test callback: fn (p &pool.PoolProcessor, idx int, wid int) &Gen {
if g.file.path == '' || !g.pref.autofree { file := p.get_item<&ast.File>(idx)
// cgen test or building V mut global_g := &Gen(p.get_shared_context())
// println('autofree=false') mut g := &Gen{
g.is_autofree = false file: file
} else { out: strings.new_builder(512000)
g.is_autofree = true cheaders: strings.new_builder(15000)
autofree_used = true includes: strings.new_builder(100)
} typedefs: strings.new_builder(100)
// anon fn may include assert and thus this needs typedefs2: strings.new_builder(100)
// to be included before any test contents are written type_definitions: strings.new_builder(100)
if g.is_test && !tests_inited { definitions: strings.new_builder(100)
g.write_tests_definitions() gowrappers: strings.new_builder(100)
tests_inited = true stringliterals: strings.new_builder(100)
} auto_str_funcs: strings.new_builder(100)
g.stmts(file.stmts) comptime_defines: strings.new_builder(100)
// Transfer embedded files pcs_declarations: strings.new_builder(100)
if file.embedded_files.len > 0 { hotcode_definitions: strings.new_builder(100)
for path in file.embedded_files { embedded_data: strings.new_builder(1000)
if path !in g.embedded_files { options: strings.new_builder(100)
g.embedded_files << path 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') 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 // to make sure type idx's are the same in cached mods
if g.pref.build_mode == .build_module { if g.pref.build_mode == .build_module {
for idx, typ in g.table.type_symbols { 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 // v files are finished, what remains is pure C code
g.gen_vlines_reset() g.gen_vlines_reset()
if g.pref.build_mode != .build_module { 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.writeln('\n// V embedded data:')
b.write_string(g.embedded_data.str()) 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 { if g.shared_functions.len > 0 {
b.writeln('\n// V shared type functions:') b.writeln('\n// V shared type functions:')
b.write_string(g.shared_functions.str()) 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() 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 { pub fn (g &Gen) hashes() string {
mut res := c_commit_hash_default.replace('@@@', version.vhash()) mut res := c_commit_hash_default.replace('@@@', version.vhash())
res += c_current_commit_hash_default.replace('@@@', version.githash(g.pref.building_v)) 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() { pub fn (mut g Gen) finish() {
@ -610,20 +756,16 @@ pub fn (mut g Gen) write_typeof_functions() {
// V type to C typecc // V type to C typecc
fn (mut g Gen) typ(t ast.Type) string { fn (mut g Gen) typ(t ast.Type) string {
styp := g.base_type(t)
if t.has_flag(.optional) { if t.has_flag(.optional) {
// Register an optional if it's not registered yet // Register an optional if it's not registered yet
return g.register_optional(t) 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 { if g.pref.nofloat {
// todo compile time if for perf? // todo compile time if for perf?
if t == ast.f32_type { if t == ast.f32_type {
@ -692,7 +834,7 @@ fn (mut g Gen) optional_type_name(t ast.Type) (string, string) {
return styp, base 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 // replace void with something else
size := if base == 'void' { 'byte' } else { base } size := if base == 'void' { 'byte' } else { base }
ret := 'struct $styp { 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 { fn (mut g Gen) register_optional(t ast.Type) string {
styp, base := g.optional_type_name(t) styp, base := g.optional_type_name(t)
if styp !in g.optionals { g.optionals[base] = styp
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()
}
return styp return styp
} }
fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string { fn (mut g Gen) write_optionals() {
sh_typ := '__shared__$base' mut done := g.done_optionals.clone()
t_idx := t.idx() for base, styp in g.optionals {
if t_idx in g.shareds { if base in done {
return sh_typ 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 { 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) { 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] = styp
g.chan_pop_optionals << opt_el_type }
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(' g.channel_definitions.writeln('
static inline $opt_el_type __Option_${styp}_popval($styp ch) { static inline $opt_el_type __Option_${styp}_popval($styp ch) {
$opt_el_type _tmp = {0}; $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) { fn (mut g Gen) register_chan_push_optional_fn(el_type string, styp string) {
if styp !in g.chan_push_optionals { g.chan_push_optionals[styp] = el_type
g.chan_push_optionals << styp }
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.register_optional(ast.void_type.set_flag(.optional))
g.channel_definitions.writeln(' g.channel_definitions.writeln('
static inline Option_void __Option_${styp}_pushval($styp ch, $el_type e) { 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) sym := g.table.get_type_symbol(t)
return '_v_type_idx_${sym.cname}()' 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() got, exp := got_.idx(), exp_.idx()
i := got | (exp << 16) 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] { if got == exp || g.sumtype_definitions[i] {
return return fn_name
} }
g.sumtype_definitions[i] = true g.sumtype_definitions[i] = true
got_sym := g.table.get_type_symbol(got) g.sumtype_casting_fns << SumtypeCastingFn{
exp_sym := g.table.get_type_symbol(exp) fn_name: fn_name
mut sb := strings.new_builder(128) 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 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));') sb.writeln('\t$got_cname* ptr = memdup(x, sizeof($got_cname));')
for embed_hierarchy in g.table.get_embeds(got_sym) { for embed_hierarchy in g.table.get_embeds(got_sym) {
// last embed in the hierarchy // 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) { 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) got_type := g.table.mktyp(got_type_raw)
exp_sym := g.table.get_type_symbol(expected_type) 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() expected_is_ptr := expected_type.is_ptr()
got_is_ptr := got_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 // 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 // TODO: temporary to allow people to migrate their code; remove soon
if got_type == ast.error_type_idx && expected_type == ast.string_type_idx { 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.prevent_sum_type_unwrapping_once = true
g.expr(expr) g.expr(expr)
} else { } 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' 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, g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, unwrapped_exp_sym.cname,
got_is_ptr, got_styp) got_is_ptr, got_styp)
@ -2916,7 +3103,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
} }
mut cloned := false mut cloned := false
if g.is_autofree && right_sym.kind in [.array, .string] { 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 cloned = true
} }
} }
@ -3092,28 +3279,38 @@ fn (mut g Gen) get_ternary_name(name string) string {
return g.ternary_names[name] 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 { if val !is ast.Ident && val !is ast.SelectorExpr {
return false return false
} }
if g.is_autofree && right_sym.kind == .array { right_sym := g.table.get_type_symbol(typ)
// `arr1 = arr2` => `arr1 = arr2.clone()` if g.is_autofree {
if add_eq { if add_eq {
g.write('=') g.write('=')
} }
g.write(' array_clone_static_to_depth(') if right_sym.kind == .array {
g.expr(val) // `arr1 = arr2` => `arr1 = arr2.clone()`
elem_type := (right_sym.info as ast.Array).elem_type shared_styp := g.typ(typ.set_nr_muls(0))
array_depth := g.get_array_depth(elem_type) if typ.share() == .shared_t {
g.write(', $array_depth)') g.write('($shared_styp*)__dup_shared_array(&($shared_styp){.mtx = {0}, .val =')
} else if g.is_autofree && right_sym.kind == .string { }
if add_eq { g.write(' array_clone_static_to_depth(')
g.write('=') 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 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 !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) { && (stop_pos == -1 || scope.parent.start_pos >= stop_pos) {
g.trace_autofree('// af parent scope:') g.trace_autofree('// af parent scope:')
g.autofree_scope_vars2(scope.parent, start_pos, end_pos, line_nr, true, stop_pos) 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}') // 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.kind == .array {
if sym.has_method('free') { if sym.has_method('free') {
free_method_name := g.typ(v.typ) + '_free' g.autofree_var_call(free_fn, v)
g.autofree_var_call(free_method_name, v)
return return
} }
g.autofree_var_call('array_free', v) g.autofree_var_call('array_free', v)
@ -3251,7 +3448,7 @@ fn (mut g Gen) autofree_variable(v ast.Var) {
return return
} }
if sym.has_method('free') { 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() { } else if g.pref.experimental && v.typ.is_ptr() && sym.name.after('.')[0].is_capital() {
// Free user reference types // Free user reference types
g.autofree_var_call('free', v) 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 return
} }
if v.typ.is_ptr() { 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 { } else {
if v.typ == ast.error_type && !v.is_autofree_tmp { if v.typ == ast.error_type && !v.is_autofree_tmp {
return 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 { match type_sym.kind {
.array { .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.write('${ptr_typ}_arr_eq($cond_var, ')
g.expr(expr) g.expr(expr)
g.write(')') g.write(')')
} }
.array_fixed { .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.write('${ptr_typ}_arr_eq($cond_var, ')
g.expr(expr) g.expr(expr)
g.write(')') g.write(')')
} }
.map { .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.write('${ptr_typ}_map_eq($cond_var, ')
g.expr(expr) g.expr(expr)
g.write(')') g.write(')')
@ -4206,7 +4419,7 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
g.write(')') g.write(')')
} }
.struct_ { .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.write('${ptr_typ}_struct_eq($cond_var, ')
g.expr(expr) g.expr(expr)
g.write(')') 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, // `error C2099: initializer is not a constant` errors in MSVC,
// so fall back to the delayed initialisation scheme: // so fall back to the delayed initialisation scheme:
g.definitions.writeln('$styp $cname; // inited later') 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 { if g.is_autofree {
g.cleanups[mod].writeln('\tstring_free(&$cname);') 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') g.definitions.writeln('$styp $cname; // inited later')
if cname == '_const_os__args' { if cname == '_const_os__args' {
if g.pref.os == .windows { 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 { } 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 { } else {
if unwrap_option { if unwrap_option {
g.inits[mod].writeln(g.expr_string_surround('\t$cname = *($styp*)', expr, g.init.writeln(g.expr_string_surround('\t$cname = *($styp*)', expr, '.data;'))
'.data;'))
} else { } 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 { if g.is_autofree {
sym := g.table.get_type_symbol(typ) sym := g.table.get_type_symbol(typ)
if styp.starts_with('Array_') { if styp.starts_with('Array_') {
g.cleanups[mod].writeln('\tarray_free(&$cname);') g.cleanup.writeln('\tarray_free(&$cname);')
} else if styp == 'string' { } else if styp == 'string' {
g.cleanups[mod].writeln('\tstring_free(&$cname);') g.cleanup.writeln('\tstring_free(&$cname);')
} else if sym.kind == .map { } else if sym.kind == .map {
g.cleanups[mod].writeln('\tmap_free(&$cname);') g.cleanup.writeln('\tmap_free(&$cname);')
} else if styp == 'IError' { } 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) { fn (mut g Gen) global_decl(node ast.GlobalDecl) {
mod := if g.pref.build_mode == .build_module && g.is_builtin_mod { 'static ' } else { '' } mod := if g.pref.build_mode == .build_module && g.is_builtin_mod { 'static ' } else { '' }
key := node.mod // module name
for field in node.fields { for field in node.fields {
if g.pref.skip_unused { if g.pref.skip_unused {
if field.name !in g.table.used_globals { 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') g.definitions.writeln(' = ${g.expr_string(field.expr)}; // global')
} else { } else {
g.definitions.writeln(';') 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 { } else {
default_initializer := g.type_default(field.typ) default_initializer := g.type_default(field.typ)
@ -5323,7 +5534,7 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
} else { } else {
g.definitions.writeln('$mod$styp $field.name; // global') g.definitions.writeln('$mod$styp $field.name; // global')
if field.name !in ['as_cast_type_indexes', 'g_memory_block'] { 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 mut cloned := false
if g.is_autofree && !field.typ.is_ptr() && field_type_sym.kind in [.array, .string] { if g.is_autofree && !field.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
g.write('/*clone1*/') 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 cloned = true
} }
} }
@ -5498,7 +5709,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
mut cloned := false mut cloned := false
if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] { if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
g.write('/*clone1*/') 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 cloned = true
} }
} }
@ -5664,8 +5875,11 @@ fn (mut g Gen) write_init_function() {
return return
} }
fn_vinit_start_pos := g.out.len fn_vinit_start_pos := g.out.len
// ___argv is declared as voidptr here, because that unifies the windows/unix logic // ___argv is declared as voidptr here, because that unifies the windows/unix logic
g.writeln('void _vinit(int ___argc, voidptr ___argv) {') 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 { if g.pref.prealloc {
g.writeln('prealloc_vinit();') 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 // if this is the case then we are going to
// buffer manip out in front of the struct // buffer manip out in front of the struct
// write the optional in and then continue // 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) { if field.typ.has_flag(.optional) {
// Dont use g.typ() here becuase it will register // Dont use g.typ() here becuase it will register
// optional and we dont want that // optional and we dont want that
styp, base := g.optional_type_name(field.typ) styp, base := g.optional_type_name(field.typ)
if styp !in g.optionals { lock g.done_optionals {
last_text := g.type_definitions.cut_to(start_pos).clone() if base !in g.done_optionals {
g.optionals << styp g.done_optionals << base
g.typedefs2.writeln('typedef struct $styp $styp;') last_text := g.type_definitions.after(start_pos).clone()
g.type_definitions.writeln('${g.optional_type_text(styp, g.type_definitions.go_back_to(start_pos)
base)};') g.typedefs2.writeln('typedef struct $styp $styp;')
g.type_definitions.write_string(last_text) g.type_definitions.writeln('${g.optional_type_text(styp,
base)};')
g.type_definitions.write_string(last_text)
}
} }
} }
type_name := g.typ(field.typ) type_name := g.typ(field.typ)

View File

@ -23,14 +23,14 @@ const c_current_commit_hash_default = '
const c_concurrency_helpers = ' const c_concurrency_helpers = '
typedef struct __shared_map __shared_map; 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) { static inline voidptr __dup_shared_map(voidptr src, int sz) {
__shared_map* dest = memdup(src, sz); __shared_map* dest = memdup(src, sz);
sync__RwMutex_init(&dest->mtx); sync__RwMutex_init(&dest->mtx);
return dest; return dest;
} }
typedef struct __shared_array __shared_array; 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) { static inline voidptr __dup_shared_array(voidptr src, int sz) {
__shared_array* dest = memdup(src, sz); __shared_array* dest = memdup(src, sz);
sync__RwMutex_init(&dest->mtx); sync__RwMutex_init(&dest->mtx);
@ -451,6 +451,7 @@ voidptr memdup(voidptr src, int sz);
#include <io.h> // _waccess #include <io.h> // _waccess
#include <direct.h> // _wgetcwd #include <direct.h> // _wgetcwd
#include <signal.h> // signal and SIGSEGV for segmentation fault handler
#ifdef _MSC_VER #ifdef _MSC_VER
// On MSVC these are the same (as long as /volatile:ms is passed) // 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 main_fn_start_pos := g.out.len
is_sokol := 'sokol' in g.table.imports is_sokol := 'sokol' in g.table.imports
if (g.pref.os == .android && g.pref.is_apk) || (g.pref.os == .ios && is_sokol) { if (g.pref.os == .android && g.pref.is_apk) || (g.pref.os == .ios && is_sokol) {
g.gen_c_android_sokol_main() g.gen_c_android_sokol_main()
} else { } 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' name = '${left.expr}.$left.field_name'
exp_type = g.comptime_var_type_map[name] exp_type = g.comptime_var_type_map[name]
} else if left is ast.TypeNode { } else if left is ast.TypeNode {
name = left.str()
// this is only allowed for generics currently, otherwise blocked by checker // this is only allowed for generics currently, otherwise blocked by checker
exp_type = g.unwrap_generic(left.typ) 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_typedefs := map[string]bool{}
mut dump_fns := strings.new_builder(100) mut dump_fns := strings.new_builder(100)
for dump_type, cname in g.table.dumps { 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() is_ptr := ast.Type(dump_type).is_ptr()
ptr_asterisk := if is_ptr { '*' } else { '' } ptr_asterisk := if is_ptr { '*' } else { '' }
dump_sym := g.table.get_type_symbol(dump_type) 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') { // if g.fileis('vweb.v') {
// println('\ngen_fn_decl() $node.name $node.is_generic $g.cur_generic_type') // 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 // loop thru each generic type and generate a function
for concrete_types in g.table.fn_generic_types[node.name] { for concrete_types in g.table.fn_generic_types[node.name] {
if g.pref.is_verbose { 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(', ') the_type := syms.map(it.name).join(', ')
println('gen fn `$node.name` for type `$the_type`') 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.gen_fn_decl(node, skip)
} }
g.table.cur_concrete_types = [] g.cur_concrete_types = []
return return
} }
cur_fn_save := g.table.cur_fn cur_fn_save := g.cur_fn
defer { defer {
g.table.cur_fn = cur_fn_save g.cur_fn = cur_fn_save
} }
unsafe { unsafe {
// TODO remove unsafe // TODO remove unsafe
g.table.cur_fn = node g.cur_fn = node
} }
fn_start_pos := g.out.len fn_start_pos := g.out.len
is_closure := node.scope.has_inherited_vars() 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) 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 if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') && !node.is_main
&& node.name != 'str' { && 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) 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 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 // Just a function header. Builtin function bodies are defined in builtin.o
g.definitions.writeln(');') // // NO BODY') g.definitions.writeln(');') // // NO BODY')
g.writeln(');') 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) g.checker_bug('CallExpr.receiver_type is 0 in method_call', node.pos)
} }
mut unwrapped_rec_type := node.receiver_type 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) unwrapped_rec_type = g.unwrap_generic(node.receiver_type)
} else { // in non-generic fn } else { // in non-generic fn
sym := g.table.get_type_symbol(node.receiver_type) sym := g.table.get_type_symbol(node.receiver_type)
match sym.info { match sym.info {
ast.Struct, ast.Interface, ast.SumType { ast.Struct, ast.Interface, ast.SumType {
generic_names := sym.info.generic_types.map(g.table.get_type_symbol(it).name) 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) sym.info.concrete_types)
{ {
unwrapped_rec_type = utyp 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) { if typ_sym.kind == .interface_ && (typ_sym.info as ast.Interface).defines_method(node.name) {
// Speaker_name_table[s._interface_idx].speak(s._object) // Speaker_name_table[s._interface_idx].speak(s._object)
$if debug_interface_method_call ? { $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.write('${c_name(receiver_type_name)}_name_table[')
g.expr(node.left) g.expr(node.left)
@ -712,7 +714,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
return return
} }
'contains' { 'contains' {
g.gen_array_contains(node) g.gen_array_contains(node.left_type, node.left, node.args[0].expr)
return return
} }
'index' { 'index' {
@ -795,7 +797,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
if rec_type.has_flag(.shared_f) { if rec_type.has_flag(.shared_f) {
rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0) 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' { } else if node.name == 'free' {
mut rec_type := node.receiver_type mut rec_type := node.receiver_type
if rec_type.has_flag(.shared_f) { 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 mut arr_info := arr_sym.info as ast.Array
if varg_type.has_flag(.generic) { if varg_type.has_flag(.generic) {
if fn_def := g.table.find_fn(node.name) { 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) node.concrete_types)
{ {
arr_info.elem_type = utyp 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) g.expr(node.left)
} }
if node.left_type.has_flag(.shared_f) { if node.left_type.has_flag(.shared_f) {
if left_is_ptr { g.write('->val')
g.write('->val')
} else {
g.write('.val')
}
} }
g.write(', &($key_type_str[]){') g.write(', &($key_type_str[]){')
g.expr(node.index) g.expr(node.index)
@ -374,10 +370,13 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
} else { } else {
g.write('(*($elem_type_str*)map_get((map*)') 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.write('&')
} }
g.expr(node.left) g.expr(node.left)
if node.left_type.has_flag(.shared_f) {
g.write('->val')
}
g.write(', &($key_type_str[]){') g.write(', &($key_type_str[]){')
g.expr(node.index) g.expr(node.index)
g.write('}, &($elem_type_str[]){ $zero }))') 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 { '' } tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
if gen_or { if gen_or {
elem_styp := g.typ(elem_type) 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(') g.write('Option_void $tmp_opt = __Option_${styp}_pushval(')
} else { } else {
g.write('__${styp}_pushval(') 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] { && left.sym.kind in [.array, .array_fixed, .alias, .map, .struct_, .sum_type] {
match left.sym.kind { match left.sym.kind {
.alias { .alias {
ptr_typ := g.gen_alias_equality_fn(left.typ) ptr_typ := g.equality_fn(left.typ)
if node.op == .ne { if node.op == .ne {
g.write('!') g.write('!')
} }
@ -131,7 +131,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.write(')') g.write(')')
} }
.array { .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 { if node.op == .ne {
g.write('!') g.write('!')
} }
@ -141,11 +141,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
} }
g.expr(node.left) g.expr(node.left)
if left.typ.has_flag(.shared_f) { if left.typ.has_flag(.shared_f) {
if left.typ.is_ptr() { g.write('->val')
g.write('->val')
} else {
g.write('.val')
}
} }
g.write(', ') g.write(', ')
if right.typ.is_ptr() && !right.typ.has_flag(.shared_f) { 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) g.expr(node.right)
if right.typ.has_flag(.shared_f) { if right.typ.has_flag(.shared_f) {
if right.typ.is_ptr() { g.write('->val')
g.write('->val')
} else {
g.write('.val')
}
} }
g.write(')') g.write(')')
} }
.array_fixed { .array_fixed {
ptr_typ := g.gen_fixed_array_equality_fn(left.unaliased) ptr_typ := g.equality_fn(left.unaliased)
if node.op == .ne { if node.op == .ne {
g.write('!') g.write('!')
} }
@ -184,7 +176,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.write(')') g.write(')')
} }
.map { .map {
ptr_typ := g.gen_map_equality_fn(left.unaliased) ptr_typ := g.equality_fn(left.unaliased)
if node.op == .ne { if node.op == .ne {
g.write('!') g.write('!')
} }
@ -201,7 +193,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.write(')') g.write(')')
} }
.struct_ { .struct_ {
ptr_typ := g.gen_struct_equality_fn(left.unaliased) ptr_typ := g.equality_fn(left.unaliased)
if node.op == .ne { if node.op == .ne {
g.write('!') g.write('!')
} }
@ -218,7 +210,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.write(')') g.write(')')
} }
.sum_type { .sum_type {
ptr_typ := g.gen_sumtype_equality_fn(left.unaliased) ptr_typ := g.equality_fn(left.unaliased)
if node.op == .ne { if node.op == .ne {
g.write('!') g.write('!')
} }
@ -385,19 +377,7 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) {
return return
} }
} }
fn_name := g.gen_array_contains_method(node.right_type) g.gen_array_contains(node.right_type, node.right, node.left)
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
} else if right.unaliased_sym.kind == .map { } else if right.unaliased_sym.kind == .map {
g.write('_IN_MAP(') g.write('_IN_MAP(')
if !left.typ.is_ptr() { 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 { if is_str {
g.write('string__eq(') g.write('string__eq(')
} else if is_array { } 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.write('${ptr_typ}_arr_eq(')
} }
g.expr(left) 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_type_str := g.typ(array_info.elem_type)
elem_sym := g.table.get_type_symbol(array_info.elem_type) elem_sym := g.table.get_type_symbol(array_info.elem_type)
g.write('array_push${noscan}((array*)') 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.write('&')
} }
g.expr(node.left) g.expr(node.left)
if node.left_type.has_flag(.shared_f) {
g.write('->val')
}
if elem_sym.kind == .function { if elem_sym.kind == .function {
g.write(', _MOV((voidptr[]){ ') g.write(', _MOV((voidptr[]){ ')
} else { } else {

View File

@ -19,29 +19,34 @@ import strings
// } // }
// Codegen json_decode/encode funcs // Codegen json_decode/encode funcs
fn (mut g Gen) gen_json_for_type(typ ast.Type) { fn (mut g Gen) gen_json_for_type(typ ast.Type) {
utyp := g.unwrap_generic(typ) utyp := g.unwrap_generic(typ).set_nr_muls(0)
mut dec := strings.new_builder(100)
mut enc := strings.new_builder(100)
sym := g.table.get_type_symbol(utyp) sym := g.table.get_type_symbol(utyp)
styp := g.typ(utyp)
g.register_optional(utyp)
if is_js_prim(sym.name) || sym.kind == .enum_ { if is_js_prim(sym.name) || sym.kind == .enum_ {
return return
} }
if sym.kind == .array { g.json_types << utyp
// return }
}
if sym.name in g.json_types { fn (mut g Gen) gen_jsons() {
return mut done := []ast.Type{}
} for i := 0; i < g.json_types.len; i++ {
g.json_types << sym.name utyp := g.json_types[i]
// println('gen_json_for_type($sym.name)') if utyp in done {
// decode_TYPE funcs receive an actual cJSON* object to decode continue
// cJSON_Parse(str) call is added by the compiler }
// Code gen decoder done << utyp
dec_fn_name := js_dec_name(styp) mut dec := strings.new_builder(100)
dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)' mut enc := strings.new_builder(100)
dec.writeln(' 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 { $dec_fn_dec {
$styp res; $styp res;
if (!root) { if (!root) {
@ -53,69 +58,70 @@ $dec_fn_dec {
} }
} }
') ')
g.json_forward_decls.writeln('$dec_fn_dec;') g.json_forward_decls.writeln('$dec_fn_dec;')
// Code gen encoder // Code gen encoder
// encode_TYPE funcs receive an object to encode // encode_TYPE funcs receive an object to encode
enc_fn_name := js_enc_name(styp) enc_fn_name := js_enc_name(styp)
enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)' enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)'
g.json_forward_decls.writeln('$enc_fn_dec;\n') g.json_forward_decls.writeln('$enc_fn_dec;\n')
enc.writeln(' enc.writeln('
$enc_fn_dec { $enc_fn_dec {
\tcJSON *o;') \tcJSON *o;')
if sym.kind == .array { if sym.kind == .array {
// Handle arrays // Handle arrays
value_type := g.table.value_type(utyp) value_type := g.table.value_type(utyp)
// If we have `[]Profile`, have to register a Profile en(de)coder first // If we have `[]Profile`, have to register a Profile en(de)coder first
g.gen_json_for_type(value_type) g.gen_json_for_type(value_type)
dec.writeln(g.decode_array(value_type)) dec.writeln(g.decode_array(value_type))
enc.writeln(g.encode_array(value_type)) enc.writeln(g.encode_array(value_type))
// enc += g.encode_array(t) // enc += g.encode_array(t)
} else if sym.kind == .map { } else if sym.kind == .map {
// Handle maps // Handle maps
m := sym.info as ast.Map m := sym.info as ast.Map
g.gen_json_for_type(m.key_type) g.gen_json_for_type(m.key_type)
g.gen_json_for_type(m.value_type) g.gen_json_for_type(m.value_type)
dec.writeln(g.decode_map(m.key_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)) enc.writeln(g.encode_map(m.key_type, m.value_type))
} else if sym.kind == .alias { } else if sym.kind == .alias {
a := sym.info as ast.Alias a := sym.info as ast.Alias
parent_typ := a.parent_type parent_typ := a.parent_type
psym := g.table.get_type_symbol(parent_typ) psym := g.table.get_type_symbol(parent_typ)
if is_js_prim(g.typ(parent_typ)) { if is_js_prim(g.typ(parent_typ)) {
g.gen_json_for_type(parent_typ) g.gen_json_for_type(parent_typ)
return continue
} }
enc.writeln('\to = cJSON_CreateObject();') enc.writeln('\to = cJSON_CreateObject();')
if psym.info is ast.Struct { if psym.info is ast.Struct {
g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec) g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec)
} else if psym.kind == .sum_type { } else if psym.kind == .sum_type {
verror('json: $sym.name aliased sumtypes does not work at the moment') 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 { } 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 { // cJSON_delete
enc.writeln('\to = cJSON_CreateObject();') // p.cgen.fns << '$dec return opt_ok(res); \n}'
// Sumtypes. Range through variants of sumtype dec.writeln('\tOption_$styp ret;')
if sym.info !is ast.SumType { dec.writeln('\topt_ok(&res, (Option*)&ret, sizeof(res));')
verror('json: $sym.name is not a sumtype') dec.writeln('\treturn ret;\n}')
} enc.writeln('\treturn o;\n}')
g.gen_sumtype_enc_dec(sym, mut enc, mut dec) g.definitions.writeln(dec.str())
} else { g.gowrappers.writeln(enc.str())
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())
} }
[inline] [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) g.gen_json_for_type(variant)
// Helpers for decoding // 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);') g.definitions.writeln('static inline $sym.cname ${variant_typ}_to_sumtype_${sym.cname}($variant_typ* x);')
// ENCODING // 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() sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
if typ.has_flag(.variadic) { 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.write('${str_fn_name}(')
g.expr(expr) g.expr(expr)
g.write(')') g.write(')')
@ -93,7 +93,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
g.write('_SLIT("<none>")') g.write('_SLIT("<none>")')
} else if sym.kind == .enum_ { } else if sym.kind == .enum_ {
if expr !is ast.EnumVal { 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.write('${str_fn_name}(')
g.enum_expr(expr) g.enum_expr(expr)
g.write(')') 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_] { || sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] {
is_ptr := typ.is_ptr() is_ptr := typ.is_ptr()
is_var_mut := expr.is_auto_deref_var() 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 { if is_ptr && !is_var_mut {
g.write('str_intp(1, _MOV((StrIntpData[]){{_SLIT("&"), $si_s_code ,{.d_s=') 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(')') // g.write(')')
} }
} else { } else {
str_fn_name := g.gen_str_method_for_type(typ) str_fn_name := g.get_str_fn(typ)
g.write('${str_fn_name}(') g.write('${str_fn_name}(')
if expr.is_auto_deref_var() { if expr.is_auto_deref_var() {
g.write('*') g.write('*')

View File

@ -7,8 +7,20 @@ import v.ast
fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type { fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
if typ.has_flag(.generic) { 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 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' name = '${left.expr}.$left.field_name'
exp_type = g.comptime_var_type_map[name] exp_type = g.comptime_var_type_map[name]
} else if left is ast.TypeNode { } else if left is ast.TypeNode {
name = left.str()
// this is only allowed for generics currently, otherwise blocked by checker // this is only allowed for generics currently, otherwise blocked by checker
exp_type = g.unwrap_generic(left.typ) 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 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('\n\n\nparse_files() nr_files=$paths.len')
println(paths) println(paths)
nr_cpus := runtime.nr_cpus() nr_cpus := runtime.nr_cpus()

View File

@ -136,6 +136,10 @@ pub fn (mut p Preferences) fill_with_defaults() {
if p.bare_builtin_dir == '' { if p.bare_builtin_dir == '' {
p.bare_builtin_dir = os.join_path(p.vroot, 'vlib', 'builtin', 'linux_bare') 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' 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_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 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. 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 is_vweb bool // skip _ var warning in templates
only_check_syntax bool // when true, just parse the files, then stop, before running checker 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 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.prealloc = true
res.build_options << arg res.build_options << arg
} }
'-parallel' { '-no-parallel' {
res.is_parallel = true res.no_parallel = true
} }
'-native' { '-native' {
res.backend = .native res.backend = .native

View File

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

View File

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