Merge branch 'master' into rebased

This commit is contained in:
hryx 2019-05-12 02:00:49 -07:00
commit 3787f34286
No known key found for this signature in database
GPG Key ID: 6A2784E15D7D95D6
220 changed files with 17682 additions and 6459 deletions

View File

@ -15,7 +15,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
set(ZIG_VERSION_MAJOR 0)
set(ZIG_VERSION_MINOR 3)
set(ZIG_VERSION_MINOR 4)
set(ZIG_VERSION_PATCH 0)
set(ZIG_VERSION "${ZIG_VERSION_MAJOR}.${ZIG_VERSION_MINOR}.${ZIG_VERSION_PATCH}")
@ -50,10 +50,6 @@ option(ZIG_FORCE_EXTERNAL_LLD "If your system has the LLD patches use it instead
find_package(llvm)
find_package(clang)
if(MINGW)
find_package(z3)
endif()
if(APPLE AND ZIG_STATIC)
list(REMOVE_ITEM LLVM_LIBRARIES "-lz")
find_library(ZLIB NAMES z zlib libz)
@ -62,6 +58,16 @@ endif()
set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zig_cpp")
# Handle multi-config builds and place each into a common lib. The VS generator
# for example will append a Debug folder by default if not explicitly specified.
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${ZIG_CPP_LIB_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${ZIG_CPP_LIB_DIR})
foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER ${CONFIG_TYPE} CONFIG_TYPE)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_TYPE} ${ZIG_CPP_LIB_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_TYPE} ${ZIG_CPP_LIB_DIR})
endforeach(CONFIG_TYPE CMAKE_CONFIGURATION_TYPES)
if(ZIG_FORCE_EXTERNAL_LLD)
find_package(lld)
include_directories(${LLVM_INCLUDE_DIRS})
@ -196,7 +202,7 @@ else()
if(MSVC)
set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -D_CRT_SECURE_NO_WARNINGS /w")
else()
set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -fno-exceptions -fno-rtti -Wno-comment")
set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Wno-comment")
if(MINGW)
set(ZIG_LLD_COMPILE_FLAGS "${ZIG_LLD_COMPILE_FLAGS} -D__STDC_FORMAT_MACROS -D__USE_MINGW_ANSI_STDIO -Wno-pedantic-ms-format")
endif()
@ -257,7 +263,6 @@ else()
embedded_lld_wasm
embedded_lld_lib
)
install(TARGETS embedded_lld_elf embedded_lld_coff embedded_lld_mingw embedded_lld_wasm embedded_lld_lib DESTINATION "${ZIG_CPP_LIB_DIR}")
endif()
# No patches have been applied to SoftFloat-3e
@ -407,6 +412,12 @@ set(SOFTFLOAT_LIBRARIES embedded_softfloat)
find_package(Threads)
# CMake doesn't let us create an empty executable, so we hang on to this one separately.
set(ZIG_MAIN_SRC "${CMAKE_SOURCE_DIR}/src/main.cpp")
# This is our shim which will be replaced by libuserland written in Zig.
set(ZIG0_SHIM_SRC "${CMAKE_SOURCE_DIR}/src/userland.cpp")
set(ZIG_SOURCES
"${CMAKE_SOURCE_DIR}/src/analyze.cpp"
"${CMAKE_SOURCE_DIR}/src/ast_render.cpp"
@ -423,7 +434,6 @@ set(ZIG_SOURCES
"${CMAKE_SOURCE_DIR}/src/ir_print.cpp"
"${CMAKE_SOURCE_DIR}/src/libc_installation.cpp"
"${CMAKE_SOURCE_DIR}/src/link.cpp"
"${CMAKE_SOURCE_DIR}/src/main.cpp"
"${CMAKE_SOURCE_DIR}/src/os.cpp"
"${CMAKE_SOURCE_DIR}/src/parser.cpp"
"${CMAKE_SOURCE_DIR}/src/range_set.cpp"
@ -477,6 +487,7 @@ set(ZIG_STD_FILES
"crypto/x25519.zig"
"cstr.zig"
"debug.zig"
"debug/leb128.zig"
"debug/failing_allocator.zig"
"dwarf.zig"
"dynamic_library.zig"
@ -507,6 +518,7 @@ set(ZIG_STD_FILES
"heap.zig"
"io.zig"
"io/seekable_stream.zig"
"io/c_out_stream.zig"
"json.zig"
"lazy_init.zig"
"linked_list.zig"
@ -521,6 +533,7 @@ set(ZIG_STD_FILES
"math/atanh.zig"
"math/big.zig"
"math/big/int.zig"
"math/big/rational.zig"
"math/cbrt.zig"
"math/ceil.zig"
"math/complex.zig"
@ -599,6 +612,7 @@ set(ZIG_STD_FILES
"os/linux.zig"
"os/linux/arm64.zig"
"os/linux/errno.zig"
"os/linux/tls.zig"
"os/linux/vdso.zig"
"os/linux/x86_64.zig"
"os/netbsd.zig"
@ -606,6 +620,8 @@ set(ZIG_STD_FILES
"os/path.zig"
"os/time.zig"
"os/uefi.zig"
"os/wasi.zig"
"os/wasi/core.zig"
"os/windows.zig"
"os/windows/advapi32.zig"
"os/windows/error.zig"
@ -615,6 +631,7 @@ set(ZIG_STD_FILES
"os/windows/shell32.zig"
"os/windows/util.zig"
"os/zen.zig"
"packed_int_array.zig"
"pdb.zig"
"priority_queue.zig"
"rand.zig"
@ -628,10 +645,15 @@ set(ZIG_STD_FILES
"special/build_runner.zig"
"special/builtin.zig"
"special/compiler_rt.zig"
"special/compiler_rt/stack_probe.zig"
"special/compiler_rt/arm/aeabi_fcmp.zig"
"special/compiler_rt/arm/aeabi_dcmp.zig"
"special/compiler_rt/addXf3.zig"
"special/compiler_rt/aulldiv.zig"
"special/compiler_rt/aullrem.zig"
"special/compiler_rt/comparetf2.zig"
"special/compiler_rt/comparedf2.zig"
"special/compiler_rt/comparesf2.zig"
"special/compiler_rt/divsf3.zig"
"special/compiler_rt/divdf3.zig"
"special/compiler_rt/divti3.zig"
@ -656,9 +678,13 @@ set(ZIG_STD_FILES
"special/compiler_rt/fixunstfdi.zig"
"special/compiler_rt/fixunstfsi.zig"
"special/compiler_rt/fixunstfti.zig"
"special/compiler_rt/floatdidf.zig"
"special/compiler_rt/floatsiXf.zig"
"special/compiler_rt/floatunsidf.zig"
"special/compiler_rt/floattidf.zig"
"special/compiler_rt/floattisf.zig"
"special/compiler_rt/floattitf.zig"
"special/compiler_rt/floatundidf.zig"
"special/compiler_rt/floatunditf.zig"
"special/compiler_rt/floatunsitf.zig"
"special/compiler_rt/floatuntidf.zig"
@ -667,7 +693,11 @@ set(ZIG_STD_FILES
"special/compiler_rt/modti3.zig"
"special/compiler_rt/mulXf3.zig"
"special/compiler_rt/muloti4.zig"
"special/compiler_rt/mulodi4.zig"
"special/compiler_rt/multi3.zig"
"special/compiler_rt/ashlti3.zig"
"special/compiler_rt/ashrti3.zig"
"special/compiler_rt/lshrti3.zig"
"special/compiler_rt/negXf2.zig"
"special/compiler_rt/popcountdi2.zig"
"special/compiler_rt/truncXfYf2.zig"
@ -676,7 +706,6 @@ set(ZIG_STD_FILES
"special/compiler_rt/udivmodti4.zig"
"special/compiler_rt/udivti3.zig"
"special/compiler_rt/umodti3.zig"
"special/fmt_runner.zig"
"special/init-exe/build.zig"
"special/init-exe/src/main.zig"
"special/init-lib/build.zig"
@ -6604,7 +6633,7 @@ endif()
if(MSVC)
set(EXE_CFLAGS "${EXE_CFLAGS}")
else()
set(EXE_CFLAGS "${EXE_CFLAGS} -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fno-exceptions -fno-rtti -Werror=strict-prototypes -Werror=old-style-definition -Werror=type-limits -Wno-missing-braces")
set(EXE_CFLAGS "${EXE_CFLAGS} -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=strict-prototypes -Werror=old-style-definition -Werror=type-limits -Wno-missing-braces")
if(MINGW)
set(EXE_CFLAGS "${EXE_CFLAGS} -D__USE_MINGW_ANSI_STDIO -Wno-pedantic-ms-format")
endif()
@ -6639,13 +6668,12 @@ set_target_properties(opt_c_util PROPERTIES
COMPILE_FLAGS "${OPTIMIZED_C_FLAGS}"
)
add_executable(zig ${ZIG_SOURCES})
set_target_properties(zig PROPERTIES
add_library(compiler STATIC ${ZIG_SOURCES})
set_target_properties(compiler PROPERTIES
COMPILE_FLAGS ${EXE_CFLAGS}
LINK_FLAGS ${EXE_LDFLAGS}
)
target_link_libraries(zig LINK_PUBLIC
target_link_libraries(compiler LINK_PUBLIC
zig_cpp
opt_c_util
${SOFTFLOAT_LIBRARIES}
@ -6654,24 +6682,63 @@ target_link_libraries(zig LINK_PUBLIC
${LLVM_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
)
if(NOT MSVC)
target_link_libraries(zig LINK_PUBLIC ${LIBXML2})
target_link_libraries(compiler LINK_PUBLIC ${LIBXML2})
endif()
if(MINGW)
target_link_libraries(zig LINK_PUBLIC ${Z3_LIBRARIES})
find_library(Z3_LIBRARIES NAMES z3 z3.dll)
target_link_libraries(compiler LINK_PUBLIC ${Z3_LIBRARIES})
endif()
if(ZIG_DIA_GUIDS_LIB)
target_link_libraries(zig LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB})
target_link_libraries(compiler LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB})
endif()
if(MSVC OR MINGW)
target_link_libraries(zig LINK_PUBLIC version)
target_link_libraries(compiler LINK_PUBLIC version)
endif()
add_executable(zig0 "${ZIG_MAIN_SRC}" "${ZIG0_SHIM_SRC}")
set_target_properties(zig0 PROPERTIES
COMPILE_FLAGS ${EXE_CFLAGS}
LINK_FLAGS ${EXE_LDFLAGS}
)
target_link_libraries(zig0 compiler)
if(WIN32)
set(LIBUSERLAND "${CMAKE_BINARY_DIR}/userland.lib")
elseif(APPLE)
set(LIBUSERLAND "${CMAKE_BINARY_DIR}/userland.o")
else()
set(LIBUSERLAND "${CMAKE_BINARY_DIR}/libuserland.a")
endif()
add_custom_command(
OUTPUT "${LIBUSERLAND}"
COMMAND zig0 ARGS build
--override-std-dir std
--override-lib-dir "${CMAKE_SOURCE_DIR}"
libuserland
"-Doutput-dir=${CMAKE_BINARY_DIR}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
DEPENDS
"${CMAKE_SOURCE_DIR}/src-self-hosted/stage1.zig"
"${CMAKE_SOURCE_DIR}/src-self-hosted/translate_c.zig"
"${CMAKE_SOURCE_DIR}/build.zig"
)
add_custom_target(userland_target DEPENDS "${LIBUSERLAND}")
add_executable(zig "${ZIG_MAIN_SRC}")
if(MINGW)
set(EXE_LDFLAGS "${EXE_LDFLAGS} -fstack-protector")
endif()
set_target_properties(zig PROPERTIES
COMPILE_FLAGS ${EXE_CFLAGS}
LINK_FLAGS ${EXE_LDFLAGS}
)
target_link_libraries(zig compiler "${LIBUSERLAND}")
add_dependencies(zig userland_target)
install(TARGETS zig DESTINATION bin)
install(TARGETS zig_cpp DESTINATION "${ZIG_CPP_LIB_DIR}")
foreach(file ${ZIG_C_HEADER_FILES})
get_filename_component(file_dir "${C_HEADERS_DEST}/${file}" DIRECTORY)
@ -6697,7 +6764,3 @@ foreach(file ${ZIG_LIBCXX_FILES})
get_filename_component(file_dir "${LIBCXX_FILES_DEST}/${file}" DIRECTORY)
install(FILES "${CMAKE_SOURCE_DIR}/libcxx/${file}" DESTINATION "${file_dir}")
endforeach()
install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/arg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/")
install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/main.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/")
install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/errmsg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/")

236
README.md
View File

@ -1,140 +1,15 @@
![ZIG](https://ziglang.org/zig-logo.svg)
A programming language designed for robustness, optimality, and
clarity.
Zig is an open-source programming language designed for **robustness**,
**optimality**, and **maintainability**.
[Download & Documentation](https://ziglang.org/download/)
## Resources
## Feature Highlights
* [Introduction](https://ziglang.org/#Introduction)
* [Download & Documentation](https://ziglang.org/download)
* [Community](https://github.com/ziglang/zig/wiki/Community)
* Small, simple language. Focus on debugging your application rather than
debugging knowledge of your programming language.
* Ships with a build system that obviates the need for a configure script
or a makefile. In fact, existing C and C++ projects may choose to depend on
Zig instead of e.g. cmake.
* A fresh take on error handling which makes writing correct code easier than
writing buggy code.
* Debug mode optimizes for fast compilation time and crashing with a stack trace
when undefined behavior *would* happen.
* ReleaseFast mode produces heavily optimized code. What other projects call
"Link Time Optimization" Zig does automatically.
* Compatible with C libraries with no wrapper necessary. Directly include
C .h files and get access to the functions and symbols therein.
* Provides standard library which competes with the C standard library and is
always compiled against statically in source form. Zig binaries do not
depend on libc unless explicitly linked.
* Optional type instead of null pointers.
* Safe unions, tagged unions, and C ABI compatible unions.
* Generics so that one can write efficient data structures that work for any
data type.
* No header files required. Top level declarations are entirely
order-independent.
* Compile-time code execution. Compile-time reflection.
* Partial compile-time function evaluation which eliminates the need for
a preprocessor or macros.
* The binaries produced by Zig have complete debugging information so you can,
for example, use GDB, MSVC, or LLDB to debug your software.
* Built-in unit tests with `zig test`.
* Friendly toward package maintainers. Reproducible build, bootstrapping
process carefully documented. Issues filed by package maintainers are
considered especially important.
* Cross-compiling is a primary use case.
* In addition to creating executables, creating a C library is a primary use
case. You can export an auto-generated .h file.
### Supported Targets
#### Tier 1 Support
* Not only can Zig generate machine code for these targets, but the standard
library cross-platform abstractions have implementations for these targets.
Thus it is practical to write a pure Zig application with no dependency on
libc.
* The CI server automatically tests these targets on every commit to master
branch, and updates ziglang.org/download with links to pre-built binaries.
* These targets have debug info capabilities and therefore produce stack
traces on failed assertions.
* ([coming soon](https://github.com/ziglang/zig/issues/514)) libc is available
for this target even when cross compiling.
#### Tier 2 Support
* There may be some standard library implementations, but many abstractions
will give an "Unsupported OS" compile error. One can link with libc or other
libraries to fill in the gaps in the standard library.
* These targets are known to work, but are not automatically tested, so there
are occasional regressions.
* Some tests may be disabled for these targets as we work toward Tier 1
support.
#### Tier 3 Support
* The standard library has little to no knowledge of the existence of this
target.
* Because Zig is based on LLVM, it has the capability to build for these
targets, and LLVM has the target enabled by default.
* These targets are not frequently tested; one will likely need to contribute
to Zig in order to build for these targets.
* The Zig compiler might need to be updated with a few things such as
- what sizes are the C integer types
- C ABI calling convention for this target
- bootstrap code and default panic handler
* `zig targets` is guaranteed to include this target.
#### Tier 4 Support
* Support for these targets is entirely experimental.
* LLVM may have the target as an experimental target, which means that you
need to use Zig-provided binaries for the target to be available, or
build LLVM from source with special configure flags. `zig targets` will
display the target if it is available.
* This target may be considered deprecated by an official party,
[such as macosx/i386](https://support.apple.com/en-us/HT208436) in which
case this target will remain forever stuck in Tier 4.
* This target may only support `--emit asm` and cannot emit object files.
#### Support Table
| | freestanding | linux | macosx | windows | freebsd | netbsd | UEFI | other |
|-------------|--------------|--------|--------|---------|---------|------- | -------|--------|
|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 2 | Tier 3 |
|i386 | Tier 2 | Tier 2 | Tier 4 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 |
|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 |
|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 |
|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 |
|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 |
|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 |
|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 |
|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 |
|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 |
|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 |
|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 |
|wasm32 | Tier 3 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
|wasm64 | Tier 3 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
|avr | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | Tier 4 |
|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | Tier 4 |
|xcore | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|nvptx | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|msp430 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|r600 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|arc | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|tce | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|le | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|amdil | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|hsail | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|spir | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|kalimba | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|shave | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
|renderscript | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 |
## Community
* IRC: `#zig` on Freenode ([Channel Logs](https://irclog.whitequark.org/zig/)).
* Reddit: [/r/zig](https://www.reddit.com/r/zig)
* Email list: [~andrewrk/ziglang@lists.sr.ht](https://lists.sr.ht/%7Eandrewrk/ziglang)
## Building
## Building from Source
[![Build Status](https://dev.azure.com/ziglang/zig/_apis/build/status/ziglang.zig?branchName=master)](https://dev.azure.com/ziglang/zig/_build/latest?definitionId=1&branchName=master)
@ -150,12 +25,14 @@ Note that you can
* cmake >= 2.8.5
* gcc >= 5.0.0 or clang >= 3.6.0
* LLVM, Clang, LLD development libraries == 8.x, compiled with the same gcc or clang version above
- Use the system package manager, or [build from source](https://github.com/ziglang/zig/wiki/How-to-build-LLVM,-libclang,-and-liblld-from-source#posix).
##### Windows
* cmake >= 2.8.5
* Microsoft Visual Studio 2017 (version 15.8)
* LLVM, Clang, LLD development libraries == 8.x, compiled with the same MSVC version above
- Use the [pre-built binaries](https://github.com/ziglang/zig/wiki/How-to-build-LLVM,-libclang,-and-liblld-from-source#pre-built-binaries) or [build from source](https://github.com/ziglang/zig/wiki/How-to-build-LLVM,-libclang,-and-liblld-from-source#windows).
#### Instructions
@ -165,9 +42,7 @@ Note that you can
mkdir build
cd build
cmake ..
make
make install
bin/zig build --build-file ../build.zig test
```
##### MacOS
@ -179,7 +54,6 @@ mkdir build
cd build
cmake .. -DCMAKE_PREFIX_PATH=/usr/local/Cellar/llvm/8.0.0
make install
bin/zig build --build-file ../build.zig test
```
##### Windows
@ -222,3 +96,95 @@ use stage 1.
```
./stage2/bin/zig build --build-file ../build.zig install -Drelease-fast
```
## Contributing
### Start a Project Using Zig
One of the best ways you can contribute to Zig is to start using it for a
personal project. Here are some great examples:
* [Oxid](https://github.com/dbandstra/oxid) - arcade style game
* [TM35-Metronome](https://github.com/TM35-Metronome) - tools for modifying and randomizing Pokémon games
* [trOS](https://github.com/sjdh02/trOS) - tiny aarch64 baremetal OS thingy
Without fail, these projects lead to discovering bugs and helping flesh out use
cases, which lead to further design iterations of Zig. Importantly, each issue
found this way comes with real world motivations, so it is easy to explain
your reasoning behind proposals and feature requests.
Ideally, such a project will help you to learn new skills and add something
to your personal portfolio at the same time.
### Spread the Word
Another way to contribute is to write about Zig, or speak about Zig at a
conference, or do either of those things for your project which uses Zig.
Here are some examples:
* [Iterative Replacement of C with Zig](http://tiehuis.github.io/blog/zig1.html)
* [The Right Tool for the Right Job: Redis Modules & Zig](https://www.youtube.com/watch?v=eCHM8-_poZY)
Zig is a brand new language, with no advertising budget. Word of mouth is the
only way people find out about the project, and the more people hear about it,
the more people will use it, and the better chance we have to take over the
world.
### Finding Contributor Friendly Issues
Please note that issues labeled
[Proposal](https://github.com/ziglang/zig/issues?q=is%3Aissue+is%3Aopen+label%3Aproposal)
but do not also have the
[Accepted](https://github.com/ziglang/zig/issues?q=is%3Aissue+is%3Aopen+label%3Aaccepted)
label are still under consideration, and efforts to implement such a proposal
have a high risk of being wasted. If you are interested in a proposal which is
still under consideration, please express your interest in the issue tracker,
providing extra insights and considerations that others have not yet expressed.
The most highly regarded argument in such a discussion is a real world use case.
The issue label
[Contributor Friendly](https://github.com/ziglang/zig/issues?q=is%3Aissue+is%3Aopen+label%3A%22contributor+friendly%22)
exists to help contributors find issues that are "limited in scope and/or
knowledge of Zig internals."
### Editing Source Code
First, build the Stage 1 compiler as described in [the Building section](#building).
When making changes to the standard library, be sure to edit the files in the
`std` directory and not the installed copy in the build directory. If you add a
new file to the standard library, you must also add the file path in
CMakeLists.txt.
To test changes, do the following from the build directory:
1. Run `make install` (on POSIX) or
`msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows).
2. `bin/zig build --build-file ../build.zig test` (on POSIX) or
`bin\zig.exe build --build-file ..\build.zig test` (on Windows).
That runs the whole test suite, which does a lot of extra testing that you
likely won't always need, and can take upwards of 2 hours. This is what the
CI server runs when you make a pull request.
To save time, you can add the `--help` option to the `zig build` command and
see what options are available. One of the most helpful ones is
`-Dskip-release`. Adding this option to the command in step 2 above will take
the time down from around 2 hours to about 6 minutes, and this is a good
enough amount of testing before making a pull request.
Another example is choosing a different set of things to test. For example,
`test-std` instead of `test` will only run the standard library tests, and
not the other ones. Combining this suggestion with the previous one, you could
do this:
`bin/zig build --build-file ../build.zig test-std -Dskip-release` (on POSIX) or
`bin\zig.exe build --build-file ..\build.zig test-std -Dskip-release` (on Windows).
This will run only the standard library tests, in debug mode only, for all
targets (it will cross-compile the tests for non-native targets but not run
them).
When making changes to the compiler source code, the most helpful test step to
run is `test-behavior`. When editing documentation it is `docs`. You can find
this information and more in the `--help` menu.

View File

@ -65,6 +65,8 @@ pub fn build(b: *Builder) !void {
b.default_step.dependOn(&exe.step);
addLibUserlandStep(b);
const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;
@ -380,3 +382,28 @@ const Context = struct {
dia_guids_lib: []const u8,
llvm: LibraryDep,
};
fn addLibUserlandStep(b: *Builder) void {
// Sadly macOS requires hacks to work around the buggy MACH-O linker code.
const artifact = if (builtin.os == .macosx)
b.addObject("userland", "src-self-hosted/stage1.zig")
else
b.addStaticLibrary("userland", "src-self-hosted/stage1.zig");
artifact.disable_gen_h = true;
if (builtin.os == .macosx) {
artifact.disable_stack_probing = true;
} else {
artifact.bundle_compiler_rt = true;
}
artifact.setTarget(builtin.arch, builtin.os, builtin.abi);
artifact.linkSystemLibrary("c");
const libuserland_step = b.step("libuserland", "Build the userland compiler library for use in stage1");
libuserland_step.dependOn(&artifact.step);
const output_dir = b.option(
[]const u8,
"output-dir",
"For libuserland step, where to put the output",
) orelse return;
artifact.setOutputDir(output_dir);
}

View File

@ -6,7 +6,7 @@ set -e
if [ "${BUILD_REASON}" != "PullRequest" ]; then
cd "$ZIGBUILDDIR"
rm release/*.lib
rm release/*.exe
mv ../LICENSE release/
mv ../zig-cache/langref.html release/
mv release/bin/zig.exe release/

View File

@ -44,7 +44,7 @@ else()
/usr/local/llvm80/include
/mingw64/include)
macro(FIND_AND_ADD_CLANG_LIB _libname_)
macro(FIND_AND_ADD_CLANG_LIB _libname_)
string(TOUPPER ${_libname_} _prettylibname_)
find_library(CLANG_${_prettylibname_}_LIB NAMES ${_libname_}
PATHS

View File

@ -111,8 +111,8 @@ void CodeSection::writeTo(uint8_t *Buf) {
memcpy(Buf, CodeSectionHeader.data(), CodeSectionHeader.size());
// Write code section bodies
parallelForEach(Functions,
[&](const InputChunk *Chunk) { Chunk->writeTo(Buf); });
for (const InputChunk *Chunk : Functions)
Chunk->writeTo(Buf);
}
uint32_t CodeSection::numRelocations() const {
@ -176,7 +176,7 @@ void DataSection::writeTo(uint8_t *Buf) {
// Write data section headers
memcpy(Buf, DataSectionHeader.data(), DataSectionHeader.size());
parallelForEach(Segments, [&](const OutputSegment *Segment) {
for (const OutputSegment *Segment : Segments) {
// Write data segment header
uint8_t *SegStart = Buf + Segment->SectionOffset;
memcpy(SegStart, Segment->Header.data(), Segment->Header.size());
@ -184,7 +184,7 @@ void DataSection::writeTo(uint8_t *Buf) {
// Write segment data payload
for (const InputChunk *Chunk : Segment->InputSegments)
Chunk->writeTo(Buf);
});
}
}
uint32_t DataSection::numRelocations() const {
@ -232,8 +232,8 @@ void CustomSection::writeTo(uint8_t *Buf) {
Buf += NameData.size();
// Write custom sections payload
parallelForEach(InputSections,
[&](const InputSection *Section) { Section->writeTo(Buf); });
for (const InputSection *Section : InputSections)
Section->writeTo(Buf);
}
uint32_t CustomSection::numRelocations() const {

View File

@ -265,6 +265,7 @@ const SeeAlsoItem = struct {
const ExpectedOutcome = enum {
Succeed,
Fail,
BuildFail,
};
const Code = struct {
@ -468,6 +469,8 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
code_kind_id = Code.Id{ .Exe = ExpectedOutcome.Succeed };
} else if (mem.eql(u8, code_kind_str, "exe_err")) {
code_kind_id = Code.Id{ .Exe = ExpectedOutcome.Fail };
} else if (mem.eql(u8, code_kind_str, "exe_build_err")) {
code_kind_id = Code.Id{ .Exe = ExpectedOutcome.BuildFail };
} else if (mem.eql(u8, code_kind_str, "test")) {
code_kind_id = Code.Id.Test;
} else if (mem.eql(u8, code_kind_str, "test_err")) {
@ -509,6 +512,10 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
target_str = "x86_64-windows";
} else if (mem.eql(u8, end_tag_name, "target_linux_x86_64")) {
target_str = "x86_64-linux";
} else if (mem.eql(u8, end_tag_name, "target_wasm")) {
target_str = "wasm32-freestanding";
} else if (mem.eql(u8, end_tag_name, "target_wasi")) {
target_str = "wasm32-wasi";
} else if (mem.eql(u8, end_tag_name, "link_libc")) {
link_libc = true;
} else if (mem.eql(u8, end_tag_name, "code_end")) {
@ -1025,6 +1032,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
tmp_dir_name,
"--name",
code.name,
"--color",
"on",
});
try out.print("<pre><code class=\"shell\">$ zig build-exe {}.zig", code.name);
switch (code.mode) {
@ -1059,14 +1068,52 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
}
if (code.target_str) |triple| {
try build_args.appendSlice([][]const u8{ "-target", triple });
if (!code.is_inline) {
try out.print(" -target {}", triple);
}
}
if (expected_outcome == .BuildFail) {
const result = try os.ChildProcess.exec(
allocator,
build_args.toSliceConst(),
null,
&env_map,
max_doc_file_size,
);
switch (result.term) {
os.ChildProcess.Term.Exited => |exit_code| {
if (exit_code == 0) {
warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
for (build_args.toSliceConst()) |arg|
warn("{} ", arg)
else
warn("\n");
return parseError(tokenizer, code.source_token, "example incorrectly compiled");
}
},
else => {
warn("{}\nThe following command crashed:\n", result.stderr);
for (build_args.toSliceConst()) |arg|
warn("{} ", arg)
else
warn("\n");
return parseError(tokenizer, code.source_token, "example compile crashed");
},
}
const escaped_stderr = try escapeHtml(allocator, result.stderr);
const colored_stderr = try termColor(allocator, escaped_stderr);
try out.print("\n{}</code></pre>\n", colored_stderr);
break :code_block;
}
_ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile");
if (code.target_str) |triple| {
if (mem.startsWith(u8, triple, "x86_64-linux") and
if (mem.startsWith(u8, triple, "wasm32") or
mem.startsWith(u8, triple, "x86_64-linux") and
(builtin.os != builtin.Os.linux or builtin.arch != builtin.Arch.x86_64))
{
// skip execution
try out.print("</code></pre>\n");
break :code_block;
}
}
@ -1130,6 +1177,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
}
if (code.target_str) |triple| {
try test_args.appendSlice([][]const u8{ "-target", triple });
try out.print(" -target {}", triple);
}
const result = exec(allocator, &env_map, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed");
const escaped_stderr = try escapeHtml(allocator, result.stderr);
@ -1310,6 +1358,11 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
},
}
if (code.target_str) |triple| {
try build_args.appendSlice([][]const u8{ "-target", triple });
try out.print(" -target {}", triple);
}
if (maybe_error_match) |error_match| {
const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, &env_map, max_doc_file_size);
switch (result.term) {

View File

@ -4,11 +4,12 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<title>Documentation - The Zig Programming Language</title>
<link rel="icon" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNSAxMCIgZmlsbD0iI2Y3YTQxZCI+PHBhdGggZD0iTTAsMSBIMy41IFYzIEgyIFY3IEgzLjY4MiBMMS44ODEsOSBIMCBaIE0xNSw5IEgxMS41IFY3IEgxMyBWMyBIMTEuMzE4IEwxMy4xMTksMSBIMTUgWiBNNCwxIEg5LjkxMiBMMTMuMzI4LDAuMDIxIEw3LjA0NSw3IEgxMSBWOSBINS4wODggTDEuNjcyLDkuOTc5IEw3Ljk1NSwzIEg0IFoiLz48L3N2Zz4="/>
<style type="text/css">
body{
font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif;
}
a {
a:not(:hover) {
text-decoration: none;
}
table, th, td {
@ -158,13 +159,15 @@
<div id="contents">
{#header_open|Introduction#}
<p>
Zig is an open-source programming language designed for <strong>robustness</strong>,
<strong>optimality</strong>, and <strong>clarity</strong>.
Zig is a general-purpose programming language designed for <strong>robustness</strong>,
<strong>optimality</strong>, and <strong>maintainability</strong>.
</p>
<ul>
<li><strong>Robust</strong> - behavior is correct even for edge cases such as out of memory.</li>
<li><strong>Optimal</strong> - write programs the best way they can behave and perform.</li>
<li><strong>Clear</strong> - precisely communicate your intent to the compiler and other programmers. The language imposes a low overhead to reading code.</li>
<li><strong>Maintainable</strong> - precisely communicate intent to the compiler and other programmers.
The language imposes a low overhead to reading code and is resilient to changing requirements
and environments.</li>
</ul>
<p>
Often the most efficient way to learn something new is to see examples, so
@ -3125,7 +3128,7 @@ test "while null capture" {
while (eventuallyNullSequence()) |value| {
sum2 += value;
} else {
assert(sum1 == 3);
assert(sum2 == 3);
}
}
@ -7180,14 +7183,6 @@ pub const FloatMode = enum {
{#see_also|Floating Point Operations#}
{#header_close#}
{#header_open|@setGlobalLinkage#}
<pre>{#syntax#}@setGlobalLinkage(global_variable_name, comptime linkage: GlobalLinkage){#endsyntax#}</pre>
<p>
{#syntax#}GlobalLinkage{#endsyntax#} can be found with {#syntax#}@import("builtin").GlobalLinkage{#endsyntax#}.
</p>
{#see_also|Compile Variables#}
{#header_close#}
{#header_open|@setRuntimeSafety#}
<pre>{#syntax#}@setRuntimeSafety(safety_on: bool){#endsyntax#}</pre>
<p>
@ -7217,6 +7212,8 @@ test "@setRuntimeSafety" {
}
}
{#code_end#}
<p>Note: it is <a href="https://github.com/ziglang/zig/issues/978">planned</a> to replace
{#syntax#}@setRuntimeSafety{#endsyntax#} with <code>@optimizeFor</code></p>
{#header_close#}
@ -7272,6 +7269,10 @@ test "@setRuntimeSafety" {
consider whether you want to use {#syntax#}@sizeOf(T){#endsyntax#} or
{#syntax#}@typeInfo(T).Int.bits{#endsyntax#}.
</p>
<p>
This function measures the size at runtime. For types that are disallowed at runtime, such as
{#syntax#}comptime_int{#endsyntax#} and {#syntax#}type{#endsyntax#}, the result is {#syntax#}0{#endsyntax#}.
</p>
{#see_also|@typeInfo#}
{#header_close#}
@ -7360,20 +7361,30 @@ fn List(comptime T: type) type {
<pre>{#syntax#}@truncate(comptime T: type, integer: var) T{#endsyntax#}</pre>
<p>
This function truncates bits from an integer type, resulting in a smaller
integer type.
or same-sized integer type.
</p>
<p>
The following produces a crash in {#link|Debug#} mode and {#link|Undefined Behavior#} in
{#link|ReleaseFast#} mode:
The following produces safety-checked {#link|Undefined Behavior#}:
</p>
<pre>{#syntax#}const a: u16 = 0xabcd;
const b: u8 = u8(a);{#endsyntax#}</pre>
{#code_begin|test_err|cast truncated bits#}
test "integer cast panic" {
var a: u16 = 0xabcd;
var b: u8 = @intCast(u8, a);
}
{#code_end#}
<p>
However this is well defined and working code:
</p>
<pre>{#syntax#}const a: u16 = 0xabcd;
const b: u8 = @truncate(u8, a);
// b is now 0xcd{#endsyntax#}</pre>
{#code_begin|test|truncate#}
const std = @import("std");
const assert = std.debug.assert;
test "integer truncation" {
var a: u16 = 0xabcd;
var b: u8 = @truncate(u8, a);
assert(b == 0xcd);
}
{#code_end#}
<p>
This function always truncates the significant bits of the integer, regardless
of endianness on the target platform.
@ -8840,20 +8851,21 @@ export fn add(a: i32, b: i32) i32 {
return a + b;
}
{#code_end#}
<p>To make a shared library:</p>
<p>To make a static library:</p>
<pre><code class="shell">$ zig build-lib mathtest.zig
</code></pre>
<p>To make a static library:</p>
<pre><code class="shell">$ zig build-lib mathtest.zig --static
<p>To make a shared library:</p>
<pre><code class="shell">$ zig build-lib mathtest.zig -dynamic
</code></pre>
<p>Here is an example with the {#link|Zig Build System#}:</p>
<p class="file">test.c</p>
<pre><code class="cpp">// This header is generated by zig from mathtest.zig
#include "mathtest.h"
#include &lt;assert.h&gt;
#include &lt;stdio.h&gt;
int main(int argc, char **argv) {
assert(add(42, 1337) == 1379);
int32_t result = add(42, 1337);
printf("%d\n", result);
return 0;
}</code></pre>
<p class="file">build.zig</p>
@ -8863,10 +8875,10 @@ const Builder = @import("std").build.Builder;
pub fn build(b: *Builder) void {
const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
const exe = b.addCExecutable("test");
exe.addCompileFlags([][]const u8{"-std=c99"});
exe.addSourceFile("test.c");
const exe = b.addExecutable("test", null);
exe.addCSourceFile("test.c", [][]const u8{"-std=c99"});
exe.linkLibrary(lib);
exe.linkSystemLibrary("c");
b.default_step.dependOn(&exe.step);
@ -8877,10 +8889,9 @@ pub fn build(b: *Builder) void {
}
{#code_end#}
<p class="file">terminal</p>
<pre><code class="shell">$ zig build
$ ./test
$ echo $?
0</code></pre>
<pre><code class="shell">$ zig build test
1379
</code></pre>
{#see_also|export#}
{#header_close#}
{#header_open|Mixing Object Files#}
@ -8947,6 +8958,33 @@ all your base are belong to us</code></pre>
{#see_also|Targets|Zig Build System#}
{#header_close#}
{#header_close#}
{#header_open|WebAssembly#}
{#header_open|Freestanding#}
{#code_begin|exe|wasm#}
{#target_wasm#}
extern fn print(i32) void;
export fn add(a: i32, b: i32) void {
print(a + b);
}
{#code_end#}
{#header_close#}
{#header_open|WASI#}
{#code_begin|exe|wasi#}
{#target_wasi#}
const std = @import("std");
pub fn main() !void {
const args = try std.os.argsAlloc(std.heap.wasm_allocator);
defer std.os.argsFree(std.heap.wasm_allocator, args);
for (args) |arg, i| {
std.debug.warn("{}: {}\n", i, arg);
}
}
{#code_end#}
{#header_close#}
{#header_close#}
{#header_open|Targets#}
<p>
Zig supports generating code for all targets that LLVM supports. Here is
@ -9430,8 +9468,6 @@ PrimaryExpr
IfExpr &lt;- IfPrefix Expr (KEYWORD_else Payload? Expr)?
LabeledExpr &lt;- BlockLabel? (Block / LoopExpr)
Block &lt;- LBRACE Statement* RBRACE
LoopExpr &lt;- KEYWORD_inline? (ForExpr / WhileExpr)
@ -9440,6 +9476,8 @@ ForExpr &lt;- ForPrefix Expr (KEYWORD_else Expr)?
WhileExpr &lt;- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
CurlySuffixExpr &lt;- TypeExpr InitList?
InitList
&lt;- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
/ LBRACE Expr (COMMA Expr)* COMMA? RBRACE
@ -9457,6 +9495,7 @@ PrimaryTypeExpr
&lt;- BUILTINIDENTIFIER FnCallArguments
/ CHAR_LITERAL
/ ContainerDecl
/ DOT IDENTIFIER
/ ErrorSetDecl
/ FLOAT
/ FnProto

866
src-self-hosted/clang.zig Normal file
View File

@ -0,0 +1,866 @@
pub const struct_ZigClangAPValue = @OpaqueType();
pub const struct_ZigClangAPSInt = @OpaqueType();
pub const struct_ZigClangASTContext = @OpaqueType();
pub const struct_ZigClangASTUnit = @OpaqueType();
pub const struct_ZigClangArraySubscriptExpr = @OpaqueType();
pub const struct_ZigClangArrayType = @OpaqueType();
pub const struct_ZigClangAttributedType = @OpaqueType();
pub const struct_ZigClangBinaryOperator = @OpaqueType();
pub const struct_ZigClangBreakStmt = @OpaqueType();
pub const struct_ZigClangBuiltinType = @OpaqueType();
pub const struct_ZigClangCStyleCastExpr = @OpaqueType();
pub const struct_ZigClangCallExpr = @OpaqueType();
pub const struct_ZigClangCaseStmt = @OpaqueType();
pub const struct_ZigClangCompoundAssignOperator = @OpaqueType();
pub const ZigClangCompoundStmt = @OpaqueType();
pub const struct_ZigClangConditionalOperator = @OpaqueType();
pub const struct_ZigClangConstantArrayType = @OpaqueType();
pub const struct_ZigClangContinueStmt = @OpaqueType();
pub const struct_ZigClangDecayedType = @OpaqueType();
pub const struct_ZigClangDecl = @OpaqueType();
pub const struct_ZigClangDeclRefExpr = @OpaqueType();
pub const struct_ZigClangDeclStmt = @OpaqueType();
pub const struct_ZigClangDefaultStmt = @OpaqueType();
pub const struct_ZigClangDiagnosticOptions = @OpaqueType();
pub const struct_ZigClangDiagnosticsEngine = @OpaqueType();
pub const struct_ZigClangDoStmt = @OpaqueType();
pub const struct_ZigClangElaboratedType = @OpaqueType();
pub const struct_ZigClangEnumConstantDecl = @OpaqueType();
pub const struct_ZigClangEnumDecl = @OpaqueType();
pub const struct_ZigClangEnumType = @OpaqueType();
pub const struct_ZigClangExpr = @OpaqueType();
pub const struct_ZigClangFieldDecl = @OpaqueType();
pub const struct_ZigClangFileID = @OpaqueType();
pub const struct_ZigClangForStmt = @OpaqueType();
pub const struct_ZigClangFullSourceLoc = @OpaqueType();
pub const ZigClangFunctionDecl = @OpaqueType();
pub const struct_ZigClangFunctionProtoType = @OpaqueType();
pub const struct_ZigClangIfStmt = @OpaqueType();
pub const struct_ZigClangImplicitCastExpr = @OpaqueType();
pub const struct_ZigClangIncompleteArrayType = @OpaqueType();
pub const struct_ZigClangIntegerLiteral = @OpaqueType();
pub const struct_ZigClangMacroDefinitionRecord = @OpaqueType();
pub const struct_ZigClangMemberExpr = @OpaqueType();
pub const struct_ZigClangNamedDecl = @OpaqueType();
pub const struct_ZigClangNone = @OpaqueType();
pub const struct_ZigClangPCHContainerOperations = @OpaqueType();
pub const struct_ZigClangParenExpr = @OpaqueType();
pub const struct_ZigClangParenType = @OpaqueType();
pub const struct_ZigClangParmVarDecl = @OpaqueType();
pub const struct_ZigClangPointerType = @OpaqueType();
pub const struct_ZigClangPreprocessedEntity = @OpaqueType();
pub const struct_ZigClangRecordDecl = @OpaqueType();
pub const struct_ZigClangRecordType = @OpaqueType();
pub const struct_ZigClangReturnStmt = @OpaqueType();
pub const struct_ZigClangSkipFunctionBodiesScope = @OpaqueType();
pub const struct_ZigClangSourceManager = @OpaqueType();
pub const struct_ZigClangSourceRange = @OpaqueType();
pub const struct_ZigClangStmt = @OpaqueType();
pub const struct_ZigClangStringLiteral = @OpaqueType();
pub const struct_ZigClangStringRef = @OpaqueType();
pub const struct_ZigClangSwitchStmt = @OpaqueType();
pub const struct_ZigClangTagDecl = @OpaqueType();
pub const struct_ZigClangType = @OpaqueType();
pub const struct_ZigClangTypedefNameDecl = @OpaqueType();
pub const struct_ZigClangTypedefType = @OpaqueType();
pub const struct_ZigClangUnaryExprOrTypeTraitExpr = @OpaqueType();
pub const struct_ZigClangUnaryOperator = @OpaqueType();
pub const struct_ZigClangValueDecl = @OpaqueType();
pub const struct_ZigClangVarDecl = @OpaqueType();
pub const struct_ZigClangWhileStmt = @OpaqueType();
pub const ZigClangFunctionType = @OpaqueType();
pub const ZigClangBO = extern enum {
PtrMemD,
PtrMemI,
Mul,
Div,
Rem,
Add,
Sub,
Shl,
Shr,
Cmp,
LT,
GT,
LE,
GE,
EQ,
NE,
And,
Xor,
Or,
LAnd,
LOr,
Assign,
MulAssign,
DivAssign,
RemAssign,
AddAssign,
SubAssign,
ShlAssign,
ShrAssign,
AndAssign,
XorAssign,
OrAssign,
Comma,
};
pub const ZigClangUO = extern enum {
PostInc,
PostDec,
PreInc,
PreDec,
AddrOf,
Deref,
Plus,
Minus,
Not,
LNot,
Real,
Imag,
Extension,
Coawait,
};
pub const ZigClangTypeClass = extern enum {
Builtin,
Complex,
Pointer,
BlockPointer,
LValueReference,
RValueReference,
MemberPointer,
ConstantArray,
IncompleteArray,
VariableArray,
DependentSizedArray,
DependentSizedExtVector,
DependentAddressSpace,
Vector,
DependentVector,
ExtVector,
FunctionProto,
FunctionNoProto,
UnresolvedUsing,
Paren,
Typedef,
Adjusted,
Decayed,
TypeOfExpr,
TypeOf,
Decltype,
UnaryTransform,
Record,
Enum,
Elaborated,
Attributed,
TemplateTypeParm,
SubstTemplateTypeParm,
SubstTemplateTypeParmPack,
TemplateSpecialization,
Auto,
DeducedTemplateSpecialization,
InjectedClassName,
DependentName,
DependentTemplateSpecialization,
PackExpansion,
ObjCTypeParam,
ObjCObject,
ObjCInterface,
ObjCObjectPointer,
Pipe,
Atomic,
};
pub const ZigClangStmtClass = extern enum {
NoStmtClass = 0,
GCCAsmStmtClass = 1,
MSAsmStmtClass = 2,
AttributedStmtClass = 3,
BreakStmtClass = 4,
CXXCatchStmtClass = 5,
CXXForRangeStmtClass = 6,
CXXTryStmtClass = 7,
CapturedStmtClass = 8,
CompoundStmtClass = 9,
ContinueStmtClass = 10,
CoreturnStmtClass = 11,
CoroutineBodyStmtClass = 12,
DeclStmtClass = 13,
DoStmtClass = 14,
BinaryConditionalOperatorClass = 15,
ConditionalOperatorClass = 16,
AddrLabelExprClass = 17,
ArrayInitIndexExprClass = 18,
ArrayInitLoopExprClass = 19,
ArraySubscriptExprClass = 20,
ArrayTypeTraitExprClass = 21,
AsTypeExprClass = 22,
AtomicExprClass = 23,
BinaryOperatorClass = 24,
CompoundAssignOperatorClass = 25,
BlockExprClass = 26,
CXXBindTemporaryExprClass = 27,
CXXBoolLiteralExprClass = 28,
CXXConstructExprClass = 29,
CXXTemporaryObjectExprClass = 30,
CXXDefaultArgExprClass = 31,
CXXDefaultInitExprClass = 32,
CXXDeleteExprClass = 33,
CXXDependentScopeMemberExprClass = 34,
CXXFoldExprClass = 35,
CXXInheritedCtorInitExprClass = 36,
CXXNewExprClass = 37,
CXXNoexceptExprClass = 38,
CXXNullPtrLiteralExprClass = 39,
CXXPseudoDestructorExprClass = 40,
CXXScalarValueInitExprClass = 41,
CXXStdInitializerListExprClass = 42,
CXXThisExprClass = 43,
CXXThrowExprClass = 44,
CXXTypeidExprClass = 45,
CXXUnresolvedConstructExprClass = 46,
CXXUuidofExprClass = 47,
CallExprClass = 48,
CUDAKernelCallExprClass = 49,
CXXMemberCallExprClass = 50,
CXXOperatorCallExprClass = 51,
UserDefinedLiteralClass = 52,
CStyleCastExprClass = 53,
CXXFunctionalCastExprClass = 54,
CXXConstCastExprClass = 55,
CXXDynamicCastExprClass = 56,
CXXReinterpretCastExprClass = 57,
CXXStaticCastExprClass = 58,
ObjCBridgedCastExprClass = 59,
ImplicitCastExprClass = 60,
CharacterLiteralClass = 61,
ChooseExprClass = 62,
CompoundLiteralExprClass = 63,
ConvertVectorExprClass = 64,
CoawaitExprClass = 65,
CoyieldExprClass = 66,
DeclRefExprClass = 67,
DependentCoawaitExprClass = 68,
DependentScopeDeclRefExprClass = 69,
DesignatedInitExprClass = 70,
DesignatedInitUpdateExprClass = 71,
ExpressionTraitExprClass = 72,
ExtVectorElementExprClass = 73,
FixedPointLiteralClass = 74,
FloatingLiteralClass = 75,
ConstantExprClass = 76,
ExprWithCleanupsClass = 77,
FunctionParmPackExprClass = 78,
GNUNullExprClass = 79,
GenericSelectionExprClass = 80,
ImaginaryLiteralClass = 81,
ImplicitValueInitExprClass = 82,
InitListExprClass = 83,
IntegerLiteralClass = 84,
LambdaExprClass = 85,
MSPropertyRefExprClass = 86,
MSPropertySubscriptExprClass = 87,
MaterializeTemporaryExprClass = 88,
MemberExprClass = 89,
NoInitExprClass = 90,
OMPArraySectionExprClass = 91,
ObjCArrayLiteralClass = 92,
ObjCAvailabilityCheckExprClass = 93,
ObjCBoolLiteralExprClass = 94,
ObjCBoxedExprClass = 95,
ObjCDictionaryLiteralClass = 96,
ObjCEncodeExprClass = 97,
ObjCIndirectCopyRestoreExprClass = 98,
ObjCIsaExprClass = 99,
ObjCIvarRefExprClass = 100,
ObjCMessageExprClass = 101,
ObjCPropertyRefExprClass = 102,
ObjCProtocolExprClass = 103,
ObjCSelectorExprClass = 104,
ObjCStringLiteralClass = 105,
ObjCSubscriptRefExprClass = 106,
OffsetOfExprClass = 107,
OpaqueValueExprClass = 108,
UnresolvedLookupExprClass = 109,
UnresolvedMemberExprClass = 110,
PackExpansionExprClass = 111,
ParenExprClass = 112,
ParenListExprClass = 113,
PredefinedExprClass = 114,
PseudoObjectExprClass = 115,
ShuffleVectorExprClass = 116,
SizeOfPackExprClass = 117,
StmtExprClass = 118,
StringLiteralClass = 119,
SubstNonTypeTemplateParmExprClass = 120,
SubstNonTypeTemplateParmPackExprClass = 121,
TypeTraitExprClass = 122,
TypoExprClass = 123,
UnaryExprOrTypeTraitExprClass = 124,
UnaryOperatorClass = 125,
VAArgExprClass = 126,
ForStmtClass = 127,
GotoStmtClass = 128,
IfStmtClass = 129,
IndirectGotoStmtClass = 130,
LabelStmtClass = 131,
MSDependentExistsStmtClass = 132,
NullStmtClass = 133,
OMPAtomicDirectiveClass = 134,
OMPBarrierDirectiveClass = 135,
OMPCancelDirectiveClass = 136,
OMPCancellationPointDirectiveClass = 137,
OMPCriticalDirectiveClass = 138,
OMPFlushDirectiveClass = 139,
OMPDistributeDirectiveClass = 140,
OMPDistributeParallelForDirectiveClass = 141,
OMPDistributeParallelForSimdDirectiveClass = 142,
OMPDistributeSimdDirectiveClass = 143,
OMPForDirectiveClass = 144,
OMPForSimdDirectiveClass = 145,
OMPParallelForDirectiveClass = 146,
OMPParallelForSimdDirectiveClass = 147,
OMPSimdDirectiveClass = 148,
OMPTargetParallelForSimdDirectiveClass = 149,
OMPTargetSimdDirectiveClass = 150,
OMPTargetTeamsDistributeDirectiveClass = 151,
OMPTargetTeamsDistributeParallelForDirectiveClass = 152,
OMPTargetTeamsDistributeParallelForSimdDirectiveClass = 153,
OMPTargetTeamsDistributeSimdDirectiveClass = 154,
OMPTaskLoopDirectiveClass = 155,
OMPTaskLoopSimdDirectiveClass = 156,
OMPTeamsDistributeDirectiveClass = 157,
OMPTeamsDistributeParallelForDirectiveClass = 158,
OMPTeamsDistributeParallelForSimdDirectiveClass = 159,
OMPTeamsDistributeSimdDirectiveClass = 160,
OMPMasterDirectiveClass = 161,
OMPOrderedDirectiveClass = 162,
OMPParallelDirectiveClass = 163,
OMPParallelSectionsDirectiveClass = 164,
OMPSectionDirectiveClass = 165,
OMPSectionsDirectiveClass = 166,
OMPSingleDirectiveClass = 167,
OMPTargetDataDirectiveClass = 168,
OMPTargetDirectiveClass = 169,
OMPTargetEnterDataDirectiveClass = 170,
OMPTargetExitDataDirectiveClass = 171,
OMPTargetParallelDirectiveClass = 172,
OMPTargetParallelForDirectiveClass = 173,
OMPTargetTeamsDirectiveClass = 174,
OMPTargetUpdateDirectiveClass = 175,
OMPTaskDirectiveClass = 176,
OMPTaskgroupDirectiveClass = 177,
OMPTaskwaitDirectiveClass = 178,
OMPTaskyieldDirectiveClass = 179,
OMPTeamsDirectiveClass = 180,
ObjCAtCatchStmtClass = 181,
ObjCAtFinallyStmtClass = 182,
ObjCAtSynchronizedStmtClass = 183,
ObjCAtThrowStmtClass = 184,
ObjCAtTryStmtClass = 185,
ObjCAutoreleasePoolStmtClass = 186,
ObjCForCollectionStmtClass = 187,
ReturnStmtClass = 188,
SEHExceptStmtClass = 189,
SEHFinallyStmtClass = 190,
SEHLeaveStmtClass = 191,
SEHTryStmtClass = 192,
CaseStmtClass = 193,
DefaultStmtClass = 194,
SwitchStmtClass = 195,
WhileStmtClass = 196,
};
pub const ZigClangCK = extern enum {
Dependent,
BitCast,
LValueBitCast,
LValueToRValue,
NoOp,
BaseToDerived,
DerivedToBase,
UncheckedDerivedToBase,
Dynamic,
ToUnion,
ArrayToPointerDecay,
FunctionToPointerDecay,
NullToPointer,
NullToMemberPointer,
BaseToDerivedMemberPointer,
DerivedToBaseMemberPointer,
MemberPointerToBoolean,
ReinterpretMemberPointer,
UserDefinedConversion,
ConstructorConversion,
IntegralToPointer,
PointerToIntegral,
PointerToBoolean,
ToVoid,
VectorSplat,
IntegralCast,
IntegralToBoolean,
IntegralToFloating,
FixedPointCast,
FixedPointToBoolean,
FloatingToIntegral,
FloatingToBoolean,
BooleanToSignedIntegral,
FloatingCast,
CPointerToObjCPointerCast,
BlockPointerToObjCPointerCast,
AnyPointerToBlockPointerCast,
ObjCObjectLValueCast,
FloatingRealToComplex,
FloatingComplexToReal,
FloatingComplexToBoolean,
FloatingComplexCast,
FloatingComplexToIntegralComplex,
IntegralRealToComplex,
IntegralComplexToReal,
IntegralComplexToBoolean,
IntegralComplexCast,
IntegralComplexToFloatingComplex,
ARCProduceObject,
ARCConsumeObject,
ARCReclaimReturnedObject,
ARCExtendBlockObject,
AtomicToNonAtomic,
NonAtomicToAtomic,
CopyAndAutoreleaseBlockObject,
BuiltinFnToFnPtr,
ZeroToOCLOpaqueType,
AddressSpaceConversion,
IntToOCLSampler,
};
pub const ZigClangAPValueKind = extern enum {
Uninitialized,
Int,
Float,
ComplexInt,
ComplexFloat,
LValue,
Vector,
Array,
Struct,
Union,
MemberPointer,
AddrLabelDiff,
};
pub extern fn ZigClangSourceManager_getSpellingLoc(arg0: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation;
pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*]const u8;
pub extern fn ZigClangSourceManager_getSpellingLineNumber(arg0: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
pub extern fn ZigClangSourceManager_getSpellingColumnNumber(arg0: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
pub extern fn ZigClangSourceManager_getCharacterData(arg0: ?*const struct_ZigClangSourceManager, SL: struct_ZigClangSourceLocation) [*c]const u8;
pub extern fn ZigClangASTContext_getPointerType(arg0: ?*const struct_ZigClangASTContext, T: struct_ZigClangQualType) struct_ZigClangQualType;
pub extern fn ZigClangASTUnit_getASTContext(arg0: ?*struct_ZigClangASTUnit) ?*struct_ZigClangASTContext;
pub extern fn ZigClangASTUnit_getSourceManager(self: *struct_ZigClangASTUnit) *struct_ZigClangSourceManager;
pub extern fn ZigClangASTUnit_visitLocalTopLevelDecls(self: *struct_ZigClangASTUnit, context: ?*c_void, Fn: ?extern fn (?*c_void, *const struct_ZigClangDecl) bool) bool;
pub extern fn ZigClangRecordType_getDecl(record_ty: ?*const struct_ZigClangRecordType) ?*const struct_ZigClangRecordDecl;
pub extern fn ZigClangEnumType_getDecl(record_ty: ?*const struct_ZigClangEnumType) ?*const struct_ZigClangEnumDecl;
pub extern fn ZigClangRecordDecl_getCanonicalDecl(record_decl: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangTagDecl;
pub extern fn ZigClangEnumDecl_getCanonicalDecl(arg0: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangTagDecl;
pub extern fn ZigClangTypedefNameDecl_getCanonicalDecl(arg0: ?*const struct_ZigClangTypedefNameDecl) ?*const struct_ZigClangTypedefNameDecl;
pub extern fn ZigClangRecordDecl_getDefinition(arg0: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangRecordDecl;
pub extern fn ZigClangEnumDecl_getDefinition(arg0: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangEnumDecl;
pub extern fn ZigClangRecordDecl_getLocation(arg0: ?*const struct_ZigClangRecordDecl) struct_ZigClangSourceLocation;
pub extern fn ZigClangEnumDecl_getLocation(arg0: ?*const struct_ZigClangEnumDecl) struct_ZigClangSourceLocation;
pub extern fn ZigClangTypedefNameDecl_getLocation(arg0: ?*const struct_ZigClangTypedefNameDecl) struct_ZigClangSourceLocation;
pub extern fn ZigClangDecl_getLocation(self: *const ZigClangDecl) ZigClangSourceLocation;
pub extern fn ZigClangRecordDecl_isUnion(record_decl: ?*const struct_ZigClangRecordDecl) bool;
pub extern fn ZigClangRecordDecl_isStruct(record_decl: ?*const struct_ZigClangRecordDecl) bool;
pub extern fn ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl: ?*const struct_ZigClangRecordDecl) bool;
pub extern fn ZigClangEnumDecl_getIntegerType(arg0: ?*const struct_ZigClangEnumDecl) struct_ZigClangQualType;
pub extern fn ZigClangDecl_getName_bytes_begin(decl: ?*const struct_ZigClangDecl) [*c]const u8;
pub extern fn ZigClangSourceLocation_eq(a: struct_ZigClangSourceLocation, b: struct_ZigClangSourceLocation) bool;
pub extern fn ZigClangTypedefType_getDecl(arg0: ?*const struct_ZigClangTypedefType) ?*const struct_ZigClangTypedefNameDecl;
pub extern fn ZigClangTypedefNameDecl_getUnderlyingType(arg0: ?*const struct_ZigClangTypedefNameDecl) struct_ZigClangQualType;
pub extern fn ZigClangQualType_getCanonicalType(arg0: struct_ZigClangQualType) struct_ZigClangQualType;
pub extern fn ZigClangQualType_getTypePtr(self: struct_ZigClangQualType) *const struct_ZigClangType;
pub extern fn ZigClangQualType_addConst(arg0: [*c]struct_ZigClangQualType) void;
pub extern fn ZigClangQualType_eq(arg0: struct_ZigClangQualType, arg1: struct_ZigClangQualType) bool;
pub extern fn ZigClangQualType_isConstQualified(arg0: struct_ZigClangQualType) bool;
pub extern fn ZigClangQualType_isVolatileQualified(arg0: struct_ZigClangQualType) bool;
pub extern fn ZigClangQualType_isRestrictQualified(arg0: struct_ZigClangQualType) bool;
pub extern fn ZigClangType_getTypeClass(self: ?*const struct_ZigClangType) ZigClangTypeClass;
pub extern fn ZigClangType_isVoidType(self: ?*const struct_ZigClangType) bool;
pub extern fn ZigClangType_getTypeClassName(self: *const struct_ZigClangType) [*]const u8;
pub extern fn ZigClangStmt_getBeginLoc(self: *const struct_ZigClangStmt) struct_ZigClangSourceLocation;
pub extern fn ZigClangStmt_getStmtClass(self: ?*const struct_ZigClangStmt) ZigClangStmtClass;
pub extern fn ZigClangStmt_classof_Expr(self: ?*const struct_ZigClangStmt) bool;
pub extern fn ZigClangExpr_getStmtClass(self: ?*const struct_ZigClangExpr) ZigClangStmtClass;
pub extern fn ZigClangExpr_getType(self: ?*const struct_ZigClangExpr) struct_ZigClangQualType;
pub extern fn ZigClangExpr_getBeginLoc(self: *const struct_ZigClangExpr) struct_ZigClangSourceLocation;
pub extern fn ZigClangAPValue_getKind(self: ?*const struct_ZigClangAPValue) ZigClangAPValueKind;
pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) ?*const struct_ZigClangAPSInt;
pub extern fn ZigClangAPValue_getArrayInitializedElts(self: ?*const struct_ZigClangAPValue) c_uint;
pub extern fn ZigClangAPValue_getArrayInitializedElt(self: ?*const struct_ZigClangAPValue, i: c_uint) ?*const struct_ZigClangAPValue;
pub extern fn ZigClangAPValue_getArrayFiller(self: ?*const struct_ZigClangAPValue) ?*const struct_ZigClangAPValue;
pub extern fn ZigClangAPValue_getArraySize(self: ?*const struct_ZigClangAPValue) c_uint;
pub extern fn ZigClangAPValue_getLValueBase(self: ?*const struct_ZigClangAPValue) struct_ZigClangAPValueLValueBase;
pub extern fn ZigClangAPSInt_isSigned(self: ?*const struct_ZigClangAPSInt) bool;
pub extern fn ZigClangAPSInt_isNegative(self: ?*const struct_ZigClangAPSInt) bool;
pub extern fn ZigClangAPSInt_negate(self: ?*const struct_ZigClangAPSInt) ?*const struct_ZigClangAPSInt;
pub extern fn ZigClangAPSInt_free(self: ?*const struct_ZigClangAPSInt) void;
pub extern fn ZigClangAPSInt_getRawData(self: ?*const struct_ZigClangAPSInt) [*c]const u64;
pub extern fn ZigClangAPSInt_getNumWords(self: ?*const struct_ZigClangAPSInt) c_uint;
pub extern fn ZigClangAPValueLValueBase_dyn_cast_Expr(self: struct_ZigClangAPValueLValueBase) ?*const struct_ZigClangExpr;
pub extern fn ZigClangASTUnit_delete(arg0: ?*struct_ZigClangASTUnit) void;
pub extern fn ZigClangFunctionDecl_getType(self: *const ZigClangFunctionDecl) struct_ZigClangQualType;
pub extern fn ZigClangFunctionDecl_getLocation(self: *const ZigClangFunctionDecl) struct_ZigClangSourceLocation;
pub extern fn ZigClangFunctionDecl_hasBody(self: *const ZigClangFunctionDecl) bool;
pub extern fn ZigClangFunctionDecl_getStorageClass(self: *const ZigClangFunctionDecl) ZigClangStorageClass;
pub extern fn ZigClangFunctionDecl_getParamDecl(self: *const ZigClangFunctionDecl, i: c_uint) *const struct_ZigClangParmVarDecl;
pub extern fn ZigClangFunctionDecl_getBody(self: *const ZigClangFunctionDecl) *const struct_ZigClangStmt;
pub extern fn ZigClangBuiltinType_getKind(self: *const struct_ZigClangBuiltinType) ZigClangBuiltinTypeKind;
pub extern fn ZigClangFunctionType_getNoReturnAttr(self: *const ZigClangFunctionType) bool;
pub extern fn ZigClangFunctionType_getCallConv(self: *const ZigClangFunctionType) ZigClangCallingConv;
pub extern fn ZigClangFunctionType_getReturnType(self: *const ZigClangFunctionType) ZigClangQualType;
pub extern fn ZigClangFunctionProtoType_isVariadic(self: *const struct_ZigClangFunctionProtoType) bool;
pub extern fn ZigClangFunctionProtoType_getNumParams(self: *const struct_ZigClangFunctionProtoType) c_uint;
pub extern fn ZigClangFunctionProtoType_getParamType(self: *const struct_ZigClangFunctionProtoType, i: c_uint) ZigClangQualType;
pub const ZigClangSourceLocation = struct_ZigClangSourceLocation;
pub const ZigClangQualType = struct_ZigClangQualType;
pub const ZigClangAPValueLValueBase = struct_ZigClangAPValueLValueBase;
pub const ZigClangAPValue = struct_ZigClangAPValue;
pub const ZigClangAPSInt = struct_ZigClangAPSInt;
pub const ZigClangASTContext = struct_ZigClangASTContext;
pub const ZigClangASTUnit = struct_ZigClangASTUnit;
pub const ZigClangArraySubscriptExpr = struct_ZigClangArraySubscriptExpr;
pub const ZigClangArrayType = struct_ZigClangArrayType;
pub const ZigClangAttributedType = struct_ZigClangAttributedType;
pub const ZigClangBinaryOperator = struct_ZigClangBinaryOperator;
pub const ZigClangBreakStmt = struct_ZigClangBreakStmt;
pub const ZigClangBuiltinType = struct_ZigClangBuiltinType;
pub const ZigClangCStyleCastExpr = struct_ZigClangCStyleCastExpr;
pub const ZigClangCallExpr = struct_ZigClangCallExpr;
pub const ZigClangCaseStmt = struct_ZigClangCaseStmt;
pub const ZigClangCompoundAssignOperator = struct_ZigClangCompoundAssignOperator;
pub const ZigClangConditionalOperator = struct_ZigClangConditionalOperator;
pub const ZigClangConstantArrayType = struct_ZigClangConstantArrayType;
pub const ZigClangContinueStmt = struct_ZigClangContinueStmt;
pub const ZigClangDecayedType = struct_ZigClangDecayedType;
pub const ZigClangDecl = struct_ZigClangDecl;
pub const ZigClangDeclRefExpr = struct_ZigClangDeclRefExpr;
pub const ZigClangDeclStmt = struct_ZigClangDeclStmt;
pub const ZigClangDefaultStmt = struct_ZigClangDefaultStmt;
pub const ZigClangDiagnosticOptions = struct_ZigClangDiagnosticOptions;
pub const ZigClangDiagnosticsEngine = struct_ZigClangDiagnosticsEngine;
pub const ZigClangDoStmt = struct_ZigClangDoStmt;
pub const ZigClangElaboratedType = struct_ZigClangElaboratedType;
pub const ZigClangEnumConstantDecl = struct_ZigClangEnumConstantDecl;
pub const ZigClangEnumDecl = struct_ZigClangEnumDecl;
pub const ZigClangEnumType = struct_ZigClangEnumType;
pub const ZigClangExpr = struct_ZigClangExpr;
pub const ZigClangFieldDecl = struct_ZigClangFieldDecl;
pub const ZigClangFileID = struct_ZigClangFileID;
pub const ZigClangForStmt = struct_ZigClangForStmt;
pub const ZigClangFullSourceLoc = struct_ZigClangFullSourceLoc;
pub const ZigClangFunctionProtoType = struct_ZigClangFunctionProtoType;
pub const ZigClangIfStmt = struct_ZigClangIfStmt;
pub const ZigClangImplicitCastExpr = struct_ZigClangImplicitCastExpr;
pub const ZigClangIncompleteArrayType = struct_ZigClangIncompleteArrayType;
pub const ZigClangIntegerLiteral = struct_ZigClangIntegerLiteral;
pub const ZigClangMacroDefinitionRecord = struct_ZigClangMacroDefinitionRecord;
pub const ZigClangMemberExpr = struct_ZigClangMemberExpr;
pub const ZigClangNamedDecl = struct_ZigClangNamedDecl;
pub const ZigClangNone = struct_ZigClangNone;
pub const ZigClangPCHContainerOperations = struct_ZigClangPCHContainerOperations;
pub const ZigClangParenExpr = struct_ZigClangParenExpr;
pub const ZigClangParenType = struct_ZigClangParenType;
pub const ZigClangParmVarDecl = struct_ZigClangParmVarDecl;
pub const ZigClangPointerType = struct_ZigClangPointerType;
pub const ZigClangPreprocessedEntity = struct_ZigClangPreprocessedEntity;
pub const ZigClangRecordDecl = struct_ZigClangRecordDecl;
pub const ZigClangRecordType = struct_ZigClangRecordType;
pub const ZigClangReturnStmt = struct_ZigClangReturnStmt;
pub const ZigClangSkipFunctionBodiesScope = struct_ZigClangSkipFunctionBodiesScope;
pub const ZigClangSourceManager = struct_ZigClangSourceManager;
pub const ZigClangSourceRange = struct_ZigClangSourceRange;
pub const ZigClangStmt = struct_ZigClangStmt;
pub const ZigClangStringLiteral = struct_ZigClangStringLiteral;
pub const ZigClangStringRef = struct_ZigClangStringRef;
pub const ZigClangSwitchStmt = struct_ZigClangSwitchStmt;
pub const ZigClangTagDecl = struct_ZigClangTagDecl;
pub const ZigClangType = struct_ZigClangType;
pub const ZigClangTypedefNameDecl = struct_ZigClangTypedefNameDecl;
pub const ZigClangTypedefType = struct_ZigClangTypedefType;
pub const ZigClangUnaryExprOrTypeTraitExpr = struct_ZigClangUnaryExprOrTypeTraitExpr;
pub const ZigClangUnaryOperator = struct_ZigClangUnaryOperator;
pub const ZigClangValueDecl = struct_ZigClangValueDecl;
pub const ZigClangVarDecl = struct_ZigClangVarDecl;
pub const ZigClangWhileStmt = struct_ZigClangWhileStmt;
pub const struct_ZigClangSourceLocation = extern struct {
ID: c_uint,
};
pub const Stage2ErrorMsg = extern struct {
filename_ptr: ?[*]const u8,
filename_len: usize,
msg_ptr: [*]const u8,
msg_len: usize,
// valid until the ASTUnit is freed
source: ?[*]const u8,
// 0 based
line: c_uint,
// 0 based
column: c_uint,
// byte offset into source
offset: c_uint,
};
pub extern fn ZigClangErrorMsg_delete(ptr: [*c]Stage2ErrorMsg, len: usize) void;
pub extern fn ZigClangLoadFromCommandLine(
args_begin: [*]?[*]const u8,
args_end: [*]?[*]const u8,
errors_ptr: *[*]Stage2ErrorMsg,
errors_len: *usize,
resources_path: [*c]const u8,
) ?*ZigClangASTUnit;
pub extern fn ZigClangDecl_getKind(decl: *const ZigClangDecl) ZigClangDeclKind;
pub extern fn ZigClangDecl_getDeclKindName(decl: *const struct_ZigClangDecl) [*]const u8;
pub const ZigClangDeclKind = extern enum {
AccessSpec,
Block,
Captured,
ClassScopeFunctionSpecialization,
Empty,
Export,
ExternCContext,
FileScopeAsm,
Friend,
FriendTemplate,
Import,
LinkageSpec,
Label,
Namespace,
NamespaceAlias,
ObjCCompatibleAlias,
ObjCCategory,
ObjCCategoryImpl,
ObjCImplementation,
ObjCInterface,
ObjCProtocol,
ObjCMethod,
ObjCProperty,
BuiltinTemplate,
ClassTemplate,
FunctionTemplate,
TypeAliasTemplate,
VarTemplate,
TemplateTemplateParm,
Enum,
Record,
CXXRecord,
ClassTemplateSpecialization,
ClassTemplatePartialSpecialization,
TemplateTypeParm,
ObjCTypeParam,
TypeAlias,
Typedef,
UnresolvedUsingTypename,
Using,
UsingDirective,
UsingPack,
UsingShadow,
ConstructorUsingShadow,
Binding,
Field,
ObjCAtDefsField,
ObjCIvar,
Function,
CXXDeductionGuide,
CXXMethod,
CXXConstructor,
CXXConversion,
CXXDestructor,
MSProperty,
NonTypeTemplateParm,
Var,
Decomposition,
ImplicitParam,
OMPCapturedExpr,
ParmVar,
VarTemplateSpecialization,
VarTemplatePartialSpecialization,
EnumConstant,
IndirectField,
OMPDeclareReduction,
UnresolvedUsingValue,
OMPRequires,
OMPThreadPrivate,
ObjCPropertyImpl,
PragmaComment,
PragmaDetectMismatch,
StaticAssert,
TranslationUnit,
};
pub const struct_ZigClangQualType = extern struct {
ptr: ?*c_void,
};
pub const ZigClangBuiltinTypeKind = extern enum {
OCLImage1dRO,
OCLImage1dArrayRO,
OCLImage1dBufferRO,
OCLImage2dRO,
OCLImage2dArrayRO,
OCLImage2dDepthRO,
OCLImage2dArrayDepthRO,
OCLImage2dMSAARO,
OCLImage2dArrayMSAARO,
OCLImage2dMSAADepthRO,
OCLImage2dArrayMSAADepthRO,
OCLImage3dRO,
OCLImage1dWO,
OCLImage1dArrayWO,
OCLImage1dBufferWO,
OCLImage2dWO,
OCLImage2dArrayWO,
OCLImage2dDepthWO,
OCLImage2dArrayDepthWO,
OCLImage2dMSAAWO,
OCLImage2dArrayMSAAWO,
OCLImage2dMSAADepthWO,
OCLImage2dArrayMSAADepthWO,
OCLImage3dWO,
OCLImage1dRW,
OCLImage1dArrayRW,
OCLImage1dBufferRW,
OCLImage2dRW,
OCLImage2dArrayRW,
OCLImage2dDepthRW,
OCLImage2dArrayDepthRW,
OCLImage2dMSAARW,
OCLImage2dArrayMSAARW,
OCLImage2dMSAADepthRW,
OCLImage2dArrayMSAADepthRW,
OCLImage3dRW,
OCLIntelSubgroupAVCMcePayload,
OCLIntelSubgroupAVCImePayload,
OCLIntelSubgroupAVCRefPayload,
OCLIntelSubgroupAVCSicPayload,
OCLIntelSubgroupAVCMceResult,
OCLIntelSubgroupAVCImeResult,
OCLIntelSubgroupAVCRefResult,
OCLIntelSubgroupAVCSicResult,
OCLIntelSubgroupAVCImeResultSingleRefStreamout,
OCLIntelSubgroupAVCImeResultDualRefStreamout,
OCLIntelSubgroupAVCImeSingleRefStreamin,
OCLIntelSubgroupAVCImeDualRefStreamin,
Void,
Bool,
Char_U,
UChar,
WChar_U,
Char8,
Char16,
Char32,
UShort,
UInt,
ULong,
ULongLong,
UInt128,
Char_S,
SChar,
WChar_S,
Short,
Int,
Long,
LongLong,
Int128,
ShortAccum,
Accum,
LongAccum,
UShortAccum,
UAccum,
ULongAccum,
ShortFract,
Fract,
LongFract,
UShortFract,
UFract,
ULongFract,
SatShortAccum,
SatAccum,
SatLongAccum,
SatUShortAccum,
SatUAccum,
SatULongAccum,
SatShortFract,
SatFract,
SatLongFract,
SatUShortFract,
SatUFract,
SatULongFract,
Half,
Float,
Double,
LongDouble,
Float16,
Float128,
NullPtr,
ObjCId,
ObjCClass,
ObjCSel,
OCLSampler,
OCLEvent,
OCLClkEvent,
OCLQueue,
OCLReserveID,
Dependent,
Overload,
BoundMember,
PseudoObject,
UnknownAny,
BuiltinFn,
ARCUnbridgedCast,
OMPArraySection,
};
pub const ZigClangCallingConv = extern enum {
C,
X86StdCall,
X86FastCall,
X86ThisCall,
X86VectorCall,
X86Pascal,
Win64,
X86_64SysV,
X86RegCall,
AAPCS,
AAPCS_VFP,
IntelOclBicc,
SpirFunction,
OpenCLKernel,
Swift,
PreserveMost,
PreserveAll,
AArch64VectorCall,
};
pub const ZigClangStorageClass = extern enum {
None,
Extern,
Static,
PrivateExtern,
Auto,
Register,
};
pub const ZigClangCompoundStmt_const_body_iterator = [*c]const *struct_ZigClangStmt;
pub extern fn ZigClangCompoundStmt_body_begin(self: *const ZigClangCompoundStmt) ZigClangCompoundStmt_const_body_iterator;
pub extern fn ZigClangCompoundStmt_body_end(self: *const ZigClangCompoundStmt) ZigClangCompoundStmt_const_body_iterator;

View File

@ -569,9 +569,9 @@ pub const Compilation = struct {
'i', 'u' => blk: {
for (name[1..]) |byte|
switch (byte) {
'0'...'9' => {},
else => break :blk,
};
'0'...'9' => {},
else => break :blk,
};
const is_signed = name[0] == 'i';
const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) {
error.Overflow => return error.Overflow,
@ -841,11 +841,9 @@ pub const Compilation = struct {
};
errdefer self.gpa().free(source_code);
const tree = try self.gpa().create(ast.Tree);
tree.* = try std.zig.parse(self.gpa(), source_code);
const tree = try std.zig.parse(self.gpa(), source_code);
errdefer {
tree.deinit();
self.gpa().destroy(tree);
}
break :blk try Scope.AstTree.create(self, tree, root_scope);

View File

@ -625,7 +625,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size);
defer allocator.free(source_code);
var tree = std.zig.parse(allocator, source_code) catch |err| {
const tree = std.zig.parse(allocator, source_code) catch |err| {
try stderr.print("error parsing stdin: {}\n", err);
os.exit(1);
};
@ -633,7 +633,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, tree, "<stdin>");
defer msg.destroy();
try msg.printToFile(stderr_file, color);
@ -642,12 +642,12 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
os.exit(1);
}
if (flags.present("check")) {
const anything_changed = try std.zig.render(allocator, io.null_out_stream, &tree);
const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree);
const code = if (anything_changed) u8(1) else u8(0);
os.exit(code);
}
_ = try std.zig.render(allocator, stdout, &tree);
_ = try std.zig.render(allocator, stdout, tree);
return;
}
@ -768,7 +768,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
};
defer fmt.loop.allocator.free(source_code);
var tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| {
const tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| {
try stderr.print("error parsing file '{}': {}\n", file_path, err);
fmt.any_error = true;
return;
@ -777,7 +777,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path);
const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, tree, file_path);
defer fmt.loop.allocator.destroy(msg);
try msg.printToFile(stderr_file, fmt.color);
@ -788,7 +788,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
}
if (check_mode) {
const anything_changed = try std.zig.render(fmt.loop.allocator, io.null_out_stream, &tree);
const anything_changed = try std.zig.render(fmt.loop.allocator, io.null_out_stream, tree);
if (anything_changed) {
try stderr.print("{}\n", file_path);
fmt.any_error = true;
@ -798,7 +798,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);
defer baf.destroy();
const anything_changed = try std.zig.render(fmt.loop.allocator, baf.stream(), &tree);
const anything_changed = try std.zig.render(fmt.loop.allocator, baf.stream(), tree);
if (anything_changed) {
try stderr.print("{}\n", file_path);
try baf.finish();
@ -858,7 +858,7 @@ fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
try stdout.write(usage);
}
const info_zen =
pub const info_zen =
\\
\\ * Communicate intent precisely.
\\ * Edge cases matter.

View File

@ -163,7 +163,6 @@ pub const Scope = struct {
pub fn destroy(self: *AstTree, comp: *Compilation) void {
comp.gpa().free(self.tree.source);
self.tree.deinit();
comp.gpa().destroy(self.tree);
comp.gpa().destroy(self);
}

View File

@ -1,32 +1,148 @@
// This is Zig code that is used by both stage1 and stage2.
// The prototypes in src/userland.h must match these definitions.
const std = @import("std");
const builtin = @import("builtin");
const os = std.os;
const io = std.io;
const mem = std.mem;
const Allocator = mem.Allocator;
const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
// ABI warning
export fn stage2_zen(ptr: *[*]const u8, len: *usize) void {
const info_zen = @import("main.zig").info_zen;
ptr.* = &info_zen;
len.* = info_zen.len;
}
// ABI warning
export fn stage2_panic(ptr: [*]const u8, len: usize) void {
@panic(ptr[0..len]);
}
// ABI warning
const TranslateMode = extern enum {
import,
translate,
};
// ABI warning
const Error = extern enum {
None,
OutOfMemory,
InvalidFormat,
SemanticAnalyzeFail,
AccessDenied,
Interrupted,
SystemResources,
FileNotFound,
FileSystem,
FileTooBig,
DivByZero,
Overflow,
PathAlreadyExists,
Unexpected,
ExactDivRemainder,
NegativeDenominator,
ShiftedOutOneBits,
CCompileErrors,
EndOfFile,
IsDir,
NotDir,
UnsupportedOperatingSystem,
SharingViolation,
PipeBusy,
PrimitiveTypeNotFound,
CacheUnavailable,
PathTooLong,
CCompilerCannotFindFile,
ReadingDepFile,
InvalidDepFile,
MissingArchitecture,
MissingOperatingSystem,
UnknownArchitecture,
UnknownOperatingSystem,
UnknownABI,
InvalidFilename,
DiskQuota,
DiskSpace,
UnexpectedWriteFailure,
UnexpectedSeekFailure,
UnexpectedFileTruncationFailure,
Unimplemented,
OperationAborted,
BrokenPipe,
NoSpaceLeft,
};
const FILE = std.c.FILE;
const ast = std.zig.ast;
const translate_c = @import("translate_c.zig");
const arg = @import("fmt/arg.zig");
const self_hosted_main = @import("fmt/main.zig");
const Args = arg.Args;
const Flag = arg.Flag;
const errmsg = @import("fmt/errmsg.zig");
/// Args should have a null terminating last arg.
export fn stage2_translate_c(
out_ast: **ast.Tree,
out_errors_ptr: *[*]translate_c.ClangErrMsg,
out_errors_len: *usize,
args_begin: [*]?[*]const u8,
args_end: [*]?[*]const u8,
mode: TranslateMode,
resources_path: [*]const u8,
) Error {
var errors: []translate_c.ClangErrMsg = undefined;
out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, switch (mode) {
.import => translate_c.Mode.import,
.translate => translate_c.Mode.translate,
}, &errors, resources_path) catch |err| switch (err) {
// TODO after https://github.com/ziglang/zig/issues/769 we can remove error.UnsupportedType
error.SemanticAnalyzeFail, error.UnsupportedType => {
out_errors_ptr.* = errors.ptr;
out_errors_len.* = errors.len;
return Error.CCompileErrors;
},
error.OutOfMemory => return Error.OutOfMemory,
};
return Error.None;
}
var stderr_file: os.File = undefined;
var stderr: *io.OutStream(os.File.WriteError) = undefined;
var stdout: *io.OutStream(os.File.WriteError) = undefined;
export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, errors_len: usize) void {
translate_c.freeErrors(errors_ptr[0..errors_len]);
}
// This brings `zig fmt` to stage 1.
pub fn main() !void {
// Here we use an ArenaAllocator backed by a DirectAllocator because `zig fmt` is a short-lived,
// one shot program. We don't need to waste time freeing memory and finding places to squish
// bytes into. So we free everything all at once at the very end.
var direct_allocator = std.heap.DirectAllocator.init();
var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator);
const allocator = &arena.allocator;
export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
const c_out_stream = &std.io.COutStream.init(output_file).stream;
_ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
error.SystemResources => return Error.SystemResources,
error.OperationAborted => return Error.OperationAborted,
error.BrokenPipe => return Error.BrokenPipe,
error.DiskQuota => return Error.DiskQuota,
error.FileTooBig => return Error.FileTooBig,
error.NoSpaceLeft => return Error.NoSpaceLeft,
error.AccessDenied => return Error.AccessDenied,
error.OutOfMemory => return Error.OutOfMemory,
error.Unexpected => return Error.Unexpected,
error.InputOutput => return Error.FileSystem,
};
return Error.None;
}
// TODO: just use the actual self-hosted zig fmt. Until the coroutine rewrite, we use a blocking implementation.
export fn stage2_fmt(argc: c_int, argv: [*]const [*]const u8) c_int {
if (std.debug.runtime_safety) {
fmtMain(argc, argv) catch unreachable;
} else {
fmtMain(argc, argv) catch |e| {
std.debug.warn("{}\n", @errorName(e));
return -1;
};
}
return 0;
}
fn fmtMain(argc: c_int, argv: [*]const [*]const u8) !void {
const allocator = std.heap.c_allocator;
var args_list = std.ArrayList([]const u8).init(allocator);
const argc_usize = @intCast(usize, argc);
var arg_i: usize = 0;
while (arg_i < argc_usize) : (arg_i += 1) {
try args_list.append(std.mem.toSliceConst(u8, argv[arg_i]));
}
var stdout_file = try std.io.getStdOut();
var stdout_out_stream = stdout_file.outStream();
@ -35,9 +151,9 @@ pub fn main() !void {
stderr_file = try std.io.getStdErr();
var stderr_out_stream = stderr_file.outStream();
stderr = &stderr_out_stream.stream;
const args = try std.os.argsAlloc(allocator);
var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args[1..]);
const args = args_list.toSliceConst();
var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args[2..]);
defer flags.deinit();
if (flags.present("help")) {
@ -71,7 +187,7 @@ pub fn main() !void {
const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size);
defer allocator.free(source_code);
var tree = std.zig.parse(allocator, source_code) catch |err| {
const tree = std.zig.parse(allocator, source_code) catch |err| {
try stderr.print("error parsing stdin: {}\n", err);
os.exit(1);
};
@ -79,18 +195,18 @@ pub fn main() !void {
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
try printErrMsgToFile(allocator, parse_error, &tree, "<stdin>", stderr_file, color);
try printErrMsgToFile(allocator, parse_error, tree, "<stdin>", stderr_file, color);
}
if (tree.errors.len != 0) {
os.exit(1);
}
if (flags.present("check")) {
const anything_changed = try std.zig.render(allocator, io.null_out_stream, &tree);
const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree);
const code = if (anything_changed) u8(1) else u8(0);
os.exit(code);
}
_ = try std.zig.render(allocator, stdout, &tree);
_ = try std.zig.render(allocator, stdout, tree);
return;
}
@ -166,7 +282,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void
};
defer fmt.allocator.free(source_code);
var tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
const tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
try stderr.print("error parsing file '{}': {}\n", file_path, err);
fmt.any_error = true;
return;
@ -175,7 +291,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
try printErrMsgToFile(fmt.allocator, parse_error, &tree, file_path, stderr_file, fmt.color);
try printErrMsgToFile(fmt.allocator, parse_error, tree, file_path, stderr_file, fmt.color);
}
if (tree.errors.len != 0) {
fmt.any_error = true;
@ -183,17 +299,16 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void
}
if (check_mode) {
const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, &tree);
const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree);
if (anything_changed) {
try stderr.print("{}\n", file_path);
fmt.any_error = true;
}
} else {
// TODO make this evented
const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path);
defer baf.destroy();
const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), &tree);
const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree);
if (anything_changed) {
try stderr.print("{}\n", file_path);
try baf.finish();
@ -210,9 +325,14 @@ const Fmt = struct {
const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
};
fn printErrMsgToFile(allocator: *mem.Allocator, parse_error: *const ast.Error, tree: *ast.Tree,
path: []const u8, file: os.File, color: errmsg.Color,) !void
{
fn printErrMsgToFile(
allocator: *mem.Allocator,
parse_error: *const ast.Error,
tree: *ast.Tree,
path: []const u8,
file: os.File,
color: errmsg.Color,
) !void {
const color_on = switch (color) {
errmsg.Color.Auto => file.isTty(),
errmsg.Color.On => true,
@ -258,3 +378,20 @@ fn printErrMsgToFile(allocator: *mem.Allocator, parse_error: *const ast.Error, t
try stream.writeByteNTimes('~', last_token.end - first_token.start);
try stream.write("\n");
}
const os = std.os;
const io = std.io;
const mem = std.mem;
const Allocator = mem.Allocator;
const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
const arg = @import("arg.zig");
const self_hosted_main = @import("main.zig");
const Args = arg.Args;
const Flag = arg.Flag;
const errmsg = @import("errmsg.zig");
var stderr_file: os.File = undefined;
var stderr: *io.OutStream(os.File.WriteError) = undefined;
var stdout: *io.OutStream(os.File.WriteError) = undefined;

View File

@ -0,0 +1,682 @@
// This is the userland implementation of translate-c which will be used by both stage1
// and stage2. Currently the only way it is used is with `zig translate-c-2`.
const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const ast = std.zig.ast;
const Token = std.zig.Token;
use @import("clang.zig");
pub const Mode = enum {
import,
translate,
};
// TODO merge with Type.Fn.CallingConvention
const CallingConvention = builtin.TypeInfo.CallingConvention;
pub const ClangErrMsg = Stage2ErrorMsg;
pub const Error = error{
OutOfMemory,
UnsupportedType,
};
pub const TransError = error{
OutOfMemory,
UnsupportedTranslation,
};
const DeclTable = std.HashMap(usize, void, addrHash, addrEql);
fn addrHash(x: usize) u32 {
switch (@typeInfo(usize).Int.bits) {
32 => return x,
// pointers are usually aligned so we ignore the bits that are probably all 0 anyway
// usually the larger bits of addr space are unused so we just chop em off
64 => return @truncate(u32, x >> 4),
else => @compileError("unreachable"),
}
}
fn addrEql(a: usize, b: usize) bool {
return a == b;
}
const Scope = struct {
id: Id,
parent: ?*Scope,
const Id = enum {
Switch,
Var,
Block,
Root,
While,
};
const Switch = struct {
base: Scope,
};
const Var = struct {
base: Scope,
c_name: []const u8,
zig_name: []const u8,
};
const Block = struct {
base: Scope,
block_node: *ast.Node.Block,
/// Don't forget to set rbrace token later
fn create(c: *Context, parent: *Scope, lbrace_tok: ast.TokenIndex) !*Block {
const block = try c.a().create(Block);
block.* = Block{
.base = Scope{
.id = Id.Block,
.parent = parent,
},
.block_node = try c.a().create(ast.Node.Block),
};
block.block_node.* = ast.Node.Block{
.base = ast.Node{ .id = ast.Node.Id.Block },
.label = null,
.lbrace = lbrace_tok,
.statements = ast.Node.Block.StatementList.init(c.a()),
.rbrace = undefined,
};
return block;
}
};
const Root = struct {
base: Scope,
};
const While = struct {
base: Scope,
};
};
const TransResult = struct {
node: *ast.Node,
node_scope: *Scope,
child_scope: *Scope,
};
const Context = struct {
tree: *ast.Tree,
source_buffer: *std.Buffer,
err: Error,
source_manager: *ZigClangSourceManager,
decl_table: DeclTable,
global_scope: *Scope.Root,
mode: Mode,
fn a(c: *Context) *std.mem.Allocator {
return &c.tree.arena_allocator.allocator;
}
/// Convert a null-terminated C string to a slice allocated in the arena
fn str(c: *Context, s: [*]const u8) ![]u8 {
return std.mem.dupe(c.a(), u8, std.mem.toSliceConst(u8, s));
}
/// Convert a clang source location to a file:line:column string
fn locStr(c: *Context, loc: ZigClangSourceLocation) ![]u8 {
const spelling_loc = ZigClangSourceManager_getSpellingLoc(c.source_manager, loc);
const filename_c = ZigClangSourceManager_getFilename(c.source_manager, spelling_loc);
const filename = if (filename_c) |s| try c.str(s) else ([]const u8)("(no file)");
const line = ZigClangSourceManager_getSpellingLineNumber(c.source_manager, spelling_loc);
const column = ZigClangSourceManager_getSpellingColumnNumber(c.source_manager, spelling_loc);
return std.fmt.allocPrint(c.a(), "{}:{}:{}", filename, line, column);
}
};
pub fn translate(
backing_allocator: *std.mem.Allocator,
args_begin: [*]?[*]const u8,
args_end: [*]?[*]const u8,
mode: Mode,
errors: *[]ClangErrMsg,
resources_path: [*]const u8,
) !*ast.Tree {
const ast_unit = ZigClangLoadFromCommandLine(
args_begin,
args_end,
&errors.ptr,
&errors.len,
resources_path,
) orelse {
if (errors.len == 0) return error.OutOfMemory;
return error.SemanticAnalyzeFail;
};
defer ZigClangASTUnit_delete(ast_unit);
var tree_arena = std.heap.ArenaAllocator.init(backing_allocator);
errdefer tree_arena.deinit();
var arena = &tree_arena.allocator;
const root_node = try arena.create(ast.Node.Root);
root_node.* = ast.Node.Root{
.base = ast.Node{ .id = ast.Node.Id.Root },
.decls = ast.Node.Root.DeclList.init(arena),
.doc_comments = null,
// initialized with the eof token at the end
.eof_token = undefined,
};
const tree = try arena.create(ast.Tree);
tree.* = ast.Tree{
.source = undefined, // need to use Buffer.toOwnedSlice later
.root_node = root_node,
.arena_allocator = undefined,
.tokens = ast.Tree.TokenList.init(arena),
.errors = ast.Tree.ErrorList.init(arena),
};
tree.arena_allocator = tree_arena;
arena = &tree.arena_allocator.allocator;
var source_buffer = try std.Buffer.initSize(arena, 0);
var context = Context{
.tree = tree,
.source_buffer = &source_buffer,
.source_manager = ZigClangASTUnit_getSourceManager(ast_unit),
.err = undefined,
.decl_table = DeclTable.init(arena),
.global_scope = try arena.create(Scope.Root),
.mode = mode,
};
context.global_scope.* = Scope.Root{
.base = Scope{
.id = Scope.Id.Root,
.parent = null,
},
};
if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, &context, declVisitorC)) {
return context.err;
}
_ = try appendToken(&context, .Eof, "");
tree.source = source_buffer.toOwnedSlice();
if (false) {
std.debug.warn("debug source:\n{}\n==EOF==\ntokens:\n", tree.source);
var i: usize = 0;
while (i < tree.tokens.len) : (i += 1) {
const token = tree.tokens.at(i);
std.debug.warn("{}\n", token);
}
}
return tree;
}
extern fn declVisitorC(context: ?*c_void, decl: *const ZigClangDecl) bool {
const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
declVisitor(c, decl) catch |err| {
c.err = err;
return false;
};
return true;
}
fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void {
switch (ZigClangDecl_getKind(decl)) {
.Function => {
return visitFnDecl(c, @ptrCast(*const ZigClangFunctionDecl, decl));
},
.Typedef => {
try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for typedefs");
},
.Enum => {
try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for enums");
},
.Record => {
try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for structs");
},
.Var => {
try emitWarning(c, ZigClangDecl_getLocation(decl), "TODO implement translate-c for variables");
},
else => {
const decl_name = try c.str(ZigClangDecl_getDeclKindName(decl));
try emitWarning(c, ZigClangDecl_getLocation(decl), "ignoring {} declaration", decl_name);
},
}
}
fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
if (try c.decl_table.put(@ptrToInt(fn_decl), {})) |_| return; // Avoid processing this decl twice
const rp = makeRestorePoint(c);
const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl)));
const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl);
const fn_qt = ZigClangFunctionDecl_getType(fn_decl);
const fn_type = ZigClangQualType_getTypePtr(fn_qt);
var scope = &c.global_scope.base;
const has_body = ZigClangFunctionDecl_hasBody(fn_decl);
const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl);
const decl_ctx = FnDeclContext{
.fn_name = fn_name,
.has_body = has_body,
.storage_class = storage_class,
.scope = &scope,
.is_export = switch (storage_class) {
.None => has_body,
.Extern, .Static => false,
.PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern"),
.Auto => unreachable, // Not legal on functions
.Register => unreachable, // Not legal on functions
},
};
const proto_node = switch (ZigClangType_getTypeClass(fn_type)) {
.FunctionProto => blk: {
const fn_proto_type = @ptrCast(*const ZigClangFunctionProtoType, fn_type);
break :blk transFnProto(rp, fn_proto_type, fn_decl_loc, decl_ctx) catch |err| switch (err) {
error.UnsupportedType => {
return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function");
},
error.OutOfMemory => return error.OutOfMemory,
};
},
.FunctionNoProto => blk: {
const fn_no_proto_type = @ptrCast(*const ZigClangFunctionType, fn_type);
break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx) catch |err| switch (err) {
error.UnsupportedType => {
return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function");
},
error.OutOfMemory => return error.OutOfMemory,
};
},
else => unreachable,
};
if (!decl_ctx.has_body) {
const semi_tok = try appendToken(c, .Semicolon, ";");
return addTopLevelDecl(c, fn_name, &proto_node.base);
}
// actual function definition with body
const body_stmt = ZigClangFunctionDecl_getBody(fn_decl);
const result = transStmt(rp, scope, body_stmt, .unused, .r_value) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.UnsupportedTranslation => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function"),
};
assert(result.node.id == ast.Node.Id.Block);
proto_node.body_node = result.node;
return addTopLevelDecl(c, fn_name, &proto_node.base);
}
const ResultUsed = enum {
used,
unused,
};
const LRValue = enum {
l_value,
r_value,
};
fn transStmt(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangStmt,
result_used: ResultUsed,
lrvalue: LRValue,
) !TransResult {
const sc = ZigClangStmt_getStmtClass(stmt);
switch (sc) {
.CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const ZigClangCompoundStmt, stmt)),
else => {
return revertAndWarn(
rp,
error.UnsupportedTranslation,
ZigClangStmt_getBeginLoc(stmt),
"TODO implement translation of stmt class {}",
@tagName(sc),
);
},
}
}
fn transCompoundStmtInline(
rp: RestorePoint,
parent_scope: *Scope,
stmt: *const ZigClangCompoundStmt,
block_node: *ast.Node.Block,
) TransError!TransResult {
var it = ZigClangCompoundStmt_body_begin(stmt);
const end_it = ZigClangCompoundStmt_body_end(stmt);
var scope = parent_scope;
while (it != end_it) : (it += 1) {
const result = try transStmt(rp, scope, it.*, .unused, .r_value);
scope = result.child_scope;
try block_node.statements.push(result.node);
}
return TransResult{
.node = &block_node.base,
.child_scope = scope,
.node_scope = scope,
};
}
fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) !TransResult {
const lbrace_tok = try appendToken(rp.c, .LBrace, "{");
const block_scope = try Scope.Block.create(rp.c, scope, lbrace_tok);
const inline_result = try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node);
block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
return TransResult{
.node = &block_scope.block_node.base,
.node_scope = inline_result.node_scope,
.child_scope = inline_result.child_scope,
};
}
fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void {
try c.tree.root_node.decls.push(decl_node);
}
fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) Error!*ast.Node {
return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
}
fn qualTypeCanon(qt: ZigClangQualType) *const ZigClangType {
const canon = ZigClangQualType_getCanonicalType(qt);
return ZigClangQualType_getTypePtr(canon);
}
const RestorePoint = struct {
c: *Context,
token_index: ast.TokenIndex,
src_buf_index: usize,
fn activate(self: RestorePoint) void {
self.c.tree.tokens.shrink(self.token_index);
self.c.source_buffer.shrink(self.src_buf_index);
}
};
fn makeRestorePoint(c: *Context) RestorePoint {
return RestorePoint{
.c = c,
.token_index = c.tree.tokens.len,
.src_buf_index = c.source_buffer.len(),
};
}
fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSourceLocation) Error!*ast.Node {
switch (ZigClangType_getTypeClass(ty)) {
.Builtin => {
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
switch (ZigClangBuiltinType_getKind(builtin_ty)) {
.Void => return appendIdentifier(rp.c, "c_void"),
.Bool => return appendIdentifier(rp.c, "bool"),
.Char_U, .UChar, .Char_S, .Char8 => return appendIdentifier(rp.c, "u8"),
.SChar => return appendIdentifier(rp.c, "i8"),
.UShort => return appendIdentifier(rp.c, "c_ushort"),
.UInt => return appendIdentifier(rp.c, "c_uint"),
.ULong => return appendIdentifier(rp.c, "c_ulong"),
.ULongLong => return appendIdentifier(rp.c, "c_ulonglong"),
.Short => return appendIdentifier(rp.c, "c_short"),
.Int => return appendIdentifier(rp.c, "c_int"),
.Long => return appendIdentifier(rp.c, "c_long"),
.LongLong => return appendIdentifier(rp.c, "c_longlong"),
.UInt128 => return appendIdentifier(rp.c, "u128"),
.Int128 => return appendIdentifier(rp.c, "i128"),
.Float => return appendIdentifier(rp.c, "f32"),
.Double => return appendIdentifier(rp.c, "f64"),
.Float128 => return appendIdentifier(rp.c, "f128"),
.Float16 => return appendIdentifier(rp.c, "f16"),
.LongDouble => return appendIdentifier(rp.c, "c_longdouble"),
else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type"),
}
},
.FunctionProto => {
const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty);
const fn_proto = try transFnProto(rp, fn_proto_ty, source_loc, null);
return &fn_proto.base;
},
else => {
const type_name = rp.c.str(ZigClangType_getTypeClassName(ty));
return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", type_name);
},
}
}
const FnDeclContext = struct {
fn_name: []const u8,
has_body: bool,
storage_class: ZigClangStorageClass,
scope: **Scope,
is_export: bool,
};
fn transCC(
rp: RestorePoint,
fn_ty: *const ZigClangFunctionType,
source_loc: ZigClangSourceLocation,
) !CallingConvention {
const clang_cc = ZigClangFunctionType_getCallConv(fn_ty);
switch (clang_cc) {
.C => return CallingConvention.C,
.X86StdCall => return CallingConvention.Stdcall,
else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: {}", @tagName(clang_cc)),
}
}
fn transFnProto(
rp: RestorePoint,
fn_proto_ty: *const ZigClangFunctionProtoType,
source_loc: ZigClangSourceLocation,
fn_decl_context: ?FnDeclContext,
) !*ast.Node.FnProto {
const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_proto_ty);
const cc = try transCC(rp, fn_ty, source_loc);
const is_var_args = ZigClangFunctionProtoType_isVariadic(fn_proto_ty);
const param_count: usize = ZigClangFunctionProtoType_getNumParams(fn_proto_ty);
var i: usize = 0;
while (i < param_count) : (i += 1) {
return revertAndWarn(rp, error.UnsupportedType, source_loc, "TODO: implement parameters for FunctionProto in transType");
}
return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc);
}
fn transFnNoProto(
rp: RestorePoint,
fn_ty: *const ZigClangFunctionType,
source_loc: ZigClangSourceLocation,
fn_decl_context: ?FnDeclContext,
) !*ast.Node.FnProto {
const cc = try transCC(rp, fn_ty, source_loc);
const is_var_args = if (fn_decl_context) |ctx| !ctx.is_export else true;
return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc);
}
fn finishTransFnProto(
rp: RestorePoint,
fn_ty: *const ZigClangFunctionType,
source_loc: ZigClangSourceLocation,
fn_decl_context: ?FnDeclContext,
is_var_args: bool,
cc: CallingConvention,
) !*ast.Node.FnProto {
const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
// TODO check for always_inline attribute
// TODO check for align attribute
// pub extern fn name(...) T
const pub_tok = try appendToken(rp.c, .Keyword_pub, "pub");
const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null;
const extern_export_inline_tok = if (is_export)
try appendToken(rp.c, .Keyword_export, "export")
else if (cc == .C)
try appendToken(rp.c, .Keyword_extern, "extern")
else
null;
const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn");
const name_tok = if (fn_decl_context) |ctx| try appendToken(rp.c, .Identifier, ctx.fn_name) else null;
const lparen_tok = try appendToken(rp.c, .LParen, "(");
const var_args_tok = if (is_var_args) try appendToken(rp.c, .Ellipsis3, "...") else null;
const rparen_tok = try appendToken(rp.c, .RParen, ")");
const return_type_node = blk: {
if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) {
break :blk try appendIdentifier(rp.c, "noreturn");
} else {
const return_qt = ZigClangFunctionType_getReturnType(fn_ty);
if (ZigClangType_isVoidType(qualTypeCanon(return_qt))) {
break :blk try appendIdentifier(rp.c, "void");
} else {
break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) {
error.UnsupportedType => {
try emitWarning(rp.c, source_loc, "unsupported function proto return type");
return err;
},
error.OutOfMemory => return error.OutOfMemory,
};
}
}
};
const fn_proto = try rp.c.a().create(ast.Node.FnProto);
fn_proto.* = ast.Node.FnProto{
.base = ast.Node{ .id = ast.Node.Id.FnProto },
.doc_comments = null,
.visib_token = pub_tok,
.fn_token = fn_tok,
.name_token = name_tok,
.params = ast.Node.FnProto.ParamList.init(rp.c.a()),
.return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node },
.var_args_token = null, // TODO this field is broken in the AST data model
.extern_export_inline_token = extern_export_inline_tok,
.cc_token = cc_tok,
.async_attr = null,
.body_node = null,
.lib_name = null,
.align_expr = null,
.section_expr = null,
};
if (is_var_args) {
const var_arg_node = try rp.c.a().create(ast.Node.ParamDecl);
var_arg_node.* = ast.Node.ParamDecl{
.base = ast.Node{ .id = ast.Node.Id.ParamDecl },
.doc_comments = null,
.comptime_token = null,
.noalias_token = null,
.name_token = null,
.type_node = undefined,
.var_args_token = var_args_tok,
};
try fn_proto.params.push(&var_arg_node.base);
}
return fn_proto;
}
fn revertAndWarn(
rp: RestorePoint,
err: var,
source_loc: ZigClangSourceLocation,
comptime format: []const u8,
args: ...,
) (@typeOf(err) || error{OutOfMemory}) {
rp.activate();
try emitWarning(rp.c, source_loc, format, args);
return err;
}
fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: ...) !void {
_ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, c.locStr(loc), args);
}
fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: ...) !void {
// const name = @compileError(msg);
const const_tok = try appendToken(c, .Keyword_const, "const");
const name_tok = try appendToken(c, .Identifier, name);
const eq_tok = try appendToken(c, .Equal, "=");
const builtin_tok = try appendToken(c, .Builtin, "@compileError");
const lparen_tok = try appendToken(c, .LParen, "(");
const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args);
const rparen_tok = try appendToken(c, .RParen, ")");
const semi_tok = try appendToken(c, .Semicolon, ";");
const msg_node = try c.a().create(ast.Node.StringLiteral);
msg_node.* = ast.Node.StringLiteral{
.base = ast.Node{ .id = ast.Node.Id.StringLiteral },
.token = msg_tok,
};
const call_node = try c.a().create(ast.Node.BuiltinCall);
call_node.* = ast.Node.BuiltinCall{
.base = ast.Node{ .id = ast.Node.Id.BuiltinCall },
.builtin_token = builtin_tok,
.params = ast.Node.BuiltinCall.ParamList.init(c.a()),
.rparen_token = rparen_tok,
};
try call_node.params.push(&msg_node.base);
const var_decl_node = try c.a().create(ast.Node.VarDecl);
var_decl_node.* = ast.Node.VarDecl{
.base = ast.Node{ .id = ast.Node.Id.VarDecl },
.doc_comments = null,
.visib_token = null,
.thread_local_token = null,
.name_token = name_tok,
.eq_token = eq_tok,
.mut_token = const_tok,
.comptime_token = null,
.extern_export_token = null,
.lib_name = null,
.type_node = null,
.align_node = null,
.section_node = null,
.init_node = &call_node.base,
.semicolon_token = semi_tok,
};
try c.tree.root_node.decls.push(&var_decl_node.base);
}
fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex {
return appendTokenFmt(c, token_id, "{}", bytes);
}
fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: ...) !ast.TokenIndex {
const S = struct {
fn callback(context: *Context, bytes: []const u8) error{OutOfMemory}!void {
return context.source_buffer.append(bytes);
}
};
const start_index = c.source_buffer.len();
errdefer c.source_buffer.shrink(start_index);
try std.fmt.format(c, error{OutOfMemory}, S.callback, format, args);
const end_index = c.source_buffer.len();
const token_index = c.tree.tokens.len;
const new_token = try c.tree.tokens.addOne();
errdefer c.tree.tokens.shrink(token_index);
new_token.* = Token{
.id = token_id,
.start = start_index,
.end = end_index,
};
try c.source_buffer.appendByte('\n');
return token_index;
}
fn appendIdentifier(c: *Context, name: []const u8) !*ast.Node {
const token_index = try appendToken(c, .Identifier, name);
const identifier = try c.a().create(ast.Node.Identifier);
identifier.* = ast.Node.Identifier{
.base = ast.Node{ .id = ast.Node.Id.Identifier },
.token = token_index,
};
return &identifier.base;
}
pub fn freeErrors(errors: []ClangErrMsg) void {
ZigClangErrorMsg_delete(errors.ptr, errors.len);
}

View File

@ -538,21 +538,21 @@ pub const Value = struct {
switch (self.base.typ.id) {
Type.Id.Int => {
const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
if (self.big_int.len == 0) {
if (self.big_int.len() == 0) {
return llvm.ConstNull(type_ref);
}
const unsigned_val = if (self.big_int.len == 1) blk: {
const unsigned_val = if (self.big_int.len() == 1) blk: {
break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false));
} else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: {
break :blk llvm.ConstIntOfArbitraryPrecision(
type_ref,
@intCast(c_uint, self.big_int.len),
@intCast(c_uint, self.big_int.len()),
@ptrCast([*]u64, self.big_int.limbs.ptr),
);
} else {
@compileError("std.math.Big.Int.Limb size does not match LLVM");
};
return if (self.big_int.positive) unsigned_val else llvm.ConstNeg(unsigned_val);
return if (self.big_int.isPositive()) unsigned_val else llvm.ConstNeg(unsigned_val);
},
Type.Id.ComptimeInt => unreachable,
else => unreachable,

View File

@ -55,7 +55,7 @@ struct IrExecutable {
size_t mem_slot_count;
size_t next_debug_id;
size_t *backward_branch_count;
size_t backward_branch_quota;
size_t *backward_branch_quota;
ZigFn *fn_entry;
Buf *c_import_buf;
AstNode *source_node;
@ -1350,6 +1350,7 @@ struct ZigFn {
IrExecutable ir_executable;
IrExecutable analyzed_executable;
size_t prealloc_bbc;
size_t prealloc_backward_branch_quota;
AstNode **param_source_nodes;
Buf **param_names;
@ -1855,10 +1856,13 @@ struct CodeGen {
bool strip_debug_symbols;
bool is_test_build;
bool is_single_threaded;
bool want_single_threaded;
bool linker_rdynamic;
bool each_lib_rpath;
bool is_dummy_so;
bool disable_gen_h;
bool bundle_compiler_rt;
bool disable_stack_probing;
Buf *mmacosx_version_min;
Buf *mios_version_min;
@ -2291,6 +2295,7 @@ enum IrInstructionId {
IrInstructionIdVectorToArray,
IrInstructionIdArrayToVector,
IrInstructionIdAssertZero,
IrInstructionIdAssertNonNull,
};
struct IrInstruction {
@ -3480,6 +3485,12 @@ struct IrInstructionAssertZero {
IrInstruction *target;
};
struct IrInstructionAssertNonNull {
IrInstruction base;
IrInstruction *target;
};
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;

View File

@ -969,8 +969,9 @@ static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *no
Buf *type_name)
{
size_t backward_branch_count = 0;
size_t backward_branch_quota = default_backward_branch_quota;
return ir_eval_const_value(g, scope, node, type_entry,
&backward_branch_count, default_backward_branch_quota,
&backward_branch_count, &backward_branch_quota,
nullptr, nullptr, node, type_name, nullptr, nullptr);
}
@ -1907,6 +1908,18 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) {
return ErrorNone;
}
static bool type_is_valid_extern_enum_tag(CodeGen *g, ZigType *ty) {
// Only integer types are allowed by the C ABI
if(ty->id != ZigTypeIdInt)
return false;
// According to the ANSI C standard the enumeration type should be either a
// signed char, a signed integer or an unsigned one. But GCC/Clang allow
// other integral types as a compiler extension so let's accomodate them
// aswell.
return type_allowed_in_extern(g, ty);
}
static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
assert(enum_type->id == ZigTypeIdEnum);
@ -1964,7 +1977,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
enum_type->abi_size = tag_int_type->abi_size;
enum_type->abi_align = tag_int_type->abi_align;
// TODO: Are extern enums allowed to have an init_arg_expr?
if (decl_node->data.container_decl.init_arg_expr != nullptr) {
ZigType *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
if (type_is_invalid(wanted_tag_int_type)) {
@ -1973,24 +1985,29 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
enum_type->data.enumeration.is_invalid = true;
add_node_error(g, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));
} else if (wanted_tag_int_type->data.integral.is_signed) {
} else if (enum_type->data.enumeration.layout == ContainerLayoutExtern &&
!type_is_valid_extern_enum_tag(g, wanted_tag_int_type)) {
enum_type->data.enumeration.is_invalid = true;
add_node_error(g, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("expected unsigned integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));
} else if (wanted_tag_int_type->data.integral.bit_count < tag_int_type->data.integral.bit_count) {
enum_type->data.enumeration.is_invalid = true;
add_node_error(g, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("'%s' too small to hold all bits; must be at least '%s'",
buf_ptr(&wanted_tag_int_type->name), buf_ptr(&tag_int_type->name)));
ErrorMsg *msg = add_node_error(g, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("'%s' is not a valid tag type for an extern enum",
buf_ptr(&wanted_tag_int_type->name)));
add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("any integral type of size 8, 16, 32, 64 or 128 bit is valid"));
} else {
tag_int_type = wanted_tag_int_type;
}
}
enum_type->data.enumeration.tag_int_type = tag_int_type;
enum_type->size_in_bits = tag_int_type->size_in_bits;
enum_type->abi_size = tag_int_type->abi_size;
enum_type->abi_align = tag_int_type->abi_align;
BigInt bi_one;
bigint_init_unsigned(&bi_one, 1);
TypeEnumField *last_enum_field = nullptr;
for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
@ -2016,60 +2033,58 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
AstNode *tag_value = field_node->data.struct_field.value;
// In this first pass we resolve explicit tag values.
// In a second pass we will fill in the unspecified ones.
if (tag_value != nullptr) {
// A user-specified value is available
ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr);
if (type_is_invalid(result->type)) {
enum_type->data.enumeration.is_invalid = true;
continue;
}
assert(result->special != ConstValSpecialRuntime);
assert(result->type->id == ZigTypeIdInt ||
result->type->id == ZigTypeIdComptimeInt);
auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value);
if (entry == nullptr) {
bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);
} else {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &result->data.x_bigint, 10);
assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt);
ErrorMsg *msg = add_node_error(g, tag_value,
buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
add_error_note(g, msg, entry->value,
buf_sprintf("other occurrence here"));
bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);
} else {
// No value was explicitly specified: allocate the last value + 1
// or, if this is the first element, zero
if (last_enum_field != nullptr) {
bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one);
} else {
bigint_init_unsigned(&type_enum_field->value, 0);
}
// Make sure we can represent this number with tag_int_type
if (!bigint_fits_in_bits(&type_enum_field->value,
tag_int_type->size_in_bits,
tag_int_type->data.integral.is_signed)) {
enum_type->data.enumeration.is_invalid = true;
continue;
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &type_enum_field->value, 10);
add_node_error(g, field_node,
buf_sprintf("enumeration value %s too large for type '%s'",
buf_ptr(val_buf), buf_ptr(&tag_int_type->name)));
break;
}
}
}
// Now iterate again and populate the unspecified tag values
uint32_t next_maybe_unoccupied_index = 0;
// Make sure the value is unique
auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node);
if (entry != nullptr) {
enum_type->data.enumeration.is_invalid = true;
for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
AstNode *tag_value = field_node->data.struct_field.value;
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &type_enum_field->value, 10);
if (tag_value == nullptr) {
if (occupied_tag_values.size() == 0) {
bigint_init_unsigned(&type_enum_field->value, next_maybe_unoccupied_index);
next_maybe_unoccupied_index += 1;
} else {
BigInt proposed_value;
for (;;) {
bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index);
next_maybe_unoccupied_index += 1;
auto entry = occupied_tag_values.put_unique(proposed_value, field_node);
if (entry != nullptr) {
continue;
}
break;
}
bigint_init_bigint(&type_enum_field->value, &proposed_value);
}
ErrorMsg *msg = add_node_error(g, field_node,
buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
add_error_note(g, msg, entry->value,
buf_sprintf("other occurrence here"));
}
last_enum_field = type_enum_field;
}
enum_type->data.enumeration.zero_bits_loop_flag = false;
@ -2607,7 +2622,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
return ErrorNone;
}
static void get_fully_qualified_decl_name(Buf *buf, Tld *tld) {
static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, bool is_test) {
buf_resize(buf, 0);
Scope *scope = tld->parent_scope;
@ -2617,15 +2632,23 @@ static void get_fully_qualified_decl_name(Buf *buf, Tld *tld) {
ScopeDecls *decls_scope = reinterpret_cast<ScopeDecls *>(scope);
buf_append_buf(buf, &decls_scope->container_type->name);
if (buf_len(buf) != 0) buf_append_char(buf, NAMESPACE_SEP_CHAR);
buf_append_buf(buf, tld->name);
if (is_test) {
buf_append_str(buf, "test \"");
buf_append_buf(buf, tld->name);
buf_append_char(buf, '"');
} else {
buf_append_buf(buf, tld->name);
}
}
ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
ZigFn *fn_entry = allocate<ZigFn>(1);
fn_entry->prealloc_backward_branch_quota = default_backward_branch_quota;
fn_entry->codegen = g;
fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc;
fn_entry->analyzed_executable.backward_branch_quota = default_backward_branch_quota;
fn_entry->analyzed_executable.backward_branch_quota = &fn_entry->prealloc_backward_branch_quota;
fn_entry->analyzed_executable.fn_entry = fn_entry;
fn_entry->ir_executable.fn_entry = fn_entry;
fn_entry->fn_inline = inline_value;
@ -2726,7 +2749,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
if (fn_proto->is_export || is_extern) {
buf_init_from_buf(&fn_table_entry->symbol_name, tld_fn->base.name);
} else {
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base);
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, false);
}
if (fn_proto->is_export) {
@ -2787,7 +2810,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
} else if (source_node->type == NodeTypeTestDecl) {
ZigFn *fn_table_entry = create_fn_raw(g, FnInlineAuto);
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base);
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, true);
tld_fn->fn_entry = fn_table_entry;
@ -3722,7 +3745,7 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
}
if (g->verbose_ir) {
fprintf(stderr, "\n");
ast_render(g, stderr, fn_table_entry->body_node, 4);
ast_render(stderr, fn_table_entry->body_node, 4);
fprintf(stderr, "\n{ // (IR)\n");
ir_print(g, stderr, &fn_table_entry->ir_executable, 4);
fprintf(stderr, "}\n");
@ -5155,11 +5178,10 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
if (bigint_cmp(&union1->tag, &union2->tag) == CmpEQ) {
TypeUnionField *field = find_union_field_by_tag(a->type, &union1->tag);
assert(field != nullptr);
if (type_has_bits(field->type_entry)) {
zig_panic("TODO const expr analyze union field value for equality");
} else {
if (!type_has_bits(field->type_entry))
return true;
}
assert(find_union_field_by_tag(a->type, &union2->tag) != nullptr);
return const_values_equal(g, union1->payload, union2->payload);
}
return false;
}
@ -6070,7 +6092,7 @@ Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents) {
if (g->enable_cache) {
return cache_add_file_fetch(&g->cache_hash, resolved_path, contents);
} else {
return os_fetch_file_path(resolved_path, contents, false);
return os_fetch_file_path(resolved_path, contents);
}
}
@ -7222,3 +7244,16 @@ ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type) {
assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));
return type->llvm_di_type;
}
void src_assert(bool ok, AstNode *source_node) {
if (ok) return;
if (source_node == nullptr) {
fprintf(stderr, "when analyzing (unknown source location): ");
} else {
fprintf(stderr, "when analyzing %s:%u:%u: ",
buf_ptr(source_node->owner->data.structure.root_struct->path),
(unsigned)source_node->line + 1, (unsigned)source_node->column + 1);
}
const char *msg = "assertion failed";
stage2_panic(msg, strlen(msg));
}

View File

@ -247,4 +247,8 @@ Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose
LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type);
ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type);
void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_path, bool translate_c);
void src_assert(bool ok, AstNode *source_node);
#endif

View File

@ -296,7 +296,6 @@ void ast_print(FILE *f, AstNode *node, int indent) {
struct AstRender {
CodeGen *codegen;
int indent;
int indent_size;
FILE *f;
@ -633,7 +632,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
if (is_printable(c)) {
fprintf(ar->f, "'%c'", c);
} else {
fprintf(ar->f, "'\\x%x'", (int)c);
fprintf(ar->f, "'\\x%02x'", (int)c);
}
break;
}
@ -1170,9 +1169,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
}
void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size) {
void ast_render(FILE *f, AstNode *node, int indent_size) {
AstRender ar = {0};
ar.codegen = codegen;
ar.f = f;
ar.indent_size = indent_size;
ar.indent = 0;

View File

@ -15,6 +15,6 @@
void ast_print(FILE *f, AstNode *node, int indent);
void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size);
void ast_render(FILE *f, AstNode *node, int indent_size);
#endif

View File

@ -1395,7 +1395,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
uint64_t shift_amt = bigint_as_unsigned(op2);
if (op1->digit_count == 1) {
dest->data.digit = op1_digits[0] >> shift_amt;
dest->data.digit = (shift_amt < 64) ? op1_digits[0] >> shift_amt : 0;
dest->digit_count = 1;
dest->is_negative = op1->is_negative;
bigint_normalize(dest);
@ -1410,12 +1410,19 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
}
dest->digit_count = op1->digit_count - digit_shift_count;
dest->data.digits = allocate<uint64_t>(dest->digit_count);
uint64_t *digits;
if (dest->digit_count == 1) {
digits = &dest->data.digit;
} else {
digits = allocate<uint64_t>(dest->digit_count);
dest->data.digits = digits;
}
uint64_t carry = 0;
for (size_t op_digit_index = op1->digit_count - 1;;) {
uint64_t digit = op1_digits[op_digit_index];
size_t dest_digit_index = op_digit_index - digit_shift_count;
dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count);
digits[dest_digit_index] = carry | (digit >> leftover_shift_count);
carry = digit << (64 - leftover_shift_count);
if (dest_digit_index == 0) { break; }

View File

@ -10,7 +10,6 @@
#include "list.hpp"
#include <assert.h>
#include <stdint.h>
#include <ctype.h>
#include <stdarg.h>

View File

@ -124,6 +124,8 @@ static void begin_token(CTokenize *ctok, CTokId id) {
case CTokIdAsterisk:
case CTokIdBang:
case CTokIdTilde:
case CTokIdShl:
case CTokIdLt:
break;
}
}
@ -223,6 +225,10 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) {
begin_token(ctok, CTokIdDot);
end_token(ctok);
break;
case '<':
begin_token(ctok, CTokIdLt);
ctok->state = CTokStateGotLt;
break;
case '(':
begin_token(ctok, CTokIdLParen);
end_token(ctok);
@ -251,6 +257,19 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) {
return mark_error(ctok);
}
break;
case CTokStateGotLt:
switch (*c) {
case '<':
ctok->cur_tok->id = CTokIdShl;
end_token(ctok);
ctok->state = CTokStateStart;
break;
default:
end_token(ctok);
ctok->state = CTokStateStart;
continue;
}
break;
case CTokStateFloat:
switch (*c) {
case '.':
@ -791,6 +810,7 @@ found_end_of_macro:
case CTokStateNumLitIntSuffixL:
case CTokStateNumLitIntSuffixUL:
case CTokStateNumLitIntSuffixLL:
case CTokStateGotLt:
end_token(ctok);
break;
case CTokStateFloat:

View File

@ -25,6 +25,8 @@ enum CTokId {
CTokIdAsterisk,
CTokIdBang,
CTokIdTilde,
CTokIdShl,
CTokIdLt,
};
enum CNumLitSuffix {
@ -78,6 +80,7 @@ enum CTokState {
CTokStateNumLitIntSuffixL,
CTokStateNumLitIntSuffixLL,
CTokStateNumLitIntSuffixUL,
CTokStateGotLt,
};
struct CTokenize {

View File

@ -256,10 +256,10 @@ static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf, Buf *contents
}
if ((err = hash_file(chf->bin_digest, this_file, contents))) {
os_file_close(this_file);
os_file_close(&this_file);
return err;
}
os_file_close(this_file);
os_file_close(&this_file);
blake2b_update(&ch->blake, chf->bin_digest, 48);
@ -300,7 +300,7 @@ Error cache_hit(CacheHash *ch, Buf *out_digest) {
Buf line_buf = BUF_INIT;
buf_resize(&line_buf, 512);
if ((err = os_file_read_all(ch->manifest_file, &line_buf))) {
os_file_close(ch->manifest_file);
os_file_close(&ch->manifest_file);
return err;
}
@ -389,14 +389,14 @@ Error cache_hit(CacheHash *ch, Buf *out_digest) {
OsFileAttr actual_attr;
if ((err = os_file_open_r(chf->path, &this_file, &actual_attr))) {
fprintf(stderr, "Unable to open %s\n: %s", buf_ptr(chf->path), err_str(err));
os_file_close(ch->manifest_file);
os_file_close(&ch->manifest_file);
return ErrorCacheUnavailable;
}
if (chf->attr.mtime.sec == actual_attr.mtime.sec &&
chf->attr.mtime.nsec == actual_attr.mtime.nsec &&
chf->attr.inode == actual_attr.inode)
{
os_file_close(this_file);
os_file_close(&this_file);
} else {
// we have to recompute the digest.
// later we'll rewrite the manifest with the new mtime/digest values
@ -411,11 +411,11 @@ Error cache_hit(CacheHash *ch, Buf *out_digest) {
uint8_t actual_digest[48];
if ((err = hash_file(actual_digest, this_file, nullptr))) {
os_file_close(this_file);
os_file_close(ch->manifest_file);
os_file_close(&this_file);
os_file_close(&ch->manifest_file);
return err;
}
os_file_close(this_file);
os_file_close(&this_file);
if (memcmp(chf->bin_digest, actual_digest, 48) != 0) {
memcpy(chf->bin_digest, actual_digest, 48);
// keep going until we have the input file digests
@ -433,12 +433,12 @@ Error cache_hit(CacheHash *ch, Buf *out_digest) {
CacheHashFile *chf = &ch->files.at(file_i);
if ((err = populate_file_hash(ch, chf, nullptr))) {
fprintf(stderr, "Unable to hash %s: %s\n", buf_ptr(chf->path), err_str(err));
os_file_close(ch->manifest_file);
os_file_close(&ch->manifest_file);
return ErrorCacheUnavailable;
}
}
if (return_code != ErrorNone) {
os_file_close(ch->manifest_file);
if (return_code != ErrorNone && return_code != ErrorInvalidFormat) {
os_file_close(&ch->manifest_file);
}
return return_code;
}
@ -453,7 +453,7 @@ Error cache_add_file_fetch(CacheHash *ch, Buf *resolved_path, Buf *contents) {
CacheHashFile *chf = ch->files.add_one();
chf->path = resolved_path;
if ((err = populate_file_hash(ch, chf, contents))) {
os_file_close(ch->manifest_file);
os_file_close(&ch->manifest_file);
return err;
}
@ -469,7 +469,7 @@ Error cache_add_file(CacheHash *ch, Buf *path) {
Error cache_add_dep_file(CacheHash *ch, Buf *dep_file_path, bool verbose) {
Error err;
Buf *contents = buf_alloc();
if ((err = os_fetch_file_path(dep_file_path, contents, false))) {
if ((err = os_fetch_file_path(dep_file_path, contents))) {
if (verbose) {
fprintf(stderr, "unable to read .d file: %s\n", err_str(err));
}
@ -586,6 +586,6 @@ void cache_release(CacheHash *ch) {
}
}
os_file_close(ch->manifest_file);
os_file_close(&ch->manifest_file);
}

View File

@ -19,6 +19,7 @@
#include "target.hpp"
#include "util.hpp"
#include "zig_llvm.h"
#include "userland.h"
#include <stdio.h>
#include <errno.h>
@ -92,7 +93,7 @@ static const char *symbols_that_llvm_depends_on[] = {
};
CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target,
OutType out_type, BuildMode build_mode, Buf *zig_lib_dir, Buf *override_std_dir,
OutType out_type, BuildMode build_mode, Buf *override_lib_dir, Buf *override_std_dir,
ZigLibCInstallation *libc, Buf *cache_dir)
{
CodeGen *g = allocate<CodeGen>(1);
@ -100,19 +101,24 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget
codegen_add_time_event(g, "Initialize");
g->libc = libc;
g->zig_lib_dir = zig_lib_dir;
g->zig_target = target;
g->cache_dir = cache_dir;
if (override_lib_dir == nullptr) {
g->zig_lib_dir = get_zig_lib_dir();
} else {
g->zig_lib_dir = override_lib_dir;
}
if (override_std_dir == nullptr) {
g->zig_std_dir = buf_alloc();
os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
os_path_join(g->zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
} else {
g->zig_std_dir = override_std_dir;
}
g->zig_c_headers_dir = buf_alloc();
os_path_join(zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir);
os_path_join(g->zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir);
g->build_mode = build_mode;
g->out_type = out_type;
@ -393,6 +399,15 @@ static void add_uwtable_attr(CodeGen *g, LLVMValueRef fn_val) {
}
}
static void add_probe_stack_attr(CodeGen *g, LLVMValueRef fn_val) {
// Windows already emits its own stack probes
if (!g->disable_stack_probing && g->zig_target->os != OsWindows &&
(g->zig_target->arch == ZigLLVM_x86 ||
g->zig_target->arch == ZigLLVM_x86_64)) {
addLLVMFnAttrStr(fn_val, "probe-stack", "__zig_probe_stack");
}
}
static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
switch (id) {
case GlobalLinkageIdInternal:
@ -424,7 +439,7 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, ZigFn *fn_table_entry) {
}
static void maybe_export_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkageId linkage) {
if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows) {
if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows && g->is_dynamic) {
LLVMSetDLLStorageClass(global_value, LLVMDLLExportStorageClass);
}
}
@ -495,6 +510,14 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
auto entry = g->exported_symbol_names.maybe_get(symbol_name);
if (entry == nullptr) {
fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type);
if (target_is_wasm(g->zig_target)) {
assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &fn_table_entry->proto_node->data.fn_proto;
if (fn_proto-> is_extern && fn_proto->lib_name != nullptr ) {
addLLVMFnAttrStr(fn_table_entry->llvm_value, "wasm-import-module", buf_ptr(fn_proto->lib_name));
}
}
} else {
assert(entry->value->id == TldIdFn);
TldFn *tld_fn = reinterpret_cast<TldFn *>(entry->value);
@ -573,6 +596,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong");
addLLVMFnAttrStr(fn_table_entry->llvm_value, "stack-protector-buffer-size", "4");
}
add_probe_stack_attr(g, fn_table_entry->llvm_value);
}
} else {
maybe_import_dll(g, fn_table_entry->llvm_value, linkage);
@ -983,10 +1008,19 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace
LLVMBuildUnreachable(g->builder);
}
// TODO update most callsites to call gen_assertion instead of this
static void gen_safety_crash(CodeGen *g, PanicMsgId msg_id) {
gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr);
}
static void gen_assertion(CodeGen *g, PanicMsgId msg_id, IrInstruction *source_instruction) {
if (ir_want_runtime_safety(g, source_instruction)) {
gen_safety_crash(g, msg_id);
} else {
LLVMBuildUnreachable(g->builder);
}
}
static LLVMValueRef get_stacksave_fn_val(CodeGen *g) {
if (g->stacksave_fn_val)
return g->stacksave_fn_val;
@ -1022,7 +1056,7 @@ static LLVMValueRef get_write_register_fn_val(CodeGen *g) {
// !0 = !{!"sp\00"}
LLVMTypeRef param_types[] = {
LLVMMetadataTypeInContext(LLVMGetGlobalContext()),
LLVMMetadataTypeInContext(LLVMGetGlobalContext()),
LLVMIntType(g->pointer_size_bytes * 8),
};
@ -1541,11 +1575,19 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2);
Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false);
LLVMTypeRef arg_types[] = {
get_llvm_type(g, g->ptr_to_stack_trace_type),
get_llvm_type(g, g->err_tag_type),
};
LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
LLVMTypeRef fn_type_ref;
if (g->have_err_ret_tracing) {
LLVMTypeRef arg_types[] = {
get_llvm_type(g, g->ptr_to_stack_trace_type),
get_llvm_type(g, g->err_tag_type),
};
fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
} else {
LLVMTypeRef arg_types[] = {
get_llvm_type(g, g->err_tag_type),
};
fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 1, false);
}
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
addLLVMFnAttr(fn_val, "noreturn");
addLLVMFnAttr(fn_val, "cold");
@ -1567,7 +1609,15 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMPositionBuilderAtEnd(g->builder, entry_block);
ZigLLVMClearCurrentDebugLocation(g->builder);
LLVMValueRef err_val = LLVMGetParam(fn_val, 1);
LLVMValueRef err_ret_trace_arg;
LLVMValueRef err_val;
if (g->have_err_ret_tracing) {
err_ret_trace_arg = LLVMGetParam(fn_val, 0);
err_val = LLVMGetParam(fn_val, 1);
} else {
err_ret_trace_arg = nullptr;
err_val = LLVMGetParam(fn_val, 0);
}
LLVMValueRef err_table_indices[] = {
LLVMConstNull(g->builtin_types.entry_usize->llvm_type),
@ -1589,7 +1639,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, "");
gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type);
gen_panic(g, global_slice, LLVMGetParam(fn_val, 0));
gen_panic(g, global_slice, err_ret_trace_arg);
LLVMPositionBuilderAtEnd(g->builder, prev_block);
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
@ -1625,17 +1675,26 @@ static LLVMValueRef get_cur_err_ret_trace_val(CodeGen *g, Scope *scope) {
static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val, Scope *scope) {
LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, scope);
if (err_ret_trace_val == nullptr) {
ZigType *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
err_ret_trace_val = LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type));
LLVMValueRef call_instruction;
if (g->have_err_ret_tracing) {
LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, scope);
if (err_ret_trace_val == nullptr) {
ZigType *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
err_ret_trace_val = LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type));
}
LLVMValueRef args[] = {
err_ret_trace_val,
err_val,
};
call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2,
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
} else {
LLVMValueRef args[] = {
err_val,
};
call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 1,
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
}
LLVMValueRef args[] = {
err_ret_trace_val,
err_val,
};
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified),
ZigLLVM_FnInlineAuto, "");
LLVMSetTailCall(call_instruction, true);
LLVMBuildUnreachable(g->builder);
}
@ -3452,6 +3511,15 @@ static bool want_valgrind_support(CodeGen *g) {
zig_unreachable();
}
static void gen_valgrind_undef(CodeGen *g, LLVMValueRef dest_ptr, LLVMValueRef byte_count) {
static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545;
ZigType *usize = g->builtin_types.entry_usize;
LLVMValueRef zero = LLVMConstInt(usize->llvm_type, 0, false);
LLVMValueRef req = LLVMConstInt(usize->llvm_type, VG_USERREQ__MAKE_MEM_UNDEFINED, false);
LLVMValueRef ptr_as_usize = LLVMBuildPtrToInt(g->builder, dest_ptr, usize->llvm_type, "");
gen_valgrind_client_request(g, zero, req, ptr_as_usize, byte_count, zero, zero, zero);
}
static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) {
assert(type_has_bits(value_type));
uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, value_type));
@ -3466,11 +3534,7 @@ static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_
ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false);
// then tell valgrind that the memory is undefined even though we just memset it
if (want_valgrind_support(g)) {
static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545;
LLVMValueRef zero = LLVMConstInt(usize->llvm_type, 0, false);
LLVMValueRef req = LLVMConstInt(usize->llvm_type, VG_USERREQ__MAKE_MEM_UNDEFINED, false);
LLVMValueRef ptr_as_usize = LLVMBuildPtrToInt(g->builder, dest_ptr, usize->llvm_type, "");
gen_valgrind_client_request(g, zero, req, ptr_as_usize, byte_count, zero, zero, zero);
gen_valgrind_undef(g, dest_ptr, byte_count);
}
}
@ -3480,14 +3544,14 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir
if (!type_has_bits(ptr_type))
return nullptr;
bool have_init_expr = !value_is_all_undef(&instruction->value->value);
bool have_init_expr = !value_is_all_undef(&instruction->value->value);
if (have_init_expr) {
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
LLVMValueRef value = ir_llvm_value(g, instruction->value);
gen_assign_raw(g, ptr, ptr_type, value);
} else if (ir_want_runtime_safety(g, &instruction->base)) {
gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value.type,
ir_llvm_value(g, instruction->ptr));
ir_llvm_value(g, instruction->ptr));
}
return nullptr;
}
@ -3690,7 +3754,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
}
FnWalk fn_walk = {};
fn_walk.id = FnWalkIdCall;
fn_walk.data.call.inst = instruction;
fn_walk.data.call.inst = instruction;
fn_walk.data.call.is_var_args = is_var_args;
fn_walk.data.call.gen_param_values = &gen_param_values;
walk_function_params(g, fn_type, &fn_walk);
@ -3710,7 +3774,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
LLVMCallConv llvm_cc = get_llvm_cc(g, cc);
LLVMValueRef result;
if (instruction->new_stack == nullptr) {
result = ZigLLVMBuildCall(g->builder, fn_val,
gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, "");
@ -3968,19 +4032,19 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
}
static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) {
assert(maybe_type->id == ZigTypeIdOptional);
assert(maybe_type->id == ZigTypeIdOptional ||
(maybe_type->id == ZigTypeIdPointer && maybe_type->data.pointer.allow_zero));
ZigType *child_type = maybe_type->data.maybe.child_type;
if (!type_has_bits(child_type)) {
if (!type_has_bits(child_type))
return maybe_handle;
} else {
bool is_scalar = !handle_is_ptr(maybe_type);
if (is_scalar) {
return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(get_llvm_type(g, maybe_type)), "");
} else {
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, "");
return gen_load_untyped(g, maybe_field_ptr, 0, false, "");
}
}
bool is_scalar = !handle_is_ptr(maybe_type);
if (is_scalar)
return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(get_llvm_type(g, maybe_type)), "");
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, "");
return gen_load_untyped(g, maybe_field_ptr, 0, false, "");
}
static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable,
@ -4001,8 +4065,8 @@ static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *exec
if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) {
LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);
LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle);
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk");
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail");
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk");
LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block);
LLVMPositionBuilderAtEnd(g->builder, fail_block);
@ -4190,7 +4254,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
LLVMTypeRef tag_int_llvm_type = get_llvm_type(g, tag_int_type);
LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMPointerType(get_llvm_type(g, u8_slice_type), 0),
&tag_int_llvm_type, 1, false);
Buf *fn_name = get_mangled_name(g, buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)), false);
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
LLVMSetLinkage(fn_val, LLVMInternalLinkage);
@ -4490,17 +4554,27 @@ static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrI
static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrInstructionMemset *instruction) {
LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr);
LLVMValueRef char_val = ir_llvm_value(g, instruction->byte);
LLVMValueRef len_val = ir_llvm_value(g, instruction->count);
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
ZigType *ptr_type = instruction->dest_ptr->value.type;
assert(ptr_type->id == ZigTypeIdPointer);
ZigLLVMBuildMemSet(g->builder, dest_ptr_casted, char_val, len_val, get_ptr_align(g, ptr_type), ptr_type->data.pointer.is_volatile);
bool val_is_undef = value_is_all_undef(&instruction->byte->value);
LLVMValueRef fill_char;
if (val_is_undef) {
fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
} else {
fill_char = ir_llvm_value(g, instruction->byte);
}
ZigLLVMBuildMemSet(g->builder, dest_ptr_casted, fill_char, len_val, get_ptr_align(g, ptr_type),
ptr_type->data.pointer.is_volatile);
if (val_is_undef && want_valgrind_support(g)) {
gen_valgrind_undef(g, dest_ptr_casted, len_val);
}
return nullptr;
}
@ -5422,6 +5496,31 @@ static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutable *executable,
return nullptr;
}
static LLVMValueRef ir_render_assert_non_null(CodeGen *g, IrExecutable *executable,
IrInstructionAssertNonNull *instruction)
{
LLVMValueRef target = ir_llvm_value(g, instruction->target);
ZigType *target_type = instruction->target->value.type;
if (target_type->id == ZigTypeIdPointer) {
assert(target_type->data.pointer.ptr_len == PtrLenC);
LLVMValueRef non_null_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target,
LLVMConstNull(get_llvm_type(g, target_type)), "");
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "AssertNonNullFail");
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "AssertNonNullOk");
LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block);
LLVMPositionBuilderAtEnd(g->builder, fail_block);
gen_assertion(g, PanicMsgIdUnwrapOptionalFail, &instruction->base);
LLVMPositionBuilderAtEnd(g->builder, ok_block);
} else {
zig_unreachable();
}
return nullptr;
}
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
AstNode *source_node = instruction->source_node;
Scope *scope = instruction->scope;
@ -5676,6 +5775,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction);
case IrInstructionIdAssertZero:
return ir_render_assert_zero(g, executable, (IrInstructionAssertZero *)instruction);
case IrInstructionIdAssertNonNull:
return ir_render_assert_non_null(g, executable, (IrInstructionAssertNonNull *)instruction);
case IrInstructionIdResizeSlice:
return ir_render_resize_slice(g, executable, (IrInstructionResizeSlice *)instruction);
}
@ -6585,7 +6686,7 @@ static void validate_inline_fns(CodeGen *g) {
}
static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) {
if (var->is_thread_local && !g->is_single_threaded) {
if (var->is_thread_local && (!g->is_single_threaded || var->linkage != VarLinkageInternal)) {
LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
}
}
@ -6905,7 +7006,7 @@ static void do_code_gen(CodeGen *g) {
ir_render(g, fn_table_entry);
}
assert(!g->errors.length);
if (buf_len(&g->global_asm) != 0) {
@ -6942,6 +7043,11 @@ static void zig_llvm_emit_output(CodeGen *g) {
}
validate_inline_fns(g);
g->link_objects.append(output_path);
if (g->bundle_compiler_rt && (g->out_type == OutTypeObj ||
(g->out_type == OutTypeLib && !g->is_dynamic)))
{
zig_link_add_compiler_rt(g);
}
break;
case EmitFileTypeAssembly:
@ -7337,9 +7443,26 @@ static bool detect_pic(CodeGen *g) {
zig_unreachable();
}
static bool detect_single_threaded(CodeGen *g) {
if (g->want_single_threaded)
return true;
if (target_is_single_threaded(g->zig_target)) {
return true;
}
return false;
}
static bool detect_err_ret_tracing(CodeGen *g) {
return !target_is_wasm(g->zig_target) &&
g->build_mode != BuildModeFastRelease &&
g->build_mode != BuildModeSmallRelease;
}
Buf *codegen_generate_builtin_source(CodeGen *g) {
g->have_dynamic_link = detect_dynamic_link(g);
g->have_pic = detect_pic(g);
g->is_single_threaded = detect_single_threaded(g);
g->have_err_ret_tracing = detect_err_ret_tracing(g);
Buf *contents = buf_alloc();
@ -7696,7 +7819,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
assert(ContainerLayoutAuto == 0);
assert(ContainerLayoutExtern == 1);
assert(ContainerLayoutPacked == 2);
assert(CallingConventionUnspecified == 0);
assert(CallingConventionC == 1);
assert(CallingConventionCold == 2);
@ -7814,7 +7937,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
Buf *contents;
if (hit) {
contents = buf_alloc();
if ((err = os_fetch_file_path(builtin_zig_path, contents, false))) {
if ((err = os_fetch_file_path(builtin_zig_path, contents))) {
fprintf(stderr, "Unable to open '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err));
exit(1);
}
@ -7844,6 +7967,12 @@ static void init(CodeGen *g) {
g->have_dynamic_link = detect_dynamic_link(g);
g->have_pic = detect_pic(g);
g->is_single_threaded = detect_single_threaded(g);
g->have_err_ret_tracing = detect_err_ret_tracing(g);
if (target_is_single_threaded(g->zig_target)) {
g->is_single_threaded = true;
}
if (g->is_test_build) {
g->subsystem = TargetSubsystemConsole;
@ -7953,8 +8082,6 @@ static void init(CodeGen *g) {
}
}
g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease && g->build_mode != BuildModeSmallRelease;
define_builtin_fns(g);
Error err;
if ((err = define_builtin_compile_vars(g))) {
@ -8093,7 +8220,126 @@ static void detect_libc(CodeGen *g) {
}
}
AstNode *codegen_translate_c(CodeGen *g, Buf *full_path) {
// does not add the "cc" arg
void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_path, bool translate_c) {
if (translate_c) {
args.append("-x");
args.append("c");
}
if (out_dep_path != nullptr) {
args.append("-MD");
args.append("-MV");
args.append("-MF");
args.append(out_dep_path);
}
args.append("-nostdinc");
args.append("-fno-spell-checking");
if (translate_c) {
// this gives us access to preprocessing entities, presumably at
// the cost of performance
args.append("-Xclang");
args.append("-detailed-preprocessing-record");
} else {
switch (g->err_color) {
case ErrColorAuto:
break;
case ErrColorOff:
args.append("-fno-color-diagnostics");
args.append("-fno-caret-diagnostics");
break;
case ErrColorOn:
args.append("-fcolor-diagnostics");
args.append("-fcaret-diagnostics");
break;
}
}
args.append("-isystem");
args.append(buf_ptr(g->zig_c_headers_dir));
for (size_t i = 0; i < g->libc_include_dir_len; i += 1) {
Buf *include_dir = g->libc_include_dir_list[i];
args.append("-isystem");
args.append(buf_ptr(include_dir));
}
if (g->zig_target->is_native) {
args.append("-march=native");
} else {
args.append("-target");
args.append(buf_ptr(&g->triple_str));
}
if (g->zig_target->os == OsFreestanding) {
args.append("-ffreestanding");
}
if (!g->strip_debug_symbols) {
args.append("-g");
}
switch (g->build_mode) {
case BuildModeDebug:
// windows c runtime requires -D_DEBUG if using debug libraries
args.append("-D_DEBUG");
if (g->libc_link_lib != nullptr) {
args.append("-fstack-protector-strong");
args.append("--param");
args.append("ssp-buffer-size=4");
} else {
args.append("-fno-stack-protector");
}
args.append("-fno-omit-frame-pointer");
break;
case BuildModeSafeRelease:
// See the comment in the BuildModeFastRelease case for why we pass -O2 rather
// than -O3 here.
args.append("-O2");
if (g->libc_link_lib != nullptr) {
args.append("-D_FORTIFY_SOURCE=2");
args.append("-fstack-protector-strong");
args.append("--param");
args.append("ssp-buffer-size=4");
} else {
args.append("-fno-stack-protector");
}
args.append("-fomit-frame-pointer");
break;
case BuildModeFastRelease:
args.append("-DNDEBUG");
// Here we pass -O2 rather than -O3 because, although we do the equivalent of
// -O3 in Zig code, the justification for the difference here is that Zig
// has better detection and prevention of undefined behavior, so -O3 is safer for
// Zig code than it is for C code. Also, C programmers are used to their code
// running in -O2 and thus the -O3 path has been tested less.
args.append("-O2");
args.append("-fno-stack-protector");
args.append("-fomit-frame-pointer");
break;
case BuildModeSmallRelease:
args.append("-DNDEBUG");
args.append("-Os");
args.append("-fno-stack-protector");
args.append("-fomit-frame-pointer");
break;
}
if (target_supports_fpic(g->zig_target) && g->have_pic) {
args.append("-fPIC");
}
for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) {
args.append(g->clang_argv[arg_i]);
}
}
void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file, bool use_userland_implementation) {
Error err;
Buf *src_basename = buf_alloc();
Buf *src_dirname = buf_alloc();
os_path_split(full_path, src_dirname, src_basename);
@ -8105,13 +8351,47 @@ AstNode *codegen_translate_c(CodeGen *g, Buf *full_path) {
init(g);
ZigList<ErrorMsg *> errors = {0};
AstNode *root_node;
Error err = parse_h_file(&root_node, &errors, buf_ptr(full_path), g, nullptr);
Stage2TranslateMode trans_mode = buf_ends_with_str(full_path, ".h") ?
Stage2TranslateModeImport : Stage2TranslateModeTranslate;
if (err == ErrorCCompileErrors && errors.length > 0) {
for (size_t i = 0; i < errors.length; i += 1) {
ErrorMsg *err_msg = errors.at(i);
ZigList<const char *> clang_argv = {0};
add_cc_args(g, clang_argv, nullptr, true);
clang_argv.append(buf_ptr(full_path));
if (g->verbose_cc) {
fprintf(stderr, "clang");
for (size_t i = 0; i < clang_argv.length; i += 1) {
fprintf(stderr, " %s", clang_argv.at(i));
}
fprintf(stderr, "\n");
}
clang_argv.append(nullptr); // to make the [start...end] argument work
const char *resources_path = buf_ptr(g->zig_c_headers_dir);
Stage2ErrorMsg *errors_ptr;
size_t errors_len;
Stage2Ast *ast;
AstNode *root_node;
if (use_userland_implementation) {
err = stage2_translate_c(&ast, &errors_ptr, &errors_len,
&clang_argv.at(0), &clang_argv.last(), trans_mode, resources_path);
} else {
err = parse_h_file(g, &root_node, &errors_ptr, &errors_len, &clang_argv.at(0), &clang_argv.last(),
trans_mode, resources_path);
}
if (err == ErrorCCompileErrors && errors_len > 0) {
for (size_t i = 0; i < errors_len; i += 1) {
Stage2ErrorMsg *clang_err = &errors_ptr[i];
ErrorMsg *err_msg = err_msg_create_with_offset(
clang_err->filename_ptr ?
buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(),
clang_err->line, clang_err->column, clang_err->offset, clang_err->source,
buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len));
print_err_msg(err_msg, g->err_color);
}
exit(1);
@ -8122,7 +8402,12 @@ AstNode *codegen_translate_c(CodeGen *g, Buf *full_path) {
exit(1);
}
return root_node;
if (use_userland_implementation) {
stage2_render_ast(ast, out_file);
} else {
ast_render(out_file, root_node, 4);
}
}
static ZigType *add_special_code(CodeGen *g, ZigPackage *package, const char *basename) {
@ -8233,7 +8518,7 @@ static void gen_root_source(CodeGen *g) {
Error err;
// No need for using the caching system for this file fetch because it is handled
// separately.
if ((err = os_fetch_file_path(resolved_path, source_code, true))) {
if ((err = os_fetch_file_path(resolved_path, source_code))) {
fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(resolved_path), err_str(err));
exit(1);
}
@ -8308,7 +8593,7 @@ static void gen_global_asm(CodeGen *g) {
Buf *asm_file = g->assembly_files.at(i);
// No need to use the caching system for these fetches because they
// are handled separately.
if ((err = os_fetch_file_path(asm_file, &contents, false))) {
if ((err = os_fetch_file_path(asm_file, &contents))) {
zig_panic("Unable to read %s: %s", buf_ptr(asm_file), err_str(err));
}
buf_append_buf(&g->global_asm, &contents);
@ -8441,90 +8726,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
args.append("cc");
Buf *out_dep_path = buf_sprintf("%s.d", buf_ptr(out_obj_path));
args.append("-MD");
args.append("-MV");
args.append("-MF");
args.append(buf_ptr(out_dep_path));
args.append("-nostdinc");
args.append("-fno-spell-checking");
switch (g->err_color) {
case ErrColorAuto:
break;
case ErrColorOff:
args.append("-fno-color-diagnostics");
args.append("-fno-caret-diagnostics");
break;
case ErrColorOn:
args.append("-fcolor-diagnostics");
args.append("-fcaret-diagnostics");
break;
}
args.append("-isystem");
args.append(buf_ptr(g->zig_c_headers_dir));
for (size_t i = 0; i < g->libc_include_dir_len; i += 1) {
Buf *include_dir = g->libc_include_dir_list[i];
args.append("-isystem");
args.append(buf_ptr(include_dir));
}
if (g->zig_target->is_native) {
args.append("-march=native");
} else {
args.append("-target");
args.append(buf_ptr(&g->triple_str));
}
if (!g->strip_debug_symbols) {
args.append("-g");
}
switch (g->build_mode) {
case BuildModeDebug:
if (g->libc_link_lib != nullptr) {
args.append("-fstack-protector-strong");
args.append("--param");
args.append("ssp-buffer-size=4");
} else {
args.append("-fno-stack-protector");
}
args.append("-fno-omit-frame-pointer");
break;
case BuildModeSafeRelease:
// See the comment in the BuildModeFastRelease case for why we pass -O2 rather
// than -O3 here.
args.append("-O2");
if (g->libc_link_lib != nullptr) {
args.append("-D_FORTIFY_SOURCE=2");
args.append("-fstack-protector-strong");
args.append("--param");
args.append("ssp-buffer-size=4");
} else {
args.append("-fno-stack-protector");
}
args.append("-fomit-frame-pointer");
break;
case BuildModeFastRelease:
args.append("-DNDEBUG");
// Here we pass -O2 rather than -O3 because, although we do the equivalent of
// -O3 in Zig code, the justification for the difference here is that Zig
// has better detection and prevention of undefined behavior, so -O3 is safer for
// Zig code than it is for C code. Also, C programmers are used to their code
// running in -O2 and thus the -O3 path has been tested less.
args.append("-O2");
args.append("-fno-stack-protector");
args.append("-fomit-frame-pointer");
break;
case BuildModeSmallRelease:
args.append("-DNDEBUG");
args.append("-Os");
args.append("-fno-stack-protector");
args.append("-fomit-frame-pointer");
break;
}
add_cc_args(g, args, buf_ptr(out_dep_path), false);
args.append("-o");
args.append(buf_ptr(out_obj_path));
@ -8532,19 +8734,10 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
args.append("-c");
args.append(buf_ptr(c_source_file));
if (target_supports_fpic(g->zig_target) && g->have_pic) {
args.append("-fPIC");
}
for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) {
args.append(g->clang_argv[arg_i]);
}
for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) {
args.append(c_file->args.at(arg_i));
}
if (g->verbose_cc) {
print_zig_cc_cmd("zig", &args);
}
@ -9158,6 +9351,8 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
cache_bool(ch, g->linker_rdynamic);
cache_bool(ch, g->each_lib_rpath);
cache_bool(ch, g->disable_gen_h);
cache_bool(ch, g->bundle_compiler_rt);
cache_bool(ch, g->disable_stack_probing);
cache_bool(ch, want_valgrind_support(g));
cache_bool(ch, g->have_pic);
cache_bool(ch, g->have_dynamic_link);
@ -9268,6 +9463,8 @@ void codegen_build_and_link(CodeGen *g) {
g->have_dynamic_link = detect_dynamic_link(g);
g->have_pic = detect_pic(g);
g->is_single_threaded = detect_single_threaded(g);
g->have_err_ret_tracing = detect_err_ret_tracing(g);
detect_libc(g);
detect_dynamic_linker(g);

View File

@ -12,6 +12,7 @@
#include "errmsg.hpp"
#include "target.hpp"
#include "libc_installation.hpp"
#include "userland.h"
#include <stdio.h>
@ -43,6 +44,7 @@ void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patc
void codegen_add_time_event(CodeGen *g, const char *name);
void codegen_print_timing_report(CodeGen *g, FILE *f);
void codegen_link(CodeGen *g);
void zig_link_add_compiler_rt(CodeGen *g);
void codegen_build_and_link(CodeGen *g);
ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path,
@ -50,7 +52,7 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c
void codegen_add_assembly(CodeGen *g, Buf *path);
void codegen_add_object(CodeGen *g, Buf *object_path);
AstNode *codegen_translate_c(CodeGen *g, Buf *path);
void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file, bool use_userland_implementation);
Buf *codegen_generate_builtin_source(CodeGen *g);

View File

@ -179,24 +179,24 @@ Buf *get_zig_lib_dir(void) {
return &saved_lib_dir;
}
Buf *get_zig_std_dir() {
Buf *get_zig_std_dir(Buf *zig_lib_dir) {
if (saved_std_dir.list.length != 0) {
return &saved_std_dir;
}
buf_resize(&saved_std_dir, 0);
os_path_join(get_zig_lib_dir(), buf_create_from_str("std"), &saved_std_dir);
os_path_join(zig_lib_dir, buf_create_from_str("std"), &saved_std_dir);
return &saved_std_dir;
}
Buf *get_zig_special_dir() {
Buf *get_zig_special_dir(Buf *zig_lib_dir) {
if (saved_special_dir.list.length != 0) {
return &saved_special_dir;
}
buf_resize(&saved_special_dir, 0);
os_path_join(get_zig_std_dir(), buf_sprintf("special"), &saved_special_dir);
os_path_join(get_zig_std_dir(zig_lib_dir), buf_sprintf("special"), &saved_special_dir);
return &saved_special_dir;
}

View File

@ -16,7 +16,7 @@ Error get_compiler_id(Buf **result);
Buf *get_self_dynamic_linker_path(void);
Buf *get_zig_lib_dir(void);
Buf *get_zig_special_dir(void);
Buf *get_zig_std_dir(void);
Buf *get_zig_special_dir(Buf *zig_lib_dir);
Buf *get_zig_std_dir(Buf *zig_lib_dir);
#endif

View File

@ -50,6 +50,10 @@ const char *err_str(Error err) {
case ErrorUnexpectedWriteFailure: return "unexpected write failure";
case ErrorUnexpectedSeekFailure: return "unexpected seek failure";
case ErrorUnexpectedFileTruncationFailure: return "unexpected file truncation failure";
case ErrorUnimplemented: return "unimplemented";
case ErrorOperationAborted: return "operation aborted";
case ErrorBrokenPipe: return "broken pipe";
case ErrorNoSpaceLeft: return "no space left";
}
return "(invalid error)";
}

View File

@ -8,56 +8,10 @@
#ifndef ERROR_HPP
#define ERROR_HPP
#include <assert.h>
enum Error {
ErrorNone,
ErrorNoMem,
ErrorInvalidFormat,
ErrorSemanticAnalyzeFail,
ErrorAccess,
ErrorInterrupted,
ErrorSystemResources,
ErrorFileNotFound,
ErrorFileSystem,
ErrorFileTooBig,
ErrorDivByZero,
ErrorOverflow,
ErrorPathAlreadyExists,
ErrorUnexpected,
ErrorExactDivRemainder,
ErrorNegativeDenominator,
ErrorShiftedOutOneBits,
ErrorCCompileErrors,
ErrorEndOfFile,
ErrorIsDir,
ErrorNotDir,
ErrorUnsupportedOperatingSystem,
ErrorSharingViolation,
ErrorPipeBusy,
ErrorPrimitiveTypeNotFound,
ErrorCacheUnavailable,
ErrorPathTooLong,
ErrorCCompilerCannotFindFile,
ErrorReadingDepFile,
ErrorInvalidDepFile,
ErrorMissingArchitecture,
ErrorMissingOperatingSystem,
ErrorUnknownArchitecture,
ErrorUnknownOperatingSystem,
ErrorUnknownABI,
ErrorInvalidFilename,
ErrorDiskQuota,
ErrorDiskSpace,
ErrorUnexpectedWriteFailure,
ErrorUnexpectedSeekFailure,
ErrorUnexpectedFileTruncationFailure,
};
#include "userland.h"
const char *err_str(Error err);
static inline void assertNoError(Error err) {
assert(err == ErrorNone);
}
#define assertNoError(err) assert((err) == ErrorNone);
#endif

View File

@ -188,6 +188,19 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c
assert(get_src_ptr_type(const_val->type) != nullptr);
assert(const_val->special == ConstValSpecialStatic);
ConstExprValue *result;
switch (type_has_one_possible_value(g, const_val->type->data.pointer.child_type)) {
case OnePossibleValueInvalid:
zig_unreachable();
case OnePossibleValueYes:
result = create_const_vals(1);
result->type = const_val->type->data.pointer.child_type;
result->special = ConstValSpecialStatic;
return result;
case OnePossibleValueNo:
break;
}
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
@ -356,6 +369,18 @@ static void ir_ref_var(ZigVar *var) {
var->ref_count += 1;
}
ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) {
ConstExprValue *result = ir_eval_const_value(ira->codegen, scope, node, ira->codegen->builtin_types.entry_type,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr,
node, nullptr, ira->new_irb.exec, nullptr);
if (type_is_invalid(result->type))
return ira->codegen->builtin_types.entry_invalid;
assert(result->special != ConstValSpecialRuntime);
return result->data.x_type;
}
static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) {
IrBasicBlock *result = allocate<IrBasicBlock>(1);
result->scope = scope;
@ -978,6 +1003,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertZero *) {
return IrInstructionIdAssertZero;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonNull *) {
return IrInstructionIdAssertNonNull;
}
template<typename T>
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate<T>(1);
@ -3012,6 +3041,19 @@ static IrInstruction *ir_build_assert_zero(IrAnalyze *ira, IrInstruction *source
return &instruction->base;
}
static IrInstruction *ir_build_assert_non_null(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *target)
{
IrInstructionAssertNonNull *instruction = ir_build_instruction<IrInstructionAssertNonNull>(&ira->new_irb,
source_instruction->scope, source_instruction->source_node);
instruction->base.value.type = ira->codegen->builtin_types.entry_void;
instruction->target = target;
ir_ref_instruction(target, ira->new_irb.current_basic_block);
return &instruction->base;
}
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@ -5391,10 +5433,9 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
add_node_error(irb->codegen, variable_declaration->section_expr,
buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
}
if (variable_declaration->threadlocal_tok != nullptr) {
add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok,
buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol)));
}
// Parser should ensure that this never happens
assert(variable_declaration->threadlocal_tok == nullptr);
// Temporarily set the name of the IrExecutable to the VariableDeclaration
// so that the struct or enum from the init expression inherits the name.
@ -5767,8 +5808,10 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base);
if (!instr_is_unreachable(body_result))
if (!instr_is_unreachable(body_result)) {
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result));
ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
}
ir_set_cursor_at_end_and_append_block(irb, continue_block);
IrInstruction *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false);
@ -7891,6 +7934,11 @@ static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction,
return ir_add_error_node(ira, source_instruction->source_node, msg);
}
static void ir_assert(bool ok, IrInstruction *source_instruction) {
if (ok) return;
src_assert(ok, source_instruction->source_node);
}
// This function takes a comptime ptr and makes the child const value conform to the type
// described by the pointer.
static Error eval_comptime_ptr_reinterpret(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
@ -7943,7 +7991,7 @@ static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec
return &codegen->invalid_instruction->value;
}
}
return &codegen->invalid_instruction->value;
zig_unreachable();
}
static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) {
@ -8088,6 +8136,8 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) {
case 64:
dest_val->data.x_f64 = bigfloat_to_f64(bigfloat);
break;
case 80:
zig_panic("TODO");
case 128:
dest_val->data.x_f128 = bigfloat_to_f128(bigfloat);
break;
@ -9904,6 +9954,7 @@ static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *t
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs) {
ConstGlobalRefs *global_refs = dest->global_refs;
assert(!same_global_refs || src->global_refs != nullptr);
*dest = *src;
if (!same_global_refs) {
dest->global_refs = global_refs;
@ -9949,6 +10000,8 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_
case 64:
const_val->data.x_f64 = bigfloat_to_f64(&other_val->data.x_bigfloat);
break;
case 80:
zig_panic("TODO");
case 128:
const_val->data.x_f128 = bigfloat_to_f128(&other_val->data.x_bigfloat);
break;
@ -9978,6 +10031,8 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_
case 64:
const_val->data.x_f64 = bigfloat_to_f64(&bigfloat);
break;
case 80:
zig_panic("TODO");
case 128:
const_val->data.x_f128 = bigfloat_to_f128(&bigfloat);
break;
@ -10170,6 +10225,7 @@ static void ir_finish_bb(IrAnalyze *ira) {
ira->instruction_index += 1;
}
size_t my_old_bb_index = ira->old_bb_index;
ira->old_bb_index += 1;
bool need_repeat = true;
@ -10180,7 +10236,7 @@ static void ir_finish_bb(IrAnalyze *ira) {
ira->old_bb_index += 1;
continue;
}
if (old_bb->other->instruction_list.length != 0) {
if (old_bb->other->instruction_list.length != 0 || ira->old_bb_index == my_old_bb_index) {
ira->old_bb_index += 1;
continue;
}
@ -10205,16 +10261,18 @@ static IrInstruction *ir_unreach_error(IrAnalyze *ira) {
static bool ir_emit_backward_branch(IrAnalyze *ira, IrInstruction *source_instruction) {
size_t *bbc = ira->new_irb.exec->backward_branch_count;
size_t quota = ira->new_irb.exec->backward_branch_quota;
size_t *quota = ira->new_irb.exec->backward_branch_quota;
// If we're already over quota, we've already given an error message for this.
if (*bbc > quota) {
if (*bbc > *quota) {
assert(ira->codegen->errors.length > 0);
return false;
}
*bbc += 1;
if (*bbc > quota) {
ir_add_error(ira, source_instruction, buf_sprintf("evaluation exceeded %" ZIG_PRI_usize " backwards branches", quota));
if (*bbc > *quota) {
ir_add_error(ira, source_instruction,
buf_sprintf("evaluation exceeded %" ZIG_PRI_usize " backwards branches", *quota));
return false;
}
return true;
@ -10249,6 +10307,12 @@ static IrInstruction *ir_const_bool(IrAnalyze *ira, IrInstruction *source_instru
return result;
}
static IrInstruction *ir_const_undef(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) {
IrInstruction *result = ir_const(ira, source_instruction, ty);
result->value.special = ConstValSpecialUndef;
return result;
}
static IrInstruction *ir_const_void(IrAnalyze *ira, IrInstruction *source_instruction) {
return ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_void);
}
@ -10295,7 +10359,7 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
}
ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
ZigType *expected_type, size_t *backward_branch_count, size_t *backward_branch_quota,
ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
IrExecutable *parent_exec, AstNode *expected_type_source_node)
{
@ -10317,7 +10381,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod
if (codegen->verbose_ir) {
fprintf(stderr, "\nSource: ");
ast_render(codegen, stderr, node, 4);
ast_render(stderr, node, 4);
fprintf(stderr, "\n{ // (IR)\n");
ir_print(codegen, stderr, ir_executable, 2);
fprintf(stderr, "}\n");
@ -10562,19 +10626,34 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so
assert(instr_is_comptime(value));
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
assert(val);
assert(val != nullptr);
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb, source_instr->scope, source_instr->source_node);
const_instruction->base.value.special = ConstValSpecialStatic;
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.special = ConstValSpecialStatic;
if (get_codegen_ptr_type(wanted_type) != nullptr) {
const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialNull;
result->value.data.x_ptr.special = ConstPtrSpecialNull;
} else if (is_opt_err_set(wanted_type)) {
const_instruction->base.value.data.x_err_set = nullptr;
result->value.data.x_err_set = nullptr;
} else {
const_instruction->base.value.data.x_optional = nullptr;
result->value.data.x_optional = nullptr;
}
const_instruction->base.value.type = wanted_type;
return &const_instruction->base;
return result;
}
static IrInstruction *ir_analyze_null_to_c_pointer(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type)
{
assert(wanted_type->id == ZigTypeIdPointer);
assert(wanted_type->data.pointer.ptr_len == PtrLenC);
assert(instr_is_comptime(value));
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
assert(val != nullptr);
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.data.x_ptr.special = ConstPtrSpecialNull;
result->value.data.x_ptr.mut = ConstPtrMutComptimeConst;
return result;
}
static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value,
@ -11576,6 +11655,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
}
// cast from null literal to C pointer
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC &&
actual_type->id == ZigTypeIdNull)
{
return ir_analyze_null_to_c_pointer(ira, source_instr, value, wanted_type);
}
// cast from [N]T to E![]const T
if (wanted_type->id == ZigTypeIdErrorUnion &&
is_slice(wanted_type->data.error_union.payload_type) &&
@ -12193,14 +12279,12 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
IrBinOp op_id = bin_op_instruction->op_id;
bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
if (is_equality_cmp &&
if (is_equality_cmp && op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) {
return ir_const_bool(ira, &bin_op_instruction->base, (op_id == IrBinOpCmpEq));
} else if (is_equality_cmp &&
((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdOptional) ||
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional) ||
(op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull)))
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional)))
{
if (op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) {
return ir_const_bool(ira, &bin_op_instruction->base, (op_id == IrBinOpCmpEq));
}
IrInstruction *maybe_op;
if (op1->value.type->id == ZigTypeIdNull) {
maybe_op = op2;
@ -12222,6 +12306,44 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
source_node, maybe_op);
is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
if (op_id == IrBinOpCmpEq) {
IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,
bin_op_instruction->base.source_node, is_non_null);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
} else {
return is_non_null;
}
} else if (is_equality_cmp &&
((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdPointer &&
op2->value.type->data.pointer.ptr_len == PtrLenC) ||
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdPointer &&
op1->value.type->data.pointer.ptr_len == PtrLenC)))
{
IrInstruction *c_ptr_op;
if (op1->value.type->id == ZigTypeIdNull) {
c_ptr_op = op2;
} else if (op2->value.type->id == ZigTypeIdNull) {
c_ptr_op = op1;
} else {
zig_unreachable();
}
if (instr_is_comptime(c_ptr_op)) {
ConstExprValue *c_ptr_val = ir_resolve_const(ira, c_ptr_op, UndefOk);
if (!c_ptr_val)
return ira->codegen->invalid_instruction;
if (c_ptr_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &bin_op_instruction->base, ira->codegen->builtin_types.entry_bool);
bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull ||
(c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0);
bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
return ir_const_bool(ira, &bin_op_instruction->base, bool_result);
}
IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
source_node, c_ptr_op);
is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
if (op_id == IrBinOpCmpEq) {
IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,
bin_op_instruction->base.source_node, is_non_null);
@ -12231,8 +12353,9 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
return is_non_null;
}
} else if (op1->value.type->id == ZigTypeIdNull || op2->value.type->id == ZigTypeIdNull) {
ir_add_error_node(ira, source_node, buf_sprintf("only optionals (not '%s') can compare to null",
buf_ptr(&(op1->value.type->id == ZigTypeIdNull ? op2->value.type->name : op1->value.type->name))));
ZigType *non_null_type = (op1->value.type->id == ZigTypeIdNull) ? op2->value.type : op1->value.type;
ir_add_error_node(ira, source_node, buf_sprintf("comparison of '%s' with null",
buf_ptr(&non_null_type->name)));
return ira->codegen->invalid_instruction;
}
@ -13828,11 +13951,12 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i
zig_unreachable();
}
static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type,
IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst)
static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry,
ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count,
IrInstruction *async_allocator_inst)
{
Buf *realloc_field_name = buf_create_from_str(ASYNC_REALLOC_FIELD_NAME);
assert(async_allocator_inst->value.type->id == ZigTypeIdPointer);
ir_assert(async_allocator_inst->value.type->id == ZigTypeIdPointer, &call_instruction->base);
ZigType *container_type = async_allocator_inst->value.type->data.pointer.child_type;
IrInstruction *field_ptr_inst = ir_analyze_container_field_ptr(ira, realloc_field_name, &call_instruction->base,
async_allocator_inst, container_type);
@ -13840,7 +13964,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c
return ira->codegen->invalid_instruction;
}
ZigType *ptr_to_realloc_fn_type = field_ptr_inst->value.type;
assert(ptr_to_realloc_fn_type->id == ZigTypeIdPointer);
ir_assert(ptr_to_realloc_fn_type->id == ZigTypeIdPointer, &call_instruction->base);
ZigType *realloc_fn_type = ptr_to_realloc_fn_type->data.pointer.child_type;
if (realloc_fn_type->id != ZigTypeIdFn) {
@ -13875,7 +13999,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node
IrInstruction *casted_arg;
if (param_decl_node->data.param_decl.var_token == nullptr) {
AstNode *param_type_node = param_decl_node->data.param_decl.type;
ZigType *param_type = analyze_type_expr(ira->codegen, *exec_scope, param_type_node);
ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node);
if (type_is_invalid(param_type))
return false;
@ -13915,7 +14039,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
} else {
if (param_decl_node->data.param_decl.var_token == nullptr) {
AstNode *param_type_node = param_decl_node->data.param_decl.type;
ZigType *param_type = analyze_type_expr(ira->codegen, *child_scope, param_type_node);
ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node);
if (type_is_invalid(param_type))
return false;
@ -14296,7 +14420,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call
}
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
ZigType *specified_return_type = analyze_type_expr(ira->codegen, exec_scope, return_type_node);
ZigType *specified_return_type = ir_analyze_type_expr(ira, exec_scope, return_type_node);
if (type_is_invalid(specified_return_type))
return ira->codegen->invalid_instruction;
ZigType *return_type;
@ -14532,7 +14656,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call
if (fn_proto_node->data.fn_proto.return_var_token == nullptr) {
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
ZigType *specified_return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node);
ZigType *specified_return_type = ir_analyze_type_expr(ira, impl_fn->child_scope, return_type_node);
if (type_is_invalid(specified_return_type))
return ira->codegen->invalid_instruction;
if (fn_proto_node->data.fn_proto.auto_err_set) {
@ -14559,7 +14683,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call
if (call_instruction->is_async) {
AstNode *async_allocator_type_node = fn_proto_node->data.fn_proto.async_allocator_type;
if (async_allocator_type_node != nullptr) {
ZigType *async_allocator_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, async_allocator_type_node);
ZigType *async_allocator_type = ir_analyze_type_expr(ira, impl_fn->child_scope, async_allocator_type_node);
if (type_is_invalid(async_allocator_type))
return ira->codegen->invalid_instruction;
inst_fn_type_id.async_allocator_type = async_allocator_type;
@ -15822,7 +15946,7 @@ static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name,
}
}
if (!is_libc && !ira->codegen->have_pic && !ira->codegen->reported_bad_link_libc_error) {
if (!is_libc && !target_is_wasm(ira->codegen->zig_target) && !ira->codegen->have_pic && !ira->codegen->reported_bad_link_libc_error) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code",
buf_ptr(lib_name)));
@ -16696,16 +16820,16 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira,
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdBoundFn:
case ZigTypeIdMetaType:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
ir_add_error_node(ira, size_of_instruction->base.source_node,
ir_add_error_node(ira, type_value->source_node,
buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
return ira->codegen->invalid_instruction;
case ZigTypeIdMetaType:
case ZigTypeIdEnumLiteral:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
@ -16732,11 +16856,30 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira,
static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) {
ZigType *type_entry = value->value.type;
if (type_entry->id == ZigTypeIdOptional) {
if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.allow_zero) {
if (instr_is_comptime(value)) {
ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefBad);
if (!maybe_val)
ConstExprValue *c_ptr_val = ir_resolve_const(ira, value, UndefOk);
if (c_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
if (c_ptr_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool);
bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull ||
(c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0);
return ir_const_bool(ira, source_inst, !is_null);
}
IrInstruction *result = ir_build_test_nonnull(&ira->new_irb,
source_inst->scope, source_inst->source_node, value);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
} else if (type_entry->id == ZigTypeIdOptional) {
if (instr_is_comptime(value)) {
ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefOk);
if (maybe_val == nullptr)
return ira->codegen->invalid_instruction;
if (maybe_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool);
return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val));
}
@ -16770,6 +16913,32 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenC) {
if (instr_is_comptime(base_ptr)) {
ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
ConstExprValue *c_ptr_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node);
if (c_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull ||
(c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0);
if (is_null) {
ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null"));
return ira->codegen->invalid_instruction;
}
return base_ptr;
}
}
if (!safety_check_on)
return base_ptr;
IrInstruction *c_ptr_val = ir_get_deref(ira, source_instr, base_ptr);
ir_build_assert_non_null(ira, source_instr, c_ptr_val);
return base_ptr;
}
if (type_entry->id != ZigTypeIdOptional) {
ir_add_error_node(ira, base_ptr->source_node,
buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name)));
@ -16784,11 +16953,11 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr
ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node);
if (maybe_val == nullptr)
return ira->codegen->invalid_instruction;
if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node);
if (maybe_val == nullptr)
return ira->codegen->invalid_instruction;
if (optional_value_is_null(maybe_val)) {
ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null"));
return ira->codegen->invalid_instruction;
@ -17435,6 +17604,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
ConstExprValue const_val = {};
const_val.special = ConstValSpecialStatic;
const_val.type = container_type;
// const_val.global_refs = allocate<ConstGlobalRefs>(1);
const_val.data.x_struct.fields = create_const_vals(actual_field_count);
for (size_t i = 0; i < instr_field_count; i += 1) {
IrInstructionContainerInitFieldsField *field = &fields[i];
@ -17498,7 +17668,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
if (const_val.special == ConstValSpecialStatic) {
IrInstruction *result = ir_const(ira, instruction, nullptr);
ConstExprValue *out_val = &result->value;
copy_const_val(out_val, &const_val, true);
copy_const_val(out_val, &const_val, false);
out_val->type = container_type;
for (size_t i = 0; i < instr_field_count; i += 1) {
@ -17570,6 +17740,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
ConstExprValue const_val = {};
const_val.special = ConstValSpecialStatic;
const_val.type = fixed_size_array_type;
// const_val.global_refs = allocate<ConstGlobalRefs>(1);
const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count);
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope);
@ -17606,8 +17777,6 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
if (const_val.special == ConstValSpecialStatic) {
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
ConstExprValue *out_val = &result->value;
// Make sure to pass same_global_refs=false here in order not to
// zero the global_refs field for `result` (#1608)
copy_const_val(out_val, &const_val, false);
result->value.type = fixed_size_array_type;
for (size_t i = 0; i < elem_count; i += 1) {
@ -18961,8 +19130,8 @@ static IrInstruction *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ir
if (!ir_resolve_usize(ira, instruction->new_quota->child, &new_quota))
return ira->codegen->invalid_instruction;
if (new_quota > ira->new_irb.exec->backward_branch_quota) {
ira->new_irb.exec->backward_branch_quota = new_quota;
if (new_quota > *ira->new_irb.exec->backward_branch_quota) {
*ira->new_irb.exec->backward_branch_quota = new_quota;
}
return ir_const_void(ira, &instruction->base);
@ -19059,24 +19228,50 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct
fprintf(stderr, "@cImport source: %s\n", buf_ptr(&tmp_c_file_path));
}
ZigList<ErrorMsg *> errors = {0};
Buf *tmp_dep_file = buf_sprintf("%s.d", buf_ptr(&tmp_c_file_path));
ZigList<const char *> clang_argv = {0};
add_cc_args(ira->codegen, clang_argv, buf_ptr(tmp_dep_file), true);
clang_argv.append(buf_ptr(&tmp_c_file_path));
if (ira->codegen->verbose_cc) {
fprintf(stderr, "clang");
for (size_t i = 0; i < clang_argv.length; i += 1) {
fprintf(stderr, " %s", clang_argv.at(i));
}
fprintf(stderr, "\n");
}
clang_argv.append(nullptr); // to make the [start...end] argument work
AstNode *root_node;
if ((err = parse_h_file(&root_node, &errors, buf_ptr(&tmp_c_file_path), ira->codegen, tmp_dep_file))) {
Stage2ErrorMsg *errors_ptr;
size_t errors_len;
const char *resources_path = buf_ptr(ira->codegen->zig_c_headers_dir);
if ((err = parse_h_file(ira->codegen, &root_node, &errors_ptr, &errors_len,
&clang_argv.at(0), &clang_argv.last(), Stage2TranslateModeImport, resources_path)))
{
if (err != ErrorCCompileErrors) {
ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err)));
return ira->codegen->invalid_instruction;
}
assert(errors.length > 0);
ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed"));
if (ira->codegen->libc_link_lib == nullptr) {
add_error_note(ira->codegen, parent_err_msg, node,
buf_sprintf("libc headers not available; compilation does not link against libc"));
}
for (size_t i = 0; i < errors.length; i += 1) {
ErrorMsg *err_msg = errors.at(i);
for (size_t i = 0; i < errors_len; i += 1) {
Stage2ErrorMsg *clang_err = &errors_ptr[i];
ErrorMsg *err_msg = err_msg_create_with_offset(
clang_err->filename_ptr ?
buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(),
clang_err->line, clang_err->column, clang_err->offset, clang_err->source,
buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len));
err_msg_add_note(parent_err_msg, err_msg);
}
@ -19106,7 +19301,7 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct
buf_sprintf("C import failed: unable to open output file: %s", strerror(errno)));
return ira->codegen->invalid_instruction;
}
ast_render(ira->codegen, out_file, root_node, 4);
ast_render(out_file, root_node, 4);
if (fclose(out_file) != 0) {
ir_add_error_node(ira, node,
buf_sprintf("C import failed: unable to write to output file: %s", strerror(errno)));
@ -21026,18 +21221,24 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
IrInstruction *start_value = range->start->child;
IrInstruction *start_value_uncasted = range->start->child;
if (type_is_invalid(start_value_uncasted->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type);
if (type_is_invalid(start_value->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *end_value = range->end->child;
IrInstruction *end_value_uncasted = range->end->child;
if (type_is_invalid(end_value_uncasted->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type);
if (type_is_invalid(end_value->value.type))
return ira->codegen->invalid_instruction;
assert(start_value->value.type->id == ZigTypeIdErrorSet);
ir_assert(start_value->value.type->id == ZigTypeIdErrorSet, &instruction->base);
uint32_t start_index = start_value->value.data.x_err_set->value;
assert(end_value->value.type->id == ZigTypeIdErrorSet);
ir_assert(end_value->value.type->id == ZigTypeIdErrorSet, &instruction->base);
uint32_t end_index = end_value->value.data.x_err_set->value;
if (start_index != end_index) {
@ -21260,7 +21461,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
}
IrInstruction *result = ir_const(ira, target, result_type);
copy_const_val(&result->value, val, false);
copy_const_val(&result->value, val, true);
result->value.type = result_type;
return result;
}
@ -21330,7 +21531,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
}
IrInstruction *result = ir_const(ira, source_instr, dest_type);
copy_const_val(&result->value, val, false);
copy_const_val(&result->value, val, true);
result->value.type = dest_type;
// Keep the bigger alignment, it can only help-
@ -21562,7 +21763,7 @@ static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNod
static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) {
Error err;
assert(val->special == ConstValSpecialStatic);
src_assert(val->special == ConstValSpecialStatic, source_node);
switch (val->type->id) {
case ZigTypeIdInvalid:
case ZigTypeIdMetaType:
@ -21612,7 +21813,7 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
zig_panic("TODO buf_read_value_bytes enum packed");
case ContainerLayoutExtern: {
ZigType *tag_int_type = val->type->data.enumeration.tag_int_type;
assert(tag_int_type->id == ZigTypeIdInt);
src_assert(tag_int_type->id == ZigTypeIdInt, source_node);
bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count,
codegen->is_big_endian, tag_int_type->data.integral.is_signed);
return ErrorNone;
@ -21667,7 +21868,7 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
bigint_read_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian, false);
while (src_i < src_field_count) {
TypeStructField *field = &val->type->data.structure.fields[src_i];
assert(field->gen_index != SIZE_MAX);
src_assert(field->gen_index != SIZE_MAX, source_node);
if (field->gen_index != gen_i)
break;
ConstExprValue *field_val = &val->data.x_struct.fields[src_i];
@ -21743,10 +21944,10 @@ static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_
Error err;
ZigType *src_type = value->value.type;
assert(get_codegen_ptr_type(src_type) == nullptr);
assert(type_can_bit_cast(src_type));
assert(get_codegen_ptr_type(dest_type) == nullptr);
assert(type_can_bit_cast(dest_type));
ir_assert(get_codegen_ptr_type(src_type) == nullptr, source_instr);
ir_assert(type_can_bit_cast(src_type), source_instr);
ir_assert(get_codegen_ptr_type(dest_type) == nullptr, source_instr);
ir_assert(type_can_bit_cast(dest_type), source_instr);
if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
@ -21836,8 +22037,8 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
ZigType *ptr_type)
{
assert(get_src_ptr_type(ptr_type) != nullptr);
assert(type_has_bits(ptr_type));
ir_assert(get_src_ptr_type(ptr_type) != nullptr, source_instr);
ir_assert(type_has_bits(ptr_type), source_instr);
IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize);
if (type_is_invalid(casted_int->value.type))
@ -21936,7 +22137,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
case TldIdFn: {
TldFn *tld_fn = (TldFn *)tld;
ZigFn *fn_entry = tld_fn->fn_entry;
assert(fn_entry->type_entry);
ir_assert(fn_entry->type_entry, &instruction->base);
if (tld_fn->extern_lib_name != nullptr) {
add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, instruction->base.source_node);
@ -22134,7 +22335,7 @@ static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruct
ZigType *result_type = fn_type_id->param_info[arg_index].type;
if (result_type == nullptr) {
// Args are only unresolved if our function is generic.
assert(fn_type->data.fn.is_generic);
ir_assert(fn_type->data.fn.is_generic, &instruction->base);
ir_add_error(ira, arg_index_inst,
buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic",
@ -22220,7 +22421,7 @@ static IrInstruction *ir_analyze_instruction_coro_begin(IrAnalyze *ira, IrInstru
return ira->codegen->invalid_instruction;
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_entry != nullptr);
ir_assert(fn_entry != nullptr, &instruction->base);
IrInstruction *result = ir_build_coro_begin(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
coro_id, coro_mem_ptr);
result->value.type = get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type);
@ -22458,7 +22659,7 @@ static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstr
}
if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) {
assert(instruction->ordering != nullptr);
ir_assert(instruction->ordering != nullptr, &instruction->base);
ir_add_error(ira, instruction->ordering,
buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel"));
return ira->codegen->invalid_instruction;
@ -22466,7 +22667,7 @@ static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstr
if (instr_is_comptime(casted_ptr)) {
IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr);
assert(result->value.type != nullptr);
ir_assert(result->value.type != nullptr, &instruction->base);
return result;
}
@ -22496,7 +22697,7 @@ static IrInstruction *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, I
return ira->codegen->invalid_instruction;
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_entry != nullptr);
ir_assert(fn_entry != nullptr, &instruction->base);
if (type_can_fail(promise_result_type)) {
fn_entry->calls_or_awaits_errorable_fn = true;
@ -22512,9 +22713,9 @@ static IrInstruction *ir_analyze_instruction_merge_err_ret_traces(IrAnalyze *ira
if (type_is_invalid(coro_promise_ptr->value.type))
return ira->codegen->invalid_instruction;
assert(coro_promise_ptr->value.type->id == ZigTypeIdPointer);
ir_assert(coro_promise_ptr->value.type->id == ZigTypeIdPointer, &instruction->base);
ZigType *promise_frame_type = coro_promise_ptr->value.type->data.pointer.child_type;
assert(promise_frame_type->id == ZigTypeIdStruct);
ir_assert(promise_frame_type->id == ZigTypeIdStruct, &instruction->base);
ZigType *promise_result_type = promise_frame_type->data.structure.fields[1].type_entry;
if (!type_can_fail(promise_result_type)) {
@ -22606,7 +22807,7 @@ static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionS
return result;
}
assert(float_type->id == ZigTypeIdFloat);
ir_assert(float_type->id == ZigTypeIdFloat, &instruction->base);
if (float_type->data.floating.bit_count != 16 &&
float_type->data.floating.bit_count != 32 &&
float_type->data.floating.bit_count != 64) {
@ -22817,6 +23018,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
case IrInstructionIdArrayToVector:
case IrInstructionIdVectorToArray:
case IrInstructionIdAssertZero:
case IrInstructionIdAssertNonNull:
case IrInstructionIdResizeSlice:
case IrInstructionIdLoadPtrGen:
case IrInstructionIdBitCastGen:
@ -23096,7 +23298,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *old_instruction) {
IrInstruction *new_instruction = ir_analyze_instruction_nocast(ira, old_instruction);
assert(new_instruction->value.type != nullptr);
ir_assert(new_instruction->value.type != nullptr, old_instruction);
old_instruction->child = new_instruction;
return new_instruction;
}
@ -23221,6 +23423,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdCmpxchgGen:
case IrInstructionIdCmpxchgSrc:
case IrInstructionIdAssertZero:
case IrInstructionIdAssertNonNull:
case IrInstructionIdResizeSlice:
case IrInstructionIdGlobalAsm:
return true;

View File

@ -14,7 +14,7 @@ bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutable *ir_executable
bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry);
ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
ZigType *expected_type, size_t *backward_branch_count, size_t *backward_branch_quota,
ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
IrExecutable *parent_exec, AstNode *expected_type_source_node);

View File

@ -1003,6 +1003,12 @@ static void ir_print_assert_zero(IrPrint *irp, IrInstructionAssertZero *instruct
fprintf(irp->f, ")");
}
static void ir_print_assert_non_null(IrPrint *irp, IrInstructionAssertNonNull *instruction) {
fprintf(irp->f, "AssertNonNull(");
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
static void ir_print_resize_slice(IrPrint *irp, IrInstructionResizeSlice *instruction) {
fprintf(irp->f, "@resizeSlice(");
ir_print_other_instruction(irp, instruction->operand);
@ -1880,6 +1886,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdAssertZero:
ir_print_assert_zero(irp, (IrInstructionAssertZero *)instruction);
break;
case IrInstructionIdAssertNonNull:
ir_print_assert_non_null(irp, (IrInstructionAssertNonNull *)instruction);
break;
case IrInstructionIdResizeSlice:
ir_print_resize_slice(irp, (IrInstructionResizeSlice *)instruction);
break;

View File

@ -14,6 +14,7 @@ static const char *zig_libc_keys[] = {
"include_dir",
"sys_include_dir",
"crt_dir",
"static_crt_dir",
"msvc_lib_dir",
"kernel32_lib_dir",
};
@ -34,6 +35,7 @@ static void zig_libc_init_empty(ZigLibCInstallation *libc) {
buf_init_from_str(&libc->include_dir, "");
buf_init_from_str(&libc->sys_include_dir, "");
buf_init_from_str(&libc->crt_dir, "");
buf_init_from_str(&libc->static_crt_dir, "");
buf_init_from_str(&libc->msvc_lib_dir, "");
buf_init_from_str(&libc->kernel32_lib_dir, "");
}
@ -45,7 +47,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
bool found_keys[array_length(zig_libc_keys)] = {};
Buf *contents = buf_alloc();
if ((err = os_fetch_file_path(libc_file, contents, false))) {
if ((err = os_fetch_file_path(libc_file, contents))) {
if (err != ErrorFileNotFound && verbose) {
fprintf(stderr, "Unable to read '%s': %s\n", buf_ptr(libc_file), err_str(err));
}
@ -74,8 +76,9 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
match = match || zig_libc_match_key(name, value, found_keys, 0, &libc->include_dir);
match = match || zig_libc_match_key(name, value, found_keys, 1, &libc->sys_include_dir);
match = match || zig_libc_match_key(name, value, found_keys, 2, &libc->crt_dir);
match = match || zig_libc_match_key(name, value, found_keys, 3, &libc->msvc_lib_dir);
match = match || zig_libc_match_key(name, value, found_keys, 4, &libc->kernel32_lib_dir);
match = match || zig_libc_match_key(name, value, found_keys, 3, &libc->static_crt_dir);
match = match || zig_libc_match_key(name, value, found_keys, 4, &libc->msvc_lib_dir);
match = match || zig_libc_match_key(name, value, found_keys, 5, &libc->kernel32_lib_dir);
}
for (size_t i = 0; i < zig_libc_keys_len; i += 1) {
@ -110,6 +113,15 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget
}
}
if (buf_len(&libc->static_crt_dir) == 0) {
if (target->os == OsWindows && target_abi_is_gnu(target->abi)) {
if (verbose) {
fprintf(stderr, "static_crt_dir may not be empty for %s\n", target_os_name(target->os));
}
return ErrorSemanticAnalyzeFail;
}
}
if (buf_len(&libc->msvc_lib_dir) == 0) {
if (target->os == OsWindows && !target_abi_is_gnu(target->abi)) {
if (verbose) {
@ -311,6 +323,10 @@ static Error zig_libc_find_native_crt_dir_posix(ZigLibCInstallation *self, bool
#endif
#if defined(ZIG_OS_WINDOWS)
static Error zig_libc_find_native_static_crt_dir_posix(ZigLibCInstallation *self, bool verbose) {
return zig_libc_cc_print_file_name("crtbegin.o", &self->static_crt_dir, true, verbose);
}
static Error zig_libc_find_native_include_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) {
Error err;
if ((err = os_get_win32_ucrt_include_path(sdk, &self->include_dir))) {
@ -322,7 +338,7 @@ static Error zig_libc_find_native_include_dir_windows(ZigLibCInstallation *self,
return ErrorNone;
}
static Error zig_libc_find_crt_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, ZigTarget *target,
static Error zig_libc_find_native_crt_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, ZigTarget *target,
bool verbose)
{
Error err;
@ -398,11 +414,16 @@ void zig_libc_render(ZigLibCInstallation *self, FILE *file) {
"# On POSIX it's the directory that includes `sys/errno.h`.\n"
"sys_include_dir=%s\n"
"\n"
"# The directory that contains `crt1.o`.\n"
"# The directory that contains `crt1.o` or `crt2.o`.\n"
"# On POSIX, can be found with `cc -print-file-name=crt1.o`.\n"
"# Not needed when targeting MacOS.\n"
"crt_dir=%s\n"
"\n"
"# The directory that contains `crtbegin.o`.\n"
"# On POSIX, can be found with `cc -print-file-name=crtbegin.o`.\n"
"# Not needed when targeting MacOS.\n"
"static_crt_dir=%s\n"
"\n"
"# The directory that contains `vcruntime.lib`.\n"
"# Only needed when targeting MSVC on Windows.\n"
"msvc_lib_dir=%s\n"
@ -415,6 +436,7 @@ void zig_libc_render(ZigLibCInstallation *self, FILE *file) {
buf_ptr(&self->include_dir),
buf_ptr(&self->sys_include_dir),
buf_ptr(&self->crt_dir),
buf_ptr(&self->static_crt_dir),
buf_ptr(&self->msvc_lib_dir),
buf_ptr(&self->kernel32_lib_dir)
);
@ -431,6 +453,8 @@ Error zig_libc_find_native(ZigLibCInstallation *self, bool verbose) {
return err;
if ((err = zig_libc_find_native_crt_dir_posix(self, verbose)))
return err;
if ((err = zig_libc_find_native_static_crt_dir_posix(self, verbose)))
return err;
return ErrorNone;
} else {
ZigWindowsSDK *sdk;
@ -444,7 +468,7 @@ Error zig_libc_find_native(ZigLibCInstallation *self, bool verbose) {
return err;
if ((err = zig_libc_find_native_include_dir_windows(self, sdk, verbose)))
return err;
if ((err = zig_libc_find_crt_dir_windows(self, sdk, &native_target, verbose)))
if ((err = zig_libc_find_native_crt_dir_windows(self, sdk, &native_target, verbose)))
return err;
return ErrorNone;
case ZigFindWindowsSdkErrorOutOfMemory:

View File

@ -19,6 +19,7 @@ struct ZigLibCInstallation {
Buf include_dir;
Buf sys_include_dir;
Buf crt_dir;
Buf static_crt_dir;
Buf msvc_lib_dir;
Buf kernel32_lib_dir;
};
@ -29,8 +30,6 @@ void zig_libc_render(ZigLibCInstallation *self, FILE *file);
Error ATTRIBUTE_MUST_USE zig_libc_find_native(ZigLibCInstallation *self, bool verbose);
#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_WINDOWS)
Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirname, bool verbose);
#endif
#endif

View File

@ -25,6 +25,7 @@ static CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, Ou
CodeGen *child_gen = codegen_create(nullptr, root_src_path, parent_gen->zig_target, out_type,
parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir, libc, get_stage1_cache_path());
child_gen->disable_gen_h = true;
child_gen->disable_stack_probing = true;
child_gen->verbose_tokenize = parent_gen->verbose_tokenize;
child_gen->verbose_ast = parent_gen->verbose_ast;
child_gen->verbose_link = parent_gen->verbose_link;
@ -772,17 +773,15 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) {
}
}
static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) {
static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path, OutType child_out_type) {
// The Mach-O LLD code is not well maintained, and trips an assertion
// when we link compiler_rt and builtin as libraries rather than objects.
// Here we workaround this by having compiler_rt and builtin be objects.
// TODO write our own linker. https://github.com/ziglang/zig/issues/1535
OutType child_out_type = OutTypeLib;
if (parent_gen->zig_target->os == OsMacOSX) {
child_out_type = OutTypeObj;
}
CodeGen *child_gen = create_child_codegen(parent_gen, full_path, child_out_type,
parent_gen->libc);
codegen_set_out_name(child_gen, buf_create_from_str(aname));
@ -804,14 +803,14 @@ static Buf *build_a(CodeGen *parent_gen, const char *aname) {
Buf *full_path = buf_alloc();
os_path_join(parent_gen->zig_std_special_dir, source_basename, full_path);
return build_a_raw(parent_gen, aname, full_path);
return build_a_raw(parent_gen, aname, full_path, OutTypeLib);
}
static Buf *build_compiler_rt(CodeGen *parent_gen) {
static Buf *build_compiler_rt(CodeGen *parent_gen, OutType child_out_type) {
Buf *full_path = buf_alloc();
os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("compiler_rt.zig"), full_path);
return build_a_raw(parent_gen, "compiler_rt", full_path);
return build_a_raw(parent_gen, "compiler_rt", full_path, child_out_type);
}
static const char *get_darwin_arch_string(const ZigTarget *t) {
@ -1006,7 +1005,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
lj->args.append(buf_ptr(builtin_a_path));
}
Buf *compiler_rt_o_path = build_compiler_rt(g);
Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib);
lj->args.append(buf_ptr(compiler_rt_o_path));
}
@ -1091,16 +1090,35 @@ static void construct_linker_job_wasm(LinkJob *lj) {
CodeGen *g = lj->codegen;
lj->args.append("-error-limit=0");
lj->args.append("--no-entry"); // So lld doesn't look for _start.
if (g->zig_target->os != OsWASI) {
lj->args.append("--no-entry"); // So lld doesn't look for _start.
}
lj->args.append("--allow-undefined");
lj->args.append("--export-all");
lj->args.append("-o");
lj->args.append(buf_ptr(&g->output_file_path));
auto export_it = g->exported_symbol_names.entry_iterator();
decltype(g->exported_symbol_names)::Entry *curr_entry = nullptr;
while ((curr_entry = export_it.next()) != nullptr) {
Buf *arg = buf_sprintf("--export=%s", buf_ptr(curr_entry->key));
lj->args.append(buf_ptr(arg));
}
// .o files
for (size_t i = 0; i < g->link_objects.length; i += 1) {
lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
}
if (g->out_type == OutTypeExe) {
if (g->libc_link_lib == nullptr) {
Buf *builtin_a_path = build_a(g, "builtin");
lj->args.append(buf_ptr(builtin_a_path));
}
Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib);
lj->args.append(buf_ptr(compiler_rt_o_path));
}
}
static void coff_append_machine_arg(CodeGen *g, ZigList<const char *> *list) {
@ -1124,53 +1142,121 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si
}
static void add_uefi_link_args(LinkJob *lj) {
lj->args.append("/BASE:0");
lj->args.append("/ENTRY:EfiMain");
lj->args.append("/OPT:REF");
lj->args.append("/SAFESEH:NO");
lj->args.append("/MERGE:.rdata=.data");
lj->args.append("/ALIGN:32");
lj->args.append("/NODEFAULTLIB");
lj->args.append("/SECTION:.xdata,D");
lj->args.append("-BASE:0");
lj->args.append("-ENTRY:EfiMain");
lj->args.append("-OPT:REF");
lj->args.append("-SAFESEH:NO");
lj->args.append("-MERGE:.rdata=.data");
lj->args.append("-ALIGN:32");
lj->args.append("-NODEFAULTLIB");
lj->args.append("-SECTION:.xdata,D");
}
static void add_nt_link_args(LinkJob *lj, bool is_library) {
static void add_msvc_link_args(LinkJob *lj, bool is_library) {
CodeGen *g = lj->codegen;
if (lj->link_in_crt) {
// TODO: https://github.com/ziglang/zig/issues/2064
bool is_dynamic = true; // g->is_dynamic;
const char *lib_str = is_dynamic ? "" : "lib";
const char *d_str = (g->build_mode == BuildModeDebug) ? "d" : "";
// TODO: https://github.com/ziglang/zig/issues/2064
bool is_dynamic = true; // g->is_dynamic;
const char *lib_str = is_dynamic ? "" : "lib";
const char *d_str = (g->build_mode == BuildModeDebug) ? "d" : "";
if (!is_dynamic) {
Buf *cmt_lib_name = buf_sprintf("libcmt%s.lib", d_str);
lj->args.append(buf_ptr(cmt_lib_name));
} else {
Buf *msvcrt_lib_name = buf_sprintf("msvcrt%s.lib", d_str);
lj->args.append(buf_ptr(msvcrt_lib_name));
}
Buf *vcruntime_lib_name = buf_sprintf("%svcruntime%s.lib", lib_str, d_str);
lj->args.append(buf_ptr(vcruntime_lib_name));
Buf *crt_lib_name = buf_sprintf("%sucrt%s.lib", lib_str, d_str);
lj->args.append(buf_ptr(crt_lib_name));
//Visual C++ 2015 Conformance Changes
//https://msdn.microsoft.com/en-us/library/bb531344.aspx
lj->args.append("legacy_stdio_definitions.lib");
// msvcrt depends on kernel32 and ntdll
lj->args.append("kernel32.lib");
lj->args.append("ntdll.lib");
if (!is_dynamic) {
Buf *cmt_lib_name = buf_sprintf("libcmt%s.lib", d_str);
lj->args.append(buf_ptr(cmt_lib_name));
} else {
lj->args.append("/NODEFAULTLIB");
Buf *msvcrt_lib_name = buf_sprintf("msvcrt%s.lib", d_str);
lj->args.append(buf_ptr(msvcrt_lib_name));
}
Buf *vcruntime_lib_name = buf_sprintf("%svcruntime%s.lib", lib_str, d_str);
lj->args.append(buf_ptr(vcruntime_lib_name));
Buf *crt_lib_name = buf_sprintf("%sucrt%s.lib", lib_str, d_str);
lj->args.append(buf_ptr(crt_lib_name));
//Visual C++ 2015 Conformance Changes
//https://msdn.microsoft.com/en-us/library/bb531344.aspx
lj->args.append("legacy_stdio_definitions.lib");
// msvcrt depends on kernel32 and ntdll
lj->args.append("kernel32.lib");
lj->args.append("ntdll.lib");
}
static const char *get_libc_file(ZigLibCInstallation *lib, const char *file) {
Buf *out_buf = buf_alloc();
os_path_join(&lib->crt_dir, buf_create_from_str(file), out_buf);
return buf_ptr(out_buf);
}
static const char *get_libc_static_file(ZigLibCInstallation *lib, const char *file) {
Buf *out_buf = buf_alloc();
os_path_join(&lib->static_crt_dir, buf_create_from_str(file), out_buf);
return buf_ptr(out_buf);
}
static void add_mingw_link_args(LinkJob *lj, bool is_library) {
CodeGen *g = lj->codegen;
bool is_dll = g->out_type == OutTypeLib && g->is_dynamic;
if (g->zig_target->arch == ZigLLVM_x86) {
lj->args.append("-ALTERNATENAME:__image_base__=___ImageBase");
} else {
lj->args.append("-ALTERNATENAME:__image_base__=__ImageBase");
}
if (is_dll) {
lj->args.append(get_libc_file(g->libc, "dllcrt2.o"));
} else {
lj->args.append(get_libc_file(g->libc, "crt2.o"));
}
lj->args.append(get_libc_static_file(g->libc, "crtbegin.o"));
lj->args.append(get_libc_file(g->libc, "libmingw32.a"));
if (is_dll) {
lj->args.append(get_libc_static_file(g->libc, "libgcc_s.a"));
lj->args.append(get_libc_static_file(g->libc, "libgcc.a"));
} else {
lj->args.append(get_libc_static_file(g->libc, "libgcc.a"));
lj->args.append(get_libc_static_file(g->libc, "libgcc_eh.a"));
}
lj->args.append(get_libc_static_file(g->libc, "libssp.a"));
lj->args.append(get_libc_file(g->libc, "libmoldname.a"));
lj->args.append(get_libc_file(g->libc, "libmingwex.a"));
lj->args.append(get_libc_file(g->libc, "libmsvcrt.a"));
if (g->subsystem == TargetSubsystemWindows) {
lj->args.append(get_libc_file(g->libc, "libgdi32.a"));
lj->args.append(get_libc_file(g->libc, "libcomdlg32.a"));
}
lj->args.append(get_libc_file(g->libc, "libadvapi32.a"));
lj->args.append(get_libc_file(g->libc, "libadvapi32.a"));
lj->args.append(get_libc_file(g->libc, "libshell32.a"));
lj->args.append(get_libc_file(g->libc, "libuser32.a"));
lj->args.append(get_libc_file(g->libc, "libkernel32.a"));
lj->args.append(get_libc_static_file(g->libc, "crtend.o"));
}
static void add_win_link_args(LinkJob *lj, bool is_library) {
if (lj->link_in_crt) {
if (target_abi_is_gnu(lj->codegen->zig_target->abi)) {
add_mingw_link_args(lj, is_library);
} else {
add_msvc_link_args(lj, is_library);
}
} else {
lj->args.append("-NODEFAULTLIB");
if (!is_library) {
if (g->have_winmain) {
lj->args.append("/ENTRY:WinMain");
if (lj->codegen->have_winmain) {
lj->args.append("-ENTRY:WinMain");
} else {
lj->args.append("/ENTRY:WinMainCRTStartup");
lj->args.append("-ENTRY:WinMainCRTStartup");
}
}
}
@ -1180,62 +1266,24 @@ static void construct_linker_job_coff(LinkJob *lj) {
Error err;
CodeGen *g = lj->codegen;
lj->args.append("/ERRORLIMIT:0");
lj->args.append("-ERRORLIMIT:0");
lj->args.append("/NOLOGO");
lj->args.append("-NOLOGO");
if (!g->strip_debug_symbols) {
lj->args.append("/DEBUG");
lj->args.append("-DEBUG");
}
if (g->out_type == OutTypeExe) {
// TODO compile time stack upper bound detection
lj->args.append("/STACK:16777216");
lj->args.append("-STACK:16777216");
}
coff_append_machine_arg(g, &lj->args);
bool is_library = g->out_type == OutTypeLib;
switch (g->subsystem) {
case TargetSubsystemAuto:
if (g->zig_target->os == OsUefi) {
add_uefi_link_args(lj);
} else {
add_nt_link_args(lj, is_library);
}
break;
case TargetSubsystemConsole:
lj->args.append("/SUBSYSTEM:console");
add_nt_link_args(lj, is_library);
break;
case TargetSubsystemEfiApplication:
lj->args.append("/SUBSYSTEM:efi_application");
add_uefi_link_args(lj);
break;
case TargetSubsystemEfiBootServiceDriver:
lj->args.append("/SUBSYSTEM:efi_boot_service_driver");
add_uefi_link_args(lj);
break;
case TargetSubsystemEfiRom:
lj->args.append("/SUBSYSTEM:efi_rom");
add_uefi_link_args(lj);
break;
case TargetSubsystemEfiRuntimeDriver:
lj->args.append("/SUBSYSTEM:efi_runtime_driver");
add_uefi_link_args(lj);
break;
case TargetSubsystemNative:
lj->args.append("/SUBSYSTEM:native");
add_nt_link_args(lj, is_library);
break;
case TargetSubsystemPosix:
lj->args.append("/SUBSYSTEM:posix");
add_nt_link_args(lj, is_library);
break;
case TargetSubsystemWindows:
lj->args.append("/SUBSYSTEM:windows");
add_nt_link_args(lj, is_library);
break;
if (is_library && g->is_dynamic) {
lj->args.append("-DLL");
}
lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->output_file_path))));
@ -1243,13 +1291,15 @@ static void construct_linker_job_coff(LinkJob *lj) {
if (g->libc_link_lib != nullptr) {
assert(g->libc != nullptr);
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->msvc_lib_dir))));
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->kernel32_lib_dir))));
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->crt_dir))));
}
if (is_library && g->is_dynamic) {
lj->args.append("-DLL");
if (target_abi_is_gnu(g->zig_target->abi)) {
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->sys_include_dir))));
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->include_dir))));
} else {
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->msvc_lib_dir))));
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->kernel32_lib_dir))));
}
}
for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
@ -1261,6 +1311,48 @@ static void construct_linker_job_coff(LinkJob *lj) {
lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
}
switch (g->subsystem) {
case TargetSubsystemAuto:
if (g->zig_target->os == OsUefi) {
add_uefi_link_args(lj);
} else {
add_win_link_args(lj, is_library);
}
break;
case TargetSubsystemConsole:
lj->args.append("-SUBSYSTEM:console");
add_win_link_args(lj, is_library);
break;
case TargetSubsystemEfiApplication:
lj->args.append("-SUBSYSTEM:efi_application");
add_uefi_link_args(lj);
break;
case TargetSubsystemEfiBootServiceDriver:
lj->args.append("-SUBSYSTEM:efi_boot_service_driver");
add_uefi_link_args(lj);
break;
case TargetSubsystemEfiRom:
lj->args.append("-SUBSYSTEM:efi_rom");
add_uefi_link_args(lj);
break;
case TargetSubsystemEfiRuntimeDriver:
lj->args.append("-SUBSYSTEM:efi_runtime_driver");
add_uefi_link_args(lj);
break;
case TargetSubsystemNative:
lj->args.append("-SUBSYSTEM:native");
add_win_link_args(lj, is_library);
break;
case TargetSubsystemPosix:
lj->args.append("-SUBSYSTEM:posix");
add_win_link_args(lj, is_library);
break;
case TargetSubsystemWindows:
lj->args.append("-SUBSYSTEM:windows");
add_win_link_args(lj, is_library);
break;
}
if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) {
if (g->libc_link_lib == nullptr && !g->is_dummy_so) {
Buf *builtin_a_path = build_a(g, "builtin");
@ -1268,7 +1360,7 @@ static void construct_linker_job_coff(LinkJob *lj) {
}
// msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
Buf *compiler_rt_o_path = build_compiler_rt(g);
Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib);
lj->args.append(buf_ptr(compiler_rt_o_path));
}
@ -1280,11 +1372,10 @@ static void construct_linker_job_coff(LinkJob *lj) {
continue;
}
if (link_lib->provided_explicitly) {
if (lj->codegen->zig_target->abi == ZigLLVM_GNU) {
Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
lj->args.append(buf_ptr(arg));
}
else {
if (target_abi_is_gnu(lj->codegen->zig_target->abi)) {
Buf *lib_name = buf_sprintf("lib%s.a", buf_ptr(link_lib->name));
lj->args.append(buf_ptr(lib_name));
} else {
lj->args.append(buf_ptr(link_lib->name));
}
} else {
@ -1416,18 +1507,6 @@ static void get_darwin_platform(LinkJob *lj, DarwinPlatform *platform) {
}
}
static bool darwin_version_lt(DarwinPlatform *platform, int major, int minor) {
if (platform->major < major) {
return true;
} else if (platform->major > major) {
return false;
}
if (platform->minor < minor) {
return true;
}
return false;
}
static void construct_linker_job_macho(LinkJob *lj) {
CodeGen *g = lj->codegen;
@ -1524,7 +1603,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
if (g->out_type == OutTypeExe || is_dyn_lib) {
Buf *compiler_rt_o_path = build_compiler_rt(g);
Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib);
lj->args.append(buf_ptr(compiler_rt_o_path));
}
@ -1552,16 +1631,6 @@ static void construct_linker_job_macho(LinkJob *lj) {
lj->args.append("dynamic_lookup");
}
if (platform.kind == MacOS) {
if (darwin_version_lt(&platform, 10, 5)) {
lj->args.append("-lgcc_s.10.4");
} else if (darwin_version_lt(&platform, 10, 6)) {
lj->args.append("-lgcc_s.10.5");
}
} else {
zig_panic("TODO");
}
for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) {
lj->args.append("-framework");
lj->args.append(buf_ptr(g->darwin_frameworks.at(i)));
@ -1585,6 +1654,11 @@ static void construct_linker_job(LinkJob *lj) {
}
}
void zig_link_add_compiler_rt(CodeGen *g) {
Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj);
g->link_objects.append(compiler_rt_o_path);
}
void codegen_link(CodeGen *g) {
codegen_add_time_event(g, "Build Dependencies");
@ -1611,14 +1685,14 @@ void codegen_link(CodeGen *g) {
if (g->out_type == OutTypeLib && !g->is_dynamic) {
ZigList<const char *> file_names = {};
for (size_t i = 0; i < g->link_objects.length; i += 1) {
file_names.append((const char *)buf_ptr(g->link_objects.at(i)));
file_names.append(buf_ptr(g->link_objects.at(i)));
}
ZigLLVM_OSType os_type = get_llvm_os_type(g->zig_target->os);
codegen_add_time_event(g, "LLVM Link");
if (g->verbose_link) {
fprintf(stderr, "ar rcs %s", buf_ptr(&g->output_file_path));
for (size_t i = 0; i < g->link_objects.length; i += 1) {
fprintf(stderr, " %s", (const char *)buf_ptr(g->link_objects.at(i)));
for (size_t i = 0; i < file_names.length; i += 1) {
fprintf(stderr, " %s", file_names.at(i));
}
fprintf(stderr, "\n");
}

View File

@ -10,8 +10,6 @@
#include "util.hpp"
#include <assert.h>
template<typename T>
struct ZigList {
void deinit() {

View File

@ -14,6 +14,7 @@
#include "os.hpp"
#include "target.hpp"
#include "libc_installation.hpp"
#include "userland.h"
#include <stdio.h>
@ -40,6 +41,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" libc [paths_file] Display native libc paths file or validate one\n"
" run [source] [-- [args]] create executable and run immediately\n"
" translate-c [source] convert c code to zig code\n"
" translate-c-2 [source] experimental self-hosted translate-c\n"
" targets list available compilation targets\n"
" test [source] create and run a test build\n"
" version print version number and exit\n"
@ -52,11 +54,12 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" --cache [auto|off|on] build in cache, print output path to stdout\n"
" --color [auto|off|on] enable or disable colored error messages\n"
" --disable-gen-h do not generate a C header file (.h)\n"
" --disable-pic disable Position Independent Code\n"
" --enable-pic enable Position Independent Code\n"
" --disable-valgrind omit valgrind client requests in debug builds\n"
" --enable-valgrind include valgrind client requests release builds\n"
" --disable-stack-probing workaround for macosx\n"
" --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n"
" -fPIC enable Position Independent Code\n"
" -fno-PIC disable Position Independent Code\n"
" -ftime-report print timing diagnostics\n"
" --libc [file] Provide a file which specifies libc paths\n"
" --name [name] override output name\n"
@ -84,6 +87,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" --override-std-dir [arg] use an alternate Zig standard library\n"
"\n"
"Link Options:\n"
" --bundle-compiler-rt [path] for static libraries, include compiler-rt symbols\n"
" --dynamic-linker [path] set the path to ld.so\n"
" --each-lib-rpath add rpath for each used dynamic library\n"
" --library [lib] link against lib\n"
@ -131,19 +135,6 @@ static int print_libc_usage(const char *arg0, FILE *file, int return_code) {
return return_code;
}
static const char *ZIG_ZEN = "\n"
" * Communicate intent precisely.\n"
" * Edge cases matter.\n"
" * Favor reading code over writing code.\n"
" * Only one obvious way to do things.\n"
" * Runtime crashes are better than bugs.\n"
" * Compile errors are better than runtime crashes.\n"
" * Incremental improvements.\n"
" * Avoid local maximums.\n"
" * Reduce the amount one must remember.\n"
" * Minimize energy spent on coding style.\n"
" * Together we serve end users.\n";
static bool arch_available_in_llvm(ZigLLVM_ArchType arch) {
LLVMTargetRef target_ref;
char *err_msg = nullptr;
@ -211,6 +202,7 @@ enum Cmd {
CmdTargets,
CmdTest,
CmdTranslateC,
CmdTranslateCUserland,
CmdVersion,
CmdZen,
CmdLibC,
@ -324,7 +316,7 @@ int main(int argc, char **argv) {
return print_error_usage(arg0);
}
Buf *cmd_template_path = buf_alloc();
os_path_join(get_zig_special_dir(), buf_create_from_str(init_cmd), cmd_template_path);
os_path_join(get_zig_special_dir(get_zig_lib_dir()), buf_create_from_str(init_cmd), cmd_template_path);
Buf *build_zig_path = buf_alloc();
os_path_join(cmd_template_path, buf_create_from_str("build.zig"), build_zig_path);
Buf *src_dir_path = buf_alloc();
@ -341,7 +333,7 @@ int main(int argc, char **argv) {
os_path_split(cwd, nullptr, cwd_basename);
Buf *build_zig_contents = buf_alloc();
if ((err = os_fetch_file_path(build_zig_path, build_zig_contents, false))) {
if ((err = os_fetch_file_path(build_zig_path, build_zig_contents))) {
fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(build_zig_path), err_str(err));
return EXIT_FAILURE;
}
@ -356,7 +348,7 @@ int main(int argc, char **argv) {
}
Buf *main_zig_contents = buf_alloc();
if ((err = os_fetch_file_path(main_zig_path, main_zig_contents, false))) {
if ((err = os_fetch_file_path(main_zig_path, main_zig_contents))) {
fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(main_zig_path), err_str(err));
return EXIT_FAILURE;
}
@ -450,9 +442,12 @@ int main(int argc, char **argv) {
int runtime_args_start = -1;
bool system_linker_hack = false;
TargetSubsystem subsystem = TargetSubsystemAuto;
bool is_single_threaded = false;
bool want_single_threaded = false;
bool disable_gen_h = false;
bool bundle_compiler_rt = false;
bool disable_stack_probing = false;
Buf *override_std_dir = nullptr;
Buf *override_lib_dir = nullptr;
Buf *main_pkg_path = nullptr;
ValgrindSupport valgrind_support = ValgrindSupportAuto;
WantPIC want_pic = WantPICAuto;
@ -486,13 +481,27 @@ int main(int argc, char **argv) {
} else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) {
cache_dir = argv[i + 1];
i += 1;
} else if (i + 1 < argc && strcmp(argv[i], "--override-std-dir") == 0) {
override_std_dir = buf_create_from_str(argv[i + 1]);
i += 1;
args.append("--override-std-dir");
args.append(buf_ptr(override_std_dir));
} else if (i + 1 < argc && strcmp(argv[i], "--override-lib-dir") == 0) {
override_lib_dir = buf_create_from_str(argv[i + 1]);
i += 1;
args.append("--override-lib-dir");
args.append(buf_ptr(override_lib_dir));
} else {
args.append(argv[i]);
}
}
Buf *zig_lib_dir = (override_lib_dir == nullptr) ? get_zig_lib_dir() : override_lib_dir;
Buf *build_runner_path = buf_alloc();
os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path);
os_path_join(get_zig_special_dir(zig_lib_dir), buf_create_from_str("build_runner.zig"), build_runner_path);
ZigTarget target;
get_native_target(&target);
@ -512,7 +521,7 @@ int main(int argc, char **argv) {
}
CodeGen *g = codegen_create(main_pkg_path, build_runner_path, &target, OutTypeExe,
BuildModeDebug, get_zig_lib_dir(), override_std_dir, nullptr, &full_cache_dir);
BuildModeDebug, override_lib_dir, override_std_dir, nullptr, &full_cache_dir);
g->valgrind_support = valgrind_support;
g->enable_time_report = timing_info;
codegen_set_out_name(g, buf_create_from_str("build"));
@ -532,23 +541,25 @@ int main(int argc, char **argv) {
"Usage: %s build [options]\n"
"\n"
"General Options:\n"
" --help Print this help and exit\n"
" --verbose Print commands before executing them\n"
" --prefix [path] Override default install prefix\n"
" --search-prefix [path] Add a path to look for binaries, libraries, headers\n"
" --help Print this help and exit\n"
" --verbose Print commands before executing them\n"
" --prefix [path] Override default install prefix\n"
" --search-prefix [path] Add a path to look for binaries, libraries, headers\n"
"\n"
"Project-specific options become available when the build file is found.\n"
"\n"
"Advanced Options:\n"
" --build-file [file] Override path to build.zig\n"
" --cache-dir [path] Override path to cache directory\n"
" --verbose-tokenize Enable compiler debug output for tokenization\n"
" --verbose-ast Enable compiler debug output for parsing into an AST\n"
" --verbose-link Enable compiler debug output for linking\n"
" --verbose-ir Enable compiler debug output for Zig IR\n"
" --verbose-llvm-ir Enable compiler debug output for LLVM IR\n"
" --verbose-cimport Enable compiler debug output for C imports\n"
" --verbose-cc Enable compiler debug output for C compilation\n"
" --build-file [file] Override path to build.zig\n"
" --cache-dir [path] Override path to cache directory\n"
" --override-std-dir [arg] Override path to Zig standard library\n"
" --override-lib-dir [arg] Override path to Zig lib library\n"
" --verbose-tokenize Enable compiler debug output for tokenization\n"
" --verbose-ast Enable compiler debug output for parsing into an AST\n"
" --verbose-link Enable compiler debug output for linking\n"
" --verbose-ir Enable compiler debug output for Zig IR\n"
" --verbose-llvm-ir Enable compiler debug output for LLVM IR\n"
" --verbose-cimport Enable compiler debug output for C imports\n"
" --verbose-cc Enable compiler debug output for C compilation\n"
"\n"
, zig_exe_path);
return EXIT_SUCCESS;
@ -581,36 +592,7 @@ int main(int argc, char **argv) {
}
return (term.how == TerminationIdClean) ? term.code : -1;
} else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) {
init_all_targets();
ZigTarget target;
get_native_target(&target);
Buf *fmt_runner_path = buf_alloc();
os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path);
Buf *cache_dir_buf = buf_create_from_str(cache_dir ? cache_dir : default_zig_cache_name);
CodeGen *g = codegen_create(main_pkg_path, fmt_runner_path, &target, OutTypeExe,
BuildModeDebug, get_zig_lib_dir(), nullptr, nullptr, cache_dir_buf);
g->valgrind_support = valgrind_support;
g->is_single_threaded = true;
codegen_set_out_name(g, buf_create_from_str("fmt"));
g->enable_cache = true;
codegen_build_and_link(g);
// TODO standardize os.cpp so that the args are supposed to have the exe
ZigList<const char*> args_with_exe = {0};
ZigList<const char*> args_without_exe = {0};
const char *exec_path = buf_ptr(&g->output_file_path);
args_with_exe.append(exec_path);
for (int i = 2; i < argc; i += 1) {
args_with_exe.append(argv[i]);
args_without_exe.append(argv[i]);
}
args_with_exe.append(nullptr);
os_execv(exec_path, args_with_exe.items);
Termination term;
os_spawn_process(exec_path, args_without_exe, &term);
return term.code;
return stage2_fmt(argc, argv);
}
for (int i = 1; i < argc; i += 1) {
@ -664,16 +646,20 @@ int main(int argc, char **argv) {
valgrind_support = ValgrindSupportEnabled;
} else if (strcmp(arg, "--disable-valgrind") == 0) {
valgrind_support = ValgrindSupportDisabled;
} else if (strcmp(arg, "--enable-pic") == 0) {
} else if (strcmp(arg, "-fPIC") == 0) {
want_pic = WantPICEnabled;
} else if (strcmp(arg, "--disable-pic") == 0) {
} else if (strcmp(arg, "-fno-PIC") == 0) {
want_pic = WantPICDisabled;
} else if (strcmp(arg, "--system-linker-hack") == 0) {
system_linker_hack = true;
} else if (strcmp(arg, "--single-threaded") == 0) {
is_single_threaded = true;
want_single_threaded = true;
} else if (strcmp(arg, "--disable-gen-h") == 0) {
disable_gen_h = true;
} else if (strcmp(arg, "--bundle-compiler-rt") == 0) {
bundle_compiler_rt = true;
} else if (strcmp(arg, "--disable-stack-probing") == 0) {
disable_stack_probing = true;
} else if (strcmp(arg, "--test-cmd-bin") == 0) {
test_exec_args.append(nullptr);
} else if (arg[1] == 'L' && arg[2] != 0) {
@ -757,6 +743,8 @@ int main(int argc, char **argv) {
llvm_argv.append(argv[i]);
} else if (strcmp(arg, "--override-std-dir") == 0) {
override_std_dir = buf_create_from_str(argv[i]);
} else if (strcmp(arg, "--override-lib-dir") == 0) {
override_lib_dir = buf_create_from_str(argv[i]);
} else if (strcmp(arg, "--main-pkg-path") == 0) {
main_pkg_path = buf_create_from_str(argv[i]);
} else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
@ -775,7 +763,11 @@ int main(int argc, char **argv) {
if (argv[i][0] == '-') {
c_file->args.append(argv[i]);
i += 1;
continue;
if (i < argc) {
continue;
}
break;
} else {
c_file->source_path = argv[i];
c_source_files.append(c_file);
@ -867,6 +859,8 @@ int main(int argc, char **argv) {
cmd = CmdLibC;
} else if (strcmp(arg, "translate-c") == 0) {
cmd = CmdTranslateC;
} else if (strcmp(arg, "translate-c-2") == 0) {
cmd = CmdTranslateCUserland;
} else if (strcmp(arg, "test") == 0) {
cmd = CmdTest;
out_type = OutTypeExe;
@ -883,6 +877,7 @@ int main(int argc, char **argv) {
case CmdBuild:
case CmdRun:
case CmdTranslateC:
case CmdTranslateCUserland:
case CmdTest:
case CmdLibC:
if (!in_file) {
@ -959,10 +954,10 @@ int main(int argc, char **argv) {
}
case CmdBuiltin: {
CodeGen *g = codegen_create(main_pkg_path, nullptr, &target,
out_type, build_mode, get_zig_lib_dir(), override_std_dir, nullptr, nullptr);
out_type, build_mode, override_lib_dir, override_std_dir, nullptr, nullptr);
g->valgrind_support = valgrind_support;
g->want_pic = want_pic;
g->is_single_threaded = is_single_threaded;
g->want_single_threaded = want_single_threaded;
Buf *builtin_source = codegen_generate_builtin_source(g);
if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
@ -973,6 +968,7 @@ int main(int argc, char **argv) {
case CmdRun:
case CmdBuild:
case CmdTranslateC:
case CmdTranslateCUserland:
case CmdTest:
{
if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0 &&
@ -985,14 +981,16 @@ int main(int argc, char **argv) {
" * --assembly argument\n"
" * --c-source argument\n");
return print_error_usage(arg0);
} else if ((cmd == CmdTranslateC || cmd == CmdTest || cmd == CmdRun) && !in_file) {
} else if ((cmd == CmdTranslateC || cmd == CmdTranslateCUserland ||
cmd == CmdTest || cmd == CmdRun) && !in_file)
{
fprintf(stderr, "Expected source file argument.\n");
return print_error_usage(arg0);
}
assert(cmd != CmdBuild || out_type != OutTypeUnknown);
bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC);
bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC || cmd == CmdTranslateCUserland);
if (cmd == CmdRun) {
out_name = "run";
@ -1026,7 +1024,8 @@ int main(int argc, char **argv) {
return print_error_usage(arg0);
}
Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf;
Buf *zig_root_source_file = (cmd == CmdTranslateC || cmd == CmdTranslateCUserland) ?
nullptr : in_file_buf;
if (cmd == CmdRun && buf_out_name == nullptr) {
buf_out_name = buf_create_from_str("run");
@ -1050,7 +1049,7 @@ int main(int argc, char **argv) {
cache_dir_buf = buf_create_from_str(cache_dir);
}
CodeGen *g = codegen_create(main_pkg_path, zig_root_source_file, &target, out_type, build_mode,
get_zig_lib_dir(), override_std_dir, libc, cache_dir_buf);
override_lib_dir, override_std_dir, libc, cache_dir_buf);
if (llvm_argv.length >= 2) codegen_set_llvm_argv(g, llvm_argv.items + 1, llvm_argv.length - 2);
g->valgrind_support = valgrind_support;
g->want_pic = want_pic;
@ -1060,7 +1059,7 @@ int main(int argc, char **argv) {
codegen_set_out_name(g, buf_out_name);
codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
codegen_set_is_test(g, cmd == CmdTest);
g->is_single_threaded = is_single_threaded;
g->want_single_threaded = want_single_threaded;
codegen_set_linker_script(g, linker_script);
if (each_lib_rpath)
codegen_set_each_lib_rpath(g, each_lib_rpath);
@ -1079,6 +1078,8 @@ int main(int argc, char **argv) {
g->verbose_cc = verbose_cc;
g->output_dir = output_dir;
g->disable_gen_h = disable_gen_h;
g->bundle_compiler_rt = bundle_compiler_rt;
g->disable_stack_probing = disable_stack_probing;
codegen_set_errmsg_color(g, color);
g->system_linker_hack = system_linker_hack;
@ -1144,14 +1145,15 @@ int main(int argc, char **argv) {
codegen_print_timing_report(g, stdout);
if (cmd == CmdRun) {
const char *exec_path = buf_ptr(&g->output_file_path);
ZigList<const char*> args = {0};
args.append(exec_path);
if (runtime_args_start != -1) {
for (int i = runtime_args_start; i < argc; ++i) {
args.append(argv[i]);
}
}
const char *exec_path = buf_ptr(&g->output_file_path);
args.append(nullptr);
os_execv(exec_path, args.items);
@ -1169,9 +1171,8 @@ int main(int argc, char **argv) {
} else {
zig_unreachable();
}
} else if (cmd == CmdTranslateC) {
AstNode *root_node = codegen_translate_c(g, in_file_buf);
ast_render(g, stdout, root_node, 4);
} else if (cmd == CmdTranslateC || cmd == CmdTranslateCUserland) {
codegen_translate_c(g, in_file_buf, stdout, cmd == CmdTranslateCUserland);
if (timing_info)
codegen_print_timing_report(g, stderr);
return EXIT_SUCCESS;
@ -1228,9 +1229,13 @@ int main(int argc, char **argv) {
case CmdVersion:
printf("%s\n", ZIG_VERSION_STRING);
return EXIT_SUCCESS;
case CmdZen:
printf("%s\n", ZIG_ZEN);
case CmdZen: {
const char *ptr;
size_t len;
stage2_zen(&ptr, &len);
fwrite(ptr, len, 1, stdout);
return EXIT_SUCCESS;
}
case CmdTargets:
return print_target_list(stdout);
case CmdNone:

View File

@ -751,39 +751,15 @@ Buf os_path_resolve(Buf **paths_ptr, size_t paths_len) {
#endif
}
Error os_fetch_file(FILE *f, Buf *out_buf, bool skip_shebang) {
Error os_fetch_file(FILE *f, Buf *out_buf) {
static const ssize_t buf_size = 0x2000;
buf_resize(out_buf, buf_size);
ssize_t actual_buf_len = 0;
bool first_read = true;
for (;;) {
size_t amt_read = fread(buf_ptr(out_buf) + actual_buf_len, 1, buf_size, f);
actual_buf_len += amt_read;
if (skip_shebang && first_read && buf_starts_with_str(out_buf, "#!")) {
size_t i = 0;
while (true) {
if (i > buf_len(out_buf)) {
zig_panic("shebang line exceeded %zd characters", buf_size);
}
size_t current_pos = i;
i += 1;
if (out_buf->list.at(current_pos) == '\n') {
break;
}
}
ZigList<char> *list = &out_buf->list;
memmove(list->items, list->items + i, list->length - i);
list->length -= i;
actual_buf_len -= i;
}
if (amt_read != buf_size) {
if (feof(f)) {
buf_resize(out_buf, actual_buf_len);
@ -794,7 +770,6 @@ Error os_fetch_file(FILE *f, Buf *out_buf, bool skip_shebang) {
}
buf_resize(out_buf, actual_buf_len + buf_size);
first_read = false;
}
zig_unreachable();
}
@ -864,8 +839,8 @@ static Error os_exec_process_posix(const char *exe, ZigList<const char *> &args,
FILE *stdout_f = fdopen(stdout_pipe[0], "rb");
FILE *stderr_f = fdopen(stderr_pipe[0], "rb");
Error err1 = os_fetch_file(stdout_f, out_stdout, false);
Error err2 = os_fetch_file(stderr_f, out_stderr, false);
Error err1 = os_fetch_file(stdout_f, out_stdout);
Error err2 = os_fetch_file(stderr_f, out_stderr);
fclose(stdout_f);
fclose(stderr_f);
@ -1097,7 +1072,7 @@ Error os_copy_file(Buf *src_path, Buf *dest_path) {
}
}
Error os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang) {
Error os_fetch_file_path(Buf *full_path, Buf *out_contents) {
FILE *f = fopen(buf_ptr(full_path), "rb");
if (!f) {
switch (errno) {
@ -1116,7 +1091,7 @@ Error os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang) {
return ErrorFileSystem;
}
}
Error result = os_fetch_file(f, out_contents, skip_shebang);
Error result = os_fetch_file(f, out_contents);
fclose(f);
return result;
}
@ -1772,8 +1747,14 @@ Error os_get_app_data_dir(Buf *out_path, const char *appname) {
// TODO use /etc/passwd
return ErrorFileNotFound;
}
buf_resize(out_path, 0);
buf_appendf(out_path, "%s/.local/share/%s", home_dir, appname);
if (home_dir[0] == 0) {
return ErrorFileNotFound;
}
buf_init_from_str(out_path, home_dir);
if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') {
buf_append_char(out_path, '/');
}
buf_appendf(out_path, ".local/share/%s", appname);
return ErrorNone;
#endif
}
@ -2081,11 +2062,13 @@ Error os_file_overwrite(OsFile file, Buf *contents) {
#endif
}
void os_file_close(OsFile file) {
void os_file_close(OsFile *file) {
#if defined(ZIG_OS_WINDOWS)
CloseHandle(file);
CloseHandle(*file);
*file = NULL;
#else
close(file);
close(*file);
*file = -1;
#endif
}

View File

@ -121,13 +121,13 @@ Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file);
Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len);
Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents);
Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents);
void os_file_close(OsFile file);
void os_file_close(OsFile *file);
Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents);
Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path);
Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents, bool skip_shebang);
Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang);
Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents);
Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents);
Error ATTRIBUTE_MUST_USE os_get_cwd(Buf *out_cwd);

View File

@ -577,7 +577,7 @@ static AstNode *ast_parse_top_level_comptime(ParseContext *pc) {
// TopLevelDecl
// <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block)
// / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? VarDecl
// / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl
// / KEYWORD_use Expr SEMICOLON
static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
Token *first = eat_token_if(pc, TokenIdKeywordExport);
@ -591,17 +591,22 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
lib_name = eat_token_if(pc, TokenIdStringLiteral);
if (first->id != TokenIdKeywordInline) {
Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
AstNode *var_decl = ast_parse_var_decl(pc);
if (var_decl != nullptr) {
assert(var_decl->type == NodeTypeVariableDeclaration);
var_decl->line = first->start_line;
var_decl->column = first->start_column;
var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw;
var_decl->data.variable_declaration.visib_mod = visib_mod;
var_decl->data.variable_declaration.is_extern = first->id == TokenIdKeywordExtern;
var_decl->data.variable_declaration.is_export = first->id == TokenIdKeywordExport;
var_decl->data.variable_declaration.lib_name = token_buf(lib_name);
return var_decl;
}
if (thread_local_kw != nullptr)
put_back_token(pc);
}
AstNode *fn_proto = ast_parse_fn_proto(pc);
@ -632,13 +637,18 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
ast_invalid_token_error(pc, peek_token(pc));
}
Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
AstNode *var_decl = ast_parse_var_decl(pc);
if (var_decl != nullptr) {
assert(var_decl->type == NodeTypeVariableDeclaration);
var_decl->data.variable_declaration.visib_mod = visib_mod;
var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw;
return var_decl;
}
if (thread_local_kw != nullptr)
put_back_token(pc);
AstNode *fn_proto = ast_parse_fn_proto(pc);
if (fn_proto != nullptr) {
AstNode *body = ast_parse_block(pc);
@ -741,17 +751,12 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
static AstNode *ast_parse_var_decl(ParseContext *pc) {
Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst);
if (mut_kw == nullptr)
mut_kw = eat_token_if(pc, TokenIdKeywordVar);
if (mut_kw == nullptr) {
if (thread_local_kw == nullptr) {
return nullptr;
} else {
ast_invalid_token_error(pc, peek_token(pc));
}
}
if (mut_kw == nullptr)
return nullptr;
Token *identifier = expect_token(pc, TokenIdSymbol);
AstNode *type_expr = nullptr;
if (eat_token_if(pc, TokenIdColon) != nullptr)
@ -766,7 +771,6 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {
expect_token(pc, TokenIdSemicolon);
AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw);
res->data.variable_declaration.threadlocal_tok = thread_local_kw;
res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst;
res->data.variable_declaration.symbol = token_buf(identifier);
res->data.variable_declaration.type = type_expr;
@ -952,17 +956,10 @@ static AstNode *ast_parse_labeled_statement(ParseContext *pc) {
// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
static AstNode *ast_parse_loop_statement(ParseContext *pc) {
Token *label = ast_parse_block_label(pc);
Token *first = label;
Token *inline_token = eat_token_if(pc, TokenIdKeywordInline);
if (first == nullptr)
first = inline_token;
AstNode *for_statement = ast_parse_for_statement(pc);
if (for_statement != nullptr) {
assert(for_statement->type == NodeTypeForExpr);
for_statement->data.for_expr.name = token_buf(label);
for_statement->data.for_expr.is_inline = inline_token != nullptr;
return for_statement;
}
@ -970,12 +967,11 @@ static AstNode *ast_parse_loop_statement(ParseContext *pc) {
AstNode *while_statement = ast_parse_while_statement(pc);
if (while_statement != nullptr) {
assert(while_statement->type == NodeTypeWhileExpr);
while_statement->data.while_expr.name = token_buf(label);
while_statement->data.while_expr.is_inline = inline_token != nullptr;
return while_statement;
}
if (first != nullptr)
if (inline_token != nullptr)
ast_invalid_token_error(pc, peek_token(pc));
return nullptr;
}
@ -1117,7 +1113,7 @@ static AstNode *ast_parse_bool_and_expr(ParseContext *pc) {
// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
static AstNode *ast_parse_compare_expr(ParseContext *pc) {
return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_compare_op, ast_parse_bitwise_expr);
return ast_parse_bin_op_expr(pc, BinOpChainOnce, ast_parse_compare_op, ast_parse_bitwise_expr);
}
// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
@ -1162,10 +1158,6 @@ static AstNode *ast_parse_prefix_expr(ParseContext *pc) {
// / Block
// / CurlySuffixExpr
static AstNode *ast_parse_primary_expr(ParseContext *pc) {
AstNode *enum_lit = ast_parse_enum_lit(pc);
if (enum_lit != nullptr)
return enum_lit;
AstNode *asm_expr = ast_parse_asm_expr(pc);
if (asm_expr != nullptr)
return asm_expr;
@ -1246,11 +1238,8 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc) {
}
AstNode *block = ast_parse_block(pc);
if (block != nullptr) {
assert(block->type == NodeTypeBlock);
block->data.block.name = token_buf(label);
if (block != nullptr)
return block;
}
AstNode *curly_suffix = ast_parse_curly_suffix_expr(pc);
if (curly_suffix != nullptr)
@ -1503,6 +1492,7 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
// <- BUILTINIDENTIFIER FnCallArguments
// / CHAR_LITERAL
// / ContainerDecl
// / DOT IDENTIFIER
// / ErrorSetDecl
// / FLOAT
// / FnProto
@ -1563,6 +1553,10 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) {
if (container_decl != nullptr)
return container_decl;
AstNode *enum_lit = ast_parse_enum_lit(pc);
if (enum_lit != nullptr)
return enum_lit;
AstNode *error_set_decl = ast_parse_error_set_decl(pc);
if (error_set_decl != nullptr)
return error_set_decl;
@ -1672,32 +1666,26 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) {
// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
static AstNode *ast_parse_container_decl(ParseContext *pc) {
Token *extern_token = eat_token_if(pc, TokenIdKeywordExtern);
if (extern_token != nullptr) {
AstNode *res = ast_parse_container_decl_auto(pc);
if (res == nullptr) {
Token *layout_token = eat_token_if(pc, TokenIdKeywordExtern);
if (layout_token == nullptr)
layout_token = eat_token_if(pc, TokenIdKeywordPacked);
AstNode *res = ast_parse_container_decl_auto(pc);
if (res == nullptr) {
if (layout_token != nullptr)
put_back_token(pc);
return nullptr;
}
assert(res->type == NodeTypeContainerDecl);
res->line = extern_token->start_line;
res->column = extern_token->start_column;
res->data.container_decl.layout = ContainerLayoutExtern;
return res;
return nullptr;
}
Token *packed_token = eat_token_if(pc, TokenIdKeywordPacked);
if (packed_token != nullptr) {
AstNode *res = ast_expect(pc, ast_parse_container_decl_auto);
assert(res->type == NodeTypeContainerDecl);
res->line = packed_token->start_line;
res->column = packed_token->start_column;
res->data.container_decl.layout = ContainerLayoutPacked;
return res;
assert(res->type == NodeTypeContainerDecl);
if (layout_token != nullptr) {
res->line = layout_token->start_line;
res->column = layout_token->start_column;
res->data.container_decl.layout = layout_token->id == TokenIdKeywordExtern
? ContainerLayoutExtern
: ContainerLayoutPacked;
}
return ast_parse_container_decl_auto(pc);
return res;
}
// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
@ -1971,7 +1959,14 @@ static AstNode *ast_parse_field_init(ParseContext *pc) {
return nullptr;
Token *name = expect_token(pc, TokenIdSymbol);
expect_token(pc, TokenIdEq);
if (eat_token_if(pc, TokenIdEq) == nullptr) {
// Because ".Name" can also be intepreted as an enum literal, we should put back
// those two tokens again so that the parser can try to parse them as the enum
// literal later.
put_back_token(pc);
put_back_token(pc);
return nullptr;
}
AstNode *expr = ast_expect(pc, ast_parse_expr);
AstNode *res = ast_create_node(pc, NodeTypeStructValueField, first);
@ -2750,12 +2745,19 @@ static AstNode *ast_parse_container_decl_auto(ParseContext *pc) {
}
// ContainerDeclType
// <- (KEYWORD_struct / KEYWORD_enum) (LPAREN Expr RPAREN)?
// <- KEYWORD_struct
// / KEYWORD_enum (LPAREN Expr RPAREN)?
// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
static AstNode *ast_parse_container_decl_type(ParseContext *pc) {
Token *first = eat_token_if(pc, TokenIdKeywordStruct);
if (first == nullptr)
first = eat_token_if(pc, TokenIdKeywordEnum);
if (first != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first);
res->data.container_decl.init_arg_expr = nullptr;
res->data.container_decl.kind = ContainerKindStruct;
return res;
}
first = eat_token_if(pc, TokenIdKeywordEnum);
if (first != nullptr) {
AstNode *init_arg_expr = nullptr;
if (eat_token_if(pc, TokenIdLParen) != nullptr) {
@ -2764,9 +2766,7 @@ static AstNode *ast_parse_container_decl_type(ParseContext *pc) {
}
AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first);
res->data.container_decl.init_arg_expr = init_arg_expr;
res->data.container_decl.kind = first->id == TokenIdKeywordStruct
? ContainerKindStruct
: ContainerKindEnum;
res->data.container_decl.kind = ContainerKindEnum;
return res;
}

View File

@ -894,10 +894,25 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
case CIntTypeCount:
zig_unreachable();
}
case OsIOS:
switch (id) {
case CIntTypeShort:
case CIntTypeUShort:
return 16;
case CIntTypeInt:
case CIntTypeUInt:
return 32;
case CIntTypeLong:
case CIntTypeULong:
case CIntTypeLongLong:
case CIntTypeULongLong:
return 64;
case CIntTypeCount:
zig_unreachable();
}
case OsAnanas:
case OsCloudABI:
case OsDragonFly:
case OsIOS:
case OsKFreeBSD:
case OsLv2:
case OsSolaris:
@ -950,6 +965,8 @@ const char *target_exe_file_ext(const ZigTarget *target) {
return ".exe";
} else if (target->os == OsUefi) {
return ".efi";
} else if (target_is_wasm(target)) {
return ".wasm";
} else {
return "";
}
@ -1350,6 +1367,14 @@ bool target_is_musl(const ZigTarget *target) {
return target->os == OsLinux && target_abi_is_musl(target->abi);
}
bool target_is_wasm(const ZigTarget *target) {
return target->arch == ZigLLVM_wasm32 || target->arch == ZigLLVM_wasm64;
}
bool target_is_single_threaded(const ZigTarget *target) {
return target_is_wasm(target);
}
ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) {
switch (os) {
case OsFreestanding:

View File

@ -170,6 +170,8 @@ bool target_abi_is_gnu(ZigLLVM_EnvironmentType abi);
bool target_abi_is_musl(ZigLLVM_EnvironmentType abi);
bool target_is_glibc(const ZigTarget *target);
bool target_is_musl(const ZigTarget *target);
bool target_is_wasm(const ZigTarget *target);
bool target_is_single_threaded(const ZigTarget *target);
uint32_t target_arch_pointer_bit_width(ZigLLVM_ArchType arch);

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,9 @@
#include "all_types.hpp"
Error parse_h_file(AstNode **out_root_node, ZigList<ErrorMsg *> *errors, const char *target_file,
CodeGen *codegen, Buf *tmp_dep_file);
Error parse_h_file(CodeGen *codegen, AstNode **out_root_node,
Stage2ErrorMsg **errors_ptr, size_t *errors_len,
const char **args_begin, const char **args_end,
Stage2TranslateMode mode, const char *resources_path);
#endif

44
src/userland.cpp Normal file
View File

@ -0,0 +1,44 @@
// This file is a shim for zig1. The real implementations of these are in
// src-self-hosted/stage1.zig
#include "userland.h"
#include "ast_render.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Error stage2_translate_c(struct Stage2Ast **out_ast,
struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len,
const char **args_begin, const char **args_end, enum Stage2TranslateMode mode,
const char *resources_path)
{
const char *msg = "stage0 called stage2_translate_c";
stage2_panic(msg, strlen(msg));
}
void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len) {
const char *msg = "stage0 called stage2_free_clang_errors";
stage2_panic(msg, strlen(msg));
}
void stage2_zen(const char **ptr, size_t *len) {
const char *msg = "stage0 called stage2_zen";
stage2_panic(msg, strlen(msg));
}
void stage2_panic(const char *ptr, size_t len) {
fwrite(ptr, 1, len, stderr);
fprintf(stderr, "\n");
fflush(stderr);
abort();
}
void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file) {
const char *msg = "stage0 called stage2_render_ast";
stage2_panic(msg, strlen(msg));
}
int stage2_fmt(int argc, char **argv) {
const char *msg = "stage0 called stage2_fmt";
stage2_panic(msg, strlen(msg));
}

120
src/userland.h Normal file
View File

@ -0,0 +1,120 @@
/*
* Copyright (c) 2019 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef ZIG_USERLAND_H
#define ZIG_USERLAND_H
#include <stddef.h>
#include <stdio.h>
#ifdef __cplusplus
#define ZIG_EXTERN_C extern "C"
#else
#define ZIG_EXTERN_C
#endif
#if defined(_MSC_VER)
#define ZIG_ATTRIBUTE_NORETURN __declspec(noreturn)
#else
#define ZIG_ATTRIBUTE_NORETURN __attribute__((noreturn))
#endif
// ABI warning: the types and declarations in this file must match both those in
// userland.cpp and src-self-hosted/stage1.zig.
// ABI warning
enum Error {
ErrorNone,
ErrorNoMem,
ErrorInvalidFormat,
ErrorSemanticAnalyzeFail,
ErrorAccess,
ErrorInterrupted,
ErrorSystemResources,
ErrorFileNotFound,
ErrorFileSystem,
ErrorFileTooBig,
ErrorDivByZero,
ErrorOverflow,
ErrorPathAlreadyExists,
ErrorUnexpected,
ErrorExactDivRemainder,
ErrorNegativeDenominator,
ErrorShiftedOutOneBits,
ErrorCCompileErrors,
ErrorEndOfFile,
ErrorIsDir,
ErrorNotDir,
ErrorUnsupportedOperatingSystem,
ErrorSharingViolation,
ErrorPipeBusy,
ErrorPrimitiveTypeNotFound,
ErrorCacheUnavailable,
ErrorPathTooLong,
ErrorCCompilerCannotFindFile,
ErrorReadingDepFile,
ErrorInvalidDepFile,
ErrorMissingArchitecture,
ErrorMissingOperatingSystem,
ErrorUnknownArchitecture,
ErrorUnknownOperatingSystem,
ErrorUnknownABI,
ErrorInvalidFilename,
ErrorDiskQuota,
ErrorDiskSpace,
ErrorUnexpectedWriteFailure,
ErrorUnexpectedSeekFailure,
ErrorUnexpectedFileTruncationFailure,
ErrorUnimplemented,
ErrorOperationAborted,
ErrorBrokenPipe,
ErrorNoSpaceLeft,
};
// ABI warning
enum Stage2TranslateMode {
Stage2TranslateModeImport,
Stage2TranslateModeTranslate,
};
// ABI warning
struct Stage2ErrorMsg {
const char *filename_ptr; // can be null
size_t filename_len;
const char *msg_ptr;
size_t msg_len;
const char *source; // valid until the ASTUnit is freed. can be null
unsigned line; // 0 based
unsigned column; // 0 based
unsigned offset; // byte offset into source
};
// ABI warning
struct Stage2Ast;
// ABI warning
ZIG_EXTERN_C enum Error stage2_translate_c(struct Stage2Ast **out_ast,
struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len,
const char **args_begin, const char **args_end, enum Stage2TranslateMode mode,
const char *resources_path);
// ABI warning
ZIG_EXTERN_C void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len);
// ABI warning
ZIG_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file);
// ABI warning
ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len);
// ABI warning
ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len);
// ABI warning
ZIG_EXTERN_C int stage2_fmt(int argc, char **argv);
#endif

View File

@ -10,17 +10,25 @@
#include <stdarg.h>
#include "util.hpp"
#include "userland.h"
void zig_panic(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
fflush(stderr);
va_end(ap);
stage2_panic(nullptr, 0);
abort();
}
void assert(bool ok) {
if (!ok) {
const char *msg = "Assertion failed. This is a bug in the Zig compiler.";
stage2_panic(msg, strlen(msg));
}
}
uint32_t int_hash(int i) {
return (uint32_t)(i % UINT32_MAX);
}

View File

@ -48,6 +48,10 @@ void zig_panic(const char *format, ...);
#define zig_unreachable() zig_panic("unreachable: %s:%s:%d", __FILE__, __func__, __LINE__)
// Assertions in stage1 are always on, and they call zig @panic.
#undef assert
void assert(bool ok);
#if defined(_MSC_VER)
static inline int clzll(unsigned long long mask) {
unsigned long lz;

File diff suppressed because it is too large Load Diff

View File

@ -8,14 +8,13 @@
#ifndef ZIG_ZIG_CLANG_H
#define ZIG_ZIG_CLANG_H
#ifdef __cplusplus
#define ZIG_EXTERN_C extern "C"
#else
#define ZIG_EXTERN_C
#endif
#include "userland.h"
#include <inttypes.h>
#include <stdbool.h>
// ATTENTION: If you modify this file, be sure to update the corresponding
// extern function declarations in the self-hosted compiler.
// extern function declarations in the self-hosted compiler file
// src-self-hosted/clang.zig.
struct ZigClangSourceLocation {
unsigned ID;
@ -25,7 +24,14 @@ struct ZigClangQualType {
void *ptr;
};
struct ZigClangAPValueLValueBase {
void *Ptr;
unsigned CallIndex;
unsigned Version;
};
struct ZigClangAPValue;
struct ZigClangAPSInt;
struct ZigClangASTContext;
struct ZigClangASTUnit;
struct ZigClangArraySubscriptExpr;
@ -82,10 +88,10 @@ struct ZigClangSkipFunctionBodiesScope;
struct ZigClangSourceManager;
struct ZigClangSourceRange;
struct ZigClangStmt;
struct ZigClangStorageClass;
struct ZigClangStringLiteral;
struct ZigClangStringRef;
struct ZigClangSwitchStmt;
struct ZigClangTagDecl;
struct ZigClangType;
struct ZigClangTypedefNameDecl;
struct ZigClangTypedefType;
@ -94,6 +100,9 @@ struct ZigClangUnaryOperator;
struct ZigClangValueDecl;
struct ZigClangVarDecl;
struct ZigClangWhileStmt;
struct ZigClangFunctionType;
typedef struct ZigClangStmt *const * ZigClangCompoundStmt_const_body_iterator;
enum ZigClangBO {
ZigClangBO_PtrMemD,
@ -148,112 +157,674 @@ enum ZigClangUO {
ZigClangUO_Coawait,
};
//struct ZigClangCC_AAPCS;
//struct ZigClangCC_AAPCS_VFP;
//struct ZigClangCC_C;
//struct ZigClangCC_IntelOclBicc;
//struct ZigClangCC_OpenCLKernel;
//struct ZigClangCC_PreserveAll;
//struct ZigClangCC_PreserveMost;
//struct ZigClangCC_SpirFunction;
//struct ZigClangCC_Swift;
//struct ZigClangCC_Win64;
//struct ZigClangCC_X86FastCall;
//struct ZigClangCC_X86Pascal;
//struct ZigClangCC_X86RegCall;
//struct ZigClangCC_X86StdCall;
//struct ZigClangCC_X86ThisCall;
//struct ZigClangCC_X86VectorCall;
//struct ZigClangCC_X86_64SysV;
enum ZigClangTypeClass {
ZigClangType_Builtin,
ZigClangType_Complex,
ZigClangType_Pointer,
ZigClangType_BlockPointer,
ZigClangType_LValueReference,
ZigClangType_RValueReference,
ZigClangType_MemberPointer,
ZigClangType_ConstantArray,
ZigClangType_IncompleteArray,
ZigClangType_VariableArray,
ZigClangType_DependentSizedArray,
ZigClangType_DependentSizedExtVector,
ZigClangType_DependentAddressSpace,
ZigClangType_Vector,
ZigClangType_DependentVector,
ZigClangType_ExtVector,
ZigClangType_FunctionProto,
ZigClangType_FunctionNoProto,
ZigClangType_UnresolvedUsing,
ZigClangType_Paren,
ZigClangType_Typedef,
ZigClangType_Adjusted,
ZigClangType_Decayed,
ZigClangType_TypeOfExpr,
ZigClangType_TypeOf,
ZigClangType_Decltype,
ZigClangType_UnaryTransform,
ZigClangType_Record,
ZigClangType_Enum,
ZigClangType_Elaborated,
ZigClangType_Attributed,
ZigClangType_TemplateTypeParm,
ZigClangType_SubstTemplateTypeParm,
ZigClangType_SubstTemplateTypeParmPack,
ZigClangType_TemplateSpecialization,
ZigClangType_Auto,
ZigClangType_DeducedTemplateSpecialization,
ZigClangType_InjectedClassName,
ZigClangType_DependentName,
ZigClangType_DependentTemplateSpecialization,
ZigClangType_PackExpansion,
ZigClangType_ObjCTypeParam,
ZigClangType_ObjCObject,
ZigClangType_ObjCInterface,
ZigClangType_ObjCObjectPointer,
ZigClangType_Pipe,
ZigClangType_Atomic,
};
//struct ZigClangCK_ARCConsumeObject;
//struct ZigClangCK_ARCExtendBlockObject;
//struct ZigClangCK_ARCProduceObject;
//struct ZigClangCK_ARCReclaimReturnedObject;
//struct ZigClangCK_AddressSpaceConversion;
//struct ZigClangCK_AnyPointerToBlockPointerCast;
//struct ZigClangCK_ArrayToPointerDecay;
//struct ZigClangCK_AtomicToNonAtomic;
//struct ZigClangCK_BaseToDerived;
//struct ZigClangCK_BaseToDerivedMemberPointer;
//struct ZigClangCK_BitCast;
//struct ZigClangCK_BlockPointerToObjCPointerCast;
//struct ZigClangCK_BooleanToSignedIntegral;
//struct ZigClangCK_BuiltinFnToFnPtr;
//struct ZigClangCK_CPointerToObjCPointerCast;
//struct ZigClangCK_ConstructorConversion;
//struct ZigClangCK_CopyAndAutoreleaseBlockObject;
//struct ZigClangCK_Dependent;
//struct ZigClangCK_DerivedToBase;
//struct ZigClangCK_DerivedToBaseMemberPointer;
//struct ZigClangCK_Dynamic;
//struct ZigClangCK_FloatingCast;
//struct ZigClangCK_FloatingComplexCast;
//struct ZigClangCK_FloatingComplexToBoolean;
//struct ZigClangCK_FloatingComplexToIntegralComplex;
//struct ZigClangCK_FloatingComplexToReal;
//struct ZigClangCK_FloatingRealToComplex;
//struct ZigClangCK_FloatingToBoolean;
//struct ZigClangCK_FloatingToIntegral;
//struct ZigClangCK_FunctionToPointerDecay;
//struct ZigClangCK_IntToOCLSampler;
//struct ZigClangCK_IntegralCast;
//struct ZigClangCK_IntegralComplexCast;
//struct ZigClangCK_IntegralComplexToBoolean;
//struct ZigClangCK_IntegralComplexToFloatingComplex;
//struct ZigClangCK_IntegralComplexToReal;
//struct ZigClangCK_IntegralRealToComplex;
//struct ZigClangCK_IntegralToBoolean;
//struct ZigClangCK_IntegralToFloating;
//struct ZigClangCK_IntegralToPointer;
//struct ZigClangCK_LValueBitCast;
//struct ZigClangCK_LValueToRValue;
//struct ZigClangCK_MemberPointerToBoolean;
//struct ZigClangCK_NoOp;
//struct ZigClangCK_NonAtomicToAtomic;
//struct ZigClangCK_NullToMemberPointer;
//struct ZigClangCK_NullToPointer;
//struct ZigClangCK_ObjCObjectLValueCast;
//struct ZigClangCK_PointerToBoolean;
//struct ZigClangCK_PointerToIntegral;
//struct ZigClangCK_ReinterpretMemberPointer;
//struct ZigClangCK_ToUnion;
//struct ZigClangCK_ToVoid;
//struct ZigClangCK_UncheckedDerivedToBase;
//struct ZigClangCK_UserDefinedConversion;
//struct ZigClangCK_VectorSplat;
//struct ZigClangCK_ZeroToOCLEvent;
//struct ZigClangCK_ZeroToOCLQueue;
enum ZigClangStmtClass {
ZigClangStmt_NoStmtClass = 0,
ZigClangStmt_GCCAsmStmtClass,
ZigClangStmt_MSAsmStmtClass,
ZigClangStmt_AttributedStmtClass,
ZigClangStmt_BreakStmtClass,
ZigClangStmt_CXXCatchStmtClass,
ZigClangStmt_CXXForRangeStmtClass,
ZigClangStmt_CXXTryStmtClass,
ZigClangStmt_CapturedStmtClass,
ZigClangStmt_CompoundStmtClass,
ZigClangStmt_ContinueStmtClass,
ZigClangStmt_CoreturnStmtClass,
ZigClangStmt_CoroutineBodyStmtClass,
ZigClangStmt_DeclStmtClass,
ZigClangStmt_DoStmtClass,
ZigClangStmt_BinaryConditionalOperatorClass,
ZigClangStmt_ConditionalOperatorClass,
ZigClangStmt_AddrLabelExprClass,
ZigClangStmt_ArrayInitIndexExprClass,
ZigClangStmt_ArrayInitLoopExprClass,
ZigClangStmt_ArraySubscriptExprClass,
ZigClangStmt_ArrayTypeTraitExprClass,
ZigClangStmt_AsTypeExprClass,
ZigClangStmt_AtomicExprClass,
ZigClangStmt_BinaryOperatorClass,
ZigClangStmt_CompoundAssignOperatorClass,
ZigClangStmt_BlockExprClass,
ZigClangStmt_CXXBindTemporaryExprClass,
ZigClangStmt_CXXBoolLiteralExprClass,
ZigClangStmt_CXXConstructExprClass,
ZigClangStmt_CXXTemporaryObjectExprClass,
ZigClangStmt_CXXDefaultArgExprClass,
ZigClangStmt_CXXDefaultInitExprClass,
ZigClangStmt_CXXDeleteExprClass,
ZigClangStmt_CXXDependentScopeMemberExprClass,
ZigClangStmt_CXXFoldExprClass,
ZigClangStmt_CXXInheritedCtorInitExprClass,
ZigClangStmt_CXXNewExprClass,
ZigClangStmt_CXXNoexceptExprClass,
ZigClangStmt_CXXNullPtrLiteralExprClass,
ZigClangStmt_CXXPseudoDestructorExprClass,
ZigClangStmt_CXXScalarValueInitExprClass,
ZigClangStmt_CXXStdInitializerListExprClass,
ZigClangStmt_CXXThisExprClass,
ZigClangStmt_CXXThrowExprClass,
ZigClangStmt_CXXTypeidExprClass,
ZigClangStmt_CXXUnresolvedConstructExprClass,
ZigClangStmt_CXXUuidofExprClass,
ZigClangStmt_CallExprClass,
ZigClangStmt_CUDAKernelCallExprClass,
ZigClangStmt_CXXMemberCallExprClass,
ZigClangStmt_CXXOperatorCallExprClass,
ZigClangStmt_UserDefinedLiteralClass,
ZigClangStmt_CStyleCastExprClass,
ZigClangStmt_CXXFunctionalCastExprClass,
ZigClangStmt_CXXConstCastExprClass,
ZigClangStmt_CXXDynamicCastExprClass,
ZigClangStmt_CXXReinterpretCastExprClass,
ZigClangStmt_CXXStaticCastExprClass,
ZigClangStmt_ObjCBridgedCastExprClass,
ZigClangStmt_ImplicitCastExprClass,
ZigClangStmt_CharacterLiteralClass,
ZigClangStmt_ChooseExprClass,
ZigClangStmt_CompoundLiteralExprClass,
ZigClangStmt_ConvertVectorExprClass,
ZigClangStmt_CoawaitExprClass,
ZigClangStmt_CoyieldExprClass,
ZigClangStmt_DeclRefExprClass,
ZigClangStmt_DependentCoawaitExprClass,
ZigClangStmt_DependentScopeDeclRefExprClass,
ZigClangStmt_DesignatedInitExprClass,
ZigClangStmt_DesignatedInitUpdateExprClass,
ZigClangStmt_ExpressionTraitExprClass,
ZigClangStmt_ExtVectorElementExprClass,
ZigClangStmt_FixedPointLiteralClass,
ZigClangStmt_FloatingLiteralClass,
ZigClangStmt_ConstantExprClass,
ZigClangStmt_ExprWithCleanupsClass,
ZigClangStmt_FunctionParmPackExprClass,
ZigClangStmt_GNUNullExprClass,
ZigClangStmt_GenericSelectionExprClass,
ZigClangStmt_ImaginaryLiteralClass,
ZigClangStmt_ImplicitValueInitExprClass,
ZigClangStmt_InitListExprClass,
ZigClangStmt_IntegerLiteralClass,
ZigClangStmt_LambdaExprClass,
ZigClangStmt_MSPropertyRefExprClass,
ZigClangStmt_MSPropertySubscriptExprClass,
ZigClangStmt_MaterializeTemporaryExprClass,
ZigClangStmt_MemberExprClass,
ZigClangStmt_NoInitExprClass,
ZigClangStmt_OMPArraySectionExprClass,
ZigClangStmt_ObjCArrayLiteralClass,
ZigClangStmt_ObjCAvailabilityCheckExprClass,
ZigClangStmt_ObjCBoolLiteralExprClass,
ZigClangStmt_ObjCBoxedExprClass,
ZigClangStmt_ObjCDictionaryLiteralClass,
ZigClangStmt_ObjCEncodeExprClass,
ZigClangStmt_ObjCIndirectCopyRestoreExprClass,
ZigClangStmt_ObjCIsaExprClass,
ZigClangStmt_ObjCIvarRefExprClass,
ZigClangStmt_ObjCMessageExprClass,
ZigClangStmt_ObjCPropertyRefExprClass,
ZigClangStmt_ObjCProtocolExprClass,
ZigClangStmt_ObjCSelectorExprClass,
ZigClangStmt_ObjCStringLiteralClass,
ZigClangStmt_ObjCSubscriptRefExprClass,
ZigClangStmt_OffsetOfExprClass,
ZigClangStmt_OpaqueValueExprClass,
ZigClangStmt_UnresolvedLookupExprClass,
ZigClangStmt_UnresolvedMemberExprClass,
ZigClangStmt_PackExpansionExprClass,
ZigClangStmt_ParenExprClass,
ZigClangStmt_ParenListExprClass,
ZigClangStmt_PredefinedExprClass,
ZigClangStmt_PseudoObjectExprClass,
ZigClangStmt_ShuffleVectorExprClass,
ZigClangStmt_SizeOfPackExprClass,
ZigClangStmt_StmtExprClass,
ZigClangStmt_StringLiteralClass,
ZigClangStmt_SubstNonTypeTemplateParmExprClass,
ZigClangStmt_SubstNonTypeTemplateParmPackExprClass,
ZigClangStmt_TypeTraitExprClass,
ZigClangStmt_TypoExprClass,
ZigClangStmt_UnaryExprOrTypeTraitExprClass,
ZigClangStmt_UnaryOperatorClass,
ZigClangStmt_VAArgExprClass,
ZigClangStmt_ForStmtClass,
ZigClangStmt_GotoStmtClass,
ZigClangStmt_IfStmtClass,
ZigClangStmt_IndirectGotoStmtClass,
ZigClangStmt_LabelStmtClass,
ZigClangStmt_MSDependentExistsStmtClass,
ZigClangStmt_NullStmtClass,
ZigClangStmt_OMPAtomicDirectiveClass,
ZigClangStmt_OMPBarrierDirectiveClass,
ZigClangStmt_OMPCancelDirectiveClass,
ZigClangStmt_OMPCancellationPointDirectiveClass,
ZigClangStmt_OMPCriticalDirectiveClass,
ZigClangStmt_OMPFlushDirectiveClass,
ZigClangStmt_OMPDistributeDirectiveClass,
ZigClangStmt_OMPDistributeParallelForDirectiveClass,
ZigClangStmt_OMPDistributeParallelForSimdDirectiveClass,
ZigClangStmt_OMPDistributeSimdDirectiveClass,
ZigClangStmt_OMPForDirectiveClass,
ZigClangStmt_OMPForSimdDirectiveClass,
ZigClangStmt_OMPParallelForDirectiveClass,
ZigClangStmt_OMPParallelForSimdDirectiveClass,
ZigClangStmt_OMPSimdDirectiveClass,
ZigClangStmt_OMPTargetParallelForSimdDirectiveClass,
ZigClangStmt_OMPTargetSimdDirectiveClass,
ZigClangStmt_OMPTargetTeamsDistributeDirectiveClass,
ZigClangStmt_OMPTargetTeamsDistributeParallelForDirectiveClass,
ZigClangStmt_OMPTargetTeamsDistributeParallelForSimdDirectiveClass,
ZigClangStmt_OMPTargetTeamsDistributeSimdDirectiveClass,
ZigClangStmt_OMPTaskLoopDirectiveClass,
ZigClangStmt_OMPTaskLoopSimdDirectiveClass,
ZigClangStmt_OMPTeamsDistributeDirectiveClass,
ZigClangStmt_OMPTeamsDistributeParallelForDirectiveClass,
ZigClangStmt_OMPTeamsDistributeParallelForSimdDirectiveClass,
ZigClangStmt_OMPTeamsDistributeSimdDirectiveClass,
ZigClangStmt_OMPMasterDirectiveClass,
ZigClangStmt_OMPOrderedDirectiveClass,
ZigClangStmt_OMPParallelDirectiveClass,
ZigClangStmt_OMPParallelSectionsDirectiveClass,
ZigClangStmt_OMPSectionDirectiveClass,
ZigClangStmt_OMPSectionsDirectiveClass,
ZigClangStmt_OMPSingleDirectiveClass,
ZigClangStmt_OMPTargetDataDirectiveClass,
ZigClangStmt_OMPTargetDirectiveClass,
ZigClangStmt_OMPTargetEnterDataDirectiveClass,
ZigClangStmt_OMPTargetExitDataDirectiveClass,
ZigClangStmt_OMPTargetParallelDirectiveClass,
ZigClangStmt_OMPTargetParallelForDirectiveClass,
ZigClangStmt_OMPTargetTeamsDirectiveClass,
ZigClangStmt_OMPTargetUpdateDirectiveClass,
ZigClangStmt_OMPTaskDirectiveClass,
ZigClangStmt_OMPTaskgroupDirectiveClass,
ZigClangStmt_OMPTaskwaitDirectiveClass,
ZigClangStmt_OMPTaskyieldDirectiveClass,
ZigClangStmt_OMPTeamsDirectiveClass,
ZigClangStmt_ObjCAtCatchStmtClass,
ZigClangStmt_ObjCAtFinallyStmtClass,
ZigClangStmt_ObjCAtSynchronizedStmtClass,
ZigClangStmt_ObjCAtThrowStmtClass,
ZigClangStmt_ObjCAtTryStmtClass,
ZigClangStmt_ObjCAutoreleasePoolStmtClass,
ZigClangStmt_ObjCForCollectionStmtClass,
ZigClangStmt_ReturnStmtClass,
ZigClangStmt_SEHExceptStmtClass,
ZigClangStmt_SEHFinallyStmtClass,
ZigClangStmt_SEHLeaveStmtClass,
ZigClangStmt_SEHTryStmtClass,
ZigClangStmt_CaseStmtClass,
ZigClangStmt_DefaultStmtClass,
ZigClangStmt_SwitchStmtClass,
ZigClangStmt_WhileStmtClass,
};
//struct ZigClangETK_Class;
//struct ZigClangETK_Enum;
//struct ZigClangETK_Interface;
//struct ZigClangETK_None;
//struct ZigClangETK_Struct;
//struct ZigClangETK_Typename;
//struct ZigClangETK_Union;
enum ZigClangCK {
ZigClangCK_Dependent,
ZigClangCK_BitCast,
ZigClangCK_LValueBitCast,
ZigClangCK_LValueToRValue,
ZigClangCK_NoOp,
ZigClangCK_BaseToDerived,
ZigClangCK_DerivedToBase,
ZigClangCK_UncheckedDerivedToBase,
ZigClangCK_Dynamic,
ZigClangCK_ToUnion,
ZigClangCK_ArrayToPointerDecay,
ZigClangCK_FunctionToPointerDecay,
ZigClangCK_NullToPointer,
ZigClangCK_NullToMemberPointer,
ZigClangCK_BaseToDerivedMemberPointer,
ZigClangCK_DerivedToBaseMemberPointer,
ZigClangCK_MemberPointerToBoolean,
ZigClangCK_ReinterpretMemberPointer,
ZigClangCK_UserDefinedConversion,
ZigClangCK_ConstructorConversion,
ZigClangCK_IntegralToPointer,
ZigClangCK_PointerToIntegral,
ZigClangCK_PointerToBoolean,
ZigClangCK_ToVoid,
ZigClangCK_VectorSplat,
ZigClangCK_IntegralCast,
ZigClangCK_IntegralToBoolean,
ZigClangCK_IntegralToFloating,
ZigClangCK_FixedPointCast,
ZigClangCK_FixedPointToBoolean,
ZigClangCK_FloatingToIntegral,
ZigClangCK_FloatingToBoolean,
ZigClangCK_BooleanToSignedIntegral,
ZigClangCK_FloatingCast,
ZigClangCK_CPointerToObjCPointerCast,
ZigClangCK_BlockPointerToObjCPointerCast,
ZigClangCK_AnyPointerToBlockPointerCast,
ZigClangCK_ObjCObjectLValueCast,
ZigClangCK_FloatingRealToComplex,
ZigClangCK_FloatingComplexToReal,
ZigClangCK_FloatingComplexToBoolean,
ZigClangCK_FloatingComplexCast,
ZigClangCK_FloatingComplexToIntegralComplex,
ZigClangCK_IntegralRealToComplex,
ZigClangCK_IntegralComplexToReal,
ZigClangCK_IntegralComplexToBoolean,
ZigClangCK_IntegralComplexCast,
ZigClangCK_IntegralComplexToFloatingComplex,
ZigClangCK_ARCProduceObject,
ZigClangCK_ARCConsumeObject,
ZigClangCK_ARCReclaimReturnedObject,
ZigClangCK_ARCExtendBlockObject,
ZigClangCK_AtomicToNonAtomic,
ZigClangCK_NonAtomicToAtomic,
ZigClangCK_CopyAndAutoreleaseBlockObject,
ZigClangCK_BuiltinFnToFnPtr,
ZigClangCK_ZeroToOCLOpaqueType,
ZigClangCK_AddressSpaceConversion,
ZigClangCK_IntToOCLSampler,
};
//struct ZigClangSC_None;
//struct ZigClangSC_PrivateExtern;
//struct ZigClangSC_Static;
enum ZigClangAPValueKind {
ZigClangAPValueUninitialized,
ZigClangAPValueInt,
ZigClangAPValueFloat,
ZigClangAPValueComplexInt,
ZigClangAPValueComplexFloat,
ZigClangAPValueLValue,
ZigClangAPValueVector,
ZigClangAPValueArray,
ZigClangAPValueStruct,
ZigClangAPValueUnion,
ZigClangAPValueMemberPointer,
ZigClangAPValueAddrLabelDiff,
};
//struct ZigClangTU_Complete;
enum ZigClangDeclKind {
ZigClangDeclAccessSpec,
ZigClangDeclBlock,
ZigClangDeclCaptured,
ZigClangDeclClassScopeFunctionSpecialization,
ZigClangDeclEmpty,
ZigClangDeclExport,
ZigClangDeclExternCContext,
ZigClangDeclFileScopeAsm,
ZigClangDeclFriend,
ZigClangDeclFriendTemplate,
ZigClangDeclImport,
ZigClangDeclLinkageSpec,
ZigClangDeclLabel,
ZigClangDeclNamespace,
ZigClangDeclNamespaceAlias,
ZigClangDeclObjCCompatibleAlias,
ZigClangDeclObjCCategory,
ZigClangDeclObjCCategoryImpl,
ZigClangDeclObjCImplementation,
ZigClangDeclObjCInterface,
ZigClangDeclObjCProtocol,
ZigClangDeclObjCMethod,
ZigClangDeclObjCProperty,
ZigClangDeclBuiltinTemplate,
ZigClangDeclClassTemplate,
ZigClangDeclFunctionTemplate,
ZigClangDeclTypeAliasTemplate,
ZigClangDeclVarTemplate,
ZigClangDeclTemplateTemplateParm,
ZigClangDeclEnum,
ZigClangDeclRecord,
ZigClangDeclCXXRecord,
ZigClangDeclClassTemplateSpecialization,
ZigClangDeclClassTemplatePartialSpecialization,
ZigClangDeclTemplateTypeParm,
ZigClangDeclObjCTypeParam,
ZigClangDeclTypeAlias,
ZigClangDeclTypedef,
ZigClangDeclUnresolvedUsingTypename,
ZigClangDeclUsing,
ZigClangDeclUsingDirective,
ZigClangDeclUsingPack,
ZigClangDeclUsingShadow,
ZigClangDeclConstructorUsingShadow,
ZigClangDeclBinding,
ZigClangDeclField,
ZigClangDeclObjCAtDefsField,
ZigClangDeclObjCIvar,
ZigClangDeclFunction,
ZigClangDeclCXXDeductionGuide,
ZigClangDeclCXXMethod,
ZigClangDeclCXXConstructor,
ZigClangDeclCXXConversion,
ZigClangDeclCXXDestructor,
ZigClangDeclMSProperty,
ZigClangDeclNonTypeTemplateParm,
ZigClangDeclVar,
ZigClangDeclDecomposition,
ZigClangDeclImplicitParam,
ZigClangDeclOMPCapturedExpr,
ZigClangDeclParmVar,
ZigClangDeclVarTemplateSpecialization,
ZigClangDeclVarTemplatePartialSpecialization,
ZigClangDeclEnumConstant,
ZigClangDeclIndirectField,
ZigClangDeclOMPDeclareReduction,
ZigClangDeclUnresolvedUsingValue,
ZigClangDeclOMPRequires,
ZigClangDeclOMPThreadPrivate,
ZigClangDeclObjCPropertyImpl,
ZigClangDeclPragmaComment,
ZigClangDeclPragmaDetectMismatch,
ZigClangDeclStaticAssert,
ZigClangDeclTranslationUnit,
};
ZIG_EXTERN_C ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const ZigClangSourceManager *,
ZigClangSourceLocation Loc);
ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const ZigClangSourceManager *,
ZigClangSourceLocation SpellingLoc);
ZIG_EXTERN_C unsigned ZigClangSourceManager_getSpellingLineNumber(const ZigClangSourceManager *,
ZigClangSourceLocation Loc);
ZIG_EXTERN_C unsigned ZigClangSourceManager_getSpellingColumnNumber(const ZigClangSourceManager *,
ZigClangSourceLocation Loc);
ZIG_EXTERN_C const char* ZigClangSourceManager_getCharacterData(const ZigClangSourceManager *,
ZigClangSourceLocation SL);
enum ZigClangBuiltinTypeKind {
ZigClangBuiltinTypeOCLImage1dRO,
ZigClangBuiltinTypeOCLImage1dArrayRO,
ZigClangBuiltinTypeOCLImage1dBufferRO,
ZigClangBuiltinTypeOCLImage2dRO,
ZigClangBuiltinTypeOCLImage2dArrayRO,
ZigClangBuiltinTypeOCLImage2dDepthRO,
ZigClangBuiltinTypeOCLImage2dArrayDepthRO,
ZigClangBuiltinTypeOCLImage2dMSAARO,
ZigClangBuiltinTypeOCLImage2dArrayMSAARO,
ZigClangBuiltinTypeOCLImage2dMSAADepthRO,
ZigClangBuiltinTypeOCLImage2dArrayMSAADepthRO,
ZigClangBuiltinTypeOCLImage3dRO,
ZigClangBuiltinTypeOCLImage1dWO,
ZigClangBuiltinTypeOCLImage1dArrayWO,
ZigClangBuiltinTypeOCLImage1dBufferWO,
ZigClangBuiltinTypeOCLImage2dWO,
ZigClangBuiltinTypeOCLImage2dArrayWO,
ZigClangBuiltinTypeOCLImage2dDepthWO,
ZigClangBuiltinTypeOCLImage2dArrayDepthWO,
ZigClangBuiltinTypeOCLImage2dMSAAWO,
ZigClangBuiltinTypeOCLImage2dArrayMSAAWO,
ZigClangBuiltinTypeOCLImage2dMSAADepthWO,
ZigClangBuiltinTypeOCLImage2dArrayMSAADepthWO,
ZigClangBuiltinTypeOCLImage3dWO,
ZigClangBuiltinTypeOCLImage1dRW,
ZigClangBuiltinTypeOCLImage1dArrayRW,
ZigClangBuiltinTypeOCLImage1dBufferRW,
ZigClangBuiltinTypeOCLImage2dRW,
ZigClangBuiltinTypeOCLImage2dArrayRW,
ZigClangBuiltinTypeOCLImage2dDepthRW,
ZigClangBuiltinTypeOCLImage2dArrayDepthRW,
ZigClangBuiltinTypeOCLImage2dMSAARW,
ZigClangBuiltinTypeOCLImage2dArrayMSAARW,
ZigClangBuiltinTypeOCLImage2dMSAADepthRW,
ZigClangBuiltinTypeOCLImage2dArrayMSAADepthRW,
ZigClangBuiltinTypeOCLImage3dRW,
ZigClangBuiltinTypeOCLIntelSubgroupAVCMcePayload,
ZigClangBuiltinTypeOCLIntelSubgroupAVCImePayload,
ZigClangBuiltinTypeOCLIntelSubgroupAVCRefPayload,
ZigClangBuiltinTypeOCLIntelSubgroupAVCSicPayload,
ZigClangBuiltinTypeOCLIntelSubgroupAVCMceResult,
ZigClangBuiltinTypeOCLIntelSubgroupAVCImeResult,
ZigClangBuiltinTypeOCLIntelSubgroupAVCRefResult,
ZigClangBuiltinTypeOCLIntelSubgroupAVCSicResult,
ZigClangBuiltinTypeOCLIntelSubgroupAVCImeResultSingleRefStreamout,
ZigClangBuiltinTypeOCLIntelSubgroupAVCImeResultDualRefStreamout,
ZigClangBuiltinTypeOCLIntelSubgroupAVCImeSingleRefStreamin,
ZigClangBuiltinTypeOCLIntelSubgroupAVCImeDualRefStreamin,
ZigClangBuiltinTypeVoid,
ZigClangBuiltinTypeBool,
ZigClangBuiltinTypeChar_U,
ZigClangBuiltinTypeUChar,
ZigClangBuiltinTypeWChar_U,
ZigClangBuiltinTypeChar8,
ZigClangBuiltinTypeChar16,
ZigClangBuiltinTypeChar32,
ZigClangBuiltinTypeUShort,
ZigClangBuiltinTypeUInt,
ZigClangBuiltinTypeULong,
ZigClangBuiltinTypeULongLong,
ZigClangBuiltinTypeUInt128,
ZigClangBuiltinTypeChar_S,
ZigClangBuiltinTypeSChar,
ZigClangBuiltinTypeWChar_S,
ZigClangBuiltinTypeShort,
ZigClangBuiltinTypeInt,
ZigClangBuiltinTypeLong,
ZigClangBuiltinTypeLongLong,
ZigClangBuiltinTypeInt128,
ZigClangBuiltinTypeShortAccum,
ZigClangBuiltinTypeAccum,
ZigClangBuiltinTypeLongAccum,
ZigClangBuiltinTypeUShortAccum,
ZigClangBuiltinTypeUAccum,
ZigClangBuiltinTypeULongAccum,
ZigClangBuiltinTypeShortFract,
ZigClangBuiltinTypeFract,
ZigClangBuiltinTypeLongFract,
ZigClangBuiltinTypeUShortFract,
ZigClangBuiltinTypeUFract,
ZigClangBuiltinTypeULongFract,
ZigClangBuiltinTypeSatShortAccum,
ZigClangBuiltinTypeSatAccum,
ZigClangBuiltinTypeSatLongAccum,
ZigClangBuiltinTypeSatUShortAccum,
ZigClangBuiltinTypeSatUAccum,
ZigClangBuiltinTypeSatULongAccum,
ZigClangBuiltinTypeSatShortFract,
ZigClangBuiltinTypeSatFract,
ZigClangBuiltinTypeSatLongFract,
ZigClangBuiltinTypeSatUShortFract,
ZigClangBuiltinTypeSatUFract,
ZigClangBuiltinTypeSatULongFract,
ZigClangBuiltinTypeHalf,
ZigClangBuiltinTypeFloat,
ZigClangBuiltinTypeDouble,
ZigClangBuiltinTypeLongDouble,
ZigClangBuiltinTypeFloat16,
ZigClangBuiltinTypeFloat128,
ZigClangBuiltinTypeNullPtr,
ZigClangBuiltinTypeObjCId,
ZigClangBuiltinTypeObjCClass,
ZigClangBuiltinTypeObjCSel,
ZigClangBuiltinTypeOCLSampler,
ZigClangBuiltinTypeOCLEvent,
ZigClangBuiltinTypeOCLClkEvent,
ZigClangBuiltinTypeOCLQueue,
ZigClangBuiltinTypeOCLReserveID,
ZigClangBuiltinTypeDependent,
ZigClangBuiltinTypeOverload,
ZigClangBuiltinTypeBoundMember,
ZigClangBuiltinTypePseudoObject,
ZigClangBuiltinTypeUnknownAny,
ZigClangBuiltinTypeBuiltinFn,
ZigClangBuiltinTypeARCUnbridgedCast,
ZigClangBuiltinTypeOMPArraySection,
};
ZIG_EXTERN_C ZigClangQualType ZigClangASTContext_getPointerType(const ZigClangASTContext*, ZigClangQualType T);
enum ZigClangCallingConv {
ZigClangCallingConv_C, // __attribute__((cdecl))
ZigClangCallingConv_X86StdCall, // __attribute__((stdcall))
ZigClangCallingConv_X86FastCall, // __attribute__((fastcall))
ZigClangCallingConv_X86ThisCall, // __attribute__((thiscall))
ZigClangCallingConv_X86VectorCall, // __attribute__((vectorcall))
ZigClangCallingConv_X86Pascal, // __attribute__((pascal))
ZigClangCallingConv_Win64, // __attribute__((ms_abi))
ZigClangCallingConv_X86_64SysV, // __attribute__((sysv_abi))
ZigClangCallingConv_X86RegCall, // __attribute__((regcall))
ZigClangCallingConv_AAPCS, // __attribute__((pcs("aapcs")))
ZigClangCallingConv_AAPCS_VFP, // __attribute__((pcs("aapcs-vfp")))
ZigClangCallingConv_IntelOclBicc, // __attribute__((intel_ocl_bicc))
ZigClangCallingConv_SpirFunction, // default for OpenCL functions on SPIR target
ZigClangCallingConv_OpenCLKernel, // inferred for OpenCL kernels
ZigClangCallingConv_Swift, // __attribute__((swiftcall))
ZigClangCallingConv_PreserveMost, // __attribute__((preserve_most))
ZigClangCallingConv_PreserveAll, // __attribute__((preserve_all))
ZigClangCallingConv_AArch64VectorCall, // __attribute__((aarch64_vector_pcs))
};
enum ZigClangStorageClass {
// These are legal on both functions and variables.
ZigClangStorageClass_None,
ZigClangStorageClass_Extern,
ZigClangStorageClass_Static,
ZigClangStorageClass_PrivateExtern,
// These are only legal on variables.
ZigClangStorageClass_Auto,
ZigClangStorageClass_Register,
};
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const struct ZigClangSourceManager *,
struct ZigClangSourceLocation Loc);
ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const struct ZigClangSourceManager *,
struct ZigClangSourceLocation SpellingLoc);
ZIG_EXTERN_C unsigned ZigClangSourceManager_getSpellingLineNumber(const struct ZigClangSourceManager *,
struct ZigClangSourceLocation Loc);
ZIG_EXTERN_C unsigned ZigClangSourceManager_getSpellingColumnNumber(const struct ZigClangSourceManager *,
struct ZigClangSourceLocation Loc);
ZIG_EXTERN_C const char* ZigClangSourceManager_getCharacterData(const struct ZigClangSourceManager *,
struct ZigClangSourceLocation SL);
ZIG_EXTERN_C struct ZigClangQualType ZigClangASTContext_getPointerType(const struct ZigClangASTContext*, struct ZigClangQualType T);
// Can return null.
ZIG_EXTERN_C struct ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char **args_end,
struct Stage2ErrorMsg **errors_ptr, size_t *errors_len, const char *resources_path);
ZIG_EXTERN_C void ZigClangASTUnit_delete(struct ZigClangASTUnit *);
ZIG_EXTERN_C void ZigClangErrorMsg_delete(struct Stage2ErrorMsg *ptr, size_t len);
ZIG_EXTERN_C struct ZigClangASTContext *ZigClangASTUnit_getASTContext(struct ZigClangASTUnit *);
ZIG_EXTERN_C struct ZigClangSourceManager *ZigClangASTUnit_getSourceManager(struct ZigClangASTUnit *);
ZIG_EXTERN_C bool ZigClangASTUnit_visitLocalTopLevelDecls(struct ZigClangASTUnit *, void *context,
bool (*Fn)(void *context, const struct ZigClangDecl *decl));
ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangRecordType_getDecl(const struct ZigClangRecordType *record_ty);
ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumType_getDecl(const struct ZigClangEnumType *record_ty);
ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangRecordDecl_getCanonicalDecl(const struct ZigClangRecordDecl *record_decl);
ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangEnumDecl_getCanonicalDecl(const struct ZigClangEnumDecl *);
ZIG_EXTERN_C const struct ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const struct ZigClangTypedefNameDecl *);
ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const struct ZigClangRecordDecl *);
ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumDecl_getDefinition(const struct ZigClangEnumDecl *);
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangRecordDecl_getLocation(const struct ZigClangRecordDecl *);
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangEnumDecl_getLocation(const struct ZigClangEnumDecl *);
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangTypedefNameDecl_getLocation(const struct ZigClangTypedefNameDecl *);
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDecl_getLocation(const struct ZigClangDecl *);
ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionDecl_getType(const struct ZigClangFunctionDecl *);
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFunctionDecl_getLocation(const struct ZigClangFunctionDecl *);
ZIG_EXTERN_C bool ZigClangFunctionDecl_hasBody(const struct ZigClangFunctionDecl *);
ZIG_EXTERN_C enum ZigClangStorageClass ZigClangFunctionDecl_getStorageClass(const struct ZigClangFunctionDecl *);
ZIG_EXTERN_C const struct ZigClangParmVarDecl *ZigClangFunctionDecl_getParamDecl(const struct ZigClangFunctionDecl *, unsigned i);
ZIG_EXTERN_C const struct ZigClangStmt *ZigClangFunctionDecl_getBody(const struct ZigClangFunctionDecl *);
ZIG_EXTERN_C bool ZigClangRecordDecl_isUnion(const struct ZigClangRecordDecl *record_decl);
ZIG_EXTERN_C bool ZigClangRecordDecl_isStruct(const struct ZigClangRecordDecl *record_decl);
ZIG_EXTERN_C bool ZigClangRecordDecl_isAnonymousStructOrUnion(const struct ZigClangRecordDecl *record_decl);
ZIG_EXTERN_C struct ZigClangQualType ZigClangEnumDecl_getIntegerType(const struct ZigClangEnumDecl *);
ZIG_EXTERN_C const char *ZigClangDecl_getName_bytes_begin(const struct ZigClangDecl *decl);
ZIG_EXTERN_C enum ZigClangDeclKind ZigClangDecl_getKind(const struct ZigClangDecl *decl);
ZIG_EXTERN_C const char *ZigClangDecl_getDeclKindName(const struct ZigClangDecl *decl);
ZIG_EXTERN_C bool ZigClangSourceLocation_eq(struct ZigClangSourceLocation a, struct ZigClangSourceLocation b);
ZIG_EXTERN_C const struct ZigClangTypedefNameDecl *ZigClangTypedefType_getDecl(const struct ZigClangTypedefType *);
ZIG_EXTERN_C struct ZigClangQualType ZigClangTypedefNameDecl_getUnderlyingType(const struct ZigClangTypedefNameDecl *);
ZIG_EXTERN_C struct ZigClangQualType ZigClangQualType_getCanonicalType(struct ZigClangQualType);
ZIG_EXTERN_C const struct ZigClangType *ZigClangQualType_getTypePtr(struct ZigClangQualType);
ZIG_EXTERN_C void ZigClangQualType_addConst(struct ZigClangQualType *);
ZIG_EXTERN_C bool ZigClangQualType_eq(struct ZigClangQualType, struct ZigClangQualType);
ZIG_EXTERN_C bool ZigClangQualType_isConstQualified(struct ZigClangQualType);
ZIG_EXTERN_C bool ZigClangQualType_isVolatileQualified(struct ZigClangQualType);
ZIG_EXTERN_C bool ZigClangQualType_isRestrictQualified(struct ZigClangQualType);
ZIG_EXTERN_C enum ZigClangTypeClass ZigClangType_getTypeClass(const struct ZigClangType *self);
ZIG_EXTERN_C struct ZigClangQualType ZigClangType_getPointeeType(const struct ZigClangType *self);
ZIG_EXTERN_C bool ZigClangType_isVoidType(const struct ZigClangType *self);
ZIG_EXTERN_C const char *ZigClangType_getTypeClassName(const struct ZigClangType *self);
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangStmt_getBeginLoc(const struct ZigClangStmt *self);
ZIG_EXTERN_C enum ZigClangStmtClass ZigClangStmt_getStmtClass(const struct ZigClangStmt *self);
ZIG_EXTERN_C bool ZigClangStmt_classof_Expr(const struct ZigClangStmt *self);
ZIG_EXTERN_C enum ZigClangStmtClass ZigClangExpr_getStmtClass(const struct ZigClangExpr *self);
ZIG_EXTERN_C struct ZigClangQualType ZigClangExpr_getType(const struct ZigClangExpr *self);
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangExpr_getBeginLoc(const struct ZigClangExpr *self);
ZIG_EXTERN_C enum ZigClangAPValueKind ZigClangAPValue_getKind(const struct ZigClangAPValue *self);
ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangAPValue_getInt(const struct ZigClangAPValue *self);
ZIG_EXTERN_C unsigned ZigClangAPValue_getArrayInitializedElts(const struct ZigClangAPValue *self);
ZIG_EXTERN_C const struct ZigClangAPValue *ZigClangAPValue_getArrayInitializedElt(const struct ZigClangAPValue *self, unsigned i);
ZIG_EXTERN_C const struct ZigClangAPValue *ZigClangAPValue_getArrayFiller(const struct ZigClangAPValue *self);
ZIG_EXTERN_C unsigned ZigClangAPValue_getArraySize(const struct ZigClangAPValue *self);
ZIG_EXTERN_C struct ZigClangAPValueLValueBase ZigClangAPValue_getLValueBase(const struct ZigClangAPValue *self);
ZIG_EXTERN_C bool ZigClangAPSInt_isSigned(const struct ZigClangAPSInt *self);
ZIG_EXTERN_C bool ZigClangAPSInt_isNegative(const struct ZigClangAPSInt *self);
ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangAPSInt_negate(const struct ZigClangAPSInt *self);
ZIG_EXTERN_C void ZigClangAPSInt_free(const struct ZigClangAPSInt *self);
ZIG_EXTERN_C const uint64_t *ZigClangAPSInt_getRawData(const struct ZigClangAPSInt *self);
ZIG_EXTERN_C unsigned ZigClangAPSInt_getNumWords(const struct ZigClangAPSInt *self);
ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAPValueLValueBase_dyn_cast_Expr(struct ZigClangAPValueLValueBase self);
ZIG_EXTERN_C enum ZigClangBuiltinTypeKind ZigClangBuiltinType_getKind(const struct ZigClangBuiltinType *self);
ZIG_EXTERN_C bool ZigClangFunctionType_getNoReturnAttr(const struct ZigClangFunctionType *self);
ZIG_EXTERN_C enum ZigClangCallingConv ZigClangFunctionType_getCallConv(const struct ZigClangFunctionType *self);
ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClangFunctionType *self);
ZIG_EXTERN_C bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self);
ZIG_EXTERN_C unsigned ZigClangFunctionProtoType_getNumParams(const struct ZigClangFunctionProtoType *self);
ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionProtoType_getParamType(const struct ZigClangFunctionProtoType *self, unsigned i);
ZIG_EXTERN_C ZigClangCompoundStmt_const_body_iterator ZigClangCompoundStmt_body_begin(const struct ZigClangCompoundStmt *self);
ZIG_EXTERN_C ZigClangCompoundStmt_const_body_iterator ZigClangCompoundStmt_body_end(const struct ZigClangCompoundStmt *self);
ZIG_EXTERN_C ZigClangASTContext *ZigClangASTUnit_getASTContext(ZigClangASTUnit *);
ZIG_EXTERN_C ZigClangSourceManager *ZigClangASTUnit_getSourceManager(ZigClangASTUnit *);
ZIG_EXTERN_C bool ZigClangASTUnit_visitLocalTopLevelDecls(ZigClangASTUnit *, void *context,
bool (*Fn)(void *context, const ZigClangDecl *decl));
#endif

View File

@ -111,6 +111,17 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
new_item_ptr.* = item;
}
pub fn orderedRemove(self: *Self, i: usize) T {
const newlen = self.len - 1;
if (newlen == i) return self.pop();
const old_item = self.at(i);
for (self.items[i..newlen]) |*b, j| b.* = self.items[i + 1 + j];
self.items[newlen] = undefined;
self.len = newlen;
return old_item;
}
/// Removes the element at the specified index and returns it.
/// The empty slot is filled from the end of the list.
pub fn swapRemove(self: *Self, i: usize) T {
@ -279,6 +290,33 @@ test "std.ArrayList.basic" {
testing.expect(list.pop() == 33);
}
test "std.ArrayList.orderedRemove" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
try list.append(3);
try list.append(4);
try list.append(5);
try list.append(6);
try list.append(7);
//remove from middle
testing.expectEqual(i32(4), list.orderedRemove(3));
testing.expectEqual(i32(5), list.at(3));
testing.expectEqual(usize(6), list.len);
//remove from end
testing.expectEqual(i32(7), list.orderedRemove(5));
testing.expectEqual(usize(5), list.len);
//remove from front
testing.expectEqual(i32(1), list.orderedRemove(0));
testing.expectEqual(i32(2), list.at(0));
testing.expectEqual(usize(4), list.len);
}
test "std.ArrayList.swapRemove" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();

View File

@ -50,6 +50,8 @@ pub const Builder = struct {
build_root: []const u8,
cache_root: []const u8,
release_mode: ?builtin.Mode,
override_std_dir: ?[]const u8,
override_lib_dir: ?[]const u8,
pub const CStd = enum {
C89,
@ -133,6 +135,8 @@ pub const Builder = struct {
},
.have_install_step = false,
.release_mode = null,
.override_std_dir = null,
.override_lib_dir = null,
};
self.detectNativeSystemPaths();
self.default_step = self.step("default", "Build the project");
@ -937,8 +941,11 @@ pub const LibExeObjStep = struct {
verbose_link: bool,
verbose_cc: bool,
disable_gen_h: bool,
bundle_compiler_rt: bool,
disable_stack_probing: bool,
c_std: Builder.CStd,
override_std_dir: ?[]const u8,
override_lib_dir: ?[]const u8,
main_pkg_path: ?[]const u8,
exec_cmd_args: ?[]const ?[]const u8,
name_prefix: []const u8,
@ -1039,11 +1046,14 @@ pub const LibExeObjStep = struct {
.c_std = Builder.CStd.C99,
.system_linker_hack = false,
.override_std_dir = null,
.override_lib_dir = null,
.main_pkg_path = null,
.exec_cmd_args = null,
.name_prefix = "",
.filter = null,
.disable_gen_h = false,
.bundle_compiler_rt = false,
.disable_stack_probing = false,
.output_dir = null,
.need_system_paths = false,
.single_threaded = false,
@ -1446,6 +1456,12 @@ pub const LibExeObjStep = struct {
if (self.disable_gen_h) {
try zig_args.append("--disable-gen-h");
}
if (self.bundle_compiler_rt) {
try zig_args.append("--bundle-compiler-rt");
}
if (self.disable_stack_probing) {
try zig_args.append("--disable-stack-probing");
}
switch (self.target) {
Target.Native => {},
@ -1528,6 +1544,17 @@ pub const LibExeObjStep = struct {
if (self.override_std_dir) |dir| {
try zig_args.append("--override-std-dir");
try zig_args.append(builder.pathFromRoot(dir));
} else if (self.builder.override_std_dir) |dir| {
try zig_args.append("--override-std-dir");
try zig_args.append(builder.pathFromRoot(dir));
}
if (self.override_lib_dir) |dir| {
try zig_args.append("--override-lib-dir");
try zig_args.append(builder.pathFromRoot(dir));
} else if (self.builder.override_lib_dir) |dir| {
try zig_args.append("--override-lib-dir");
try zig_args.append(builder.pathFromRoot(dir));
}
if (self.main_pkg_path) |dir| {

View File

@ -12,6 +12,12 @@ pub use switch (builtin.os) {
// TODO https://github.com/ziglang/zig/issues/265 on this whole file
pub const FILE = @OpaqueType();
pub extern "c" fn fopen(filename: [*]const u8, modes: [*]const u8) ?*FILE;
pub extern "c" fn fclose(stream: *FILE) c_int;
pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
pub extern "c" fn abort() noreturn;
pub extern "c" fn exit(code: c_int) noreturn;
pub extern "c" fn isatty(fd: c_int) c_int;

View File

@ -42,14 +42,36 @@ pub const pthread_attr_t = extern struct {
};
pub const msghdr = extern struct {
msg_name: *u8,
/// optional address
msg_name: ?*sockaddr,
/// size of address
msg_namelen: socklen_t,
msg_iov: *iovec,
/// scatter/gather array
msg_iov: [*]iovec,
/// # elements in msg_iov
msg_iovlen: i32,
__pad1: i32,
msg_control: *u8,
/// ancillary data
msg_control: ?*c_void,
/// ancillary data buffer len
msg_controllen: socklen_t,
__pad2: socklen_t,
/// flags on received message
msg_flags: i32,
};
pub const msghdr_const = extern struct {
/// optional address
msg_name: ?*const sockaddr,
/// size of address
msg_namelen: socklen_t,
/// scatter/gather array
msg_iov: [*]iovec_const,
/// # elements in msg_iov
msg_iovlen: i32,
/// ancillary data
msg_control: ?*c_void,
/// ancillary data buffer len
msg_controllen: socklen_t,
/// flags on received message
msg_flags: i32,
};

View File

@ -1,3 +1,4 @@
const linux = @import("../os/linux.zig");
pub use @import("../os/linux/errno.zig");
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int;
@ -11,3 +12,6 @@ pub const pthread_attr_t = extern struct {
/// See std.elf for constants for this
pub extern fn getauxval(__type: c_ulong) c_ulong;
pub const dl_iterate_phdr_callback = extern fn (info: *linux.dl_phdr_info, size: usize, data: ?*c_void) c_int;
pub extern fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;

View File

@ -42,14 +42,36 @@ pub const pthread_attr_t = extern struct {
};
pub const msghdr = extern struct {
msg_name: *u8,
/// optional address
msg_name: ?*sockaddr,
/// size of address
msg_namelen: socklen_t,
msg_iov: *iovec,
/// scatter/gather array
msg_iov: [*]iovec,
/// # elements in msg_iov
msg_iovlen: i32,
__pad1: i32,
msg_control: *u8,
/// ancillary data
msg_control: ?*c_void,
/// ancillary data buffer len
msg_controllen: socklen_t,
__pad2: socklen_t,
/// flags on received message
msg_flags: i32,
};
pub const msghdr_const = extern struct {
/// optional address
msg_name: ?*const sockaddr,
/// size of address
msg_namelen: socklen_t,
/// scatter/gather array
msg_iov: [*]iovec_const,
/// # elements in msg_iov
msg_iovlen: i32,
/// ancillary data
msg_control: ?*c_void,
/// ancillary data buffer len
msg_controllen: socklen_t,
/// flags on received message
msg_flags: i32,
};

View File

@ -142,7 +142,7 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]
assert(in.len >= out.len);
assert(counter +% (in.len >> 6) >= counter);
var cursor: u64 = 0;
var cursor: usize = 0;
var k: [8]u32 = undefined;
var c: [4]u32 = undefined;
@ -161,7 +161,8 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]
c[3] = mem.readIntSliceLittle(u32, nonce[4..8]);
const block_size = (1 << 6);
const big_block = (block_size << 32);
// The full block size is greater than the address space on a 32bit machine
const big_block = if (@sizeOf(usize) > 4) (block_size << 32) else maxInt(usize);
// first partial big block
if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {

View File

@ -13,6 +13,8 @@ const ArrayList = std.ArrayList;
const builtin = @import("builtin");
const maxInt = std.math.maxInt;
const leb = @import("debug/leb128.zig");
pub const FailingAllocator = @import("debug/failing_allocator.zig").FailingAllocator;
pub const failing_allocator = &FailingAllocator.init(global_allocator, 0).allocator;
@ -214,14 +216,14 @@ pub fn writeStackTrace(
tty_color: bool,
) !void {
var frame_index: usize = 0;
var frames_left: usize = stack_trace.index;
var frames_left: usize = std.math.min(stack_trace.index, stack_trace.instruction_addresses.len);
while (frames_left != 0) : ({
frames_left -= 1;
frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
}) {
const return_address = stack_trace.instruction_addresses[frame_index];
try printSourceAtAddress(debug_info, out_stream, return_address, tty_color);
try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_color);
}
}
@ -263,7 +265,7 @@ pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color
}
var it = StackIterator.init(start_addr);
while (it.next()) |return_address| {
try printSourceAtAddress(debug_info, out_stream, return_address, tty_color);
try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_color);
}
}
@ -376,7 +378,6 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres
// There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records)
// from now on. We will iterate through them, and eventually find a LineInfo that we're interested in,
// breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection.
const subsection_end_index = sect_offset + subsect_hdr.Length;
while (line_index < subsection_end_index) {
@ -690,9 +691,9 @@ pub fn printSourceAtAddressDwarf(
return;
};
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| {
if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address)) |line_info| {
defer line_info.deinit();
const symbol_name = "???";
const symbol_name = getSymbolNameDwarf(debug_info, address) orelse "???";
try printLineInfo(
out_stream,
line_info,
@ -969,6 +970,8 @@ fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Sec
pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void {
di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator);
di.compile_unit_list = ArrayList(CompileUnit).init(allocator);
di.func_list = ArrayList(Func).init(allocator);
try scanAllFunctions(di);
try scanAllCompileUnits(di);
}
@ -992,6 +995,7 @@ pub fn openElfDebugInfo(
.debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")),
.abbrev_table_list = undefined,
.compile_unit_list = undefined,
.func_list = undefined,
};
try openDwarfDebugInfo(&di, allocator);
return di;
@ -1162,6 +1166,7 @@ pub const DwarfInfo = struct {
debug_ranges: ?Section,
abbrev_table_list: ArrayList(AbbrevTableHeader),
compile_unit_list: ArrayList(CompileUnit),
func_list: ArrayList(Func),
pub const Section = struct {
offset: usize,
@ -1178,7 +1183,7 @@ pub const DwarfInfo = struct {
};
pub const DebugInfo = switch (builtin.os) {
builtin.Os.macosx => struct {
builtin.Os.macosx, builtin.Os.ios => struct {
symbols: []const MachoSymbol,
strings: []const u8,
ofiles: OFileTable,
@ -1213,7 +1218,6 @@ const CompileUnit = struct {
version: u16,
is_64: bool,
die: *Die,
index: usize,
pc_range: ?PcRange,
};
@ -1244,21 +1248,19 @@ const FormValue = union(enum) {
ExprLoc: []u8,
Flag: bool,
SecOffset: u64,
Ref: []u8,
Ref: u64,
RefAddr: u64,
RefSig8: u64,
String: []u8,
StrPtr: u64,
};
const Constant = struct {
payload: []u8,
payload: u64,
signed: bool,
fn asUnsignedLe(self: *const Constant) !u64 {
if (self.payload.len > @sizeOf(u64)) return error.InvalidDebugInfo;
if (self.signed) return error.InvalidDebugInfo;
return mem.readVarInt(u64, self.payload, builtin.Endian.Little);
return self.payload;
}
};
@ -1304,6 +1306,14 @@ const Die = struct {
};
}
fn getAttrRef(self: *const Die, id: u64) !u64 {
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) {
FormValue.Ref => |value| value,
else => error.InvalidDebugInfo,
};
}
fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 {
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) {
@ -1443,11 +1453,18 @@ fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !
return parseFormValueBlockLen(allocator, in_stream, block_len);
}
fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue {
fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue {
return FormValue{
.Const = Constant{
.signed = signed,
.payload = try readAllocBytes(allocator, in_stream, size),
.payload = switch (size) {
1 => try in_stream.readIntLittle(u8),
2 => try in_stream.readIntLittle(u16),
4 => try in_stream.readIntLittle(u32),
8 => try in_stream.readIntLittle(u64),
-1 => if (signed) @bitCast(u64, try leb.readILEB128(i64, in_stream)) else try leb.readULEB128(u64, in_stream),
else => @compileError("Invalid size"),
},
},
};
}
@ -1460,14 +1477,17 @@ fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLittle(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLittle(u64) else unreachable;
}
fn parseFormValueRefLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
const buf = try readAllocBytes(allocator, in_stream, size);
return FormValue{ .Ref = buf };
}
fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type) !FormValue {
const block_len = try in_stream.readIntLittle(T);
return parseFormValueRefLen(allocator, in_stream, block_len);
fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue {
return FormValue{
.Ref = switch (size) {
1 => try in_stream.readIntLittle(u8),
2 => try in_stream.readIntLittle(u16),
4 => try in_stream.readIntLittle(u32),
8 => try in_stream.readIntLittle(u64),
-1 => try leb.readULEB128(u64, in_stream),
else => unreachable,
},
};
}
fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue {
@ -1477,7 +1497,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
DW.FORM_block => x: {
const block_len = try readULeb128(in_stream);
const block_len = try leb.readULEB128(usize, in_stream);
return parseFormValueBlockLen(allocator, in_stream, block_len);
},
DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
@ -1485,12 +1505,11 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4),
DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8),
DW.FORM_udata, DW.FORM_sdata => {
const block_len = try readULeb128(in_stream);
const signed = form_id == DW.FORM_sdata;
return parseFormValueConstant(allocator, in_stream, signed, block_len);
return parseFormValueConstant(allocator, in_stream, signed, -1);
},
DW.FORM_exprloc => {
const size = try readULeb128(in_stream);
const size = try leb.readULEB128(usize, in_stream);
const buf = try readAllocBytes(allocator, in_stream, size);
return FormValue{ .ExprLoc = buf };
},
@ -1498,22 +1517,19 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
DW.FORM_flag_present => FormValue{ .Flag = true },
DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8),
DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16),
DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, u32),
DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, u64),
DW.FORM_ref_udata => {
const ref_len = try readULeb128(in_stream);
return parseFormValueRefLen(allocator, in_stream, ref_len);
},
DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, 1),
DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, 2),
DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, 4),
DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, 8),
DW.FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1),
DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_ref_sig8 => FormValue{ .RefSig8 = try in_stream.readIntLittle(u64) },
DW.FORM_ref_sig8 => FormValue{ .Ref = try in_stream.readIntLittle(u64) },
DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) },
DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_indirect => {
const child_form_id = try readULeb128(in_stream);
const child_form_id = try leb.readULEB128(u64, in_stream);
return parseFormValue(allocator, in_stream, child_form_id, is_64);
},
else => error.InvalidDebugInfo,
@ -1523,19 +1539,19 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable {
var result = AbbrevTable.init(di.allocator());
while (true) {
const abbrev_code = try readULeb128(di.dwarf_in_stream);
const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);
if (abbrev_code == 0) return result;
try result.append(AbbrevTableEntry{
.abbrev_code = abbrev_code,
.tag_id = try readULeb128(di.dwarf_in_stream),
.tag_id = try leb.readULEB128(u64, di.dwarf_in_stream),
.has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes,
.attrs = ArrayList(AbbrevAttr).init(di.allocator()),
});
const attrs = &result.items[result.len - 1].attrs;
while (true) {
const attr_id = try readULeb128(di.dwarf_in_stream);
const form_id = try readULeb128(di.dwarf_in_stream);
const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream);
const form_id = try leb.readULEB128(u64, di.dwarf_in_stream);
if (attr_id == 0 and form_id == 0) break;
try attrs.append(AbbrevAttr{
.attr_id = attr_id,
@ -1568,8 +1584,28 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
return null;
}
fn parseDie1(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);
if (abbrev_code == 0) return null;
const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
var result = Die{
.tag_id = table_entry.tag_id,
.has_children = table_entry.has_children,
.attrs = ArrayList(Die.Attr).init(di.allocator()),
};
try result.attrs.resize(table_entry.attrs.len);
for (table_entry.attrs.toSliceConst()) |attr, i| {
result.attrs.items[i] = Die.Attr{
.id = attr.attr_id,
.value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64),
};
}
return result;
}
fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
const abbrev_code = try readULeb128(di.dwarf_in_stream);
const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);
const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
var result = Die{
@ -1682,9 +1718,9 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
while (true) {
const file_name = readStringMem(&ptr);
if (file_name.len == 0) break;
const dir_index = try readULeb128Mem(&ptr);
const mtime = try readULeb128Mem(&ptr);
const len_bytes = try readULeb128Mem(&ptr);
const dir_index = try leb.readULEB128Mem(usize, &ptr);
const mtime = try leb.readULEB128Mem(usize, &ptr);
const len_bytes = try leb.readULEB128Mem(usize, &ptr);
try file_entries.append(FileEntry{
.file_name = file_name,
.dir_index = dir_index,
@ -1698,7 +1734,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
const opcode = readByteMem(&ptr);
if (opcode == DW.LNS_extended_op) {
const op_size = try readULeb128Mem(&ptr);
const op_size = try leb.readULEB128Mem(u64, &ptr);
if (op_size < 1) return error.InvalidDebugInfo;
var sub_op = readByteMem(&ptr);
switch (sub_op) {
@ -1713,9 +1749,9 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
},
DW.LNE_define_file => {
const file_name = readStringMem(&ptr);
const dir_index = try readULeb128Mem(&ptr);
const mtime = try readULeb128Mem(&ptr);
const len_bytes = try readULeb128Mem(&ptr);
const dir_index = try leb.readULEB128Mem(usize, &ptr);
const mtime = try leb.readULEB128Mem(usize, &ptr);
const len_bytes = try leb.readULEB128Mem(usize, &ptr);
try file_entries.append(FileEntry{
.file_name = file_name,
.dir_index = dir_index,
@ -1743,19 +1779,19 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
prog.basic_block = false;
},
DW.LNS_advance_pc => {
const arg = try readULeb128Mem(&ptr);
const arg = try leb.readULEB128Mem(u64, &ptr);
prog.address += arg * minimum_instruction_length;
},
DW.LNS_advance_line => {
const arg = try readILeb128Mem(&ptr);
const arg = try leb.readILEB128Mem(i64, &ptr);
prog.line += arg;
},
DW.LNS_set_file => {
const arg = try readULeb128Mem(&ptr);
const arg = try leb.readULEB128Mem(u64, &ptr);
prog.file = arg;
},
DW.LNS_set_column => {
const arg = try readULeb128Mem(&ptr);
const arg = try leb.readULEB128Mem(u64, &ptr);
prog.column = arg;
},
DW.LNS_negate_stmt => {
@ -1787,182 +1823,292 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {
const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list);
const debug_line_end = di.debug_line.offset + di.debug_line.size;
var this_offset = di.debug_line.offset;
var this_index: usize = 0;
assert(line_info_offset < di.debug_line.size);
while (this_offset < debug_line_end) : (this_index += 1) {
try di.dwarf_seekable_stream.seekTo(this_offset);
try di.dwarf_seekable_stream.seekTo(di.debug_line.offset + line_info_offset);
var is_64: bool = undefined;
const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
if (unit_length == 0) return error.MissingDebugInfo;
const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
var is_64: bool = undefined;
const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
if (unit_length == 0) {
return error.MissingDebugInfo;
}
const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
if (compile_unit.index != this_index) {
this_offset += next_offset;
continue;
const version = try di.dwarf_in_stream.readInt(u16, di.endian);
// TODO support 3 and 5
if (version != 2 and version != 4) return error.InvalidDebugInfo;
const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian);
const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length;
const minimum_instruction_length = try di.dwarf_in_stream.readByte();
if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
if (version >= 4) {
// maximum_operations_per_instruction
_ = try di.dwarf_in_stream.readByte();
}
const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0;
const line_base = try di.dwarf_in_stream.readByteSigned();
const line_range = try di.dwarf_in_stream.readByte();
if (line_range == 0) return error.InvalidDebugInfo;
const opcode_base = try di.dwarf_in_stream.readByte();
const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
{
var i: usize = 0;
while (i < opcode_base - 1) : (i += 1) {
standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte();
}
}
const version = try di.dwarf_in_stream.readInt(u16, di.endian);
// TODO support 3 and 5
if (version != 2 and version != 4) return error.InvalidDebugInfo;
var include_directories = ArrayList([]u8).init(di.allocator());
try include_directories.append(compile_unit_cwd);
while (true) {
const dir = try di.readString();
if (dir.len == 0) break;
try include_directories.append(dir);
}
const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian);
const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length;
var file_entries = ArrayList(FileEntry).init(di.allocator());
var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
const minimum_instruction_length = try di.dwarf_in_stream.readByte();
if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
while (true) {
const file_name = try di.readString();
if (file_name.len == 0) break;
const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream);
const mtime = try leb.readULEB128(usize, di.dwarf_in_stream);
const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream);
try file_entries.append(FileEntry{
.file_name = file_name,
.dir_index = dir_index,
.mtime = mtime,
.len_bytes = len_bytes,
});
}
if (version >= 4) {
// maximum_operations_per_instruction
_ = try di.dwarf_in_stream.readByte();
}
try di.dwarf_seekable_stream.seekTo(prog_start_offset);
const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0;
const line_base = try di.dwarf_in_stream.readByteSigned();
while (true) {
const opcode = try di.dwarf_in_stream.readByte();
const line_range = try di.dwarf_in_stream.readByte();
if (line_range == 0) return error.InvalidDebugInfo;
const opcode_base = try di.dwarf_in_stream.readByte();
const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
{
var i: usize = 0;
while (i < opcode_base - 1) : (i += 1) {
standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte();
if (opcode == DW.LNS_extended_op) {
const op_size = try leb.readULEB128(u64, di.dwarf_in_stream);
if (op_size < 1) return error.InvalidDebugInfo;
var sub_op = try di.dwarf_in_stream.readByte();
switch (sub_op) {
DW.LNE_end_sequence => {
prog.end_sequence = true;
if (try prog.checkLineMatch()) |info| return info;
return error.MissingDebugInfo;
},
DW.LNE_set_address => {
const addr = try di.dwarf_in_stream.readInt(usize, di.endian);
prog.address = addr;
},
DW.LNE_define_file => {
const file_name = try di.readString();
const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream);
const mtime = try leb.readULEB128(usize, di.dwarf_in_stream);
const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream);
try file_entries.append(FileEntry{
.file_name = file_name,
.dir_index = dir_index,
.mtime = mtime,
.len_bytes = len_bytes,
});
},
else => {
const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
try di.dwarf_seekable_stream.seekForward(fwd_amt);
},
}
} else if (opcode >= opcode_base) {
// special opcodes
const adjusted_opcode = opcode - opcode_base;
const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
prog.line += inc_line;
prog.address += inc_addr;
if (try prog.checkLineMatch()) |info| return info;
prog.basic_block = false;
} else {
switch (opcode) {
DW.LNS_copy => {
if (try prog.checkLineMatch()) |info| return info;
prog.basic_block = false;
},
DW.LNS_advance_pc => {
const arg = try leb.readULEB128(u64, di.dwarf_in_stream);
prog.address += arg * minimum_instruction_length;
},
DW.LNS_advance_line => {
const arg = try leb.readILEB128(i64, di.dwarf_in_stream);
prog.line += arg;
},
DW.LNS_set_file => {
const arg = try leb.readULEB128(u64, di.dwarf_in_stream);
prog.file = arg;
},
DW.LNS_set_column => {
const arg = try leb.readULEB128(u64, di.dwarf_in_stream);
prog.column = arg;
},
DW.LNS_negate_stmt => {
prog.is_stmt = !prog.is_stmt;
},
DW.LNS_set_basic_block => {
prog.basic_block = true;
},
DW.LNS_const_add_pc => {
const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
prog.address += inc_addr;
},
DW.LNS_fixed_advance_pc => {
const arg = try di.dwarf_in_stream.readInt(u16, di.endian);
prog.address += arg;
},
DW.LNS_set_prologue_end => {},
else => {
if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
const len_bytes = standard_opcode_lengths[opcode - 1];
try di.dwarf_seekable_stream.seekForward(len_bytes);
},
}
}
var include_directories = ArrayList([]u8).init(di.allocator());
try include_directories.append(compile_unit_cwd);
while (true) {
const dir = try di.readString();
if (dir.len == 0) break;
try include_directories.append(dir);
}
var file_entries = ArrayList(FileEntry).init(di.allocator());
var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
while (true) {
const file_name = try di.readString();
if (file_name.len == 0) break;
const dir_index = try readULeb128(di.dwarf_in_stream);
const mtime = try readULeb128(di.dwarf_in_stream);
const len_bytes = try readULeb128(di.dwarf_in_stream);
try file_entries.append(FileEntry{
.file_name = file_name,
.dir_index = dir_index,
.mtime = mtime,
.len_bytes = len_bytes,
});
}
try di.dwarf_seekable_stream.seekTo(prog_start_offset);
while (true) {
const opcode = try di.dwarf_in_stream.readByte();
if (opcode == DW.LNS_extended_op) {
const op_size = try readULeb128(di.dwarf_in_stream);
if (op_size < 1) return error.InvalidDebugInfo;
var sub_op = try di.dwarf_in_stream.readByte();
switch (sub_op) {
DW.LNE_end_sequence => {
prog.end_sequence = true;
if (try prog.checkLineMatch()) |info| return info;
return error.MissingDebugInfo;
},
DW.LNE_set_address => {
const addr = try di.dwarf_in_stream.readInt(usize, di.endian);
prog.address = addr;
},
DW.LNE_define_file => {
const file_name = try di.readString();
const dir_index = try readULeb128(di.dwarf_in_stream);
const mtime = try readULeb128(di.dwarf_in_stream);
const len_bytes = try readULeb128(di.dwarf_in_stream);
try file_entries.append(FileEntry{
.file_name = file_name,
.dir_index = dir_index,
.mtime = mtime,
.len_bytes = len_bytes,
});
},
else => {
const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
try di.dwarf_seekable_stream.seekForward(fwd_amt);
},
}
} else if (opcode >= opcode_base) {
// special opcodes
const adjusted_opcode = opcode - opcode_base;
const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
prog.line += inc_line;
prog.address += inc_addr;
if (try prog.checkLineMatch()) |info| return info;
prog.basic_block = false;
} else {
switch (opcode) {
DW.LNS_copy => {
if (try prog.checkLineMatch()) |info| return info;
prog.basic_block = false;
},
DW.LNS_advance_pc => {
const arg = try readULeb128(di.dwarf_in_stream);
prog.address += arg * minimum_instruction_length;
},
DW.LNS_advance_line => {
const arg = try readILeb128(di.dwarf_in_stream);
prog.line += arg;
},
DW.LNS_set_file => {
const arg = try readULeb128(di.dwarf_in_stream);
prog.file = arg;
},
DW.LNS_set_column => {
const arg = try readULeb128(di.dwarf_in_stream);
prog.column = arg;
},
DW.LNS_negate_stmt => {
prog.is_stmt = !prog.is_stmt;
},
DW.LNS_set_basic_block => {
prog.basic_block = true;
},
DW.LNS_const_add_pc => {
const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
prog.address += inc_addr;
},
DW.LNS_fixed_advance_pc => {
const arg = try di.dwarf_in_stream.readInt(u16, di.endian);
prog.address += arg;
},
DW.LNS_set_prologue_end => {},
else => {
if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
const len_bytes = standard_opcode_lengths[opcode - 1];
try di.dwarf_seekable_stream.seekForward(len_bytes);
},
}
}
}
this_offset += next_offset;
}
return error.MissingDebugInfo;
}
const Func = struct {
pc_range: ?PcRange,
name: ?[]u8,
};
fn getSymbolNameDwarf(di: *DwarfInfo, address: u64) ?[]const u8 {
for (di.func_list.toSliceConst()) |*func| {
if (func.pc_range) |range| {
if (address >= range.start and address < range.end) {
return func.name;
}
}
}
return null;
}
fn scanAllFunctions(di: *DwarfInfo) !void {
const debug_info_end = di.debug_info.offset + di.debug_info.size;
var this_unit_offset = di.debug_info.offset;
while (this_unit_offset < debug_info_end) {
try di.dwarf_seekable_stream.seekTo(this_unit_offset);
var is_64: bool = undefined;
const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
if (unit_length == 0) return;
const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
const version = try di.dwarf_in_stream.readInt(u16, di.endian);
if (version < 2 or version > 5) return error.InvalidDebugInfo;
const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian);
const address_size = try di.dwarf_in_stream.readByte();
if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
const compile_unit_pos = try di.dwarf_seekable_stream.getPos();
const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset);
try di.dwarf_seekable_stream.seekTo(compile_unit_pos);
const next_unit_pos = this_unit_offset + next_offset;
while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) {
const die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse continue;
const after_die_offset = try di.dwarf_seekable_stream.getPos();
switch (die_obj.tag_id) {
DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => {
const fn_name = x: {
var depth: i32 = 3;
var this_die_obj = die_obj;
// Prenvent endless loops
while (depth > 0) : (depth -= 1) {
if (this_die_obj.getAttr(DW.AT_name)) |_| {
const name = try this_die_obj.getAttrString(di, DW.AT_name);
break :x name;
} else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| {
// Follow the DIE it points to and repeat
const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin);
if (ref_offset > next_offset) return error.InvalidDebugInfo;
try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset);
this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
} else if (this_die_obj.getAttr(DW.AT_specification)) |ref| {
// Follow the DIE it points to and repeat
const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification);
if (ref_offset > next_offset) return error.InvalidDebugInfo;
try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset);
this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
} else {
break :x null;
}
}
break :x null;
};
const pc_range = x: {
if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| {
if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| {
const pc_end = switch (high_pc_value.*) {
FormValue.Address => |value| value,
FormValue.Const => |value| b: {
const offset = try value.asUnsignedLe();
break :b (low_pc + offset);
},
else => return error.InvalidDebugInfo,
};
break :x PcRange{
.start = low_pc,
.end = pc_end,
};
} else {
break :x null;
}
} else |err| {
if (err != error.MissingDebugInfo) return err;
break :x null;
}
};
try di.func_list.append(Func{
.name = fn_name,
.pc_range = pc_range,
});
},
else => {
continue;
},
}
try di.dwarf_seekable_stream.seekTo(after_die_offset);
}
this_unit_offset += next_offset;
}
}
fn scanAllCompileUnits(di: *DwarfInfo) !void {
const debug_info_end = di.debug_info.offset + di.debug_info.size;
var this_unit_offset = di.debug_info.offset;
var cu_index: usize = 0;
while (this_unit_offset < debug_info_end) {
try di.dwarf_seekable_stream.seekTo(this_unit_offset);
@ -2019,11 +2165,9 @@ fn scanAllCompileUnits(di: *DwarfInfo) !void {
.is_64 = is_64,
.pc_range = pc_range,
.die = compile_unit_die,
.index = cu_index,
});
this_unit_offset += next_offset;
cu_index += 1;
}
}
@ -2098,52 +2242,6 @@ fn readStringMem(ptr: *[*]const u8) []const u8 {
return result;
}
fn readULeb128Mem(ptr: *[*]const u8) !u64 {
var result: u64 = 0;
var shift: usize = 0;
var i: usize = 0;
while (true) {
const byte = ptr.*[i];
i += 1;
var operand: u64 = undefined;
if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
result |= operand;
if ((byte & 0b10000000) == 0) {
ptr.* += i;
return result;
}
shift += 7;
}
}
fn readILeb128Mem(ptr: *[*]const u8) !i64 {
var result: i64 = 0;
var shift: usize = 0;
var i: usize = 0;
while (true) {
const byte = ptr.*[i];
i += 1;
var operand: i64 = undefined;
if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
result |= operand;
shift += 7;
if ((byte & 0b10000000) == 0) {
if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift));
ptr.* += i;
return result;
}
}
}
fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
const first_32_bits = try in_stream.readIntLittle(u32);
is_64.* = (first_32_bits == 0xffffffff);
@ -2155,46 +2253,6 @@ fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool)
}
}
fn readULeb128(in_stream: var) !u64 {
var result: u64 = 0;
var shift: usize = 0;
while (true) {
const byte = try in_stream.readByte();
var operand: u64 = undefined;
if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
result |= operand;
if ((byte & 0b10000000) == 0) return result;
shift += 7;
}
}
fn readILeb128(in_stream: var) !i64 {
var result: i64 = 0;
var shift: usize = 0;
while (true) {
const byte = try in_stream.readByte();
var operand: i64 = undefined;
if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
result |= operand;
shift += 7;
if ((byte & 0b10000000) == 0) {
if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift));
return result;
}
}
}
/// This should only be used in temporary test programs.
pub const global_allocator = &global_fixed_allocator.allocator;
var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]);

View File

@ -10,6 +10,7 @@ pub const FailingAllocator = struct {
internal_allocator: *mem.Allocator,
allocated_bytes: usize,
freed_bytes: usize,
allocations: usize,
deallocations: usize,
pub fn init(allocator: *mem.Allocator, fail_index: usize) FailingAllocator {
@ -19,6 +20,7 @@ pub const FailingAllocator = struct {
.index = 0,
.allocated_bytes = 0,
.freed_bytes = 0,
.allocations = 0,
.deallocations = 0,
.allocator = mem.Allocator{
.reallocFn = realloc,
@ -39,19 +41,25 @@ pub const FailingAllocator = struct {
new_size,
new_align,
);
if (new_size <= old_mem.len) {
if (new_size < old_mem.len) {
self.freed_bytes += old_mem.len - new_size;
} else {
if (new_size == 0)
self.deallocations += 1;
} else if (new_size > old_mem.len) {
self.allocated_bytes += new_size - old_mem.len;
if (old_mem.len == 0)
self.allocations += 1;
}
self.deallocations += 1;
self.index += 1;
return result;
}
fn shrink(allocator: *mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
self.freed_bytes += old_mem.len - new_size;
return self.internal_allocator.shrinkFn(self.internal_allocator, old_mem, old_align, new_size, new_align);
const r = self.internal_allocator.shrinkFn(self.internal_allocator, old_mem, old_align, new_size, new_align);
self.freed_bytes += old_mem.len - r.len;
if (new_size == 0)
self.deallocations += 1;
return r;
}
};

228
std/debug/leb128.zig Normal file
View File

@ -0,0 +1,228 @@
const std = @import("std");
const testing = std.testing;
pub fn readULEB128(comptime T: type, in_stream: var) !T {
const ShiftT = @IntType(false, std.math.log2(T.bit_count));
var result: T = 0;
var shift: usize = 0;
while (true) {
const byte = try in_stream.readByte();
if (shift > T.bit_count)
return error.Overflow;
var operand: T = undefined;
if (@shlWithOverflow(T, byte & 0x7f, @intCast(ShiftT, shift), &operand))
return error.Overflow;
result |= operand;
if ((byte & 0x80) == 0)
return result;
shift += 7;
}
}
pub fn readULEB128Mem(comptime T: type, ptr: *[*]const u8) !T {
const ShiftT = @IntType(false, std.math.log2(T.bit_count));
var result: T = 0;
var shift: usize = 0;
var i: usize = 0;
while (true) : (i += 1) {
const byte = ptr.*[i];
if (shift > T.bit_count)
return error.Overflow;
var operand: T = undefined;
if (@shlWithOverflow(T, byte & 0x7f, @intCast(ShiftT, shift), &operand))
return error.Overflow;
result |= operand;
if ((byte & 0x80) == 0) {
ptr.* += i;
return result;
}
shift += 7;
}
}
pub fn readILEB128(comptime T: type, in_stream: var) !T {
const UT = @IntType(false, T.bit_count);
const ShiftT = @IntType(false, std.math.log2(T.bit_count));
var result: UT = 0;
var shift: usize = 0;
while (true) {
const byte = u8(try in_stream.readByte());
if (shift > T.bit_count)
return error.Overflow;
var operand: UT = undefined;
if (@shlWithOverflow(UT, UT(byte & 0x7f), @intCast(ShiftT, shift), &operand)) {
if (byte != 0x7f)
return error.Overflow;
}
result |= operand;
shift += 7;
if ((byte & 0x80) == 0) {
if (shift < T.bit_count and (byte & 0x40) != 0) {
result |= @bitCast(UT, @intCast(T, -1)) << @intCast(ShiftT, shift);
}
return @bitCast(T, result);
}
}
}
pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T {
const UT = @IntType(false, T.bit_count);
const ShiftT = @IntType(false, std.math.log2(T.bit_count));
var result: UT = 0;
var shift: usize = 0;
var i: usize = 0;
while (true) : (i += 1) {
const byte = ptr.*[i];
if (shift > T.bit_count)
return error.Overflow;
var operand: UT = undefined;
if (@shlWithOverflow(UT, UT(byte & 0x7f), @intCast(ShiftT, shift), &operand)) {
if (byte != 0x7f)
return error.Overflow;
}
result |= operand;
shift += 7;
if ((byte & 0x80) == 0) {
if (shift < T.bit_count and (byte & 0x40) != 0) {
result |= @bitCast(UT, @intCast(T, -1)) << @intCast(ShiftT, shift);
}
ptr.* += i;
return @bitCast(T, result);
}
}
}
fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
var in_stream = std.io.SliceInStream.init(encoded);
return try readILEB128(T, &in_stream.stream);
}
fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T {
var in_stream = std.io.SliceInStream.init(encoded);
return try readULEB128(T, &in_stream.stream);
}
fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
var in_stream = std.io.SliceInStream.init(encoded);
const v1 = readILEB128(T, &in_stream.stream);
var in_ptr = encoded.ptr;
const v2 = readILEB128Mem(T, &in_ptr);
testing.expectEqual(v1, v2);
return v1;
}
fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
var in_stream = std.io.SliceInStream.init(encoded);
const v1 = readULEB128(T, &in_stream.stream);
var in_ptr = encoded.ptr;
const v2 = readULEB128Mem(T, &in_ptr);
testing.expectEqual(v1, v2);
return v1;
}
test "deserialize signed LEB128" {
// Truncated
testing.expectError(error.EndOfStream, test_read_stream_ileb128(i64, "\x80"));
// Overflow
testing.expectError(error.Overflow, test_read_ileb128(i8, "\x80\x80\x40"));
testing.expectError(error.Overflow, test_read_ileb128(i16, "\x80\x80\x80\x40"));
testing.expectError(error.Overflow, test_read_ileb128(i32, "\x80\x80\x80\x80\x40"));
testing.expectError(error.Overflow, test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40"));
testing.expectError(error.Overflow, test_read_ileb128(i8, "\xff\x7e"));
// Decode SLEB128
testing.expect((try test_read_ileb128(i64, "\x00")) == 0);
testing.expect((try test_read_ileb128(i64, "\x01")) == 1);
testing.expect((try test_read_ileb128(i64, "\x3f")) == 63);
testing.expect((try test_read_ileb128(i64, "\x40")) == -64);
testing.expect((try test_read_ileb128(i64, "\x41")) == -63);
testing.expect((try test_read_ileb128(i64, "\x7f")) == -1);
testing.expect((try test_read_ileb128(i64, "\x80\x01")) == 128);
testing.expect((try test_read_ileb128(i64, "\x81\x01")) == 129);
testing.expect((try test_read_ileb128(i64, "\xff\x7e")) == -129);
testing.expect((try test_read_ileb128(i64, "\x80\x7f")) == -128);
testing.expect((try test_read_ileb128(i64, "\x81\x7f")) == -127);
testing.expect((try test_read_ileb128(i64, "\xc0\x00")) == 64);
testing.expect((try test_read_ileb128(i64, "\xc7\x9f\x7f")) == -12345);
testing.expect((try test_read_ileb128(i8, "\xff\x7f")) == -1);
testing.expect((try test_read_ileb128(i16, "\xff\xff\x7f")) == -1);
testing.expect((try test_read_ileb128(i32, "\xff\xff\xff\xff\x7f")) == -1);
testing.expect((try test_read_ileb128(i32, "\x80\x80\x80\x80\x08")) == -0x80000000);
testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")) == @bitCast(i64, @intCast(u64, 0x8000000000000000)));
testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x40")) == -0x4000000000000000);
testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7f")) == -0x8000000000000000);
// Decode unnormalized SLEB128 with extra padding bytes.
testing.expect((try test_read_ileb128(i64, "\x80\x00")) == 0);
testing.expect((try test_read_ileb128(i64, "\x80\x80\x00")) == 0);
testing.expect((try test_read_ileb128(i64, "\xff\x00")) == 0x7f);
testing.expect((try test_read_ileb128(i64, "\xff\x80\x00")) == 0x7f);
testing.expect((try test_read_ileb128(i64, "\x80\x81\x00")) == 0x80);
testing.expect((try test_read_ileb128(i64, "\x80\x81\x80\x00")) == 0x80);
}
test "deserialize unsigned LEB128" {
// Truncated
testing.expectError(error.EndOfStream, test_read_stream_uleb128(u64, "\x80"));
// Overflow
testing.expectError(error.Overflow, test_read_uleb128(u8, "\x80\x02"));
testing.expectError(error.Overflow, test_read_uleb128(u8, "\x80\x80\x40"));
testing.expectError(error.Overflow, test_read_uleb128(u16, "\x80\x80\x84"));
testing.expectError(error.Overflow, test_read_uleb128(u16, "\x80\x80\x80\x40"));
testing.expectError(error.Overflow, test_read_uleb128(u32, "\x80\x80\x80\x80\x90"));
testing.expectError(error.Overflow, test_read_uleb128(u32, "\x80\x80\x80\x80\x40"));
testing.expectError(error.Overflow, test_read_uleb128(u64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40"));
// Decode ULEB128
testing.expect((try test_read_uleb128(u64, "\x00")) == 0);
testing.expect((try test_read_uleb128(u64, "\x01")) == 1);
testing.expect((try test_read_uleb128(u64, "\x3f")) == 63);
testing.expect((try test_read_uleb128(u64, "\x40")) == 64);
testing.expect((try test_read_uleb128(u64, "\x7f")) == 0x7f);
testing.expect((try test_read_uleb128(u64, "\x80\x01")) == 0x80);
testing.expect((try test_read_uleb128(u64, "\x81\x01")) == 0x81);
testing.expect((try test_read_uleb128(u64, "\x90\x01")) == 0x90);
testing.expect((try test_read_uleb128(u64, "\xff\x01")) == 0xff);
testing.expect((try test_read_uleb128(u64, "\x80\x02")) == 0x100);
testing.expect((try test_read_uleb128(u64, "\x81\x02")) == 0x101);
testing.expect((try test_read_uleb128(u64, "\x80\xc1\x80\x80\x10")) == 4294975616);
testing.expect((try test_read_uleb128(u64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")) == 0x8000000000000000);
// Decode ULEB128 with extra padding bytes
testing.expect((try test_read_uleb128(u64, "\x80\x00")) == 0);
testing.expect((try test_read_uleb128(u64, "\x80\x80\x00")) == 0);
testing.expect((try test_read_uleb128(u64, "\xff\x00")) == 0x7f);
testing.expect((try test_read_uleb128(u64, "\xff\x80\x00")) == 0x7f);
testing.expect((try test_read_uleb128(u64, "\x80\x81\x00")) == 0x80);
testing.expect((try test_read_uleb128(u64, "\x80\x81\x80\x00")) == 0x80);
}

View File

@ -13,6 +13,7 @@ pub const TAG_reference_type = 0x10;
pub const TAG_compile_unit = 0x11;
pub const TAG_string_type = 0x12;
pub const TAG_structure_type = 0x13;
pub const TAG_subroutine = 0x14;
pub const TAG_subroutine_type = 0x15;
pub const TAG_typedef = 0x16;
pub const TAG_union_type = 0x17;
@ -241,6 +242,9 @@ pub const AT_const_expr = 0x6c;
pub const AT_enum_class = 0x6d;
pub const AT_linkage_name = 0x6e;
// DWARF 5
pub const AT_alignment = 0x88;
pub const AT_lo_user = 0x2000; // Implementation-defined range start.
pub const AT_hi_user = 0x3fff; // Implementation-defined range end.

View File

@ -19,6 +19,89 @@ pub const DynLib = switch (builtin.os) {
else => void,
};
// The link_map structure is not completely specified beside the fields
// reported below, any libc is free to store additional data in the remaining
// space.
// An iterator is provided in order to traverse the linked list in a idiomatic
// fashion.
const LinkMap = extern struct {
l_addr: usize,
l_name: [*]const u8,
l_ld: ?*elf.Dyn,
l_next: ?*LinkMap,
l_prev: ?*LinkMap,
pub const Iterator = struct {
current: ?*LinkMap,
fn end(self: *Iterator) bool {
return self.current == null;
}
fn next(self: *Iterator) ?*LinkMap {
if (self.current) |it| {
self.current = it.l_next;
return it;
}
return null;
}
};
};
const RDebug = extern struct {
r_version: i32,
r_map: ?*LinkMap,
r_brk: usize,
r_ldbase: usize,
};
fn elf_get_va_offset(phdrs: []elf.Phdr) !usize {
for (phdrs) |*phdr| {
if (phdr.p_type == elf.PT_LOAD) {
return @ptrToInt(phdr) - phdr.p_vaddr;
}
}
return error.InvalidExe;
}
pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
const va_offset = try elf_get_va_offset(phdrs);
const dyn_table = init: {
for (phdrs) |*phdr| {
if (phdr.p_type == elf.PT_DYNAMIC) {
const ptr = @intToPtr([*]elf.Dyn, va_offset + phdr.p_vaddr);
break :init ptr[0..phdr.p_memsz / @sizeOf(elf.Dyn)];
}
}
// No PT_DYNAMIC means this is either a statically-linked program or a
// badly corrupted one
return LinkMap.Iterator{.current = null};
};
const link_map_ptr = init: {
for (dyn_table) |*dyn| {
switch (dyn.d_tag) {
elf.DT_DEBUG => {
const r_debug = @intToPtr(*RDebug, dyn.d_un.d_ptr);
if (r_debug.r_version != 1) return error.InvalidExe;
break :init r_debug.r_map;
},
elf.DT_PLTGOT => {
const got_table = @intToPtr([*]usize, dyn.d_un.d_ptr);
// The address to the link_map structure is stored in the
// second slot
break :init @intToPtr(?*LinkMap, got_table[1]);
},
else => { }
}
}
return error.InvalidExe;
};
return LinkMap.Iterator{.current = link_map_ptr};
}
pub const LinuxDynLib = struct {
elf_lib: ElfLib,
fd: i32,

View File

@ -877,6 +877,11 @@ pub const Phdr = switch (@sizeOf(usize)) {
8 => Elf64_Phdr,
else => @compileError("expected pointer size of 32 or 64"),
};
pub const Dyn = switch (@sizeOf(usize)) {
4 => Elf32_Dyn,
8 => Elf64_Dyn,
else => @compileError("expected pointer size of 32 or 64"),
};
pub const Shdr = switch (@sizeOf(usize)) {
4 => Elf32_Shdr,
8 => Elf64_Shdr,

View File

@ -8,6 +8,8 @@ const builtin = @import("builtin");
const errol = @import("fmt/errol.zig");
const lossyCast = std.math.lossyCast;
pub const default_max_depth = 3;
/// Renders fmt string with args, calling output with slices of bytes.
/// If `output` returns an error, the error is returned from `format` and
/// `output` is not called again.
@ -49,7 +51,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
start_index = i;
},
'}' => {
try formatType(args[next_arg], fmt[0..0], context, Errors, output);
try formatType(args[next_arg], fmt[0..0], context, Errors, output, default_max_depth);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@ -69,7 +71,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
State.FormatString => switch (c) {
'}' => {
const s = start_index + 1;
try formatType(args[next_arg], fmt[s..i], context, Errors, output);
try formatType(args[next_arg], fmt[s..i], context, Errors, output, default_max_depth);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@ -108,6 +110,7 @@ pub fn formatType(
context: var,
comptime Errors: type,
output: fn (@typeOf(context), []const u8) Errors!void,
max_depth: usize,
) Errors!void {
const T = @typeOf(value);
switch (@typeInfo(T)) {
@ -122,16 +125,16 @@ pub fn formatType(
},
builtin.TypeId.Optional => {
if (value) |payload| {
return formatType(payload, fmt, context, Errors, output);
return formatType(payload, fmt, context, Errors, output, max_depth);
} else {
return output(context, "null");
}
},
builtin.TypeId.ErrorUnion => {
if (value) |payload| {
return formatType(payload, fmt, context, Errors, output);
return formatType(payload, fmt, context, Errors, output, max_depth);
} else |err| {
return formatType(err, fmt, context, Errors, output);
return formatType(err, fmt, context, Errors, output, max_depth);
}
},
builtin.TypeId.ErrorSet => {
@ -164,10 +167,13 @@ pub fn formatType(
switch (comptime @typeId(T)) {
builtin.TypeId.Enum => {
try output(context, ".");
try formatType(@tagName(value), "", context, Errors, output);
try formatType(@tagName(value), "", context, Errors, output, max_depth);
return;
},
builtin.TypeId.Struct => {
if (max_depth == 0) {
return output(context, "{ ... }");
}
comptime var field_i = 0;
inline while (field_i < @memberCount(T)) : (field_i += 1) {
if (field_i == 0) {
@ -177,11 +183,14 @@ pub fn formatType(
}
try output(context, @memberName(T, field_i));
try output(context, " = ");
try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output);
try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth-1);
}
try output(context, " }");
},
builtin.TypeId.Union => {
if (max_depth == 0) {
return output(context, "{ ... }");
}
const info = @typeInfo(T).Union;
if (info.tag_type) |UnionTagType| {
try output(context, "{ .");
@ -189,7 +198,7 @@ pub fn formatType(
try output(context, " = ");
inline for (info.fields) |u_field| {
if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) {
try formatType(@field(value, u_field.name), "", context, Errors, output);
try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth-1);
}
}
try output(context, " }");
@ -210,7 +219,7 @@ pub fn formatType(
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
},
builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
return formatType(value.*, fmt, context, Errors, output);
return formatType(value.*, fmt, context, Errors, output, max_depth);
},
else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
},
@ -986,17 +995,17 @@ test "fmt.format" {
{
var buf1: [32]u8 = undefined;
var context = BufPrintContext{ .remaining = buf1[0..] };
try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite);
try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
var res = buf1[0 .. buf1.len - context.remaining.len];
testing.expect(mem.eql(u8, res, "1234"));
context = BufPrintContext{ .remaining = buf1[0..] };
try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite);
try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
res = buf1[0 .. buf1.len - context.remaining.len];
testing.expect(mem.eql(u8, res, "a"));
context = BufPrintContext{ .remaining = buf1[0..] };
try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite);
try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
res = buf1[0 .. buf1.len - context.remaining.len];
testing.expect(mem.eql(u8, res, "1100"));
}
@ -1364,6 +1373,20 @@ test "fmt.format" {
try testFmt("E.Two", "{}", inst);
}
//self-referential struct format
{
const S = struct {
const SelfType = @This();
a: ?*SelfType,
};
var inst = S{
.a = null,
};
inst.a = &inst;
try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst);
}
//print bytes as hex
{
const some_bytes = "\xCA\xFE\xBA\xBE";
@ -1449,3 +1472,64 @@ test "fmt.formatIntValue with comptime_int" {
try formatIntValue(value, "", &buf, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append);
assert(mem.eql(u8, buf.toSlice(), "123456789123456789"));
}
test "fmt.formatType max_depth" {
const Vec2 = struct {
const SelfType = @This();
x: f32,
y: f32,
pub fn format(
self: SelfType,
comptime fmt: []const u8,
context: var,
comptime Errors: type,
output: fn (@typeOf(context), []const u8) Errors!void,
) Errors!void {
return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y);
}
};
const E = enum {
One,
Two,
Three,
};
const TU = union(enum) {
const SelfType = @This();
float: f32,
int: u32,
ptr: ?*SelfType,
};
const S = struct {
const SelfType = @This();
a: ?*SelfType,
tu: TU,
e: E,
vec: Vec2,
};
var inst = S{
.a = null,
.tu = TU{ .ptr = null },
.e = E.Two,
.vec = Vec2{ .x = 10.2, .y = 2.22 },
};
inst.a = &inst;
inst.tu.ptr = &inst.tu;
var buf0 = try std.Buffer.init(std.debug.global_allocator, "");
try formatType(inst, "", &buf0, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 0);
assert(mem.eql(u8, buf0.toSlice(), "S{ ... }"));
var buf1 = try std.Buffer.init(std.debug.global_allocator, "");
try formatType(inst, "", &buf1, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 1);
assert(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }"));
var buf2 = try std.Buffer.init(std.debug.global_allocator, "");
try formatType(inst, "", &buf2, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 2);
assert(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }"));
var buf3 = try std.Buffer.init(std.debug.global_allocator, "");
try formatType(inst, "", &buf3, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 3);
assert(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }"));
}

View File

@ -118,7 +118,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
};
}
self.incrementModificationCount();
try self.ensureCapacity();
try self.autoCapacity();
const put_result = self.internalPut(key);
assert(put_result.old_kv == null);
return GetOrPutResult{
@ -135,15 +135,37 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
return res.kv;
}
fn ensureCapacity(self: *Self) !void {
if (self.entries.len == 0) {
return self.initCapacity(16);
fn optimizedCapacity(expected_count: usize) usize {
// ensure that the hash map will be at most 60% full if
// expected_count items are put into it
var optimized_capacity = expected_count * 5 / 3;
// round capacity to the next power of two
const pow = math.log2_int_ceil(usize, optimized_capacity);
return math.pow(usize, 2, pow);
}
/// Increases capacity so that the hash map will be at most
/// 60% full when expected_count items are put into it
pub fn ensureCapacity(self: *Self, expected_count: usize) !void {
const optimized_capacity = optimizedCapacity(expected_count);
return self.ensureCapacityExact(optimized_capacity);
}
/// Sets the capacity to the new capacity if the new
/// capacity is greater than the current capacity.
/// New capacity must be a power of two.
fn ensureCapacityExact(self: *Self, new_capacity: usize) !void {
const is_power_of_two = new_capacity & (new_capacity-1) == 0;
assert(is_power_of_two);
if (new_capacity <= self.entries.len) {
return;
}
// if we get too full (60%), double the capacity
if (self.size * 5 >= self.entries.len * 3) {
const old_entries = self.entries;
try self.initCapacity(self.entries.len * 2);
const old_entries = self.entries;
try self.initCapacity(new_capacity);
self.incrementModificationCount();
if (old_entries.len > 0) {
// dump all of the old elements into the new table
for (old_entries) |*old_entry| {
if (old_entry.used) {
@ -156,8 +178,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
/// Returns the kv pair that was already there.
pub fn put(self: *Self, key: K, value: V) !?KV {
try self.autoCapacity();
return putAssumeCapacity(self, key, value);
}
pub fn putAssumeCapacity(self: *Self, key: K, value: V) ?KV {
assert(self.count() < self.entries.len);
self.incrementModificationCount();
try self.ensureCapacity();
const put_result = self.internalPut(key);
put_result.new_entry.kv.value = value;
@ -175,7 +202,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
return hm.get(key) != null;
}
pub fn remove(hm: *Self, key: K) ?*KV {
pub fn remove(hm: *Self, key: K) ?KV {
if (hm.entries.len == 0) return null;
hm.incrementModificationCount();
const start_index = hm.keyToIndex(key);
@ -189,13 +216,14 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
if (!eql(entry.kv.key, key)) continue;
const removed_kv = entry.kv;
while (roll_over < hm.entries.len) : (roll_over += 1) {
const next_index = (start_index + roll_over + 1) % hm.entries.len;
const next_entry = &hm.entries[next_index];
if (!next_entry.used or next_entry.distance_from_start_index == 0) {
entry.used = false;
hm.size -= 1;
return &entry.kv;
return removed_kv;
}
entry.* = next_entry.*;
entry.distance_from_start_index -= 1;
@ -226,6 +254,16 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
return other;
}
fn autoCapacity(self: *Self) !void {
if (self.entries.len == 0) {
return self.ensureCapacityExact(16);
}
// if we get too full (60%), double the capacity
if (self.size * 5 >= self.entries.len * 3) {
return self.ensureCapacityExact(self.entries.len * 2);
}
}
fn initCapacity(hm: *Self, capacity: usize) !void {
hm.entries = try hm.allocator.alloc(Entry, capacity);
hm.size = 0;
@ -371,7 +409,10 @@ test "basic hash map usage" {
testing.expect(map.contains(2));
testing.expect(map.get(2).?.value == 22);
_ = map.remove(2);
const rmv1 = map.remove(2);
testing.expect(rmv1.?.key == 2);
testing.expect(rmv1.?.value == 22);
testing.expect(map.remove(2) == null);
testing.expect(map.get(2) == null);
}
@ -423,6 +464,24 @@ test "iterator hash map" {
testing.expect(entry.value == values[0]);
}
test "ensure capacity" {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
var map = AutoHashMap(i32, i32).init(&direct_allocator.allocator);
defer map.deinit();
try map.ensureCapacity(20);
const initialCapacity = map.entries.len;
testing.expect(initialCapacity >= 20);
var i : i32 = 0;
while (i < 20) : (i += 1) {
testing.expect(map.putAssumeCapacity(i, i+10) == null);
}
// shouldn't resize from putAssumeCapacity
testing.expect(initialCapacity == map.entries.len);
}
pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) {
return struct {
fn hash(key: K) u32 {

View File

@ -34,9 +34,6 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new
/// Thread-safe and lock-free.
pub const DirectAllocator = struct {
allocator: Allocator,
heap_handle: ?HeapHandle,
const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;
pub fn init() DirectAllocator {
return DirectAllocator{
@ -44,21 +41,15 @@ pub const DirectAllocator = struct {
.reallocFn = realloc,
.shrinkFn = shrink,
},
.heap_handle = if (builtin.os == Os.windows) null else {},
};
}
pub fn deinit(self: *DirectAllocator) void {
switch (builtin.os) {
Os.windows => if (self.heap_handle) |heap_handle| {
_ = os.windows.HeapDestroy(heap_handle);
},
else => {},
}
}
pub fn deinit(self: *DirectAllocator) void {}
fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
const self = @fieldParentPtr(DirectAllocator, "allocator", allocator);
if (n == 0)
return (([*]u8)(undefined))[0..0];
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
@ -68,39 +59,76 @@ pub const DirectAllocator = struct {
if (addr == p.MAP_FAILED) return error.OutOfMemory;
if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n];
const aligned_addr = (addr & ~usize(alignment - 1)) + alignment;
const aligned_addr = mem.alignForward(addr, alignment);
// We can unmap the unused portions of our mmap, but we must only
// pass munmap bytes that exist outside our allocated pages or it
// will happily eat us too.
// Since alignment > page_size, we are by definition on a page boundary.
const unused_start = addr;
const unused_len = aligned_addr - 1 - unused_start;
const err = p.munmap(unused_start, unused_len);
assert(p.getErrno(err) == 0);
// It is impossible that there is an unoccupied page at the top of our
// mmap.
// Unmap the extra bytes that were only requested in order to guarantee
// that the range of memory we were provided had a proper alignment in
// it somewhere. The extra bytes could be at the beginning, or end, or both.
const unused_start_len = aligned_addr - addr;
if (unused_start_len != 0) {
const err = p.munmap(addr, unused_start_len);
assert(p.getErrno(err) == 0);
}
const aligned_end_addr = std.mem.alignForward(aligned_addr + n, os.page_size);
const unused_end_len = addr + alloc_size - aligned_end_addr;
if (unused_end_len != 0) {
const err = p.munmap(aligned_end_addr, unused_end_len);
assert(p.getErrno(err) == 0);
}
return @intToPtr([*]u8, aligned_addr)[0..n];
},
Os.windows => {
const amt = n + alignment + @sizeOf(usize);
const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
const heap_handle = optional_heap_handle orelse blk: {
const hh = os.windows.HeapCreate(0, amt, 0) orelse return error.OutOfMemory;
const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh;
_ = os.windows.HeapDestroy(hh);
break :blk other_hh.?; // can't be null because of the cmpxchg
};
const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
const root_addr = @ptrToInt(ptr);
const adjusted_addr = mem.alignForward(root_addr, alignment);
const record_addr = adjusted_addr + n;
@intToPtr(*align(1) usize, record_addr).* = root_addr;
return @intToPtr([*]u8, adjusted_addr)[0..n];
.windows => {
const w = os.windows;
// Although officially it's at least aligned to page boundary,
// Windows is known to reserve pages on a 64K boundary. It's
// even more likely that the requested alignment is <= 64K than
// 4K, so we're just allocating blindly and hoping for the best.
// see https://devblogs.microsoft.com/oldnewthing/?p=42223
const addr = w.VirtualAlloc(
null,
n,
w.MEM_COMMIT | w.MEM_RESERVE,
w.PAGE_READWRITE,
) orelse return error.OutOfMemory;
// If the allocation is sufficiently aligned, use it.
if (@ptrToInt(addr) & (alignment - 1) == 0) {
return @ptrCast([*]u8, addr)[0..n];
}
// If it wasn't, actually do an explicitely aligned allocation.
if (w.VirtualFree(addr, 0, w.MEM_RELEASE) == 0) unreachable;
const alloc_size = n + alignment;
const final_addr = while (true) {
// Reserve a range of memory large enough to find a sufficiently
// aligned address.
const reserved_addr = w.VirtualAlloc(
null,
alloc_size,
w.MEM_RESERVE,
w.PAGE_NOACCESS,
) orelse return error.OutOfMemory;
const aligned_addr = mem.alignForward(@ptrToInt(reserved_addr), alignment);
// Release the reserved pages (not actually used).
if (w.VirtualFree(reserved_addr, 0, w.MEM_RELEASE) == 0) unreachable;
// At this point, it is possible that another thread has
// obtained some memory space that will cause the next
// VirtualAlloc call to fail. To handle this, we will retry
// until it succeeds.
if (w.VirtualAlloc(
@intToPtr(*c_void, aligned_addr),
n,
w.MEM_COMMIT | w.MEM_RESERVE,
w.PAGE_READWRITE,
)) |ptr| break ptr;
} else unreachable; // TODO else unreachable should not be necessary
return @ptrCast([*]u8, final_addr)[0..n];
},
else => @compileError("Unsupported OS"),
}
@ -118,13 +146,31 @@ pub const DirectAllocator = struct {
}
return old_mem[0..new_size];
},
Os.windows => return realloc(allocator, old_mem, old_align, new_size, new_align) catch {
const old_adjusted_addr = @ptrToInt(old_mem.ptr);
const old_record_addr = old_adjusted_addr + old_mem.len;
const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
const old_ptr = @intToPtr(*c_void, root_addr);
const new_record_addr = old_record_addr - new_size + old_mem.len;
@intToPtr(*align(1) usize, new_record_addr).* = root_addr;
.windows => {
const w = os.windows;
if (new_size == 0) {
// From the docs:
// "If the dwFreeType parameter is MEM_RELEASE, this parameter
// must be 0 (zero). The function frees the entire region that
// is reserved in the initial allocation call to VirtualAlloc."
// So we can only use MEM_RELEASE when actually releasing the
// whole allocation.
if (w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE) == 0) unreachable;
} else {
const base_addr = @ptrToInt(old_mem.ptr);
const old_addr_end = base_addr + old_mem.len;
const new_addr_end = base_addr + new_size;
const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
if (old_addr_end > new_addr_end_rounded) {
// For shrinking that is not releasing, we will only
// decommit the pages not needed anymore.
if (w.VirtualFree(
@intToPtr(*c_void, new_addr_end_rounded),
old_addr_end - new_addr_end_rounded,
w.MEM_DECOMMIT,
) == 0) unreachable;
}
}
return old_mem[0..new_size];
},
else => @compileError("Unsupported OS"),
@ -138,38 +184,170 @@ pub const DirectAllocator = struct {
return shrink(allocator, old_mem, old_align, new_size, new_align);
}
const result = try alloc(allocator, new_size, new_align);
mem.copy(u8, result, old_mem);
_ = os.posix.munmap(@ptrToInt(old_mem.ptr), old_mem.len);
if (old_mem.len != 0) {
@memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
_ = os.posix.munmap(@ptrToInt(old_mem.ptr), old_mem.len);
}
return result;
},
Os.windows => {
if (old_mem.len == 0) return alloc(allocator, new_size, new_align);
.windows => {
if (old_mem.len == 0) {
return alloc(allocator, new_size, new_align);
}
const self = @fieldParentPtr(DirectAllocator, "allocator", allocator);
const old_adjusted_addr = @ptrToInt(old_mem.ptr);
const old_record_addr = old_adjusted_addr + old_mem.len;
const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
const old_ptr = @intToPtr(*c_void, root_addr);
const amt = new_size + new_align + @sizeOf(usize);
const new_ptr = os.windows.HeapReAlloc(
self.heap_handle.?,
0,
old_ptr,
amt,
) orelse return error.OutOfMemory;
const offset = old_adjusted_addr - root_addr;
const new_root_addr = @ptrToInt(new_ptr);
const new_adjusted_addr = new_root_addr + offset;
assert(new_adjusted_addr % new_align == 0);
const new_record_addr = new_adjusted_addr + new_size;
@intToPtr(*align(1) usize, new_record_addr).* = new_root_addr;
return @intToPtr([*]u8, new_adjusted_addr)[0..new_size];
if (new_size <= old_mem.len and new_align <= old_align) {
return shrink(allocator, old_mem, old_align, new_size, new_align);
}
const w = os.windows;
const base_addr = @ptrToInt(old_mem.ptr);
if (new_align > old_align and base_addr & (new_align - 1) != 0) {
// Current allocation doesn't satisfy the new alignment.
// For now we'll do a new one no matter what, but maybe
// there is something smarter to do instead.
const result = try alloc(allocator, new_size, new_align);
assert(old_mem.len != 0);
@memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
if (w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE) == 0) unreachable;
return result;
}
const old_addr_end = base_addr + old_mem.len;
const old_addr_end_rounded = mem.alignForward(old_addr_end, os.page_size);
const new_addr_end = base_addr + new_size;
const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
if (new_addr_end_rounded == old_addr_end_rounded) {
// The reallocation fits in the already allocated pages.
return @ptrCast([*]u8, old_mem.ptr)[0..new_size];
}
assert(new_addr_end_rounded > old_addr_end_rounded);
// We need to commit new pages.
const additional_size = new_addr_end - old_addr_end_rounded;
const realloc_addr = w.VirtualAlloc(
@intToPtr(*c_void, old_addr_end_rounded),
additional_size,
w.MEM_COMMIT | w.MEM_RESERVE,
w.PAGE_READWRITE,
) orelse {
// Committing new pages at the end of the existing allocation
// failed, we need to try a new one.
const new_alloc_mem = try alloc(allocator, new_size, new_align);
@memcpy(new_alloc_mem.ptr, old_mem.ptr, old_mem.len);
if (w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE) == 0) unreachable;
return new_alloc_mem;
};
assert(@ptrToInt(realloc_addr) == old_addr_end_rounded);
return @ptrCast([*]u8, old_mem.ptr)[0..new_size];
},
else => @compileError("Unsupported OS"),
}
}
};
pub const HeapAllocator = switch (builtin.os) {
.windows => struct {
allocator: Allocator,
heap_handle: ?HeapHandle,
const HeapHandle = os.windows.HANDLE;
pub fn init() HeapAllocator {
return HeapAllocator{
.allocator = Allocator{
.reallocFn = realloc,
.shrinkFn = shrink,
},
.heap_handle = null,
};
}
pub fn deinit(self: *HeapAllocator) void {
if (self.heap_handle) |heap_handle| {
_ = os.windows.HeapDestroy(heap_handle);
}
}
fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
if (n == 0)
return (([*]u8)(undefined))[0..0];
const amt = n + alignment + @sizeOf(usize);
const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
const heap_handle = optional_heap_handle orelse blk: {
const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0;
const hh = os.windows.HeapCreate(options, amt, 0) orelse return error.OutOfMemory;
const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh;
_ = os.windows.HeapDestroy(hh);
break :blk other_hh.?; // can't be null because of the cmpxchg
};
const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
const root_addr = @ptrToInt(ptr);
const adjusted_addr = mem.alignForward(root_addr, alignment);
const record_addr = adjusted_addr + n;
@intToPtr(*align(1) usize, record_addr).* = root_addr;
return @intToPtr([*]u8, adjusted_addr)[0..n];
}
fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
return realloc(allocator, old_mem, old_align, new_size, new_align) catch {
const old_adjusted_addr = @ptrToInt(old_mem.ptr);
const old_record_addr = old_adjusted_addr + old_mem.len;
const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
const old_ptr = @intToPtr(*c_void, root_addr);
const new_record_addr = old_record_addr - new_size + old_mem.len;
@intToPtr(*align(1) usize, new_record_addr).* = root_addr;
return old_mem[0..new_size];
};
}
fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
if (old_mem.len == 0) return alloc(allocator, new_size, new_align);
const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
const old_adjusted_addr = @ptrToInt(old_mem.ptr);
const old_record_addr = old_adjusted_addr + old_mem.len;
const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
const old_ptr = @intToPtr(*c_void, root_addr);
if (new_size == 0) {
if (os.windows.HeapFree(self.heap_handle.?, 0, old_ptr) == 0) unreachable;
return old_mem[0..0];
}
const amt = new_size + new_align + @sizeOf(usize);
const new_ptr = os.windows.HeapReAlloc(
self.heap_handle.?,
0,
old_ptr,
amt,
) orelse return error.OutOfMemory;
const offset = old_adjusted_addr - root_addr;
const new_root_addr = @ptrToInt(new_ptr);
var new_adjusted_addr = new_root_addr + offset;
const offset_is_valid = new_adjusted_addr + new_size + @sizeOf(usize) <= new_root_addr + amt;
const offset_is_aligned = new_adjusted_addr % new_align == 0;
if (!offset_is_valid or !offset_is_aligned) {
// If HeapReAlloc didn't happen to move the memory to the new alignment,
// or the memory starting at the old offset would be outside of the new allocation,
// then we need to copy the memory to a valid aligned address and use that
const new_aligned_addr = mem.alignForward(new_root_addr, new_align);
@memcpy(@intToPtr([*]u8, new_aligned_addr), @intToPtr([*]u8, new_adjusted_addr), std.math.min(old_mem.len, new_size));
new_adjusted_addr = new_aligned_addr;
}
const new_record_addr = new_adjusted_addr + new_size;
@intToPtr(*align(1) usize, new_record_addr).* = new_root_addr;
return @intToPtr([*]u8, new_adjusted_addr)[0..new_size];
}
},
else => @compileError("Unsupported OS"),
};
/// This allocator takes an existing allocator, wraps it, and provides an interface
/// where you can allocate without freeing, and then free it all together.
pub const ArenaAllocator = struct {
@ -250,7 +428,7 @@ pub const ArenaAllocator = struct {
return error.OutOfMemory;
} else {
const result = try alloc(allocator, new_size, new_align);
mem.copy(u8, result, old_mem);
@memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
return result;
}
}
@ -306,6 +484,103 @@ pub const FixedBufferAllocator = struct {
} else if (new_size <= old_mem.len and new_align <= old_align) {
// We can't do anything with the memory, so tell the client to keep it.
return error.OutOfMemory;
} else {
const result = try alloc(allocator, new_size, new_align);
@memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
return result;
}
}
fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
return old_mem[0..new_size];
}
};
// FIXME: Exposed LLVM intrinsics is a bug
// See: https://github.com/ziglang/zig/issues/2291
extern fn @"llvm.wasm.memory.size.i32"(u32) u32;
extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32;
pub const wasm_allocator = &wasm_allocator_state.allocator;
var wasm_allocator_state = WasmAllocator{
.allocator = Allocator{
.reallocFn = WasmAllocator.realloc,
.shrinkFn = WasmAllocator.shrink,
},
.start_ptr = undefined,
.num_pages = 0,
.end_index = 0,
};
const WasmAllocator = struct {
allocator: Allocator,
start_ptr: [*]u8,
num_pages: usize,
end_index: usize,
comptime {
if (builtin.arch != .wasm32) {
@compileError("WasmAllocator is only available for wasm32 arch");
}
}
fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 {
const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
const addr = @ptrToInt(self.start_ptr) + self.end_index;
const adjusted_addr = mem.alignForward(addr, alignment);
const adjusted_index = self.end_index + (adjusted_addr - addr);
const new_end_index = adjusted_index + size;
if (new_end_index > self.num_pages * os.page_size) {
const required_memory = new_end_index - (self.num_pages * os.page_size);
var num_pages: usize = required_memory / os.page_size;
if (required_memory % os.page_size != 0) {
num_pages += 1;
}
const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, num_pages));
if (prev_page == -1) {
return error.OutOfMemory;
}
self.num_pages += num_pages;
}
const result = self.start_ptr[adjusted_index..new_end_index];
self.end_index = new_end_index;
return result;
}
// Check if memory is the last "item" and is aligned correctly
fn is_last_item(allocator: *Allocator, memory: []u8, alignment: u29) bool {
const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
return memory.ptr == self.start_ptr + self.end_index - memory.len and mem.alignForward(@ptrToInt(memory.ptr), alignment) == @ptrToInt(memory.ptr);
}
fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
// Initialize start_ptr at the first realloc
if (self.num_pages == 0) {
self.start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * os.page_size);
}
if (is_last_item(allocator, old_mem, new_align)) {
const start_index = self.end_index - old_mem.len;
const new_end_index = start_index + new_size;
if (new_end_index > self.num_pages * os.page_size) {
_ = try alloc(allocator, new_end_index - self.end_index, new_align);
}
const result = self.start_ptr[start_index..new_end_index];
self.end_index = new_end_index;
return result;
} else if (new_size <= old_mem.len and new_align <= old_align) {
return error.OutOfMemory;
} else {
const result = try alloc(allocator, new_size, new_align);
mem.copy(u8, result, old_mem);
@ -360,7 +635,7 @@ pub const ThreadSafeFixedBufferAllocator = blk: {
return error.OutOfMemory;
} else {
const result = try alloc(allocator, new_size, new_align);
mem.copy(u8, result, old_mem);
@memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
return result;
}
}
@ -470,6 +745,31 @@ test "DirectAllocator" {
try testAllocator(allocator);
try testAllocatorAligned(allocator, 16);
try testAllocatorLargeAlignment(allocator);
try testAllocatorAlignedShrink(allocator);
if (builtin.os == .windows) {
// Trying really large alignment. As mentionned in the implementation,
// VirtualAlloc returns 64K aligned addresses. We want to make sure
// DirectAllocator works beyond that, as it's not tested by
// `testAllocatorLargeAlignment`.
const slice = try allocator.alignedAlloc(u8, 1 << 20, 128);
slice[0] = 0x12;
slice[127] = 0x34;
allocator.free(slice);
}
}
test "HeapAllocator" {
if (builtin.os == .windows) {
var heap_allocator = HeapAllocator.init();
defer heap_allocator.deinit();
const allocator = &heap_allocator.allocator;
try testAllocator(allocator);
try testAllocatorAligned(allocator, 16);
try testAllocatorLargeAlignment(allocator);
try testAllocatorAlignedShrink(allocator);
}
}
test "ArenaAllocator" {
@ -482,15 +782,17 @@ test "ArenaAllocator" {
try testAllocator(&arena_allocator.allocator);
try testAllocatorAligned(&arena_allocator.allocator, 16);
try testAllocatorLargeAlignment(&arena_allocator.allocator);
try testAllocatorAlignedShrink(&arena_allocator.allocator);
}
var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined;
var test_fixed_buffer_allocator_memory: [80000 * @sizeOf(u64)]u8 = undefined;
test "FixedBufferAllocator" {
var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
try testAllocator(&fixed_buffer_allocator.allocator);
try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator);
}
test "FixedBufferAllocator Reuse memory on realloc" {
@ -528,6 +830,7 @@ test "ThreadSafeFixedBufferAllocator" {
try testAllocator(&fixed_buffer_allocator.allocator);
try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator);
}
fn testAllocator(allocator: *mem.Allocator) !void {
@ -610,3 +913,32 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo
allocator.free(slice);
}
fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!void {
var debug_buffer: [1000]u8 = undefined;
const debug_allocator = &FixedBufferAllocator.init(&debug_buffer).allocator;
const alloc_size = os.page_size * 2 + 50;
var slice = try allocator.alignedAlloc(u8, 16, alloc_size);
defer allocator.free(slice);
var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator);
// On Windows, VirtualAlloc returns addresses aligned to a 64K boundary,
// which is 16 pages, hence the 32. This test may require to increase
// the size of the allocations feeding the `allocator` parameter if they
// fail, because of this high over-alignment we want to have.
while (@ptrToInt(slice.ptr) == mem.alignForward(@ptrToInt(slice.ptr), os.page_size * 32)) {
try stuff_to_free.append(slice);
slice = try allocator.alignedAlloc(u8, 16, alloc_size);
}
while (stuff_to_free.popOrNull()) |item| {
allocator.free(item);
}
slice[0] = 0x12;
slice[60] = 0x34;
// realloc to a smaller size but with a larger alignment
slice = try allocator.alignedRealloc(slice, os.page_size * 32, alloc_size / 2);
testing.expect(slice[0] == 0x12);
testing.expect(slice[60] == 0x34);
}

View File

@ -36,6 +36,7 @@ pub fn getStdIn() GetStdIoErrs!File {
}
pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
pub const COutStream = @import("io/c_out_stream.zig").COutStream;
pub fn InStream(comptime ReadError: type) type {
return struct {
@ -194,8 +195,8 @@ pub fn InStream(comptime ReadError: type) type {
return mem.readVarInt(ReturnType, bytes, endian);
}
pub fn skipBytes(self: *Self, num_bytes: usize) !void {
var i: usize = 0;
pub fn skipBytes(self: *Self, num_bytes: u64) !void {
var i: u64 = 0;
while (i < num_bytes) : (i += 1) {
_ = try self.readByte();
}
@ -289,7 +290,7 @@ pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptim
var file = try File.openRead(path);
defer file.close();
const size = try file.getEndPos();
const size = try math.cast(usize, try file.getEndPos());
const buf = try allocator.alignedAlloc(u8, A, size);
errdefer allocator.free(buf);
@ -742,7 +743,7 @@ pub fn CountingOutStream(comptime OutStreamError: type) type {
pub const Error = OutStreamError;
pub stream: Stream,
pub bytes_written: usize,
pub bytes_written: u64,
child_stream: *Stream,
pub fn init(child_stream: *Stream) Self {
@ -1089,8 +1090,11 @@ test "io.readLineSliceFrom" {
}
pub const Packing = enum {
Byte, /// Pack data to byte alignment
Bit, /// Pack data to bit alignment
/// Pack data to byte alignment
Byte,
/// Pack data to bit alignment
Bit,
};
/// Creates a deserializer that deserializes types from any stream.
@ -1111,10 +1115,12 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
pub const Stream = InStream(Error);
pub fn init(in_stream: *Stream) Self {
return Self{ .in_stream = switch (packing) {
.Bit => BitInStream(endian, Stream.Error).init(in_stream),
.Byte => in_stream,
} };
return Self{
.in_stream = switch (packing) {
.Bit => BitInStream(endian, Stream.Error).init(in_stream),
.Byte => in_stream,
},
};
}
pub fn alignToByte(self: *Self) void {
@ -1281,7 +1287,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
ptr.* = null;
return;
}
ptr.* = OC(undefined); //make it non-null so the following .? is guaranteed safe
const val_ptr = &ptr.*.?;
try self.deserializeInto(val_ptr);
@ -1320,10 +1326,12 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
pub const Stream = OutStream(Error);
pub fn init(out_stream: *Stream) Self {
return Self{ .out_stream = switch (packing) {
.Bit => BitOutStream(endian, Stream.Error).init(out_stream),
.Byte => out_stream,
} };
return Self{
.out_stream = switch (packing) {
.Bit => BitOutStream(endian, Stream.Error).init(out_stream),
.Byte => out_stream,
},
};
}
/// Flushes any unwritten bits to the stream
@ -1447,7 +1455,6 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
test "import io tests" {
comptime {
_ = @import("io_test.zig");
_ = @import("io/test.zig");
}
}

48
std/io/c_out_stream.zig Normal file
View File

@ -0,0 +1,48 @@
const std = @import("../std.zig");
const OutStream = std.io.OutStream;
const builtin = @import("builtin");
const posix = std.os.posix;
/// TODO make std.os.FILE use *FILE when linking libc and this just becomes
/// std.io.FileOutStream because std.os.File.write would do this when linking
/// libc.
pub const COutStream = struct {
pub const Error = std.os.File.WriteError;
pub const Stream = OutStream(Error);
stream: Stream,
c_file: *std.c.FILE,
pub fn init(c_file: *std.c.FILE) COutStream {
return COutStream{
.c_file = c_file,
.stream = Stream{ .writeFn = writeFn },
};
}
fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
const self = @fieldParentPtr(COutStream, "stream", out_stream);
const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, self.c_file);
if (amt_written == bytes.len) return;
// TODO errno on windows. should we have a posix layer for windows?
if (builtin.os == .windows) {
return error.InputOutput;
}
const errno = std.c._errno().*;
switch (errno) {
0 => unreachable,
posix.EINVAL => unreachable,
posix.EFAULT => unreachable,
posix.EAGAIN => unreachable, // this is a blocking API
posix.EBADF => unreachable, // always a race condition
posix.EDESTADDRREQ => unreachable, // connect was never called
posix.EDQUOT => return error.DiskQuota,
posix.EFBIG => return error.FileTooBig,
posix.EIO => return error.InputOutput,
posix.ENOSPC => return error.NoSpaceLeft,
posix.EPERM => return error.AccessDenied,
posix.EPIPE => return error.BrokenPipe,
else => return std.os.unexpectedErrorPosix(@intCast(usize, errno)),
}
}
};

View File

@ -7,25 +7,25 @@ pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType
pub const SeekError = SeekErrorType;
pub const GetSeekPosError = GetSeekPosErrorType;
seekToFn: fn (self: *Self, pos: usize) SeekError!void,
seekForwardFn: fn (self: *Self, pos: isize) SeekError!void,
seekToFn: fn (self: *Self, pos: u64) SeekError!void,
seekForwardFn: fn (self: *Self, pos: i64) SeekError!void,
getPosFn: fn (self: *Self) GetSeekPosError!usize,
getEndPosFn: fn (self: *Self) GetSeekPosError!usize,
getPosFn: fn (self: *Self) GetSeekPosError!u64,
getEndPosFn: fn (self: *Self) GetSeekPosError!u64,
pub fn seekTo(self: *Self, pos: usize) SeekError!void {
pub fn seekTo(self: *Self, pos: u64) SeekError!void {
return self.seekToFn(self, pos);
}
pub fn seekForward(self: *Self, amt: isize) SeekError!void {
pub fn seekForward(self: *Self, amt: i64) SeekError!void {
return self.seekForwardFn(self, amt);
}
pub fn getEndPos(self: *Self) GetSeekPosError!usize {
pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
return self.getEndPosFn(self);
}
pub fn getPos(self: *Self) GetSeekPosError!usize {
pub fn getPos(self: *Self) GetSeekPosError!u64 {
return self.getPosFn(self);
}
};

View File

@ -1,4 +1,4 @@
const std = @import("std.zig");
const std = @import("../std.zig");
const io = std.io;
const meta = std.meta;
const trait = std.trait;
@ -589,3 +589,14 @@ test "Deserializer bad data" {
try testBadData(.Big, .Bit);
try testBadData(.Little, .Bit);
}
test "c out stream" {
if (!builtin.link_libc) return error.SkipZigTest;
const filename = c"tmp_io_test_file.txt";
const out_file = std.c.fopen(filename, c"w") orelse return error.UnableToOpenTestFile;
defer std.os.deleteFileC(filename) catch {};
const out_stream = &io.COutStream.init(out_file).stream;
try out_stream.print("hi: {}\n", i32(123));
}

View File

@ -1400,3 +1400,7 @@ test "json.parser.dynamic" {
const double = image.Object.get("double").?.value;
testing.expect(double.Float == 1.3412);
}
test "import more json tests" {
_ = @import("json/test.zig");
}

1904
std/json/test.zig Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,17 @@
// Special Cases:
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// - acos(x) = nan if x < -1 or x > 1
// https://git.musl-libc.org/cgit/musl/tree/src/math/acosf.c
// https://git.musl-libc.org/cgit/musl/tree/src/math/acos.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
/// Returns the arc-cosine of x.
///
/// Special cases:
/// - acos(x) = nan if x < -1 or x > 1
pub fn acos(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {

View File

@ -1,13 +1,19 @@
// Special Cases:
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// - acosh(x) = snan if x < 1
// - acosh(nan) = nan
// https://git.musl-libc.org/cgit/musl/tree/src/math/acoshf.c
// https://git.musl-libc.org/cgit/musl/tree/src/math/acosh.c
const builtin = @import("builtin");
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
/// Returns the hyperbolic arc-cosine of x.
///
/// Special cases:
/// - acosh(x) = snan if x < 1
/// - acosh(nan) = nan
pub fn acosh(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {

View File

@ -1,12 +1,18 @@
// Special Cases:
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// - asin(+-0) = +-0
// - asin(x) = nan if x < -1 or x > 1
// https://git.musl-libc.org/cgit/musl/tree/src/math/asinf.c
// https://git.musl-libc.org/cgit/musl/tree/src/math/asin.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
/// Returns the arc-sin of x.
///
/// Special Cases:
/// - asin(+-0) = +-0
/// - asin(x) = nan if x < -1 or x > 1
pub fn asin(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {

View File

@ -1,14 +1,20 @@
// Special Cases:
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// - asinh(+-0) = +-0
// - asinh(+-inf) = +-inf
// - asinh(nan) = nan
// https://git.musl-libc.org/cgit/musl/tree/src/math/asinhf.c
// https://git.musl-libc.org/cgit/musl/tree/src/math/asinh.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
/// Returns the hyperbolic arc-sin of x.
///
/// Special Cases:
/// - asinh(+-0) = +-0
/// - asinh(+-inf) = +-inf
/// - asinh(nan) = nan
pub fn asinh(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {

View File

@ -1,12 +1,18 @@
// Special Cases:
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// - atan(+-0) = +-0
// - atan(+-inf) = +-pi/2
// https://git.musl-libc.org/cgit/musl/tree/src/math/atanf.c
// https://git.musl-libc.org/cgit/musl/tree/src/math/atan.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
/// Returns the arc-tangent of x.
///
/// Special Cases:
/// - atan(+-0) = +-0
/// - atan(+-inf) = +-pi/2
pub fn atan(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {

View File

@ -1,27 +1,33 @@
// Special Cases:
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// atan2(y, nan) = nan
// atan2(nan, x) = nan
// atan2(+0, x>=0) = +0
// atan2(-0, x>=0) = -0
// atan2(+0, x<=-0) = +pi
// atan2(-0, x<=-0) = -pi
// atan2(y>0, 0) = +pi/2
// atan2(y<0, 0) = -pi/2
// atan2(+inf, +inf) = +pi/4
// atan2(-inf, +inf) = -pi/4
// atan2(+inf, -inf) = 3pi/4
// atan2(-inf, -inf) = -3pi/4
// atan2(y, +inf) = 0
// atan2(y>0, -inf) = +pi
// atan2(y<0, -inf) = -pi
// atan2(+inf, x) = +pi/2
// atan2(-inf, x) = -pi/2
// https://git.musl-libc.org/cgit/musl/tree/src/math/atan2f.c
// https://git.musl-libc.org/cgit/musl/tree/src/math/atan2.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
/// Returns the arc-tangent of y/x.
///
/// Special Cases:
/// - atan2(y, nan) = nan
/// - atan2(nan, x) = nan
/// - atan2(+0, x>=0) = +0
/// - atan2(-0, x>=0) = -0
/// - atan2(+0, x<=-0) = +pi
/// - atan2(-0, x<=-0) = -pi
/// - atan2(y>0, 0) = +pi/2
/// - atan2(y<0, 0) = -pi/2
/// - atan2(+inf, +inf) = +pi/4
/// - atan2(-inf, +inf) = -pi/4
/// - atan2(+inf, -inf) = 3pi/4
/// - atan2(-inf, -inf) = -3pi/4
/// - atan2(y, +inf) = 0
/// - atan2(y>0, -inf) = +pi
/// - atan2(y<0, -inf) = -pi
/// - atan2(+inf, x) = +pi/2
/// - atan2(-inf, x) = -pi/2
pub fn atan2(comptime T: type, y: T, x: T) T {
return switch (T) {
f32 => atan2_32(y, x),

View File

@ -1,14 +1,20 @@
// Special Cases:
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// - atanh(+-1) = +-inf with signal
// - atanh(x) = nan if |x| > 1 with signal
// - atanh(nan) = nan
// https://git.musl-libc.org/cgit/musl/tree/src/math/atanhf.c
// https://git.musl-libc.org/cgit/musl/tree/src/math/atanh.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
/// Returns the hyperbolic arc-tangent of x.
///
/// Special Cases:
/// - atanh(+-1) = +-inf with signal
/// - atanh(x) = nan if |x| > 1 with signal
/// - atanh(nan) = nan
pub fn atanh(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {

View File

@ -1,5 +1,7 @@
pub use @import("big/int.zig");
pub use @import("big/rational.zig");
test "math.big" {
_ = @import("big/int.zig");
_ = @import("big/rational.zig");
}

File diff suppressed because it is too large Load Diff

938
std/math/big/rational.zig Normal file
View File

@ -0,0 +1,938 @@
const std = @import("../../std.zig");
const builtin = @import("builtin");
const debug = std.debug;
const math = std.math;
const mem = std.mem;
const testing = std.testing;
const Allocator = mem.Allocator;
const ArrayList = std.ArrayList;
const TypeId = builtin.TypeId;
const bn = @import("int.zig");
const Limb = bn.Limb;
const DoubleLimb = bn.DoubleLimb;
const Int = bn.Int;
/// An arbitrary-precision rational number.
///
/// Memory is allocated as needed for operations to ensure full precision is kept. The precision
/// of a Rational is only bounded by memory.
///
/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers,
/// gcd(p, q) = 1 always.
pub const Rational = struct {
/// Numerator. Determines the sign of the Rational.
p: Int,
/// Denominator. Sign is ignored.
q: Int,
/// Create a new Rational. A small amount of memory will be allocated on initialization.
/// This will be 2 * Int.default_capacity.
pub fn init(a: *Allocator) !Rational {
return Rational{
.p = try Int.init(a),
.q = try Int.initSet(a, 1),
};
}
/// Frees all memory associated with a Rational.
pub fn deinit(self: *Rational) void {
self.p.deinit();
self.q.deinit();
}
/// Set a Rational from a primitive integer type.
pub fn setInt(self: *Rational, a: var) !void {
try self.p.set(a);
try self.q.set(1);
}
/// Set a Rational from a string of the form `A/B` where A and B are base-10 integers.
pub fn setFloatString(self: *Rational, str: []const u8) !void {
// TODO: Accept a/b fractions and exponent form
if (str.len == 0) {
return error.InvalidFloatString;
}
const State = enum {
Integer,
Fractional,
};
var state = State.Integer;
var point: ?usize = null;
var start: usize = 0;
if (str[0] == '-') {
start += 1;
}
for (str) |c, i| {
switch (state) {
State.Integer => {
switch (c) {
'.' => {
state = State.Fractional;
point = i;
},
'0'...'9' => {
// okay
},
else => {
return error.InvalidFloatString;
},
}
},
State.Fractional => {
switch (c) {
'0'...'9' => {
// okay
},
else => {
return error.InvalidFloatString;
},
}
},
}
}
// TODO: batch the multiplies by 10
if (point) |i| {
try self.p.setString(10, str[0..i]);
const base = Int.initFixed(([]Limb{10})[0..]);
var j: usize = start;
while (j < str.len - i - 1) : (j += 1) {
try self.p.mul(self.p, base);
}
try self.q.setString(10, str[i + 1 ..]);
try self.p.add(self.p, self.q);
try self.q.set(1);
var k: usize = i + 1;
while (k < str.len) : (k += 1) {
try self.q.mul(self.q, base);
}
try self.reduce();
} else {
try self.p.setString(10, str[0..]);
try self.q.set(1);
}
}
/// Set a Rational from a floating-point value. The rational will have enough precision to
/// completely represent the provided float.
pub fn setFloat(self: *Rational, comptime T: type, f: T) !void {
// Translated from golang.go/src/math/big/rat.go.
debug.assert(@typeId(T) == builtin.TypeId.Float);
const UnsignedIntType = @IntType(false, T.bit_count);
const f_bits = @bitCast(UnsignedIntType, f);
const exponent_bits = math.floatExponentBits(T);
const exponent_bias = (1 << (exponent_bits - 1)) - 1;
const mantissa_bits = math.floatMantissaBits(T);
const exponent_mask = (1 << exponent_bits) - 1;
const mantissa_mask = (1 << mantissa_bits) - 1;
var exponent = @intCast(i16, (f_bits >> mantissa_bits) & exponent_mask);
var mantissa = f_bits & mantissa_mask;
switch (exponent) {
exponent_mask => {
return error.NonFiniteFloat;
},
0 => {
// denormal
exponent -= exponent_bias - 1;
},
else => {
// normal
mantissa |= 1 << mantissa_bits;
exponent -= exponent_bias;
},
}
var shift: i16 = mantissa_bits - exponent;
// factor out powers of two early from rational
while (mantissa & 1 == 0 and shift > 0) {
mantissa >>= 1;
shift -= 1;
}
try self.p.set(mantissa);
self.p.setSign(f >= 0);
try self.q.set(1);
if (shift >= 0) {
try self.q.shiftLeft(self.q, @intCast(usize, shift));
} else {
try self.p.shiftLeft(self.p, @intCast(usize, -shift));
}
try self.reduce();
}
/// Return a floating-point value that is the closest value to a Rational.
///
/// The result may not be exact if the Rational is too precise or too large for the
/// target type.
pub fn toFloat(self: Rational, comptime T: type) !T {
// Translated from golang.go/src/math/big/rat.go.
// TODO: Indicate whether the result is not exact.
debug.assert(@typeId(T) == builtin.TypeId.Float);
const fsize = T.bit_count;
const BitReprType = @IntType(false, T.bit_count);
const msize = math.floatMantissaBits(T);
const msize1 = msize + 1;
const msize2 = msize1 + 1;
const esize = math.floatExponentBits(T);
const ebias = (1 << (esize - 1)) - 1;
const emin = 1 - ebias;
const emax = ebias;
if (self.p.eqZero()) {
return 0;
}
// 1. left-shift a or sub so that a/b is in [1 << msize1, 1 << (msize2 + 1)]
var exp = @intCast(isize, self.p.bitCountTwosComp()) - @intCast(isize, self.q.bitCountTwosComp());
var a2 = try self.p.clone();
defer a2.deinit();
var b2 = try self.q.clone();
defer b2.deinit();
const shift = msize2 - exp;
if (shift >= 0) {
try a2.shiftLeft(a2, @intCast(usize, shift));
} else {
try b2.shiftLeft(b2, @intCast(usize, -shift));
}
// 2. compute quotient and remainder
var q = try Int.init(self.p.allocator.?);
defer q.deinit();
// unused
var r = try Int.init(self.p.allocator.?);
defer r.deinit();
try Int.divTrunc(&q, &r, a2, b2);
var mantissa = extractLowBits(q, BitReprType);
var have_rem = r.len() > 0;
// 3. q didn't fit in msize2 bits, redo division b2 << 1
if (mantissa >> msize2 == 1) {
if (mantissa & 1 == 1) {
have_rem = true;
}
mantissa >>= 1;
exp += 1;
}
if (mantissa >> msize1 != 1) {
// NOTE: This can be hit if the limb size is small (u8/16).
@panic("unexpected bits in result");
}
// 4. Rounding
if (emin - msize <= exp and exp <= emin) {
// denormal
const shift1 = @intCast(math.Log2Int(BitReprType), emin - (exp - 1));
const lost_bits = mantissa & ((@intCast(BitReprType, 1) << shift1) - 1);
have_rem = have_rem or lost_bits != 0;
mantissa >>= shift1;
exp = 2 - ebias;
}
// round q using round-half-to-even
var exact = !have_rem;
if (mantissa & 1 != 0) {
exact = false;
if (have_rem or (mantissa & 2 != 0)) {
mantissa += 1;
if (mantissa >= 1 << msize2) {
// 11...1 => 100...0
mantissa >>= 1;
exp += 1;
}
}
}
mantissa >>= 1;
const f = math.scalbn(@intToFloat(T, mantissa), @intCast(i32, exp - msize1));
if (math.isInf(f)) {
exact = false;
}
return if (self.p.isPositive()) f else -f;
}
/// Set a rational from an integer ratio.
pub fn setRatio(self: *Rational, p: var, q: var) !void {
try self.p.set(p);
try self.q.set(q);
self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
self.q.setSign(true);
try self.reduce();
if (self.q.eqZero()) {
@panic("cannot set rational with denominator = 0");
}
}
/// Set a Rational directly from an Int.
pub fn copyInt(self: *Rational, a: Int) !void {
try self.p.copy(a);
try self.q.set(1);
}
/// Set a Rational directly from a ratio of two Int's.
pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {
try self.p.copy(a);
try self.q.copy(b);
self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
self.q.setSign(true);
try self.reduce();
}
/// Make a Rational positive.
pub fn abs(r: *Rational) void {
r.p.abs();
}
/// Negate the sign of a Rational.
pub fn negate(r: *Rational) void {
r.p.negate();
}
/// Efficiently swap a Rational with another. This swaps the limb pointers and a full copy is not
/// performed. The address of the limbs field will not be the same after this function.
pub fn swap(r: *Rational, other: *Rational) void {
r.p.swap(&other.p);
r.q.swap(&other.q);
}
/// Returns -1, 0, 1 if a < b, a == b or a > b respectively.
pub fn cmp(a: Rational, b: Rational) !i8 {
return cmpInternal(a, b, true);
}
/// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
pub fn cmpAbs(a: Rational, b: Rational) !i8 {
return cmpInternal(a, b, false);
}
// p/q > x/y iff p*y > x*q
fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !i8 {
// TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid
// the memory allocations here?
var q = try Int.init(a.p.allocator.?);
defer q.deinit();
var p = try Int.init(b.p.allocator.?);
defer p.deinit();
try q.mul(a.p, b.q);
try p.mul(b.p, a.q);
return if (is_abs) q.cmpAbs(p) else q.cmp(p);
}
/// rma = a + b.
///
/// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
///
/// Returns an error if memory could not be allocated.
pub fn add(rma: *Rational, a: Rational, b: Rational) !void {
var r = rma;
var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr;
var sr: Rational = undefined;
if (aliased) {
sr = try Rational.init(rma.p.allocator.?);
r = &sr;
aliased = true;
}
defer if (aliased) {
rma.swap(r);
r.deinit();
};
try r.p.mul(a.p, b.q);
try r.q.mul(b.p, a.q);
try r.p.add(r.p, r.q);
try r.q.mul(a.q, b.q);
try r.reduce();
}
/// rma = a - b.
///
/// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
///
/// Returns an error if memory could not be allocated.
pub fn sub(rma: *Rational, a: Rational, b: Rational) !void {
var r = rma;
var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr;
var sr: Rational = undefined;
if (aliased) {
sr = try Rational.init(rma.p.allocator.?);
r = &sr;
aliased = true;
}
defer if (aliased) {
rma.swap(r);
r.deinit();
};
try r.p.mul(a.p, b.q);
try r.q.mul(b.p, a.q);
try r.p.sub(r.p, r.q);
try r.q.mul(a.q, b.q);
try r.reduce();
}
/// rma = a * b.
///
/// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
///
/// Returns an error if memory could not be allocated.
pub fn mul(r: *Rational, a: Rational, b: Rational) !void {
try r.p.mul(a.p, b.p);
try r.q.mul(a.q, b.q);
try r.reduce();
}
/// rma = a / b.
///
/// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
///
/// Returns an error if memory could not be allocated.
pub fn div(r: *Rational, a: Rational, b: Rational) !void {
if (b.p.eqZero()) {
@panic("division by zero");
}
try r.p.mul(a.p, b.q);
try r.q.mul(b.p, a.q);
try r.reduce();
}
/// Invert the numerator and denominator fields of a Rational. p/q => q/p.
pub fn invert(r: *Rational) void {
Int.swap(&r.p, &r.q);
}
// reduce r/q such that gcd(r, q) = 1
fn reduce(r: *Rational) !void {
var a = try Int.init(r.p.allocator.?);
defer a.deinit();
const sign = r.p.isPositive();
r.p.abs();
try gcd(&a, r.p, r.q);
r.p.setSign(sign);
const one = Int.initFixed(([]Limb{1})[0..]);
if (a.cmp(one) != 0) {
var unused = try Int.init(r.p.allocator.?);
defer unused.deinit();
// TODO: divexact would be useful here
// TODO: don't copy r.q for div
try Int.divTrunc(&r.p, &unused, r.p, a);
try Int.divTrunc(&r.q, &unused, r.q, a);
}
}
};
const SignedDoubleLimb = @IntType(true, DoubleLimb.bit_count);
fn gcd(rma: *Int, x: Int, y: Int) !void {
rma.assertWritable();
var r = rma;
var aliased = rma.limbs.ptr == x.limbs.ptr or rma.limbs.ptr == y.limbs.ptr;
var sr: Int = undefined;
if (aliased) {
sr = try Int.initCapacity(rma.allocator.?, math.max(x.len(), y.len()));
r = &sr;
aliased = true;
}
defer if (aliased) {
rma.swap(r);
r.deinit();
};
try gcdLehmer(r, x, y);
}
// Storage must live for the lifetime of the returned value
fn FixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Int {
std.debug.assert(storage.len >= 2);
var A_is_positive = A >= 0;
const Au = @intCast(DoubleLimb, if (A < 0) -A else A);
storage[0] = @truncate(Limb, Au);
storage[1] = @truncate(Limb, Au >> Limb.bit_count);
var Ap = Int.initFixed(storage[0..2]);
Ap.setSign(A_is_positive);
return Ap;
}
fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void {
var x = try xa.clone();
x.abs();
defer x.deinit();
var y = try ya.clone();
y.abs();
defer y.deinit();
if (x.cmp(y) < 0) {
x.swap(&y);
}
var T = try Int.init(r.allocator.?);
defer T.deinit();
while (y.len() > 1) {
debug.assert(x.isPositive() and y.isPositive());
debug.assert(x.len() >= y.len());
var xh: SignedDoubleLimb = x.limbs[x.len() - 1];
var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1];
var A: SignedDoubleLimb = 1;
var B: SignedDoubleLimb = 0;
var C: SignedDoubleLimb = 0;
var D: SignedDoubleLimb = 1;
while (yh + C != 0 and yh + D != 0) {
const q = @divFloor(xh + A, yh + C);
const qp = @divFloor(xh + B, yh + D);
if (q != qp) {
break;
}
var t = A - q * C;
A = C;
C = t;
t = B - q * D;
B = D;
D = t;
t = xh - q * yh;
xh = yh;
yh = t;
}
if (B == 0) {
// T = x % y, r is unused
try Int.divTrunc(r, &T, x, y);
debug.assert(T.isPositive());
x.swap(&y);
y.swap(&T);
} else {
var storage: [8]Limb = undefined;
const Ap = FixedIntFromSignedDoubleLimb(A, storage[0..2]);
const Bp = FixedIntFromSignedDoubleLimb(B, storage[2..4]);
const Cp = FixedIntFromSignedDoubleLimb(C, storage[4..6]);
const Dp = FixedIntFromSignedDoubleLimb(D, storage[6..8]);
// T = Ax + By
try r.mul(x, Ap);
try T.mul(y, Bp);
try T.add(r.*, T);
// u = Cx + Dy, r as u
try x.mul(x, Cp);
try r.mul(y, Dp);
try r.add(x, r.*);
x.swap(&T);
y.swap(r);
}
}
// euclidean algorithm
debug.assert(x.cmp(y) >= 0);
while (!y.eqZero()) {
try Int.divTrunc(&T, r, x, y);
x.swap(&y);
y.swap(r);
}
r.swap(&x);
}
var buffer: [64 * 8192]u8 = undefined;
var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
var al = &fixed.allocator;
test "big.rational gcd non-one small" {
var a = try Int.initSet(al, 17);
var b = try Int.initSet(al, 97);
var r = try Int.init(al);
try gcd(&r, a, b);
testing.expect((try r.to(u32)) == 1);
}
test "big.rational gcd non-one small" {
var a = try Int.initSet(al, 4864);
var b = try Int.initSet(al, 3458);
var r = try Int.init(al);
try gcd(&r, a, b);
testing.expect((try r.to(u32)) == 38);
}
test "big.rational gcd non-one large" {
var a = try Int.initSet(al, 0xffffffffffffffff);
var b = try Int.initSet(al, 0xffffffffffffffff7777);
var r = try Int.init(al);
try gcd(&r, a, b);
testing.expect((try r.to(u32)) == 4369);
}
test "big.rational gcd large multi-limb result" {
var a = try Int.initSet(al, 0x12345678123456781234567812345678123456781234567812345678);
var b = try Int.initSet(al, 0x12345671234567123456712345671234567123456712345671234567);
var r = try Int.init(al);
try gcd(&r, a, b);
testing.expect((try r.to(u256)) == 0xf000000ff00000fff0000ffff000fffff00ffffff1);
}
test "big.rational gcd one large" {
var a = try Int.initSet(al, 1897056385327307);
var b = try Int.initSet(al, 2251799813685248);
var r = try Int.init(al);
try gcd(&r, a, b);
testing.expect((try r.to(u64)) == 1);
}
fn extractLowBits(a: Int, comptime T: type) T {
testing.expect(@typeId(T) == builtin.TypeId.Int);
if (T.bit_count <= Limb.bit_count) {
return @truncate(T, a.limbs[0]);
} else {
var r: T = 0;
comptime var i: usize = 0;
// Remainder is always 0 since if T.bit_count >= Limb.bit_count -> Limb | T and both
// are powers of two.
inline while (i < T.bit_count / Limb.bit_count) : (i += 1) {
r |= math.shl(T, a.limbs[i], i * Limb.bit_count);
}
return r;
}
}
test "big.rational extractLowBits" {
var a = try Int.initSet(al, 0x11112222333344441234567887654321);
const a1 = extractLowBits(a, u8);
testing.expect(a1 == 0x21);
const a2 = extractLowBits(a, u16);
testing.expect(a2 == 0x4321);
const a3 = extractLowBits(a, u32);
testing.expect(a3 == 0x87654321);
const a4 = extractLowBits(a, u64);
testing.expect(a4 == 0x1234567887654321);
const a5 = extractLowBits(a, u128);
testing.expect(a5 == 0x11112222333344441234567887654321);
}
test "big.rational set" {
var a = try Rational.init(al);
try a.setInt(5);
testing.expect((try a.p.to(u32)) == 5);
testing.expect((try a.q.to(u32)) == 1);
try a.setRatio(7, 3);
testing.expect((try a.p.to(u32)) == 7);
testing.expect((try a.q.to(u32)) == 3);
try a.setRatio(9, 3);
testing.expect((try a.p.to(i32)) == 3);
testing.expect((try a.q.to(i32)) == 1);
try a.setRatio(-9, 3);
testing.expect((try a.p.to(i32)) == -3);
testing.expect((try a.q.to(i32)) == 1);
try a.setRatio(9, -3);
testing.expect((try a.p.to(i32)) == -3);
testing.expect((try a.q.to(i32)) == 1);
try a.setRatio(-9, -3);
testing.expect((try a.p.to(i32)) == 3);
testing.expect((try a.q.to(i32)) == 1);
}
test "big.rational setFloat" {
var a = try Rational.init(al);
try a.setFloat(f64, 2.5);
testing.expect((try a.p.to(i32)) == 5);
testing.expect((try a.q.to(i32)) == 2);
try a.setFloat(f32, -2.5);
testing.expect((try a.p.to(i32)) == -5);
testing.expect((try a.q.to(i32)) == 2);
try a.setFloat(f32, 3.141593);
// = 3.14159297943115234375
testing.expect((try a.p.to(u32)) == 3294199);
testing.expect((try a.q.to(u32)) == 1048576);
try a.setFloat(f64, 72.141593120712409172417410926841290461290467124);
// = 72.1415931207124145885245525278151035308837890625
testing.expect((try a.p.to(u128)) == 5076513310880537);
testing.expect((try a.q.to(u128)) == 70368744177664);
}
test "big.rational setFloatString" {
var a = try Rational.init(al);
try a.setFloatString("72.14159312071241458852455252781510353");
// = 72.1415931207124145885245525278151035308837890625
testing.expect((try a.p.to(u128)) == 7214159312071241458852455252781510353);
testing.expect((try a.q.to(u128)) == 100000000000000000000000000000000000);
}
test "big.rational toFloat" {
var a = try Rational.init(al);
// = 3.14159297943115234375
try a.setRatio(3294199, 1048576);
testing.expect((try a.toFloat(f64)) == 3.14159297943115234375);
// = 72.1415931207124145885245525278151035308837890625
try a.setRatio(5076513310880537, 70368744177664);
testing.expect((try a.toFloat(f64)) == 72.141593120712409172417410926841290461290467124);
}
test "big.rational set/to Float round-trip" {
var a = try Rational.init(al);
var prng = std.rand.DefaultPrng.init(0x5EED);
var i: usize = 0;
while (i < 512) : (i += 1) {
const r = prng.random.float(f64);
try a.setFloat(f64, r);
testing.expect((try a.toFloat(f64)) == r);
}
}
test "big.rational copy" {
var a = try Rational.init(al);
const b = try Int.initSet(al, 5);
try a.copyInt(b);
testing.expect((try a.p.to(u32)) == 5);
testing.expect((try a.q.to(u32)) == 1);
const c = try Int.initSet(al, 7);
const d = try Int.initSet(al, 3);
try a.copyRatio(c, d);
testing.expect((try a.p.to(u32)) == 7);
testing.expect((try a.q.to(u32)) == 3);
const e = try Int.initSet(al, 9);
const f = try Int.initSet(al, 3);
try a.copyRatio(e, f);
testing.expect((try a.p.to(u32)) == 3);
testing.expect((try a.q.to(u32)) == 1);
}
test "big.rational negate" {
var a = try Rational.init(al);
try a.setInt(-50);
testing.expect((try a.p.to(i32)) == -50);
testing.expect((try a.q.to(i32)) == 1);
a.negate();
testing.expect((try a.p.to(i32)) == 50);
testing.expect((try a.q.to(i32)) == 1);
a.negate();
testing.expect((try a.p.to(i32)) == -50);
testing.expect((try a.q.to(i32)) == 1);
}
test "big.rational abs" {
var a = try Rational.init(al);
try a.setInt(-50);
testing.expect((try a.p.to(i32)) == -50);
testing.expect((try a.q.to(i32)) == 1);
a.abs();
testing.expect((try a.p.to(i32)) == 50);
testing.expect((try a.q.to(i32)) == 1);
a.abs();
testing.expect((try a.p.to(i32)) == 50);
testing.expect((try a.q.to(i32)) == 1);
}
test "big.rational swap" {
var a = try Rational.init(al);
var b = try Rational.init(al);
try a.setRatio(50, 23);
try b.setRatio(17, 3);
testing.expect((try a.p.to(u32)) == 50);
testing.expect((try a.q.to(u32)) == 23);
testing.expect((try b.p.to(u32)) == 17);
testing.expect((try b.q.to(u32)) == 3);
a.swap(&b);
testing.expect((try a.p.to(u32)) == 17);
testing.expect((try a.q.to(u32)) == 3);
testing.expect((try b.p.to(u32)) == 50);
testing.expect((try b.q.to(u32)) == 23);
}
test "big.rational cmp" {
var a = try Rational.init(al);
var b = try Rational.init(al);
try a.setRatio(500, 231);
try b.setRatio(18903, 8584);
testing.expect((try a.cmp(b)) < 0);
try a.setRatio(890, 10);
try b.setRatio(89, 1);
testing.expect((try a.cmp(b)) == 0);
}
test "big.rational add single-limb" {
var a = try Rational.init(al);
var b = try Rational.init(al);
try a.setRatio(500, 231);
try b.setRatio(18903, 8584);
testing.expect((try a.cmp(b)) < 0);
try a.setRatio(890, 10);
try b.setRatio(89, 1);
testing.expect((try a.cmp(b)) == 0);
}
test "big.rational add" {
var a = try Rational.init(al);
var b = try Rational.init(al);
var r = try Rational.init(al);
try a.setRatio(78923, 23341);
try b.setRatio(123097, 12441414);
try a.add(a, b);
try r.setRatio(984786924199, 290395044174);
testing.expect((try a.cmp(r)) == 0);
}
test "big.rational sub" {
var a = try Rational.init(al);
var b = try Rational.init(al);
var r = try Rational.init(al);
try a.setRatio(78923, 23341);
try b.setRatio(123097, 12441414);
try a.sub(a, b);
try r.setRatio(979040510045, 290395044174);
testing.expect((try a.cmp(r)) == 0);
}
test "big.rational mul" {
var a = try Rational.init(al);
var b = try Rational.init(al);
var r = try Rational.init(al);
try a.setRatio(78923, 23341);
try b.setRatio(123097, 12441414);
try a.mul(a, b);
try r.setRatio(571481443, 17082061422);
testing.expect((try a.cmp(r)) == 0);
}
test "big.rational div" {
var a = try Rational.init(al);
var b = try Rational.init(al);
var r = try Rational.init(al);
try a.setRatio(78923, 23341);
try b.setRatio(123097, 12441414);
try a.div(a, b);
try r.setRatio(75531824394, 221015929);
testing.expect((try a.cmp(r)) == 0);
}
test "big.rational div" {
var a = try Rational.init(al);
var r = try Rational.init(al);
try a.setRatio(78923, 23341);
a.invert();
try r.setRatio(23341, 78923);
testing.expect((try a.cmp(r)) == 0);
try a.setRatio(-78923, 23341);
a.invert();
try r.setRatio(-23341, 78923);
testing.expect((try a.cmp(r)) == 0);
}

View File

@ -1,13 +1,19 @@
// Special Cases:
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// - cbrt(+-0) = +-0
// - cbrt(+-inf) = +-inf
// - cbrt(nan) = nan
// https://git.musl-libc.org/cgit/musl/tree/src/math/cbrtf.c
// https://git.musl-libc.org/cgit/musl/tree/src/math/cbrt.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
/// Returns the cube root of x.
///
/// Special Cases:
/// - cbrt(+-0) = +-0
/// - cbrt(+-inf) = +-inf
/// - cbrt(nan) = nan
pub fn cbrt(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {

View File

@ -1,14 +1,20 @@
// Special Cases:
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// - ceil(+-0) = +-0
// - ceil(+-inf) = +-inf
// - ceil(nan) = nan
// https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
// https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c
const builtin = @import("builtin");
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
/// Returns the least integer value greater than of equal to x.
///
/// Special Cases:
/// - ceil(+-0) = +-0
/// - ceil(+-inf) = +-inf
/// - ceil(nan) = nan
pub fn ceil(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {

View File

@ -23,13 +23,18 @@ pub const sqrt = @import("complex/sqrt.zig").sqrt;
pub const tanh = @import("complex/tanh.zig").tanh;
pub const tan = @import("complex/tan.zig").tan;
/// A complex number consisting of a real an imaginary part. T must be a floating-point value.
pub fn Complex(comptime T: type) type {
return struct {
const Self = @This();
/// Real part.
re: T,
/// Imaginary part.
im: T,
/// Create a new Complex number from the given real and imaginary parts.
pub fn new(re: T, im: T) Self {
return Self{
.re = re,
@ -37,6 +42,7 @@ pub fn Complex(comptime T: type) type {
};
}
/// Returns the sum of two complex numbers.
pub fn add(self: Self, other: Self) Self {
return Self{
.re = self.re + other.re,
@ -44,6 +50,7 @@ pub fn Complex(comptime T: type) type {
};
}
/// Returns the subtraction of two complex numbers.
pub fn sub(self: Self, other: Self) Self {
return Self{
.re = self.re - other.re,
@ -51,6 +58,7 @@ pub fn Complex(comptime T: type) type {
};
}
/// Returns the product of two complex numbers.
pub fn mul(self: Self, other: Self) Self {
return Self{
.re = self.re * other.re - self.im * other.im,
@ -58,6 +66,7 @@ pub fn Complex(comptime T: type) type {
};
}
/// Returns the quotient of two complex numbers.
pub fn div(self: Self, other: Self) Self {
const re_num = self.re * other.re + self.im * other.im;
const im_num = self.im * other.re - self.re * other.im;
@ -69,6 +78,7 @@ pub fn Complex(comptime T: type) type {
};
}
/// Returns the complex conjugate of a number.
pub fn conjugate(self: Self) Self {
return Self{
.re = self.re,
@ -76,6 +86,7 @@ pub fn Complex(comptime T: type) type {
};
}
/// Returns the reciprocal of a complex number.
pub fn reciprocal(self: Self) Self {
const m = self.re * self.re + self.im * self.im;
return Self{
@ -84,6 +95,7 @@ pub fn Complex(comptime T: type) type {
};
}
/// Returns the magnitude of a complex number.
pub fn magnitude(self: Self) T {
return math.sqrt(self.re * self.re + self.im * self.im);
}

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
/// Returns the absolute value (modulus) of z.
pub fn abs(z: var) @typeOf(z.re) {
const T = @typeOf(z.re);
return math.hypot(T, z.re, z.im);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
/// Returns the arc-cosine of z.
pub fn acos(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const q = cmath.asin(z);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
/// Returns the hyperbolic arc-cosine of z.
pub fn acosh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const q = cmath.acos(z);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
/// Returns the angular component (in radians) of z.
pub fn arg(z: var) @typeOf(z.re) {
const T = @typeOf(z.re);
return math.atan2(T, z.im, z.re);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
// Returns the arc-sine of z.
pub fn asin(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const x = z.re;

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
/// Returns the hyperbolic arc-sine of z.
pub fn asinh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const q = Complex(T).new(-z.im, z.re);

View File

@ -1,9 +1,16 @@
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// https://git.musl-libc.org/cgit/musl/tree/src/complex/catanf.c
// https://git.musl-libc.org/cgit/musl/tree/src/complex/catan.c
const std = @import("../../std.zig");
const testing = std.testing;
const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
/// Returns the arc-tangent of z.
pub fn atan(z: var) @typeOf(z) {
const T = @typeOf(z.re);
return switch (T) {

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
/// Returns the hyperbolic arc-tangent of z.
pub fn atanh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const q = Complex(T).new(-z.im, z.re);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
/// Returns the complex conjugate of z.
pub fn conj(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
return Complex(T).new(z.re, -z.im);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
/// Returns the cosine of z.
pub fn cos(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const p = Complex(T).new(-z.im, z.re);

View File

@ -1,3 +1,9 @@
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// https://git.musl-libc.org/cgit/musl/tree/src/complex/ccoshf.c
// https://git.musl-libc.org/cgit/musl/tree/src/complex/ccosh.c
const std = @import("../../std.zig");
const testing = std.testing;
const math = std.math;
@ -6,6 +12,7 @@ const Complex = cmath.Complex;
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
/// Returns the hyperbolic arc-cosine of z.
pub fn cosh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
return switch (T) {

View File

@ -1,3 +1,9 @@
// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// https://git.musl-libc.org/cgit/musl/tree/src/complex/cexpf.c
// https://git.musl-libc.org/cgit/musl/tree/src/complex/cexp.c
const std = @import("../../std.zig");
const testing = std.testing;
const math = std.math;
@ -6,6 +12,7 @@ const Complex = cmath.Complex;
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
/// Returns e raised to the power of z (e^z).
pub fn exp(z: var) @typeOf(z) {
const T = @typeOf(z.re);

Some files were not shown because too many files have changed in this diff Show More