diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46359fc831..03c04b3220 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -191,7 +191,7 @@ jobs: - name: v.c can be compiled and run with -os cross run: | ./v -os cross -o /tmp/v.c cmd/v - gcc -g -std=gnu11 -w -o v_from_vc /tmp/v.c -lm -lpthread + gcc -g -std=gnu11 -I ./thirdparty/stdatomic/nix -w -o v_from_vc /tmp/v.c -lm -lpthread ls -lart v_from_vc ./v_from_vc version @@ -235,7 +235,7 @@ jobs: - name: g++ version run: g++-9 --version - name: V self compilation with g++ - run: ./v -cc g++-9 -o v2 cmd/v && ./v2 -cc g++-9 -o v3 cmd/v + run: ./v -cc g++-9 -no-std -cflags -std=c++11 -o v2 cmd/v && ./v2 -cc g++-9 -no-std -cflags -std=c++11 -o v3 cmd/v ## - name: Running tests with g++ ## run: ./v -cc g++-9 -silent test-self @@ -323,7 +323,7 @@ jobs: - name: v.c can be compiled and run with -os cross run: | ./v -os cross -o /tmp/v.c cmd/v - cc -g -std=gnu11 -w -o v_from_vc /tmp/v.c -lm -lpthread + cc -g -std=gnu11 -w -o v_from_vc /tmp/v.c -I ./thirdparty/stdatomic/nix -lm -lpthread ls -lart v_from_vc ./v_from_vc version @@ -627,7 +627,7 @@ jobs: gcc --version .\make.bat -gcc - name: Test new v.c - run: .\v.exe -o v.c cmd/v && gcc -Werror -municode -w v.c + run: .\v.exe -o v.c cmd/v && gcc -Werror -I ./thirdparty/stdatomic/win -municode -w v.c - name: Install dependencies run: | .\v.exe setup-freetype @@ -715,7 +715,7 @@ jobs: run: | .\make.bat -tcc --verbose - name: Test new v.c - run: .\v.exe -o v.c cmd/v && .\thirdparty\tcc\tcc.exe -Werror -w -ladvapi32 -bt10 v.c + run: .\v.exe -o v.c cmd/v && .\thirdparty\tcc\tcc.exe -I ./thirdparty/stdatomic/win -Werror -w -ladvapi32 -bt10 v.c - name: Install dependencies run: | .\v.exe setup-freetype @@ -764,7 +764,7 @@ jobs: .\v.exe wipe-cache .\make.bat -tcc32 --verbose - name: Test new v.c - run: .\v.exe -o v.c cmd/v && .\thirdparty\tcc\tcc.exe -Werror -w -ladvapi32 -bt10 v.c + run: .\v.exe -o v.c cmd/v && .\thirdparty\tcc\tcc.exe -I ./thirdparty/stdatomic/win -Werror -w -ladvapi32 -bt10 v.c - name: v doctor run: ./v doctor diff --git a/.github/workflows/ci_cross.yml b/.github/workflows/ci_cross.yml index c73f1d118a..a58d29ba39 100644 --- a/.github/workflows/ci_cross.yml +++ b/.github/workflows/ci_cross.yml @@ -64,14 +64,14 @@ jobs: - name: v.c can be compiled and run with -os cross run: | ./v -os cross -o /tmp/v.c cmd/v - gcc -g -std=gnu11 -w -o v_from_vc /tmp/v.c -lm -lpthread + gcc -g -std=gnu11 -I ./thirdparty/stdatomic/nix -w -o v_from_vc /tmp/v.c -lm -lpthread ls -lart v_from_vc ./v_from_vc version - name: v_win.c can be compiled and run with -os windows run: | ./v -os windows -o /tmp/v_win.c cmd/v - x86_64-w64-mingw32-gcc /tmp/v_win.c -std=c99 -w -municode -o v_from_vc.exe + x86_64-w64-mingw32-gcc -I ./thirdparty/stdatomic/win /tmp/v_win.c -std=c99 -w -municode -o v_from_vc.exe ls -lart v_from_vc.exe wine v_from_vc.exe version diff --git a/.gitignore b/.gitignore index 72191f36d9..7eb3a552a8 100644 --- a/.gitignore +++ b/.gitignore @@ -99,3 +99,5 @@ shell.nix default.nix flake.nix .envrc + +thirdparty/stdatomic/nix/cpp/*.h diff --git a/Dockerfile.cross b/Dockerfile.cross index 22a0ff0ce1..3a13c634b8 100644 --- a/Dockerfile.cross +++ b/Dockerfile.cross @@ -4,7 +4,7 @@ LABEL maintainer="Vitaly Takmazov " COPY . . RUN make RUN ./v -os windows -o v.c cmd/v -RUN x86_64-w64-mingw32-gcc v.c -std=c99 -w -municode -o v.exe +RUN x86_64-w64-mingw32-gcc v.c -std=c99 -I ./thirdparty/stdatomic/win -w -municode -o v.exe RUN file v.exe CMD [ "bash" ] diff --git a/Makefile b/Makefile index 756fa7651c..63c83b79b3 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,6 @@ CC ?= cc all: rm -rf vc/ git clone --depth 1 --quiet https://github.com/vlang/vc - $(CC) -std=gnu11 -w -o v vc/v.c -lm -lexecinfo + $(CC) -std=gnu11 -w -o v vc/v.c -lm -lexecinfo -I ./thirdparty/stdatomic/nix rm -rf vc/ @echo "V has been successfully built" diff --git a/cmd/tools/fast/fast.v b/cmd/tools/fast/fast.v index 3b0b282172..992267f0d9 100644 --- a/cmd/tools/fast/fast.v +++ b/cmd/tools/fast/fast.v @@ -21,11 +21,13 @@ fn main() { println('fast.html generator needs to be located in `v/cmd/tools/fast`') } println('fast.html generator\n') - println('Fetching updates...') - ret := os.system('$vdir/v up') - if ret != 0 { - println('failed to update V') - return + if !os.args.contains('-noupdate') { + println('Fetching updates...') + ret := os.system('$vdir/v up') + if ret != 0 { + println('failed to update V') + return + } } // Fetch the last commit's hash commit := exec('git rev-parse HEAD')[..8] @@ -55,7 +57,6 @@ fn main() { } else { exec('./v -o vprod -prod -prealloc cmd/v') } - // println('cur vdir="$vdir"') // cache vlib modules exec('$vdir/v wipe-cache') exec('$vdir/v -o v2 -prod cmd/v') diff --git a/cmd/tools/modules/vgit/vgit.v b/cmd/tools/modules/vgit/vgit.v index efa2e8ab0d..e4e158d21f 100644 --- a/cmd/tools/modules/vgit/vgit.v +++ b/cmd/tools/modules/vgit/vgit.v @@ -111,10 +111,10 @@ pub fn (mut vgit_context VGitContext) compile_oldv_if_needed() { mut command_for_building_v_from_c_source := '' mut command_for_selfbuilding := '' if 'windows' == os.user_os() { - command_for_building_v_from_c_source = '$vgit_context.cc -std=c99 -municode -w -o cv.exe "$vgit_context.path_vc/v_win.c" ' + command_for_building_v_from_c_source = '$vgit_context.cc -std=c99 -I ./thirdparty/stdatomic/win -municode -w -o cv.exe "$vgit_context.path_vc/v_win.c" ' command_for_selfbuilding = './cv.exe -o $vgit_context.vexename {SOURCE}' } else { - command_for_building_v_from_c_source = '$vgit_context.cc -std=gnu11 -w -o cv "$vgit_context.path_vc/v.c" -lm -lpthread' + command_for_building_v_from_c_source = '$vgit_context.cc -std=gnu11 -I ./thirdparty/stdatomic/nix -w -o cv "$vgit_context.path_vc/v.c" -lm -lpthread' command_for_selfbuilding = './cv -o $vgit_context.vexename {SOURCE}' } scripting.chdir(vgit_context.workdir) diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index b037d1f6b7..3d4f322c89 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -8,6 +8,7 @@ import v.parser import v.ast import v.pref import v.errors +import strings struct Context { mut: @@ -208,7 +209,7 @@ fn (t Tree) type_node(typ ast.Type) &Node { return create_null() } else { type_name := t.table.get_type_name(typ) - return create_string(type_name) + return create_string(strings.repeat(`&`, typ.nr_muls()) + type_name) } } diff --git a/cmd/tools/vtest-all.v b/cmd/tools/vtest-all.v index b0544248e4..52ef6ecd15 100644 --- a/cmd/tools/vtest-all.v +++ b/cmd/tools/vtest-all.v @@ -148,7 +148,7 @@ fn get_all_commands() []Command { } $if macos || linux { res << Command{ - line: '$vexe -o v.c cmd/v && cc -Werror v.c && rm -rf a.out' + line: '$vexe -o v.c cmd/v && cc -I "$vroot/thirdparty/stdatomic/nix" -lpthread v.c && rm -rf a.out' label: 'v.c should be buildable with no warnings...' okmsg: 'v.c can be compiled without warnings. This is good :)' rmfile: 'v.c' diff --git a/cmd/v/help/build.txt b/cmd/v/help/build.txt index f43605989c..a81e593bf7 100644 --- a/cmd/v/help/build.txt +++ b/cmd/v/help/build.txt @@ -90,6 +90,9 @@ NB: the build flags are shared with the run command too: The checker will abort prematurely once this limit has been reached. Setting this to 0 or a negative value, will disable the limit. + -no-parallel + Do not run the compiler in parallel (currently only the cgen stage has parallelization). + -profile-no-inline Skip [inline] functions when profiling. diff --git a/make.bat b/make.bat index d5171b3a44..e65463f2a1 100644 --- a/make.bat +++ b/make.bat @@ -175,10 +175,10 @@ REM By default, use tcc, since we have it prebuilt: :tcc32_strap echo ^> Attempting to build v_win.c with TCC if !flag_verbose! EQU 1 ( - echo [Debug] "!tcc_exe!" -ladvapi32 -bt10 -w -o v.exe vc\v_win.c>>"!log_file!" - echo "!tcc_exe!" -ladvapi32 -bt10 -w -o v.exe vc\v_win.c + echo [Debug] "!tcc_exe!" -ladvapi32 -I .\thirdparty\stdatomic\win -bt10 -w -o v.exe vc\v_win.c>>"!log_file!" + echo "!tcc_exe!" -ladvapi32 -I .\thirdparty\stdatomic\win -bt10 -w -o v.exe vc\v_win.c ) -"!tcc_exe!" -ladvapi32 -bt10 -w -o v.exe vc\v_win.c>>"!log_file!" +"!tcc_exe!" -ladvapi32 -I .\thirdparty\stdatomic\win -bt10 -w -o v.exe vc\v_win.c>>"!log_file!" if %ERRORLEVEL% NEQ 0 goto :compile_error echo ^> Compiling with .\v.exe self @@ -202,8 +202,8 @@ if %ERRORLEVEL% NEQ 0 ( echo ^> Attempting to build v_win.c with Clang if !flag_verbose! EQU 1 ( - echo [Debug] clang -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_File!" - echo clang -std=c99 -municode -w -o v.exe .\vc\v_win.c + echo [Debug] clang -std=c99 -municode -I .\thirdparty\stdatomic\win -w -o v.exe .\vc\v_win.c>>"!log_File!" + echo clang -std=c99 -municode -I .\thirdparty\stdatomic\win -w -o v.exe .\vc\v_win.c ) clang -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_file!" if %ERRORLEVEL% NEQ 0 ( @@ -231,10 +231,10 @@ if %ERRORLEVEL% NEQ 0 ( echo ^> Attempting to build v_win.c with GCC if !flag_verbose! EQU 1 ( - echo [Debug] gcc -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_File!" - echo gcc -std=c99 -municode -w -o v.exe .\vc\v_win.c + echo [Debug] gcc -std=c99 -municode -I .\thirdparty\stdatomic\win -w -o v.exe .\vc\v_win.c>>"!log_File!" + echo gcc -std=c99 -municode -I .\thirdparty\stdatomic\win -w -o v.exe .\vc\v_win.c ) -gcc -std=c99 -municode -w -o v.exe .\vc\v_win.c>>"!log_File!" +gcc -std=c99 -municode -I .\thirdparty\stdatomic\win -w -o v.exe .\vc\v_win.c>>"!log_File!" if %ERRORLEVEL% NEQ 0 ( REM In most cases, compile errors happen because the version of GCC installed is too old gcc --version>>"!log_File!" @@ -279,8 +279,8 @@ set ObjFile=.v.c.obj echo ^> Attempting to build v_win.c with MSVC if !flag_verbose! EQU 1 ( - echo [Debug] cl.exe /volatile:ms /Fo%ObjFile% /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no>>"!log_file!" - echo cl.exe /volatile:ms /Fo%ObjFile% /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no + echo [Debug] cl.exe /volatile:ms /Fo%ObjFile% /I .\thirdparty\stdatomic\win /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no>>"!log_file!" + echo cl.exe /volatile:ms /Fo%ObjFile% /I .\thirdparty\stdatomic\win /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no ) cl.exe /volatile:ms /Fo%ObjFile% /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no>>"!log_file!" if %ERRORLEVEL% NEQ 0 ( diff --git a/thirdparty/stdatomic/nix/atomic.h b/thirdparty/stdatomic/nix/atomic.h index 3bcb867cd9..96043a3e11 100644 --- a/thirdparty/stdatomic/nix/atomic.h +++ b/thirdparty/stdatomic/nix/atomic.h @@ -275,160 +275,160 @@ static inline unsigned char atomic_fetch_xor_byte(unsigned char* x, unsigned cha // Since V might be confused with "generic" C functions either we provide special versions // for gcc/clang, too static inline unsigned long long atomic_load_u64(unsigned long long* x) { - return atomic_load_explicit((_Atomic unsigned long long*)x, memory_order_seq_cst); + return atomic_load_explicit((_Atomic (unsigned long long)*)x, memory_order_seq_cst); } static inline void atomic_store_u64(unsigned long long* x, unsigned long long y) { - atomic_store_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); + atomic_store_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst); } static inline int atomic_compare_exchange_weak_u64(unsigned long long* x, unsigned long long* expected, unsigned long long y) { - return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned long long*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); + return (int)atomic_compare_exchange_weak_explicit((_Atomic(unsigned long long)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); } static inline int atomic_compare_exchange_strong_u64(unsigned long long* x, unsigned long long* expected, unsigned long long y) { - return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned long long*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); + return (int)atomic_compare_exchange_strong_explicit((_Atomic(unsigned long long)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); } static inline unsigned long long atomic_exchange_u64(unsigned long long* x, unsigned long long y) { - return atomic_exchange_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); + return atomic_exchange_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst); } static inline unsigned long long atomic_fetch_add_u64(unsigned long long* x, unsigned long long y) { - return atomic_fetch_add_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); + return atomic_fetch_add_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst); } static inline unsigned long long atomic_fetch_sub_u64(unsigned long long* x, unsigned long long y) { - return atomic_fetch_sub_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); + return atomic_fetch_sub_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst); } static inline unsigned long long atomic_fetch_and_u64(unsigned long long* x, unsigned long long y) { - return atomic_fetch_and_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); + return atomic_fetch_and_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst); } static inline unsigned long long atomic_fetch_or_u64(unsigned long long* x, unsigned long long y) { - return atomic_fetch_or_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); + return atomic_fetch_or_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst); } static inline unsigned long long atomic_fetch_xor_u64(unsigned long long* x, unsigned long long y) { - return atomic_fetch_xor_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); + return atomic_fetch_xor_explicit((_Atomic(unsigned long long)*)x, y, memory_order_seq_cst); } static inline void* atomic_load_ptr(void** x) { - return (void*)atomic_load_explicit((_Atomic uintptr_t*)x, memory_order_seq_cst); + return (void*)atomic_load_explicit((_Atomic(uintptr_t)*)x, memory_order_seq_cst); } static inline void atomic_store_ptr(void** x, void* y) { - atomic_store_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); + atomic_store_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst); } static inline int atomic_compare_exchange_weak_ptr(void** x, void** expected, void* y) { - return (int)atomic_compare_exchange_weak_explicit((_Atomic uintptr_t*)x, (unsigned long *)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst); + return (int)atomic_compare_exchange_weak_explicit((_Atomic(uintptr_t)*)x, (unsigned long *)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst); } static inline int atomic_compare_exchange_strong_ptr(void** x, void** expected, void* y) { - return (int)atomic_compare_exchange_strong_explicit((_Atomic uintptr_t*)x, (unsigned long *)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst); + return (int)atomic_compare_exchange_strong_explicit((_Atomic(uintptr_t)*)x, (unsigned long *)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst); } static inline void* atomic_exchange_ptr(void** x, void* y) { - return (void*)atomic_exchange_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); + return (void*)atomic_exchange_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst); } static inline void* atomic_fetch_add_ptr(void** x, void* y) { - return (void*)atomic_fetch_add_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); + return (void*)atomic_fetch_add_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst); } static inline void* atomic_fetch_sub_ptr(void** x, void* y) { - return (void*)atomic_fetch_sub_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); + return (void*)atomic_fetch_sub_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst); } static inline void* atomic_fetch_and_ptr(void** x, void* y) { - return (void*)atomic_fetch_and_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); + return (void*)atomic_fetch_and_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst); } static inline void* atomic_fetch_or_ptr(void** x, void* y) { - return (void*)atomic_fetch_or_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); + return (void*)atomic_fetch_or_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst); } static inline void* atomic_fetch_xor_ptr(void** x, void* y) { - return (void*)atomic_fetch_xor_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); + return (void*)atomic_fetch_xor_explicit((_Atomic(uintptr_t)*)x, (uintptr_t)y, memory_order_seq_cst); } static inline unsigned atomic_load_u32(unsigned* x) { - return atomic_load_explicit((_Atomic unsigned*)x, memory_order_seq_cst); + return atomic_load_explicit((_Atomic(unsigned)*)x, memory_order_seq_cst); } static inline void atomic_store_u32(unsigned* x, unsigned y) { - atomic_store_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); + atomic_store_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst); } static inline int atomic_compare_exchange_weak_u32(unsigned* x, unsigned* expected, unsigned y) { - return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); + return (int)atomic_compare_exchange_weak_explicit((_Atomic(unsigned)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); } static inline int atomic_compare_exchange_strong_u32(unsigned* x, unsigned* expected, unsigned y) { - return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); + return (int)atomic_compare_exchange_strong_explicit((_Atomic(unsigned)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); } static inline unsigned atomic_exchange_u32(unsigned* x, unsigned y) { - return atomic_exchange_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); + return atomic_exchange_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst); } static inline unsigned atomic_fetch_add_u32(unsigned* x, unsigned y) { - return atomic_fetch_add_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); + return atomic_fetch_add_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst); } static inline unsigned atomic_fetch_sub_u32(unsigned* x, unsigned y) { - return atomic_fetch_sub_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); + return atomic_fetch_sub_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst); } static inline unsigned atomic_fetch_and_u32(unsigned* x, unsigned y) { - return atomic_fetch_and_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); + return atomic_fetch_and_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst); } static inline unsigned atomic_fetch_or_u32(unsigned* x, unsigned y) { - return atomic_fetch_or_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); + return atomic_fetch_or_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst); } static inline unsigned atomic_fetch_xor_u32(unsigned* x, unsigned y) { - return atomic_fetch_xor_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); + return atomic_fetch_xor_explicit((_Atomic(unsigned)*)x, y, memory_order_seq_cst); } static inline unsigned short atomic_load_u16(unsigned short* x) { - return atomic_load_explicit((_Atomic unsigned short*)x, memory_order_seq_cst); + return atomic_load_explicit((_Atomic(unsigned short)*)x, memory_order_seq_cst); } static inline void atomic_store_u16(void* x, unsigned short y) { - atomic_store_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); + atomic_store_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst); } static inline int atomic_compare_exchange_weak_u16(void* x, unsigned short* expected, unsigned short y) { - return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned short*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); + return (int)atomic_compare_exchange_weak_explicit((_Atomic(unsigned short)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); } static inline int atomic_compare_exchange_strong_u16(unsigned short* x, unsigned short* expected, unsigned short y) { - return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned short*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); + return (int)atomic_compare_exchange_strong_explicit((_Atomic(unsigned short)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); } static inline unsigned short atomic_exchange_u16(unsigned short* x, unsigned short y) { - return atomic_exchange_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); + return atomic_exchange_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst); } static inline unsigned short atomic_fetch_add_u16(unsigned short* x, unsigned short y) { - return atomic_fetch_add_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); + return atomic_fetch_add_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst); } static inline unsigned short atomic_fetch_sub_u16(unsigned short* x, unsigned short y) { - return atomic_fetch_sub_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); + return atomic_fetch_sub_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst); } static inline unsigned short atomic_fetch_and_u16(unsigned short* x, unsigned short y) { - return atomic_fetch_and_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); + return atomic_fetch_and_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst); } static inline unsigned short atomic_fetch_or_u16(unsigned short* x, unsigned short y) { - return atomic_fetch_or_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); + return atomic_fetch_or_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst); } static inline unsigned short atomic_fetch_xor_u16(unsigned short* x, unsigned short y) { - return atomic_fetch_xor_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); + return atomic_fetch_xor_explicit((_Atomic(unsigned short)*)x, y, memory_order_seq_cst); } static inline unsigned char atomic_load_byte(unsigned char* x) { - return atomic_load_explicit((_Atomic unsigned char*)x, memory_order_seq_cst); + return atomic_load_explicit((_Atomic(unsigned char)*)x, memory_order_seq_cst); } static inline void atomic_store_byte(unsigned char* x, unsigned char y) { - atomic_store_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); + atomic_store_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst); } static inline int atomic_compare_exchange_weak_byte(unsigned char* x, unsigned char* expected, unsigned char y) { - return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned char*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); + return (int)atomic_compare_exchange_weak_explicit((_Atomic(unsigned char)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); } static inline int atomic_compare_exchange_strong_byte(unsigned char* x, unsigned char* expected, unsigned char y) { - return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned char*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); + return (int)atomic_compare_exchange_strong_explicit((_Atomic(unsigned char)*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); } static inline unsigned char atomic_exchange_byte(unsigned char* x, unsigned char y) { - return atomic_exchange_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); + return atomic_exchange_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst); } static inline unsigned char atomic_fetch_add_byte(unsigned char* x, unsigned char y) { - return atomic_fetch_add_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); + return atomic_fetch_add_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst); } static inline unsigned char atomic_fetch_sub_byte(unsigned char* x, unsigned char y) { - return atomic_fetch_sub_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); + return atomic_fetch_sub_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst); } static inline unsigned char atomic_fetch_and_byte(unsigned char* x, unsigned char y) { - return atomic_fetch_and_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); + return atomic_fetch_and_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst); } static inline unsigned char atomic_fetch_or_byte(unsigned char* x, unsigned char y) { - return atomic_fetch_or_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); + return atomic_fetch_or_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst); } static inline unsigned char atomic_fetch_xor_byte(unsigned char* x, unsigned char y) { - return atomic_fetch_xor_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); + return atomic_fetch_xor_explicit((_Atomic(unsigned char)*)x, y, memory_order_seq_cst); } #endif diff --git a/thirdparty/stdatomic/nix/atomic_cpp.h b/thirdparty/stdatomic/nix/atomic_cpp.h index 252f5c6bae..adc36b9c3d 100644 --- a/thirdparty/stdatomic/nix/atomic_cpp.h +++ b/thirdparty/stdatomic/nix/atomic_cpp.h @@ -30,7 +30,7 @@ #ifndef _STDATOMIC_H_ #define _STDATOMIC_H_ #include -#if defined(__cplusplus) && defined(_USING_LIBCXX) +#if defined(__cplusplus) #ifdef __clang__ #if __has_feature(cxx_atomic) #define _STDATOMIC_HAVE_ATOMIC @@ -42,8 +42,9 @@ #endif #endif #ifdef _STDATOMIC_HAVE_ATOMIC -/* We have a usable C++ ; use it instead. */ -#include + +#include "cpp/atomic.h" + #undef _Atomic /* Also defined by for gcc. But not used in macros. */ /* Also a clang intrinsic. */ diff --git a/thirdparty/stdatomic/nix/cpp/gen.v b/thirdparty/stdatomic/nix/cpp/gen.v new file mode 100644 index 0000000000..317d27c937 --- /dev/null +++ b/thirdparty/stdatomic/nix/cpp/gen.v @@ -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 ', '#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 ', '#include "bitsatomicbase.h"') + os.write_file(outfile, outtext) ? +} diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 188e28dbb7..7e2cfb843a 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -126,3 +126,10 @@ pub: arg string kind AttributeKind } + +[used] +fn v_segmentation_fault_handler(signal int) { + eprintln('signal 11: segmentation fault') + print_backtrace() + exit(128 + 11) +} diff --git a/vlib/sync/channels.v b/vlib/sync/channels.v index 49f1396d49..63f9afe3a9 100644 --- a/vlib/sync/channels.v +++ b/vlib/sync/channels.v @@ -623,8 +623,8 @@ pub fn channel_select(mut channels []&Channel, dir []Direction, mut objrefs []vo } subscr[i].prev = unsafe { &ch.write_subscriber } unsafe { - subscr[i].nxt = C.atomic_exchange_ptr(&voidptr(&ch.write_subscriber), - &subscr[i]) + subscr[i].nxt = &Subscription(C.atomic_exchange_ptr(&voidptr(&ch.write_subscriber), + &subscr[i])) } if voidptr(subscr[i].nxt) != voidptr(0) { subscr[i].nxt.prev = unsafe { &subscr[i].nxt } @@ -637,7 +637,8 @@ pub fn channel_select(mut channels []&Channel, dir []Direction, mut objrefs []vo } subscr[i].prev = unsafe { &ch.read_subscriber } unsafe { - subscr[i].nxt = C.atomic_exchange_ptr(&voidptr(&ch.read_subscriber), &subscr[i]) + subscr[i].nxt = &Subscription(C.atomic_exchange_ptr(&voidptr(&ch.read_subscriber), + &subscr[i])) } if voidptr(subscr[i].nxt) != voidptr(0) { subscr[i].nxt.prev = unsafe { &subscr[i].nxt } diff --git a/vlib/sync/sync_default.c.v b/vlib/sync/sync_default.c.v index 76f0b44faa..cb2963fff3 100644 --- a/vlib/sync/sync_default.c.v +++ b/vlib/sync/sync_default.c.v @@ -32,6 +32,18 @@ fn C.sem_trywait(voidptr) int fn C.sem_timedwait(voidptr, voidptr) int fn C.sem_destroy(voidptr) int +[typedef] +struct C.pthread_mutex_t {} + +[typedef] +struct C.pthread_rwlock_t {} + +[typedef] +struct C.pthread_rwlockattr_t {} + +[typedef] +struct C.sem_t {} + // [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function. [heap] pub struct Mutex { diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index afa623f3f5..6f35f779d3 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1259,7 +1259,7 @@ pub fn (mut t Table) bitsize_to_type(bit_size int) Type { } } -pub fn (mut t Table) does_type_implement_interface(typ Type, inter_typ Type) bool { +pub fn (t Table) does_type_implement_interface(typ Type, inter_typ Type) bool { if typ.idx() == inter_typ.idx() { // same type -> already casted to the interface return true diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 217176e26b..2fa2dd3b8a 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -556,7 +556,7 @@ pub enum Kind { thread } -pub fn (t &TypeSymbol) str() string { +pub fn (t TypeSymbol) str() string { return t.name } @@ -641,11 +641,6 @@ pub fn (t &TypeSymbol) is_heap() bool { } } -/* -pub fn (t TypeSymbol) str() string { - return t.name -} -*/ pub fn (mut t Table) register_builtin_type_symbols() { // reserve index 0 so nothing can go there // save index check, 0 will mean not found diff --git a/vlib/v/builder/cc.v b/vlib/v/builder/cc.v index 9188477e71..7b8166f219 100644 --- a/vlib/v/builder/cc.v +++ b/vlib/v/builder/cc.v @@ -550,6 +550,18 @@ fn (mut v Builder) cc() { v.setup_ccompiler_options(ccompiler) v.build_thirdparty_obj_files() v.setup_output_name() + + if v.pref.os != .windows && ccompiler.contains('++') { + for file in v.parsed_files { + if file.imports.any(it.mod.contains('sync')) { + x := @VEXE + ' run ' + + os.join_path(@VEXEROOT, 'thirdparty', 'stdatomic', 'nix', 'cpp', 'gen.v') + + ' ' + ccompiler + os.execute(x) + break + } + } + } // mut libs := []string{} // builtin.o os.o http.o etc if v.pref.build_mode == .build_module { diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index da8ccf29f1..8940edfff4 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -418,7 +418,9 @@ pub fn (mut c Checker) fail_if_unreadable(expr ast.Expr, typ ast.Type, what stri pos = expr.left.position().extend(expr.pos) c.fail_if_unreadable(expr.left, expr.left_type, what) } - else {} + else { + pos = expr.position() + } } if typ.has_flag(.shared_f) { c.error('you have to create a handle and `rlock` it to use a `shared` element as non-mut $what', diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index e223310956..37a5b4bb64 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -226,7 +226,15 @@ pub fn (mut f Fmt) short_module(name string) string { idx := vals.len - 1 mname, tprefix := f.get_modname_prefix(vals[..idx].join('.')) symname := vals[vals.len - 1] - aname := f.mod2alias[mname] + mut aname := f.mod2alias[mname] + if aname == '' { + for _, v in f.mod2alias { + if v == mname { + aname = mname + break + } + } + } if aname == '' { return symname } diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index c7d1ff0a27..1e99e13f49 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -254,7 +254,9 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) { // the only argument can only be an infix expression like `a < b` or `b.field > a.field` if node.args.len == 0 { comparison_type = g.unwrap(info.elem_type.set_nr_muls(0)) - if compare_fn in g.array_sort_fn { + shared a := g.array_sort_fn + array_sort_fn := a.clone() + if compare_fn in array_sort_fn { g.gen_array_sort_call(node, compare_fn) return } @@ -273,7 +275,9 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) { if is_reverse { compare_fn += '_reverse' } - if compare_fn in g.array_sort_fn { + shared a := g.array_sort_fn + array_sort_fn := a.clone() + if compare_fn in array_sort_fn { g.gen_array_sort_call(node, compare_fn) return } @@ -300,8 +304,9 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) { // Register a new custom `compare_xxx` function for qsort() // TODO: move to checker - g.table.register_fn(name: compare_fn, return_type: ast.int_type) - g.array_sort_fn[compare_fn] = true + lock g.array_sort_fn { + g.array_sort_fn << compare_fn + } stype_arg := g.typ(info.elem_type) g.definitions.writeln('int ${compare_fn}($stype_arg* a, $stype_arg* b) {') @@ -485,16 +490,22 @@ fn (mut g Gen) gen_array_prepend(node ast.CallExpr) { } } -fn (mut g Gen) gen_array_contains_method(left_type ast.Type) string { - mut unwrap_left_type := g.unwrap_generic(left_type) - if unwrap_left_type.share() == .shared_t { - unwrap_left_type = unwrap_left_type.clear_flag(.shared_f) - } - mut left_sym := g.table.get_type_symbol(unwrap_left_type) - left_final_sym := g.table.get_final_type_symbol(unwrap_left_type) - mut left_type_str := g.typ(unwrap_left_type).replace('*', '') - fn_name := '${left_type_str}_contains' - if !left_sym.has_method('contains') { +fn (mut g Gen) get_array_contains_method(typ ast.Type) string { + t := g.table.get_final_type_symbol(g.unwrap_generic(typ).set_nr_muls(0)).idx + g.array_contains_types << t + return g.typ(t) + '_contains' +} + +fn (mut g Gen) gen_array_contains_methods() { + mut done := []ast.Type{} + for t in g.array_contains_types { + left_final_sym := g.table.get_final_type_symbol(t) + if left_final_sym.idx in done || g.table.get_type_symbol(t).has_method('contains') { + continue + } + done << t + mut left_type_str := g.typ(t) + fn_name := '${left_type_str}_contains' left_info := left_final_sym.info as ast.Array mut elem_type_str := g.typ(left_info.elem_type) elem_sym := g.table.get_type_symbol(left_info.elem_type) @@ -509,15 +520,15 @@ fn (mut g Gen) gen_array_contains_method(left_type ast.Type) string { if elem_sym.kind == .string { fn_builder.writeln('\t\tif (fast_string_eq(((string*)a.data)[i], v)) {') } else if elem_sym.kind == .array && left_info.elem_type.nr_muls() == 0 { - ptr_typ := g.gen_array_equality_fn(left_info.elem_type) + ptr_typ := g.equality_fn(left_info.elem_type) fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq((($elem_type_str*)a.data)[i], v)) {') } else if elem_sym.kind == .function { fn_builder.writeln('\t\tif (((voidptr*)a.data)[i] == v) {') } else if elem_sym.kind == .map && left_info.elem_type.nr_muls() == 0 { - ptr_typ := g.gen_map_equality_fn(left_info.elem_type) + ptr_typ := g.equality_fn(left_info.elem_type) fn_builder.writeln('\t\tif (${ptr_typ}_map_eq((($elem_type_str*)a.data)[i], v)) {') } else if elem_sym.kind == .struct_ && left_info.elem_type.nr_muls() == 0 { - ptr_typ := g.gen_struct_equality_fn(left_info.elem_type) + ptr_typ := g.equality_fn(left_info.elem_type) fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq((($elem_type_str*)a.data)[i], v)) {') } else { fn_builder.writeln('\t\tif ((($elem_type_str*)a.data)[i] == v) {') @@ -528,41 +539,43 @@ fn (mut g Gen) gen_array_contains_method(left_type ast.Type) string { fn_builder.writeln('\treturn false;') fn_builder.writeln('}') g.auto_fn_definitions << fn_builder.str() - left_sym.register_method(&ast.Fn{ - name: 'contains' - params: [ast.Param{ - typ: unwrap_left_type - }, ast.Param{ - typ: left_info.elem_type - }] - }) } - return fn_name } // `nums.contains(2)` -fn (mut g Gen) gen_array_contains(node ast.CallExpr) { - fn_name := g.gen_array_contains_method(node.left_type) +fn (mut g Gen) gen_array_contains(typ ast.Type, left ast.Expr, right ast.Expr) { + fn_name := g.get_array_contains_method(typ) g.write('${fn_name}(') - if node.left_type.is_ptr() && node.left_type.share() != .shared_t { - g.write('*') + g.write(strings.repeat(`*`, typ.nr_muls())) + if typ.share() == .shared_t { + g.out.go_back(1) } - g.expr(node.left) - if node.left_type.share() == .shared_t { + g.expr(left) + if typ.share() == .shared_t { g.write('->val') } g.write(', ') - g.expr(node.args[0].expr) + g.expr(right) g.write(')') } -fn (mut g Gen) gen_array_index_method(left_type ast.Type) string { - unwrap_left_type := g.unwrap_generic(left_type) - mut left_sym := g.table.get_type_symbol(unwrap_left_type) - mut left_type_str := g.typ(unwrap_left_type).trim('*') - fn_name := '${left_type_str}_index' - if !left_sym.has_method('index') { - info := left_sym.info as ast.Array +fn (mut g Gen) get_array_index_method(typ ast.Type) string { + t := g.unwrap_generic(typ).set_nr_muls(0) + g.array_index_types << t + return g.typ(t) + '_index' +} + +fn (mut g Gen) gen_array_index_methods() { + mut done := []ast.Type{} + for t in g.array_index_types { + if t in done || g.table.get_type_symbol(t).has_method('index') { + continue + } + done << t + final_left_sym := g.table.get_final_type_symbol(t) + mut left_type_str := g.typ(t) + fn_name := '${left_type_str}_index' + info := final_left_sym.info as ast.Array mut elem_type_str := g.typ(info.elem_type) elem_sym := g.table.get_type_symbol(info.elem_type) if elem_sym.kind == .function { @@ -577,15 +590,15 @@ fn (mut g Gen) gen_array_index_method(left_type ast.Type) string { if elem_sym.kind == .string { fn_builder.writeln('\t\tif (fast_string_eq(*pelem, v)) {') } else if elem_sym.kind == .array && !info.elem_type.is_ptr() { - ptr_typ := g.gen_array_equality_fn(info.elem_type) + ptr_typ := g.equality_fn(info.elem_type) fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq( *pelem, v)) {') } else if elem_sym.kind == .function && !info.elem_type.is_ptr() { fn_builder.writeln('\t\tif ( pelem == v) {') } else if elem_sym.kind == .map && !info.elem_type.is_ptr() { - ptr_typ := g.gen_map_equality_fn(info.elem_type) + ptr_typ := g.equality_fn(info.elem_type) fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(( *pelem, v))) {') } else if elem_sym.kind == .struct_ && !info.elem_type.is_ptr() { - ptr_typ := g.gen_struct_equality_fn(info.elem_type) + ptr_typ := g.equality_fn(info.elem_type) fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq( *pelem, v)) {') } else { fn_builder.writeln('\t\tif (*pelem == v) {') @@ -596,21 +609,12 @@ fn (mut g Gen) gen_array_index_method(left_type ast.Type) string { fn_builder.writeln('\treturn -1;') fn_builder.writeln('}') g.auto_fn_definitions << fn_builder.str() - left_sym.register_method(&ast.Fn{ - name: 'index' - params: [ast.Param{ - typ: unwrap_left_type - }, ast.Param{ - typ: info.elem_type - }] - }) } - return fn_name } // `nums.index(2)` fn (mut g Gen) gen_array_index(node ast.CallExpr) { - fn_name := g.gen_array_index_method(node.left_type) + fn_name := g.get_array_index_method(node.left_type) g.write('${fn_name}(') if node.left_type.is_ptr() { g.write('*') diff --git a/vlib/v/gen/c/assert.v b/vlib/v/gen/c/assert.v index 5779177090..856afea800 100644 --- a/vlib/v/gen/c/assert.v +++ b/vlib/v/gen/c/assert.v @@ -20,7 +20,7 @@ fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) { } } g.inside_ternary++ - if g.is_test { + if g.pref.is_test { g.write('if (') g.expr(node.expr) g.write(')') diff --git a/vlib/v/gen/c/auto_eq_methods.v b/vlib/v/gen/c/auto_eq_methods.v index dc83b30b51..0811f71438 100644 --- a/vlib/v/gen/c/auto_eq_methods.v +++ b/vlib/v/gen/c/auto_eq_methods.v @@ -5,13 +5,52 @@ module c import strings import v.ast +fn (mut g Gen) equality_fn(typ ast.Type) string { + g.needed_equality_fns << typ.set_nr_muls(0) + return g.typ(g.unwrap_generic(typ).set_nr_muls(0)) +} + +fn (mut g Gen) gen_equality_fns() { + for needed_typ in g.needed_equality_fns { + if needed_typ in g.generated_eq_fns { + continue + } + sym := g.table.get_type_symbol(needed_typ) + match sym.kind { + .sum_type { + g.gen_sumtype_equality_fn(needed_typ) + } + .struct_ { + g.gen_struct_equality_fn(needed_typ) + } + .array { + g.gen_array_equality_fn(needed_typ) + } + .array_fixed { + g.gen_fixed_array_equality_fn(needed_typ) + } + .map { + g.gen_map_equality_fn(needed_typ) + } + .alias { + g.gen_alias_equality_fn(needed_typ) + } + else { + verror('could not generate equality function for type $sym.kind') + } + } + } +} + fn (mut g Gen) gen_sumtype_equality_fn(left_type ast.Type) string { left := g.unwrap(left_type) ptr_styp := g.typ(left.typ.set_nr_muls(0)) - if ptr_styp in g.sumtype_fn_definitions { + + if left_type in g.generated_eq_fns { return ptr_styp } - g.sumtype_fn_definitions << ptr_styp + g.generated_eq_fns << left_type + info := left.sym.sumtype_info() g.type_definitions.writeln('static bool ${ptr_styp}_sumtype_eq($ptr_styp a, $ptr_styp b); // auto') @@ -59,10 +98,10 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string { left := g.unwrap(left_type) ptr_styp := g.typ(left.typ.set_nr_muls(0)) fn_name := ptr_styp.replace('struct ', '') - if fn_name in g.struct_fn_definitions { + if left_type in g.generated_eq_fns { return fn_name } - g.struct_fn_definitions << fn_name + g.generated_eq_fns << left_type info := left.sym.struct_info() g.type_definitions.writeln('static bool ${fn_name}_struct_eq($ptr_styp a, $ptr_styp b); // auto') @@ -124,10 +163,10 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string { fn (mut g Gen) gen_alias_equality_fn(left_type ast.Type) string { left := g.unwrap(left_type) ptr_styp := g.typ(left.typ.set_nr_muls(0)) - if ptr_styp in g.alias_fn_definitions { + if left_type in g.generated_eq_fns { return ptr_styp } - g.alias_fn_definitions << ptr_styp + g.generated_eq_fns << left_type info := left.sym.info as ast.Alias g.type_definitions.writeln('static bool ${ptr_styp}_alias_eq($ptr_styp a, $ptr_styp b); // auto') @@ -164,10 +203,10 @@ fn (mut g Gen) gen_alias_equality_fn(left_type ast.Type) string { fn (mut g Gen) gen_array_equality_fn(left_type ast.Type) string { left := g.unwrap(left_type) ptr_styp := g.typ(left.typ.set_nr_muls(0)) - if ptr_styp in g.array_fn_definitions { + if left_type in g.generated_eq_fns { return ptr_styp } - g.array_fn_definitions << ptr_styp + g.generated_eq_fns << left_type elem := g.unwrap(left.sym.array_info().elem_type) ptr_elem_styp := g.typ(elem.typ) g.type_definitions.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b); // auto') @@ -216,10 +255,10 @@ fn (mut g Gen) gen_array_equality_fn(left_type ast.Type) string { fn (mut g Gen) gen_fixed_array_equality_fn(left_type ast.Type) string { left := g.unwrap(left_type) ptr_styp := g.typ(left.typ.set_nr_muls(0)) - if ptr_styp in g.array_fn_definitions { + if left_type in g.generated_eq_fns { return ptr_styp } - g.array_fn_definitions << ptr_styp + g.generated_eq_fns << left_type elem_info := left.sym.array_fixed_info() elem := g.unwrap(elem_info.elem_type) size := elem_info.size @@ -266,10 +305,10 @@ fn (mut g Gen) gen_fixed_array_equality_fn(left_type ast.Type) string { fn (mut g Gen) gen_map_equality_fn(left_type ast.Type) string { left := g.unwrap(left_type) ptr_styp := g.typ(left.typ.set_nr_muls(0)) - if ptr_styp in g.map_fn_definitions { + if left_type in g.generated_eq_fns { return ptr_styp } - g.map_fn_definitions << ptr_styp + g.generated_eq_fns << left_type value := g.unwrap(left.sym.map_info().value_type) ptr_value_styp := g.typ(value.typ) g.type_definitions.writeln('static bool ${ptr_styp}_map_eq($ptr_styp a, $ptr_styp b); // auto') diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v index aa9ddcc4a3..7da606605c 100644 --- a/vlib/v/gen/c/auto_str_methods.v +++ b/vlib/v/gen/c/auto_str_methods.v @@ -117,9 +117,26 @@ fn (mut g Gen) gen_str_default(sym ast.TypeSymbol, styp string, str_fn_name stri g.auto_str_funcs.writeln('}') } -fn (mut g Gen) gen_str_method_for_type(typ ast.Type) string { - styp := g.typ(typ).replace('*', '') - mut sym := g.table.get_type_symbol(g.unwrap_generic(typ)) +struct StrType { + styp string +mut: + typ ast.Type +} + +fn (mut g Gen) get_str_fn(typ ast.Type) string { + mut unwrapped := g.unwrap_generic(typ).set_nr_muls(0).clear_flag(.variadic) + if g.pref.nofloat { + if typ == ast.f32_type { + unwrapped = ast.u32_type + } else if typ == ast.f64_type { + unwrapped = ast.u64_type + } + } + if typ.has_flag(.optional) { + unwrapped.set_flag(.optional) + } + styp := g.typ(unwrapped) + mut sym := g.table.get_type_symbol(unwrapped) mut str_fn_name := styp_to_str_fn_name(styp) if mut sym.info is ast.Alias { if sym.info.is_import { @@ -127,85 +144,80 @@ fn (mut g Gen) gen_str_method_for_type(typ ast.Type) string { str_fn_name = styp_to_str_fn_name(sym.name) } } - sym_has_str_method, str_method_expects_ptr, str_nr_args := sym.str_method_info() - already_generated_key := '$styp:$str_fn_name' - if !sym_has_str_method && already_generated_key !in g.str_types && !typ.has_flag(.optional) { - $if debugautostr ? { - eprintln('> gen_str_for_type: |typ: ${typ:5}, ${sym.name:20}|has_str: ${sym_has_str_method:5}|expects_ptr: ${str_method_expects_ptr:5}|nr_args: ${str_nr_args:1}|fn_name: ${str_fn_name:20}') - } - g.str_types << already_generated_key - if g.pref.nofloat { - if sym.name == 'f32' { - return g.gen_str_method_for_type(ast.u32_type) - // return '' - } else if sym.name == 'f64' { - return g.gen_str_method_for_type(ast.u64_type) - // return '' - } - } - match mut sym.info { - ast.Alias { - if sym.info.is_import { - g.gen_str_default(sym, styp, str_fn_name) - } else { - g.gen_str_for_alias(sym.info, styp, str_fn_name) - } - } - ast.Array { - g.gen_str_for_array(sym.info, styp, str_fn_name) - } - ast.ArrayFixed { - g.gen_str_for_array_fixed(sym.info, styp, str_fn_name) - } - ast.Enum { - g.gen_str_for_enum(sym.info, styp, str_fn_name) - } - ast.FnType { - g.gen_str_for_fn_type(sym.info, styp, str_fn_name) - } - ast.Struct { - g.gen_str_for_struct(sym.info, styp, str_fn_name) - } - ast.Map { - g.gen_str_for_map(sym.info, styp, str_fn_name) - } - ast.MultiReturn { - g.gen_str_for_multi_return(sym.info, styp, str_fn_name) - } - ast.SumType { - g.gen_str_for_union_sum_type(sym.info, styp, str_fn_name) - } - ast.Interface { - g.gen_str_for_interface(sym.info, styp, str_fn_name) - } - ast.Chan { - g.gen_str_for_chan(sym.info, styp, str_fn_name) - } - ast.Thread { - g.gen_str_for_thread(sym.info, styp, str_fn_name) - } - else { - println(g.table.type_str(typ)) - verror("1could not generate string method '$str_fn_name' for type '$styp'") - } - } - } - if typ.has_flag(.optional) { - option_already_generated_key := 'option_$already_generated_key' - if option_already_generated_key !in g.str_types { - g.gen_str_for_option(typ, styp, str_fn_name) - g.str_types << option_already_generated_key - } - return str_fn_name + g.str_types << StrType{ + typ: unwrapped + styp: styp } return str_fn_name } +fn (mut g Gen) final_gen_str(typ StrType) { + if typ in g.generated_str_fns { + return + } + g.generated_str_fns << typ + sym := g.table.get_type_symbol(typ.typ) + if sym.has_method('str') && !typ.typ.has_flag(.optional) { + return + } + styp := typ.styp + str_fn_name := styp_to_str_fn_name(styp) + if typ.typ.has_flag(.optional) { + g.gen_str_for_option(typ.typ, styp, str_fn_name) + return + } + match mut sym.info { + ast.Alias { + if sym.info.is_import { + g.gen_str_default(sym, styp, str_fn_name) + } else { + g.gen_str_for_alias(sym.info, styp, str_fn_name) + } + } + ast.Array { + g.gen_str_for_array(sym.info, styp, str_fn_name) + } + ast.ArrayFixed { + g.gen_str_for_array_fixed(sym.info, styp, str_fn_name) + } + ast.Enum { + g.gen_str_for_enum(sym.info, styp, str_fn_name) + } + ast.FnType { + g.gen_str_for_fn_type(sym.info, styp, str_fn_name) + } + ast.Struct { + g.gen_str_for_struct(sym.info, styp, str_fn_name) + } + ast.Map { + g.gen_str_for_map(sym.info, styp, str_fn_name) + } + ast.MultiReturn { + g.gen_str_for_multi_return(sym.info, styp, str_fn_name) + } + ast.SumType { + g.gen_str_for_union_sum_type(sym.info, styp, str_fn_name) + } + ast.Interface { + g.gen_str_for_interface(sym.info, styp, str_fn_name) + } + ast.Chan { + g.gen_str_for_chan(sym.info, styp, str_fn_name) + } + ast.Thread { + g.gen_str_for_thread(sym.info, styp, str_fn_name) + } + else { + verror("could not generate string method $str_fn_name for type '$styp'") + } + } +} + fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string) { parent_type := typ.clear_flag(.optional) sym := g.table.get_type_symbol(parent_type) sym_has_str_method, _, _ := sym.str_method_info() - parent_str_fn_name := g.gen_str_method_for_type(parent_type) + parent_str_fn_name := g.get_str_fn(parent_type) g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }') @@ -232,7 +244,7 @@ fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string) } fn (mut g Gen) gen_str_for_alias(info ast.Alias, styp string, str_fn_name string) { - parent_str_fn_name := g.gen_str_method_for_type(info.parent_type) + parent_str_fn_name := g.get_str_fn(info.parent_type) mut clean_type_v_type_name := util.strip_main_name(styp.replace('__', '.')) g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto') g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }') @@ -261,7 +273,7 @@ fn (mut g Gen) gen_str_for_multi_return(info ast.MultiReturn, styp string, str_f sym := g.table.get_type_symbol(typ) is_arg_ptr := typ.is_ptr() sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() - arg_str_fn_name := g.gen_str_method_for_type(typ) + arg_str_fn_name := g.get_str_fn(typ) if should_use_indent_func(sym.kind) && !sym_has_str_method { fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}(a.arg$i));') @@ -346,7 +358,7 @@ fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, str_fn_nam fn_builder.writeln('static string indent_${str_fn_name}($styp x, int indent_count) { /* gen_str_for_interface */') for typ in info.types { subtype := g.table.get_type_symbol(typ) - mut func_name := g.gen_str_method_for_type(typ) + mut func_name := g.get_str_fn(typ) sym_has_str_method, str_method_expects_ptr, _ := subtype.str_method_info() if should_use_indent_func(subtype.kind) && !sym_has_str_method { func_name = 'indent_$func_name' @@ -405,7 +417,7 @@ fn (mut g Gen) gen_str_for_union_sum_type(info ast.SumType, styp string, str_fn_ fn_builder.writeln('\tswitch(x._typ) {') for typ in info.variants { typ_str := g.typ(typ) - mut func_name := g.gen_str_method_for_type(typ) + mut func_name := g.get_str_fn(typ) sym := g.table.get_type_symbol(typ) sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' } @@ -512,7 +524,7 @@ fn (mut g Gen) gen_str_for_array(info ast.Array, styp string, str_fn_name string field_styp := g.typ(typ) is_elem_ptr := typ.is_ptr() sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() - mut elem_str_fn_name := g.gen_str_method_for_type(typ) + mut elem_str_fn_name := g.get_str_fn(typ) if sym.kind == .byte { elem_str_fn_name = elem_str_fn_name + '_escaped' } @@ -584,7 +596,7 @@ fn (mut g Gen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_fn_ } is_elem_ptr := typ.is_ptr() sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() - elem_str_fn_name := g.gen_str_method_for_type(typ) + elem_str_fn_name := g.get_str_fn(typ) g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto') g.auto_str_funcs.writeln('static string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}') @@ -645,7 +657,7 @@ fn (mut g Gen) gen_str_for_map(info ast.Map, styp string, str_fn_name string) { key_styp := g.typ(key_typ) key_str_fn_name := key_styp.replace('*', '') + '_str' if !key_sym.has_method('str') { - g.gen_str_method_for_type(key_typ) + g.get_str_fn(key_typ) } mut val_typ := info.value_type @@ -657,7 +669,7 @@ fn (mut g Gen) gen_str_for_map(info ast.Map, styp string, str_fn_name string) { val_styp := g.typ(val_typ) elem_str_fn_name := val_styp.replace('*', '') + '_str' if !val_sym.has_method('str') { - g.gen_str_method_for_type(val_typ) + g.get_str_fn(val_typ) } g.type_definitions.writeln('static string ${str_fn_name}($styp m); // auto') @@ -813,7 +825,7 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, styp string, str_fn_name stri field_styp_fn_name := if has_custom_str { '${field_styp}_str' } else { - g.gen_str_method_for_type(field.typ) + g.get_str_fn(field.typ) } // manage the fact hat with float we use always the g representation diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 2f1757a982..b1b9746d46 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -11,6 +11,7 @@ import v.token import v.util import v.util.version import v.depgraph +import sync.pool const ( // NB: some of the words in c_reserved, are not reserved in C, @@ -37,11 +38,12 @@ fn string_array_to_map(a []string) map[string]bool { } struct Gen { - pref &pref.Preferences - module_built string - field_data_type ast.Type // cache her to avoid map lookups + pref &pref.Preferences + field_data_type ast.Type // cache her to avoid map lookups + module_built string + timers_should_print bool + table &ast.Table mut: - table &ast.Table out strings.Builder cheaders strings.Builder includes strings.Builder // all C #includes required by V modules @@ -50,7 +52,10 @@ mut: type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) global_inits map[string]strings.Builder // default initializers for globals (goes in _vinit()) + global_init strings.Builder // thread local of the above inits map[string]strings.Builder // contents of `void _vinit/2{}` + init strings.Builder + cleanup strings.Builder cleanups map[string]strings.Builder // contents of `void _vcleanup(){}` gowrappers strings.Builder // all go callsite wrappers stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined @@ -62,7 +67,6 @@ mut: shared_types strings.Builder // shared/lock types shared_functions strings.Builder // shared constructors channel_definitions strings.Builder // channel related code - options_typedefs strings.Builder // Option typedefs options strings.Builder // `Option_xxxx` types json_forward_decls strings.Builder // json type forward decls enum_typedefs strings.Builder // enum types @@ -70,32 +74,33 @@ mut: file &ast.File fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 last_fn_c_name string - tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn. - tmp_count2 int // a separate tmp var counter for autofree fn calls - tmp_count_declarations int // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations - global_tmp_count int // like tmp_count but global and not resetted in each function - is_assign_lhs bool // inside left part of assign expr (for array_set(), etc) - discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage - is_void_expr_stmt bool // ExprStmt whos result is discarded - is_arraymap_set bool // map or array set value state - is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc - is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc) - is_shared bool // for initialization of hidden mutex in `[rw]shared` literals - is_vlines_enabled bool // is it safe to generate #line directives when -g is passed - inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls) - arraymap_set_pos int // map or array set value position - vlines_path string // set to the proper path for generating #line directives - optionals []string // to avoid duplicates TODO perf, use map - chan_pop_optionals []string // types for `x := <-ch or {...}` - chan_push_optionals []string // types for `ch <- x or {...}` + tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn. + tmp_count2 int // a separate tmp var counter for autofree fn calls + tmp_count_declarations int // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations + global_tmp_count int // like tmp_count but global and not resetted in each function + is_assign_lhs bool // inside left part of assign expr (for array_set(), etc) + discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage + is_void_expr_stmt bool // ExprStmt whos result is discarded + is_arraymap_set bool // map or array set value state + is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc + is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc) + is_shared bool // for initialization of hidden mutex in `[rw]shared` literals + is_vlines_enabled bool // is it safe to generate #line directives when -g is passed + inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls) + arraymap_set_pos int // map or array set value position + vlines_path string // set to the proper path for generating #line directives + optionals map[string]string // to avoid duplicates + done_optionals shared []string // to avoid duplicates + chan_pop_optionals map[string]string // types for `x := <-ch or {...}` + chan_push_optionals map[string]string // types for `ch <- x or {...}` cur_lock ast.LockExpr mtxs string // array of mutexes if the `lock` has multiple variables labeled_loops map[string]&ast.Stmt inner_loop &ast.Stmt - shareds []int // types with hidden mutex for which decl has been emitted - inside_ternary int // ?: comma separated statements on a single line - inside_map_postfix bool // inside map++/-- postfix expr - inside_map_infix bool // inside map< fn counter name is_builtin_mod bool hotcode_fn_names []string @@ -177,11 +178,18 @@ mut: as_cast_type_names map[string]string // table for type name lookup in runtime (for __as_cast) obf_table map[string]string // main_fn_decl_node ast.FnDecl - expected_cast_type ast.Type // for match expr of sumtypes - defer_vars []string - anon_fn bool - array_sort_fn map[string]bool - nr_closures int + nr_closures int + array_sort_fn shared []string + expected_cast_type ast.Type // for match expr of sumtypes + defer_vars []string + anon_fn bool + tests_inited bool + cur_concrete_types []ast.Type // do not use table.cur_concrete_types because table is global, so should not be accessed by different threads + cur_fn &ast.FnDecl = 0 // same here + needed_equality_fns []ast.Type + generated_eq_fns []ast.Type + array_contains_types []ast.Type + array_index_types []ast.Type } pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { @@ -200,7 +208,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { $if time_cgening ? { timers_should_print = true } - mut g := Gen{ + mut global_g := Gen{ file: 0 out: strings.new_builder(512000) cheaders: strings.new_builder(15000) @@ -216,7 +224,6 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { pcs_declarations: strings.new_builder(100) hotcode_definitions: strings.new_builder(100) embedded_data: strings.new_builder(1000) - options_typedefs: strings.new_builder(100) options: strings.new_builder(100) shared_types: strings.new_builder(100) shared_functions: strings.new_builder(100) @@ -227,61 +234,182 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { table: table pref: pref fn_decl: 0 - is_autofree: true + is_autofree: pref.autofree indent: -1 module_built: module_built + timers_should_print: timers_should_print timers: util.new_timers(timers_should_print) inner_loop: &ast.EmptyStmt{} field_data_type: ast.Type(table.find_type_idx('FieldData')) + init: strings.new_builder(100) } - g.timers.start('cgen init') - for mod in g.table.modules { - g.inits[mod] = strings.new_builder(100) - g.global_inits[mod] = strings.new_builder(100) - g.cleanups[mod] = strings.new_builder(100) + // anon fn may include assert and thus this needs + // to be included before any test contents are written + if pref.is_test { + global_g.write_tests_definitions() } - g.init() - g.timers.show('cgen init') - mut tests_inited := false - mut autofree_used := false - for file in files { - g.timers.start('cgen_file $file.path') - g.file = file - if g.pref.is_vlines { - g.vlines_path = util.vlines_escape_path(file.path, g.pref.ccompiler) - } - // println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len') - // building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v')) - g.is_test = g.pref.is_test - if g.file.path == '' || !g.pref.autofree { - // cgen test or building V - // println('autofree=false') - g.is_autofree = false - } else { - g.is_autofree = true - autofree_used = true - } - // anon fn may include assert and thus this needs - // to be included before any test contents are written - if g.is_test && !tests_inited { - g.write_tests_definitions() - tests_inited = true - } - g.stmts(file.stmts) - // Transfer embedded files - if file.embedded_files.len > 0 { - for path in file.embedded_files { - if path !in g.embedded_files { - g.embedded_files << path + + global_g.timers.start('cgen init') + for mod in global_g.table.modules { + global_g.inits[mod] = strings.new_builder(200) + global_g.global_inits[mod] = strings.new_builder(100) + global_g.cleanups[mod] = strings.new_builder(100) + } + global_g.init() + global_g.timers.show('cgen init') + global_g.tests_inited = false + if !pref.no_parallel { + mut pp := pool.new_pool_processor( + callback: fn (p &pool.PoolProcessor, idx int, wid int) &Gen { + file := p.get_item<&ast.File>(idx) + mut global_g := &Gen(p.get_shared_context()) + mut g := &Gen{ + file: file + out: strings.new_builder(512000) + cheaders: strings.new_builder(15000) + includes: strings.new_builder(100) + typedefs: strings.new_builder(100) + typedefs2: strings.new_builder(100) + type_definitions: strings.new_builder(100) + definitions: strings.new_builder(100) + gowrappers: strings.new_builder(100) + stringliterals: strings.new_builder(100) + auto_str_funcs: strings.new_builder(100) + comptime_defines: strings.new_builder(100) + pcs_declarations: strings.new_builder(100) + hotcode_definitions: strings.new_builder(100) + embedded_data: strings.new_builder(1000) + options: strings.new_builder(100) + shared_types: strings.new_builder(100) + shared_functions: strings.new_builder(100) + channel_definitions: strings.new_builder(100) + json_forward_decls: strings.new_builder(100) + enum_typedefs: strings.new_builder(100) + sql_buf: strings.new_builder(100) + init: strings.new_builder(100) + global_init: strings.new_builder(0) + cleanup: strings.new_builder(100) + table: global_g.table + pref: global_g.pref + fn_decl: 0 + indent: -1 + module_built: global_g.module_built + timers: util.new_timers(global_g.timers_should_print) + inner_loop: &ast.EmptyStmt{} + field_data_type: ast.Type(global_g.table.find_type_idx('FieldData')) + array_sort_fn: global_g.array_sort_fn + done_optionals: global_g.done_optionals + is_autofree: global_g.pref.autofree + } + g.gen() + return g + } + ) + pp.set_shared_context(global_g) // TODO: make global_g shared + pp.work_on_items(files) + global_g.timers.start('cgen unification') + // tg = thread gen + for g in pp.get_results() { + global_g.out.write(g.out) or { panic(err) } + global_g.cheaders.write(g.cheaders) or { panic(err) } + global_g.includes.write(g.includes) or { panic(err) } + global_g.typedefs.write(g.typedefs) or { panic(err) } + global_g.typedefs2.write(g.typedefs2) or { panic(err) } + global_g.type_definitions.write(g.type_definitions) or { panic(err) } + global_g.definitions.write(g.definitions) or { panic(err) } + global_g.gowrappers.write(g.gowrappers) or { panic(err) } + global_g.stringliterals.write(g.stringliterals) or { panic(err) } + global_g.auto_str_funcs.write(g.auto_str_funcs) or { panic(err) } + global_g.comptime_defines.write(g.comptime_defines) or { panic(err) } + global_g.pcs_declarations.write(g.pcs_declarations) or { panic(err) } + global_g.hotcode_definitions.write(g.hotcode_definitions) or { panic(err) } + global_g.embedded_data.write(g.embedded_data) or { panic(err) } + global_g.shared_types.write(g.shared_types) or { panic(err) } + global_g.shared_functions.write(g.channel_definitions) or { panic(err) } + // merge maps + for k, v in g.shareds { + global_g.shareds[k] = v + } + for k, v in g.chan_pop_optionals { + global_g.chan_pop_optionals[k] = v + } + for k, v in g.chan_push_optionals { + global_g.chan_push_optionals[k] = v + } + for k, v in g.optionals { + global_g.optionals[k] = v + } + for k, v in g.as_cast_type_names { + global_g.as_cast_type_names[k] = v + } + for k, v in g.sumtype_definitions { + global_g.sumtype_definitions[k] = v + } + global_g.json_forward_decls.write(g.json_forward_decls) or { panic(err) } + global_g.enum_typedefs.write(g.enum_typedefs) or { panic(err) } + global_g.channel_definitions.write(g.channel_definitions) or { panic(err) } + global_g.sql_buf.write(g.sql_buf) or { panic(err) } + + global_g.cleanups[g.file.mod.name].write(g.cleanup) or { panic(err) } // strings.Builder.write never fails; it is like that in the source + global_g.inits[g.file.mod.name].write(g.init) or { panic(err) } + global_g.global_inits[g.file.mod.name].write(g.global_init) or { panic(err) } + + for str_type in g.str_types { + global_g.str_types << str_type + } + for scf in g.sumtype_casting_fns { + if scf !in global_g.sumtype_casting_fns { + global_g.sumtype_casting_fns << scf } } + + global_g.nr_closures += g.nr_closures + global_g.has_main = global_g.has_main || g.has_main + + global_g.threaded_fns << g.threaded_fns + global_g.waiter_fns << g.waiter_fns + global_g.auto_fn_definitions << g.auto_fn_definitions + global_g.anon_fn_definitions << g.anon_fn_definitions + global_g.needed_equality_fns << g.needed_equality_fns // duplicates are resolved later in gen_equality_fns + global_g.array_contains_types << g.array_contains_types + global_g.array_index_types << g.array_index_types + global_g.pcs << g.pcs + global_g.json_types << g.json_types + global_g.hotcode_fn_names << g.hotcode_fn_names } - g.timers.show('cgen_file $file.path') + } else { + for file in files { + global_g.file = file + global_g.gen() + global_g.inits[file.mod.name].write(global_g.init) or { panic(err) } + global_g.init = strings.new_builder(100) + global_g.cleanups[file.mod.name].write(global_g.cleanup) or { panic(err) } + global_g.cleanup = strings.new_builder(100) + global_g.global_inits[file.mod.name].write(global_g.global_init) or { panic(err) } + global_g.global_init = strings.new_builder(100) + } + global_g.timers.start('cgen unification') } + + global_g.gen_jsons() + global_g.write_optionals() + global_g.dump_expr_definitions() // this uses global_g.get_str_fn, so it has to go before the below for loop + for i := 0; i < global_g.str_types.len; i++ { + global_g.final_gen_str(global_g.str_types[i]) + } + for sumtype_casting_fn in global_g.sumtype_casting_fns { + global_g.write_sumtype_casting_fn(sumtype_casting_fn) + } + global_g.write_shareds() + global_g.write_chan_pop_optional_fns() + global_g.write_chan_push_optional_fns() + global_g.gen_array_contains_methods() + global_g.gen_array_index_methods() + global_g.gen_equality_fns() + global_g.timers.show('cgen unification') + + mut g := global_g g.timers.start('cgen common') - if autofree_used { - g.is_autofree = true // so that void _vcleanup is generated - } // to make sure type idx's are the same in cached mods if g.pref.build_mode == .build_module { for idx, typ in g.table.type_symbols { @@ -299,7 +427,6 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { } } // - g.dump_expr_definitions() // v files are finished, what remains is pure C code g.gen_vlines_reset() if g.pref.build_mode != .build_module { @@ -354,10 +481,6 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { b.writeln('\n// V embedded data:') b.write_string(g.embedded_data.str()) } - if g.options_typedefs.len > 0 { - b.writeln('\n// V option typedefs:') - b.write_string(g.options_typedefs.str()) - } if g.shared_functions.len > 0 { b.writeln('\n// V shared type functions:') b.write_string(g.shared_functions.str()) @@ -398,6 +521,22 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { return b.str() } +pub fn (mut g Gen) gen() { + g.timers.start('cgen_file $g.file.path') + + if g.pref.is_vlines { + g.vlines_path = util.vlines_escape_path(g.file.path, g.pref.ccompiler) + } + g.stmts(g.file.stmts) + // Transfer embedded files + for path in g.file.embedded_files { + if path !in g.embedded_files { + g.embedded_files << path + } + } + g.timers.show('cgen_file $g.file.path') +} + pub fn (g &Gen) hashes() string { mut res := c_commit_hash_default.replace('@@@', version.vhash()) res += c_current_commit_hash_default.replace('@@@', version.githash(g.pref.building_v)) @@ -508,6 +647,13 @@ pub fn (mut g Gen) init() { } } } + // we know that this is being called before the multi-threading starts + // and this is being called in the main thread, so we can mutate the table + mut muttable := unsafe { &ast.Table(g.table) } + muttable.used_fns['v_segmentation_fault_handler'] = true + muttable.used_fns['eprintln'] = true + muttable.used_fns['print_backtrace'] = true + muttable.used_fns['exit'] = true } pub fn (mut g Gen) finish() { @@ -610,20 +756,16 @@ pub fn (mut g Gen) write_typeof_functions() { // V type to C typecc fn (mut g Gen) typ(t ast.Type) string { - styp := g.base_type(t) if t.has_flag(.optional) { // Register an optional if it's not registered yet return g.register_optional(t) + } else { + return g.base_type(t) } - /* - if styp.starts_with('C__') { - return styp[3..] - } - */ - return styp } -fn (mut g Gen) base_type(t ast.Type) string { +fn (mut g Gen) base_type(_t ast.Type) string { + t := g.unwrap_generic(_t) if g.pref.nofloat { // todo compile time if for perf? if t == ast.f32_type { @@ -692,7 +834,7 @@ fn (mut g Gen) optional_type_name(t ast.Type) (string, string) { return styp, base } -fn (g &Gen) optional_type_text(styp string, base string) string { +fn (g Gen) optional_type_text(styp string, base string) string { // replace void with something else size := if base == 'void' { 'byte' } else { base } ret := 'struct $styp { @@ -705,32 +847,44 @@ fn (g &Gen) optional_type_text(styp string, base string) string { fn (mut g Gen) register_optional(t ast.Type) string { styp, base := g.optional_type_name(t) - if styp !in g.optionals { - g.typedefs2.writeln('typedef struct $styp $styp;') - g.options.write_string(g.optional_type_text(styp, base)) - g.options.writeln(';\n') - g.optionals << styp.clone() - } + g.optionals[base] = styp return styp } -fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string { - sh_typ := '__shared__$base' - t_idx := t.idx() - if t_idx in g.shareds { - return sh_typ +fn (mut g Gen) write_optionals() { + mut done := g.done_optionals.clone() + for base, styp in g.optionals { + if base in done { + continue + } + done << base + g.typedefs2.writeln('typedef struct $styp $styp;') + g.options.write_string(g.optional_type_text(styp, base) + ';\n\n') + } +} + +fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string { + g.shareds[t.idx()] = base + return '__shared__$base' +} + +fn (mut g Gen) write_shareds() { + mut done_types := []int{} + for typ, base in g.shareds { + if typ in done_types { + continue + } + done_types << typ + sh_typ := '__shared__$base' + mtx_typ := 'sync__RwMutex' + g.shared_types.writeln('struct $sh_typ { $mtx_typ mtx; $base val; };') + g.shared_functions.writeln('static inline voidptr __dup${sh_typ}(voidptr src, int sz) {') + g.shared_functions.writeln('\t$sh_typ* dest = memdup(src, sz);') + g.shared_functions.writeln('\tsync__RwMutex_init(&dest->mtx);') + g.shared_functions.writeln('\treturn dest;') + g.shared_functions.writeln('}') + g.typedefs2.writeln('typedef struct $sh_typ $sh_typ;') } - mtx_typ := 'sync__RwMutex' - g.shared_types.writeln('struct $sh_typ { $base val; $mtx_typ mtx; };') - g.shared_functions.writeln('static inline voidptr __dup${sh_typ}(voidptr src, int sz) {') - g.shared_functions.writeln('\t$sh_typ* dest = memdup(src, sz);') - g.shared_functions.writeln('\tsync__RwMutex_init(&dest->mtx);') - g.shared_functions.writeln('\treturn dest;') - g.shared_functions.writeln('}') - g.typedefs2.writeln('typedef struct $sh_typ $sh_typ;') - // println('registered shared type $sh_typ') - g.shareds << t_idx - return sh_typ } fn (mut g Gen) register_thread_array_wait_call(eltyp string) string { @@ -765,8 +919,16 @@ $ret_typ ${fn_name}($thread_arr_typ a) { } fn (mut g Gen) register_chan_pop_optional_call(opt_el_type string, styp string) { - if opt_el_type !in g.chan_pop_optionals { - g.chan_pop_optionals << opt_el_type + g.chan_pop_optionals[opt_el_type] = styp +} + +fn (mut g Gen) write_chan_pop_optional_fns() { + mut done := []string{} + for opt_el_type, styp in g.chan_pop_optionals { + if opt_el_type in done { + continue + } + done << opt_el_type g.channel_definitions.writeln(' static inline $opt_el_type __Option_${styp}_popval($styp ch) { $opt_el_type _tmp = {0}; @@ -778,9 +940,17 @@ static inline $opt_el_type __Option_${styp}_popval($styp ch) { } } -fn (mut g Gen) register_chan_push_optional_call(el_type string, styp string) { - if styp !in g.chan_push_optionals { - g.chan_push_optionals << styp +fn (mut g Gen) register_chan_push_optional_fn(el_type string, styp string) { + g.chan_push_optionals[styp] = el_type +} + +fn (mut g Gen) write_chan_push_optional_fns() { + mut done := []string{} + for styp, el_type in g.chan_push_optionals { + if styp in done { + continue + } + done << styp g.register_optional(ast.void_type.set_flag(.optional)) g.channel_definitions.writeln(' static inline Option_void __Option_${styp}_pushval($styp ch, $el_type e) { @@ -828,7 +998,7 @@ fn (g &Gen) type_sidx(t ast.Type) string { sym := g.table.get_type_symbol(t) return '_v_type_idx_${sym.cname}()' } - return '$t.idx()' + return t.idx().str() } // @@ -1920,18 +2090,35 @@ fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { } } -fn (mut g Gen) write_sumtype_casting_fn(got_ ast.Type, exp_ ast.Type) { +struct SumtypeCastingFn { + fn_name string + got ast.Type + exp ast.Type +} + +fn (mut g Gen) get_sumtype_casting_fn(got_ ast.Type, exp_ ast.Type) string { got, exp := got_.idx(), exp_.idx() i := got | (exp << 16) + got_cname, exp_cname := g.table.get_type_symbol(got).cname, g.table.get_type_symbol(exp).cname + fn_name := '${got_cname}_to_sumtype_$exp_cname' if got == exp || g.sumtype_definitions[i] { - return + return fn_name } g.sumtype_definitions[i] = true - got_sym := g.table.get_type_symbol(got) - exp_sym := g.table.get_type_symbol(exp) - mut sb := strings.new_builder(128) + g.sumtype_casting_fns << SumtypeCastingFn{ + fn_name: fn_name + got: got + exp: exp + } + return fn_name +} + +fn (mut g Gen) write_sumtype_casting_fn(fun SumtypeCastingFn) { + got, exp := fun.got, fun.exp + got_sym, exp_sym := g.table.get_type_symbol(got), g.table.get_type_symbol(exp) got_cname, exp_cname := got_sym.cname, exp_sym.cname - sb.writeln('static inline $exp_cname ${got_cname}_to_sumtype_${exp_cname}($got_cname* x) {') + mut sb := strings.new_builder(128) + sb.writeln('static inline $exp_cname ${fun.fn_name}($got_cname* x) {') sb.writeln('\t$got_cname* ptr = memdup(x, sizeof($got_cname));') for embed_hierarchy in g.table.get_embeds(got_sym) { // last embed in the hierarchy @@ -1998,9 +2185,9 @@ fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp_is_ptr fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) { got_type := g.table.mktyp(got_type_raw) exp_sym := g.table.get_type_symbol(expected_type) + got_sym := g.table.get_type_symbol(got_type) expected_is_ptr := expected_type.is_ptr() got_is_ptr := got_type.is_ptr() - got_sym := g.table.get_type_symbol(got_type) // allow using the new Error struct as a string, to avoid a breaking change // TODO: temporary to allow people to migrate their code; remove soon if got_type == ast.error_type_idx && expected_type == ast.string_type_idx { @@ -2073,7 +2260,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ g.prevent_sum_type_unwrapping_once = true g.expr(expr) } else { - g.write_sumtype_casting_fn(unwrapped_got_type, unwrapped_expected_type) + g.get_sumtype_casting_fn(unwrapped_got_type, unwrapped_expected_type) fname := '${unwrapped_got_sym.cname}_to_sumtype_$unwrapped_exp_sym.cname' g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, unwrapped_exp_sym.cname, got_is_ptr, got_styp) @@ -2916,7 +3103,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } mut cloned := false if g.is_autofree && right_sym.kind in [.array, .string] { - if g.gen_clone_assignment(val, right_sym, false) { + if g.gen_clone_assignment(val, unwrapped_val_type, false) { cloned = true } } @@ -3092,28 +3279,38 @@ fn (mut g Gen) get_ternary_name(name string) string { return g.ternary_names[name] } -fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym ast.TypeSymbol, add_eq bool) bool { +fn (mut g Gen) gen_clone_assignment(val ast.Expr, typ ast.Type, add_eq bool) bool { if val !is ast.Ident && val !is ast.SelectorExpr { return false } - if g.is_autofree && right_sym.kind == .array { - // `arr1 = arr2` => `arr1 = arr2.clone()` + right_sym := g.table.get_type_symbol(typ) + if g.is_autofree { if add_eq { g.write('=') } - g.write(' array_clone_static_to_depth(') - g.expr(val) - elem_type := (right_sym.info as ast.Array).elem_type - array_depth := g.get_array_depth(elem_type) - g.write(', $array_depth)') - } else if g.is_autofree && right_sym.kind == .string { - if add_eq { - g.write('=') + if right_sym.kind == .array { + // `arr1 = arr2` => `arr1 = arr2.clone()` + shared_styp := g.typ(typ.set_nr_muls(0)) + if typ.share() == .shared_t { + g.write('($shared_styp*)__dup_shared_array(&($shared_styp){.mtx = {0}, .val =') + } + g.write(' array_clone_static_to_depth(') + g.expr(val) + if typ.share() == .shared_t { + g.write('->val') + } + elem_type := (right_sym.info as ast.Array).elem_type + array_depth := g.get_array_depth(elem_type) + g.write(', $array_depth)') + if typ.share() == .shared_t { + g.write('}, sizeof($shared_styp))') + } + } else if right_sym.kind == .string { + // `str1 = str2` => `str1 = str2.clone()` + g.write(' string_clone_static(') + g.expr(val) + g.write(')') } - // `str1 = str2` => `str1 = str2.clone()` - g.write(' string_clone_static(') - g.expr(val) - g.write(')') } return true } @@ -3203,7 +3400,7 @@ fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, start_pos int, end_pos int // } // ``` // if !isnil(scope.parent) && line_nr > 0 { - if free_parent_scopes && !isnil(scope.parent) + if free_parent_scopes && !isnil(scope.parent) && !scope.detached_from_parent && (stop_pos == -1 || scope.parent.start_pos >= stop_pos) { g.trace_autofree('// af parent scope:') g.autofree_scope_vars2(scope.parent, start_pos, end_pos, line_nr, true, stop_pos) @@ -3217,10 +3414,10 @@ fn (mut g Gen) autofree_variable(v ast.Var) { // eprintln(' > var name: ${v.name:-20s} | is_arg: ${v.is_arg.str():6} | var type: ${int(v.typ):8} | type_name: ${sym.name:-33s}') } // } + free_fn := g.typ(v.typ.set_nr_muls(0)) + '_free' if sym.kind == .array { if sym.has_method('free') { - free_method_name := g.typ(v.typ) + '_free' - g.autofree_var_call(free_method_name, v) + g.autofree_var_call(free_fn, v) return } g.autofree_var_call('array_free', v) @@ -3251,7 +3448,7 @@ fn (mut g Gen) autofree_variable(v ast.Var) { return } if sym.has_method('free') { - g.autofree_var_call(c_name(sym.name) + '_free', v) + g.autofree_var_call(free_fn, v) } else if g.pref.experimental && v.typ.is_ptr() && sym.name.after('.')[0].is_capital() { // Free user reference types g.autofree_var_call('free', v) @@ -3281,7 +3478,23 @@ fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) { return } if v.typ.is_ptr() { - g.writeln('\t${free_fn_name}(${c_name(v.name)}); // autofreed ptr var') + g.write('\t') + if v.typ.share() == .shared_t { + g.write(free_fn_name.replace_each(['__shared__', ''])) + } else { + g.write(free_fn_name) + } + g.write('(') + if v.typ.share() == .shared_t { + g.write('&') + } + g.write(strings.repeat(`*`, v.typ.nr_muls() - 1)) // dereference if it is a pointer to a pointer + g.write(c_name(v.name)) + if v.typ.share() == .shared_t { + g.write('->val') + } + + g.writeln('); // autofreed ptr var') } else { if v.typ == ast.error_type && !v.is_autofree_tmp { return @@ -4183,19 +4396,19 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str } match type_sym.kind { .array { - ptr_typ := g.gen_array_equality_fn(node.cond_type) + ptr_typ := g.equality_fn(node.cond_type) g.write('${ptr_typ}_arr_eq($cond_var, ') g.expr(expr) g.write(')') } .array_fixed { - ptr_typ := g.gen_fixed_array_equality_fn(node.cond_type) + ptr_typ := g.equality_fn(node.cond_type) g.write('${ptr_typ}_arr_eq($cond_var, ') g.expr(expr) g.write(')') } .map { - ptr_typ := g.gen_map_equality_fn(node.cond_type) + ptr_typ := g.equality_fn(node.cond_type) g.write('${ptr_typ}_map_eq($cond_var, ') g.expr(expr) g.write(')') @@ -4206,7 +4419,7 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str g.write(')') } .struct_ { - ptr_typ := g.gen_struct_equality_fn(node.cond_type) + ptr_typ := g.equality_fn(node.cond_type) g.write('${ptr_typ}_struct_eq($cond_var, ') g.expr(expr) g.write(')') @@ -5236,7 +5449,7 @@ fn (mut g Gen) const_decl_precomputed(mod string, name string, ct_value ast.Comp // `error C2099: initializer is not a constant` errors in MSVC, // so fall back to the delayed initialisation scheme: g.definitions.writeln('$styp $cname; // inited later') - g.inits[mod].writeln('\t$cname = _SLIT("$escaped_val");') + g.init.writeln('\t$cname = _SLIT("$escaped_val");') if g.is_autofree { g.cleanups[mod].writeln('\tstring_free(&$cname);') } @@ -5269,35 +5482,33 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ g.definitions.writeln('$styp $cname; // inited later') if cname == '_const_os__args' { if g.pref.os == .windows { - g.inits[mod].writeln('\t_const_os__args = os__init_os_args_wide(___argc, (byteptr*)___argv);') + g.init.writeln('\t_const_os__args = os__init_os_args_wide(___argc, (byteptr*)___argv);') } else { - g.inits[mod].writeln('\t_const_os__args = os__init_os_args(___argc, (byte**)___argv);') + g.init.writeln('\t_const_os__args = os__init_os_args(___argc, (byte**)___argv);') } } else { if unwrap_option { - g.inits[mod].writeln(g.expr_string_surround('\t$cname = *($styp*)', expr, - '.data;')) + g.init.writeln(g.expr_string_surround('\t$cname = *($styp*)', expr, '.data;')) } else { - g.inits[mod].writeln(g.expr_string_surround('\t$cname = ', expr, ';')) + g.init.writeln(g.expr_string_surround('\t$cname = ', expr, ';')) } } if g.is_autofree { sym := g.table.get_type_symbol(typ) if styp.starts_with('Array_') { - g.cleanups[mod].writeln('\tarray_free(&$cname);') + g.cleanup.writeln('\tarray_free(&$cname);') } else if styp == 'string' { - g.cleanups[mod].writeln('\tstring_free(&$cname);') + g.cleanup.writeln('\tstring_free(&$cname);') } else if sym.kind == .map { - g.cleanups[mod].writeln('\tmap_free(&$cname);') + g.cleanup.writeln('\tmap_free(&$cname);') } else if styp == 'IError' { - g.cleanups[mod].writeln('\tIError_free(&$cname);') + g.cleanup.writeln('\tIError_free(&$cname);') } } } fn (mut g Gen) global_decl(node ast.GlobalDecl) { mod := if g.pref.build_mode == .build_module && g.is_builtin_mod { 'static ' } else { '' } - key := node.mod // module name for field in node.fields { if g.pref.skip_unused { if field.name !in g.table.used_globals { @@ -5314,7 +5525,7 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) { g.definitions.writeln(' = ${g.expr_string(field.expr)}; // global') } else { g.definitions.writeln(';') - g.global_inits[key].writeln('\t$field.name = ${g.expr_string(field.expr)}; // global') + g.global_init.writeln('\t$field.name = ${g.expr_string(field.expr)}; // global') } } else { default_initializer := g.type_default(field.typ) @@ -5323,7 +5534,7 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) { } else { g.definitions.writeln('$mod$styp $field.name; // global') if field.name !in ['as_cast_type_indexes', 'g_memory_block'] { - g.global_inits[key].writeln('\t$field.name = *($styp*)&(($styp[]){${g.type_default(field.typ)}}[0]); // global') + g.global_init.writeln('\t$field.name = *($styp*)&(($styp[]){${g.type_default(field.typ)}}[0]); // global') } } } @@ -5406,7 +5617,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { mut cloned := false if g.is_autofree && !field.typ.is_ptr() && field_type_sym.kind in [.array, .string] { g.write('/*clone1*/') - if g.gen_clone_assignment(field.expr, field_type_sym, false) { + if g.gen_clone_assignment(field.expr, field.typ, false) { cloned = true } } @@ -5498,7 +5709,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { mut cloned := false if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] { g.write('/*clone1*/') - if g.gen_clone_assignment(sfield.expr, field_type_sym, false) { + if g.gen_clone_assignment(sfield.expr, sfield.typ, false) { cloned = true } } @@ -5664,8 +5875,11 @@ fn (mut g Gen) write_init_function() { return } fn_vinit_start_pos := g.out.len + // ___argv is declared as voidptr here, because that unifies the windows/unix logic g.writeln('void _vinit(int ___argc, voidptr ___argv) {') + + g.writeln('#if __STDC_HOSTED__ == 1\n\tsignal(SIGSEGV, v_segmentation_fault_handler);\n#endif') if g.pref.prealloc { g.writeln('prealloc_vinit();') } @@ -5815,17 +6029,21 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) { // if this is the case then we are going to // buffer manip out in front of the struct // write the optional in and then continue + // FIXME: for parallel cgen (two different files using the same optional in struct fields) if field.typ.has_flag(.optional) { // Dont use g.typ() here becuase it will register // optional and we dont want that styp, base := g.optional_type_name(field.typ) - if styp !in g.optionals { - last_text := g.type_definitions.cut_to(start_pos).clone() - g.optionals << styp - g.typedefs2.writeln('typedef struct $styp $styp;') - g.type_definitions.writeln('${g.optional_type_text(styp, - base)};') - g.type_definitions.write_string(last_text) + lock g.done_optionals { + if base !in g.done_optionals { + g.done_optionals << base + last_text := g.type_definitions.after(start_pos).clone() + g.type_definitions.go_back_to(start_pos) + g.typedefs2.writeln('typedef struct $styp $styp;') + g.type_definitions.writeln('${g.optional_type_text(styp, + base)};') + g.type_definitions.write_string(last_text) + } } } type_name := g.typ(field.typ) diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v index 7989006991..9cb75a34d3 100644 --- a/vlib/v/gen/c/cheaders.v +++ b/vlib/v/gen/c/cheaders.v @@ -23,14 +23,14 @@ const c_current_commit_hash_default = ' const c_concurrency_helpers = ' typedef struct __shared_map __shared_map; -struct __shared_map { map val; sync__RwMutex mtx; }; +struct __shared_map { sync__RwMutex mtx; map val; }; static inline voidptr __dup_shared_map(voidptr src, int sz) { __shared_map* dest = memdup(src, sz); sync__RwMutex_init(&dest->mtx); return dest; } typedef struct __shared_array __shared_array; -struct __shared_array { array val; sync__RwMutex mtx; }; +struct __shared_array { sync__RwMutex mtx; array val; }; static inline voidptr __dup_shared_array(voidptr src, int sz) { __shared_array* dest = memdup(src, sz); sync__RwMutex_init(&dest->mtx); @@ -451,6 +451,7 @@ voidptr memdup(voidptr src, int sz); #include // _waccess #include // _wgetcwd + #include // signal and SIGSEGV for segmentation fault handler #ifdef _MSC_VER // On MSVC these are the same (as long as /volatile:ms is passed) diff --git a/vlib/v/gen/c/cmain.v b/vlib/v/gen/c/cmain.v index 6e741a671c..3e4925a69e 100644 --- a/vlib/v/gen/c/cmain.v +++ b/vlib/v/gen/c/cmain.v @@ -14,7 +14,6 @@ pub fn (mut g Gen) gen_c_main() { main_fn_start_pos := g.out.len is_sokol := 'sokol' in g.table.imports - if (g.pref.os == .android && g.pref.is_apk) || (g.pref.os == .ios && is_sokol) { g.gen_c_android_sokol_main() } else { diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 8fdfa9337e..2ceb47b374 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -364,7 +364,6 @@ fn (mut g Gen) comp_if_cond(cond ast.Expr, pkg_exist bool) bool { name = '${left.expr}.$left.field_name' exp_type = g.comptime_var_type_map[name] } else if left is ast.TypeNode { - name = left.str() // this is only allowed for generics currently, otherwise blocked by checker exp_type = g.unwrap_generic(left.typ) } diff --git a/vlib/v/gen/c/dumpexpr.v b/vlib/v/gen/c/dumpexpr.v index e7a6554af1..b58e83d912 100644 --- a/vlib/v/gen/c/dumpexpr.v +++ b/vlib/v/gen/c/dumpexpr.v @@ -21,7 +21,7 @@ fn (mut g Gen) dump_expr_definitions() { mut dump_typedefs := map[string]bool{} mut dump_fns := strings.new_builder(100) for dump_type, cname in g.table.dumps { - to_string_fn_name := g.gen_str_method_for_type(dump_type) + to_string_fn_name := g.get_str_fn(dump_type) is_ptr := ast.Type(dump_type).is_ptr() ptr_asterisk := if is_ptr { '*' } else { '' } dump_sym := g.table.get_type_symbol(dump_type) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index e9b3c30759..e81189bfcf 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -143,7 +143,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { // if g.fileis('vweb.v') { // println('\ngen_fn_decl() $node.name $node.is_generic $g.cur_generic_type') // } - if node.generic_names.len > 0 && g.table.cur_concrete_types.len == 0 { // need the cur_concrete_type check to avoid inf. recursion + if node.generic_names.len > 0 && g.cur_concrete_types.len == 0 { // need the cur_concrete_type check to avoid inf. recursion // loop thru each generic type and generate a function for concrete_types in g.table.fn_generic_types[node.name] { if g.pref.is_verbose { @@ -151,19 +151,19 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { the_type := syms.map(it.name).join(', ') println('gen fn `$node.name` for type `$the_type`') } - g.table.cur_concrete_types = concrete_types + g.cur_concrete_types = concrete_types g.gen_fn_decl(node, skip) } - g.table.cur_concrete_types = [] + g.cur_concrete_types = [] return } - cur_fn_save := g.table.cur_fn + cur_fn_save := g.cur_fn defer { - g.table.cur_fn = cur_fn_save + g.cur_fn = cur_fn_save } unsafe { // TODO remove unsafe - g.table.cur_fn = node + g.cur_fn = node } fn_start_pos := g.out.len is_closure := node.scope.has_inherited_vars() @@ -206,7 +206,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { } mut type_name := g.typ(node.return_type) - name = g.generic_fn_name(g.table.cur_concrete_types, name, true) + name = g.generic_fn_name(g.cur_concrete_types, name, true) if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') && !node.is_main && node.name != 'str' { @@ -288,7 +288,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { } arg_str := g.out.after(arg_start_pos) if node.no_body || ((g.pref.use_cache && g.pref.build_mode != .build_module) && node.is_builtin - && !g.is_test) || skip { + && !g.pref.is_test) || skip { // Just a function header. Builtin function bodies are defined in builtin.o g.definitions.writeln(');') // // NO BODY') g.writeln(');') @@ -644,14 +644,16 @@ fn (mut g Gen) method_call(node ast.CallExpr) { g.checker_bug('CallExpr.receiver_type is 0 in method_call', node.pos) } mut unwrapped_rec_type := node.receiver_type - if g.table.cur_fn.generic_names.len > 0 { // in generic fn + if g.cur_fn != 0 && g.cur_fn.generic_names.len > 0 { // in generic fn unwrapped_rec_type = g.unwrap_generic(node.receiver_type) } else { // in non-generic fn sym := g.table.get_type_symbol(node.receiver_type) match sym.info { ast.Struct, ast.Interface, ast.SumType { generic_names := sym.info.generic_types.map(g.table.get_type_symbol(it).name) - if utyp := g.table.resolve_generic_to_concrete(node.receiver_type, generic_names, + // see comment at top of vlib/v/gen/c/utils.v + mut muttable := unsafe { &ast.Table(g.table) } + if utyp := muttable.resolve_generic_to_concrete(node.receiver_type, generic_names, sym.info.concrete_types) { unwrapped_rec_type = utyp @@ -671,7 +673,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) { if typ_sym.kind == .interface_ && (typ_sym.info as ast.Interface).defines_method(node.name) { // Speaker_name_table[s._interface_idx].speak(s._object) $if debug_interface_method_call ? { - eprintln('>>> interface typ_sym.name: $typ_sym.name | receiver_type_name: $receiver_type_name') + eprintln('>>> interface typ_sym.name: $typ_sym.name | receiver_type_name: $receiver_type_name | pos: $node.pos') } g.write('${c_name(receiver_type_name)}_name_table[') g.expr(node.left) @@ -712,7 +714,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) { return } 'contains' { - g.gen_array_contains(node) + g.gen_array_contains(node.left_type, node.left, node.args[0].expr) return } 'index' { @@ -795,7 +797,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) { if rec_type.has_flag(.shared_f) { rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0) } - g.gen_str_method_for_type(rec_type) + g.get_str_fn(rec_type) } else if node.name == 'free' { mut rec_type := node.receiver_type if rec_type.has_flag(.shared_f) { @@ -1333,7 +1335,8 @@ fn (mut g Gen) call_args(node ast.CallExpr) { mut arr_info := arr_sym.info as ast.Array if varg_type.has_flag(.generic) { if fn_def := g.table.find_fn(node.name) { - if utyp := g.table.resolve_generic_to_concrete(arr_info.elem_type, fn_def.generic_names, + mut muttable := unsafe { &ast.Table(g.table) } + if utyp := muttable.resolve_generic_to_concrete(arr_info.elem_type, fn_def.generic_names, node.concrete_types) { arr_info.elem_type = utyp diff --git a/vlib/v/gen/c/index.v b/vlib/v/gen/c/index.v index 26dc455570..a677dbe5ff 100644 --- a/vlib/v/gen/c/index.v +++ b/vlib/v/gen/c/index.v @@ -347,11 +347,7 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) { g.expr(node.left) } if node.left_type.has_flag(.shared_f) { - if left_is_ptr { - g.write('->val') - } else { - g.write('.val') - } + g.write('->val') } g.write(', &($key_type_str[]){') g.expr(node.index) @@ -374,10 +370,13 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) { } else { g.write('(*($elem_type_str*)map_get((map*)') } - if !left_is_ptr { + if !left_is_ptr || node.left_type.has_flag(.shared_f) { g.write('&') } g.expr(node.left) + if node.left_type.has_flag(.shared_f) { + g.write('->val') + } g.write(', &($key_type_str[]){') g.expr(node.index) g.write('}, &($elem_type_str[]){ $zero }))') diff --git a/vlib/v/gen/c/infix_expr.v b/vlib/v/gen/c/infix_expr.v index 20d1f19b3a..89f8c75bfa 100644 --- a/vlib/v/gen/c/infix_expr.v +++ b/vlib/v/gen/c/infix_expr.v @@ -63,7 +63,7 @@ fn (mut g Gen) infix_expr_arrow_op(node ast.InfixExpr) { tmp_opt := if gen_or { g.new_tmp_var() } else { '' } if gen_or { elem_styp := g.typ(elem_type) - g.register_chan_push_optional_call(elem_styp, styp) + g.register_chan_push_optional_fn(elem_styp, styp) g.write('Option_void $tmp_opt = __Option_${styp}_pushval(') } else { g.write('__${styp}_pushval(') @@ -114,7 +114,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { && left.sym.kind in [.array, .array_fixed, .alias, .map, .struct_, .sum_type] { match left.sym.kind { .alias { - ptr_typ := g.gen_alias_equality_fn(left.typ) + ptr_typ := g.equality_fn(left.typ) if node.op == .ne { g.write('!') } @@ -131,7 +131,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { g.write(')') } .array { - ptr_typ := g.gen_array_equality_fn(left.unaliased.clear_flag(.shared_f)) + ptr_typ := g.equality_fn(left.unaliased.clear_flag(.shared_f)) if node.op == .ne { g.write('!') } @@ -141,11 +141,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { } g.expr(node.left) if left.typ.has_flag(.shared_f) { - if left.typ.is_ptr() { - g.write('->val') - } else { - g.write('.val') - } + g.write('->val') } g.write(', ') if right.typ.is_ptr() && !right.typ.has_flag(.shared_f) { @@ -153,16 +149,12 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { } g.expr(node.right) if right.typ.has_flag(.shared_f) { - if right.typ.is_ptr() { - g.write('->val') - } else { - g.write('.val') - } + g.write('->val') } g.write(')') } .array_fixed { - ptr_typ := g.gen_fixed_array_equality_fn(left.unaliased) + ptr_typ := g.equality_fn(left.unaliased) if node.op == .ne { g.write('!') } @@ -184,7 +176,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { g.write(')') } .map { - ptr_typ := g.gen_map_equality_fn(left.unaliased) + ptr_typ := g.equality_fn(left.unaliased) if node.op == .ne { g.write('!') } @@ -201,7 +193,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { g.write(')') } .struct_ { - ptr_typ := g.gen_struct_equality_fn(left.unaliased) + ptr_typ := g.equality_fn(left.unaliased) if node.op == .ne { g.write('!') } @@ -218,7 +210,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { g.write(')') } .sum_type { - ptr_typ := g.gen_sumtype_equality_fn(left.unaliased) + ptr_typ := g.equality_fn(left.unaliased) if node.op == .ne { g.write('!') } @@ -385,19 +377,7 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) { return } } - fn_name := g.gen_array_contains_method(node.right_type) - g.write('(${fn_name}(') - if right.typ.is_ptr() && right.typ.share() != .shared_t { - g.write('*') - } - g.expr(node.right) - if right.typ.share() == .shared_t { - g.write('->val') - } - g.write(', ') - g.expr(node.left) - g.write('))') - return + g.gen_array_contains(node.right_type, node.right, node.left) } else if right.unaliased_sym.kind == .map { g.write('_IN_MAP(') if !left.typ.is_ptr() { @@ -437,7 +417,7 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) { if is_str { g.write('string__eq(') } else if is_array { - ptr_typ := g.gen_array_equality_fn(right.elem_type) + ptr_typ := g.equality_fn(right.elem_type) g.write('${ptr_typ}_arr_eq(') } g.expr(left) @@ -580,10 +560,16 @@ fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) { elem_type_str := g.typ(array_info.elem_type) elem_sym := g.table.get_type_symbol(array_info.elem_type) g.write('array_push${noscan}((array*)') - if !left.typ.is_ptr() { + if node.left_type.has_flag(.shared_f) && !node.left_type.deref().is_ptr() { + } + if !left.typ.is_ptr() + || (node.left_type.has_flag(.shared_f) && !node.left_type.deref().is_ptr()) { g.write('&') } g.expr(node.left) + if node.left_type.has_flag(.shared_f) { + g.write('->val') + } if elem_sym.kind == .function { g.write(', _MOV((voidptr[]){ ') } else { diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v index 7d313092d3..d6b3e47b79 100644 --- a/vlib/v/gen/c/json.v +++ b/vlib/v/gen/c/json.v @@ -19,29 +19,34 @@ import strings // } // Codegen json_decode/encode funcs fn (mut g Gen) gen_json_for_type(typ ast.Type) { - utyp := g.unwrap_generic(typ) - mut dec := strings.new_builder(100) - mut enc := strings.new_builder(100) + utyp := g.unwrap_generic(typ).set_nr_muls(0) sym := g.table.get_type_symbol(utyp) - styp := g.typ(utyp) - g.register_optional(utyp) if is_js_prim(sym.name) || sym.kind == .enum_ { return } - if sym.kind == .array { - // return - } - if sym.name in g.json_types { - return - } - g.json_types << sym.name - // println('gen_json_for_type($sym.name)') - // decode_TYPE funcs receive an actual cJSON* object to decode - // cJSON_Parse(str) call is added by the compiler - // Code gen decoder - dec_fn_name := js_dec_name(styp) - dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)' - dec.writeln(' + g.json_types << utyp +} + +fn (mut g Gen) gen_jsons() { + mut done := []ast.Type{} + for i := 0; i < g.json_types.len; i++ { + utyp := g.json_types[i] + if utyp in done { + continue + } + done << utyp + mut dec := strings.new_builder(100) + mut enc := strings.new_builder(100) + sym := g.table.get_type_symbol(utyp) + styp := g.typ(utyp) + g.register_optional(utyp) + // println('gen_json_for_type($sym.name)') + // decode_TYPE funcs receive an actual cJSON* object to decode + // cJSON_Parse(str) call is added by the compiler + // Code gen decoder + dec_fn_name := js_dec_name(styp) + dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)' + dec.writeln(' $dec_fn_dec { $styp res; if (!root) { @@ -53,69 +58,70 @@ $dec_fn_dec { } } ') - g.json_forward_decls.writeln('$dec_fn_dec;') - // Code gen encoder - // encode_TYPE funcs receive an object to encode - enc_fn_name := js_enc_name(styp) - enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)' - g.json_forward_decls.writeln('$enc_fn_dec;\n') - enc.writeln(' + g.json_forward_decls.writeln('$dec_fn_dec;') + // Code gen encoder + // encode_TYPE funcs receive an object to encode + enc_fn_name := js_enc_name(styp) + enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)' + g.json_forward_decls.writeln('$enc_fn_dec;\n') + enc.writeln(' $enc_fn_dec { \tcJSON *o;') - if sym.kind == .array { - // Handle arrays - value_type := g.table.value_type(utyp) - // If we have `[]Profile`, have to register a Profile en(de)coder first - g.gen_json_for_type(value_type) - dec.writeln(g.decode_array(value_type)) - enc.writeln(g.encode_array(value_type)) - // enc += g.encode_array(t) - } else if sym.kind == .map { - // Handle maps - m := sym.info as ast.Map - g.gen_json_for_type(m.key_type) - g.gen_json_for_type(m.value_type) - dec.writeln(g.decode_map(m.key_type, m.value_type)) - enc.writeln(g.encode_map(m.key_type, m.value_type)) - } else if sym.kind == .alias { - a := sym.info as ast.Alias - parent_typ := a.parent_type - psym := g.table.get_type_symbol(parent_typ) - if is_js_prim(g.typ(parent_typ)) { - g.gen_json_for_type(parent_typ) - return - } - enc.writeln('\to = cJSON_CreateObject();') - if psym.info is ast.Struct { - g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec) - } else if psym.kind == .sum_type { - verror('json: $sym.name aliased sumtypes does not work at the moment') + if sym.kind == .array { + // Handle arrays + value_type := g.table.value_type(utyp) + // If we have `[]Profile`, have to register a Profile en(de)coder first + g.gen_json_for_type(value_type) + dec.writeln(g.decode_array(value_type)) + enc.writeln(g.encode_array(value_type)) + // enc += g.encode_array(t) + } else if sym.kind == .map { + // Handle maps + m := sym.info as ast.Map + g.gen_json_for_type(m.key_type) + g.gen_json_for_type(m.value_type) + dec.writeln(g.decode_map(m.key_type, m.value_type)) + enc.writeln(g.encode_map(m.key_type, m.value_type)) + } else if sym.kind == .alias { + a := sym.info as ast.Alias + parent_typ := a.parent_type + psym := g.table.get_type_symbol(parent_typ) + if is_js_prim(g.typ(parent_typ)) { + g.gen_json_for_type(parent_typ) + continue + } + enc.writeln('\to = cJSON_CreateObject();') + if psym.info is ast.Struct { + g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec) + } else if psym.kind == .sum_type { + verror('json: $sym.name aliased sumtypes does not work at the moment') + } else { + verror('json: $sym.name is not struct') + } + } else if sym.kind == .sum_type { + enc.writeln('\to = cJSON_CreateObject();') + // Sumtypes. Range through variants of sumtype + if sym.info !is ast.SumType { + verror('json: $sym.name is not a sumtype') + } + g.gen_sumtype_enc_dec(sym, mut enc, mut dec) } else { - verror('json: $sym.name is not struct') + enc.writeln('\to = cJSON_CreateObject();') + // Structs. Range through fields + if sym.info !is ast.Struct { + verror('json: $sym.name is not struct') + } + g.gen_struct_enc_dec(sym.info, styp, mut enc, mut dec) } - } else if sym.kind == .sum_type { - enc.writeln('\to = cJSON_CreateObject();') - // Sumtypes. Range through variants of sumtype - if sym.info !is ast.SumType { - verror('json: $sym.name is not a sumtype') - } - g.gen_sumtype_enc_dec(sym, mut enc, mut dec) - } else { - enc.writeln('\to = cJSON_CreateObject();') - // Structs. Range through fields - if sym.info !is ast.Struct { - verror('json: $sym.name is not struct') - } - g.gen_struct_enc_dec(sym.info, styp, mut enc, mut dec) + // cJSON_delete + // p.cgen.fns << '$dec return opt_ok(res); \n}' + dec.writeln('\tOption_$styp ret;') + dec.writeln('\topt_ok(&res, (Option*)&ret, sizeof(res));') + dec.writeln('\treturn ret;\n}') + enc.writeln('\treturn o;\n}') + g.definitions.writeln(dec.str()) + g.gowrappers.writeln(enc.str()) } - // cJSON_delete - // p.cgen.fns << '$dec return opt_ok(res); \n}' - dec.writeln('\tOption_$styp ret;') - dec.writeln('\topt_ok(&res, (Option*)&ret, sizeof(res));') - dec.writeln('\treturn ret;\n}') - enc.writeln('\treturn o;\n}') - g.definitions.writeln(dec.str()) - g.gowrappers.writeln(enc.str()) } [inline] @@ -150,7 +156,7 @@ fn (mut g Gen) gen_sumtype_enc_dec(sym ast.TypeSymbol, mut enc strings.Builder, g.gen_json_for_type(variant) // Helpers for decoding - g.write_sumtype_casting_fn(variant, typ) + g.get_sumtype_casting_fn(variant, typ) g.definitions.writeln('static inline $sym.cname ${variant_typ}_to_sumtype_${sym.cname}($variant_typ* x);') // ENCODING diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v index 6b8c195afe..7be2455350 100644 --- a/vlib/v/gen/c/str.v +++ b/vlib/v/gen/c/str.v @@ -80,7 +80,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { } sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() if typ.has_flag(.variadic) { - str_fn_name := g.gen_str_method_for_type(typ) + str_fn_name := g.get_str_fn(typ) g.write('${str_fn_name}(') g.expr(expr) g.write(')') @@ -93,7 +93,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { g.write('_SLIT("")') } else if sym.kind == .enum_ { if expr !is ast.EnumVal { - str_fn_name := g.gen_str_method_for_type(typ) + str_fn_name := g.get_str_fn(typ) g.write('${str_fn_name}(') g.enum_expr(expr) g.write(')') @@ -106,7 +106,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { || sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] { is_ptr := typ.is_ptr() is_var_mut := expr.is_auto_deref_var() - str_fn_name := g.gen_str_method_for_type(typ) + str_fn_name := g.get_str_fn(typ) if is_ptr && !is_var_mut { g.write('str_intp(1, _MOV((StrIntpData[]){{_SLIT("&"), $si_s_code ,{.d_s=') } @@ -132,7 +132,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { // g.write(')') } } else { - str_fn_name := g.gen_str_method_for_type(typ) + str_fn_name := g.get_str_fn(typ) g.write('${str_fn_name}(') if expr.is_auto_deref_var() { g.write('*') diff --git a/vlib/v/gen/c/utils.v b/vlib/v/gen/c/utils.v index a4768c8a3f..ac10429733 100644 --- a/vlib/v/gen/c/utils.v +++ b/vlib/v/gen/c/utils.v @@ -7,8 +7,20 @@ import v.ast fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type { if typ.has_flag(.generic) { - if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names, - g.table.cur_concrete_types) + /* + resolve_generic_to_concrete should not mutate the table. + It mutates if the generic type is for example []T and the + concrete type is an array type that has not been registered + yet. This should have already happened in the checker, since + it also calls resolve_generic_to_concrete. g.table is made + non-mut to make sure no one else can accidentally mutates the table. + */ + mut muttable := unsafe { &ast.Table(g.table) } + if t_typ := muttable.resolve_generic_to_concrete(typ, if g.cur_fn != 0 { + g.cur_fn.generic_names + } else { + []string{} + }, g.cur_concrete_types) { return t_typ } diff --git a/vlib/v/gen/js/comptime.v b/vlib/v/gen/js/comptime.v index d4c55ee1f5..69d60412d4 100644 --- a/vlib/v/gen/js/comptime.v +++ b/vlib/v/gen/js/comptime.v @@ -129,7 +129,6 @@ fn (mut g JsGen) comp_if_cond(cond ast.Expr, pkg_exist bool) bool { name = '${left.expr}.$left.field_name' exp_type = g.comptime_var_type_map[name] } else if left is ast.TypeNode { - name = left.str() // this is only allowed for generics currently, otherwise blocked by checker exp_type = g.unwrap_generic(left.typ) } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 76feddb99b..5d71961980 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -346,7 +346,7 @@ pub fn parse_files(paths []string, table &ast.Table, pref &pref.Preferences) []& } $if macos { /* - if pref.is_parallel && paths[0].contains('/array.v') { + if !pref.no_parallel && paths[0].contains('/array.v') { println('\n\n\nparse_files() nr_files=$paths.len') println(paths) nr_cpus := runtime.nr_cpus() diff --git a/vlib/v/pref/default.v b/vlib/v/pref/default.v index 6a8fa5a0ab..0b0f64d857 100644 --- a/vlib/v/pref/default.v +++ b/vlib/v/pref/default.v @@ -136,6 +136,10 @@ pub fn (mut p Preferences) fill_with_defaults() { if p.bare_builtin_dir == '' { p.bare_builtin_dir = os.join_path(p.vroot, 'vlib', 'builtin', 'linux_bare') } + $if prealloc { + eprintln('disabling parallel cgen, since V was built with -prealloc') + p.no_parallel = true + } } pub const cc_to_windows = 'x86_64-w64-mingw32-gcc' diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index ea0ec9d6b4..1eaf836647 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -175,7 +175,7 @@ pub mut: no_rsp bool // when true, pass C backend options directly on the CLI (do not use `.rsp` files for them, some older C compilers do not support them) no_std bool // when true, do not pass -std=c99 to the C backend use_color ColorOutput // whether the warnings/errors should use ANSI color escapes. - is_parallel bool + no_parallel bool is_vweb bool // skip _ var warning in templates only_check_syntax bool // when true, just parse the files, then stop, before running checker check_only bool // same as only_check_syntax, but also runs the checker @@ -474,8 +474,8 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences res.prealloc = true res.build_options << arg } - '-parallel' { - res.is_parallel = true + '-no-parallel' { + res.no_parallel = true } '-native' { res.backend = .native diff --git a/vlib/v/tests/comptime_for_test.v b/vlib/v/tests/comptime_for_test.v index 4c070fc0c6..6675211ba0 100644 --- a/vlib/v/tests/comptime_for_test.v +++ b/vlib/v/tests/comptime_for_test.v @@ -85,12 +85,10 @@ fn test_comptime_for_fields() { if field.name == 'f' { assert sizeof(field) == 8 assert isreftype(field) == false - // assert typeof(field) == 'u64' assert typeof(field).name == 'u64' fields_found++ } if field.name == 'g' { - // assert typeof(field) == 'string' assert typeof(field).name == 'string' assert isreftype(field) == true fields_found++ diff --git a/vlib/v/tests/shared_autolock_test.v b/vlib/v/tests/shared_autolock_test.v index 4ca7b065c4..cf9f6b6418 100644 --- a/vlib/v/tests/shared_autolock_test.v +++ b/vlib/v/tests/shared_autolock_test.v @@ -7,7 +7,7 @@ fn inc_array_elem(shared b []int, i int) { } fn test_autolock_array() { - shared a := &[1, 2, 7, 5] + shared a := [1, 2, 7, 5] t := go inc_array_elem(shared a, 2) for _ in 0 .. iterations { a[2]++ @@ -23,7 +23,7 @@ fn inc_map_elem(shared b map[string]int, k string) { } fn test_autolock_map() { - shared m := &{ + shared m := { 'xy': 1 'qwe': 2 'asd': 7