aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-02-25 21:04:23 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-02-25 21:04:23 -0700
commit0b58b617998b79a765b54f88fbe90ca2798b3d3e (patch)
treeca6cc4b6bcc2b93166d196049ee49416afe781ad /lib/std
parentdc325669e360f7a9dfa24f85a62fa386529dade6 (diff)
parentfd208d9d5913a0929e444deb97b91092c427bb14 (diff)
downloadzig-0b58b617998b79a765b54f88fbe90ca2798b3d3e.tar.gz
zig-0b58b617998b79a765b54f88fbe90ca2798b3d3e.zip
Merge remote-tracking branch 'origin/master' into llvm12
Conflicts: * src/clang.zig * src/llvm.zig - this file got moved to src/llvm/bindings.zig in master branch so I had to put the new LLVM arch/os enum tags into it. * lib/std/target.zig, src/stage1/target.cpp - haiku had an inconsistency with its default target ABI, gnu vs eabi. In this commit we make it gnu in both places to match the latest changes by @hoanga. * src/translate_c.zig
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/Progress.zig366
-rw-r--r--lib/std/SemanticVersion.zig33
-rw-r--r--lib/std/Thread.zig565
-rw-r--r--lib/std/Thread/AutoResetEvent.zig228
-rw-r--r--lib/std/Thread/Condition.zig194
-rw-r--r--lib/std/Thread/Mutex.zig319
-rw-r--r--lib/std/Thread/ResetEvent.zig297
-rw-r--r--lib/std/Thread/RwLock.zig308
-rw-r--r--lib/std/Thread/Semaphore.zig39
-rw-r--r--lib/std/Thread/StaticResetEvent.zig (renamed from lib/std/reset_event.zig)355
-rw-r--r--lib/std/array_hash_map.zig397
-rw-r--r--lib/std/array_list.zig225
-rw-r--r--lib/std/array_list_sentineled.zig229
-rw-r--r--lib/std/ascii.zig2
-rw-r--r--lib/std/atomic.zig2
-rw-r--r--lib/std/atomic/bool.zig2
-rw-r--r--lib/std/atomic/int.zig2
-rw-r--r--lib/std/atomic/queue.zig14
-rw-r--r--lib/std/atomic/stack.zig2
-rw-r--r--lib/std/auto_reset_event.zig227
-rw-r--r--lib/std/base64.zig16
-rw-r--r--lib/std/buf_map.zig2
-rw-r--r--lib/std/buf_set.zig2
-rw-r--r--lib/std/build.zig475
-rw-r--r--lib/std/build/check_file.zig10
-rw-r--r--lib/std/build/emit_raw.zig6
-rw-r--r--lib/std/build/fmt.zig2
-rw-r--r--lib/std/build/run.zig45
-rw-r--r--lib/std/build/translate_c.zig6
-rw-r--r--lib/std/build/write_file.zig13
-rw-r--r--lib/std/builtin.zig29
-rw-r--r--lib/std/c.zig52
-rw-r--r--lib/std/c/ast.zig12
-rw-r--r--lib/std/c/builtins.zig184
-rw-r--r--lib/std/c/darwin.zig22
-rw-r--r--lib/std/c/dragonfly.zig3
-rw-r--r--lib/std/c/emscripten.zig5
-rw-r--r--lib/std/c/freebsd.zig17
-rw-r--r--lib/std/c/fuchsia.zig5
-rw-r--r--lib/std/c/haiku.zig54
-rw-r--r--lib/std/c/hermit.zig5
-rw-r--r--lib/std/c/linux.zig40
-rw-r--r--lib/std/c/minix.zig2
-rw-r--r--lib/std/c/netbsd.zig21
-rw-r--r--lib/std/c/openbsd.zig15
-rw-r--r--lib/std/c/parse.zig11
-rw-r--r--lib/std/c/solaris.zig2
-rw-r--r--lib/std/c/tokenizer.zig12
-rw-r--r--lib/std/c/windows.zig2
-rw-r--r--lib/std/child_process.zig216
-rw-r--r--lib/std/coff.zig13
-rw-r--r--lib/std/compress.zig4
-rw-r--r--lib/std/compress/deflate.zig4
-rw-r--r--lib/std/compress/gzip.zig2
-rw-r--r--lib/std/compress/zlib.zig2
-rw-r--r--lib/std/comptime_string_map.zig2
-rw-r--r--lib/std/crypto.zig16
-rw-r--r--lib/std/crypto/25519/curve25519.zig6
-rw-r--r--lib/std/crypto/25519/ed25519.zig20
-rw-r--r--lib/std/crypto/25519/edwards25519.zig12
-rw-r--r--lib/std/crypto/25519/field.zig30
-rw-r--r--lib/std/crypto/25519/ristretto255.zig12
-rw-r--r--lib/std/crypto/25519/scalar.zig4
-rw-r--r--lib/std/crypto/25519/x25519.zig8
-rw-r--r--lib/std/crypto/aegis.zig14
-rw-r--r--lib/std/crypto/aes.zig10
-rw-r--r--lib/std/crypto/aes/aesni.zig34
-rw-r--r--lib/std/crypto/aes/armcrypto.zig34
-rw-r--r--lib/std/crypto/aes/soft.zig22
-rw-r--r--lib/std/crypto/aes_gcm.zig2
-rw-r--r--lib/std/crypto/bcrypt.zig12
-rw-r--r--lib/std/crypto/benchmark.zig4
-rw-r--r--lib/std/crypto/blake2.zig192
-rw-r--r--lib/std/crypto/blake3.zig10
-rw-r--r--lib/std/crypto/chacha20.zig14
-rw-r--r--lib/std/crypto/ghash.zig6
-rw-r--r--lib/std/crypto/gimli.zig51
-rw-r--r--lib/std/crypto/hkdf.zig2
-rw-r--r--lib/std/crypto/hmac.zig2
-rw-r--r--lib/std/crypto/md5.zig2
-rw-r--r--lib/std/crypto/modes.zig2
-rw-r--r--lib/std/crypto/pbkdf2.zig2
-rw-r--r--lib/std/crypto/poly1305.zig2
-rw-r--r--lib/std/crypto/salsa20.zig41
-rw-r--r--lib/std/crypto/sha1.zig2
-rw-r--r--lib/std/crypto/sha2.zig2
-rw-r--r--lib/std/crypto/sha3.zig16
-rw-r--r--lib/std/crypto/siphash.zig6
-rw-r--r--lib/std/crypto/test.zig2
-rw-r--r--lib/std/crypto/tlcsprng.zig157
-rw-r--r--lib/std/crypto/utils.zig8
-rw-r--r--lib/std/cstr.zig2
-rw-r--r--lib/std/debug.zig120
-rw-r--r--lib/std/dwarf.zig28
-rw-r--r--lib/std/dwarf_bits.zig2
-rw-r--r--lib/std/dynamic_library.zig2
-rw-r--r--lib/std/elf.zig18
-rw-r--r--lib/std/event.zig2
-rw-r--r--lib/std/event/batch.zig21
-rw-r--r--lib/std/event/channel.zig2
-rw-r--r--lib/std/event/future.zig2
-rw-r--r--lib/std/event/group.zig2
-rw-r--r--lib/std/event/lock.zig4
-rw-r--r--lib/std/event/locked.zig2
-rw-r--r--lib/std/event/loop.zig25
-rw-r--r--lib/std/event/rwlock.zig2
-rw-r--r--lib/std/event/rwlocked.zig2
-rw-r--r--lib/std/event/wait_group.zig4
-rw-r--r--lib/std/fifo.zig26
-rw-r--r--lib/std/fmt.zig737
-rw-r--r--lib/std/fmt/errol.zig2
-rw-r--r--lib/std/fmt/errol/enum3.zig2
-rw-r--r--lib/std/fmt/errol/lookup.zig2
-rw-r--r--lib/std/fmt/parse_float.zig10
-rw-r--r--lib/std/fs.zig108
-rw-r--r--lib/std/fs/file.zig59
-rw-r--r--lib/std/fs/get_app_data_dir.zig14
-rw-r--r--lib/std/fs/path.zig41
-rw-r--r--lib/std/fs/test.zig48
-rw-r--r--lib/std/fs/wasi.zig4
-rw-r--r--lib/std/fs/watch.zig741
-rw-r--r--lib/std/hash.zig2
-rw-r--r--lib/std/hash/adler.zig2
-rw-r--r--lib/std/hash/auto_hash.zig69
-rw-r--r--lib/std/hash/benchmark.zig2
-rw-r--r--lib/std/hash/cityhash.zig200
-rw-r--r--lib/std/hash/crc.zig12
-rw-r--r--lib/std/hash/fnv.zig2
-rw-r--r--lib/std/hash/murmur.zig2
-rw-r--r--lib/std/hash/wyhash.zig2
-rw-r--r--lib/std/hash_map.zig13
-rw-r--r--lib/std/heap.zig32
-rw-r--r--lib/std/heap/arena_allocator.zig2
-rw-r--r--lib/std/heap/general_purpose_allocator.zig54
-rw-r--r--lib/std/heap/logging_allocator.zig34
-rw-r--r--lib/std/io.zig67
-rw-r--r--lib/std/io/auto_indenting_stream.zig154
-rw-r--r--lib/std/io/bit_in_stream.zig10
-rw-r--r--lib/std/io/bit_out_stream.zig10
-rw-r--r--lib/std/io/bit_reader.zig9
-rw-r--r--lib/std/io/bit_writer.zig8
-rw-r--r--lib/std/io/buffered_atomic_file.zig24
-rw-r--r--lib/std/io/buffered_in_stream.zig10
-rw-r--r--lib/std/io/buffered_out_stream.zig10
-rw-r--r--lib/std/io/buffered_reader.zig9
-rw-r--r--lib/std/io/buffered_writer.zig9
-rw-r--r--lib/std/io/c_out_stream.zig10
-rw-r--r--lib/std/io/c_writer.zig4
-rw-r--r--lib/std/io/change_detection_stream.zig2
-rw-r--r--lib/std/io/counting_out_stream.zig10
-rw-r--r--lib/std/io/counting_reader.zig48
-rw-r--r--lib/std/io/counting_writer.zig9
-rw-r--r--lib/std/io/find_byte_writer.zig (renamed from lib/std/io/find_byte_out_stream.zig)10
-rw-r--r--lib/std/io/fixed_buffer_stream.zig18
-rw-r--r--lib/std/io/in_stream.zig7
-rw-r--r--lib/std/io/limited_reader.zig50
-rw-r--r--lib/std/io/multi_out_stream.zig10
-rw-r--r--lib/std/io/multi_writer.zig9
-rw-r--r--lib/std/io/peek_stream.zig19
-rw-r--r--lib/std/io/reader.zig56
-rw-r--r--lib/std/io/seekable_stream.zig2
-rw-r--r--lib/std/io/serialization.zig616
-rw-r--r--lib/std/io/stream_source.zig18
-rw-r--r--lib/std/io/test.zig8
-rw-r--r--lib/std/io/writer.zig2
-rw-r--r--lib/std/json.zig228
-rw-r--r--lib/std/json/test.zig2
-rw-r--r--lib/std/json/write_stream.zig4
-rw-r--r--lib/std/leb128.zig2
-rw-r--r--lib/std/linked_list.zig8
-rw-r--r--lib/std/log.zig9
-rw-r--r--lib/std/macho.zig84
-rw-r--r--lib/std/math.zig53
-rw-r--r--lib/std/math/acos.zig2
-rw-r--r--lib/std/math/acosh.zig2
-rw-r--r--lib/std/math/asin.zig2
-rw-r--r--lib/std/math/asinh.zig2
-rw-r--r--lib/std/math/atan.zig2
-rw-r--r--lib/std/math/atan2.zig2
-rw-r--r--lib/std/math/atanh.zig2
-rw-r--r--lib/std/math/big.zig4
-rw-r--r--lib/std/math/big/int.zig14
-rw-r--r--lib/std/math/big/int_test.zig8
-rw-r--r--lib/std/math/big/rational.zig2
-rw-r--r--lib/std/math/cbrt.zig2
-rw-r--r--lib/std/math/ceil.zig2
-rw-r--r--lib/std/math/complex.zig2
-rw-r--r--lib/std/math/complex/abs.zig2
-rw-r--r--lib/std/math/complex/acos.zig2
-rw-r--r--lib/std/math/complex/acosh.zig2
-rw-r--r--lib/std/math/complex/arg.zig2
-rw-r--r--lib/std/math/complex/asin.zig2
-rw-r--r--lib/std/math/complex/asinh.zig2
-rw-r--r--lib/std/math/complex/atan.zig2
-rw-r--r--lib/std/math/complex/atanh.zig2
-rw-r--r--lib/std/math/complex/conj.zig2
-rw-r--r--lib/std/math/complex/cos.zig2
-rw-r--r--lib/std/math/complex/cosh.zig2
-rw-r--r--lib/std/math/complex/exp.zig2
-rw-r--r--lib/std/math/complex/ldexp.zig2
-rw-r--r--lib/std/math/complex/log.zig2
-rw-r--r--lib/std/math/complex/pow.zig2
-rw-r--r--lib/std/math/complex/proj.zig2
-rw-r--r--lib/std/math/complex/sin.zig2
-rw-r--r--lib/std/math/complex/sinh.zig2
-rw-r--r--lib/std/math/complex/sqrt.zig2
-rw-r--r--lib/std/math/complex/tan.zig2
-rw-r--r--lib/std/math/complex/tanh.zig2
-rw-r--r--lib/std/math/copysign.zig2
-rw-r--r--lib/std/math/cos.zig2
-rw-r--r--lib/std/math/cosh.zig2
-rw-r--r--lib/std/math/epsilon.zig2
-rw-r--r--lib/std/math/exp.zig2
-rw-r--r--lib/std/math/exp2.zig2
-rw-r--r--lib/std/math/expm1.zig2
-rw-r--r--lib/std/math/expo2.zig2
-rw-r--r--lib/std/math/fabs.zig2
-rw-r--r--lib/std/math/floor.zig2
-rw-r--r--lib/std/math/fma.zig2
-rw-r--r--lib/std/math/frexp.zig2
-rw-r--r--lib/std/math/hypot.zig2
-rw-r--r--lib/std/math/ilogb.zig2
-rw-r--r--lib/std/math/inf.zig2
-rw-r--r--lib/std/math/isfinite.zig2
-rw-r--r--lib/std/math/isinf.zig2
-rw-r--r--lib/std/math/isnan.zig2
-rw-r--r--lib/std/math/isnormal.zig2
-rw-r--r--lib/std/math/ln.zig2
-rw-r--r--lib/std/math/log.zig2
-rw-r--r--lib/std/math/log10.zig2
-rw-r--r--lib/std/math/log1p.zig2
-rw-r--r--lib/std/math/log2.zig2
-rw-r--r--lib/std/math/modf.zig2
-rw-r--r--lib/std/math/nan.zig2
-rw-r--r--lib/std/math/pow.zig2
-rw-r--r--lib/std/math/powi.zig2
-rw-r--r--lib/std/math/round.zig2
-rw-r--r--lib/std/math/scalbn.zig2
-rw-r--r--lib/std/math/signbit.zig2
-rw-r--r--lib/std/math/sin.zig2
-rw-r--r--lib/std/math/sinh.zig2
-rw-r--r--lib/std/math/sqrt.zig2
-rw-r--r--lib/std/math/tan.zig2
-rw-r--r--lib/std/math/tanh.zig2
-rw-r--r--lib/std/math/trunc.zig2
-rw-r--r--lib/std/mem.zig92
-rw-r--r--lib/std/mem/Allocator.zig10
-rw-r--r--lib/std/meta.zig197
-rw-r--r--lib/std/meta/trailer_flags.zig24
-rw-r--r--lib/std/meta/trait.zig82
-rw-r--r--lib/std/multi_array_list.zig446
-rw-r--r--lib/std/mutex.zig330
-rw-r--r--lib/std/net.zig159
-rw-r--r--lib/std/net/test.zig52
-rw-r--r--lib/std/once.zig4
-rw-r--r--lib/std/os.zig372
-rw-r--r--lib/std/os/bits.zig22
-rw-r--r--lib/std/os/bits/darwin.zig187
-rw-r--r--lib/std/os/bits/dragonfly.zig10
-rw-r--r--lib/std/os/bits/freebsd.zig134
-rw-r--r--lib/std/os/bits/haiku.zig1450
-rw-r--r--lib/std/os/bits/linux.zig242
-rw-r--r--lib/std/os/bits/linux/arm-eabi.zig4
-rw-r--r--lib/std/os/bits/linux/arm64.zig4
-rw-r--r--lib/std/os/bits/linux/errno-generic.zig2
-rw-r--r--lib/std/os/bits/linux/errno-mips.zig2
-rw-r--r--lib/std/os/bits/linux/i386.zig4
-rw-r--r--lib/std/os/bits/linux/mips.zig3
-rw-r--r--lib/std/os/bits/linux/netlink.zig2
-rw-r--r--lib/std/os/bits/linux/powerpc64.zig4
-rw-r--r--lib/std/os/bits/linux/prctl.zig2
-rw-r--r--lib/std/os/bits/linux/riscv64.zig4
-rw-r--r--lib/std/os/bits/linux/securebits.zig2
-rw-r--r--lib/std/os/bits/linux/sparc64.zig2
-rw-r--r--lib/std/os/bits/linux/x86_64.zig4
-rw-r--r--lib/std/os/bits/netbsd.zig34
-rw-r--r--lib/std/os/bits/openbsd.zig92
-rw-r--r--lib/std/os/bits/wasi.zig2
-rw-r--r--lib/std/os/bits/windows.zig3
-rw-r--r--lib/std/os/darwin.zig2
-rw-r--r--lib/std/os/dragonfly.zig2
-rw-r--r--lib/std/os/freebsd.zig2
-rw-r--r--lib/std/os/haiku.zig (renamed from lib/std/io/out_stream.zig)5
-rw-r--r--lib/std/os/linux.zig57
-rw-r--r--lib/std/os/linux/arm-eabi.zig2
-rw-r--r--lib/std/os/linux/arm64.zig2
-rw-r--r--lib/std/os/linux/bpf.zig2
-rw-r--r--lib/std/os/linux/bpf/btf.zig2
-rw-r--r--lib/std/os/linux/bpf/btf_ext.zig2
-rw-r--r--lib/std/os/linux/bpf/helpers.zig2
-rw-r--r--lib/std/os/linux/bpf/kern.zig2
-rw-r--r--lib/std/os/linux/i386.zig2
-rw-r--r--lib/std/os/linux/io_uring.zig103
-rw-r--r--lib/std/os/linux/mips.zig2
-rw-r--r--lib/std/os/linux/powerpc64.zig2
-rw-r--r--lib/std/os/linux/riscv64.zig2
-rw-r--r--lib/std/os/linux/test.zig11
-rw-r--r--lib/std/os/linux/tls.zig41
-rw-r--r--lib/std/os/linux/vdso.zig2
-rw-r--r--lib/std/os/linux/x86_64.zig2
-rw-r--r--lib/std/os/netbsd.zig2
-rw-r--r--lib/std/os/openbsd.zig2
-rw-r--r--lib/std/os/test.zig77
-rw-r--r--lib/std/os/uefi.zig19
-rw-r--r--lib/std/os/uefi/protocols.zig2
-rw-r--r--lib/std/os/uefi/protocols/absolute_pointer_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/device_path_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/edid_active_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/edid_discovered_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/edid_override_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/file_protocol.zig18
-rw-r--r--lib/std/os/uefi/protocols/graphics_output_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/hii.zig2
-rw-r--r--lib/std/os/uefi/protocols/hii_database_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/hii_popup_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/ip6_config_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/ip6_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/ip6_service_binding_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/loaded_image_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/managed_network_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/managed_network_service_binding_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/rng_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/shell_parameters_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/simple_file_system_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/simple_network_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/simple_pointer_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/simple_text_input_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/simple_text_output_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/udp6_protocol.zig2
-rw-r--r--lib/std/os/uefi/protocols/udp6_service_binding_protocol.zig2
-rw-r--r--lib/std/os/uefi/status.zig2
-rw-r--r--lib/std/os/uefi/tables.zig2
-rw-r--r--lib/std/os/uefi/tables/boot_services.zig2
-rw-r--r--lib/std/os/uefi/tables/configuration_table.zig2
-rw-r--r--lib/std/os/uefi/tables/runtime_services.zig2
-rw-r--r--lib/std/os/uefi/tables/system_table.zig2
-rw-r--r--lib/std/os/uefi/tables/table_header.zig2
-rw-r--r--lib/std/os/wasi.zig2
-rw-r--r--lib/std/os/windows.zig154
-rw-r--r--lib/std/os/windows/advapi32.zig2
-rw-r--r--lib/std/os/windows/bits.zig54
-rw-r--r--lib/std/os/windows/gdi32.zig2
-rw-r--r--lib/std/os/windows/kernel32.zig18
-rw-r--r--lib/std/os/windows/lang.zig2
-rw-r--r--lib/std/os/windows/ntdll.zig10
-rw-r--r--lib/std/os/windows/ntstatus.zig2
-rw-r--r--lib/std/os/windows/ole32.zig2
-rw-r--r--lib/std/os/windows/psapi.zig2
-rw-r--r--lib/std/os/windows/shell32.zig2
-rw-r--r--lib/std/os/windows/sublang.zig2
-rw-r--r--lib/std/os/windows/user32.zig707
-rw-r--r--lib/std/os/windows/win32error.zig2
-rw-r--r--lib/std/os/windows/ws2_32.zig2
-rw-r--r--lib/std/packed_int_array.zig28
-rw-r--r--lib/std/pdb.zig8
-rw-r--r--lib/std/priority_queue.zig2
-rw-r--r--lib/std/process.zig86
-rw-r--r--lib/std/progress.zig310
-rw-r--r--lib/std/rand.zig583
-rw-r--r--lib/std/rand/Gimli.zig40
-rw-r--r--lib/std/rand/Isaac64.zig210
-rw-r--r--lib/std/rand/Pcg.zig101
-rw-r--r--lib/std/rand/Sfc64.zig108
-rw-r--r--lib/std/rand/Xoroshiro128.zig133
-rw-r--r--lib/std/rand/ziggurat.zig16
-rw-r--r--lib/std/sort.zig6
-rw-r--r--lib/std/special/build_runner.zig20
-rw-r--r--lib/std/special/c.zig14
-rw-r--r--lib/std/special/compiler_rt.zig27
-rw-r--r--lib/std/special/compiler_rt/addXf3.zig2
-rw-r--r--lib/std/special/compiler_rt/addXf3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/arm.zig22
-rw-r--r--lib/std/special/compiler_rt/ashldi3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/ashlti3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/ashrdi3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/ashrti3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/atomics.zig2
-rw-r--r--lib/std/special/compiler_rt/aulldiv.zig2
-rw-r--r--lib/std/special/compiler_rt/aullrem.zig2
-rw-r--r--lib/std/special/compiler_rt/clear_cache.zig2
-rw-r--r--lib/std/special/compiler_rt/clzsi2.zig2
-rw-r--r--lib/std/special/compiler_rt/clzsi2_test.zig2
-rw-r--r--lib/std/special/compiler_rt/compareXf2.zig2
-rw-r--r--lib/std/special/compiler_rt/comparedf2_test.zig2
-rw-r--r--lib/std/special/compiler_rt/comparesf2_test.zig2
-rw-r--r--lib/std/special/compiler_rt/divdf3.zig2
-rw-r--r--lib/std/special/compiler_rt/divdf3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/divsf3.zig2
-rw-r--r--lib/std/special/compiler_rt/divsf3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/divtf3.zig2
-rw-r--r--lib/std/special/compiler_rt/divtf3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/divti3.zig2
-rw-r--r--lib/std/special/compiler_rt/divti3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/emutls.zig395
-rw-r--r--lib/std/special/compiler_rt/extendXfYf2.zig2
-rw-r--r--lib/std/special/compiler_rt/extendXfYf2_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixdfdi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixdfdi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixdfsi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixdfsi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixdfti.zig2
-rw-r--r--lib/std/special/compiler_rt/fixdfti_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixint.zig2
-rw-r--r--lib/std/special/compiler_rt/fixint_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixsfdi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixsfdi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixsfsi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixsfsi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixsfti.zig2
-rw-r--r--lib/std/special/compiler_rt/fixsfti_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixtfdi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixtfdi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixtfsi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixtfsi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixtfti.zig2
-rw-r--r--lib/std/special/compiler_rt/fixtfti_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixuint.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunsdfdi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunsdfdi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunsdfsi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunsdfsi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunsdfti.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunsdfti_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunssfdi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunssfdi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunssfsi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunssfsi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunssfti.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunssfti_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunstfdi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunstfdi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunstfsi.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunstfsi_test.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunstfti.zig2
-rw-r--r--lib/std/special/compiler_rt/fixunstfti_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floatXisf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatdidf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatdidf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floatdisf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floatditf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatditf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floatsiXf.zig2
-rw-r--r--lib/std/special/compiler_rt/floattidf.zig2
-rw-r--r--lib/std/special/compiler_rt/floattidf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floattisf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floattitf.zig2
-rw-r--r--lib/std/special/compiler_rt/floattitf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floatundidf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatundidf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floatundisf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatunditf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatunditf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floatunsidf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatunsisf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatunsitf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatunsitf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floatuntidf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatuntidf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floatuntisf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatuntisf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/floatuntitf.zig2
-rw-r--r--lib/std/special/compiler_rt/floatuntitf_test.zig2
-rw-r--r--lib/std/special/compiler_rt/int.zig2
-rw-r--r--lib/std/special/compiler_rt/lshrdi3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/lshrti3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/modti3.zig2
-rw-r--r--lib/std/special/compiler_rt/modti3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/mulXf3.zig2
-rw-r--r--lib/std/special/compiler_rt/mulXf3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/muldi3.zig2
-rw-r--r--lib/std/special/compiler_rt/muldi3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/mulodi4.zig2
-rw-r--r--lib/std/special/compiler_rt/mulodi4_test.zig2
-rw-r--r--lib/std/special/compiler_rt/muloti4.zig2
-rw-r--r--lib/std/special/compiler_rt/muloti4_test.zig2
-rw-r--r--lib/std/special/compiler_rt/multi3.zig2
-rw-r--r--lib/std/special/compiler_rt/multi3_test.zig2
-rw-r--r--lib/std/special/compiler_rt/negXf2.zig2
-rw-r--r--lib/std/special/compiler_rt/popcountdi2.zig2
-rw-r--r--lib/std/special/compiler_rt/popcountdi2_test.zig2
-rw-r--r--lib/std/special/compiler_rt/shift.zig4
-rw-r--r--lib/std/special/compiler_rt/sparc.zig79
-rw-r--r--lib/std/special/compiler_rt/stack_probe.zig2
-rw-r--r--lib/std/special/compiler_rt/truncXfYf2.zig2
-rw-r--r--lib/std/special/compiler_rt/truncXfYf2_test.zig2
-rw-r--r--lib/std/special/compiler_rt/udivmod.zig2
-rw-r--r--lib/std/special/compiler_rt/udivmoddi4_test.zig2
-rw-r--r--lib/std/special/compiler_rt/udivmodti4.zig2
-rw-r--r--lib/std/special/compiler_rt/udivmodti4_test.zig2
-rw-r--r--lib/std/special/compiler_rt/udivti3.zig2
-rw-r--r--lib/std/special/compiler_rt/umodti3.zig2
-rw-r--r--lib/std/special/ssp.zig2
-rw-r--r--lib/std/special/test_runner.zig28
-rw-r--r--lib/std/spinlock.zig90
-rw-r--r--lib/std/start.zig13
-rw-r--r--lib/std/start_windows_tls.zig2
-rw-r--r--lib/std/std.zig49
-rw-r--r--lib/std/target.zig93
-rw-r--r--lib/std/target/aarch64.zig2
-rw-r--r--lib/std/target/amdgpu.zig2
-rw-r--r--lib/std/target/arm.zig2
-rw-r--r--lib/std/target/avr.zig2
-rw-r--r--lib/std/target/bpf.zig2
-rw-r--r--lib/std/target/hexagon.zig2
-rw-r--r--lib/std/target/mips.zig2
-rw-r--r--lib/std/target/msp430.zig2
-rw-r--r--lib/std/target/nvptx.zig2
-rw-r--r--lib/std/target/powerpc.zig4
-rw-r--r--lib/std/target/riscv.zig2
-rw-r--r--lib/std/target/sparc.zig2
-rw-r--r--lib/std/target/systemz.zig2
-rw-r--r--lib/std/target/wasm.zig2
-rw-r--r--lib/std/target/x86.zig2
-rw-r--r--lib/std/testing.zig63
-rw-r--r--lib/std/testing/failing_allocator.zig2
-rw-r--r--lib/std/thread.zig496
-rw-r--r--lib/std/time.zig2
-rw-r--r--lib/std/time/epoch.zig2
-rw-r--r--lib/std/unicode.zig2
-rw-r--r--lib/std/unicode/throughput_test.zig4
-rw-r--r--lib/std/valgrind.zig4
-rw-r--r--lib/std/valgrind/callgrind.zig2
-rw-r--r--lib/std/valgrind/memcheck.zig2
-rw-r--r--lib/std/wasm.zig280
-rw-r--r--lib/std/zig.zig8
-rw-r--r--lib/std/zig/ast.zig5745
-rw-r--r--lib/std/zig/cross_target.zig26
-rw-r--r--lib/std/zig/fmt.zig72
-rw-r--r--lib/std/zig/parse.zig6378
-rw-r--r--lib/std/zig/parser_test.zig1705
-rw-r--r--lib/std/zig/perf_test.zig4
-rw-r--r--lib/std/zig/render.zig4674
-rw-r--r--lib/std/zig/string_literal.zig2
-rw-r--r--lib/std/zig/system.zig134
-rw-r--r--lib/std/zig/system/macos.zig800
-rw-r--r--lib/std/zig/system/windows.zig45
-rw-r--r--lib/std/zig/system/x86.zig6
-rw-r--r--lib/std/zig/tokenizer.zig1666
539 files changed, 24192 insertions, 16172 deletions
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
new file mode 100644
index 0000000000..4afd191b93
--- /dev/null
+++ b/lib/std/Progress.zig
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! This API non-allocating, non-fallible, and thread-safe.
+//! The tradeoff is that users of this API must provide the storage
+//! for each `Progress.Node`.
+//!
+//! Initialize the struct directly, overriding these fields as desired:
+//! * `refresh_rate_ms`
+//! * `initial_delay_ms`
+
+const std = @import("std");
+const windows = std.os.windows;
+const testing = std.testing;
+const assert = std.debug.assert;
+const Progress = @This();
+
+/// `null` if the current node (and its children) should
+/// not print on update()
+terminal: ?std.fs.File = undefined,
+
+/// Whether the terminal supports ANSI escape codes.
+supports_ansi_escape_codes: bool = false,
+
+/// If the terminal is "dumb", don't print output.
+/// This can be useful if you don't want to print all
+/// the stages of code generation if there are a lot.
+/// You should not use it if the user should see output
+/// for example showing the user what tests run.
+dont_print_on_dumb: bool = false,
+
+root: Node = undefined,
+
+/// Keeps track of how much time has passed since the beginning.
+/// Used to compare with `initial_delay_ms` and `refresh_rate_ms`.
+timer: std.time.Timer = undefined,
+
+/// When the previous refresh was written to the terminal.
+/// Used to compare with `refresh_rate_ms`.
+prev_refresh_timestamp: u64 = undefined,
+
+/// This buffer represents the maximum number of bytes written to the terminal
+/// with each refresh.
+output_buffer: [100]u8 = undefined,
+
+/// How many nanoseconds between writing updates to the terminal.
+refresh_rate_ns: u64 = 50 * std.time.ns_per_ms,
+
+/// How many nanoseconds to keep the output hidden
+initial_delay_ns: u64 = 500 * std.time.ns_per_ms,
+
+done: bool = true,
+
+/// Protects the `refresh` function, as well as `node.recently_updated_child`.
+/// Without this, callsites would call `Node.end` and then free `Node` memory
+/// while it was still being accessed by the `refresh` function.
+update_lock: std.Thread.Mutex = .{},
+
+/// Keeps track of how many columns in the terminal have been output, so that
+/// we can move the cursor back later.
+columns_written: usize = undefined,
+
+/// Represents one unit of progress. Each node can have children nodes, or
+/// one can use integers with `update`.
+pub const Node = struct {
+ context: *Progress,
+ parent: ?*Node,
+ name: []const u8,
+ /// Must be handled atomically to be thread-safe.
+ recently_updated_child: ?*Node = null,
+ /// Must be handled atomically to be thread-safe. 0 means null.
+ unprotected_estimated_total_items: usize,
+ /// Must be handled atomically to be thread-safe.
+ unprotected_completed_items: usize,
+
+ /// Create a new child progress node. Thread-safe.
+ /// Call `Node.end` when done.
+ /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
+ /// API to set `self.parent.recently_updated_child` with the return value.
+ /// Until that is fixed you probably want to call `activate` on the return value.
+ /// Passing 0 for `estimated_total_items` means unknown.
+ pub fn start(self: *Node, name: []const u8, estimated_total_items: usize) Node {
+ return Node{
+ .context = self.context,
+ .parent = self,
+ .name = name,
+ .unprotected_estimated_total_items = estimated_total_items,
+ .unprotected_completed_items = 0,
+ };
+ }
+
+ /// This is the same as calling `start` and then `end` on the returned `Node`. Thread-safe.
+ pub fn completeOne(self: *Node) void {
+ self.activate();
+ _ = @atomicRmw(usize, &self.unprotected_completed_items, .Add, 1, .Monotonic);
+ self.context.maybeRefresh();
+ }
+
+ /// Finish a started `Node`. Thread-safe.
+ pub fn end(self: *Node) void {
+ self.context.maybeRefresh();
+ if (self.parent) |parent| {
+ {
+ const held = self.context.update_lock.acquire();
+ defer held.release();
+ _ = @cmpxchgStrong(?*Node, &parent.recently_updated_child, self, null, .Monotonic, .Monotonic);
+ }
+ parent.completeOne();
+ } else {
+ const held = self.context.update_lock.acquire();
+ defer held.release();
+ self.context.done = true;
+ self.context.refreshWithHeldLock();
+ }
+ }
+
+ /// Tell the parent node that this node is actively being worked on. Thread-safe.
+ pub fn activate(self: *Node) void {
+ if (self.parent) |parent| {
+ @atomicStore(?*Node, &parent.recently_updated_child, self, .Release);
+ }
+ }
+
+ /// Thread-safe. 0 means unknown.
+ pub fn setEstimatedTotalItems(self: *Node, count: usize) void {
+ @atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic);
+ }
+
+ /// Thread-safe.
+ pub fn setCompletedItems(self: *Node, completed_items: usize) void {
+ @atomicStore(usize, &self.unprotected_completed_items, completed_items, .Monotonic);
+ }
+};
+
+/// Create a new progress node.
+/// Call `Node.end` when done.
+/// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
+/// API to return Progress rather than accept it as a parameter.
+/// `estimated_total_items` value of 0 means unknown.
+pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*Node {
+ const stderr = std.io.getStdErr();
+ self.terminal = null;
+ if (stderr.supportsAnsiEscapeCodes()) {
+ self.terminal = stderr;
+ self.supports_ansi_escape_codes = true;
+ } else if (std.builtin.os.tag == .windows and stderr.isTty()) {
+ self.terminal = stderr;
+ } else if (std.builtin.os.tag != .windows) {
+ // we are in a "dumb" terminal like in acme or writing to a file
+ self.terminal = stderr;
+ }
+ self.root = Node{
+ .context = self,
+ .parent = null,
+ .name = name,
+ .unprotected_estimated_total_items = estimated_total_items,
+ .unprotected_completed_items = 0,
+ };
+ self.columns_written = 0;
+ self.prev_refresh_timestamp = 0;
+ self.timer = try std.time.Timer.start();
+ self.done = false;
+ return &self.root;
+}
+
+/// Updates the terminal if enough time has passed since last update. Thread-safe.
+pub fn maybeRefresh(self: *Progress) void {
+ const now = self.timer.read();
+ if (now < self.initial_delay_ns) return;
+ const held = self.update_lock.tryAcquire() orelse return;
+ defer held.release();
+ // TODO I have observed this to happen sometimes. I think we need to follow Rust's
+ // lead and guarantee monotonically increasing times in the std lib itself.
+ if (now < self.prev_refresh_timestamp) return;
+ if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;
+ return self.refreshWithHeldLock();
+}
+
+/// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe.
+pub fn refresh(self: *Progress) void {
+ const held = self.update_lock.tryAcquire() orelse return;
+ defer held.release();
+
+ return self.refreshWithHeldLock();
+}
+
+fn refreshWithHeldLock(self: *Progress) void {
+ const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows);
+ if (is_dumb and self.dont_print_on_dumb) return;
+ const file = self.terminal orelse return;
+
+ const prev_columns_written = self.columns_written;
+ var end: usize = 0;
+ if (self.columns_written > 0) {
+ // restore the cursor position by moving the cursor
+ // `columns_written` cells to the left, then clear the rest of the
+ // line
+ if (self.supports_ansi_escape_codes) {
+ end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;
+ end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
+ } else if (std.builtin.os.tag == .windows) winapi: {
+ var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
+ if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
+ unreachable;
+
+ var cursor_pos = windows.COORD{
+ .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
+ .Y = info.dwCursorPosition.Y,
+ };
+
+ if (cursor_pos.X < 0)
+ cursor_pos.X = 0;
+
+ const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X);
+
+ var written: windows.DWORD = undefined;
+ if (windows.kernel32.FillConsoleOutputAttribute(
+ file.handle,
+ info.wAttributes,
+ fill_chars,
+ cursor_pos,
+ &written,
+ ) != windows.TRUE) {
+ // Stop trying to write to this file.
+ self.terminal = null;
+ break :winapi;
+ }
+ if (windows.kernel32.FillConsoleOutputCharacterA(
+ file.handle,
+ ' ',
+ fill_chars,
+ cursor_pos,
+ &written,
+ ) != windows.TRUE) unreachable;
+
+ if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE)
+ unreachable;
+ } else {
+ // we are in a "dumb" terminal like in acme or writing to a file
+ self.output_buffer[end] = '\n';
+ end += 1;
+ }
+
+ self.columns_written = 0;
+ }
+
+ if (!self.done) {
+ var need_ellipse = false;
+ var maybe_node: ?*Node = &self.root;
+ while (maybe_node) |node| {
+ if (need_ellipse) {
+ self.bufWrite(&end, "... ", .{});
+ }
+ need_ellipse = false;
+ const eti = @atomicLoad(usize, &node.unprotected_estimated_total_items, .Monotonic);
+ const completed_items = @atomicLoad(usize, &node.unprotected_completed_items, .Monotonic);
+ if (node.name.len != 0 or eti > 0) {
+ if (node.name.len != 0) {
+ self.bufWrite(&end, "{s}", .{node.name});
+ need_ellipse = true;
+ }
+ if (eti > 0) {
+ if (need_ellipse) self.bufWrite(&end, " ", .{});
+ self.bufWrite(&end, "[{d}/{d}] ", .{ completed_items + 1, eti });
+ need_ellipse = false;
+ } else if (completed_items != 0) {
+ if (need_ellipse) self.bufWrite(&end, " ", .{});
+ self.bufWrite(&end, "[{d}] ", .{completed_items + 1});
+ need_ellipse = false;
+ }
+ }
+ maybe_node = @atomicLoad(?*Node, &node.recently_updated_child, .Acquire);
+ }
+ if (need_ellipse) {
+ self.bufWrite(&end, "... ", .{});
+ }
+ }
+
+ _ = file.write(self.output_buffer[0..end]) catch |e| {
+ // Stop trying to write to this file once it errors.
+ self.terminal = null;
+ };
+ self.prev_refresh_timestamp = self.timer.read();
+}
+
+pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
+ const file = self.terminal orelse return;
+ self.refresh();
+ file.writer().print(format, args) catch {
+ self.terminal = null;
+ return;
+ };
+ self.columns_written = 0;
+}
+
+fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
+ if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
+ const amt = written.len;
+ end.* += amt;
+ self.columns_written += amt;
+ } else |err| switch (err) {
+ error.NoSpaceLeft => {
+ self.columns_written += self.output_buffer.len - end.*;
+ end.* = self.output_buffer.len;
+ },
+ }
+ const bytes_needed_for_esc_codes_at_end = if (std.builtin.os.tag == .windows) 0 else 11;
+ const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
+ if (end.* > max_end) {
+ const suffix = "... ";
+ self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
+ std.mem.copy(u8, self.output_buffer[max_end..], suffix);
+ end.* = max_end + suffix.len;
+ }
+}
+
+test "basic functionality" {
+ var disable = true;
+ if (disable) {
+ // This test is disabled because it uses time.sleep() and is therefore slow. It also
+ // prints bogus progress data to stderr.
+ return error.SkipZigTest;
+ }
+ var progress = Progress{};
+ const root_node = try progress.start("", 100);
+ defer root_node.end();
+
+ const sub_task_names = [_][]const u8{
+ "reticulating splines",
+ "adjusting shoes",
+ "climbing towers",
+ "pouring juice",
+ };
+ var next_sub_task: usize = 0;
+
+ var i: usize = 0;
+ while (i < 100) : (i += 1) {
+ var node = root_node.start(sub_task_names[next_sub_task], 5);
+ node.activate();
+ next_sub_task = (next_sub_task + 1) % sub_task_names.len;
+
+ node.completeOne();
+ std.time.sleep(5 * std.time.ns_per_ms);
+ node.completeOne();
+ node.completeOne();
+ std.time.sleep(5 * std.time.ns_per_ms);
+ node.completeOne();
+ node.completeOne();
+ std.time.sleep(5 * std.time.ns_per_ms);
+
+ node.end();
+
+ std.time.sleep(5 * std.time.ns_per_ms);
+ }
+ {
+ var node = root_node.start("this is a really long name designed to activate the truncation code. let's find out if it works", 0);
+ node.activate();
+ std.time.sleep(10 * std.time.ns_per_ms);
+ progress.refresh();
+ std.time.sleep(10 * std.time.ns_per_ms);
+ node.end();
+ }
+}
diff --git a/lib/std/SemanticVersion.zig b/lib/std/SemanticVersion.zig
index 74e515f9d1..52c3ae59df 100644
--- a/lib/std/SemanticVersion.zig
+++ b/lib/std/SemanticVersion.zig
@@ -102,7 +102,7 @@ pub fn parse(text: []const u8) !Version {
if (extra_index == null) return ver;
// Slice optional pre-release or build metadata components.
- const extra = text[extra_index.?..text.len];
+ const extra: []const u8 = text[extra_index.?..text.len];
if (extra[0] == '-') {
const build_index = std.mem.indexOfScalar(u8, extra, '+');
ver.pre = extra[1..(build_index orelse extra.len)];
@@ -163,9 +163,9 @@ pub fn format(
out_stream: anytype,
) !void {
if (fmt.len != 0) @compileError("Unknown format string: '" ++ fmt ++ "'");
- try std.fmt.format(out_stream, "{}.{}.{}", .{ self.major, self.minor, self.patch });
- if (self.pre) |pre| try std.fmt.format(out_stream, "-{}", .{pre});
- if (self.build) |build| try std.fmt.format(out_stream, "+{}", .{build});
+ try std.fmt.format(out_stream, "{d}.{d}.{d}", .{ self.major, self.minor, self.patch });
+ if (self.pre) |pre| try std.fmt.format(out_stream, "-{s}", .{pre});
+ if (self.build) |build| try std.fmt.format(out_stream, "+{s}", .{build});
}
const expect = std.testing.expect;
@@ -205,7 +205,7 @@ test "SemanticVersion format" {
"1.2.3----R-S.12.9.1--.12+meta",
"1.2.3----RC-SNAPSHOT.12.9.1--.12",
"1.0.0+0.build.1-rc.10000aaa-kk-0.1",
- }) |valid| try testFmt(valid, "{}", .{try parse(valid)});
+ }) |valid| try std.testing.expectFmt(valid, "{}", .{try parse(valid)});
// Invalid version strings should be rejected.
for ([_][]const u8{
@@ -253,7 +253,9 @@ test "SemanticVersion format" {
// Valid version string that may overflow.
const big_valid = "99999999999999999999999.999999999999999999.99999999999999999";
- if (parse(big_valid)) |ver| try testFmt(big_valid, "{}", .{ver}) else |err| expect(err == error.Overflow);
+ if (parse(big_valid)) |ver| {
+ try std.testing.expectFmt(big_valid, "{}", .{ver});
+ } else |err| expect(err == error.Overflow);
// Invalid version string that may overflow.
const big_invalid = "99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12";
@@ -280,16 +282,11 @@ test "SemanticVersion precedence" {
expect(order(try parse("1.0.0-rc.1"), try parse("1.0.0")) == .lt);
}
-// This is copy-pasted from fmt.zig since it is not public.
-fn testFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {
- var buf: [100]u8 = undefined;
- const result = try std.fmt.bufPrint(buf[0..], template, args);
- if (std.mem.eql(u8, result, expected)) return;
-
- std.debug.warn("\n====== expected this output: =========\n", .{});
- std.debug.warn("{}", .{expected});
- std.debug.warn("\n======== instead found this: =========\n", .{});
- std.debug.warn("{}", .{result});
- std.debug.warn("\n======================================\n", .{});
- return error.TestFailed;
+test "zig_version" {
+ // An approximate Zig build that predates this test.
+ const older_version = .{ .major = 0, .minor = 8, .patch = 0, .pre = "dev.874" };
+
+ // Simulated compatibility check using Zig version.
+ const compatible = comptime @import("builtin").zig_version.order(older_version) == .gt;
+ if (!compatible) @compileError("zig_version test failed");
}
diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig
new file mode 100644
index 0000000000..77277bd154
--- /dev/null
+++ b/lib/std/Thread.zig
@@ -0,0 +1,565 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! This struct represents a kernel thread, and acts as a namespace for concurrency
+//! primitives that operate on kernel threads. For concurrency primitives that support
+//! both evented I/O and async I/O, see the respective names in the top level std namespace.
+
+data: Data,
+
+pub const AutoResetEvent = @import("Thread/AutoResetEvent.zig");
+pub const ResetEvent = @import("Thread/ResetEvent.zig");
+pub const StaticResetEvent = @import("Thread/StaticResetEvent.zig");
+pub const Mutex = @import("Thread/Mutex.zig");
+pub const Semaphore = @import("Thread/Semaphore.zig");
+pub const Condition = @import("Thread/Condition.zig");
+
+pub const use_pthreads = std.Target.current.os.tag != .windows and builtin.link_libc;
+
+const Thread = @This();
+const std = @import("std.zig");
+const builtin = std.builtin;
+const os = std.os;
+const mem = std.mem;
+const windows = std.os.windows;
+const c = std.c;
+const assert = std.debug.assert;
+
+const bad_startfn_ret = "expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'";
+
+/// Represents a kernel thread handle.
+/// May be an integer or a pointer depending on the platform.
+/// On Linux and POSIX, this is the same as Id.
+pub const Handle = if (use_pthreads)
+ c.pthread_t
+else switch (std.Target.current.os.tag) {
+ .linux => i32,
+ .windows => windows.HANDLE,
+ else => void,
+};
+
+/// Represents a unique ID per thread.
+/// May be an integer or pointer depending on the platform.
+/// On Linux and POSIX, this is the same as Handle.
+pub const Id = switch (std.Target.current.os.tag) {
+ .windows => windows.DWORD,
+ else => Handle,
+};
+
+pub const Data = if (use_pthreads)
+ struct {
+ handle: Thread.Handle,
+ memory: []u8,
+ }
+else switch (std.Target.current.os.tag) {
+ .linux => struct {
+ handle: Thread.Handle,
+ memory: []align(mem.page_size) u8,
+ },
+ .windows => struct {
+ handle: Thread.Handle,
+ alloc_start: *c_void,
+ heap_handle: windows.HANDLE,
+ },
+ else => struct {},
+};
+
+/// Signals the processor that it is inside a busy-wait spin-loop ("spin lock").
+pub fn spinLoopHint() void {
+ switch (std.Target.current.cpu.arch) {
+ .i386, .x86_64 => asm volatile ("pause" ::: "memory"),
+ .arm, .aarch64 => asm volatile ("yield" ::: "memory"),
+ else => {},
+ }
+}
+
+/// Returns the ID of the calling thread.
+/// Makes a syscall every time the function is called.
+/// On Linux and POSIX, this Id is the same as a Handle.
+pub fn getCurrentId() Id {
+ if (use_pthreads) {
+ return c.pthread_self();
+ } else return switch (std.Target.current.os.tag) {
+ .linux => os.linux.gettid(),
+ .windows => windows.kernel32.GetCurrentThreadId(),
+ else => @compileError("Unsupported OS"),
+ };
+}
+
+/// Returns the handle of this thread.
+/// On Linux and POSIX, this is the same as Id.
+/// On Linux, it is possible that the thread spawned with `spawn`
+/// finishes executing entirely before the clone syscall completes. In this
+/// case, this function will return 0 rather than the no-longer-existing thread's
+/// pid.
+pub fn handle(self: Thread) Handle {
+ return self.data.handle;
+}
+
+pub fn wait(self: *Thread) void {
+ if (use_pthreads) {
+ const err = c.pthread_join(self.data.handle, null);
+ switch (err) {
+ 0 => {},
+ os.EINVAL => unreachable,
+ os.ESRCH => unreachable,
+ os.EDEADLK => unreachable,
+ else => unreachable,
+ }
+ std.heap.c_allocator.free(self.data.memory);
+ std.heap.c_allocator.destroy(self);
+ } else switch (std.Target.current.os.tag) {
+ .linux => {
+ while (true) {
+ const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst);
+ if (pid_value == 0) break;
+ const rc = os.linux.futex_wait(&self.data.handle, os.linux.FUTEX_WAIT, pid_value, null);
+ switch (os.linux.getErrno(rc)) {
+ 0 => continue,
+ os.EINTR => continue,
+ os.EAGAIN => continue,
+ else => unreachable,
+ }
+ }
+ os.munmap(self.data.memory);
+ },
+ .windows => {
+ windows.WaitForSingleObjectEx(self.data.handle, windows.INFINITE, false) catch unreachable;
+ windows.CloseHandle(self.data.handle);
+ windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start);
+ },
+ else => @compileError("Unsupported OS"),
+ }
+}
+
+pub const SpawnError = error{
+ /// A system-imposed limit on the number of threads was encountered.
+ /// There are a number of limits that may trigger this error:
+ /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)),
+ /// which limits the number of processes and threads for a real
+ /// user ID, was reached;
+ /// * the kernel's system-wide limit on the number of processes and
+ /// threads, /proc/sys/kernel/threads-max, was reached (see
+ /// proc(5));
+ /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was
+ /// reached (see proc(5)); or
+ /// * the PID limit (pids.max) imposed by the cgroup "process num‐
+ /// ber" (PIDs) controller was reached.
+ ThreadQuotaExceeded,
+
+ /// The kernel cannot allocate sufficient memory to allocate a task structure
+ /// for the child, or to copy those parts of the caller's context that need to
+ /// be copied.
+ SystemResources,
+
+ /// Not enough userland memory to spawn the thread.
+ OutOfMemory,
+
+ /// `mlockall` is enabled, and the memory needed to spawn the thread
+ /// would exceed the limit.
+ LockedMemoryLimitExceeded,
+
+ Unexpected,
+};
+
+/// caller must call wait on the returned thread
+/// fn startFn(@TypeOf(context)) T
+/// where T is u8, noreturn, void, or !void
+/// caller must call wait on the returned thread
+pub fn spawn(context: anytype, comptime startFn: anytype) SpawnError!*Thread {
+ if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
+ // TODO compile-time call graph analysis to determine stack upper bound
+ // https://github.com/ziglang/zig/issues/157
+ const default_stack_size = 16 * 1024 * 1024;
+
+ const Context = @TypeOf(context);
+ comptime assert(@typeInfo(@TypeOf(startFn)).Fn.args[0].arg_type.? == Context);
+
+ if (std.Target.current.os.tag == .windows) {
+ const WinThread = struct {
+ const OuterContext = struct {
+ thread: Thread,
+ inner: Context,
+ };
+ fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {
+ const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
+
+ switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
+ .NoReturn => {
+ startFn(arg);
+ },
+ .Void => {
+ startFn(arg);
+ return 0;
+ },
+ .Int => |info| {
+ if (info.bits != 8) {
+ @compileError(bad_startfn_ret);
+ }
+ return startFn(arg);
+ },
+ .ErrorUnion => |info| {
+ if (info.payload != void) {
+ @compileError(bad_startfn_ret);
+ }
+ startFn(arg) catch |err| {
+ std.debug.warn("error: {s}\n", .{@errorName(err)});
+ if (@errorReturnTrace()) |trace| {
+ std.debug.dumpStackTrace(trace.*);
+ }
+ };
+ return 0;
+ },
+ else => @compileError(bad_startfn_ret),
+ }
+ }
+ };
+
+ const heap_handle = windows.kernel32.GetProcessHeap() orelse return error.OutOfMemory;
+ const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
+ const bytes_ptr = windows.kernel32.HeapAlloc(heap_handle, 0, byte_count) orelse return error.OutOfMemory;
+ errdefer assert(windows.kernel32.HeapFree(heap_handle, 0, bytes_ptr) != 0);
+ const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count];
+ const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
+ outer_context.* = WinThread.OuterContext{
+ .thread = Thread{
+ .data = Thread.Data{
+ .heap_handle = heap_handle,
+ .alloc_start = bytes_ptr,
+ .handle = undefined,
+ },
+ },
+ .inner = context,
+ };
+
+ const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);
+ outer_context.thread.data.handle = windows.kernel32.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse {
+ switch (windows.kernel32.GetLastError()) {
+ else => |err| return windows.unexpectedError(err),
+ }
+ };
+ return &outer_context.thread;
+ }
+
+ const MainFuncs = struct {
+ fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 {
+ const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
+
+ switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
+ .NoReturn => {
+ startFn(arg);
+ },
+ .Void => {
+ startFn(arg);
+ return 0;
+ },
+ .Int => |info| {
+ if (info.bits != 8) {
+ @compileError(bad_startfn_ret);
+ }
+ return startFn(arg);
+ },
+ .ErrorUnion => |info| {
+ if (info.payload != void) {
+ @compileError(bad_startfn_ret);
+ }
+ startFn(arg) catch |err| {
+ std.debug.warn("error: {s}\n", .{@errorName(err)});
+ if (@errorReturnTrace()) |trace| {
+ std.debug.dumpStackTrace(trace.*);
+ }
+ };
+ return 0;
+ },
+ else => @compileError(bad_startfn_ret),
+ }
+ }
+ fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {
+ const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*;
+
+ switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
+ .NoReturn => {
+ startFn(arg);
+ },
+ .Void => {
+ startFn(arg);
+ return null;
+ },
+ .Int => |info| {
+ if (info.bits != 8) {
+ @compileError(bad_startfn_ret);
+ }
+ // pthreads don't support exit status, ignore value
+ _ = startFn(arg);
+ return null;
+ },
+ .ErrorUnion => |info| {
+ if (info.payload != void) {
+ @compileError(bad_startfn_ret);
+ }
+ startFn(arg) catch |err| {
+ std.debug.warn("error: {s}\n", .{@errorName(err)});
+ if (@errorReturnTrace()) |trace| {
+ std.debug.dumpStackTrace(trace.*);
+ }
+ };
+ return null;
+ },
+ else => @compileError(bad_startfn_ret),
+ }
+ }
+ };
+
+ if (Thread.use_pthreads) {
+ var attr: c.pthread_attr_t = undefined;
+ if (c.pthread_attr_init(&attr) != 0) return error.SystemResources;
+ defer assert(c.pthread_attr_destroy(&attr) == 0);
+
+ const thread_obj = try std.heap.c_allocator.create(Thread);
+ errdefer std.heap.c_allocator.destroy(thread_obj);
+ if (@sizeOf(Context) > 0) {
+ thread_obj.data.memory = try std.heap.c_allocator.allocAdvanced(
+ u8,
+ @alignOf(Context),
+ @sizeOf(Context),
+ .at_least,
+ );
+ errdefer std.heap.c_allocator.free(thread_obj.data.memory);
+ mem.copy(u8, thread_obj.data.memory, mem.asBytes(&context));
+ } else {
+ thread_obj.data.memory = @as([*]u8, undefined)[0..0];
+ }
+
+ // Use the same set of parameters used by the libc-less impl.
+ assert(c.pthread_attr_setstacksize(&attr, default_stack_size) == 0);
+ assert(c.pthread_attr_setguardsize(&attr, mem.page_size) == 0);
+
+ const err = c.pthread_create(
+ &thread_obj.data.handle,
+ &attr,
+ MainFuncs.posixThreadMain,
+ thread_obj.data.memory.ptr,
+ );
+ switch (err) {
+ 0 => return thread_obj,
+ os.EAGAIN => return error.SystemResources,
+ os.EPERM => unreachable,
+ os.EINVAL => unreachable,
+ else => return os.unexpectedErrno(@intCast(usize, err)),
+ }
+
+ return thread_obj;
+ }
+
+ var guard_end_offset: usize = undefined;
+ var stack_end_offset: usize = undefined;
+ var thread_start_offset: usize = undefined;
+ var context_start_offset: usize = undefined;
+ var tls_start_offset: usize = undefined;
+ const mmap_len = blk: {
+ var l: usize = mem.page_size;
+ // Allocate a guard page right after the end of the stack region
+ guard_end_offset = l;
+ // The stack itself, which grows downwards.
+ l = mem.alignForward(l + default_stack_size, mem.page_size);
+ stack_end_offset = l;
+ // Above the stack, so that it can be in the same mmap call, put the Thread object.
+ l = mem.alignForward(l, @alignOf(Thread));
+ thread_start_offset = l;
+ l += @sizeOf(Thread);
+ // Next, the Context object.
+ if (@sizeOf(Context) != 0) {
+ l = mem.alignForward(l, @alignOf(Context));
+ context_start_offset = l;
+ l += @sizeOf(Context);
+ }
+ // Finally, the Thread Local Storage, if any.
+ l = mem.alignForward(l, os.linux.tls.tls_image.alloc_align);
+ tls_start_offset = l;
+ l += os.linux.tls.tls_image.alloc_size;
+ // Round the size to the page size.
+ break :blk mem.alignForward(l, mem.page_size);
+ };
+
+ const mmap_slice = mem: {
+ // Map the whole stack with no rw permissions to avoid
+ // committing the whole region right away
+ const mmap_slice = os.mmap(
+ null,
+ mmap_len,
+ os.PROT_NONE,
+ os.MAP_PRIVATE | os.MAP_ANONYMOUS,
+ -1,
+ 0,
+ ) catch |err| switch (err) {
+ error.MemoryMappingNotSupported => unreachable,
+ error.AccessDenied => unreachable,
+ error.PermissionDenied => unreachable,
+ else => |e| return e,
+ };
+ errdefer os.munmap(mmap_slice);
+
+ // Map everything but the guard page as rw
+ os.mprotect(
+ mmap_slice[guard_end_offset..],
+ os.PROT_READ | os.PROT_WRITE,
+ ) catch |err| switch (err) {
+ error.AccessDenied => unreachable,
+ else => |e| return e,
+ };
+
+ break :mem mmap_slice;
+ };
+
+ const mmap_addr = @ptrToInt(mmap_slice.ptr);
+
+ const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
+ thread_ptr.data.memory = mmap_slice;
+
+ var arg: usize = undefined;
+ if (@sizeOf(Context) != 0) {
+ arg = mmap_addr + context_start_offset;
+ const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
+ context_ptr.* = context;
+ }
+
+ if (std.Target.current.os.tag == .linux) {
+ const flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES |
+ os.CLONE_SIGHAND | os.CLONE_THREAD | os.CLONE_SYSVSEM |
+ os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID |
+ os.CLONE_DETACHED | os.CLONE_SETTLS;
+ // This structure is only needed when targeting i386
+ var user_desc: if (std.Target.current.cpu.arch == .i386) os.linux.user_desc else void = undefined;
+
+ const tls_area = mmap_slice[tls_start_offset..];
+ const tp_value = os.linux.tls.prepareTLS(tls_area);
+
+ const newtls = blk: {
+ if (std.Target.current.cpu.arch == .i386) {
+ user_desc = os.linux.user_desc{
+ .entry_number = os.linux.tls.tls_image.gdt_entry_number,
+ .base_addr = tp_value,
+ .limit = 0xfffff,
+ .seg_32bit = 1,
+ .contents = 0, // Data
+ .read_exec_only = 0,
+ .limit_in_pages = 1,
+ .seg_not_present = 0,
+ .useable = 1,
+ };
+ break :blk @ptrToInt(&user_desc);
+ } else {
+ break :blk tp_value;
+ }
+ };
+
+ const rc = os.linux.clone(
+ MainFuncs.linuxThreadMain,
+ mmap_addr + stack_end_offset,
+ flags,
+ arg,
+ &thread_ptr.data.handle,
+ newtls,
+ &thread_ptr.data.handle,
+ );
+ switch (os.errno(rc)) {
+ 0 => return thread_ptr,
+ os.EAGAIN => return error.ThreadQuotaExceeded,
+ os.EINVAL => unreachable,
+ os.ENOMEM => return error.SystemResources,
+ os.ENOSPC => unreachable,
+ os.EPERM => unreachable,
+ os.EUSERS => unreachable,
+ else => |err| return os.unexpectedErrno(err),
+ }
+ } else {
+ @compileError("Unsupported OS");
+ }
+}
+
+pub const CpuCountError = error{
+ PermissionDenied,
+ SystemResources,
+ Unexpected,
+};
+
+pub fn cpuCount() CpuCountError!usize {
+ switch (std.Target.current.os.tag) {
+ .linux => {
+ const cpu_set = try os.sched_getaffinity(0);
+ return @as(usize, os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast
+ },
+ .windows => {
+ return os.windows.peb().NumberOfProcessors;
+ },
+ .openbsd => {
+ var count: c_int = undefined;
+ var count_size: usize = @sizeOf(c_int);
+ const mib = [_]c_int{ os.CTL_HW, os.HW_NCPUONLINE };
+ os.sysctl(&mib, &count, &count_size, null, 0) catch |err| switch (err) {
+ error.NameTooLong, error.UnknownName => unreachable,
+ else => |e| return e,
+ };
+ return @intCast(usize, count);
+ },
+ .haiku => {
+ var count: u32 = undefined;
+ var system_info: os.system_info = undefined;
+ const rc = os.system.get_system_info(&system_info);
+ count = system_info.cpu_count;
+ return @intCast(usize, count);
+ },
+ else => {
+ var count: c_int = undefined;
+ var count_len: usize = @sizeOf(c_int);
+ const name = if (comptime std.Target.current.isDarwin()) "hw.logicalcpu" else "hw.ncpu";
+ os.sysctlbynameZ(name, &count, &count_len, null, 0) catch |err| switch (err) {
+ error.NameTooLong, error.UnknownName => unreachable,
+ else => |e| return e,
+ };
+ return @intCast(usize, count);
+ },
+ }
+}
+
+pub fn getCurrentThreadId() u64 {
+ switch (std.Target.current.os.tag) {
+ .linux => {
+ // Use the syscall directly as musl doesn't provide a wrapper.
+ return @bitCast(u32, os.linux.gettid());
+ },
+ .windows => {
+ return os.windows.kernel32.GetCurrentThreadId();
+ },
+ .macos, .ios, .watchos, .tvos => {
+ var thread_id: u64 = undefined;
+ // Pass thread=null to get the current thread ID.
+ assert(c.pthread_threadid_np(null, &thread_id) == 0);
+ return thread_id;
+ },
+ .netbsd => {
+ return @bitCast(u32, c._lwp_self());
+ },
+ .freebsd => {
+ return @bitCast(u32, c.pthread_getthreadid_np());
+ },
+ .openbsd => {
+ return @bitCast(u32, c.getthrid());
+ },
+ .haiku => {
+ return @bitCast(u32, c.find_thread(null));
+ },
+ else => {
+ @compileError("getCurrentThreadId not implemented for this platform");
+ },
+ }
+}
+
+test {
+ if (!builtin.single_threaded) {
+ std.testing.refAllDecls(@This());
+ }
+}
diff --git a/lib/std/Thread/AutoResetEvent.zig b/lib/std/Thread/AutoResetEvent.zig
new file mode 100644
index 0000000000..8b8b5658bf
--- /dev/null
+++ b/lib/std/Thread/AutoResetEvent.zig
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! Similar to `StaticResetEvent` but on `set()` it also (atomically) does `reset()`.
+//! Unlike StaticResetEvent, `wait()` can only be called by one thread (MPSC-like).
+//!
+//! AutoResetEvent has 3 possible states:
+//! - UNSET: the AutoResetEvent is currently unset
+//! - SET: the AutoResetEvent was notified before a wait() was called
+//! - <StaticResetEvent pointer>: there is an active waiter waiting for a notification.
+//!
+//! When attempting to wait:
+//! if the event is unset, it registers a ResetEvent pointer to be notified when the event is set
+//! if the event is already set, then it consumes the notification and resets the event.
+//!
+//! When attempting to notify:
+//! if the event is unset, then we set the event
+//! if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent
+//!
+//! This ensures that the event is automatically reset after a wait() has been issued
+//! and avoids the race condition when using StaticResetEvent in the following scenario:
+//! thread 1 | thread 2
+//! StaticResetEvent.wait() |
+//! | StaticResetEvent.set()
+//! | StaticResetEvent.set()
+//! StaticResetEvent.reset() |
+//! StaticResetEvent.wait() | (missed the second .set() notification above)
+
+state: usize = UNSET,
+
+const std = @import("../std.zig");
+const builtin = @import("builtin");
+const testing = std.testing;
+const assert = std.debug.assert;
+const StaticResetEvent = std.Thread.StaticResetEvent;
+const AutoResetEvent = @This();
+
+const UNSET = 0;
+const SET = 1;
+
+/// the minimum alignment for the `*StaticResetEvent` created by wait*()
+const event_align = std.math.max(@alignOf(StaticResetEvent), 2);
+
+pub fn wait(self: *AutoResetEvent) void {
+ self.waitFor(null) catch unreachable;
+}
+
+pub fn timedWait(self: *AutoResetEvent, timeout: u64) error{TimedOut}!void {
+ return self.waitFor(timeout);
+}
+
+fn waitFor(self: *AutoResetEvent, timeout: ?u64) error{TimedOut}!void {
+ // lazily initialized StaticResetEvent
+ var reset_event: StaticResetEvent align(event_align) = undefined;
+ var has_reset_event = false;
+
+ var state = @atomicLoad(usize, &self.state, .SeqCst);
+ while (true) {
+ // consume a notification if there is any
+ if (state == SET) {
+ @atomicStore(usize, &self.state, UNSET, .SeqCst);
+ return;
+ }
+
+ // check if theres currently a pending ResetEvent pointer already registered
+ if (state != UNSET) {
+ unreachable; // multiple waiting threads on the same AutoResetEvent
+ }
+
+ // lazily initialize the ResetEvent if it hasn't been already
+ if (!has_reset_event) {
+ has_reset_event = true;
+ reset_event = .{};
+ }
+
+ // Since the AutoResetEvent currently isnt set,
+ // try to register our ResetEvent on it to wait
+ // for a set() call from another thread.
+ if (@cmpxchgWeak(
+ usize,
+ &self.state,
+ UNSET,
+ @ptrToInt(&reset_event),
+ .SeqCst,
+ .SeqCst,
+ )) |new_state| {
+ state = new_state;
+ continue;
+ }
+
+ // if no timeout was specified, then just wait forever
+ const timeout_ns = timeout orelse {
+ reset_event.wait();
+ return;
+ };
+
+ // wait with a timeout and return if signalled via set()
+ switch (reset_event.timedWait(timeout_ns)) {
+ .event_set => return,
+ .timed_out => {},
+ }
+
+ // If we timed out, we need to transition the AutoResetEvent back to UNSET.
+ // If we don't, then when we return, a set() thread could observe a pointer to an invalid ResetEvent.
+ state = @cmpxchgStrong(
+ usize,
+ &self.state,
+ @ptrToInt(&reset_event),
+ UNSET,
+ .SeqCst,
+ .SeqCst,
+ ) orelse return error.TimedOut;
+
+ // We didn't manage to unregister ourselves from the state.
+ if (state == SET) {
+ unreachable; // AutoResetEvent notified without waking up the waiting thread
+ } else if (state != UNSET) {
+ unreachable; // multiple waiting threads on the same AutoResetEvent observed when timing out
+ }
+
+ // This menas a set() thread saw our ResetEvent pointer, acquired it, and is trying to wake it up.
+ // We need to wait for it to wake up our ResetEvent before we can return and invalidate it.
+ // We don't return error.TimedOut here as it technically notified us while we were "timing out".
+ reset_event.wait();
+ return;
+ }
+}
+
+pub fn set(self: *AutoResetEvent) void {
+ var state = @atomicLoad(usize, &self.state, .SeqCst);
+ while (true) {
+ // If the AutoResetEvent is already set, there is nothing else left to do
+ if (state == SET) {
+ return;
+ }
+
+ // If the AutoResetEvent isn't set,
+ // then try to leave a notification for the wait() thread that we set() it.
+ if (state == UNSET) {
+ state = @cmpxchgWeak(
+ usize,
+ &self.state,
+ UNSET,
+ SET,
+ .SeqCst,
+ .SeqCst,
+ ) orelse return;
+ continue;
+ }
+
+ // There is a ResetEvent pointer registered on the AutoResetEvent event thats waiting.
+ // Try to acquire ownership of it so that we can wake it up.
+ // This also resets the AutoResetEvent so that there is no race condition as defined above.
+ if (@cmpxchgWeak(
+ usize,
+ &self.state,
+ state,
+ UNSET,
+ .SeqCst,
+ .SeqCst,
+ )) |new_state| {
+ state = new_state;
+ continue;
+ }
+
+ const reset_event = @intToPtr(*align(event_align) StaticResetEvent, state);
+ reset_event.set();
+ return;
+ }
+}
+
+test "basic usage" {
+ // test local code paths
+ {
+ var event = AutoResetEvent{};
+ testing.expectError(error.TimedOut, event.timedWait(1));
+ event.set();
+ event.wait();
+ }
+
+ // test cross-thread signaling
+ if (builtin.single_threaded)
+ return;
+
+ const Context = struct {
+ value: u128 = 0,
+ in: AutoResetEvent = AutoResetEvent{},
+ out: AutoResetEvent = AutoResetEvent{},
+
+ const Self = @This();
+
+ fn sender(self: *Self) void {
+ testing.expect(self.value == 0);
+ self.value = 1;
+ self.out.set();
+
+ self.in.wait();
+ testing.expect(self.value == 2);
+ self.value = 3;
+ self.out.set();
+
+ self.in.wait();
+ testing.expect(self.value == 4);
+ }
+
+ fn receiver(self: *Self) void {
+ self.out.wait();
+ testing.expect(self.value == 1);
+ self.value = 2;
+ self.in.set();
+
+ self.out.wait();
+ testing.expect(self.value == 3);
+ self.value = 4;
+ self.in.set();
+ }
+ };
+
+ var context = Context{};
+ const send_thread = try std.Thread.spawn(&context, Context.sender);
+ const recv_thread = try std.Thread.spawn(&context, Context.receiver);
+
+ send_thread.wait();
+ recv_thread.wait();
+}
diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig
new file mode 100644
index 0000000000..a14b57f6b4
--- /dev/null
+++ b/lib/std/Thread/Condition.zig
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! A condition provides a way for a kernel thread to block until it is signaled
+//! to wake up. Spurious wakeups are possible.
+//! This API supports static initialization and does not require deinitialization.
+
+impl: Impl = .{},
+
+const std = @import("../std.zig");
+const Condition = @This();
+const windows = std.os.windows;
+const linux = std.os.linux;
+const Mutex = std.Thread.Mutex;
+const assert = std.debug.assert;
+
+pub fn wait(cond: *Condition, mutex: *Mutex) void {
+ cond.impl.wait(mutex);
+}
+
+pub fn signal(cond: *Condition) void {
+ cond.impl.signal();
+}
+
+pub fn broadcast(cond: *Condition) void {
+ cond.impl.broadcast();
+}
+
+const Impl = if (std.builtin.single_threaded)
+ SingleThreadedCondition
+else if (std.Target.current.os.tag == .windows)
+ WindowsCondition
+else if (std.Thread.use_pthreads)
+ PthreadCondition
+else
+ AtomicCondition;
+
+pub const SingleThreadedCondition = struct {
+ pub fn wait(cond: *SingleThreadedCondition, mutex: *Mutex) void {
+ unreachable; // deadlock detected
+ }
+
+ pub fn signal(cond: *SingleThreadedCondition) void {}
+
+ pub fn broadcast(cond: *SingleThreadedCondition) void {}
+};
+
+pub const WindowsCondition = struct {
+ cond: windows.CONDITION_VARIABLE = windows.CONDITION_VARIABLE_INIT,
+
+ pub fn wait(cond: *WindowsCondition, mutex: *Mutex) void {
+ const rc = windows.kernel32.SleepConditionVariableSRW(
+ &cond.cond,
+ &mutex.srwlock,
+ windows.INFINITE,
+ @as(windows.ULONG, 0),
+ );
+ assert(rc != windows.FALSE);
+ }
+
+ pub fn signal(cond: *WindowsCondition) void {
+ windows.kernel32.WakeConditionVariable(&cond.cond);
+ }
+
+ pub fn broadcast(cond: *WindowsCondition) void {
+ windows.kernel32.WakeAllConditionVariable(&cond.cond);
+ }
+};
+
+pub const PthreadCondition = struct {
+ cond: std.c.pthread_cond_t = .{},
+
+ pub fn wait(cond: *PthreadCondition, mutex: *Mutex) void {
+ const rc = std.c.pthread_cond_wait(&cond.cond, &mutex.impl.pthread_mutex);
+ assert(rc == 0);
+ }
+
+ pub fn signal(cond: *PthreadCondition) void {
+ const rc = std.c.pthread_cond_signal(&cond.cond);
+ assert(rc == 0);
+ }
+
+ pub fn broadcast(cond: *PthreadCondition) void {
+ const rc = std.c.pthread_cond_broadcast(&cond.cond);
+ assert(rc == 0);
+ }
+};
+
+pub const AtomicCondition = struct {
+ pending: bool = false,
+ queue_mutex: Mutex = .{},
+ queue_list: QueueList = .{},
+
+ pub const QueueList = std.SinglyLinkedList(QueueItem);
+
+ pub const QueueItem = struct {
+ futex: i32 = 0,
+
+ fn wait(cond: *@This()) void {
+ while (@atomicLoad(i32, &cond.futex, .Acquire) == 0) {
+ switch (std.Target.current.os.tag) {
+ .linux => {
+ switch (linux.getErrno(linux.futex_wait(
+ &cond.futex,
+ linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAIT,
+ 0,
+ null,
+ ))) {
+ 0 => {},
+ std.os.EINTR => {},
+ std.os.EAGAIN => {},
+ else => unreachable,
+ }
+ },
+ else => spinLoopHint(),
+ }
+ }
+ }
+
+ fn notify(cond: *@This()) void {
+ @atomicStore(i32, &cond.futex, 1, .Release);
+
+ switch (std.Target.current.os.tag) {
+ .linux => {
+ switch (linux.getErrno(linux.futex_wake(
+ &cond.futex,
+ linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE,
+ 1,
+ ))) {
+ 0 => {},
+ std.os.EFAULT => {},
+ else => unreachable,
+ }
+ },
+ else => {},
+ }
+ }
+ };
+
+ pub fn wait(cond: *AtomicCondition, mutex: *Mutex) void {
+ var waiter = QueueList.Node{ .data = .{} };
+
+ {
+ const held = cond.queue_mutex.acquire();
+ defer held.release();
+
+ cond.queue_list.prepend(&waiter);
+ @atomicStore(bool, &cond.pending, true, .SeqCst);
+ }
+
+ mutex.unlock();
+ waiter.data.wait();
+ mutex.lock();
+ }
+
+ pub fn signal(cond: *AtomicCondition) void {
+ if (@atomicLoad(bool, &cond.pending, .SeqCst) == false)
+ return;
+
+ const maybe_waiter = blk: {
+ const held = cond.queue_mutex.acquire();
+ defer held.release();
+
+ const maybe_waiter = cond.queue_list.popFirst();
+ @atomicStore(bool, &cond.pending, cond.queue_list.first != null, .SeqCst);
+ break :blk maybe_waiter;
+ };
+
+ if (maybe_waiter) |waiter|
+ waiter.data.notify();
+ }
+
+ pub fn broadcast(cond: *AtomicCondition) void {
+ if (@atomicLoad(bool, &cond.pending, .SeqCst) == false)
+ return;
+
+ @atomicStore(bool, &cond.pending, false, .SeqCst);
+
+ var waiters = blk: {
+ const held = cond.queue_mutex.acquire();
+ defer held.release();
+
+ const waiters = cond.queue_list;
+ cond.queue_list = .{};
+ break :blk waiters;
+ };
+
+ while (waiters.popFirst()) |waiter|
+ waiter.data.notify();
+ }
+};
diff --git a/lib/std/Thread/Mutex.zig b/lib/std/Thread/Mutex.zig
new file mode 100644
index 0000000000..94711bcda0
--- /dev/null
+++ b/lib/std/Thread/Mutex.zig
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! Lock may be held only once. If the same thread tries to acquire
+//! the same mutex twice, it deadlocks. This type supports static
+//! initialization and is at most `@sizeOf(usize)` in size. When an
+//! application is built in single threaded release mode, all the
+//! functions are no-ops. In single threaded debug mode, there is
+//! deadlock detection.
+//!
+//! Example usage:
+//! var m = Mutex{};
+//!
+//! const lock = m.acquire();
+//! defer lock.release();
+//! ... critical code
+//!
+//! Non-blocking:
+//! if (m.tryAcquire) |lock| {
+//! defer lock.release();
+//! // ... critical section
+//! } else {
+//! // ... lock not acquired
+//! }
+
+impl: Impl = .{},
+
+const Mutex = @This();
+const std = @import("../std.zig");
+const builtin = std.builtin;
+const os = std.os;
+const assert = std.debug.assert;
+const windows = os.windows;
+const linux = os.linux;
+const testing = std.testing;
+const StaticResetEvent = std.thread.StaticResetEvent;
+
+/// Try to acquire the mutex without blocking. Returns `null` if the mutex is
+/// unavailable. Otherwise returns `Held`. Call `release` on `Held`.
+pub fn tryAcquire(m: *Mutex) ?Impl.Held {
+ return m.impl.tryAcquire();
+}
+
+/// Acquire the mutex. Deadlocks if the mutex is already
+/// held by the calling thread.
+pub fn acquire(m: *Mutex) Impl.Held {
+ return m.impl.acquire();
+}
+
+const Impl = if (builtin.single_threaded)
+ Dummy
+else if (builtin.os.tag == .windows)
+ WindowsMutex
+else if (std.Thread.use_pthreads)
+ PthreadMutex
+else
+ AtomicMutex;
+
+pub const AtomicMutex = struct {
+ state: State = .unlocked,
+
+ const State = enum(i32) {
+ unlocked,
+ locked,
+ waiting,
+ };
+
+ pub const Held = struct {
+ mutex: *AtomicMutex,
+
+ pub fn release(held: Held) void {
+ switch (@atomicRmw(State, &held.mutex.state, .Xchg, .unlocked, .Release)) {
+ .unlocked => unreachable,
+ .locked => {},
+ .waiting => held.mutex.unlockSlow(),
+ }
+ }
+ };
+
+ pub fn tryAcquire(m: *AtomicMutex) ?Held {
+ if (@cmpxchgStrong(
+ State,
+ &m.state,
+ .unlocked,
+ .locked,
+ .Acquire,
+ .Monotonic,
+ ) == null) {
+ return Held{ .mutex = m };
+ } else {
+ return null;
+ }
+ }
+
+ pub fn acquire(m: *AtomicMutex) Held {
+ switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) {
+ .unlocked => {},
+ else => |s| m.lockSlow(s),
+ }
+ return Held{ .mutex = m };
+ }
+
+ fn lockSlow(m: *AtomicMutex, current_state: State) void {
+ @setCold(true);
+ var new_state = current_state;
+
+ var spin: u8 = 0;
+ while (spin < 100) : (spin += 1) {
+ const state = @cmpxchgWeak(
+ State,
+ &m.state,
+ .unlocked,
+ new_state,
+ .Acquire,
+ .Monotonic,
+ ) orelse return;
+
+ switch (state) {
+ .unlocked => {},
+ .locked => {},
+ .waiting => break,
+ }
+
+ var iter = std.math.min(32, spin + 1);
+ while (iter > 0) : (iter -= 1)
+ std.Thread.spinLoopHint();
+ }
+
+ new_state = .waiting;
+ while (true) {
+ switch (@atomicRmw(State, &m.state, .Xchg, new_state, .Acquire)) {
+ .unlocked => return,
+ else => {},
+ }
+ switch (std.Target.current.os.tag) {
+ .linux => {
+ switch (linux.getErrno(linux.futex_wait(
+ @ptrCast(*const i32, &m.state),
+ linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAIT,
+ @enumToInt(new_state),
+ null,
+ ))) {
+ 0 => {},
+ std.os.EINTR => {},
+ std.os.EAGAIN => {},
+ else => unreachable,
+ }
+ },
+ else => std.Thread.spinLoopHint(),
+ }
+ }
+ }
+
+ fn unlockSlow(m: *AtomicMutex) void {
+ @setCold(true);
+
+ switch (std.Target.current.os.tag) {
+ .linux => {
+ switch (linux.getErrno(linux.futex_wake(
+ @ptrCast(*const i32, &m.state),
+ linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE,
+ 1,
+ ))) {
+ 0 => {},
+ std.os.EFAULT => {},
+ else => unreachable,
+ }
+ },
+ else => {},
+ }
+ }
+};
+
+pub const PthreadMutex = struct {
+ pthread_mutex: std.c.pthread_mutex_t = .{},
+
+ pub const Held = struct {
+ mutex: *PthreadMutex,
+
+ pub fn release(held: Held) void {
+ switch (std.c.pthread_mutex_unlock(&held.mutex.pthread_mutex)) {
+ 0 => return,
+ std.c.EINVAL => unreachable,
+ std.c.EAGAIN => unreachable,
+ std.c.EPERM => unreachable,
+ else => unreachable,
+ }
+ }
+ };
+
+ /// Try to acquire the mutex without blocking. Returns null if
+ /// the mutex is unavailable. Otherwise returns Held. Call
+ /// release on Held.
+ pub fn tryAcquire(m: *PthreadMutex) ?Held {
+ if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == 0) {
+ return Held{ .mutex = m };
+ } else {
+ return null;
+ }
+ }
+
+ /// Acquire the mutex. Will deadlock if the mutex is already
+ /// held by the calling thread.
+ pub fn acquire(m: *PthreadMutex) Held {
+ switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) {
+ 0 => return Held{ .mutex = m },
+ std.c.EINVAL => unreachable,
+ std.c.EBUSY => unreachable,
+ std.c.EAGAIN => unreachable,
+ std.c.EDEADLK => unreachable,
+ std.c.EPERM => unreachable,
+ else => unreachable,
+ }
+ }
+};
+
+/// This has the sematics as `Mutex`, however it does not actually do any
+/// synchronization. Operations are safety-checked no-ops.
+pub const Dummy = struct {
+ lock: @TypeOf(lock_init) = lock_init,
+
+ const lock_init = if (std.debug.runtime_safety) false else {};
+
+ pub const Held = struct {
+ mutex: *Dummy,
+
+ pub fn release(held: Held) void {
+ if (std.debug.runtime_safety) {
+ held.mutex.lock = false;
+ }
+ }
+ };
+
+ /// Try to acquire the mutex without blocking. Returns null if
+ /// the mutex is unavailable. Otherwise returns Held. Call
+ /// release on Held.
+ pub fn tryAcquire(m: *Dummy) ?Held {
+ if (std.debug.runtime_safety) {
+ if (m.lock) return null;
+ m.lock = true;
+ }
+ return Held{ .mutex = m };
+ }
+
+ /// Acquire the mutex. Will deadlock if the mutex is already
+ /// held by the calling thread.
+ pub fn acquire(m: *Dummy) Held {
+ return m.tryAcquire() orelse @panic("deadlock detected");
+ }
+};
+
+const WindowsMutex = struct {
+ srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT,
+
+ pub const Held = struct {
+ mutex: *WindowsMutex,
+
+ pub fn release(held: Held) void {
+ windows.kernel32.ReleaseSRWLockExclusive(&held.mutex.srwlock);
+ }
+ };
+
+ pub fn tryAcquire(m: *WindowsMutex) ?Held {
+ if (windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE) {
+ return Held{ .mutex = m };
+ } else {
+ return null;
+ }
+ }
+
+ pub fn acquire(m: *WindowsMutex) Held {
+ windows.kernel32.AcquireSRWLockExclusive(&m.srwlock);
+ return Held{ .mutex = m };
+ }
+};
+
+const TestContext = struct {
+ mutex: *Mutex,
+ data: i128,
+
+ const incr_count = 10000;
+};
+
+test "basic usage" {
+ var mutex = Mutex{};
+
+ var context = TestContext{
+ .mutex = &mutex,
+ .data = 0,
+ };
+
+ if (builtin.single_threaded) {
+ worker(&context);
+ testing.expect(context.data == TestContext.incr_count);
+ } else {
+ const thread_count = 10;
+ var threads: [thread_count]*std.Thread = undefined;
+ for (threads) |*t| {
+ t.* = try std.Thread.spawn(&context, worker);
+ }
+ for (threads) |t|
+ t.wait();
+
+ testing.expect(context.data == thread_count * TestContext.incr_count);
+ }
+}
+
+fn worker(ctx: *TestContext) void {
+ var i: usize = 0;
+ while (i != TestContext.incr_count) : (i += 1) {
+ const held = ctx.mutex.acquire();
+ defer held.release();
+
+ ctx.data += 1;
+ }
+}
diff --git a/lib/std/Thread/ResetEvent.zig b/lib/std/Thread/ResetEvent.zig
new file mode 100644
index 0000000000..622f9be98e
--- /dev/null
+++ b/lib/std/Thread/ResetEvent.zig
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! A thread-safe resource which supports blocking until signaled.
+//! This API is for kernel threads, not evented I/O.
+//! This API requires being initialized at runtime, and initialization
+//! can fail. Once initialized, the core operations cannot fail.
+//! If you need an abstraction that cannot fail to be initialized, see
+//! `std.Thread.StaticResetEvent`. However if you can handle initialization failure,
+//! it is preferred to use `ResetEvent`.
+
+const ResetEvent = @This();
+const std = @import("../std.zig");
+const builtin = std.builtin;
+const testing = std.testing;
+const assert = std.debug.assert;
+const c = std.c;
+const os = std.os;
+const time = std.time;
+
+impl: Impl,
+
+pub const Impl = if (builtin.single_threaded)
+ std.Thread.StaticResetEvent.DebugEvent
+else if (std.Target.current.isDarwin())
+ DarwinEvent
+else if (std.Thread.use_pthreads)
+ PosixEvent
+else
+ std.Thread.StaticResetEvent.AtomicEvent;
+
+pub const InitError = error{SystemResources};
+
+/// After `init`, it is legal to call any other function.
+pub fn init(ev: *ResetEvent) InitError!void {
+ return ev.impl.init();
+}
+
+/// This function is not thread-safe.
+/// After `deinit`, the only legal function to call is `init`.
+pub fn deinit(ev: *ResetEvent) void {
+ return ev.impl.deinit();
+}
+
+/// Sets the event if not already set and wakes up all the threads waiting on
+/// the event. It is safe to call `set` multiple times before calling `wait`.
+/// However it is illegal to call `set` after `wait` is called until the event
+/// is `reset`. This function is thread-safe.
+pub fn set(ev: *ResetEvent) void {
+ return ev.impl.set();
+}
+
+/// Resets the event to its original, unset state.
+/// This function is *not* thread-safe. It is equivalent to calling
+/// `deinit` followed by `init` but without the possibility of failure.
+pub fn reset(ev: *ResetEvent) void {
+ return ev.impl.reset();
+}
+
+/// Wait for the event to be set by blocking the current thread.
+/// Thread-safe. No spurious wakeups.
+/// Upon return from `wait`, the only functions available to be called
+/// in `ResetEvent` are `reset` and `deinit`.
+pub fn wait(ev: *ResetEvent) void {
+ return ev.impl.wait();
+}
+
+pub const TimedWaitResult = enum { event_set, timed_out };
+
+/// Wait for the event to be set by blocking the current thread.
+/// A timeout in nanoseconds can be provided as a hint for how
+/// long the thread should block on the unset event before returning
+/// `TimedWaitResult.timed_out`.
+/// Thread-safe. No precision of timing is guaranteed.
+/// Upon return from `wait`, the only functions available to be called
+/// in `ResetEvent` are `reset` and `deinit`.
+pub fn timedWait(ev: *ResetEvent, timeout_ns: u64) TimedWaitResult {
+ return ev.impl.timedWait(timeout_ns);
+}
+
+/// Apple has decided to not support POSIX semaphores, so we go with a
+/// different approach using Grand Central Dispatch. This API is exposed
+/// by libSystem so it is guaranteed to be available on all Darwin platforms.
+pub const DarwinEvent = struct {
+ sem: c.dispatch_semaphore_t = undefined,
+
+ pub fn init(ev: *DarwinEvent) !void {
+ ev.* = .{
+ .sem = c.dispatch_semaphore_create(0) orelse return error.SystemResources,
+ };
+ }
+
+ pub fn deinit(ev: *DarwinEvent) void {
+ c.dispatch_release(ev.sem);
+ ev.* = undefined;
+ }
+
+ pub fn set(ev: *DarwinEvent) void {
+ // Empirically this returns the numerical value of the semaphore.
+ _ = c.dispatch_semaphore_signal(ev.sem);
+ }
+
+ pub fn wait(ev: *DarwinEvent) void {
+ assert(c.dispatch_semaphore_wait(ev.sem, c.DISPATCH_TIME_FOREVER) == 0);
+ }
+
+ pub fn timedWait(ev: *DarwinEvent, timeout_ns: u64) TimedWaitResult {
+ const t = c.dispatch_time(c.DISPATCH_TIME_NOW, @intCast(i64, timeout_ns));
+ if (c.dispatch_semaphore_wait(ev.sem, t) != 0) {
+ return .timed_out;
+ } else {
+ return .event_set;
+ }
+ }
+
+ pub fn reset(ev: *DarwinEvent) void {
+ // Keep calling until the semaphore goes back down to 0.
+ while (c.dispatch_semaphore_wait(ev.sem, c.DISPATCH_TIME_NOW) == 0) {}
+ }
+};
+
+/// POSIX semaphores must be initialized at runtime because they are allowed to
+/// be implemented as file descriptors, in which case initialization would require
+/// a syscall to open the fd.
+pub const PosixEvent = struct {
+ sem: c.sem_t = undefined,
+
+ pub fn init(ev: *PosixEvent) !void {
+ switch (c.getErrno(c.sem_init(&ev.sem, 0, 0))) {
+ 0 => return,
+ else => return error.SystemResources,
+ }
+ }
+
+ pub fn deinit(ev: *PosixEvent) void {
+ assert(c.sem_destroy(&ev.sem) == 0);
+ ev.* = undefined;
+ }
+
+ pub fn set(ev: *PosixEvent) void {
+ assert(c.sem_post(&ev.sem) == 0);
+ }
+
+ pub fn wait(ev: *PosixEvent) void {
+ while (true) {
+ switch (c.getErrno(c.sem_wait(&ev.sem))) {
+ 0 => return,
+ c.EINTR => continue,
+ c.EINVAL => unreachable,
+ else => unreachable,
+ }
+ }
+ }
+
+ pub fn timedWait(ev: *PosixEvent, timeout_ns: u64) TimedWaitResult {
+ var ts: os.timespec = undefined;
+ var timeout_abs = timeout_ns;
+ os.clock_gettime(os.CLOCK_REALTIME, &ts) catch return .timed_out;
+ timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s;
+ timeout_abs += @intCast(u64, ts.tv_nsec);
+ ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s));
+ ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s));
+ while (true) {
+ switch (c.getErrno(c.sem_timedwait(&ev.sem, &ts))) {
+ 0 => return .event_set,
+ c.EINTR => continue,
+ c.EINVAL => unreachable,
+ c.ETIMEDOUT => return .timed_out,
+ else => unreachable,
+ }
+ }
+ }
+
+ pub fn reset(ev: *PosixEvent) void {
+ while (true) {
+ switch (c.getErrno(c.sem_trywait(&ev.sem))) {
+ 0 => continue, // Need to make it go to zero.
+ c.EINTR => continue,
+ c.EINVAL => unreachable,
+ c.EAGAIN => return, // The semaphore currently has the value zero.
+ else => unreachable,
+ }
+ }
+ }
+};
+
+test "basic usage" {
+ var event: ResetEvent = undefined;
+ try event.init();
+ defer event.deinit();
+
+ // test event setting
+ event.set();
+
+ // test event resetting
+ event.reset();
+
+ // test event waiting (non-blocking)
+ event.set();
+ event.wait();
+ event.reset();
+
+ event.set();
+ testing.expectEqual(TimedWaitResult.event_set, event.timedWait(1));
+
+ // test cross-thread signaling
+ if (builtin.single_threaded)
+ return;
+
+ const Context = struct {
+ const Self = @This();
+
+ value: u128,
+ in: ResetEvent,
+ out: ResetEvent,
+
+ fn init(self: *Self) !void {
+ self.* = .{
+ .value = 0,
+ .in = undefined,
+ .out = undefined,
+ };
+ try self.in.init();
+ try self.out.init();
+ }
+
+ fn deinit(self: *Self) void {
+ self.in.deinit();
+ self.out.deinit();
+ self.* = undefined;
+ }
+
+ fn sender(self: *Self) void {
+ // update value and signal input
+ testing.expect(self.value == 0);
+ self.value = 1;
+ self.in.set();
+
+ // wait for receiver to update value and signal output
+ self.out.wait();
+ testing.expect(self.value == 2);
+
+ // update value and signal final input
+ self.value = 3;
+ self.in.set();
+ }
+
+ fn receiver(self: *Self) void {
+ // wait for sender to update value and signal input
+ self.in.wait();
+ assert(self.value == 1);
+
+ // update value and signal output
+ self.in.reset();
+ self.value = 2;
+ self.out.set();
+
+ // wait for sender to update value and signal final input
+ self.in.wait();
+ assert(self.value == 3);
+ }
+
+ fn sleeper(self: *Self) void {
+ self.in.set();
+ time.sleep(time.ns_per_ms * 2);
+ self.value = 5;
+ self.out.set();
+ }
+
+ fn timedWaiter(self: *Self) !void {
+ self.in.wait();
+ testing.expectEqual(TimedWaitResult.timed_out, self.out.timedWait(time.ns_per_us));
+ try self.out.timedWait(time.ns_per_ms * 100);
+ testing.expect(self.value == 5);
+ }
+ };
+
+ var context: Context = undefined;
+ try context.init();
+ defer context.deinit();
+ const receiver = try std.Thread.spawn(&context, Context.receiver);
+ defer receiver.wait();
+ context.sender();
+
+ if (false) {
+ // I have now observed this fail on macOS, Windows, and Linux.
+ // https://github.com/ziglang/zig/issues/7009
+ var timed = Context.init();
+ defer timed.deinit();
+ const sleeper = try std.Thread.spawn(&timed, Context.sleeper);
+ defer sleeper.wait();
+ try timed.timedWaiter();
+ }
+}
diff --git a/lib/std/Thread/RwLock.zig b/lib/std/Thread/RwLock.zig
new file mode 100644
index 0000000000..1d606a9cf1
--- /dev/null
+++ b/lib/std/Thread/RwLock.zig
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! A lock that supports one writer or many readers.
+//! This API is for kernel threads, not evented I/O.
+//! This API requires being initialized at runtime, and initialization
+//! can fail. Once initialized, the core operations cannot fail.
+
+impl: Impl,
+
+const RwLock = @This();
+const std = @import("../std.zig");
+const builtin = std.builtin;
+const assert = std.debug.assert;
+const Mutex = std.Thread.Mutex;
+const Semaphore = std.Semaphore;
+const CondVar = std.CondVar;
+
+pub const Impl = if (builtin.single_threaded)
+ SingleThreadedRwLock
+else if (std.Thread.use_pthreads)
+ PthreadRwLock
+else
+ DefaultRwLock;
+
+pub fn init(rwl: *RwLock) void {
+ return rwl.impl.init();
+}
+
+pub fn deinit(rwl: *RwLock) void {
+ return rwl.impl.deinit();
+}
+
+/// Attempts to obtain exclusive lock ownership.
+/// Returns `true` if the lock is obtained, `false` otherwise.
+pub fn tryLock(rwl: *RwLock) bool {
+ return rwl.impl.tryLock();
+}
+
+/// Blocks until exclusive lock ownership is acquired.
+pub fn lock(rwl: *RwLock) void {
+ return rwl.impl.lock();
+}
+
+/// Releases a held exclusive lock.
+/// Asserts the lock is held exclusively.
+pub fn unlock(rwl: *RwLock) void {
+ return rwl.impl.unlock();
+}
+
+/// Attempts to obtain shared lock ownership.
+/// Returns `true` if the lock is obtained, `false` otherwise.
+pub fn tryLockShared(rwl: *RwLock) bool {
+ return rwl.impl.tryLockShared();
+}
+
+/// Blocks until shared lock ownership is acquired.
+pub fn lockShared(rwl: *RwLock) void {
+ return rwl.impl.lockShared();
+}
+
+/// Releases a held shared lock.
+pub fn unlockShared(rwl: *RwLock) void {
+ return rwl.impl.unlockShared();
+}
+
+/// Single-threaded applications use this for deadlock checks in
+/// debug mode, and no-ops in release modes.
+pub const SingleThreadedRwLock = struct {
+ state: enum { unlocked, locked_exclusive, locked_shared },
+ shared_count: usize,
+
+ pub fn init(rwl: *SingleThreadedRwLock) void {
+ rwl.* = .{
+ .state = .unlocked,
+ .shared_count = 0,
+ };
+ }
+
+ pub fn deinit(rwl: *SingleThreadedRwLock) void {
+ assert(rwl.state == .unlocked);
+ assert(rwl.shared_count == 0);
+ }
+
+ /// Attempts to obtain exclusive lock ownership.
+ /// Returns `true` if the lock is obtained, `false` otherwise.
+ pub fn tryLock(rwl: *SingleThreadedRwLock) bool {
+ switch (rwl.state) {
+ .unlocked => {
+ assert(rwl.shared_count == 0);
+ rwl.state = .locked_exclusive;
+ return true;
+ },
+ .locked_exclusive, .locked_shared => return false,
+ }
+ }
+
+ /// Blocks until exclusive lock ownership is acquired.
+ pub fn lock(rwl: *SingleThreadedRwLock) void {
+ assert(rwl.state == .unlocked); // deadlock detected
+ assert(rwl.shared_count == 0); // corrupted state detected
+ rwl.state = .locked_exclusive;
+ }
+
+ /// Releases a held exclusive lock.
+ /// Asserts the lock is held exclusively.
+ pub fn unlock(rwl: *SingleThreadedRwLock) void {
+ assert(rwl.state == .locked_exclusive);
+ assert(rwl.shared_count == 0); // corrupted state detected
+ rwl.state = .unlocked;
+ }
+
+ /// Attempts to obtain shared lock ownership.
+ /// Returns `true` if the lock is obtained, `false` otherwise.
+ pub fn tryLockShared(rwl: *SingleThreadedRwLock) bool {
+ switch (rwl.state) {
+ .unlocked => {
+ rwl.state = .locked_shared;
+ assert(rwl.shared_count == 0);
+ rwl.shared_count = 1;
+ return true;
+ },
+ .locked_exclusive, .locked_shared => return false,
+ }
+ }
+
+ /// Blocks until shared lock ownership is acquired.
+ pub fn lockShared(rwl: *SingleThreadedRwLock) void {
+ switch (rwl.state) {
+ .unlocked => {
+ rwl.state = .locked_shared;
+ assert(rwl.shared_count == 0);
+ rwl.shared_count = 1;
+ },
+ .locked_shared => {
+ rwl.shared_count += 1;
+ },
+ .locked_exclusive => unreachable, // deadlock detected
+ }
+ }
+
+ /// Releases a held shared lock.
+ pub fn unlockShared(rwl: *SingleThreadedRwLock) void {
+ switch (rwl.state) {
+ .unlocked => unreachable, // too many calls to `unlockShared`
+ .locked_exclusive => unreachable, // exclusively held lock
+ .locked_shared => {
+ rwl.shared_count -= 1;
+ if (rwl.shared_count == 0) {
+ rwl.state = .unlocked;
+ }
+ },
+ }
+ }
+};
+
+pub const PthreadRwLock = struct {
+ rwlock: pthread_rwlock_t,
+
+ pub fn init(rwl: *PthreadRwLock) void {
+ rwl.* = .{ .rwlock = .{} };
+ }
+
+ pub fn deinit(rwl: *PthreadRwLock) void {
+ const safe_rc = switch (std.builtin.os.tag) {
+ .dragonfly, .netbsd => std.os.EAGAIN,
+ else => 0,
+ };
+
+ const rc = std.c.pthread_rwlock_destroy(&rwl.rwlock);
+ assert(rc == 0 or rc == safe_rc);
+
+ rwl.* = undefined;
+ }
+
+ pub fn tryLock(rwl: *PthreadRwLock) bool {
+ return pthread_rwlock_trywrlock(&rwl.rwlock) == 0;
+ }
+
+ pub fn lock(rwl: *PthreadRwLock) void {
+ const rc = pthread_rwlock_wrlock(&rwl.rwlock);
+ assert(rc == 0);
+ }
+
+ pub fn unlock(rwl: *PthreadRwLock) void {
+ const rc = pthread_rwlock_unlock(&rwl.rwlock);
+ assert(rc == 0);
+ }
+
+ pub fn tryLockShared(rwl: *PthreadRwLock) bool {
+ return pthread_rwlock_tryrdlock(&rwl.rwlock) == 0;
+ }
+
+ pub fn lockShared(rwl: *PthreadRwLock) void {
+ const rc = pthread_rwlock_rdlock(&rwl.rwlock);
+ assert(rc == 0);
+ }
+
+ pub fn unlockShared(rwl: *PthreadRwLock) void {
+ const rc = pthread_rwlock_unlock(&rwl.rwlock);
+ assert(rc == 0);
+ }
+};
+
+pub const DefaultRwLock = struct {
+ state: usize,
+ mutex: Mutex,
+ semaphore: Semaphore,
+
+ const IS_WRITING: usize = 1;
+ const WRITER: usize = 1 << 1;
+ const READER: usize = 1 << (1 + std.meta.bitCount(Count));
+ const WRITER_MASK: usize = std.math.maxInt(Count) << @ctz(usize, WRITER);
+ const READER_MASK: usize = std.math.maxInt(Count) << @ctz(usize, READER);
+ const Count = std.meta.Int(.unsigned, @divFloor(std.meta.bitCount(usize) - 1, 2));
+
+ pub fn init(rwl: *DefaultRwLock) void {
+ rwl.* = .{
+ .state = 0,
+ .mutex = Mutex.init(),
+ .semaphore = Semaphore.init(0),
+ };
+ }
+
+ pub fn deinit(rwl: *DefaultRwLock) void {
+ rwl.semaphore.deinit();
+ rwl.mutex.deinit();
+ rwl.* = undefined;
+ }
+
+ pub fn tryLock(rwl: *DefaultRwLock) bool {
+ if (rwl.mutex.tryLock()) {
+ const state = @atomicLoad(usize, &rwl.state, .SeqCst);
+ if (state & READER_MASK == 0) {
+ _ = @atomicRmw(usize, &rwl.state, .Or, IS_WRITING, .SeqCst);
+ return true;
+ }
+
+ rwl.mutex.unlock();
+ }
+
+ return false;
+ }
+
+ pub fn lock(rwl: *DefaultRwLock) void {
+ _ = @atomicRmw(usize, &rwl.state, .Add, WRITER, .SeqCst);
+ rwl.mutex.lock();
+
+ const state = @atomicRmw(usize, &rwl.state, .Or, IS_WRITING, .SeqCst);
+ if (state & READER_MASK != 0)
+ rwl.semaphore.wait();
+ }
+
+ pub fn unlock(rwl: *DefaultRwLock) void {
+ _ = @atomicRmw(usize, &rwl.state, .And, ~IS_WRITING, .SeqCst);
+ rwl.mutex.unlock();
+ }
+
+ pub fn tryLockShared(rwl: *DefaultRwLock) bool {
+ const state = @atomicLoad(usize, &rwl.state, .SeqCst);
+ if (state & (IS_WRITING | WRITER_MASK) == 0) {
+ _ = @cmpxchgStrong(
+ usize,
+ &rwl.state,
+ state,
+ state + READER,
+ .SeqCst,
+ .SeqCst,
+ ) orelse return true;
+ }
+
+ if (rwl.mutex.tryLock()) {
+ _ = @atomicRmw(usize, &rwl.state, .Add, READER, .SeqCst);
+ rwl.mutex.unlock();
+ return true;
+ }
+
+ return false;
+ }
+
+ pub fn lockShared(rwl: *DefaultRwLock) void {
+ var state = @atomicLoad(usize, &rwl.state, .SeqCst);
+ while (state & (IS_WRITING | WRITER_MASK) == 0) {
+ state = @cmpxchgWeak(
+ usize,
+ &rwl.state,
+ state,
+ state + READER,
+ .SeqCst,
+ .SeqCst,
+ ) orelse return;
+ }
+
+ rwl.mutex.lock();
+ _ = @atomicRmw(usize, &rwl.state, .Add, READER, .SeqCst);
+ rwl.mutex.unlock();
+ }
+
+ pub fn unlockShared(rwl: *DefaultRwLock) void {
+ const state = @atomicRmw(usize, &rwl.state, .Sub, READER, .SeqCst);
+
+ if ((state & READER_MASK == READER) and (state & IS_WRITING != 0))
+ rwl.semaphore.post();
+ }
+};
diff --git a/lib/std/Thread/Semaphore.zig b/lib/std/Thread/Semaphore.zig
new file mode 100644
index 0000000000..169975b362
--- /dev/null
+++ b/lib/std/Thread/Semaphore.zig
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! A semaphore is an unsigned integer that blocks the kernel thread if
+//! the number would become negative.
+//! This API supports static initialization and does not require deinitialization.
+
+mutex: Mutex = .{},
+cond: Condition = .{},
+/// It is OK to initialize this field to any value.
+permits: usize = 0,
+
+const Semaphore = @This();
+const std = @import("../std.zig");
+const Mutex = std.Thread.Mutex;
+const Condition = std.Thread.Condition;
+
+pub fn wait(sem: *Semaphore) void {
+ const held = sem.mutex.acquire();
+ defer held.release();
+
+ while (sem.permits == 0)
+ sem.cond.wait(&sem.mutex);
+
+ sem.permits -= 1;
+ if (sem.permits > 0)
+ sem.cond.signal();
+}
+
+pub fn post(sem: *Semaphore) void {
+ const held = sem.mutex.acquire();
+ defer held.release();
+
+ sem.permits += 1;
+ sem.cond.signal();
+}
diff --git a/lib/std/reset_event.zig b/lib/std/Thread/StaticResetEvent.zig
index 5da53985c6..6d90d7cf9a 100644
--- a/lib/std/reset_event.zig
+++ b/lib/std/Thread/StaticResetEvent.zig
@@ -1,245 +1,188 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
-const std = @import("std.zig");
-const builtin = @import("builtin");
-const testing = std.testing;
-const SpinLock = std.SpinLock;
+
+//! A thread-safe resource which supports blocking until signaled.
+//! This API is for kernel threads, not evented I/O.
+//! This API is statically initializable. It cannot fail to be initialized
+//! and it requires no deinitialization. The downside is that it may not
+//! integrate as cleanly into other synchronization APIs, or, in a worst case,
+//! may be forced to fall back on spin locking. As a rule of thumb, prefer
+//! to use `std.Thread.ResetEvent` when possible, and use `StaticResetEvent` when
+//! the logic needs stronger API guarantees.
+
+const std = @import("../std.zig");
+const StaticResetEvent = @This();
const assert = std.debug.assert;
-const c = std.c;
const os = std.os;
const time = std.time;
-const linux = os.linux;
-const windows = os.windows;
-
-/// A resource object which supports blocking until signaled.
-/// Once finished, the `deinit()` method should be called for correctness.
-pub const ResetEvent = struct {
- os_event: OsEvent,
-
- pub const OsEvent = if (builtin.single_threaded)
- DebugEvent
- else if (builtin.link_libc and builtin.os.tag != .windows and builtin.os.tag != .linux)
- PosixEvent
- else
- AtomicEvent;
-
- pub fn init() ResetEvent {
- return ResetEvent{ .os_event = OsEvent.init() };
- }
-
- pub fn deinit(self: *ResetEvent) void {
- self.os_event.deinit();
- }
-
- /// Returns whether or not the event is currenetly set
- pub fn isSet(self: *ResetEvent) bool {
- return self.os_event.isSet();
- }
-
- /// Sets the event if not already set and
- /// wakes up all the threads waiting on the event.
- pub fn set(self: *ResetEvent) void {
- return self.os_event.set();
- }
-
- /// Resets the event to its original, unset state.
- pub fn reset(self: *ResetEvent) void {
- return self.os_event.reset();
- }
-
- /// Wait for the event to be set by blocking the current thread.
- pub fn wait(self: *ResetEvent) void {
- return self.os_event.wait(null) catch unreachable;
- }
-
- /// Wait for the event to be set by blocking the current thread.
- /// A timeout in nanoseconds can be provided as a hint for how
- /// long the thread should block on the unset event before throwing error.TimedOut.
- pub fn timedWait(self: *ResetEvent, timeout_ns: u64) !void {
- return self.os_event.wait(timeout_ns);
- }
-};
+const linux = std.os.linux;
+const windows = std.os.windows;
+const testing = std.testing;
-const DebugEvent = struct {
- is_set: bool,
+impl: Impl = .{},
- fn init() DebugEvent {
- return DebugEvent{ .is_set = false };
- }
+pub const Impl = if (std.builtin.single_threaded)
+ DebugEvent
+else
+ AtomicEvent;
- fn deinit(self: *DebugEvent) void {
- self.* = undefined;
- }
+/// Sets the event if not already set and wakes up all the threads waiting on
+/// the event. It is safe to call `set` multiple times before calling `wait`.
+/// However it is illegal to call `set` after `wait` is called until the event
+/// is `reset`. This function is thread-safe.
+pub fn set(ev: *StaticResetEvent) void {
+ return ev.impl.set();
+}
- fn isSet(self: *DebugEvent) bool {
- return self.is_set;
- }
+/// Wait for the event to be set by blocking the current thread.
+/// Thread-safe. No spurious wakeups.
+/// Upon return from `wait`, the only function available to be called
+/// in `StaticResetEvent` is `reset`.
+pub fn wait(ev: *StaticResetEvent) void {
+ return ev.impl.wait();
+}
- fn reset(self: *DebugEvent) void {
- self.is_set = false;
- }
+/// Resets the event to its original, unset state.
+/// This function is *not* thread-safe. It is equivalent to calling
+/// `deinit` followed by `init` but without the possibility of failure.
+pub fn reset(ev: *StaticResetEvent) void {
+ return ev.impl.reset();
+}
- fn set(self: *DebugEvent) void {
- self.is_set = true;
- }
+pub const TimedWaitResult = std.Thread.ResetEvent.TimedWaitResult;
+
+/// Wait for the event to be set by blocking the current thread.
+/// A timeout in nanoseconds can be provided as a hint for how
+/// long the thread should block on the unset event before returning
+/// `TimedWaitResult.timed_out`.
+/// Thread-safe. No precision of timing is guaranteed.
+/// Upon return from `timedWait`, the only function available to be called
+/// in `StaticResetEvent` is `reset`.
+pub fn timedWait(ev: *StaticResetEvent, timeout_ns: u64) TimedWaitResult {
+ return ev.impl.timedWait(timeout_ns);
+}
- fn wait(self: *DebugEvent, timeout: ?u64) !void {
- if (self.is_set)
- return;
- if (timeout != null)
- return error.TimedOut;
- @panic("deadlock detected");
- }
-};
+/// For single-threaded builds, we use this to detect deadlocks.
+/// In unsafe modes this ends up being no-ops.
+pub const DebugEvent = struct {
+ state: State = State.unset,
-const PosixEvent = struct {
- is_set: bool,
- cond: c.pthread_cond_t,
- mutex: c.pthread_mutex_t,
-
- fn init() PosixEvent {
- return PosixEvent{
- .is_set = false,
- .cond = c.PTHREAD_COND_INITIALIZER,
- .mutex = c.PTHREAD_MUTEX_INITIALIZER,
- };
- }
+ const State = enum {
+ unset,
+ set,
+ waited,
+ };
- fn deinit(self: *PosixEvent) void {
- // on dragonfly or openbsd, *destroy() functions can return EINVAL
- // for statically initialized pthread structures
- const err = if (builtin.os.tag == .dragonfly or builtin.os.tag == .openbsd)
- os.EINVAL
- else
- 0;
-
- const retm = c.pthread_mutex_destroy(&self.mutex);
- assert(retm == 0 or retm == err);
- const retc = c.pthread_cond_destroy(&self.cond);
- assert(retc == 0 or retc == err);
+ /// This function is provided so that this type can be re-used inside
+ /// `std.Thread.ResetEvent`.
+ pub fn init(ev: *DebugEvent) void {
+ ev.* = .{};
}
- fn isSet(self: *PosixEvent) bool {
- assert(c.pthread_mutex_lock(&self.mutex) == 0);
- defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
-
- return self.is_set;
+ /// This function is provided so that this type can be re-used inside
+ /// `std.Thread.ResetEvent`.
+ pub fn deinit(ev: *DebugEvent) void {
+ ev.* = undefined;
}
- fn reset(self: *PosixEvent) void {
- assert(c.pthread_mutex_lock(&self.mutex) == 0);
- defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
-
- self.is_set = false;
+ pub fn set(ev: *DebugEvent) void {
+ switch (ev.state) {
+ .unset => ev.state = .set,
+ .set => {},
+ .waited => unreachable, // Not allowed to call `set` until `reset`.
+ }
}
- fn set(self: *PosixEvent) void {
- assert(c.pthread_mutex_lock(&self.mutex) == 0);
- defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
-
- if (!self.is_set) {
- self.is_set = true;
- assert(c.pthread_cond_broadcast(&self.cond) == 0);
+ pub fn wait(ev: *DebugEvent) void {
+ switch (ev.state) {
+ .unset => unreachable, // Deadlock detected.
+ .set => return,
+ .waited => unreachable, // Not allowed to call `wait` until `reset`.
}
}
- fn wait(self: *PosixEvent, timeout: ?u64) !void {
- assert(c.pthread_mutex_lock(&self.mutex) == 0);
- defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
-
- // quick guard before possibly calling time syscalls below
- if (self.is_set)
- return;
-
- var ts: os.timespec = undefined;
- if (timeout) |timeout_ns| {
- var timeout_abs = timeout_ns;
- if (comptime std.Target.current.isDarwin()) {
- var tv: os.darwin.timeval = undefined;
- assert(os.darwin.gettimeofday(&tv, null) == 0);
- timeout_abs += @intCast(u64, tv.tv_sec) * time.ns_per_s;
- timeout_abs += @intCast(u64, tv.tv_usec) * time.ns_per_us;
- } else {
- os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable;
- timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s;
- timeout_abs += @intCast(u64, ts.tv_nsec);
- }
- ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s));
- ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s));
+ pub fn timedWait(ev: *DebugEvent, timeout: u64) TimedWaitResult {
+ switch (ev.state) {
+ .unset => return .timed_out,
+ .set => return .event_set,
+ .waited => unreachable, // Not allowed to call `wait` until `reset`.
}
+ }
- while (!self.is_set) {
- const rc = switch (timeout == null) {
- true => c.pthread_cond_wait(&self.cond, &self.mutex),
- else => c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts),
- };
- switch (rc) {
- 0 => {},
- os.ETIMEDOUT => return error.TimedOut,
- os.EINVAL => unreachable,
- os.EPERM => unreachable,
- else => unreachable,
- }
- }
+ pub fn reset(ev: *DebugEvent) void {
+ ev.state = .unset;
}
};
-const AtomicEvent = struct {
- waiters: u32,
+pub const AtomicEvent = struct {
+ waiters: u32 = 0,
const WAKE = 1 << 0;
const WAIT = 1 << 1;
- fn init() AtomicEvent {
- return AtomicEvent{ .waiters = 0 };
+ /// This function is provided so that this type can be re-used inside
+ /// `std.Thread.ResetEvent`.
+ pub fn init(ev: *AtomicEvent) void {
+ ev.* = .{};
}
- fn deinit(self: *AtomicEvent) void {
- self.* = undefined;
+ /// This function is provided so that this type can be re-used inside
+ /// `std.Thread.ResetEvent`.
+ pub fn deinit(ev: *AtomicEvent) void {
+ ev.* = undefined;
}
- fn isSet(self: *const AtomicEvent) bool {
- return @atomicLoad(u32, &self.waiters, .Acquire) == WAKE;
- }
-
- fn reset(self: *AtomicEvent) void {
- @atomicStore(u32, &self.waiters, 0, .Monotonic);
+ pub fn set(ev: *AtomicEvent) void {
+ const waiters = @atomicRmw(u32, &ev.waiters, .Xchg, WAKE, .Release);
+ if (waiters >= WAIT) {
+ return Futex.wake(&ev.waiters, waiters >> 1);
+ }
}
- fn set(self: *AtomicEvent) void {
- const waiters = @atomicRmw(u32, &self.waiters, .Xchg, WAKE, .Release);
- if (waiters >= WAIT) {
- return Futex.wake(&self.waiters, waiters >> 1);
+ pub fn wait(ev: *AtomicEvent) void {
+ switch (ev.timedWait(null)) {
+ .timed_out => unreachable,
+ .event_set => return,
}
}
- fn wait(self: *AtomicEvent, timeout: ?u64) !void {
- var waiters = @atomicLoad(u32, &self.waiters, .Acquire);
+ pub fn timedWait(ev: *AtomicEvent, timeout: ?u64) TimedWaitResult {
+ var waiters = @atomicLoad(u32, &ev.waiters, .Acquire);
while (waiters != WAKE) {
- waiters = @cmpxchgWeak(u32, &self.waiters, waiters, waiters + WAIT, .Acquire, .Acquire) orelse return Futex.wait(&self.waiters, timeout);
+ waiters = @cmpxchgWeak(u32, &ev.waiters, waiters, waiters + WAIT, .Acquire, .Acquire) orelse {
+ if (Futex.wait(&ev.waiters, timeout)) |_| {
+ return .event_set;
+ } else |_| {
+ return .timed_out;
+ }
+ };
}
+ return .event_set;
}
- pub const Futex = switch (builtin.os.tag) {
+ pub fn reset(ev: *AtomicEvent) void {
+ @atomicStore(u32, &ev.waiters, 0, .Monotonic);
+ }
+
+ pub const Futex = switch (std.Target.current.os.tag) {
.windows => WindowsFutex,
.linux => LinuxFutex,
else => SpinFutex,
};
- const SpinFutex = struct {
+ pub const SpinFutex = struct {
fn wake(waiters: *u32, wake_count: u32) void {}
fn wait(waiters: *u32, timeout: ?u64) !void {
- // TODO: handle platforms where a monotonic timer isnt available
var timer: time.Timer = undefined;
if (timeout != null)
- timer = time.Timer.start() catch unreachable;
+ timer = time.Timer.start() catch return error.TimedOut;
while (@atomicLoad(u32, waiters, .Acquire) != WAKE) {
- SpinLock.yield();
+ std.os.sched_yield() catch std.Thread.spinLoopHint();
if (timeout) |timeout_ns| {
if (timer.read() >= timeout_ns)
return error.TimedOut;
@@ -248,7 +191,7 @@ const AtomicEvent = struct {
}
};
- const LinuxFutex = struct {
+ pub const LinuxFutex = struct {
fn wake(waiters: *u32, wake_count: u32) void {
const waiting = std.math.maxInt(i32); // wake_count
const ptr = @ptrCast(*const i32, waiters);
@@ -283,7 +226,7 @@ const AtomicEvent = struct {
}
};
- const WindowsFutex = struct {
+ pub const WindowsFutex = struct {
pub fn wake(waiters: *u32, wake_count: u32) void {
const handle = getEventHandle() orelse return SpinFutex.wake(waiters, wake_count);
const key = @ptrCast(*const c_void, waiters);
@@ -350,7 +293,7 @@ const AtomicEvent = struct {
return @intToPtr(?windows.HANDLE, handle);
},
LOADING => {
- SpinLock.yield();
+ std.os.sched_yield() catch std.Thread.spinLoopHint();
handle = @atomicLoad(usize, &event_handle, .Monotonic);
},
else => {
@@ -362,48 +305,33 @@ const AtomicEvent = struct {
};
};
-test "ResetEvent" {
- var event = ResetEvent.init();
- defer event.deinit();
+test "basic usage" {
+ var event = StaticResetEvent{};
// test event setting
- testing.expect(event.isSet() == false);
event.set();
- testing.expect(event.isSet() == true);
// test event resetting
event.reset();
- testing.expect(event.isSet() == false);
// test event waiting (non-blocking)
event.set();
event.wait();
- try event.timedWait(1);
+ event.reset();
+
+ event.set();
+ testing.expectEqual(TimedWaitResult.event_set, event.timedWait(1));
// test cross-thread signaling
- if (builtin.single_threaded)
+ if (std.builtin.single_threaded)
return;
const Context = struct {
const Self = @This();
- value: u128,
- in: ResetEvent,
- out: ResetEvent,
-
- fn init() Self {
- return Self{
- .value = 0,
- .in = ResetEvent.init(),
- .out = ResetEvent.init(),
- };
- }
-
- fn deinit(self: *Self) void {
- self.in.deinit();
- self.out.deinit();
- self.* = undefined;
- }
+ value: u128 = 0,
+ in: StaticResetEvent = .{},
+ out: StaticResetEvent = .{},
fn sender(self: *Self) void {
// update value and signal input
@@ -444,14 +372,13 @@ test "ResetEvent" {
fn timedWaiter(self: *Self) !void {
self.in.wait();
- testing.expectError(error.TimedOut, self.out.timedWait(time.ns_per_us));
+ testing.expectEqual(TimedWaitResult.timed_out, self.out.timedWait(time.ns_per_us));
try self.out.timedWait(time.ns_per_ms * 100);
testing.expect(self.value == 5);
}
};
- var context = Context.init();
- defer context.deinit();
+ var context = Context{};
const receiver = try std.Thread.spawn(&context, Context.receiver);
defer receiver.wait();
context.sender();
diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig
index e5ad26cb45..7b0d9ea4dd 100644
--- a/lib/std/array_hash_map.zig
+++ b/lib/std/array_hash_map.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -99,6 +99,16 @@ pub fn ArrayHashMap(
};
}
+ /// `ArrayHashMap` takes ownership of the passed in array list. The array list must have
+ /// been allocated with `allocator`.
+ /// Deinitialize with `deinit`.
+ pub fn fromOwnedArrayList(allocator: *Allocator, entries: std.ArrayListUnmanaged(Entry)) !Self {
+ return Self{
+ .unmanaged = try Unmanaged.fromOwnedArrayList(allocator, entries),
+ .allocator = allocator,
+ };
+ }
+
pub fn deinit(self: *Self) void {
self.unmanaged.deinit(self.allocator);
self.* = undefined;
@@ -214,17 +224,38 @@ pub fn ArrayHashMap(
}
/// If there is an `Entry` with a matching key, it is deleted from
- /// the hash map, and then returned from this function.
- pub fn remove(self: *Self, key: K) ?Entry {
- return self.unmanaged.remove(key);
+ /// the hash map, and then returned from this function. The entry is
+ /// removed from the underlying array by swapping it with the last
+ /// element.
+ pub fn swapRemove(self: *Self, key: K) ?Entry {
+ return self.unmanaged.swapRemove(key);
}
- /// Asserts there is an `Entry` with matching key, deletes it from the hash map,
- /// and discards it.
+ /// If there is an `Entry` with a matching key, it is deleted from
+ /// the hash map, and then returned from this function. The entry is
+ /// removed from the underlying array by shifting all elements forward
+ /// thereby maintaining the current ordering.
+ pub fn orderedRemove(self: *Self, key: K) ?Entry {
+ return self.unmanaged.orderedRemove(key);
+ }
+
+ /// TODO: deprecated: call swapRemoveAssertDiscard instead.
pub fn removeAssertDiscard(self: *Self, key: K) void {
return self.unmanaged.removeAssertDiscard(key);
}
+ /// Asserts there is an `Entry` with matching key, deletes it from the hash map
+ /// by swapping it with the last element, and discards it.
+ pub fn swapRemoveAssertDiscard(self: *Self, key: K) void {
+ return self.unmanaged.swapRemoveAssertDiscard(key);
+ }
+
+ /// Asserts there is an `Entry` with matching key, deletes it from the hash map
+ /// by by shifting all elements forward thereby maintaining the current ordering.
+ pub fn orderedRemoveAssertDiscard(self: *Self, key: K) void {
+ return self.unmanaged.orderedRemoveAssertDiscard(key);
+ }
+
pub fn items(self: Self) []Entry {
return self.unmanaged.items();
}
@@ -233,6 +264,29 @@ pub fn ArrayHashMap(
var other = try self.unmanaged.clone(self.allocator);
return other.promote(self.allocator);
}
+
+ /// Rebuilds the key indexes. If the underlying entries has been modified directly, users
+ /// can call `reIndex` to update the indexes to account for these new entries.
+ pub fn reIndex(self: *Self) !void {
+ return self.unmanaged.reIndex(self.allocator);
+ }
+
+ /// Shrinks the underlying `Entry` array to `new_len` elements and discards any associated
+ /// index entries. Keeps capacity the same.
+ pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
+ return self.unmanaged.shrinkRetainingCapacity(new_len);
+ }
+
+ /// Shrinks the underlying `Entry` array to `new_len` elements and discards any associated
+ /// index entries. Reduces allocated capacity.
+ pub fn shrinkAndFree(self: *Self, new_len: usize) void {
+ return self.unmanaged.shrinkAndFree(self.allocator, new_len);
+ }
+
+ /// Removes the last inserted `Entry` in the hash map and returns it.
+ pub fn pop(self: *Self) Entry {
+ return self.unmanaged.pop();
+ }
};
}
@@ -286,6 +340,7 @@ pub fn ArrayHashMapUnmanaged(
pub const GetOrPutResult = struct {
entry: *Entry,
found_existing: bool,
+ index: usize,
};
pub const Managed = ArrayHashMap(K, V, hash, eql, store_hash);
@@ -294,6 +349,12 @@ pub fn ArrayHashMapUnmanaged(
const linear_scan_max = 8;
+ const RemovalType = enum {
+ swap,
+ ordered,
+ index_only,
+ };
+
pub fn promote(self: Self, allocator: *Allocator) Managed {
return .{
.unmanaged = self,
@@ -301,6 +362,15 @@ pub fn ArrayHashMapUnmanaged(
};
}
+ /// `ArrayHashMapUnmanaged` takes ownership of the passed in array list. The array list must
+ /// have been allocated with `allocator`.
+ /// Deinitialize with `deinit`.
+ pub fn fromOwnedArrayList(allocator: *Allocator, entries: std.ArrayListUnmanaged(Entry)) !Self {
+ var array_hash_map = Self{ .entries = entries };
+ try array_hash_map.reIndex(allocator);
+ return array_hash_map;
+ }
+
pub fn deinit(self: *Self, allocator: *Allocator) void {
self.entries.deinit(allocator);
if (self.index_header) |header| {
@@ -323,7 +393,7 @@ pub fn ArrayHashMapUnmanaged(
}
pub fn clearAndFree(self: *Self, allocator: *Allocator) void {
- self.entries.shrink(allocator, 0);
+ self.entries.shrinkAndFree(allocator, 0);
if (self.index_header) |header| {
header.free(allocator);
self.index_header = null;
@@ -343,9 +413,11 @@ pub fn ArrayHashMapUnmanaged(
pub fn getOrPut(self: *Self, allocator: *Allocator, key: K) !GetOrPutResult {
self.ensureCapacity(allocator, self.entries.items.len + 1) catch |err| {
// "If key exists this function cannot fail."
+ const index = self.getIndex(key) orelse return err;
return GetOrPutResult{
- .entry = self.getEntry(key) orelse return err,
+ .entry = &self.entries.items[index],
.found_existing = true,
+ .index = index,
};
};
return self.getOrPutAssumeCapacity(key);
@@ -362,11 +434,12 @@ pub fn ArrayHashMapUnmanaged(
const header = self.index_header orelse {
// Linear scan.
const h = if (store_hash) hash(key) else {};
- for (self.entries.items) |*item| {
+ for (self.entries.items) |*item, i| {
if (item.hash == h and eql(key, item.key)) {
return GetOrPutResult{
.entry = item,
.found_existing = true,
+ .index = i,
};
}
}
@@ -379,6 +452,7 @@ pub fn ArrayHashMapUnmanaged(
return GetOrPutResult{
.entry = new_entry,
.found_existing = false,
+ .index = self.entries.items.len - 1,
};
};
@@ -524,30 +598,36 @@ pub fn ArrayHashMapUnmanaged(
}
/// If there is an `Entry` with a matching key, it is deleted from
- /// the hash map, and then returned from this function.
- pub fn remove(self: *Self, key: K) ?Entry {
- const header = self.index_header orelse {
- // Linear scan.
- const h = if (store_hash) hash(key) else {};
- for (self.entries.items) |item, i| {
- if (item.hash == h and eql(key, item.key)) {
- return self.entries.swapRemove(i);
- }
- }
- return null;
- };
- switch (header.capacityIndexType()) {
- .u8 => return self.removeInternal(key, header, u8),
- .u16 => return self.removeInternal(key, header, u16),
- .u32 => return self.removeInternal(key, header, u32),
- .usize => return self.removeInternal(key, header, usize),
- }
+ /// the hash map, and then returned from this function. The entry is
+ /// removed from the underlying array by swapping it with the last
+ /// element.
+ pub fn swapRemove(self: *Self, key: K) ?Entry {
+ return self.removeInternal(key, .swap);
}
- /// Asserts there is an `Entry` with matching key, deletes it from the hash map,
- /// and discards it.
+ /// If there is an `Entry` with a matching key, it is deleted from
+ /// the hash map, and then returned from this function. The entry is
+ /// removed from the underlying array by shifting all elements forward
+ /// thereby maintaining the current ordering.
+ pub fn orderedRemove(self: *Self, key: K) ?Entry {
+ return self.removeInternal(key, .ordered);
+ }
+
+ /// TODO deprecated: call swapRemoveAssertDiscard instead.
pub fn removeAssertDiscard(self: *Self, key: K) void {
- assert(self.remove(key) != null);
+ return self.swapRemoveAssertDiscard(key);
+ }
+
+ /// Asserts there is an `Entry` with matching key, deletes it from the hash map
+ /// by swapping it with the last element, and discards it.
+ pub fn swapRemoveAssertDiscard(self: *Self, key: K) void {
+ assert(self.swapRemove(key) != null);
+ }
+
+ /// Asserts there is an `Entry` with matching key, deletes it from the hash map
+ /// by by shifting all elements forward thereby maintaining the current ordering.
+ pub fn orderedRemoveAssertDiscard(self: *Self, key: K) void {
+ assert(self.orderedRemove(key) != null);
}
pub fn items(self: Self) []Entry {
@@ -566,9 +646,85 @@ pub fn ArrayHashMapUnmanaged(
return other;
}
- fn removeInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) ?Entry {
+ /// Rebuilds the key indexes. If the underlying entries has been modified directly, users
+ /// can call `reIndex` to update the indexes to account for these new entries.
+ pub fn reIndex(self: *Self, allocator: *Allocator) !void {
+ if (self.entries.capacity <= linear_scan_max) return;
+ // We're going to rebuild the index header and replace the existing one (if any). The
+ // indexes should sized such that they will be at most 60% full.
+ const needed_len = self.entries.capacity * 5 / 3;
+ const new_indexes_len = math.ceilPowerOfTwo(usize, needed_len) catch unreachable;
+ const new_header = try IndexHeader.alloc(allocator, new_indexes_len);
+ self.insertAllEntriesIntoNewHeader(new_header);
+ if (self.index_header) |header|
+ header.free(allocator);
+ self.index_header = new_header;
+ }
+
+ /// Shrinks the underlying `Entry` array to `new_len` elements and discards any associated
+ /// index entries. Keeps capacity the same.
+ pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
+ // Remove index entries from the new length onwards.
+ // Explicitly choose to ONLY remove index entries and not the underlying array list
+ // entries as we're going to remove them in the subsequent shrink call.
+ var i: usize = new_len;
+ while (i < self.entries.items.len) : (i += 1)
+ _ = self.removeWithHash(self.entries.items[i].key, self.entries.items[i].hash, .index_only);
+ self.entries.shrinkRetainingCapacity(new_len);
+ }
+
+ /// Shrinks the underlying `Entry` array to `new_len` elements and discards any associated
+ /// index entries. Reduces allocated capacity.
+ pub fn shrinkAndFree(self: *Self, allocator: *Allocator, new_len: usize) void {
+ // Remove index entries from the new length onwards.
+ // Explicitly choose to ONLY remove index entries and not the underlying array list
+ // entries as we're going to remove them in the subsequent shrink call.
+ var i: usize = new_len;
+ while (i < self.entries.items.len) : (i += 1)
+ _ = self.removeWithHash(self.entries.items[i].key, self.entries.items[i].hash, .index_only);
+ self.entries.shrinkAndFree(allocator, new_len);
+ }
+
+ /// Removes the last inserted `Entry` in the hash map and returns it.
+ pub fn pop(self: *Self) Entry {
+ const top = self.entries.pop();
+ _ = self.removeWithHash(top.key, top.hash, .index_only);
+ return top;
+ }
+
+ fn removeInternal(self: *Self, key: K, comptime removal_type: RemovalType) ?Entry {
+ const key_hash = if (store_hash) hash(key) else {};
+ return self.removeWithHash(key, key_hash, removal_type);
+ }
+
+ fn removeWithHash(self: *Self, key: K, key_hash: Hash, comptime removal_type: RemovalType) ?Entry {
+ const header = self.index_header orelse {
+ // If we're only removing index entries and we have no index header, there's no need
+ // to continue.
+ if (removal_type == .index_only) return null;
+ // Linear scan.
+ for (self.entries.items) |item, i| {
+ if (item.hash == key_hash and eql(key, item.key)) {
+ switch (removal_type) {
+ .swap => return self.entries.swapRemove(i),
+ .ordered => return self.entries.orderedRemove(i),
+ .index_only => unreachable,
+ }
+ }
+ }
+ return null;
+ };
+ switch (header.capacityIndexType()) {
+ .u8 => return self.removeWithIndex(key, key_hash, header, u8, removal_type),
+ .u16 => return self.removeWithIndex(key, key_hash, header, u16, removal_type),
+ .u32 => return self.removeWithIndex(key, key_hash, header, u32, removal_type),
+ .usize => return self.removeWithIndex(key, key_hash, header, usize, removal_type),
+ }
+ }
+
+ fn removeWithIndex(self: *Self, key: K, key_hash: Hash, header: *IndexHeader, comptime I: type, comptime removal_type: RemovalType) ?Entry {
const indexes = header.indexes(I);
- const h = hash(key);
+ const h = if (store_hash) key_hash else hash(key);
const start_index = header.constrainIndex(h);
var roll_over: usize = 0;
while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) {
@@ -583,11 +739,26 @@ pub fn ArrayHashMapUnmanaged(
if (!hash_match or !eql(key, entry.key))
continue;
- const removed_entry = self.entries.swapRemove(index.entry_index);
- if (self.entries.items.len > 0 and self.entries.items.len != index.entry_index) {
- // Because of the swap remove, now we need to update the index that was
- // pointing to the last entry and is now pointing to this removed item slot.
- self.updateEntryIndex(header, self.entries.items.len, index.entry_index, I, indexes);
+ var removed_entry: ?Entry = undefined;
+ switch (removal_type) {
+ .swap => {
+ removed_entry = self.entries.swapRemove(index.entry_index);
+ if (self.entries.items.len > 0 and self.entries.items.len != index.entry_index) {
+ // Because of the swap remove, now we need to update the index that was
+ // pointing to the last entry and is now pointing to this removed item slot.
+ self.updateEntryIndex(header, self.entries.items.len, index.entry_index, I, indexes);
+ }
+ },
+ .ordered => {
+ removed_entry = self.entries.orderedRemove(index.entry_index);
+ var i: usize = index.entry_index;
+ while (i < self.entries.items.len) : (i += 1) {
+ // Because of the ordered remove, everything from the entry index onwards has
+ // been shifted forward so we'll need to update the index entries.
+ self.updateEntryIndex(header, i + 1, i, I, indexes);
+ }
+ },
+ .index_only => removed_entry = null,
}
// Now we have to shift over the following indexes.
@@ -658,6 +829,7 @@ pub fn ArrayHashMapUnmanaged(
return .{
.found_existing = false,
.entry = new_entry,
+ .index = self.entries.items.len - 1,
};
}
@@ -669,6 +841,7 @@ pub fn ArrayHashMapUnmanaged(
return .{
.found_existing = true,
.entry = entry,
+ .index = index.entry_index,
};
}
if (index.distance_from_start_index < distance_from_start_index) {
@@ -710,6 +883,7 @@ pub fn ArrayHashMapUnmanaged(
return .{
.found_existing = false,
.entry = new_entry,
+ .index = self.entries.items.len - 1,
};
}
if (next_index.distance_from_start_index < distance_from_start_index) {
@@ -901,11 +1075,13 @@ test "basic hash map usage" {
const gop1 = try map.getOrPut(5);
testing.expect(gop1.found_existing == true);
testing.expect(gop1.entry.value == 55);
+ testing.expect(gop1.index == 4);
gop1.entry.value = 77;
testing.expect(map.getEntry(5).?.value == 77);
const gop2 = try map.getOrPut(99);
testing.expect(gop2.found_existing == false);
+ testing.expect(gop2.index == 5);
gop2.entry.value = 42;
testing.expect(map.getEntry(99).?.value == 42);
@@ -919,13 +1095,32 @@ test "basic hash map usage" {
testing.expect(map.getEntry(2).?.value == 22);
testing.expect(map.get(2).? == 22);
- const rmv1 = map.remove(2);
+ const rmv1 = map.swapRemove(2);
testing.expect(rmv1.?.key == 2);
testing.expect(rmv1.?.value == 22);
- testing.expect(map.remove(2) == null);
+ testing.expect(map.swapRemove(2) == null);
testing.expect(map.getEntry(2) == null);
testing.expect(map.get(2) == null);
+ // Since we've used `swapRemove` above, the index of this entry should remain unchanged.
+ testing.expect(map.getIndex(100).? == 1);
+ const gop5 = try map.getOrPut(5);
+ testing.expect(gop5.found_existing == true);
+ testing.expect(gop5.entry.value == 77);
+ testing.expect(gop5.index == 4);
+
+ // Whereas, if we do an `orderedRemove`, it should move the index forward one spot.
+ const rmv2 = map.orderedRemove(100);
+ testing.expect(rmv2.?.key == 100);
+ testing.expect(rmv2.?.value == 41);
+ testing.expect(map.orderedRemove(100) == null);
+ testing.expect(map.getEntry(100) == null);
+ testing.expect(map.get(100) == null);
+ const gop6 = try map.getOrPut(5);
+ testing.expect(gop6.found_existing == true);
+ testing.expect(gop6.entry.value == 77);
+ testing.expect(gop6.index == 3);
+
map.removeAssertDiscard(3);
}
@@ -1019,6 +1214,130 @@ test "clone" {
}
}
+test "shrink" {
+ var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
+ defer map.deinit();
+
+ // This test is more interesting if we insert enough entries to allocate the index header.
+ const num_entries = 20;
+ var i: i32 = 0;
+ while (i < num_entries) : (i += 1)
+ testing.expect((try map.fetchPut(i, i * 10)) == null);
+
+ testing.expect(map.unmanaged.index_header != null);
+ testing.expect(map.count() == num_entries);
+
+ // Test `shrinkRetainingCapacity`.
+ map.shrinkRetainingCapacity(17);
+ testing.expect(map.count() == 17);
+ testing.expect(map.capacity() == 20);
+ i = 0;
+ while (i < num_entries) : (i += 1) {
+ const gop = try map.getOrPut(i);
+ if (i < 17) {
+ testing.expect(gop.found_existing == true);
+ testing.expect(gop.entry.value == i * 10);
+ } else testing.expect(gop.found_existing == false);
+ }
+
+ // Test `shrinkAndFree`.
+ map.shrinkAndFree(15);
+ testing.expect(map.count() == 15);
+ testing.expect(map.capacity() == 15);
+ i = 0;
+ while (i < num_entries) : (i += 1) {
+ const gop = try map.getOrPut(i);
+ if (i < 15) {
+ testing.expect(gop.found_existing == true);
+ testing.expect(gop.entry.value == i * 10);
+ } else testing.expect(gop.found_existing == false);
+ }
+}
+
+test "pop" {
+ var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
+ defer map.deinit();
+
+ testing.expect((try map.fetchPut(1, 11)) == null);
+ testing.expect((try map.fetchPut(2, 22)) == null);
+ testing.expect((try map.fetchPut(3, 33)) == null);
+ testing.expect((try map.fetchPut(4, 44)) == null);
+
+ const pop1 = map.pop();
+ testing.expect(pop1.key == 4 and pop1.value == 44);
+ const pop2 = map.pop();
+ testing.expect(pop2.key == 3 and pop2.value == 33);
+ const pop3 = map.pop();
+ testing.expect(pop3.key == 2 and pop3.value == 22);
+ const pop4 = map.pop();
+ testing.expect(pop4.key == 1 and pop4.value == 11);
+}
+
+test "reIndex" {
+ var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
+ defer map.deinit();
+
+ // Populate via the API.
+ const num_indexed_entries = 20;
+ var i: i32 = 0;
+ while (i < num_indexed_entries) : (i += 1)
+ testing.expect((try map.fetchPut(i, i * 10)) == null);
+
+ // Make sure we allocated an index header.
+ testing.expect(map.unmanaged.index_header != null);
+
+ // Now write to the underlying array list directly.
+ const num_unindexed_entries = 20;
+ const hash = getAutoHashFn(i32);
+ var al = &map.unmanaged.entries;
+ while (i < num_indexed_entries + num_unindexed_entries) : (i += 1) {
+ try al.append(std.testing.allocator, .{
+ .key = i,
+ .value = i * 10,
+ .hash = hash(i),
+ });
+ }
+
+ // After reindexing, we should see everything.
+ try map.reIndex();
+ i = 0;
+ while (i < num_indexed_entries + num_unindexed_entries) : (i += 1) {
+ const gop = try map.getOrPut(i);
+ testing.expect(gop.found_existing == true);
+ testing.expect(gop.entry.value == i * 10);
+ testing.expect(gop.index == i);
+ }
+}
+
+test "fromOwnedArrayList" {
+ comptime const array_hash_map_type = AutoArrayHashMap(i32, i32);
+ var al = std.ArrayListUnmanaged(array_hash_map_type.Entry){};
+ const hash = getAutoHashFn(i32);
+
+ // Populate array list.
+ const num_entries = 20;
+ var i: i32 = 0;
+ while (i < num_entries) : (i += 1) {
+ try al.append(std.testing.allocator, .{
+ .key = i,
+ .value = i * 10,
+ .hash = hash(i),
+ });
+ }
+
+ // Now instantiate using `fromOwnedArrayList`.
+ var map = try array_hash_map_type.fromOwnedArrayList(std.testing.allocator, al);
+ defer map.deinit();
+
+ i = 0;
+ while (i < num_entries) : (i += 1) {
+ const gop = try map.getOrPut(i);
+ testing.expect(gop.found_existing == true);
+ testing.expect(gop.entry.value == i * 10);
+ testing.expect(gop.index == i);
+ }
+}
+
pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) {
return struct {
fn hash(key: K) u32 {
diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig
index ab47510cb7..f30a86d8f7 100644
--- a/lib/std/array_list.zig
+++ b/lib/std/array_list.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -12,10 +12,20 @@ const Allocator = mem.Allocator;
/// A contiguous, growable list of items in memory.
/// This is a wrapper around an array of T values. Initialize with `init`.
+///
+/// This struct internally stores a `std.mem.Allocator` for memory management.
+/// To manually specify an allocator with each method call see `ArrayListUnmanaged`.
pub fn ArrayList(comptime T: type) type {
return ArrayListAligned(T, null);
}
+/// A contiguous, growable list of arbitrarily aligned items in memory.
+/// This is a wrapper around an array of T values aligned to `alignment`-byte
+/// addresses. If the specified alignment is `null`, then `@alignOf(T)` is used.
+/// Initialize with `init`.
+///
+/// This struct internally stores a `std.mem.Allocator` for memory management.
+/// To manually specify an allocator with each method call see `ArrayListAlignedUnmanaged`.
pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
if (alignment) |a| {
if (a == @alignOf(T)) {
@@ -24,9 +34,18 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
}
return struct {
const Self = @This();
-
- /// Content of the ArrayList
+ /// Contents of the list. Pointers to elements in this slice are
+ /// **invalid after resizing operations** on the ArrayList, unless the
+ /// operation explicitly either: (1) states otherwise or (2) lists the
+ /// invalidated pointers.
+ ///
+ /// The allocator used determines how element pointers are
+ /// invalidated, so the behavior may vary between lists. To avoid
+ /// illegal behavior, take into account the above paragraph plus the
+ /// explicit statements given in each method.
items: Slice,
+ /// How many T values this list can hold without allocating
+ /// additional memory.
capacity: usize,
allocator: *Allocator,
@@ -42,7 +61,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
};
}
- /// Initialize with capacity to hold at least num elements.
+ /// Initialize with capacity to hold at least `num` elements.
/// Deinitialize with `deinit` or use `toOwnedSlice`.
pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
var self = Self.init(allocator);
@@ -79,11 +98,23 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
};
}
+ /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields
+ /// of this ArrayList. This ArrayList retains ownership of underlying memory.
+ /// Deprecated: use `moveToUnmanaged` which has different semantics.
pub fn toUnmanaged(self: Self) ArrayListAlignedUnmanaged(T, alignment) {
return .{ .items = self.items, .capacity = self.capacity };
}
- /// The caller owns the returned memory. ArrayList becomes empty.
+ /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields
+ /// of this ArrayList. Empties this ArrayList.
+ pub fn moveToUnmanaged(self: *Self) ArrayListAlignedUnmanaged(T, alignment) {
+ const allocator = self.allocator;
+ const result = .{ .items = self.items, .capacity = self.capacity };
+ self.* = init(allocator);
+ return result;
+ }
+
+ /// The caller owns the returned memory. Empties this ArrayList.
pub fn toOwnedSlice(self: *Self) Slice {
const allocator = self.allocator;
const result = allocator.shrink(self.allocatedSlice(), self.items.len);
@@ -91,6 +122,13 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
return result;
}
+ /// The caller owns the returned memory. Empties this ArrayList.
+ pub fn toOwnedSliceSentinel(self: *Self, comptime sentinel: T) ![:sentinel]T {
+ try self.append(sentinel);
+ const result = self.toOwnedSlice();
+ return result[0 .. result.len - 1 :sentinel];
+ }
+
/// Insert `item` at index `n` by moving `list[n .. list.len]` to make room.
/// This operation is O(N).
pub fn insert(self: *Self, n: usize, item: T) !void {
@@ -111,9 +149,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
mem.copy(T, self.items[i .. i + items.len], items);
}
- /// Replace range of elements `list[start..start+len]` with `new_items`
- /// grows list if `len < new_items.len`. may allocate
- /// shrinks list if `len > new_items.len`
+ /// Replace range of elements `list[start..start+len]` with `new_items`.
+ /// Grows list if `len < new_items.len`.
+ /// Shrinks list if `len > new_items.len`.
+ /// Invalidates pointers if this ArrayList is resized.
pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: SliceConst) !void {
const after_range = start + len;
const range = self.items[start..after_range];
@@ -144,15 +183,18 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
new_item_ptr.* = item;
}
- /// Extend the list by 1 element, but asserting `self.capacity`
- /// is sufficient to hold an additional item.
+ /// Extend the list by 1 element, but assert `self.capacity`
+ /// is sufficient to hold an additional item. **Does not**
+ /// invalidate pointers.
pub fn appendAssumeCapacity(self: *Self, item: T) void {
const new_item_ptr = self.addOneAssumeCapacity();
new_item_ptr.* = item;
}
- /// Remove the element at index `i` from the list and return its value.
+ /// Remove the element at index `i`, shift elements after index
+ /// `i` forward, and return the removed element.
/// Asserts the array has at least one item.
+ /// Invalidates pointers to end of list.
/// This operation is O(N).
pub fn orderedRemove(self: *Self, i: usize) T {
const newlen = self.items.len - 1;
@@ -184,7 +226,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
}
/// Append the slice of items to the list, asserting the capacity is already
- /// enough to store the new items.
+ /// enough to store the new items. **Does not** invalidate pointers.
pub fn appendSliceAssumeCapacity(self: *Self, items: SliceConst) void {
const oldlen = self.items.len;
const newlen = self.items.len + items.len;
@@ -200,9 +242,6 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
return .{ .context = self };
}
- /// Deprecated: use `writer`
- pub const outStream = writer;
-
/// Same as `append` except it returns the number of bytes written, which is always the same
/// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API.
fn appendWrite(self: *Self, m: []const u8) !usize {
@@ -220,7 +259,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
}
/// Append a value to the list `n` times.
- /// Asserts the capacity is enough.
+ /// Asserts the capacity is enough. **Does not** invalidate pointers.
pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
const new_len = self.items.len + n;
assert(new_len <= self.capacity);
@@ -236,8 +275,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
}
/// Reduce allocated capacity to `new_len`.
- /// Invalidates element pointers.
- pub fn shrink(self: *Self, new_len: usize) void {
+ /// May invalidate element pointers.
+ pub fn shrinkAndFree(self: *Self, new_len: usize) void {
assert(new_len <= self.items.len);
self.items = self.allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) {
@@ -250,13 +289,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
}
/// Reduce length to `new_len`.
- /// Invalidates element pointers.
- /// Keeps capacity the same.
+ /// Invalidates pointers for the elements `items[new_len..]`.
pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
assert(new_len <= self.items.len);
self.items.len = new_len;
}
+ /// Modify the array so that it can hold at least `new_capacity` items.
+ /// Invalidates pointers if additional memory is needed.
pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
var better_capacity = self.capacity;
if (better_capacity >= new_capacity) return;
@@ -273,14 +313,13 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
}
/// Increases the array's length to match the full capacity that is already allocated.
- /// The new elements have `undefined` values. This operation does not invalidate any
- /// element pointers.
+ /// The new elements have `undefined` values. **Does not** invalidate pointers.
pub fn expandToCapacity(self: *Self) void {
self.items.len = self.capacity;
}
/// Increase length by 1, returning pointer to the new item.
- /// The returned pointer becomes invalid when the list is resized.
+ /// The returned pointer becomes invalid when the list resized.
pub fn addOne(self: *Self) !*T {
const newlen = self.items.len + 1;
try self.ensureCapacity(newlen);
@@ -290,6 +329,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
/// Increase length by 1, returning pointer to the new item.
/// Asserts that there is already space for the new item without allocating more.
/// The returned pointer becomes invalid when the list is resized.
+ /// **Does not** invalidate element pointers.
pub fn addOneAssumeCapacity(self: *Self) *T {
assert(self.items.len < self.capacity);
@@ -299,6 +339,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
/// Resize the array, adding `n` new elements, which have `undefined` values.
/// The return value is an array pointing to the newly allocated elements.
+ /// The returned pointer becomes invalid when the list is resized.
+ /// Resizes list if `self.capacity` is not large enough.
pub fn addManyAsArray(self: *Self, comptime n: usize) !*[n]T {
const prev_len = self.items.len;
try self.resize(self.items.len + n);
@@ -308,6 +350,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
/// Resize the array, adding `n` new elements, which have `undefined` values.
/// The return value is an array pointing to the newly allocated elements.
/// Asserts that there is already space for the new item without allocating more.
+ /// **Does not** invalidate element pointers.
+ /// The returned pointer becomes invalid when the list is resized.
pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
assert(self.items.len + n <= self.capacity);
const prev_len = self.items.len;
@@ -317,33 +361,51 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
/// Remove and return the last element from the list.
/// Asserts the list has at least one item.
+ /// Invalidates pointers to the removed element.
pub fn pop(self: *Self) T {
const val = self.items[self.items.len - 1];
self.items.len -= 1;
return val;
}
- /// Remove and return the last element from the list.
- /// If the list is empty, returns `null`.
+ /// Remove and return the last element from the list, or
+ /// return `null` if list is empty.
+ /// Invalidates pointers to the removed element, if any.
pub fn popOrNull(self: *Self) ?T {
if (self.items.len == 0) return null;
return self.pop();
}
- // For a nicer API, `items.len` is the length, not the capacity.
- // This requires "unsafe" slicing.
- fn allocatedSlice(self: Self) Slice {
+ /// Returns a slice of all the items plus the extra capacity, whose memory
+ /// contents are `undefined`.
+ pub fn allocatedSlice(self: Self) Slice {
+ // For a nicer API, `items.len` is the length, not the capacity.
+ // This requires "unsafe" slicing.
return self.items.ptr[0..self.capacity];
}
+
+ /// Returns a slice of only the extra capacity after items.
+ /// This can be useful for writing directly into an ArrayList.
+ /// Note that such an operation must be followed up with a direct
+ /// modification of `self.items.len`.
+ pub fn unusedCapacitySlice(self: Self) Slice {
+ return self.allocatedSlice()[self.items.len..];
+ }
};
}
-/// Bring-your-own allocator with every function call.
-/// Initialize directly and deinitialize with `deinit` or use `toOwnedSlice`.
+/// An ArrayList, but the allocator is passed as a parameter to the relevant functions
+/// rather than stored in the struct itself. The same allocator **must** be used throughout
+/// the entire lifetime of an ArrayListUnmanaged. Initialize directly or with
+/// `initCapacity`, and deinitialize with `deinit` or use `toOwnedSlice`.
pub fn ArrayListUnmanaged(comptime T: type) type {
return ArrayListAlignedUnmanaged(T, null);
}
+/// An ArrayListAligned, but the allocator is passed as a parameter to the relevant
+/// functions rather than stored in the struct itself. The same allocator **must**
+/// be used throughout the entire lifetime of an ArrayListAlignedUnmanaged.
+/// Initialize directly or with `initCapacity`, and deinitialize with `deinit` or use `toOwnedSlice`.
pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) type {
if (alignment) |a| {
if (a == @alignOf(T)) {
@@ -352,9 +414,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
}
return struct {
const Self = @This();
-
- /// Content of the ArrayList.
+ /// Contents of the list. Pointers to elements in this slice are
+ /// **invalid after resizing operations** on the ArrayList, unless the
+ /// operation explicitly either: (1) states otherwise or (2) lists the
+ /// invalidated pointers.
+ ///
+ /// The allocator used determines how element pointers are
+ /// invalidated, so the behavior may vary between lists. To avoid
+ /// illegal behavior, take into account the above paragraph plus the
+ /// explicit statements given in each method.
items: Slice = &[_]T{},
+ /// How many T values this list can hold without allocating
+ /// additional memory.
capacity: usize = 0,
pub const Slice = if (alignment) |a| ([]align(a) T) else []T;
@@ -378,6 +449,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
self.* = undefined;
}
+ /// Convert this list into an analogous memory-managed one.
+ /// The returned list has ownership of the underlying memory.
pub fn toManaged(self: *Self, allocator: *Allocator) ArrayListAligned(T, alignment) {
return .{ .items = self.items, .capacity = self.capacity, .allocator = allocator };
}
@@ -389,8 +462,16 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
return result;
}
+ /// The caller owns the returned memory. ArrayList becomes empty.
+ pub fn toOwnedSliceSentinel(self: *Self, allocator: *Allocator, comptime sentinel: T) ![:sentinel]T {
+ try self.append(allocator, sentinel);
+ const result = self.toOwnedSlice(allocator);
+ return result[0 .. result.len - 1 :sentinel];
+ }
+
/// Insert `item` at index `n`. Moves `list[n .. list.len]`
- /// to make room.
+ /// to higher indices to make room.
+ /// This operation is O(N).
pub fn insert(self: *Self, allocator: *Allocator, n: usize, item: T) !void {
try self.ensureCapacity(allocator, self.items.len + 1);
self.items.len += 1;
@@ -399,8 +480,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
self.items[n] = item;
}
- /// Insert slice `items` at index `i`. Moves
- /// `list[i .. list.len]` to make room.
+ /// Insert slice `items` at index `i`. Moves `list[i .. list.len]` to
+ /// higher indicices make room.
/// This operation is O(N).
pub fn insertSlice(self: *Self, allocator: *Allocator, i: usize, items: SliceConst) !void {
try self.ensureCapacity(allocator, self.items.len + items.len);
@@ -411,8 +492,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
}
/// Replace range of elements `list[start..start+len]` with `new_items`
- /// grows list if `len < new_items.len`. may allocate
- /// shrinks list if `len > new_items.len`
+ /// Grows list if `len < new_items.len`.
+ /// Shrinks list if `len > new_items.len`
+ /// Invalidates pointers if this ArrayList is resized.
pub fn replaceRange(self: *Self, allocator: *Allocator, start: usize, len: usize, new_items: SliceConst) !void {
var managed = self.toManaged(allocator);
try managed.replaceRange(start, len, new_items);
@@ -433,7 +515,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
}
/// Remove the element at index `i` from the list and return its value.
- /// Asserts the array has at least one item.
+ /// Asserts the array has at least one item. Invalidates pointers to
+ /// last element.
/// This operation is O(N).
pub fn orderedRemove(self: *Self, i: usize) T {
const newlen = self.items.len - 1;
@@ -448,6 +531,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
/// Removes the element at the specified index and returns it.
/// The empty slot is filled from the end of the list.
+ /// Invalidates pointers to last element.
/// This operation is O(1).
pub fn swapRemove(self: *Self, i: usize) T {
if (self.items.len - 1 == i) return self.pop();
@@ -474,14 +558,6 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
mem.copy(T, self.items[oldlen..], items);
}
- /// Same as `append` except it returns the number of bytes written, which is always the same
- /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
- /// This function may be called only when `T` is `u8`.
- fn appendWrite(self: *Self, allocator: *Allocator, m: []const u8) !usize {
- try self.appendSlice(allocator, m);
- return m.len;
- }
-
/// Append a value to the list `n` times.
/// Allocates more memory as necessary.
pub fn appendNTimes(self: *Self, allocator: *Allocator, value: T, n: usize) !void {
@@ -491,6 +567,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
}
/// Append a value to the list `n` times.
+ /// **Does not** invalidate pointers.
/// Asserts the capacity is enough.
pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
const new_len = self.items.len + n;
@@ -500,15 +577,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
}
/// Adjust the list's length to `new_len`.
- /// Does not initialize added items if any.
+ /// Does not initialize added items, if any.
pub fn resize(self: *Self, allocator: *Allocator, new_len: usize) !void {
try self.ensureCapacity(allocator, new_len);
self.items.len = new_len;
}
/// Reduce allocated capacity to `new_len`.
- /// Invalidates element pointers.
- pub fn shrink(self: *Self, allocator: *Allocator, new_len: usize) void {
+ pub fn shrinkAndFree(self: *Self, allocator: *Allocator, new_len: usize) void {
assert(new_len <= self.items.len);
self.items = allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) {
@@ -521,13 +597,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
}
/// Reduce length to `new_len`.
- /// Invalidates element pointers.
+ /// Invalidates pointers to elements `items[new_len..]`.
/// Keeps capacity the same.
pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
assert(new_len <= self.items.len);
self.items.len = new_len;
}
+ /// Modify the array so that it can hold at least `new_capacity` items.
+ /// Invalidates pointers if additional memory is needed.
pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void {
var better_capacity = self.capacity;
if (better_capacity >= new_capacity) return;
@@ -544,13 +622,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
/// Increases the array's length to match the full capacity that is already allocated.
/// The new elements have `undefined` values.
- /// This operation does not invalidate any element pointers.
+ /// **Does not** invalidate pointers.
pub fn expandToCapacity(self: *Self) void {
self.items.len = self.capacity;
}
/// Increase length by 1, returning pointer to the new item.
- /// The returned pointer becomes invalid when the list is resized.
+ /// The returned pointer becomes invalid when the list resized.
pub fn addOne(self: *Self, allocator: *Allocator) !*T {
const newlen = self.items.len + 1;
try self.ensureCapacity(allocator, newlen);
@@ -559,8 +637,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
/// Increase length by 1, returning pointer to the new item.
/// Asserts that there is already space for the new item without allocating more.
- /// The returned pointer becomes invalid when the list is resized.
- /// This operation does not invalidate any element pointers.
+ /// **Does not** invalidate pointers.
+ /// The returned pointer becomes invalid when the list resized.
pub fn addOneAssumeCapacity(self: *Self) *T {
assert(self.items.len < self.capacity);
@@ -570,6 +648,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
/// Resize the array, adding `n` new elements, which have `undefined` values.
/// The return value is an array pointing to the newly allocated elements.
+ /// The returned pointer becomes invalid when the list is resized.
pub fn addManyAsArray(self: *Self, allocator: *Allocator, comptime n: usize) !*[n]T {
const prev_len = self.items.len;
try self.resize(allocator, self.items.len + n);
@@ -579,6 +658,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
/// Resize the array, adding `n` new elements, which have `undefined` values.
/// The return value is an array pointing to the newly allocated elements.
/// Asserts that there is already space for the new item without allocating more.
+ /// **Does not** invalidate pointers.
+ /// The returned pointer becomes invalid when the list is resized.
pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
assert(self.items.len + n <= self.capacity);
const prev_len = self.items.len;
@@ -588,7 +669,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
/// Remove and return the last element from the list.
/// Asserts the list has at least one item.
- /// This operation does not invalidate any element pointers.
+ /// Invalidates pointers to last element.
pub fn pop(self: *Self) T {
const val = self.items[self.items.len - 1];
self.items.len -= 1;
@@ -597,7 +678,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
/// Remove and return the last element from the list.
/// If the list is empty, returns `null`.
- /// This operation does not invalidate any element pointers.
+ /// Invalidates pointers to last element.
pub fn popOrNull(self: *Self) ?T {
if (self.items.len == 0) return null;
return self.pop();
@@ -1047,13 +1128,13 @@ test "std.ArrayList/ArrayListUnmanaged: ArrayList(T) of struct T" {
}
}
-test "std.ArrayList(u8) implements outStream" {
+test "std.ArrayList(u8) implements writer" {
var buffer = ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit();
const x: i32 = 42;
const y: i32 = 1234;
- try buffer.outStream().print("x: {}\ny: {}\n", .{ x, y });
+ try buffer.writer().print("x: {}\ny: {}\n", .{ x, y });
testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.items);
}
@@ -1071,7 +1152,7 @@ test "std.ArrayList/ArrayListUnmanaged.shrink still sets length on error.OutOfMe
try list.append(2);
try list.append(3);
- list.shrink(1);
+ list.shrinkAndFree(1);
testing.expect(list.items.len == 1);
}
{
@@ -1081,7 +1162,7 @@ test "std.ArrayList/ArrayListUnmanaged.shrink still sets length on error.OutOfMe
try list.append(a, 2);
try list.append(a, 3);
- list.shrink(a, 1);
+ list.shrinkAndFree(a, 1);
testing.expect(list.items.len == 1);
}
}
@@ -1121,3 +1202,27 @@ test "std.ArrayList/ArrayListUnmanaged.addManyAsArray" {
testing.expectEqualSlices(u8, list.items, "aoeuasdf");
}
}
+
+test "std.ArrayList/ArrayListUnmanaged.toOwnedSliceSentinel" {
+ const a = testing.allocator;
+ {
+ var list = ArrayList(u8).init(a);
+ defer list.deinit();
+
+ try list.appendSlice("foobar");
+
+ const result = try list.toOwnedSliceSentinel(0);
+ defer a.free(result);
+ testing.expectEqualStrings(result, mem.spanZ(result.ptr));
+ }
+ {
+ var list = ArrayListUnmanaged(u8){};
+ defer list.deinit(a);
+
+ try list.appendSlice(a, "foobar");
+
+ const result = try list.toOwnedSliceSentinel(a, 0);
+ defer a.free(result);
+ testing.expectEqualStrings(result, mem.spanZ(result.ptr));
+ }
+}
diff --git a/lib/std/array_list_sentineled.zig b/lib/std/array_list_sentineled.zig
deleted file mode 100644
index 31f8f4fd80..0000000000
--- a/lib/std/array_list_sentineled.zig
+++ /dev/null
@@ -1,229 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("std.zig");
-const debug = std.debug;
-const mem = std.mem;
-const Allocator = mem.Allocator;
-const assert = debug.assert;
-const testing = std.testing;
-const ArrayList = std.ArrayList;
-
-/// A contiguous, growable list of items in memory, with a sentinel after them.
-/// The sentinel is maintained when appending, resizing, etc.
-/// If you do not need a sentinel, consider using `ArrayList` instead.
-pub fn ArrayListSentineled(comptime T: type, comptime sentinel: T) type {
- return struct {
- list: ArrayList(T),
-
- const Self = @This();
-
- /// Must deinitialize with deinit.
- pub fn init(allocator: *Allocator, m: []const T) !Self {
- var self = try initSize(allocator, m.len);
- mem.copy(T, self.list.items, m);
- return self;
- }
-
- /// Initialize memory to size bytes of undefined values.
- /// Must deinitialize with deinit.
- pub fn initSize(allocator: *Allocator, size: usize) !Self {
- var self = initNull(allocator);
- try self.resize(size);
- return self;
- }
-
- /// Initialize with capacity to hold at least num bytes.
- /// Must deinitialize with deinit.
- pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
- var self = Self{ .list = try ArrayList(T).initCapacity(allocator, num + 1) };
- self.list.appendAssumeCapacity(sentinel);
- return self;
- }
-
- /// Must deinitialize with deinit.
- /// None of the other operations are valid until you do one of these:
- /// * `replaceContents`
- /// * `resize`
- pub fn initNull(allocator: *Allocator) Self {
- return Self{ .list = ArrayList(T).init(allocator) };
- }
-
- /// Must deinitialize with deinit.
- pub fn initFromBuffer(buffer: Self) !Self {
- return Self.init(buffer.list.allocator, buffer.span());
- }
-
- /// Takes ownership of the passed in slice. The slice must have been
- /// allocated with `allocator`.
- /// Must deinitialize with deinit.
- pub fn fromOwnedSlice(allocator: *Allocator, slice: []T) !Self {
- var self = Self{ .list = ArrayList(T).fromOwnedSlice(allocator, slice) };
- try self.list.append(sentinel);
- return self;
- }
-
- /// The caller owns the returned memory. The list becomes null and is safe to `deinit`.
- pub fn toOwnedSlice(self: *Self) [:sentinel]T {
- const allocator = self.list.allocator;
- const result = self.list.toOwnedSlice();
- self.* = initNull(allocator);
- return result[0 .. result.len - 1 :sentinel];
- }
-
- /// Only works when `T` is `u8`.
- pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: anytype) !Self {
- const size = std.math.cast(usize, std.fmt.count(format, args)) catch |err| switch (err) {
- error.Overflow => return error.OutOfMemory,
- };
- var self = try Self.initSize(allocator, size);
- assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size);
- return self;
- }
-
- pub fn deinit(self: *Self) void {
- self.list.deinit();
- }
-
- pub fn span(self: anytype) @TypeOf(self.list.items[0..:sentinel]) {
- return self.list.items[0..self.len() :sentinel];
- }
-
- pub fn shrink(self: *Self, new_len: usize) void {
- assert(new_len <= self.len());
- self.list.shrink(new_len + 1);
- self.list.items[self.len()] = sentinel;
- }
-
- pub fn resize(self: *Self, new_len: usize) !void {
- try self.list.resize(new_len + 1);
- self.list.items[self.len()] = sentinel;
- }
-
- pub fn isNull(self: Self) bool {
- return self.list.items.len == 0;
- }
-
- pub fn len(self: Self) usize {
- return self.list.items.len - 1;
- }
-
- pub fn capacity(self: Self) usize {
- return if (self.list.capacity > 0)
- self.list.capacity - 1
- else
- 0;
- }
-
- pub fn appendSlice(self: *Self, m: []const T) !void {
- const old_len = self.len();
- try self.resize(old_len + m.len);
- mem.copy(T, self.list.items[old_len..], m);
- }
-
- pub fn append(self: *Self, byte: T) !void {
- const old_len = self.len();
- try self.resize(old_len + 1);
- self.list.items[old_len] = byte;
- }
-
- pub fn eql(self: Self, m: []const T) bool {
- return mem.eql(T, self.span(), m);
- }
-
- pub fn startsWith(self: Self, m: []const T) bool {
- if (self.len() < m.len) return false;
- return mem.eql(T, self.list.items[0..m.len], m);
- }
-
- pub fn endsWith(self: Self, m: []const T) bool {
- const l = self.len();
- if (l < m.len) return false;
- const start = l - m.len;
- return mem.eql(T, self.list.items[start..l], m);
- }
-
- pub fn replaceContents(self: *Self, m: []const T) !void {
- try self.resize(m.len);
- mem.copy(T, self.list.items, m);
- }
-
- /// Initializes an OutStream which will append to the list.
- /// This function may be called only when `T` is `u8`.
- pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) {
- return .{ .context = self };
- }
-
- /// Same as `append` except it returns the number of bytes written, which is always the same
- /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
- /// This function may be called only when `T` is `u8`.
- pub fn appendWrite(self: *Self, m: []const u8) !usize {
- try self.appendSlice(m);
- return m.len;
- }
- };
-}
-
-test "simple" {
- var buf = try ArrayListSentineled(u8, 0).init(testing.allocator, "");
- defer buf.deinit();
-
- testing.expect(buf.len() == 0);
- try buf.appendSlice("hello");
- try buf.appendSlice(" ");
- try buf.appendSlice("world");
- testing.expect(buf.eql("hello world"));
- testing.expect(mem.eql(u8, mem.spanZ(buf.span().ptr), buf.span()));
-
- var buf2 = try ArrayListSentineled(u8, 0).initFromBuffer(buf);
- defer buf2.deinit();
- testing.expect(buf.eql(buf2.span()));
-
- testing.expect(buf.startsWith("hell"));
- testing.expect(buf.endsWith("orld"));
-
- try buf2.resize(4);
- testing.expect(buf.startsWith(buf2.span()));
-}
-
-test "initSize" {
- var buf = try ArrayListSentineled(u8, 0).initSize(testing.allocator, 3);
- defer buf.deinit();
- testing.expect(buf.len() == 3);
- try buf.appendSlice("hello");
- testing.expect(mem.eql(u8, buf.span()[3..], "hello"));
-}
-
-test "initCapacity" {
- var buf = try ArrayListSentineled(u8, 0).initCapacity(testing.allocator, 10);
- defer buf.deinit();
- testing.expect(buf.len() == 0);
- testing.expect(buf.capacity() >= 10);
- const old_cap = buf.capacity();
- try buf.appendSlice("hello");
- testing.expect(buf.len() == 5);
- testing.expect(buf.capacity() == old_cap);
- testing.expect(mem.eql(u8, buf.span(), "hello"));
-}
-
-test "print" {
- var buf = try ArrayListSentineled(u8, 0).init(testing.allocator, "");
- defer buf.deinit();
-
- try buf.outStream().print("Hello {} the {}", .{ 2, "world" });
- testing.expect(buf.eql("Hello 2 the world"));
-}
-
-test "outStream" {
- var buffer = try ArrayListSentineled(u8, 0).initSize(testing.allocator, 0);
- defer buffer.deinit();
- const buf_stream = buffer.outStream();
-
- const x: i32 = 42;
- const y: i32 = 1234;
- try buf_stream.print("x: {}\ny: {}\n", .{ x, y });
-
- testing.expect(mem.eql(u8, buffer.span(), "x: 42\ny: 1234\n"));
-}
diff --git a/lib/std/ascii.zig b/lib/std/ascii.zig
index c8dc37c99c..7d391b7c4b 100644
--- a/lib/std/ascii.zig
+++ b/lib/std/ascii.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/atomic.zig b/lib/std/atomic.zig
index 4402ca462b..ab80fce872 100644
--- a/lib/std/atomic.zig
+++ b/lib/std/atomic.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/atomic/bool.zig b/lib/std/atomic/bool.zig
index 27a265bbc1..c968b862b9 100644
--- a/lib/std/atomic/bool.zig
+++ b/lib/std/atomic/bool.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/atomic/int.zig b/lib/std/atomic/int.zig
index b06575e05f..1a3bead2df 100644
--- a/lib/std/atomic/int.zig
+++ b/lib/std/atomic/int.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/atomic/queue.zig b/lib/std/atomic/queue.zig
index dd139106b4..f5f63944ab 100644
--- a/lib/std/atomic/queue.zig
+++ b/lib/std/atomic/queue.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -16,7 +16,7 @@ pub fn Queue(comptime T: type) type {
return struct {
head: ?*Node,
tail: ?*Node,
- mutex: std.Mutex,
+ mutex: std.Thread.Mutex,
pub const Self = @This();
pub const Node = std.TailQueue(T).Node;
@@ -27,7 +27,7 @@ pub fn Queue(comptime T: type) type {
return Self{
.head = null,
.tail = null,
- .mutex = std.Mutex{},
+ .mutex = std.Thread.Mutex{},
};
}
@@ -122,7 +122,7 @@ pub fn Queue(comptime T: type) type {
/// Dumps the contents of the queue to `stderr`.
pub fn dump(self: *Self) void {
- self.dumpToStream(std.io.getStdErr().outStream()) catch return;
+ self.dumpToStream(std.io.getStdErr().writer()) catch return;
}
/// Dumps the contents of the queue to `stream`.
@@ -351,7 +351,7 @@ test "std.atomic.Queue dump" {
// Test empty stream
fbs.reset();
- try queue.dumpToStream(fbs.outStream());
+ try queue.dumpToStream(fbs.writer());
expect(mem.eql(u8, buffer[0..fbs.pos],
\\head: (null)
\\tail: (null)
@@ -367,7 +367,7 @@ test "std.atomic.Queue dump" {
queue.put(&node_0);
fbs.reset();
- try queue.dumpToStream(fbs.outStream());
+ try queue.dumpToStream(fbs.writer());
var expected = try std.fmt.bufPrint(expected_buffer[0..],
\\head: 0x{x}=1
@@ -387,7 +387,7 @@ test "std.atomic.Queue dump" {
queue.put(&node_1);
fbs.reset();
- try queue.dumpToStream(fbs.outStream());
+ try queue.dumpToStream(fbs.writer());
expected = try std.fmt.bufPrint(expected_buffer[0..],
\\head: 0x{x}=1
diff --git a/lib/std/atomic/stack.zig b/lib/std/atomic/stack.zig
index 1e289bae70..d55a8f81a3 100644
--- a/lib/std/atomic/stack.zig
+++ b/lib/std/atomic/stack.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/auto_reset_event.zig b/lib/std/auto_reset_event.zig
deleted file mode 100644
index 7e13dc1aba..0000000000
--- a/lib/std/auto_reset_event.zig
+++ /dev/null
@@ -1,227 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("std.zig");
-const builtin = @import("builtin");
-const testing = std.testing;
-const assert = std.debug.assert;
-
-/// Similar to std.ResetEvent but on `set()` it also (atomically) does `reset()`.
-/// Unlike std.ResetEvent, `wait()` can only be called by one thread (MPSC-like).
-pub const AutoResetEvent = struct {
- // AutoResetEvent has 3 possible states:
- // - UNSET: the AutoResetEvent is currently unset
- // - SET: the AutoResetEvent was notified before a wait() was called
- // - <std.ResetEvent pointer>: there is an active waiter waiting for a notification.
- //
- // When attempting to wait:
- // if the event is unset, it registers a ResetEvent pointer to be notified when the event is set
- // if the event is already set, then it consumes the notification and resets the event.
- //
- // When attempting to notify:
- // if the event is unset, then we set the event
- // if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent
- //
- // This ensures that the event is automatically reset after a wait() has been issued
- // and avoids the race condition when using std.ResetEvent in the following scenario:
- // thread 1 | thread 2
- // std.ResetEvent.wait() |
- // | std.ResetEvent.set()
- // | std.ResetEvent.set()
- // std.ResetEvent.reset() |
- // std.ResetEvent.wait() | (missed the second .set() notification above)
- state: usize = UNSET,
-
- const UNSET = 0;
- const SET = 1;
-
- // the minimum alignment for the `*std.ResetEvent` created by wait*()
- const event_align = std.math.max(@alignOf(std.ResetEvent), 2);
-
- pub fn wait(self: *AutoResetEvent) void {
- self.waitFor(null) catch unreachable;
- }
-
- pub fn timedWait(self: *AutoResetEvent, timeout: u64) error{TimedOut}!void {
- return self.waitFor(timeout);
- }
-
- fn waitFor(self: *AutoResetEvent, timeout: ?u64) error{TimedOut}!void {
- // lazily initialized std.ResetEvent
- var reset_event: std.ResetEvent align(event_align) = undefined;
- var has_reset_event = false;
- defer if (has_reset_event) {
- reset_event.deinit();
- };
-
- var state = @atomicLoad(usize, &self.state, .SeqCst);
- while (true) {
- // consume a notification if there is any
- if (state == SET) {
- @atomicStore(usize, &self.state, UNSET, .SeqCst);
- return;
- }
-
- // check if theres currently a pending ResetEvent pointer already registered
- if (state != UNSET) {
- unreachable; // multiple waiting threads on the same AutoResetEvent
- }
-
- // lazily initialize the ResetEvent if it hasn't been already
- if (!has_reset_event) {
- has_reset_event = true;
- reset_event = std.ResetEvent.init();
- }
-
- // Since the AutoResetEvent currently isnt set,
- // try to register our ResetEvent on it to wait
- // for a set() call from another thread.
- if (@cmpxchgWeak(
- usize,
- &self.state,
- UNSET,
- @ptrToInt(&reset_event),
- .SeqCst,
- .SeqCst,
- )) |new_state| {
- state = new_state;
- continue;
- }
-
- // if no timeout was specified, then just wait forever
- const timeout_ns = timeout orelse {
- reset_event.wait();
- return;
- };
-
- // wait with a timeout and return if signalled via set()
- if (reset_event.timedWait(timeout_ns)) |_| {
- return;
- } else |timed_out| {}
-
- // If we timed out, we need to transition the AutoResetEvent back to UNSET.
- // If we don't, then when we return, a set() thread could observe a pointer to an invalid ResetEvent.
- state = @cmpxchgStrong(
- usize,
- &self.state,
- @ptrToInt(&reset_event),
- UNSET,
- .SeqCst,
- .SeqCst,
- ) orelse return error.TimedOut;
-
- // We didn't manage to unregister ourselves from the state.
- if (state == SET) {
- unreachable; // AutoResetEvent notified without waking up the waiting thread
- } else if (state != UNSET) {
- unreachable; // multiple waiting threads on the same AutoResetEvent observed when timing out
- }
-
- // This menas a set() thread saw our ResetEvent pointer, acquired it, and is trying to wake it up.
- // We need to wait for it to wake up our ResetEvent before we can return and invalidate it.
- // We don't return error.TimedOut here as it technically notified us while we were "timing out".
- reset_event.wait();
- return;
- }
- }
-
- pub fn set(self: *AutoResetEvent) void {
- var state = @atomicLoad(usize, &self.state, .SeqCst);
- while (true) {
- // If the AutoResetEvent is already set, there is nothing else left to do
- if (state == SET) {
- return;
- }
-
- // If the AutoResetEvent isn't set,
- // then try to leave a notification for the wait() thread that we set() it.
- if (state == UNSET) {
- state = @cmpxchgWeak(
- usize,
- &self.state,
- UNSET,
- SET,
- .SeqCst,
- .SeqCst,
- ) orelse return;
- continue;
- }
-
- // There is a ResetEvent pointer registered on the AutoResetEvent event thats waiting.
- // Try to acquire ownership of it so that we can wake it up.
- // This also resets the AutoResetEvent so that there is no race condition as defined above.
- if (@cmpxchgWeak(
- usize,
- &self.state,
- state,
- UNSET,
- .SeqCst,
- .SeqCst,
- )) |new_state| {
- state = new_state;
- continue;
- }
-
- const reset_event = @intToPtr(*align(event_align) std.ResetEvent, state);
- reset_event.set();
- return;
- }
- }
-};
-
-test "std.AutoResetEvent" {
- // test local code paths
- {
- var event = AutoResetEvent{};
- testing.expectError(error.TimedOut, event.timedWait(1));
- event.set();
- event.wait();
- }
-
- // test cross-thread signaling
- if (builtin.single_threaded)
- return;
-
- const Context = struct {
- value: u128 = 0,
- in: AutoResetEvent = AutoResetEvent{},
- out: AutoResetEvent = AutoResetEvent{},
-
- const Self = @This();
-
- fn sender(self: *Self) void {
- testing.expect(self.value == 0);
- self.value = 1;
- self.out.set();
-
- self.in.wait();
- testing.expect(self.value == 2);
- self.value = 3;
- self.out.set();
-
- self.in.wait();
- testing.expect(self.value == 4);
- }
-
- fn receiver(self: *Self) void {
- self.out.wait();
- testing.expect(self.value == 1);
- self.value = 2;
- self.in.set();
-
- self.out.wait();
- testing.expect(self.value == 3);
- self.value = 4;
- self.in.set();
- }
- };
-
- var context = Context{};
- const send_thread = try std.Thread.spawn(&context, Context.sender);
- const recv_thread = try std.Thread.spawn(&context, Context.receiver);
-
- send_thread.wait();
- recv_thread.wait();
-}
diff --git a/lib/std/base64.zig b/lib/std/base64.zig
index 0b91a1cf0a..e6a780c239 100644
--- a/lib/std/base64.zig
+++ b/lib/std/base64.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -38,8 +38,8 @@ pub const Base64Encoder = struct {
}
/// dest.len must be what you get from ::calcSize.
- pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) void {
- assert(dest.len == Base64Encoder.calcSize(source.len));
+ pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 {
+ assert(dest.len >= Base64Encoder.calcSize(source.len));
var i: usize = 0;
var out_index: usize = 0;
@@ -78,6 +78,7 @@ pub const Base64Encoder = struct {
dest[out_index] = encoder.pad_char;
out_index += 1;
}
+ return dest[0..out_index];
}
};
@@ -221,12 +222,10 @@ pub const Base64DecoderWithIgnore = struct {
} else if (decoder_with_ignore.char_is_ignored[c]) {
// we can even ignore chars during the padding
continue;
- } else
- return error.InvalidCharacter;
+ } else return error.InvalidCharacter;
}
break;
- } else
- return error.InvalidCharacter;
+ } else return error.InvalidCharacter;
}
switch (available_chars) {
@@ -398,8 +397,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void
// Base64Encoder
{
var buffer: [0x100]u8 = undefined;
- var encoded = buffer[0..Base64Encoder.calcSize(expected_decoded.len)];
- standard_encoder.encode(encoded, expected_decoded);
+ const encoded = standard_encoder.encode(&buffer, expected_decoded);
testing.expectEqualSlices(u8, expected_encoded, encoded);
}
diff --git a/lib/std/buf_map.zig b/lib/std/buf_map.zig
index 3be724e10e..29d1ac4876 100644
--- a/lib/std/buf_map.zig
+++ b/lib/std/buf_map.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/buf_set.zig b/lib/std/buf_set.zig
index f48e6c594c..75c5ae742d 100644
--- a/lib/std/buf_set.zig
+++ b/lib/std/buf_set.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/build.zig b/lib/std/build.zig
index dacfaf5f75..e11402e493 100644
--- a/lib/std/build.zig
+++ b/lib/std/build.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -272,15 +272,57 @@ pub const Builder = struct {
return LibExeObjStep.createSharedLibrary(self, name, root_src_param, kind);
}
+ pub fn addSharedLibraryFromWriteFileStep(
+ self: *Builder,
+ name: []const u8,
+ wfs: *WriteFileStep,
+ basename: []const u8,
+ kind: LibExeObjStep.SharedLibKind,
+ ) *LibExeObjStep {
+ return LibExeObjStep.createSharedLibrary(self, name, @as(FileSource, .{
+ .write_file = .{
+ .step = wfs,
+ .basename = basename,
+ },
+ }), kind);
+ }
+
pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null;
return LibExeObjStep.createStaticLibrary(self, name, root_src_param);
}
+ pub fn addStaticLibraryFromWriteFileStep(
+ self: *Builder,
+ name: []const u8,
+ wfs: *WriteFileStep,
+ basename: []const u8,
+ ) *LibExeObjStep {
+ return LibExeObjStep.createStaticLibrary(self, name, @as(FileSource, .{
+ .write_file = .{
+ .step = wfs,
+ .basename = basename,
+ },
+ }));
+ }
+
pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep {
return LibExeObjStep.createTest(self, "test", .{ .path = root_src });
}
+ pub fn addTestFromWriteFileStep(
+ self: *Builder,
+ wfs: *WriteFileStep,
+ basename: []const u8,
+ ) *LibExeObjStep {
+ return LibExeObjStep.createTest(self, "test", @as(FileSource, .{
+ .write_file = .{
+ .step = wfs,
+ .basename = basename,
+ },
+ }));
+ }
+
pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep {
const obj_step = LibExeObjStep.createObject(self, name, null);
obj_step.addAssemblyFile(src);
@@ -294,7 +336,7 @@ pub const Builder = struct {
/// To run an executable built with zig build, see `LibExeObjStep.run`.
pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep {
assert(argv.len >= 1);
- const run_step = RunStep.create(self, self.fmt("run {}", .{argv[0]}));
+ const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
run_step.addArgs(argv);
return run_step;
}
@@ -303,6 +345,14 @@ pub const Builder = struct {
return self.allocator.dupe(u8, bytes) catch unreachable;
}
+ pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 {
+ const array = self.allocator.alloc([]u8, strings.len) catch unreachable;
+ for (strings) |s, i| {
+ array[i] = self.dupe(s);
+ }
+ return array;
+ }
+
pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {
const the_copy = self.dupe(bytes);
for (the_copy) |*byte| {
@@ -409,7 +459,7 @@ pub const Builder = struct {
for (self.installed_files.items) |installed_file| {
const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
if (self.verbose) {
- warn("rm {}\n", .{full_path});
+ warn("rm {s}\n", .{full_path});
}
fs.cwd().deleteTree(full_path) catch {};
}
@@ -419,7 +469,7 @@ pub const Builder = struct {
fn makeOneStep(self: *Builder, s: *Step) anyerror!void {
if (s.loop_flag) {
- warn("Dependency loop detected:\n {}\n", .{s.name});
+ warn("Dependency loop detected:\n {s}\n", .{s.name});
return error.DependencyLoopDetected;
}
s.loop_flag = true;
@@ -427,7 +477,7 @@ pub const Builder = struct {
for (s.dependencies.items) |dep| {
self.makeOneStep(dep) catch |err| {
if (err == error.DependencyLoopDetected) {
- warn(" {}\n", .{s.name});
+ warn(" {s}\n", .{s.name});
}
return err;
};
@@ -444,11 +494,13 @@ pub const Builder = struct {
return &top_level_step.step;
}
}
- warn("Cannot run step '{}' because it does not exist\n", .{name});
+ warn("Cannot run step '{s}' because it does not exist\n", .{name});
return error.InvalidStepName;
}
- pub fn option(self: *Builder, comptime T: type, name: []const u8, description: []const u8) ?T {
+ pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
+ const name = self.dupe(name_raw);
+ const description = self.dupe(description_raw);
const type_id = comptime typeToEnum(T);
const available_option = AvailableOption{
.name = name,
@@ -456,7 +508,7 @@ pub const Builder = struct {
.description = description,
};
if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
- panic("Option '{}' declared twice", .{name});
+ panic("Option '{s}' declared twice", .{name});
}
self.available_options_list.append(available_option) catch unreachable;
@@ -471,32 +523,32 @@ pub const Builder = struct {
} else if (mem.eql(u8, s, "false")) {
return false;
} else {
- warn("Expected -D{} to be a boolean, but received '{}'\n", .{ name, s });
+ warn("Expected -D{s} to be a boolean, but received '{s}'\n\n", .{ name, s });
self.markInvalidUserInput();
return null;
}
},
.List => {
- warn("Expected -D{} to be a boolean, but received a list.\n", .{name});
+ warn("Expected -D{s} to be a boolean, but received a list.\n\n", .{name});
self.markInvalidUserInput();
return null;
},
},
.Int => switch (entry.value.value) {
.Flag => {
- warn("Expected -D{} to be an integer, but received a boolean.\n", .{name});
+ warn("Expected -D{s} to be an integer, but received a boolean.\n\n", .{name});
self.markInvalidUserInput();
return null;
},
.Scalar => |s| {
const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
error.Overflow => {
- warn("-D{} value {} cannot fit into type {}.\n", .{ name, s, @typeName(T) });
+ warn("-D{s} value {s} cannot fit into type {s}.\n\n", .{ name, s, @typeName(T) });
self.markInvalidUserInput();
return null;
},
else => {
- warn("Expected -D{} to be an integer of type {}.\n", .{ name, @typeName(T) });
+ warn("Expected -D{s} to be an integer of type {s}.\n\n", .{ name, @typeName(T) });
self.markInvalidUserInput();
return null;
},
@@ -504,15 +556,34 @@ pub const Builder = struct {
return n;
},
.List => {
- warn("Expected -D{} to be an integer, but received a list.\n", .{name});
+ warn("Expected -D{s} to be an integer, but received a list.\n\n", .{name});
+ self.markInvalidUserInput();
+ return null;
+ },
+ },
+ .Float => switch (entry.value.value) {
+ .Flag => {
+ warn("Expected -D{s} to be a float, but received a boolean.\n\n", .{name});
+ self.markInvalidUserInput();
+ return null;
+ },
+ .Scalar => |s| {
+ const n = std.fmt.parseFloat(T, s) catch |err| {
+ warn("Expected -D{s} to be a float of type {s}.\n\n", .{ name, @typeName(T) });
+ self.markInvalidUserInput();
+ return null;
+ };
+ return n;
+ },
+ .List => {
+ warn("Expected -D{s} to be a float, but received a list.\n\n", .{name});
self.markInvalidUserInput();
return null;
},
},
- .Float => panic("TODO float options to build script", .{}),
.Enum => switch (entry.value.value) {
.Flag => {
- warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
+ warn("Expected -D{s} to be a string, but received a boolean.\n\n", .{name});
self.markInvalidUserInput();
return null;
},
@@ -520,25 +591,25 @@ pub const Builder = struct {
if (std.meta.stringToEnum(T, s)) |enum_lit| {
return enum_lit;
} else {
- warn("Expected -D{} to be of type {}.\n", .{ name, @typeName(T) });
+ warn("Expected -D{s} to be of type {s}.\n\n", .{ name, @typeName(T) });
self.markInvalidUserInput();
return null;
}
},
.List => {
- warn("Expected -D{} to be a string, but received a list.\n", .{name});
+ warn("Expected -D{s} to be a string, but received a list.\n\n", .{name});
self.markInvalidUserInput();
return null;
},
},
.String => switch (entry.value.value) {
.Flag => {
- warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
+ warn("Expected -D{s} to be a string, but received a boolean.\n\n", .{name});
self.markInvalidUserInput();
return null;
},
.List => {
- warn("Expected -D{} to be a string, but received a list.\n", .{name});
+ warn("Expected -D{s} to be a string, but received a list.\n\n", .{name});
self.markInvalidUserInput();
return null;
},
@@ -546,7 +617,7 @@ pub const Builder = struct {
},
.List => switch (entry.value.value) {
.Flag => {
- warn("Expected -D{} to be a list, but received a boolean.\n", .{name});
+ warn("Expected -D{s} to be a list, but received a boolean.\n\n", .{name});
self.markInvalidUserInput();
return null;
},
@@ -562,7 +633,7 @@ pub const Builder = struct {
const step_info = self.allocator.create(TopLevelStep) catch unreachable;
step_info.* = TopLevelStep{
.step = Step.initNoOp(.TopLevel, name, self.allocator),
- .description = description,
+ .description = self.dupe(description),
};
self.top_level_steps.append(step_info) catch unreachable;
return &step_info.step;
@@ -573,7 +644,7 @@ pub const Builder = struct {
if (self.release_mode != null) {
@panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice");
}
- const description = self.fmt("Create a release build ({})", .{@tagName(mode)});
+ const description = self.fmt("Create a release build ({s})", .{@tagName(mode)});
self.is_release = self.option(bool, "release", description) orelse false;
self.release_mode = if (self.is_release) mode else builtin.Mode.Debug;
}
@@ -596,7 +667,7 @@ pub const Builder = struct {
else if (!release_fast and !release_safe and !release_small)
builtin.Mode.Debug
else x: {
- warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)", .{});
+ warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n\n", .{});
self.markInvalidUserInput();
break :x builtin.Mode.Debug;
};
@@ -627,43 +698,50 @@ pub const Builder = struct {
.diagnostics = &diags,
}) catch |err| switch (err) {
error.UnknownCpuModel => {
- std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
+ warn("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':\n", .{
diags.cpu_name.?,
@tagName(diags.arch.?),
});
for (diags.arch.?.allCpuModels()) |cpu| {
- std.debug.warn(" {}\n", .{cpu.name});
+ warn(" {s}\n", .{cpu.name});
}
- process.exit(1);
+ warn("\n", .{});
+ self.markInvalidUserInput();
+ return args.default_target;
},
error.UnknownCpuFeature => {
- std.debug.warn(
- \\Unknown CPU feature: '{}'
- \\Available CPU features for architecture '{}':
+ warn(
+ \\Unknown CPU feature: '{s}'
+ \\Available CPU features for architecture '{s}':
\\
, .{
diags.unknown_feature_name,
@tagName(diags.arch.?),
});
for (diags.arch.?.allFeaturesList()) |feature| {
- std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
+ warn(" {s}: {s}\n", .{ feature.name, feature.description });
}
- process.exit(1);
+ warn("\n", .{});
+ self.markInvalidUserInput();
+ return args.default_target;
},
error.UnknownOperatingSystem => {
- std.debug.warn(
- \\Unknown OS: '{}'
+ warn(
+ \\Unknown OS: '{s}'
\\Available operating systems:
\\
, .{diags.os_name});
inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
- std.debug.warn(" {}\n", .{field.name});
+ warn(" {s}\n", .{field.name});
}
- process.exit(1);
+ warn("\n", .{});
+ self.markInvalidUserInput();
+ return args.default_target;
},
else => |e| {
- std.debug.warn("Unable to parse target '{}': {}\n", .{ triple, @errorName(e) });
- process.exit(1);
+ warn("Unable to parse target '{s}': {s}\n\n", .{ triple, @errorName(e) });
+ self.markInvalidUserInput();
+ return args.default_target;
},
};
@@ -677,22 +755,24 @@ pub const Builder = struct {
break :whitelist_check;
}
}
- std.debug.warn("Chosen target '{}' does not match one of the supported targets:\n", .{
+ warn("Chosen target '{s}' does not match one of the supported targets:\n", .{
selected_canonicalized_triple,
});
for (list) |t| {
const t_triple = t.zigTriple(self.allocator) catch unreachable;
- std.debug.warn(" {}\n", .{t_triple});
+ warn(" {s}\n", .{t_triple});
}
- // TODO instead of process exit, return error and have a zig build flag implemented by
- // the build runner that turns process exits into error return traces
- process.exit(1);
+ warn("\n", .{});
+ self.markInvalidUserInput();
+ return args.default_target;
}
return selected_target;
}
- pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
+ pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool {
+ const name = self.dupe(name_raw);
+ const value = self.dupe(value_raw);
const gop = try self.user_input_options.getOrPut(name);
if (!gop.found_existing) {
gop.entry.value = UserInputOption{
@@ -726,14 +806,15 @@ pub const Builder = struct {
}) catch unreachable;
},
UserValue.Flag => {
- warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", .{ name, value, name });
+ warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.\n", .{ name, value, name });
return true;
},
}
return false;
}
- pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {
+ pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool {
+ const name = self.dupe(name_raw);
const gop = try self.user_input_options.getOrPut(name);
if (!gop.found_existing) {
gop.entry.value = UserInputOption{
@@ -747,11 +828,11 @@ pub const Builder = struct {
// option already exists
switch (gop.entry.value.value) {
UserValue.Scalar => |s| {
- warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s });
+ warn("Flag '-D{s}' conflicts with option '-D{s}={s}'.\n", .{ name, name, s });
return true;
},
UserValue.List => {
- warn("Flag '-D{}' conflicts with multiple options of the same name.\n", .{name});
+ warn("Flag '-D{s}' conflicts with multiple options of the same name.\n", .{name});
return true;
},
UserValue.Flag => {},
@@ -794,7 +875,7 @@ pub const Builder = struct {
while (true) {
const entry = it.next() orelse break;
if (!entry.value.used) {
- warn("Invalid option: -D{}\n\n", .{entry.key});
+ warn("Invalid option: -D{s}\n\n", .{entry.key});
self.markInvalidUserInput();
}
}
@@ -807,9 +888,9 @@ pub const Builder = struct {
}
fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
- if (cwd) |yes_cwd| warn("cd {} && ", .{yes_cwd});
+ if (cwd) |yes_cwd| warn("cd {s} && ", .{yes_cwd});
for (argv) |arg| {
- warn("{} ", .{arg});
+ warn("{s} ", .{arg});
}
warn("\n", .{});
}
@@ -826,7 +907,7 @@ pub const Builder = struct {
child.env_map = env_map;
const term = child.spawnAndWait() catch |err| {
- warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) });
+ warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
return err;
};
@@ -849,7 +930,7 @@ pub const Builder = struct {
pub fn makePath(self: *Builder, path: []const u8) !void {
fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
- warn("Unable to create path {}: {}\n", .{ path, @errorName(err) });
+ warn("Unable to create path {s}: {s}\n", .{ path, @errorName(err) });
return err;
};
}
@@ -925,15 +1006,16 @@ pub const Builder = struct {
}
pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void {
- self.installed_files.append(InstalledFile{
+ const file = InstalledFile{
.dir = dir,
.path = dest_rel_path,
- }) catch unreachable;
+ };
+ self.installed_files.append(file.dupe(self)) catch unreachable;
}
pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
if (self.verbose) {
- warn("cp {} {} ", .{ source_path, dest_path });
+ warn("cp {s} {s} ", .{ source_path, dest_path });
}
const cwd = fs.cwd();
const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
@@ -962,7 +1044,7 @@ pub const Builder = struct {
const full_path = try fs.path.join(self.allocator, &[_][]const u8{
search_prefix,
"bin",
- self.fmt("{}{}", .{ name, exe_extension }),
+ self.fmt("{s}{s}", .{ name, exe_extension }),
});
return fs.realpathAlloc(self.allocator, full_path) catch continue;
}
@@ -976,7 +1058,7 @@ pub const Builder = struct {
while (it.next()) |path| {
const full_path = try fs.path.join(self.allocator, &[_][]const u8{
path,
- self.fmt("{}{}", .{ name, exe_extension }),
+ self.fmt("{s}{s}", .{ name, exe_extension }),
});
return fs.realpathAlloc(self.allocator, full_path) catch continue;
}
@@ -989,7 +1071,7 @@ pub const Builder = struct {
for (paths) |path| {
const full_path = try fs.path.join(self.allocator, &[_][]const u8{
path,
- self.fmt("{}{}", .{ name, exe_extension }),
+ self.fmt("{s}{s}", .{ name, exe_extension }),
});
return fs.realpathAlloc(self.allocator, full_path) catch continue;
}
@@ -1012,10 +1094,11 @@ pub const Builder = struct {
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Pipe;
child.stderr_behavior = stderr_behavior;
+ child.env_map = self.env_map;
try child.spawn();
- const stdout = try child.stdout.?.inStream().readAllAlloc(self.allocator, max_output_size);
+ const stdout = try child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size);
errdefer self.allocator.free(stdout);
const term = try child.wait();
@@ -1044,19 +1127,19 @@ pub const Builder = struct {
var code: u8 = undefined;
return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
error.FileNotFound => {
- if (src_step) |s| warn("{}...", .{s.name});
+ if (src_step) |s| warn("{s}...", .{s.name});
warn("Unable to spawn the following command: file not found\n", .{});
printCmd(null, argv);
std.os.exit(@truncate(u8, code));
},
error.ExitCodeFailure => {
- if (src_step) |s| warn("{}...", .{s.name});
- warn("The following command exited with error code {}:\n", .{code});
+ if (src_step) |s| warn("{s}...", .{s.name});
+ warn("The following command exited with error code {d}:\n", .{code});
printCmd(null, argv);
std.os.exit(@truncate(u8, code));
},
error.ProcessTerminated => {
- if (src_step) |s| warn("{}...", .{s.name});
+ if (src_step) |s| warn("{s}...", .{s.name});
warn("The following command terminated unexpectedly:\n", .{});
printCmd(null, argv);
std.os.exit(@truncate(u8, code));
@@ -1070,10 +1153,11 @@ pub const Builder = struct {
}
pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void {
- self.search_prefixes.append(search_prefix) catch unreachable;
+ self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable;
}
pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
+ assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
const base_dir = switch (dir) {
.Prefix => self.install_path,
.Bin => self.exe_dir,
@@ -1090,6 +1174,7 @@ pub const Builder = struct {
fn execPkgConfigList(self: *Builder, out_code: *u8) ![]const PkgConfigPkg {
const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
var list = ArrayList(PkgConfigPkg).init(self.allocator);
+ errdefer list.deinit();
var line_it = mem.tokenize(stdout, "\r\n");
while (line_it.next()) |line| {
if (mem.trim(u8, line, " \t").len == 0) continue;
@@ -1099,7 +1184,7 @@ pub const Builder = struct {
.desc = tok_it.rest(),
});
}
- return list.items;
+ return list.toOwnedSlice();
}
fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg {
@@ -1154,9 +1239,16 @@ pub const Pkg = struct {
dependencies: ?[]const Pkg = null,
};
-const CSourceFile = struct {
+pub const CSourceFile = struct {
source: FileSource,
args: []const []const u8,
+
+ fn dupe(self: CSourceFile, b: *Builder) CSourceFile {
+ return .{
+ .source = self.source.dupe(b),
+ .args = b.dupeStrings(self.args),
+ };
+ }
};
const CSourceFiles = struct {
@@ -1198,6 +1290,17 @@ pub const FileSource = union(enum) {
.translate_c => |tc| tc.getOutputPath(),
};
}
+
+ pub fn dupe(self: FileSource, b: *Builder) FileSource {
+ return switch (self) {
+ .path => |p| .{ .path = b.dupe(p) },
+ .write_file => |wf| .{ .write_file = .{
+ .step = wf.step,
+ .basename = b.dupe(wf.basename),
+ } },
+ .translate_c => |tc| .{ .translate_c = tc },
+ };
+ }
};
const BuildOptionArtifactArg = struct {
@@ -1205,6 +1308,12 @@ const BuildOptionArtifactArg = struct {
artifact: *LibExeObjStep,
};
+const BuildOptionWriteFileArg = struct {
+ name: []const u8,
+ write_file: *WriteFileStep,
+ basename: []const u8,
+};
+
pub const LibExeObjStep = struct {
step: Step,
builder: *Builder,
@@ -1233,6 +1342,7 @@ pub const LibExeObjStep = struct {
bundle_compiler_rt: ?bool = null,
disable_stack_probing: bool,
disable_sanitize_c: bool,
+ sanitize_thread: bool,
rdynamic: bool,
c_std: Builder.CStd,
override_lib_dir: ?[]const u8,
@@ -1251,6 +1361,7 @@ pub const LibExeObjStep = struct {
packages: ArrayList(Pkg),
build_options_contents: std.ArrayList(u8),
build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg),
+ build_options_write_file_args: std.ArrayList(BuildOptionWriteFileArg),
object_src: []const u8,
@@ -1303,11 +1414,15 @@ pub const LibExeObjStep = struct {
/// Position Independent Executable
pie: ?bool = null,
+ red_zone: ?bool = null,
+
subsystem: ?builtin.SubSystem = null,
/// Overrides the default stack size
stack_size: ?u64 = null,
+ want_lto: ?bool = null,
+
const LinkObject = union(enum) {
StaticPath: []const u8,
OtherStep: *LibExeObjStep,
@@ -1370,14 +1485,16 @@ pub const LibExeObjStep = struct {
fn initExtraArgs(
builder: *Builder,
- name: []const u8,
- root_src: ?FileSource,
+ name_raw: []const u8,
+ root_src_raw: ?FileSource,
kind: Kind,
is_dynamic: bool,
ver: ?Version,
) LibExeObjStep {
+ const name = builder.dupe(name_raw);
+ const root_src: ?FileSource = if (root_src_raw) |rsrc| rsrc.dupe(builder) else null;
if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
- panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
+ panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
}
var self = LibExeObjStep{
.strip = false,
@@ -1393,9 +1510,9 @@ pub const LibExeObjStep = struct {
.step = Step.init(.LibExeObj, name, builder.allocator, make),
.version = ver,
.out_filename = undefined,
- .out_h_filename = builder.fmt("{}.h", .{name}),
+ .out_h_filename = builder.fmt("{s}.h", .{name}),
.out_lib_filename = undefined,
- .out_pdb_filename = builder.fmt("{}.pdb", .{name}),
+ .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
.major_only_filename = undefined,
.name_only_filename = undefined,
.packages = ArrayList(Pkg).init(builder.allocator),
@@ -1407,6 +1524,7 @@ pub const LibExeObjStep = struct {
.object_src = undefined,
.build_options_contents = std.ArrayList(u8).init(builder.allocator),
.build_options_artifact_args = std.ArrayList(BuildOptionArtifactArg).init(builder.allocator),
+ .build_options_write_file_args = std.ArrayList(BuildOptionWriteFileArg).init(builder.allocator),
.c_std = Builder.CStd.C99,
.override_lib_dir = null,
.main_pkg_path = null,
@@ -1415,6 +1533,7 @@ pub const LibExeObjStep = struct {
.filter = null,
.disable_stack_probing = false,
.disable_sanitize_c = false,
+ .sanitize_thread = false,
.rdynamic = false,
.output_dir = null,
.single_threaded = false,
@@ -1500,7 +1619,7 @@ pub const LibExeObjStep = struct {
// It doesn't have to be native. We catch that if you actually try to run it.
// Consider that this is declarative; the run step may not be run unless a user
// option is supplied.
- const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {}", .{exe.step.name}));
+ const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
run_step.addArtifactArg(exe);
if (exe.vcpkg_bin_path) |path| {
@@ -1511,12 +1630,12 @@ pub const LibExeObjStep = struct {
}
pub fn setLinkerScriptPath(self: *LibExeObjStep, path: []const u8) void {
- self.linker_script = path;
+ self.linker_script = self.builder.dupePath(path);
}
pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
assert(self.target.isDarwin());
- self.frameworks.put(framework_name) catch unreachable;
+ self.frameworks.put(self.builder.dupe(framework_name)) catch unreachable;
}
/// Returns whether the library, executable, or object depends on a particular system library.
@@ -1651,7 +1770,7 @@ pub const LibExeObjStep = struct {
} else if (mem.eql(u8, tok, "-pthread")) {
self.linkLibC();
} else if (self.builder.verbose) {
- warn("Ignoring pkg-config flag '{}'\n", .{tok});
+ warn("Ignoring pkg-config flag '{s}'\n", .{tok});
}
}
}
@@ -1680,25 +1799,23 @@ pub const LibExeObjStep = struct {
pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
assert(self.kind == Kind.Test);
- self.name_prefix = text;
+ self.name_prefix = self.builder.dupe(text);
}
pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
assert(self.kind == Kind.Test);
- self.filter = text;
+ self.filter = if (text) |t| self.builder.dupe(t) else null;
}
/// Handy when you have many C/C++ source files and want them all to have the same flags.
pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
- const flags_copy = self.builder.allocator.alloc([]u8, flags.len) catch unreachable;
- for (flags) |flag, i| {
- flags_copy[i] = self.builder.dupe(flag);
- }
+ const files_copy = self.builder.dupeStrings(files);
+ const flags_copy = self.builder.dupeStrings(flags);
c_source_files.* = .{
- .files = files,
+ .files = files_copy,
.flags = flags_copy,
};
self.link_objects.append(LinkObject{ .CSourceFiles = c_source_files }) catch unreachable;
@@ -1713,14 +1830,7 @@ pub const LibExeObjStep = struct {
pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
-
- const args_copy = self.builder.allocator.alloc([]u8, source.args.len) catch unreachable;
- for (source.args) |arg, i| {
- args_copy[i] = self.builder.dupe(arg);
- }
-
- c_source_file.* = source;
- c_source_file.args = args_copy;
+ c_source_file.* = source.dupe(self.builder);
self.link_objects.append(LinkObject{ .CSourceFile = c_source_file }) catch unreachable;
}
@@ -1737,15 +1847,15 @@ pub const LibExeObjStep = struct {
}
pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
- self.override_lib_dir = self.builder.dupe(dir_path);
+ self.override_lib_dir = self.builder.dupePath(dir_path);
}
pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
- self.main_pkg_path = dir_path;
+ self.main_pkg_path = self.builder.dupePath(dir_path);
}
pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?[]const u8) void {
- self.libc_file = libc_file;
+ self.libc_file = if (libc_file) |f| self.builder.dupe(f) else null;
}
/// Unless setOutputDir was called, this function must be called only in
@@ -1805,8 +1915,9 @@ pub const LibExeObjStep = struct {
}
pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
- self.link_objects.append(LinkObject{ .AssemblyFile = source }) catch unreachable;
- source.addStepDependencies(&self.step);
+ const source_duped = source.dupe(self.builder);
+ self.link_objects.append(LinkObject{ .AssemblyFile = source_duped }) catch unreachable;
+ source_duped.addStepDependencies(&self.step);
}
pub fn addObjectFile(self: *LibExeObjStep, path: []const u8) void {
@@ -1819,28 +1930,28 @@ pub const LibExeObjStep = struct {
}
pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
- const out = self.build_options_contents.outStream();
+ const out = self.build_options_contents.writer();
switch (T) {
[]const []const u8 => {
- out.print("pub const {z}: []const []const u8 = &[_][]const u8{{\n", .{name}) catch unreachable;
+ out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
for (value) |slice| {
- out.print(" \"{Z}\",\n", .{slice}) catch unreachable;
+ out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
}
out.writeAll("};\n") catch unreachable;
return;
},
[:0]const u8 => {
- out.print("pub const {z}: [:0]const u8 = \"{Z}\";\n", .{ name, value }) catch unreachable;
+ out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
return;
},
[]const u8 => {
- out.print("pub const {z}: []const u8 = \"{Z}\";\n", .{ name, value }) catch unreachable;
+ out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
return;
},
?[]const u8 => {
- out.print("pub const {z}: ?[]const u8 = ", .{name}) catch unreachable;
+ out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
if (value) |payload| {
- out.print("\"{Z}\";\n", .{payload}) catch unreachable;
+ out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
} else {
out.writeAll("null;\n") catch unreachable;
}
@@ -1848,14 +1959,14 @@ pub const LibExeObjStep = struct {
},
std.builtin.Version => {
out.print(
- \\pub const {z}: @import("builtin").Version = .{{
+ \\pub const {}: @import("builtin").Version = .{{
\\ .major = {d},
\\ .minor = {d},
\\ .patch = {d},
\\}};
\\
, .{
- name,
+ std.zig.fmtId(name),
value.major,
value.minor,
@@ -1864,23 +1975,23 @@ pub const LibExeObjStep = struct {
},
std.SemanticVersion => {
out.print(
- \\pub const {z}: @import("std").SemanticVersion = .{{
+ \\pub const {}: @import("std").SemanticVersion = .{{
\\ .major = {d},
\\ .minor = {d},
\\ .patch = {d},
\\
, .{
- name,
+ std.zig.fmtId(name),
value.major,
value.minor,
value.patch,
}) catch unreachable;
if (value.pre) |some| {
- out.print(" .pre = \"{Z}\",\n", .{some}) catch unreachable;
+ out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
}
if (value.build) |some| {
- out.print(" .build = \"{Z}\",\n", .{some}) catch unreachable;
+ out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
}
out.writeAll("};\n") catch unreachable;
return;
@@ -1889,24 +2000,41 @@ pub const LibExeObjStep = struct {
}
switch (@typeInfo(T)) {
.Enum => |enum_info| {
- out.print("pub const {z} = enum {{\n", .{@typeName(T)}) catch unreachable;
+ out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
inline for (enum_info.fields) |field| {
- out.print(" {z},\n", .{field.name}) catch unreachable;
+ out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
}
out.writeAll("};\n") catch unreachable;
},
else => {},
}
- out.print("pub const {z}: {} = {};\n", .{ name, @typeName(T), value }) catch unreachable;
+ out.print("pub const {}: {s} = {};\n", .{ std.zig.fmtId(name), @typeName(T), value }) catch unreachable;
}
/// The value is the path in the cache dir.
/// Adds a dependency automatically.
pub fn addBuildOptionArtifact(self: *LibExeObjStep, name: []const u8, artifact: *LibExeObjStep) void {
- self.build_options_artifact_args.append(.{ .name = name, .artifact = artifact }) catch unreachable;
+ self.build_options_artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
self.step.dependOn(&artifact.step);
}
+ /// The value is the path in the cache dir.
+ /// Adds a dependency automatically.
+ /// basename refers to the basename of the WriteFileStep
+ pub fn addBuildOptionWriteFile(
+ self: *LibExeObjStep,
+ name: []const u8,
+ write_file: *WriteFileStep,
+ basename: []const u8,
+ ) void {
+ self.build_options_write_file_args.append(.{
+ .name = name,
+ .write_file = write_file,
+ .basename = basename,
+ }) catch unreachable;
+ self.step.dependOn(&write_file.step);
+ }
+
pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void {
self.include_dirs.append(IncludeDir{ .RawPathSystem = self.builder.dupe(path) }) catch unreachable;
}
@@ -1973,7 +2101,11 @@ pub const LibExeObjStep = struct {
pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
assert(self.kind == Kind.Test);
- self.exec_cmd_args = args;
+ const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
+ for (args) |arg, i| {
+ duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
+ }
+ self.exec_cmd_args = duped_args;
}
fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
@@ -2019,7 +2151,7 @@ pub const LibExeObjStep = struct {
const builder = self.builder;
if (self.root_src == null and self.link_objects.items.len == 0) {
- warn("{}: linker needs 1 or more objects to link\n", .{self.step.name});
+ warn("{s}: linker needs 1 or more objects to link\n", .{self.step.name});
return error.NeedAnObject;
}
@@ -2123,16 +2255,32 @@ pub const LibExeObjStep = struct {
}
}
- if (self.build_options_contents.items.len > 0 or self.build_options_artifact_args.items.len > 0) {
- // Render build artifact options at the last minute, now that the path is known.
+ if (self.build_options_contents.items.len > 0 or
+ self.build_options_artifact_args.items.len > 0 or
+ self.build_options_write_file_args.items.len > 0)
+ {
+ // Render build artifact and write file options at the last minute, now that the path is known.
+ //
+ // Note that pathFromRoot uses resolve path, so this will have
+ // correct behavior even if getOutputPath is already absolute.
for (self.build_options_artifact_args.items) |item| {
- const out = self.build_options_contents.writer();
- out.print("pub const {}: []const u8 = \"{Z}\";\n", .{ item.name, item.artifact.getOutputPath() }) catch unreachable;
+ self.addBuildOption(
+ []const u8,
+ item.name,
+ self.builder.pathFromRoot(item.artifact.getOutputPath()),
+ );
+ }
+ for (self.build_options_write_file_args.items) |item| {
+ self.addBuildOption(
+ []const u8,
+ item.name,
+ self.builder.pathFromRoot(item.write_file.getOutputPath(item.basename)),
+ );
}
const build_options_file = try fs.path.join(
builder.allocator,
- &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) },
+ &[_][]const u8{ builder.cache_root, builder.fmt("{s}_build_options.zig", .{self.name}) },
);
const path_from_root = builder.pathFromRoot(build_options_file);
try fs.cwd().writeFile(path_from_root, self.build_options_contents.items);
@@ -2230,9 +2378,19 @@ pub const LibExeObjStep = struct {
if (self.disable_stack_probing) {
try zig_args.append("-fno-stack-check");
}
+ if (self.red_zone) |red_zone| {
+ if (red_zone) {
+ try zig_args.append("-mred-zone");
+ } else {
+ try zig_args.append("-mno-red-zone");
+ }
+ }
if (self.disable_sanitize_c) {
try zig_args.append("-fno-sanitize-c");
}
+ if (self.sanitize_thread) {
+ try zig_args.append("-fsanitize-thread");
+ }
if (self.rdynamic) {
try zig_args.append("-rdynamic");
}
@@ -2262,16 +2420,16 @@ pub const LibExeObjStep = struct {
} else {
var mcpu_buffer = std.ArrayList(u8).init(builder.allocator);
- try mcpu_buffer.outStream().print("-mcpu={}", .{cross.cpu.model.name});
+ try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name});
for (all_features) |feature, i_usize| {
const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
const in_cpu_set = populated_cpu_features.isEnabled(i);
const in_actual_set = cross.cpu.features.isEnabled(i);
if (in_cpu_set and !in_actual_set) {
- try mcpu_buffer.outStream().print("-{}", .{feature.name});
+ try mcpu_buffer.writer().print("-{s}", .{feature.name});
} else if (!in_cpu_set and in_actual_set) {
- try mcpu_buffer.outStream().print("+{}", .{feature.name});
+ try mcpu_buffer.writer().print("+{s}", .{feature.name});
}
}
@@ -2433,6 +2591,14 @@ pub const LibExeObjStep = struct {
}
}
+ if (self.want_lto) |lto| {
+ if (lto) {
+ try zig_args.append("-flto");
+ } else {
+ try zig_args.append("-fno-lto");
+ }
+ }
+
if (self.subsystem) |subsystem| {
try zig_args.append("--subsystem");
try zig_args.append(switch (subsystem) {
@@ -2504,7 +2670,7 @@ pub const InstallArtifactStep = struct {
const self = builder.allocator.create(Self) catch unreachable;
self.* = Self{
.builder = builder,
- .step = Step.init(.InstallArtifact, builder.fmt("install {}", .{artifact.step.name}), builder.allocator, make),
+ .step = Step.init(.InstallArtifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
.artifact = artifact,
.dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
.Obj => unreachable,
@@ -2580,10 +2746,10 @@ pub const InstallFileStep = struct {
builder.pushInstalledFile(dir, dest_rel_path);
return InstallFileStep{
.builder = builder,
- .step = Step.init(.InstallFile, builder.fmt("install {}", .{src_path}), builder.allocator, make),
- .src_path = src_path,
- .dir = dir,
- .dest_rel_path = dest_rel_path,
+ .step = Step.init(.InstallFile, builder.fmt("install {s}", .{src_path}), builder.allocator, make),
+ .src_path = builder.dupePath(src_path),
+ .dir = dir.dupe(builder),
+ .dest_rel_path = builder.dupePath(dest_rel_path),
};
}
@@ -2600,6 +2766,18 @@ pub const InstallDirectoryOptions = struct {
install_dir: InstallDir,
install_subdir: []const u8,
exclude_extensions: ?[]const []const u8 = null,
+
+ fn dupe(self: InstallDirectoryOptions, b: *Builder) InstallDirectoryOptions {
+ return .{
+ .source_dir = b.dupe(self.source_dir),
+ .install_dir = self.install_dir.dupe(b),
+ .install_subdir = b.dupe(self.install_subdir),
+ .exclude_extensions = if (self.exclude_extensions) |extensions|
+ b.dupeStrings(extensions)
+ else
+ null,
+ };
+ }
};
pub const InstallDirStep = struct {
@@ -2614,8 +2792,8 @@ pub const InstallDirStep = struct {
builder.pushInstalledFile(options.install_dir, options.install_subdir);
return InstallDirStep{
.builder = builder,
- .step = Step.init(.InstallDir, builder.fmt("install {}/", .{options.source_dir}), builder.allocator, make),
- .options = options,
+ .step = Step.init(.InstallDir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
+ .options = options.dupe(builder),
};
}
@@ -2650,14 +2828,14 @@ pub const LogStep = struct {
pub fn init(builder: *Builder, data: []const u8) LogStep {
return LogStep{
.builder = builder,
- .step = Step.init(.Log, builder.fmt("log {}", .{data}), builder.allocator, make),
- .data = data,
+ .step = Step.init(.Log, builder.fmt("log {s}", .{data}), builder.allocator, make),
+ .data = builder.dupe(data),
};
}
fn make(step: *Step) anyerror!void {
const self = @fieldParentPtr(LogStep, "step", step);
- warn("{}", .{self.data});
+ warn("{s}", .{self.data});
}
};
@@ -2669,8 +2847,8 @@ pub const RemoveDirStep = struct {
pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep {
return RemoveDirStep{
.builder = builder,
- .step = Step.init(.RemoveDir, builder.fmt("RemoveDir {}", .{dir_path}), builder.allocator, make),
- .dir_path = dir_path,
+ .step = Step.init(.RemoveDir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
+ .dir_path = builder.dupePath(dir_path),
};
}
@@ -2679,7 +2857,7 @@ pub const RemoveDirStep = struct {
const full_path = self.builder.pathFromRoot(self.dir_path);
fs.cwd().deleteTree(full_path) catch |err| {
- warn("Unable to remove {}: {}\n", .{ full_path, @errorName(err) });
+ warn("Unable to remove {s}: {s}\n", .{ full_path, @errorName(err) });
return err;
};
}
@@ -2714,7 +2892,7 @@ pub const Step = struct {
pub fn init(id: Id, name: []const u8, allocator: *Allocator, makeFn: fn (*Step) anyerror!void) Step {
return Step{
.id = id,
- .name = name,
+ .name = allocator.dupe(u8, name) catch unreachable,
.makeFn = makeFn,
.dependencies = ArrayList(*Step).init(allocator),
.loop_flag = false,
@@ -2767,7 +2945,7 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj
&[_][]const u8{ out_dir, filename_major_only },
) catch unreachable;
fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
- warn("Unable to symlink {} -> {}\n", .{ major_only_path, out_basename });
+ warn("Unable to symlink {s} -> {s}\n", .{ major_only_path, out_basename });
return err;
};
// sym link for libfoo.so to libfoo.so.1
@@ -2776,7 +2954,7 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj
&[_][]const u8{ out_dir, filename_name_only },
) catch unreachable;
fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
- warn("Unable to symlink {} -> {}\n", .{ name_only_path, filename_major_only });
+ warn("Unable to symlink {s} -> {s}\n", .{ name_only_path, filename_major_only });
return err;
};
}
@@ -2821,11 +2999,28 @@ pub const InstallDir = union(enum) {
Header: void,
/// A path relative to the prefix
Custom: []const u8,
+
+ fn dupe(self: InstallDir, builder: *Builder) InstallDir {
+ if (self == .Custom) {
+ // Written with this temporary to avoid RLS problems
+ const duped_path = builder.dupe(self.Custom);
+ return .{ .Custom = duped_path };
+ } else {
+ return self;
+ }
+ }
};
pub const InstalledFile = struct {
dir: InstallDir,
path: []const u8,
+
+ pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile {
+ return .{
+ .dir = self.dir.dupe(builder),
+ .path = builder.dupe(self.path),
+ };
+ }
};
test "Builder.dupePkg()" {
@@ -2943,7 +3138,7 @@ test "LibExeObjStep.addPackage" {
std.testing.expectEqualStrings(pkg_top.name, dupe.name);
}
-test "" {
+test {
// The only purpose of this test is to get all these untested functions
// to be referenced to avoid regression so it is okay to skip some targets.
if (comptime std.Target.current.cpu.arch.ptrBitWidth() == 64) {
diff --git a/lib/std/build/check_file.zig b/lib/std/build/check_file.zig
index e1372c0fe9..28c98547b7 100644
--- a/lib/std/build/check_file.zig
+++ b/lib/std/build/check_file.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -27,8 +27,8 @@ pub const CheckFileStep = struct {
self.* = CheckFileStep{
.builder = builder,
.step = Step.init(.CheckFile, "CheckFile", builder.allocator, make),
- .source = source,
- .expected_matches = expected_matches,
+ .source = source.dupe(builder),
+ .expected_matches = builder.dupeStrings(expected_matches),
};
self.source.addStepDependencies(&self.step);
return self;
@@ -45,9 +45,9 @@ pub const CheckFileStep = struct {
warn(
\\
\\========= Expected to find: ===================
- \\{}
+ \\{s}
\\========= But file does not contain it: =======
- \\{}
+ \\{s}
\\
, .{ expected_match, contents });
return error.TestFailed;
diff --git a/lib/std/build/emit_raw.zig b/lib/std/build/emit_raw.zig
index ce2b6dfe2a..721b38b7a2 100644
--- a/lib/std/build/emit_raw.zig
+++ b/lib/std/build/emit_raw.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -189,7 +189,7 @@ pub const InstallRawStep = struct {
pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *Self {
const self = builder.allocator.create(Self) catch unreachable;
self.* = Self{
- .step = Step.init(.InstallRaw, builder.fmt("install raw binary {}", .{artifact.step.name}), builder.allocator, make),
+ .step = Step.init(.InstallRaw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
.builder = builder,
.artifact = artifact,
.dest_dir = switch (artifact.kind) {
@@ -223,6 +223,6 @@ pub const InstallRawStep = struct {
}
};
-test "" {
+test {
std.testing.refAllDecls(InstallRawStep);
}
diff --git a/lib/std/build/fmt.zig b/lib/std/build/fmt.zig
index 8f0176c00e..069cd348bc 100644
--- a/lib/std/build/fmt.zig
+++ b/lib/std/build/fmt.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/build/run.zig b/lib/std/build/run.zig
index 137d11f9b5..ca39b0216e 100644
--- a/lib/std/build/run.zig
+++ b/lib/std/build/run.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -76,7 +76,7 @@ pub const RunStep = struct {
self.argv.append(Arg{
.WriteFile = .{
.step = write_file,
- .file_name = file_name,
+ .file_name = self.builder.dupePath(file_name),
},
}) catch unreachable;
self.step.dependOn(&write_file.step);
@@ -116,10 +116,10 @@ pub const RunStep = struct {
}
if (prev_path) |pp| {
- const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", .{ pp, search_path });
+ const new_path = self.builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
env_map.set(key, new_path) catch unreachable;
} else {
- env_map.set(key, search_path) catch unreachable;
+ env_map.set(key, self.builder.dupePath(search_path)) catch unreachable;
}
}
@@ -134,15 +134,18 @@ pub const RunStep = struct {
pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
const env_map = self.getEnvMap();
- env_map.set(key, value) catch unreachable;
+ env_map.set(
+ self.builder.dupe(key),
+ self.builder.dupe(value),
+ ) catch unreachable;
}
pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
- self.stderr_action = .{ .expect_exact = bytes };
+ self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
}
pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
- self.stdout_action = .{ .expect_exact = bytes };
+ self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
}
fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
@@ -189,7 +192,7 @@ pub const RunStep = struct {
child.stderr_behavior = stdIoActionToBehavior(self.stderr_action);
child.spawn() catch |err| {
- warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) });
+ warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
return err;
};
@@ -200,7 +203,7 @@ pub const RunStep = struct {
switch (self.stdout_action) {
.expect_exact, .expect_matches => {
- stdout = child.stdout.?.inStream().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
+ stdout = child.stdout.?.reader().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
},
.inherit, .ignore => {},
}
@@ -210,13 +213,13 @@ pub const RunStep = struct {
switch (self.stderr_action) {
.expect_exact, .expect_matches => {
- stderr = child.stderr.?.inStream().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
+ stderr = child.stderr.?.reader().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
},
.inherit, .ignore => {},
}
const term = child.wait() catch |err| {
- warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) });
+ warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
return err;
};
@@ -245,9 +248,9 @@ pub const RunStep = struct {
warn(
\\
\\========= Expected this stderr: =========
- \\{}
+ \\{s}
\\========= But found: ====================
- \\{}
+ \\{s}
\\
, .{ expected_bytes, stderr.? });
printCmd(cwd, argv);
@@ -259,9 +262,9 @@ pub const RunStep = struct {
warn(
\\
\\========= Expected to find in stderr: =========
- \\{}
+ \\{s}
\\========= But stderr does not contain it: =====
- \\{}
+ \\{s}
\\
, .{ match, stderr.? });
printCmd(cwd, argv);
@@ -277,9 +280,9 @@ pub const RunStep = struct {
warn(
\\
\\========= Expected this stdout: =========
- \\{}
+ \\{s}
\\========= But found: ====================
- \\{}
+ \\{s}
\\
, .{ expected_bytes, stdout.? });
printCmd(cwd, argv);
@@ -291,9 +294,9 @@ pub const RunStep = struct {
warn(
\\
\\========= Expected to find in stdout: =========
- \\{}
+ \\{s}
\\========= But stdout does not contain it: =====
- \\{}
+ \\{s}
\\
, .{ match, stdout.? });
printCmd(cwd, argv);
@@ -304,9 +307,9 @@ pub const RunStep = struct {
}
fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
- if (cwd) |yes_cwd| warn("cd {} && ", .{yes_cwd});
+ if (cwd) |yes_cwd| warn("cd {s} && ", .{yes_cwd});
for (argv) |arg| {
- warn("{} ", .{arg});
+ warn("{s} ", .{arg});
}
warn("\n", .{});
}
diff --git a/lib/std/build/translate_c.zig b/lib/std/build/translate_c.zig
index 688a7df419..4009079e3d 100644
--- a/lib/std/build/translate_c.zig
+++ b/lib/std/build/translate_c.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -57,11 +57,11 @@ pub const TranslateCStep = struct {
}
pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
- self.include_dirs.append(include_dir) catch unreachable;
+ self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
}
pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
- return CheckFileStep.create(self.builder, .{ .translate_c = self }, expected_matches);
+ return CheckFileStep.create(self.builder, .{ .translate_c = self }, self.builder.dupeStrings(expected_matches));
}
fn make(step: *Step) !void {
diff --git a/lib/std/build/write_file.zig b/lib/std/build/write_file.zig
index 923df960de..6e88aa5633 100644
--- a/lib/std/build/write_file.zig
+++ b/lib/std/build/write_file.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -32,7 +32,10 @@ pub const WriteFileStep = struct {
}
pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
- self.files.append(.{ .basename = basename, .bytes = bytes }) catch unreachable;
+ self.files.append(.{
+ .basename = self.builder.dupePath(basename),
+ .bytes = self.builder.dupe(bytes),
+ }) catch unreachable;
}
/// Unless setOutputDir was called, this function must be called only in
@@ -72,7 +75,7 @@ pub const WriteFileStep = struct {
var digest: [48]u8 = undefined;
hash.final(&digest);
var hash_basename: [64]u8 = undefined;
- fs.base64_encoder.encode(&hash_basename, &digest);
+ _ = fs.base64_encoder.encode(&hash_basename, &digest);
self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
self.builder.cache_root,
"o",
@@ -80,14 +83,14 @@ pub const WriteFileStep = struct {
});
// TODO replace with something like fs.makePathAndOpenDir
fs.cwd().makePath(self.output_dir) catch |err| {
- warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) });
+ warn("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
return err;
};
var dir = try fs.cwd().openDir(self.output_dir, .{});
defer dir.close();
for (self.files.items) |file| {
dir.writeFile(file.basename, file.bytes) catch |err| {
- warn("unable to write {} into {}: {}\n", .{
+ warn("unable to write {s} into {s}: {s}\n", .{
file.basename,
self.output_dir,
@errorName(err),
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index fa6b2ab6b4..93de8ae3b9 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -67,12 +67,12 @@ pub const StackTrace = struct {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const debug_info = std.debug.getSelfDebugInfo() catch |err| {
- return writer.print("\nUnable to print stack trace: Unable to open debug info: {}\n", .{@errorName(err)});
+ return writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});
};
const tty_config = std.debug.detectTTYConfig();
try writer.writeAll("\n");
std.debug.writeStackTrace(self, writer, &arena.allocator, debug_info, tty_config) catch |err| {
- try writer.print("Unable to print stack trace: {}\n", .{@errorName(err)});
+ try writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)});
};
try writer.writeAll("\n");
}
@@ -155,6 +155,7 @@ pub const CallingConvention = enum {
C,
Naked,
Async,
+ Inline,
Interrupt,
Signal,
Stdcall,
@@ -175,7 +176,7 @@ pub const SourceLocation = struct {
column: u32,
};
-pub const TypeId = @TagType(TypeInfo);
+pub const TypeId = std.meta.Tag(TypeInfo);
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
@@ -404,21 +405,13 @@ pub const TypeInfo = union(enum) {
/// therefore must be kept in sync with the compiler implementation.
pub const FnDecl = struct {
fn_type: type,
- inline_type: Inline,
+ is_noinline: bool,
is_var_args: bool,
is_extern: bool,
is_export: bool,
lib_name: ?[]const u8,
return_type: type,
arg_names: []const []const u8,
-
- /// This data structure is used by the Zig language code generation and
- /// therefore must be kept in sync with the compiler implementation.
- pub const Inline = enum {
- Auto,
- Always,
- Never,
- };
};
};
};
@@ -529,12 +522,12 @@ pub const Version = struct {
if (fmt.len == 0) {
if (self.patch == 0) {
if (self.minor == 0) {
- return std.fmt.format(out_stream, "{}", .{self.major});
+ return std.fmt.format(out_stream, "{d}", .{self.major});
} else {
- return std.fmt.format(out_stream, "{}.{}", .{ self.major, self.minor });
+ return std.fmt.format(out_stream, "{d}.{d}", .{ self.major, self.minor });
}
} else {
- return std.fmt.format(out_stream, "{}.{}.{}", .{ self.major, self.minor, self.patch });
+ return std.fmt.format(out_stream, "{d}.{d}.{d}", .{ self.major, self.minor, self.patch });
}
} else {
@compileError("Unknown format string: '" ++ fmt ++ "'");
@@ -683,7 +676,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
}
},
.wasi => {
- std.debug.warn("{}", .{msg});
+ std.debug.warn("{s}", .{msg});
std.os.abort();
},
.uefi => {
@@ -692,7 +685,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
},
else => {
const first_trace_addr = @returnAddress();
- std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", .{msg});
+ std.debug.panicExtra(error_return_trace, first_trace_addr, "{s}", .{msg});
},
}
}
diff --git a/lib/std/c.zig b/lib/std/c.zig
index a8ac19053d..1688824dd9 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -12,8 +12,9 @@ pub const Token = tokenizer.Token;
pub const Tokenizer = tokenizer.Tokenizer;
pub const parse = @import("c/parse.zig").parse;
pub const ast = @import("c/ast.zig");
+pub const builtins = @import("c/builtins.zig");
-test "" {
+test {
_ = tokenizer;
}
@@ -72,14 +73,15 @@ pub fn versionCheck(glibc_version: builtin.Version) type {
pub extern "c" var environ: [*:null]?[*:0]u8;
-pub extern "c" fn fopen(filename: [*:0]const u8, modes: [*:0]const u8) ?*FILE;
+pub extern "c" fn fopen(noalias filename: [*:0]const u8, noalias modes: [*:0]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 fwrite(noalias ptr: [*]const u8, size_of_type: usize, item_count: usize, noalias stream: *FILE) usize;
+pub extern "c" fn fread(noalias ptr: [*]u8, size_of_type: usize, item_count: usize, noalias stream: *FILE) usize;
pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
pub extern "c" fn abort() noreturn;
pub extern "c" fn exit(code: c_int) noreturn;
+pub extern "c" fn _exit(code: c_int) noreturn;
pub extern "c" fn isatty(fd: fd_t) c_int;
pub extern "c" fn close(fd: fd_t) c_int;
pub extern "c" fn lseek(fd: fd_t, offset: off_t, whence: c_int) off_t;
@@ -98,6 +100,8 @@ pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: u64)
pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: u64) *c_void;
pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int;
pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int;
+pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int;
+pub extern "c" fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: c_int) c_int;
pub extern "c" fn unlink(path: [*:0]const u8) c_int;
pub extern "c" fn unlinkat(dirfd: fd_t, path: [*:0]const u8, flags: c_uint) c_int;
pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
@@ -106,7 +110,6 @@ pub extern "c" fn fork() c_int;
pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int;
pub extern "c" fn faccessat(dirfd: fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int;
pub extern "c" fn pipe(fds: *[2]fd_t) c_int;
-pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;
pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;
@@ -151,9 +154,9 @@ pub extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint
pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int;
pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int;
pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int;
-pub extern "c" fn accept(sockfd: fd_t, addr: ?*sockaddr, addrlen: ?*socklen_t) c_int;
-pub extern "c" fn accept4(sockfd: fd_t, addr: ?*sockaddr, addrlen: ?*socklen_t, flags: c_uint) c_int;
-pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*c_void, optlen: *socklen_t) c_int;
+pub extern "c" fn accept(sockfd: fd_t, noalias addr: ?*sockaddr, noalias addrlen: ?*socklen_t) c_int;
+pub extern "c" fn accept4(sockfd: fd_t, noalias addr: ?*sockaddr, noalias addrlen: ?*socklen_t, flags: c_uint) c_int;
+pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, noalias optval: ?*c_void, noalias optlen: *socklen_t) c_int;
pub extern "c" fn setsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*const c_void, optlen: socklen_t) c_int;
pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize;
pub extern "c" fn sendto(
@@ -264,6 +267,22 @@ pub extern "c" fn pthread_attr_setguardsize(attr: *pthread_attr_t, guardsize: us
pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
pub extern "c" fn pthread_self() pthread_t;
pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;
+pub extern "c" fn pthread_atfork(
+ prepare: ?fn () callconv(.C) void,
+ parent: ?fn () callconv(.C) void,
+ child: ?fn () callconv(.C) void,
+) c_int;
+pub extern "c" fn pthread_key_create(key: *pthread_key_t, destructor: ?fn (value: *c_void) callconv(.C) void) c_int;
+pub extern "c" fn pthread_key_delete(key: pthread_key_t) c_int;
+pub extern "c" fn pthread_getspecific(key: pthread_key_t) ?*c_void;
+pub extern "c" fn pthread_setspecific(key: pthread_key_t, value: ?*c_void) c_int;
+pub extern "c" fn sem_init(sem: *sem_t, pshared: c_int, value: c_uint) c_int;
+pub extern "c" fn sem_destroy(sem: *sem_t) c_int;
+pub extern "c" fn sem_post(sem: *sem_t) c_int;
+pub extern "c" fn sem_wait(sem: *sem_t) c_int;
+pub extern "c" fn sem_trywait(sem: *sem_t) c_int;
+pub extern "c" fn sem_timedwait(sem: *sem_t, abs_timeout: *const timespec) c_int;
+pub extern "c" fn sem_getvalue(sem: *sem_t, sval: *c_int) c_int;
pub extern "c" fn kqueue() c_int;
pub extern "c" fn kevent(
@@ -310,6 +329,7 @@ pub extern "c" fn dn_expand(
pub const PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{};
pub extern "c" fn pthread_mutex_lock(mutex: *pthread_mutex_t) c_int;
pub extern "c" fn pthread_mutex_unlock(mutex: *pthread_mutex_t) c_int;
+pub extern "c" fn pthread_mutex_trylock(mutex: *pthread_mutex_t) c_int;
pub extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) c_int;
pub const PTHREAD_COND_INITIALIZER = pthread_cond_t{};
@@ -319,6 +339,13 @@ pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c_int;
pub extern "c" fn pthread_cond_broadcast(cond: *pthread_cond_t) c_int;
pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int;
+pub extern "c" fn pthread_rwlock_destroy(rwl: *pthread_rwlock_t) callconv(.C) c_int;
+pub extern "c" fn pthread_rwlock_rdlock(rwl: *pthread_rwlock_t) callconv(.C) c_int;
+pub extern "c" fn pthread_rwlock_wrlock(rwl: *pthread_rwlock_t) callconv(.C) c_int;
+pub extern "c" fn pthread_rwlock_tryrdlock(rwl: *pthread_rwlock_t) callconv(.C) c_int;
+pub extern "c" fn pthread_rwlock_trywrlock(rwl: *pthread_rwlock_t) callconv(.C) c_int;
+pub extern "c" fn pthread_rwlock_unlock(rwl: *pthread_rwlock_t) callconv(.C) c_int;
+
pub const pthread_t = *opaque {};
pub const FILE = opaque {};
@@ -336,6 +363,13 @@ pub extern "c" fn prctl(option: c_int, ...) c_int;
pub extern "c" fn getrlimit(resource: rlimit_resource, rlim: *rlimit) c_int;
pub extern "c" fn setrlimit(resource: rlimit_resource, rlim: *const rlimit) c_int;
+pub extern "c" fn fmemopen(noalias buf: ?*c_void, size: usize, noalias mode: [*:0]const u8) ?*FILE;
+
+pub extern "c" fn syslog(priority: c_int, message: [*:0]const u8, ...) void;
+pub extern "c" fn openlog(ident: [*:0]const u8, logopt: c_int, facility: c_int) void;
+pub extern "c" fn closelog() void;
+pub extern "c" fn setlogmask(maskpri: c_int) c_int;
+
pub const max_align_t = if (std.Target.current.abi == .msvc)
f64
else if (std.Target.current.isDarwin())
diff --git a/lib/std/c/ast.zig b/lib/std/c/ast.zig
index 274481f66e..71455c0ea3 100644
--- a/lib/std/c/ast.zig
+++ b/lib/std/c/ast.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -110,15 +110,15 @@ pub const Error = union(enum) {
pub const ExpectedToken = struct {
token: TokenIndex,
- expected_id: @TagType(Token.Id),
+ expected_id: std.meta.Tag(Token.Id),
pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
const found_token = tree.tokens.at(self.token);
if (found_token.id == .Invalid) {
- return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()});
+ return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()});
} else {
const token_name = found_token.id.symbol();
- return stream.print("expected '{}', found '{}'", .{ self.expected_id.symbol(), token_name });
+ return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name });
}
}
};
@@ -131,7 +131,7 @@ pub const Error = union(enum) {
try stream.write("invalid type specifier '");
try type_spec.spec.print(tree, stream);
const token_name = tree.tokens.at(self.token).id.symbol();
- return stream.print("{}'", .{token_name});
+ return stream.print("{s}'", .{token_name});
}
};
@@ -140,7 +140,7 @@ pub const Error = union(enum) {
name: TokenIndex,
pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
- return stream.print("must use '{}' tag to refer to type '{}'", .{ tree.slice(kw), tree.slice(name) });
+ return stream.print("must use '{s}' tag to refer to type '{s}'", .{ tree.slice(kw), tree.slice(name) });
}
};
diff --git a/lib/std/c/builtins.zig b/lib/std/c/builtins.zig
new file mode 100644
index 0000000000..c11bf0a391
--- /dev/null
+++ b/lib/std/c/builtins.zig
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+const std = @import("std");
+
+pub fn __builtin_bswap16(val: u16) callconv(.Inline) u16 {
+ return @byteSwap(u16, val);
+}
+pub fn __builtin_bswap32(val: u32) callconv(.Inline) u32 {
+ return @byteSwap(u32, val);
+}
+pub fn __builtin_bswap64(val: u64) callconv(.Inline) u64 {
+ return @byteSwap(u64, val);
+}
+
+pub fn __builtin_signbit(val: f64) callconv(.Inline) c_int {
+ return @boolToInt(std.math.signbit(val));
+}
+pub fn __builtin_signbitf(val: f32) callconv(.Inline) c_int {
+ return @boolToInt(std.math.signbit(val));
+}
+
+pub fn __builtin_popcount(val: c_uint) callconv(.Inline) c_int {
+ // popcount of a c_uint will never exceed the capacity of a c_int
+ @setRuntimeSafety(false);
+ return @bitCast(c_int, @as(c_uint, @popCount(c_uint, val)));
+}
+pub fn __builtin_ctz(val: c_uint) callconv(.Inline) c_int {
+ // Returns the number of trailing 0-bits in val, starting at the least significant bit position.
+ // In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint
+ @setRuntimeSafety(false);
+ return @bitCast(c_int, @as(c_uint, @ctz(c_uint, val)));
+}
+pub fn __builtin_clz(val: c_uint) callconv(.Inline) c_int {
+ // Returns the number of leading 0-bits in x, starting at the most significant bit position.
+ // In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint
+ @setRuntimeSafety(false);
+ return @bitCast(c_int, @as(c_uint, @clz(c_uint, val)));
+}
+
+pub fn __builtin_sqrt(val: f64) callconv(.Inline) f64 {
+ return @sqrt(val);
+}
+pub fn __builtin_sqrtf(val: f32) callconv(.Inline) f32 {
+ return @sqrt(val);
+}
+
+pub fn __builtin_sin(val: f64) callconv(.Inline) f64 {
+ return @sin(val);
+}
+pub fn __builtin_sinf(val: f32) callconv(.Inline) f32 {
+ return @sin(val);
+}
+pub fn __builtin_cos(val: f64) callconv(.Inline) f64 {
+ return @cos(val);
+}
+pub fn __builtin_cosf(val: f32) callconv(.Inline) f32 {
+ return @cos(val);
+}
+
+pub fn __builtin_exp(val: f64) callconv(.Inline) f64 {
+ return @exp(val);
+}
+pub fn __builtin_expf(val: f32) callconv(.Inline) f32 {
+ return @exp(val);
+}
+pub fn __builtin_exp2(val: f64) callconv(.Inline) f64 {
+ return @exp2(val);
+}
+pub fn __builtin_exp2f(val: f32) callconv(.Inline) f32 {
+ return @exp2(val);
+}
+pub fn __builtin_log(val: f64) callconv(.Inline) f64 {
+ return @log(val);
+}
+pub fn __builtin_logf(val: f32) callconv(.Inline) f32 {
+ return @log(val);
+}
+pub fn __builtin_log2(val: f64) callconv(.Inline) f64 {
+ return @log2(val);
+}
+pub fn __builtin_log2f(val: f32) callconv(.Inline) f32 {
+ return @log2(val);
+}
+pub fn __builtin_log10(val: f64) callconv(.Inline) f64 {
+ return @log10(val);
+}
+pub fn __builtin_log10f(val: f32) callconv(.Inline) f32 {
+ return @log10(val);
+}
+
+// Standard C Library bug: The absolute value of the most negative integer remains negative.
+pub fn __builtin_abs(val: c_int) callconv(.Inline) c_int {
+ return std.math.absInt(val) catch std.math.minInt(c_int);
+}
+pub fn __builtin_fabs(val: f64) callconv(.Inline) f64 {
+ return @fabs(val);
+}
+pub fn __builtin_fabsf(val: f32) callconv(.Inline) f32 {
+ return @fabs(val);
+}
+
+pub fn __builtin_floor(val: f64) callconv(.Inline) f64 {
+ return @floor(val);
+}
+pub fn __builtin_floorf(val: f32) callconv(.Inline) f32 {
+ return @floor(val);
+}
+pub fn __builtin_ceil(val: f64) callconv(.Inline) f64 {
+ return @ceil(val);
+}
+pub fn __builtin_ceilf(val: f32) callconv(.Inline) f32 {
+ return @ceil(val);
+}
+pub fn __builtin_trunc(val: f64) callconv(.Inline) f64 {
+ return @trunc(val);
+}
+pub fn __builtin_truncf(val: f32) callconv(.Inline) f32 {
+ return @trunc(val);
+}
+pub fn __builtin_round(val: f64) callconv(.Inline) f64 {
+ return @round(val);
+}
+pub fn __builtin_roundf(val: f32) callconv(.Inline) f32 {
+ return @round(val);
+}
+
+pub fn __builtin_strlen(s: [*c]const u8) callconv(.Inline) usize {
+ return std.mem.lenZ(s);
+}
+pub fn __builtin_strcmp(s1: [*c]const u8, s2: [*c]const u8) callconv(.Inline) c_int {
+ return @as(c_int, std.cstr.cmp(s1, s2));
+}
+
+pub fn __builtin_object_size(ptr: ?*const c_void, ty: c_int) callconv(.Inline) usize {
+ // clang semantics match gcc's: https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html
+ // If it is not possible to determine which objects ptr points to at compile time,
+ // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0
+ // for type 2 or 3.
+ if (ty == 0 or ty == 1) return @bitCast(usize, -@as(c_long, 1));
+ if (ty == 2 or ty == 3) return 0;
+ unreachable;
+}
+
+pub fn __builtin___memset_chk(
+ dst: ?*c_void,
+ val: c_int,
+ len: usize,
+ remaining: usize,
+) callconv(.Inline) ?*c_void {
+ if (len > remaining) @panic("std.c.builtins.memset_chk called with len > remaining");
+ return __builtin_memset(dst, val, len);
+}
+
+pub fn __builtin_memset(dst: ?*c_void, val: c_int, len: usize) callconv(.Inline) ?*c_void {
+ const dst_cast = @ptrCast([*c]u8, dst);
+ @memset(dst_cast, @bitCast(u8, @truncate(i8, val)), len);
+ return dst;
+}
+
+pub fn __builtin___memcpy_chk(
+ noalias dst: ?*c_void,
+ noalias src: ?*const c_void,
+ len: usize,
+ remaining: usize,
+) callconv(.Inline) ?*c_void {
+ if (len > remaining) @panic("std.c.builtins.memcpy_chk called with len > remaining");
+ return __builtin_memcpy(dst, src, len);
+}
+
+pub fn __builtin_memcpy(
+ noalias dst: ?*c_void,
+ noalias src: ?*const c_void,
+ len: usize,
+) callconv(.Inline) ?*c_void {
+ const dst_cast = @ptrCast([*c]u8, dst);
+ const src_cast = @ptrCast([*c]const u8, src);
+
+ @memcpy(dst_cast, src_cast, len);
+ return dst;
+}
diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig
index 635e0f97d4..b5c3fbf977 100644
--- a/lib/std/c/darwin.zig
+++ b/lib/std/c/darwin.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -177,6 +177,11 @@ pub const pthread_cond_t = extern struct {
__sig: c_long = 0x3CB0B1BB,
__opaque: [__PTHREAD_COND_SIZE__]u8 = [_]u8{0} ** __PTHREAD_COND_SIZE__,
};
+pub const pthread_rwlock_t = extern struct {
+ __sig: c_long = 0x2DA8B3B4,
+ __opaque: [192]u8 = [_]u8{0} ** 192,
+};
+pub const sem_t = c_int;
const __PTHREAD_MUTEX_SIZE__ = if (@sizeOf(usize) == 8) 56 else 40;
const __PTHREAD_COND_SIZE__ = if (@sizeOf(usize) == 8) 40 else 24;
@@ -185,4 +190,19 @@ pub const pthread_attr_t = extern struct {
__opaque: [56]u8,
};
+const pthread_t = std.c.pthread_t;
+pub extern "c" fn pthread_threadid_np(thread: ?pthread_t, thread_id: *u64) c_int;
+
pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void;
+
+// Grand Central Dispatch is exposed by libSystem.
+pub const dispatch_semaphore_t = *opaque {};
+pub const dispatch_time_t = u64;
+pub const DISPATCH_TIME_NOW = @as(dispatch_time_t, 0);
+pub const DISPATCH_TIME_FOREVER = ~@as(dispatch_time_t, 0);
+pub extern "c" fn dispatch_semaphore_create(value: isize) ?dispatch_semaphore_t;
+pub extern "c" fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) isize;
+pub extern "c" fn dispatch_semaphore_signal(dsema: dispatch_semaphore_t) isize;
+
+pub extern "c" fn dispatch_release(object: *c_void) void;
+pub extern "c" fn dispatch_time(when: dispatch_time_t, delta: i64) dispatch_time_t;
diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig
index 3261d34b78..4e6650094b 100644
--- a/lib/std/c/dragonfly.zig
+++ b/lib/std/c/dragonfly.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -13,6 +13,7 @@ pub fn _errno() *c_int {
pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
+pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
pub const dl_iterate_phdr_callback = fn (info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int;
pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;
diff --git a/lib/std/c/emscripten.zig b/lib/std/c/emscripten.zig
index e94a6f1004..526eb9e99c 100644
--- a/lib/std/c/emscripten.zig
+++ b/lib/std/c/emscripten.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -9,5 +9,8 @@ pub const pthread_mutex_t = extern struct {
pub const pthread_cond_t = extern struct {
size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T,
};
+pub const pthread_rwlock_t = extern struct {
+ size: [32]u8 align(4) = [_]u8{0} ** 32,
+};
const __SIZEOF_PTHREAD_COND_T = 48;
const __SIZEOF_PTHREAD_MUTEX_T = 28;
diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig
index 8fa78b0d6f..795b36dc68 100644
--- a/lib/std/c/freebsd.zig
+++ b/lib/std/c/freebsd.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -13,6 +13,9 @@ pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
+pub extern "c" fn pthread_getthreadid_np() c_int;
+pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
+
pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
pub extern "c" fn malloc_usable_size(?*const c_void) usize;
@@ -41,12 +44,24 @@ pub const pthread_mutex_t = extern struct {
pub const pthread_cond_t = extern struct {
inner: ?*c_void = null,
};
+pub const pthread_rwlock_t = extern struct {
+ ptr: ?*c_void = null,
+};
pub const pthread_attr_t = extern struct {
__size: [56]u8,
__align: c_long,
};
+pub const sem_t = extern struct {
+ _magic: u32,
+ _kern: extern struct {
+ _count: u32,
+ _flags: u32,
+ },
+ _padding: u32,
+};
+
pub const EAI = extern enum(c_int) {
/// address family for hostname not supported
ADDRFAMILY = 1,
diff --git a/lib/std/c/fuchsia.zig b/lib/std/c/fuchsia.zig
index ceeb34a763..fc34f49d22 100644
--- a/lib/std/c/fuchsia.zig
+++ b/lib/std/c/fuchsia.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -9,5 +9,8 @@ pub const pthread_mutex_t = extern struct {
pub const pthread_cond_t = extern struct {
size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T,
};
+pub const pthread_rwlock_t = extern struct {
+ size: [56]u8 align(@alignOf(usize)) = [_]u8{0} ** 56,
+};
const __SIZEOF_PTHREAD_COND_T = 48;
const __SIZEOF_PTHREAD_MUTEX_T = 40;
diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig
index 6b56e163c8..e361a7520e 100644
--- a/lib/std/c/haiku.zig
+++ b/lib/std/c/haiku.zig
@@ -1,8 +1,51 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
+
+//
+const std = @import("../std.zig");
+const builtin = std.builtin;
+
+usingnamespace std.c;
+
+extern "c" fn _errnop() *c_int;
+
+pub const _errno = _errnop;
+
+pub extern "c" fn find_directory(which: c_int, volume: i32, createIt: bool, path_ptr: [*]u8, length: i32) u64;
+
+pub extern "c" fn find_thread(thread_name: ?*c_void) i32;
+
+pub extern "c" fn get_system_info(system_info: *system_info) usize;
+
+// TODO revisit if abi changes or better option becomes apparent
+pub extern "c" fn _get_next_image_info(team: c_int, cookie: *i32, image_info: *image_info) usize;
+
+pub extern "c" fn _kern_read_dir(fd: c_int, buf_ptr: [*]u8, nbytes: usize, maxcount: u32) usize;
+
+pub extern "c" fn _kern_read_stat(fd: c_int, path_ptr: [*]u8, traverse_link: bool, libc_stat: *libc_stat, stat_size: i32) usize;
+
+pub extern "c" fn _kern_get_current_team() i32;
+
+pub const sem_t = extern struct {
+ _magic: u32,
+ _kern: extern struct {
+ _count: u32,
+ _flags: u32,
+ },
+ _padding: u32,
+};
+
+pub const pthread_attr_t = extern struct {
+ __detach_state: i32,
+ __sched_priority: i32,
+ __stack_size: i32,
+ __guard_size: i32,
+ __stack_address: ?*c_void,
+};
+
pub const pthread_mutex_t = extern struct {
flags: u32 = 0,
lock: i32 = 0,
@@ -17,3 +60,12 @@ pub const pthread_cond_t = extern struct {
waiter_count: i32 = 0,
lock: i32 = 0,
};
+pub const pthread_rwlock_t = extern struct {
+ flags: u32 = 0,
+ owner: i32 = -1,
+ lock_sem: i32 = 0,
+ lock_count: i32 = 0,
+ reader_count: i32 = 0,
+ writer_count: i32 = 0,
+ waiters: [2]?*c_void = [_]?*c_void{ null, null },
+};
diff --git a/lib/std/c/hermit.zig b/lib/std/c/hermit.zig
index 6762e60962..a159395ab3 100644
--- a/lib/std/c/hermit.zig
+++ b/lib/std/c/hermit.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -9,3 +9,6 @@ pub const pthread_mutex_t = extern struct {
pub const pthread_cond_t = extern struct {
inner: usize = ~@as(usize, 0),
};
+pub const pthread_rwlock_t = extern struct {
+ ptr: usize = std.math.maxInt(usize),
+};
diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig
index 21124d1030..d2018f6f79 100644
--- a/lib/std/c/linux.zig
+++ b/lib/std/c/linux.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -86,6 +86,7 @@ pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_
pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
pub extern "c" fn memfd_create(name: [*:0]const u8, flags: c_uint) c_int;
+pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
pub extern "c" fn fallocate(fd: fd_t, mode: c_int, offset: off_t, len: off_t) c_int;
@@ -106,6 +107,12 @@ pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *con
pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
pub extern "c" fn malloc_usable_size(?*const c_void) usize;
+pub extern "c" fn madvise(
+ addr: *align(std.mem.page_size) c_void,
+ length: usize,
+ advice: c_uint,
+) c_int;
+
pub const pthread_attr_t = extern struct {
__size: [56]u8,
__align: c_long,
@@ -117,6 +124,36 @@ pub const pthread_mutex_t = extern struct {
pub const pthread_cond_t = extern struct {
size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T,
};
+pub const pthread_rwlock_t = switch (std.builtin.abi) {
+ .android => switch (@sizeOf(usize)) {
+ 4 => extern struct {
+ lock: std.c.pthread_mutex_t = std.c.PTHREAD_MUTEX_INITIALIZER,
+ cond: std.c.pthread_cond_t = std.c.PTHREAD_COND_INITIALIZER,
+ numLocks: c_int = 0,
+ writerThreadId: c_int = 0,
+ pendingReaders: c_int = 0,
+ pendingWriters: c_int = 0,
+ attr: i32 = 0,
+ __reserved: [12]u8 = [_]u8{0} ** 2,
+ },
+ 8 => extern struct {
+ numLocks: c_int = 0,
+ writerThreadId: c_int = 0,
+ pendingReaders: c_int = 0,
+ pendingWriters: c_int = 0,
+ attr: i32 = 0,
+ __reserved: [36]u8 = [_]u8{0} ** 36,
+ },
+ else => unreachable,
+ },
+ else => extern struct {
+ size: [56]u8 align(@alignOf(usize)) = [_]u8{0} ** 56,
+ },
+};
+pub const sem_t = extern struct {
+ __size: [__SIZEOF_SEM_T]u8 align(@alignOf(usize)),
+};
+
const __SIZEOF_PTHREAD_COND_T = 48;
const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os.tag == .fuchsia) 40 else switch (builtin.abi) {
.musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24,
@@ -128,6 +165,7 @@ const __SIZEOF_PTHREAD_MUTEX_T = if (builtin.os.tag == .fuchsia) 40 else switch
},
else => unreachable,
};
+const __SIZEOF_SEM_T = 4 * @sizeOf(usize);
pub const RTLD_LAZY = 1;
pub const RTLD_NOW = 2;
diff --git a/lib/std/c/minix.zig b/lib/std/c/minix.zig
index 2bc1bac47a..6cf5684079 100644
--- a/lib/std/c/minix.zig
+++ b/lib/std/c/minix.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig
index e6d7e86bee..7169095197 100644
--- a/lib/std/c/netbsd.zig
+++ b/lib/std/c/netbsd.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -14,6 +14,9 @@ pub const _errno = __errno;
pub const dl_iterate_phdr_callback = fn (info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int;
pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;
+pub extern "c" fn _lwp_self() lwpid_t;
+
+pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void;
pub extern "c" fn __fstat50(fd: fd_t, buf: *Stat) c_int;
pub extern "c" fn __stat50(path: [*:0]const u8, buf: *Stat) c_int;
@@ -52,6 +55,22 @@ pub const pthread_cond_t = extern struct {
ptc_private: ?*c_void = null,
};
+pub const pthread_rwlock_t = extern struct {
+ ptr_magic: c_uint = 0x99990009,
+ ptr_interlock: switch (std.builtin.arch) {
+ .aarch64, .sparc, .x86_64, .i386 => u8,
+ .arm, .powerpc => c_int,
+ else => unreachable,
+ } = 0,
+ ptr_rblocked_first: ?*u8 = null,
+ ptr_rblocked_last: ?*u8 = null,
+ ptr_wblocked_first: ?*u8 = null,
+ ptr_wblocked_last: ?*u8 = null,
+ ptr_nreaders: c_uint = 0,
+ ptr_owner: std.c.pthread_t = null,
+ ptr_private: ?*c_void = null,
+};
+
const pthread_spin_t = switch (builtin.arch) {
.aarch64, .aarch64_be, .aarch64_32 => u8,
.mips, .mipsel, .mips64, .mips64el => u32,
diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig
index ab193abb6b..cac3df867d 100644
--- a/lib/std/c/openbsd.zig
+++ b/lib/std/c/openbsd.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -16,6 +16,9 @@ pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_
pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void;
+pub extern "c" fn getthrid() pid_t;
+pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
+
pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
@@ -25,12 +28,20 @@ pub const pthread_mutex_t = extern struct {
pub const pthread_cond_t = extern struct {
inner: ?*c_void = null,
};
+pub const pthread_rwlock_t = extern struct {
+ ptr: ?*c_void = null,
+};
pub const pthread_spinlock_t = extern struct {
inner: ?*c_void = null,
};
-
pub const pthread_attr_t = extern struct {
inner: ?*c_void = null,
};
+pub const pthread_key_t = c_int;
+
+pub const sem_t = ?*opaque {};
pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
+
+pub extern "c" fn pledge(promises: ?[*:0]const u8, execpromises: ?[*:0]const u8) c_int;
+pub extern "c" fn unveil(path: ?[*:0]const u8, permissions: ?[*:0]const u8) c_int;
diff --git a/lib/std/c/parse.zig b/lib/std/c/parse.zig
index d5b1a4a01e..29d4ba2fe1 100644
--- a/lib/std/c/parse.zig
+++ b/lib/std/c/parse.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -26,7 +26,7 @@ pub const Options = struct {
None,
/// Some warnings are errors
- Some: []@TagType(ast.Error),
+ Some: []std.meta.Tag(ast.Error),
/// All warnings are errors
All,
@@ -300,8 +300,7 @@ const Parser = struct {
try node.initializers.push((try parser.initializer(dr)) orelse return parser.err(.{
.ExpectedInitializer = .{ .token = parser.it.index },
}));
- } else
- try node.initializers.push(&dr.base);
+ } else try node.initializers.push(&dr.base);
if (parser.eatToken(.Comma) != null) break;
dr = @fieldParentPtr(Node.Declarator, "base", (try parser.declarator(.Must)) orelse return parser.err(.{
.ExpectedDeclarator = .{ .token = parser.it.index },
@@ -1363,7 +1362,7 @@ const Parser = struct {
return &node.base;
}
- fn eatToken(parser: *Parser, id: @TagType(Token.Id)) ?TokenIndex {
+ fn eatToken(parser: *Parser, id: std.meta.Tag(Token.Id)) ?TokenIndex {
while (true) {
switch ((parser.it.next() orelse return null).id) {
.LineComment, .MultiLineComment, .Nl => continue,
@@ -1377,7 +1376,7 @@ const Parser = struct {
}
}
- fn expectToken(parser: *Parser, id: @TagType(Token.Id)) Error!TokenIndex {
+ fn expectToken(parser: *Parser, id: std.meta.Tag(Token.Id)) Error!TokenIndex {
while (true) {
switch ((parser.it.next() orelse return error.ParseError).id) {
.LineComment, .MultiLineComment, .Nl => continue,
diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig
index 49ce0886f7..ed043018d0 100644
--- a/lib/std/c/solaris.zig
+++ b/lib/std/c/solaris.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/c/tokenizer.zig b/lib/std/c/tokenizer.zig
index 9e9b5f4147..2e1969e269 100644
--- a/lib/std/c/tokenizer.zig
+++ b/lib/std/c/tokenizer.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -131,7 +131,7 @@ pub const Token = struct {
Keyword_error,
Keyword_pragma,
- pub fn symbol(id: @TagType(Id)) []const u8 {
+ pub fn symbol(id: std.meta.TagType(Id)) []const u8 {
return switch (id) {
.Invalid => "Invalid",
.Eof => "Eof",
@@ -347,7 +347,7 @@ pub const Token = struct {
pub const Tokenizer = struct {
buffer: []const u8,
index: usize = 0,
- prev_tok_id: @TagType(Token.Id) = .Invalid,
+ prev_tok_id: std.meta.TagType(Token.Id) = .Invalid,
pp_directive: bool = false,
pub fn next(self: *Tokenizer) Token {
@@ -446,7 +446,7 @@ pub const Tokenizer = struct {
'L' => {
state = .L;
},
- 'a'...'t', 'v'...'z', 'A'...'K', 'M'...'T', 'V'...'Z', '_' => {
+ 'a'...'t', 'v'...'z', 'A'...'K', 'M'...'T', 'V'...'Z', '_', '$' => {
state = .Identifier;
},
'=' => {
@@ -776,7 +776,7 @@ pub const Tokenizer = struct {
},
},
.Identifier => switch (c) {
- 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
+ 'a'...'z', 'A'...'Z', '_', '0'...'9', '$' => {},
else => {
result.id = Token.getKeyword(self.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;
if (self.prev_tok_id == .Hash)
@@ -1552,7 +1552,7 @@ fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) void {
for (expected_tokens) |expected_token_id| {
const token = tokenizer.next();
if (!std.meta.eql(token.id, expected_token_id)) {
- std.debug.panic("expected {}, found {}\n", .{ @tagName(expected_token_id), @tagName(token.id) });
+ std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) });
}
}
const last_token = tokenizer.next();
diff --git a/lib/std/c/windows.zig b/lib/std/c/windows.zig
index f96da56c1f..bed2e421ff 100644
--- a/lib/std/c/windows.zig
+++ b/lib/std/c/windows.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig
index b61fe9470d..d37dd9fdf5 100644
--- a/lib/std/child_process.zig
+++ b/lib/std/child_process.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -15,11 +15,11 @@ const windows = os.windows;
const mem = std.mem;
const debug = std.debug;
const BufMap = std.BufMap;
-const ArrayListSentineled = std.ArrayListSentineled;
const builtin = @import("builtin");
const Os = builtin.Os;
const TailQueue = std.TailQueue;
const maxInt = std.math.maxInt;
+const assert = std.debug.assert;
pub const ChildProcess = struct {
pid: if (builtin.os.tag == .windows) void else i32,
@@ -186,6 +186,58 @@ pub const ChildProcess = struct {
pub const exec2 = @compileError("deprecated: exec2 is renamed to exec");
+ fn collectOutputPosix(
+ child: *const ChildProcess,
+ stdout: *std.ArrayList(u8),
+ stderr: *std.ArrayList(u8),
+ max_output_bytes: usize,
+ ) !void {
+ var poll_fds = [_]os.pollfd{
+ .{ .fd = child.stdout.?.handle, .events = os.POLLIN, .revents = undefined },
+ .{ .fd = child.stderr.?.handle, .events = os.POLLIN, .revents = undefined },
+ };
+
+ var dead_fds: usize = 0;
+ // We ask for ensureCapacity with this much extra space. This has more of an
+ // effect on small reads because once the reads start to get larger the amount
+ // of space an ArrayList will allocate grows exponentially.
+ const bump_amt = 512;
+
+ while (dead_fds < poll_fds.len) {
+ const events = try os.poll(&poll_fds, std.math.maxInt(i32));
+ if (events == 0) continue;
+
+ // Try reading whatever is available before checking the error
+ // conditions.
+ if (poll_fds[0].revents & os.POLLIN != 0) {
+ // stdout is ready.
+ const new_capacity = std.math.min(stdout.items.len + bump_amt, max_output_bytes);
+ try stdout.ensureCapacity(new_capacity);
+ const buf = stdout.unusedCapacitySlice();
+ if (buf.len == 0) return error.StdoutStreamTooLong;
+ stdout.items.len += try os.read(poll_fds[0].fd, buf);
+ }
+ if (poll_fds[1].revents & os.POLLIN != 0) {
+ // stderr is ready.
+ const new_capacity = std.math.min(stderr.items.len + bump_amt, max_output_bytes);
+ try stderr.ensureCapacity(new_capacity);
+ const buf = stderr.unusedCapacitySlice();
+ if (buf.len == 0) return error.StderrStreamTooLong;
+ stderr.items.len += try os.read(poll_fds[1].fd, buf);
+ }
+
+ // Exclude the fds that signaled an error.
+ if (poll_fds[0].revents & (os.POLLERR | os.POLLNVAL | os.POLLHUP) != 0) {
+ poll_fds[0].fd = -1;
+ dead_fds += 1;
+ }
+ if (poll_fds[1].revents & (os.POLLERR | os.POLLNVAL | os.POLLHUP) != 0) {
+ poll_fds[1].fd = -1;
+ dead_fds += 1;
+ }
+ }
+ }
+
/// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
/// If it succeeds, the caller owns result.stdout and result.stderr memory.
pub fn exec(args: struct {
@@ -210,19 +262,33 @@ pub const ChildProcess = struct {
try child.spawn();
- const stdout_in = child.stdout.?.reader();
- const stderr_in = child.stderr.?.reader();
+ // TODO collect output in a deadlock-avoiding way on Windows.
+ // https://github.com/ziglang/zig/issues/6343
+ if (builtin.os.tag == .windows) {
+ const stdout_in = child.stdout.?.reader();
+ const stderr_in = child.stderr.?.reader();
+
+ const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes);
+ errdefer args.allocator.free(stdout);
+ const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes);
+ errdefer args.allocator.free(stderr);
+
+ return ExecResult{
+ .term = try child.wait(),
+ .stdout = stdout,
+ .stderr = stderr,
+ };
+ }
+
+ var stdout = std.ArrayList(u8).init(args.allocator);
+ var stderr = std.ArrayList(u8).init(args.allocator);
- // TODO https://github.com/ziglang/zig/issues/6343
- const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes);
- errdefer args.allocator.free(stdout);
- const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes);
- errdefer args.allocator.free(stderr);
+ try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes);
return ExecResult{
.term = try child.wait(),
- .stdout = stdout,
- .stderr = stderr,
+ .stdout = stdout.toOwnedSlice(),
+ .stderr = stderr.toOwnedSlice(),
};
}
@@ -377,19 +443,37 @@ pub const ChildProcess = struct {
if (any_ignore) os.close(dev_null_fd);
}
- var env_map_owned: BufMap = undefined;
- var we_own_env_map: bool = undefined;
- const env_map = if (self.env_map) |env_map| x: {
- we_own_env_map = false;
- break :x env_map;
- } else x: {
- we_own_env_map = true;
- env_map_owned = try process.getEnvMap(self.allocator);
- break :x &env_map_owned;
+ var arena_allocator = std.heap.ArenaAllocator.init(self.allocator);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ // The POSIX standard does not allow malloc() between fork() and execve(),
+ // and `self.allocator` may be a libc allocator.
+ // I have personally observed the child process deadlocking when it tries
+ // to call malloc() due to a heap allocation between fork() and execve(),
+ // in musl v1.1.24.
+ // Additionally, we want to reduce the number of possible ways things
+ // can fail between fork() and execve().
+ // Therefore, we do all the allocation for the execve() before the fork().
+ // This means we must do the null-termination of argv and env vars here.
+ const argv_buf = try arena.allocSentinel(?[*:0]u8, self.argv.len, null);
+ for (self.argv) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
+
+ const envp = m: {
+ if (self.env_map) |env_map| {
+ const envp_buf = try createNullDelimitedEnvMap(arena, env_map);
+ break :m envp_buf.ptr;
+ } else if (std.builtin.link_libc) {
+ break :m std.c.environ;
+ } else if (std.builtin.output_mode == .Exe) {
+ // Then we have Zig start code and this works.
+ // TODO type-safety for null-termination of `os.environ`.
+ break :m @ptrCast([*:null]?[*:0]u8, os.environ.ptr);
+ } else {
+ // TODO come up with a solution for this.
+ @compileError("missing std lib enhancement: ChildProcess implementation has no way to collect the environment variables to forward to the child process");
+ }
};
- defer {
- if (we_own_env_map) env_map_owned.deinit();
- }
// This pipe is used to communicate errors between the time of fork
// and execve from the child process to the parent process.
@@ -439,7 +523,10 @@ pub const ChildProcess = struct {
os.setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err);
}
- const err = os.execvpe_expandArg0(self.allocator, self.expand_arg0, self.argv, env_map);
+ const err = switch (self.expand_arg0) {
+ .expand => os.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
+ .no_expand => os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
+ };
forkChildErrReport(err_pipe[1], err);
}
@@ -749,38 +836,37 @@ fn windowsCreateProcess(app_name: [*:0]u16, cmd_line: [*:0]u16, envp_ptr: ?[*]u1
/// Caller must dealloc.
fn windowsCreateCommandLine(allocator: *mem.Allocator, argv: []const []const u8) ![:0]u8 {
- var buf = try ArrayListSentineled(u8, 0).initSize(allocator, 0);
+ var buf = std.ArrayList(u8).init(allocator);
defer buf.deinit();
- const buf_stream = buf.outStream();
for (argv) |arg, arg_i| {
- if (arg_i != 0) try buf_stream.writeByte(' ');
+ if (arg_i != 0) try buf.append(' ');
if (mem.indexOfAny(u8, arg, " \t\n\"") == null) {
- try buf_stream.writeAll(arg);
+ try buf.appendSlice(arg);
continue;
}
- try buf_stream.writeByte('"');
+ try buf.append('"');
var backslash_count: usize = 0;
for (arg) |byte| {
switch (byte) {
'\\' => backslash_count += 1,
'"' => {
- try buf_stream.writeByteNTimes('\\', backslash_count * 2 + 1);
- try buf_stream.writeByte('"');
+ try buf.appendNTimes('\\', backslash_count * 2 + 1);
+ try buf.append('"');
backslash_count = 0;
},
else => {
- try buf_stream.writeByteNTimes('\\', backslash_count);
- try buf_stream.writeByte(byte);
+ try buf.appendNTimes('\\', backslash_count);
+ try buf.append(byte);
backslash_count = 0;
},
}
}
- try buf_stream.writeByteNTimes('\\', backslash_count * 2);
- try buf_stream.writeByte('"');
+ try buf.appendNTimes('\\', backslash_count * 2);
+ try buf.append('"');
}
- return buf.toOwnedSlice();
+ return buf.toOwnedSliceSentinel(0);
}
fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {
@@ -821,8 +907,9 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
// which we really do not want to run in the fork child. I caught LLVM doing this and
// it caused a deadlock instead of doing an exit syscall. In the words of Avril Lavigne,
// "Why'd you have to go and make things so complicated?"
- if (std.Target.current.os.tag == .linux) {
- std.os.linux.exit(1); // By-pass libc regardless of whether it is linked.
+ if (builtin.link_libc) {
+ // The _exit(2) function does nothing but make the exit syscall, unlike exit(3)
+ std.c._exit(1);
}
os.exit(1);
}
@@ -835,7 +922,7 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {
.capable_io_mode = .blocking,
.intended_io_mode = .blocking,
};
- file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
+ file.writer().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources;
}
fn readIntFd(fd: i32) !ErrInt {
@@ -883,3 +970,54 @@ pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap)
i += 1;
return allocator.shrink(result, i);
}
+
+pub fn createNullDelimitedEnvMap(arena: *mem.Allocator, env_map: *const std.BufMap) ![:null]?[*:0]u8 {
+ const envp_count = env_map.count();
+ const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null);
+ {
+ var it = env_map.iterator();
+ var i: usize = 0;
+ while (it.next()) |pair| : (i += 1) {
+ const env_buf = try arena.allocSentinel(u8, pair.key.len + pair.value.len + 1, 0);
+ mem.copy(u8, env_buf, pair.key);
+ env_buf[pair.key.len] = '=';
+ mem.copy(u8, env_buf[pair.key.len + 1 ..], pair.value);
+ envp_buf[i] = env_buf.ptr;
+ }
+ assert(i == envp_count);
+ }
+ return envp_buf;
+}
+
+test "createNullDelimitedEnvMap" {
+ const testing = std.testing;
+ const allocator = testing.allocator;
+ var envmap = BufMap.init(allocator);
+ defer envmap.deinit();
+
+ try envmap.set("HOME", "/home/ifreund");
+ try envmap.set("WAYLAND_DISPLAY", "wayland-1");
+ try envmap.set("DISPLAY", ":1");
+ try envmap.set("DEBUGINFOD_URLS", " ");
+ try envmap.set("XCURSOR_SIZE", "24");
+
+ var arena = std.heap.ArenaAllocator.init(allocator);
+ defer arena.deinit();
+ const environ = try createNullDelimitedEnvMap(&arena.allocator, &envmap);
+
+ testing.expectEqual(@as(usize, 5), environ.len);
+
+ inline for (.{
+ "HOME=/home/ifreund",
+ "WAYLAND_DISPLAY=wayland-1",
+ "DISPLAY=:1",
+ "DEBUGINFOD_URLS= ",
+ "XCURSOR_SIZE=24",
+ }) |target| {
+ for (environ) |variable| {
+ if (mem.eql(u8, mem.span(variable orelse continue), target)) break;
+ } else {
+ testing.expect(false); // Environment variable not found
+ }
+ }
+}
diff --git a/lib/std/coff.zig b/lib/std/coff.zig
index 1fdf3f8893..edeff89cc5 100644
--- a/lib/std/coff.zig
+++ b/lib/std/coff.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -127,7 +127,7 @@ pub const Coff = struct {
pub fn loadHeader(self: *Coff) !void {
const pe_pointer_offset = 0x3C;
- const in = self.in_file.inStream();
+ const in = self.in_file.reader();
var magic: [2]u8 = undefined;
try in.readNoEof(magic[0..]);
@@ -163,7 +163,7 @@ pub const Coff = struct {
}
fn loadOptionalHeader(self: *Coff) !void {
- const in = self.in_file.inStream();
+ const in = self.in_file.reader();
self.pe_header.magic = try in.readIntLittle(u16);
// For now we're only interested in finding the reference to the .pdb,
// so we'll skip most of this header, which size is different in 32
@@ -173,8 +173,7 @@ pub const Coff = struct {
skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32);
} else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64);
- } else
- return error.InvalidPEMagic;
+ } else return error.InvalidPEMagic;
try self.in_file.seekBy(skip_size);
@@ -206,7 +205,7 @@ pub const Coff = struct {
const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
- const in = self.in_file.inStream();
+ const in = self.in_file.reader();
try self.in_file.seekTo(file_offset);
// Find the correct DebugDirectoryEntry, and where its data is stored.
@@ -257,7 +256,7 @@ pub const Coff = struct {
try self.sections.ensureCapacity(self.coff_header.number_of_sections);
- const in = self.in_file.inStream();
+ const in = self.in_file.reader();
var name: [8]u8 = undefined;
diff --git a/lib/std/compress.zig b/lib/std/compress.zig
index 95f496021e..972031c182 100644
--- a/lib/std/compress.zig
+++ b/lib/std/compress.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -9,7 +9,7 @@ pub const deflate = @import("compress/deflate.zig");
pub const gzip = @import("compress/gzip.zig");
pub const zlib = @import("compress/zlib.zig");
-test "" {
+test {
_ = gzip;
_ = zlib;
}
diff --git a/lib/std/compress/deflate.zig b/lib/std/compress/deflate.zig
index addd1b1a27..e680dc9e6f 100644
--- a/lib/std/compress/deflate.zig
+++ b/lib/std/compress/deflate.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -209,7 +209,7 @@ pub fn InflateStream(comptime ReaderType: type) type {
// Insert a single byte into the window.
// Assumes there's enough space.
- inline fn appendUnsafe(self: *WSelf, value: u8) void {
+ fn appendUnsafe(self: *WSelf, value: u8) callconv(.Inline) void {
self.buf[self.wi] = value;
self.wi = (self.wi + 1) & (self.buf.len - 1);
self.el += 1;
diff --git a/lib/std/compress/gzip.zig b/lib/std/compress/gzip.zig
index aad1731393..89aa12207b 100644
--- a/lib/std/compress/gzip.zig
+++ b/lib/std/compress/gzip.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/compress/zlib.zig b/lib/std/compress/zlib.zig
index 63ef6c2aee..7ef644ef6d 100644
--- a/lib/std/compress/zlib.zig
+++ b/lib/std/compress/zlib.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/comptime_string_map.zig b/lib/std/comptime_string_map.zig
index ed647124a8..4882924ae5 100644
--- a/lib/std/comptime_string_map.zig
+++ b/lib/std/comptime_string_map.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig
index 6eb934473f..da6ec2edf0 100644
--- a/lib/std/crypto.zig
+++ b/lib/std/crypto.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -134,10 +134,15 @@ pub const nacl = struct {
pub const utils = @import("crypto/utils.zig");
+/// This is a thread-local, cryptographically secure pseudo random number generator.
+pub const random = &@import("crypto/tlcsprng.zig").interface;
+
const std = @import("std.zig");
-pub const randomBytes = std.os.getrandom;
test "crypto" {
+ const please_windows_dont_oom = std.Target.current.os.tag == .windows;
+ if (please_windows_dont_oom) return error.SkipZigTest;
+
inline for (std.meta.declarations(@This())) |decl| {
switch (decl.data) {
.Type => |t| {
@@ -178,6 +183,13 @@ test "crypto" {
_ = @import("crypto/25519/ristretto255.zig");
}
+test "CSPRNG" {
+ const a = random.int(u64);
+ const b = random.int(u64);
+ const c = random.int(u64);
+ std.testing.expect(a ^ b ^ c != 0);
+}
+
test "issue #4532: no index out of bounds" {
const types = [_]type{
hash.Md5,
diff --git a/lib/std/crypto/25519/curve25519.zig b/lib/std/crypto/25519/curve25519.zig
index 3ca7af7a41..765ffa1629 100644
--- a/lib/std/crypto/25519/curve25519.zig
+++ b/lib/std/crypto/25519/curve25519.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -15,12 +15,12 @@ pub const Curve25519 = struct {
x: Fe,
/// Decode a Curve25519 point from its compressed (X) coordinates.
- pub inline fn fromBytes(s: [32]u8) Curve25519 {
+ pub fn fromBytes(s: [32]u8) callconv(.Inline) Curve25519 {
return .{ .x = Fe.fromBytes(s) };
}
/// Encode a Curve25519 point.
- pub inline fn toBytes(p: Curve25519) [32]u8 {
+ pub fn toBytes(p: Curve25519) callconv(.Inline) [32]u8 {
return p.x.toBytes();
}
diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig
index 842b08d706..5c7ec0cdac 100644
--- a/lib/std/crypto/25519/ed25519.zig
+++ b/lib/std/crypto/25519/ed25519.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -43,7 +43,7 @@ pub const Ed25519 = struct {
pub fn create(seed: ?[seed_length]u8) !KeyPair {
const ss = seed orelse ss: {
var random_seed: [seed_length]u8 = undefined;
- try crypto.randomBytes(&random_seed);
+ crypto.random.bytes(&random_seed);
break :ss random_seed;
};
var az: [Sha512.digest_length]u8 = undefined;
@@ -179,7 +179,7 @@ pub const Ed25519 = struct {
var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
for (z_batch) |*z| {
- try std.crypto.randomBytes(z[0..16]);
+ std.crypto.random.bytes(z[0..16]);
mem.set(u8, z[16..], 0);
}
@@ -207,7 +207,7 @@ pub const Ed25519 = struct {
test "ed25519 key pair creation" {
var seed: [32]u8 = undefined;
- try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
+ _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
const key_pair = try Ed25519.KeyPair.create(seed);
var buf: [256]u8 = undefined;
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair.secret_key}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
@@ -216,7 +216,7 @@ test "ed25519 key pair creation" {
test "ed25519 signature" {
var seed: [32]u8 = undefined;
- try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
+ _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
const key_pair = try Ed25519.KeyPair.create(seed);
const sig = try Ed25519.sign("test", key_pair, null);
@@ -232,8 +232,8 @@ test "ed25519 batch verification" {
const key_pair = try Ed25519.KeyPair.create(null);
var msg1: [32]u8 = undefined;
var msg2: [32]u8 = undefined;
- try std.crypto.randomBytes(&msg1);
- try std.crypto.randomBytes(&msg2);
+ std.crypto.random.bytes(&msg1);
+ std.crypto.random.bytes(&msg2);
const sig1 = try Ed25519.sign(&msg1, key_pair, null);
const sig2 = try Ed25519.sign(&msg2, key_pair, null);
var signature_batch = [_]Ed25519.BatchElement{
@@ -339,11 +339,11 @@ test "ed25519 test vectors" {
};
for (entries) |entry, i| {
var msg: [entry.msg_hex.len / 2]u8 = undefined;
- try fmt.hexToBytes(&msg, entry.msg_hex);
+ _ = try fmt.hexToBytes(&msg, entry.msg_hex);
var public_key: [32]u8 = undefined;
- try fmt.hexToBytes(&public_key, entry.public_key_hex);
+ _ = try fmt.hexToBytes(&public_key, entry.public_key_hex);
var sig: [64]u8 = undefined;
- try fmt.hexToBytes(&sig, entry.sig_hex);
+ _ = try fmt.hexToBytes(&sig, entry.sig_hex);
if (entry.expected) |error_type| {
std.testing.expectError(error_type, Ed25519.verify(sig, &msg, public_key));
} else {
diff --git a/lib/std/crypto/25519/edwards25519.zig b/lib/std/crypto/25519/edwards25519.zig
index 008a4535b3..d4238f87bb 100644
--- a/lib/std/crypto/25519/edwards25519.zig
+++ b/lib/std/crypto/25519/edwards25519.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -92,7 +92,7 @@ pub const Edwards25519 = struct {
}
/// Flip the sign of the X coordinate.
- pub inline fn neg(p: Edwards25519) Edwards25519 {
+ pub fn neg(p: Edwards25519) callconv(.Inline) Edwards25519 {
return .{ .x = p.x.neg(), .y = p.y, .z = p.z, .t = p.t.neg() };
}
@@ -137,14 +137,14 @@ pub const Edwards25519 = struct {
return p.add(q.neg());
}
- inline fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) void {
+ fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) callconv(.Inline) void {
p.x.cMov(a.x, c);
p.y.cMov(a.y, c);
p.z.cMov(a.z, c);
p.t.cMov(a.t, c);
}
- inline fn pcSelect(comptime n: usize, pc: [n]Edwards25519, b: u8) Edwards25519 {
+ fn pcSelect(comptime n: usize, pc: [n]Edwards25519, b: u8) callconv(.Inline) Edwards25519 {
var t = Edwards25519.identityElement;
comptime var i: u8 = 1;
inline while (i < pc.len) : (i += 1) {
@@ -484,8 +484,8 @@ test "edwards25519 packing/unpacking" {
test "edwards25519 point addition/substraction" {
var s1: [32]u8 = undefined;
var s2: [32]u8 = undefined;
- try std.crypto.randomBytes(&s1);
- try std.crypto.randomBytes(&s2);
+ std.crypto.random.bytes(&s1);
+ std.crypto.random.bytes(&s2);
const p = try Edwards25519.basePoint.clampedMul(s1);
const q = try Edwards25519.basePoint.clampedMul(s2);
const r = p.add(q).add(q).sub(q).sub(q);
diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig
index d2002ce52d..320cb1bb51 100644
--- a/lib/std/crypto/25519/field.zig
+++ b/lib/std/crypto/25519/field.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -52,7 +52,7 @@ pub const Fe = struct {
pub const edwards25519sqrtam2 = Fe{ .limbs = .{ 1693982333959686, 608509411481997, 2235573344831311, 947681270984193, 266558006233600 } };
/// Return true if the field element is zero
- pub inline fn isZero(fe: Fe) bool {
+ pub fn isZero(fe: Fe) callconv(.Inline) bool {
var reduced = fe;
reduced.reduce();
const limbs = reduced.limbs;
@@ -60,7 +60,7 @@ pub const Fe = struct {
}
/// Return true if both field elements are equivalent
- pub inline fn equivalent(a: Fe, b: Fe) bool {
+ pub fn equivalent(a: Fe, b: Fe) callconv(.Inline) bool {
return a.sub(b).isZero();
}
@@ -164,7 +164,7 @@ pub const Fe = struct {
}
/// Add a field element
- pub inline fn add(a: Fe, b: Fe) Fe {
+ pub fn add(a: Fe, b: Fe) callconv(.Inline) Fe {
var fe: Fe = undefined;
comptime var i = 0;
inline while (i < 5) : (i += 1) {
@@ -174,7 +174,7 @@ pub const Fe = struct {
}
/// Substract a field elememnt
- pub inline fn sub(a: Fe, b: Fe) Fe {
+ pub fn sub(a: Fe, b: Fe) callconv(.Inline) Fe {
var fe = b;
comptime var i = 0;
inline while (i < 4) : (i += 1) {
@@ -193,17 +193,17 @@ pub const Fe = struct {
}
/// Negate a field element
- pub inline fn neg(a: Fe) Fe {
+ pub fn neg(a: Fe) callconv(.Inline) Fe {
return zero.sub(a);
}
/// Return true if a field element is negative
- pub inline fn isNegative(a: Fe) bool {
+ pub fn isNegative(a: Fe) callconv(.Inline) bool {
return (a.toBytes()[0] & 1) != 0;
}
/// Conditonally replace a field element with `a` if `c` is positive
- pub inline fn cMov(fe: *Fe, a: Fe, c: u64) void {
+ pub fn cMov(fe: *Fe, a: Fe, c: u64) callconv(.Inline) void {
const mask: u64 = 0 -% c;
var x = fe.*;
comptime var i = 0;
@@ -244,7 +244,7 @@ pub const Fe = struct {
}
}
- inline fn _carry128(r: *[5]u128) Fe {
+ fn _carry128(r: *[5]u128) callconv(.Inline) Fe {
var rs: [5]u64 = undefined;
comptime var i = 0;
inline while (i < 4) : (i += 1) {
@@ -265,7 +265,7 @@ pub const Fe = struct {
}
/// Multiply two field elements
- pub inline fn mul(a: Fe, b: Fe) Fe {
+ pub fn mul(a: Fe, b: Fe) callconv(.Inline) Fe {
var ax: [5]u128 = undefined;
var bx: [5]u128 = undefined;
var a19: [5]u128 = undefined;
@@ -288,7 +288,7 @@ pub const Fe = struct {
return _carry128(&r);
}
- inline fn _sq(a: Fe, double: comptime bool) Fe {
+ fn _sq(a: Fe, double: comptime bool) callconv(.Inline) Fe {
var ax: [5]u128 = undefined;
var r: [5]u128 = undefined;
comptime var i = 0;
@@ -317,17 +317,17 @@ pub const Fe = struct {
}
/// Square a field element
- pub inline fn sq(a: Fe) Fe {
+ pub fn sq(a: Fe) callconv(.Inline) Fe {
return _sq(a, false);
}
/// Square and double a field element
- pub inline fn sq2(a: Fe) Fe {
+ pub fn sq2(a: Fe) callconv(.Inline) Fe {
return _sq(a, true);
}
/// Multiply a field element with a small (32-bit) integer
- pub inline fn mul32(a: Fe, comptime n: u32) Fe {
+ pub fn mul32(a: Fe, comptime n: u32) callconv(.Inline) Fe {
const sn = @intCast(u128, n);
var fe: Fe = undefined;
var x: u128 = 0;
@@ -342,7 +342,7 @@ pub const Fe = struct {
}
/// Square a field element `n` times
- inline fn sqn(a: Fe, comptime n: comptime_int) Fe {
+ fn sqn(a: Fe, comptime n: comptime_int) callconv(.Inline) Fe {
var i: usize = 0;
var fe = a;
while (i < n) : (i += 1) {
diff --git a/lib/std/crypto/25519/ristretto255.zig b/lib/std/crypto/25519/ristretto255.zig
index 16d301592a..df85422f65 100644
--- a/lib/std/crypto/25519/ristretto255.zig
+++ b/lib/std/crypto/25519/ristretto255.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -42,7 +42,7 @@ pub const Ristretto255 = struct {
}
/// Reject the neutral element.
- pub inline fn rejectIdentity(p: Ristretto255) !void {
+ pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) !void {
return p.p.rejectIdentity();
}
@@ -141,19 +141,19 @@ pub const Ristretto255 = struct {
}
/// Double a Ristretto255 element.
- pub inline fn dbl(p: Ristretto255) Ristretto255 {
+ pub fn dbl(p: Ristretto255) callconv(.Inline) Ristretto255 {
return .{ .p = p.p.dbl() };
}
/// Add two Ristretto255 elements.
- pub inline fn add(p: Ristretto255, q: Ristretto255) Ristretto255 {
+ pub fn add(p: Ristretto255, q: Ristretto255) callconv(.Inline) Ristretto255 {
return .{ .p = p.p.add(q.p) };
}
/// Multiply a Ristretto255 element with a scalar.
/// Return error.WeakPublicKey if the resulting element is
/// the identity element.
- pub inline fn mul(p: Ristretto255, s: [encoded_length]u8) !Ristretto255 {
+ pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) !Ristretto255 {
return Ristretto255{ .p = try p.p.mul(s) };
}
@@ -173,7 +173,7 @@ test "ristretto255" {
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76");
var r: [Ristretto255.encoded_length]u8 = undefined;
- try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919");
+ _ = try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919");
var q = try Ristretto255.fromBytes(r);
q = q.dbl().add(p);
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E");
diff --git a/lib/std/crypto/25519/scalar.zig b/lib/std/crypto/25519/scalar.zig
index c5e460d29e..ceff153bff 100644
--- a/lib/std/crypto/25519/scalar.zig
+++ b/lib/std/crypto/25519/scalar.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -46,7 +46,7 @@ pub fn reduce64(s: [64]u8) [32]u8 {
/// Perform the X25519 "clamping" operation.
/// The scalar is then guaranteed to be a multiple of the cofactor.
-pub inline fn clamp(s: *[32]u8) void {
+pub fn clamp(s: *[32]u8) callconv(.Inline) void {
s[0] &= 248;
s[31] = (s[31] & 127) | 64;
}
diff --git a/lib/std/crypto/25519/x25519.zig b/lib/std/crypto/25519/x25519.zig
index 3b3ff551fe..5d0479bd4d 100644
--- a/lib/std/crypto/25519/x25519.zig
+++ b/lib/std/crypto/25519/x25519.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -34,7 +34,7 @@ pub const X25519 = struct {
pub fn create(seed: ?[seed_length]u8) !KeyPair {
const sk = seed orelse sk: {
var random_seed: [seed_length]u8 = undefined;
- try crypto.randomBytes(&random_seed);
+ crypto.random.bytes(&random_seed);
break :sk random_seed;
};
var kp: KeyPair = undefined;
@@ -85,8 +85,8 @@ const htest = @import("../test.zig");
test "x25519 public key calculation from secret key" {
var sk: [32]u8 = undefined;
var pk_expected: [32]u8 = undefined;
- try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
- try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50");
+ _ = try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
+ _ = try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50");
const pk_calculated = try X25519.recoverPublicKey(sk);
std.testing.expectEqual(pk_calculated, pk_expected);
}
diff --git a/lib/std/crypto/aegis.zig b/lib/std/crypto/aegis.zig
index f3060ef615..2983f68ce8 100644
--- a/lib/std/crypto/aegis.zig
+++ b/lib/std/crypto/aegis.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -35,7 +35,7 @@ const State128L = struct {
return state;
}
- inline fn update(state: *State128L, d1: AesBlock, d2: AesBlock) void {
+ fn update(state: *State128L, d1: AesBlock, d2: AesBlock) callconv(.Inline) void {
const blocks = &state.blocks;
const tmp = blocks[7];
comptime var i: usize = 7;
@@ -81,8 +81,8 @@ const State128L = struct {
while (i < 7) : (i += 1) {
state.update(tmp, tmp);
}
- return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).
- xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes();
+ return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4])
+ .xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes();
}
};
@@ -207,7 +207,7 @@ const State256 = struct {
return state;
}
- inline fn update(state: *State256, d: AesBlock) void {
+ fn update(state: *State256, d: AesBlock) callconv(.Inline) void {
const blocks = &state.blocks;
const tmp = blocks[5].encrypt(blocks[0]);
comptime var i: usize = 5;
@@ -244,8 +244,8 @@ const State256 = struct {
while (i < 7) : (i += 1) {
state.update(tmp);
}
- return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).
- xorBlocks(blocks[5]).toBytes();
+ return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4])
+ .xorBlocks(blocks[5]).toBytes();
}
};
diff --git a/lib/std/crypto/aes.zig b/lib/std/crypto/aes.zig
index ada55fa975..2a81492c8a 100644
--- a/lib/std/crypto/aes.zig
+++ b/lib/std/crypto/aes.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -122,11 +122,11 @@ test "expand 128-bit key" {
var exp: [16]u8 = undefined;
for (enc.key_schedule.round_keys) |round_key, i| {
- try std.fmt.hexToBytes(&exp, exp_enc[i]);
+ _ = try std.fmt.hexToBytes(&exp, exp_enc[i]);
testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
}
for (enc.key_schedule.round_keys) |round_key, i| {
- try std.fmt.hexToBytes(&exp, exp_dec[i]);
+ _ = try std.fmt.hexToBytes(&exp, exp_dec[i]);
testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
}
}
@@ -144,11 +144,11 @@ test "expand 256-bit key" {
var exp: [16]u8 = undefined;
for (enc.key_schedule.round_keys) |round_key, i| {
- try std.fmt.hexToBytes(&exp, exp_enc[i]);
+ _ = try std.fmt.hexToBytes(&exp, exp_enc[i]);
testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
}
for (dec.key_schedule.round_keys) |round_key, i| {
- try std.fmt.hexToBytes(&exp, exp_dec[i]);
+ _ = try std.fmt.hexToBytes(&exp, exp_dec[i]);
testing.expectEqualSlices(u8, &exp, &round_key.toBytes());
}
}
diff --git a/lib/std/crypto/aes/aesni.zig b/lib/std/crypto/aes/aesni.zig
index 3d694875bf..13b3f8e527 100644
--- a/lib/std/crypto/aes/aesni.zig
+++ b/lib/std/crypto/aes/aesni.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -19,24 +19,24 @@ pub const Block = struct {
repr: BlockVec,
/// Convert a byte sequence into an internal representation.
- pub inline fn fromBytes(bytes: *const [16]u8) Block {
+ pub fn fromBytes(bytes: *const [16]u8) callconv(.Inline) Block {
const repr = mem.bytesToValue(BlockVec, bytes);
return Block{ .repr = repr };
}
/// Convert the internal representation of a block into a byte sequence.
- pub inline fn toBytes(block: Block) [16]u8 {
+ pub fn toBytes(block: Block) callconv(.Inline) [16]u8 {
return mem.toBytes(block.repr);
}
/// XOR the block with a byte sequence.
- pub inline fn xorBytes(block: Block, bytes: *const [16]u8) [16]u8 {
+ pub fn xorBytes(block: Block, bytes: *const [16]u8) callconv(.Inline) [16]u8 {
const x = block.repr ^ fromBytes(bytes).repr;
return mem.toBytes(x);
}
/// Encrypt a block with a round key.
- pub inline fn encrypt(block: Block, round_key: Block) Block {
+ pub fn encrypt(block: Block, round_key: Block) callconv(.Inline) Block {
return Block{
.repr = asm (
\\ vaesenc %[rk], %[in], %[out]
@@ -48,7 +48,7 @@ pub const Block = struct {
}
/// Encrypt a block with the last round key.
- pub inline fn encryptLast(block: Block, round_key: Block) Block {
+ pub fn encryptLast(block: Block, round_key: Block) callconv(.Inline) Block {
return Block{
.repr = asm (
\\ vaesenclast %[rk], %[in], %[out]
@@ -60,7 +60,7 @@ pub const Block = struct {
}
/// Decrypt a block with a round key.
- pub inline fn decrypt(block: Block, inv_round_key: Block) Block {
+ pub fn decrypt(block: Block, inv_round_key: Block) callconv(.Inline) Block {
return Block{
.repr = asm (
\\ vaesdec %[rk], %[in], %[out]
@@ -72,7 +72,7 @@ pub const Block = struct {
}
/// Decrypt a block with the last round key.
- pub inline fn decryptLast(block: Block, inv_round_key: Block) Block {
+ pub fn decryptLast(block: Block, inv_round_key: Block) callconv(.Inline) Block {
return Block{
.repr = asm (
\\ vaesdeclast %[rk], %[in], %[out]
@@ -84,17 +84,17 @@ pub const Block = struct {
}
/// Apply the bitwise XOR operation to the content of two blocks.
- pub inline fn xorBlocks(block1: Block, block2: Block) Block {
+ pub fn xorBlocks(block1: Block, block2: Block) callconv(.Inline) Block {
return Block{ .repr = block1.repr ^ block2.repr };
}
/// Apply the bitwise AND operation to the content of two blocks.
- pub inline fn andBlocks(block1: Block, block2: Block) Block {
+ pub fn andBlocks(block1: Block, block2: Block) callconv(.Inline) Block {
return Block{ .repr = block1.repr & block2.repr };
}
/// Apply the bitwise OR operation to the content of two blocks.
- pub inline fn orBlocks(block1: Block, block2: Block) Block {
+ pub fn orBlocks(block1: Block, block2: Block) callconv(.Inline) Block {
return Block{ .repr = block1.repr | block2.repr };
}
@@ -114,7 +114,7 @@ pub const Block = struct {
};
/// Encrypt multiple blocks in parallel, each their own round key.
- pub inline fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block {
+ pub fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
@@ -124,7 +124,7 @@ pub const Block = struct {
}
/// Decrypt multiple blocks in parallel, each their own round key.
- pub inline fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block {
+ pub fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
@@ -134,7 +134,7 @@ pub const Block = struct {
}
/// Encrypt multiple blocks in parallel with the same round key.
- pub inline fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ pub fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
@@ -144,7 +144,7 @@ pub const Block = struct {
}
/// Decrypt multiple blocks in parallel with the same round key.
- pub inline fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ pub fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
@@ -154,7 +154,7 @@ pub const Block = struct {
}
/// Encrypt multiple blocks in parallel with the same last round key.
- pub inline fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ pub fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
@@ -164,7 +164,7 @@ pub const Block = struct {
}
/// Decrypt multiple blocks in parallel with the same last round key.
- pub inline fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ pub fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
diff --git a/lib/std/crypto/aes/armcrypto.zig b/lib/std/crypto/aes/armcrypto.zig
index 79eb9dda75..d331783284 100644
--- a/lib/std/crypto/aes/armcrypto.zig
+++ b/lib/std/crypto/aes/armcrypto.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -19,18 +19,18 @@ pub const Block = struct {
repr: BlockVec,
/// Convert a byte sequence into an internal representation.
- pub inline fn fromBytes(bytes: *const [16]u8) Block {
+ pub fn fromBytes(bytes: *const [16]u8) callconv(.Inline) Block {
const repr = mem.bytesToValue(BlockVec, bytes);
return Block{ .repr = repr };
}
/// Convert the internal representation of a block into a byte sequence.
- pub inline fn toBytes(block: Block) [16]u8 {
+ pub fn toBytes(block: Block) callconv(.Inline) [16]u8 {
return mem.toBytes(block.repr);
}
/// XOR the block with a byte sequence.
- pub inline fn xorBytes(block: Block, bytes: *const [16]u8) [16]u8 {
+ pub fn xorBytes(block: Block, bytes: *const [16]u8) callconv(.Inline) [16]u8 {
const x = block.repr ^ fromBytes(bytes).repr;
return mem.toBytes(x);
}
@@ -38,7 +38,7 @@ pub const Block = struct {
const zero = Vector(2, u64){ 0, 0 };
/// Encrypt a block with a round key.
- pub inline fn encrypt(block: Block, round_key: Block) Block {
+ pub fn encrypt(block: Block, round_key: Block) callconv(.Inline) Block {
return Block{
.repr = asm (
\\ mov %[out].16b, %[in].16b
@@ -54,7 +54,7 @@ pub const Block = struct {
}
/// Encrypt a block with the last round key.
- pub inline fn encryptLast(block: Block, round_key: Block) Block {
+ pub fn encryptLast(block: Block, round_key: Block) callconv(.Inline) Block {
return Block{
.repr = asm (
\\ mov %[out].16b, %[in].16b
@@ -69,7 +69,7 @@ pub const Block = struct {
}
/// Decrypt a block with a round key.
- pub inline fn decrypt(block: Block, inv_round_key: Block) Block {
+ pub fn decrypt(block: Block, inv_round_key: Block) callconv(.Inline) Block {
return Block{
.repr = asm (
\\ mov %[out].16b, %[in].16b
@@ -85,7 +85,7 @@ pub const Block = struct {
}
/// Decrypt a block with the last round key.
- pub inline fn decryptLast(block: Block, inv_round_key: Block) Block {
+ pub fn decryptLast(block: Block, inv_round_key: Block) callconv(.Inline) Block {
return Block{
.repr = asm (
\\ mov %[out].16b, %[in].16b
@@ -100,17 +100,17 @@ pub const Block = struct {
}
/// Apply the bitwise XOR operation to the content of two blocks.
- pub inline fn xorBlocks(block1: Block, block2: Block) Block {
+ pub fn xorBlocks(block1: Block, block2: Block) callconv(.Inline) Block {
return Block{ .repr = block1.repr ^ block2.repr };
}
/// Apply the bitwise AND operation to the content of two blocks.
- pub inline fn andBlocks(block1: Block, block2: Block) Block {
+ pub fn andBlocks(block1: Block, block2: Block) callconv(.Inline) Block {
return Block{ .repr = block1.repr & block2.repr };
}
/// Apply the bitwise OR operation to the content of two blocks.
- pub inline fn orBlocks(block1: Block, block2: Block) Block {
+ pub fn orBlocks(block1: Block, block2: Block) callconv(.Inline) Block {
return Block{ .repr = block1.repr | block2.repr };
}
@@ -120,7 +120,7 @@ pub const Block = struct {
pub const optimal_parallel_blocks = 8;
/// Encrypt multiple blocks in parallel, each their own round key.
- pub inline fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block {
+ pub fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
@@ -130,7 +130,7 @@ pub const Block = struct {
}
/// Decrypt multiple blocks in parallel, each their own round key.
- pub inline fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block {
+ pub fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
@@ -140,7 +140,7 @@ pub const Block = struct {
}
/// Encrypt multiple blocks in parallel with the same round key.
- pub inline fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ pub fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
@@ -150,7 +150,7 @@ pub const Block = struct {
}
/// Decrypt multiple blocks in parallel with the same round key.
- pub inline fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ pub fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
@@ -160,7 +160,7 @@ pub const Block = struct {
}
/// Encrypt multiple blocks in parallel with the same last round key.
- pub inline fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ pub fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
@@ -170,7 +170,7 @@ pub const Block = struct {
}
/// Decrypt multiple blocks in parallel with the same last round key.
- pub inline fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block {
+ pub fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block {
comptime var i = 0;
var out: [count]Block = undefined;
inline while (i < count) : (i += 1) {
diff --git a/lib/std/crypto/aes/soft.zig b/lib/std/crypto/aes/soft.zig
index e9108820b1..6f305b4050 100644
--- a/lib/std/crypto/aes/soft.zig
+++ b/lib/std/crypto/aes/soft.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -18,7 +18,7 @@ pub const Block = struct {
repr: BlockVec align(16),
/// Convert a byte sequence into an internal representation.
- pub inline fn fromBytes(bytes: *const [16]u8) Block {
+ pub fn fromBytes(bytes: *const [16]u8) callconv(.Inline) Block {
const s0 = mem.readIntBig(u32, bytes[0..4]);
const s1 = mem.readIntBig(u32, bytes[4..8]);
const s2 = mem.readIntBig(u32, bytes[8..12]);
@@ -27,7 +27,7 @@ pub const Block = struct {
}
/// Convert the internal representation of a block into a byte sequence.
- pub inline fn toBytes(block: Block) [16]u8 {
+ pub fn toBytes(block: Block) callconv(.Inline) [16]u8 {
var bytes: [16]u8 = undefined;
mem.writeIntBig(u32, bytes[0..4], block.repr[0]);
mem.writeIntBig(u32, bytes[4..8], block.repr[1]);
@@ -37,7 +37,7 @@ pub const Block = struct {
}
/// XOR the block with a byte sequence.
- pub inline fn xorBytes(block: Block, bytes: *const [16]u8) [16]u8 {
+ pub fn xorBytes(block: Block, bytes: *const [16]u8) callconv(.Inline) [16]u8 {
const block_bytes = block.toBytes();
var x: [16]u8 = undefined;
comptime var i: usize = 0;
@@ -48,7 +48,7 @@ pub const Block = struct {
}
/// Encrypt a block with a round key.
- pub inline fn encrypt(block: Block, round_key: Block) Block {
+ pub fn encrypt(block: Block, round_key: Block) callconv(.Inline) Block {
const src = &block.repr;
const s0 = block.repr[0];
@@ -65,7 +65,7 @@ pub const Block = struct {
}
/// Encrypt a block with the last round key.
- pub inline fn encryptLast(block: Block, round_key: Block) Block {
+ pub fn encryptLast(block: Block, round_key: Block) callconv(.Inline) Block {
const src = &block.repr;
const t0 = block.repr[0];
@@ -87,7 +87,7 @@ pub const Block = struct {
}
/// Decrypt a block with a round key.
- pub inline fn decrypt(block: Block, round_key: Block) Block {
+ pub fn decrypt(block: Block, round_key: Block) callconv(.Inline) Block {
const src = &block.repr;
const s0 = block.repr[0];
@@ -104,7 +104,7 @@ pub const Block = struct {
}
/// Decrypt a block with the last round key.
- pub inline fn decryptLast(block: Block, round_key: Block) Block {
+ pub fn decryptLast(block: Block, round_key: Block) callconv(.Inline) Block {
const src = &block.repr;
const t0 = block.repr[0];
@@ -126,7 +126,7 @@ pub const Block = struct {
}
/// Apply the bitwise XOR operation to the content of two blocks.
- pub inline fn xorBlocks(block1: Block, block2: Block) Block {
+ pub fn xorBlocks(block1: Block, block2: Block) callconv(.Inline) Block {
var x: BlockVec = undefined;
comptime var i = 0;
inline while (i < 4) : (i += 1) {
@@ -136,7 +136,7 @@ pub const Block = struct {
}
/// Apply the bitwise AND operation to the content of two blocks.
- pub inline fn andBlocks(block1: Block, block2: Block) Block {
+ pub fn andBlocks(block1: Block, block2: Block) callconv(.Inline) Block {
var x: BlockVec = undefined;
comptime var i = 0;
inline while (i < 4) : (i += 1) {
@@ -146,7 +146,7 @@ pub const Block = struct {
}
/// Apply the bitwise OR operation to the content of two blocks.
- pub inline fn orBlocks(block1: Block, block2: Block) Block {
+ pub fn orBlocks(block1: Block, block2: Block) callconv(.Inline) Block {
var x: BlockVec = undefined;
comptime var i = 0;
inline while (i < 4) : (i += 1) {
diff --git a/lib/std/crypto/aes_gcm.zig b/lib/std/crypto/aes_gcm.zig
index e57decb2b2..5ef3f93963 100644
--- a/lib/std/crypto/aes_gcm.zig
+++ b/lib/std/crypto/aes_gcm.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto/bcrypt.zig b/lib/std/crypto/bcrypt.zig
index 4cec59961b..caceb6d7b9 100644
--- a/lib/std/crypto/bcrypt.zig
+++ b/lib/std/crypto/bcrypt.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -109,9 +109,7 @@ const State = struct {
}
}
- const Halves = struct {
- l: u32, r: u32
- };
+ const Halves = struct { l: u32, r: u32 };
fn feistelF(state: State, x: u32) u32 {
var r = state.sboxes[0][@truncate(u8, x >> 24)];
@@ -247,7 +245,7 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8)
Codec.encode(ct_str[0..], ct[0 .. ct.len - 1]);
var s_buf: [hash_length]u8 = undefined;
- const s = fmt.bufPrint(s_buf[0..], "$2b${}{}${}{}", .{ rounds_log / 10, rounds_log % 10, salt_str, ct_str }) catch unreachable;
+ const s = fmt.bufPrint(s_buf[0..], "$2b${d}{d}${s}{s}", .{ rounds_log / 10, rounds_log % 10, salt_str, ct_str }) catch unreachable;
debug.assert(s.len == s_buf.len);
return s_buf;
}
@@ -262,7 +260,7 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8)
/// and then use the resulting hash as the password parameter for bcrypt.
pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 {
var salt: [salt_length]u8 = undefined;
- try crypto.randomBytes(&salt);
+ crypto.random.bytes(&salt);
return strHashInternal(password, rounds_log, salt);
}
@@ -283,7 +281,7 @@ pub fn strVerify(h: [hash_length]u8, password: []const u8) BcryptError!void {
test "bcrypt codec" {
var salt: [salt_length]u8 = undefined;
- try crypto.randomBytes(&salt);
+ crypto.random.bytes(&salt);
var salt_str: [salt_str_length]u8 = undefined;
Codec.encode(salt_str[0..], salt[0..]);
var salt2: [salt_length]u8 = undefined;
diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig
index 7a0253861b..00336aef87 100644
--- a/lib/std/crypto/benchmark.zig
+++ b/lib/std/crypto/benchmark.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -314,7 +314,7 @@ fn mode(comptime x: comptime_int) comptime_int {
}
pub fn main() !void {
- const stdout = std.io.getStdOut().outStream();
+ const stdout = std.io.getStdOut().writer();
var buffer: [1024]u8 = undefined;
var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
diff --git a/lib/std/crypto/blake2.zig b/lib/std/crypto/blake2.zig
index b90661aa19..4203a99459 100644
--- a/lib/std/crypto/blake2.zig
+++ b/lib/std/crypto/blake2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -33,6 +33,7 @@ fn roundParam(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundP
// Blake2s
pub const Blake2s128 = Blake2s(128);
+pub const Blake2s160 = Blake2s(160);
pub const Blake2s224 = Blake2s(224);
pub const Blake2s256 = Blake2s(256);
@@ -137,12 +138,8 @@ pub fn Blake2s(comptime out_bits: usize) type {
mem.set(u8, d.buf[d.buf_len..], 0);
d.t += d.buf_len;
d.round(d.buf[0..], true);
-
- const rr = d.h[0 .. digest_length / 4];
-
- for (rr) |s, j| {
- mem.writeIntSliceLittle(u32, out[4 * j ..], s);
- }
+ for (d.h) |*x| x.* = mem.nativeToLittle(u32, x.*);
+ mem.copy(u8, out[0..], @ptrCast(*[digest_length]u8, &d.h));
}
fn round(d: *Self, b: *const [64]u8, last: bool) void {
@@ -195,6 +192,89 @@ pub fn Blake2s(comptime out_bits: usize) type {
};
}
+test "blake2s160 single" {
+ const h1 = "354c9c33f735962418bdacb9479873429c34916f";
+ htest.assertEqualHash(Blake2s160, h1, "");
+
+ const h2 = "5ae3b99be29b01834c3b508521ede60438f8de17";
+ htest.assertEqualHash(Blake2s160, h2, "abc");
+
+ const h3 = "5a604fec9713c369e84b0ed68daed7d7504ef240";
+ htest.assertEqualHash(Blake2s160, h3, "The quick brown fox jumps over the lazy dog");
+
+ const h4 = "b60c4dc60e2681e58fbc24e77f07e02c69e72ed0";
+ htest.assertEqualHash(Blake2s160, h4, "a" ** 32 ++ "b" ** 32);
+}
+
+test "blake2s160 streaming" {
+ var h = Blake2s160.init(.{});
+ var out: [20]u8 = undefined;
+
+ const h1 = "354c9c33f735962418bdacb9479873429c34916f";
+
+ h.final(out[0..]);
+ htest.assertEqual(h1, out[0..]);
+
+ const h2 = "5ae3b99be29b01834c3b508521ede60438f8de17";
+
+ h = Blake2s160.init(.{});
+ h.update("abc");
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
+
+ h = Blake2s160.init(.{});
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
+
+ const h3 = "b60c4dc60e2681e58fbc24e77f07e02c69e72ed0";
+
+ h = Blake2s160.init(.{});
+ h.update("a" ** 32);
+ h.update("b" ** 32);
+ h.final(out[0..]);
+ htest.assertEqual(h3, out[0..]);
+
+ h = Blake2s160.init(.{});
+ h.update("a" ** 32 ++ "b" ** 32);
+ h.final(out[0..]);
+ htest.assertEqual(h3, out[0..]);
+
+ const h4 = "4667fd60791a7fe41f939bca646b4529e296bd68";
+
+ h = Blake2s160.init(.{ .context = [_]u8{0x69} ** 8, .salt = [_]u8{0x42} ** 8 });
+ h.update("a" ** 32);
+ h.update("b" ** 32);
+ h.final(out[0..]);
+ htest.assertEqual(h4, out[0..]);
+
+ h = Blake2s160.init(.{ .context = [_]u8{0x69} ** 8, .salt = [_]u8{0x42} ** 8 });
+ h.update("a" ** 32 ++ "b" ** 32);
+ h.final(out[0..]);
+ htest.assertEqual(h4, out[0..]);
+}
+
+test "comptime blake2s160" {
+ //comptime
+ {
+ @setEvalBranchQuota(10000);
+ var block = [_]u8{0} ** Blake2s160.block_length;
+ var out: [Blake2s160.digest_length]u8 = undefined;
+
+ const h1 = "2c56ad9d0b2c8b474aafa93ab307db2f0940105f";
+
+ htest.assertEqualHash(Blake2s160, h1, block[0..]);
+
+ var h = Blake2s160.init(.{});
+ h.update(&block);
+ h.final(out[0..]);
+
+ htest.assertEqual(h1, out[0..]);
+ }
+}
+
test "blake2s224 single" {
const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4";
htest.assertEqualHash(Blake2s224, h1, "");
@@ -373,6 +453,7 @@ test "comptime blake2s256" {
// Blake2b
pub const Blake2b128 = Blake2b(128);
+pub const Blake2b160 = Blake2b(160);
pub const Blake2b256 = Blake2b(256);
pub const Blake2b384 = Blake2b(384);
pub const Blake2b512 = Blake2b(512);
@@ -480,12 +561,8 @@ pub fn Blake2b(comptime out_bits: usize) type {
mem.set(u8, d.buf[d.buf_len..], 0);
d.t += d.buf_len;
d.round(d.buf[0..], true);
-
- const rr = d.h[0 .. digest_length / 8];
-
- for (rr) |s, j| {
- mem.writeIntSliceLittle(u64, out[8 * j ..], s);
- }
+ for (d.h) |*x| x.* = mem.nativeToLittle(u64, x.*);
+ mem.copy(u8, out[0..], @ptrCast(*[digest_length]u8, &d.h));
}
fn round(d: *Self, b: *const [128]u8, last: bool) void {
@@ -538,6 +615,95 @@ pub fn Blake2b(comptime out_bits: usize) type {
};
}
+test "blake2b160 single" {
+ const h1 = "3345524abf6bbe1809449224b5972c41790b6cf2";
+ htest.assertEqualHash(Blake2b160, h1, "");
+
+ const h2 = "384264f676f39536840523f284921cdc68b6846b";
+ htest.assertEqualHash(Blake2b160, h2, "abc");
+
+ const h3 = "3c523ed102ab45a37d54f5610d5a983162fde84f";
+ htest.assertEqualHash(Blake2b160, h3, "The quick brown fox jumps over the lazy dog");
+
+ const h4 = "43758f5de1740f651f1ae39de92260fe8bd5a11f";
+ htest.assertEqualHash(Blake2b160, h4, "a" ** 64 ++ "b" ** 64);
+}
+
+test "blake2b160 streaming" {
+ var h = Blake2b160.init(.{});
+ var out: [20]u8 = undefined;
+
+ const h1 = "3345524abf6bbe1809449224b5972c41790b6cf2";
+
+ h.final(out[0..]);
+ htest.assertEqual(h1, out[0..]);
+
+ const h2 = "384264f676f39536840523f284921cdc68b6846b";
+
+ h = Blake2b160.init(.{});
+ h.update("abc");
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
+
+ h = Blake2b160.init(.{});
+ h.update("a");
+ h.update("b");
+ h.update("c");
+ h.final(out[0..]);
+ htest.assertEqual(h2, out[0..]);
+
+ const h3 = "43758f5de1740f651f1ae39de92260fe8bd5a11f";
+
+ h = Blake2b160.init(.{});
+ h.update("a" ** 64 ++ "b" ** 64);
+ h.final(out[0..]);
+ htest.assertEqual(h3, out[0..]);
+
+ h = Blake2b160.init(.{});
+ h.update("a" ** 64);
+ h.update("b" ** 64);
+ h.final(out[0..]);
+ htest.assertEqual(h3, out[0..]);
+
+ h = Blake2b160.init(.{});
+ h.update("a" ** 64);
+ h.update("b" ** 64);
+ h.final(out[0..]);
+ htest.assertEqual(h3, out[0..]);
+
+ const h4 = "72328f8a8200663752fc302d372b5dd9b49dd8dc";
+
+ h = Blake2b160.init(.{ .context = [_]u8{0x69} ** 16, .salt = [_]u8{0x42} ** 16 });
+ h.update("a" ** 64);
+ h.update("b" ** 64);
+ h.final(out[0..]);
+ htest.assertEqual(h4, out[0..]);
+
+ h = Blake2b160.init(.{ .context = [_]u8{0x69} ** 16, .salt = [_]u8{0x42} ** 16 });
+ h.update("a" ** 64);
+ h.update("b" ** 64);
+ h.final(out[0..]);
+ htest.assertEqual(h4, out[0..]);
+}
+
+test "comptime blake2b160" {
+ comptime {
+ @setEvalBranchQuota(10000);
+ var block = [_]u8{0} ** Blake2b160.block_length;
+ var out: [Blake2b160.digest_length]u8 = undefined;
+
+ const h1 = "8d26f158f564e3293b42f5e3d34263cb173aa9c9";
+
+ htest.assertEqualHash(Blake2b160, h1, block[0..]);
+
+ var h = Blake2b160.init(.{});
+ h.update(&block);
+ h.final(out[0..]);
+
+ htest.assertEqual(h1, out[0..]);
+ }
+}
+
test "blake2b384 single" {
const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100";
htest.assertEqualHash(Blake2b384, h1, "");
diff --git a/lib/std/crypto/blake3.zig b/lib/std/crypto/blake3.zig
index b22429b8e2..a10c50b074 100644
--- a/lib/std/crypto/blake3.zig
+++ b/lib/std/crypto/blake3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -66,7 +66,7 @@ const CompressVectorized = struct {
const Lane = Vector(4, u32);
const Rows = [4]Lane;
- inline fn g(comptime even: bool, rows: *Rows, m: Lane) void {
+ fn g(comptime even: bool, rows: *Rows, m: Lane) callconv(.Inline) void {
rows[0] +%= rows[1] +% m;
rows[3] ^= rows[0];
rows[3] = math.rotr(Lane, rows[3], if (even) 8 else 16);
@@ -75,13 +75,13 @@ const CompressVectorized = struct {
rows[1] = math.rotr(Lane, rows[1], if (even) 7 else 12);
}
- inline fn diagonalize(rows: *Rows) void {
+ fn diagonalize(rows: *Rows) callconv(.Inline) void {
rows[0] = @shuffle(u32, rows[0], undefined, [_]i32{ 3, 0, 1, 2 });
rows[3] = @shuffle(u32, rows[3], undefined, [_]i32{ 2, 3, 0, 1 });
rows[2] = @shuffle(u32, rows[2], undefined, [_]i32{ 1, 2, 3, 0 });
}
- inline fn undiagonalize(rows: *Rows) void {
+ fn undiagonalize(rows: *Rows) callconv(.Inline) void {
rows[0] = @shuffle(u32, rows[0], undefined, [_]i32{ 1, 2, 3, 0 });
rows[3] = @shuffle(u32, rows[3], undefined, [_]i32{ 2, 3, 0, 1 });
rows[2] = @shuffle(u32, rows[2], undefined, [_]i32{ 3, 0, 1, 2 });
@@ -663,7 +663,7 @@ fn testBlake3(hasher: *Blake3, input_len: usize, expected_hex: [262]u8) void {
// Compare to expected value
var expected_bytes: [expected_hex.len / 2]u8 = undefined;
- fmt.hexToBytes(expected_bytes[0..], expected_hex[0..]) catch unreachable;
+ _ = fmt.hexToBytes(expected_bytes[0..], expected_hex[0..]) catch unreachable;
testing.expectEqual(actual_bytes, expected_bytes);
// Restore initial state
diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig
index 5acd2bd4f5..0f79707279 100644
--- a/lib/std/crypto/chacha20.zig
+++ b/lib/std/crypto/chacha20.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -35,7 +35,7 @@ const ChaCha20VecImpl = struct {
};
}
- inline fn chacha20Core(x: *BlockVec, input: BlockVec) void {
+ fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
x.* = input;
var r: usize = 0;
@@ -80,7 +80,7 @@ const ChaCha20VecImpl = struct {
}
}
- inline fn hashToBytes(out: *[64]u8, x: BlockVec) void {
+ fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
var i: usize = 0;
while (i < 4) : (i += 1) {
mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]);
@@ -90,7 +90,7 @@ const ChaCha20VecImpl = struct {
}
}
- inline fn contextFeedback(x: *BlockVec, ctx: BlockVec) void {
+ fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
x[0] +%= ctx[0];
x[1] +%= ctx[1];
x[2] +%= ctx[2];
@@ -190,7 +190,7 @@ const ChaCha20NonVecImpl = struct {
};
}
- inline fn chacha20Core(x: *BlockVec, input: BlockVec) void {
+ fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
x.* = input;
const rounds = comptime [_]QuarterRound{
@@ -219,7 +219,7 @@ const ChaCha20NonVecImpl = struct {
}
}
- inline fn hashToBytes(out: *[64]u8, x: BlockVec) void {
+ fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
var i: usize = 0;
while (i < 4) : (i += 1) {
mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]);
@@ -229,7 +229,7 @@ const ChaCha20NonVecImpl = struct {
}
}
- inline fn contextFeedback(x: *BlockVec, ctx: BlockVec) void {
+ fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
var i: usize = 0;
while (i < 16) : (i += 1) {
x[i] +%= ctx[i];
diff --git a/lib/std/crypto/ghash.zig b/lib/std/crypto/ghash.zig
index d5d4ae98ea..ffc9ef41ae 100644
--- a/lib/std/crypto/ghash.zig
+++ b/lib/std/crypto/ghash.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -95,7 +95,7 @@ pub const Ghash = struct {
}
}
- inline fn clmul_pclmul(x: u64, y: u64) u64 {
+ fn clmul_pclmul(x: u64, y: u64) callconv(.Inline) u64 {
const Vector = std.meta.Vector;
const product = asm (
\\ vpclmulqdq $0x00, %[x], %[y], %[out]
@@ -106,7 +106,7 @@ pub const Ghash = struct {
return product[0];
}
- inline fn clmul_pmull(x: u64, y: u64) u64 {
+ fn clmul_pmull(x: u64, y: u64) callconv(.Inline) u64 {
const Vector = std.meta.Vector;
const product = asm (
\\ pmull %[out].1q, %[x].1d, %[y].1d
diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig
index 78ab88b9cf..1c1d6c79db 100644
--- a/lib/std/crypto/gimli.zig
+++ b/lib/std/crypto/gimli.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -48,7 +48,7 @@ pub const State = struct {
return mem.asBytes(&self.data);
}
- inline fn endianSwap(self: *Self) void {
+ fn endianSwap(self: *Self) callconv(.Inline) void {
for (self.data) |*w| {
w.* = mem.littleToNative(u32, w.*);
}
@@ -116,7 +116,7 @@ pub const State = struct {
const Lane = Vector(4, u32);
- inline fn shift(x: Lane, comptime n: comptime_int) Lane {
+ fn shift(x: Lane, comptime n: comptime_int) callconv(.Inline) Lane {
return x << @splat(4, @as(u5, n));
}
@@ -229,18 +229,17 @@ pub const Hash = struct {
const buf = self.state.toSlice();
var in = data;
while (in.len > 0) {
- var left = State.RATE - self.buf_off;
- if (left == 0) {
- self.state.permute();
- self.buf_off = 0;
- left = State.RATE;
- }
+ const left = State.RATE - self.buf_off;
const ps = math.min(in.len, left);
for (buf[self.buf_off .. self.buf_off + ps]) |*p, i| {
p.* ^= in[i];
}
self.buf_off += ps;
in = in[ps..];
+ if (self.buf_off == State.RATE) {
+ self.state.permute();
+ self.buf_off = 0;
+ }
}
}
@@ -271,12 +270,28 @@ pub fn hash(out: []u8, in: []const u8, options: Hash.Options) void {
test "hash" {
// a test vector (30) from NIST KAT submission.
var msg: [58 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C");
+ _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C");
var md: [32]u8 = undefined;
hash(&md, &msg, .{});
htest.assertEqual("1C9A03DC6A5DDC5444CFC6F4B154CFF5CF081633B2CEA4D7D0AE7CCFED5AAA44", &md);
}
+test "hash test vector 17" {
+ var msg: [32 / 2]u8 = undefined;
+ _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F");
+ var md: [32]u8 = undefined;
+ hash(&md, &msg, .{});
+ htest.assertEqual("404C130AF1B9023A7908200919F690FFBB756D5176E056FFDE320016A37C7282", &md);
+}
+
+test "hash test vector 33" {
+ var msg: [32]u8 = undefined;
+ _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ var md: [32]u8 = undefined;
+ hash(&md, &msg, .{});
+ htest.assertEqual("A8F4FA28708BDA7EFB4C1914CA4AFA9E475B82D588D36504F87DBB0ED9AB3C4B", &md);
+}
+
pub const Aead = struct {
pub const tag_length = State.RATE;
pub const nonce_length = 16;
@@ -421,9 +436,9 @@ pub const Aead = struct {
test "cipher" {
var key: [32]u8 = undefined;
- try std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ _ = try std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
var nonce: [16]u8 = undefined;
- try std.fmt.hexToBytes(&nonce, "000102030405060708090A0B0C0D0E0F");
+ _ = try std.fmt.hexToBytes(&nonce, "000102030405060708090A0B0C0D0E0F");
{ // test vector (1) from NIST KAT submission.
const ad: [0]u8 = undefined;
const pt: [0]u8 = undefined;
@@ -441,7 +456,7 @@ test "cipher" {
{ // test vector (34) from NIST KAT submission.
const ad: [0]u8 = undefined;
var pt: [2 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&pt, "00");
+ _ = try std.fmt.hexToBytes(&pt, "00");
var ct: [pt.len]u8 = undefined;
var tag: [16]u8 = undefined;
@@ -455,9 +470,9 @@ test "cipher" {
}
{ // test vector (106) from NIST KAT submission.
var ad: [12 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&ad, "000102030405");
+ _ = try std.fmt.hexToBytes(&ad, "000102030405");
var pt: [6 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&pt, "000102");
+ _ = try std.fmt.hexToBytes(&pt, "000102");
var ct: [pt.len]u8 = undefined;
var tag: [16]u8 = undefined;
@@ -471,9 +486,9 @@ test "cipher" {
}
{ // test vector (790) from NIST KAT submission.
var ad: [60 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&ad, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D");
+ _ = try std.fmt.hexToBytes(&ad, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D");
var pt: [46 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516");
+ _ = try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516");
var ct: [pt.len]u8 = undefined;
var tag: [16]u8 = undefined;
@@ -488,7 +503,7 @@ test "cipher" {
{ // test vector (1057) from NIST KAT submission.
const ad: [0]u8 = undefined;
var pt: [64 / 2]u8 = undefined;
- try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ _ = try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
var ct: [pt.len]u8 = undefined;
var tag: [16]u8 = undefined;
diff --git a/lib/std/crypto/hkdf.zig b/lib/std/crypto/hkdf.zig
index b1c6f58acd..c0f919ef82 100644
--- a/lib/std/crypto/hkdf.zig
+++ b/lib/std/crypto/hkdf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto/hmac.zig b/lib/std/crypto/hmac.zig
index 3978ff6b81..7f29c62941 100644
--- a/lib/std/crypto/hmac.zig
+++ b/lib/std/crypto/hmac.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto/md5.zig b/lib/std/crypto/md5.zig
index 8b454c52a7..78454ce3c1 100644
--- a/lib/std/crypto/md5.zig
+++ b/lib/std/crypto/md5.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto/modes.zig b/lib/std/crypto/modes.zig
index a81d30e50f..a74704d1ae 100644
--- a/lib/std/crypto/modes.zig
+++ b/lib/std/crypto/modes.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto/pbkdf2.zig b/lib/std/crypto/pbkdf2.zig
index 85c8e01105..25df1ba440 100644
--- a/lib/std/crypto/pbkdf2.zig
+++ b/lib/std/crypto/pbkdf2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto/poly1305.zig b/lib/std/crypto/poly1305.zig
index 0b7b4cd64a..739c057178 100644
--- a/lib/std/crypto/poly1305.zig
+++ b/lib/std/crypto/poly1305.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto/salsa20.zig b/lib/std/crypto/salsa20.zig
index dd3e4fe99b..e22668f998 100644
--- a/lib/std/crypto/salsa20.zig
+++ b/lib/std/crypto/salsa20.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -37,7 +37,7 @@ const Salsa20VecImpl = struct {
};
}
- inline fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) void {
+ fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) callconv(.Inline) void {
const n1n2n3n0 = Lane{ input[3][1], input[3][2], input[3][3], input[3][0] };
const n1n2 = Half{ n1n2n3n0[0], n1n2n3n0[1] };
const n3n0 = Half{ n1n2n3n0[2], n1n2n3n0[3] };
@@ -146,9 +146,9 @@ const Salsa20VecImpl = struct {
while (j < 64) : (j += 1) {
xout[j] ^= buf[j];
}
- ctx[2][0] +%= 1;
- if (ctx[2][0] == 0) {
- ctx[2][1] += 1;
+ ctx[3][2] +%= 1;
+ if (ctx[3][2] == 0) {
+ ctx[3][3] += 1;
}
}
if (i < in.len) {
@@ -211,7 +211,7 @@ const Salsa20NonVecImpl = struct {
d: u6,
};
- inline fn Rp(a: usize, b: usize, c: usize, d: u6) QuarterRound {
+ fn Rp(a: usize, b: usize, c: usize, d: u6) callconv(.Inline) QuarterRound {
return QuarterRound{
.a = a,
.b = b,
@@ -220,7 +220,7 @@ const Salsa20NonVecImpl = struct {
};
}
- inline fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) void {
+ fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) callconv(.Inline) void {
const arx_steps = comptime [_]QuarterRound{
Rp(4, 0, 12, 7), Rp(8, 4, 0, 9), Rp(12, 8, 4, 13), Rp(0, 12, 8, 18),
Rp(9, 5, 1, 7), Rp(13, 9, 5, 9), Rp(1, 13, 9, 13), Rp(5, 1, 13, 18),
@@ -571,9 +571,9 @@ test "xsalsa20poly1305" {
var key: [XSalsa20Poly1305.key_length]u8 = undefined;
var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined;
var tag: [XSalsa20Poly1305.tag_length]u8 = undefined;
- try crypto.randomBytes(&msg);
- try crypto.randomBytes(&key);
- try crypto.randomBytes(&nonce);
+ crypto.random.bytes(&msg);
+ crypto.random.bytes(&key);
+ crypto.random.bytes(&nonce);
XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key);
try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key);
@@ -585,9 +585,9 @@ test "xsalsa20poly1305 secretbox" {
var key: [XSalsa20Poly1305.key_length]u8 = undefined;
var nonce: [Box.nonce_length]u8 = undefined;
var boxed: [msg.len + Box.tag_length]u8 = undefined;
- try crypto.randomBytes(&msg);
- try crypto.randomBytes(&key);
- try crypto.randomBytes(&nonce);
+ crypto.random.bytes(&msg);
+ crypto.random.bytes(&key);
+ crypto.random.bytes(&nonce);
SecretBox.seal(boxed[0..], msg[0..], nonce, key);
try SecretBox.open(msg2[0..], boxed[0..], nonce, key);
@@ -598,8 +598,8 @@ test "xsalsa20poly1305 box" {
var msg2: [msg.len]u8 = undefined;
var nonce: [Box.nonce_length]u8 = undefined;
var boxed: [msg.len + Box.tag_length]u8 = undefined;
- try crypto.randomBytes(&msg);
- try crypto.randomBytes(&nonce);
+ crypto.random.bytes(&msg);
+ crypto.random.bytes(&nonce);
var kp1 = try Box.KeyPair.create(null);
var kp2 = try Box.KeyPair.create(null);
@@ -611,9 +611,18 @@ test "xsalsa20poly1305 sealedbox" {
var msg: [100]u8 = undefined;
var msg2: [msg.len]u8 = undefined;
var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;
- try crypto.randomBytes(&msg);
+ crypto.random.bytes(&msg);
var kp = try Box.KeyPair.create(null);
try SealedBox.seal(boxed[0..], msg[0..], kp.public_key);
try SealedBox.open(msg2[0..], boxed[0..], kp);
}
+
+test "secretbox twoblocks" {
+ const key = [_]u8{ 0xc9, 0xc9, 0x4d, 0xcf, 0x68, 0xbe, 0x00, 0xe4, 0x7f, 0xe6, 0x13, 0x26, 0xfc, 0xc4, 0x2f, 0xd0, 0xdb, 0x93, 0x91, 0x1c, 0x09, 0x94, 0x89, 0xe1, 0x1b, 0x88, 0x63, 0x18, 0x86, 0x64, 0x8b, 0x7b };
+ const nonce = [_]u8{ 0xa4, 0x33, 0xe9, 0x0a, 0x07, 0x68, 0x6e, 0x9a, 0x2b, 0x6d, 0xd4, 0x59, 0x04, 0x72, 0x3e, 0xd3, 0x8a, 0x67, 0x55, 0xc7, 0x9e, 0x3e, 0x77, 0xdc };
+ const msg = [_]u8{'a'} ** 97;
+ var ciphertext: [msg.len + SecretBox.tag_length]u8 = undefined;
+ SecretBox.seal(&ciphertext, &msg, nonce, key);
+ htest.assertEqual("b05760e217288ba079caa2fd57fd3701784974ffcfda20fe523b89211ad8af065a6eb37cdb29d51aca5bd75dafdd21d18b044c54bb7c526cf576c94ee8900f911ceab0147e82b667a28c52d58ceb29554ff45471224d37b03256b01c119b89ff6d36855de8138d103386dbc9d971f52261", &ciphertext);
+}
diff --git a/lib/std/crypto/sha1.zig b/lib/std/crypto/sha1.zig
index c2ae0a6544..ac699f0ef1 100644
--- a/lib/std/crypto/sha1.zig
+++ b/lib/std/crypto/sha1.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig
index 4e06a214dd..5a37004e8c 100644
--- a/lib/std/crypto/sha2.zig
+++ b/lib/std/crypto/sha2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig
index 3aecf25e5b..d68821133e 100644
--- a/lib/std/crypto/sha3.zig
+++ b/lib/std/crypto/sha3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -13,6 +13,8 @@ pub const Sha3_224 = Keccak(224, 0x06);
pub const Sha3_256 = Keccak(256, 0x06);
pub const Sha3_384 = Keccak(384, 0x06);
pub const Sha3_512 = Keccak(512, 0x06);
+pub const Keccak_256 = Keccak(256, 0x01);
+pub const Keccak_512 = Keccak(512, 0x01);
fn Keccak(comptime bits: usize, comptime delim: u8) type {
return struct {
@@ -297,3 +299,15 @@ test "sha3-512 aligned final" {
h.update(&block);
h.final(out[0..]);
}
+
+test "keccak-256 single" {
+ htest.assertEqualHash(Keccak_256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "");
+ htest.assertEqualHash(Keccak_256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc");
+ htest.assertEqualHash(Keccak_256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
+}
+
+test "keccak-512 single" {
+ htest.assertEqualHash(Keccak_512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", "");
+ htest.assertEqualHash(Keccak_512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc");
+ htest.assertEqualHash(Keccak_512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
+}
diff --git a/lib/std/crypto/siphash.zig b/lib/std/crypto/siphash.zig
index 5dfd2bf326..67bb2a329a 100644
--- a/lib/std/crypto/siphash.zig
+++ b/lib/std/crypto/siphash.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -7,10 +7,10 @@
// SipHash is a moderately fast pseudorandom function, returning a 64-bit or 128-bit tag for an arbitrary long input.
//
// Typical use cases include:
-// - protection against against DoS attacks for hash tables and bloom filters
+// - protection against DoS attacks for hash tables and bloom filters
// - authentication of short-lived messages in online protocols
//
-// https://131002.net/siphash/
+// https://www.aumasson.jp/siphash/siphash.pdf
const std = @import("../std.zig");
const assert = std.debug.assert;
const testing = std.testing;
diff --git a/lib/std/crypto/test.zig b/lib/std/crypto/test.zig
index 692e331e39..cab07c50ec 100644
--- a/lib/std/crypto/test.zig
+++ b/lib/std/crypto/test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/crypto/tlcsprng.zig b/lib/std/crypto/tlcsprng.zig
new file mode 100644
index 0000000000..115a7ab882
--- /dev/null
+++ b/lib/std/crypto/tlcsprng.zig
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! Thread-local cryptographically secure pseudo-random number generator.
+//! This file has public declarations that are intended to be used internally
+//! by the standard library; this namespace is not intended to be exposed
+//! directly to standard library users.
+
+const std = @import("std");
+const root = @import("root");
+const mem = std.mem;
+
+/// We use this as a layer of indirection because global const pointers cannot
+/// point to thread-local variables.
+pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill };
+
+const os_has_fork = switch (std.Target.current.os.tag) {
+ .dragonfly,
+ .freebsd,
+ .ios,
+ .kfreebsd,
+ .linux,
+ .macos,
+ .netbsd,
+ .openbsd,
+ .solaris,
+ .tvos,
+ .watchos,
+ .haiku,
+ => true,
+
+ else => false,
+};
+const os_has_arc4random = std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf");
+const want_fork_safety = os_has_fork and !os_has_arc4random and
+ (std.meta.globalOption("crypto_fork_safety", bool) orelse true);
+const maybe_have_wipe_on_fork = std.Target.current.os.isAtLeast(.linux, .{
+ .major = 4,
+ .minor = 14,
+}) orelse true;
+
+const WipeMe = struct {
+ init_state: enum { uninitialized, initialized, failed },
+ gimli: std.crypto.core.Gimli,
+};
+const wipe_align = if (maybe_have_wipe_on_fork) mem.page_size else @alignOf(WipeMe);
+
+threadlocal var wipe_me: WipeMe align(wipe_align) = .{
+ .gimli = undefined,
+ .init_state = .uninitialized,
+};
+
+fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void {
+ if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) {
+ // arc4random is already a thread-local CSPRNG.
+ return std.c.arc4random_buf(buffer.ptr, buffer.len);
+ }
+ // Allow applications to decide they would prefer to have every call to
+ // std.crypto.random always make an OS syscall, rather than rely on an
+ // application implementation of a CSPRNG.
+ if (comptime std.meta.globalOption("crypto_always_getrandom", bool) orelse false) {
+ return fillWithOsEntropy(buffer);
+ }
+ switch (wipe_me.init_state) {
+ .uninitialized => {
+ if (want_fork_safety) {
+ if (maybe_have_wipe_on_fork) {
+ if (std.os.madvise(
+ @ptrCast([*]align(mem.page_size) u8, &wipe_me),
+ @sizeOf(@TypeOf(wipe_me)),
+ std.os.MADV_WIPEONFORK,
+ )) |_| {
+ return initAndFill(buffer);
+ } else |_| if (std.Thread.use_pthreads) {
+ return setupPthreadAtforkAndFill(buffer);
+ } else {
+ // Since we failed to set up fork safety, we fall back to always
+ // calling getrandom every time.
+ wipe_me.init_state = .failed;
+ return fillWithOsEntropy(buffer);
+ }
+ } else if (std.Thread.use_pthreads) {
+ return setupPthreadAtforkAndFill(buffer);
+ } else {
+ // We have no mechanism to provide fork safety, but we want fork safety,
+ // so we fall back to calling getrandom every time.
+ wipe_me.init_state = .failed;
+ return fillWithOsEntropy(buffer);
+ }
+ } else {
+ return initAndFill(buffer);
+ }
+ },
+ .initialized => {
+ return fillWithCsprng(buffer);
+ },
+ .failed => {
+ if (want_fork_safety) {
+ return fillWithOsEntropy(buffer);
+ } else {
+ unreachable;
+ }
+ },
+ }
+}
+
+fn setupPthreadAtforkAndFill(buffer: []u8) void {
+ const failed = std.c.pthread_atfork(null, null, childAtForkHandler) != 0;
+ if (failed) {
+ wipe_me.init_state = .failed;
+ return fillWithOsEntropy(buffer);
+ } else {
+ return initAndFill(buffer);
+ }
+}
+
+fn childAtForkHandler() callconv(.C) void {
+ const wipe_slice = @ptrCast([*]u8, &wipe_me)[0..@sizeOf(@TypeOf(wipe_me))];
+ std.crypto.utils.secureZero(u8, wipe_slice);
+}
+
+fn fillWithCsprng(buffer: []u8) void {
+ if (buffer.len != 0) {
+ wipe_me.gimli.squeeze(buffer);
+ } else {
+ wipe_me.gimli.permute();
+ }
+ mem.set(u8, wipe_me.gimli.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
+}
+
+fn fillWithOsEntropy(buffer: []u8) void {
+ std.os.getrandom(buffer) catch @panic("getrandom() failed to provide entropy");
+}
+
+fn initAndFill(buffer: []u8) void {
+ var seed: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
+ // Because we panic on getrandom() failing, we provide the opportunity
+ // to override the default seed function. This also makes
+ // `std.crypto.random` available on freestanding targets, provided that
+ // the `cryptoRandomSeed` function is provided.
+ if (@hasDecl(root, "cryptoRandomSeed")) {
+ root.cryptoRandomSeed(&seed);
+ } else {
+ fillWithOsEntropy(&seed);
+ }
+
+ wipe_me.gimli = std.crypto.core.Gimli.init(seed);
+
+ // This is at the end so that accidental recursive dependencies result
+ // in stack overflows instead of invalid random data.
+ wipe_me.init_state = .initialized;
+
+ return fillWithCsprng(buffer);
+}
diff --git a/lib/std/crypto/utils.zig b/lib/std/crypto/utils.zig
index 33ad6360f6..08271ac9f4 100644
--- a/lib/std/crypto/utils.zig
+++ b/lib/std/crypto/utils.zig
@@ -51,8 +51,8 @@ pub fn secureZero(comptime T: type, s: []T) void {
test "crypto.utils.timingSafeEql" {
var a: [100]u8 = undefined;
var b: [100]u8 = undefined;
- try std.crypto.randomBytes(a[0..]);
- try std.crypto.randomBytes(b[0..]);
+ std.crypto.random.bytes(a[0..]);
+ std.crypto.random.bytes(b[0..]);
testing.expect(!timingSafeEql([100]u8, a, b));
mem.copy(u8, a[0..], b[0..]);
testing.expect(timingSafeEql([100]u8, a, b));
@@ -61,8 +61,8 @@ test "crypto.utils.timingSafeEql" {
test "crypto.utils.timingSafeEql (vectors)" {
var a: [100]u8 = undefined;
var b: [100]u8 = undefined;
- try std.crypto.randomBytes(a[0..]);
- try std.crypto.randomBytes(b[0..]);
+ std.crypto.random.bytes(a[0..]);
+ std.crypto.random.bytes(b[0..]);
const v1: std.meta.Vector(100, u8) = a;
const v2: std.meta.Vector(100, u8) = b;
testing.expect(!timingSafeEql(std.meta.Vector(100, u8), v1, v2));
diff --git a/lib/std/cstr.zig b/lib/std/cstr.zig
index 4e11ad9201..33f32ae892 100644
--- a/lib/std/cstr.zig
+++ b/lib/std/cstr.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index be5635ee8f..f32c1a6156 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -50,7 +50,7 @@ pub const LineInfo = struct {
}
};
-var stderr_mutex = std.Mutex{};
+var stderr_mutex = std.Thread.Mutex{};
/// Deprecated. Use `std.log` functions for logging or `std.debug.print` for
/// "printf debugging".
@@ -65,7 +65,7 @@ pub fn print(comptime fmt: []const u8, args: anytype) void {
nosuspend stderr.print(fmt, args) catch return;
}
-pub fn getStderrMutex() *std.Mutex {
+pub fn getStderrMutex() *std.Thread.Mutex {
return &stderr_mutex;
}
@@ -108,11 +108,11 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
return;
}
const debug_info = getSelfDebugInfo() catch |err| {
- stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return;
+ stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
return;
};
writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| {
- stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return;
+ stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
return;
};
}
@@ -129,7 +129,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
return;
}
const debug_info = getSelfDebugInfo() catch |err| {
- stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return;
+ stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
return;
};
const tty_config = detectTTYConfig();
@@ -199,11 +199,11 @@ pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void {
return;
}
const debug_info = getSelfDebugInfo() catch |err| {
- stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return;
+ stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
return;
};
writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| {
- stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return;
+ stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
return;
};
}
@@ -235,7 +235,7 @@ pub fn panic(comptime format: []const u8, args: anytype) noreturn {
var panicking: u8 = 0;
// Locked to avoid interleaving panic messages from multiple threads.
-var panic_mutex = std.Mutex{};
+var panic_mutex = std.Thread.Mutex{};
/// Counts how many times the panic handler is invoked by this thread.
/// This is used to catch and handle panics triggered by the panic handler.
@@ -250,6 +250,24 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
resetSegfaultHandler();
}
+ if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64)
+ nosuspend {
+ // As a workaround for not having threadlocal variable support in LLD for this target,
+ // we have a simpler panic implementation that does not use threadlocal variables.
+ // TODO https://github.com/ziglang/zig/issues/7527
+ const stderr = io.getStdErr().writer();
+ if (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst) == 0) {
+ stderr.print("panic: " ++ format ++ "\n", args) catch os.abort();
+ if (trace) |t| {
+ dumpStackTrace(t.*);
+ }
+ dumpCurrentStackTrace(first_trace_addr);
+ } else {
+ stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
+ }
+ os.abort();
+ };
+
nosuspend switch (panic_stage) {
0 => {
panic_stage = 1;
@@ -262,6 +280,12 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
defer held.release();
const stderr = io.getStdErr().writer();
+ if (builtin.single_threaded) {
+ stderr.print("panic: ", .{}) catch os.abort();
+ } else {
+ const current_thread_id = std.Thread.getCurrentThreadId();
+ stderr.print("thread {d} panic: ", .{current_thread_id}) catch os.abort();
+ }
stderr.print(format ++ "\n", args) catch os.abort();
if (trace) |t| {
dumpStackTrace(t.*);
@@ -274,9 +298,8 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
// and call abort()
// Sleep forever without hammering the CPU
- var event = std.ResetEvent.init();
+ var event: std.Thread.StaticResetEvent = .{};
event.wait();
-
unreachable;
}
},
@@ -512,15 +535,15 @@ fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void {
const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;
- const signature = try modi.inStream().readIntLittle(u32);
+ const signature = try modi.reader().readIntLittle(u32);
if (signature != 4)
return error.InvalidDebugInfo;
mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
- try modi.inStream().readNoEof(mod.symbols);
+ try modi.reader().readNoEof(mod.symbols);
mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);
- try modi.inStream().readNoEof(mod.subsect_info);
+ try modi.reader().readNoEof(mod.subsect_info);
var sect_offset: usize = 0;
var skip_len: usize = undefined;
@@ -606,7 +629,7 @@ fn printLineInfo(
tty_config.setColor(out_stream, .White);
if (line_info) |*li| {
- try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column });
+ try out_stream.print("{s}:{d}:{d}", .{ li.file_name, li.line, li.column });
} else {
try out_stream.writeAll("???:?:?");
}
@@ -614,7 +637,7 @@ fn printLineInfo(
tty_config.setColor(out_stream, .Reset);
try out_stream.writeAll(": ");
tty_config.setColor(out_stream, .Dim);
- try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name });
+ try out_stream.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name });
tty_config.setColor(out_stream, .Reset);
try out_stream.writeAll("\n");
@@ -634,6 +657,7 @@ fn printLineInfo(
} else |err| switch (err) {
error.EndOfFile, error.FileNotFound => {},
error.BadPathName => {},
+ error.AccessDenied => {},
else => return err,
}
}
@@ -699,11 +723,11 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
try di.pdb.openFile(di.coff, path);
var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;
- const version = try pdb_stream.inStream().readIntLittle(u32);
- const signature = try pdb_stream.inStream().readIntLittle(u32);
- const age = try pdb_stream.inStream().readIntLittle(u32);
+ const version = try pdb_stream.reader().readIntLittle(u32);
+ const signature = try pdb_stream.reader().readIntLittle(u32);
+ const age = try pdb_stream.reader().readIntLittle(u32);
var guid: [16]u8 = undefined;
- try pdb_stream.inStream().readNoEof(&guid);
+ try pdb_stream.reader().readNoEof(&guid);
if (version != 20000404) // VC70, only value observed by LLVM team
return error.UnknownPDBVersion;
if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age)
@@ -711,9 +735,9 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
// We validated the executable and pdb match.
const string_table_index = str_tab_index: {
- const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32);
+ const name_bytes_len = try pdb_stream.reader().readIntLittle(u32);
const name_bytes = try allocator.alloc(u8, name_bytes_len);
- try pdb_stream.inStream().readNoEof(name_bytes);
+ try pdb_stream.reader().readNoEof(name_bytes);
const HashTableHeader = packed struct {
Size: u32,
@@ -723,17 +747,17 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
return cap * 2 / 3 + 1;
}
};
- const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader);
+ const hash_tbl_hdr = try pdb_stream.reader().readStruct(HashTableHeader);
if (hash_tbl_hdr.Capacity == 0)
return error.InvalidDebugInfo;
if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
return error.InvalidDebugInfo;
- const present = try readSparseBitVector(&pdb_stream.inStream(), allocator);
+ const present = try readSparseBitVector(&pdb_stream.reader(), allocator);
if (present.len != hash_tbl_hdr.Size)
return error.InvalidDebugInfo;
- const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator);
+ const deleted = try readSparseBitVector(&pdb_stream.reader(), allocator);
const Bucket = struct {
first: u32,
@@ -741,8 +765,8 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
};
const bucket_list = try allocator.alloc(Bucket, present.len);
for (present) |_| {
- const name_offset = try pdb_stream.inStream().readIntLittle(u32);
- const name_index = try pdb_stream.inStream().readIntLittle(u32);
+ const name_offset = try pdb_stream.reader().readIntLittle(u32);
+ const name_index = try pdb_stream.reader().readIntLittle(u32);
const name = mem.spanZ(std.meta.assumeSentinel(name_bytes.ptr + name_offset, 0));
if (mem.eql(u8, name, "/names")) {
break :str_tab_index name_index;
@@ -757,7 +781,7 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
const dbi = di.pdb.dbi;
// Dbi Header
- const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader);
+ const dbi_stream_header = try dbi.reader().readStruct(pdb.DbiStreamHeader);
if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team
return error.UnknownPDBVersion;
if (dbi_stream_header.Age != age)
@@ -771,7 +795,7 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
// Module Info Substream
var mod_info_offset: usize = 0;
while (mod_info_offset != mod_info_size) {
- const mod_info = try dbi.inStream().readStruct(pdb.ModInfo);
+ const mod_info = try dbi.reader().readStruct(pdb.ModInfo);
var this_record_len: usize = @sizeOf(pdb.ModInfo);
const module_name = try dbi.readNullTermString(allocator);
@@ -809,14 +833,14 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);
var sect_cont_offset: usize = 0;
if (section_contrib_size != 0) {
- const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32));
+ const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.reader().readIntLittle(u32));
if (ver != pdb.SectionContrSubstreamVersion.Ver60)
return error.InvalidDebugInfo;
sect_cont_offset += @sizeOf(u32);
}
while (sect_cont_offset != section_contrib_size) {
const entry = try sect_contribs.addOne();
- entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry);
+ entry.* = try dbi.reader().readStruct(pdb.SectionContribEntry);
sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
if (sect_cont_offset > section_contrib_size)
@@ -1101,12 +1125,15 @@ pub const DebugInfo = struct {
}
pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
- if (comptime std.Target.current.isDarwin())
- return self.lookupModuleDyld(address)
- else if (builtin.os.tag == .windows)
- return self.lookupModuleWin32(address)
- else
+ if (comptime std.Target.current.isDarwin()) {
+ return self.lookupModuleDyld(address);
+ } else if (builtin.os.tag == .windows) {
+ return self.lookupModuleWin32(address);
+ } else if (builtin.os.tag == .haiku) {
+ return self.lookupModuleHaiku(address);
+ } else {
return self.lookupModuleDl(address);
+ }
}
fn lookupModuleDyld(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
@@ -1312,6 +1339,10 @@ pub const DebugInfo = struct {
return obj_di;
}
+
+ fn lookupModuleHaiku(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
+ @panic("TODO implement lookup module for Haiku");
+ }
};
const SymbolInfo = struct {
@@ -1696,6 +1727,7 @@ fn getDebugInfoAllocator() *mem.Allocator {
pub const have_segfault_handling_support = switch (builtin.os.tag) {
.linux, .netbsd => true,
.windows => true,
+ .freebsd, .openbsd => @hasDecl(os, "ucontext_t"),
else => false,
};
pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
@@ -1757,7 +1789,9 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v
const addr = switch (builtin.os.tag) {
.linux => @ptrToInt(info.fields.sigfault.addr),
+ .freebsd => @ptrToInt(info.addr),
.netbsd => @ptrToInt(info.info.reason.fault.addr),
+ .openbsd => @ptrToInt(info.data.fault.addr),
else => unreachable,
};
@@ -1781,8 +1815,18 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v
},
.x86_64 => {
const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
- const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]);
- const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_RBP]);
+ const ip = switch (builtin.os.tag) {
+ .linux, .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]),
+ .freebsd => @intCast(usize, ctx.mcontext.rip),
+ .openbsd => @intCast(usize, ctx.sc_rip),
+ else => unreachable,
+ };
+ const bp = switch (builtin.os.tag) {
+ .linux, .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG_RBP]),
+ .openbsd => @intCast(usize, ctx.sc_rbp),
+ .freebsd => @intCast(usize, ctx.mcontext.rbp),
+ else => unreachable,
+ };
dumpStackTraceFromBase(bp, ip);
},
.arm => {
diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig
index 3ef0540bdd..7df3a1bff6 100644
--- a/lib/std/dwarf.zig
+++ b/lib/std/dwarf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -213,13 +213,11 @@ const LineNumberProgram = struct {
return error.MissingDebugInfo;
} else if (self.prev_file - 1 >= self.file_entries.items.len) {
return error.InvalidDebugInfo;
- } else
- &self.file_entries.items[self.prev_file - 1];
+ } else &self.file_entries.items[self.prev_file - 1];
const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
return error.InvalidDebugInfo;
- } else
- self.include_dirs[file_entry.dir_index];
+ } else self.include_dirs[file_entry.dir_index];
const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name });
errdefer self.file_entries.allocator.free(file_name);
return debug.LineInfo{
@@ -408,15 +406,12 @@ pub const DwarfInfo = struct {
fn scanAllFunctions(di: *DwarfInfo) !void {
var stream = io.fixedBufferStream(di.debug_info);
- const in = &stream.inStream();
+ const in = &stream.reader();
const seekable = &stream.seekableStream();
var this_unit_offset: u64 = 0;
while (this_unit_offset < try seekable.getEndPos()) {
- seekable.seekTo(this_unit_offset) catch |err| switch (err) {
- error.EndOfStream => unreachable,
- else => return err,
- };
+ try seekable.seekTo(this_unit_offset);
var is_64: bool = undefined;
const unit_length = try readUnitLength(in, di.endian, &is_64);
@@ -515,15 +510,12 @@ pub const DwarfInfo = struct {
fn scanAllCompileUnits(di: *DwarfInfo) !void {
var stream = io.fixedBufferStream(di.debug_info);
- const in = &stream.inStream();
+ const in = &stream.reader();
const seekable = &stream.seekableStream();
var this_unit_offset: u64 = 0;
while (this_unit_offset < try seekable.getEndPos()) {
- seekable.seekTo(this_unit_offset) catch |err| switch (err) {
- error.EndOfStream => unreachable,
- else => return err,
- };
+ try seekable.seekTo(this_unit_offset);
var is_64: bool = undefined;
const unit_length = try readUnitLength(in, di.endian, &is_64);
@@ -591,7 +583,7 @@ pub const DwarfInfo = struct {
if (di.debug_ranges) |debug_ranges| {
if (compile_unit.die.getAttrSecOffset(AT_ranges)) |ranges_offset| {
var stream = io.fixedBufferStream(debug_ranges);
- const in = &stream.inStream();
+ const in = &stream.reader();
const seekable = &stream.seekableStream();
// All the addresses in the list are relative to the value
@@ -646,7 +638,7 @@ pub const DwarfInfo = struct {
fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {
var stream = io.fixedBufferStream(di.debug_abbrev);
- const in = &stream.inStream();
+ const in = &stream.reader();
const seekable = &stream.seekableStream();
try seekable.seekTo(offset);
@@ -697,7 +689,7 @@ pub const DwarfInfo = struct {
pub fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo {
var stream = io.fixedBufferStream(di.debug_line);
- const in = &stream.inStream();
+ const in = &stream.reader();
const seekable = &stream.seekableStream();
const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT_comp_dir);
diff --git a/lib/std/dwarf_bits.zig b/lib/std/dwarf_bits.zig
index bf9c97154c..99e268beb7 100644
--- a/lib/std/dwarf_bits.zig
+++ b/lib/std/dwarf_bits.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig
index f2e138c3f3..98c59d4105 100644
--- a/lib/std/dynamic_library.zig
+++ b/lib/std/dynamic_library.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/elf.zig b/lib/std/elf.zig
index a28780fd03..cfb6b448c0 100644
--- a/lib/std/elf.zig
+++ b/lib/std/elf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -720,10 +720,10 @@ pub const Elf32_Rel = extern struct {
r_offset: Elf32_Addr,
r_info: Elf32_Word,
- pub inline fn r_sym(self: @This()) u24 {
+ pub fn r_sym(self: @This()) callconv(.Inline) u24 {
return @truncate(u24, self.r_info >> 8);
}
- pub inline fn r_type(self: @This()) u8 {
+ pub fn r_type(self: @This()) callconv(.Inline) u8 {
return @truncate(u8, self.r_info & 0xff);
}
};
@@ -731,10 +731,10 @@ pub const Elf64_Rel = extern struct {
r_offset: Elf64_Addr,
r_info: Elf64_Xword,
- pub inline fn r_sym(self: @This()) u32 {
+ pub fn r_sym(self: @This()) callconv(.Inline) u32 {
return @truncate(u32, self.r_info >> 32);
}
- pub inline fn r_type(self: @This()) u32 {
+ pub fn r_type(self: @This()) callconv(.Inline) u32 {
return @truncate(u32, self.r_info & 0xffffffff);
}
};
@@ -743,10 +743,10 @@ pub const Elf32_Rela = extern struct {
r_info: Elf32_Word,
r_addend: Elf32_Sword,
- pub inline fn r_sym(self: @This()) u24 {
+ pub fn r_sym(self: @This()) callconv(.Inline) u24 {
return @truncate(u24, self.r_info >> 8);
}
- pub inline fn r_type(self: @This()) u8 {
+ pub fn r_type(self: @This()) callconv(.Inline) u8 {
return @truncate(u8, self.r_info & 0xff);
}
};
@@ -755,10 +755,10 @@ pub const Elf64_Rela = extern struct {
r_info: Elf64_Xword,
r_addend: Elf64_Sxword,
- pub inline fn r_sym(self: @This()) u32 {
+ pub fn r_sym(self: @This()) callconv(.Inline) u32 {
return @truncate(u32, self.r_info >> 32);
}
- pub inline fn r_type(self: @This()) u32 {
+ pub fn r_type(self: @This()) callconv(.Inline) u32 {
return @truncate(u32, self.r_info & 0xffffffff);
}
};
diff --git a/lib/std/event.zig b/lib/std/event.zig
index bba17a3388..cd4af07d64 100644
--- a/lib/std/event.zig
+++ b/lib/std/event.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/event/batch.zig b/lib/std/event/batch.zig
index 2ace2f7914..5368c5336d 100644
--- a/lib/std/event/batch.zig
+++ b/lib/std/event/batch.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -98,15 +98,16 @@ pub fn Batch(
/// This function is *not* thread-safe. It must be called from one thread at
/// a time, however, it need not be the same thread.
pub fn wait(self: *Self) CollectedResult {
- for (self.jobs) |*job| if (job.frame) |f| {
- job.result = if (async_ok) await f else nosuspend await f;
- if (CollectedResult != void) {
- job.result catch |err| {
- self.collected_result = err;
- };
- }
- job.frame = null;
- };
+ for (self.jobs) |*job|
+ if (job.frame) |f| {
+ job.result = if (async_ok) await f else nosuspend await f;
+ if (CollectedResult != void) {
+ job.result catch |err| {
+ self.collected_result = err;
+ };
+ }
+ job.frame = null;
+ };
return self.collected_result;
}
};
diff --git a/lib/std/event/channel.zig b/lib/std/event/channel.zig
index b7c55360d7..2711488705 100644
--- a/lib/std/event/channel.zig
+++ b/lib/std/event/channel.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/event/future.zig b/lib/std/event/future.zig
index 40c7845d53..30a2e46ec5 100644
--- a/lib/std/event/future.zig
+++ b/lib/std/event/future.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/event/group.zig b/lib/std/event/group.zig
index e91adbbe8c..b052c15704 100644
--- a/lib/std/event/group.zig
+++ b/lib/std/event/group.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/event/lock.zig b/lib/std/event/lock.zig
index 6819e413d2..d48c6c1520 100644
--- a/lib/std/event/lock.zig
+++ b/lib/std/event/lock.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -16,7 +16,7 @@ const Loop = std.event.Loop;
/// Allows only one actor to hold the lock.
/// TODO: make this API also work in blocking I/O mode.
pub const Lock = struct {
- mutex: std.Mutex = std.Mutex{},
+ mutex: std.Thread.Mutex = std.Thread.Mutex{},
head: usize = UNLOCKED,
const UNLOCKED = 0;
diff --git a/lib/std/event/locked.zig b/lib/std/event/locked.zig
index 9a53116fd6..c9303274a9 100644
--- a/lib/std/event/locked.zig
+++ b/lib/std/event/locked.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig
index a000820348..912f99e961 100644
--- a/lib/std/event/loop.zig
+++ b/lib/std/event/loop.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -29,7 +29,7 @@ pub const Loop = struct {
fs_thread: *Thread,
fs_queue: std.atomic.Queue(Request),
fs_end_request: Request.Node,
- fs_thread_wakeup: std.ResetEvent,
+ fs_thread_wakeup: std.Thread.ResetEvent,
/// For resources that have the same lifetime as the `Loop`.
/// This is only used by `Loop` for the thread pool and associated resources.
@@ -164,9 +164,10 @@ pub const Loop = struct {
.fs_end_request = .{ .data = .{ .msg = .end, .finish = .NoAction } },
.fs_queue = std.atomic.Queue(Request).init(),
.fs_thread = undefined,
- .fs_thread_wakeup = std.ResetEvent.init(),
+ .fs_thread_wakeup = undefined,
.delay_queue = undefined,
};
+ try self.fs_thread_wakeup.init();
errdefer self.fs_thread_wakeup.deinit();
errdefer self.arena.deinit();
@@ -439,13 +440,11 @@ pub const Loop = struct {
.overlapped = ResumeNode.overlapped_init,
},
};
- var need_to_delete = false;
+ var need_to_delete = true;
defer if (need_to_delete) self.linuxRemoveFd(fd);
suspend {
- if (self.linuxAddFd(fd, &resume_node.base, flags)) |_| {
- need_to_delete = true;
- } else |err| switch (err) {
+ self.linuxAddFd(fd, &resume_node.base, flags) catch |err| switch (err) {
error.FileDescriptorNotRegistered => unreachable,
error.OperationCausesCircularLoop => unreachable,
error.FileDescriptorIncompatibleWithEpoll => unreachable,
@@ -455,6 +454,7 @@ pub const Loop = struct {
error.UserResourceLimitReached,
error.Unexpected,
=> {
+ need_to_delete = false;
// Fall back to a blocking poll(). Ideally this codepath is never hit, since
// epoll should be just fine. But this is better than incorrect behavior.
var poll_flags: i16 = 0;
@@ -478,7 +478,7 @@ pub const Loop = struct {
};
resume @frame();
},
- }
+ };
}
}
@@ -784,7 +784,7 @@ pub const Loop = struct {
timer: std.time.Timer,
waiters: Waiters,
thread: *std.Thread,
- event: std.AutoResetEvent,
+ event: std.Thread.AutoResetEvent,
is_running: bool,
/// Initialize the delay queue by spawning the timer thread
@@ -795,9 +795,10 @@ pub const Loop = struct {
.waiters = DelayQueue.Waiters{
.entries = std.atomic.Queue(anyframe).init(),
},
- .thread = try std.Thread.spawn(self, DelayQueue.run),
- .event = std.AutoResetEvent{},
+ .event = std.Thread.AutoResetEvent{},
.is_running = true,
+ // Must be last so that it can read the other state, such as `is_running`.
+ .thread = try std.Thread.spawn(self, DelayQueue.run),
};
}
@@ -1261,7 +1262,7 @@ pub const Loop = struct {
flags: u32,
dest_addr: ?*const os.sockaddr,
addrlen: os.socklen_t,
- ) os.SendError!usize {
+ ) os.SendToError!usize {
while (true) {
return os.sendto(sockfd, buf, flags, dest_addr, addrlen) catch |err| switch (err) {
error.WouldBlock => {
diff --git a/lib/std/event/rwlock.zig b/lib/std/event/rwlock.zig
index 3e3928d379..750131beda 100644
--- a/lib/std/event/rwlock.zig
+++ b/lib/std/event/rwlock.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/event/rwlocked.zig b/lib/std/event/rwlocked.zig
index 4fb25b59a1..0272ca39c7 100644
--- a/lib/std/event/rwlocked.zig
+++ b/lib/std/event/rwlocked.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/event/wait_group.zig b/lib/std/event/wait_group.zig
index c59e3b233d..0b83c18c74 100644
--- a/lib/std/event/wait_group.zig
+++ b/lib/std/event/wait_group.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -30,7 +30,7 @@ pub fn WaitGroupGeneric(comptime counter_size: u16) type {
return struct {
counter: CounterType = 0,
max_counter: CounterType = std.math.maxInt(CounterType),
- mutex: std.Mutex = .{},
+ mutex: std.Thread.Mutex = .{},
waiters: ?*Waiter = null,
const Waiter = struct {
next: ?*Waiter,
diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig
index 9a7d2209f5..b0771bba16 100644
--- a/lib/std/fifo.zig
+++ b/lib/std/fifo.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -44,6 +44,8 @@ pub fn LinearFifo(
count: usize,
const Self = @This();
+ pub const Reader = std.io.Reader(*Self, error{}, readFn);
+ pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
// Type of Self argument for slice operations.
// If buffer is inline (Static) then we need to ensure we haven't
@@ -153,7 +155,7 @@ pub fn LinearFifo(
var start = self.head + offset;
if (start >= self.buf.len) {
start -= self.buf.len;
- return self.buf[start .. self.count - offset];
+ return self.buf[start .. start + (self.count - offset)];
} else {
const end = math.min(self.head + self.count, self.buf.len);
return self.buf[start..end];
@@ -228,12 +230,7 @@ pub fn LinearFifo(
return self.read(dest);
}
- pub fn reader(self: *Self) std.io.Reader(*Self, error{}, readFn) {
- return .{ .context = self };
- }
-
- /// Deprecated: `use reader`
- pub fn inStream(self: *Self) std.io.InStream(*Self, error{}, readFn) {
+ pub fn reader(self: *Self) Reader {
return .{ .context = self };
}
@@ -317,18 +314,13 @@ pub fn LinearFifo(
}
/// Same as `write` except it returns the number of bytes written, which is always the same
- /// as `bytes.len`. The purpose of this function existing is to match `std.io.OutStream` API.
+ /// as `bytes.len`. The purpose of this function existing is to match `std.io.Writer` API.
fn appendWrite(self: *Self, bytes: []const u8) error{OutOfMemory}!usize {
try self.write(bytes);
return bytes.len;
}
- pub fn writer(self: *Self) std.io.Writer(*Self, error{OutOfMemory}, appendWrite) {
- return .{ .context = self };
- }
-
- /// Deprecated: `use writer`
- pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) {
+ pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
@@ -437,6 +429,8 @@ test "LinearFifo(u8, .Dynamic)" {
fifo.writeAssumeCapacity("6<chars<11");
testing.expectEqualSlices(u8, "HELLO6<char", fifo.readableSlice(0));
testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(11));
+ testing.expectEqualSlices(u8, "11", fifo.readableSlice(13));
+ testing.expectEqualSlices(u8, "", fifo.readableSlice(15));
fifo.discard(11);
testing.expectEqualSlices(u8, "s<11", fifo.readableSlice(0));
fifo.discard(4);
@@ -466,7 +460,7 @@ test "LinearFifo(u8, .Dynamic)" {
fifo.shrink(0);
{
- try fifo.writer().print("{}, {}!", .{ "Hello", "World" });
+ try fifo.writer().print("{s}, {s}!", .{ "Hello", "World" });
var result: [30]u8 = undefined;
testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]);
testing.expectEqual(@as(usize, 0), fifo.readableLength());
diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig
index 485303518a..fca21000cf 100644
--- a/lib/std/fmt.zig
+++ b/lib/std/fmt.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -12,6 +12,7 @@ const meta = std.meta;
const builtin = @import("builtin");
const errol = @import("fmt/errol.zig");
const lossyCast = std.math.lossyCast;
+const expectFmt = std.testing.expectFmt;
pub const default_max_depth = 3;
@@ -68,6 +69,7 @@ pub const FormatOptions = struct {
/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max.
/// - `u`: output integer as an UTF-8 sequence. Integer type must have 21 bits at max.
/// - `*`: output the address of the value instead of the value itself.
+/// - `any`: output a value of any type using its default format
///
/// If a formatted user type contains a function of the type
/// ```
@@ -367,6 +369,51 @@ pub fn format(
}
}
+pub fn formatAddress(value: anytype, options: FormatOptions, writer: anytype) @TypeOf(writer).Error!void {
+ const T = @TypeOf(value);
+
+ switch (@typeInfo(T)) {
+ .Pointer => |info| {
+ try writer.writeAll(@typeName(info.child) ++ "@");
+ if (info.size == .Slice)
+ try formatInt(@ptrToInt(value.ptr), 16, false, FormatOptions{}, writer)
+ else
+ try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
+ return;
+ },
+ .Optional => |info| {
+ if (@typeInfo(info.child) == .Pointer) {
+ try writer.writeAll(@typeName(info.child) ++ "@");
+ try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
+ return;
+ }
+ },
+ else => {},
+ }
+
+ @compileError("Cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier");
+}
+
+// This ANY const is a workaround for: https://github.com/ziglang/zig/issues/7948
+const ANY = "any";
+
+fn defaultSpec(comptime T: type) [:0]const u8 {
+ switch (@typeInfo(T)) {
+ .Array => |_| return ANY,
+ .Pointer => |ptr_info| switch (ptr_info.size) {
+ .One => switch (@typeInfo(ptr_info.child)) {
+ .Array => |_| return "*",
+ else => {},
+ },
+ .Many, .C => return "*",
+ .Slice => return ANY,
+ },
+ .Optional => |info| return defaultSpec(info.child),
+ else => {},
+ }
+ return "";
+}
+
pub fn formatType(
value: anytype,
comptime fmt: []const u8,
@@ -374,21 +421,19 @@ pub fn formatType(
writer: anytype,
max_depth: usize,
) @TypeOf(writer).Error!void {
- if (comptime std.mem.eql(u8, fmt, "*")) {
- try writer.writeAll(@typeName(std.meta.Child(@TypeOf(value))));
- try writer.writeAll("@");
- try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
- return;
+ const actual_fmt = comptime if (std.mem.eql(u8, fmt, ANY)) defaultSpec(@TypeOf(value)) else fmt;
+ if (comptime std.mem.eql(u8, actual_fmt, "*")) {
+ return formatAddress(value, options, writer);
}
const T = @TypeOf(value);
if (comptime std.meta.trait.hasFn("format")(T)) {
- return try value.format(fmt, options, writer);
+ return try value.format(actual_fmt, options, writer);
}
switch (@typeInfo(T)) {
.ComptimeInt, .Int, .ComptimeFloat, .Float => {
- return formatValue(value, fmt, options, writer);
+ return formatValue(value, actual_fmt, options, writer);
},
.Void => {
return formatBuf("void", options, writer);
@@ -398,16 +443,16 @@ pub fn formatType(
},
.Optional => {
if (value) |payload| {
- return formatType(payload, fmt, options, writer, max_depth);
+ return formatType(payload, actual_fmt, options, writer, max_depth);
} else {
return formatBuf("null", options, writer);
}
},
.ErrorUnion => {
if (value) |payload| {
- return formatType(payload, fmt, options, writer, max_depth);
+ return formatType(payload, actual_fmt, options, writer, max_depth);
} else |err| {
- return formatType(err, fmt, options, writer, max_depth);
+ return formatType(err, actual_fmt, options, writer, max_depth);
}
},
.ErrorSet => {
@@ -433,22 +478,21 @@ pub fn formatType(
}
try writer.writeAll("(");
- try formatType(@enumToInt(value), fmt, options, writer, max_depth);
+ try formatType(@enumToInt(value), actual_fmt, options, writer, max_depth);
try writer.writeAll(")");
},
- .Union => {
+ .Union => |info| {
try writer.writeAll(@typeName(T));
if (max_depth == 0) {
return writer.writeAll("{ ... }");
}
- const info = @typeInfo(T).Union;
if (info.tag_type) |UnionTagType| {
try writer.writeAll("{ .");
try writer.writeAll(@tagName(@as(UnionTagType, value)));
try writer.writeAll(" = ");
inline for (info.fields) |u_field| {
if (value == @field(UnionTagType, u_field.name)) {
- try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1);
+ try formatType(@field(value, u_field.name), ANY, options, writer, max_depth - 1);
}
}
try writer.writeAll(" }");
@@ -456,13 +500,13 @@ pub fn formatType(
try format(writer, "@{x}", .{@ptrToInt(&value)});
}
},
- .Struct => |StructT| {
+ .Struct => |info| {
try writer.writeAll(@typeName(T));
if (max_depth == 0) {
return writer.writeAll("{ ... }");
}
try writer.writeAll("{");
- inline for (StructT.fields) |f, i| {
+ inline for (info.fields) |f, i| {
if (i == 0) {
try writer.writeAll(" .");
} else {
@@ -470,77 +514,99 @@ pub fn formatType(
}
try writer.writeAll(f.name);
try writer.writeAll(" = ");
- try formatType(@field(value, f.name), fmt, options, writer, max_depth - 1);
+ try formatType(@field(value, f.name), ANY, options, writer, max_depth - 1);
}
try writer.writeAll(" }");
},
.Pointer => |ptr_info| switch (ptr_info.size) {
.One => switch (@typeInfo(ptr_info.child)) {
.Array => |info| {
+ if (actual_fmt.len == 0)
+ @compileError("cannot format array ref without a specifier (i.e. {s} or {*})");
if (info.child == u8) {
- return formatText(value, fmt, options, writer);
+ if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) {
+ return formatText(value, actual_fmt, options, writer);
+ }
}
- return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) });
+ @compileError("Unknown format string: '" ++ actual_fmt ++ "'");
},
.Enum, .Union, .Struct => {
- return formatType(value.*, fmt, options, writer, max_depth);
+ return formatType(value.*, actual_fmt, options, writer, max_depth);
},
- else => return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) }),
+ else => return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }),
},
.Many, .C => {
+ if (actual_fmt.len == 0)
+ @compileError("cannot format pointer without a specifier (i.e. {s} or {*})");
if (ptr_info.sentinel) |sentinel| {
- return formatType(mem.span(value), fmt, options, writer, max_depth);
+ return formatType(mem.span(value), actual_fmt, options, writer, max_depth);
}
if (ptr_info.child == u8) {
- if (fmt.len > 0 and fmt[0] == 's') {
- return formatText(mem.span(value), fmt, options, writer);
+ if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) {
+ return formatText(mem.span(value), actual_fmt, options, writer);
}
}
- return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) });
+ @compileError("Unknown format string: '" ++ actual_fmt ++ "'");
},
.Slice => {
- if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
- return formatText(value, fmt, options, writer);
+ if (actual_fmt.len == 0)
+ @compileError("cannot format slice without a specifier (i.e. {s} or {any})");
+ if (max_depth == 0) {
+ return writer.writeAll("{ ... }");
}
if (ptr_info.child == u8) {
- return formatText(value, fmt, options, writer);
+ if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) {
+ return formatText(value, actual_fmt, options, writer);
+ }
+ }
+ try writer.writeAll("{ ");
+ for (value) |elem, i| {
+ try formatType(elem, actual_fmt, options, writer, max_depth - 1);
+ if (i != value.len - 1) {
+ try writer.writeAll(", ");
+ }
}
- return format(writer, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) });
+ try writer.writeAll(" }");
},
},
.Array => |info| {
- const Slice = @Type(builtin.TypeInfo{
- .Pointer = .{
- .size = .Slice,
- .is_const = true,
- .is_volatile = false,
- .is_allowzero = false,
- .alignment = @alignOf(info.child),
- .child = info.child,
- .sentinel = null,
- },
- });
- return formatType(@as(Slice, &value), fmt, options, writer, max_depth);
+ if (actual_fmt.len == 0)
+ @compileError("cannot format array without a specifier (i.e. {s} or {any})");
+ if (max_depth == 0) {
+ return writer.writeAll("{ ... }");
+ }
+ if (info.child == u8) {
+ if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) {
+ return formatText(&value, actual_fmt, options, writer);
+ }
+ }
+ try writer.writeAll("{ ");
+ for (value) |elem, i| {
+ try formatType(elem, actual_fmt, options, writer, max_depth - 1);
+ if (i < value.len - 1) {
+ try writer.writeAll(", ");
+ }
+ }
+ try writer.writeAll(" }");
},
- .Vector => {
- const len = @typeInfo(T).Vector.len;
+ .Vector => |info| {
try writer.writeAll("{ ");
var i: usize = 0;
- while (i < len) : (i += 1) {
- try formatValue(value[i], fmt, options, writer);
- if (i < len - 1) {
+ while (i < info.len) : (i += 1) {
+ try formatValue(value[i], actual_fmt, options, writer);
+ if (i < info.len - 1) {
try writer.writeAll(", ");
}
}
try writer.writeAll(" }");
},
.Fn => {
- return format(writer, "{}@{x}", .{ @typeName(T), @ptrToInt(value) });
+ return format(writer, "{s}@{x}", .{ @typeName(T), @ptrToInt(value) });
},
.Type => return formatBuf(@typeName(value), options, writer),
.EnumLiteral => {
const buffer = [_]u8{'.'} ++ @tagName(value);
- return formatType(buffer, fmt, options, writer, max_depth);
+ return formatBuf(buffer, options, writer);
},
.Null => return formatBuf("null", options, writer),
else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
@@ -580,8 +646,7 @@ pub fn formatIntValue(
const int_value = if (@TypeOf(value) == comptime_int) blk: {
const Int = math.IntFittingRange(value, value);
break :blk @as(Int, value);
- } else
- value;
+ } else value;
if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) {
radix = 10;
@@ -592,13 +657,6 @@ pub fn formatIntValue(
} else {
@compileError("Cannot print integer that is larger than 8 bits as a ascii");
}
- } else if (comptime std.mem.eql(u8, fmt, "Z")) {
- if (@typeInfo(@TypeOf(int_value)).Int.bits <= 8) {
- const c: u8 = int_value;
- return formatZigEscapes(@as(*const [1]u8, &c), options, writer);
- } else {
- @compileError("Cannot escape character with more than 8 bits");
- }
} else if (comptime std.mem.eql(u8, fmt, "u")) {
if (@typeInfo(@TypeOf(int_value)).Int.bits <= 21) {
return formatUnicodeCodepoint(@as(u21, int_value), options, writer);
@@ -618,7 +676,7 @@ pub fn formatIntValue(
radix = 8;
uppercase = false;
} else {
- @compileError("Unknown format string: '" ++ fmt ++ "'");
+ @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
}
return formatInt(int_value, radix, uppercase, options, writer);
@@ -645,7 +703,7 @@ fn formatFloatValue(
else => |e| return e,
};
} else {
- @compileError("Unknown format string: '" ++ fmt ++ "'");
+ @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
}
return formatBuf(buf_stream.getWritten(), options, writer);
@@ -657,7 +715,7 @@ pub fn formatText(
options: FormatOptions,
writer: anytype,
) !void {
- if (comptime std.mem.eql(u8, fmt, "s") or (fmt.len == 0)) {
+ if (comptime std.mem.eql(u8, fmt, "s")) {
return formatBuf(bytes, options, writer);
} else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {
for (bytes) |c| {
@@ -674,12 +732,8 @@ pub fn formatText(
}
}
return;
- } else if (comptime std.mem.eql(u8, fmt, "z")) {
- return formatZigIdentifier(bytes, options, writer);
- } else if (comptime std.mem.eql(u8, fmt, "Z")) {
- return formatZigEscapes(bytes, options, writer);
} else {
- @compileError("Unknown format string: '" ++ fmt ++ "'");
+ @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
}
}
@@ -742,52 +796,6 @@ pub fn formatBuf(
}
}
-/// Print the string as a Zig identifier escaping it with @"" syntax if needed.
-pub fn formatZigIdentifier(
- bytes: []const u8,
- options: FormatOptions,
- writer: anytype,
-) !void {
- if (isValidZigIdentifier(bytes)) {
- return writer.writeAll(bytes);
- }
- try writer.writeAll("@\"");
- try formatZigEscapes(bytes, options, writer);
- try writer.writeByte('"');
-}
-
-fn isValidZigIdentifier(bytes: []const u8) bool {
- for (bytes) |c, i| {
- switch (c) {
- '_', 'a'...'z', 'A'...'Z' => {},
- '0'...'9' => if (i == 0) return false,
- else => return false,
- }
- }
- return std.zig.Token.getKeyword(bytes) == null;
-}
-
-pub fn formatZigEscapes(
- bytes: []const u8,
- options: FormatOptions,
- writer: anytype,
-) !void {
- for (bytes) |byte| switch (byte) {
- '\n' => try writer.writeAll("\\n"),
- '\r' => try writer.writeAll("\\r"),
- '\t' => try writer.writeAll("\\t"),
- '\\' => try writer.writeAll("\\\\"),
- '"' => try writer.writeAll("\\\""),
- '\'' => try writer.writeAll("\\'"),
- ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte),
- // Use hex escapes for rest any unprintable characters.
- else => {
- try writer.writeAll("\\x");
- try formatInt(byte, 16, false, .{ .width = 2, .fill = '0' }, writer);
- },
- };
-}
-
/// Print a float in scientific notation to the specified precision. Null uses full precision.
/// It should be the case that every full precision, printed value can be re-parsed back to the
/// same type unambiguously.
@@ -1078,8 +1086,7 @@ pub fn formatInt(
const int_value = if (@TypeOf(value) == comptime_int) blk: {
const Int = math.IntFittingRange(value, value);
break :blk @as(Int, value);
- } else
- value;
+ } else value;
const value_info = @typeInfo(@TypeOf(int_value)).Int;
@@ -1125,6 +1132,103 @@ pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, op
return fbs.pos;
}
+/// Formats a number of nanoseconds according to its magnitude:
+///
+/// - #ns
+/// - [#y][#w][#d][#h][#m]#[.###][u|m]s
+pub fn formatDuration(ns: u64, writer: anytype) !void {
+ var ns_remaining = ns;
+ inline for (.{
+ .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' },
+ .{ .ns = std.time.ns_per_week, .sep = 'w' },
+ .{ .ns = std.time.ns_per_day, .sep = 'd' },
+ .{ .ns = std.time.ns_per_hour, .sep = 'h' },
+ .{ .ns = std.time.ns_per_min, .sep = 'm' },
+ }) |unit| {
+ if (ns_remaining >= unit.ns) {
+ const units = ns_remaining / unit.ns;
+ try formatInt(units, 10, false, .{}, writer);
+ try writer.writeByte(unit.sep);
+ ns_remaining -= units * unit.ns;
+ if (ns_remaining == 0) return;
+ }
+ }
+
+ inline for (.{
+ .{ .ns = std.time.ns_per_s, .sep = "s" },
+ .{ .ns = std.time.ns_per_ms, .sep = "ms" },
+ .{ .ns = std.time.ns_per_us, .sep = "us" },
+ }) |unit| {
+ const kunits = ns_remaining * 1000 / unit.ns;
+ if (kunits >= 1000) {
+ try formatInt(kunits / 1000, 10, false, .{}, writer);
+ if (kunits > 1000) {
+ // Write up to 3 decimal places
+ const frac = kunits % 1000;
+ var buf = [_]u8{ '.', 0, 0, 0 };
+ _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 });
+ var end: usize = 4;
+ while (end > 1) : (end -= 1) {
+ if (buf[end - 1] != '0') break;
+ }
+ try writer.writeAll(buf[0..end]);
+ }
+ try writer.writeAll(unit.sep);
+ return;
+ }
+ }
+
+ try formatInt(ns, 10, false, .{}, writer);
+ try writer.writeAll("ns");
+ return;
+}
+
+test "formatDuration" {
+ var buf: [24]u8 = undefined;
+ inline for (.{
+ .{ .s = "0ns", .d = 0 },
+ .{ .s = "1ns", .d = 1 },
+ .{ .s = "999ns", .d = std.time.ns_per_us - 1 },
+ .{ .s = "1us", .d = std.time.ns_per_us },
+ .{ .s = "1.45us", .d = 1450 },
+ .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 },
+ .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 },
+ .{ .s = "1ms", .d = std.time.ns_per_ms + 1 },
+ .{ .s = "1.5ms", .d = 3 * std.time.ns_per_ms / 2 },
+ .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 },
+ .{ .s = "1s", .d = std.time.ns_per_s },
+ .{ .s = "59.999s", .d = std.time.ns_per_min - 1 },
+ .{ .s = "1m", .d = std.time.ns_per_min },
+ .{ .s = "1h", .d = std.time.ns_per_hour },
+ .{ .s = "1d", .d = std.time.ns_per_day },
+ .{ .s = "1w", .d = std.time.ns_per_week },
+ .{ .s = "1y", .d = 365 * std.time.ns_per_day },
+ .{ .s = "1y52w23h59m59.999s", .d = 730 * std.time.ns_per_day - 1 }, // 365d = 52w1d
+ .{ .s = "1y1h1.001s", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms },
+ .{ .s = "1y1h1s", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us },
+ .{ .s = "1y1h999.999us", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1 },
+ .{ .s = "1y1h1ms", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms },
+ .{ .s = "1y1h1ms", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1 },
+ }) |tc| {
+ const slice = try bufPrint(&buf, "{}", .{duration(tc.d)});
+ std.testing.expectEqualStrings(tc.s, slice);
+ }
+}
+
+/// Wraps a `u64` to format with `formatDuration`.
+const Duration = struct {
+ ns: u64,
+
+ pub fn format(self: Duration, comptime fmt: []const u8, options: FormatOptions, writer: anytype) !void {
+ return formatDuration(self.ns, writer);
+ }
+};
+
+/// Formats a number of nanoseconds according to its magnitude. See `formatDuration`.
+pub fn duration(ns: u64) Duration {
+ return Duration{ .ns = ns };
+}
+
pub const ParseIntError = error{
/// The result cannot fit in the type specified
Overflow,
@@ -1133,6 +1237,32 @@ pub const ParseIntError = error{
InvalidCharacter,
};
+/// Creates a Formatter type from a format function. Wrapping data in Formatter(func) causes
+/// the data to be formatted using the given function `func`. `func` must be of the following
+/// form:
+///
+/// fn formatExample(
+/// data: T,
+/// comptime fmt: []const u8,
+/// options: std.fmt.FormatOptions,
+/// writer: anytype,
+/// ) !void;
+///
+pub fn Formatter(comptime format_fn: anytype) type {
+ const Data = @typeInfo(@TypeOf(format_fn)).Fn.args[0].arg_type.?;
+ return struct {
+ data: Data,
+ pub fn format(
+ self: @This(),
+ comptime fmt: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) @TypeOf(writer).Error!void {
+ try format_fn(self.data, fmt, options, writer);
+ }
+ };
+}
+
/// Parses the string `buf` as signed or unsigned representation in the
/// specified radix of an integral value of type `T`.
///
@@ -1414,91 +1544,91 @@ test "parse unsigned comptime" {
}
test "escaped braces" {
- try testFmt("escaped: {{foo}}\n", "escaped: {{{{foo}}}}\n", .{});
- try testFmt("escaped: {foo}\n", "escaped: {{foo}}\n", .{});
+ try expectFmt("escaped: {{foo}}\n", "escaped: {{{{foo}}}}\n", .{});
+ try expectFmt("escaped: {foo}\n", "escaped: {{foo}}\n", .{});
}
test "optional" {
{
const value: ?i32 = 1234;
- try testFmt("optional: 1234\n", "optional: {}\n", .{value});
+ try expectFmt("optional: 1234\n", "optional: {}\n", .{value});
}
{
const value: ?i32 = null;
- try testFmt("optional: null\n", "optional: {}\n", .{value});
+ try expectFmt("optional: null\n", "optional: {}\n", .{value});
}
{
const value = @intToPtr(?*i32, 0xf000d000);
- try testFmt("optional: *i32@f000d000\n", "optional: {*}\n", .{value});
+ try expectFmt("optional: *i32@f000d000\n", "optional: {*}\n", .{value});
}
}
test "error" {
{
const value: anyerror!i32 = 1234;
- try testFmt("error union: 1234\n", "error union: {}\n", .{value});
+ try expectFmt("error union: 1234\n", "error union: {}\n", .{value});
}
{
const value: anyerror!i32 = error.InvalidChar;
- try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value});
+ try expectFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value});
}
}
test "int.small" {
{
const value: u3 = 0b101;
- try testFmt("u3: 5\n", "u3: {}\n", .{value});
+ try expectFmt("u3: 5\n", "u3: {}\n", .{value});
}
}
test "int.specifier" {
{
const value: u8 = 'a';
- try testFmt("u8: a\n", "u8: {c}\n", .{value});
+ try expectFmt("u8: a\n", "u8: {c}\n", .{value});
}
{
const value: u8 = 0b1100;
- try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value});
+ try expectFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value});
}
{
const value: u16 = 0o1234;
- try testFmt("u16: 0o1234\n", "u16: 0o{o}\n", .{value});
+ try expectFmt("u16: 0o1234\n", "u16: 0o{o}\n", .{value});
}
{
const value: u8 = 'a';
- try testFmt("UTF-8: a\n", "UTF-8: {u}\n", .{value});
+ try expectFmt("UTF-8: a\n", "UTF-8: {u}\n", .{value});
}
{
const value: u21 = 0x1F310;
- try testFmt("UTF-8: 🌐\n", "UTF-8: {u}\n", .{value});
+ try expectFmt("UTF-8: 🌐\n", "UTF-8: {u}\n", .{value});
}
{
const value: u21 = 0xD800;
- try testFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value});
+ try expectFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value});
}
{
const value: u21 = 0x110001;
- try testFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value});
+ try expectFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value});
}
}
test "int.padded" {
- try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)});
- try testFmt("u8: '1000'", "u8: '{:0<4}'", .{@as(u8, 1)});
- try testFmt("u8: '0001'", "u8: '{:0>4}'", .{@as(u8, 1)});
- try testFmt("u8: '0100'", "u8: '{:0^4}'", .{@as(u8, 1)});
- try testFmt("i8: '-1 '", "i8: '{:<4}'", .{@as(i8, -1)});
- try testFmt("i8: ' -1'", "i8: '{:>4}'", .{@as(i8, -1)});
- try testFmt("i8: ' -1 '", "i8: '{:^4}'", .{@as(i8, -1)});
- try testFmt("i16: '-1234'", "i16: '{:4}'", .{@as(i16, -1234)});
- try testFmt("i16: '+1234'", "i16: '{:4}'", .{@as(i16, 1234)});
- try testFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)});
- try testFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)});
- try testFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)});
-
- try testFmt("UTF-8: 'ü '", "UTF-8: '{u:<4}'", .{'ü'});
- try testFmt("UTF-8: ' ü'", "UTF-8: '{u:>4}'", .{'ü'});
- try testFmt("UTF-8: ' ü '", "UTF-8: '{u:^4}'", .{'ü'});
+ try expectFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)});
+ try expectFmt("u8: '1000'", "u8: '{:0<4}'", .{@as(u8, 1)});
+ try expectFmt("u8: '0001'", "u8: '{:0>4}'", .{@as(u8, 1)});
+ try expectFmt("u8: '0100'", "u8: '{:0^4}'", .{@as(u8, 1)});
+ try expectFmt("i8: '-1 '", "i8: '{:<4}'", .{@as(i8, -1)});
+ try expectFmt("i8: ' -1'", "i8: '{:>4}'", .{@as(i8, -1)});
+ try expectFmt("i8: ' -1 '", "i8: '{:^4}'", .{@as(i8, -1)});
+ try expectFmt("i16: '-1234'", "i16: '{:4}'", .{@as(i16, -1234)});
+ try expectFmt("i16: '+1234'", "i16: '{:4}'", .{@as(i16, 1234)});
+ try expectFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)});
+ try expectFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)});
+ try expectFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)});
+
+ try expectFmt("UTF-8: 'ü '", "UTF-8: '{u:<4}'", .{'ü'});
+ try expectFmt("UTF-8: ' ü'", "UTF-8: '{u:>4}'", .{'ü'});
+ try expectFmt("UTF-8: ' ü '", "UTF-8: '{u:^4}'", .{'ü'});
}
test "buffer" {
@@ -1521,11 +1651,12 @@ test "buffer" {
test "array" {
{
const value: [3]u8 = "abc".*;
- try testFmt("array: abc\n", "array: {}\n", .{value});
- try testFmt("array: abc\n", "array: {}\n", .{&value});
+ try expectFmt("array: abc\n", "array: {s}\n", .{value});
+ try expectFmt("array: abc\n", "array: {s}\n", .{&value});
+ try expectFmt("array: { 97, 98, 99 }\n", "array: {d}\n", .{value});
var buf: [100]u8 = undefined;
- try testFmt(
+ try expectFmt(
try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}),
"array: {*}\n",
.{&value},
@@ -1536,62 +1667,60 @@ test "array" {
test "slice" {
{
const value: []const u8 = "abc";
- try testFmt("slice: abc\n", "slice: {}\n", .{value});
+ try expectFmt("slice: abc\n", "slice: {s}\n", .{value});
}
{
var runtime_zero: usize = 0;
const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[runtime_zero..runtime_zero];
- try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value});
+ try expectFmt("slice: []const u8@deadbeef\n", "slice: {*}\n", .{value});
}
{
const null_term_slice: [:0]const u8 = "\x00hello\x00";
- try testFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice});
+ try expectFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice});
}
- try testFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"});
- try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});
-}
+ try expectFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"});
+ try expectFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});
-test "escape non-printable" {
- try testFmt("abc", "{e}", .{"abc"});
- try testFmt("ab\\xffc", "{e}", .{"ab\xffc"});
- try testFmt("ab\\xFFc", "{E}", .{"ab\xffc"});
+ {
+ var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 };
+ var runtime_zero: usize = 0;
+ try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{int_slice[runtime_zero..]});
+ try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]});
+ try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]});
+ try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]});
+ }
}
-test "escape invalid identifiers" {
- try testFmt("@\"while\"", "{z}", .{"while"});
- try testFmt("hello", "{z}", .{"hello"});
- try testFmt("@\"11\\\"23\"", "{z}", .{"11\"23"});
- try testFmt("@\"11\\x0f23\"", "{z}", .{"11\x0F23"});
- try testFmt("\\x0f", "{Z}", .{0x0f});
- try testFmt(
- \\" \\ hi \x07 \x11 \" derp \'"
- , "\"{Z}\"", .{" \\ hi \x07 \x11 \" derp '"});
+test "escape non-printable" {
+ try expectFmt("abc", "{e}", .{"abc"});
+ try expectFmt("ab\\xffc", "{e}", .{"ab\xffc"});
+ try expectFmt("ab\\xFFc", "{E}", .{"ab\xffc"});
}
test "pointer" {
{
const value = @intToPtr(*align(1) i32, 0xdeadbeef);
- try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value});
- try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value});
+ try expectFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value});
+ try expectFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value});
}
{
const value = @intToPtr(fn () void, 0xdeadbeef);
- try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value});
+ try expectFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value});
}
{
const value = @intToPtr(fn () void, 0xdeadbeef);
- try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value});
+ try expectFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value});
}
}
test "cstr" {
- try testFmt(
+ try expectFmt(
"cstr: Test C\n",
"cstr: {s}\n",
.{@ptrCast([*c]const u8, "Test C")},
);
- try testFmt(
+ try expectFmt(
"cstr: Test C\n",
"cstr: {s:10}\n",
.{@ptrCast([*c]const u8, "Test C")},
@@ -1599,8 +1728,8 @@ test "cstr" {
}
test "filesize" {
- try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)});
- try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)});
+ try expectFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)});
+ try expectFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)});
}
test "struct" {
@@ -1609,8 +1738,8 @@ test "struct" {
field: u8,
};
const value = Struct{ .field = 42 };
- try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value});
- try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value});
+ try expectFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value});
+ try expectFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value});
}
{
const Struct = struct {
@@ -1618,7 +1747,7 @@ test "struct" {
b: u1,
};
const value = Struct{ .a = 0, .b = 1 };
- try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value});
+ try expectFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value});
}
}
@@ -1628,13 +1757,13 @@ test "enum" {
Two,
};
const value = Enum.Two;
- try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value});
- try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value});
- try testFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One});
- try testFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two});
+ try expectFmt("enum: Enum.Two\n", "enum: {}\n", .{value});
+ try expectFmt("enum: Enum.Two\n", "enum: {}\n", .{&value});
+ try expectFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One});
+ try expectFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two});
// test very large enum to verify ct branch quota is large enough
- try testFmt("enum: Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION});
+ try expectFmt("enum: Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION});
}
test "non-exhaustive enum" {
@@ -1643,78 +1772,78 @@ test "non-exhaustive enum" {
Two = 0xbeef,
_,
};
- try testFmt("enum: Enum.One\n", "enum: {}\n", .{Enum.One});
- try testFmt("enum: Enum.Two\n", "enum: {}\n", .{Enum.Two});
- try testFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)});
- try testFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One});
- try testFmt("enum: Enum.Two\n", "enum: {x}\n", .{Enum.Two});
- try testFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two});
- try testFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)});
+ try expectFmt("enum: Enum.One\n", "enum: {}\n", .{Enum.One});
+ try expectFmt("enum: Enum.Two\n", "enum: {}\n", .{Enum.Two});
+ try expectFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)});
+ try expectFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One});
+ try expectFmt("enum: Enum.Two\n", "enum: {x}\n", .{Enum.Two});
+ try expectFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two});
+ try expectFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)});
}
test "float.scientific" {
- try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)});
- try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)});
- try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)});
- try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)});
+ try expectFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)});
+ try expectFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)});
+ try expectFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)});
+ try expectFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)});
}
test "float.scientific.precision" {
- try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)});
- try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))});
- try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))});
+ try expectFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)});
+ try expectFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))});
+ try expectFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))});
// libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
// In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
- try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))});
+ try expectFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))});
}
test "float.special" {
- try testFmt("f64: nan", "f64: {}", .{math.nan_f64});
+ try expectFmt("f64: nan", "f64: {}", .{math.nan_f64});
// negative nan is not defined by IEE 754,
// and ARM thus normalizes it to positive nan
if (builtin.arch != builtin.Arch.arm) {
- try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64});
+ try expectFmt("f64: -nan", "f64: {}", .{-math.nan_f64});
}
- try testFmt("f64: inf", "f64: {}", .{math.inf_f64});
- try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64});
+ try expectFmt("f64: inf", "f64: {}", .{math.inf_f64});
+ try expectFmt("f64: -inf", "f64: {}", .{-math.inf_f64});
}
test "float.decimal" {
- try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)});
- try testFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)});
- try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)});
- try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)});
+ try expectFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)});
+ try expectFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)});
+ try expectFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)});
+ try expectFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)});
// -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
// -11.12339... is rounded back up to -11.1234
- try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)});
- try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)});
- try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)});
- try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)});
- try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)});
- try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)});
- try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)});
- try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)});
- try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)});
- try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)});
+ try expectFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)});
+ try expectFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)});
+ try expectFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)});
+ try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)});
+ try expectFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)});
+ try expectFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)});
+ try expectFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)});
+ try expectFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)});
+ try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)});
+ try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)});
}
test "float.libc.sanity" {
- try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))});
- try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))});
- try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))});
- try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))});
- try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))});
+ try expectFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))});
+ try expectFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))});
+ try expectFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))});
+ try expectFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))});
+ try expectFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))});
// libc differences
//
// This is 0.015625 exactly according to gdb. We thus round down,
// however glibc rounds up for some reason. This occurs for all
// floats of the form x.yyyy25 on a precision point.
- try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))});
+ try expectFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))});
// errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
// also rounds to 630 so I'm inclined to believe libc is not
// optimal here.
- try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))});
+ try expectFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))});
}
test "custom" {
@@ -1744,12 +1873,12 @@ test "custom" {
.x = 10.2,
.y = 2.22,
};
- try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value});
- try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value});
+ try expectFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value});
+ try expectFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value});
// same thing but not passing a pointer
- try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value});
- try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value});
+ try expectFmt("point: (10.200,2.220)\n", "point: {}\n", .{value});
+ try expectFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value});
}
test "struct" {
@@ -1763,7 +1892,7 @@ test "struct" {
.b = error.Unused,
};
- try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst});
+ try expectFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst});
}
test "union" {
@@ -1786,7 +1915,7 @@ test "union" {
const uu_inst = UU{ .int = 456 };
const eu_inst = EU{ .float = 321.123 };
- try testFmt("TU{ .int = 123 }", "{}", .{tu_inst});
+ try expectFmt("TU{ .int = 123 }", "{}", .{tu_inst});
var buf: [100]u8 = undefined;
const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst});
@@ -1805,7 +1934,7 @@ test "enum" {
const inst = E.Two;
- try testFmt("E.Two", "{}", .{inst});
+ try expectFmt("E.Two", "{}", .{inst});
}
test "struct.self-referential" {
@@ -1819,7 +1948,7 @@ test "struct.self-referential" {
};
inst.a = &inst;
- try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst});
+ try expectFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst});
}
test "struct.zero-size" {
@@ -1834,53 +1963,51 @@ test "struct.zero-size" {
const a = A{};
const b = B{ .a = a, .c = 0 };
- try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b});
+ try expectFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b});
}
test "bytes.hex" {
const some_bytes = "\xCA\xFE\xBA\xBE";
- try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});
- try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes});
+ try expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});
+ try expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes});
//Test Slices
- try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]});
- try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]});
+ try expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]});
+ try expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]});
const bytes_with_zeros = "\x00\x0E\xBA\xBE";
- try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});
-}
-
-fn testFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {
- var buf: [100]u8 = undefined;
- const result = try bufPrint(buf[0..], template, args);
- if (mem.eql(u8, result, expected)) return;
-
- std.debug.warn("\n====== expected this output: =========\n", .{});
- std.debug.warn("{}", .{expected});
- std.debug.warn("\n======== instead found this: =========\n", .{});
- std.debug.warn("{}", .{result});
- std.debug.warn("\n======================================\n", .{});
- return error.TestFailed;
+ try expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});
}
pub const trim = @compileError("deprecated; use std.mem.trim with std.ascii.spaces instead");
pub const isWhiteSpace = @compileError("deprecated; use std.ascii.isSpace instead");
-pub fn hexToBytes(out: []u8, input: []const u8) !void {
- if (out.len * 2 < input.len)
+/// Decodes the sequence of bytes represented by the specified string of
+/// hexadecimal characters.
+/// Returns a slice of the output buffer containing the decoded bytes.
+pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 {
+ // Expect 0 or n pairs of hexadecimal digits.
+ if (input.len & 1 != 0)
return error.InvalidLength;
+ if (out.len * 2 < input.len)
+ return error.NoSpaceLeft;
var in_i: usize = 0;
- while (in_i != input.len) : (in_i += 2) {
+ while (in_i < input.len) : (in_i += 2) {
const hi = try charToDigit(input[in_i], 16);
const lo = try charToDigit(input[in_i + 1], 16);
out[in_i / 2] = (hi << 4) | lo;
}
+
+ return out[0 .. in_i / 2];
}
test "hexToBytes" {
- const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706";
- var pb: [32]u8 = undefined;
- try hexToBytes(pb[0..], test_hex_str);
- try testFmt(test_hex_str, "{X}", .{pb});
+ var buf: [32]u8 = undefined;
+ try expectFmt("90" ** 32, "{X}", .{try hexToBytes(&buf, "90" ** 32)});
+ try expectFmt("ABCD", "{X}", .{try hexToBytes(&buf, "ABCD")});
+ try expectFmt("", "{X}", .{try hexToBytes(&buf, "")});
+ std.testing.expectError(error.InvalidCharacter, hexToBytes(&buf, "012Z"));
+ std.testing.expectError(error.InvalidLength, hexToBytes(&buf, "AAA"));
+ std.testing.expectError(error.NoSpaceLeft, hexToBytes(buf[0..1], "ABAB"));
}
test "formatIntValue with comptime_int" {
@@ -1900,8 +2027,8 @@ test "formatFloatValue with comptime_float" {
try formatFloatValue(value, "", FormatOptions{}, fbs.writer());
std.testing.expect(mem.eql(u8, fbs.getWritten(), "1.0e+00"));
- try testFmt("1.0e+00", "{}", .{value});
- try testFmt("1.0e+00", "{}", .{1.0});
+ try expectFmt("1.0e+00", "{}", .{value});
+ try expectFmt("1.0e+00", "{}", .{1.0});
}
test "formatType max_depth" {
@@ -1970,19 +2097,19 @@ test "formatType max_depth" {
}
test "positional" {
- try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) });
- try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) });
- try testFmt("0 0", "{0} {0}", .{@as(usize, 0)});
- try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) });
- try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) });
+ try expectFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) });
+ try expectFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) });
+ try expectFmt("0 0", "{0} {0}", .{@as(usize, 0)});
+ try expectFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) });
+ try expectFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) });
}
test "positional with specifier" {
- try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)});
+ try expectFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)});
}
test "positional/alignment/width/precision" {
- try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)});
+ try expectFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)});
}
test "vector" {
@@ -2003,76 +2130,76 @@ test "vector" {
const vi64: std.meta.Vector(4, i64) = [_]i64{ -2, -1, 0, 1 };
const vu64: std.meta.Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 };
- try testFmt("{ true, false, true, false }", "{}", .{vbool});
- try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64});
- try testFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64});
- try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64});
- try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64});
- try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64});
- try testFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64});
+ try expectFmt("{ true, false, true, false }", "{}", .{vbool});
+ try expectFmt("{ -2, -1, 0, 1 }", "{}", .{vi64});
+ try expectFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64});
+ try expectFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64});
+ try expectFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64});
+ try expectFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64});
+ try expectFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64});
}
test "enum-literal" {
- try testFmt(".hello_world", "{}", .{.hello_world});
+ try expectFmt(".hello_world", "{s}", .{.hello_world});
}
test "padding" {
- try testFmt("Simple", "{}", .{"Simple"});
- try testFmt(" true", "{:10}", .{true});
- try testFmt(" true", "{:>10}", .{true});
- try testFmt("======true", "{:=>10}", .{true});
- try testFmt("true======", "{:=<10}", .{true});
- try testFmt(" true ", "{:^10}", .{true});
- try testFmt("===true===", "{:=^10}", .{true});
- try testFmt(" Minimum width", "{:18} width", .{"Minimum"});
- try testFmt("==================Filled", "{:=>24}", .{"Filled"});
- try testFmt(" Centered ", "{:^24}", .{"Centered"});
- try testFmt("-", "{:-^1}", .{""});
- try testFmt("==crêpe===", "{:=^10}", .{"crêpe"});
- try testFmt("=====crêpe", "{:=>10}", .{"crêpe"});
- try testFmt("crêpe=====", "{:=<10}", .{"crêpe"});
+ try expectFmt("Simple", "{s}", .{"Simple"});
+ try expectFmt(" true", "{:10}", .{true});
+ try expectFmt(" true", "{:>10}", .{true});
+ try expectFmt("======true", "{:=>10}", .{true});
+ try expectFmt("true======", "{:=<10}", .{true});
+ try expectFmt(" true ", "{:^10}", .{true});
+ try expectFmt("===true===", "{:=^10}", .{true});
+ try expectFmt(" Minimum width", "{s:18} width", .{"Minimum"});
+ try expectFmt("==================Filled", "{s:=>24}", .{"Filled"});
+ try expectFmt(" Centered ", "{s:^24}", .{"Centered"});
+ try expectFmt("-", "{s:-^1}", .{""});
+ try expectFmt("==crêpe===", "{s:=^10}", .{"crêpe"});
+ try expectFmt("=====crêpe", "{s:=>10}", .{"crêpe"});
+ try expectFmt("crêpe=====", "{s:=<10}", .{"crêpe"});
}
test "decimal float padding" {
var number: f32 = 3.1415;
- try testFmt("left-pad: **3.141\n", "left-pad: {d:*>7.3}\n", .{number});
- try testFmt("center-pad: *3.141*\n", "center-pad: {d:*^7.3}\n", .{number});
- try testFmt("right-pad: 3.141**\n", "right-pad: {d:*<7.3}\n", .{number});
+ try expectFmt("left-pad: **3.141\n", "left-pad: {d:*>7.3}\n", .{number});
+ try expectFmt("center-pad: *3.141*\n", "center-pad: {d:*^7.3}\n", .{number});
+ try expectFmt("right-pad: 3.141**\n", "right-pad: {d:*<7.3}\n", .{number});
}
test "sci float padding" {
var number: f32 = 3.1415;
- try testFmt("left-pad: **3.141e+00\n", "left-pad: {e:*>11.3}\n", .{number});
- try testFmt("center-pad: *3.141e+00*\n", "center-pad: {e:*^11.3}\n", .{number});
- try testFmt("right-pad: 3.141e+00**\n", "right-pad: {e:*<11.3}\n", .{number});
+ try expectFmt("left-pad: **3.141e+00\n", "left-pad: {e:*>11.3}\n", .{number});
+ try expectFmt("center-pad: *3.141e+00*\n", "center-pad: {e:*^11.3}\n", .{number});
+ try expectFmt("right-pad: 3.141e+00**\n", "right-pad: {e:*<11.3}\n", .{number});
}
test "null" {
const inst = null;
- try testFmt("null", "{}", .{inst});
+ try expectFmt("null", "{}", .{inst});
}
test "type" {
- try testFmt("u8", "{}", .{u8});
- try testFmt("?f32", "{}", .{?f32});
- try testFmt("[]const u8", "{}", .{[]const u8});
+ try expectFmt("u8", "{}", .{u8});
+ try expectFmt("?f32", "{}", .{?f32});
+ try expectFmt("[]const u8", "{}", .{[]const u8});
}
test "named arguments" {
- try testFmt("hello world!", "{} world{c}", .{ "hello", '!' });
- try testFmt("hello world!", "{[greeting]} world{[punctuation]c}", .{ .punctuation = '!', .greeting = "hello" });
- try testFmt("hello world!", "{[1]} world{[0]c}", .{ '!', "hello" });
+ try expectFmt("hello world!", "{s} world{c}", .{ "hello", '!' });
+ try expectFmt("hello world!", "{[greeting]s} world{[punctuation]c}", .{ .punctuation = '!', .greeting = "hello" });
+ try expectFmt("hello world!", "{[1]s} world{[0]c}", .{ '!', "hello" });
}
test "runtime width specifier" {
var width: usize = 9;
- try testFmt("~~hello~~", "{:~^[1]}", .{ "hello", width });
- try testFmt("~~hello~~", "{:~^[width]}", .{ .string = "hello", .width = width });
+ try expectFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width });
+ try expectFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width });
}
test "runtime precision specifier" {
var number: f32 = 3.1415;
var precision: usize = 2;
- try testFmt("3.14e+00", "{:1.[1]}", .{ number, precision });
- try testFmt("3.14e+00", "{:1.[precision]}", .{ .number = number, .precision = precision });
+ try expectFmt("3.14e+00", "{:1.[1]}", .{ number, precision });
+ try expectFmt("3.14e+00", "{:1.[precision]}", .{ .number = number, .precision = precision });
}
diff --git a/lib/std/fmt/errol.zig b/lib/std/fmt/errol.zig
index 6a0a2256d8..a4f46e7f13 100644
--- a/lib/std/fmt/errol.zig
+++ b/lib/std/fmt/errol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/fmt/errol/enum3.zig b/lib/std/fmt/errol/enum3.zig
index 9dbe27c072..db671b8d25 100644
--- a/lib/std/fmt/errol/enum3.zig
+++ b/lib/std/fmt/errol/enum3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/fmt/errol/lookup.zig b/lib/std/fmt/errol/lookup.zig
index 85a4234bca..4499d3fdef 100644
--- a/lib/std/fmt/errol/lookup.zig
+++ b/lib/std/fmt/errol/lookup.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig
index 4396676d9e..324b06898e 100644
--- a/lib/std/fmt/parse_float.zig
+++ b/lib/std/fmt/parse_float.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -52,21 +52,21 @@ const Z96 = struct {
d2: u32,
// d = s >> 1
- inline fn shiftRight1(d: *Z96, s: Z96) void {
+ fn shiftRight1(d: *Z96, s: Z96) callconv(.Inline) void {
d.d0 = (s.d0 >> 1) | ((s.d1 & 1) << 31);
d.d1 = (s.d1 >> 1) | ((s.d2 & 1) << 31);
d.d2 = s.d2 >> 1;
}
// d = s << 1
- inline fn shiftLeft1(d: *Z96, s: Z96) void {
+ fn shiftLeft1(d: *Z96, s: Z96) callconv(.Inline) void {
d.d2 = (s.d2 << 1) | ((s.d1 & (1 << 31)) >> 31);
d.d1 = (s.d1 << 1) | ((s.d0 & (1 << 31)) >> 31);
d.d0 = s.d0 << 1;
}
// d += s
- inline fn add(d: *Z96, s: Z96) void {
+ fn add(d: *Z96, s: Z96) callconv(.Inline) void {
var w = @as(u64, d.d0) + @as(u64, s.d0);
d.d0 = @truncate(u32, w);
@@ -80,7 +80,7 @@ const Z96 = struct {
}
// d -= s
- inline fn sub(d: *Z96, s: Z96) void {
+ fn sub(d: *Z96, s: Z96) callconv(.Inline) void {
var w = @as(u64, d.d0) -% @as(u64, s.d0);
d.d0 = @truncate(u32, w);
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index 8b949a57f1..79385708af 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -39,7 +39,7 @@ pub const Watch = @import("fs/watch.zig").Watch;
/// fit into a UTF-8 encoded array of this length.
/// The byte count includes room for a null sentinel byte.
pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
- .linux, .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd => os.PATH_MAX,
+ .linux, .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .haiku => os.PATH_MAX,
// Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
// If it would require 4 UTF-8 bytes, then there would be a surrogate
// pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
@@ -82,8 +82,8 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
mem.copy(u8, tmp_path[0..], dirname);
tmp_path[dirname.len] = path.sep;
while (true) {
- try crypto.randomBytes(rand_buf[0..]);
- base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
+ crypto.random.bytes(rand_buf[0..]);
+ _ = base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
if (cwd().symLink(existing_path, tmp_path, .{})) {
return cwd().rename(tmp_path, new_path);
@@ -153,15 +153,14 @@ pub const AtomicFile = struct {
) InitError!AtomicFile {
var rand_buf: [RANDOM_BYTES]u8 = undefined;
var tmp_path_buf: [TMP_PATH_LEN:0]u8 = undefined;
- // TODO: should be able to use TMP_PATH_LEN here.
- tmp_path_buf[base64.Base64Encoder.calcSize(RANDOM_BYTES)] = 0;
while (true) {
- try crypto.randomBytes(rand_buf[0..]);
- base64_encoder.encode(&tmp_path_buf, &rand_buf);
+ crypto.random.bytes(rand_buf[0..]);
+ const tmp_path = base64_encoder.encode(&tmp_path_buf, &rand_buf);
+ tmp_path_buf[tmp_path.len] = 0;
const file = dir.createFile(
- &tmp_path_buf,
+ tmp_path,
.{ .mode = mode, .exclusive = true },
) catch |err| switch (err) {
error.PathAlreadyExists => continue,
@@ -428,6 +427,78 @@ pub const Dir = struct {
}
}
},
+ .haiku => struct {
+ dir: Dir,
+ buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
+ index: usize,
+ end_index: usize,
+
+ const Self = @This();
+
+ pub const Error = IteratorError;
+
+ /// Memory such as file names referenced in this returned entry becomes invalid
+ /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
+ pub fn next(self: *Self) Error!?Entry {
+ start_over: while (true) {
+ // TODO: find a better max
+ const HAIKU_MAX_COUNT = 10000;
+ if (self.index >= self.end_index) {
+ const rc = os.system._kern_read_dir(
+ self.dir.fd,
+ &self.buf,
+ self.buf.len,
+ HAIKU_MAX_COUNT,
+ );
+ if (rc == 0) return null;
+ if (rc < 0) {
+ switch (os.errno(rc)) {
+ os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
+ os.EFAULT => unreachable,
+ os.ENOTDIR => unreachable,
+ os.EINVAL => unreachable,
+ else => |err| return os.unexpectedErrno(err),
+ }
+ }
+ self.index = 0;
+ self.end_index = @intCast(usize, rc);
+ }
+ const haiku_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);
+ const next_index = self.index + haiku_entry.reclen();
+ self.index = next_index;
+ const name = mem.spanZ(@ptrCast([*:0]u8, &haiku_entry.d_name));
+
+ if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or (haiku_entry.d_ino == 0)) {
+ continue :start_over;
+ }
+
+ var stat_info: os.libc_stat = undefined;
+ const rc2 = os.system._kern_read_stat(
+ self.dir.fd,
+ &haiku_entry.d_name,
+ false,
+ &stat_info,
+ 0,
+ );
+ const statmode = stat_info.mode & os.S_IFMT;
+
+ const entry_kind = switch (statmode) {
+ os.S_IFDIR => Entry.Kind.Directory,
+ os.S_IFBLK => Entry.Kind.BlockDevice,
+ os.S_IFCHR => Entry.Kind.CharacterDevice,
+ os.S_IFLNK => Entry.Kind.SymLink,
+ os.S_IFREG => Entry.Kind.File,
+ os.S_IFIFO => Entry.Kind.NamedPipe,
+ else => Entry.Kind.Unknown,
+ };
+
+ return Entry{
+ .name = name,
+ .kind = entry_kind,
+ };
+ }
+ }
+ },
.linux => struct {
dir: Dir,
buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
@@ -622,14 +693,20 @@ pub const Dir = struct {
pub fn iterate(self: Dir) Iterator {
switch (builtin.os.tag) {
- .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd => return Iterator{
+ .macos,
+ .ios,
+ .freebsd,
+ .netbsd,
+ .dragonfly,
+ .openbsd,
+ => return Iterator{
.dir = self,
.seek = 0,
.index = 0,
.end_index = 0,
.buf = undefined,
},
- .linux => return Iterator{
+ .linux, .haiku => return Iterator{
.dir = self,
.index = 0,
.end_index = 0,
@@ -2184,10 +2261,10 @@ pub const Walker = struct {
while (true) {
if (self.stack.items.len == 0) return null;
// `top` becomes invalid after appending to `self.stack`.
- const top = &self.stack.items[self.stack.items.len - 1];
+ var top = &self.stack.items[self.stack.items.len - 1];
const dirname_len = top.dirname_len;
if (try top.dir_it.next()) |base| {
- self.name_buffer.shrink(dirname_len);
+ self.name_buffer.shrinkRetainingCapacity(dirname_len);
try self.name_buffer.append(path.sep);
try self.name_buffer.appendSlice(base.name);
if (base.kind == .Directory) {
@@ -2201,6 +2278,7 @@ pub const Walker = struct {
.dir_it = new_dir.iterate(),
.dirname_len = self.name_buffer.items.len,
});
+ top = &self.stack.items[self.stack.items.len - 1];
}
}
return Entry{
@@ -2339,7 +2417,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
// TODO could this slice from 0 to out_len instead?
return mem.spanZ(std.meta.assumeSentinel(out_buffer.ptr, 0));
},
- .openbsd => {
+ .openbsd, .haiku => {
// OpenBSD doesn't support getting the path of a running process, so try to guess it
if (os.argv.len == 0)
return error.FileNotFound;
@@ -2475,7 +2553,7 @@ fn copy_file(fd_in: os.fd_t, fd_out: os.fd_t) CopyFileError!void {
}
}
-test "" {
+test {
if (builtin.os.tag != .wasi) {
_ = makeDirAbsolute;
_ = makeDirAbsoluteZ;
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
index dc7b39eef1..baea3b4e7f 100644
--- a/lib/std/fs/file.zig
+++ b/lib/std/fs/file.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -117,7 +117,7 @@ pub const File = struct {
truncate: bool = true,
/// Ensures that this open call creates the file, otherwise causes
- /// `error.FileAlreadyExists` to be returned.
+ /// `error.PathAlreadyExists` to be returned.
exclusive: bool = false,
/// Open the file with a lock to prevent other processes from accessing it at the
@@ -459,6 +459,7 @@ pub const File = struct {
return index;
}
+ /// See https://github.com/ziglang/zig/issues/7699
pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {
if (is_windows) {
// TODO improve this to use ReadFileScatter
@@ -479,6 +480,7 @@ pub const File = struct {
/// is not an error condition.
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
/// order to handle partial reads from the underlying OS layer.
+ /// See https://github.com/ziglang/zig/issues/7699
pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {
if (iovecs.len == 0) return;
@@ -500,6 +502,7 @@ pub const File = struct {
}
}
+ /// See https://github.com/ziglang/zig/issues/7699
pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize {
if (is_windows) {
// TODO improve this to use ReadFileScatter
@@ -520,6 +523,7 @@ pub const File = struct {
/// is not an error condition.
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
/// order to handle partial reads from the underlying OS layer.
+ /// See https://github.com/ziglang/zig/issues/7699
pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {
if (iovecs.len == 0) return;
@@ -582,6 +586,8 @@ pub const File = struct {
}
}
+ /// See https://github.com/ziglang/zig/issues/7699
+ /// See equivalent function: `std.net.Stream.writev`.
pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize {
if (is_windows) {
// TODO improve this to use WriteFileScatter
@@ -599,6 +605,8 @@ pub const File = struct {
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
/// order to handle partial writes from the underlying OS layer.
+ /// See https://github.com/ziglang/zig/issues/7699
+ /// See equivalent function: `std.net.Stream.writevAll`.
pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {
if (iovecs.len == 0) return;
@@ -615,6 +623,7 @@ pub const File = struct {
}
}
+ /// See https://github.com/ziglang/zig/issues/7699
pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize {
if (is_windows) {
// TODO improve this to use WriteFileScatter
@@ -632,6 +641,7 @@ pub const File = struct {
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
/// order to handle partial writes from the underlying OS layer.
+ /// See https://github.com/ziglang/zig/issues/7699
pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void {
if (iovecs.len == 0) return;
@@ -690,7 +700,7 @@ pub const File = struct {
header_count: usize = 0,
};
- pub const WriteFileError = ReadError || WriteError;
+ pub const WriteFileError = ReadError || error{EndOfStream} || WriteError;
pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
return self.writeFileAllSendfile(in_file, args) catch |err| switch (err) {
@@ -698,6 +708,8 @@ pub const File = struct {
error.FastOpenAlreadyInProgress,
error.MessageTooBig,
error.FileDescriptorNotASocket,
+ error.NetworkUnreachable,
+ error.NetworkSubsystemFailed,
=> return self.writeFileAllUnseekable(in_file, args),
else => |e| return e,
@@ -712,23 +724,14 @@ pub const File = struct {
try self.writevAll(headers);
- var buffer: [4096]u8 = undefined;
- {
- var index: usize = 0;
- // Skip in_offset bytes.
- while (index < args.in_offset) {
- const ask = math.min(buffer.len, args.in_offset - index);
- const amt = try in_file.read(buffer[0..ask]);
- index += amt;
- }
- }
- const in_len = args.in_len orelse math.maxInt(u64);
- var index: usize = 0;
- while (index < in_len) {
- const ask = math.min(buffer.len, in_len - index);
- const amt = try in_file.read(buffer[0..ask]);
- if (amt == 0) break;
- index += try self.write(buffer[0..amt]);
+ try in_file.reader().skipBytes(args.in_offset, .{ .buf_size = 4096 });
+
+ var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();
+ if (args.in_len) |len| {
+ var stream = std.io.limitedReader(in_file.reader(), len);
+ try fifo.pump(stream.reader(), self.writer());
+ } else {
+ try fifo.pump(in_file.reader(), self.writer());
}
try self.writevAll(trailers);
@@ -803,32 +806,16 @@ pub const File = struct {
pub const Reader = io.Reader(File, ReadError, read);
- /// Deprecated: use `Reader`
- pub const InStream = Reader;
-
pub fn reader(file: File) Reader {
return .{ .context = file };
}
- /// Deprecated: use `reader`
- pub fn inStream(file: File) Reader {
- return .{ .context = file };
- }
-
pub const Writer = io.Writer(File, WriteError, write);
- /// Deprecated: use `Writer`
- pub const OutStream = Writer;
-
pub fn writer(file: File) Writer {
return .{ .context = file };
}
- /// Deprecated: use `writer`
- pub fn outStream(file: File) Writer {
- return .{ .context = file };
- }
-
pub const SeekableStream = io.SeekableStream(
File,
SeekError,
diff --git a/lib/std/fs/get_app_data_dir.zig b/lib/std/fs/get_app_data_dir.zig
index 9e7e54e1b5..18f8458eb2 100644
--- a/lib/std/fs/get_app_data_dir.zig
+++ b/lib/std/fs/get_app_data_dir.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -56,6 +56,18 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
};
return fs.path.join(allocator, &[_][]const u8{ home_dir, ".local", "share", appname });
},
+ .haiku => {
+ var dir_path_ptr: [*:0]u8 = undefined;
+ // TODO look into directory_which
+ const be_user_settings = 0xbbe;
+ const rc = os.system.find_directory(be_user_settings, -1, true, dir_path_ptr, 1) ;
+ const settings_dir = try allocator.dupeZ(u8, mem.spanZ(dir_path_ptr));
+ defer allocator.free(settings_dir);
+ switch (rc) {
+ 0 => return fs.path.join(allocator, &[_][]const u8{ settings_dir, appname }),
+ else => return error.AppDataDirUnavailable,
+ }
+ },
else => @compileError("Unsupported OS"),
}
}
diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig
index 9043889aa9..c04b9dff66 100644
--- a/lib/std/fs/path.zig
+++ b/lib/std/fs/path.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -39,7 +39,7 @@ pub fn isSep(byte: u8) bool {
/// This is different from mem.join in that the separator will not be repeated if
/// it is found at the end or beginning of a pair of consecutive paths.
-fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u8 {
+fn joinSep(allocator: *Allocator, separator: u8, sepPredicate: fn (u8) bool, paths: []const []const u8) ![]u8 {
if (paths.len == 0) return &[0]u8{};
const total_len = blk: {
@@ -48,8 +48,8 @@ fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u
while (i < paths.len) : (i += 1) {
const prev_path = paths[i - 1];
const this_path = paths[i];
- const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator);
- const this_sep = (this_path.len != 0 and this_path[0] == separator);
+ const prev_sep = (prev_path.len != 0 and sepPredicate(prev_path[prev_path.len - 1]));
+ const this_sep = (this_path.len != 0 and sepPredicate(this_path[0]));
sum += @boolToInt(!prev_sep and !this_sep);
sum += if (prev_sep and this_sep) this_path.len - 1 else this_path.len;
}
@@ -65,8 +65,8 @@ fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u
while (i < paths.len) : (i += 1) {
const prev_path = paths[i - 1];
const this_path = paths[i];
- const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator);
- const this_sep = (this_path.len != 0 and this_path[0] == separator);
+ const prev_sep = (prev_path.len != 0 and sepPredicate(prev_path[prev_path.len - 1]));
+ const this_sep = (this_path.len != 0 and sepPredicate(this_path[0]));
if (!prev_sep and !this_sep) {
buf[buf_index] = separator;
buf_index += 1;
@@ -80,28 +80,30 @@ fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u
return buf;
}
-pub const join = if (builtin.os.tag == .windows) joinWindows else joinPosix;
-
-/// Naively combines a series of paths with the native path seperator.
-/// Allocates memory for the result, which must be freed by the caller.
-pub fn joinWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
- return joinSep(allocator, sep_windows, paths);
-}
-
/// Naively combines a series of paths with the native path seperator.
/// Allocates memory for the result, which must be freed by the caller.
-pub fn joinPosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
- return joinSep(allocator, sep_posix, paths);
+pub fn join(allocator: *Allocator, paths: []const []const u8) ![]u8 {
+ return joinSep(allocator, sep, isSep, paths);
}
fn testJoinWindows(paths: []const []const u8, expected: []const u8) void {
- const actual = joinWindows(testing.allocator, paths) catch @panic("fail");
+ const windowsIsSep = struct {
+ fn isSep(byte: u8) bool {
+ return byte == '/' or byte == '\\';
+ }
+ }.isSep;
+ const actual = joinSep(testing.allocator, sep_windows, windowsIsSep, paths) catch @panic("fail");
defer testing.allocator.free(actual);
testing.expectEqualSlices(u8, expected, actual);
}
fn testJoinPosix(paths: []const []const u8, expected: []const u8) void {
- const actual = joinPosix(testing.allocator, paths) catch @panic("fail");
+ const posixIsSep = struct {
+ fn isSep(byte: u8) bool {
+ return byte == '/';
+ }
+ }.isSep;
+ const actual = joinSep(testing.allocator, sep_posix, posixIsSep, paths) catch @panic("fail");
defer testing.allocator.free(actual);
testing.expectEqualSlices(u8, expected, actual);
}
@@ -119,6 +121,9 @@ test "join" {
"c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig",
);
+ testJoinWindows(&[_][]const u8{ "c:\\", "a", "b/", "c" }, "c:\\a\\b/c");
+ testJoinWindows(&[_][]const u8{ "c:\\a/", "b\\", "/c" }, "c:\\a/b\\c");
+
testJoinPosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c");
testJoinPosix(&[_][]const u8{ "/a/b/", "c" }, "/a/b/c");
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
index 61df39be0c..e9f28a0b60 100644
--- a/lib/std/fs/test.zig
+++ b/lib/std/fs/test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -750,7 +750,7 @@ test "open file with exclusive lock twice, make sure it waits" {
errdefer file.close();
const S = struct {
- const C = struct { dir: *fs.Dir, evt: *std.ResetEvent };
+ const C = struct { dir: *fs.Dir, evt: *std.Thread.ResetEvent };
fn checkFn(ctx: C) !void {
const file1 = try ctx.dir.createFile(filename, .{ .lock = .Exclusive });
defer file1.close();
@@ -758,7 +758,8 @@ test "open file with exclusive lock twice, make sure it waits" {
}
};
- var evt = std.ResetEvent.init();
+ var evt: std.Thread.ResetEvent = undefined;
+ try evt.init();
defer evt.deinit();
const t = try std.Thread.spawn(S.C{ .dir = &tmp.dir, .evt = &evt }, S.checkFn);
@@ -771,8 +772,6 @@ test "open file with exclusive lock twice, make sure it waits" {
std.time.sleep(SLEEP_TIMEOUT_NS);
if (timer.read() >= SLEEP_TIMEOUT_NS) break;
}
- // Check that createFile is still waiting for the lock to be released.
- testing.expect(!evt.isSet());
file.close();
// No timeout to avoid failures on heavily loaded systems.
evt.wait();
@@ -795,3 +794,42 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
try fs.deleteFileAbsolute(filename);
}
+
+test "walker" {
+ if (builtin.os.tag == .wasi) return error.SkipZigTest;
+
+ var arena = ArenaAllocator.init(testing.allocator);
+ defer arena.deinit();
+ var allocator = &arena.allocator;
+
+ var tmp = tmpDir(.{});
+ defer tmp.cleanup();
+
+ const nb_dirs = 8;
+
+ var i: usize = 0;
+ var sub_dir = tmp.dir;
+ while (i < nb_dirs) : (i += 1) {
+ const dir_name = try std.fmt.allocPrint(allocator, "{}", .{i});
+ try sub_dir.makeDir(dir_name);
+ sub_dir = try sub_dir.openDir(dir_name, .{});
+ }
+
+ const tmp_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
+
+ var walker = try fs.walkPath(testing.allocator, tmp_path);
+ defer walker.deinit();
+
+ i = 0;
+ var expected_dir_name: []const u8 = "";
+ while (i < nb_dirs) : (i += 1) {
+ const name = try std.fmt.allocPrint(allocator, "{}", .{i});
+ expected_dir_name = if (expected_dir_name.len == 0)
+ name
+ else
+ try fs.path.join(allocator, &[_][]const u8{ expected_dir_name, name });
+
+ var entry = (try walker.next()).?;
+ testing.expectEqualStrings(expected_dir_name, try fs.path.relative(allocator, tmp_path, entry.path));
+ }
+}
diff --git a/lib/std/fs/wasi.zig b/lib/std/fs/wasi.zig
index cad86e2314..cf5431f994 100644
--- a/lib/std/fs/wasi.zig
+++ b/lib/std/fs/wasi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -38,7 +38,7 @@ pub const PreopenType = union(PreopenTypeTag) {
pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype) !void {
try out_stream.print("PreopenType{{ ", .{});
switch (self) {
- PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{path}),
+ PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{std.zig.fmtId(path)}),
}
return out_stream.print(" }}", .{});
}
diff --git a/lib/std/fs/watch.zig b/lib/std/fs/watch.zig
index 2e75b865cf..1c7cb68b32 100644
--- a/lib/std/fs/watch.zig
+++ b/lib/std/fs/watch.zig
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
-const std = @import("../std.zig");
+const std = @import("std");
const builtin = @import("builtin");
const event = std.event;
const assert = std.debug.assert;
@@ -24,16 +24,6 @@ const WatchEventId = enum {
Delete,
};
-fn eqlString(a: []const u16, b: []const u16) bool {
- if (a.len != b.len) return false;
- if (a.ptr == b.ptr) return true;
- return mem.compare(u16, a, b) == .Equal;
-}
-
-fn hashString(s: []const u16) u32 {
- return @truncate(u32, std.hash.Wyhash.hash(0, mem.sliceAsBytes(s)));
-}
-
const WatchEventError = error{
UserResourceLimitReached,
SystemResources,
@@ -43,7 +33,7 @@ const WatchEventError = error{
pub fn Watch(comptime V: type) type {
return struct {
- channel: *event.Channel(Event.Error!Event),
+ channel: event.Channel(Event.Error!Event),
os_data: OsData,
allocator: *Allocator,
@@ -57,10 +47,10 @@ pub fn Watch(comptime V: type) type {
};
const KqOsData = struct {
- file_table: FileTable,
table_lock: event.Lock,
+ file_table: FileTable,
- const FileTable = std.StringHashMap(*Put);
+ const FileTable = std.StringHashMapUnmanaged(*Put);
const Put = struct {
putter_frame: @Frame(kqPutEvents),
cancelled: bool = false,
@@ -71,21 +61,15 @@ pub fn Watch(comptime V: type) type {
const WindowsOsData = struct {
table_lock: event.Lock,
dir_table: DirTable,
- all_putters: std.atomic.Queue(Put),
- ref_count: std.atomic.Int(usize),
-
- const Put = struct {
- putter: anyframe,
- cancelled: bool = false,
- };
+ cancelled: bool = false,
- const DirTable = std.StringHashMap(*Dir);
- const FileTable = std.HashMap([]const u16, V, hashString, eqlString);
+ const DirTable = std.StringHashMapUnmanaged(*Dir);
+ const FileTable = std.StringHashMapUnmanaged(V);
const Dir = struct {
putter_frame: @Frame(windowsDirReader),
file_table: FileTable,
- table_lock: event.Lock,
+ dir_handle: os.windows.HANDLE,
};
};
@@ -96,8 +80,8 @@ pub fn Watch(comptime V: type) type {
table_lock: event.Lock,
cancelled: bool = false,
- const WdTable = std.AutoHashMap(i32, Dir);
- const FileTable = std.StringHashMap(V);
+ const WdTable = std.AutoHashMapUnmanaged(i32, Dir);
+ const FileTable = std.StringHashMapUnmanaged(V);
const Dir = struct {
dirname: []const u8,
@@ -110,19 +94,14 @@ pub fn Watch(comptime V: type) type {
pub const Event = struct {
id: Id,
data: V,
+ dirname: []const u8,
+ basename: []const u8,
pub const Id = WatchEventId;
pub const Error = WatchEventError;
};
pub fn init(allocator: *Allocator, event_buf_count: usize) !*Self {
- const channel = try allocator.create(event.Channel(Event.Error!Event));
- errdefer allocator.destroy(channel);
- var buf = try allocator.alloc(Event.Error!Event, event_buf_count);
- errdefer allocator.free(buf);
- channel.init(buf);
- errdefer channel.deinit();
-
const self = try allocator.create(Self);
errdefer allocator.destroy(self);
@@ -133,15 +112,17 @@ pub fn Watch(comptime V: type) type {
self.* = Self{
.allocator = allocator,
- .channel = channel,
+ .channel = undefined,
.os_data = OsData{
.putter_frame = undefined,
.inotify_fd = inotify_fd,
.wd_table = OsData.WdTable.init(allocator),
- .table_lock = event.Lock.init(),
+ .table_lock = event.Lock{},
},
};
+ var buf = try allocator.alloc(Event.Error!Event, event_buf_count);
+ self.channel.init(buf);
self.os_data.putter_frame = async self.linuxEventPutter();
return self;
},
@@ -149,82 +130,93 @@ pub fn Watch(comptime V: type) type {
.windows => {
self.* = Self{
.allocator = allocator,
- .channel = channel,
+ .channel = undefined,
.os_data = OsData{
- .table_lock = event.Lock.init(),
+ .table_lock = event.Lock{},
.dir_table = OsData.DirTable.init(allocator),
- .ref_count = std.atomic.Int(usize).init(1),
- .all_putters = std.atomic.Queue(anyframe).init(),
},
};
+
+ var buf = try allocator.alloc(Event.Error!Event, event_buf_count);
+ self.channel.init(buf);
return self;
},
.macos, .freebsd, .netbsd, .dragonfly, .openbsd => {
self.* = Self{
.allocator = allocator,
- .channel = channel,
+ .channel = undefined,
.os_data = OsData{
- .table_lock = event.Lock.init(),
+ .table_lock = event.Lock{},
.file_table = OsData.FileTable.init(allocator),
},
};
+
+ var buf = try allocator.alloc(Event.Error!Event, event_buf_count);
+ self.channel.init(buf);
return self;
},
else => @compileError("Unsupported OS"),
}
}
- /// All addFile calls and removeFile calls must have completed.
pub fn deinit(self: *Self) void {
switch (builtin.os.tag) {
.macos, .freebsd, .netbsd, .dragonfly, .openbsd => {
- // TODO we need to cancel the frames before destroying the lock
- self.os_data.table_lock.deinit();
var it = self.os_data.file_table.iterator();
while (it.next()) |entry| {
- entry.cancelled = true;
- await entry.value.putter;
+ entry.value.cancelled = true;
+ // @TODO Close the fd here?
+ await entry.value.putter_frame;
self.allocator.free(entry.key);
- self.allocator.free(entry.value);
+ self.allocator.destroy(entry.value);
}
- self.channel.deinit();
- self.allocator.destroy(self.channel.buffer_nodes);
- self.allocator.destroy(self);
},
.linux => {
self.os_data.cancelled = true;
+ {
+ // Remove all directory watches linuxEventPutter will take care of
+ // cleaning up the memory and closing the inotify fd.
+ var dir_it = self.os_data.wd_table.iterator();
+ while (dir_it.next()) |wd_entry| {
+ const rc = os.linux.inotify_rm_watch(self.os_data.inotify_fd, wd_entry.key);
+ // Errno can only be EBADF, EINVAL if either the inotify fs or the wd are invalid
+ std.debug.assert(rc == 0);
+ }
+ }
await self.os_data.putter_frame;
- self.allocator.destroy(self);
},
.windows => {
- while (self.os_data.all_putters.get()) |putter_node| {
- putter_node.cancelled = true;
- await putter_node.frame;
+ self.os_data.cancelled = true;
+ var dir_it = self.os_data.dir_table.iterator();
+ while (dir_it.next()) |dir_entry| {
+ if (windows.kernel32.CancelIoEx(dir_entry.value.dir_handle, null) != 0) {
+ // We canceled the pending ReadDirectoryChangesW operation, but our
+ // frame is still suspending, now waiting indefinitely.
+ // Thus, it is safe to resume it ourslves
+ resume dir_entry.value.putter_frame;
+ } else {
+ std.debug.assert(windows.kernel32.GetLastError() == .NOT_FOUND);
+ // We are at another suspend point, we can await safely for the
+ // function to exit the loop
+ await dir_entry.value.putter_frame;
+ }
+
+ self.allocator.free(dir_entry.key);
+ var file_it = dir_entry.value.file_table.iterator();
+ while (file_it.next()) |file_entry| {
+ self.allocator.free(file_entry.key);
+ }
+ dir_entry.value.file_table.deinit(self.allocator);
+ self.allocator.destroy(dir_entry.value);
}
- self.deref();
+ self.os_data.dir_table.deinit(self.allocator);
},
else => @compileError("Unsupported OS"),
}
- }
-
- fn ref(self: *Self) void {
- _ = self.os_data.ref_count.incr();
- }
-
- fn deref(self: *Self) void {
- if (self.os_data.ref_count.decr() == 1) {
- self.os_data.table_lock.deinit();
- var it = self.os_data.dir_table.iterator();
- while (it.next()) |entry| {
- self.allocator.free(entry.key);
- self.allocator.destroy(entry.value);
- }
- self.os_data.dir_table.deinit();
- self.channel.deinit();
- self.allocator.destroy(self.channel.buffer_nodes);
- self.allocator.destroy(self);
- }
+ self.allocator.free(self.channel.buffer_nodes);
+ self.channel.deinit();
+ self.allocator.destroy(self);
}
pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
@@ -237,217 +229,208 @@ pub fn Watch(comptime V: type) type {
}
fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {
- const resolved_path = try std.fs.path.resolve(self.allocator, [_][]const u8{file_path});
- var resolved_path_consumed = false;
- defer if (!resolved_path_consumed) self.allocator.free(resolved_path);
+ var realpath_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ const realpath = try os.realpath(file_path, &realpath_buf);
- var close_op = try CloseOperation.start(self.allocator);
- var close_op_consumed = false;
- defer if (!close_op_consumed) close_op.finish();
-
- const flags = if (comptime std.Target.current.isDarwin()) os.O_SYMLINK | os.O_EVTONLY else 0;
- const mode = 0;
- const fd = try openPosix(self.allocator, resolved_path, flags, mode);
- close_op.setHandle(fd);
+ const held = self.os_data.table_lock.acquire();
+ defer held.release();
- var put = try self.allocator.create(OsData.Put);
- errdefer self.allocator.destroy(put);
- put.* = OsData.Put{
- .value = value,
- .putter_frame = undefined,
- };
- put.putter_frame = async self.kqPutEvents(close_op, put);
- close_op_consumed = true;
- errdefer {
- put.cancelled = true;
- await put.putter_frame;
+ const gop = try self.os_data.file_table.getOrPut(self.allocator, realpath);
+ errdefer self.os_data.file_table.removeAssertDiscard(realpath);
+ if (gop.found_existing) {
+ const prev_value = gop.entry.value.value;
+ gop.entry.value.value = value;
+ return prev_value;
}
- const result = blk: {
- const held = self.os_data.table_lock.acquire();
- defer held.release();
-
- const gop = try self.os_data.file_table.getOrPut(resolved_path);
- if (gop.found_existing) {
- const prev_value = gop.kv.value.value;
- await gop.kv.value.putter_frame;
- gop.kv.value = put;
- break :blk prev_value;
- } else {
- resolved_path_consumed = true;
- gop.kv.value = put;
- break :blk null;
- }
+ gop.entry.key = try self.allocator.dupe(u8, realpath);
+ errdefer self.allocator.free(gop.entry.key);
+ gop.entry.value = try self.allocator.create(OsData.Put);
+ errdefer self.allocator.destroy(gop.entry.value);
+ gop.entry.value.* = .{
+ .putter_frame = undefined,
+ .value = value,
};
- return result;
+ // @TODO Can I close this fd and get an error from bsdWaitKev?
+ const flags = if (comptime std.Target.current.isDarwin()) os.O_SYMLINK | os.O_EVTONLY else 0;
+ const fd = try os.open(realpath, flags, 0);
+ gop.entry.value.putter_frame = async self.kqPutEvents(fd, gop.entry.key, gop.entry.value);
+ return null;
}
- fn kqPutEvents(self: *Self, close_op: *CloseOperation, put: *OsData.Put) void {
+ fn kqPutEvents(self: *Self, fd: os.fd_t, file_path: []const u8, put: *OsData.Put) void {
global_event_loop.beginOneEvent();
-
defer {
- close_op.finish();
global_event_loop.finishOneEvent();
+ // @TODO: Remove this if we force close otherwise
+ os.close(fd);
}
+ // We need to manually do a bsdWaitKev to access the fflags.
+ var resume_node = event.Loop.ResumeNode.Basic{
+ .base = .{
+ .id = .Basic,
+ .handle = @frame(),
+ .overlapped = event.Loop.ResumeNode.overlapped_init,
+ },
+ .kev = undefined,
+ };
+
+ var kevs = [1]os.Kevent{undefined};
+ const kev = &kevs[0];
+
while (!put.cancelled) {
- if (global_event_loop.bsdWaitKev(
- @intCast(usize, close_op.getHandle()),
- os.EVFILT_VNODE,
- os.NOTE_WRITE | os.NOTE_DELETE,
- )) |kev| {
- // TODO handle EV_ERROR
- if (kev.fflags & os.NOTE_DELETE != 0) {
- self.channel.put(Self.Event{
- .id = Event.Id.Delete,
- .data = put.value,
- });
- } else if (kev.fflags & os.NOTE_WRITE != 0) {
- self.channel.put(Self.Event{
- .id = Event.Id.CloseWrite,
- .data = put.value,
- });
- }
- } else |err| switch (err) {
- error.EventNotFound => unreachable,
- error.ProcessNotFound => unreachable,
- error.Overflow => unreachable,
- error.AccessDenied, error.SystemResources => |casted_err| {
- self.channel.put(casted_err);
- },
+ kev.* = os.Kevent{
+ .ident = @intCast(usize, fd),
+ .filter = os.EVFILT_VNODE,
+ .flags = os.EV_ADD | os.EV_ENABLE | os.EV_CLEAR | os.EV_ONESHOT |
+ os.NOTE_WRITE | os.NOTE_DELETE | os.NOTE_REVOKE,
+ .fflags = 0,
+ .data = 0,
+ .udata = @ptrToInt(&resume_node.base),
+ };
+ suspend {
+ global_event_loop.beginOneEvent();
+ errdefer global_event_loop.finishOneEvent();
+
+ const empty_kevs = &[0]os.Kevent{};
+ _ = os.kevent(global_event_loop.os_data.kqfd, &kevs, empty_kevs, null) catch |err| switch (err) {
+ error.EventNotFound,
+ error.ProcessNotFound,
+ error.Overflow,
+ => unreachable,
+ error.AccessDenied, error.SystemResources => |e| {
+ self.channel.put(e);
+ continue;
+ },
+ };
+ }
+
+ if (kev.flags & os.EV_ERROR != 0) {
+ self.channel.put(os.unexpectedErrno(os.errno(kev.data)));
+ continue;
+ }
+
+ if (kev.fflags & os.NOTE_DELETE != 0 or kev.fflags & os.NOTE_REVOKE != 0) {
+ self.channel.put(Self.Event{
+ .id = .Delete,
+ .data = put.value,
+ .dirname = std.fs.path.dirname(file_path) orelse "/",
+ .basename = std.fs.path.basename(file_path),
+ });
+ } else if (kev.fflags & os.NOTE_WRITE != 0) {
+ self.channel.put(Self.Event{
+ .id = .CloseWrite,
+ .data = put.value,
+ .dirname = std.fs.path.dirname(file_path) orelse "/",
+ .basename = std.fs.path.basename(file_path),
+ });
}
}
}
fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {
- const dirname = std.fs.path.dirname(file_path) orelse ".";
- const dirname_with_null = try std.cstr.addNullByte(self.allocator, dirname);
- var dirname_with_null_consumed = false;
- defer if (!dirname_with_null_consumed) self.channel.free(dirname_with_null);
-
+ const dirname = std.fs.path.dirname(file_path) orelse if (file_path[0] == '/') "/" else ".";
const basename = std.fs.path.basename(file_path);
- const basename_with_null = try std.cstr.addNullByte(self.allocator, basename);
- var basename_with_null_consumed = false;
- defer if (!basename_with_null_consumed) self.allocator.free(basename_with_null);
- const wd = try os.inotify_add_watchZ(
+ const wd = try os.inotify_add_watch(
self.os_data.inotify_fd,
- dirname_with_null.ptr,
- os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK,
+ dirname,
+ os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_DELETE | os.linux.IN_EXCL_UNLINK,
);
// wd is either a newly created watch or an existing one.
const held = self.os_data.table_lock.acquire();
defer held.release();
- const gop = try self.os_data.wd_table.getOrPut(wd);
+ const gop = try self.os_data.wd_table.getOrPut(self.allocator, wd);
+ errdefer self.os_data.wd_table.removeAssertDiscard(wd);
if (!gop.found_existing) {
- gop.kv.value = OsData.Dir{
- .dirname = dirname_with_null,
+ gop.entry.value = OsData.Dir{
+ .dirname = try self.allocator.dupe(u8, dirname),
.file_table = OsData.FileTable.init(self.allocator),
};
- dirname_with_null_consumed = true;
}
- const dir = &gop.kv.value;
- const file_table_gop = try dir.file_table.getOrPut(basename_with_null);
+ const dir = &gop.entry.value;
+ const file_table_gop = try dir.file_table.getOrPut(self.allocator, basename);
+ errdefer dir.file_table.removeAssertDiscard(basename);
if (file_table_gop.found_existing) {
- const prev_value = file_table_gop.kv.value;
- file_table_gop.kv.value = value;
+ const prev_value = file_table_gop.entry.value;
+ file_table_gop.entry.value = value;
return prev_value;
} else {
- file_table_gop.kv.value = value;
- basename_with_null_consumed = true;
+ file_table_gop.entry.key = try self.allocator.dupe(u8, basename);
+ file_table_gop.entry.value = value;
return null;
}
}
fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
// TODO we might need to convert dirname and basename to canonical file paths ("short"?)
- const dirname = try self.allocator.dupe(u8, std.fs.path.dirname(file_path) orelse ".");
- var dirname_consumed = false;
- defer if (!dirname_consumed) self.allocator.free(dirname);
-
- const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, dirname);
- defer self.allocator.free(dirname_utf16le);
+ const dirname = std.fs.path.dirname(file_path) orelse if (file_path[0] == '/') "/" else ".";
+ var dirname_path_space: windows.PathSpace = undefined;
+ dirname_path_space.len = try std.unicode.utf8ToUtf16Le(&dirname_path_space.data, dirname);
+ dirname_path_space.data[dirname_path_space.len] = 0;
- // TODO https://github.com/ziglang/zig/issues/265
const basename = std.fs.path.basename(file_path);
- const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, basename);
- var basename_utf16le_null_consumed = false;
- defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null);
- const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
-
- const dir_handle = try windows.OpenFile(dirname_utf16le, .{
- .dir = std.fs.cwd().fd,
- .access_mask = windows.FILE_LIST_DIRECTORY,
- .creation = windows.FILE_OPEN,
- .io_mode = .blocking,
- .open_dir = true,
- });
- var dir_handle_consumed = false;
- defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);
+ var basename_path_space: windows.PathSpace = undefined;
+ basename_path_space.len = try std.unicode.utf8ToUtf16Le(&basename_path_space.data, basename);
+ basename_path_space.data[basename_path_space.len] = 0;
const held = self.os_data.table_lock.acquire();
defer held.release();
- const gop = try self.os_data.dir_table.getOrPut(dirname);
+ const gop = try self.os_data.dir_table.getOrPut(self.allocator, dirname);
+ errdefer self.os_data.dir_table.removeAssertDiscard(dirname);
if (gop.found_existing) {
- const dir = gop.kv.value;
- const held_dir_lock = dir.table_lock.acquire();
- defer held_dir_lock.release();
+ const dir = gop.entry.value;
- const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null);
+ const file_gop = try dir.file_table.getOrPut(self.allocator, basename);
+ errdefer dir.file_table.removeAssertDiscard(basename);
if (file_gop.found_existing) {
- const prev_value = file_gop.kv.value;
- file_gop.kv.value = value;
+ const prev_value = file_gop.entry.value;
+ file_gop.entry.value = value;
return prev_value;
} else {
- file_gop.kv.value = value;
- basename_utf16le_null_consumed = true;
+ file_gop.entry.value = value;
+ file_gop.entry.key = try self.allocator.dupe(u8, basename);
return null;
}
} else {
- errdefer _ = self.os_data.dir_table.remove(dirname);
+ const dir_handle = try windows.OpenFile(dirname_path_space.span(), .{
+ .dir = std.fs.cwd().fd,
+ .access_mask = windows.FILE_LIST_DIRECTORY,
+ .creation = windows.FILE_OPEN,
+ .io_mode = .evented,
+ .open_dir = true,
+ });
+ errdefer windows.CloseHandle(dir_handle);
+
const dir = try self.allocator.create(OsData.Dir);
errdefer self.allocator.destroy(dir);
+ gop.entry.key = try self.allocator.dupe(u8, dirname);
+ errdefer self.allocator.free(gop.entry.key);
+
dir.* = OsData.Dir{
.file_table = OsData.FileTable.init(self.allocator),
- .table_lock = event.Lock.init(),
.putter_frame = undefined,
+ .dir_handle = dir_handle,
};
- gop.kv.value = dir;
- assert((try dir.file_table.put(basename_utf16le_no_null, value)) == null);
- basename_utf16le_null_consumed = true;
-
- dir.putter_frame = async self.windowsDirReader(dir_handle, dir);
- dir_handle_consumed = true;
-
- dirname_consumed = true;
-
+ gop.entry.value = dir;
+ try dir.file_table.put(self.allocator, try self.allocator.dupe(u8, basename), value);
+ dir.putter_frame = async self.windowsDirReader(dir, gop.entry.key);
return null;
}
}
- fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {
- self.ref();
- defer self.deref();
-
- defer os.close(dir_handle);
-
- var putter_node = std.atomic.Queue(anyframe).Node{
- .data = .{ .putter = @frame() },
- .prev = null,
- .next = null,
- };
- self.os_data.all_putters.put(&putter_node);
- defer _ = self.os_data.all_putters.remove(&putter_node);
-
+ fn windowsDirReader(self: *Self, dir: *OsData.Dir, dirname: []const u8) void {
+ defer os.close(dir.dir_handle);
var resume_node = Loop.ResumeNode.Basic{
.base = Loop.ResumeNode{
- .id = Loop.ResumeNode.Id.Basic,
+ .id = .Basic,
.handle = @frame(),
.overlapped = windows.OVERLAPPED{
.Internal = 0,
@@ -458,157 +441,193 @@ pub fn Watch(comptime V: type) type {
},
},
};
- var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined;
- // TODO handle this error not in the channel but in the setup
- _ = windows.CreateIoCompletionPort(
- dir_handle,
- global_event_loop.os_data.io_port,
- undefined,
- undefined,
- ) catch |err| {
- self.channel.put(err);
- return;
- };
+ var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined;
- while (!putter_node.data.cancelled) {
- {
- // TODO only 1 beginOneEvent for the whole function
- global_event_loop.beginOneEvent();
- errdefer global_event_loop.finishOneEvent();
- errdefer {
- _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped);
- }
- suspend {
- _ = windows.kernel32.ReadDirectoryChangesW(
- dir_handle,
- &event_buf,
- @intCast(windows.DWORD, event_buf.len),
- windows.FALSE, // watch subtree
- windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |
- windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |
- windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
- windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
- null, // number of bytes transferred (unused for async)
- &resume_node.base.overlapped,
- null, // completion routine - unused because we use IOCP
- );
- }
+ global_event_loop.beginOneEvent();
+ defer global_event_loop.finishOneEvent();
+
+ while (!self.os_data.cancelled) main_loop: {
+ suspend {
+ _ = windows.kernel32.ReadDirectoryChangesW(
+ dir.dir_handle,
+ &event_buf,
+ event_buf.len,
+ windows.FALSE, // watch subtree
+ windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |
+ windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |
+ windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
+ windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
+ null, // number of bytes transferred (unused for async)
+ &resume_node.base.overlapped,
+ null, // completion routine - unused because we use IOCP
+ );
}
+
var bytes_transferred: windows.DWORD = undefined;
- if (windows.kernel32.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {
- const err = switch (windows.kernel32.GetLastError()) {
+ if (windows.kernel32.GetOverlappedResult(
+ dir.dir_handle,
+ &resume_node.base.overlapped,
+ &bytes_transferred,
+ windows.FALSE,
+ ) == 0) {
+ const potential_error = windows.kernel32.GetLastError();
+ const err = switch (potential_error) {
+ .OPERATION_ABORTED, .IO_INCOMPLETE => err_blk: {
+ if (self.os_data.cancelled)
+ break :main_loop
+ else
+ break :err_blk windows.unexpectedError(potential_error);
+ },
else => |err| windows.unexpectedError(err),
};
self.channel.put(err);
} else {
- // can't use @bytesToSlice because of the special variable length name field
- var ptr = event_buf[0..].ptr;
+ var ptr: [*]u8 = &event_buf;
const end_ptr = ptr + bytes_transferred;
- var ev: *windows.FILE_NOTIFY_INFORMATION = undefined;
- while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) {
- ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr);
+ while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) {
+ const ev = @ptrCast(*const windows.FILE_NOTIFY_INFORMATION, ptr);
const emit = switch (ev.Action) {
windows.FILE_ACTION_REMOVED => WatchEventId.Delete,
- windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite,
+ windows.FILE_ACTION_MODIFIED => .CloseWrite,
else => null,
};
if (emit) |id| {
- const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];
- const user_value = blk: {
- const held = dir.table_lock.acquire();
- defer held.release();
-
- if (dir.file_table.get(basename_utf16le)) |entry| {
- break :blk entry.value;
- } else {
- break :blk null;
- }
- };
- if (user_value) |v| {
+ const basename_ptr = @ptrCast([*]u16, ptr + @sizeOf(windows.FILE_NOTIFY_INFORMATION));
+ const basename_utf16le = basename_ptr[0 .. ev.FileNameLength / 2];
+ var basename_data: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ const basename = basename_data[0 .. std.unicode.utf16leToUtf8(&basename_data, basename_utf16le) catch unreachable];
+
+ if (dir.file_table.getEntry(basename)) |entry| {
self.channel.put(Event{
.id = id,
- .data = v,
+ .data = entry.value,
+ .dirname = dirname,
+ .basename = entry.key,
});
}
}
+
if (ev.NextEntryOffset == 0) break;
+ ptr = @alignCast(@alignOf(windows.FILE_NOTIFY_INFORMATION), ptr + ev.NextEntryOffset);
}
}
}
}
- pub fn removeFile(self: *Self, file_path: []const u8) ?V {
- @panic("TODO");
+ pub fn removeFile(self: *Self, file_path: []const u8) !?V {
+ switch (builtin.os.tag) {
+ .linux => {
+ const dirname = std.fs.path.dirname(file_path) orelse if (file_path[0] == '/') "/" else ".";
+ const basename = std.fs.path.basename(file_path);
+
+ const held = self.os_data.table_lock.acquire();
+ defer held.release();
+
+ const dir = self.os_data.wd_table.get(dirname) orelse return null;
+ if (dir.file_table.remove(basename)) |file_entry| {
+ self.allocator.free(file_entry.key);
+ return file_entry.value;
+ }
+ return null;
+ },
+ .windows => {
+ const dirname = std.fs.path.dirname(file_path) orelse if (file_path[0] == '/') "/" else ".";
+ const basename = std.fs.path.basename(file_path);
+
+ const held = self.os_data.table_lock.acquire();
+ defer held.release();
+
+ const dir = self.os_data.dir_table.get(dirname) orelse return null;
+ if (dir.file_table.remove(basename)) |file_entry| {
+ self.allocator.free(file_entry.key);
+ return file_entry.value;
+ }
+ return null;
+ },
+ .macos, .freebsd, .netbsd, .dragonfly, .openbsd => {
+ var realpath_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ const realpath = try os.realpath(file_path, &realpath_buf);
+
+ const held = self.os_data.table_lock.acquire();
+ defer held.release();
+
+ const entry = self.os_data.file_table.get(realpath) orelse return null;
+ entry.value.cancelled = true;
+ // @TODO Close the fd here?
+ await entry.value.putter_frame;
+ self.allocator.free(entry.key);
+ self.allocator.destroy(entry.value);
+
+ self.os_data.file_table.removeAssertDiscard(realpath);
+ },
+ else => @compileError("Unsupported OS"),
+ }
}
fn linuxEventPutter(self: *Self) void {
global_event_loop.beginOneEvent();
defer {
- self.os_data.table_lock.deinit();
- var wd_it = self.os_data.wd_table.iterator();
- while (wd_it.next()) |wd_entry| {
- var file_it = wd_entry.value.file_table.iterator();
- while (file_it.next()) |file_entry| {
- self.allocator.free(file_entry.key);
- }
- self.allocator.free(wd_entry.value.dirname);
- wd_entry.value.file_table.deinit();
- }
- self.os_data.wd_table.deinit();
- global_event_loop.finishOneEvent();
+ std.debug.assert(self.os_data.wd_table.count() == 0);
+ self.os_data.wd_table.deinit(self.allocator);
os.close(self.os_data.inotify_fd);
- self.channel.deinit();
self.allocator.free(self.channel.buffer_nodes);
+ self.channel.deinit();
+ global_event_loop.finishOneEvent();
}
var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
while (!self.os_data.cancelled) {
- const rc = os.linux.read(self.os_data.inotify_fd, &event_buf, event_buf.len);
- const errno = os.linux.getErrno(rc);
- switch (errno) {
- 0 => {
- // can't use @bytesToSlice because of the special variable length name field
- var ptr = event_buf[0..].ptr;
- const end_ptr = ptr + event_buf.len;
- var ev: *os.linux.inotify_event = undefined;
- while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) {
- ev = @ptrCast(*os.linux.inotify_event, ptr);
- if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
- const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
- // `ev.len` counts all bytes in `ev.name` including terminating null byte.
- const basename_with_null = basename_ptr[0..ev.len];
- const user_value = blk: {
- const held = self.os_data.table_lock.acquire();
- defer held.release();
-
- const dir = &self.os_data.wd_table.get(ev.wd).?.value;
- if (dir.file_table.get(basename_with_null)) |entry| {
- break :blk entry.value;
- } else {
- break :blk null;
- }
- };
- if (user_value) |v| {
- self.channel.put(Event{
- .id = WatchEventId.CloseWrite,
- .data = v,
- });
- }
+ const bytes_read = global_event_loop.read(self.os_data.inotify_fd, &event_buf, false) catch unreachable;
+
+ var ptr: [*]u8 = &event_buf;
+ const end_ptr = ptr + bytes_read;
+ while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) {
+ const ev = @ptrCast(*const os.linux.inotify_event, ptr);
+ if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
+ const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
+ const basename = std.mem.span(@ptrCast([*:0]u8, basename_ptr));
+
+ const dir = &self.os_data.wd_table.get(ev.wd).?;
+ if (dir.file_table.getEntry(basename)) |file_value| {
+ self.channel.put(Event{
+ .id = .CloseWrite,
+ .data = file_value.value,
+ .dirname = dir.dirname,
+ .basename = file_value.key,
+ });
+ }
+ } else if (ev.mask & os.linux.IN_IGNORED == os.linux.IN_IGNORED) {
+ // Directory watch was removed
+ const held = self.os_data.table_lock.acquire();
+ defer held.release();
+ if (self.os_data.wd_table.remove(ev.wd)) |*wd_entry| {
+ var file_it = wd_entry.value.file_table.iterator();
+ while (file_it.next()) |file_entry| {
+ self.allocator.free(file_entry.key);
}
-
- ptr = @alignCast(@alignOf(os.linux.inotify_event), ptr + @sizeOf(os.linux.inotify_event) + ev.len);
+ self.allocator.free(wd_entry.value.dirname);
+ wd_entry.value.file_table.deinit(self.allocator);
}
- },
- os.linux.EINTR => continue,
- os.linux.EINVAL => unreachable,
- os.linux.EFAULT => unreachable,
- os.linux.EAGAIN => {
- global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN | os.EPOLLONESHOT);
- },
- else => unreachable,
+ } else if (ev.mask & os.linux.IN_DELETE == os.linux.IN_DELETE) {
+ // File or directory was removed or deleted
+ const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
+ const basename = std.mem.span(@ptrCast([*:0]u8, basename_ptr));
+
+ const dir = &self.os_data.wd_table.get(ev.wd).?;
+ if (dir.file_table.getEntry(basename)) |file_value| {
+ self.channel.put(Event{
+ .id = .Delete,
+ .data = file_value.value,
+ .dirname = dir.dirname,
+ .basename = file_value.key,
+ });
+ }
+ }
+
+ ptr = @alignCast(@alignOf(os.linux.inotify_event), ptr + @sizeOf(os.linux.inotify_event) + ev.len);
}
}
}
@@ -617,19 +636,19 @@ pub fn Watch(comptime V: type) type {
const test_tmp_dir = "std_event_fs_test";
-test "write a file, watch it, write it again" {
- // TODO re-enable this test
- if (true) return error.SkipZigTest;
+test "write a file, watch it, write it again, delete it" {
+ if (!std.io.is_async) return error.SkipZigTest;
+ // TODO https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
- try fs.cwd().makePath(test_tmp_dir);
- defer fs.cwd().deleteTree(test_tmp_dir) catch {};
+ try std.fs.cwd().makePath(test_tmp_dir);
+ defer std.fs.cwd().deleteTree(test_tmp_dir) catch {};
- const allocator = std.heap.page_allocator;
- return testFsWatch(&allocator);
+ return testWriteWatchWriteDelete(std.testing.allocator);
}
-fn testFsWatch(allocator: *Allocator) !void {
- const file_path = try std.fs.path.join(allocator, [_][]const u8{ test_tmp_dir, "file.txt" });
+fn testWriteWatchWriteDelete(allocator: *Allocator) !void {
+ const file_path = try std.fs.path.join(allocator, &[_][]const u8{ test_tmp_dir, "file.txt" });
defer allocator.free(file_path);
const contents =
@@ -639,9 +658,10 @@ fn testFsWatch(allocator: *Allocator) !void {
const line2_offset = 7;
// first just write then read the file
- try writeFile(allocator, file_path, contents);
+ try std.fs.cwd().writeFile(file_path, contents);
- const read_contents = try readFile(allocator, file_path, 1024 * 1024);
+ const read_contents = try std.fs.cwd().readFileAlloc(allocator, file_path, 1024 * 1024);
+ defer allocator.free(read_contents);
testing.expectEqualSlices(u8, contents, read_contents);
// now watch the file
@@ -650,28 +670,49 @@ fn testFsWatch(allocator: *Allocator) !void {
testing.expect((try watch.addFile(file_path, {})) == null);
- const ev = watch.channel.get();
+ var ev = async watch.channel.get();
var ev_consumed = false;
- defer if (!ev_consumed) await ev;
+ defer if (!ev_consumed) {
+ _ = await ev;
+ };
// overwrite line 2
- const fd = try await openReadWrite(file_path, File.default_mode);
+ const file = try std.fs.cwd().openFile(file_path, .{ .read = true, .write = true });
{
- defer os.close(fd);
-
- try pwritev(allocator, fd, []const []const u8{"lorem ipsum"}, line2_offset);
+ defer file.close();
+ const write_contents = "lorem ipsum";
+ var iovec = [_]os.iovec_const{.{
+ .iov_base = write_contents,
+ .iov_len = write_contents.len,
+ }};
+ _ = try file.pwritevAll(&iovec, line2_offset);
}
- ev_consumed = true;
switch ((try await ev).id) {
- WatchEventId.CloseWrite => {},
- WatchEventId.Delete => @panic("wrong event"),
+ .CloseWrite => {
+ ev_consumed = true;
+ },
+ .Delete => @panic("wrong event"),
}
- const contents_updated = try readFile(allocator, file_path, 1024 * 1024);
+
+ const contents_updated = try std.fs.cwd().readFileAlloc(allocator, file_path, 1024 * 1024);
+ defer allocator.free(contents_updated);
+
testing.expectEqualSlices(u8,
\\line 1
\\lorem ipsum
, contents_updated);
- // TODO test deleting the file and then re-adding it. we should get events for both
+ ev = async watch.channel.get();
+ ev_consumed = false;
+
+ try std.fs.cwd().deleteFile(file_path);
+ switch ((try await ev).id) {
+ .Delete => {
+ ev_consumed = true;
+ },
+ .CloseWrite => @panic("wrong event"),
+ }
}
+
+// TODO Test: Add another file watch, remove the old file watch, get an event in the new
diff --git a/lib/std/hash.zig b/lib/std/hash.zig
index 7bac378316..f5b94725e2 100644
--- a/lib/std/hash.zig
+++ b/lib/std/hash.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/hash/adler.zig b/lib/std/hash/adler.zig
index a3fc915f76..9cd85ba7cf 100644
--- a/lib/std/hash/adler.zig
+++ b/lib/std/hash/adler.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig
index 2e707d5450..4afc2b425b 100644
--- a/lib/std/hash/auto_hash.zig
+++ b/lib/std/hash/auto_hash.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -99,7 +99,16 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
// Help the optimizer see that hashing an int is easy by inlining!
// TODO Check if the situation is better after #561 is resolved.
- .Int => @call(.{ .modifier = .always_inline }, hasher.update, .{std.mem.asBytes(&key)}),
+ .Int => {
+ if (comptime meta.trait.hasUniqueRepresentation(Key)) {
+ @call(.{ .modifier = .always_inline }, hasher.update, .{std.mem.asBytes(&key)});
+ } else {
+ // Take only the part containing the key value, the remaining
+ // bytes are undefined and must not be hashed!
+ const byte_size = comptime std.math.divCeil(comptime_int, @bitSizeOf(Key), 8) catch unreachable;
+ @call(.{ .modifier = .always_inline }, hasher.update, .{std.mem.asBytes(&key)[0..byte_size]});
+ }
+ },
.Bool => hash(hasher, @boolToInt(key), strat),
.Enum => hash(hasher, @enumToInt(key), strat),
@@ -160,21 +169,38 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
}
}
+fn typeContainsSlice(comptime K: type) bool {
+ comptime {
+ if (meta.trait.isSlice(K)) {
+ return true;
+ }
+ if (meta.trait.is(.Struct)(K)) {
+ inline for (@typeInfo(K).Struct.fields) |field| {
+ if (typeContainsSlice(field.field_type)) {
+ return true;
+ }
+ }
+ }
+ if (meta.trait.is(.Union)(K)) {
+ inline for (@typeInfo(K).Union.fields) |field| {
+ if (typeContainsSlice(field.field_type)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
+
/// Provides generic hashing for any eligible type.
/// Only hashes `key` itself, pointers are not followed.
-/// Slices are rejected to avoid ambiguity on the user's intention.
+/// Slices as well as unions and structs containing slices are rejected to avoid
+/// ambiguity on the user's intention.
pub fn autoHash(hasher: anytype, key: anytype) void {
const Key = @TypeOf(key);
- if (comptime meta.trait.isSlice(Key)) {
- comptime assert(@hasDecl(std, "StringHashMap")); // detect when the following message needs updated
- const extra_help = if (Key == []const u8)
- " Consider std.StringHashMap for hashing the contents of []const u8."
- else
- "";
-
- @compileError("std.auto_hash.autoHash does not allow slices (here " ++ @typeName(Key) ++
- ") because the intent is unclear. Consider using std.auto_hash.hash or providing your own hash function instead." ++
- extra_help);
+ if (comptime typeContainsSlice(Key)) {
+ @compileError("std.auto_hash.autoHash does not allow slices as well as unions and structs containing slices here (" ++ @typeName(Key) ++
+ ") because the intent is unclear. Consider using std.auto_hash.hash or providing your own hash function instead.");
}
hash(hasher, key, .Shallow);
@@ -211,6 +237,23 @@ fn testHashDeepRecursive(key: anytype) u64 {
return hasher.final();
}
+test "typeContainsSlice" {
+ comptime {
+ testing.expect(!typeContainsSlice(meta.Tag(std.builtin.TypeInfo)));
+
+ testing.expect(typeContainsSlice([]const u8));
+ testing.expect(!typeContainsSlice(u8));
+ const A = struct { x: []const u8 };
+ const B = struct { a: A };
+ const C = struct { b: B };
+ const D = struct { x: u8 };
+ testing.expect(typeContainsSlice(A));
+ testing.expect(typeContainsSlice(B));
+ testing.expect(typeContainsSlice(C));
+ testing.expect(!typeContainsSlice(D));
+ }
+}
+
test "hash pointer" {
const array = [_]u32{ 123, 123, 123 };
const a = &array[0];
diff --git a/lib/std/hash/benchmark.zig b/lib/std/hash/benchmark.zig
index f0cafa9971..19bb9aa8bf 100644
--- a/lib/std/hash/benchmark.zig
+++ b/lib/std/hash/benchmark.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/hash/cityhash.zig b/lib/std/hash/cityhash.zig
index 38e62d88ef..d7f2f1a9eb 100644
--- a/lib/std/hash/cityhash.zig
+++ b/lib/std/hash/cityhash.zig
@@ -1,11 +1,24 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
const std = @import("std");
const builtin = @import("builtin");
+fn offsetPtr(ptr: [*]const u8, offset: usize) callconv(.Inline) [*]const u8 {
+ // ptr + offset doesn't work at comptime so we need this instead.
+ return @ptrCast([*]const u8, &ptr[offset]);
+}
+
+fn fetch32(ptr: [*]const u8, offset: usize) u32 {
+ return std.mem.readIntLittle(u32, offsetPtr(ptr, offset)[0..4]);
+}
+
+fn fetch64(ptr: [*]const u8, offset: usize) u64 {
+ return std.mem.readIntLittle(u64, offsetPtr(ptr, offset)[0..8]);
+}
+
pub const CityHash32 = struct {
const Self = @This();
@@ -13,14 +26,6 @@ pub const CityHash32 = struct {
const c1: u32 = 0xcc9e2d51;
const c2: u32 = 0x1b873593;
- fn fetch32(ptr: [*]const u8) u32 {
- var v: u32 = undefined;
- @memcpy(@ptrCast([*]u8, &v), ptr, 4);
- if (builtin.endian == .Big)
- return @byteSwap(u32, v);
- return v;
- }
-
// A 32-bit to 32-bit integer hash copied from Murmur3.
fn fmix(h: u32) u32 {
var h1: u32 = h;
@@ -66,21 +71,21 @@ pub const CityHash32 = struct {
var c: u32 = 9;
const d: u32 = b;
- a +%= fetch32(str.ptr);
- b +%= fetch32(str.ptr + str.len - 4);
- c +%= fetch32(str.ptr + ((str.len >> 1) & 4));
+ a +%= fetch32(str.ptr, 0);
+ b +%= fetch32(str.ptr, str.len - 4);
+ c +%= fetch32(str.ptr, (str.len >> 1) & 4);
return fmix(mur(c, mur(b, mur(a, d))));
}
fn hash32Len13To24(str: []const u8) u32 {
const len: u32 = @truncate(u32, str.len);
- const a: u32 = fetch32(str.ptr + (str.len >> 1) - 4);
- const b: u32 = fetch32(str.ptr + 4);
- const c: u32 = fetch32(str.ptr + str.len - 8);
- const d: u32 = fetch32(str.ptr + (str.len >> 1));
- const e: u32 = fetch32(str.ptr);
- const f: u32 = fetch32(str.ptr + str.len - 4);
+ const a: u32 = fetch32(str.ptr, (str.len >> 1) - 4);
+ const b: u32 = fetch32(str.ptr, 4);
+ const c: u32 = fetch32(str.ptr, str.len - 8);
+ const d: u32 = fetch32(str.ptr, str.len >> 1);
+ const e: u32 = fetch32(str.ptr, 0);
+ const f: u32 = fetch32(str.ptr, str.len - 4);
return fmix(mur(f, mur(e, mur(d, mur(c, mur(b, mur(a, len)))))));
}
@@ -101,11 +106,11 @@ pub const CityHash32 = struct {
var g: u32 = c1 *% len;
var f: u32 = g;
- const a0: u32 = rotr32(fetch32(str.ptr + str.len - 4) *% c1, 17) *% c2;
- const a1: u32 = rotr32(fetch32(str.ptr + str.len - 8) *% c1, 17) *% c2;
- const a2: u32 = rotr32(fetch32(str.ptr + str.len - 16) *% c1, 17) *% c2;
- const a3: u32 = rotr32(fetch32(str.ptr + str.len - 12) *% c1, 17) *% c2;
- const a4: u32 = rotr32(fetch32(str.ptr + str.len - 20) *% c1, 17) *% c2;
+ const a0: u32 = rotr32(fetch32(str.ptr, str.len - 4) *% c1, 17) *% c2;
+ const a1: u32 = rotr32(fetch32(str.ptr, str.len - 8) *% c1, 17) *% c2;
+ const a2: u32 = rotr32(fetch32(str.ptr, str.len - 16) *% c1, 17) *% c2;
+ const a3: u32 = rotr32(fetch32(str.ptr, str.len - 12) *% c1, 17) *% c2;
+ const a4: u32 = rotr32(fetch32(str.ptr, str.len - 20) *% c1, 17) *% c2;
h ^= a0;
h = rotr32(h, 19);
@@ -125,11 +130,11 @@ pub const CityHash32 = struct {
var iters = (str.len - 1) / 20;
var ptr = str.ptr;
while (iters != 0) : (iters -= 1) {
- const b0: u32 = rotr32(fetch32(ptr) *% c1, 17) *% c2;
- const b1: u32 = fetch32(ptr + 4);
- const b2: u32 = rotr32(fetch32(ptr + 8) *% c1, 17) *% c2;
- const b3: u32 = rotr32(fetch32(ptr + 12) *% c1, 17) *% c2;
- const b4: u32 = fetch32(ptr + 16);
+ const b0: u32 = rotr32(fetch32(ptr, 0) *% c1, 17) *% c2;
+ const b1: u32 = fetch32(ptr, 4);
+ const b2: u32 = rotr32(fetch32(ptr, 8) *% c1, 17) *% c2;
+ const b3: u32 = rotr32(fetch32(ptr, 12) *% c1, 17) *% c2;
+ const b4: u32 = fetch32(ptr, 16);
h ^= b0;
h = rotr32(h, 18);
@@ -152,7 +157,7 @@ pub const CityHash32 = struct {
h = f;
f = g;
g = t;
- ptr += 20;
+ ptr = offsetPtr(ptr, 20);
}
g = rotr32(g, 11) *% c1;
g = rotr32(g, 17) *% c1;
@@ -176,22 +181,6 @@ pub const CityHash64 = struct {
const k1: u64 = 0xb492b66fbe98f273;
const k2: u64 = 0x9ae16a3b2f90404f;
- fn fetch32(ptr: [*]const u8) u32 {
- var v: u32 = undefined;
- @memcpy(@ptrCast([*]u8, &v), ptr, 4);
- if (builtin.endian == .Big)
- return @byteSwap(u32, v);
- return v;
- }
-
- fn fetch64(ptr: [*]const u8) u64 {
- var v: u64 = undefined;
- @memcpy(@ptrCast([*]u8, &v), ptr, 8);
- if (builtin.endian == .Big)
- return @byteSwap(u64, v);
- return v;
- }
-
// Rotate right helper
fn rotr64(x: u64, comptime r: u64) u64 {
return (x >> r) | (x << (64 - r));
@@ -222,16 +211,16 @@ pub const CityHash64 = struct {
const len: u64 = @as(u64, str.len);
if (len >= 8) {
const mul: u64 = k2 +% len *% 2;
- const a: u64 = fetch64(str.ptr) +% k2;
- const b: u64 = fetch64(str.ptr + str.len - 8);
+ const a: u64 = fetch64(str.ptr, 0) +% k2;
+ const b: u64 = fetch64(str.ptr, str.len - 8);
const c: u64 = rotr64(b, 37) *% mul +% a;
const d: u64 = (rotr64(a, 25) +% b) *% mul;
return hashLen16Mul(c, d, mul);
}
if (len >= 4) {
const mul: u64 = k2 +% len *% 2;
- const a: u64 = fetch32(str.ptr);
- return hashLen16Mul(len +% (a << 3), fetch32(str.ptr + str.len - 4), mul);
+ const a: u64 = fetch32(str.ptr, 0);
+ return hashLen16Mul(len +% (a << 3), fetch32(str.ptr, str.len - 4), mul);
}
if (len > 0) {
const a: u8 = str[0];
@@ -247,10 +236,10 @@ pub const CityHash64 = struct {
fn hashLen17To32(str: []const u8) u64 {
const len: u64 = @as(u64, str.len);
const mul: u64 = k2 +% len *% 2;
- const a: u64 = fetch64(str.ptr) *% k1;
- const b: u64 = fetch64(str.ptr + 8);
- const c: u64 = fetch64(str.ptr + str.len - 8) *% mul;
- const d: u64 = fetch64(str.ptr + str.len - 16) *% k2;
+ const a: u64 = fetch64(str.ptr, 0) *% k1;
+ const b: u64 = fetch64(str.ptr, 8);
+ const c: u64 = fetch64(str.ptr, str.len - 8) *% mul;
+ const d: u64 = fetch64(str.ptr, str.len - 16) *% k2;
return hashLen16Mul(rotr64(a +% b, 43) +% rotr64(c, 30) +% d, a +% rotr64(b +% k2, 18) +% c, mul);
}
@@ -258,14 +247,14 @@ pub const CityHash64 = struct {
fn hashLen33To64(str: []const u8) u64 {
const len: u64 = @as(u64, str.len);
const mul: u64 = k2 +% len *% 2;
- const a: u64 = fetch64(str.ptr) *% k2;
- const b: u64 = fetch64(str.ptr + 8);
- const c: u64 = fetch64(str.ptr + str.len - 24);
- const d: u64 = fetch64(str.ptr + str.len - 32);
- const e: u64 = fetch64(str.ptr + 16) *% k2;
- const f: u64 = fetch64(str.ptr + 24) *% 9;
- const g: u64 = fetch64(str.ptr + str.len - 8);
- const h: u64 = fetch64(str.ptr + str.len - 16) *% mul;
+ const a: u64 = fetch64(str.ptr, 0) *% k2;
+ const b: u64 = fetch64(str.ptr, 8);
+ const c: u64 = fetch64(str.ptr, str.len - 24);
+ const d: u64 = fetch64(str.ptr, str.len - 32);
+ const e: u64 = fetch64(str.ptr, 16) *% k2;
+ const f: u64 = fetch64(str.ptr, 24) *% 9;
+ const g: u64 = fetch64(str.ptr, str.len - 8);
+ const h: u64 = fetch64(str.ptr, str.len - 16) *% mul;
const u: u64 = rotr64(a +% g, 43) +% (rotr64(b, 30) +% c) *% 9;
const v: u64 = ((a +% g) ^ d) +% f +% 1;
@@ -297,10 +286,10 @@ pub const CityHash64 = struct {
fn weakHashLen32WithSeeds(ptr: [*]const u8, a: u64, b: u64) WeakPair {
return @call(.{ .modifier = .always_inline }, weakHashLen32WithSeedsHelper, .{
- fetch64(ptr),
- fetch64(ptr + 8),
- fetch64(ptr + 16),
- fetch64(ptr + 24),
+ fetch64(ptr, 0),
+ fetch64(ptr, 8),
+ fetch64(ptr, 16),
+ fetch64(ptr, 24),
a,
b,
});
@@ -319,29 +308,29 @@ pub const CityHash64 = struct {
var len: u64 = @as(u64, str.len);
- var x: u64 = fetch64(str.ptr + str.len - 40);
- var y: u64 = fetch64(str.ptr + str.len - 16) +% fetch64(str.ptr + str.len - 56);
- var z: u64 = hashLen16(fetch64(str.ptr + str.len - 48) +% len, fetch64(str.ptr + str.len - 24));
- var v: WeakPair = weakHashLen32WithSeeds(str.ptr + str.len - 64, len, z);
- var w: WeakPair = weakHashLen32WithSeeds(str.ptr + str.len - 32, y +% k1, x);
+ var x: u64 = fetch64(str.ptr, str.len - 40);
+ var y: u64 = fetch64(str.ptr, str.len - 16) +% fetch64(str.ptr, str.len - 56);
+ var z: u64 = hashLen16(fetch64(str.ptr, str.len - 48) +% len, fetch64(str.ptr, str.len - 24));
+ var v: WeakPair = weakHashLen32WithSeeds(offsetPtr(str.ptr, str.len - 64), len, z);
+ var w: WeakPair = weakHashLen32WithSeeds(offsetPtr(str.ptr, str.len - 32), y +% k1, x);
- x = x *% k1 +% fetch64(str.ptr);
+ x = x *% k1 +% fetch64(str.ptr, 0);
len = (len - 1) & ~@intCast(u64, 63);
var ptr: [*]const u8 = str.ptr;
while (true) {
- x = rotr64(x +% y +% v.first +% fetch64(ptr + 8), 37) *% k1;
- y = rotr64(y +% v.second +% fetch64(ptr + 48), 42) *% k1;
+ x = rotr64(x +% y +% v.first +% fetch64(ptr, 8), 37) *% k1;
+ y = rotr64(y +% v.second +% fetch64(ptr, 48), 42) *% k1;
x ^= w.second;
- y +%= v.first +% fetch64(ptr + 40);
+ y +%= v.first +% fetch64(ptr, 40);
z = rotr64(z +% w.first, 33) *% k1;
v = weakHashLen32WithSeeds(ptr, v.second *% k1, x +% w.first);
- w = weakHashLen32WithSeeds(ptr + 32, z +% w.second, y +% fetch64(ptr + 16));
+ w = weakHashLen32WithSeeds(offsetPtr(ptr, 32), z +% w.second, y +% fetch64(ptr, 16));
const t: u64 = z;
z = x;
x = t;
- ptr += 64;
+ ptr = offsetPtr(ptr, 64);
len -= 64;
if (len == 0)
break;
@@ -359,27 +348,31 @@ pub const CityHash64 = struct {
}
};
-fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {
- const hashbytes = hashbits / 8;
+fn SMHasherTest(comptime hash_fn: anytype) u32 {
+ const HashResult = @typeInfo(@TypeOf(hash_fn)).Fn.return_type.?;
+
var key: [256]u8 = undefined;
- var hashes: [hashbytes * 256]u8 = undefined;
- var final: [hashbytes]u8 = undefined;
+ var hashes_bytes: [256 * @sizeOf(HashResult)]u8 = undefined;
+ var final: HashResult = 0;
- @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@TypeOf(key)));
- @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@TypeOf(hashes)));
- @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@TypeOf(final)));
+ std.mem.set(u8, &key, 0);
+ std.mem.set(u8, &hashes_bytes, 0);
var i: u32 = 0;
while (i < 256) : (i += 1) {
key[i] = @intCast(u8, i);
- var h = hash_fn(key[0..i], 256 - i);
- if (builtin.endian == .Big)
- h = @byteSwap(@TypeOf(h), h);
- @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes);
+ var h: HashResult = hash_fn(key[0..i], 256 - i);
+
+ // comptime can't really do reinterpret casting yet,
+ // so we need to write the bytes manually.
+ for (hashes_bytes[i * @sizeOf(HashResult) ..][0..@sizeOf(HashResult)]) |*byte| {
+ byte.* = @truncate(u8, h);
+ h = h >> 8;
+ }
}
- return @truncate(u32, hash_fn(&hashes, 0));
+ return @truncate(u32, hash_fn(&hashes_bytes, 0));
}
fn CityHash32hashIgnoreSeed(str: []const u8, seed: u32) u32 {
@@ -387,13 +380,32 @@ fn CityHash32hashIgnoreSeed(str: []const u8, seed: u32) u32 {
}
test "cityhash32" {
- // Note: SMHasher doesn't provide a 32bit version of the algorithm.
- // Note: The implementation was verified against the Google Abseil version.
- std.testing.expectEqual(SMHasherTest(CityHash32hashIgnoreSeed, 32), 0x68254F81);
+ const Test = struct {
+ fn doTest() void {
+ // Note: SMHasher doesn't provide a 32bit version of the algorithm.
+ // Note: The implementation was verified against the Google Abseil version.
+ std.testing.expectEqual(SMHasherTest(CityHash32hashIgnoreSeed), 0x68254F81);
+ std.testing.expectEqual(SMHasherTest(CityHash32hashIgnoreSeed), 0x68254F81);
+ }
+ };
+ Test.doTest();
+ // TODO This is uncommented to prevent OOM on the CI server. Re-enable this test
+ // case once we ship stage2.
+ //@setEvalBranchQuota(50000);
+ //comptime Test.doTest();
}
test "cityhash64" {
- // Note: This is not compliant with the SMHasher implementation of CityHash64!
- // Note: The implementation was verified against the Google Abseil version.
- std.testing.expectEqual(SMHasherTest(CityHash64.hashWithSeed, 64), 0x5FABC5C5);
+ const Test = struct {
+ fn doTest() void {
+ // Note: This is not compliant with the SMHasher implementation of CityHash64!
+ // Note: The implementation was verified against the Google Abseil version.
+ std.testing.expectEqual(SMHasherTest(CityHash64.hashWithSeed), 0x5FABC5C5);
+ }
+ };
+ Test.doTest();
+ // TODO This is uncommented to prevent OOM on the CI server. Re-enable this test
+ // case once we ship stage2.
+ //@setEvalBranchQuota(50000);
+ //comptime Test.doTest();
}
diff --git a/lib/std/hash/crc.zig b/lib/std/hash/crc.zig
index 6290369fca..a2d6ed429c 100644
--- a/lib/std/hash/crc.zig
+++ b/lib/std/hash/crc.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -102,7 +102,11 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type {
};
}
+const please_windows_dont_oom = std.Target.current.os.tag == .windows;
+
test "crc32 ieee" {
+ if (please_windows_dont_oom) return error.SkipZigTest;
+
const Crc32Ieee = Crc32WithPoly(.IEEE);
testing.expect(Crc32Ieee.hash("") == 0x00000000);
@@ -111,6 +115,8 @@ test "crc32 ieee" {
}
test "crc32 castagnoli" {
+ if (please_windows_dont_oom) return error.SkipZigTest;
+
const Crc32Castagnoli = Crc32WithPoly(.Castagnoli);
testing.expect(Crc32Castagnoli.hash("") == 0x00000000);
@@ -167,6 +173,8 @@ pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type {
}
test "small crc32 ieee" {
+ if (please_windows_dont_oom) return error.SkipZigTest;
+
const Crc32Ieee = Crc32SmallWithPoly(.IEEE);
testing.expect(Crc32Ieee.hash("") == 0x00000000);
@@ -175,6 +183,8 @@ test "small crc32 ieee" {
}
test "small crc32 castagnoli" {
+ if (please_windows_dont_oom) return error.SkipZigTest;
+
const Crc32Castagnoli = Crc32SmallWithPoly(.Castagnoli);
testing.expect(Crc32Castagnoli.hash("") == 0x00000000);
diff --git a/lib/std/hash/fnv.zig b/lib/std/hash/fnv.zig
index 81285be9a8..99e3bd482d 100644
--- a/lib/std/hash/fnv.zig
+++ b/lib/std/hash/fnv.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/hash/murmur.zig b/lib/std/hash/murmur.zig
index 1e9156be4b..65dd523396 100644
--- a/lib/std/hash/murmur.zig
+++ b/lib/std/hash/murmur.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/hash/wyhash.zig b/lib/std/hash/wyhash.zig
index 8799a36b39..45530eccff 100644
--- a/lib/std/hash/wyhash.zig
+++ b/lib/std/hash/wyhash.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig
index d330863277..679575875d 100644
--- a/lib/std/hash_map.zig
+++ b/lib/std/hash_map.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -17,6 +17,17 @@ const Allocator = mem.Allocator;
const Wyhash = std.hash.Wyhash;
pub fn getAutoHashFn(comptime K: type) (fn (K) u64) {
+ comptime {
+ assert(@hasDecl(std, "StringHashMap")); // detect when the following message needs updated
+ if (K == []const u8) {
+ @compileError("std.auto_hash.autoHash does not allow slices here (" ++
+ @typeName(K) ++
+ ") because the intent is unclear. " ++
+ "Consider using std.StringHashMap for hashing the contents of []const u8. " ++
+ "Alternatively, consider using std.auto_hash.hash or providing your own hash function instead.");
+ }
+ }
+
return struct {
fn hash(key: K) u64 {
if (comptime trait.hasUniqueRepresentation(K)) {
diff --git a/lib/std/heap.zig b/lib/std/heap.zig
index a0484d4fdc..3e1a24beea 100644
--- a/lib/std/heap.zig
+++ b/lib/std/heap.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -160,8 +160,8 @@ var c_allocator_state = Allocator{
/// Asserts allocations are within `@alignOf(std.c.max_align_t)` and directly calls
/// `malloc`/`free`. Does not attempt to utilize `malloc_usable_size`.
/// This allocator is safe to use as the backing allocator with
-/// `ArenaAllocator` and `GeneralPurposeAllocator`, and is more optimal in these cases
-/// than to using `c_allocator`.
+/// `ArenaAllocator` for example and is more optimal in such a case
+/// than `c_allocator`.
pub const raw_c_allocator = &raw_c_allocator_state;
var raw_c_allocator_state = Allocator{
.allocFn = rawCAlloc,
@@ -789,7 +789,7 @@ pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) Stack
.fallback_allocator = fallback_allocator,
.fixed_buffer_allocator = undefined,
.allocator = Allocator{
- .allocFn = StackFallbackAllocator(size).realloc,
+ .allocFn = StackFallbackAllocator(size).alloc,
.resizeFn = StackFallbackAllocator(size).resize,
},
};
@@ -815,25 +815,25 @@ pub fn StackFallbackAllocator(comptime size: usize) type {
ptr_align: u29,
len_align: u29,
return_address: usize,
- ) error{OutOfMemory}![*]u8 {
+ ) error{OutOfMemory}![]u8 {
const self = @fieldParentPtr(Self, "allocator", allocator);
- return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, ptr_align) catch
- return fallback_allocator.alloc(len, ptr_align);
+ return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator.allocator, len, ptr_align, len_align, return_address) catch
+ return self.fallback_allocator.allocFn(self.fallback_allocator, len, ptr_align, len_align, return_address);
}
fn resize(
- self: *Allocator,
+ allocator: *Allocator,
buf: []u8,
buf_align: u29,
new_len: usize,
len_align: u29,
return_address: usize,
- ) error{OutOfMemory}!void {
+ ) error{OutOfMemory}!usize {
const self = @fieldParentPtr(Self, "allocator", allocator);
if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
- try self.fixed_buffer_allocator.resize(buf, new_len);
+ return FixedBufferAllocator.resize(&self.fixed_buffer_allocator.allocator, buf, buf_align, new_len, len_align, return_address);
} else {
- try self.fallback_allocator.resize(buf, new_len);
+ return self.fallback_allocator.resizeFn(self.fallback_allocator, buf, buf_align, new_len, len_align, return_address);
}
}
};
@@ -970,6 +970,16 @@ test "FixedBufferAllocator.reset" {
testing.expect(y.* == Y);
}
+test "StackFallbackAllocator" {
+ const fallback_allocator = page_allocator;
+ var stack_allocator = stackFallback(4096, fallback_allocator);
+
+ try testAllocator(stack_allocator.get());
+ try testAllocatorAligned(stack_allocator.get());
+ try testAllocatorLargeAlignment(stack_allocator.get());
+ try testAllocatorAlignedShrink(stack_allocator.get());
+}
+
test "FixedBufferAllocator Reuse memory on realloc" {
var small_fixed_buffer: [10]u8 = undefined;
// check if we re-use the memory
diff --git a/lib/std/heap/arena_allocator.zig b/lib/std/heap/arena_allocator.zig
index b7ee1d54c1..1b301bbb50 100644
--- a/lib/std/heap/arena_allocator.zig
+++ b/lib/std/heap/arena_allocator.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/heap/general_purpose_allocator.zig b/lib/std/heap/general_purpose_allocator.zig
index acda1e116e..c731f22d66 100644
--- a/lib/std/heap/general_purpose_allocator.zig
+++ b/lib/std/heap/general_purpose_allocator.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -98,7 +98,7 @@
//! in a `std.HashMap` using the backing allocator.
const std = @import("std");
-const log = std.log.scoped(.std);
+const log = std.log.scoped(.gpa);
const math = std.math;
const assert = std.debug.assert;
const mem = std.mem;
@@ -149,19 +149,22 @@ pub const Config = struct {
thread_safe: bool = !std.builtin.single_threaded,
/// What type of mutex you'd like to use, for thread safety.
- /// when specfied, the mutex type must have the same shape as `std.Mutex` and
- /// `std.mutex.Dummy`, and have no required fields. Specifying this field causes
+ /// when specfied, the mutex type must have the same shape as `std.Thread.Mutex` and
+ /// `std.Thread.Mutex.Dummy`, and have no required fields. Specifying this field causes
/// the `thread_safe` field to be ignored.
///
/// when null (default):
- /// * the mutex type defaults to `std.Mutex` when thread_safe is enabled.
- /// * the mutex type defaults to `std.mutex.Dummy` otherwise.
+ /// * the mutex type defaults to `std.Thread.Mutex` when thread_safe is enabled.
+ /// * the mutex type defaults to `std.Thread.Mutex.Dummy` otherwise.
MutexType: ?type = null,
/// This is a temporary debugging trick you can use to turn segfaults into more helpful
/// logged error messages with stack trace details. The downside is that every allocation
/// will be leaked!
never_unmap: bool = false,
+
+ /// Enables emitting info messages with the size and address of every allocation.
+ verbose_log: bool = false,
};
pub fn GeneralPurposeAllocator(comptime config: Config) type {
@@ -187,9 +190,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
const mutex_init = if (config.MutexType) |T|
T{}
else if (config.thread_safe)
- std.Mutex{}
+ std.Thread.Mutex{}
else
- std.mutex.Dummy{};
+ std.Thread.Mutex.Dummy{};
const stack_n = config.stack_trace_frames;
const one_trace_size = @sizeOf(usize) * stack_n;
@@ -314,7 +317,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
if (is_used) {
const slot_index = @intCast(SlotIndex, used_bits_byte * 8 + bit_index);
const stack_trace = bucketStackTrace(bucket, size_class, slot_index, .alloc);
- log.err("Memory leak detected: {}", .{stack_trace});
+ log.err("Memory leak detected: {s}", .{stack_trace});
leaks = true;
}
if (bit_index == math.maxInt(u3))
@@ -342,7 +345,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
}
var it = self.large_allocations.iterator();
while (it.next()) |large_alloc| {
- log.err("Memory leak detected: {}", .{large_alloc.value.getStackTrace()});
+ log.err("Memory leak detected: {s}", .{large_alloc.value.getStackTrace()});
leaks = true;
}
return leaks;
@@ -443,7 +446,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
.index = 0,
};
std.debug.captureStackTrace(ret_addr, &free_stack_trace);
- log.err("Allocation size {} bytes does not match free size {}. Allocation: {} Free: {}", .{
+ log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {s} Free: {s}", .{
entry.value.bytes.len,
old_mem.len,
entry.value.getStackTrace(),
@@ -454,10 +457,19 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
const result_len = try self.backing_allocator.resizeFn(self.backing_allocator, old_mem, old_align, new_size, len_align, ret_addr);
if (result_len == 0) {
+ if (config.verbose_log) {
+ log.info("large free {d} bytes at {*}", .{ old_mem.len, old_mem.ptr });
+ }
+
self.large_allocations.removeAssertDiscard(@ptrToInt(old_mem.ptr));
return 0;
}
+ if (config.verbose_log) {
+ log.info("large resize {d} bytes at {*} to {d}", .{
+ old_mem.len, old_mem.ptr, new_size,
+ });
+ }
entry.value.bytes = old_mem.ptr[0..result_len];
collectStackTrace(ret_addr, &entry.value.stack_addresses);
return result_len;
@@ -526,7 +538,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
.index = 0,
};
std.debug.captureStackTrace(ret_addr, &second_free_stack_trace);
- log.err("Double free detected. Allocation: {} First free: {} Second free: {}", .{
+ log.err("Double free detected. Allocation: {s} First free: {s} Second free: {s}", .{
alloc_stack_trace,
free_stack_trace,
second_free_stack_trace,
@@ -568,6 +580,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
} else {
@memset(old_mem.ptr, undefined, old_mem.len);
}
+ if (config.verbose_log) {
+ log.info("small free {d} bytes at {*}", .{ old_mem.len, old_mem.ptr });
+ }
return @as(usize, 0);
}
const new_aligned_size = math.max(new_size, old_align);
@@ -576,6 +591,11 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
if (old_mem.len > new_size) {
@memset(old_mem.ptr + new_size, undefined, old_mem.len - new_size);
}
+ if (config.verbose_log) {
+ log.info("small resize {d} bytes at {*} to {d}", .{
+ old_mem.len, old_mem.ptr, new_size,
+ });
+ }
return new_size;
}
return error.OutOfMemory;
@@ -623,6 +643,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
gop.entry.value.bytes = slice;
collectStackTrace(ret_addr, &gop.entry.value.stack_addresses);
+ if (config.verbose_log) {
+ log.info("large alloc {d} bytes at {*}", .{ slice.len, slice.ptr });
+ }
return slice;
}
@@ -632,6 +655,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
const ptr = try self.allocSlot(new_size_class, ret_addr);
+ if (config.verbose_log) {
+ log.info("small alloc {d} bytes at {*}", .{ len, ptr });
+ }
return ptr[0..len];
}
@@ -869,9 +895,9 @@ test "realloc large object to small object" {
}
test "overrideable mutexes" {
- var gpa = GeneralPurposeAllocator(.{ .MutexType = std.Mutex }){
+ var gpa = GeneralPurposeAllocator(.{ .MutexType = std.Thread.Mutex }){
.backing_allocator = std.testing.allocator,
- .mutex = std.Mutex{},
+ .mutex = std.Thread.Mutex{},
};
defer std.testing.expect(!gpa.deinit());
const allocator = &gpa.allocator;
diff --git a/lib/std/heap/logging_allocator.zig b/lib/std/heap/logging_allocator.zig
index f4e0ada764..7885571ab3 100644
--- a/lib/std/heap/logging_allocator.zig
+++ b/lib/std/heap/logging_allocator.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -9,22 +9,22 @@ const Allocator = std.mem.Allocator;
/// This allocator is used in front of another allocator and logs to the provided stream
/// on every call to the allocator. Stream errors are ignored.
/// If https://github.com/ziglang/zig/issues/2586 is implemented, this API can be improved.
-pub fn LoggingAllocator(comptime OutStreamType: type) type {
+pub fn LoggingAllocator(comptime Writer: type) type {
return struct {
allocator: Allocator,
parent_allocator: *Allocator,
- out_stream: OutStreamType,
+ writer: Writer,
const Self = @This();
- pub fn init(parent_allocator: *Allocator, out_stream: OutStreamType) Self {
+ pub fn init(parent_allocator: *Allocator, writer: Writer) Self {
return Self{
.allocator = Allocator{
.allocFn = alloc,
.resizeFn = resize,
},
.parent_allocator = parent_allocator,
- .out_stream = out_stream,
+ .writer = writer,
};
}
@@ -36,12 +36,12 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
ra: usize,
) error{OutOfMemory}![]u8 {
const self = @fieldParentPtr(Self, "allocator", allocator);
- self.out_stream.print("alloc : {}", .{len}) catch {};
+ self.writer.print("alloc : {}", .{len}) catch {};
const result = self.parent_allocator.allocFn(self.parent_allocator, len, ptr_align, len_align, ra);
if (result) |buff| {
- self.out_stream.print(" success!\n", .{}) catch {};
+ self.writer.print(" success!\n", .{}) catch {};
} else |err| {
- self.out_stream.print(" failure!\n", .{}) catch {};
+ self.writer.print(" failure!\n", .{}) catch {};
}
return result;
}
@@ -56,20 +56,20 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
) error{OutOfMemory}!usize {
const self = @fieldParentPtr(Self, "allocator", allocator);
if (new_len == 0) {
- self.out_stream.print("free : {}\n", .{buf.len}) catch {};
+ self.writer.print("free : {}\n", .{buf.len}) catch {};
} else if (new_len <= buf.len) {
- self.out_stream.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {};
+ self.writer.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {};
} else {
- self.out_stream.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
+ self.writer.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
}
if (self.parent_allocator.resizeFn(self.parent_allocator, buf, buf_align, new_len, len_align, ra)) |resized_len| {
if (new_len > buf.len) {
- self.out_stream.print(" success!\n", .{}) catch {};
+ self.writer.print(" success!\n", .{}) catch {};
}
return resized_len;
} else |e| {
std.debug.assert(new_len > buf.len);
- self.out_stream.print(" failure!\n", .{}) catch {};
+ self.writer.print(" failure!\n", .{}) catch {};
return e;
}
}
@@ -78,9 +78,9 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
pub fn loggingAllocator(
parent_allocator: *Allocator,
- out_stream: anytype,
-) LoggingAllocator(@TypeOf(out_stream)) {
- return LoggingAllocator(@TypeOf(out_stream)).init(parent_allocator, out_stream);
+ writer: anytype,
+) LoggingAllocator(@TypeOf(writer)) {
+ return LoggingAllocator(@TypeOf(writer)).init(parent_allocator, writer);
}
test "LoggingAllocator" {
@@ -89,7 +89,7 @@ test "LoggingAllocator" {
var allocator_buf: [10]u8 = undefined;
var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf));
- const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.outStream()).allocator;
+ const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.writer()).allocator;
var a = try allocator.alloc(u8, 10);
a = allocator.shrink(a, 5);
diff --git a/lib/std/io.zig b/lib/std/io.zig
index 3f02128a6c..b529c57866 100644
--- a/lib/std/io.zig
+++ b/lib/std/io.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -107,26 +107,14 @@ pub fn getStdIn() File {
}
pub const Reader = @import("io/reader.zig").Reader;
-/// Deprecated: use `Reader`
-pub const InStream = Reader;
pub const Writer = @import("io/writer.zig").Writer;
-/// Deprecated: use `Writer`
-pub const OutStream = Writer;
pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
pub const BufferedWriter = @import("io/buffered_writer.zig").BufferedWriter;
pub const bufferedWriter = @import("io/buffered_writer.zig").bufferedWriter;
-/// Deprecated: use `BufferedWriter`
-pub const BufferedOutStream = BufferedWriter;
-/// Deprecated: use `bufferedWriter`
-pub const bufferedOutStream = bufferedWriter;
pub const BufferedReader = @import("io/buffered_reader.zig").BufferedReader;
pub const bufferedReader = @import("io/buffered_reader.zig").bufferedReader;
-/// Deprecated: use `BufferedReader`
-pub const BufferedInStream = BufferedReader;
-/// Deprecated: use `bufferedReader`
-pub const bufferedInStream = bufferedReader;
pub const PeekStream = @import("io/peek_stream.zig").PeekStream;
pub const peekStream = @import("io/peek_stream.zig").peekStream;
@@ -136,55 +124,33 @@ pub const fixedBufferStream = @import("io/fixed_buffer_stream.zig").fixedBufferS
pub const CWriter = @import("io/c_writer.zig").CWriter;
pub const cWriter = @import("io/c_writer.zig").cWriter;
-/// Deprecated: use `CWriter`
-pub const COutStream = CWriter;
-/// Deprecated: use `cWriter`
-pub const cOutStream = cWriter;
+
+pub const LimitedReader = @import("io/limited_reader.zig").LimitedReader;
+pub const limitedReader = @import("io/limited_reader.zig").limitedReader;
pub const CountingWriter = @import("io/counting_writer.zig").CountingWriter;
pub const countingWriter = @import("io/counting_writer.zig").countingWriter;
-/// Deprecated: use `CountingWriter`
-pub const CountingOutStream = CountingWriter;
-/// Deprecated: use `countingWriter`
-pub const countingOutStream = countingWriter;
+pub const CountingReader = @import("io/counting_reader.zig").CountingReader;
+pub const countingReader = @import("io/counting_reader.zig").countingReader;
pub const MultiWriter = @import("io/multi_writer.zig").MultiWriter;
pub const multiWriter = @import("io/multi_writer.zig").multiWriter;
-/// Deprecated: use `MultiWriter`
-pub const MultiOutStream = MultiWriter;
-/// Deprecated: use `multiWriter`
-pub const multiOutStream = multiWriter;
pub const BitReader = @import("io/bit_reader.zig").BitReader;
pub const bitReader = @import("io/bit_reader.zig").bitReader;
-/// Deprecated: use `BitReader`
-pub const BitInStream = BitReader;
-/// Deprecated: use `bitReader`
-pub const bitInStream = bitReader;
pub const BitWriter = @import("io/bit_writer.zig").BitWriter;
pub const bitWriter = @import("io/bit_writer.zig").bitWriter;
-/// Deprecated: use `BitWriter`
-pub const BitOutStream = BitWriter;
-/// Deprecated: use `bitWriter`
-pub const bitOutStream = bitWriter;
-
-pub const AutoIndentingStream = @import("io/auto_indenting_stream.zig").AutoIndentingStream;
-pub const autoIndentingStream = @import("io/auto_indenting_stream.zig").autoIndentingStream;
pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;
pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream;
-pub const FindByteOutStream = @import("io/find_byte_out_stream.zig").FindByteOutStream;
-pub const findByteOutStream = @import("io/find_byte_out_stream.zig").findByteOutStream;
-
-pub const Packing = @import("io/serialization.zig").Packing;
-
-pub const Serializer = @import("io/serialization.zig").Serializer;
-pub const serializer = @import("io/serialization.zig").serializer;
-
-pub const Deserializer = @import("io/serialization.zig").Deserializer;
-pub const deserializer = @import("io/serialization.zig").deserializer;
+pub const FindByteWriter = @import("io/find_byte_writer.zig").FindByteWriter;
+pub const findByteWriter = @import("io/find_byte_writer.zig").findByteWriter;
+/// Deprecated: use `FindByteWriter`.
+pub const FindByteOutStream = FindByteWriter;
+/// Deprecated: use `findByteWriter`.
+pub const findByteOutStream = findByteWriter;
pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;
@@ -193,12 +159,7 @@ pub const StreamSource = @import("io/stream_source.zig").StreamSource;
/// A Writer that doesn't write to anything.
pub const null_writer = @as(NullWriter, .{ .context = {} });
-/// Deprecated: use `null_writer`
-pub const null_out_stream = null_writer;
-
const NullWriter = Writer(void, error{}, dummyWrite);
-/// Deprecated: use NullWriter
-const NullOutStream = NullWriter;
fn dummyWrite(context: void, data: []const u8) error{}!usize {
return data.len;
}
@@ -207,7 +168,7 @@ test "null_writer" {
null_writer.writeAll("yay" ** 10) catch |err| switch (err) {};
}
-test "" {
+test {
_ = @import("io/bit_reader.zig");
_ = @import("io/bit_writer.zig");
_ = @import("io/buffered_atomic_file.zig");
@@ -215,12 +176,12 @@ test "" {
_ = @import("io/buffered_writer.zig");
_ = @import("io/c_writer.zig");
_ = @import("io/counting_writer.zig");
+ _ = @import("io/counting_reader.zig");
_ = @import("io/fixed_buffer_stream.zig");
_ = @import("io/reader.zig");
_ = @import("io/writer.zig");
_ = @import("io/peek_stream.zig");
_ = @import("io/seekable_stream.zig");
- _ = @import("io/serialization.zig");
_ = @import("io/stream_source.zig");
_ = @import("io/test.zig");
}
diff --git a/lib/std/io/auto_indenting_stream.zig b/lib/std/io/auto_indenting_stream.zig
deleted file mode 100644
index bea4af7519..0000000000
--- a/lib/std/io/auto_indenting_stream.zig
+++ /dev/null
@@ -1,154 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-
-const std = @import("../std.zig");
-const io = std.io;
-const mem = std.mem;
-const assert = std.debug.assert;
-
-/// Automatically inserts indentation of written data by keeping
-/// track of the current indentation level
-pub fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
- return struct {
- const Self = @This();
- pub const Error = UnderlyingWriter.Error;
- pub const Writer = io.Writer(*Self, Error, write);
-
- underlying_writer: UnderlyingWriter,
-
- indent_count: usize = 0,
- indent_delta: usize,
- current_line_empty: bool = true,
- indent_one_shot_count: usize = 0, // automatically popped when applied
- applied_indent: usize = 0, // the most recently applied indent
- indent_next_line: usize = 0, // not used until the next line
-
- pub fn writer(self: *Self) Writer {
- return .{ .context = self };
- }
-
- pub fn write(self: *Self, bytes: []const u8) Error!usize {
- if (bytes.len == 0)
- return @as(usize, 0);
-
- try self.applyIndent();
- return self.writeNoIndent(bytes);
- }
-
- // Change the indent delta without changing the final indentation level
- pub fn setIndentDelta(self: *Self, indent_delta: usize) void {
- if (self.indent_delta == indent_delta) {
- return;
- } else if (self.indent_delta > indent_delta) {
- assert(self.indent_delta % indent_delta == 0);
- self.indent_count = self.indent_count * (self.indent_delta / indent_delta);
- } else {
- // assert that the current indentation (in spaces) in a multiple of the new delta
- assert((self.indent_count * self.indent_delta) % indent_delta == 0);
- self.indent_count = self.indent_count / (indent_delta / self.indent_delta);
- }
- self.indent_delta = indent_delta;
- }
-
- fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
- if (bytes.len == 0)
- return @as(usize, 0);
-
- try self.underlying_writer.writeAll(bytes);
- if (bytes[bytes.len - 1] == '\n')
- self.resetLine();
- return bytes.len;
- }
-
- pub fn insertNewline(self: *Self) Error!void {
- _ = try self.writeNoIndent("\n");
- }
-
- fn resetLine(self: *Self) void {
- self.current_line_empty = true;
- self.indent_next_line = 0;
- }
-
- /// Insert a newline unless the current line is blank
- pub fn maybeInsertNewline(self: *Self) Error!void {
- if (!self.current_line_empty)
- try self.insertNewline();
- }
-
- /// Push default indentation
- pub fn pushIndent(self: *Self) void {
- // Doesn't actually write any indentation.
- // Just primes the stream to be able to write the correct indentation if it needs to.
- self.indent_count += 1;
- }
-
- /// Push an indent that is automatically popped after being applied
- pub fn pushIndentOneShot(self: *Self) void {
- self.indent_one_shot_count += 1;
- self.pushIndent();
- }
-
- /// Turns all one-shot indents into regular indents
- /// Returns number of indents that must now be manually popped
- pub fn lockOneShotIndent(self: *Self) usize {
- var locked_count = self.indent_one_shot_count;
- self.indent_one_shot_count = 0;
- return locked_count;
- }
-
- /// Push an indent that should not take effect until the next line
- pub fn pushIndentNextLine(self: *Self) void {
- self.indent_next_line += 1;
- self.pushIndent();
- }
-
- pub fn popIndent(self: *Self) void {
- assert(self.indent_count != 0);
- self.indent_count -= 1;
-
- if (self.indent_next_line > 0)
- self.indent_next_line -= 1;
- }
-
- /// Writes ' ' bytes if the current line is empty
- fn applyIndent(self: *Self) Error!void {
- const current_indent = self.currentIndent();
- if (self.current_line_empty and current_indent > 0) {
- try self.underlying_writer.writeByteNTimes(' ', current_indent);
- self.applied_indent = current_indent;
- }
-
- self.indent_count -= self.indent_one_shot_count;
- self.indent_one_shot_count = 0;
- self.current_line_empty = false;
- }
-
- /// Checks to see if the most recent indentation exceeds the currently pushed indents
- pub fn isLineOverIndented(self: *Self) bool {
- if (self.current_line_empty) return false;
- return self.applied_indent > self.currentIndent();
- }
-
- fn currentIndent(self: *Self) usize {
- var indent_current: usize = 0;
- if (self.indent_count > 0) {
- const indent_count = self.indent_count - self.indent_next_line;
- indent_current = indent_count * self.indent_delta;
- }
- return indent_current;
- }
- };
-}
-
-pub fn autoIndentingStream(
- indent_delta: usize,
- underlying_writer: anytype,
-) AutoIndentingStream(@TypeOf(underlying_writer)) {
- return AutoIndentingStream(@TypeOf(underlying_writer)){
- .underlying_writer = underlying_writer,
- .indent_delta = indent_delta,
- };
-}
diff --git a/lib/std/io/bit_in_stream.zig b/lib/std/io/bit_in_stream.zig
deleted file mode 100644
index a027deb802..0000000000
--- a/lib/std/io/bit_in_stream.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-/// Deprecated: use `std.io.bit_reader.BitReader`
-pub const BitInStream = @import("./bit_reader.zig").BitReader;
-
-/// Deprecated: use `std.io.bit_reader.bitReader`
-pub const bitInStream = @import("./bit_reader.zig").bitReader;
diff --git a/lib/std/io/bit_out_stream.zig b/lib/std/io/bit_out_stream.zig
deleted file mode 100644
index 171fb542da..0000000000
--- a/lib/std/io/bit_out_stream.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-/// Deprecated: use `std.io.bit_writer.BitWriter`
-pub const BitOutStream = @import("./bit_writer.zig").BitWriter;
-
-/// Deprecated: use `std.io.bit_writer.bitWriter`
-pub const bitOutStream = @import("./bit_writer.zig").bitWriter;
diff --git a/lib/std/io/bit_reader.zig b/lib/std/io/bit_reader.zig
index 75d217068e..213cd2b503 100644
--- a/lib/std/io/bit_reader.zig
+++ b/lib/std/io/bit_reader.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -21,8 +21,6 @@ pub fn BitReader(endian: builtin.Endian, comptime ReaderType: type) type {
pub const Error = ReaderType.Error;
pub const Reader = io.Reader(*Self, Error, read);
- /// Deprecated: use `Reader`
- pub const InStream = io.InStream(*Self, Error, read);
const Self = @This();
const u8_bit_count = comptime meta.bitCount(u8);
@@ -165,11 +163,6 @@ pub fn BitReader(endian: builtin.Endian, comptime ReaderType: type) type {
pub fn reader(self: *Self) Reader {
return .{ .context = self };
}
-
- /// Deprecated: use `reader`
- pub fn inStream(self: *Self) InStream {
- return .{ .context = self };
- }
};
}
diff --git a/lib/std/io/bit_writer.zig b/lib/std/io/bit_writer.zig
index d2ea9b525e..3ad2b75efb 100644
--- a/lib/std/io/bit_writer.zig
+++ b/lib/std/io/bit_writer.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -21,8 +21,6 @@ pub fn BitWriter(endian: builtin.Endian, comptime WriterType: type) type {
pub const Error = WriterType.Error;
pub const Writer = io.Writer(*Self, Error, write);
- /// Deprecated: use `Writer`
- pub const OutStream = io.OutStream(*Self, Error, write);
const Self = @This();
const u8_bit_count = comptime meta.bitCount(u8);
@@ -141,10 +139,6 @@ pub fn BitWriter(endian: builtin.Endian, comptime WriterType: type) type {
pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
- /// Deprecated: use `writer`
- pub fn outStream(self: *Self) OutStream {
- return .{ .context = self };
- }
};
}
diff --git a/lib/std/io/buffered_atomic_file.zig b/lib/std/io/buffered_atomic_file.zig
index 6284d4e44f..1aed190a47 100644
--- a/lib/std/io/buffered_atomic_file.zig
+++ b/lib/std/io/buffered_atomic_file.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -10,13 +10,13 @@ const File = std.fs.File;
pub const BufferedAtomicFile = struct {
atomic_file: fs.AtomicFile,
- file_stream: File.OutStream,
- buffered_stream: BufferedOutStream,
+ file_writer: File.Writer,
+ buffered_writer: BufferedWriter,
allocator: *mem.Allocator,
pub const buffer_size = 4096;
- pub const BufferedOutStream = std.io.BufferedOutStream(buffer_size, File.OutStream);
- pub const OutStream = std.io.OutStream(*BufferedOutStream, BufferedOutStream.Error, BufferedOutStream.write);
+ pub const BufferedWriter = std.io.BufferedWriter(buffer_size, File.Writer);
+ pub const Writer = std.io.Writer(*BufferedWriter, BufferedWriter.Error, BufferedWriter.write);
/// TODO when https://github.com/ziglang/zig/issues/2761 is solved
/// this API will not need an allocator
@@ -29,8 +29,8 @@ pub const BufferedAtomicFile = struct {
var self = try allocator.create(BufferedAtomicFile);
self.* = BufferedAtomicFile{
.atomic_file = undefined,
- .file_stream = undefined,
- .buffered_stream = undefined,
+ .file_writer = undefined,
+ .buffered_writer = undefined,
.allocator = allocator,
};
errdefer allocator.destroy(self);
@@ -38,8 +38,8 @@ pub const BufferedAtomicFile = struct {
self.atomic_file = try dir.atomicFile(dest_path, atomic_file_options);
errdefer self.atomic_file.deinit();
- self.file_stream = self.atomic_file.file.outStream();
- self.buffered_stream = .{ .unbuffered_writer = self.file_stream };
+ self.file_writer = self.atomic_file.file.writer();
+ self.buffered_writer = .{ .unbuffered_writer = self.file_writer };
return self;
}
@@ -50,11 +50,11 @@ pub const BufferedAtomicFile = struct {
}
pub fn finish(self: *BufferedAtomicFile) !void {
- try self.buffered_stream.flush();
+ try self.buffered_writer.flush();
try self.atomic_file.finish();
}
- pub fn stream(self: *BufferedAtomicFile) OutStream {
- return .{ .context = &self.buffered_stream };
+ pub fn writer(self: *BufferedAtomicFile) Writer {
+ return .{ .context = &self.buffered_writer };
}
};
diff --git a/lib/std/io/buffered_in_stream.zig b/lib/std/io/buffered_in_stream.zig
deleted file mode 100644
index f055978152..0000000000
--- a/lib/std/io/buffered_in_stream.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-/// Deprecated: use `std.io.buffered_reader.BufferedReader`
-pub const BufferedInStream = @import("./buffered_reader.zig").BufferedReader;
-
-/// Deprecated: use `std.io.buffered_reader.bufferedReader`
-pub const bufferedInStream = @import("./buffered_reader.zig").bufferedReader;
diff --git a/lib/std/io/buffered_out_stream.zig b/lib/std/io/buffered_out_stream.zig
deleted file mode 100644
index 5f1eaa6faf..0000000000
--- a/lib/std/io/buffered_out_stream.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-/// Deprecated: use `std.io.buffered_writer.BufferedWriter`
-pub const BufferedOutStream = @import("./buffered_writer.zig").BufferedWriter;
-
-/// Deprecated: use `std.io.buffered_writer.bufferedWriter`
-pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter;
diff --git a/lib/std/io/buffered_reader.zig b/lib/std/io/buffered_reader.zig
index 58c4f3b4fc..5fda7f2741 100644
--- a/lib/std/io/buffered_reader.zig
+++ b/lib/std/io/buffered_reader.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -15,8 +15,6 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty
pub const Error = ReaderType.Error;
pub const Reader = io.Reader(*Self, Error, read);
- /// Deprecated: use `Reader`
- pub const InStream = Reader;
const Self = @This();
const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
@@ -45,11 +43,6 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty
pub fn reader(self: *Self) Reader {
return .{ .context = self };
}
-
- /// Deprecated: use `reader`
- pub fn inStream(self: *Self) InStream {
- return .{ .context = self };
- }
};
}
diff --git a/lib/std/io/buffered_writer.zig b/lib/std/io/buffered_writer.zig
index bee3ff48af..056ff08987 100644
--- a/lib/std/io/buffered_writer.zig
+++ b/lib/std/io/buffered_writer.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -13,8 +13,6 @@ pub fn BufferedWriter(comptime buffer_size: usize, comptime WriterType: type) ty
pub const Error = WriterType.Error;
pub const Writer = io.Writer(*Self, Error, write);
- /// Deprecated: use `Writer`
- pub const OutStream = Writer;
const Self = @This();
const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
@@ -32,11 +30,6 @@ pub fn BufferedWriter(comptime buffer_size: usize, comptime WriterType: type) ty
return .{ .context = self };
}
- /// Deprecated: use writer
- pub fn outStream(self: *Self) Writer {
- return .{ .context = self };
- }
-
pub fn write(self: *Self, bytes: []const u8) Error!usize {
if (bytes.len >= self.fifo.writableLength()) {
try self.flush();
diff --git a/lib/std/io/c_out_stream.zig b/lib/std/io/c_out_stream.zig
deleted file mode 100644
index 69f4d9f5af..0000000000
--- a/lib/std/io/c_out_stream.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-/// Deprecated: use `std.io.c_writer.CWriter`
-pub const COutStream = @import("./c_writer.zig").CWriter;
-
-/// Deprecated: use `std.io.c_writer.cWriter`
-pub const cOutStream = @import("./c_writer.zig").cWriter;
diff --git a/lib/std/io/c_writer.zig b/lib/std/io/c_writer.zig
index 9fd10d827e..fa7d7eb13a 100644
--- a/lib/std/io/c_writer.zig
+++ b/lib/std/io/c_writer.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -34,7 +34,7 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u
}
}
-test "" {
+test {
if (!builtin.link_libc) return error.SkipZigTest;
const filename = "tmp_io_test_file.txt";
diff --git a/lib/std/io/change_detection_stream.zig b/lib/std/io/change_detection_stream.zig
index 52c3372094..57ef8a82bd 100644
--- a/lib/std/io/change_detection_stream.zig
+++ b/lib/std/io/change_detection_stream.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/io/counting_out_stream.zig b/lib/std/io/counting_out_stream.zig
deleted file mode 100644
index fecdf8adb0..0000000000
--- a/lib/std/io/counting_out_stream.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-/// Deprecated: use `std.io.counting_writer.CountingWriter`
-pub const CountingOutStream = @import("./counting_writer.zig").CountingWriter;
-
-/// Deprecated: use `std.io.counting_writer.countingWriter`
-pub const countingOutStream = @import("./counting_writer.zig").countingWriter;
diff --git a/lib/std/io/counting_reader.zig b/lib/std/io/counting_reader.zig
new file mode 100644
index 0000000000..1369155a73
--- /dev/null
+++ b/lib/std/io/counting_reader.zig
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("../std.zig");
+const io = std.io;
+const testing = std.testing;
+
+/// A Reader that counts how many bytes has been read from it.
+pub fn CountingReader(comptime ReaderType: anytype) type {
+ return struct {
+ child_reader: ReaderType,
+ bytes_read: u64 = 0,
+
+ pub const Error = ReaderType.Error;
+ pub const Reader = io.Reader(*@This(), Error, read);
+
+ pub fn read(self: *@This(), buf: []u8) Error!usize {
+ const amt = try self.child_reader.read(buf);
+ self.bytes_read += amt;
+ return amt;
+ }
+
+ pub fn reader(self: *@This()) Reader {
+ return .{ .context = self };
+ }
+ };
+}
+
+pub fn countingReader(reader: anytype) CountingReader(@TypeOf(reader)) {
+ return .{ .child_reader = reader };
+}
+
+test "io.CountingReader" {
+ const bytes = "yay" ** 100;
+ var fbs = io.fixedBufferStream(bytes);
+
+ var counting_stream = countingReader(fbs.reader());
+ const stream = counting_stream.reader();
+
+ //read and discard all bytes
+ while (stream.readByte()) |_| {} else |err| {
+ testing.expect(err == error.EndOfStream);
+ }
+
+ testing.expect(counting_stream.bytes_read == bytes.len);
+}
diff --git a/lib/std/io/counting_writer.zig b/lib/std/io/counting_writer.zig
index aefd459b90..f68c257486 100644
--- a/lib/std/io/counting_writer.zig
+++ b/lib/std/io/counting_writer.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -15,8 +15,6 @@ pub fn CountingWriter(comptime WriterType: type) type {
pub const Error = WriterType.Error;
pub const Writer = io.Writer(*Self, Error, write);
- /// Deprecated: use `Writer`
- pub const OutStream = Writer;
const Self = @This();
@@ -29,11 +27,6 @@ pub fn CountingWriter(comptime WriterType: type) type {
pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
-
- /// Deprecated: use `writer`
- pub fn outStream(self: *Self) OutStream {
- return .{ .context = self };
- }
};
}
diff --git a/lib/std/io/find_byte_out_stream.zig b/lib/std/io/find_byte_writer.zig
index 70e1e190b1..db45114d3e 100644
--- a/lib/std/io/find_byte_out_stream.zig
+++ b/lib/std/io/find_byte_writer.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -8,9 +8,9 @@ const std = @import("../std.zig");
const io = std.io;
const assert = std.debug.assert;
-/// An OutStream that returns whether the given character has been written to it.
+/// A Writer that returns whether the given character has been written to it.
/// The contents are not written to anything.
-pub fn FindByteOutStream(comptime UnderlyingWriter: type) type {
+pub fn FindByteWriter(comptime UnderlyingWriter: type) type {
return struct {
const Self = @This();
pub const Error = UnderlyingWriter.Error;
@@ -37,8 +37,8 @@ pub fn FindByteOutStream(comptime UnderlyingWriter: type) type {
};
}
-pub fn findByteOutStream(byte: u8, underlying_writer: anytype) FindByteOutStream(@TypeOf(underlying_writer)) {
- return FindByteOutStream(@TypeOf(underlying_writer)){
+pub fn findByteWriter(byte: u8, underlying_writer: anytype) FindByteWriter(@TypeOf(underlying_writer)) {
+ return FindByteWriter(@TypeOf(underlying_writer)){
.underlying_writer = underlying_writer,
.byte = byte,
.byte_found = false,
diff --git a/lib/std/io/fixed_buffer_stream.zig b/lib/std/io/fixed_buffer_stream.zig
index b1d2aaf89a..f86fd5a8d8 100644
--- a/lib/std/io/fixed_buffer_stream.zig
+++ b/lib/std/io/fixed_buffer_stream.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -23,11 +23,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
pub const GetSeekPosError = error{};
pub const Reader = io.Reader(*Self, ReadError, read);
- /// Deprecated: use `Reader`
- pub const InStream = io.InStream(*Self, ReadError, read);
pub const Writer = io.Writer(*Self, WriteError, write);
- /// Deprecated: use `Writer`
- pub const OutStream = Writer;
pub const SeekableStream = io.SeekableStream(
*Self,
@@ -45,20 +41,10 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
return .{ .context = self };
}
- /// Deprecated: use `inStream`
- pub fn inStream(self: *Self) InStream {
- return .{ .context = self };
- }
-
pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
- /// Deprecated: use `writer`
- pub fn outStream(self: *Self) OutStream {
- return .{ .context = self };
- }
-
pub fn seekableStream(self: *Self) SeekableStream {
return .{ .context = self };
}
@@ -147,7 +133,7 @@ test "FixedBufferStream output" {
var fbs = fixedBufferStream(&buf);
const stream = fbs.writer();
- try stream.print("{}{}!", .{ "Hello", "World" });
+ try stream.print("{s}{s}!", .{ "Hello", "World" });
testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten());
}
diff --git a/lib/std/io/in_stream.zig b/lib/std/io/in_stream.zig
deleted file mode 100644
index 4583591d42..0000000000
--- a/lib/std/io/in_stream.zig
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-/// Deprecated: use `std.io.reader.Reader`
-pub const InStream = @import("./reader.zig").Reader;
diff --git a/lib/std/io/limited_reader.zig b/lib/std/io/limited_reader.zig
new file mode 100644
index 0000000000..734558b1e6
--- /dev/null
+++ b/lib/std/io/limited_reader.zig
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("../std.zig");
+const io = std.io;
+const assert = std.debug.assert;
+const testing = std.testing;
+
+pub fn LimitedReader(comptime ReaderType: type) type {
+ return struct {
+ inner_reader: ReaderType,
+ bytes_left: u64,
+
+ pub const Error = ReaderType.Error;
+ pub const Reader = io.Reader(*Self, Error, read);
+
+ const Self = @This();
+
+ pub fn read(self: *Self, dest: []u8) Error!usize {
+ const max_read = std.math.min(self.bytes_left, dest.len);
+ const n = try self.inner_reader.read(dest[0..max_read]);
+ self.bytes_left -= n;
+ return n;
+ }
+
+ pub fn reader(self: *Self) Reader {
+ return .{ .context = self };
+ }
+ };
+}
+
+/// Returns an initialised `LimitedReader`
+/// `bytes_left` is a `u64` to be able to take 64 bit file offsets
+pub fn limitedReader(inner_reader: anytype, bytes_left: u64) LimitedReader(@TypeOf(inner_reader)) {
+ return .{ .inner_reader = inner_reader, .bytes_left = bytes_left };
+}
+
+test "basic usage" {
+ const data = "hello world";
+ var fbs = std.io.fixedBufferStream(data);
+ var early_stream = limitedReader(fbs.reader(), 3);
+
+ var buf: [5]u8 = undefined;
+ testing.expectEqual(@as(usize, 3), try early_stream.reader().read(&buf));
+ testing.expectEqualSlices(u8, data[0..3], buf[0..3]);
+ testing.expectEqual(@as(usize, 0), try early_stream.reader().read(&buf));
+ testing.expectError(error.EndOfStream, early_stream.reader().skipBytes(10, .{}));
+}
diff --git a/lib/std/io/multi_out_stream.zig b/lib/std/io/multi_out_stream.zig
deleted file mode 100644
index 7b96cc3d15..0000000000
--- a/lib/std/io/multi_out_stream.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-/// Deprecated: use `std.io.multi_writer.MultiWriter`
-pub const MultiOutStream = @import("./multi_writer.zig").MultiWriter;
-
-/// Deprecated: use `std.io.multi_writer.multiWriter`
-pub const multiOutStream = @import("./multi_writer.zig").multiWriter;
diff --git a/lib/std/io/multi_writer.zig b/lib/std/io/multi_writer.zig
index 7ee43eddeb..639dd3cd18 100644
--- a/lib/std/io/multi_writer.zig
+++ b/lib/std/io/multi_writer.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -22,18 +22,11 @@ pub fn MultiWriter(comptime Writers: type) type {
pub const Error = ErrSet;
pub const Writer = io.Writer(*Self, Error, write);
- /// Deprecated: use `Writer`
- pub const OutStream = Writer;
pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
- /// Deprecated: use `writer`
- pub fn outStream(self: *Self) OutStream {
- return .{ .context = self };
- }
-
pub fn write(self: *Self, bytes: []const u8) Error!usize {
var batch = std.event.Batch(Error!void, self.streams.len, .auto_async).init();
comptime var i = 0;
diff --git a/lib/std/io/peek_stream.zig b/lib/std/io/peek_stream.zig
index 82554d05ca..b431b0184d 100644
--- a/lib/std/io/peek_stream.zig
+++ b/lib/std/io/peek_stream.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -16,13 +16,11 @@ pub fn PeekStream(
comptime ReaderType: type,
) type {
return struct {
- unbuffered_in_stream: ReaderType,
+ unbuffered_reader: ReaderType,
fifo: FifoType,
pub const Error = ReaderType.Error;
pub const Reader = io.Reader(*Self, Error, read);
- /// Deprecated: use `Reader`
- pub const InStream = Reader;
const Self = @This();
const FifoType = std.fifo.LinearFifo(u8, buffer_type);
@@ -31,7 +29,7 @@ pub fn PeekStream(
.Static => struct {
pub fn init(base: ReaderType) Self {
return .{
- .unbuffered_in_stream = base,
+ .unbuffered_reader = base,
.fifo = FifoType.init(),
};
}
@@ -39,7 +37,7 @@ pub fn PeekStream(
.Slice => struct {
pub fn init(base: ReaderType, buf: []u8) Self {
return .{
- .unbuffered_in_stream = base,
+ .unbuffered_reader = base,
.fifo = FifoType.init(buf),
};
}
@@ -47,7 +45,7 @@ pub fn PeekStream(
.Dynamic => struct {
pub fn init(base: ReaderType, allocator: *mem.Allocator) Self {
return .{
- .unbuffered_in_stream = base,
+ .unbuffered_reader = base,
.fifo = FifoType.init(allocator),
};
}
@@ -68,18 +66,13 @@ pub fn PeekStream(
if (dest_index == dest.len) return dest_index;
// ask the backing stream for more
- dest_index += try self.unbuffered_in_stream.read(dest[dest_index..]);
+ dest_index += try self.unbuffered_reader.read(dest[dest_index..]);
return dest_index;
}
pub fn reader(self: *Self) Reader {
return .{ .context = self };
}
-
- /// Deprecated: use `reader`
- pub fn inStream(self: *Self) InStream {
- return .{ .context = self };
- }
};
}
diff --git a/lib/std/io/reader.zig b/lib/std/io/reader.zig
index b9a9eb3eeb..916e2155fa 100644
--- a/lib/std/io/reader.zig
+++ b/lib/std/io/reader.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -76,12 +76,12 @@ pub fn Reader(
start_index += bytes_read;
if (start_index - original_len > max_append_size) {
- array_list.shrink(original_len + max_append_size);
+ array_list.shrinkAndFree(original_len + max_append_size);
return error.StreamTooLong;
}
if (bytes_read != dest_slice.len) {
- array_list.shrink(start_index);
+ array_list.shrinkAndFree(start_index);
return;
}
@@ -101,35 +101,6 @@ pub fn Reader(
return array_list.toOwnedSlice();
}
- /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` or end-of-stream is found.
- /// Does not include the delimiter in the result.
- /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
- /// `std.ArrayList` is populated with `max_size` bytes from the stream.
- pub fn readUntilDelimiterOrEofArrayList(
- self: Self,
- array_list: *std.ArrayList(u8),
- delimiter: u8,
- max_size: usize,
- ) !void {
- array_list.shrink(0);
- while (true) {
- var byte: u8 = self.readByte() catch |err| switch (err) {
- error.EndOfStream => return,
- else => |e| return e,
- };
-
- if (byte == delimiter) {
- return;
- }
-
- if (array_list.items.len == max_size) {
- return error.StreamTooLong;
- }
-
- try array_list.append(byte);
- }
- }
-
/// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found.
/// Does not include the delimiter in the result.
/// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
@@ -140,7 +111,7 @@ pub fn Reader(
delimiter: u8,
max_size: usize,
) !void {
- array_list.shrink(0);
+ array_list.shrinkRetainingCapacity(0);
while (true) {
var byte: u8 = try self.readByte();
@@ -173,7 +144,10 @@ pub fn Reader(
}
/// Allocates enough memory to read until `delimiter` or end-of-stream.
- /// If the allocated memory would be greater than `max_size`, returns `error.StreamTooLong`.
+ /// If the allocated memory would be greater than `max_size`, returns
+ /// `error.StreamTooLong`. If end-of-stream is found, returns the rest
+ /// of the stream. If this function is called again after that, returns
+ /// null.
/// Caller owns returned memory.
/// If this function returns an error, the contents from the stream read so far are lost.
pub fn readUntilDelimiterOrEofAlloc(
@@ -181,10 +155,17 @@ pub fn Reader(
allocator: *mem.Allocator,
delimiter: u8,
max_size: usize,
- ) ![]u8 {
+ ) !?[]u8 {
var array_list = std.ArrayList(u8).init(allocator);
defer array_list.deinit();
- try self.readUntilDelimiterOrEofArrayList(&array_list, delimiter, max_size);
+ self.readUntilDelimiterArrayList(&array_list, delimiter, max_size) catch |err| switch (err) {
+ error.EndOfStream => if (array_list.items.len == 0) {
+ return null;
+ } else {
+ return array_list.toOwnedSlice();
+ },
+ else => |e| return e,
+ };
return array_list.toOwnedSlice();
}
@@ -290,8 +271,9 @@ pub fn Reader(
buf_size: usize = 512,
};
+ // `num_bytes` is a `u64` to match `off_t`
/// Reads `num_bytes` bytes from the stream and discards them
- pub fn skipBytes(self: Self, num_bytes: usize, comptime options: SkipBytesOptions) !void {
+ pub fn skipBytes(self: Self, num_bytes: u64, comptime options: SkipBytesOptions) !void {
var buf: [options.buf_size]u8 = undefined;
var remaining = num_bytes;
diff --git a/lib/std/io/seekable_stream.zig b/lib/std/io/seekable_stream.zig
index 15e537baa2..4ba39fff42 100644
--- a/lib/std/io/seekable_stream.zig
+++ b/lib/std/io/seekable_stream.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/io/serialization.zig b/lib/std/io/serialization.zig
deleted file mode 100644
index 3fbc203242..0000000000
--- a/lib/std/io/serialization.zig
+++ /dev/null
@@ -1,616 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("../std.zig");
-const builtin = std.builtin;
-const io = std.io;
-const assert = std.debug.assert;
-const math = std.math;
-const meta = std.meta;
-const trait = meta.trait;
-const testing = std.testing;
-
-pub const Packing = enum {
- /// Pack data to byte alignment
- Byte,
-
- /// Pack data to bit alignment
- Bit,
-};
-
-/// Creates a deserializer that deserializes types from any stream.
-/// If `is_packed` is true, the data stream is treated as bit-packed,
-/// otherwise data is expected to be packed to the smallest byte.
-/// Types may implement a custom deserialization routine with a
-/// function named `deserialize` in the form of:
-/// ```
-/// pub fn deserialize(self: *Self, deserializer: anytype) !void
-/// ```
-/// which will be called when the deserializer is used to deserialize
-/// that type. It will pass a pointer to the type instance to deserialize
-/// into and a pointer to the deserializer struct.
-pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime ReaderType: type) type {
- return struct {
- in_stream: if (packing == .Bit) io.BitReader(endian, ReaderType) else ReaderType,
-
- const Self = @This();
-
- pub fn init(in_stream: ReaderType) Self {
- return Self{
- .in_stream = switch (packing) {
- .Bit => io.bitReader(endian, in_stream),
- .Byte => in_stream,
- },
- };
- }
-
- pub fn alignToByte(self: *Self) void {
- if (packing == .Byte) return;
- self.in_stream.alignToByte();
- }
-
- //@BUG: inferred error issue. See: #1386
- fn deserializeInt(self: *Self, comptime T: type) (ReaderType.Error || error{EndOfStream})!T {
- comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
-
- const u8_bit_count = 8;
- const t_bit_count = comptime meta.bitCount(T);
-
- const U = std.meta.Int(.unsigned, t_bit_count);
- const Log2U = math.Log2Int(U);
- const int_size = (t_bit_count + 7) / 8;
-
- if (packing == .Bit) {
- const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
- return @bitCast(T, result);
- }
-
- var buffer: [int_size]u8 = undefined;
- const read_size = try self.in_stream.read(buffer[0..]);
- if (read_size < int_size) return error.EndOfStream;
-
- if (int_size == 1) {
- if (t_bit_count == 8) return @bitCast(T, buffer[0]);
- const PossiblySignedByte = std.meta.Int(@typeInfo(T).Int.signedness, 8);
- return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));
- }
-
- var result = @as(U, 0);
- for (buffer) |byte, i| {
- switch (endian) {
- .Big => {
- result = (result << u8_bit_count) | byte;
- },
- .Little => {
- result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i);
- },
- }
- }
-
- return @bitCast(T, result);
- }
-
- /// Deserializes and returns data of the specified type from the stream
- pub fn deserialize(self: *Self, comptime T: type) !T {
- var value: T = undefined;
- try self.deserializeInto(&value);
- return value;
- }
-
- /// Deserializes data into the type pointed to by `ptr`
- pub fn deserializeInto(self: *Self, ptr: anytype) !void {
- const T = @TypeOf(ptr);
- comptime assert(trait.is(.Pointer)(T));
-
- if (comptime trait.isSlice(T) or comptime trait.isPtrTo(.Array)(T)) {
- for (ptr) |*v|
- try self.deserializeInto(v);
- return;
- }
-
- comptime assert(trait.isSingleItemPtr(T));
-
- const C = comptime meta.Child(T);
- const child_type_id = @typeInfo(C);
-
- //custom deserializer: fn(self: *Self, deserializer: anytype) !void
- if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
-
- if (comptime trait.isPacked(C) and packing != .Bit) {
- var packed_deserializer = deserializer(endian, .Bit, self.in_stream);
- return packed_deserializer.deserializeInto(ptr);
- }
-
- switch (child_type_id) {
- .Void => return,
- .Bool => ptr.* = (try self.deserializeInt(u1)) > 0,
- .Float, .Int => ptr.* = try self.deserializeInt(C),
- .Struct => {
- const info = @typeInfo(C).Struct;
-
- inline for (info.fields) |*field_info| {
- const name = field_info.name;
- const FieldType = field_info.field_type;
-
- if (FieldType == void or FieldType == u0) continue;
-
- //it doesn't make any sense to read pointers
- if (comptime trait.is(.Pointer)(FieldType)) {
- @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++
- @typeName(C) ++ " because it " ++ "is of pointer-type " ++
- @typeName(FieldType) ++ ".");
- }
-
- try self.deserializeInto(&@field(ptr, name));
- }
- },
- .Union => {
- const info = @typeInfo(C).Union;
- if (info.tag_type) |TagType| {
- //we avoid duplicate iteration over the enum tags
- // by getting the int directly and casting it without
- // safety. If it is bad, it will be caught anyway.
- const TagInt = @TagType(TagType);
- const tag = try self.deserializeInt(TagInt);
-
- inline for (info.fields) |field_info| {
- if (@enumToInt(@field(TagType, field_info.name)) == tag) {
- const name = field_info.name;
- const FieldType = field_info.field_type;
- ptr.* = @unionInit(C, name, undefined);
- try self.deserializeInto(&@field(ptr, name));
- return;
- }
- }
- //This is reachable if the enum data is bad
- return error.InvalidEnumTag;
- }
- @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++
- " because it is an untagged union. Use a custom deserialize().");
- },
- .Optional => {
- const OC = comptime meta.Child(C);
- const exists = (try self.deserializeInt(u1)) > 0;
- if (!exists) {
- ptr.* = null;
- return;
- }
-
- ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe
- const val_ptr = &ptr.*.?;
- try self.deserializeInto(val_ptr);
- },
- .Enum => {
- var value = try self.deserializeInt(@TagType(C));
- ptr.* = try meta.intToEnum(C, value);
- },
- else => {
- @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented).");
- },
- }
- }
- };
-}
-
-pub fn deserializer(
- comptime endian: builtin.Endian,
- comptime packing: Packing,
- in_stream: anytype,
-) Deserializer(endian, packing, @TypeOf(in_stream)) {
- return Deserializer(endian, packing, @TypeOf(in_stream)).init(in_stream);
-}
-
-/// Creates a serializer that serializes types to any stream.
-/// If `is_packed` is true, the data will be bit-packed into the stream.
-/// Note that the you must call `serializer.flush()` when you are done
-/// writing bit-packed data in order ensure any unwritten bits are committed.
-/// If `is_packed` is false, data is packed to the smallest byte. In the case
-/// of packed structs, the struct will written bit-packed and with the specified
-/// endianess, after which data will resume being written at the next byte boundary.
-/// Types may implement a custom serialization routine with a
-/// function named `serialize` in the form of:
-/// ```
-/// pub fn serialize(self: Self, serializer: anytype) !void
-/// ```
-/// which will be called when the serializer is used to serialize that type. It will
-/// pass a const pointer to the type instance to be serialized and a pointer
-/// to the serializer struct.
-pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime OutStreamType: type) type {
- return struct {
- out_stream: if (packing == .Bit) io.BitOutStream(endian, OutStreamType) else OutStreamType,
-
- const Self = @This();
- pub const Error = OutStreamType.Error;
-
- pub fn init(out_stream: OutStreamType) Self {
- return Self{
- .out_stream = switch (packing) {
- .Bit => io.bitOutStream(endian, out_stream),
- .Byte => out_stream,
- },
- };
- }
-
- /// Flushes any unwritten bits to the stream
- pub fn flush(self: *Self) Error!void {
- if (packing == .Bit) return self.out_stream.flushBits();
- }
-
- fn serializeInt(self: *Self, value: anytype) Error!void {
- const T = @TypeOf(value);
- comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
-
- const t_bit_count = comptime meta.bitCount(T);
- const u8_bit_count = comptime meta.bitCount(u8);
-
- const U = std.meta.Int(.unsigned, t_bit_count);
- const Log2U = math.Log2Int(U);
- const int_size = (t_bit_count + 7) / 8;
-
- const u_value = @bitCast(U, value);
-
- if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count);
-
- var buffer: [int_size]u8 = undefined;
- if (int_size == 1) buffer[0] = u_value;
-
- for (buffer) |*byte, i| {
- const idx = switch (endian) {
- .Big => int_size - i - 1,
- .Little => i,
- };
- const shift = @intCast(Log2U, idx * u8_bit_count);
- const v = u_value >> shift;
- byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
- }
-
- try self.out_stream.writeAll(&buffer);
- }
-
- /// Serializes the passed value into the stream
- pub fn serialize(self: *Self, value: anytype) Error!void {
- const T = comptime @TypeOf(value);
-
- if (comptime trait.isIndexable(T)) {
- for (value) |v|
- try self.serialize(v);
- return;
- }
-
- //custom serializer: fn(self: Self, serializer: anytype) !void
- if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
-
- if (comptime trait.isPacked(T) and packing != .Bit) {
- var packed_serializer = Serializer(endian, .Bit, OutStreamType).init(self.out_stream);
- try packed_serializer.serialize(value);
- try packed_serializer.flush();
- return;
- }
-
- switch (@typeInfo(T)) {
- .Void => return,
- .Bool => try self.serializeInt(@as(u1, @boolToInt(value))),
- .Float, .Int => try self.serializeInt(value),
- .Struct => {
- const info = @typeInfo(T);
-
- inline for (info.Struct.fields) |*field_info| {
- const name = field_info.name;
- const FieldType = field_info.field_type;
-
- if (FieldType == void or FieldType == u0) continue;
-
- //It doesn't make sense to write pointers
- if (comptime trait.is(.Pointer)(FieldType)) {
- @compileError("Will not " ++ "serialize field " ++ name ++
- " of struct " ++ @typeName(T) ++ " because it " ++
- "is of pointer-type " ++ @typeName(FieldType) ++ ".");
- }
- try self.serialize(@field(value, name));
- }
- },
- .Union => {
- const info = @typeInfo(T).Union;
- if (info.tag_type) |TagType| {
- const active_tag = meta.activeTag(value);
- try self.serialize(active_tag);
- //This inline loop is necessary because active_tag is a runtime
- // value, but @field requires a comptime value. Our alternative
- // is to check each field for a match
- inline for (info.fields) |field_info| {
- if (@field(TagType, field_info.name) == active_tag) {
- const name = field_info.name;
- const FieldType = field_info.field_type;
- try self.serialize(@field(value, name));
- return;
- }
- }
- unreachable;
- }
- @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++
- " because it is an untagged union. Use a custom serialize().");
- },
- .Optional => {
- if (value == null) {
- try self.serializeInt(@as(u1, @boolToInt(false)));
- return;
- }
- try self.serializeInt(@as(u1, @boolToInt(true)));
-
- const OC = comptime meta.Child(T);
- const val_ptr = &value.?;
- try self.serialize(val_ptr.*);
- },
- .Enum => {
- try self.serializeInt(@enumToInt(value));
- },
- else => @compileError("Cannot serialize " ++ @tagName(@typeInfo(T)) ++ " types (unimplemented)."),
- }
- }
- };
-}
-
-pub fn serializer(
- comptime endian: builtin.Endian,
- comptime packing: Packing,
- out_stream: anytype,
-) Serializer(endian, packing, @TypeOf(out_stream)) {
- return Serializer(endian, packing, @TypeOf(out_stream)).init(out_stream);
-}
-
-fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
- @setEvalBranchQuota(1500);
- //@NOTE: if this test is taking too long, reduce the maximum tested bitsize
- const max_test_bitsize = 128;
-
- const total_bytes = comptime blk: {
- var bytes = 0;
- comptime var i = 0;
- while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0);
- break :blk bytes * 2;
- };
-
- var data_mem: [total_bytes]u8 = undefined;
- var out = io.fixedBufferStream(&data_mem);
- var _serializer = serializer(endian, packing, out.outStream());
-
- var in = io.fixedBufferStream(&data_mem);
- var _deserializer = deserializer(endian, packing, in.reader());
-
- comptime var i = 0;
- inline while (i <= max_test_bitsize) : (i += 1) {
- const U = std.meta.Int(.unsigned, i);
- const S = std.meta.Int(.signed, i);
- try _serializer.serializeInt(@as(U, i));
- if (i != 0) try _serializer.serializeInt(@as(S, -1)) else try _serializer.serialize(@as(S, 0));
- }
- try _serializer.flush();
-
- i = 0;
- inline while (i <= max_test_bitsize) : (i += 1) {
- const U = std.meta.Int(.unsigned, i);
- const S = std.meta.Int(.signed, i);
- const x = try _deserializer.deserializeInt(U);
- const y = try _deserializer.deserializeInt(S);
- testing.expect(x == @as(U, i));
- if (i != 0) testing.expect(y == @as(S, -1)) else testing.expect(y == 0);
- }
-
- const u8_bit_count = comptime meta.bitCount(u8);
- //0 + 1 + 2 + ... n = (n * (n + 1)) / 2
- //and we have each for unsigned and signed, so * 2
- const total_bits = (max_test_bitsize * (max_test_bitsize + 1));
- const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
- const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
-
- testing.expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes);
-
- //Verify that empty error set works with serializer.
- //deserializer is covered by FixedBufferStream
- var null_serializer = io.serializer(endian, packing, std.io.null_out_stream);
- try null_serializer.serialize(data_mem[0..]);
- try null_serializer.flush();
-}
-
-test "Serializer/Deserializer Int" {
- try testIntSerializerDeserializer(.Big, .Byte);
- try testIntSerializerDeserializer(.Little, .Byte);
- // TODO these tests are disabled due to tripping an LLVM assertion
- // https://github.com/ziglang/zig/issues/2019
- //try testIntSerializerDeserializer(builtin.Endian.Big, true);
- //try testIntSerializerDeserializer(builtin.Endian.Little, true);
-}
-
-fn testIntSerializerDeserializerInfNaN(
- comptime endian: builtin.Endian,
- comptime packing: io.Packing,
-) !void {
- const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8);
- var data_mem: [mem_size]u8 = undefined;
-
- var out = io.fixedBufferStream(&data_mem);
- var _serializer = serializer(endian, packing, out.outStream());
-
- var in = io.fixedBufferStream(&data_mem);
- var _deserializer = deserializer(endian, packing, in.reader());
-
- //@TODO: isInf/isNan not currently implemented for f128.
- try _serializer.serialize(std.math.nan(f16));
- try _serializer.serialize(std.math.inf(f16));
- try _serializer.serialize(std.math.nan(f32));
- try _serializer.serialize(std.math.inf(f32));
- try _serializer.serialize(std.math.nan(f64));
- try _serializer.serialize(std.math.inf(f64));
- //try serializer.serialize(std.math.nan(f128));
- //try serializer.serialize(std.math.inf(f128));
- const nan_check_f16 = try _deserializer.deserialize(f16);
- const inf_check_f16 = try _deserializer.deserialize(f16);
- const nan_check_f32 = try _deserializer.deserialize(f32);
- _deserializer.alignToByte();
- const inf_check_f32 = try _deserializer.deserialize(f32);
- const nan_check_f64 = try _deserializer.deserialize(f64);
- const inf_check_f64 = try _deserializer.deserialize(f64);
- //const nan_check_f128 = try deserializer.deserialize(f128);
- //const inf_check_f128 = try deserializer.deserialize(f128);
- testing.expect(std.math.isNan(nan_check_f16));
- testing.expect(std.math.isInf(inf_check_f16));
- testing.expect(std.math.isNan(nan_check_f32));
- testing.expect(std.math.isInf(inf_check_f32));
- testing.expect(std.math.isNan(nan_check_f64));
- testing.expect(std.math.isInf(inf_check_f64));
- //expect(std.math.isNan(nan_check_f128));
- //expect(std.math.isInf(inf_check_f128));
-}
-
-test "Serializer/Deserializer Int: Inf/NaN" {
- try testIntSerializerDeserializerInfNaN(.Big, .Byte);
- try testIntSerializerDeserializerInfNaN(.Little, .Byte);
- try testIntSerializerDeserializerInfNaN(.Big, .Bit);
- try testIntSerializerDeserializerInfNaN(.Little, .Bit);
-}
-
-fn testAlternateSerializer(self: anytype, _serializer: anytype) !void {
- try _serializer.serialize(self.f_f16);
-}
-
-fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
- const ColorType = enum(u4) {
- RGB8 = 1,
- RA16 = 2,
- R32 = 3,
- };
-
- const TagAlign = union(enum(u32)) {
- A: u8,
- B: u8,
- C: u8,
- };
-
- const Color = union(ColorType) {
- RGB8: struct {
- r: u8,
- g: u8,
- b: u8,
- a: u8,
- },
- RA16: struct {
- r: u16,
- a: u16,
- },
- R32: u32,
- };
-
- const PackedStruct = packed struct {
- f_i3: i3,
- f_u2: u2,
- };
-
- //to test custom serialization
- const Custom = struct {
- f_f16: f16,
- f_unused_u32: u32,
-
- pub fn deserialize(self: *@This(), _deserializer: anytype) !void {
- try _deserializer.deserializeInto(&self.f_f16);
- self.f_unused_u32 = 47;
- }
-
- pub const serialize = testAlternateSerializer;
- };
-
- const MyStruct = struct {
- f_i3: i3,
- f_u8: u8,
- f_tag_align: TagAlign,
- f_u24: u24,
- f_i19: i19,
- f_void: void,
- f_f32: f32,
- f_f128: f128,
- f_packed_0: PackedStruct,
- f_i7arr: [10]i7,
- f_of64n: ?f64,
- f_of64v: ?f64,
- f_color_type: ColorType,
- f_packed_1: PackedStruct,
- f_custom: Custom,
- f_color: Color,
- };
-
- const my_inst = MyStruct{
- .f_i3 = -1,
- .f_u8 = 8,
- .f_tag_align = TagAlign{ .B = 148 },
- .f_u24 = 24,
- .f_i19 = 19,
- .f_void = {},
- .f_f32 = 32.32,
- .f_f128 = 128.128,
- .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 },
- .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
- .f_of64n = null,
- .f_of64v = 64.64,
- .f_color_type = ColorType.R32,
- .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 },
- .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 },
- .f_color = Color{ .R32 = 123822 },
- };
-
- var data_mem: [@sizeOf(MyStruct)]u8 = undefined;
- var out = io.fixedBufferStream(&data_mem);
- var _serializer = serializer(endian, packing, out.outStream());
-
- var in = io.fixedBufferStream(&data_mem);
- var _deserializer = deserializer(endian, packing, in.reader());
-
- try _serializer.serialize(my_inst);
-
- const my_copy = try _deserializer.deserialize(MyStruct);
- testing.expect(meta.eql(my_copy, my_inst));
-}
-
-test "Serializer/Deserializer generic" {
- try testSerializerDeserializer(builtin.Endian.Big, .Byte);
- try testSerializerDeserializer(builtin.Endian.Little, .Byte);
- try testSerializerDeserializer(builtin.Endian.Big, .Bit);
- try testSerializerDeserializer(builtin.Endian.Little, .Bit);
-}
-
-fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
- const E = enum(u14) {
- One = 1,
- Two = 2,
- };
-
- const A = struct {
- e: E,
- };
-
- const C = union(E) {
- One: u14,
- Two: f16,
- };
-
- var data_mem: [4]u8 = undefined;
- var out = io.fixedBufferStream(&data_mem);
- var _serializer = serializer(endian, packing, out.outStream());
-
- var in = io.fixedBufferStream(&data_mem);
- var _deserializer = deserializer(endian, packing, in.reader());
-
- try _serializer.serialize(@as(u14, 3));
- testing.expectError(error.InvalidEnumTag, _deserializer.deserialize(A));
- out.pos = 0;
- try _serializer.serialize(@as(u14, 3));
- try _serializer.serialize(@as(u14, 88));
- testing.expectError(error.InvalidEnumTag, _deserializer.deserialize(C));
-}
-
-test "Deserializer bad data" {
- try testBadData(.Big, .Byte);
- try testBadData(.Little, .Byte);
- try testBadData(.Big, .Bit);
- try testBadData(.Little, .Bit);
-}
diff --git a/lib/std/io/stream_source.zig b/lib/std/io/stream_source.zig
index 3bfe0d2e64..df0d6cd352 100644
--- a/lib/std/io/stream_source.zig
+++ b/lib/std/io/stream_source.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -9,7 +9,7 @@ const testing = std.testing;
/// Provides `io.Reader`, `io.Writer`, and `io.SeekableStream` for in-memory buffers as
/// well as files.
-/// For memory sources, if the supplied byte buffer is const, then `io.OutStream` is not available.
+/// For memory sources, if the supplied byte buffer is const, then `io.Writer` is not available.
/// The error set of the stream functions is the error set of the corresponding file functions.
pub const StreamSource = union(enum) {
buffer: io.FixedBufferStream([]u8),
@@ -22,11 +22,7 @@ pub const StreamSource = union(enum) {
pub const GetSeekPosError = std.fs.File.GetPosError;
pub const Reader = io.Reader(*StreamSource, ReadError, read);
- /// Deprecated: use `Reader`
- pub const InStream = Reader;
pub const Writer = io.Writer(*StreamSource, WriteError, write);
- /// Deprecated: use `Writer`
- pub const OutStream = Writer;
pub const SeekableStream = io.SeekableStream(
*StreamSource,
SeekError,
@@ -89,20 +85,10 @@ pub const StreamSource = union(enum) {
return .{ .context = self };
}
- /// Deprecated: use `reader`
- pub fn inStream(self: *StreamSource) InStream {
- return .{ .context = self };
- }
-
pub fn writer(self: *StreamSource) Writer {
return .{ .context = self };
}
- /// Deprecated: use `writer`
- pub fn outStream(self: *StreamSource) OutStream {
- return .{ .context = self };
- }
-
pub fn seekableStream(self: *StreamSource) SeekableStream {
return .{ .context = self };
}
diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig
index 584369966b..9fdef0de1d 100644
--- a/lib/std/io/test.zig
+++ b/lib/std/io/test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -30,8 +30,8 @@ test "write a file, read it, then delete it" {
var file = try tmp.dir.createFile(tmp_file_name, .{});
defer file.close();
- var buf_stream = io.bufferedOutStream(file.outStream());
- const st = buf_stream.outStream();
+ var buf_stream = io.bufferedWriter(file.writer());
+ const st = buf_stream.writer();
try st.print("begin", .{});
try st.writeAll(data[0..]);
try st.print("end", .{});
@@ -72,7 +72,7 @@ test "BitStreams with File Stream" {
var file = try tmp.dir.createFile(tmp_file_name, .{});
defer file.close();
- var bit_stream = io.bitOutStream(builtin.endian, file.outStream());
+ var bit_stream = io.bitWriter(builtin.endian, file.writer());
try bit_stream.writeBits(@as(u2, 1), 1);
try bit_stream.writeBits(@as(u5, 2), 2);
diff --git a/lib/std/io/writer.zig b/lib/std/io/writer.zig
index 770cd5f0fa..0a9edb425a 100644
--- a/lib/std/io/writer.zig
+++ b/lib/std/io/writer.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/json.zig b/lib/std/json.zig
index ac1047d1a9..f9fc371049 100644
--- a/lib/std/json.zig
+++ b/lib/std/json.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -246,7 +246,7 @@ pub const StreamingParser = struct {
// Only call this function to generate array/object final state.
pub fn fromInt(x: anytype) State {
debug.assert(x == 0 or x == 1);
- const T = @TagType(State);
+ const T = std.meta.Tag(State);
return @intToEnum(State, @intCast(T, x));
}
};
@@ -1138,7 +1138,7 @@ pub const TokenStream = struct {
}
};
-fn checkNext(p: *TokenStream, id: std.meta.TagType(Token)) void {
+fn checkNext(p: *TokenStream, id: std.meta.Tag(Token)) void {
const token = (p.next() catch unreachable).?;
debug.assert(std.meta.activeTag(token) == id);
}
@@ -1255,6 +1255,7 @@ pub const Value = union(enum) {
Bool: bool,
Integer: i64,
Float: f64,
+ NumberString: []const u8,
String: []const u8,
Array: Array,
Object: ObjectMap,
@@ -1269,6 +1270,7 @@ pub const Value = union(enum) {
.Bool => |inner| try stringify(inner, options, out_stream),
.Integer => |inner| try stringify(inner, options, out_stream),
.Float => |inner| try stringify(inner, options, out_stream),
+ .NumberString => |inner| try out_stream.writeAll(inner),
.String => |inner| try stringify(inner, options, out_stream),
.Array => |inner| try stringify(inner.items, options, out_stream),
.Object => |inner| {
@@ -1323,31 +1325,37 @@ test "Value.jsonStringify" {
{
var buffer: [10]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buffer);
- try @as(Value, .Null).jsonStringify(.{}, fbs.outStream());
+ try @as(Value, .Null).jsonStringify(.{}, fbs.writer());
testing.expectEqualSlices(u8, fbs.getWritten(), "null");
}
{
var buffer: [10]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buffer);
- try (Value{ .Bool = true }).jsonStringify(.{}, fbs.outStream());
+ try (Value{ .Bool = true }).jsonStringify(.{}, fbs.writer());
testing.expectEqualSlices(u8, fbs.getWritten(), "true");
}
{
var buffer: [10]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buffer);
- try (Value{ .Integer = 42 }).jsonStringify(.{}, fbs.outStream());
+ try (Value{ .Integer = 42 }).jsonStringify(.{}, fbs.writer());
testing.expectEqualSlices(u8, fbs.getWritten(), "42");
}
{
var buffer: [10]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buffer);
- try (Value{ .Float = 42 }).jsonStringify(.{}, fbs.outStream());
+ try (Value{ .NumberString = "43" }).jsonStringify(.{}, fbs.writer());
+ testing.expectEqualSlices(u8, fbs.getWritten(), "43");
+ }
+ {
+ var buffer: [10]u8 = undefined;
+ var fbs = std.io.fixedBufferStream(&buffer);
+ try (Value{ .Float = 42 }).jsonStringify(.{}, fbs.writer());
testing.expectEqualSlices(u8, fbs.getWritten(), "4.2e+01");
}
{
var buffer: [10]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buffer);
- try (Value{ .String = "weeee" }).jsonStringify(.{}, fbs.outStream());
+ try (Value{ .String = "weeee" }).jsonStringify(.{}, fbs.writer());
testing.expectEqualSlices(u8, fbs.getWritten(), "\"weeee\"");
}
{
@@ -1356,11 +1364,11 @@ test "Value.jsonStringify" {
var vals = [_]Value{
.{ .Integer = 1 },
.{ .Integer = 2 },
- .{ .Integer = 3 },
+ .{ .NumberString = "3" },
};
try (Value{
.Array = Array.fromOwnedSlice(undefined, &vals),
- }).jsonStringify(.{}, fbs.outStream());
+ }).jsonStringify(.{}, fbs.writer());
testing.expectEqualSlices(u8, fbs.getWritten(), "[1,2,3]");
}
{
@@ -1369,11 +1377,70 @@ test "Value.jsonStringify" {
var obj = ObjectMap.init(testing.allocator);
defer obj.deinit();
try obj.putNoClobber("a", .{ .String = "b" });
- try (Value{ .Object = obj }).jsonStringify(.{}, fbs.outStream());
+ try (Value{ .Object = obj }).jsonStringify(.{}, fbs.writer());
testing.expectEqualSlices(u8, fbs.getWritten(), "{\"a\":\"b\"}");
}
}
+/// parse tokens from a stream, returning `false` if they do not decode to `value`
+fn parsesTo(comptime T: type, value: T, tokens: *TokenStream, options: ParseOptions) !bool {
+ // TODO: should be able to write this function to not require an allocator
+ const tmp = try parse(T, tokens, options);
+ defer parseFree(T, tmp, options);
+
+ return parsedEqual(tmp, value);
+}
+
+/// Returns if a value returned by `parse` is deep-equal to another value
+fn parsedEqual(a: anytype, b: @TypeOf(a)) bool {
+ switch (@typeInfo(@TypeOf(a))) {
+ .Optional => {
+ if (a == null and b == null) return true;
+ if (a == null or b == null) return false;
+ return parsedEqual(a.?, b.?);
+ },
+ .Union => |unionInfo| {
+ if (info.tag_type) |UnionTag| {
+ const tag_a = std.meta.activeTag(a);
+ const tag_b = std.meta.activeTag(b);
+ if (tag_a != tag_b) return false;
+
+ inline for (info.fields) |field_info| {
+ if (@field(UnionTag, field_info.name) == tag_a) {
+ return parsedEqual(@field(a, field_info.name), @field(b, field_info.name));
+ }
+ }
+ return false;
+ } else {
+ unreachable;
+ }
+ },
+ .Array => {
+ for (a) |e, i|
+ if (!parsedEqual(e, b[i])) return false;
+ return true;
+ },
+ .Struct => |info| {
+ inline for (info.fields) |field_info| {
+ if (!parsedEqual(@field(a, field_info.name), @field(b, field_info.name))) return false;
+ }
+ return true;
+ },
+ .Pointer => |ptrInfo| switch (ptrInfo.size) {
+ .One => return parsedEqual(a.*, b.*),
+ .Slice => {
+ if (a.len != b.len) return false;
+ for (a) |e, i|
+ if (!parsedEqual(e, b[i])) return false;
+ return true;
+ },
+ .Many, .C => unreachable,
+ },
+ else => return a == b,
+ }
+ unreachable;
+}
+
pub const ParseOptions = struct {
allocator: ?*Allocator = null,
@@ -1454,6 +1521,8 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
// Parsing some types won't have OutOfMemory in their
// error-sets, for the condition to be valid, merge it in.
if (@as(@TypeOf(err) || error{OutOfMemory}, err) == error.OutOfMemory) return err;
+ // Bubble up AllocatorRequired, as it indicates missing option
+ if (@as(@TypeOf(err) || error{AllocatorRequired}, err) == error.AllocatorRequired) return err;
// otherwise continue through the `inline for`
}
}
@@ -1471,7 +1540,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
var fields_seen = [_]bool{false} ** structInfo.fields.len;
errdefer {
inline for (structInfo.fields) |field, i| {
- if (fields_seen[i]) {
+ if (fields_seen[i] and !field.is_comptime) {
parseFree(field.field_type, @field(r, field.name), options);
}
}
@@ -1504,7 +1573,13 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
parseFree(field.field_type, @field(r, field.name), options);
}
}
- @field(r, field.name) = try parse(field.field_type, tokens, options);
+ if (field.is_comptime) {
+ if (!try parsesTo(field.field_type, field.default_value.?, tokens, options)) {
+ return error.UnexpectedValue;
+ }
+ } else {
+ @field(r, field.name) = try parse(field.field_type, tokens, options);
+ }
fields_seen[i] = true;
found = true;
break;
@@ -1518,7 +1593,9 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
inline for (structInfo.fields) |field, i| {
if (!fields_seen[i]) {
if (field.default_value) |default| {
- @field(r, field.name) = default;
+ if (!field.is_comptime) {
+ @field(r, field.name) = default;
+ }
} else {
return error.MissingField;
}
@@ -1553,7 +1630,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);
switch (stringToken.escapes) {
.None => mem.copy(u8, &r, source_slice),
- .Some => try unescapeString(&r, source_slice),
+ .Some => try unescapeValidString(&r, source_slice),
}
return r;
},
@@ -1600,7 +1677,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
.Some => |some_escapes| {
const output = try allocator.alloc(u8, stringToken.decodedLength());
errdefer allocator.free(output);
- try unescapeString(output, source_slice);
+ try unescapeValidString(output, source_slice);
return output;
},
}
@@ -1731,18 +1808,6 @@ test "parse into tagged union" {
testing.expectEqual(T{ .float = 1.5 }, try parse(T, &TokenStream.init("1.5"), ParseOptions{}));
}
- { // if union matches string member, fails with NoUnionMembersMatched rather than AllocatorRequired
- // Note that this behaviour wasn't necessarily by design, but was
- // what fell out of the implementation and may result in interesting
- // API breakage if changed
- const T = union(enum) {
- int: i32,
- float: f64,
- string: []const u8,
- };
- testing.expectError(error.NoUnionMembersMatched, parse(T, &TokenStream.init("\"foo\""), ParseOptions{}));
- }
-
{ // failing allocations should be bubbled up instantly without trying next member
var fail_alloc = testing.FailingAllocator.init(testing.allocator, 0);
const options = ParseOptions{ .allocator = &fail_alloc.allocator };
@@ -1772,6 +1837,25 @@ test "parse into tagged union" {
}
}
+test "parse union bubbles up AllocatorRequired" {
+ { // string member first in union (and not matching)
+ const T = union(enum) {
+ string: []const u8,
+ int: i32,
+ };
+ testing.expectError(error.AllocatorRequired, parse(T, &TokenStream.init("42"), ParseOptions{}));
+ }
+
+ { // string member not first in union (and matching)
+ const T = union(enum) {
+ int: i32,
+ float: f64,
+ string: []const u8,
+ };
+ testing.expectError(error.AllocatorRequired, parse(T, &TokenStream.init("\"foo\""), ParseOptions{}));
+ }
+}
+
test "parseFree descends into tagged union" {
var fail_alloc = testing.FailingAllocator.init(testing.allocator, 1);
const options = ParseOptions{ .allocator = &fail_alloc.allocator };
@@ -1782,13 +1866,50 @@ test "parseFree descends into tagged union" {
};
// use a string with unicode escape so we know result can't be a reference to global constant
const r = try parse(T, &TokenStream.init("\"with\\u0105unicode\""), options);
- testing.expectEqual(@TagType(T).string, @as(@TagType(T), r));
+ testing.expectEqual(std.meta.Tag(T).string, @as(std.meta.Tag(T), r));
testing.expectEqualSlices(u8, "withąunicode", r.string);
testing.expectEqual(@as(usize, 0), fail_alloc.deallocations);
parseFree(T, r, options);
testing.expectEqual(@as(usize, 1), fail_alloc.deallocations);
}
+test "parse with comptime field" {
+ {
+ const T = struct {
+ comptime a: i32 = 0,
+ b: bool,
+ };
+ testing.expectEqual(T{ .a = 0, .b = true }, try parse(T, &TokenStream.init(
+ \\{
+ \\ "a": 0,
+ \\ "b": true
+ \\}
+ ), ParseOptions{}));
+ }
+
+ { // string comptime values currently require an allocator
+ const T = union(enum) {
+ foo: struct {
+ comptime kind: []const u8 = "boolean",
+ b: bool,
+ },
+ bar: struct {
+ comptime kind: []const u8 = "float",
+ b: f64,
+ },
+ };
+
+ const r = try std.json.parse(T, &std.json.TokenStream.init(
+ \\{
+ \\ "kind": "float",
+ \\ "b": 1.0
+ \\}
+ ), .{
+ .allocator = std.testing.allocator,
+ });
+ }
+}
+
test "parse into struct with no fields" {
const T = struct {};
testing.expectEqual(T{}, try parse(T, &TokenStream.init("{}"), ParseOptions{}));
@@ -1897,7 +2018,7 @@ pub const Parser = struct {
pub fn reset(p: *Parser) void {
p.state = .Simple;
- p.stack.shrink(0);
+ p.stack.shrinkRetainingCapacity(0);
}
pub fn parse(p: *Parser, input: []const u8) !ValueTree {
@@ -2077,31 +2198,36 @@ pub const Parser = struct {
}
}
- fn parseString(p: *Parser, allocator: *Allocator, s: std.meta.TagPayloadType(Token, Token.String), input: []const u8, i: usize) !Value {
+ fn parseString(p: *Parser, allocator: *Allocator, s: std.meta.TagPayload(Token, Token.String), input: []const u8, i: usize) !Value {
const slice = s.slice(input, i);
switch (s.escapes) {
.None => return Value{ .String = if (p.copy_strings) try allocator.dupe(u8, slice) else slice },
.Some => |some_escapes| {
const output = try allocator.alloc(u8, s.decodedLength());
errdefer allocator.free(output);
- try unescapeString(output, slice);
+ try unescapeValidString(output, slice);
return Value{ .String = output };
},
}
}
- fn parseNumber(p: *Parser, n: std.meta.TagPayloadType(Token, Token.Number), input: []const u8, i: usize) !Value {
+ fn parseNumber(p: *Parser, n: std.meta.TagPayload(Token, Token.Number), input: []const u8, i: usize) !Value {
return if (n.is_integer)
- Value{ .Integer = try std.fmt.parseInt(i64, n.slice(input, i), 10) }
+ Value{
+ .Integer = std.fmt.parseInt(i64, n.slice(input, i), 10) catch |e| switch (e) {
+ error.Overflow => return Value{ .NumberString = n.slice(input, i) },
+ error.InvalidCharacter => |err| return err,
+ },
+ }
else
Value{ .Float = try std.fmt.parseFloat(f64, n.slice(input, i)) };
}
};
-// Unescape a JSON string
-// Only to be used on strings already validated by the parser
-// (note the unreachable statements and lack of bounds checking)
-pub fn unescapeString(output: []u8, input: []const u8) !void {
+/// Unescape a JSON string
+/// Only to be used on strings already validated by the parser
+/// (note the unreachable statements and lack of bounds checking)
+pub fn unescapeValidString(output: []u8, input: []const u8) !void {
var inIndex: usize = 0;
var outIndex: usize = 0;
@@ -2180,7 +2306,8 @@ test "json.parser.dynamic" {
\\ "Animated" : false,
\\ "IDs": [116, 943, 234, 38793],
\\ "ArrayOfObject": [{"n": "m"}],
- \\ "double": 1.3412
+ \\ "double": 1.3412,
+ \\ "LargeInt": 18446744073709551615
\\ }
\\}
;
@@ -2212,6 +2339,9 @@ test "json.parser.dynamic" {
const double = image.Object.get("double").?;
testing.expect(double.Float == 1.3412);
+
+ const large_int = image.Object.get("LargeInt").?;
+ testing.expect(mem.eql(u8, large_int.NumberString, "18446744073709551615"));
}
test "import more json tests" {
@@ -2223,7 +2353,7 @@ test "write json then parse it" {
var out_buffer: [1000]u8 = undefined;
var fixed_buffer_stream = std.io.fixedBufferStream(&out_buffer);
- const out_stream = fixed_buffer_stream.outStream();
+ const out_stream = fixed_buffer_stream.writer();
var jw = writeStream(out_stream, 4);
try jw.beginObject();
@@ -2620,9 +2750,9 @@ pub fn stringify(
}
fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions) !void {
- const ValidationOutStream = struct {
+ const ValidationWriter = struct {
const Self = @This();
- pub const OutStream = std.io.OutStream(*Self, Error, write);
+ pub const Writer = std.io.Writer(*Self, Error, write);
pub const Error = error{
TooMuchData,
DifferentData,
@@ -2634,7 +2764,7 @@ fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions
return .{ .expected_remaining = exp };
}
- pub fn outStream(self: *Self) OutStream {
+ pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
@@ -2642,9 +2772,9 @@ fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions
if (self.expected_remaining.len < bytes.len) {
std.debug.warn(
\\====== expected this output: =========
- \\{}
+ \\{s}
\\======== instead found this: =========
- \\{}
+ \\{s}
\\======================================
, .{
self.expected_remaining,
@@ -2655,9 +2785,9 @@ fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions
if (!mem.eql(u8, self.expected_remaining[0..bytes.len], bytes)) {
std.debug.warn(
\\====== expected this output: =========
- \\{}
+ \\{s}
\\======== instead found this: =========
- \\{}
+ \\{s}
\\======================================
, .{
self.expected_remaining[0..bytes.len],
@@ -2670,8 +2800,8 @@ fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions
}
};
- var vos = ValidationOutStream.init(expected);
- try stringify(value, options, vos.outStream());
+ var vos = ValidationWriter.init(expected);
+ try stringify(value, options, vos.writer());
if (vos.expected_remaining.len > 0) return error.NotEnoughData;
}
diff --git a/lib/std/json/test.zig b/lib/std/json/test.zig
index f1f351e84f..897e2e3364 100644
--- a/lib/std/json/test.zig
+++ b/lib/std/json/test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/json/write_stream.zig b/lib/std/json/write_stream.zig
index ee2c12154f..b4a8aed84c 100644
--- a/lib/std/json/write_stream.zig
+++ b/lib/std/json/write_stream.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -238,7 +238,7 @@ pub fn writeStream(
test "json write stream" {
var out_buf: [1024]u8 = undefined;
var slice_stream = std.io.fixedBufferStream(&out_buf);
- const out = slice_stream.outStream();
+ const out = slice_stream.writer();
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_allocator.deinit();
diff --git a/lib/std/leb128.zig b/lib/std/leb128.zig
index 2a8a6fc2ff..90a329545f 100644
--- a/lib/std/leb128.zig
+++ b/lib/std/leb128.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/linked_list.zig b/lib/std/linked_list.zig
index 870b823aac..2a6b58c8c9 100644
--- a/lib/std/linked_list.zig
+++ b/lib/std/linked_list.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -62,7 +62,7 @@ pub fn SinglyLinkedList(comptime T: type) type {
/// This operation is O(N).
pub fn countChildren(node: *const Node) usize {
var count: usize = 0;
- var it: ?*const Node = node;
+ var it: ?*const Node = node.next;
while (it) |n| : (it = n.next) {
count += 1;
}
@@ -123,6 +123,8 @@ test "basic SinglyLinkedList test" {
const L = SinglyLinkedList(u32);
var list = L{};
+ testing.expect(list.len() == 0);
+
var one = L.Node{ .data = 1 };
var two = L.Node{ .data = 2 };
var three = L.Node{ .data = 3 };
@@ -135,6 +137,8 @@ test "basic SinglyLinkedList test" {
two.insertAfter(&three); // {1, 2, 3, 5}
three.insertAfter(&four); // {1, 2, 3, 4, 5}
+ testing.expect(list.len() == 5);
+
// Traverse forwards.
{
var it = list.first;
diff --git a/lib/std/log.zig b/lib/std/log.zig
index 0cc2b54452..215e611bc1 100644
--- a/lib/std/log.zig
+++ b/lib/std/log.zig
@@ -1,11 +1,8 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
-const std = @import("std.zig");
-const builtin = std.builtin;
-const root = @import("root");
//! std.log is a standardized interface for logging which allows for the logging
//! of programs and libraries using this interface to be formatted and filtered
@@ -77,6 +74,10 @@ const root = @import("root");
//! [err] (nice_library): Something went very wrong, sorry
//! ```
+const std = @import("std.zig");
+const builtin = std.builtin;
+const root = @import("root");
+
pub const Level = enum {
/// Emergency: a condition that cannot be handled, usually followed by a
/// panic.
diff --git a/lib/std/macho.zig b/lib/std/macho.zig
index ec0d23cd92..6785abffca 100644
--- a/lib/std/macho.zig
+++ b/lib/std/macho.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -1257,6 +1257,51 @@ pub const VM_PROT_WRITE: vm_prot_t = 0x2;
/// VM execute permission
pub const VM_PROT_EXECUTE: vm_prot_t = 0x4;
+// The following are used to encode rebasing information
+pub const REBASE_TYPE_POINTER: u8 = 1;
+pub const REBASE_TYPE_TEXT_ABSOLUTE32: u8 = 2;
+pub const REBASE_TYPE_TEXT_PCREL32: u8 = 3;
+
+pub const REBASE_OPCODE_MASK: u8 = 0xF0;
+pub const REBASE_IMMEDIATE_MASK: u8 = 0x0F;
+pub const REBASE_OPCODE_DONE: u8 = 0x00;
+pub const REBASE_OPCODE_SET_TYPE_IMM: u8 = 0x10;
+pub const REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x20;
+pub const REBASE_OPCODE_ADD_ADDR_ULEB: u8 = 0x30;
+pub const REBASE_OPCODE_ADD_ADDR_IMM_SCALED: u8 = 0x40;
+pub const REBASE_OPCODE_DO_REBASE_IMM_TIMES: u8 = 0x50;
+pub const REBASE_OPCODE_DO_REBASE_ULEB_TIMES: u8 = 0x60;
+pub const REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: u8 = 0x70;
+pub const REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: u8 = 0x80;
+
+// The following are used to encode binding information
+pub const BIND_TYPE_POINTER: u8 = 1;
+pub const BIND_TYPE_TEXT_ABSOLUTE32: u8 = 2;
+pub const BIND_TYPE_TEXT_PCREL32: u8 = 3;
+
+pub const BIND_SPECIAL_DYLIB_SELF: i8 = 0;
+pub const BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: i8 = -1;
+pub const BIND_SPECIAL_DYLIB_FLAT_LOOKUP: i8 = -2;
+
+pub const BIND_SYMBOL_FLAGS_WEAK_IMPORT: u8 = 0x1;
+pub const BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION: u8 = 0x8;
+
+pub const BIND_OPCODE_MASK: u8 = 0xf0;
+pub const BIND_IMMEDIATE_MASK: u8 = 0x0f;
+pub const BIND_OPCODE_DONE: u8 = 0x00;
+pub const BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: u8 = 0x10;
+pub const BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: u8 = 0x20;
+pub const BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: u8 = 0x30;
+pub const BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: u8 = 0x40;
+pub const BIND_OPCODE_SET_TYPE_IMM: u8 = 0x50;
+pub const BIND_OPCODE_SET_ADDEND_SLEB: u8 = 0x60;
+pub const BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x70;
+pub const BIND_OPCODE_ADD_ADDR_ULEB: 0x80;
+pub const BIND_OPCODE_DO_BIND: u8 = 0x90;
+pub const BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: u8 = 0xa0;
+pub const BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: u8 = 0xb0;
+pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = xc0;
+
pub const reloc_type_x86_64 = packed enum(u4) {
/// for absolute addresses
X86_64_RELOC_UNSIGNED = 0,
@@ -1289,6 +1334,41 @@ pub const reloc_type_x86_64 = packed enum(u4) {
X86_64_RELOC_TLV,
};
+pub const reloc_type_arm64 = packed enum(u4) {
+ /// For pointers.
+ ARM64_RELOC_UNSIGNED = 0,
+
+ /// Must be followed by a ARM64_RELOC_UNSIGNED.
+ ARM64_RELOC_SUBTRACTOR,
+
+ /// A B/BL instruction with 26-bit displacement.
+ ARM64_RELOC_BRANCH26,
+
+ /// Pc-rel distance to page of target.
+ ARM64_RELOC_PAGE21,
+
+ /// Offset within page, scaled by r_length.
+ ARM64_RELOC_PAGEOFF12,
+
+ /// Pc-rel distance to page of GOT slot.
+ ARM64_RELOC_GOT_LOAD_PAGE21,
+
+ /// Offset within page of GOT slot, scaled by r_length.
+ ARM64_RELOC_GOT_LOAD_PAGEOFF12,
+
+ /// For pointers to GOT slots.
+ ARM64_RELOC_POINTER_TO_GOT,
+
+ /// Pc-rel distance to page of TLVP slot.
+ ARM64_RELOC_TLVP_LOAD_PAGE21,
+
+ /// Offset within page of TLVP slot, scaled by r_length.
+ ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
+
+ /// Must be followed by PAGE21 or PAGEOFF12.
+ ARM64_RELOC_ADDEND,
+};
+
/// This symbol is a reference to an external non-lazy (data) symbol.
pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0x0;
@@ -1333,7 +1413,7 @@ pub const N_WEAK_DEF: u16 = 0x80;
/// This bit is only available in .o files (MH_OBJECT filetype)
pub const N_SYMBOL_RESOLVER: u16 = 0x100;
-// The following are used on the flags byte of a terminal node // in the export information.
+// The following are used on the flags byte of a terminal node in the export information.
pub const EXPORT_SYMBOL_FLAGS_KIND_MASK: u8 = 0x03;
pub const EXPORT_SYMBOL_FLAGS_KIND_REGULAR: u8 = 0x00;
pub const EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: u8 = 0x01;
diff --git a/lib/std/math.zig b/lib/std/math.zig
index a51cac6e7d..6e7c5c0915 100644
--- a/lib/std/math.zig
+++ b/lib/std/math.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -278,7 +278,7 @@ pub const Complex = complex.Complex;
pub const big = @import("math/big.zig");
-test "" {
+test {
std.testing.refAllDecls(@This());
}
@@ -415,6 +415,7 @@ pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) {
}
pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
+ if (T == comptime_int) return a + b;
var answer: T = undefined;
return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
}
@@ -1070,16 +1071,52 @@ test "std.math.log2_int_ceil" {
testing.expect(log2_int_ceil(u32, 10) == 4);
}
+///Cast a value to a different type. If the value doesn't fit in, or can't be perfectly represented by,
+///the new type, it will be converted to the closest possible representation.
pub fn lossyCast(comptime T: type, value: anytype) T {
- switch (@typeInfo(@TypeOf(value))) {
- .Int => return @intToFloat(T, value),
- .Float => return @floatCast(T, value),
- .ComptimeInt => return @as(T, value),
- .ComptimeFloat => return @as(T, value),
- else => @compileError("bad type"),
+ switch (@typeInfo(T)) {
+ .Float => {
+ switch (@typeInfo(@TypeOf(value))) {
+ .Int => return @intToFloat(T, value),
+ .Float => return @floatCast(T, value),
+ .ComptimeInt => return @as(T, value),
+ .ComptimeFloat => return @as(T, value),
+ else => @compileError("bad type"),
+ }
+ },
+ .Int => {
+ switch (@typeInfo(@TypeOf(value))) {
+ .Int, .ComptimeInt => {
+ if (value > maxInt(T)) {
+ return @as(T, maxInt(T));
+ } else if (value < minInt(T)) {
+ return @as(T, minInt(T));
+ } else {
+ return @intCast(T, value);
+ }
+ },
+ .Float, .ComptimeFloat => {
+ if (value > maxInt(T)) {
+ return @as(T, maxInt(T));
+ } else if (value < minInt(T)) {
+ return @as(T, minInt(T));
+ } else {
+ return @floatToInt(T, value);
+ }
+ },
+ else => @compileError("bad type"),
+ }
+ },
+ else => @compileError("bad result type"),
}
}
+test "math.lossyCast" {
+ testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767));
+ testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0));
+ testing.expect(lossyCast(i9, @as(u32, 200)) == @as(i9, 200));
+}
+
test "math.f64_min" {
const f64_min_u64 = 0x0010000000000000;
const fmin: f64 = f64_min;
diff --git a/lib/std/math/acos.zig b/lib/std/math/acos.zig
index 0153fd6835..7f3d4bfe9b 100644
--- a/lib/std/math/acos.zig
+++ b/lib/std/math/acos.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/acosh.zig b/lib/std/math/acosh.zig
index 773b125bea..0993989d47 100644
--- a/lib/std/math/acosh.zig
+++ b/lib/std/math/acosh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/asin.zig b/lib/std/math/asin.zig
index 38602a76d2..c4fca95c10 100644
--- a/lib/std/math/asin.zig
+++ b/lib/std/math/asin.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/asinh.zig b/lib/std/math/asinh.zig
index 4dc0702a0a..a2c8ee3583 100644
--- a/lib/std/math/asinh.zig
+++ b/lib/std/math/asinh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/atan.zig b/lib/std/math/atan.zig
index 1608328579..59dda307cc 100644
--- a/lib/std/math/atan.zig
+++ b/lib/std/math/atan.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/atan2.zig b/lib/std/math/atan2.zig
index cb4c28e713..3ecabe9e31 100644
--- a/lib/std/math/atan2.zig
+++ b/lib/std/math/atan2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/atanh.zig b/lib/std/math/atanh.zig
index ffebc58ed4..87d92a9fa5 100644
--- a/lib/std/math/atanh.zig
+++ b/lib/std/math/atanh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/big.zig b/lib/std/math/big.zig
index 5e2073954c..8ae214c666 100644
--- a/lib/std/math/big.zig
+++ b/lib/std/math/big.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -20,7 +20,7 @@ comptime {
assert(limb_info.signedness == .unsigned);
}
-test "" {
+test {
_ = int;
_ = Rational;
_ = Limb;
diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig
index dd0b925692..d1d7b33508 100644
--- a/lib/std/math/big/int.zig
+++ b/lib/std/math/big/int.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -549,8 +549,8 @@ pub const Mutable = struct {
return;
}
- const r_len = llshr(r.limbs[0..], a.limbs[0..a.limbs.len], shift);
- r.len = a.limbs.len - (shift / limb_bits);
+ llshr(r.limbs[0..], a.limbs[0..a.limbs.len], shift);
+ r.normalize(a.limbs.len - (shift / limb_bits));
r.positive = a.positive;
}
@@ -607,7 +607,7 @@ pub const Mutable = struct {
/// it will have the same length as it had when the function was called.
pub fn gcd(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void {
const prev_len = limbs_buffer.items.len;
- defer limbs_buffer.shrink(prev_len);
+ defer limbs_buffer.shrinkRetainingCapacity(prev_len);
const x_copy = if (rma.limbs.ptr == x.limbs.ptr) blk: {
const start = limbs_buffer.items.len;
try limbs_buffer.appendSlice(x.limbs);
@@ -1348,7 +1348,9 @@ pub const Const = struct {
/// Returns true if `a == 0`.
pub fn eqZero(a: Const) bool {
- return a.limbs.len == 1 and a.limbs[0] == 0;
+ var d: Limb = 0;
+ for (a.limbs) |limb| d |= limb;
+ return d == 0;
}
/// Returns true if `|a| == |b|`.
@@ -2344,6 +2346,6 @@ fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable {
};
}
-test "" {
+test {
_ = @import("int_test.zig");
}
diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig
index 1f4bd65974..179e55ff69 100644
--- a/lib/std/math/big/int_test.zig
+++ b/lib/std/math/big/int_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -1287,6 +1287,12 @@ test "big.int shift-right multi" {
try a.shiftRight(a, 67);
testing.expect((try a.to(u64)) == 0x1fffe0001dddc222);
+
+ try a.set(0xffff0000eeee1111dddd2222cccc3333);
+ try a.shiftRight(a, 63);
+ try a.shiftRight(a, 63);
+ try a.shiftRight(a, 2);
+ testing.expect(a.eqZero());
}
test "big.int shift-left single" {
diff --git a/lib/std/math/big/rational.zig b/lib/std/math/big/rational.zig
index 8eb1d9f2b3..2299205e7b 100644
--- a/lib/std/math/big/rational.zig
+++ b/lib/std/math/big/rational.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/cbrt.zig b/lib/std/math/cbrt.zig
index c516cae73b..a876e0a9d1 100644
--- a/lib/std/math/cbrt.zig
+++ b/lib/std/math/cbrt.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/ceil.zig b/lib/std/math/ceil.zig
index 2f043300b1..d313475717 100644
--- a/lib/std/math/ceil.zig
+++ b/lib/std/math/ceil.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex.zig b/lib/std/math/complex.zig
index f9f13a1161..e046ed9fa9 100644
--- a/lib/std/math/complex.zig
+++ b/lib/std/math/complex.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/abs.zig b/lib/std/math/complex/abs.zig
index 228b56c286..609cdba5a7 100644
--- a/lib/std/math/complex/abs.zig
+++ b/lib/std/math/complex/abs.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/acos.zig b/lib/std/math/complex/acos.zig
index 47130c8a98..b7c43e9381 100644
--- a/lib/std/math/complex/acos.zig
+++ b/lib/std/math/complex/acos.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/acosh.zig b/lib/std/math/complex/acosh.zig
index 51626b10a4..d7d596e084 100644
--- a/lib/std/math/complex/acosh.zig
+++ b/lib/std/math/complex/acosh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/arg.zig b/lib/std/math/complex/arg.zig
index 43c1d93874..7c3b00bd5d 100644
--- a/lib/std/math/complex/arg.zig
+++ b/lib/std/math/complex/arg.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/asin.zig b/lib/std/math/complex/asin.zig
index 4911ccc2b2..0ed352b3b7 100644
--- a/lib/std/math/complex/asin.zig
+++ b/lib/std/math/complex/asin.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/asinh.zig b/lib/std/math/complex/asinh.zig
index e93a2dabd7..762a601fbf 100644
--- a/lib/std/math/complex/asinh.zig
+++ b/lib/std/math/complex/asinh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/atan.zig b/lib/std/math/complex/atan.zig
index e838751d73..af40c05a81 100644
--- a/lib/std/math/complex/atan.zig
+++ b/lib/std/math/complex/atan.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/atanh.zig b/lib/std/math/complex/atanh.zig
index f3d378315f..2c3708f57f 100644
--- a/lib/std/math/complex/atanh.zig
+++ b/lib/std/math/complex/atanh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/conj.zig b/lib/std/math/complex/conj.zig
index 159469da86..b79c7de6ca 100644
--- a/lib/std/math/complex/conj.zig
+++ b/lib/std/math/complex/conj.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/cos.zig b/lib/std/math/complex/cos.zig
index 2abfce58c6..66fd5b9b7b 100644
--- a/lib/std/math/complex/cos.zig
+++ b/lib/std/math/complex/cos.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/cosh.zig b/lib/std/math/complex/cosh.zig
index 0a6be49e3e..e43cd1d665 100644
--- a/lib/std/math/complex/cosh.zig
+++ b/lib/std/math/complex/cosh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/exp.zig b/lib/std/math/complex/exp.zig
index 156a947a32..eb738a6d88 100644
--- a/lib/std/math/complex/exp.zig
+++ b/lib/std/math/complex/exp.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/ldexp.zig b/lib/std/math/complex/ldexp.zig
index b1cf8a0e42..3ae0382fe3 100644
--- a/lib/std/math/complex/ldexp.zig
+++ b/lib/std/math/complex/ldexp.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/log.zig b/lib/std/math/complex/log.zig
index 88175d00cc..90124af2eb 100644
--- a/lib/std/math/complex/log.zig
+++ b/lib/std/math/complex/log.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/pow.zig b/lib/std/math/complex/pow.zig
index 30636dd10d..a6589262cd 100644
--- a/lib/std/math/complex/pow.zig
+++ b/lib/std/math/complex/pow.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/proj.zig b/lib/std/math/complex/proj.zig
index 67f087f8ba..42886d8263 100644
--- a/lib/std/math/complex/proj.zig
+++ b/lib/std/math/complex/proj.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/sin.zig b/lib/std/math/complex/sin.zig
index d5e2713b13..4288dbb1a1 100644
--- a/lib/std/math/complex/sin.zig
+++ b/lib/std/math/complex/sin.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/sinh.zig b/lib/std/math/complex/sinh.zig
index 8c8930c0ba..2861d99f9a 100644
--- a/lib/std/math/complex/sinh.zig
+++ b/lib/std/math/complex/sinh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/sqrt.zig b/lib/std/math/complex/sqrt.zig
index a01473a5ea..e03ed221eb 100644
--- a/lib/std/math/complex/sqrt.zig
+++ b/lib/std/math/complex/sqrt.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/tan.zig b/lib/std/math/complex/tan.zig
index 8d6e5da313..04d900bd99 100644
--- a/lib/std/math/complex/tan.zig
+++ b/lib/std/math/complex/tan.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/complex/tanh.zig b/lib/std/math/complex/tanh.zig
index cf82f04f41..19fda8d82f 100644
--- a/lib/std/math/complex/tanh.zig
+++ b/lib/std/math/complex/tanh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/copysign.zig b/lib/std/math/copysign.zig
index 1547382cbd..2804d10495 100644
--- a/lib/std/math/copysign.zig
+++ b/lib/std/math/copysign.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/cos.zig b/lib/std/math/cos.zig
index f8135e5d4f..21804a8e5e 100644
--- a/lib/std/math/cos.zig
+++ b/lib/std/math/cos.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/cosh.zig b/lib/std/math/cosh.zig
index c3736415d3..25d22057ef 100644
--- a/lib/std/math/cosh.zig
+++ b/lib/std/math/cosh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/epsilon.zig b/lib/std/math/epsilon.zig
index 3243b085ad..61758f1ee0 100644
--- a/lib/std/math/epsilon.zig
+++ b/lib/std/math/epsilon.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/exp.zig b/lib/std/math/exp.zig
index 87e1031a24..1156cc6c5a 100644
--- a/lib/std/math/exp.zig
+++ b/lib/std/math/exp.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/exp2.zig b/lib/std/math/exp2.zig
index 1c25504de9..155d10c7f1 100644
--- a/lib/std/math/exp2.zig
+++ b/lib/std/math/exp2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/expm1.zig b/lib/std/math/expm1.zig
index 1c22db342a..8389b01eb9 100644
--- a/lib/std/math/expm1.zig
+++ b/lib/std/math/expm1.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/expo2.zig b/lib/std/math/expo2.zig
index a81c9920e0..b88d4c2236 100644
--- a/lib/std/math/expo2.zig
+++ b/lib/std/math/expo2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/fabs.zig b/lib/std/math/fabs.zig
index f263bfbc58..d59d185b99 100644
--- a/lib/std/math/fabs.zig
+++ b/lib/std/math/fabs.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/floor.zig b/lib/std/math/floor.zig
index d28b5e102c..6e0b99f47c 100644
--- a/lib/std/math/floor.zig
+++ b/lib/std/math/floor.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/fma.zig b/lib/std/math/fma.zig
index 852bbe9d75..1b04e1aa18 100644
--- a/lib/std/math/fma.zig
+++ b/lib/std/math/fma.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/frexp.zig b/lib/std/math/frexp.zig
index 3f73c9eec3..5f7bafb494 100644
--- a/lib/std/math/frexp.zig
+++ b/lib/std/math/frexp.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/hypot.zig b/lib/std/math/hypot.zig
index f04a42d1d5..78aef476f9 100644
--- a/lib/std/math/hypot.zig
+++ b/lib/std/math/hypot.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/ilogb.zig b/lib/std/math/ilogb.zig
index a6fb031973..e43012b831 100644
--- a/lib/std/math/ilogb.zig
+++ b/lib/std/math/ilogb.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/inf.zig b/lib/std/math/inf.zig
index f2e0283e03..5011193e95 100644
--- a/lib/std/math/inf.zig
+++ b/lib/std/math/inf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/isfinite.zig b/lib/std/math/isfinite.zig
index 938c495d65..5266b918df 100644
--- a/lib/std/math/isfinite.zig
+++ b/lib/std/math/isfinite.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/isinf.zig b/lib/std/math/isinf.zig
index 2ecd9c2b9c..b7c3199f15 100644
--- a/lib/std/math/isinf.zig
+++ b/lib/std/math/isinf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/isnan.zig b/lib/std/math/isnan.zig
index fc58e7334c..498d181118 100644
--- a/lib/std/math/isnan.zig
+++ b/lib/std/math/isnan.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/isnormal.zig b/lib/std/math/isnormal.zig
index b9ff515bdc..6317535203 100644
--- a/lib/std/math/isnormal.zig
+++ b/lib/std/math/isnormal.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/ln.zig b/lib/std/math/ln.zig
index cb5c966ab8..e0ce32a7e1 100644
--- a/lib/std/math/ln.zig
+++ b/lib/std/math/ln.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/log.zig b/lib/std/math/log.zig
index 240ef759d1..ef4d4bbb97 100644
--- a/lib/std/math/log.zig
+++ b/lib/std/math/log.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/log10.zig b/lib/std/math/log10.zig
index 269bc1d228..719e0cf51d 100644
--- a/lib/std/math/log10.zig
+++ b/lib/std/math/log10.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/log1p.zig b/lib/std/math/log1p.zig
index c0ca027ffb..4eaee2c43f 100644
--- a/lib/std/math/log1p.zig
+++ b/lib/std/math/log1p.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/log2.zig b/lib/std/math/log2.zig
index 201364a8cf..c44672751e 100644
--- a/lib/std/math/log2.zig
+++ b/lib/std/math/log2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/modf.zig b/lib/std/math/modf.zig
index fe73f0ce75..390b3e4f49 100644
--- a/lib/std/math/modf.zig
+++ b/lib/std/math/modf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/nan.zig b/lib/std/math/nan.zig
index b8e3b517e2..98051b155a 100644
--- a/lib/std/math/nan.zig
+++ b/lib/std/math/nan.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/pow.zig b/lib/std/math/pow.zig
index d4ea8876cc..5c49c95865 100644
--- a/lib/std/math/pow.zig
+++ b/lib/std/math/pow.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/powi.zig b/lib/std/math/powi.zig
index 8846ee8833..e415b74d87 100644
--- a/lib/std/math/powi.zig
+++ b/lib/std/math/powi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/round.zig b/lib/std/math/round.zig
index 0855b11cbb..9167bcfc82 100644
--- a/lib/std/math/round.zig
+++ b/lib/std/math/round.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/scalbn.zig b/lib/std/math/scalbn.zig
index 7243084dd4..cf8ff9003d 100644
--- a/lib/std/math/scalbn.zig
+++ b/lib/std/math/scalbn.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/signbit.zig b/lib/std/math/signbit.zig
index defd02aa3a..9fb245c3c6 100644
--- a/lib/std/math/signbit.zig
+++ b/lib/std/math/signbit.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/sin.zig b/lib/std/math/sin.zig
index 0f30e6749f..d051e3f88a 100644
--- a/lib/std/math/sin.zig
+++ b/lib/std/math/sin.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/sinh.zig b/lib/std/math/sinh.zig
index d39c7ee002..16329a9108 100644
--- a/lib/std/math/sinh.zig
+++ b/lib/std/math/sinh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/sqrt.zig b/lib/std/math/sqrt.zig
index 54c2fff13a..38609115d8 100644
--- a/lib/std/math/sqrt.zig
+++ b/lib/std/math/sqrt.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/tan.zig b/lib/std/math/tan.zig
index b80e2fbb27..d0e8a0d4f8 100644
--- a/lib/std/math/tan.zig
+++ b/lib/std/math/tan.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/tanh.zig b/lib/std/math/tanh.zig
index 81df2aed35..c53f03122b 100644
--- a/lib/std/math/tanh.zig
+++ b/lib/std/math/tanh.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/math/trunc.zig b/lib/std/math/trunc.zig
index 935da85013..69c300efee 100644
--- a/lib/std/math/trunc.zig
+++ b/lib/std/math/trunc.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/mem.zig b/lib/std/mem.zig
index 22e340810e..5f23a10401 100644
--- a/lib/std/mem.zig
+++ b/lib/std/mem.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -991,6 +991,40 @@ test "mem.count" {
testing.expect(count(u8, "owowowu", "owowu") == 1);
}
+/// Returns true if the haystack contains expected_count or more needles
+/// needle.len must be > 0
+/// does not count overlapping needles
+pub fn containsAtLeast(comptime T: type, haystack: []const T, expected_count: usize, needle: []const T) bool {
+ assert(needle.len > 0);
+ if (expected_count == 0) return true;
+
+ var i: usize = 0;
+ var found: usize = 0;
+
+ while (indexOfPos(T, haystack, i, needle)) |idx| {
+ i = idx + needle.len;
+ found += 1;
+ if (found == expected_count) return true;
+ }
+ return false;
+}
+
+test "mem.containsAtLeast" {
+ testing.expect(containsAtLeast(u8, "aa", 0, "a"));
+ testing.expect(containsAtLeast(u8, "aa", 1, "a"));
+ testing.expect(containsAtLeast(u8, "aa", 2, "a"));
+ testing.expect(!containsAtLeast(u8, "aa", 3, "a"));
+
+ testing.expect(containsAtLeast(u8, "radaradar", 1, "radar"));
+ testing.expect(!containsAtLeast(u8, "radaradar", 2, "radar"));
+
+ testing.expect(containsAtLeast(u8, "radarradaradarradar", 3, "radar"));
+ testing.expect(!containsAtLeast(u8, "radarradaradarradar", 4, "radar"));
+
+ testing.expect(containsAtLeast(u8, " radar radar ", 2, "radar"));
+ testing.expect(!containsAtLeast(u8, " radar radar ", 3, "radar"));
+}
+
/// Reads an integer from memory with size equal to bytes.len.
/// T specifies the return type, which must be large enough to store
/// the result.
@@ -1473,7 +1507,7 @@ pub fn joinZ(allocator: *Allocator, separator: []const u8, slices: []const []con
}
fn joinMaybeZ(allocator: *Allocator, separator: []const u8, slices: []const []const u8, zero: bool) ![]u8 {
- if (slices.len == 0) return &[0]u8{};
+ if (slices.len == 0) return if (zero) try allocator.dupe(u8, &[1]u8{0}) else &[0]u8{};
const total_len = blk: {
var sum: usize = separator.len * (slices.len - 1);
@@ -1502,6 +1536,11 @@ fn joinMaybeZ(allocator: *Allocator, separator: []const u8, slices: []const []co
test "mem.join" {
{
+ const str = try join(testing.allocator, ",", &[_][]const u8{});
+ defer testing.allocator.free(str);
+ testing.expect(eql(u8, str, ""));
+ }
+ {
const str = try join(testing.allocator, ",", &[_][]const u8{ "a", "b", "c" });
defer testing.allocator.free(str);
testing.expect(eql(u8, str, "a,b,c"));
@@ -1520,6 +1559,12 @@ test "mem.join" {
test "mem.joinZ" {
{
+ const str = try joinZ(testing.allocator, ",", &[_][]const u8{});
+ defer testing.allocator.free(str);
+ testing.expect(eql(u8, str, ""));
+ testing.expectEqual(str[str.len], 0);
+ }
+ {
const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "b", "c" });
defer testing.allocator.free(str);
testing.expect(eql(u8, str, "a,b,c"));
@@ -2402,3 +2447,46 @@ test "freeing empty string with null-terminated sentinel" {
const empty_string = try dupeZ(testing.allocator, u8, "");
testing.allocator.free(empty_string);
}
+
+/// Returns a slice with the given new alignment,
+/// all other pointer attributes copied from `AttributeSource`.
+fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: u29) type {
+ const info = @typeInfo(AttributeSource).Pointer;
+ return @Type(.{
+ .Pointer = .{
+ .size = .Slice,
+ .is_const = info.is_const,
+ .is_volatile = info.is_volatile,
+ .is_allowzero = info.is_allowzero,
+ .alignment = new_alignment,
+ .child = info.child,
+ .sentinel = null,
+ },
+ });
+}
+
+/// Returns the largest slice in the given bytes that conforms to the new alignment,
+/// or `null` if the given bytes contain no conforming address.
+pub fn alignInBytes(bytes: []u8, comptime new_alignment: usize) ?[]align(new_alignment) u8 {
+ const begin_address = @ptrToInt(bytes.ptr);
+ const end_address = begin_address + bytes.len;
+
+ const begin_address_aligned = mem.alignForward(begin_address, new_alignment);
+ const new_length = std.math.sub(usize, end_address, begin_address_aligned) catch |e| switch (e) {
+ error.Overflow => return null,
+ };
+ const alignment_offset = begin_address_aligned - begin_address;
+ return @alignCast(new_alignment, bytes[alignment_offset .. alignment_offset + new_length]);
+}
+
+/// Returns the largest sub-slice within the given slice that conforms to the new alignment,
+/// or `null` if the given slice contains no conforming address.
+pub fn alignInSlice(slice: anytype, comptime new_alignment: usize) ?AlignedSlice(@TypeOf(slice), new_alignment) {
+ const bytes = sliceAsBytes(slice);
+ const aligned_bytes = alignInBytes(bytes, new_alignment) orelse return null;
+
+ const Element = @TypeOf(slice[0]);
+ const slice_length_bytes = aligned_bytes.len - (aligned_bytes.len % @sizeOf(Element));
+ const aligned_slice = bytesAsSlice(Element, aligned_bytes[0..slice_length_bytes]);
+ return @alignCast(new_alignment, aligned_slice);
+}
diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig
index 4511acb275..6cc888fa10 100644
--- a/lib/std/mem/Allocator.zig
+++ b/lib/std/mem/Allocator.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -146,12 +146,8 @@ fn moveBytes(
assert(new_len > 0);
const new_mem = try self.allocFn(self, new_len, new_alignment, len_align, return_address);
@memcpy(new_mem.ptr, old_mem.ptr, math.min(new_len, old_mem.len));
- // TODO DISABLED TO AVOID BUGS IN TRANSLATE C
- // TODO see also https://github.com/ziglang/zig/issues/4298
- // use './zig build test-translate-c' to reproduce, some of the symbols in the
- // generated C code will be a sequence of 0xaa (the undefined value), meaning
- // it is printing data that has been freed
- //@memset(old_mem.ptr, undefined, old_mem.len);
+ // TODO https://github.com/ziglang/zig/issues/4298
+ @memset(old_mem.ptr, undefined, old_mem.len);
_ = self.shrinkBytes(old_mem, old_align, 0, 0, return_address);
return new_mem;
}
diff --git a/lib/std/meta.zig b/lib/std/meta.zig
index 2cf7f6de81..7ec29dcd0e 100644
--- a/lib/std/meta.zig
+++ b/lib/std/meta.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -9,6 +9,7 @@ const debug = std.debug;
const mem = std.mem;
const math = std.math;
const testing = std.testing;
+const root = @import("root");
pub const trait = @import("meta/trait.zig");
pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;
@@ -486,19 +487,14 @@ test "std.meta.fields" {
testing.expect(comptime uf[0].field_type == u8);
}
-pub fn fieldInfo(comptime T: type, comptime field_name: []const u8) switch (@typeInfo(T)) {
+pub fn fieldInfo(comptime T: type, comptime field: FieldEnum(T)) switch (@typeInfo(T)) {
.Struct => TypeInfo.StructField,
.Union => TypeInfo.UnionField,
.ErrorSet => TypeInfo.Error,
.Enum => TypeInfo.EnumField,
else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
} {
- inline for (comptime fields(T)) |field| {
- if (comptime mem.eql(u8, field.name, field_name))
- return field;
- }
-
- @compileError("'" ++ @typeName(T) ++ "' has no field '" ++ field_name ++ "'");
+ return fields(T)[@enumToInt(field)];
}
test "std.meta.fieldInfo" {
@@ -513,10 +509,10 @@ test "std.meta.fieldInfo" {
a: u8,
};
- const e1f = comptime fieldInfo(E1, "A");
- const e2f = comptime fieldInfo(E2, "A");
- const sf = comptime fieldInfo(S1, "a");
- const uf = comptime fieldInfo(U1, "a");
+ const e1f = fieldInfo(E1, .A);
+ const e2f = fieldInfo(E2, .A);
+ const sf = fieldInfo(S1, .a);
+ const uf = fieldInfo(U1, .a);
testing.expect(mem.eql(u8, e1f.name, "A"));
testing.expect(mem.eql(u8, e2f.name, "A"));
@@ -538,9 +534,7 @@ pub fn fieldNames(comptime T: type) *const [fields(T).len][]const u8 {
}
test "std.meta.fieldNames" {
- const E1 = enum {
- A, B
- };
+ const E1 = enum { A, B };
const E2 = error{A};
const S1 = struct {
a: u8,
@@ -567,15 +561,55 @@ test "std.meta.fieldNames" {
testing.expectEqualSlices(u8, u1names[1], "b");
}
-pub fn TagType(comptime T: type) type {
+pub fn FieldEnum(comptime T: type) type {
+ const fieldInfos = fields(T);
+ var enumFields: [fieldInfos.len]std.builtin.TypeInfo.EnumField = undefined;
+ var decls = [_]std.builtin.TypeInfo.Declaration{};
+ inline for (fieldInfos) |field, i| {
+ enumFields[i] = .{
+ .name = field.name,
+ .value = i,
+ };
+ }
+ return @Type(.{
+ .Enum = .{
+ .layout = .Auto,
+ .tag_type = std.math.IntFittingRange(0, fieldInfos.len - 1),
+ .fields = &enumFields,
+ .decls = &decls,
+ .is_exhaustive = true,
+ },
+ });
+}
+
+fn expectEqualEnum(expected: anytype, actual: @TypeOf(expected)) void {
+ // TODO: https://github.com/ziglang/zig/issues/7419
+ // testing.expectEqual(@typeInfo(expected).Enum, @typeInfo(actual).Enum);
+ testing.expectEqual(@typeInfo(expected).Enum.layout, @typeInfo(actual).Enum.layout);
+ testing.expectEqual(@typeInfo(expected).Enum.tag_type, @typeInfo(actual).Enum.tag_type);
+ comptime testing.expectEqualSlices(std.builtin.TypeInfo.EnumField, @typeInfo(expected).Enum.fields, @typeInfo(actual).Enum.fields);
+ comptime testing.expectEqualSlices(std.builtin.TypeInfo.Declaration, @typeInfo(expected).Enum.decls, @typeInfo(actual).Enum.decls);
+ testing.expectEqual(@typeInfo(expected).Enum.is_exhaustive, @typeInfo(actual).Enum.is_exhaustive);
+}
+
+test "std.meta.FieldEnum" {
+ expectEqualEnum(enum { a }, FieldEnum(struct { a: u8 }));
+ expectEqualEnum(enum { a, b, c }, FieldEnum(struct { a: u8, b: void, c: f32 }));
+ expectEqualEnum(enum { a, b, c }, FieldEnum(union { a: u8, b: void, c: f32 }));
+}
+
+// Deprecated: use Tag
+pub const TagType = Tag;
+
+pub fn Tag(comptime T: type) type {
return switch (@typeInfo(T)) {
.Enum => |info| info.tag_type,
- .Union => |info| if (info.tag_type) |Tag| Tag else null,
+ .Union => |info| info.tag_type orelse @compileError(@typeName(T) ++ " has no tag type"),
else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"),
};
}
-test "std.meta.TagType" {
+test "std.meta.Tag" {
const E = enum(u8) {
C = 33,
D,
@@ -585,14 +619,14 @@ test "std.meta.TagType" {
D: u16,
};
- testing.expect(TagType(E) == u8);
- testing.expect(TagType(U) == E);
+ testing.expect(Tag(E) == u8);
+ testing.expect(Tag(U) == E);
}
///Returns the active tag of a tagged union
-pub fn activeTag(u: anytype) @TagType(@TypeOf(u)) {
+pub fn activeTag(u: anytype) Tag(@TypeOf(u)) {
const T = @TypeOf(u);
- return @as(@TagType(T), u);
+ return @as(Tag(T), u);
}
test "std.meta.activeTag" {
@@ -613,13 +647,15 @@ test "std.meta.activeTag" {
testing.expect(activeTag(u) == UE.Float);
}
+const TagPayloadType = TagPayload;
+
///Given a tagged union type, and an enum, return the type of the union
/// field corresponding to the enum tag.
-pub fn TagPayloadType(comptime U: type, tag: @TagType(U)) type {
+pub fn TagPayload(comptime U: type, tag: Tag(U)) type {
testing.expect(trait.is(.Union)(U));
const info = @typeInfo(U).Union;
- const tag_info = @typeInfo(@TagType(U)).Enum;
+ const tag_info = @typeInfo(Tag(U)).Enum;
inline for (info.fields) |field_info| {
if (comptime mem.eql(u8, field_info.name, @tagName(tag)))
@@ -629,14 +665,14 @@ pub fn TagPayloadType(comptime U: type, tag: @TagType(U)) type {
unreachable;
}
-test "std.meta.TagPayloadType" {
+test "std.meta.TagPayload" {
const Event = union(enum) {
Moved: struct {
from: i32,
to: i32,
},
};
- const MovedEvent = TagPayloadType(Event, Event.Moved);
+ const MovedEvent = TagPayload(Event, Event.Moved);
var e: Event = undefined;
testing.expect(MovedEvent == @TypeOf(e.Moved));
}
@@ -661,13 +697,13 @@ pub fn eql(a: anytype, b: @TypeOf(a)) bool {
}
},
.Union => |info| {
- if (info.tag_type) |Tag| {
+ if (info.tag_type) |UnionTag| {
const tag_a = activeTag(a);
const tag_b = activeTag(b);
if (tag_a != tag_b) return false;
inline for (info.fields) |field_info| {
- if (@field(Tag, field_info.name) == tag_a) {
+ if (@field(UnionTag, field_info.name) == tag_a) {
return eql(@field(a, field_info.name), @field(b, field_info.name));
}
}
@@ -789,9 +825,9 @@ test "intToEnum with error return" {
pub const IntToEnumError = error{InvalidEnumTag};
-pub fn intToEnum(comptime Tag: type, tag_int: anytype) IntToEnumError!Tag {
- inline for (@typeInfo(Tag).Enum.fields) |f| {
- const this_tag_value = @field(Tag, f.name);
+pub fn intToEnum(comptime EnumTag: type, tag_int: anytype) IntToEnumError!EnumTag {
+ inline for (@typeInfo(EnumTag).Enum.fields) |f| {
+ const this_tag_value = @field(EnumTag, f.name);
if (tag_int == @enumToInt(this_tag_value)) {
return this_tag_value;
}
@@ -946,9 +982,60 @@ test "std.meta.cast" {
/// Given a value returns its size as C's sizeof operator would.
/// This is for translate-c and is not intended for general use.
pub fn sizeof(target: anytype) usize {
- switch (@typeInfo(@TypeOf(target))) {
- .Type => return @sizeOf(target),
- .Float, .Int, .Struct, .Union, .Enum => return @sizeOf(@TypeOf(target)),
+ const T: type = if (@TypeOf(target) == type) target else @TypeOf(target);
+ switch (@typeInfo(T)) {
+ .Float, .Int, .Struct, .Union, .Enum, .Array, .Bool, .Vector => return @sizeOf(T),
+ .Fn => {
+ // sizeof(main) returns 1, sizeof(&main) returns pointer size.
+ // We cannot distinguish those types in Zig, so use pointer size.
+ return @sizeOf(T);
+ },
+ .Null => return @sizeOf(*c_void),
+ .Void => {
+ // Note: sizeof(void) is 1 on clang/gcc and 0 on MSVC.
+ return 1;
+ },
+ .Opaque => {
+ if (T == c_void) {
+ // Note: sizeof(void) is 1 on clang/gcc and 0 on MSVC.
+ return 1;
+ } else {
+ @compileError("Cannot use C sizeof on opaque type " ++ @typeName(T));
+ }
+ },
+ .Optional => |opt| {
+ if (@typeInfo(opt.child) == .Pointer) {
+ return sizeof(opt.child);
+ } else {
+ @compileError("Cannot use C sizeof on non-pointer optional " ++ @typeName(T));
+ }
+ },
+ .Pointer => |ptr| {
+ if (ptr.size == .Slice) {
+ @compileError("Cannot use C sizeof on slice type " ++ @typeName(T));
+ }
+ // for strings, sizeof("a") returns 2.
+ // normal pointer decay scenarios from C are handled
+ // in the .Array case above, but strings remain literals
+ // and are therefore always pointers, so they need to be
+ // specially handled here.
+ if (ptr.size == .One and ptr.is_const and @typeInfo(ptr.child) == .Array) {
+ const array_info = @typeInfo(ptr.child).Array;
+ if ((array_info.child == u8 or array_info.child == u16) and
+ array_info.sentinel != null and
+ array_info.sentinel.? == 0)
+ {
+ // length of the string plus one for the null terminator.
+ return (array_info.len + 1) * @sizeOf(array_info.child);
+ }
+ }
+ // When zero sized pointers are removed, this case will no
+ // longer be reachable and can be deleted.
+ if (@sizeOf(T) == 0) {
+ return @sizeOf(*c_void);
+ }
+ return @sizeOf(T);
+ },
.ComptimeFloat => return @sizeOf(f64), // TODO c_double #3999
.ComptimeInt => {
// TODO to get the correct result we have to translate
@@ -958,7 +1045,7 @@ pub fn sizeof(target: anytype) usize {
// TODO test if target fits in int, long or long long
return @sizeOf(c_int);
},
- else => @compileError("TODO implement std.meta.sizeof for type " ++ @typeName(@TypeOf(target))),
+ else => @compileError("std.meta.sizeof does not support type " ++ @typeName(T)),
}
}
@@ -966,12 +1053,45 @@ test "sizeof" {
const E = extern enum(c_int) { One, _ };
const S = extern struct { a: u32 };
+ const ptr_size = @sizeOf(*c_void);
+
testing.expect(sizeof(u32) == 4);
testing.expect(sizeof(@as(u32, 2)) == 4);
testing.expect(sizeof(2) == @sizeOf(c_int));
+
+ testing.expect(sizeof(2.0) == @sizeOf(f64));
+
testing.expect(sizeof(E) == @sizeOf(c_int));
testing.expect(sizeof(E.One) == @sizeOf(c_int));
+
testing.expect(sizeof(S) == 4);
+
+ testing.expect(sizeof([_]u32{ 4, 5, 6 }) == 12);
+ testing.expect(sizeof([3]u32) == 12);
+ testing.expect(sizeof([3:0]u32) == 16);
+ testing.expect(sizeof(&[_]u32{ 4, 5, 6 }) == ptr_size);
+
+ testing.expect(sizeof(*u32) == ptr_size);
+ testing.expect(sizeof([*]u32) == ptr_size);
+ testing.expect(sizeof([*c]u32) == ptr_size);
+ testing.expect(sizeof(?*u32) == ptr_size);
+ testing.expect(sizeof(?[*]u32) == ptr_size);
+ testing.expect(sizeof(*c_void) == ptr_size);
+ testing.expect(sizeof(*void) == ptr_size);
+ testing.expect(sizeof(null) == ptr_size);
+
+ testing.expect(sizeof("foobar") == 7);
+ testing.expect(sizeof(&[_:0]u16{ 'f', 'o', 'o', 'b', 'a', 'r' }) == 14);
+ testing.expect(sizeof(*const [4:0]u8) == 5);
+ testing.expect(sizeof(*[4:0]u8) == ptr_size);
+ testing.expect(sizeof([*]const [4:0]u8) == ptr_size);
+ testing.expect(sizeof(*const *const [4:0]u8) == ptr_size);
+ testing.expect(sizeof(*const [4]u8) == ptr_size);
+
+ testing.expect(sizeof(sizeof) == @sizeOf(@TypeOf(sizeof)));
+
+ testing.expect(sizeof(void) == 1);
+ testing.expect(sizeof(c_void) == 1);
}
/// For a given function type, returns a tuple type which fields will
@@ -1085,3 +1205,10 @@ test "Tuple" {
TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 }));
TupleTester.assertTuple(.{ u32, f16, []const u8, void }, Tuple(&[_]type{ u32, f16, []const u8, void }));
}
+
+/// TODO: https://github.com/ziglang/zig/issues/425
+pub fn globalOption(comptime name: []const u8, comptime T: type) ?T {
+ if (!@hasDecl(root, name))
+ return null;
+ return @as(T, @field(root, name));
+}
diff --git a/lib/std/meta/trailer_flags.zig b/lib/std/meta/trailer_flags.zig
index 505afdb3c8..1697e9fe43 100644
--- a/lib/std/meta/trailer_flags.zig
+++ b/lib/std/meta/trailer_flags.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -21,20 +21,7 @@ pub fn TrailerFlags(comptime Fields: type) type {
pub const Int = meta.Int(.unsigned, bit_count);
pub const bit_count = @typeInfo(Fields).Struct.fields.len;
- pub const FieldEnum = blk: {
- comptime var fields: [bit_count]TypeInfo.EnumField = undefined;
- inline for (@typeInfo(Fields).Struct.fields) |struct_field, i|
- fields[i] = .{ .name = struct_field.name, .value = i };
- break :blk @Type(.{
- .Enum = .{
- .layout = .Auto,
- .tag_type = std.math.IntFittingRange(0, bit_count - 1),
- .fields = &fields,
- .decls = &[_]TypeInfo.Declaration{},
- .is_exhaustive = true,
- },
- });
- };
+ pub const FieldEnum = std.meta.FieldEnum(Fields);
pub const InitStruct = blk: {
comptime var fields: [bit_count]TypeInfo.StructField = undefined;
@@ -135,10 +122,7 @@ pub fn TrailerFlags(comptime Fields: type) type {
}
pub fn Field(comptime field: FieldEnum) type {
- inline for (@typeInfo(Fields).Struct.fields) |field_info, i| {
- if (i == @enumToInt(field))
- return field_info.field_type;
- }
+ return @typeInfo(Fields).Struct.fields[@enumToInt(field)].field_type;
}
pub fn sizeInBytes(self: Self) usize {
@@ -162,7 +146,7 @@ test "TrailerFlags" {
b: bool,
c: u64,
});
- testing.expectEqual(u2, @TagType(Flags.FieldEnum));
+ testing.expectEqual(u2, meta.Tag(Flags.FieldEnum));
var flags = Flags.init(.{
.b = true,
diff --git a/lib/std/meta/trait.zig b/lib/std/meta/trait.zig
index eb294a857c..e67f9b9bc4 100644
--- a/lib/std/meta/trait.zig
+++ b/lib/std/meta/trait.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -298,6 +298,20 @@ pub fn isNumber(comptime T: type) bool {
};
}
+pub fn isIntegerNumber(comptime T: type) bool {
+ return switch (@typeInfo(T)) {
+ .Int, .ComptimeInt => true,
+ else => false,
+ };
+}
+
+pub fn isFloatingNumber(comptime T: type) bool {
+ return switch (@typeInfo(T)) {
+ .Float, .ComptimeFloat => true,
+ else => false,
+ };
+}
+
test "std.meta.trait.isNumber" {
const NotANumber = struct {
number: u8,
@@ -476,15 +490,20 @@ pub fn hasUniqueRepresentation(comptime T: type) bool {
else => return false, // TODO can we know if it's true for some of these types ?
.AnyFrame,
- .Bool,
.BoundFn,
.Enum,
.ErrorSet,
.Fn,
- .Int, // TODO check that it is still true
- .Pointer,
=> return true,
+ .Bool => return false,
+
+ // The padding bits are undefined.
+ .Int => |info| return (info.bits % 8) == 0 and
+ (info.bits == 0 or std.math.isPowerOfTwo(info.bits)),
+
+ .Pointer => |info| return info.size != .Slice,
+
.Array => |info| return comptime hasUniqueRepresentation(info.child),
.Struct => |info| {
@@ -525,14 +544,49 @@ test "std.meta.trait.hasUniqueRepresentation" {
testing.expect(hasUniqueRepresentation(TestStruct3));
- testing.expect(hasUniqueRepresentation(i1));
- testing.expect(hasUniqueRepresentation(u2));
- testing.expect(hasUniqueRepresentation(i3));
- testing.expect(hasUniqueRepresentation(u4));
- testing.expect(hasUniqueRepresentation(i5));
- testing.expect(hasUniqueRepresentation(u6));
- testing.expect(hasUniqueRepresentation(i7));
- testing.expect(hasUniqueRepresentation(u8));
- testing.expect(hasUniqueRepresentation(i9));
- testing.expect(hasUniqueRepresentation(u10));
+ const TestStruct4 = struct { a: []const u8 };
+
+ testing.expect(!hasUniqueRepresentation(TestStruct4));
+
+ const TestStruct5 = struct { a: TestStruct4 };
+
+ testing.expect(!hasUniqueRepresentation(TestStruct5));
+
+ const TestUnion1 = packed union {
+ a: u32,
+ b: u16,
+ };
+
+ testing.expect(!hasUniqueRepresentation(TestUnion1));
+
+ const TestUnion2 = extern union {
+ a: u32,
+ b: u16,
+ };
+
+ testing.expect(!hasUniqueRepresentation(TestUnion2));
+
+ const TestUnion3 = union {
+ a: u32,
+ b: u16,
+ };
+
+ testing.expect(!hasUniqueRepresentation(TestUnion3));
+
+ const TestUnion4 = union(enum) {
+ a: u32,
+ b: u16,
+ };
+
+ testing.expect(!hasUniqueRepresentation(TestUnion4));
+
+ inline for ([_]type{ i0, u8, i16, u32, i64 }) |T| {
+ testing.expect(hasUniqueRepresentation(T));
+ }
+ inline for ([_]type{ i1, u9, i17, u33, i24 }) |T| {
+ testing.expect(!hasUniqueRepresentation(T));
+ }
+
+ testing.expect(!hasUniqueRepresentation([]u8));
+ testing.expect(!hasUniqueRepresentation([]const u8));
}
diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig
new file mode 100644
index 0000000000..3306fd3ef0
--- /dev/null
+++ b/lib/std/multi_array_list.zig
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("std.zig");
+const assert = std.debug.assert;
+const meta = std.meta;
+const mem = std.mem;
+const Allocator = mem.Allocator;
+
+pub fn MultiArrayList(comptime S: type) type {
+ return struct {
+ bytes: [*]align(@alignOf(S)) u8 = undefined,
+ len: usize = 0,
+ capacity: usize = 0,
+
+ pub const Elem = S;
+
+ pub const Field = meta.FieldEnum(S);
+
+ pub const Slice = struct {
+ /// This array is indexed by the field index which can be obtained
+ /// by using @enumToInt() on the Field enum
+ ptrs: [fields.len][*]u8,
+ len: usize,
+ capacity: usize,
+
+ pub fn items(self: Slice, comptime field: Field) []FieldType(field) {
+ const byte_ptr = self.ptrs[@enumToInt(field)];
+ const F = FieldType(field);
+ const casted_ptr = @ptrCast([*]F, @alignCast(@alignOf(F), byte_ptr));
+ return casted_ptr[0..self.len];
+ }
+
+ pub fn toMultiArrayList(self: Slice) Self {
+ if (self.ptrs.len == 0) {
+ return .{};
+ }
+ const unaligned_ptr = self.ptrs[sizes.fields[0]];
+ const aligned_ptr = @alignCast(@alignOf(S), unaligned_ptr);
+ const casted_ptr = @ptrCast([*]align(@alignOf(S)) u8, aligned_ptr);
+ return .{
+ .bytes = casted_ptr,
+ .len = self.len,
+ .capacity = self.capacity,
+ };
+ }
+
+ pub fn deinit(self: *Slice, gpa: *Allocator) void {
+ var other = self.toMultiArrayList();
+ other.deinit(gpa);
+ self.* = undefined;
+ }
+ };
+
+ const Self = @This();
+
+ const fields = meta.fields(S);
+ /// `sizes.bytes` is an array of @sizeOf each S field. Sorted by alignment, descending.
+ /// `sizes.fields` is an array mapping from `sizes.bytes` array index to field index.
+ const sizes = blk: {
+ const Data = struct {
+ size: usize,
+ size_index: usize,
+ alignment: usize,
+ };
+ var data: [fields.len]Data = undefined;
+ for (fields) |field_info, i| {
+ data[i] = .{
+ .size = @sizeOf(field_info.field_type),
+ .size_index = i,
+ .alignment = field_info.alignment,
+ };
+ }
+ const Sort = struct {
+ fn lessThan(trash: *i32, lhs: Data, rhs: Data) bool {
+ return lhs.alignment >= rhs.alignment;
+ }
+ };
+ var trash: i32 = undefined; // workaround for stage1 compiler bug
+ std.sort.sort(Data, &data, &trash, Sort.lessThan);
+ var sizes_bytes: [fields.len]usize = undefined;
+ var field_indexes: [fields.len]usize = undefined;
+ for (data) |elem, i| {
+ sizes_bytes[i] = elem.size;
+ field_indexes[i] = elem.size_index;
+ }
+ break :blk .{
+ .bytes = sizes_bytes,
+ .fields = field_indexes,
+ };
+ };
+
+ /// Release all allocated memory.
+ pub fn deinit(self: *Self, gpa: *Allocator) void {
+ gpa.free(self.allocatedBytes());
+ self.* = undefined;
+ }
+
+ /// The caller owns the returned memory. Empties this MultiArrayList.
+ pub fn toOwnedSlice(self: *Self) Slice {
+ const result = self.slice();
+ self.* = .{};
+ return result;
+ }
+
+ pub fn slice(self: Self) Slice {
+ var result: Slice = .{
+ .ptrs = undefined,
+ .len = self.len,
+ .capacity = self.capacity,
+ };
+ var ptr: [*]u8 = self.bytes;
+ for (sizes.bytes) |field_size, i| {
+ result.ptrs[sizes.fields[i]] = ptr;
+ ptr += field_size * self.capacity;
+ }
+ return result;
+ }
+
+ pub fn items(self: Self, comptime field: Field) []FieldType(field) {
+ return self.slice().items(field);
+ }
+
+ /// Overwrite one array element with new data.
+ pub fn set(self: *Self, index: usize, elem: S) void {
+ const slices = self.slice();
+ inline for (fields) |field_info, i| {
+ slices.items(@intToEnum(Field, i))[index] = @field(elem, field_info.name);
+ }
+ }
+
+ /// Obtain all the data for one array element.
+ pub fn get(self: *Self, index: usize) S {
+ const slices = self.slice();
+ var result: S = undefined;
+ inline for (fields) |field_info, i| {
+ @field(elem, field_info.name) = slices.items(@intToEnum(Field, i))[index];
+ }
+ return result;
+ }
+
+ /// Extend the list by 1 element. Allocates more memory as necessary.
+ pub fn append(self: *Self, gpa: *Allocator, elem: S) !void {
+ try self.ensureCapacity(gpa, self.len + 1);
+ self.appendAssumeCapacity(elem);
+ }
+
+ /// Extend the list by 1 element, but asserting `self.capacity`
+ /// is sufficient to hold an additional item.
+ pub fn appendAssumeCapacity(self: *Self, elem: S) void {
+ assert(self.len < self.capacity);
+ self.len += 1;
+ self.set(self.len - 1, elem);
+ }
+
+ /// Adjust the list's length to `new_len`.
+ /// Does not initialize added items, if any.
+ pub fn resize(self: *Self, gpa: *Allocator, new_len: usize) !void {
+ try self.ensureCapacity(gpa, new_len);
+ self.len = new_len;
+ }
+
+ /// Attempt to reduce allocated capacity to `new_len`.
+ /// If `new_len` is greater than zero, this may fail to reduce the capacity,
+ /// but the data remains intact and the length is updated to new_len.
+ pub fn shrinkAndFree(self: *Self, gpa: *Allocator, new_len: usize) void {
+ if (new_len == 0) {
+ gpa.free(self.allocatedBytes());
+ self.* = .{};
+ return;
+ }
+ assert(new_len <= self.capacity);
+ assert(new_len <= self.len);
+
+ const other_bytes = gpa.allocAdvanced(
+ u8,
+ @alignOf(S),
+ capacityInBytes(new_len),
+ .exact,
+ ) catch {
+ const self_slice = self.slice();
+ inline for (fields) |field_info, i| {
+ const field = @intToEnum(Field, i);
+ const dest_slice = self_slice.items(field)[new_len..];
+ const byte_count = dest_slice.len * @sizeOf(field_info.field_type);
+ // We use memset here for more efficient codegen in safety-checked,
+ // valgrind-enabled builds. Otherwise the valgrind client request
+ // will be repeated for every element.
+ @memset(@ptrCast([*]u8, dest_slice.ptr), undefined, byte_count);
+ }
+ self.len = new_len;
+ return;
+ };
+ var other = Self{
+ .bytes = other_bytes.ptr,
+ .capacity = new_len,
+ .len = new_len,
+ };
+ self.len = new_len;
+ const self_slice = self.slice();
+ const other_slice = other.slice();
+ inline for (fields) |field_info, i| {
+ const field = @intToEnum(Field, i);
+ // TODO we should be able to use std.mem.copy here but it causes a
+ // test failure on aarch64 with -OReleaseFast
+ const src_slice = mem.sliceAsBytes(self_slice.items(field));
+ const dst_slice = mem.sliceAsBytes(other_slice.items(field));
+ @memcpy(dst_slice.ptr, src_slice.ptr, src_slice.len);
+ }
+ gpa.free(self.allocatedBytes());
+ self.* = other;
+ }
+
+ /// Reduce length to `new_len`.
+ /// Invalidates pointers to elements `items[new_len..]`.
+ /// Keeps capacity the same.
+ pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
+ self.len = new_len;
+ }
+
+ /// Modify the array so that it can hold at least `new_capacity` items.
+ /// Implements super-linear growth to achieve amortized O(1) append operations.
+ /// Invalidates pointers if additional memory is needed.
+ pub fn ensureCapacity(self: *Self, gpa: *Allocator, new_capacity: usize) !void {
+ var better_capacity = self.capacity;
+ if (better_capacity >= new_capacity) return;
+
+ while (true) {
+ better_capacity += better_capacity / 2 + 8;
+ if (better_capacity >= new_capacity) break;
+ }
+
+ return self.setCapacity(gpa, better_capacity);
+ }
+
+ /// Modify the array so that it can hold exactly `new_capacity` items.
+ /// Invalidates pointers if additional memory is needed.
+ /// `new_capacity` must be greater or equal to `len`.
+ pub fn setCapacity(self: *Self, gpa: *Allocator, new_capacity: usize) !void {
+ assert(new_capacity >= self.len);
+ const new_bytes = try gpa.allocAdvanced(
+ u8,
+ @alignOf(S),
+ capacityInBytes(new_capacity),
+ .exact,
+ );
+ if (self.len == 0) {
+ self.bytes = new_bytes.ptr;
+ self.capacity = new_capacity;
+ return;
+ }
+ var other = Self{
+ .bytes = new_bytes.ptr,
+ .capacity = new_capacity,
+ .len = self.len,
+ };
+ const self_slice = self.slice();
+ const other_slice = other.slice();
+ inline for (fields) |field_info, i| {
+ const field = @intToEnum(Field, i);
+ // TODO we should be able to use std.mem.copy here but it causes a
+ // test failure on aarch64 with -OReleaseFast
+ const src_slice = mem.sliceAsBytes(self_slice.items(field));
+ const dst_slice = mem.sliceAsBytes(other_slice.items(field));
+ @memcpy(dst_slice.ptr, src_slice.ptr, src_slice.len);
+ }
+ gpa.free(self.allocatedBytes());
+ self.* = other;
+ }
+
+ fn capacityInBytes(capacity: usize) usize {
+ const sizes_vector: std.meta.Vector(sizes.bytes.len, usize) = sizes.bytes;
+ const capacity_vector = @splat(sizes.bytes.len, capacity);
+ return @reduce(.Add, capacity_vector * sizes_vector);
+ }
+
+ fn allocatedBytes(self: Self) []align(@alignOf(S)) u8 {
+ return self.bytes[0..capacityInBytes(self.capacity)];
+ }
+
+ fn FieldType(field: Field) type {
+ return meta.fieldInfo(S, field).field_type;
+ }
+ };
+}
+
+test "basic usage" {
+ const testing = std.testing;
+ const ally = testing.allocator;
+
+ const Foo = struct {
+ a: u32,
+ b: []const u8,
+ c: u8,
+ };
+
+ var list = MultiArrayList(Foo){};
+ defer list.deinit(ally);
+
+ try list.ensureCapacity(ally, 2);
+
+ list.appendAssumeCapacity(.{
+ .a = 1,
+ .b = "foobar",
+ .c = 'a',
+ });
+
+ list.appendAssumeCapacity(.{
+ .a = 2,
+ .b = "zigzag",
+ .c = 'b',
+ });
+
+ testing.expectEqualSlices(u32, list.items(.a), &[_]u32{ 1, 2 });
+ testing.expectEqualSlices(u8, list.items(.c), &[_]u8{ 'a', 'b' });
+
+ testing.expectEqual(@as(usize, 2), list.items(.b).len);
+ testing.expectEqualStrings("foobar", list.items(.b)[0]);
+ testing.expectEqualStrings("zigzag", list.items(.b)[1]);
+
+ try list.append(ally, .{
+ .a = 3,
+ .b = "fizzbuzz",
+ .c = 'c',
+ });
+
+ testing.expectEqualSlices(u32, list.items(.a), &[_]u32{ 1, 2, 3 });
+ testing.expectEqualSlices(u8, list.items(.c), &[_]u8{ 'a', 'b', 'c' });
+
+ testing.expectEqual(@as(usize, 3), list.items(.b).len);
+ testing.expectEqualStrings("foobar", list.items(.b)[0]);
+ testing.expectEqualStrings("zigzag", list.items(.b)[1]);
+ testing.expectEqualStrings("fizzbuzz", list.items(.b)[2]);
+
+ // Add 6 more things to force a capacity increase.
+ var i: usize = 0;
+ while (i < 6) : (i += 1) {
+ try list.append(ally, .{
+ .a = @intCast(u32, 4 + i),
+ .b = "whatever",
+ .c = @intCast(u8, 'd' + i),
+ });
+ }
+
+ testing.expectEqualSlices(
+ u32,
+ &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 },
+ list.items(.a),
+ );
+ testing.expectEqualSlices(
+ u8,
+ &[_]u8{ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' },
+ list.items(.c),
+ );
+
+ list.shrinkAndFree(ally, 3);
+
+ testing.expectEqualSlices(u32, list.items(.a), &[_]u32{ 1, 2, 3 });
+ testing.expectEqualSlices(u8, list.items(.c), &[_]u8{ 'a', 'b', 'c' });
+
+ testing.expectEqual(@as(usize, 3), list.items(.b).len);
+ testing.expectEqualStrings("foobar", list.items(.b)[0]);
+ testing.expectEqualStrings("zigzag", list.items(.b)[1]);
+ testing.expectEqualStrings("fizzbuzz", list.items(.b)[2]);
+}
+
+// This was observed to fail on aarch64 with LLVM 11, when the capacityInBytes
+// function used the @reduce code path.
+test "regression test for @reduce bug" {
+ const ally = std.testing.allocator;
+ var list = MultiArrayList(struct {
+ tag: std.zig.Token.Tag,
+ start: u32,
+ }){};
+ defer list.deinit(ally);
+
+ try list.ensureCapacity(ally, 20);
+
+ try list.append(ally, .{ .tag = .keyword_const, .start = 0 });
+ try list.append(ally, .{ .tag = .identifier, .start = 6 });
+ try list.append(ally, .{ .tag = .equal, .start = 10 });
+ try list.append(ally, .{ .tag = .builtin, .start = 12 });
+ try list.append(ally, .{ .tag = .l_paren, .start = 19 });
+ try list.append(ally, .{ .tag = .string_literal, .start = 20 });
+ try list.append(ally, .{ .tag = .r_paren, .start = 25 });
+ try list.append(ally, .{ .tag = .semicolon, .start = 26 });
+ try list.append(ally, .{ .tag = .keyword_pub, .start = 29 });
+ try list.append(ally, .{ .tag = .keyword_fn, .start = 33 });
+ try list.append(ally, .{ .tag = .identifier, .start = 36 });
+ try list.append(ally, .{ .tag = .l_paren, .start = 40 });
+ try list.append(ally, .{ .tag = .r_paren, .start = 41 });
+ try list.append(ally, .{ .tag = .identifier, .start = 43 });
+ try list.append(ally, .{ .tag = .bang, .start = 51 });
+ try list.append(ally, .{ .tag = .identifier, .start = 52 });
+ try list.append(ally, .{ .tag = .l_brace, .start = 57 });
+ try list.append(ally, .{ .tag = .identifier, .start = 63 });
+ try list.append(ally, .{ .tag = .period, .start = 66 });
+ try list.append(ally, .{ .tag = .identifier, .start = 67 });
+ try list.append(ally, .{ .tag = .period, .start = 70 });
+ try list.append(ally, .{ .tag = .identifier, .start = 71 });
+ try list.append(ally, .{ .tag = .l_paren, .start = 75 });
+ try list.append(ally, .{ .tag = .string_literal, .start = 76 });
+ try list.append(ally, .{ .tag = .comma, .start = 113 });
+ try list.append(ally, .{ .tag = .period, .start = 115 });
+ try list.append(ally, .{ .tag = .l_brace, .start = 116 });
+ try list.append(ally, .{ .tag = .r_brace, .start = 117 });
+ try list.append(ally, .{ .tag = .r_paren, .start = 118 });
+ try list.append(ally, .{ .tag = .semicolon, .start = 119 });
+ try list.append(ally, .{ .tag = .r_brace, .start = 121 });
+ try list.append(ally, .{ .tag = .eof, .start = 123 });
+
+ const tags = list.items(.tag);
+ std.testing.expectEqual(tags[1], .identifier);
+ std.testing.expectEqual(tags[2], .equal);
+ std.testing.expectEqual(tags[3], .builtin);
+ std.testing.expectEqual(tags[4], .l_paren);
+ std.testing.expectEqual(tags[5], .string_literal);
+ std.testing.expectEqual(tags[6], .r_paren);
+ std.testing.expectEqual(tags[7], .semicolon);
+ std.testing.expectEqual(tags[8], .keyword_pub);
+ std.testing.expectEqual(tags[9], .keyword_fn);
+ std.testing.expectEqual(tags[10], .identifier);
+ std.testing.expectEqual(tags[11], .l_paren);
+ std.testing.expectEqual(tags[12], .r_paren);
+ std.testing.expectEqual(tags[13], .identifier);
+ std.testing.expectEqual(tags[14], .bang);
+ std.testing.expectEqual(tags[15], .identifier);
+ std.testing.expectEqual(tags[16], .l_brace);
+ std.testing.expectEqual(tags[17], .identifier);
+ std.testing.expectEqual(tags[18], .period);
+ std.testing.expectEqual(tags[19], .identifier);
+ std.testing.expectEqual(tags[20], .period);
+ std.testing.expectEqual(tags[21], .identifier);
+ std.testing.expectEqual(tags[22], .l_paren);
+ std.testing.expectEqual(tags[23], .string_literal);
+ std.testing.expectEqual(tags[24], .comma);
+ std.testing.expectEqual(tags[25], .period);
+ std.testing.expectEqual(tags[26], .l_brace);
+ std.testing.expectEqual(tags[27], .r_brace);
+ std.testing.expectEqual(tags[28], .r_paren);
+ std.testing.expectEqual(tags[29], .semicolon);
+ std.testing.expectEqual(tags[30], .r_brace);
+ std.testing.expectEqual(tags[31], .eof);
+}
diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig
deleted file mode 100644
index fb54e04289..0000000000
--- a/lib/std/mutex.zig
+++ /dev/null
@@ -1,330 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("std.zig");
-const builtin = @import("builtin");
-const os = std.os;
-const assert = std.debug.assert;
-const windows = os.windows;
-const testing = std.testing;
-const SpinLock = std.SpinLock;
-const ResetEvent = std.ResetEvent;
-
-/// Lock may be held only once. If the same thread tries to acquire
-/// the same mutex twice, it deadlocks. This type supports static
-/// initialization and is at most `@sizeOf(usize)` in size. When an
-/// application is built in single threaded release mode, all the
-/// functions are no-ops. In single threaded debug mode, there is
-/// deadlock detection.
-///
-/// Example usage:
-/// var m = Mutex{};
-///
-/// const lock = m.acquire();
-/// defer lock.release();
-/// ... critical code
-///
-/// Non-blocking:
-/// if (m.tryAcquire) |lock| {
-/// defer lock.release();
-/// // ... critical section
-/// } else {
-/// // ... lock not acquired
-/// }
-pub const Mutex = if (builtin.single_threaded)
- Dummy
-else if (builtin.os.tag == .windows)
- WindowsMutex
-else if (builtin.link_libc or builtin.os.tag == .linux)
- // stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs
- struct {
- state: usize = 0,
-
- /// number of times to spin trying to acquire the lock.
- /// https://webkit.org/blog/6161/locking-in-webkit/
- const SPIN_COUNT = 40;
-
- const MUTEX_LOCK: usize = 1 << 0;
- const QUEUE_LOCK: usize = 1 << 1;
- const QUEUE_MASK: usize = ~(MUTEX_LOCK | QUEUE_LOCK);
-
- const Node = struct {
- next: ?*Node,
- event: ResetEvent,
- };
-
- pub fn tryAcquire(self: *Mutex) ?Held {
- if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)
- return null;
- return Held{ .mutex = self };
- }
-
- pub fn acquire(self: *Mutex) Held {
- return self.tryAcquire() orelse {
- self.acquireSlow();
- return Held{ .mutex = self };
- };
- }
-
- fn acquireSlow(self: *Mutex) void {
- // inlining the fast path and hiding *Slow()
- // calls behind a @setCold(true) appears to
- // improve performance in release builds.
- @setCold(true);
- while (true) {
-
- // try and spin for a bit to acquire the mutex if theres currently no queue
- var spin_count: u32 = SPIN_COUNT;
- var state = @atomicLoad(usize, &self.state, .Monotonic);
- while (spin_count != 0) : (spin_count -= 1) {
- if (state & MUTEX_LOCK == 0) {
- _ = @cmpxchgWeak(usize, &self.state, state, state | MUTEX_LOCK, .Acquire, .Monotonic) orelse return;
- } else if (state & QUEUE_MASK == 0) {
- break;
- }
- SpinLock.yield();
- state = @atomicLoad(usize, &self.state, .Monotonic);
- }
-
- // create the ResetEvent node on the stack
- // (faster than threadlocal on platforms like OSX)
- var node: Node = undefined;
- node.event = ResetEvent.init();
- defer node.event.deinit();
-
- // we've spun too long, try and add our node to the LIFO queue.
- // if the mutex becomes available in the process, try and grab it instead.
- while (true) {
- if (state & MUTEX_LOCK == 0) {
- _ = @cmpxchgWeak(usize, &self.state, state, state | MUTEX_LOCK, .Acquire, .Monotonic) orelse return;
- } else {
- node.next = @intToPtr(?*Node, state & QUEUE_MASK);
- const new_state = @ptrToInt(&node) | (state & ~QUEUE_MASK);
- _ = @cmpxchgWeak(usize, &self.state, state, new_state, .Release, .Monotonic) orelse {
- node.event.wait();
- break;
- };
- }
- SpinLock.yield();
- state = @atomicLoad(usize, &self.state, .Monotonic);
- }
- }
- }
-
- /// Returned when the lock is acquired. Call release to
- /// release.
- pub const Held = struct {
- mutex: *Mutex,
-
- /// Release the held lock.
- pub fn release(self: Held) void {
- // first, remove the lock bit so another possibly parallel acquire() can succeed.
- // use .Sub since it can be usually compiled down more efficiency
- // (`lock sub` on x86) vs .And ~MUTEX_LOCK (`lock cmpxchg` loop on x86)
- const state = @atomicRmw(usize, &self.mutex.state, .Sub, MUTEX_LOCK, .Release);
-
- // if the LIFO queue isnt locked and it has a node, try and wake up the node.
- if ((state & QUEUE_LOCK) == 0 and (state & QUEUE_MASK) != 0)
- self.mutex.releaseSlow();
- }
- };
-
- fn releaseSlow(self: *Mutex) void {
- @setCold(true);
-
- // try and lock the LFIO queue to pop a node off,
- // stopping altogether if its already locked or the queue is empty
- var state = @atomicLoad(usize, &self.state, .Monotonic);
- while (true) : (SpinLock.loopHint(1)) {
- if (state & QUEUE_LOCK != 0 or state & QUEUE_MASK == 0)
- return;
- state = @cmpxchgWeak(usize, &self.state, state, state | QUEUE_LOCK, .Acquire, .Monotonic) orelse break;
- }
-
- // acquired the QUEUE_LOCK, try and pop a node to wake it.
- // if the mutex is locked, then unset QUEUE_LOCK and let
- // the thread who holds the mutex do the wake-up on unlock()
- while (true) : (SpinLock.loopHint(1)) {
- if ((state & MUTEX_LOCK) != 0) {
- state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Acquire) orelse return;
- } else {
- const node = @intToPtr(*Node, state & QUEUE_MASK);
- const new_state = @ptrToInt(node.next);
- state = @cmpxchgWeak(usize, &self.state, state, new_state, .Release, .Acquire) orelse {
- node.event.set();
- return;
- };
- }
- }
- }
- }
-
- // for platforms without a known OS blocking
- // primitive, default to SpinLock for correctness
-else
- SpinLock;
-
-/// This has the sematics as `Mutex`, however it does not actually do any
-/// synchronization. Operations are safety-checked no-ops.
-pub const Dummy = struct {
- lock: @TypeOf(lock_init) = lock_init,
-
- const lock_init = if (std.debug.runtime_safety) false else {};
-
- pub const Held = struct {
- mutex: *Dummy,
-
- pub fn release(self: Held) void {
- if (std.debug.runtime_safety) {
- self.mutex.lock = false;
- }
- }
- };
-
- /// Create a new mutex in unlocked state.
- pub const init = Dummy{};
-
- /// Try to acquire the mutex without blocking. Returns null if
- /// the mutex is unavailable. Otherwise returns Held. Call
- /// release on Held.
- pub fn tryAcquire(self: *Dummy) ?Held {
- if (std.debug.runtime_safety) {
- if (self.lock) return null;
- self.lock = true;
- }
- return Held{ .mutex = self };
- }
-
- /// Acquire the mutex. Will deadlock if the mutex is already
- /// held by the calling thread.
- pub fn acquire(self: *Dummy) Held {
- return self.tryAcquire() orelse @panic("deadlock detected");
- }
-};
-
-// https://locklessinc.com/articles/keyed_events/
-const WindowsMutex = struct {
- state: State = State{ .waiters = 0 },
-
- const State = extern union {
- locked: u8,
- waiters: u32,
- };
-
- const WAKE = 1 << 8;
- const WAIT = 1 << 9;
-
- pub fn tryAcquire(self: *WindowsMutex) ?Held {
- if (@atomicRmw(u8, &self.state.locked, .Xchg, 1, .Acquire) != 0)
- return null;
- return Held{ .mutex = self };
- }
-
- pub fn acquire(self: *WindowsMutex) Held {
- return self.tryAcquire() orelse self.acquireSlow();
- }
-
- fn acquireSpinning(self: *WindowsMutex) Held {
- @setCold(true);
- while (true) : (SpinLock.yield()) {
- return self.tryAcquire() orelse continue;
- }
- }
-
- fn acquireSlow(self: *WindowsMutex) Held {
- // try to use NT keyed events for blocking, falling back to spinlock if unavailable
- @setCold(true);
- const handle = ResetEvent.OsEvent.Futex.getEventHandle() orelse return self.acquireSpinning();
- const key = @ptrCast(*const c_void, &self.state.waiters);
-
- while (true) : (SpinLock.loopHint(1)) {
- const waiters = @atomicLoad(u32, &self.state.waiters, .Monotonic);
-
- // try and take lock if unlocked
- if ((waiters & 1) == 0) {
- if (@atomicRmw(u8, &self.state.locked, .Xchg, 1, .Acquire) == 0) {
- return Held{ .mutex = self };
- }
-
- // otherwise, try and update the waiting count.
- // then unset the WAKE bit so that another unlocker can wake up a thread.
- } else if (@cmpxchgWeak(u32, &self.state.waiters, waiters, (waiters + WAIT) | 1, .Monotonic, .Monotonic) == null) {
- const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null);
- assert(rc == .SUCCESS);
- _ = @atomicRmw(u32, &self.state.waiters, .Sub, WAKE, .Monotonic);
- }
- }
- }
-
- pub const Held = struct {
- mutex: *WindowsMutex,
-
- pub fn release(self: Held) void {
- // unlock without a rmw/cmpxchg instruction
- @atomicStore(u8, @ptrCast(*u8, &self.mutex.state.locked), 0, .Release);
- const handle = ResetEvent.OsEvent.Futex.getEventHandle() orelse return;
- const key = @ptrCast(*const c_void, &self.mutex.state.waiters);
-
- while (true) : (SpinLock.loopHint(1)) {
- const waiters = @atomicLoad(u32, &self.mutex.state.waiters, .Monotonic);
-
- // no one is waiting
- if (waiters < WAIT) return;
- // someone grabbed the lock and will do the wake instead
- if (waiters & 1 != 0) return;
- // someone else is currently waking up
- if (waiters & WAKE != 0) return;
-
- // try to decrease the waiter count & set the WAKE bit meaning a thread is waking up
- if (@cmpxchgWeak(u32, &self.mutex.state.waiters, waiters, waiters - WAIT + WAKE, .Release, .Monotonic) == null) {
- const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null);
- assert(rc == .SUCCESS);
- return;
- }
- }
- }
- };
-};
-
-const TestContext = struct {
- mutex: *Mutex,
- data: i128,
-
- const incr_count = 10000;
-};
-
-test "std.Mutex" {
- var mutex = Mutex{};
-
- var context = TestContext{
- .mutex = &mutex,
- .data = 0,
- };
-
- if (builtin.single_threaded) {
- worker(&context);
- testing.expect(context.data == TestContext.incr_count);
- } else {
- const thread_count = 10;
- var threads: [thread_count]*std.Thread = undefined;
- for (threads) |*t| {
- t.* = try std.Thread.spawn(&context, worker);
- }
- for (threads) |t|
- t.wait();
-
- testing.expect(context.data == thread_count * TestContext.incr_count);
- }
-}
-
-fn worker(ctx: *TestContext) void {
- var i: usize = 0;
- while (i != TestContext.incr_count) : (i += 1) {
- const held = ctx.mutex.acquire();
- defer held.release();
-
- ctx.data += 1;
- }
-}
diff --git a/lib/std/net.zig b/lib/std/net.zig
index 7e285b1fac..636596c117 100644
--- a/lib/std/net.zig
+++ b/lib/std/net.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -10,8 +10,13 @@ const net = @This();
const mem = std.mem;
const os = std.os;
const fs = std.fs;
+const io = std.io;
-pub const has_unix_sockets = @hasDecl(os, "sockaddr_un");
+// Windows 10 added support for unix sockets in build 17063, redstone 4 is the
+// first release to support them.
+pub const has_unix_sockets = @hasDecl(os, "sockaddr_un") and
+ (builtin.os.tag != .windows or
+ std.Target.current.os.version_range.windows.isAtLeast(.win10_rs4) orelse false);
pub const Address = extern union {
any: os.sockaddr,
@@ -154,7 +159,7 @@ pub const Address = extern union {
unreachable;
}
- try std.fmt.format(out_stream, "{}", .{&self.un.path});
+ try std.fmt.format(out_stream, "{s}", .{&self.un.path});
},
else => unreachable,
}
@@ -596,7 +601,7 @@ pub const Ip6Address = extern struct {
}
};
-pub fn connectUnixSocket(path: []const u8) !fs.File {
+pub fn connectUnixSocket(path: []const u8) !Stream {
const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
const sockfd = try os.socket(
os.AF_UNIX,
@@ -614,7 +619,7 @@ pub fn connectUnixSocket(path: []const u8) !fs.File {
try os.connect(sockfd, &addr.any, addr.getOsSockLen());
}
- return fs.File{
+ return Stream{
.handle = sockfd,
};
}
@@ -648,7 +653,7 @@ pub const AddressList = struct {
};
/// All memory allocated with `allocator` will be freed before this function returns.
-pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16) !fs.File {
+pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16) !Stream {
const list = try getAddressList(allocator, name, port);
defer list.deinit();
@@ -665,7 +670,7 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16)
return std.os.ConnectError.ConnectionRefused;
}
-pub fn tcpConnectToAddress(address: Address) !fs.File {
+pub fn tcpConnectToAddress(address: Address) !Stream {
const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
const sock_flags = os.SOCK_STREAM | nonblock |
(if (builtin.os.tag == .windows) 0 else os.SOCK_CLOEXEC);
@@ -679,7 +684,7 @@ pub fn tcpConnectToAddress(address: Address) !fs.File {
try os.connect(sockfd, &address.any, address.getOsSockLen());
}
- return fs.File{ .handle = sockfd };
+ return Stream{ .handle = sockfd };
}
/// Call `AddressList.deinit` on the result.
@@ -783,13 +788,13 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
var lookup_addrs = std.ArrayList(LookupAddr).init(allocator);
defer lookup_addrs.deinit();
- var canon = std.ArrayListSentineled(u8, 0).initNull(arena);
+ var canon = std.ArrayList(u8).init(arena);
defer canon.deinit();
try linuxLookupName(&lookup_addrs, &canon, name, family, flags, port);
result.addrs = try arena.alloc(Address, lookup_addrs.items.len);
- if (!canon.isNull()) {
+ if (canon.items.len != 0) {
result.canon_name = canon.toOwnedSlice();
}
@@ -818,7 +823,7 @@ const DAS_ORDER_SHIFT = 0;
fn linuxLookupName(
addrs: *std.ArrayList(LookupAddr),
- canon: *std.ArrayListSentineled(u8, 0),
+ canon: *std.ArrayList(u8),
opt_name: ?[]const u8,
family: os.sa_family_t,
flags: u32,
@@ -826,7 +831,8 @@ fn linuxLookupName(
) !void {
if (opt_name) |name| {
// reject empty name and check len so it fits into temp bufs
- try canon.replaceContents(name);
+ canon.items.len = 0;
+ try canon.appendSlice(name);
if (Address.parseExpectingFamily(name, family, port)) |addr| {
try addrs.append(LookupAddr{ .addr = addr });
} else |name_err| if ((flags & std.c.AI_NUMERICHOST) != 0) {
@@ -1091,7 +1097,7 @@ fn linuxLookupNameFromNull(
fn linuxLookupNameFromHosts(
addrs: *std.ArrayList(LookupAddr),
- canon: *std.ArrayListSentineled(u8, 0),
+ canon: *std.ArrayList(u8),
name: []const u8,
family: os.sa_family_t,
port: u16,
@@ -1105,7 +1111,7 @@ fn linuxLookupNameFromHosts(
};
defer file.close();
- const stream = std.io.bufferedInStream(file.inStream()).inStream();
+ const stream = std.io.bufferedReader(file.reader()).reader();
var line_buf: [512]u8 = undefined;
while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
error.StreamTooLong => blk: {
@@ -1142,7 +1148,8 @@ fn linuxLookupNameFromHosts(
// first name is canonical name
const name_text = first_name_text.?;
if (isValidHostName(name_text)) {
- try canon.replaceContents(name_text);
+ canon.items.len = 0;
+ try canon.appendSlice(name_text);
}
}
}
@@ -1161,7 +1168,7 @@ pub fn isValidHostName(hostname: []const u8) bool {
fn linuxLookupNameFromDnsSearch(
addrs: *std.ArrayList(LookupAddr),
- canon: *std.ArrayListSentineled(u8, 0),
+ canon: *std.ArrayList(u8),
name: []const u8,
family: os.sa_family_t,
port: u16,
@@ -1177,10 +1184,10 @@ fn linuxLookupNameFromDnsSearch(
if (byte == '.') dots += 1;
}
- const search = if (rc.search.isNull() or dots >= rc.ndots or mem.endsWith(u8, name, "."))
+ const search = if (dots >= rc.ndots or mem.endsWith(u8, name, "."))
""
else
- rc.search.span();
+ rc.search.items;
var canon_name = name;
@@ -1193,30 +1200,30 @@ fn linuxLookupNameFromDnsSearch(
// name is not a CNAME record) and serves as a buffer for passing
// the full requested name to name_from_dns.
try canon.resize(canon_name.len);
- mem.copy(u8, canon.span(), canon_name);
+ mem.copy(u8, canon.items, canon_name);
try canon.append('.');
var tok_it = mem.tokenize(search, " \t");
while (tok_it.next()) |tok| {
- canon.shrink(canon_name.len + 1);
+ canon.shrinkRetainingCapacity(canon_name.len + 1);
try canon.appendSlice(tok);
- try linuxLookupNameFromDns(addrs, canon, canon.span(), family, rc, port);
+ try linuxLookupNameFromDns(addrs, canon, canon.items, family, rc, port);
if (addrs.items.len != 0) return;
}
- canon.shrink(canon_name.len);
+ canon.shrinkRetainingCapacity(canon_name.len);
return linuxLookupNameFromDns(addrs, canon, name, family, rc, port);
}
const dpc_ctx = struct {
addrs: *std.ArrayList(LookupAddr),
- canon: *std.ArrayListSentineled(u8, 0),
+ canon: *std.ArrayList(u8),
port: u16,
};
fn linuxLookupNameFromDns(
addrs: *std.ArrayList(LookupAddr),
- canon: *std.ArrayListSentineled(u8, 0),
+ canon: *std.ArrayList(u8),
name: []const u8,
family: os.sa_family_t,
rc: ResolvConf,
@@ -1271,7 +1278,7 @@ const ResolvConf = struct {
attempts: u32,
ndots: u32,
timeout: u32,
- search: std.ArrayListSentineled(u8, 0),
+ search: std.ArrayList(u8),
ns: std.ArrayList(LookupAddr),
fn deinit(rc: *ResolvConf) void {
@@ -1286,7 +1293,7 @@ const ResolvConf = struct {
fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
rc.* = ResolvConf{
.ns = std.ArrayList(LookupAddr).init(allocator),
- .search = std.ArrayListSentineled(u8, 0).initNull(allocator),
+ .search = std.ArrayList(u8).init(allocator),
.ndots = 1,
.timeout = 5,
.attempts = 2,
@@ -1302,7 +1309,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
};
defer file.close();
- const stream = std.io.bufferedInStream(file.inStream()).inStream();
+ const stream = std.io.bufferedReader(file.reader()).reader();
var line_buf: [512]u8 = undefined;
while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
error.StreamTooLong => blk: {
@@ -1338,7 +1345,8 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
const ip_txt = line_it.next() orelse continue;
try linuxLookupNameFromNumericUnspec(&rc.ns, ip_txt, 53);
} else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) {
- try rc.search.replaceContents(line_it.rest());
+ rc.search.items.len = 0;
+ try rc.search.appendSlice(line_it.rest());
}
}
@@ -1569,13 +1577,100 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)
_ = try os.dn_expand(packet, data, &tmp);
const canon_name = mem.spanZ(std.meta.assumeSentinel(&tmp, 0));
if (isValidHostName(canon_name)) {
- try ctx.canon.replaceContents(canon_name);
+ ctx.canon.items.len = 0;
+ try ctx.canon.appendSlice(canon_name);
}
},
else => return,
}
}
+pub const Stream = struct {
+ // Underlying socket descriptor.
+ // Note that on some platforms this may not be interchangeable with a
+ // regular files descriptor.
+ handle: os.socket_t,
+
+ pub fn close(self: Stream) void {
+ os.closeSocket(self.handle);
+ }
+
+ pub const ReadError = os.ReadError;
+ pub const WriteError = os.WriteError;
+
+ pub const Reader = io.Reader(Stream, ReadError, read);
+ pub const Writer = io.Writer(Stream, WriteError, write);
+
+ pub fn reader(self: Stream) Reader {
+ return .{ .context = self };
+ }
+
+ pub fn writer(self: Stream) Writer {
+ return .{ .context = self };
+ }
+
+ pub fn read(self: Stream, buffer: []u8) ReadError!usize {
+ if (std.Target.current.os.tag == .windows) {
+ return os.windows.ReadFile(self.handle, buffer, null, io.default_mode);
+ }
+
+ if (std.io.is_async) {
+ return std.event.Loop.instance.?.read(self.handle, buffer, false);
+ } else {
+ return os.read(self.handle, buffer);
+ }
+ }
+
+ /// TODO in evented I/O mode, this implementation incorrectly uses the event loop's
+ /// file system thread instead of non-blocking. It needs to be reworked to properly
+ /// use non-blocking I/O.
+ pub fn write(self: Stream, buffer: []const u8) WriteError!usize {
+ if (std.Target.current.os.tag == .windows) {
+ return os.windows.WriteFile(self.handle, buffer, null, io.default_mode);
+ }
+
+ if (std.io.is_async) {
+ return std.event.Loop.instance.?.write(self.handle, buffer, false);
+ } else {
+ return os.write(self.handle, buffer);
+ }
+ }
+
+ /// See https://github.com/ziglang/zig/issues/7699
+ /// See equivalent function: `std.fs.File.writev`.
+ pub fn writev(self: Stream, iovecs: []const os.iovec_const) WriteError!usize {
+ if (std.io.is_async) {
+ // TODO improve to actually take advantage of writev syscall, if available.
+ if (iovecs.len == 0) return 0;
+ const first_buffer = iovecs[0].iov_base[0..iovecs[0].iov_len];
+ try self.write(first_buffer);
+ return first_buffer.len;
+ } else {
+ return os.writev(self.handle, iovecs);
+ }
+ }
+
+ /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
+ /// order to handle partial writes from the underlying OS layer.
+ /// See https://github.com/ziglang/zig/issues/7699
+ /// See equivalent function: `std.fs.File.writevAll`.
+ pub fn writevAll(self: Stream, iovecs: []os.iovec_const) WriteError!void {
+ if (iovecs.len == 0) return;
+
+ var i: usize = 0;
+ while (true) {
+ var amt = try self.writev(iovecs[i..]);
+ while (amt >= iovecs[i].iov_len) {
+ amt -= iovecs[i].iov_len;
+ i += 1;
+ if (i >= iovecs.len) return;
+ }
+ iovecs[i].iov_base += amt;
+ iovecs[i].iov_len -= amt;
+ }
+ }
+};
+
pub const StreamServer = struct {
/// Copied from `Options` on `init`.
kernel_backlog: u31,
@@ -1682,7 +1777,7 @@ pub const StreamServer = struct {
} || os.UnexpectedError;
pub const Connection = struct {
- file: fs.File,
+ stream: Stream,
address: Address,
};
@@ -1701,7 +1796,7 @@ pub const StreamServer = struct {
if (accept_result) |fd| {
return Connection{
- .file = fs.File{ .handle = fd },
+ .stream = Stream{ .handle = fd },
.address = accepted_addr,
};
} else |err| switch (err) {
@@ -1711,6 +1806,6 @@ pub const StreamServer = struct {
}
};
-test "" {
+test {
_ = @import("net/test.zig");
}
diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig
index 9f40bb5a3b..10a9c4e18b 100644
--- a/lib/std/net/test.zig
+++ b/lib/std/net/test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -145,7 +145,7 @@ test "listen on a port, send bytes, receive bytes" {
// Try only the IPv4 variant as some CI builders have no IPv6 localhost
// configured.
- const localhost = try net.Address.parseIp("127.0.0.1", 8080);
+ const localhost = try net.Address.parseIp("127.0.0.1", 0);
var server = net.StreamServer.init(.{});
defer server.deinit();
@@ -165,8 +165,9 @@ test "listen on a port, send bytes, receive bytes" {
defer t.wait();
var client = try server.accept();
+ defer client.stream.close();
var buf: [16]u8 = undefined;
- const n = try client.file.reader().read(&buf);
+ const n = try client.stream.reader().read(&buf);
testing.expectEqual(@as(usize, 12), n);
testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
@@ -249,6 +250,49 @@ fn testServer(server: *net.StreamServer) anyerror!void {
var client = try server.accept();
- const stream = client.file.outStream();
+ const stream = client.stream.writer();
try stream.print("hello from server\n", .{});
}
+
+test "listen on a unix socket, send bytes, receive bytes" {
+ if (builtin.single_threaded) return error.SkipZigTest;
+ if (!net.has_unix_sockets) return error.SkipZigTest;
+
+ if (std.builtin.os.tag == .windows) {
+ _ = try std.os.windows.WSAStartup(2, 2);
+ }
+ defer {
+ if (std.builtin.os.tag == .windows) {
+ std.os.windows.WSACleanup() catch unreachable;
+ }
+ }
+
+ var server = net.StreamServer.init(.{});
+ defer server.deinit();
+
+ const socket_path = "socket.unix";
+
+ var socket_addr = try net.Address.initUnix(socket_path);
+ defer std.fs.cwd().deleteFile(socket_path) catch {};
+ try server.listen(socket_addr);
+
+ const S = struct {
+ fn clientFn(_: void) !void {
+ const socket = try net.connectUnixSocket(socket_path);
+ defer socket.close();
+
+ _ = try socket.writer().writeAll("Hello world!");
+ }
+ };
+
+ const t = try std.Thread.spawn({}, S.clientFn);
+ defer t.wait();
+
+ var client = try server.accept();
+ defer client.stream.close();
+ var buf: [16]u8 = undefined;
+ const n = try client.stream.reader().read(&buf);
+
+ testing.expectEqual(@as(usize, 12), n);
+ testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
+}
diff --git a/lib/std/once.zig b/lib/std/once.zig
index 6e0e4867d8..efa99060d3 100644
--- a/lib/std/once.zig
+++ b/lib/std/once.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -15,7 +15,7 @@ pub fn once(comptime f: fn () void) Once(f) {
pub fn Once(comptime f: fn () void) type {
return struct {
done: bool = false,
- mutex: std.Mutex = std.Mutex{},
+ mutex: std.Thread.Mutex = std.Thread.Mutex{},
/// Call the function `f`.
/// If `call` is invoked multiple times `f` will be executed only the
diff --git a/lib/std/os.zig b/lib/std/os.zig
index b42cdf756f..24d9041639 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -32,6 +32,7 @@ const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES;
pub const darwin = @import("os/darwin.zig");
pub const dragonfly = @import("os/dragonfly.zig");
pub const freebsd = @import("os/freebsd.zig");
+pub const haiku = @import("os/haiku.zig");
pub const netbsd = @import("os/netbsd.zig");
pub const openbsd = @import("os/openbsd.zig");
pub const linux = @import("os/linux.zig");
@@ -43,7 +44,7 @@ comptime {
assert(@import("std") == std); // std lib tests require --override-lib-dir
}
-test "" {
+test {
_ = darwin;
_ = freebsd;
_ = linux;
@@ -52,6 +53,7 @@ test "" {
_ = uefi;
_ = wasi;
_ = windows;
+ _ = haiku;
_ = @import("os/test.zig");
}
@@ -66,6 +68,7 @@ else if (builtin.link_libc)
else switch (builtin.os.tag) {
.macos, .ios, .watchos, .tvos => darwin,
.freebsd => freebsd,
+ .haiku => haiku,
.linux => linux,
.netbsd => netbsd,
.openbsd => openbsd,
@@ -191,7 +194,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
.capable_io_mode = .blocking,
.intended_io_mode = .blocking,
};
- const stream = file.inStream();
+ const stream = file.reader();
stream.readNoEof(buf) catch return error.Unexpected;
}
@@ -324,7 +327,7 @@ pub const ReadError = error{
/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
-/// For POSIX the limit is `math.maxInt(isize)`.
+/// The corresponding POSIX limit is `math.maxInt(isize)`.
pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
if (builtin.os.tag == .windows) {
return windows.ReadFile(fd, buf, null, std.io.default_mode);
@@ -447,6 +450,12 @@ pub const PReadError = ReadError || error{Unseekable};
/// return error.WouldBlock when EAGAIN is received.
/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
+///
+/// Linux has a limit on how many bytes may be transferred in one `pread` call, which is `0x7ffff000`
+/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
+/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
+/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
+/// The corresponding POSIX limit is `math.maxInt(isize)`.
pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
if (builtin.os.tag == .windows) {
return windows.ReadFile(fd, buf, offset, std.io.default_mode);
@@ -478,8 +487,16 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
}
}
+ // Prevent EINVAL.
+ const max_count = switch (std.Target.current.os.tag) {
+ .linux => 0x7ffff000,
+ .macos, .ios, .watchos, .tvos => math.maxInt(i32),
+ else => math.maxInt(isize),
+ };
+ const adjusted_len = math.min(max_count, buf.len);
+
while (true) {
- const rc = system.pread(fd, buf.ptr, buf.len, offset);
+ const rc = system.pread(fd, buf.ptr, adjusted_len, offset);
switch (errno(rc)) {
0 => return @intCast(usize, rc),
EINTR => continue,
@@ -585,7 +602,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
/// On these systems, the read races with concurrent writes to the same file descriptor.
pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
const have_pread_but_not_preadv = switch (std.Target.current.os.tag) {
- .windows, .macos, .ios, .watchos, .tvos => true,
+ .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
else => false,
};
if (have_pread_but_not_preadv) {
@@ -918,7 +935,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
/// If `iov.len` is larger than will fit in a `u31`, a partial write will occur.
pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize {
const have_pwrite_but_not_pwritev = switch (std.Target.current.os.tag) {
- .windows, .macos, .ios, .watchos, .tvos => true,
+ .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
else => false,
};
@@ -1348,89 +1365,10 @@ pub fn execvpeZ_expandArg0(
/// If `file` is an absolute path, this is the same as `execveZ`.
pub fn execvpeZ(
file: [*:0]const u8,
- argv: [*:null]const ?[*:0]const u8,
+ argv_ptr: [*:null]const ?[*:0]const u8,
envp: [*:null]const ?[*:0]const u8,
) ExecveError {
- return execvpeZ_expandArg0(.no_expand, file, argv, envp);
-}
-
-/// This is the same as `execvpe` except if the `arg0_expand` parameter is set to `.expand`,
-/// then argv[0] will be replaced with the expanded version of it, after resolving in accordance
-/// with the PATH environment variable.
-pub fn execvpe_expandArg0(
- allocator: *mem.Allocator,
- arg0_expand: Arg0Expand,
- argv_slice: []const []const u8,
- env_map: *const std.BufMap,
-) (ExecveError || error{OutOfMemory}) {
- const argv_buf = try allocator.alloc(?[*:0]u8, argv_slice.len + 1);
- mem.set(?[*:0]u8, argv_buf, null);
- defer {
- for (argv_buf) |arg| {
- const arg_buf = mem.spanZ(arg) orelse break;
- allocator.free(arg_buf);
- }
- allocator.free(argv_buf);
- }
- for (argv_slice) |arg, i| {
- const arg_buf = try allocator.alloc(u8, arg.len + 1);
- @memcpy(arg_buf.ptr, arg.ptr, arg.len);
- arg_buf[arg.len] = 0;
- argv_buf[i] = arg_buf[0..arg.len :0].ptr;
- }
- argv_buf[argv_slice.len] = null;
- const argv_ptr = argv_buf[0..argv_slice.len :null].ptr;
-
- const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
- defer freeNullDelimitedEnvMap(allocator, envp_buf);
-
- switch (arg0_expand) {
- .expand => return execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr),
- .no_expand => return execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr),
- }
-}
-
-/// This function must allocate memory to add a null terminating bytes on path and each arg.
-/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
-/// pointers after the args and after the environment variables.
-/// `argv_slice[0]` is the executable path.
-/// This function also uses the PATH environment variable to get the full path to the executable.
-pub fn execvpe(
- allocator: *mem.Allocator,
- argv_slice: []const []const u8,
- env_map: *const std.BufMap,
-) (ExecveError || error{OutOfMemory}) {
- return execvpe_expandArg0(allocator, .no_expand, argv_slice, env_map);
-}
-
-pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![:null]?[*:0]u8 {
- const envp_count = env_map.count();
- const envp_buf = try allocator.alloc(?[*:0]u8, envp_count + 1);
- mem.set(?[*:0]u8, envp_buf, null);
- errdefer freeNullDelimitedEnvMap(allocator, envp_buf);
- {
- var it = env_map.iterator();
- var i: usize = 0;
- while (it.next()) |pair| : (i += 1) {
- const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2);
- @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len);
- env_buf[pair.key.len] = '=';
- @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);
- const len = env_buf.len - 1;
- env_buf[len] = 0;
- envp_buf[i] = env_buf[0..len :0].ptr;
- }
- assert(i == envp_count);
- }
- return envp_buf[0..envp_count :null];
-}
-
-pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void {
- for (envp_buf) |env| {
- const env_buf = if (env) |ptr| ptr[0 .. mem.len(ptr) + 1] else break;
- allocator.free(env_buf);
- }
- allocator.free(envp_buf);
+ return execvpeZ_expandArg0(.no_expand, file, argv_ptr, envp);
}
/// Get an environment variable.
@@ -1699,6 +1637,92 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:
}
}
+pub const LinkError = UnexpectedError || error{
+ AccessDenied,
+ DiskQuota,
+ PathAlreadyExists,
+ FileSystem,
+ SymLinkLoop,
+ LinkQuotaExceeded,
+ NameTooLong,
+ FileNotFound,
+ SystemResources,
+ NoSpaceLeft,
+ ReadOnlyFileSystem,
+ NotSameFileSystem,
+};
+
+pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void {
+ switch (errno(system.link(oldpath, newpath, flags))) {
+ 0 => return,
+ EACCES => return error.AccessDenied,
+ EDQUOT => return error.DiskQuota,
+ EEXIST => return error.PathAlreadyExists,
+ EFAULT => unreachable,
+ EIO => return error.FileSystem,
+ ELOOP => return error.SymLinkLoop,
+ EMLINK => return error.LinkQuotaExceeded,
+ ENAMETOOLONG => return error.NameTooLong,
+ ENOENT => return error.FileNotFound,
+ ENOMEM => return error.SystemResources,
+ ENOSPC => return error.NoSpaceLeft,
+ EPERM => return error.AccessDenied,
+ EROFS => return error.ReadOnlyFileSystem,
+ EXDEV => return error.NotSameFileSystem,
+ EINVAL => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+}
+
+pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void {
+ const old = try toPosixPath(oldpath);
+ const new = try toPosixPath(newpath);
+ return try linkZ(&old, &new, flags);
+}
+
+pub const LinkatError = LinkError || error{NotDir};
+
+pub fn linkatZ(
+ olddir: fd_t,
+ oldpath: [*:0]const u8,
+ newdir: fd_t,
+ newpath: [*:0]const u8,
+ flags: i32,
+) LinkatError!void {
+ switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) {
+ 0 => return,
+ EACCES => return error.AccessDenied,
+ EDQUOT => return error.DiskQuota,
+ EEXIST => return error.PathAlreadyExists,
+ EFAULT => unreachable,
+ EIO => return error.FileSystem,
+ ELOOP => return error.SymLinkLoop,
+ EMLINK => return error.LinkQuotaExceeded,
+ ENAMETOOLONG => return error.NameTooLong,
+ ENOENT => return error.FileNotFound,
+ ENOMEM => return error.SystemResources,
+ ENOSPC => return error.NoSpaceLeft,
+ ENOTDIR => return error.NotDir,
+ EPERM => return error.AccessDenied,
+ EROFS => return error.ReadOnlyFileSystem,
+ EXDEV => return error.NotSameFileSystem,
+ EINVAL => unreachable,
+ else => |err| return unexpectedErrno(err),
+ }
+}
+
+pub fn linkat(
+ olddir: fd_t,
+ oldpath: []const u8,
+ newdir: fd_t,
+ newpath: []const u8,
+ flags: i32,
+) LinkatError!void {
+ const old = try toPosixPath(oldpath);
+ const new = try toPosixPath(newpath);
+ return try linkatZ(olddir, &old, newdir, &new, flags);
+}
+
pub const UnlinkError = error{
FileNotFound,
@@ -3225,6 +3249,9 @@ pub const ConnectError = error{
/// The given path for the unix socket does not exist.
FileNotFound,
+
+ /// Connection was reset by peer before connect could complete.
+ ConnectionResetByPeer,
} || UnexpectedError;
/// Initiate a connection on a socket.
@@ -3239,8 +3266,9 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne
.WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
.WSAECONNREFUSED => return error.ConnectionRefused,
.WSAETIMEDOUT => return error.ConnectionTimedOut,
- .WSAEHOSTUNREACH // TODO: should we return NetworkUnreachable in this case as well?
- , .WSAENETUNREACH => return error.NetworkUnreachable,
+ .WSAEHOSTUNREACH, // TODO: should we return NetworkUnreachable in this case as well?
+ .WSAENETUNREACH,
+ => return error.NetworkUnreachable,
.WSAEFAULT => unreachable,
.WSAEINVAL => unreachable,
.WSAEISCONN => unreachable,
@@ -3302,6 +3330,7 @@ pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
ETIMEDOUT => return error.ConnectionTimedOut,
+ ECONNRESET => return error.ConnectionResetByPeer,
else => |err| return unexpectedErrno(err),
},
EBADF => unreachable, // The argument sockfd is not a valid file descriptor.
@@ -3830,31 +3859,54 @@ pub fn pipe() PipeError![2]fd_t {
}
pub fn pipe2(flags: u32) PipeError![2]fd_t {
- if (comptime std.Target.current.isDarwin()) {
- var fds: [2]fd_t = try pipe();
- if (flags == 0) return fds;
- errdefer {
- close(fds[0]);
- close(fds[1]);
- }
- for (fds) |fd| switch (errno(system.fcntl(fd, F_SETFL, flags))) {
- 0 => {},
+ if (@hasDecl(system, "pipe2")) {
+ var fds: [2]fd_t = undefined;
+ switch (errno(system.pipe2(&fds, flags))) {
+ 0 => return fds,
EINVAL => unreachable, // Invalid flags
- EBADF => unreachable, // Always a race condition
+ EFAULT => unreachable, // Invalid fds pointer
+ ENFILE => return error.SystemFdQuotaExceeded,
+ EMFILE => return error.ProcessFdQuotaExceeded,
else => |err| return unexpectedErrno(err),
- };
+ }
+ }
+
+ var fds: [2]fd_t = try pipe();
+ errdefer {
+ close(fds[0]);
+ close(fds[1]);
+ }
+
+ if (flags == 0)
return fds;
+
+ // O_CLOEXEC is special, it's a file descriptor flag and must be set using
+ // F_SETFD.
+ if (flags & O_CLOEXEC != 0) {
+ for (fds) |fd| {
+ switch (errno(system.fcntl(fd, F_SETFD, @as(u32, FD_CLOEXEC)))) {
+ 0 => {},
+ EINVAL => unreachable, // Invalid flags
+ EBADF => unreachable, // Always a race condition
+ else => |err| return unexpectedErrno(err),
+ }
+ }
}
- var fds: [2]fd_t = undefined;
- switch (errno(system.pipe2(&fds, flags))) {
- 0 => return fds,
- EINVAL => unreachable, // Invalid flags
- EFAULT => unreachable, // Invalid fds pointer
- ENFILE => return error.SystemFdQuotaExceeded,
- EMFILE => return error.ProcessFdQuotaExceeded,
- else => |err| return unexpectedErrno(err),
+ const new_flags = flags & ~@as(u32, O_CLOEXEC);
+ // Set every other flag affecting the file status using F_SETFL.
+ if (new_flags != 0) {
+ for (fds) |fd| {
+ switch (errno(system.fcntl(fd, F_SETFL, new_flags))) {
+ 0 => {},
+ EINVAL => unreachable, // Invalid flags
+ EBADF => unreachable, // Always a race condition
+ else => |err| return unexpectedErrno(err),
+ }
+ }
}
+
+ return fds;
}
pub const SysCtlError = error{
@@ -3872,7 +3924,10 @@ pub fn sysctl(
newlen: usize,
) SysCtlError!void {
if (builtin.os.tag == .wasi) {
- @panic("unsupported");
+ @panic("unsupported"); // TODO should be compile error, not panic
+ }
+ if (builtin.os.tag == .haiku) {
+ @panic("unsupported"); // TODO should be compile error, not panic
}
const name_len = math.cast(c_uint, name.len) catch return error.NameTooLong;
@@ -3896,7 +3951,10 @@ pub fn sysctlbynameZ(
newlen: usize,
) SysCtlError!void {
if (builtin.os.tag == .wasi) {
- @panic("unsupported");
+ @panic("unsupported"); // TODO should be compile error, not panic
+ }
+ if (builtin.os.tag == .haiku) {
+ @panic("unsupported"); // TODO should be compile error, not panic
}
switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
@@ -4335,7 +4393,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
},
.linux => {
var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined;
- const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable;
+ const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{d}\x00", .{fd}) catch unreachable;
const target = readlinkZ(std.meta.assumeSentinel(proc_path.ptr, 0), out_buffer) catch |err| {
switch (err) {
@@ -4566,7 +4624,7 @@ pub const UnexpectedError = error{
/// and you get an unexpected error.
pub fn unexpectedErrno(err: usize) UnexpectedError {
if (unexpected_error_tracing) {
- std.debug.warn("unexpected errno: {}\n", .{err});
+ std.debug.warn("unexpected errno: {d}\n", .{err});
std.debug.dumpCurrentStackTrace(null);
}
return error.Unexpected;
@@ -4774,6 +4832,12 @@ pub const SendError = error{
BrokenPipe,
FileDescriptorNotASocket,
+
+ /// Network is unreachable.
+ NetworkUnreachable,
+
+ /// The local network interface used to reach the destination is down.
+ NetworkSubsystemFailed,
} || UnexpectedError;
pub const SendToError = SendError || error{
@@ -4790,15 +4854,8 @@ pub const SendToError = SendError || error{
FileNotFound,
NotDir,
- /// Network is unreachable.
- NetworkUnreachable,
-
- /// Insufficient memory was available to fulfill the request.
- SystemResources,
-
/// The socket is not connected (connection-oriented sockets only).
SocketNotConnected,
- WouldBlock,
AddressNotAvailable,
};
@@ -4853,7 +4910,7 @@ pub fn sendto(
.WSAEHOSTUNREACH => return error.NetworkUnreachable,
// TODO: WSAEINPROGRESS, WSAEINTR
.WSAEINVAL => unreachable,
- .WSAENETDOWN => return error.NetworkUnreachable,
+ .WSAENETDOWN => return error.NetworkSubsystemFailed,
.WSAENETRESET => return error.ConnectionResetByPeer,
.WSAENETUNREACH => return error.NetworkUnreachable,
.WSAENOTCONN => return error.SocketNotConnected,
@@ -4882,7 +4939,6 @@ pub fn sendto(
EMSGSIZE => return error.MessageTooBig,
ENOBUFS => return error.SystemResources,
ENOMEM => return error.SystemResources,
- ENOTCONN => unreachable, // The socket is not connected, and no target has been given.
ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
EPIPE => return error.BrokenPipe,
@@ -4892,6 +4948,9 @@ pub fn sendto(
ENOENT => return error.FileNotFound,
ENOTDIR => return error.NotDir,
EHOSTUNREACH => return error.NetworkUnreachable,
+ ENETUNREACH => return error.NetworkUnreachable,
+ ENOTCONN => return error.SocketNotConnected,
+ ENETDOWN => return error.NetworkSubsystemFailed,
else => |err| return unexpectedErrno(err),
}
}
@@ -4923,7 +4982,17 @@ pub fn send(
buf: []const u8,
flags: u32,
) SendError!usize {
- return sendto(sockfd, buf, flags, null, 0);
+ return sendto(sockfd, buf, flags, null, 0) catch |err| switch (err) {
+ error.AddressFamilyNotSupported => unreachable,
+ error.SymLinkLoop => unreachable,
+ error.NameTooLong => unreachable,
+ error.FileNotFound => unreachable,
+ error.NotDir => unreachable,
+ error.NetworkUnreachable => unreachable,
+ error.AddressNotAvailable => unreachable,
+ error.SocketNotConnected => unreachable,
+ else => |e| return e,
+ };
}
pub const SendFileError = PReadError || WriteError || SendError;
@@ -4967,7 +5036,7 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize {
///
/// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000`
/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
-/// well as stuffing the errno codes into the last `4096` values. This is cited on the `sendfile` man page.
+/// well as stuffing the errno codes into the last `4096` values. This is noted on the `sendfile` man page.
/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
/// The corresponding POSIX limit on this is `math.maxInt(isize)`.
pub fn sendfile(
@@ -5338,7 +5407,8 @@ pub const PollError = error{
pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
while (true) {
- const rc = system.poll(fds.ptr, fds.len, timeout);
+ const fds_count = math.cast(nfds_t, fds.len) catch return error.SystemResources;
+ const rc = system.poll(fds.ptr, fds_count, timeout);
if (builtin.os.tag == .windows) {
if (rc == windows.ws2_32.SOCKET_ERROR) {
switch (windows.ws2_32.WSAGetLastError()) {
@@ -5549,6 +5619,9 @@ pub const SetSockOptError = error{
/// Insufficient resources are available in the system to complete the call.
SystemResources,
+ // Setting the socket option requires more elevated permissions.
+ PermissionDenied,
+
NetworkSubsystemFailed,
FileDescriptorNotASocket,
SocketNotBound,
@@ -5581,6 +5654,7 @@ pub fn setsockopt(fd: socket_t, level: u32, optname: u32, opt: []const u8) SetSo
ENOPROTOOPT => return error.InvalidProtocolOption,
ENOMEM => return error.SystemResources,
ENOBUFS => return error.SystemResources,
+ EPERM => return error.PermissionDenied,
else => |err| return unexpectedErrno(err),
}
}
@@ -5845,3 +5919,51 @@ pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void
else => |err| return unexpectedErrno(err),
}
}
+
+pub const MadviseError = error{
+ /// advice is MADV_REMOVE, but the specified address range is not a shared writable mapping.
+ AccessDenied,
+ /// advice is MADV_HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability.
+ PermissionDenied,
+ /// A kernel resource was temporarily unavailable.
+ SystemResources,
+ /// One of the following:
+ /// * addr is not page-aligned or length is negative
+ /// * advice is not valid
+ /// * advice is MADV_DONTNEED or MADV_REMOVE and the specified address range
+ /// includes locked, Huge TLB pages, or VM_PFNMAP pages.
+ /// * advice is MADV_MERGEABLE or MADV_UNMERGEABLE, but the kernel was not
+ /// configured with CONFIG_KSM.
+ /// * advice is MADV_FREE or MADV_WIPEONFORK but the specified address range
+ /// includes file, Huge TLB, MAP_SHARED, or VM_PFNMAP ranges.
+ InvalidSyscall,
+ /// (for MADV_WILLNEED) Paging in this area would exceed the process's
+ /// maximum resident set size.
+ WouldExceedMaximumResidentSetSize,
+ /// One of the following:
+ /// * (for MADV_WILLNEED) Not enough memory: paging in failed.
+ /// * Addresses in the specified range are not currently mapped, or
+ /// are outside the address space of the process.
+ OutOfMemory,
+ /// The madvise syscall is not available on this version and configuration
+ /// of the Linux kernel.
+ MadviseUnavailable,
+ /// The operating system returned an undocumented error code.
+ Unexpected,
+};
+
+/// Give advice about use of memory.
+/// This syscall is optional and is sometimes configured to be disabled.
+pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void {
+ switch (errno(system.madvise(ptr, length, advice))) {
+ 0 => return,
+ EACCES => return error.AccessDenied,
+ EAGAIN => return error.SystemResources,
+ EBADF => unreachable, // The map exists, but the area maps something that isn't a file.
+ EINVAL => return error.InvalidSyscall,
+ EIO => return error.WouldExceedMaximumResidentSetSize,
+ ENOMEM => return error.OutOfMemory,
+ ENOSYS => return error.MadviseUnavailable,
+ else => |err| return unexpectedErrno(err),
+ }
+}
diff --git a/lib/std/os/bits.zig b/lib/std/os/bits.zig
index a9d65370ad..5d1de28bad 100644
--- a/lib/std/os/bits.zig
+++ b/lib/std/os/bits.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -15,6 +15,7 @@ pub usingnamespace switch (std.Target.current.os.tag) {
.macos, .ios, .tvos, .watchos => @import("bits/darwin.zig"),
.dragonfly => @import("bits/dragonfly.zig"),
.freebsd => @import("bits/freebsd.zig"),
+ .haiku => @import("bits/haiku.zig"),
.linux => @import("bits/linux.zig"),
.netbsd => @import("bits/netbsd.zig"),
.openbsd => @import("bits/openbsd.zig"),
@@ -34,3 +35,22 @@ pub const iovec_const = extern struct {
iov_base: [*]const u8,
iov_len: usize,
};
+
+// syslog
+
+/// system is unusable
+pub const LOG_EMERG = 0;
+/// action must be taken immediately
+pub const LOG_ALERT = 1;
+/// critical conditions
+pub const LOG_CRIT = 2;
+/// error conditions
+pub const LOG_ERR = 3;
+/// warning conditions
+pub const LOG_WARNING = 4;
+/// normal but significant condition
+pub const LOG_NOTICE = 5;
+/// informational
+pub const LOG_INFO = 6;
+/// debug-level messages
+pub const LOG_DEBUG = 7;
diff --git a/lib/std/os/bits/darwin.zig b/lib/std/os/bits/darwin.zig
index 8bd40ed9a3..aca24b1c0c 100644
--- a/lib/std/os/bits/darwin.zig
+++ b/lib/std/os/bits/darwin.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -1461,7 +1461,7 @@ pub const LOCK_EX = 2;
pub const LOCK_UN = 8;
pub const LOCK_NB = 4;
-pub const nfds_t = usize;
+pub const nfds_t = u32;
pub const pollfd = extern struct {
fd: fd_t,
events: i16,
@@ -1554,3 +1554,186 @@ pub const rlimit = extern struct {
pub const SHUT_RD = 0;
pub const SHUT_WR = 1;
pub const SHUT_RDWR = 2;
+
+// Term
+pub const VEOF = 0;
+pub const VEOL = 1;
+pub const VEOL2 = 2;
+pub const VERASE = 3;
+pub const VWERASE = 4;
+pub const VKILL = 5;
+pub const VREPRINT = 6;
+pub const VINTR = 8;
+pub const VQUIT = 9;
+pub const VSUSP = 10;
+pub const VDSUSP = 11;
+pub const VSTART = 12;
+pub const VSTOP = 13;
+pub const VLNEXT = 14;
+pub const VDISCARD = 15;
+pub const VMIN = 16;
+pub const VTIME = 17;
+pub const VSTATUS = 18;
+pub const NCCS = 20; // 2 spares (7, 19)
+
+pub const IGNBRK = 0x00000001; // ignore BREAK condition
+pub const BRKINT = 0x00000002; // map BREAK to SIGINTR
+pub const IGNPAR = 0x00000004; // ignore (discard) parity errors
+pub const PARMRK = 0x00000008; // mark parity and framing errors
+pub const INPCK = 0x00000010; // enable checking of parity errors
+pub const ISTRIP = 0x00000020; // strip 8th bit off chars
+pub const INLCR = 0x00000040; // map NL into CR
+pub const IGNCR = 0x00000080; // ignore CR
+pub const ICRNL = 0x00000100; // map CR to NL (ala CRMOD)
+pub const IXON = 0x00000200; // enable output flow control
+pub const IXOFF = 0x00000400; // enable input flow control
+pub const IXANY = 0x00000800; // any char will restart after stop
+pub const IMAXBEL = 0x00002000; // ring bell on input queue full
+pub const IUTF8 = 0x00004000; // maintain state for UTF-8 VERASE
+
+pub const OPOST = 0x00000001; //enable following output processing
+pub const ONLCR = 0x00000002; // map NL to CR-NL (ala CRMOD)
+pub const OXTABS = 0x00000004; // expand tabs to spaces
+pub const ONOEOT = 0x00000008; // discard EOT's (^D) on output)
+
+pub const OCRNL = 0x00000010; // map CR to NL on output
+pub const ONOCR = 0x00000020; // no CR output at column 0
+pub const ONLRET = 0x00000040; // NL performs CR function
+pub const OFILL = 0x00000080; // use fill characters for delay
+pub const NLDLY = 0x00000300; // \n delay
+pub const TABDLY = 0x00000c04; // horizontal tab delay
+pub const CRDLY = 0x00003000; // \r delay
+pub const FFDLY = 0x00004000; // form feed delay
+pub const BSDLY = 0x00008000; // \b delay
+pub const VTDLY = 0x00010000; // vertical tab delay
+pub const OFDEL = 0x00020000; // fill is DEL, else NUL
+
+pub const NL0 = 0x00000000;
+pub const NL1 = 0x00000100;
+pub const NL2 = 0x00000200;
+pub const NL3 = 0x00000300;
+pub const TAB0 = 0x00000000;
+pub const TAB1 = 0x00000400;
+pub const TAB2 = 0x00000800;
+pub const TAB3 = 0x00000004;
+pub const CR0 = 0x00000000;
+pub const CR1 = 0x00001000;
+pub const CR2 = 0x00002000;
+pub const CR3 = 0x00003000;
+pub const FF0 = 0x00000000;
+pub const FF1 = 0x00004000;
+pub const BS0 = 0x00000000;
+pub const BS1 = 0x00008000;
+pub const VT0 = 0x00000000;
+pub const VT1 = 0x00010000;
+
+pub const CIGNORE = 0x00000001; // ignore control flags
+pub const CSIZE = 0x00000300; // character size mask
+pub const CS5 = 0x00000000; // 5 bits (pseudo)
+pub const CS6 = 0x00000100; // 6 bits
+pub const CS7 = 0x00000200; // 7 bits
+pub const CS8 = 0x00000300; // 8 bits
+pub const CSTOPB = 0x0000040; // send 2 stop bits
+pub const CREAD = 0x00000800; // enable receiver
+pub const PARENB = 0x00001000; // parity enable
+pub const PARODD = 0x00002000; // odd parity, else even
+pub const HUPCL = 0x00004000; // hang up on last close
+pub const CLOCAL = 0x00008000; // ignore modem status lines
+pub const CCTS_OFLOW = 0x00010000; // CTS flow control of output
+pub const CRTSCTS = (CCTS_OFLOW | CRTS_IFLOW);
+pub const CRTS_IFLOW = 0x00020000; // RTS flow control of input
+pub const CDTR_IFLOW = 0x00040000; // DTR flow control of input
+pub const CDSR_OFLOW = 0x00080000; // DSR flow control of output
+pub const CCAR_OFLOW = 0x00100000; // DCD flow control of output
+pub const MDMBUF = 0x00100000; // old name for CCAR_OFLOW
+
+pub const ECHOKE = 0x00000001; // visual erase for line kill
+pub const ECHOE = 0x00000002; // visually erase chars
+pub const ECHOK = 0x00000004; // echo NL after line kill
+pub const ECHO = 0x00000008; // enable echoing
+pub const ECHONL = 0x00000010; // echo NL even if ECHO is off
+pub const ECHOPRT = 0x00000020; // visual erase mode for hardcopy
+pub const ECHOCTL = 0x00000040; // echo control chars as ^(Char)
+pub const ISIG = 0x00000080; // enable signals INTR, QUIT, [D]SUSP
+pub const ICANON = 0x00000100; // canonicalize input lines
+pub const ALTWERASE = 0x00000200; // use alternate WERASE algorithm
+pub const IEXTEN = 0x00000400; // enable DISCARD and LNEXT
+pub const EXTPROC = 0x00000800; // external processing
+pub const TOSTOP = 0x00400000; // stop background jobs from output
+pub const FLUSHO = 0x00800000; // output being flushed (state)
+pub const NOKERNINFO = 0x02000000; // no kernel output from VSTATUS
+pub const PENDIN = 0x20000000; // XXX retype pending input (state)
+pub const NOFLSH = 0x80000000; // don't flush after interrupt
+
+pub const TCSANOW = 0; // make change immediate
+pub const TCSADRAIN = 1; // drain output, then change
+pub const TCSAFLUSH = 2; // drain output, flush input
+pub const TCSASOFT = 0x10; // flag - don't alter h.w. state
+pub const TCSA = extern enum(c_uint) {
+ NOW,
+ DRAIN,
+ FLUSH,
+ _,
+};
+
+pub const B0 = 0;
+pub const B50 = 50;
+pub const B75 = 75;
+pub const B110 = 110;
+pub const B134 = 134;
+pub const B150 = 150;
+pub const B200 = 200;
+pub const B300 = 300;
+pub const B600 = 600;
+pub const B1200 = 1200;
+pub const B1800 = 1800;
+pub const B2400 = 2400;
+pub const B4800 = 4800;
+pub const B9600 = 9600;
+pub const B19200 = 19200;
+pub const B38400 = 38400;
+pub const B7200 = 7200;
+pub const B14400 = 14400;
+pub const B28800 = 28800;
+pub const B57600 = 57600;
+pub const B76800 = 76800;
+pub const B115200 = 115200;
+pub const B230400 = 230400;
+pub const EXTA = 19200;
+pub const EXTB = 38400;
+
+pub const TCIFLUSH = 1;
+pub const TCOFLUSH = 2;
+pub const TCIOFLUSH = 3;
+pub const TCOOFF = 1;
+pub const TCOON = 2;
+pub const TCIOFF = 3;
+pub const TCION = 4;
+
+pub const cc_t = u8;
+pub const speed_t = u64;
+pub const tcflag_t = u64;
+
+pub const termios = extern struct {
+ iflag: tcflag_t, // input flags
+ oflag: tcflag_t, // output flags
+ cflag: tcflag_t, // control flags
+ lflag: tcflag_t, // local flags
+ cc: [NCCS]cc_t, // control chars
+ ispeed: speed_t align(8), // input speed
+ ospeed: speed_t, // output speed
+};
+
+pub const winsize = extern struct {
+ ws_row: u16,
+ ws_col: u16,
+ ws_xpixel: u16,
+ ws_ypixel: u16,
+};
+
+pub const TIOCGWINSZ = ior(0x40000000, 't', 104, @sizeOf(winsize));
+pub const IOCPARM_MASK = 0x1fff;
+
+fn ior(inout: u32, group: usize, num: usize, len: usize) usize {
+ return (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num));
+}
diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig
index 61b6b9f363..3df6eb43de 100644
--- a/lib/std/os/bits/dragonfly.zig
+++ b/lib/std/os/bits/dragonfly.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -20,6 +20,7 @@ pub const off_t = c_long;
pub const mode_t = c_uint;
pub const uid_t = u32;
pub const gid_t = u32;
+pub const suseconds_t = c_long;
pub const ENOTSUP = EOPNOTSUPP;
pub const EWOULDBLOCK = EAGAIN;
@@ -190,6 +191,13 @@ pub const timespec = extern struct {
tv_nsec: c_long,
};
+pub const timeval = extern struct {
+ /// seconds
+ tv_sec: time_t,
+ /// microseconds
+ tv_usec: suseconds_t,
+};
+
pub const CTL_UNSPEC = 0;
pub const CTL_KERN = 1;
pub const CTL_VM = 2;
diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig
index 6378dcaf8d..8529c5e3db 100644
--- a/lib/std/os/bits/freebsd.zig
+++ b/lib/std/os/bits/freebsd.zig
@@ -1,20 +1,32 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
const std = @import("../../std.zig");
+const builtin = std.builtin;
const maxInt = std.math.maxInt;
-// See https://svnweb.freebsd.org/base/head/sys/sys/_types.h?view=co
-// TODO: audit pid_t/mode_t. They should likely be i32 and u16, respectively
-pub const fd_t = c_int;
-pub const pid_t = c_int;
+pub const blksize_t = i32;
+pub const blkcnt_t = i64;
+pub const clockid_t = i32;
+pub const fsblkcnt_t = u64;
+pub const fsfilcnt_t = u64;
+pub const nlink_t = u64;
+pub const fd_t = i32;
+pub const pid_t = i32;
pub const uid_t = u32;
pub const gid_t = u32;
-pub const mode_t = c_uint;
+pub const mode_t = u16;
+pub const off_t = i64;
+pub const ino_t = u64;
+pub const dev_t = u64;
+pub const time_t = i64;
+// The signedness is not constant across different architectures.
+pub const clock_t = isize;
pub const socklen_t = u32;
+pub const suseconds_t = c_long;
/// Renamed from `kevent` to `Kevent` to avoid conflict with function name.
pub const Kevent = extern struct {
@@ -116,20 +128,17 @@ pub const msghdr_const = extern struct {
msg_flags: i32,
};
-pub const off_t = i64;
-pub const ino_t = u64;
-
pub const libc_stat = extern struct {
- dev: u64,
+ dev: dev_t,
ino: ino_t,
- nlink: usize,
+ nlink: nlink_t,
- mode: u16,
+ mode: mode_t,
__pad0: u16,
uid: uid_t,
gid: gid_t,
__pad1: u32,
- rdev: u64,
+ rdev: dev_t,
atim: timespec,
mtim: timespec,
@@ -161,6 +170,13 @@ pub const timespec = extern struct {
tv_nsec: isize,
};
+pub const timeval = extern struct {
+ /// seconds
+ tv_sec: time_t,
+ /// microseconds
+ tv_usec: suseconds_t,
+};
+
pub const dirent = extern struct {
d_fileno: usize,
d_off: i64,
@@ -553,6 +569,9 @@ pub const EV_ONESHOT = 0x0010;
/// clear event state after reporting
pub const EV_CLEAR = 0x0020;
+/// error, event data contains errno
+pub const EV_ERROR = 0x4000;
+
/// force immediate event output
/// ... with or without EV_ERROR
/// ... use KEVENT_FLAG_ERROR_EVENTS
@@ -796,16 +815,16 @@ pub const sigval = extern union {
pub const _SIG_WORDS = 4;
pub const _SIG_MAXSIG = 128;
-pub inline fn _SIG_IDX(sig: usize) usize {
+pub fn _SIG_IDX(sig: usize) callconv(.Inline) usize {
return sig - 1;
}
-pub inline fn _SIG_WORD(sig: usize) usize {
+pub fn _SIG_WORD(sig: usize) callconv(.Inline) usize {
return_SIG_IDX(sig) >> 5;
}
-pub inline fn _SIG_BIT(sig: usize) usize {
+pub fn _SIG_BIT(sig: usize) callconv(.Inline) usize {
return 1 << (_SIG_IDX(sig) & 31);
}
-pub inline fn _SIG_VALID(sig: usize) usize {
+pub fn _SIG_VALID(sig: usize) callconv(.Inline) usize {
return sig <= _SIG_MAXSIG and sig > 0;
}
@@ -815,6 +834,53 @@ pub const sigset_t = extern struct {
pub const empty_sigset = sigset_t{ .__bits = [_]u32{0} ** _SIG_WORDS };
+pub usingnamespace switch (builtin.arch) {
+ .x86_64 => struct {
+ pub const ucontext_t = extern struct {
+ sigmask: sigset_t,
+ mcontext: mcontext_t,
+ link: ?*ucontext_t,
+ stack: stack_t,
+ flags: c_int,
+ __spare__: [4]c_int,
+ };
+
+ /// XXX x86_64 specific
+ pub const mcontext_t = extern struct {
+ onstack: u64,
+ rdi: u64,
+ rsi: u64,
+ rdx: u64,
+ rcx: u64,
+ r8: u64,
+ r9: u64,
+ rax: u64,
+ rbx: u64,
+ rbp: u64,
+ r10: u64,
+ r11: u64,
+ r12: u64,
+ r13: u64,
+ r14: u64,
+ r15: u64,
+ trapno: u32,
+ fs: u16,
+ gs: u16,
+ addr: u64,
+ flags: u32,
+ es: u16,
+ ds: u16,
+ err: u64,
+ rip: u64,
+ cs: u64,
+ rflags: u64,
+ rsp: u64,
+ ss: u64,
+ };
+ },
+ else => struct {},
+};
+
pub const EPERM = 1; // Operation not permitted
pub const ENOENT = 2; // No such file or directory
pub const ESRCH = 3; // No such process
@@ -1432,3 +1498,37 @@ pub const rlimit = extern struct {
pub const SHUT_RD = 0;
pub const SHUT_WR = 1;
pub const SHUT_RDWR = 2;
+
+pub const nfds_t = u32;
+
+pub const pollfd = extern struct {
+ fd: fd_t,
+ events: i16,
+ revents: i16,
+};
+
+/// any readable data available.
+pub const POLLIN = 0x0001;
+/// OOB/Urgent readable data.
+pub const POLLPRI = 0x0002;
+/// file descriptor is writeable.
+pub const POLLOUT = 0x0004;
+/// non-OOB/URG data available.
+pub const POLLRDNORM = 0x0040;
+/// no write type differentiation.
+pub const POLLWRNORM = POLLOUT;
+/// OOB/Urgent readable data.
+pub const POLLRDBAND = 0x0080;
+/// OOB/Urgent data can be written.
+pub const POLLWRBAND = 0x0100;
+/// like POLLIN, except ignore EOF.
+pub const POLLINIGNEOF = 0x2000;
+/// some poll error occurred.
+pub const POLLERR = 0x0008;
+/// file descriptor was "hung up".
+pub const POLLHUP = 0x0010;
+/// requested events "invalid".
+pub const POLLNVAL = 0x0020;
+
+pub const POLLSTANDARD = POLLIN | POLLPRI | POLLOUT | POLLRDNORM | POLLRDBAND |
+ POLLWRBAND | POLLERR | POLLHUP | POLLNVAL;
diff --git a/lib/std/os/bits/haiku.zig b/lib/std/os/bits/haiku.zig
new file mode 100644
index 0000000000..59631fd40e
--- /dev/null
+++ b/lib/std/os/bits/haiku.zig
@@ -0,0 +1,1450 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("../../std.zig");
+const maxInt = std.math.maxInt;
+
+pub const fd_t = c_int;
+pub const pid_t = c_int;
+pub const uid_t = u32;
+pub const gid_t = u32;
+pub const mode_t = c_uint;
+
+pub const socklen_t = u32;
+
+/// Renamed from `kevent` to `Kevent` to avoid conflict with function name.
+pub const Kevent = extern struct {
+ ident: usize,
+ filter: i16,
+ flags: u16,
+ fflags: u32,
+ data: i64,
+ udata: usize,
+ // TODO ext
+};
+
+// Modes and flags for dlopen()
+// include/dlfcn.h
+
+pub const POLLIN = 0x0001;
+pub const POLLERR = 0x0004;
+pub const POLLNVAL = 0x1000;
+pub const POLLHUP = 0x0080;
+
+/// Bind function calls lazily.
+pub const RTLD_LAZY = 1;
+
+/// Bind function calls immediately.
+pub const RTLD_NOW = 2;
+
+pub const RTLD_MODEMASK = 0x3;
+
+/// Make symbols globally available.
+pub const RTLD_GLOBAL = 0x100;
+
+/// Opposite of RTLD_GLOBAL, and the default.
+pub const RTLD_LOCAL = 0;
+
+/// Trace loaded objects and exit.
+pub const RTLD_TRACE = 0x200;
+
+/// Do not remove members.
+pub const RTLD_NODELETE = 0x01000;
+
+/// Do not load if not already loaded.
+pub const RTLD_NOLOAD = 0x02000;
+
+pub const dl_phdr_info = extern struct {
+ dlpi_addr: usize,
+ dlpi_name: ?[*:0]const u8,
+ dlpi_phdr: [*]std.elf.Phdr,
+ dlpi_phnum: u16,
+};
+
+pub const Flock = extern struct {
+ l_start: off_t,
+ l_len: off_t,
+ l_pid: pid_t,
+ l_type: i16,
+ l_whence: i16,
+ l_sysid: i32,
+ __unused: [4]u8,
+};
+
+pub const msghdr = extern struct {
+ /// optional address
+ msg_name: ?*sockaddr,
+
+ /// size of address
+ msg_namelen: socklen_t,
+
+ /// scatter/gather array
+ msg_iov: [*]iovec,
+
+ /// # 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,
+};
+
+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,
+};
+
+pub const off_t = i64;
+pub const ino_t = u64;
+
+pub const nfds_t = u32;
+
+pub const pollfd = extern struct {
+ fd: i32,
+ events: i16,
+ revents: i16,
+};
+
+pub const libc_stat = extern struct {
+ dev: i32,
+ ino: u64,
+ mode: u32,
+ nlink: i32,
+ uid: i32,
+ gid: i32,
+ size: i64,
+ rdev: i32,
+ blksize: i32,
+ atim: timespec,
+ mtim: timespec,
+ ctim: timespec,
+ crtim: timespec,
+ st_type: u32,
+ blocks: i64,
+
+ pub fn atime(self: @This()) timespec {
+ return self.atim;
+ }
+ pub fn mtime(self: @This()) timespec {
+ return self.mtim;
+ }
+ pub fn ctime(self: @This()) timespec {
+ return self.ctim;
+ }
+ pub fn crtime(self: @This()) timespec {
+ return self.crtim;
+ }
+};
+
+pub const timespec = extern struct {
+ tv_sec: isize,
+ tv_nsec: isize,
+};
+
+pub const dirent = extern struct {
+ d_dev: i32,
+ d_pdev: i32,
+ d_ino: i64,
+ d_pino: i64,
+ d_reclen: u16,
+ d_name: [256]u8,
+
+ pub fn reclen(self: dirent) u16 {
+ return self.d_reclen;
+ }
+};
+
+pub const image_info = extern struct {
+ id: u32, //image_id
+ type: u32, // image_type
+ sequence: i32,
+ init_order: i32,
+ init_routine: *c_void,
+ term_routine: *c_void,
+ device: i32,
+ node: i32,
+ name: [1024]u8,
+ text: *c_void,
+ data: *c_void,
+ text_size: i32,
+ data_size: i32,
+ api_version: i32,
+ abi: i32,
+};
+
+pub const system_info = extern struct {
+ boot_time: i64,
+ cpu_count: u32,
+ max_pages: u64,
+ used_pages: u64,
+ cached_pages: u64,
+ block_cache_pages: u64,
+ ignored_pages: u64,
+ needed_memory: u64,
+ free_memory: u64,
+ max_swap_pages: u64,
+ free_swap_pages: u64,
+ page_faults: u32,
+ max_sems: u32,
+ used_sems: u32,
+ max_ports: u32,
+ used_ports: u32,
+ max_threads: u32,
+ used_threads: u32,
+ max_teams: u32,
+ used_teams: u32,
+ kernel_name: [256]u8,
+ kernel_build_date: [32]u8,
+ kernel_build_time: [32]u8,
+ kernel_version: i64,
+ abi: u32,
+};
+
+pub const in_port_t = u16;
+pub const sa_family_t = u8;
+
+pub const sockaddr = extern struct {
+ /// total length
+ len: u8,
+
+ /// address family
+ family: sa_family_t,
+
+ /// actually longer; address value
+ data: [14]u8,
+};
+
+pub const sockaddr_in = extern struct {
+ len: u8 = @sizeOf(sockaddr_in),
+ family: sa_family_t = AF_INET,
+ port: in_port_t,
+ addr: u32,
+ zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+pub const sockaddr_in6 = extern struct {
+ len: u8 = @sizeOf(sockaddr_in6),
+ family: sa_family_t = AF_INET6,
+ port: in_port_t,
+ flowinfo: u32,
+ addr: [16]u8,
+ scope_id: u32,
+};
+
+pub const sockaddr_un = extern struct {
+ len: u8 = @sizeOf(sockaddr_un),
+ family: sa_family_t = AF_UNIX,
+ path: [104]u8,
+};
+
+pub const CTL_KERN = 1;
+pub const CTL_DEBUG = 5;
+
+pub const KERN_PROC = 14; // struct: process entries
+pub const KERN_PROC_PATHNAME = 12; // path to executable
+
+pub const PATH_MAX = 1024;
+
+pub const STDIN_FILENO = 0;
+pub const STDOUT_FILENO = 1;
+pub const STDERR_FILENO = 2;
+
+pub const PROT_NONE = 0;
+pub const PROT_READ = 1;
+pub const PROT_WRITE = 2;
+pub const PROT_EXEC = 4;
+
+pub const CLOCK_REALTIME = 0;
+pub const CLOCK_VIRTUAL = 1;
+pub const CLOCK_PROF = 2;
+pub const CLOCK_MONOTONIC = 4;
+pub const CLOCK_UPTIME = 5;
+pub const CLOCK_UPTIME_PRECISE = 7;
+pub const CLOCK_UPTIME_FAST = 8;
+pub const CLOCK_REALTIME_PRECISE = 9;
+pub const CLOCK_REALTIME_FAST = 10;
+pub const CLOCK_MONOTONIC_PRECISE = 11;
+pub const CLOCK_MONOTONIC_FAST = 12;
+pub const CLOCK_SECOND = 13;
+pub const CLOCK_THREAD_CPUTIME_ID = 14;
+pub const CLOCK_PROCESS_CPUTIME_ID = 15;
+
+pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));
+pub const MAP_SHARED = 0x0001;
+pub const MAP_PRIVATE = 0x0002;
+pub const MAP_FIXED = 0x0010;
+pub const MAP_STACK = 0x0400;
+pub const MAP_NOSYNC = 0x0800;
+pub const MAP_ANON = 0x1000;
+pub const MAP_ANONYMOUS = MAP_ANON;
+pub const MAP_FILE = 0;
+
+pub const MAP_GUARD = 0x00002000;
+pub const MAP_EXCL = 0x00004000;
+pub const MAP_NOCORE = 0x00020000;
+pub const MAP_PREFAULT_READ = 0x00040000;
+pub const MAP_32BIT = 0x00080000;
+
+pub const WNOHANG = 1;
+pub const WUNTRACED = 2;
+pub const WSTOPPED = WUNTRACED;
+pub const WCONTINUED = 4;
+pub const WNOWAIT = 8;
+pub const WEXITED = 16;
+pub const WTRAPPED = 32;
+
+pub const SA_ONSTACK = 0x0001;
+pub const SA_RESTART = 0x0002;
+pub const SA_RESETHAND = 0x0004;
+pub const SA_NOCLDSTOP = 0x0008;
+pub const SA_NODEFER = 0x0010;
+pub const SA_NOCLDWAIT = 0x0020;
+pub const SA_SIGINFO = 0x0040;
+
+pub const SIGHUP = 1;
+pub const SIGINT = 2;
+pub const SIGQUIT = 3;
+pub const SIGILL = 4;
+pub const SIGTRAP = 5;
+pub const SIGABRT = 6;
+pub const SIGIOT = SIGABRT;
+pub const SIGEMT = 7;
+pub const SIGFPE = 8;
+pub const SIGKILL = 9;
+pub const SIGBUS = 10;
+pub const SIGSEGV = 11;
+pub const SIGSYS = 12;
+pub const SIGPIPE = 13;
+pub const SIGALRM = 14;
+pub const SIGTERM = 15;
+pub const SIGURG = 16;
+pub const SIGSTOP = 17;
+pub const SIGTSTP = 18;
+pub const SIGCONT = 19;
+pub const SIGCHLD = 20;
+pub const SIGTTIN = 21;
+pub const SIGTTOU = 22;
+pub const SIGIO = 23;
+pub const SIGXCPU = 24;
+pub const SIGXFSZ = 25;
+pub const SIGVTALRM = 26;
+pub const SIGPROF = 27;
+pub const SIGWINCH = 28;
+pub const SIGINFO = 29;
+pub const SIGUSR1 = 30;
+pub const SIGUSR2 = 31;
+pub const SIGTHR = 32;
+pub const SIGLWP = SIGTHR;
+pub const SIGLIBRT = 33;
+
+pub const SIGRTMIN = 65;
+pub const SIGRTMAX = 126;
+
+// access function
+pub const F_OK = 0; // test for existence of file
+pub const X_OK = 1; // test for execute or search permission
+pub const W_OK = 2; // test for write permission
+pub const R_OK = 4; // test for read permission
+
+pub const O_RDONLY = 0x0000;
+pub const O_WRONLY = 0x0001;
+pub const O_RDWR = 0x0002;
+pub const O_ACCMODE = 0x0003;
+
+pub const O_SHLOCK = 0x0010;
+pub const O_EXLOCK = 0x0020;
+
+pub const O_CREAT = 0x0200;
+pub const O_EXCL = 0x0800;
+pub const O_NOCTTY = 0x8000;
+pub const O_TRUNC = 0x0400;
+pub const O_APPEND = 0x0008;
+pub const O_NONBLOCK = 0x0004;
+pub const O_DSYNC = 0o10000;
+pub const O_SYNC = 0x0080;
+pub const O_RSYNC = 0o4010000;
+pub const O_DIRECTORY = 0x20000;
+pub const O_NOFOLLOW = 0x0100;
+pub const O_CLOEXEC = 0x00100000;
+
+pub const O_ASYNC = 0x0040;
+pub const O_DIRECT = 0x00010000;
+pub const O_NOATIME = 0o1000000;
+pub const O_PATH = 0o10000000;
+pub const O_TMPFILE = 0o20200000;
+pub const O_NDELAY = O_NONBLOCK;
+
+pub const F_DUPFD = 0;
+pub const F_GETFD = 1;
+pub const F_SETFD = 2;
+pub const F_GETFL = 3;
+pub const F_SETFL = 4;
+
+pub const F_GETOWN = 5;
+pub const F_SETOWN = 6;
+
+pub const F_GETLK = 11;
+pub const F_SETLK = 12;
+pub const F_SETLKW = 13;
+
+pub const F_RDLCK = 1;
+pub const F_WRLCK = 3;
+pub const F_UNLCK = 2;
+
+pub const LOCK_SH = 1;
+pub const LOCK_EX = 2;
+pub const LOCK_UN = 8;
+pub const LOCK_NB = 4;
+
+pub const F_SETOWN_EX = 15;
+pub const F_GETOWN_EX = 16;
+
+pub const F_GETOWNER_UIDS = 17;
+
+pub const FD_CLOEXEC = 1;
+
+pub const SEEK_SET = 0;
+pub const SEEK_CUR = 1;
+pub const SEEK_END = 2;
+
+pub const SIG_BLOCK = 1;
+pub const SIG_UNBLOCK = 2;
+pub const SIG_SETMASK = 3;
+
+pub const SOCK_STREAM = 1;
+pub const SOCK_DGRAM = 2;
+pub const SOCK_RAW = 3;
+pub const SOCK_RDM = 4;
+pub const SOCK_SEQPACKET = 5;
+
+pub const SOCK_CLOEXEC = 0x10000000;
+pub const SOCK_NONBLOCK = 0x20000000;
+
+pub const SO_DEBUG = 0x00000001;
+pub const SO_ACCEPTCONN = 0x00000002;
+pub const SO_REUSEADDR = 0x00000004;
+pub const SO_KEEPALIVE = 0x00000008;
+pub const SO_DONTROUTE = 0x00000010;
+pub const SO_BROADCAST = 0x00000020;
+pub const SO_USELOOPBACK = 0x00000040;
+pub const SO_LINGER = 0x00000080;
+pub const SO_OOBINLINE = 0x00000100;
+pub const SO_REUSEPORT = 0x00000200;
+pub const SO_TIMESTAMP = 0x00000400;
+pub const SO_NOSIGPIPE = 0x00000800;
+pub const SO_ACCEPTFILTER = 0x00001000;
+pub const SO_BINTIME = 0x00002000;
+pub const SO_NO_OFFLOAD = 0x00004000;
+pub const SO_NO_DDP = 0x00008000;
+pub const SO_REUSEPORT_LB = 0x00010000;
+
+pub const SO_SNDBUF = 0x1001;
+pub const SO_RCVBUF = 0x1002;
+pub const SO_SNDLOWAT = 0x1003;
+pub const SO_RCVLOWAT = 0x1004;
+pub const SO_SNDTIMEO = 0x1005;
+pub const SO_RCVTIMEO = 0x1006;
+pub const SO_ERROR = 0x1007;
+pub const SO_TYPE = 0x1008;
+pub const SO_LABEL = 0x1009;
+pub const SO_PEERLABEL = 0x1010;
+pub const SO_LISTENQLIMIT = 0x1011;
+pub const SO_LISTENQLEN = 0x1012;
+pub const SO_LISTENINCQLEN = 0x1013;
+pub const SO_SETFIB = 0x1014;
+pub const SO_USER_COOKIE = 0x1015;
+pub const SO_PROTOCOL = 0x1016;
+pub const SO_PROTOTYPE = SO_PROTOCOL;
+pub const SO_TS_CLOCK = 0x1017;
+pub const SO_MAX_PACING_RATE = 0x1018;
+pub const SO_DOMAIN = 0x1019;
+
+pub const SOL_SOCKET = 0xffff;
+
+pub const PF_UNSPEC = AF_UNSPEC;
+pub const PF_LOCAL = AF_LOCAL;
+pub const PF_UNIX = PF_LOCAL;
+pub const PF_INET = AF_INET;
+pub const PF_IMPLINK = AF_IMPLINK;
+pub const PF_PUP = AF_PUP;
+pub const PF_CHAOS = AF_CHAOS;
+pub const PF_NETBIOS = AF_NETBIOS;
+pub const PF_ISO = AF_ISO;
+pub const PF_OSI = AF_ISO;
+pub const PF_ECMA = AF_ECMA;
+pub const PF_DATAKIT = AF_DATAKIT;
+pub const PF_CCITT = AF_CCITT;
+pub const PF_DECnet = AF_DECnet;
+pub const PF_DLI = AF_DLI;
+pub const PF_LAT = AF_LAT;
+pub const PF_HYLINK = AF_HYLINK;
+pub const PF_APPLETALK = AF_APPLETALK;
+pub const PF_ROUTE = AF_ROUTE;
+pub const PF_LINK = AF_LINK;
+pub const PF_XTP = pseudo_AF_XTP;
+pub const PF_COIP = AF_COIP;
+pub const PF_CNT = AF_CNT;
+pub const PF_SIP = AF_SIP;
+pub const PF_IPX = AF_IPX;
+pub const PF_RTIP = pseudo_AF_RTIP;
+pub const PF_PIP = psuedo_AF_PIP;
+pub const PF_ISDN = AF_ISDN;
+pub const PF_KEY = pseudo_AF_KEY;
+pub const PF_INET6 = pseudo_AF_INET6;
+pub const PF_NATM = AF_NATM;
+pub const PF_ATM = AF_ATM;
+pub const PF_NETGRAPH = AF_NETGRAPH;
+pub const PF_SLOW = AF_SLOW;
+pub const PF_SCLUSTER = AF_SCLUSTER;
+pub const PF_ARP = AF_ARP;
+pub const PF_BLUETOOTH = AF_BLUETOOTH;
+pub const PF_IEEE80211 = AF_IEEE80211;
+pub const PF_INET_SDP = AF_INET_SDP;
+pub const PF_INET6_SDP = AF_INET6_SDP;
+pub const PF_MAX = AF_MAX;
+
+pub const AF_UNSPEC = 0;
+pub const AF_UNIX = 1;
+pub const AF_LOCAL = AF_UNIX;
+pub const AF_FILE = AF_LOCAL;
+pub const AF_INET = 2;
+pub const AF_IMPLINK = 3;
+pub const AF_PUP = 4;
+pub const AF_CHAOS = 5;
+pub const AF_NETBIOS = 6;
+pub const AF_ISO = 7;
+pub const AF_OSI = AF_ISO;
+pub const AF_ECMA = 8;
+pub const AF_DATAKIT = 9;
+pub const AF_CCITT = 10;
+pub const AF_SNA = 11;
+pub const AF_DECnet = 12;
+pub const AF_DLI = 13;
+pub const AF_LAT = 14;
+pub const AF_HYLINK = 15;
+pub const AF_APPLETALK = 16;
+pub const AF_ROUTE = 17;
+pub const AF_LINK = 18;
+pub const pseudo_AF_XTP = 19;
+pub const AF_COIP = 20;
+pub const AF_CNT = 21;
+pub const pseudo_AF_RTIP = 22;
+pub const AF_IPX = 23;
+pub const AF_SIP = 24;
+pub const pseudo_AF_PIP = 25;
+pub const AF_ISDN = 26;
+pub const AF_E164 = AF_ISDN;
+pub const pseudo_AF_KEY = 27;
+pub const AF_INET6 = 28;
+pub const AF_NATM = 29;
+pub const AF_ATM = 30;
+pub const pseudo_AF_HDRCMPLT = 31;
+pub const AF_NETGRAPH = 32;
+pub const AF_SLOW = 33;
+pub const AF_SCLUSTER = 34;
+pub const AF_ARP = 35;
+pub const AF_BLUETOOTH = 36;
+pub const AF_IEEE80211 = 37;
+pub const AF_INET_SDP = 40;
+pub const AF_INET6_SDP = 42;
+pub const AF_MAX = 42;
+
+pub const DT_UNKNOWN = 0;
+pub const DT_FIFO = 1;
+pub const DT_CHR = 2;
+pub const DT_DIR = 4;
+pub const DT_BLK = 6;
+pub const DT_REG = 8;
+pub const DT_LNK = 10;
+pub const DT_SOCK = 12;
+pub const DT_WHT = 14;
+
+/// add event to kq (implies enable)
+pub const EV_ADD = 0x0001;
+
+/// delete event from kq
+pub const EV_DELETE = 0x0002;
+
+/// enable event
+pub const EV_ENABLE = 0x0004;
+
+/// disable event (not reported)
+pub const EV_DISABLE = 0x0008;
+
+/// only report one occurrence
+pub const EV_ONESHOT = 0x0010;
+
+/// clear event state after reporting
+pub const EV_CLEAR = 0x0020;
+
+/// force immediate event output
+/// ... with or without EV_ERROR
+/// ... use KEVENT_FLAG_ERROR_EVENTS
+/// on syscalls supporting flags
+pub const EV_RECEIPT = 0x0040;
+
+/// disable event after reporting
+pub const EV_DISPATCH = 0x0080;
+
+pub const EVFILT_READ = -1;
+pub const EVFILT_WRITE = -2;
+
+/// attached to aio requests
+pub const EVFILT_AIO = -3;
+
+/// attached to vnodes
+pub const EVFILT_VNODE = -4;
+
+/// attached to struct proc
+pub const EVFILT_PROC = -5;
+
+/// attached to struct proc
+pub const EVFILT_SIGNAL = -6;
+
+/// timers
+pub const EVFILT_TIMER = -7;
+
+/// Process descriptors
+pub const EVFILT_PROCDESC = -8;
+
+/// Filesystem events
+pub const EVFILT_FS = -9;
+
+pub const EVFILT_LIO = -10;
+
+/// User events
+pub const EVFILT_USER = -11;
+
+/// Sendfile events
+pub const EVFILT_SENDFILE = -12;
+
+pub const EVFILT_EMPTY = -13;
+
+/// On input, NOTE_TRIGGER causes the event to be triggered for output.
+pub const NOTE_TRIGGER = 0x01000000;
+
+/// ignore input fflags
+pub const NOTE_FFNOP = 0x00000000;
+
+/// and fflags
+pub const NOTE_FFAND = 0x40000000;
+
+/// or fflags
+pub const NOTE_FFOR = 0x80000000;
+
+/// copy fflags
+pub const NOTE_FFCOPY = 0xc0000000;
+
+/// mask for operations
+pub const NOTE_FFCTRLMASK = 0xc0000000;
+pub const NOTE_FFLAGSMASK = 0x00ffffff;
+
+/// low water mark
+pub const NOTE_LOWAT = 0x00000001;
+
+/// behave like poll()
+pub const NOTE_FILE_POLL = 0x00000002;
+
+/// vnode was removed
+pub const NOTE_DELETE = 0x00000001;
+
+/// data contents changed
+pub const NOTE_WRITE = 0x00000002;
+
+/// size increased
+pub const NOTE_EXTEND = 0x00000004;
+
+/// attributes changed
+pub const NOTE_ATTRIB = 0x00000008;
+
+/// link count changed
+pub const NOTE_LINK = 0x00000010;
+
+/// vnode was renamed
+pub const NOTE_RENAME = 0x00000020;
+
+/// vnode access was revoked
+pub const NOTE_REVOKE = 0x00000040;
+
+/// vnode was opened
+pub const NOTE_OPEN = 0x00000080;
+
+/// file closed, fd did not allow write
+pub const NOTE_CLOSE = 0x00000100;
+
+/// file closed, fd did allow write
+pub const NOTE_CLOSE_WRITE = 0x00000200;
+
+/// file was read
+pub const NOTE_READ = 0x00000400;
+
+/// process exited
+pub const NOTE_EXIT = 0x80000000;
+
+/// process forked
+pub const NOTE_FORK = 0x40000000;
+
+/// process exec'd
+pub const NOTE_EXEC = 0x20000000;
+
+/// mask for signal & exit status
+pub const NOTE_PDATAMASK = 0x000fffff;
+pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK);
+
+/// data is seconds
+pub const NOTE_SECONDS = 0x00000001;
+
+/// data is milliseconds
+pub const NOTE_MSECONDS = 0x00000002;
+
+/// data is microseconds
+pub const NOTE_USECONDS = 0x00000004;
+
+/// data is nanoseconds
+pub const NOTE_NSECONDS = 0x00000008;
+
+/// timeout is absolute
+pub const NOTE_ABSTIME = 0x00000010;
+
+pub const TIOCEXCL = 0x2000740d;
+pub const TIOCNXCL = 0x2000740e;
+pub const TIOCSCTTY = 0x20007461;
+pub const TIOCGPGRP = 0x40047477;
+pub const TIOCSPGRP = 0x80047476;
+pub const TIOCOUTQ = 0x40047473;
+pub const TIOCSTI = 0x80017472;
+pub const TIOCGWINSZ = 0x40087468;
+pub const TIOCSWINSZ = 0x80087467;
+pub const TIOCMGET = 0x4004746a;
+pub const TIOCMBIS = 0x8004746c;
+pub const TIOCMBIC = 0x8004746b;
+pub const TIOCMSET = 0x8004746d;
+pub const FIONREAD = 0x4004667f;
+pub const TIOCCONS = 0x80047462;
+pub const TIOCPKT = 0x80047470;
+pub const FIONBIO = 0x8004667e;
+pub const TIOCNOTTY = 0x20007471;
+pub const TIOCSETD = 0x8004741b;
+pub const TIOCGETD = 0x4004741a;
+pub const TIOCSBRK = 0x2000747b;
+pub const TIOCCBRK = 0x2000747a;
+pub const TIOCGSID = 0x40047463;
+pub const TIOCGPTN = 0x4004740f;
+pub const TIOCSIG = 0x2004745f;
+
+pub fn WEXITSTATUS(s: u32) u32 {
+ return (s & 0xff00) >> 8;
+}
+pub fn WTERMSIG(s: u32) u32 {
+ return s & 0x7f;
+}
+pub fn WSTOPSIG(s: u32) u32 {
+ return WEXITSTATUS(s);
+}
+pub fn WIFEXITED(s: u32) bool {
+ return WTERMSIG(s) == 0;
+}
+pub fn WIFSTOPPED(s: u32) bool {
+ return @intCast(u16, (((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00;
+}
+pub fn WIFSIGNALED(s: u32) bool {
+ return (s & 0xffff) -% 1 < 0xff;
+}
+
+pub const winsize = extern struct {
+ ws_row: u16,
+ ws_col: u16,
+ ws_xpixel: u16,
+ ws_ypixel: u16,
+};
+
+const NSIG = 32;
+
+pub const SIG_ERR = @intToPtr(fn (i32) callconv(.C) void, maxInt(usize));
+pub const SIG_DFL = @intToPtr(fn (i32) callconv(.C) void, 0);
+pub const SIG_IGN = @intToPtr(fn (i32) callconv(.C) void, 1);
+
+/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
+pub const Sigaction = extern struct {
+ /// signal handler
+ __sigaction_u: extern union {
+ __sa_handler: fn (i32) callconv(.C) void,
+ __sa_sigaction: fn (i32, *__siginfo, usize) callconv(.C) void,
+ },
+
+ /// see signal options
+ sa_flags: u32,
+
+ /// signal mask to apply
+ sa_mask: sigset_t,
+};
+
+pub const _SIG_WORDS = 4;
+pub const _SIG_MAXSIG = 128;
+
+pub inline fn _SIG_IDX(sig: usize) usize {
+ return sig - 1;
+}
+pub inline fn _SIG_WORD(sig: usize) usize {
+ return_SIG_IDX(sig) >> 5;
+}
+pub inline fn _SIG_BIT(sig: usize) usize {
+ return 1 << (_SIG_IDX(sig) & 31);
+}
+pub inline fn _SIG_VALID(sig: usize) usize {
+ return sig <= _SIG_MAXSIG and sig > 0;
+}
+
+pub const sigset_t = extern struct {
+ __bits: [_SIG_WORDS]u32,
+};
+
+pub const EPERM = 1; // Operation not permitted
+pub const ENOENT = 2; // No such file or directory
+pub const ESRCH = 3; // No such process
+pub const EINTR = 4; // Interrupted system call
+pub const EIO = 5; // Input/output error
+pub const ENXIO = 6; // Device not configured
+pub const E2BIG = 7; // Argument list too long
+pub const ENOEXEC = 8; // Exec format error
+pub const EBADF = 9; // Bad file descriptor
+pub const ECHILD = 10; // No child processes
+pub const EDEADLK = 11; // Resource deadlock avoided
+// 11 was EAGAIN
+pub const ENOMEM = 12; // Cannot allocate memory
+pub const EACCES = 13; // Permission denied
+pub const EFAULT = 14; // Bad address
+pub const ENOTBLK = 15; // Block device required
+pub const EBUSY = 16; // Device busy
+pub const EEXIST = 17; // File exists
+pub const EXDEV = 18; // Cross-device link
+pub const ENODEV = 19; // Operation not supported by device
+pub const ENOTDIR = 20; // Not a directory
+pub const EISDIR = 21; // Is a directory
+pub const EINVAL = 22; // Invalid argument
+pub const ENFILE = 23; // Too many open files in system
+pub const EMFILE = 24; // Too many open files
+pub const ENOTTY = 25; // Inappropriate ioctl for device
+pub const ETXTBSY = 26; // Text file busy
+pub const EFBIG = 27; // File too large
+pub const ENOSPC = 28; // No space left on device
+pub const ESPIPE = 29; // Illegal seek
+pub const EROFS = 30; // Read-only filesystem
+pub const EMLINK = 31; // Too many links
+pub const EPIPE = 32; // Broken pipe
+
+// math software
+pub const EDOM = 33; // Numerical argument out of domain
+pub const ERANGE = 34; // Result too large
+
+// non-blocking and interrupt i/o
+pub const EAGAIN = 35; // Resource temporarily unavailable
+pub const EWOULDBLOCK = EAGAIN; // Operation would block
+pub const EINPROGRESS = 36; // Operation now in progress
+pub const EALREADY = 37; // Operation already in progress
+
+// ipc/network software -- argument errors
+pub const ENOTSOCK = 38; // Socket operation on non-socket
+pub const EDESTADDRREQ = 39; // Destination address required
+pub const EMSGSIZE = 40; // Message too long
+pub const EPROTOTYPE = 41; // Protocol wrong type for socket
+pub const ENOPROTOOPT = 42; // Protocol not available
+pub const EPROTONOSUPPORT = 43; // Protocol not supported
+pub const ESOCKTNOSUPPORT = 44; // Socket type not supported
+pub const EOPNOTSUPP = 45; // Operation not supported
+pub const ENOTSUP = EOPNOTSUPP; // Operation not supported
+pub const EPFNOSUPPORT = 46; // Protocol family not supported
+pub const EAFNOSUPPORT = 47; // Address family not supported by protocol family
+pub const EADDRINUSE = 48; // Address already in use
+pub const EADDRNOTAVAIL = 49; // Can't assign requested address
+
+// ipc/network software -- operational errors
+pub const ENETDOWN = 50; // Network is down
+pub const ENETUNREACH = 51; // Network is unreachable
+pub const ENETRESET = 52; // Network dropped connection on reset
+pub const ECONNABORTED = 53; // Software caused connection abort
+pub const ECONNRESET = 54; // Connection reset by peer
+pub const ENOBUFS = 55; // No buffer space available
+pub const EISCONN = 56; // Socket is already connected
+pub const ENOTCONN = 57; // Socket is not connected
+pub const ESHUTDOWN = 58; // Can't send after socket shutdown
+pub const ETOOMANYREFS = 59; // Too many references: can't splice
+pub const ETIMEDOUT = 60; // Operation timed out
+pub const ECONNREFUSED = 61; // Connection refused
+
+pub const ELOOP = 62; // Too many levels of symbolic links
+pub const ENAMETOOLONG = 63; // File name too long
+
+// should be rearranged
+pub const EHOSTDOWN = 64; // Host is down
+pub const EHOSTUNREACH = 65; // No route to host
+pub const ENOTEMPTY = 66; // Directory not empty
+
+// quotas & mush
+pub const EPROCLIM = 67; // Too many processes
+pub const EUSERS = 68; // Too many users
+pub const EDQUOT = 69; // Disc quota exceeded
+
+// Network File System
+pub const ESTALE = 70; // Stale NFS file handle
+pub const EREMOTE = 71; // Too many levels of remote in path
+pub const EBADRPC = 72; // RPC struct is bad
+pub const ERPCMISMATCH = 73; // RPC version wrong
+pub const EPROGUNAVAIL = 74; // RPC prog. not avail
+pub const EPROGMISMATCH = 75; // Program version wrong
+pub const EPROCUNAVAIL = 76; // Bad procedure for program
+
+pub const ENOLCK = 77; // No locks available
+pub const ENOSYS = 78; // Function not implemented
+
+pub const EFTYPE = 79; // Inappropriate file type or format
+pub const EAUTH = 80; // Authentication error
+pub const ENEEDAUTH = 81; // Need authenticator
+pub const EIDRM = 82; // Identifier removed
+pub const ENOMSG = 83; // No message of desired type
+pub const EOVERFLOW = 84; // Value too large to be stored in data type
+pub const ECANCELED = 85; // Operation canceled
+pub const EILSEQ = 86; // Illegal byte sequence
+pub const ENOATTR = 87; // Attribute not found
+
+pub const EDOOFUS = 88; // Programming error
+
+pub const EBADMSG = 89; // Bad message
+pub const EMULTIHOP = 90; // Multihop attempted
+pub const ENOLINK = 91; // Link has been severed
+pub const EPROTO = 92; // Protocol error
+
+pub const ENOTCAPABLE = 93; // Capabilities insufficient
+pub const ECAPMODE = 94; // Not permitted in capability mode
+pub const ENOTRECOVERABLE = 95; // State not recoverable
+pub const EOWNERDEAD = 96; // Previous owner died
+
+pub const ELAST = 96; // Must be equal largest errno
+
+pub const MINSIGSTKSZ = switch (builtin.arch) {
+ .i386, .x86_64 => 2048,
+ .arm, .aarch64 => 4096,
+ else => @compileError("MINSIGSTKSZ not defined for this architecture"),
+};
+pub const SIGSTKSZ = MINSIGSTKSZ + 32768;
+
+pub const SS_ONSTACK = 1;
+pub const SS_DISABLE = 4;
+
+pub const stack_t = extern struct {
+ ss_sp: [*]u8,
+ ss_size: isize,
+ ss_flags: i32,
+};
+
+pub const S_IFMT = 0o170000;
+
+pub const S_IFIFO = 0o010000;
+pub const S_IFCHR = 0o020000;
+pub const S_IFDIR = 0o040000;
+pub const S_IFBLK = 0o060000;
+pub const S_IFREG = 0o100000;
+pub const S_IFLNK = 0o120000;
+pub const S_IFSOCK = 0o140000;
+pub const S_IFWHT = 0o160000;
+
+pub const S_ISUID = 0o4000;
+pub const S_ISGID = 0o2000;
+pub const S_ISVTX = 0o1000;
+pub const S_IRWXU = 0o700;
+pub const S_IRUSR = 0o400;
+pub const S_IWUSR = 0o200;
+pub const S_IXUSR = 0o100;
+pub const S_IRWXG = 0o070;
+pub const S_IRGRP = 0o040;
+pub const S_IWGRP = 0o020;
+pub const S_IXGRP = 0o010;
+pub const S_IRWXO = 0o007;
+pub const S_IROTH = 0o004;
+pub const S_IWOTH = 0o002;
+pub const S_IXOTH = 0o001;
+
+pub fn S_ISFIFO(m: u32) bool {
+ return m & S_IFMT == S_IFIFO;
+}
+
+pub fn S_ISCHR(m: u32) bool {
+ return m & S_IFMT == S_IFCHR;
+}
+
+pub fn S_ISDIR(m: u32) bool {
+ return m & S_IFMT == S_IFDIR;
+}
+
+pub fn S_ISBLK(m: u32) bool {
+ return m & S_IFMT == S_IFBLK;
+}
+
+pub fn S_ISREG(m: u32) bool {
+ return m & S_IFMT == S_IFREG;
+}
+
+pub fn S_ISLNK(m: u32) bool {
+ return m & S_IFMT == S_IFLNK;
+}
+
+pub fn S_ISSOCK(m: u32) bool {
+ return m & S_IFMT == S_IFSOCK;
+}
+
+pub fn S_IWHT(m: u32) bool {
+ return m & S_IFMT == S_IFWHT;
+}
+
+pub const HOST_NAME_MAX = 255;
+
+/// Magic value that specify the use of the current working directory
+/// to determine the target of relative file paths in the openat() and
+/// similar syscalls.
+pub const AT_FDCWD = -100;
+
+/// Check access using effective user and group ID
+pub const AT_EACCESS = 0x0100;
+
+/// Do not follow symbolic links
+pub const AT_SYMLINK_NOFOLLOW = 0x0200;
+
+/// Follow symbolic link
+pub const AT_SYMLINK_FOLLOW = 0x0400;
+
+/// Remove directory instead of file
+pub const AT_REMOVEDIR = 0x0800;
+
+pub const addrinfo = extern struct {
+ flags: i32,
+ family: i32,
+ socktype: i32,
+ protocol: i32,
+ addrlen: socklen_t,
+ canonname: ?[*:0]u8,
+ addr: ?*sockaddr,
+ next: ?*addrinfo,
+};
+
+/// Fail if not under dirfd
+pub const AT_BENEATH = 0x1000;
+
+/// dummy for IP
+pub const IPPROTO_IP = 0;
+
+/// control message protocol
+pub const IPPROTO_ICMP = 1;
+
+/// tcp
+pub const IPPROTO_TCP = 6;
+
+/// user datagram protocol
+pub const IPPROTO_UDP = 17;
+
+/// IP6 header
+pub const IPPROTO_IPV6 = 41;
+
+/// raw IP packet
+pub const IPPROTO_RAW = 255;
+
+/// IP6 hop-by-hop options
+pub const IPPROTO_HOPOPTS = 0;
+
+/// group mgmt protocol
+pub const IPPROTO_IGMP = 2;
+
+/// gateway^2 (deprecated)
+pub const IPPROTO_GGP = 3;
+
+/// IPv4 encapsulation
+pub const IPPROTO_IPV4 = 4;
+
+/// for compatibility
+pub const IPPROTO_IPIP = IPPROTO_IPV4;
+
+/// Stream protocol II
+pub const IPPROTO_ST = 7;
+
+/// exterior gateway protocol
+pub const IPPROTO_EGP = 8;
+
+/// private interior gateway
+pub const IPPROTO_PIGP = 9;
+
+/// BBN RCC Monitoring
+pub const IPPROTO_RCCMON = 10;
+
+/// network voice protocol
+pub const IPPROTO_NVPII = 11;
+
+/// pup
+pub const IPPROTO_PUP = 12;
+
+/// Argus
+pub const IPPROTO_ARGUS = 13;
+
+/// EMCON
+pub const IPPROTO_EMCON = 14;
+
+/// Cross Net Debugger
+pub const IPPROTO_XNET = 15;
+
+/// Chaos
+pub const IPPROTO_CHAOS = 16;
+
+/// Multiplexing
+pub const IPPROTO_MUX = 18;
+
+/// DCN Measurement Subsystems
+pub const IPPROTO_MEAS = 19;
+
+/// Host Monitoring
+pub const IPPROTO_HMP = 20;
+
+/// Packet Radio Measurement
+pub const IPPROTO_PRM = 21;
+
+/// xns idp
+pub const IPPROTO_IDP = 22;
+
+/// Trunk-1
+pub const IPPROTO_TRUNK1 = 23;
+
+/// Trunk-2
+pub const IPPROTO_TRUNK2 = 24;
+
+/// Leaf-1
+pub const IPPROTO_LEAF1 = 25;
+
+/// Leaf-2
+pub const IPPROTO_LEAF2 = 26;
+
+/// Reliable Data
+pub const IPPROTO_RDP = 27;
+
+/// Reliable Transaction
+pub const IPPROTO_IRTP = 28;
+
+/// tp-4 w/ class negotiation
+pub const IPPROTO_TP = 29;
+
+/// Bulk Data Transfer
+pub const IPPROTO_BLT = 30;
+
+/// Network Services
+pub const IPPROTO_NSP = 31;
+
+/// Merit Internodal
+pub const IPPROTO_INP = 32;
+
+/// Datagram Congestion Control Protocol
+pub const IPPROTO_DCCP = 33;
+
+/// Third Party Connect
+pub const IPPROTO_3PC = 34;
+
+/// InterDomain Policy Routing
+pub const IPPROTO_IDPR = 35;
+
+/// XTP
+pub const IPPROTO_XTP = 36;
+
+/// Datagram Delivery
+pub const IPPROTO_DDP = 37;
+
+/// Control Message Transport
+pub const IPPROTO_CMTP = 38;
+
+/// TP++ Transport
+pub const IPPROTO_TPXX = 39;
+
+/// IL transport protocol
+pub const IPPROTO_IL = 40;
+
+/// Source Demand Routing
+pub const IPPROTO_SDRP = 42;
+
+/// IP6 routing header
+pub const IPPROTO_ROUTING = 43;
+
+/// IP6 fragmentation header
+pub const IPPROTO_FRAGMENT = 44;
+
+/// InterDomain Routing
+pub const IPPROTO_IDRP = 45;
+
+/// resource reservation
+pub const IPPROTO_RSVP = 46;
+
+/// General Routing Encap.
+pub const IPPROTO_GRE = 47;
+
+/// Mobile Host Routing
+pub const IPPROTO_MHRP = 48;
+
+/// BHA
+pub const IPPROTO_BHA = 49;
+
+/// IP6 Encap Sec. Payload
+pub const IPPROTO_ESP = 50;
+
+/// IP6 Auth Header
+pub const IPPROTO_AH = 51;
+
+/// Integ. Net Layer Security
+pub const IPPROTO_INLSP = 52;
+
+/// IP with encryption
+pub const IPPROTO_SWIPE = 53;
+
+/// Next Hop Resolution
+pub const IPPROTO_NHRP = 54;
+
+/// IP Mobility
+pub const IPPROTO_MOBILE = 55;
+
+/// Transport Layer Security
+pub const IPPROTO_TLSP = 56;
+
+/// SKIP
+pub const IPPROTO_SKIP = 57;
+
+/// ICMP6
+pub const IPPROTO_ICMPV6 = 58;
+
+/// IP6 no next header
+pub const IPPROTO_NONE = 59;
+
+/// IP6 destination option
+pub const IPPROTO_DSTOPTS = 60;
+
+/// any host internal protocol
+pub const IPPROTO_AHIP = 61;
+
+/// CFTP
+pub const IPPROTO_CFTP = 62;
+
+/// "hello" routing protocol
+pub const IPPROTO_HELLO = 63;
+
+/// SATNET/Backroom EXPAK
+pub const IPPROTO_SATEXPAK = 64;
+
+/// Kryptolan
+pub const IPPROTO_KRYPTOLAN = 65;
+
+/// Remote Virtual Disk
+pub const IPPROTO_RVD = 66;
+
+/// Pluribus Packet Core
+pub const IPPROTO_IPPC = 67;
+
+/// Any distributed FS
+pub const IPPROTO_ADFS = 68;
+
+/// Satnet Monitoring
+pub const IPPROTO_SATMON = 69;
+
+/// VISA Protocol
+pub const IPPROTO_VISA = 70;
+
+/// Packet Core Utility
+pub const IPPROTO_IPCV = 71;
+
+/// Comp. Prot. Net. Executive
+pub const IPPROTO_CPNX = 72;
+
+/// Comp. Prot. HeartBeat
+pub const IPPROTO_CPHB = 73;
+
+/// Wang Span Network
+pub const IPPROTO_WSN = 74;
+
+/// Packet Video Protocol
+pub const IPPROTO_PVP = 75;
+
+/// BackRoom SATNET Monitoring
+pub const IPPROTO_BRSATMON = 76;
+
+/// Sun net disk proto (temp.)
+pub const IPPROTO_ND = 77;
+
+/// WIDEBAND Monitoring
+pub const IPPROTO_WBMON = 78;
+
+/// WIDEBAND EXPAK
+pub const IPPROTO_WBEXPAK = 79;
+
+/// ISO cnlp
+pub const IPPROTO_EON = 80;
+
+/// VMTP
+pub const IPPROTO_VMTP = 81;
+
+/// Secure VMTP
+pub const IPPROTO_SVMTP = 82;
+
+/// Banyon VINES
+pub const IPPROTO_VINES = 83;
+
+/// TTP
+pub const IPPROTO_TTP = 84;
+
+/// NSFNET-IGP
+pub const IPPROTO_IGP = 85;
+
+/// dissimilar gateway prot.
+pub const IPPROTO_DGP = 86;
+
+/// TCF
+pub const IPPROTO_TCF = 87;
+
+/// Cisco/GXS IGRP
+pub const IPPROTO_IGRP = 88;
+
+/// OSPFIGP
+pub const IPPROTO_OSPFIGP = 89;
+
+/// Strite RPC protocol
+pub const IPPROTO_SRPC = 90;
+
+/// Locus Address Resoloution
+pub const IPPROTO_LARP = 91;
+
+/// Multicast Transport
+pub const IPPROTO_MTP = 92;
+
+/// AX.25 Frames
+pub const IPPROTO_AX25 = 93;
+
+/// IP encapsulated in IP
+pub const IPPROTO_IPEIP = 94;
+
+/// Mobile Int.ing control
+pub const IPPROTO_MICP = 95;
+
+/// Semaphore Comm. security
+pub const IPPROTO_SCCSP = 96;
+
+/// Ethernet IP encapsulation
+pub const IPPROTO_ETHERIP = 97;
+
+/// encapsulation header
+pub const IPPROTO_ENCAP = 98;
+
+/// any private encr. scheme
+pub const IPPROTO_APES = 99;
+
+/// GMTP
+pub const IPPROTO_GMTP = 100;
+
+/// payload compression (IPComp)
+pub const IPPROTO_IPCOMP = 108;
+
+/// SCTP
+pub const IPPROTO_SCTP = 132;
+
+/// IPv6 Mobility Header
+pub const IPPROTO_MH = 135;
+
+/// UDP-Lite
+pub const IPPROTO_UDPLITE = 136;
+
+/// IP6 Host Identity Protocol
+pub const IPPROTO_HIP = 139;
+
+/// IP6 Shim6 Protocol
+pub const IPPROTO_SHIM6 = 140;
+
+/// Protocol Independent Mcast
+pub const IPPROTO_PIM = 103;
+
+/// CARP
+pub const IPPROTO_CARP = 112;
+
+/// PGM
+pub const IPPROTO_PGM = 113;
+
+/// MPLS-in-IP
+pub const IPPROTO_MPLS = 137;
+
+/// PFSYNC
+pub const IPPROTO_PFSYNC = 240;
+
+/// Reserved
+pub const IPPROTO_RESERVED_253 = 253;
+
+/// Reserved
+pub const IPPROTO_RESERVED_254 = 254;
+
+pub const rlimit_resource = extern enum(c_int) {
+ CPU = 0,
+ FSIZE = 1,
+ DATA = 2,
+ STACK = 3,
+ CORE = 4,
+ RSS = 5,
+ MEMLOCK = 6,
+ NPROC = 7,
+ NOFILE = 8,
+ SBSIZE = 9,
+ VMEM = 10,
+ AS = 10,
+ NPTS = 11,
+ SWAP = 12,
+ KQUEUES = 13,
+ UMTXP = 14,
+
+ _,
+};
+
+pub const rlim_t = i64;
+
+/// No limit
+pub const RLIM_INFINITY: rlim_t = (1 << 63) - 1;
+
+pub const RLIM_SAVED_MAX = RLIM_INFINITY;
+pub const RLIM_SAVED_CUR = RLIM_INFINITY;
+
+pub const rlimit = extern struct {
+ /// Soft limit
+ cur: rlim_t,
+ /// Hard limit
+ max: rlim_t,
+};
+
+pub const SHUT_RD = 0;
+pub const SHUT_WR = 1;
+pub const SHUT_RDWR = 2;
+
+// TODO fill out if needed
+pub const directory_which = extern enum(c_int) {
+ B_USER_SETTINGS_DIRECTORY = 0xbbe,
+
+ _,
+};
diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig
index e86a08e861..8d3d5c49a3 100644
--- a/lib/std/os/bits/linux.zig
+++ b/lib/std/os/bits/linux.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -533,6 +533,59 @@ pub const SOL_TLS = 282;
pub const SOMAXCONN = 128;
+pub const IP_TOS = 1;
+pub const IP_TTL = 2;
+pub const IP_HDRINCL = 3;
+pub const IP_OPTIONS = 4;
+pub const IP_ROUTER_ALERT = 5;
+pub const IP_RECVOPTS = 6;
+pub const IP_RETOPTS = 7;
+pub const IP_PKTINFO = 8;
+pub const IP_PKTOPTIONS = 9;
+pub const IP_PMTUDISC = 10;
+pub const IP_MTU_DISCOVER = 10;
+pub const IP_RECVERR = 11;
+pub const IP_RECVTTL = 12;
+pub const IP_RECVTOS = 13;
+pub const IP_MTU = 14;
+pub const IP_FREEBIND = 15;
+pub const IP_IPSEC_POLICY = 16;
+pub const IP_XFRM_POLICY = 17;
+pub const IP_PASSSEC = 18;
+pub const IP_TRANSPARENT = 19;
+pub const IP_ORIGDSTADDR = 20;
+pub const IP_RECVORIGDSTADDR = IP_ORIGDSTADDR;
+pub const IP_MINTTL = 21;
+pub const IP_NODEFRAG = 22;
+pub const IP_CHECKSUM = 23;
+pub const IP_BIND_ADDRESS_NO_PORT = 24;
+pub const IP_RECVFRAGSIZE = 25;
+pub const IP_MULTICAST_IF = 32;
+pub const IP_MULTICAST_TTL = 33;
+pub const IP_MULTICAST_LOOP = 34;
+pub const IP_ADD_MEMBERSHIP = 35;
+pub const IP_DROP_MEMBERSHIP = 36;
+pub const IP_UNBLOCK_SOURCE = 37;
+pub const IP_BLOCK_SOURCE = 38;
+pub const IP_ADD_SOURCE_MEMBERSHIP = 39;
+pub const IP_DROP_SOURCE_MEMBERSHIP = 40;
+pub const IP_MSFILTER = 41;
+pub const IP_MULTICAST_ALL = 49;
+pub const IP_UNICAST_IF = 50;
+
+pub const IP_RECVRETOPTS = IP_RETOPTS;
+
+pub const IP_PMTUDISC_DONT = 0;
+pub const IP_PMTUDISC_WANT = 1;
+pub const IP_PMTUDISC_DO = 2;
+pub const IP_PMTUDISC_PROBE = 3;
+pub const IP_PMTUDISC_INTERFACE = 4;
+pub const IP_PMTUDISC_OMIT = 5;
+
+pub const IP_DEFAULT_MULTICAST_TTL = 1;
+pub const IP_DEFAULT_MULTICAST_LOOP = 1;
+pub const IP_MAX_MEMBERSHIPS = 20;
+
pub const MSG_OOB = 0x0001;
pub const MSG_PEEK = 0x0002;
pub const MSG_DONTROUTE = 0x0004;
@@ -1284,6 +1337,9 @@ pub const IORING_SETUP_CLAMP = 1 << 4;
/// attach to existing wq
pub const IORING_SETUP_ATTACH_WQ = 1 << 5;
+/// start with ring disabled
+pub const IORING_SETUP_R_DISABLED = 1 << 6;
+
pub const io_sqring_offsets = extern struct {
/// offset of ring head
head: u32,
@@ -1430,6 +1486,11 @@ pub const io_uring_cqe = extern struct {
flags: u32,
};
+// io_uring_cqe.flags
+
+/// If set, the upper 16 bits are the buffer ID
+pub const IORING_CQE_F_BUFFER = 1 << 0;
+
pub const IORING_OFF_SQ_RING = 0;
pub const IORING_OFF_CQ_RING = 0x8000000;
pub const IORING_OFF_SQES = 0x10000000;
@@ -1439,7 +1500,7 @@ pub const IORING_ENTER_GETEVENTS = 1 << 0;
pub const IORING_ENTER_SQ_WAKEUP = 1 << 1;
// io_uring_register opcodes and arguments
-pub const IORING_REGISTER = extern enum(u32) {
+pub const IORING_REGISTER = extern enum(u8) {
REGISTER_BUFFERS,
UNREGISTER_BUFFERS,
REGISTER_FILES,
@@ -1451,11 +1512,13 @@ pub const IORING_REGISTER = extern enum(u32) {
REGISTER_PROBE,
REGISTER_PERSONALITY,
UNREGISTER_PERSONALITY,
+ REGISTER_RESTRICTIONS,
+ REGISTER_ENABLE_RINGS,
_,
};
-pub const io_uring_files_update = struct {
+pub const io_uring_files_update = extern struct {
offset: u32,
resv: u32,
fds: u64,
@@ -1463,7 +1526,7 @@ pub const io_uring_files_update = struct {
pub const IO_URING_OP_SUPPORTED = 1 << 0;
-pub const io_uring_probe_op = struct {
+pub const io_uring_probe_op = extern struct {
op: IORING_OP,
resv: u8,
@@ -1474,7 +1537,7 @@ pub const io_uring_probe_op = struct {
resv2: u32,
};
-pub const io_uring_probe = struct {
+pub const io_uring_probe = extern struct {
/// last opcode supported
last_op: IORING_OP,
@@ -1487,6 +1550,39 @@ pub const io_uring_probe = struct {
// Followed by up to `ops_len` io_uring_probe_op structures
};
+pub const io_uring_restriction = extern struct {
+ opcode: u16,
+ arg: extern union {
+ /// IORING_RESTRICTION_REGISTER_OP
+ register_op: IORING_REGISTER,
+
+ /// IORING_RESTRICTION_SQE_OP
+ sqe_op: IORING_OP,
+
+ /// IORING_RESTRICTION_SQE_FLAGS_*
+ sqe_flags: u8,
+ },
+ resv: u8,
+ resv2: u32[3],
+};
+
+/// io_uring_restriction->opcode values
+pub const IORING_RESTRICTION = extern enum(u8) {
+ /// Allow an io_uring_register(2) opcode
+ REGISTER_OP = 0,
+
+ /// Allow an sqe opcode
+ SQE_OP = 1,
+
+ /// Allow sqe flags
+ SQE_FLAGS_ALLOWED = 2,
+
+ /// Require sqe flags (these flags must be set on each submission)
+ SQE_FLAGS_REQUIRED = 3,
+
+ _,
+};
+
pub const utsname = extern struct {
sysname: [64:0]u8,
nodename: [64:0]u8,
@@ -1837,6 +1933,120 @@ pub const tcflag_t = u32;
pub const NCCS = 32;
+pub const B0 = 0o0000000;
+pub const B50 = 0o0000001;
+pub const B75 = 0o0000002;
+pub const B110 = 0o0000003;
+pub const B134 = 0o0000004;
+pub const B150 = 0o0000005;
+pub const B200 = 0o0000006;
+pub const B300 = 0o0000007;
+pub const B600 = 0o0000010;
+pub const B1200 = 0o0000011;
+pub const B1800 = 0o0000012;
+pub const B2400 = 0o0000013;
+pub const B4800 = 0o0000014;
+pub const B9600 = 0o0000015;
+pub const B19200 = 0o0000016;
+pub const B38400 = 0o0000017;
+pub const BOTHER = 0o0010000;
+pub const B57600 = 0o0010001;
+pub const B115200 = 0o0010002;
+pub const B230400 = 0o0010003;
+pub const B460800 = 0o0010004;
+pub const B500000 = 0o0010005;
+pub const B576000 = 0o0010006;
+pub const B921600 = 0o0010007;
+pub const B1000000 = 0o0010010;
+pub const B1152000 = 0o0010011;
+pub const B1500000 = 0o0010012;
+pub const B2000000 = 0o0010013;
+pub const B2500000 = 0o0010014;
+pub const B3000000 = 0o0010015;
+pub const B3500000 = 0o0010016;
+pub const B4000000 = 0o0010017;
+
+pub usingnamespace switch (builtin.arch) {
+ .powerpc, .powerpc64, .powerpc64le => struct {
+ pub const VINTR = 0;
+ pub const VQUIT = 1;
+ pub const VERASE = 2;
+ pub const VKILL = 3;
+ pub const VEOF = 4;
+ pub const VMIN = 5;
+ pub const VEOL = 6;
+ pub const VTIME = 7;
+ pub const VEOL2 = 8;
+ pub const VSWTC = 9;
+ pub const VWERASE = 10;
+ pub const VREPRINT = 11;
+ pub const VSUSP = 12;
+ pub const VSTART = 13;
+ pub const VSTOP = 14;
+ pub const VLNEXT = 15;
+ pub const VDISCARD = 16;
+ },
+ .sparc, .sparcv9 => struct {
+ pub const VINTR = 0;
+ pub const VQUIT = 1;
+ pub const VERASE = 2;
+ pub const VKILL = 3;
+ pub const VEOF = 4;
+ pub const VEOL = 5;
+ pub const VEOL2 = 6;
+ pub const VSWTC = 7;
+ pub const VSTART = 8;
+ pub const VSTOP = 9;
+ pub const VSUSP = 10;
+ pub const VDSUSP = 11;
+ pub const VREPRINT = 12;
+ pub const VDISCARD = 13;
+ pub const VWERASE = 14;
+ pub const VLNEXT = 15;
+ pub const VMIN = VEOF;
+ pub const VTIME = VEOL;
+ },
+ .mips, .mipsel, .mips64, .mips64el => struct {
+ pub const VINTR = 0;
+ pub const VQUIT = 1;
+ pub const VERASE = 2;
+ pub const VKILL = 3;
+ pub const VMIN = 4;
+ pub const VTIME = 5;
+ pub const VEOL2 = 6;
+ pub const VSWTC = 7;
+ pub const VSWTCH = 7;
+ pub const VSTART = 8;
+ pub const VSTOP = 9;
+ pub const VSUSP = 10;
+ pub const VREPRINT = 12;
+ pub const VDISCARD = 13;
+ pub const VWERASE = 14;
+ pub const VLNEXT = 15;
+ pub const VEOF = 16;
+ pub const VEOL = 17;
+ },
+ else => struct {
+ pub const VINTR = 0;
+ pub const VQUIT = 1;
+ pub const VERASE = 2;
+ pub const VKILL = 3;
+ pub const VEOF = 4;
+ pub const VTIME = 5;
+ pub const VMIN = 6;
+ pub const VSWTC = 7;
+ pub const VSTART = 8;
+ pub const VSTOP = 9;
+ pub const VSUSP = 10;
+ pub const VEOL = 11;
+ pub const VREPRINT = 12;
+ pub const VDISCARD = 13;
+ pub const VWERASE = 14;
+ pub const VLNEXT = 15;
+ pub const VEOL2 = 16;
+ },
+};
+
pub const IGNBRK = 1;
pub const BRKINT = 2;
pub const IGNPAR = 4;
@@ -2012,3 +2222,25 @@ pub const rlimit = extern struct {
/// Hard limit
max: rlim_t,
};
+
+pub const MADV_NORMAL = 0;
+pub const MADV_RANDOM = 1;
+pub const MADV_SEQUENTIAL = 2;
+pub const MADV_WILLNEED = 3;
+pub const MADV_DONTNEED = 4;
+pub const MADV_FREE = 8;
+pub const MADV_REMOVE = 9;
+pub const MADV_DONTFORK = 10;
+pub const MADV_DOFORK = 11;
+pub const MADV_MERGEABLE = 12;
+pub const MADV_UNMERGEABLE = 13;
+pub const MADV_HUGEPAGE = 14;
+pub const MADV_NOHUGEPAGE = 15;
+pub const MADV_DONTDUMP = 16;
+pub const MADV_DODUMP = 17;
+pub const MADV_WIPEONFORK = 18;
+pub const MADV_KEEPONFORK = 19;
+pub const MADV_COLD = 20;
+pub const MADV_PAGEOUT = 21;
+pub const MADV_HWPOISON = 100;
+pub const MADV_SOFT_OFFLINE = 101;
diff --git a/lib/std/os/bits/linux/arm-eabi.zig b/lib/std/os/bits/linux/arm-eabi.zig
index d89e75ccd6..5673cb952b 100644
--- a/lib/std/os/bits/linux/arm-eabi.zig
+++ b/lib/std/os/bits/linux/arm-eabi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -411,6 +411,8 @@ pub const SYS = extern enum(usize) {
openat2 = 437,
pidfd_getfd = 438,
faccessat2 = 439,
+ process_madvise = 440,
+ epoll_pwait2 = 441,
breakpoint = 0x0f0001,
cacheflush = 0x0f0002,
diff --git a/lib/std/os/bits/linux/arm64.zig b/lib/std/os/bits/linux/arm64.zig
index 22955aa896..a069b6adf1 100644
--- a/lib/std/os/bits/linux/arm64.zig
+++ b/lib/std/os/bits/linux/arm64.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -312,6 +312,8 @@ pub const SYS = extern enum(usize) {
openat2 = 437,
pidfd_getfd = 438,
faccessat2 = 439,
+ process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/linux/errno-generic.zig b/lib/std/os/bits/linux/errno-generic.zig
index a99f20a1a8..f55aa3698e 100644
--- a/lib/std/os/bits/linux/errno-generic.zig
+++ b/lib/std/os/bits/linux/errno-generic.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/bits/linux/errno-mips.zig b/lib/std/os/bits/linux/errno-mips.zig
index 1258863086..2c74fa6f8c 100644
--- a/lib/std/os/bits/linux/errno-mips.zig
+++ b/lib/std/os/bits/linux/errno-mips.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/bits/linux/i386.zig b/lib/std/os/bits/linux/i386.zig
index 92b21727c1..7ef34eb96b 100644
--- a/lib/std/os/bits/linux/i386.zig
+++ b/lib/std/os/bits/linux/i386.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -447,6 +447,8 @@ pub const SYS = extern enum(usize) {
openat2 = 437,
pidfd_getfd = 438,
faccessat2 = 439,
+ process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/linux/mips.zig b/lib/std/os/bits/linux/mips.zig
index cfd9c7adce..412a1f48be 100644
--- a/lib/std/os/bits/linux/mips.zig
+++ b/lib/std/os/bits/linux/mips.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -430,6 +430,7 @@ pub const SYS = extern enum(usize) {
pidfd_getfd = Linux + 438,
faccessat2 = Linux + 439,
process_madvise = Linux + 440,
+ epoll_pwait2 = Linux + 441,
_,
};
diff --git a/lib/std/os/bits/linux/netlink.zig b/lib/std/os/bits/linux/netlink.zig
index 3e75733b9a..72596cb1ab 100644
--- a/lib/std/os/bits/linux/netlink.zig
+++ b/lib/std/os/bits/linux/netlink.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/bits/linux/powerpc64.zig b/lib/std/os/bits/linux/powerpc64.zig
index 4313a609e4..e0e9347aa1 100644
--- a/lib/std/os/bits/linux/powerpc64.zig
+++ b/lib/std/os/bits/linux/powerpc64.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -408,6 +408,8 @@ pub const SYS = extern enum(usize) {
openat2 = 437,
pidfd_getfd = 438,
faccessat2 = 439,
+ process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/linux/prctl.zig b/lib/std/os/bits/linux/prctl.zig
index 6a601784ef..151acf4e71 100644
--- a/lib/std/os/bits/linux/prctl.zig
+++ b/lib/std/os/bits/linux/prctl.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/bits/linux/riscv64.zig b/lib/std/os/bits/linux/riscv64.zig
index a61b56db38..0cbdea415c 100644
--- a/lib/std/os/bits/linux/riscv64.zig
+++ b/lib/std/os/bits/linux/riscv64.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -309,6 +309,8 @@ pub const SYS = extern enum(usize) {
openat2 = 437,
pidfd_getfd = 438,
faccessat2 = 439,
+ process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/linux/securebits.zig b/lib/std/os/bits/linux/securebits.zig
index 0086a694d9..374f7c9f02 100644
--- a/lib/std/os/bits/linux/securebits.zig
+++ b/lib/std/os/bits/linux/securebits.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/bits/linux/sparc64.zig b/lib/std/os/bits/linux/sparc64.zig
index d4c65de36d..5644256a95 100644
--- a/lib/std/os/bits/linux/sparc64.zig
+++ b/lib/std/os/bits/linux/sparc64.zig
@@ -386,6 +386,8 @@ pub const SYS = extern enum(usize) {
openat2 = 437,
pidfd_getfd = 438,
faccessat2 = 439,
+ process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/linux/x86_64.zig b/lib/std/os/bits/linux/x86_64.zig
index 665c8f6eba..52fee679c5 100644
--- a/lib/std/os/bits/linux/x86_64.zig
+++ b/lib/std/os/bits/linux/x86_64.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -374,6 +374,8 @@ pub const SYS = extern enum(usize) {
openat2 = 437,
pidfd_getfd = 438,
faccessat2 = 439,
+ process_madvise = 440,
+ epoll_pwait2 = 441,
_,
};
diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig
index be25284b73..57ae70ddbf 100644
--- a/lib/std/os/bits/netbsd.zig
+++ b/lib/std/os/bits/netbsd.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -22,6 +22,7 @@ pub const socklen_t = u32;
pub const time_t = i64;
pub const uid_t = u32;
pub const lwpid_t = i32;
+pub const suseconds_t = c_int;
/// Renamed from `kevent` to `Kevent` to avoid conflict with function name.
pub const Kevent = extern struct {
@@ -190,6 +191,13 @@ pub const timespec = extern struct {
tv_nsec: isize,
};
+pub const timeval = extern struct {
+ /// seconds
+ tv_sec: time_t,
+ /// microseconds
+ tv_usec: suseconds_t,
+};
+
pub const MAXNAMLEN = 511;
pub const dirent = extern struct {
@@ -788,16 +796,16 @@ pub const _ksiginfo = extern struct {
pub const _SIG_WORDS = 4;
pub const _SIG_MAXSIG = 128;
-pub inline fn _SIG_IDX(sig: usize) usize {
+pub fn _SIG_IDX(sig: usize) callconv(.Inline) usize {
return sig - 1;
}
-pub inline fn _SIG_WORD(sig: usize) usize {
+pub fn _SIG_WORD(sig: usize) callconv(.Inline) usize {
return_SIG_IDX(sig) >> 5;
}
-pub inline fn _SIG_BIT(sig: usize) usize {
+pub fn _SIG_BIT(sig: usize) callconv(.Inline) usize {
return 1 << (_SIG_IDX(sig) & 31);
}
-pub inline fn _SIG_VALID(sig: usize) usize {
+pub fn _SIG_VALID(sig: usize) callconv(.Inline) usize {
return sig <= _SIG_MAXSIG and sig > 0;
}
@@ -828,13 +836,15 @@ pub const ucontext_t = extern struct {
sigmask: sigset_t,
stack: stack_t,
mcontext: mcontext_t,
- __pad: [switch (builtin.arch) {
- .i386 => 4,
- .mips, .mipsel, .mips64, .mips64el => 14,
- .arm, .armeb, .thumb, .thumbeb => 1,
- .sparc, .sparcel, .sparcv9 => if (@sizeOf(usize) == 4) 43 else 8,
- else => 0,
- }]u32,
+ __pad: [
+ switch (builtin.arch) {
+ .i386 => 4,
+ .mips, .mipsel, .mips64, .mips64el => 14,
+ .arm, .armeb, .thumb, .thumbeb => 1,
+ .sparc, .sparcel, .sparcv9 => if (@sizeOf(usize) == 4) 43 else 8,
+ else => 0,
+ }
+ ]u32,
};
pub const EPERM = 1; // Operation not permitted
diff --git a/lib/std/os/bits/openbsd.zig b/lib/std/os/bits/openbsd.zig
index 2aae0cef09..8d1e74ec20 100644
--- a/lib/std/os/bits/openbsd.zig
+++ b/lib/std/os/bits/openbsd.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -781,11 +781,17 @@ pub const siginfo_t = extern struct {
data: extern union {
proc: extern struct {
pid: pid_t,
- uid: uid_t,
- value: sigval,
- utime: clock_t,
- stime: clock_t,
- status: c_int,
+ pdata: extern union {
+ kill: extern struct {
+ uid: uid_t,
+ value: sigval,
+ },
+ cld: extern struct {
+ utime: clock_t,
+ stime: clock_t,
+ status: c_int,
+ },
+ },
},
fault: extern struct {
addr: ?*c_void,
@@ -803,6 +809,60 @@ comptime {
std.debug.assert(@sizeOf(siginfo_t) == 136);
}
+pub usingnamespace switch (builtin.arch) {
+ .x86_64 => struct {
+ pub const ucontext_t = extern struct {
+ sc_rdi: c_long,
+ sc_rsi: c_long,
+ sc_rdx: c_long,
+ sc_rcx: c_long,
+ sc_r8: c_long,
+ sc_r9: c_long,
+ sc_r10: c_long,
+ sc_r11: c_long,
+ sc_r12: c_long,
+ sc_r13: c_long,
+ sc_r14: c_long,
+ sc_r15: c_long,
+ sc_rbp: c_long,
+ sc_rbx: c_long,
+ sc_rax: c_long,
+ sc_gs: c_long,
+ sc_fs: c_long,
+ sc_es: c_long,
+ sc_ds: c_long,
+ sc_trapno: c_long,
+ sc_err: c_long,
+ sc_rip: c_long,
+ sc_cs: c_long,
+ sc_rflags: c_long,
+ sc_rsp: c_long,
+ sc_ss: c_long,
+
+ sc_fpstate: fxsave64,
+ __sc_unused: c_int,
+ sc_mask: c_int,
+ sc_cookie: c_long,
+ };
+
+ pub const fxsave64 = packed struct {
+ fx_fcw: u16,
+ fx_fsw: u16,
+ fx_ftw: u8,
+ fx_unused1: u8,
+ fx_fop: u16,
+ fx_rip: u64,
+ fx_rdp: u64,
+ fx_mxcsr: u32,
+ fx_mxcsr_mask: u32,
+ fx_st: [8][2]u64,
+ fx_xmm: [16][2]u64,
+ fx_unused3: [96]u8,
+ };
+ },
+ else => struct {},
+};
+
pub const sigset_t = c_uint;
pub const empty_sigset: sigset_t = 0;
@@ -1149,3 +1209,23 @@ pub const rlimit = extern struct {
pub const SHUT_RD = 0;
pub const SHUT_WR = 1;
pub const SHUT_RDWR = 2;
+
+pub const nfds_t = c_uint;
+
+pub const pollfd = extern struct {
+ fd: fd_t,
+ events: c_short,
+ revents: c_short,
+};
+
+pub const POLLIN = 0x0001;
+pub const POLLPRI = 0x0002;
+pub const POLLOUT = 0x0004;
+pub const POLLERR = 0x0008;
+pub const POLLHUP = 0x0010;
+pub const POLLNVAL = 0x0020;
+pub const POLLRDNORM = 0x0040;
+pub const POLLNORM = POLLRDNORM;
+pub const POLLWRNORM = POLLOUT;
+pub const POLLRDBAND = 0x0080;
+pub const POLLWRBAND = 0x0100;
diff --git a/lib/std/os/bits/wasi.zig b/lib/std/os/bits/wasi.zig
index 07275fc229..8b2f5c3351 100644
--- a/lib/std/os/bits/wasi.zig
+++ b/lib/std/os/bits/wasi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/bits/windows.zig b/lib/std/os/bits/windows.zig
index dda57208f8..28a6a251f8 100644
--- a/lib/std/os/bits/windows.zig
+++ b/lib/std/os/bits/windows.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -243,6 +243,7 @@ pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP;
pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6;
pub const IPPROTO_RM = ws2_32.IPPROTO_RM;
+pub const nfds_t = c_ulong;
pub const pollfd = ws2_32.pollfd;
pub const POLLRDNORM = ws2_32.POLLRDNORM;
diff --git a/lib/std/os/darwin.zig b/lib/std/os/darwin.zig
index 1bd983398b..87a9ed12ac 100644
--- a/lib/std/os/darwin.zig
+++ b/lib/std/os/darwin.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/dragonfly.zig b/lib/std/os/dragonfly.zig
index a713a009ad..572b470239 100644
--- a/lib/std/os/dragonfly.zig
+++ b/lib/std/os/dragonfly.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/freebsd.zig b/lib/std/os/freebsd.zig
index a713a009ad..572b470239 100644
--- a/lib/std/os/freebsd.zig
+++ b/lib/std/os/freebsd.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/io/out_stream.zig b/lib/std/os/haiku.zig
index c937ccf16a..a713a009ad 100644
--- a/lib/std/io/out_stream.zig
+++ b/lib/std/os/haiku.zig
@@ -3,5 +3,6 @@
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
-/// Deprecated: use `std.io.writer.Writer`
-pub const OutStream = @import("./writer.zig").Writer;
+const std = @import("../std.zig");
+pub usingnamespace std.c;
+pub usingnamespace @import("bits.zig");
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
index f840f6a255..035cdabe63 100644
--- a/lib/std/os/linux.zig
+++ b/lib/std/os/linux.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -126,7 +126,7 @@ pub fn fork() usize {
/// It is advised to avoid this function and use clone instead, because
/// the compiler is not aware of how vfork affects control flow and you may
/// see different results in optimized builds.
-pub inline fn vfork() usize {
+pub fn vfork() callconv(.Inline) usize {
return @call(.{ .modifier = .always_inline }, syscall0, .{.vfork});
}
@@ -634,6 +634,37 @@ pub fn tgkill(tgid: pid_t, tid: pid_t, sig: i32) usize {
return syscall2(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig)));
}
+pub fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) usize {
+ if (@hasField(SYS, "link")) {
+ return syscall3(
+ .link,
+ @ptrToInt(oldpath),
+ @ptrToInt(newpath),
+ @bitCast(usize, @as(isize, flags)),
+ );
+ } else {
+ return syscall5(
+ .linkat,
+ @bitCast(usize, @as(isize, AT_FDCWD)),
+ @ptrToInt(oldpath),
+ @bitCast(usize, @as(isize, AT_FDCWD)),
+ @ptrToInt(newpath),
+ @bitCast(usize, @as(isize, flags)),
+ );
+ }
+}
+
+pub fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: i32) usize {
+ return syscall5(
+ .linkat,
+ @bitCast(usize, @as(isize, oldfd)),
+ @ptrToInt(oldpath),
+ @bitCast(usize, @as(isize, newfd)),
+ @ptrToInt(newpath),
+ @bitCast(usize, @as(isize, flags)),
+ );
+}
+
pub fn unlink(path: [*:0]const u8) usize {
if (@hasField(SYS, "unlink")) {
return syscall1(.unlink, @ptrToInt(path));
@@ -745,33 +776,33 @@ pub fn setregid(rgid: gid_t, egid: gid_t) usize {
pub fn getuid() uid_t {
if (@hasField(SYS, "getuid32")) {
- return @as(uid_t, syscall0(.getuid32));
+ return @intCast(uid_t, syscall0(.getuid32));
} else {
- return @as(uid_t, syscall0(.getuid));
+ return @intCast(uid_t, syscall0(.getuid));
}
}
pub fn getgid() gid_t {
if (@hasField(SYS, "getgid32")) {
- return @as(gid_t, syscall0(.getgid32));
+ return @intCast(gid_t, syscall0(.getgid32));
} else {
- return @as(gid_t, syscall0(.getgid));
+ return @intCast(gid_t, syscall0(.getgid));
}
}
pub fn geteuid() uid_t {
if (@hasField(SYS, "geteuid32")) {
- return @as(uid_t, syscall0(.geteuid32));
+ return @intCast(uid_t, syscall0(.geteuid32));
} else {
- return @as(uid_t, syscall0(.geteuid));
+ return @intCast(uid_t, syscall0(.geteuid));
}
}
pub fn getegid() gid_t {
if (@hasField(SYS, "getegid32")) {
- return @as(gid_t, syscall0(.getegid32));
+ return @intCast(gid_t, syscall0(.getegid32));
} else {
- return @as(gid_t, syscall0(.getegid));
+ return @intCast(gid_t, syscall0(.getegid));
}
}
@@ -1351,7 +1382,11 @@ pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit,
);
}
-test "" {
+pub fn madvise(address: [*]u8, len: usize, advice: u32) usize {
+ return syscall3(.madvise, @ptrToInt(address), len, advice);
+}
+
+test {
if (builtin.os.tag == .linux) {
_ = @import("linux/test.zig");
}
diff --git a/lib/std/os/linux/arm-eabi.zig b/lib/std/os/linux/arm-eabi.zig
index a72799a26a..bac7048615 100644
--- a/lib/std/os/linux/arm-eabi.zig
+++ b/lib/std/os/linux/arm-eabi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig
index 6727cbce8e..dd5c3ef3af 100644
--- a/lib/std/os/linux/arm64.zig
+++ b/lib/std/os/linux/arm64.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/bpf.zig b/lib/std/os/linux/bpf.zig
index 44c938feb8..0d7e0a19ed 100644
--- a/lib/std/os/linux/bpf.zig
+++ b/lib/std/os/linux/bpf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/bpf/btf.zig b/lib/std/os/linux/bpf/btf.zig
index 5338994aff..b28f65945a 100644
--- a/lib/std/os/linux/bpf/btf.zig
+++ b/lib/std/os/linux/bpf/btf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/bpf/btf_ext.zig b/lib/std/os/linux/bpf/btf_ext.zig
index ce412fdf4e..ca713f1910 100644
--- a/lib/std/os/linux/bpf/btf_ext.zig
+++ b/lib/std/os/linux/bpf/btf_ext.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/bpf/helpers.zig b/lib/std/os/linux/bpf/helpers.zig
index 9228e1f1fd..c6f0bc0b5e 100644
--- a/lib/std/os/linux/bpf/helpers.zig
+++ b/lib/std/os/linux/bpf/helpers.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/bpf/kern.zig b/lib/std/os/linux/bpf/kern.zig
index a2e9d36aa1..d1b4347d85 100644
--- a/lib/std/os/linux/bpf/kern.zig
+++ b/lib/std/os/linux/bpf/kern.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/i386.zig b/lib/std/os/linux/i386.zig
index ed5bc88f0f..c1ac6938fb 100644
--- a/lib/std/os/linux/i386.zig
+++ b/lib/std/os/linux/i386.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig
index 0fdc7651d2..b47d4c7b32 100644
--- a/lib/std/os/linux/io_uring.zig
+++ b/lib/std/os/linux/io_uring.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -28,7 +28,7 @@ pub const IO_Uring = struct {
/// call on how many entries the submission and completion queues will ultimately have,
/// see https://github.com/torvalds/linux/blob/v5.8/fs/io_uring.c#L8027-L8050.
/// Matches the interface of io_uring_queue_init() in liburing.
- pub fn init(entries: u12, flags: u32) !IO_Uring {
+ pub fn init(entries: u13, flags: u32) !IO_Uring {
var params = mem.zeroInit(io_uring_params, .{
.flags = flags,
.sq_thread_idle = 1000,
@@ -39,17 +39,15 @@ pub const IO_Uring = struct {
/// A powerful way to setup an io_uring, if you want to tweak io_uring_params such as submission
/// queue thread cpu affinity or thread idle timeout (the kernel and our default is 1 second).
/// `params` is passed by reference because the kernel needs to modify the parameters.
- /// You may only set the `flags`, `sq_thread_cpu` and `sq_thread_idle` parameters.
- /// Every other parameter belongs to the kernel and must be zeroed.
/// Matches the interface of io_uring_queue_init_params() in liburing.
- pub fn init_params(entries: u12, p: *io_uring_params) !IO_Uring {
+ pub fn init_params(entries: u13, p: *io_uring_params) !IO_Uring {
if (entries == 0) return error.EntriesZero;
if (!std.math.isPowerOfTwo(entries)) return error.EntriesNotPowerOfTwo;
assert(p.sq_entries == 0);
- assert(p.cq_entries == 0);
+ assert(p.cq_entries == 0 or p.flags & linux.IORING_SETUP_CQSIZE != 0);
assert(p.features == 0);
- assert(p.wq_fd == 0);
+ assert(p.wq_fd == 0 or p.flags & linux.IORING_SETUP_ATTACH_WQ != 0);
assert(p.resv[0] == 0);
assert(p.resv[1] == 0);
assert(p.resv[2] == 0);
@@ -558,6 +556,22 @@ pub const IO_Uring = struct {
return sqe;
}
+ /// Queues (but does not submit) an SQE to perform an `fallocate(2)`.
+ /// Returns a pointer to the SQE.
+ pub fn fallocate(
+ self: *IO_Uring,
+ user_data: u64,
+ fd: os.fd_t,
+ mode: i32,
+ offset: u64,
+ len: u64,
+ ) !*io_uring_sqe {
+ const sqe = try self.get_sqe();
+ io_uring_prep_fallocate(sqe, fd, mode, offset, len);
+ sqe.user_data = user_data;
+ return sqe;
+ }
+
/// Registers an array of file descriptors.
/// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must
/// retrieve a reference to the file, and once I/O has completed the file reference must be
@@ -896,6 +910,30 @@ pub fn io_uring_prep_timeout_remove(sqe: *io_uring_sqe, timeout_user_data: u64,
};
}
+pub fn io_uring_prep_fallocate(
+ sqe: *io_uring_sqe,
+ fd: os.fd_t,
+ mode: i32,
+ offset: u64,
+ len: u64,
+) void {
+ sqe.* = .{
+ .opcode = .FALLOCATE,
+ .flags = 0,
+ .ioprio = 0,
+ .fd = fd,
+ .off = offset,
+ .addr = len,
+ .len = @intCast(u32, mode),
+ .rw_flags = 0,
+ .user_data = 0,
+ .buf_index = 0,
+ .personality = 0,
+ .splice_fd_in = 0,
+ .__pad2 = [2]u64{ 0, 0 },
+ };
+}
+
test "structs/offsets/entries" {
if (builtin.os.tag != .linux) return error.SkipZigTest;
@@ -1378,11 +1416,10 @@ test "timeout_remove" {
// Timeout remove operations set the fd to -1, which results in EBADF before EINVAL.
// We use IORING_FEAT_RW_CUR_POS as a safety check here to make sure we are at least pre-5.6.
// We don't want to skip this test for newer kernels.
- if (
- cqe_timeout.user_data == 0x99999999 and
+ if (cqe_timeout.user_data == 0x99999999 and
cqe_timeout.res == -linux.EBADF and
- (ring.features & linux.IORING_FEAT_RW_CUR_POS) == 0
- ) {
+ (ring.features & linux.IORING_FEAT_RW_CUR_POS) == 0)
+ {
return error.SkipZigTest;
}
testing.expectEqual(linux.io_uring_cqe{
@@ -1398,3 +1435,47 @@ test "timeout_remove" {
.flags = 0,
}, cqe_timeout_remove);
}
+
+test "fallocate" {
+ if (builtin.os.tag != .linux) return error.SkipZigTest;
+
+ var ring = IO_Uring.init(1, 0) catch |err| switch (err) {
+ error.SystemOutdated => return error.SkipZigTest,
+ error.PermissionDenied => return error.SkipZigTest,
+ else => return err,
+ };
+ defer ring.deinit();
+
+ const path = "test_io_uring_fallocate";
+ const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 });
+ defer file.close();
+ defer std.fs.cwd().deleteFile(path) catch {};
+
+ testing.expectEqual(@as(u64, 0), (try file.stat()).size);
+
+ const len: u64 = 65536;
+ const sqe = try ring.fallocate(0xaaaaaaaa, file.handle, 0, 0, len);
+ testing.expectEqual(linux.IORING_OP.FALLOCATE, sqe.opcode);
+ testing.expectEqual(file.handle, sqe.fd);
+ testing.expectEqual(@as(u32, 1), try ring.submit());
+
+ const cqe = try ring.copy_cqe();
+ switch (-cqe.res) {
+ 0 => {},
+ // This kernel's io_uring does not yet implement fallocate():
+ linux.EINVAL => return error.SkipZigTest,
+ // This kernel does not implement fallocate():
+ linux.ENOSYS => return error.SkipZigTest,
+ // The filesystem containing the file referred to by fd does not support this operation;
+ // or the mode is not supported by the filesystem containing the file referred to by fd:
+ linux.EOPNOTSUPP => return error.SkipZigTest,
+ else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),
+ }
+ testing.expectEqual(linux.io_uring_cqe{
+ .user_data = 0xaaaaaaaa,
+ .res = 0,
+ .flags = 0,
+ }, cqe);
+
+ testing.expectEqual(len, (try file.stat()).size);
+}
diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig
index ad673e06f9..2622628533 100644
--- a/lib/std/os/linux/mips.zig
+++ b/lib/std/os/linux/mips.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig
index 7252000f24..567ad2bc1f 100644
--- a/lib/std/os/linux/powerpc64.zig
+++ b/lib/std/os/linux/powerpc64.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig
index 034340d0b3..d58e080407 100644
--- a/lib/std/os/linux/riscv64.zig
+++ b/lib/std/os/linux/riscv64.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig
index 7b3840288a..039678e405 100644
--- a/lib/std/os/linux/test.zig
+++ b/lib/std/os/linux/test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -9,6 +9,7 @@ const linux = std.os.linux;
const mem = std.mem;
const elf = std.elf;
const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
const fs = std.fs;
test "fallocate" {
@@ -99,3 +100,11 @@ test "statx" {
expect(@bitCast(u64, @as(i64, stat_buf.blksize)) == statx_buf.blksize);
expect(@bitCast(u64, @as(i64, stat_buf.blocks)) == statx_buf.blocks);
}
+
+test "user and group ids" {
+ if (builtin.link_libc) return error.SkipZigTest;
+ expectEqual(linux.getauxval(elf.AT_UID), linux.getuid());
+ expectEqual(linux.getauxval(elf.AT_GID), linux.getgid());
+ expectEqual(linux.getauxval(elf.AT_EUID), linux.geteuid());
+ expectEqual(linux.getauxval(elf.AT_EGID), linux.getegid());
+}
diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig
index f6c339bc2c..614f5b4395 100644
--- a/lib/std/os/linux/tls.zig
+++ b/lib/std/os/linux/tls.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -300,7 +300,7 @@ fn initTLS() void {
};
}
-inline fn alignPtrCast(comptime T: type, ptr: [*]u8) *T {
+fn alignPtrCast(comptime T: type, ptr: [*]u8) callconv(.Inline) *T {
return @ptrCast(*T, @alignCast(@alignOf(*T), ptr));
}
@@ -327,32 +327,43 @@ pub fn prepareTLS(area: []u8) usize {
if (tls_tp_points_past_tcb) tls_image.data_offset else tls_image.tcb_offset;
}
-var main_thread_tls_buffer: [256]u8 = undefined;
+// The main motivation for the size chosen here is this is how much ends up being
+// requested for the thread local variables of the std.crypto.random implementation.
+// I'm not sure why it ends up being so much; the struct itself is only 64 bytes.
+// I think it has to do with being page aligned and LLVM or LLD is not smart enough
+// to lay out the TLS data in a space conserving way. Anyway I think it's fine
+// because it's less than 3 pages of memory, and putting it in the ELF like this
+// is equivalent to moving the mmap call below into the kernel, avoiding syscall
+// overhead.
+var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined;
pub fn initStaticTLS() void {
initTLS();
- const alloc_tls_area: []u8 = blk: {
- const full_alloc_size = tls_image.alloc_size + tls_image.alloc_align - 1;
-
+ const tls_area = blk: {
// Fast path for the common case where the TLS data is really small,
- // avoid an allocation and use our local buffer
- if (full_alloc_size < main_thread_tls_buffer.len)
- break :blk main_thread_tls_buffer[0..];
+ // avoid an allocation and use our local buffer.
+ if (tls_image.alloc_align <= mem.page_size and
+ tls_image.alloc_size <= main_thread_tls_buffer.len)
+ {
+ break :blk main_thread_tls_buffer[0..tls_image.alloc_size];
+ }
- break :blk os.mmap(
+ const alloc_tls_area = os.mmap(
null,
- full_alloc_size,
+ tls_image.alloc_size + tls_image.alloc_align - 1,
os.PROT_READ | os.PROT_WRITE,
os.MAP_PRIVATE | os.MAP_ANONYMOUS,
-1,
0,
) catch os.abort();
- };
- // Make sure the slice is correctly aligned
- const start = @ptrToInt(alloc_tls_area.ptr) & (tls_image.alloc_align - 1);
- const tls_area = alloc_tls_area[start .. start + tls_image.alloc_size];
+ // Make sure the slice is correctly aligned.
+ const begin_addr = @ptrToInt(alloc_tls_area.ptr);
+ const begin_aligned_addr = mem.alignForward(begin_addr, tls_image.alloc_align);
+ const start = begin_aligned_addr - begin_addr;
+ break :blk alloc_tls_area[start .. start + tls_image.alloc_size];
+ };
const tp_value = prepareTLS(tls_area);
setThreadPointer(tp_value);
diff --git a/lib/std/os/linux/vdso.zig b/lib/std/os/linux/vdso.zig
index eb99c7407b..f2e4f1f5bc 100644
--- a/lib/std/os/linux/vdso.zig
+++ b/lib/std/os/linux/vdso.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig
index 8987e1aab3..b9b3ff47eb 100644
--- a/lib/std/os/linux/x86_64.zig
+++ b/lib/std/os/linux/x86_64.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/netbsd.zig b/lib/std/os/netbsd.zig
index a713a009ad..572b470239 100644
--- a/lib/std/os/netbsd.zig
+++ b/lib/std/os/netbsd.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/openbsd.zig b/lib/std/os/openbsd.zig
index a713a009ad..572b470239 100644
--- a/lib/std/os/openbsd.zig
+++ b/lib/std/os/openbsd.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig
index 8ad172679b..f08d4d58fa 100644
--- a/lib/std/os/test.zig
+++ b/lib/std/os/test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -189,6 +189,75 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
expect(mem.eql(u8, target_path, given));
}
+test "link with relative paths" {
+ if (builtin.os.tag != .linux) return error.SkipZigTest;
+ var cwd = fs.cwd();
+
+ cwd.deleteFile("example.txt") catch {};
+ cwd.deleteFile("new.txt") catch {};
+
+ try cwd.writeFile("example.txt", "example");
+ try os.link("example.txt", "new.txt", 0);
+
+ const efd = try cwd.openFile("example.txt", .{});
+ defer efd.close();
+
+ const nfd = try cwd.openFile("new.txt", .{});
+ defer nfd.close();
+
+ {
+ const estat = try os.fstat(efd.handle);
+ const nstat = try os.fstat(nfd.handle);
+
+ testing.expectEqual(estat.ino, nstat.ino);
+ testing.expectEqual(@as(usize, 2), nstat.nlink);
+ }
+
+ try os.unlink("new.txt");
+
+ {
+ const estat = try os.fstat(efd.handle);
+ testing.expectEqual(@as(usize, 1), estat.nlink);
+ }
+
+ try cwd.deleteFile("example.txt");
+}
+
+test "linkat with different directories" {
+ if (builtin.os.tag != .linux) return error.SkipZigTest;
+ var cwd = fs.cwd();
+ var tmp = tmpDir(.{});
+
+ cwd.deleteFile("example.txt") catch {};
+ tmp.dir.deleteFile("new.txt") catch {};
+
+ try cwd.writeFile("example.txt", "example");
+ try os.linkat(cwd.fd, "example.txt", tmp.dir.fd, "new.txt", 0);
+
+ const efd = try cwd.openFile("example.txt", .{});
+ defer efd.close();
+
+ const nfd = try tmp.dir.openFile("new.txt", .{});
+
+ {
+ defer nfd.close();
+ const estat = try os.fstat(efd.handle);
+ const nstat = try os.fstat(nfd.handle);
+
+ testing.expectEqual(estat.ino, nstat.ino);
+ testing.expectEqual(@as(usize, 2), nstat.nlink);
+ }
+
+ try os.unlinkat(tmp.dir.fd, "new.txt", 0);
+
+ {
+ const estat = try os.fstat(efd.handle);
+ testing.expectEqual(@as(usize, 1), estat.nlink);
+ }
+
+ try cwd.deleteFile("example.txt");
+}
+
test "fstatat" {
// enable when `fstat` and `fstatat` are implemented on Windows
if (builtin.os.tag == .windows) return error.SkipZigTest;
@@ -475,7 +544,7 @@ test "mmap" {
const file = try tmp.dir.createFile(test_out_file, .{});
defer file.close();
- const stream = file.outStream();
+ const stream = file.writer();
var i: u32 = 0;
while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
@@ -499,7 +568,7 @@ test "mmap" {
defer os.munmap(data);
var mem_stream = io.fixedBufferStream(data);
- const stream = mem_stream.inStream();
+ const stream = mem_stream.reader();
var i: u32 = 0;
while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
@@ -523,7 +592,7 @@ test "mmap" {
defer os.munmap(data);
var mem_stream = io.fixedBufferStream(data);
- const stream = mem_stream.inStream();
+ const stream = mem_stream.reader();
var i: u32 = alloc_size / 2 / @sizeOf(u32);
while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig
index ba1544105c..1942165999 100644
--- a/lib/std/os/uefi.zig
+++ b/lib/std/os/uefi.zig
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
+const std = @import("../std.zig");
+
/// A protocol is an interface identified by a GUID.
pub const protocols = @import("uefi/protocols.zig");
@@ -33,10 +35,10 @@ pub const Guid = extern struct {
self: @This(),
comptime f: []const u8,
options: std.fmt.FormatOptions,
- out_stream: anytype,
- ) Errors!void {
+ writer: anytype,
+ ) !void {
if (f.len == 0) {
- return std.fmt.format(out_stream, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{
+ return std.fmt.format(writer, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{
self.time_low,
self.time_mid,
self.time_high_and_version,
@@ -48,6 +50,15 @@ pub const Guid = extern struct {
@compileError("Unknown format character: '" ++ f ++ "'");
}
}
+
+ pub fn eql(a: std.os.uefi.Guid, b: std.os.uefi.Guid) bool {
+ return a.time_low == b.time_low and
+ a.time_mid == b.time_mid and
+ a.time_high_and_version == b.time_high_and_version and
+ a.clock_seq_high_and_reserved == b.clock_seq_high_and_reserved and
+ a.clock_seq_low == b.clock_seq_low and
+ std.mem.eql(u8, &a.node, &b.node);
+ }
};
/// An EFI Handle represents a collection of related interfaces.
diff --git a/lib/std/os/uefi/protocols.zig b/lib/std/os/uefi/protocols.zig
index 1519092b84..68dafdcecb 100644
--- a/lib/std/os/uefi/protocols.zig
+++ b/lib/std/os/uefi/protocols.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig b/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig
index 3ec6aab5b9..8edc11e24d 100644
--- a/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig
+++ b/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/device_path_protocol.zig b/lib/std/os/uefi/protocols/device_path_protocol.zig
index 1a998f0f78..0d1d028f60 100644
--- a/lib/std/os/uefi/protocols/device_path_protocol.zig
+++ b/lib/std/os/uefi/protocols/device_path_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/edid_active_protocol.zig b/lib/std/os/uefi/protocols/edid_active_protocol.zig
index dc8057b4f8..750ff2833b 100644
--- a/lib/std/os/uefi/protocols/edid_active_protocol.zig
+++ b/lib/std/os/uefi/protocols/edid_active_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/edid_discovered_protocol.zig b/lib/std/os/uefi/protocols/edid_discovered_protocol.zig
index 1ed2b6277d..fdbe594563 100644
--- a/lib/std/os/uefi/protocols/edid_discovered_protocol.zig
+++ b/lib/std/os/uefi/protocols/edid_discovered_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/edid_override_protocol.zig b/lib/std/os/uefi/protocols/edid_override_protocol.zig
index 83260f7b88..e451d41f32 100644
--- a/lib/std/os/uefi/protocols/edid_override_protocol.zig
+++ b/lib/std/os/uefi/protocols/edid_override_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/file_protocol.zig b/lib/std/os/uefi/protocols/file_protocol.zig
index ce34a2d6e5..782ac70810 100644
--- a/lib/std/os/uefi/protocols/file_protocol.zig
+++ b/lib/std/os/uefi/protocols/file_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -16,7 +16,7 @@ pub const FileProtocol = extern struct {
_read: fn (*const FileProtocol, *usize, [*]u8) callconv(.C) Status,
_write: fn (*const FileProtocol, *usize, [*]const u8) callconv(.C) Status,
_get_position: fn (*const FileProtocol, *u64) callconv(.C) Status,
- _set_position: fn (*const FileProtocol, *const u64) callconv(.C) Status,
+ _set_position: fn (*const FileProtocol, u64) callconv(.C) Status,
_get_info: fn (*const FileProtocol, *align(8) const Guid, *const usize, [*]u8) callconv(.C) Status,
_set_info: fn (*const FileProtocol, *align(8) const Guid, usize, [*]const u8) callconv(.C) Status,
_flush: fn (*const FileProtocol) callconv(.C) Status,
@@ -41,11 +41,19 @@ pub const FileProtocol = extern struct {
return self._write(self, buffer_size, buffer);
}
- pub fn get_info(self: *const FileProtocol, information_type: *align(8) Guid, buffer_size: *usize, buffer: [*]u8) Status {
+ pub fn getPosition(self: *const FileProtocol, position: *u64) Status {
+ return self._get_position(self, position);
+ }
+
+ pub fn setPosition(self: *const FileProtocol, position: u64) Status {
+ return self._set_position(self, position);
+ }
+
+ pub fn getInfo(self: *const FileProtocol, information_type: *align(8) Guid, buffer_size: *usize, buffer: [*]u8) Status {
return self._get_info(self, information_type, buffer_size, buffer);
}
- pub fn set_info(self: *const FileProtocol, information_type: *align(8) Guid, buffer_size: usize, buffer: [*]const u8) Status {
+ pub fn setInfo(self: *const FileProtocol, information_type: *align(8) Guid, buffer_size: usize, buffer: [*]const u8) Status {
return self._set_info(self, information_type, buffer_size, buffer);
}
@@ -73,6 +81,8 @@ pub const FileProtocol = extern struct {
pub const efi_file_directory: u64 = 0x0000000000000010;
pub const efi_file_archive: u64 = 0x0000000000000020;
pub const efi_file_valid_attr: u64 = 0x0000000000000037;
+
+ pub const efi_file_position_end_of_file: u64 = 0xffffffffffffffff;
};
pub const FileInfo = extern struct {
diff --git a/lib/std/os/uefi/protocols/graphics_output_protocol.zig b/lib/std/os/uefi/protocols/graphics_output_protocol.zig
index 3ec1c39ab8..a5e784b597 100644
--- a/lib/std/os/uefi/protocols/graphics_output_protocol.zig
+++ b/lib/std/os/uefi/protocols/graphics_output_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/hii.zig b/lib/std/os/uefi/protocols/hii.zig
index ed7c40d6ac..9d85f293b3 100644
--- a/lib/std/os/uefi/protocols/hii.zig
+++ b/lib/std/os/uefi/protocols/hii.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/hii_database_protocol.zig b/lib/std/os/uefi/protocols/hii_database_protocol.zig
index d0b16ff943..33014e1cb7 100644
--- a/lib/std/os/uefi/protocols/hii_database_protocol.zig
+++ b/lib/std/os/uefi/protocols/hii_database_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/hii_popup_protocol.zig b/lib/std/os/uefi/protocols/hii_popup_protocol.zig
index 1f5c5ce0f4..22bae95449 100644
--- a/lib/std/os/uefi/protocols/hii_popup_protocol.zig
+++ b/lib/std/os/uefi/protocols/hii_popup_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/ip6_config_protocol.zig b/lib/std/os/uefi/protocols/ip6_config_protocol.zig
index 16002f62a6..8dd0caf31a 100644
--- a/lib/std/os/uefi/protocols/ip6_config_protocol.zig
+++ b/lib/std/os/uefi/protocols/ip6_config_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/ip6_protocol.zig b/lib/std/os/uefi/protocols/ip6_protocol.zig
index 578a3cfb01..011517ba2a 100644
--- a/lib/std/os/uefi/protocols/ip6_protocol.zig
+++ b/lib/std/os/uefi/protocols/ip6_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/ip6_service_binding_protocol.zig b/lib/std/os/uefi/protocols/ip6_service_binding_protocol.zig
index 59605cc11b..69a410c01c 100644
--- a/lib/std/os/uefi/protocols/ip6_service_binding_protocol.zig
+++ b/lib/std/os/uefi/protocols/ip6_service_binding_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/loaded_image_protocol.zig b/lib/std/os/uefi/protocols/loaded_image_protocol.zig
index 96aa10f08d..a5c5610f9b 100644
--- a/lib/std/os/uefi/protocols/loaded_image_protocol.zig
+++ b/lib/std/os/uefi/protocols/loaded_image_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/managed_network_protocol.zig b/lib/std/os/uefi/protocols/managed_network_protocol.zig
index 9202c6f139..6652107a1a 100644
--- a/lib/std/os/uefi/protocols/managed_network_protocol.zig
+++ b/lib/std/os/uefi/protocols/managed_network_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/managed_network_service_binding_protocol.zig b/lib/std/os/uefi/protocols/managed_network_service_binding_protocol.zig
index 0c684336c8..f0b8c5fb15 100644
--- a/lib/std/os/uefi/protocols/managed_network_service_binding_protocol.zig
+++ b/lib/std/os/uefi/protocols/managed_network_service_binding_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/rng_protocol.zig b/lib/std/os/uefi/protocols/rng_protocol.zig
index 713b76b371..25f7c936c3 100644
--- a/lib/std/os/uefi/protocols/rng_protocol.zig
+++ b/lib/std/os/uefi/protocols/rng_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/shell_parameters_protocol.zig b/lib/std/os/uefi/protocols/shell_parameters_protocol.zig
index 7ec46b732c..338d88fc9b 100644
--- a/lib/std/os/uefi/protocols/shell_parameters_protocol.zig
+++ b/lib/std/os/uefi/protocols/shell_parameters_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/simple_file_system_protocol.zig b/lib/std/os/uefi/protocols/simple_file_system_protocol.zig
index 946c88a89b..68f08ebff8 100644
--- a/lib/std/os/uefi/protocols/simple_file_system_protocol.zig
+++ b/lib/std/os/uefi/protocols/simple_file_system_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/simple_network_protocol.zig b/lib/std/os/uefi/protocols/simple_network_protocol.zig
index f74f11a857..d29cd68873 100644
--- a/lib/std/os/uefi/protocols/simple_network_protocol.zig
+++ b/lib/std/os/uefi/protocols/simple_network_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/simple_pointer_protocol.zig b/lib/std/os/uefi/protocols/simple_pointer_protocol.zig
index 5f8ca7569e..b76b5bc512 100644
--- a/lib/std/os/uefi/protocols/simple_pointer_protocol.zig
+++ b/lib/std/os/uefi/protocols/simple_pointer_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig b/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig
index 096013bfb0..0cc1416641 100644
--- a/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig
+++ b/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/simple_text_input_protocol.zig b/lib/std/os/uefi/protocols/simple_text_input_protocol.zig
index 00fae88472..47e632021b 100644
--- a/lib/std/os/uefi/protocols/simple_text_input_protocol.zig
+++ b/lib/std/os/uefi/protocols/simple_text_input_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/simple_text_output_protocol.zig b/lib/std/os/uefi/protocols/simple_text_output_protocol.zig
index f9bbc37140..6fb56724c7 100644
--- a/lib/std/os/uefi/protocols/simple_text_output_protocol.zig
+++ b/lib/std/os/uefi/protocols/simple_text_output_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/udp6_protocol.zig b/lib/std/os/uefi/protocols/udp6_protocol.zig
index 50b7dae7c4..c2e4228998 100644
--- a/lib/std/os/uefi/protocols/udp6_protocol.zig
+++ b/lib/std/os/uefi/protocols/udp6_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/protocols/udp6_service_binding_protocol.zig b/lib/std/os/uefi/protocols/udp6_service_binding_protocol.zig
index 4f4e0a2638..620f015722 100644
--- a/lib/std/os/uefi/protocols/udp6_service_binding_protocol.zig
+++ b/lib/std/os/uefi/protocols/udp6_service_binding_protocol.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/status.zig b/lib/std/os/uefi/status.zig
index 3e86962202..ccf50d8515 100644
--- a/lib/std/os/uefi/status.zig
+++ b/lib/std/os/uefi/status.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/tables.zig b/lib/std/os/uefi/tables.zig
index b796eb6e06..649fe95cd2 100644
--- a/lib/std/os/uefi/tables.zig
+++ b/lib/std/os/uefi/tables.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/tables/boot_services.zig b/lib/std/os/uefi/tables/boot_services.zig
index 09431cf3f4..b96881fcc2 100644
--- a/lib/std/os/uefi/tables/boot_services.zig
+++ b/lib/std/os/uefi/tables/boot_services.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/tables/configuration_table.zig b/lib/std/os/uefi/tables/configuration_table.zig
index c7dedad5fb..00c8f2f429 100644
--- a/lib/std/os/uefi/tables/configuration_table.zig
+++ b/lib/std/os/uefi/tables/configuration_table.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/tables/runtime_services.zig b/lib/std/os/uefi/tables/runtime_services.zig
index a2012168ee..7436ab530c 100644
--- a/lib/std/os/uefi/tables/runtime_services.zig
+++ b/lib/std/os/uefi/tables/runtime_services.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/tables/system_table.zig b/lib/std/os/uefi/tables/system_table.zig
index 3f0624d2ce..c5b6c5f1e9 100644
--- a/lib/std/os/uefi/tables/system_table.zig
+++ b/lib/std/os/uefi/tables/system_table.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/uefi/tables/table_header.zig b/lib/std/os/uefi/tables/table_header.zig
index a8343c967a..8af1895cad 100644
--- a/lib/std/os/uefi/tables/table_header.zig
+++ b/lib/std/os/uefi/tables/table_header.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig
index 899541f3fe..3965ae77a0 100644
--- a/lib/std/os/wasi.zig
+++ b/lib/std/os/wasi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig
index b994720ce9..6f67b65252 100644
--- a/lib/std/os/windows.zig
+++ b/lib/std/os/windows.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -109,7 +109,12 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
0,
);
switch (rc) {
- .SUCCESS => return result,
+ .SUCCESS => {
+ if (std.io.is_async and options.io_mode == .evented) {
+ _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined;
+ }
+ return result;
+ },
.OBJECT_NAME_INVALID => unreachable,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
@@ -418,8 +423,6 @@ pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64, io_mode: std.io.Mo
},
},
};
- // TODO only call create io completion port once per fd
- _ = CreateIoCompletionPort(in_hFile, loop.os_data.io_port, undefined, undefined) catch undefined;
loop.beginOneEvent();
suspend {
// TODO handle buffer bigger than DWORD can hold
@@ -500,8 +503,6 @@ pub fn WriteFile(
},
},
};
- // TODO only call create io completion port once per fd
- _ = CreateIoCompletionPort(handle, loop.os_data.io_port, undefined, undefined) catch undefined;
loop.beginOneEvent();
suspend {
const adjusted_len = math.cast(DWORD, bytes.len) catch maxInt(DWORD);
@@ -857,6 +858,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
.SUCCESS => return CloseHandle(tmp_handle),
.OBJECT_NAME_INVALID => unreachable,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
+ .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.INVALID_PARAMETER => unreachable,
.FILE_IS_A_DIRECTORY => return error.IsDir,
.NOT_A_DIRECTORY => return error.NotDir,
@@ -952,7 +954,56 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
return @bitCast(u64, result);
}
+pub fn QueryObjectName(
+ handle: HANDLE,
+ out_buffer: []u16,
+) ![]u16 {
+ const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong;
+
+ const info = @ptrCast(*OBJECT_NAME_INFORMATION, out_buffer_aligned);
+ //buffer size is specified in bytes
+ const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) catch |e| switch (e) {
+ error.Overflow => std.math.maxInt(ULONG),
+ };
+ //last argument would return the length required for full_buffer, not exposed here
+ const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null);
+ switch (rc) {
+ .SUCCESS => {
+ // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0)
+ // if the object was "unnamed", not sure if this can happen for file handles
+ if (info.Name.MaximumLength == 0) return error.Unexpected;
+ // resulting string length is specified in bytes
+ const path_length_unterminated = @divExact(info.Name.Length, 2);
+ return info.Name.Buffer[0..path_length_unterminated];
+ },
+ .ACCESS_DENIED => return error.AccessDenied,
+ .INVALID_HANDLE => return error.InvalidHandle,
+ // triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH),
+ // or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL)
+ .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => return error.NameTooLong,
+ else => |e| return unexpectedStatus(e),
+ }
+}
+test "QueryObjectName" {
+ if (comptime builtin.os.tag != .windows)
+ return;
+
+ //any file will do; canonicalization works on NTFS junctions and symlinks, hardlinks remain separate paths.
+ var tmp = std.testing.tmpDir(.{});
+ defer tmp.cleanup();
+ const handle = tmp.dir.fd;
+ var out_buffer: [PATH_MAX_WIDE]u16 = undefined;
+
+ var result_path = try QueryObjectName(handle, &out_buffer);
+ const required_len_in_u16 = result_path.len + @divExact(@ptrToInt(result_path.ptr) - @ptrToInt(&out_buffer), 2) + 1;
+ //insufficient size
+ std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. required_len_in_u16 - 1]));
+ //exactly-sufficient size
+ _ = try QueryObjectName(handle, out_buffer[0..required_len_in_u16]);
+}
+
pub const GetFinalPathNameByHandleError = error{
+ AccessDenied,
BadPathName,
FileNotFound,
NameTooLong,
@@ -980,32 +1031,31 @@ pub fn GetFinalPathNameByHandle(
fmt: GetFinalPathNameByHandleFormat,
out_buffer: []u16,
) GetFinalPathNameByHandleError![]u16 {
- // Get normalized path; doesn't include volume name though.
- var path_buffer: [@sizeOf(FILE_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined;
- try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]);
-
- // Get NT volume name.
- var volume_buffer: [@sizeOf(FILE_NAME_INFORMATION) + MAX_PATH]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined; // MAX_PATH bytes should be enough since it's Windows-defined name
- try QueryInformationFile(hFile, .FileVolumeNameInformation, volume_buffer[0..]);
-
- const file_name = @ptrCast(*const FILE_NAME_INFORMATION, &path_buffer[0]);
- const file_name_u16 = @ptrCast([*]const u16, &file_name.FileName[0])[0 .. file_name.FileNameLength / 2];
-
- const volume_name = @ptrCast(*const FILE_NAME_INFORMATION, &volume_buffer[0]);
+ const final_path = QueryObjectName(hFile, out_buffer) catch |err| switch (err) {
+ // we assume InvalidHandle is close enough to FileNotFound in semantics
+ // to not further complicate the error set
+ error.InvalidHandle => return error.FileNotFound,
+ else => |e| return e,
+ };
switch (fmt.volume_name) {
.Nt => {
- // Nothing to do, we simply copy the bytes to the user-provided buffer.
- const volume_name_u16 = @ptrCast([*]const u16, &volume_name.FileName[0])[0 .. volume_name.FileNameLength / 2];
+ // the returned path is already in .Nt format
+ return final_path;
+ },
+ .Dos => {
+ // parse the string to separate volume path from file path
+ const expected_prefix = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\");
- if (out_buffer.len < volume_name_u16.len + file_name_u16.len) return error.NameTooLong;
+ // TODO find out if a path can start with something besides `\Device\<volume name>`,
+ // and if we need to handle it differently
+ // (i.e. how to determine the start and end of the volume name in that case)
+ if (!mem.eql(u16, expected_prefix, final_path[0..expected_prefix.len])) return error.Unexpected;
- std.mem.copy(u16, out_buffer[0..], volume_name_u16);
- std.mem.copy(u16, out_buffer[volume_name_u16.len..], file_name_u16);
+ const file_path_begin_index = mem.indexOfPos(u16, final_path, expected_prefix.len, &[_]u16{'\\'}) orelse unreachable;
+ const volume_name_u16 = final_path[0..file_path_begin_index];
+ const file_name_u16 = final_path[file_path_begin_index..];
- return out_buffer[0 .. volume_name_u16.len + file_name_u16.len];
- },
- .Dos => {
// Get DOS volume name. DOS volume names are actually symbolic link objects to the
// actual NT volume. For example:
// (NT) \Device\HarddiskVolume4 => (DOS) \DosDevices\C: == (DOS) C:
@@ -1038,10 +1088,10 @@ pub fn GetFinalPathNameByHandle(
var input_struct = @ptrCast(*MOUNTMGR_MOUNT_POINT, &input_buf[0]);
input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);
- input_struct.DeviceNameLength = @intCast(USHORT, volume_name.FileNameLength);
- @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, &volume_name.FileName[0]), volume_name.FileNameLength);
+ input_struct.DeviceNameLength = @intCast(USHORT, volume_name_u16.len * 2);
+ @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, volume_name_u16.ptr), volume_name_u16.len * 2);
- DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, input_buf[0..], output_buf[0..]) catch |err| switch (err) {
+ DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, &input_buf, &output_buf) catch |err| switch (err) {
error.AccessDenied => unreachable,
else => |e| return e,
};
@@ -1061,22 +1111,20 @@ pub fn GetFinalPathNameByHandle(
// Look for `\DosDevices\` prefix. We don't really care if there are more than one symlinks
// with traditional DOS drive letters, so pick the first one available.
- const prefix_u8 = "\\DosDevices\\";
- var prefix_buf_u16: [prefix_u8.len]u16 = undefined;
- const prefix_len_u16 = std.unicode.utf8ToUtf16Le(prefix_buf_u16[0..], prefix_u8[0..]) catch unreachable;
- const prefix = prefix_buf_u16[0..prefix_len_u16];
+ var prefix_buf = std.unicode.utf8ToUtf16LeStringLiteral("\\DosDevices\\");
+ const prefix = prefix_buf[0..prefix_buf.len];
- if (std.mem.startsWith(u16, symlink, prefix)) {
+ if (mem.startsWith(u16, symlink, prefix)) {
const drive_letter = symlink[prefix.len..];
if (out_buffer.len < drive_letter.len + file_name_u16.len) return error.NameTooLong;
- std.mem.copy(u16, out_buffer[0..], drive_letter);
- std.mem.copy(u16, out_buffer[drive_letter.len..], file_name_u16);
+ mem.copy(u16, out_buffer, drive_letter);
+ mem.copy(u16, out_buffer[drive_letter.len..], file_name_u16);
const total_len = drive_letter.len + file_name_u16.len;
// Validate that DOS does not contain any spurious nul bytes.
- if (std.mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| {
+ if (mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| {
return error.BadPathName;
}
@@ -1091,6 +1139,30 @@ pub fn GetFinalPathNameByHandle(
}
}
+test "GetFinalPathNameByHandle" {
+ if (comptime builtin.os.tag != .windows)
+ return;
+
+ //any file will do
+ var tmp = std.testing.tmpDir(.{});
+ defer tmp.cleanup();
+ const handle = tmp.dir.fd;
+ var buffer: [PATH_MAX_WIDE]u16 = undefined;
+
+ //check with sufficient size
+ const nt_path = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer);
+ _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer);
+
+ const required_len_in_u16 = nt_path.len + @divExact(@ptrToInt(nt_path.ptr) - @ptrToInt(&buffer), 2) + 1;
+ //check with insufficient size
+ std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. required_len_in_u16 - 1]));
+ std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. required_len_in_u16 - 1]));
+
+ //check with exactly-sufficient size
+ _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..required_len_in_u16]);
+ _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..required_len_in_u16]);
+}
+
pub const QueryInformationFileError = error{Unexpected};
pub fn QueryInformationFile(
@@ -1240,8 +1312,8 @@ pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws
}
}
-pub fn poll(fds: [*]ws2_32.pollfd, n: usize, timeout: i32) i32 {
- return ws2_32.WSAPoll(fds, @intCast(u32, n), timeout);
+pub fn poll(fds: [*]ws2_32.pollfd, n: c_ulong, timeout: i32) i32 {
+ return ws2_32.WSAPoll(fds, n, timeout);
}
pub fn WSAIoctl(
@@ -1597,7 +1669,7 @@ pub fn wToPrefixedFileW(s: []const u16) !PathSpace {
return path_space;
}
-inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID {
+fn MAKELANGID(p: c_ushort, s: c_ushort) callconv(.Inline) LANGID {
return (s << 10) | p;
}
@@ -1618,7 +1690,7 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
null,
);
_ = std.unicode.utf16leToUtf8(&buf_u8, buf_u16[0..len]) catch unreachable;
- std.debug.warn("error.Unexpected: GetLastError({}): {}\n", .{ @enumToInt(err), buf_u8[0..len] });
+ std.debug.warn("error.Unexpected: GetLastError({}): {s}\n", .{ @enumToInt(err), buf_u8[0..len] });
std.debug.dumpCurrentStackTrace(null);
}
return error.Unexpected;
diff --git a/lib/std/os/windows/advapi32.zig b/lib/std/os/windows/advapi32.zig
index 177449a70e..6fa9ae2b45 100644
--- a/lib/std/os/windows/advapi32.zig
+++ b/lib/std/os/windows/advapi32.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/windows/bits.zig b/lib/std/os/windows/bits.zig
index f5d520c580..cbeb0b483d 100644
--- a/lib/std/os/windows/bits.zig
+++ b/lib/std/os/windows/bits.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -37,6 +37,7 @@ pub const UCHAR = u8;
pub const FLOAT = f32;
pub const HANDLE = *c_void;
pub const HCRYPTPROV = ULONG_PTR;
+pub const ATOM = u16;
pub const HBRUSH = *opaque {};
pub const HCURSOR = *opaque {};
pub const HICON = *opaque {};
@@ -84,8 +85,8 @@ pub const HLOCAL = HANDLE;
pub const LANGID = c_ushort;
pub const WPARAM = usize;
-pub const LPARAM = ?*c_void;
-pub const LRESULT = ?*c_void;
+pub const LPARAM = LONG_PTR;
+pub const LRESULT = LONG_PTR;
pub const va_list = *opaque {};
@@ -770,6 +771,13 @@ pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
+pub const RECT = extern struct {
+ left: LONG,
+ top: LONG,
+ right: LONG,
+ bottom: LONG,
+};
+
pub const SMALL_RECT = extern struct {
Left: SHORT,
Top: SHORT,
@@ -777,6 +785,11 @@ pub const SMALL_RECT = extern struct {
Bottom: SHORT,
};
+pub const POINT = extern struct {
+ x: LONG,
+ y: LONG,
+};
+
pub const COORD = extern struct {
X: SHORT,
Y: SHORT,
@@ -813,7 +826,8 @@ pub const FILE_NOTIFY_INFORMATION = extern struct {
NextEntryOffset: DWORD,
Action: DWORD,
FileNameLength: DWORD,
- FileName: [1]WCHAR,
+ // Flexible array member
+ // FileName: [1]WCHAR,
};
pub const FILE_ACTION_ADDED = 0x00000001;
@@ -1301,11 +1315,13 @@ pub const PEB = extern struct {
ImageSubSystemMinorVersion: ULONG,
// note: there is padding here on 64 bit
ActiveProcessAffinityMask: KAFFINITY,
- GdiHandleBuffer: [switch (@sizeOf(usize)) {
- 4 => 0x22,
- 8 => 0x3C,
- else => unreachable,
- }]ULONG,
+ GdiHandleBuffer: [
+ switch (@sizeOf(usize)) {
+ 4 => 0x22,
+ 8 => 0x3C,
+ else => unreachable,
+ }
+ ]ULONG,
// Fields appended in 5.0 (Windows 2000):
PostProcessInitRoutine: PVOID,
@@ -1606,3 +1622,23 @@ pub const IOCTL_MOUNTMGR_QUERY_POINTS: ULONG = 0x6d0008;
pub const SD_RECEIVE = 0;
pub const SD_SEND = 1;
pub const SD_BOTH = 2;
+
+pub const OBJECT_INFORMATION_CLASS = extern enum {
+ ObjectBasicInformation = 0,
+ ObjectNameInformation = 1,
+ ObjectTypeInformation = 2,
+ ObjectTypesInformation = 3,
+ ObjectHandleFlagInformation = 4,
+ ObjectSessionInformation = 5,
+ MaxObjectInfoClass,
+};
+
+pub const OBJECT_NAME_INFORMATION = extern struct {
+ Name: UNICODE_STRING,
+};
+pub const POBJECT_NAME_INFORMATION = *OBJECT_NAME_INFORMATION;
+
+pub const SRWLOCK = usize;
+pub const SRWLOCK_INIT: SRWLOCK = 0;
+pub const CONDITION_VARIABLE = usize;
+pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = 0;
diff --git a/lib/std/os/windows/gdi32.zig b/lib/std/os/windows/gdi32.zig
index 35ebfb7789..c91e1d487c 100644
--- a/lib/std/os/windows/gdi32.zig
+++ b/lib/std/os/windows/gdi32.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig
index 444234876c..734059a08a 100644
--- a/lib/std/os/windows/kernel32.zig
+++ b/lib/std/os/windows/kernel32.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -8,7 +8,7 @@ usingnamespace @import("bits.zig");
pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*c_void;
pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong;
-pub extern "kernel32" fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) callconv(WINAPI) BOOL;
+pub extern "kernel32" fn CancelIoEx(hFile: HANDLE, lpOverlapped: ?LPOVERLAPPED) callconv(WINAPI) BOOL;
pub extern "kernel32" fn CloseHandle(hObject: HANDLE) callconv(WINAPI) BOOL;
@@ -115,6 +115,7 @@ pub extern "kernel32" fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u1
pub extern "kernel32" fn GetModuleHandleW(lpModuleName: ?[*:0]const WCHAR) callconv(WINAPI) ?HMODULE;
pub extern "kernel32" fn GetLastError() callconv(WINAPI) Win32Error;
+pub extern "kernel32" fn SetLastError(dwErrCode: Win32Error) callconv(WINAPI) void;
pub extern "kernel32" fn GetFileInformationByHandle(
hFile: HANDLE,
@@ -288,3 +289,16 @@ pub extern "kernel32" fn K32QueryWorkingSet(hProcess: HANDLE, pv: PVOID, cb: DWO
pub extern "kernel32" fn K32QueryWorkingSetEx(hProcess: HANDLE, pv: PVOID, cb: DWORD) callconv(WINAPI) BOOL;
pub extern "kernel32" fn FlushFileBuffers(hFile: HANDLE) callconv(WINAPI) BOOL;
+
+pub extern "kernel32" fn WakeAllConditionVariable(c: *CONDITION_VARIABLE) callconv(WINAPI) void;
+pub extern "kernel32" fn WakeConditionVariable(c: *CONDITION_VARIABLE) callconv(WINAPI) void;
+pub extern "kernel32" fn SleepConditionVariableSRW(
+ c: *CONDITION_VARIABLE,
+ s: *SRWLOCK,
+ t: DWORD,
+ f: ULONG,
+) callconv(WINAPI) BOOL;
+
+pub extern "kernel32" fn TryAcquireSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) BOOLEAN;
+pub extern "kernel32" fn AcquireSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) void;
+pub extern "kernel32" fn ReleaseSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) void;
diff --git a/lib/std/os/windows/lang.zig b/lib/std/os/windows/lang.zig
index 61efa3bdb3..40b363cfae 100644
--- a/lib/std/os/windows/lang.zig
+++ b/lib/std/os/windows/lang.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig
index fc485f87f2..3f76036762 100644
--- a/lib/std/os/windows/ntdll.zig
+++ b/lib/std/os/windows/ntdll.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -113,3 +113,11 @@ pub extern "NtDll" fn NtWaitForKeyedEvent(
) callconv(WINAPI) NTSTATUS;
pub extern "NtDll" fn RtlSetCurrentDirectory_U(PathName: *UNICODE_STRING) callconv(WINAPI) NTSTATUS;
+
+pub extern "NtDll" fn NtQueryObject(
+ Handle: HANDLE,
+ ObjectInformationClass: OBJECT_INFORMATION_CLASS,
+ ObjectInformation: PVOID,
+ ObjectInformationLength: ULONG,
+ ReturnLength: ?*ULONG,
+) callconv(WINAPI) NTSTATUS;
diff --git a/lib/std/os/windows/ntstatus.zig b/lib/std/os/windows/ntstatus.zig
index 0e567df510..b86cd1186b 100644
--- a/lib/std/os/windows/ntstatus.zig
+++ b/lib/std/os/windows/ntstatus.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/windows/ole32.zig b/lib/std/os/windows/ole32.zig
index 13920dd510..bf1eabd63e 100644
--- a/lib/std/os/windows/ole32.zig
+++ b/lib/std/os/windows/ole32.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/windows/psapi.zig b/lib/std/os/windows/psapi.zig
index 0d19117c30..2952df1635 100644
--- a/lib/std/os/windows/psapi.zig
+++ b/lib/std/os/windows/psapi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/windows/shell32.zig b/lib/std/os/windows/shell32.zig
index 812cbd6cfc..d184ba1036 100644
--- a/lib/std/os/windows/shell32.zig
+++ b/lib/std/os/windows/shell32.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/windows/sublang.zig b/lib/std/os/windows/sublang.zig
index 5249e8ed0a..ecc46dbfc4 100644
--- a/lib/std/os/windows/sublang.zig
+++ b/lib/std/os/windows/sublang.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/windows/user32.zig b/lib/std/os/windows/user32.zig
index f4533faaa6..186a1af59f 100644
--- a/lib/std/os/windows/user32.zig
+++ b/lib/std/os/windows/user32.zig
@@ -1,48 +1,82 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
usingnamespace @import("bits.zig");
+const std = @import("std");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+const windows = @import("../windows.zig");
+const unexpectedError = windows.unexpectedError;
+const GetLastError = windows.kernel32.GetLastError;
+const SetLastError = windows.kernel32.SetLastError;
-// PM
-pub const PM_REMOVE = 0x0001;
-pub const PM_NOREMOVE = 0x0000;
-pub const PM_NOYIELD = 0x0002;
+fn selectSymbol(comptime function_static: anytype, function_dynamic: @TypeOf(function_static), comptime os: std.Target.Os.WindowsVersion) @TypeOf(function_static) {
+ comptime {
+ const sym_ok = builtin.Target.current.os.isAtLeast(.windows, os);
+ if (sym_ok == true) return function_static;
+ if (sym_ok == null) return function_dynamic;
+ if (sym_ok == false) @compileError("Target OS range does not support function, at least " ++ @tagName(os) ++ " is required");
+ }
+}
+
+// === Messages ===
+
+pub const WNDPROC = fn (hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM) callconv(WINAPI) LRESULT;
+
+pub const MSG = extern struct {
+ hWnd: ?HWND,
+ message: UINT,
+ wParam: WPARAM,
+ lParam: LPARAM,
+ time: DWORD,
+ pt: POINT,
+ lPrivate: DWORD,
+};
-// WM
pub const WM_NULL = 0x0000;
pub const WM_CREATE = 0x0001;
pub const WM_DESTROY = 0x0002;
+pub const WM_NCDESTROY = WM_DESTROY;
pub const WM_MOVE = 0x0003;
pub const WM_SIZE = 0x0005;
-
pub const WM_ACTIVATE = 0x0006;
-pub const WM_PAINT = 0x000F;
-pub const WM_CLOSE = 0x0010;
-pub const WM_QUIT = 0x0012;
pub const WM_SETFOCUS = 0x0007;
-
pub const WM_KILLFOCUS = 0x0008;
pub const WM_ENABLE = 0x000A;
pub const WM_SETREDRAW = 0x000B;
-
-pub const WM_SYSCOLORCHANGE = 0x0015;
+pub const WM_SETTEXT = 0x000C;
+pub const WM_GETTEXT = 0x000D;
+pub const WM_GETTEXTLENGTH = 0x000E;
+pub const WM_PAINT = 0x000F;
+pub const WM_CLOSE = 0x0010;
+pub const WM_QUIT = 0x0012;
+pub const WM_ERASEBKGND = 0x0014;
pub const WM_SHOWWINDOW = 0x0018;
-
-pub const WM_WINDOWPOSCHANGING = 0x0046;
+pub const WM_CTLCOLOR = 0x0019;
+pub const WM_NEXTDLGCTL = 0x0028;
+pub const WM_DRAWITEM = 0x002B;
+pub const WM_MEASUREITEM = 0x002C;
+pub const WM_DELETEITEM = 0x002D;
+pub const WM_VKEYTOITEM = 0x002E;
+pub const WM_CHARTOITEM = 0x002F;
+pub const WM_SETFONT = 0x0030;
+pub const WM_GETFONT = 0x0031;
+pub const WM_COMPAREITEM = 0x0039;
pub const WM_WINDOWPOSCHANGED = 0x0047;
-pub const WM_POWER = 0x0048;
-
-pub const WM_CONTEXTMENU = 0x007B;
-pub const WM_STYLECHANGING = 0x007C;
-pub const WM_STYLECHANGED = 0x007D;
-pub const WM_DISPLAYCHANGE = 0x007E;
-pub const WM_GETICON = 0x007F;
-pub const WM_SETICON = 0x0080;
-
-pub const WM_INPUT_DEVICE_CHANGE = 0x00fe;
-pub const WM_INPUT = 0x00FF;
+pub const WM_NOTIFY = 0x004E;
+pub const WM_NCCALCSIZE = 0x0083;
+pub const WM_NCHITTEST = 0x0084;
+pub const WM_NCPAINT = 0x0085;
+pub const WM_GETDLGCODE = 0x0087;
+pub const WM_NCMOUSEMOVE = 0x00A0;
+pub const WM_NCLBUTTONDOWN = 0x00A1;
+pub const WM_NCLBUTTONUP = 0x00A2;
+pub const WM_NCLBUTTONDBLCLK = 0x00A3;
+pub const WM_NCRBUTTONDOWN = 0x00A4;
+pub const WM_NCRBUTTONUP = 0x00A5;
+pub const WM_NCRBUTTONDBLCLK = 0x00A6;
pub const WM_KEYFIRST = 0x0100;
pub const WM_KEYDOWN = 0x0100;
pub const WM_KEYUP = 0x0101;
@@ -54,11 +88,20 @@ pub const WM_SYSCHAR = 0x0106;
pub const WM_SYSDEADCHAR = 0x0107;
pub const WM_UNICHAR = 0x0109;
pub const WM_KEYLAST = 0x0109;
-
+pub const WM_INITDIALOG = 0x0110;
pub const WM_COMMAND = 0x0111;
pub const WM_SYSCOMMAND = 0x0112;
pub const WM_TIMER = 0x0113;
-
+pub const WM_HSCROLL = 0x0114;
+pub const WM_VSCROLL = 0x0115;
+pub const WM_ENTERIDLE = 0x0121;
+pub const WM_CTLCOLORMSGBOX = 0x0132;
+pub const WM_CTLCOLOREDIT = 0x0133;
+pub const WM_CTLCOLORLISTBOX = 0x0134;
+pub const WM_CTLCOLORBTN = 0x0135;
+pub const WM_CTLCOLORDLG = 0x0136;
+pub const WM_CTLCOLORSCROLLBAR = 0x0137;
+pub const WM_CTLCOLORSTATIC = 0x0138;
pub const WM_MOUSEFIRST = 0x0200;
pub const WM_MOUSEMOVE = 0x0200;
pub const WM_LBUTTONDOWN = 0x0201;
@@ -71,105 +114,561 @@ pub const WM_MBUTTONDOWN = 0x0207;
pub const WM_MBUTTONUP = 0x0208;
pub const WM_MBUTTONDBLCLK = 0x0209;
pub const WM_MOUSEWHEEL = 0x020A;
-pub const WM_XBUTTONDOWN = 0x020B;
-pub const WM_XBUTTONUP = 0x020C;
-pub const WM_XBUTTONDBLCLK = 0x020D;
+pub const WM_MOUSELAST = 0x020A;
+pub const WM_HOTKEY = 0x0312;
+pub const WM_CARET_CREATE = 0x03E0;
+pub const WM_CARET_DESTROY = 0x03E1;
+pub const WM_CARET_BLINK = 0x03E2;
+pub const WM_FDINPUT = 0x03F0;
+pub const WM_FDOUTPUT = 0x03F1;
+pub const WM_FDEXCEPT = 0x03F2;
+pub const WM_USER = 0x0400;
-// WA
-pub const WA_INACTIVE = 0;
-pub const WA_ACTIVE = 0x0006;
+pub extern "user32" fn GetMessageA(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) callconv(WINAPI) BOOL;
+pub fn getMessageA(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: u32, wMsgFilterMax: u32) !void {
+ const r = GetMessageA(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
+ if (r == 0) return error.Quit;
+ if (r != -1) return;
+ switch (GetLastError()) {
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
-// WS
-pub const WS_OVERLAPPED = 0x00000000;
-pub const WS_CAPTION = 0x00C00000;
-pub const WS_SYSMENU = 0x00080000;
-pub const WS_THICKFRAME = 0x00040000;
-pub const WS_MINIMIZEBOX = 0x00020000;
-pub const WS_MAXIMIZEBOX = 0x00010000;
+pub extern "user32" fn GetMessageW(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) callconv(WINAPI) BOOL;
+pub var pfnGetMessageW: @TypeOf(GetMessageW) = undefined;
+pub fn getMessageW(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: u32, wMsgFilterMax: u32) !void {
+ const function = selectSymbol(GetMessageW, pfnGetMessageW, .win2k);
-// PFD
-pub const PFD_DRAW_TO_WINDOW = 0x00000004;
-pub const PFD_SUPPORT_OPENGL = 0x00000020;
-pub const PFD_DOUBLEBUFFER = 0x00000001;
-pub const PFD_MAIN_PLANE = 0;
-pub const PFD_TYPE_RGBA = 0;
+ const r = function(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
+ if (r == 0) return error.Quit;
+ if (r != -1) return;
+ switch (GetLastError()) {
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
-// CS
-pub const CS_HREDRAW = 0x0002;
-pub const CS_VREDRAW = 0x0001;
-pub const CS_OWNDC = 0x0020;
+pub const PM_NOREMOVE = 0x0000;
+pub const PM_REMOVE = 0x0001;
+pub const PM_NOYIELD = 0x0002;
-// SW
-pub const SW_HIDE = 0;
-pub const SW_SHOW = 5;
+pub extern "user32" fn PeekMessageA(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) callconv(WINAPI) BOOL;
+pub fn peekMessageA(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: u32, wMsgFilterMax: u32, wRemoveMsg: u32) !bool {
+ const r = PeekMessageA(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
+ if (r == 0) return false;
+ if (r != -1) return true;
+ switch (GetLastError()) {
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn PeekMessageW(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) callconv(WINAPI) BOOL;
+pub var pfnPeekMessageW: @TypeOf(PeekMessageW) = undefined;
+pub fn peekMessageW(lpMsg: *MSG, hWnd: ?HWND, wMsgFilterMin: u32, wMsgFilterMax: u32, wRemoveMsg: u32) !bool {
+ const function = selectSymbol(PeekMessageW, pfnPeekMessageW, .win2k);
+
+ const r = function(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
+ if (r == 0) return false;
+ if (r != -1) return true;
+ switch (GetLastError()) {
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn TranslateMessage(lpMsg: *const MSG) callconv(WINAPI) BOOL;
+pub fn translateMessage(lpMsg: *const MSG) bool {
+ return if (TranslateMessage(lpMsg) == 0) false else true;
+}
+
+pub extern "user32" fn DispatchMessageA(lpMsg: *const MSG) callconv(WINAPI) LRESULT;
+pub fn dispatchMessageA(lpMsg: *const MSG) LRESULT {
+ return DispatchMessageA(lpMsg);
+}
+
+pub extern "user32" fn DispatchMessageW(lpMsg: *const MSG) callconv(WINAPI) LRESULT;
+pub var pfnDispatchMessageW: @TypeOf(DispatchMessageW) = undefined;
+pub fn dispatchMessageW(lpMsg: *const MSG) LRESULT {
+ const function = selectSymbol(DispatchMessageW, pfnDispatchMessageW, .win2k);
+ return function(lpMsg);
+}
-pub const WNDPROC = fn (HWND, UINT, WPARAM, LPARAM) callconv(WINAPI) LRESULT;
+pub extern "user32" fn PostQuitMessage(nExitCode: i32) callconv(WINAPI) void;
+pub fn postQuitMessage(nExitCode: i32) void {
+ PostQuitMessage(nExitCode);
+}
+
+pub extern "user32" fn DefWindowProcA(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) callconv(WINAPI) LRESULT;
+pub fn defWindowProcA(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) LRESULT {
+ return DefWindowProcA(hWnd, Msg, wParam, lParam);
+}
+
+pub extern "user32" fn DefWindowProcW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) callconv(WINAPI) LRESULT;
+pub var pfnDefWindowProcW: @TypeOf(DefWindowProcW) = undefined;
+pub fn defWindowProcW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) LRESULT {
+ const function = selectSymbol(DefWindowProcW, pfnDefWindowProcW, .win2k);
+ return function(hWnd, Msg, wParam, lParam);
+}
+
+// === Windows ===
+
+pub const CS_VREDRAW = 0x0001;
+pub const CS_HREDRAW = 0x0002;
+pub const CS_DBLCLKS = 0x0008;
+pub const CS_OWNDC = 0x0020;
+pub const CS_CLASSDC = 0x0040;
+pub const CS_PARENTDC = 0x0080;
+pub const CS_NOCLOSE = 0x0200;
+pub const CS_SAVEBITS = 0x0800;
+pub const CS_BYTEALIGNCLIENT = 0x1000;
+pub const CS_BYTEALIGNWINDOW = 0x2000;
+pub const CS_GLOBALCLASS = 0x4000;
pub const WNDCLASSEXA = extern struct {
cbSize: UINT = @sizeOf(WNDCLASSEXA),
style: UINT,
lpfnWndProc: WNDPROC,
- cbClsExtra: i32,
- cbWndExtra: i32,
+ cbClsExtra: i32 = 0,
+ cbWndExtra: i32 = 0,
hInstance: HINSTANCE,
hIcon: ?HICON,
hCursor: ?HCURSOR,
hbrBackground: ?HBRUSH,
- lpszMenuName: ?LPCSTR,
- lpszClassName: LPCSTR,
+ lpszMenuName: ?[*:0]const u8,
+ lpszClassName: [*:0]const u8,
hIconSm: ?HICON,
};
-pub const POINT = extern struct {
- x: c_long, y: c_long
+pub const WNDCLASSEXW = extern struct {
+ cbSize: UINT = @sizeOf(WNDCLASSEXW),
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: i32 = 0,
+ cbWndExtra: i32 = 0,
+ hInstance: HINSTANCE,
+ hIcon: ?HICON,
+ hCursor: ?HCURSOR,
+ hbrBackground: ?HBRUSH,
+ lpszMenuName: ?[*:0]const u16,
+ lpszClassName: [*:0]const u16,
+ hIconSm: ?HICON,
};
-pub const MSG = extern struct {
- hWnd: ?HWND,
- message: UINT,
- wParam: WPARAM,
- lParam: LPARAM,
- time: DWORD,
- pt: POINT,
- lPrivate: DWORD,
-};
+pub extern "user32" fn RegisterClassExA(*const WNDCLASSEXA) callconv(WINAPI) ATOM;
+pub fn registerClassExA(window_class: *const WNDCLASSEXA) !ATOM {
+ const atom = RegisterClassExA(window_class);
+ if (atom != 0) return atom;
+ switch (GetLastError()) {
+ .CLASS_ALREADY_EXISTS => return error.AlreadyExists,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
-pub extern "user32" fn CreateWindowExA(
- dwExStyle: DWORD,
- lpClassName: LPCSTR,
- lpWindowName: LPCSTR,
- dwStyle: DWORD,
- X: i32,
- Y: i32,
- nWidth: i32,
- nHeight: i32,
- hWindParent: ?HWND,
- hMenu: ?HMENU,
- hInstance: HINSTANCE,
- lpParam: ?LPVOID,
-) callconv(WINAPI) ?HWND;
+pub extern "user32" fn RegisterClassExW(*const WNDCLASSEXW) callconv(WINAPI) ATOM;
+pub var pfnRegisterClassExW: @TypeOf(RegisterClassExW) = undefined;
+pub fn registerClassExW(window_class: *const WNDCLASSEXW) !ATOM {
+ const function = selectSymbol(RegisterClassExW, pfnRegisterClassExW, .win2k);
+ const atom = function(window_class);
+ if (atom != 0) return atom;
+ switch (GetLastError()) {
+ .CLASS_ALREADY_EXISTS => return error.AlreadyExists,
+ .CALL_NOT_IMPLEMENTED => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn UnregisterClassA(lpClassName: [*:0]const u8, hInstance: HINSTANCE) callconv(WINAPI) BOOL;
+pub fn unregisterClassA(lpClassName: [*:0]const u8, hInstance: HINSTANCE) !void {
+ if (UnregisterClassA(lpClassName, hInstance) == 0) {
+ switch (GetLastError()) {
+ .CLASS_DOES_NOT_EXIST => return error.ClassDoesNotExist,
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+}
+
+pub extern "user32" fn UnregisterClassW(lpClassName: [*:0]const u16, hInstance: HINSTANCE) callconv(WINAPI) BOOL;
+pub var pfnUnregisterClassW: @TypeOf(UnregisterClassW) = undefined;
+pub fn unregisterClassW(lpClassName: [*:0]const u16, hInstance: HINSTANCE) !void {
+ const function = selectSymbol(UnregisterClassW, pfnUnregisterClassW, .win2k);
+ if (function(lpClassName, hInstance) == 0) {
+ switch (GetLastError()) {
+ .CLASS_DOES_NOT_EXIST => return error.ClassDoesNotExist,
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+}
+
+pub const WS_OVERLAPPED = 0x00000000;
+pub const WS_POPUP = 0x80000000;
+pub const WS_CHILD = 0x40000000;
+pub const WS_MINIMIZE = 0x20000000;
+pub const WS_VISIBLE = 0x10000000;
+pub const WS_DISABLED = 0x08000000;
+pub const WS_CLIPSIBLINGS = 0x04000000;
+pub const WS_CLIPCHILDREN = 0x02000000;
+pub const WS_MAXIMIZE = 0x01000000;
+pub const WS_CAPTION = WS_BORDER | WS_DLGFRAME;
+pub const WS_BORDER = 0x00800000;
+pub const WS_DLGFRAME = 0x00400000;
+pub const WS_VSCROLL = 0x00200000;
+pub const WS_HSCROLL = 0x00100000;
+pub const WS_SYSMENU = 0x00080000;
+pub const WS_THICKFRAME = 0x00040000;
+pub const WS_GROUP = 0x00020000;
+pub const WS_TABSTOP = 0x00010000;
+pub const WS_MINIMIZEBOX = 0x00020000;
+pub const WS_MAXIMIZEBOX = 0x00010000;
+pub const WS_TILED = WS_OVERLAPPED;
+pub const WS_ICONIC = WS_MINIMIZE;
+pub const WS_SIZEBOX = WS_THICKFRAME;
+pub const WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW;
+pub const WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
+pub const WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU;
+pub const WS_CHILDWINDOW = WS_CHILD;
+
+pub const WS_EX_DLGMODALFRAME = 0x00000001;
+pub const WS_EX_NOPARENTNOTIFY = 0x00000004;
+pub const WS_EX_TOPMOST = 0x00000008;
+pub const WS_EX_ACCEPTFILES = 0x00000010;
+pub const WS_EX_TRANSPARENT = 0x00000020;
+pub const WS_EX_MDICHILD = 0x00000040;
+pub const WS_EX_TOOLWINDOW = 0x00000080;
+pub const WS_EX_WINDOWEDGE = 0x00000100;
+pub const WS_EX_CLIENTEDGE = 0x00000200;
+pub const WS_EX_CONTEXTHELP = 0x00000400;
+pub const WS_EX_RIGHT = 0x00001000;
+pub const WS_EX_LEFT = 0x00000000;
+pub const WS_EX_RTLREADING = 0x00002000;
+pub const WS_EX_LTRREADING = 0x00000000;
+pub const WS_EX_LEFTSCROLLBAR = 0x00004000;
+pub const WS_EX_RIGHTSCROLLBAR = 0x00000000;
+pub const WS_EX_CONTROLPARENT = 0x00010000;
+pub const WS_EX_STATICEDGE = 0x00020000;
+pub const WS_EX_APPWINDOW = 0x00040000;
+pub const WS_EX_LAYERED = 0x00080000;
+pub const WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE;
+pub const WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
+
+pub const CW_USEDEFAULT = @bitCast(i32, @as(u32, 0x80000000));
+
+pub extern "user32" fn CreateWindowExA(dwExStyle: DWORD, lpClassName: [*:0]const u8, lpWindowName: [*:0]const u8, dwStyle: DWORD, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?LPVOID) callconv(WINAPI) ?HWND;
+pub fn createWindowExA(dwExStyle: u32, lpClassName: [*:0]const u8, lpWindowName: [*:0]const u8, dwStyle: u32, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?*c_void) !HWND {
+ const window = CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWindParent, hMenu, hInstance, lpParam);
+ if (window) |win| return win;
+
+ switch (GetLastError()) {
+ .CLASS_DOES_NOT_EXIST => return error.ClassDoesNotExist,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn CreateWindowExW(dwExStyle: DWORD, lpClassName: [*:0]const u16, lpWindowName: [*:0]const u16, dwStyle: DWORD, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?LPVOID) callconv(WINAPI) ?HWND;
+pub var pfnCreateWindowExW: @TypeOf(RegisterClassExW) = undefined;
+pub fn createWindowExW(dwExStyle: u32, lpClassName: [*:0]const u16, lpWindowName: [*:0]const u16, dwStyle: u32, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?*c_void) !HWND {
+ const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k);
+ const window = function(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWindParent, hMenu, hInstance, lpParam);
+ if (window) |win| return win;
+
+ switch (GetLastError()) {
+ .CLASS_DOES_NOT_EXIST => return error.ClassDoesNotExist,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn DestroyWindow(hWnd: HWND) callconv(WINAPI) BOOL;
+pub fn destroyWindow(hWnd: HWND) !void {
+ if (DestroyWindow(hWnd) == 0) {
+ switch (GetLastError()) {
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+}
+
+pub const SW_HIDE = 0;
+pub const SW_SHOWNORMAL = 1;
+pub const SW_NORMAL = 1;
+pub const SW_SHOWMINIMIZED = 2;
+pub const SW_SHOWMAXIMIZED = 3;
+pub const SW_MAXIMIZE = 3;
+pub const SW_SHOWNOACTIVATE = 4;
+pub const SW_SHOW = 5;
+pub const SW_MINIMIZE = 6;
+pub const SW_SHOWMINNOACTIVE = 7;
+pub const SW_SHOWNA = 8;
+pub const SW_RESTORE = 9;
+pub const SW_SHOWDEFAULT = 10;
+pub const SW_FORCEMINIMIZE = 11;
+pub const SW_MAX = 11;
+
+pub extern "user32" fn ShowWindow(hWnd: HWND, nCmdShow: i32) callconv(WINAPI) BOOL;
+pub fn showWindow(hWnd: HWND, nCmdShow: i32) bool {
+ return (ShowWindow(hWnd, nCmdShow) == TRUE);
+}
+
+pub extern "user32" fn UpdateWindow(hWnd: HWND) callconv(WINAPI) BOOL;
+pub fn updateWindow(hWnd: HWND) !void {
+ if (ShowWindow(hWnd, nCmdShow) == 0) {
+ switch (GetLastError()) {
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+}
+
+pub extern "user32" fn AdjustWindowRectEx(lpRect: *RECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) callconv(WINAPI) BOOL;
+pub fn adjustWindowRectEx(lpRect: *RECT, dwStyle: u32, bMenu: bool, dwExStyle: u32) !void {
+ assert(dwStyle & WS_OVERLAPPED == 0);
+
+ if (AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle) == 0) {
+ switch (GetLastError()) {
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+}
+
+pub const GWL_WNDPROC = -4;
+pub const GWL_HINSTANCE = -6;
+pub const GWL_HWNDPARENT = -8;
+pub const GWL_STYLE = -16;
+pub const GWL_EXSTYLE = -20;
+pub const GWL_USERDATA = -21;
+pub const GWL_ID = -12;
+
+pub extern "user32" fn GetWindowLongA(hWnd: HWND, nIndex: i32) callconv(WINAPI) LONG;
+pub fn getWindowLongA(hWnd: HWND, nIndex: i32) !i32 {
+ const value = GetWindowLongA(hWnd, nIndex);
+ if (value != 0) return value;
+
+ switch (GetLastError()) {
+ .SUCCESS => return 0,
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn GetWindowLongW(hWnd: HWND, nIndex: i32) callconv(WINAPI) LONG;
+pub var pfnGetWindowLongW: @TypeOf(GetWindowLongW) = undefined;
+pub fn getWindowLongW(hWnd: HWND, nIndex: i32) !i32 {
+ const function = selectSymbol(GetWindowLongW, pfnGetWindowLongW, .win2k);
+
+ const value = function(hWnd, nIndex);
+ if (value != 0) return value;
+
+ switch (GetLastError()) {
+ .SUCCESS => return 0,
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn GetWindowLongPtrA(hWnd: HWND, nIndex: i32) callconv(WINAPI) LONG_PTR;
+pub fn getWindowLongPtrA(hWnd: HWND, nIndex: i32) !isize {
+ // "When compiling for 32-bit Windows, GetWindowLongPtr is defined as a call to the GetWindowLong function."
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw
+ if (@sizeOf(LONG_PTR) == 4) return getWindowLongA(hWnd, nIndex);
+
+ const value = GetWindowLongPtrA(hWnd, nIndex);
+ if (value != 0) return value;
+
+ switch (GetLastError()) {
+ .SUCCESS => return 0,
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn GetWindowLongPtrW(hWnd: HWND, nIndex: i32) callconv(WINAPI) LONG_PTR;
+pub var pfnGetWindowLongPtrW: @TypeOf(GetWindowLongPtrW) = undefined;
+pub fn getWindowLongPtrW(hWnd: HWND, nIndex: i32) !isize {
+ if (@sizeOf(LONG_PTR) == 4) return getWindowLongW(hWnd, nIndex);
+ const function = selectSymbol(GetWindowLongPtrW, pfnGetWindowLongPtrW, .win2k);
+
+ const value = function(hWnd, nIndex);
+ if (value != 0) return value;
+
+ switch (GetLastError()) {
+ .SUCCESS => return 0,
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn SetWindowLongA(hWnd: HWND, nIndex: i32, dwNewLong: LONG) callconv(WINAPI) LONG;
+pub fn setWindowLongA(hWnd: HWND, nIndex: i32, dwNewLong: i32) !i32 {
+ // [...] you should clear the last error information by calling SetLastError with 0 before calling SetWindowLong.
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlonga
+ SetLastError(.SUCCESS);
+
+ const value = SetWindowLongA(hWnd, nIndex, dwNewLong);
+ if (value != 0) return value;
+
+ switch (GetLastError()) {
+ .SUCCESS => return 0,
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn SetWindowLongW(hWnd: HWND, nIndex: i32, dwNewLong: LONG) callconv(WINAPI) LONG;
+pub var pfnSetWindowLongW: @TypeOf(SetWindowLongW) = undefined;
+pub fn setWindowLongW(hWnd: HWND, nIndex: i32, dwNewLong: i32) !i32 {
+ const function = selectSymbol(SetWindowLongW, pfnSetWindowLongW, .win2k);
+
+ SetLastError(.SUCCESS);
+ const value = function(hWnd, nIndex, dwNewLong);
+ if (value != 0) return value;
+
+ switch (GetLastError()) {
+ .SUCCESS => return 0,
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn SetWindowLongPtrA(hWnd: HWND, nIndex: i32, dwNewLong: LONG_PTR) callconv(WINAPI) LONG_PTR;
+pub fn setWindowLongPtrA(hWnd: HWND, nIndex: i32, dwNewLong: isize) !isize {
+ // "When compiling for 32-bit Windows, GetWindowLongPtr is defined as a call to the GetWindowLong function."
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw
+ if (@sizeOf(LONG_PTR) == 4) return setWindowLongA(hWnd, nIndex, dwNewLong);
+
+ SetLastError(.SUCCESS);
+ const value = SetWindowLongPtrA(hWnd, nIndex, dwNewLong);
+ if (value != 0) return value;
+
+ switch (GetLastError()) {
+ .SUCCESS => return 0,
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn SetWindowLongPtrW(hWnd: HWND, nIndex: i32, dwNewLong: LONG_PTR) callconv(WINAPI) LONG_PTR;
+pub var pfnSetWindowLongPtrW: @TypeOf(SetWindowLongPtrW) = undefined;
+pub fn setWindowLongPtrW(hWnd: HWND, nIndex: i32, dwNewLong: isize) !isize {
+ if (@sizeOf(LONG_PTR) == 4) return setWindowLongW(hWnd, nIndex, dwNewLong);
+ const function = selectSymbol(SetWindowLongPtrW, pfnSetWindowLongPtrW, .win2k);
+
+ SetLastError(.SUCCESS);
+ const value = function(hWnd, nIndex, dwNewLong);
+ if (value != 0) return value;
+
+ switch (GetLastError()) {
+ .SUCCESS => return 0,
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
-pub extern "user32" fn RegisterClassExA(*const WNDCLASSEXA) callconv(WINAPI) c_ushort;
-pub extern "user32" fn DefWindowProcA(HWND, Msg: UINT, WPARAM, LPARAM) callconv(WINAPI) LRESULT;
-pub extern "user32" fn ShowWindow(hWnd: ?HWND, nCmdShow: i32) callconv(WINAPI) bool;
-pub extern "user32" fn UpdateWindow(hWnd: ?HWND) callconv(WINAPI) bool;
pub extern "user32" fn GetDC(hWnd: ?HWND) callconv(WINAPI) ?HDC;
+pub fn getDC(hWnd: ?HWND) !HDC {
+ const hdc = GetDC(hWnd);
+ if (hdc) |h| return h;
-pub extern "user32" fn PeekMessageA(
- lpMsg: ?*MSG,
- hWnd: ?HWND,
- wMsgFilterMin: UINT,
- wMsgFilterMax: UINT,
- wRemoveMsg: UINT,
-) callconv(WINAPI) bool;
+ switch (GetLastError()) {
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
-pub extern "user32" fn GetMessageA(
- lpMsg: ?*MSG,
- hWnd: ?HWND,
- wMsgFilterMin: UINT,
- wMsgFilterMax: UINT,
-) callconv(WINAPI) bool;
+pub extern "user32" fn ReleaseDC(hWnd: ?HWND, hDC: HDC) callconv(WINAPI) i32;
+pub fn releaseDC(hWnd: ?HWND, hDC: HDC) bool {
+ return if (ReleaseDC(hWnd, hDC) == 1) true else false;
+}
-pub extern "user32" fn TranslateMessage(lpMsg: *const MSG) callconv(WINAPI) bool;
-pub extern "user32" fn DispatchMessageA(lpMsg: *const MSG) callconv(WINAPI) LRESULT;
-pub extern "user32" fn PostQuitMessage(nExitCode: i32) callconv(WINAPI) void;
+// === Modal dialogue boxes ===
+
+pub const MB_OK = 0x00000000;
+pub const MB_OKCANCEL = 0x00000001;
+pub const MB_ABORTRETRYIGNORE = 0x00000002;
+pub const MB_YESNOCANCEL = 0x00000003;
+pub const MB_YESNO = 0x00000004;
+pub const MB_RETRYCANCEL = 0x00000005;
+pub const MB_CANCELTRYCONTINUE = 0x00000006;
+pub const MB_ICONHAND = 0x00000010;
+pub const MB_ICONQUESTION = 0x00000020;
+pub const MB_ICONEXCLAMATION = 0x00000030;
+pub const MB_ICONASTERISK = 0x00000040;
+pub const MB_USERICON = 0x00000080;
+pub const MB_ICONWARNING = MB_ICONEXCLAMATION;
+pub const MB_ICONERROR = MB_ICONHAND;
+pub const MB_ICONINFORMATION = MB_ICONASTERISK;
+pub const MB_ICONSTOP = MB_ICONHAND;
+pub const MB_DEFBUTTON1 = 0x00000000;
+pub const MB_DEFBUTTON2 = 0x00000100;
+pub const MB_DEFBUTTON3 = 0x00000200;
+pub const MB_DEFBUTTON4 = 0x00000300;
+pub const MB_APPLMODAL = 0x00000000;
+pub const MB_SYSTEMMODAL = 0x00001000;
+pub const MB_TASKMODAL = 0x00002000;
+pub const MB_HELP = 0x00004000;
+pub const MB_NOFOCUS = 0x00008000;
+pub const MB_SETFOREGROUND = 0x00010000;
+pub const MB_DEFAULT_DESKTOP_ONLY = 0x00020000;
+pub const MB_TOPMOST = 0x00040000;
+pub const MB_RIGHT = 0x00080000;
+pub const MB_RTLREADING = 0x00100000;
+pub const MB_TYPEMASK = 0x0000000F;
+pub const MB_ICONMASK = 0x000000F0;
+pub const MB_DEFMASK = 0x00000F00;
+pub const MB_MODEMASK = 0x00003000;
+pub const MB_MISCMASK = 0x0000C000;
+
+pub const IDOK = 1;
+pub const IDCANCEL = 2;
+pub const IDABORT = 3;
+pub const IDRETRY = 4;
+pub const IDIGNORE = 5;
+pub const IDYES = 6;
+pub const IDNO = 7;
+pub const IDCLOSE = 8;
+pub const IDHELP = 9;
+pub const IDTRYAGAIN = 10;
+pub const IDCONTINUE = 11;
+
+pub extern "user32" fn MessageBoxA(hWnd: ?HWND, lpText: [*:0]const u8, lpCaption: [*:0]const u8, uType: UINT) callconv(WINAPI) i32;
+pub fn messageBoxA(hWnd: ?HWND, lpText: [*:0]const u8, lpCaption: [*:0]const u8, uType: u32) !i32 {
+ const value = MessageBoxA(hWnd, lpText, lpCaption, uType);
+ if (value != 0) return value;
+ switch (GetLastError()) {
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
+
+pub extern "user32" fn MessageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: ?[*:0]const u16, uType: UINT) callconv(WINAPI) i32;
+pub var pfnMessageBoxW: @TypeOf(MessageBoxW) = undefined;
+pub fn messageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: [*:0]const u16, uType: u32) !i32 {
+ const function = selectSymbol(pfnMessageBoxW, MessageBoxW, .win2k);
+ const value = function(hWnd, lpText, lpCaption, uType);
+ if (value != 0) return value;
+ switch (GetLastError()) {
+ .INVALID_WINDOW_HANDLE => unreachable,
+ .INVALID_PARAMETER => unreachable,
+ else => |err| return windows.unexpectedError(err),
+ }
+}
diff --git a/lib/std/os/windows/win32error.zig b/lib/std/os/windows/win32error.zig
index 2e1f111d92..61bbcac8bb 100644
--- a/lib/std/os/windows/win32error.zig
+++ b/lib/std/os/windows/win32error.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig
index 7123869d65..1dd2ce738b 100644
--- a/lib/std/os/windows/ws2_32.zig
+++ b/lib/std/os/windows/ws2_32.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/packed_int_array.zig b/lib/std/packed_int_array.zig
index c3222c483c..c53d6f0505 100644
--- a/lib/std/packed_int_array.zig
+++ b/lib/std/packed_int_array.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -203,6 +203,14 @@ pub fn PackedIntArrayEndian(comptime Int: type, comptime endian: builtin.Endian,
return self;
}
+ ///Initialize all entries of a packed array to the same value
+ pub fn initAllTo(int: Int) Self {
+ // TODO: use `var self = @as(Self, undefined);` https://github.com/ziglang/zig/issues/7635
+ var self = Self{ .bytes = [_]u8{0} ** total_bytes };
+ self.setAll(int);
+ return self;
+ }
+
///Return the Int stored at index
pub fn get(self: Self, index: usize) Int {
debug.assert(index < int_count);
@@ -215,6 +223,14 @@ pub fn PackedIntArrayEndian(comptime Int: type, comptime endian: builtin.Endian,
return Io.set(&self.bytes, index, 0, int);
}
+ ///Set all entries of a packed array to the same value
+ pub fn setAll(self: *Self, int: Int) void {
+ var i: usize = 0;
+ while (i < int_count) : (i += 1) {
+ self.set(i, int);
+ }
+ }
+
///Create a PackedIntSlice of the array from given start to given end
pub fn slice(self: *Self, start: usize, end: usize) PackedIntSliceEndian(Int, endian) {
debug.assert(start < int_count);
@@ -365,7 +381,15 @@ test "PackedIntArray init" {
const PackedArray = PackedIntArray(u3, 8);
var packed_array = PackedArray.init([_]u3{ 0, 1, 2, 3, 4, 5, 6, 7 });
var i = @as(usize, 0);
- while (i < packed_array.len()) : (i += 1) testing.expect(packed_array.get(i) == i);
+ while (i < packed_array.len()) : (i += 1) testing.expectEqual(@intCast(u3, i), packed_array.get(i));
+}
+
+test "PackedIntArray initAllTo" {
+ if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
+ const PackedArray = PackedIntArray(u3, 8);
+ var packed_array = PackedArray.initAllTo(5);
+ var i = @as(usize, 0);
+ while (i < packed_array.len()) : (i += 1) testing.expectEqual(@as(u3, 5), packed_array.get(i));
}
test "PackedIntSlice" {
diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig
index c35ab6f723..6a47cd6e8b 100644
--- a/lib/std/pdb.zig
+++ b/lib/std/pdb.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -662,6 +662,7 @@ const MsfStream = struct {
fn read(self: *MsfStream, buffer: []u8) !usize {
var block_id = @intCast(usize, self.pos / self.block_size);
+ if (block_id >= self.blocks.len) return 0; // End of Stream
var block = self.blocks[block_id];
var offset = self.pos % self.block_size;
@@ -680,6 +681,7 @@ const MsfStream = struct {
if (offset == self.block_size) {
offset = 0;
block_id += 1;
+ if (block_id >= self.blocks.len) break; // End of Stream
block = self.blocks[block_id];
try self.in_file.seekTo(block * self.block_size);
}
@@ -716,8 +718,4 @@ const MsfStream = struct {
pub fn reader(self: *MsfStream) std.io.Reader(*MsfStream, Error, read) {
return .{ .context = self };
}
- /// Deprecated: use `reader`
- pub fn inStream(self: *MsfStream) std.io.InStream(*MsfStream, Error, read) {
- return .{ .context = self };
- }
};
diff --git a/lib/std/priority_queue.zig b/lib/std/priority_queue.zig
index 951e07bfb6..f5c01edff5 100644
--- a/lib/std/priority_queue.zig
+++ b/lib/std/priority_queue.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/process.zig b/lib/std/process.zig
index e12bc28c0c..3ad73db420 100644
--- a/lib/std/process.zig
+++ b/lib/std/process.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -13,6 +13,7 @@ const math = std.math;
const Allocator = mem.Allocator;
const assert = std.debug.assert;
const testing = std.testing;
+const child_process = @import("child_process.zig");
pub const abort = os.abort;
pub const exit = os.exit;
@@ -516,7 +517,8 @@ test "args iterator" {
const given_suffix = std.fs.path.basename(prog_name);
testing.expect(mem.eql(u8, expected_suffix, given_suffix));
- testing.expectEqual(it.next(ga), null);
+ testing.expect(it.skip()); // Skip over zig_exe_path, passed to the test runner
+ testing.expect(it.next(ga) == null);
testing.expect(!it.skip());
}
@@ -594,7 +596,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u16, expected_args: []const []con
for (expected_args) |expected_arg| {
const arg = it.next(std.testing.allocator).? catch unreachable;
defer std.testing.allocator.free(arg);
- testing.expectEqualSlices(u8, expected_arg, arg);
+ testing.expectEqualStrings(expected_arg, arg);
}
testing.expect(it.next(std.testing.allocator) == null);
}
@@ -607,7 +609,7 @@ pub const UserInfo = struct {
/// POSIX function which gets a uid from username.
pub fn getUserInfo(name: []const u8) !UserInfo {
return switch (builtin.os.tag) {
- .linux, .macos, .watchos, .tvos, .ios, .freebsd, .netbsd, .openbsd => posixGetUserInfo(name),
+ .linux, .macos, .watchos, .tvos, .ios, .freebsd, .netbsd, .openbsd, .haiku => posixGetUserInfo(name),
else => @compileError("Unsupported OS"),
};
}
@@ -775,6 +777,82 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]
}
return paths.toOwnedSlice();
},
+ // revisit if Haiku implements dl_iterat_phdr (https://dev.haiku-os.org/ticket/15743)
+ .haiku => {
+ var paths = List.init(allocator);
+ errdefer {
+ const slice = paths.toOwnedSlice();
+ for (slice) |item| {
+ allocator.free(item);
+ }
+ allocator.free(slice);
+ }
+
+ var b = "/boot/system/runtime_loader";
+ const item = try allocator.dupeZ(u8, mem.spanZ(b));
+ errdefer allocator.free(item);
+ try paths.append(item);
+
+ return paths.toOwnedSlice();
+ },
else => @compileError("getSelfExeSharedLibPaths unimplemented for this target"),
}
}
+
+/// Tells whether calling the `execv` or `execve` functions will be a compile error.
+pub const can_execv = std.builtin.os.tag != .windows;
+
+pub const ExecvError = std.os.ExecveError || error{OutOfMemory};
+
+/// Replaces the current process image with the executed process.
+/// This function must allocate memory to add a null terminating bytes on path and each arg.
+/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
+/// pointers after the args and after the environment variables.
+/// `argv[0]` is the executable path.
+/// This function also uses the PATH environment variable to get the full path to the executable.
+/// Due to the heap-allocation, it is illegal to call this function in a fork() child.
+/// For that use case, use the `std.os` functions directly.
+pub fn execv(allocator: *mem.Allocator, argv: []const []const u8) ExecvError {
+ return execve(allocator, argv, null);
+}
+
+/// Replaces the current process image with the executed process.
+/// This function must allocate memory to add a null terminating bytes on path and each arg.
+/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
+/// pointers after the args and after the environment variables.
+/// `argv[0]` is the executable path.
+/// This function also uses the PATH environment variable to get the full path to the executable.
+/// Due to the heap-allocation, it is illegal to call this function in a fork() child.
+/// For that use case, use the `std.os` functions directly.
+pub fn execve(
+ allocator: *mem.Allocator,
+ argv: []const []const u8,
+ env_map: ?*const std.BufMap,
+) ExecvError {
+ if (!can_execv) @compileError("The target OS does not support execv");
+
+ var arena_allocator = std.heap.ArenaAllocator.init(allocator);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const argv_buf = try arena.allocSentinel(?[*:0]u8, argv.len, null);
+ for (argv) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
+
+ const envp = m: {
+ if (env_map) |m| {
+ const envp_buf = try child_process.createNullDelimitedEnvMap(arena, m);
+ break :m envp_buf.ptr;
+ } else if (std.builtin.link_libc) {
+ break :m std.c.environ;
+ } else if (std.builtin.output_mode == .Exe) {
+ // Then we have Zig start code and this works.
+ // TODO type-safety for null-termination of `os.environ`.
+ break :m @ptrCast([*:null]?[*:0]u8, os.environ.ptr);
+ } else {
+ // TODO come up with a solution for this.
+ @compileError("missing std lib enhancement: std.process.execv implementation has no way to collect the environment variables to forward to the child process");
+ }
+ };
+
+ return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp);
+}
diff --git a/lib/std/progress.zig b/lib/std/progress.zig
deleted file mode 100644
index 82f2801fa1..0000000000
--- a/lib/std/progress.zig
+++ /dev/null
@@ -1,310 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("std");
-const windows = std.os.windows;
-const testing = std.testing;
-const assert = std.debug.assert;
-
-/// This API is non-allocating and non-fallible. The tradeoff is that users of
-/// this API must provide the storage for each `Progress.Node`.
-/// Initialize the struct directly, overriding these fields as desired:
-/// * `refresh_rate_ms`
-/// * `initial_delay_ms`
-pub const Progress = struct {
- /// `null` if the current node (and its children) should
- /// not print on update()
- terminal: ?std.fs.File = undefined,
-
- /// Whether the terminal supports ANSI escape codes.
- supports_ansi_escape_codes: bool = false,
-
- root: Node = undefined,
-
- /// Keeps track of how much time has passed since the beginning.
- /// Used to compare with `initial_delay_ms` and `refresh_rate_ms`.
- timer: std.time.Timer = undefined,
-
- /// When the previous refresh was written to the terminal.
- /// Used to compare with `refresh_rate_ms`.
- prev_refresh_timestamp: u64 = undefined,
-
- /// This buffer represents the maximum number of bytes written to the terminal
- /// with each refresh.
- output_buffer: [100]u8 = undefined,
-
- /// How many nanoseconds between writing updates to the terminal.
- refresh_rate_ns: u64 = 50 * std.time.ns_per_ms,
-
- /// How many nanoseconds to keep the output hidden
- initial_delay_ns: u64 = 500 * std.time.ns_per_ms,
-
- done: bool = true,
-
- /// Keeps track of how many columns in the terminal have been output, so that
- /// we can move the cursor back later.
- columns_written: usize = undefined,
-
- /// Represents one unit of progress. Each node can have children nodes, or
- /// one can use integers with `update`.
- pub const Node = struct {
- context: *Progress,
- parent: ?*Node,
- completed_items: usize,
- name: []const u8,
- recently_updated_child: ?*Node = null,
-
- /// This field may be updated freely.
- estimated_total_items: ?usize,
-
- /// Create a new child progress node.
- /// Call `Node.end` when done.
- /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
- /// API to set `self.parent.recently_updated_child` with the return value.
- /// Until that is fixed you probably want to call `activate` on the return value.
- pub fn start(self: *Node, name: []const u8, estimated_total_items: ?usize) Node {
- return Node{
- .context = self.context,
- .parent = self,
- .completed_items = 0,
- .name = name,
- .estimated_total_items = estimated_total_items,
- };
- }
-
- /// This is the same as calling `start` and then `end` on the returned `Node`.
- pub fn completeOne(self: *Node) void {
- if (self.parent) |parent| parent.recently_updated_child = self;
- self.completed_items += 1;
- self.context.maybeRefresh();
- }
-
- pub fn end(self: *Node) void {
- self.context.maybeRefresh();
- if (self.parent) |parent| {
- if (parent.recently_updated_child) |parent_child| {
- if (parent_child == self) {
- parent.recently_updated_child = null;
- }
- }
- parent.completeOne();
- } else {
- self.context.done = true;
- self.context.refresh();
- }
- }
-
- /// Tell the parent node that this node is actively being worked on.
- pub fn activate(self: *Node) void {
- if (self.parent) |parent| parent.recently_updated_child = self;
- }
- };
-
- /// Create a new progress node.
- /// Call `Node.end` when done.
- /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
- /// API to return Progress rather than accept it as a parameter.
- pub fn start(self: *Progress, name: []const u8, estimated_total_items: ?usize) !*Node {
- const stderr = std.io.getStdErr();
- self.terminal = null;
- if (stderr.supportsAnsiEscapeCodes()) {
- self.terminal = stderr;
- self.supports_ansi_escape_codes = true;
- } else if (std.builtin.os.tag == .windows and stderr.isTty()) {
- self.terminal = stderr;
- }
- self.root = Node{
- .context = self,
- .parent = null,
- .completed_items = 0,
- .name = name,
- .estimated_total_items = estimated_total_items,
- };
- self.columns_written = 0;
- self.prev_refresh_timestamp = 0;
- self.timer = try std.time.Timer.start();
- self.done = false;
- return &self.root;
- }
-
- /// Updates the terminal if enough time has passed since last update.
- pub fn maybeRefresh(self: *Progress) void {
- const now = self.timer.read();
- if (now < self.initial_delay_ns) return;
- if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;
- self.refresh();
- }
-
- /// Updates the terminal and resets `self.next_refresh_timestamp`.
- pub fn refresh(self: *Progress) void {
- const file = self.terminal orelse return;
-
- const prev_columns_written = self.columns_written;
- var end: usize = 0;
- if (self.columns_written > 0) {
- // restore the cursor position by moving the cursor
- // `columns_written` cells to the left, then clear the rest of the
- // line
- if (self.supports_ansi_escape_codes) {
- end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{}D", .{self.columns_written}) catch unreachable).len;
- end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
- } else if (std.builtin.os.tag == .windows) winapi: {
- var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
- if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
- unreachable;
-
- var cursor_pos = windows.COORD{
- .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
- .Y = info.dwCursorPosition.Y,
- };
-
- if (cursor_pos.X < 0)
- cursor_pos.X = 0;
-
- const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X);
-
- var written: windows.DWORD = undefined;
- if (windows.kernel32.FillConsoleOutputAttribute(
- file.handle,
- info.wAttributes,
- fill_chars,
- cursor_pos,
- &written,
- ) != windows.TRUE) {
- // Stop trying to write to this file.
- self.terminal = null;
- break :winapi;
- }
- if (windows.kernel32.FillConsoleOutputCharacterA(
- file.handle,
- ' ',
- fill_chars,
- cursor_pos,
- &written,
- ) != windows.TRUE) unreachable;
-
- if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE)
- unreachable;
- } else unreachable;
-
- self.columns_written = 0;
- }
-
- if (!self.done) {
- var need_ellipse = false;
- var maybe_node: ?*Node = &self.root;
- while (maybe_node) |node| {
- if (need_ellipse) {
- self.bufWrite(&end, "... ", .{});
- }
- need_ellipse = false;
- if (node.name.len != 0 or node.estimated_total_items != null) {
- if (node.name.len != 0) {
- self.bufWrite(&end, "{}", .{node.name});
- need_ellipse = true;
- }
- if (node.estimated_total_items) |total| {
- if (need_ellipse) self.bufWrite(&end, " ", .{});
- self.bufWrite(&end, "[{}/{}] ", .{ node.completed_items + 1, total });
- need_ellipse = false;
- } else if (node.completed_items != 0) {
- if (need_ellipse) self.bufWrite(&end, " ", .{});
- self.bufWrite(&end, "[{}] ", .{node.completed_items + 1});
- need_ellipse = false;
- }
- }
- maybe_node = node.recently_updated_child;
- }
- if (need_ellipse) {
- self.bufWrite(&end, "... ", .{});
- }
- }
-
- _ = file.write(self.output_buffer[0..end]) catch |e| {
- // Stop trying to write to this file once it errors.
- self.terminal = null;
- };
- self.prev_refresh_timestamp = self.timer.read();
- }
-
- pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
- const file = self.terminal orelse return;
- self.refresh();
- file.outStream().print(format, args) catch {
- self.terminal = null;
- return;
- };
- self.columns_written = 0;
- }
-
- fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
- if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
- const amt = written.len;
- end.* += amt;
- self.columns_written += amt;
- } else |err| switch (err) {
- error.NoSpaceLeft => {
- self.columns_written += self.output_buffer.len - end.*;
- end.* = self.output_buffer.len;
- },
- }
- const bytes_needed_for_esc_codes_at_end = if (std.builtin.os.tag == .windows) 0 else 11;
- const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
- if (end.* > max_end) {
- const suffix = "... ";
- self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
- std.mem.copy(u8, self.output_buffer[max_end..], suffix);
- end.* = max_end + suffix.len;
- }
- }
-};
-
-test "basic functionality" {
- var disable = true;
- if (disable) {
- // This test is disabled because it uses time.sleep() and is therefore slow. It also
- // prints bogus progress data to stderr.
- return error.SkipZigTest;
- }
- var progress = Progress{};
- const root_node = try progress.start("", 100);
- defer root_node.end();
-
- const sub_task_names = [_][]const u8{
- "reticulating splines",
- "adjusting shoes",
- "climbing towers",
- "pouring juice",
- };
- var next_sub_task: usize = 0;
-
- var i: usize = 0;
- while (i < 100) : (i += 1) {
- var node = root_node.start(sub_task_names[next_sub_task], 5);
- node.activate();
- next_sub_task = (next_sub_task + 1) % sub_task_names.len;
-
- node.completeOne();
- std.time.sleep(5 * std.time.ns_per_ms);
- node.completeOne();
- node.completeOne();
- std.time.sleep(5 * std.time.ns_per_ms);
- node.completeOne();
- node.completeOne();
- std.time.sleep(5 * std.time.ns_per_ms);
-
- node.end();
-
- std.time.sleep(5 * std.time.ns_per_ms);
- }
- {
- var node = root_node.start("this is a really long name designed to activate the truncation code. let's find out if it works", null);
- node.activate();
- std.time.sleep(10 * std.time.ns_per_ms);
- progress.refresh();
- std.time.sleep(10 * std.time.ns_per_ms);
- node.end();
- }
-}
diff --git a/lib/std/rand.zig b/lib/std/rand.zig
index a201968a5f..d0d400b5b0 100644
--- a/lib/std/rand.zig
+++ b/lib/std/rand.zig
@@ -1,22 +1,14 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
-//! The engines provided here should be initialized from an external source. For now, randomBytes
-//! from the crypto package is the most suitable. Be sure to use a CSPRNG when required, otherwise using
-//! a normal PRNG will be faster and use substantially less stack space.
-//!
-//! ```
-//! var buf: [8]u8 = undefined;
-//! try std.crypto.randomBytes(buf[0..]);
-//! const seed = mem.readIntLittle(u64, buf[0..8]);
-//!
-//! var r = DefaultPrng.init(seed);
-//!
-//! const s = r.random.int(u64);
-//! ```
+//! The engines provided here should be initialized from an external source.
+//! For a thread-local cryptographically secure pseudo random number generator,
+//! use `std.crypto.random`.
+//! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will
+//! be faster and use substantially less stack space.
//!
//! TODO(tiehuis): Benchmark these against other reference implementations.
@@ -36,6 +28,12 @@ pub const DefaultPrng = Xoroshiro128;
/// Cryptographically secure random numbers.
pub const DefaultCsprng = Gimli;
+pub const Isaac64 = @import("rand/Isaac64.zig");
+pub const Gimli = @import("rand/Gimli.zig");
+pub const Pcg = @import("rand/Pcg.zig");
+pub const Xoroshiro128 = @import("rand/Xoroshiro128.zig");
+pub const Sfc64 = @import("rand/Sfc64.zig");
+
pub const Random = struct {
fillFn: fn (r: *Random, buf: []u8) void,
@@ -491,7 +489,7 @@ test "Random Biased" {
//
// The number of cycles is thus limited to 64-bits regardless of the engine, but this
// is still plenty for practical purposes.
-const SplitMix64 = struct {
+pub const SplitMix64 = struct {
s: u64,
pub fn init(seed: u64) SplitMix64 {
@@ -525,557 +523,6 @@ test "splitmix64 sequence" {
}
}
-// PCG32 - http://www.pcg-random.org/
-//
-// PRNG
-pub const Pcg = struct {
- const default_multiplier = 6364136223846793005;
-
- random: Random,
-
- s: u64,
- i: u64,
-
- pub fn init(init_s: u64) Pcg {
- var pcg = Pcg{
- .random = Random{ .fillFn = fill },
- .s = undefined,
- .i = undefined,
- };
-
- pcg.seed(init_s);
- return pcg;
- }
-
- fn next(self: *Pcg) u32 {
- const l = self.s;
- self.s = l *% default_multiplier +% (self.i | 1);
-
- const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27);
- const rot = @intCast(u32, l >> 59);
-
- return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31));
- }
-
- fn seed(self: *Pcg, init_s: u64) void {
- // Pcg requires 128-bits of seed.
- var gen = SplitMix64.init(init_s);
- self.seedTwo(gen.next(), gen.next());
- }
-
- fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void {
- self.s = 0;
- self.i = (init_s << 1) | 1;
- self.s = self.s *% default_multiplier +% self.i;
- self.s +%= init_i;
- self.s = self.s *% default_multiplier +% self.i;
- }
-
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Pcg, "random", r);
-
- var i: usize = 0;
- const aligned_len = buf.len - (buf.len & 7);
-
- // Complete 4 byte segments.
- while (i < aligned_len) : (i += 4) {
- var n = self.next();
- comptime var j: usize = 0;
- inline while (j < 4) : (j += 1) {
- buf[i + j] = @truncate(u8, n);
- n >>= 8;
- }
- }
-
- // Remaining. (cuts the stream)
- if (i != buf.len) {
- var n = self.next();
- while (i < buf.len) : (i += 1) {
- buf[i] = @truncate(u8, n);
- n >>= 4;
- }
- }
- }
-};
-
-test "pcg sequence" {
- var r = Pcg.init(0);
- const s0: u64 = 0x9394bf54ce5d79de;
- const s1: u64 = 0x84e9c579ef59bbf7;
- r.seedTwo(s0, s1);
-
- const seq = [_]u32{
- 2881561918,
- 3063928540,
- 1199791034,
- 2487695858,
- 1479648952,
- 3247963454,
- };
-
- for (seq) |s| {
- expect(s == r.next());
- }
-}
-
-// Xoroshiro128+ - http://xoroshiro.di.unimi.it/
-//
-// PRNG
-pub const Xoroshiro128 = struct {
- random: Random,
-
- s: [2]u64,
-
- pub fn init(init_s: u64) Xoroshiro128 {
- var x = Xoroshiro128{
- .random = Random{ .fillFn = fill },
- .s = undefined,
- };
-
- x.seed(init_s);
- return x;
- }
-
- fn next(self: *Xoroshiro128) u64 {
- const s0 = self.s[0];
- var s1 = self.s[1];
- const r = s0 +% s1;
-
- s1 ^= s0;
- self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14);
- self.s[1] = math.rotl(u64, s1, @as(u8, 36));
-
- return r;
- }
-
- // Skip 2^64 places ahead in the sequence
- fn jump(self: *Xoroshiro128) void {
- var s0: u64 = 0;
- var s1: u64 = 0;
-
- const table = [_]u64{
- 0xbeac0467eba5facb,
- 0xd86b048b86aa9922,
- };
-
- inline for (table) |entry| {
- var b: usize = 0;
- while (b < 64) : (b += 1) {
- if ((entry & (@as(u64, 1) << @intCast(u6, b))) != 0) {
- s0 ^= self.s[0];
- s1 ^= self.s[1];
- }
- _ = self.next();
- }
- }
-
- self.s[0] = s0;
- self.s[1] = s1;
- }
-
- pub fn seed(self: *Xoroshiro128, init_s: u64) void {
- // Xoroshiro requires 128-bits of seed.
- var gen = SplitMix64.init(init_s);
-
- self.s[0] = gen.next();
- self.s[1] = gen.next();
- }
-
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Xoroshiro128, "random", r);
-
- var i: usize = 0;
- const aligned_len = buf.len - (buf.len & 7);
-
- // Complete 8 byte segments.
- while (i < aligned_len) : (i += 8) {
- var n = self.next();
- comptime var j: usize = 0;
- inline while (j < 8) : (j += 1) {
- buf[i + j] = @truncate(u8, n);
- n >>= 8;
- }
- }
-
- // Remaining. (cuts the stream)
- if (i != buf.len) {
- var n = self.next();
- while (i < buf.len) : (i += 1) {
- buf[i] = @truncate(u8, n);
- n >>= 8;
- }
- }
- }
-};
-
-test "xoroshiro sequence" {
- var r = Xoroshiro128.init(0);
- r.s[0] = 0xaeecf86f7878dd75;
- r.s[1] = 0x01cd153642e72622;
-
- const seq1 = [_]u64{
- 0xb0ba0da5bb600397,
- 0x18a08afde614dccc,
- 0xa2635b956a31b929,
- 0xabe633c971efa045,
- 0x9ac19f9706ca3cac,
- 0xf62b426578c1e3fb,
- };
-
- for (seq1) |s| {
- expect(s == r.next());
- }
-
- r.jump();
-
- const seq2 = [_]u64{
- 0x95344a13556d3e22,
- 0xb4fb32dafa4d00df,
- 0xb2011d9ccdcfe2dd,
- 0x05679a9b2119b908,
- 0xa860a1da7c9cd8a0,
- 0x658a96efe3f86550,
- };
-
- for (seq2) |s| {
- expect(s == r.next());
- }
-}
-
-// Gimli
-//
-// CSPRNG
-pub const Gimli = struct {
- random: Random,
- state: std.crypto.core.Gimli,
-
- pub const secret_seed_length = 32;
-
- /// The seed must be uniform, secret and `secret_seed_length` bytes long.
- /// It can be generated using `std.crypto.randomBytes()`.
- pub fn init(secret_seed: [secret_seed_length]u8) Gimli {
- var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
- mem.copy(u8, initial_state[0..secret_seed_length], &secret_seed);
- mem.set(u8, initial_state[secret_seed_length..], 0);
- var self = Gimli{
- .random = Random{ .fillFn = fill },
- .state = std.crypto.core.Gimli.init(initial_state),
- };
- return self;
- }
-
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Gimli, "random", r);
-
- if (buf.len != 0) {
- self.state.squeeze(buf);
- } else {
- self.state.permute();
- }
- mem.set(u8, self.state.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
- }
-};
-
-// ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html
-//
-// Follows the general idea of the implementation from here with a few shortcuts.
-// https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
-pub const Isaac64 = struct {
- random: Random,
-
- r: [256]u64,
- m: [256]u64,
- a: u64,
- b: u64,
- c: u64,
- i: usize,
-
- pub fn init(init_s: u64) Isaac64 {
- var isaac = Isaac64{
- .random = Random{ .fillFn = fill },
- .r = undefined,
- .m = undefined,
- .a = undefined,
- .b = undefined,
- .c = undefined,
- .i = undefined,
- };
-
- // seed == 0 => same result as the unseeded reference implementation
- isaac.seed(init_s, 1);
- return isaac;
- }
-
- fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
- const x = self.m[base + m1];
- self.a = mix +% self.m[base + m2];
-
- const y = self.a +% self.b +% self.m[@intCast(usize, (x >> 3) % self.m.len)];
- self.m[base + m1] = y;
-
- self.b = x +% self.m[@intCast(usize, (y >> 11) % self.m.len)];
- self.r[self.r.len - 1 - base - m1] = self.b;
- }
-
- fn refill(self: *Isaac64) void {
- const midpoint = self.r.len / 2;
-
- self.c +%= 1;
- self.b +%= self.c;
-
- {
- var i: usize = 0;
- while (i < midpoint) : (i += 4) {
- self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint);
- self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint);
- self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint);
- self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint);
- }
- }
-
- {
- var i: usize = 0;
- while (i < midpoint) : (i += 4) {
- self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0);
- self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0);
- self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0);
- self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0);
- }
- }
-
- self.i = 0;
- }
-
- fn next(self: *Isaac64) u64 {
- if (self.i >= self.r.len) {
- self.refill();
- }
-
- const value = self.r[self.i];
- self.i += 1;
- return value;
- }
-
- fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void {
- // We ignore the multi-pass requirement since we don't currently expose full access to
- // seeding the self.m array completely.
- mem.set(u64, self.m[0..], 0);
- self.m[0] = init_s;
-
- // prescrambled golden ratio constants
- var a = [_]u64{
- 0x647c4677a2884b7c,
- 0xb9f8b322c73ac862,
- 0x8c0ea5053d4712a0,
- 0xb29b2e824a595524,
- 0x82f053db8355e0ce,
- 0x48fe4a0fa5a09315,
- 0xae985bf2cbfc89ed,
- 0x98f5704f6c44c0ab,
- };
-
- comptime var i: usize = 0;
- inline while (i < rounds) : (i += 1) {
- var j: usize = 0;
- while (j < self.m.len) : (j += 8) {
- comptime var x1: usize = 0;
- inline while (x1 < 8) : (x1 += 1) {
- a[x1] +%= self.m[j + x1];
- }
-
- a[0] -%= a[4];
- a[5] ^= a[7] >> 9;
- a[7] +%= a[0];
- a[1] -%= a[5];
- a[6] ^= a[0] << 9;
- a[0] +%= a[1];
- a[2] -%= a[6];
- a[7] ^= a[1] >> 23;
- a[1] +%= a[2];
- a[3] -%= a[7];
- a[0] ^= a[2] << 15;
- a[2] +%= a[3];
- a[4] -%= a[0];
- a[1] ^= a[3] >> 14;
- a[3] +%= a[4];
- a[5] -%= a[1];
- a[2] ^= a[4] << 20;
- a[4] +%= a[5];
- a[6] -%= a[2];
- a[3] ^= a[5] >> 17;
- a[5] +%= a[6];
- a[7] -%= a[3];
- a[4] ^= a[6] << 14;
- a[6] +%= a[7];
-
- comptime var x2: usize = 0;
- inline while (x2 < 8) : (x2 += 1) {
- self.m[j + x2] = a[x2];
- }
- }
- }
-
- mem.set(u64, self.r[0..], 0);
- self.a = 0;
- self.b = 0;
- self.c = 0;
- self.i = self.r.len; // trigger refill on first value
- }
-
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Isaac64, "random", r);
-
- var i: usize = 0;
- const aligned_len = buf.len - (buf.len & 7);
-
- // Fill complete 64-byte segments
- while (i < aligned_len) : (i += 8) {
- var n = self.next();
- comptime var j: usize = 0;
- inline while (j < 8) : (j += 1) {
- buf[i + j] = @truncate(u8, n);
- n >>= 8;
- }
- }
-
- // Fill trailing, ignoring excess (cut the stream).
- if (i != buf.len) {
- var n = self.next();
- while (i < buf.len) : (i += 1) {
- buf[i] = @truncate(u8, n);
- n >>= 8;
- }
- }
- }
-};
-
-test "isaac64 sequence" {
- var r = Isaac64.init(0);
-
- // from reference implementation
- const seq = [_]u64{
- 0xf67dfba498e4937c,
- 0x84a5066a9204f380,
- 0xfee34bd5f5514dbb,
- 0x4d1664739b8f80d6,
- 0x8607459ab52a14aa,
- 0x0e78bc5a98529e49,
- 0xfe5332822ad13777,
- 0x556c27525e33d01a,
- 0x08643ca615f3149f,
- 0xd0771faf3cb04714,
- 0x30e86f68a37b008d,
- 0x3074ebc0488a3adf,
- 0x270645ea7a2790bc,
- 0x5601a0a8d3763c6a,
- 0x2f83071f53f325dd,
- 0xb9090f3d42d2d2ea,
- };
-
- for (seq) |s| {
- expect(s == r.next());
- }
-}
-
-/// Sfc64 pseudo-random number generator from Practically Random.
-/// Fastest engine of pracrand and smallest footprint.
-/// See http://pracrand.sourceforge.net/
-pub const Sfc64 = struct {
- random: Random,
-
- a: u64 = undefined,
- b: u64 = undefined,
- c: u64 = undefined,
- counter: u64 = undefined,
-
- const Rotation = 24;
- const RightShift = 11;
- const LeftShift = 3;
-
- pub fn init(init_s: u64) Sfc64 {
- var x = Sfc64{
- .random = Random{ .fillFn = fill },
- };
-
- x.seed(init_s);
- return x;
- }
-
- fn next(self: *Sfc64) u64 {
- const tmp = self.a +% self.b +% self.counter;
- self.counter += 1;
- self.a = self.b ^ (self.b >> RightShift);
- self.b = self.c +% (self.c << LeftShift);
- self.c = math.rotl(u64, self.c, Rotation) +% tmp;
- return tmp;
- }
-
- fn seed(self: *Sfc64, init_s: u64) void {
- self.a = init_s;
- self.b = init_s;
- self.c = init_s;
- self.counter = 1;
- var i: u32 = 0;
- while (i < 12) : (i += 1) {
- _ = self.next();
- }
- }
-
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Sfc64, "random", r);
-
- var i: usize = 0;
- const aligned_len = buf.len - (buf.len & 7);
-
- // Complete 8 byte segments.
- while (i < aligned_len) : (i += 8) {
- var n = self.next();
- comptime var j: usize = 0;
- inline while (j < 8) : (j += 1) {
- buf[i + j] = @truncate(u8, n);
- n >>= 8;
- }
- }
-
- // Remaining. (cuts the stream)
- if (i != buf.len) {
- var n = self.next();
- while (i < buf.len) : (i += 1) {
- buf[i] = @truncate(u8, n);
- n >>= 8;
- }
- }
- }
-};
-
-test "Sfc64 sequence" {
- // Unfortunately there does not seem to be an official test sequence.
- var r = Sfc64.init(0);
-
- const seq = [_]u64{
- 0x3acfa029e3cc6041,
- 0xf5b6515bf2ee419c,
- 0x1259635894a29b61,
- 0xb6ae75395f8ebd6,
- 0x225622285ce302e2,
- 0x520d28611395cb21,
- 0xdb909c818901599d,
- 0x8ffd195365216f57,
- 0xe8c4ad5e258ac04a,
- 0x8f8ef2c89fdb63ca,
- 0xf9865b01d98d8e2f,
- 0x46555871a65d08ba,
- 0x66868677c6298fcd,
- 0x2ce15a7e6329f57d,
- 0xb2f1833ca91ca79,
- 0x4b0890ac9bf453ca,
- };
-
- for (seq) |s| {
- expectEqual(s, r.next());
- }
-}
-
// Actual Random helper function tests, pcg engine is assumed correct.
test "Random float" {
var prng = DefaultPrng.init(0);
@@ -1147,7 +594,7 @@ fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void {
test "CSPRNG" {
var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;
- try std.crypto.randomBytes(&secret_seed);
+ std.crypto.random.bytes(&secret_seed);
var csprng = DefaultCsprng.init(secret_seed);
const a = csprng.random.int(u64);
const b = csprng.random.int(u64);
@@ -1155,6 +602,6 @@ test "CSPRNG" {
expect(a ^ b ^ c != 0);
}
-test "" {
+test {
std.testing.refAllDecls(@This());
}
diff --git a/lib/std/rand/Gimli.zig b/lib/std/rand/Gimli.zig
new file mode 100644
index 0000000000..8356c7afde
--- /dev/null
+++ b/lib/std/rand/Gimli.zig
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! CSPRNG
+
+const std = @import("std");
+const Random = std.rand.Random;
+const mem = std.mem;
+const Gimli = @This();
+
+random: Random,
+state: std.crypto.core.Gimli,
+
+pub const secret_seed_length = 32;
+
+/// The seed must be uniform, secret and `secret_seed_length` bytes long.
+pub fn init(secret_seed: [secret_seed_length]u8) Gimli {
+ var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
+ mem.copy(u8, initial_state[0..secret_seed_length], &secret_seed);
+ mem.set(u8, initial_state[secret_seed_length..], 0);
+ var self = Gimli{
+ .random = Random{ .fillFn = fill },
+ .state = std.crypto.core.Gimli.init(initial_state),
+ };
+ return self;
+}
+
+fn fill(r: *Random, buf: []u8) void {
+ const self = @fieldParentPtr(Gimli, "random", r);
+
+ if (buf.len != 0) {
+ self.state.squeeze(buf);
+ } else {
+ self.state.permute();
+ }
+ mem.set(u8, self.state.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
+}
diff --git a/lib/std/rand/Isaac64.zig b/lib/std/rand/Isaac64.zig
new file mode 100644
index 0000000000..e1d4dedf5a
--- /dev/null
+++ b/lib/std/rand/Isaac64.zig
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html
+//!
+//! Follows the general idea of the implementation from here with a few shortcuts.
+//! https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
+
+const std = @import("std");
+const Random = std.rand.Random;
+const mem = std.mem;
+const Isaac64 = @This();
+
+random: Random,
+
+r: [256]u64,
+m: [256]u64,
+a: u64,
+b: u64,
+c: u64,
+i: usize,
+
+pub fn init(init_s: u64) Isaac64 {
+ var isaac = Isaac64{
+ .random = Random{ .fillFn = fill },
+ .r = undefined,
+ .m = undefined,
+ .a = undefined,
+ .b = undefined,
+ .c = undefined,
+ .i = undefined,
+ };
+
+ // seed == 0 => same result as the unseeded reference implementation
+ isaac.seed(init_s, 1);
+ return isaac;
+}
+
+fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
+ const x = self.m[base + m1];
+ self.a = mix +% self.m[base + m2];
+
+ const y = self.a +% self.b +% self.m[@intCast(usize, (x >> 3) % self.m.len)];
+ self.m[base + m1] = y;
+
+ self.b = x +% self.m[@intCast(usize, (y >> 11) % self.m.len)];
+ self.r[self.r.len - 1 - base - m1] = self.b;
+}
+
+fn refill(self: *Isaac64) void {
+ const midpoint = self.r.len / 2;
+
+ self.c +%= 1;
+ self.b +%= self.c;
+
+ {
+ var i: usize = 0;
+ while (i < midpoint) : (i += 4) {
+ self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint);
+ self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint);
+ self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint);
+ self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint);
+ }
+ }
+
+ {
+ var i: usize = 0;
+ while (i < midpoint) : (i += 4) {
+ self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0);
+ self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0);
+ self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0);
+ self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0);
+ }
+ }
+
+ self.i = 0;
+}
+
+fn next(self: *Isaac64) u64 {
+ if (self.i >= self.r.len) {
+ self.refill();
+ }
+
+ const value = self.r[self.i];
+ self.i += 1;
+ return value;
+}
+
+fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void {
+ // We ignore the multi-pass requirement since we don't currently expose full access to
+ // seeding the self.m array completely.
+ mem.set(u64, self.m[0..], 0);
+ self.m[0] = init_s;
+
+ // prescrambled golden ratio constants
+ var a = [_]u64{
+ 0x647c4677a2884b7c,
+ 0xb9f8b322c73ac862,
+ 0x8c0ea5053d4712a0,
+ 0xb29b2e824a595524,
+ 0x82f053db8355e0ce,
+ 0x48fe4a0fa5a09315,
+ 0xae985bf2cbfc89ed,
+ 0x98f5704f6c44c0ab,
+ };
+
+ comptime var i: usize = 0;
+ inline while (i < rounds) : (i += 1) {
+ var j: usize = 0;
+ while (j < self.m.len) : (j += 8) {
+ comptime var x1: usize = 0;
+ inline while (x1 < 8) : (x1 += 1) {
+ a[x1] +%= self.m[j + x1];
+ }
+
+ a[0] -%= a[4];
+ a[5] ^= a[7] >> 9;
+ a[7] +%= a[0];
+ a[1] -%= a[5];
+ a[6] ^= a[0] << 9;
+ a[0] +%= a[1];
+ a[2] -%= a[6];
+ a[7] ^= a[1] >> 23;
+ a[1] +%= a[2];
+ a[3] -%= a[7];
+ a[0] ^= a[2] << 15;
+ a[2] +%= a[3];
+ a[4] -%= a[0];
+ a[1] ^= a[3] >> 14;
+ a[3] +%= a[4];
+ a[5] -%= a[1];
+ a[2] ^= a[4] << 20;
+ a[4] +%= a[5];
+ a[6] -%= a[2];
+ a[3] ^= a[5] >> 17;
+ a[5] +%= a[6];
+ a[7] -%= a[3];
+ a[4] ^= a[6] << 14;
+ a[6] +%= a[7];
+
+ comptime var x2: usize = 0;
+ inline while (x2 < 8) : (x2 += 1) {
+ self.m[j + x2] = a[x2];
+ }
+ }
+ }
+
+ mem.set(u64, self.r[0..], 0);
+ self.a = 0;
+ self.b = 0;
+ self.c = 0;
+ self.i = self.r.len; // trigger refill on first value
+}
+
+fn fill(r: *Random, buf: []u8) void {
+ const self = @fieldParentPtr(Isaac64, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Fill complete 64-byte segments
+ while (i < aligned_len) : (i += 8) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 8) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Fill trailing, ignoring excess (cut the stream).
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+}
+
+test "isaac64 sequence" {
+ var r = Isaac64.init(0);
+
+ // from reference implementation
+ const seq = [_]u64{
+ 0xf67dfba498e4937c,
+ 0x84a5066a9204f380,
+ 0xfee34bd5f5514dbb,
+ 0x4d1664739b8f80d6,
+ 0x8607459ab52a14aa,
+ 0x0e78bc5a98529e49,
+ 0xfe5332822ad13777,
+ 0x556c27525e33d01a,
+ 0x08643ca615f3149f,
+ 0xd0771faf3cb04714,
+ 0x30e86f68a37b008d,
+ 0x3074ebc0488a3adf,
+ 0x270645ea7a2790bc,
+ 0x5601a0a8d3763c6a,
+ 0x2f83071f53f325dd,
+ 0xb9090f3d42d2d2ea,
+ };
+
+ for (seq) |s| {
+ std.testing.expect(s == r.next());
+ }
+}
diff --git a/lib/std/rand/Pcg.zig b/lib/std/rand/Pcg.zig
new file mode 100644
index 0000000000..6be17b3bb8
--- /dev/null
+++ b/lib/std/rand/Pcg.zig
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! PCG32 - http://www.pcg-random.org/
+//!
+//! PRNG
+
+const std = @import("std");
+const Random = std.rand.Random;
+const Pcg = @This();
+
+const default_multiplier = 6364136223846793005;
+
+random: Random,
+
+s: u64,
+i: u64,
+
+pub fn init(init_s: u64) Pcg {
+ var pcg = Pcg{
+ .random = Random{ .fillFn = fill },
+ .s = undefined,
+ .i = undefined,
+ };
+
+ pcg.seed(init_s);
+ return pcg;
+}
+
+fn next(self: *Pcg) u32 {
+ const l = self.s;
+ self.s = l *% default_multiplier +% (self.i | 1);
+
+ const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27);
+ const rot = @intCast(u32, l >> 59);
+
+ return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31));
+}
+
+fn seed(self: *Pcg, init_s: u64) void {
+ // Pcg requires 128-bits of seed.
+ var gen = std.rand.SplitMix64.init(init_s);
+ self.seedTwo(gen.next(), gen.next());
+}
+
+fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void {
+ self.s = 0;
+ self.i = (init_s << 1) | 1;
+ self.s = self.s *% default_multiplier +% self.i;
+ self.s +%= init_i;
+ self.s = self.s *% default_multiplier +% self.i;
+}
+
+fn fill(r: *Random, buf: []u8) void {
+ const self = @fieldParentPtr(Pcg, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Complete 4 byte segments.
+ while (i < aligned_len) : (i += 4) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 4) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Remaining. (cuts the stream)
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 4;
+ }
+ }
+}
+
+test "pcg sequence" {
+ var r = Pcg.init(0);
+ const s0: u64 = 0x9394bf54ce5d79de;
+ const s1: u64 = 0x84e9c579ef59bbf7;
+ r.seedTwo(s0, s1);
+
+ const seq = [_]u32{
+ 2881561918,
+ 3063928540,
+ 1199791034,
+ 2487695858,
+ 1479648952,
+ 3247963454,
+ };
+
+ for (seq) |s| {
+ std.testing.expect(s == r.next());
+ }
+}
diff --git a/lib/std/rand/Sfc64.zig b/lib/std/rand/Sfc64.zig
new file mode 100644
index 0000000000..3b5f1eda82
--- /dev/null
+++ b/lib/std/rand/Sfc64.zig
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! Sfc64 pseudo-random number generator from Practically Random.
+//! Fastest engine of pracrand and smallest footprint.
+//! See http://pracrand.sourceforge.net/
+
+const std = @import("std");
+const Random = std.rand.Random;
+const math = std.math;
+const Sfc64 = @This();
+
+random: Random,
+
+a: u64 = undefined,
+b: u64 = undefined,
+c: u64 = undefined,
+counter: u64 = undefined,
+
+const Rotation = 24;
+const RightShift = 11;
+const LeftShift = 3;
+
+pub fn init(init_s: u64) Sfc64 {
+ var x = Sfc64{
+ .random = Random{ .fillFn = fill },
+ };
+
+ x.seed(init_s);
+ return x;
+}
+
+fn next(self: *Sfc64) u64 {
+ const tmp = self.a +% self.b +% self.counter;
+ self.counter += 1;
+ self.a = self.b ^ (self.b >> RightShift);
+ self.b = self.c +% (self.c << LeftShift);
+ self.c = math.rotl(u64, self.c, Rotation) +% tmp;
+ return tmp;
+}
+
+fn seed(self: *Sfc64, init_s: u64) void {
+ self.a = init_s;
+ self.b = init_s;
+ self.c = init_s;
+ self.counter = 1;
+ var i: u32 = 0;
+ while (i < 12) : (i += 1) {
+ _ = self.next();
+ }
+}
+
+fn fill(r: *Random, buf: []u8) void {
+ const self = @fieldParentPtr(Sfc64, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Complete 8 byte segments.
+ while (i < aligned_len) : (i += 8) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 8) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Remaining. (cuts the stream)
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+}
+
+test "Sfc64 sequence" {
+ // Unfortunately there does not seem to be an official test sequence.
+ var r = Sfc64.init(0);
+
+ const seq = [_]u64{
+ 0x3acfa029e3cc6041,
+ 0xf5b6515bf2ee419c,
+ 0x1259635894a29b61,
+ 0xb6ae75395f8ebd6,
+ 0x225622285ce302e2,
+ 0x520d28611395cb21,
+ 0xdb909c818901599d,
+ 0x8ffd195365216f57,
+ 0xe8c4ad5e258ac04a,
+ 0x8f8ef2c89fdb63ca,
+ 0xf9865b01d98d8e2f,
+ 0x46555871a65d08ba,
+ 0x66868677c6298fcd,
+ 0x2ce15a7e6329f57d,
+ 0xb2f1833ca91ca79,
+ 0x4b0890ac9bf453ca,
+ };
+
+ for (seq) |s| {
+ std.testing.expectEqual(s, r.next());
+ }
+}
diff --git a/lib/std/rand/Xoroshiro128.zig b/lib/std/rand/Xoroshiro128.zig
new file mode 100644
index 0000000000..816bb9f58c
--- /dev/null
+++ b/lib/std/rand/Xoroshiro128.zig
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! Xoroshiro128+ - http://xoroshiro.di.unimi.it/
+//!
+//! PRNG
+
+const std = @import("std");
+const Random = std.rand.Random;
+const math = std.math;
+const Xoroshiro128 = @This();
+
+random: Random,
+
+s: [2]u64,
+
+pub fn init(init_s: u64) Xoroshiro128 {
+ var x = Xoroshiro128{
+ .random = Random{ .fillFn = fill },
+ .s = undefined,
+ };
+
+ x.seed(init_s);
+ return x;
+}
+
+fn next(self: *Xoroshiro128) u64 {
+ const s0 = self.s[0];
+ var s1 = self.s[1];
+ const r = s0 +% s1;
+
+ s1 ^= s0;
+ self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14);
+ self.s[1] = math.rotl(u64, s1, @as(u8, 36));
+
+ return r;
+}
+
+// Skip 2^64 places ahead in the sequence
+fn jump(self: *Xoroshiro128) void {
+ var s0: u64 = 0;
+ var s1: u64 = 0;
+
+ const table = [_]u64{
+ 0xbeac0467eba5facb,
+ 0xd86b048b86aa9922,
+ };
+
+ inline for (table) |entry| {
+ var b: usize = 0;
+ while (b < 64) : (b += 1) {
+ if ((entry & (@as(u64, 1) << @intCast(u6, b))) != 0) {
+ s0 ^= self.s[0];
+ s1 ^= self.s[1];
+ }
+ _ = self.next();
+ }
+ }
+
+ self.s[0] = s0;
+ self.s[1] = s1;
+}
+
+pub fn seed(self: *Xoroshiro128, init_s: u64) void {
+ // Xoroshiro requires 128-bits of seed.
+ var gen = std.rand.SplitMix64.init(init_s);
+
+ self.s[0] = gen.next();
+ self.s[1] = gen.next();
+}
+
+fn fill(r: *Random, buf: []u8) void {
+ const self = @fieldParentPtr(Xoroshiro128, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Complete 8 byte segments.
+ while (i < aligned_len) : (i += 8) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 8) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Remaining. (cuts the stream)
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+}
+
+test "xoroshiro sequence" {
+ var r = Xoroshiro128.init(0);
+ r.s[0] = 0xaeecf86f7878dd75;
+ r.s[1] = 0x01cd153642e72622;
+
+ const seq1 = [_]u64{
+ 0xb0ba0da5bb600397,
+ 0x18a08afde614dccc,
+ 0xa2635b956a31b929,
+ 0xabe633c971efa045,
+ 0x9ac19f9706ca3cac,
+ 0xf62b426578c1e3fb,
+ };
+
+ for (seq1) |s| {
+ std.testing.expect(s == r.next());
+ }
+
+ r.jump();
+
+ const seq2 = [_]u64{
+ 0x95344a13556d3e22,
+ 0xb4fb32dafa4d00df,
+ 0xb2011d9ccdcfe2dd,
+ 0x05679a9b2119b908,
+ 0xa860a1da7c9cd8a0,
+ 0x658a96efe3f86550,
+ };
+
+ for (seq2) |s| {
+ std.testing.expect(s == r.next());
+ }
+}
diff --git a/lib/std/rand/ziggurat.zig b/lib/std/rand/ziggurat.zig
index da189637bf..fe120943d7 100644
--- a/lib/std/rand/ziggurat.zig
+++ b/lib/std/rand/ziggurat.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -131,7 +131,11 @@ fn norm_zero_case(random: *Random, u: f64) f64 {
}
}
-test "ziggurant normal dist sanity" {
+const please_windows_dont_oom = std.Target.current.os.tag == .windows;
+
+test "normal dist sanity" {
+ if (please_windows_dont_oom) return error.SkipZigTest;
+
var prng = std.rand.DefaultPrng.init(0);
var i: usize = 0;
while (i < 1000) : (i += 1) {
@@ -158,7 +162,9 @@ fn exp_zero_case(random: *Random, _: f64) f64 {
return exp_r - math.ln(random.float(f64));
}
-test "ziggurant exp dist sanity" {
+test "exp dist sanity" {
+ if (please_windows_dont_oom) return error.SkipZigTest;
+
var prng = std.rand.DefaultPrng.init(0);
var i: usize = 0;
while (i < 1000) : (i += 1) {
@@ -166,6 +172,8 @@ test "ziggurant exp dist sanity" {
}
}
-test "ziggurat table gen" {
+test "table gen" {
+ if (please_windows_dont_oom) return error.SkipZigTest;
+
const table = NormDist;
}
diff --git a/lib/std/sort.zig b/lib/std/sort.zig
index e2e4cc662d..b30fb6ae57 100644
--- a/lib/std/sort.zig
+++ b/lib/std/sort.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -1129,7 +1129,7 @@ fn swap(
}
}
-/// Use to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.
+/// Use to generate a comparator function for a given type. e.g. `sort(u8, slice, {}, comptime asc(u8))`.
pub fn asc(comptime T: type) fn (void, T, T) bool {
const impl = struct {
fn inner(context: void, a: T, b: T) bool {
@@ -1140,7 +1140,7 @@ pub fn asc(comptime T: type) fn (void, T, T) bool {
return impl.inner;
}
-/// Use to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.
+/// Use to generate a comparator function for a given type. e.g. `sort(u8, slice, {}, comptime desc(u8))`.
pub fn desc(comptime T: type) fn (void, T, T) bool {
const impl = struct {
fn inner(context: void, a: T, b: T) bool {
diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig
index 7d9ac17499..0b7baf0fc1 100644
--- a/lib/std/special/build_runner.zig
+++ b/lib/std/special/build_runner.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -57,8 +57,8 @@ pub fn main() !void {
var targets = ArrayList([]const u8).init(allocator);
- const stderr_stream = io.getStdErr().outStream();
- const stdout_stream = io.getStdOut().outStream();
+ const stderr_stream = io.getStdErr().writer();
+ const stdout_stream = io.getStdOut().writer();
while (nextArg(args, &arg_idx)) |arg| {
if (mem.startsWith(u8, arg, "-D")) {
@@ -98,7 +98,7 @@ pub fn main() !void {
return usageAndErr(builder, false, stderr_stream);
};
builder.color = std.meta.stringToEnum(@TypeOf(builder.color), next_arg) orelse {
- warn("expected [auto|on|off] after --color, found '{}'", .{next_arg});
+ warn("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
return usageAndErr(builder, false, stderr_stream);
};
} else if (mem.eql(u8, arg, "--override-lib-dir")) {
@@ -126,7 +126,7 @@ pub fn main() !void {
builder.args = argsRest(args, arg_idx);
break;
} else {
- warn("Unrecognized argument: {}\n\n", .{arg});
+ warn("Unrecognized argument: {s}\n\n", .{arg});
return usageAndErr(builder, false, stderr_stream);
}
} else {
@@ -168,7 +168,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
}
try out_stream.print(
- \\Usage: {} build [steps] [options]
+ \\Usage: {s} build [steps] [options]
\\
\\Steps:
\\
@@ -177,10 +177,10 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
const allocator = builder.allocator;
for (builder.top_level_steps.items) |top_level_step| {
const name = if (&top_level_step.step == builder.default_step)
- try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name})
+ try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name})
else
top_level_step.step.name;
- try out_stream.print(" {s:<27} {}\n", .{ name, top_level_step.description });
+ try out_stream.print(" {s:<27} {s}\n", .{ name, top_level_step.description });
}
try out_stream.writeAll(
@@ -200,12 +200,12 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
try out_stream.print(" (none)\n", .{});
} else {
for (builder.available_options_list.items) |option| {
- const name = try fmt.allocPrint(allocator, " -D{}=[{}]", .{
+ const name = try fmt.allocPrint(allocator, " -D{s}=[{s}]", .{
option.name,
Builder.typeIdName(option.type_id),
});
defer allocator.free(name);
- try out_stream.print("{s:<29} {}\n", .{ name, option.description });
+ try out_stream.print("{s:<29} {s}\n", .{ name, option.description });
}
}
diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig
index 449b70d6b0..51cbafc133 100644
--- a/lib/std/special/c.zig
+++ b/lib/std/special/c.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -172,7 +172,7 @@ test "strncmp" {
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
if (builtin.is_test) {
@setCold(true);
- std.debug.panic("{}", .{msg});
+ std.debug.panic("{s}", .{msg});
}
if (builtin.os.tag != .freestanding and builtin.os.tag != .other) {
std.os.abort();
@@ -634,6 +634,16 @@ export fn cosf(a: f32) f32 {
return math.cos(a);
}
+export fn sincos(a: f64, r_sin: *f64, r_cos: *f64) void {
+ r_sin.* = math.sin(a);
+ r_cos.* = math.cos(a);
+}
+
+export fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) void {
+ r_sin.* = math.sin(a);
+ r_cos.* = math.cos(a);
+}
+
export fn exp(a: f64) f64 {
return math.exp(a);
}
diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig
index 98d292cce9..4f12d21957 100644
--- a/lib/std/special/compiler_rt.zig
+++ b/lib/std/special/compiler_rt.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -167,6 +167,10 @@ comptime {
@export(@import("compiler_rt/clzsi2.zig").__clzsi2, .{ .name = "__clzsi2", .linkage = linkage });
+ if (builtin.link_libc and builtin.os.tag == .openbsd) {
+ @export(@import("compiler_rt/emutls.zig").__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = linkage });
+ }
+
if ((builtin.arch.isARM() or builtin.arch.isThumb()) and !is_test) {
@export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage });
@export(@import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage });
@@ -273,6 +277,25 @@ comptime {
@export(@import("compiler_rt/aullrem.zig")._aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });
}
+ if (builtin.arch.isSPARC()) {
+ // SPARC systems use a different naming scheme
+ @export(@import("compiler_rt/sparc.zig")._Qp_add, .{ .name = "_Qp_add", .linkage = linkage });
+ @export(@import("compiler_rt/sparc.zig")._Qp_div, .{ .name = "_Qp_div", .linkage = linkage });
+ @export(@import("compiler_rt/sparc.zig")._Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage });
+ @export(@import("compiler_rt/sparc.zig")._Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage });
+
+ @export(@import("compiler_rt/sparc.zig")._Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage });
+ @export(@import("compiler_rt/sparc.zig")._Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage });
+ @export(@import("compiler_rt/sparc.zig")._Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage });
+ @export(@import("compiler_rt/sparc.zig")._Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage });
+ @export(@import("compiler_rt/sparc.zig")._Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage });
+ @export(@import("compiler_rt/sparc.zig")._Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage });
+ @export(@import("compiler_rt/sparc.zig")._Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage });
+
+ @export(@import("compiler_rt/sparc.zig")._Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage });
+ @export(@import("compiler_rt/sparc.zig")._Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });
+ }
+
if (builtin.os.tag == .windows) {
// Default stack-probe functions emitted by LLVM
if (is_mingw) {
@@ -324,7 +347,7 @@ pub usingnamespace @import("compiler_rt/atomics.zig");
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
@setCold(true);
if (is_test) {
- std.debug.panic("{}", .{msg});
+ std.debug.panic("{s}", .{msg});
} else {
unreachable;
}
diff --git a/lib/std/special/compiler_rt/addXf3.zig b/lib/std/special/compiler_rt/addXf3.zig
index 27dbd440c2..5a2f3c976c 100644
--- a/lib/std/special/compiler_rt/addXf3.zig
+++ b/lib/std/special/compiler_rt/addXf3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/addXf3_test.zig b/lib/std/special/compiler_rt/addXf3_test.zig
index 3d75309507..a8f454384c 100644
--- a/lib/std/special/compiler_rt/addXf3_test.zig
+++ b/lib/std/special/compiler_rt/addXf3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/arm.zig b/lib/std/special/compiler_rt/arm.zig
index 1eecd3ceac..f100f8293c 100644
--- a/lib/std/special/compiler_rt/arm.zig
+++ b/lib/std/special/compiler_rt/arm.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -66,10 +66,7 @@ pub fn __aeabi_uidivmod() callconv(.Naked) void {
\\ ldr r1, [sp]
\\ add sp, #4
\\ pop {pc}
- :
- :
- : "memory"
- );
+ ::: "memory");
unreachable;
}
@@ -86,10 +83,7 @@ pub fn __aeabi_uldivmod() callconv(.Naked) void {
\\ ldr r3, [sp, #12]
\\ add sp, #16
\\ pop {r4, pc}
- :
- :
- : "memory"
- );
+ ::: "memory");
unreachable;
}
@@ -104,10 +98,7 @@ pub fn __aeabi_idivmod() callconv(.Naked) void {
\\ ldr r1, [sp]
\\ add sp, #4
\\ pop {pc}
- :
- :
- : "memory"
- );
+ ::: "memory");
unreachable;
}
@@ -124,9 +115,6 @@ pub fn __aeabi_ldivmod() callconv(.Naked) void {
\\ ldr r3, [sp, #12]
\\ add sp, #16
\\ pop {r4, pc}
- :
- :
- : "memory"
- );
+ ::: "memory");
unreachable;
}
diff --git a/lib/std/special/compiler_rt/ashldi3_test.zig b/lib/std/special/compiler_rt/ashldi3_test.zig
index 874681a79a..dfc3712e39 100644
--- a/lib/std/special/compiler_rt/ashldi3_test.zig
+++ b/lib/std/special/compiler_rt/ashldi3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/ashlti3_test.zig b/lib/std/special/compiler_rt/ashlti3_test.zig
index 42cb3a47bb..453fa9e77b 100644
--- a/lib/std/special/compiler_rt/ashlti3_test.zig
+++ b/lib/std/special/compiler_rt/ashlti3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/ashrdi3_test.zig b/lib/std/special/compiler_rt/ashrdi3_test.zig
index e80b95af9e..77fe286185 100644
--- a/lib/std/special/compiler_rt/ashrdi3_test.zig
+++ b/lib/std/special/compiler_rt/ashrdi3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/ashrti3_test.zig b/lib/std/special/compiler_rt/ashrti3_test.zig
index 958b8a8a74..5f4e166001 100644
--- a/lib/std/special/compiler_rt/ashrti3_test.zig
+++ b/lib/std/special/compiler_rt/ashrti3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/atomics.zig b/lib/std/special/compiler_rt/atomics.zig
index cf9854c3c6..cda87236a9 100644
--- a/lib/std/special/compiler_rt/atomics.zig
+++ b/lib/std/special/compiler_rt/atomics.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/aulldiv.zig b/lib/std/special/compiler_rt/aulldiv.zig
index 321ff288bb..196c218e24 100644
--- a/lib/std/special/compiler_rt/aulldiv.zig
+++ b/lib/std/special/compiler_rt/aulldiv.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/aullrem.zig b/lib/std/special/compiler_rt/aullrem.zig
index a14eb99be3..7d0eef5921 100644
--- a/lib/std/special/compiler_rt/aullrem.zig
+++ b/lib/std/special/compiler_rt/aullrem.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/clear_cache.zig b/lib/std/special/compiler_rt/clear_cache.zig
index 4b00721868..568373aabe 100644
--- a/lib/std/special/compiler_rt/clear_cache.zig
+++ b/lib/std/special/compiler_rt/clear_cache.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/clzsi2.zig b/lib/std/special/compiler_rt/clzsi2.zig
index e4739d47c4..c10786b462 100644
--- a/lib/std/special/compiler_rt/clzsi2.zig
+++ b/lib/std/special/compiler_rt/clzsi2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/clzsi2_test.zig b/lib/std/special/compiler_rt/clzsi2_test.zig
index 2d9ba3d1b3..2b860afd22 100644
--- a/lib/std/special/compiler_rt/clzsi2_test.zig
+++ b/lib/std/special/compiler_rt/clzsi2_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/compareXf2.zig b/lib/std/special/compiler_rt/compareXf2.zig
index eba6abb003..c903321669 100644
--- a/lib/std/special/compiler_rt/compareXf2.zig
+++ b/lib/std/special/compiler_rt/compareXf2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/comparedf2_test.zig b/lib/std/special/compiler_rt/comparedf2_test.zig
index 9d681b8f81..f5e8cfe372 100644
--- a/lib/std/special/compiler_rt/comparedf2_test.zig
+++ b/lib/std/special/compiler_rt/comparedf2_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/comparesf2_test.zig b/lib/std/special/compiler_rt/comparesf2_test.zig
index da7fe940a0..0a1f5e74f6 100644
--- a/lib/std/special/compiler_rt/comparesf2_test.zig
+++ b/lib/std/special/compiler_rt/comparesf2_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/divdf3.zig b/lib/std/special/compiler_rt/divdf3.zig
index 31d6ff0993..10a548090a 100644
--- a/lib/std/special/compiler_rt/divdf3.zig
+++ b/lib/std/special/compiler_rt/divdf3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/divdf3_test.zig b/lib/std/special/compiler_rt/divdf3_test.zig
index 04cac956d2..8bdecc7c6a 100644
--- a/lib/std/special/compiler_rt/divdf3_test.zig
+++ b/lib/std/special/compiler_rt/divdf3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/divsf3.zig b/lib/std/special/compiler_rt/divsf3.zig
index 779506d85e..3f89f12313 100644
--- a/lib/std/special/compiler_rt/divsf3.zig
+++ b/lib/std/special/compiler_rt/divsf3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/divsf3_test.zig b/lib/std/special/compiler_rt/divsf3_test.zig
index 30dcba462b..a14e8e9163 100644
--- a/lib/std/special/compiler_rt/divsf3_test.zig
+++ b/lib/std/special/compiler_rt/divsf3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/divtf3.zig b/lib/std/special/compiler_rt/divtf3.zig
index 152ffa9926..9c18e79dd5 100644
--- a/lib/std/special/compiler_rt/divtf3.zig
+++ b/lib/std/special/compiler_rt/divtf3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/divtf3_test.zig b/lib/std/special/compiler_rt/divtf3_test.zig
index cf6f2f2eaf..98910e9994 100644
--- a/lib/std/special/compiler_rt/divtf3_test.zig
+++ b/lib/std/special/compiler_rt/divtf3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/divti3.zig b/lib/std/special/compiler_rt/divti3.zig
index a065111510..03bae3f3f8 100644
--- a/lib/std/special/compiler_rt/divti3.zig
+++ b/lib/std/special/compiler_rt/divti3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/divti3_test.zig b/lib/std/special/compiler_rt/divti3_test.zig
index 18fab24ed1..a20be340c6 100644
--- a/lib/std/special/compiler_rt/divti3_test.zig
+++ b/lib/std/special/compiler_rt/divti3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/emutls.zig b/lib/std/special/compiler_rt/emutls.zig
new file mode 100644
index 0000000000..2b0fba5b34
--- /dev/null
+++ b/lib/std/special/compiler_rt/emutls.zig
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2018 LLVM Compiler Infrastructure
+// Copyright (c) 2020 Sebastien Marie <semarie@online.fr>
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+// __emutls_get_address specific builtin
+//
+// derived work from LLVM Compiler Infrastructure - release 8.0 (MIT)
+// https://github.com/llvm-mirror/compiler-rt/blob/release_80/lib/builtins/emutls.c
+//
+
+const std = @import("std");
+
+const abort = std.os.abort;
+const assert = std.debug.assert;
+const expect = std.testing.expect;
+
+// defined in C as:
+// typedef unsigned int gcc_word __attribute__((mode(word)));
+const gcc_word = usize;
+
+comptime {
+ assert(std.builtin.link_libc);
+}
+
+/// public entrypoint for generated code using EmulatedTLS
+pub fn __emutls_get_address(control: *emutls_control) callconv(.C) *c_void {
+ return control.getPointer();
+}
+
+/// Simple allocator interface, to avoid pulling in the while
+/// std allocator implementation.
+const simple_allocator = struct {
+ /// Allocate a memory chunk for requested type. Return a pointer on the data.
+ pub fn alloc(comptime T: type) *T {
+ return @ptrCast(*T, @alignCast(
+ @alignOf(T),
+ advancedAlloc(@alignOf(T), @sizeOf(T)),
+ ));
+ }
+
+ /// Allocate a slice of T, with len elements.
+ pub fn allocSlice(comptime T: type, len: usize) []T {
+ return @ptrCast([*]T, @alignCast(
+ @alignOf(T),
+ advancedAlloc(@alignOf(T), @sizeOf(T) * len),
+ ))[0 .. len - 1];
+ }
+
+ /// Allocate a memory chunk.
+ pub fn advancedAlloc(alignment: u29, size: usize) [*]u8 {
+ const minimal_alignment = std.math.max(@alignOf(usize), alignment);
+
+ var aligned_ptr: ?*c_void = undefined;
+ if (std.c.posix_memalign(&aligned_ptr, minimal_alignment, size) != 0) {
+ abort();
+ }
+
+ return @ptrCast([*]u8, aligned_ptr);
+ }
+
+ /// Resize a slice.
+ pub fn reallocSlice(comptime T: type, slice: []T, len: usize) []T {
+ var c_ptr: *c_void = @ptrCast(*c_void, slice.ptr);
+ var new_array: [*]T = @ptrCast([*]T, @alignCast(
+ @alignOf(T),
+ std.c.realloc(c_ptr, @sizeOf(T) * len) orelse abort(),
+ ));
+ return new_array[0..len];
+ }
+
+ /// Free a memory chunk allocated with simple_allocator.
+ pub fn free(ptr: anytype) void {
+ std.c.free(@ptrCast(*c_void, ptr));
+ }
+};
+
+/// Simple array of ?ObjectPointer with automatic resizing and
+/// automatic storage allocation.
+const ObjectArray = struct {
+ const ObjectPointer = *c_void;
+
+ // content of the array
+ slots: []?ObjectPointer,
+
+ /// create a new ObjectArray with n slots. must call deinit() to deallocate.
+ pub fn init(n: usize) *ObjectArray {
+ var array = simple_allocator.alloc(ObjectArray);
+ errdefer simple_allocator.free(array);
+
+ array.* = ObjectArray{
+ .slots = simple_allocator.allocSlice(?ObjectPointer, n),
+ };
+ errdefer simple_allocator.free(array.slots);
+
+ for (array.slots) |*object| {
+ object.* = null;
+ }
+
+ return array;
+ }
+
+ /// deallocate the ObjectArray.
+ pub fn deinit(self: *ObjectArray) void {
+ // deallocated used objects in the array
+ for (self.slots) |*object| {
+ simple_allocator.free(object.*);
+ }
+ simple_allocator.free(self.slots);
+ simple_allocator.free(self);
+ }
+
+ /// resize the ObjectArray if needed.
+ pub fn ensureLength(self: *ObjectArray, new_len: usize) *ObjectArray {
+ const old_len = self.slots.len;
+
+ if (old_len > new_len) {
+ return self;
+ }
+
+ // reallocate
+ self.slots = simple_allocator.reallocSlice(?ObjectPointer, self.slots, new_len);
+
+ // init newly added slots
+ for (self.slots[old_len..]) |*object| {
+ object.* = null;
+ }
+
+ return self;
+ }
+
+ /// Retrieve the pointer at request index, using control to initialize it if needed.
+ pub fn getPointer(self: *ObjectArray, index: usize, control: *emutls_control) ObjectPointer {
+ if (self.slots[index] == null) {
+ // initialize the slot
+ const size = control.size;
+ const alignment = @truncate(u29, control.alignment);
+
+ var data = simple_allocator.advancedAlloc(alignment, size);
+ errdefer simple_allocator.free(data);
+
+ if (control.default_value) |value| {
+ // default value: copy the content to newly allocated object.
+ @memcpy(data, @ptrCast([*]u8, value), size);
+ } else {
+ // no default: return zeroed memory.
+ @memset(data, 0, size);
+ }
+
+ self.slots[index] = @ptrCast(*c_void, data);
+ }
+
+ return self.slots[index].?;
+ }
+};
+
+// Global stucture for Thread Storage.
+// It provides thread-safety for on-demand storage of Thread Objects.
+const current_thread_storage = struct {
+ var key: std.c.pthread_key_t = undefined;
+ var init_once = std.once(current_thread_storage.init);
+
+ /// Return a per thread ObjectArray with at least the expected index.
+ pub fn getArray(index: usize) *ObjectArray {
+ if (current_thread_storage.getspecific()) |array| {
+ // we already have a specific. just ensure the array is
+ // big enough for the wanted index.
+ return array.ensureLength(index);
+ }
+
+ // no specific. we need to create a new array.
+
+ // make it to contains at least 16 objects (to avoid too much
+ // reallocation at startup).
+ const size = std.math.max(16, index);
+
+ // create a new array and store it.
+ var array: *ObjectArray = ObjectArray.init(size);
+ current_thread_storage.setspecific(array);
+ return array;
+ }
+
+ /// Return casted thread specific value.
+ fn getspecific() ?*ObjectArray {
+ return @ptrCast(
+ ?*ObjectArray,
+ @alignCast(
+ @alignOf(ObjectArray),
+ std.c.pthread_getspecific(current_thread_storage.key),
+ ),
+ );
+ }
+
+ /// Set casted thread specific value.
+ fn setspecific(new: ?*ObjectArray) void {
+ if (std.c.pthread_setspecific(current_thread_storage.key, @ptrCast(*c_void, new)) != 0) {
+ abort();
+ }
+ }
+
+ /// Initialize pthread_key_t.
+ fn init() void {
+ if (std.c.pthread_key_create(&current_thread_storage.key, current_thread_storage.deinit) != 0) {
+ abort();
+ }
+ }
+
+ /// Invoked by pthread specific destructor. the passed argument is the ObjectArray pointer.
+ fn deinit(arrayPtr: *c_void) callconv(.C) void {
+ var array = @ptrCast(
+ *ObjectArray,
+ @alignCast(@alignOf(ObjectArray), arrayPtr),
+ );
+ array.deinit();
+ }
+};
+
+const emutls_control = extern struct {
+ // A emutls_control value is a global value across all
+ // threads. The threads shares the index of TLS variable. The data
+ // array (containing address of allocated variables) is thread
+ // specific and stored using pthread_setspecific().
+
+ // size of the object in bytes
+ size: gcc_word,
+
+ // alignment of the object in bytes
+ alignment: gcc_word,
+
+ object: extern union {
+ // data[index-1] is the object address / 0 = uninit
+ index: usize,
+
+ // object address, when in single thread env (not used)
+ address: *c_void,
+ },
+
+ // null or non-zero initial value for the object
+ default_value: ?*c_void,
+
+ // global Mutex used to serialize control.index initialization.
+ var mutex: std.c.pthread_mutex_t = std.c.PTHREAD_MUTEX_INITIALIZER;
+
+ // global counter for keeping track of requested indexes.
+ // access should be done with mutex held.
+ var next_index: usize = 1;
+
+ /// Simple wrapper for global lock.
+ fn lock() void {
+ if (std.c.pthread_mutex_lock(&emutls_control.mutex) != 0) {
+ abort();
+ }
+ }
+
+ /// Simple wrapper for global unlock.
+ fn unlock() void {
+ if (std.c.pthread_mutex_unlock(&emutls_control.mutex) != 0) {
+ abort();
+ }
+ }
+
+ /// Helper to retrieve nad initialize global unique index per emutls variable.
+ pub fn getIndex(self: *emutls_control) usize {
+ // Two threads could race against the same emutls_control.
+
+ // Use atomic for reading coherent value lockless.
+ const index_lockless = @atomicLoad(usize, &self.object.index, .Acquire);
+
+ if (index_lockless != 0) {
+ // index is already initialized, return it.
+ return index_lockless;
+ }
+
+ // index is uninitialized: take global lock to avoid possible race.
+ emutls_control.lock();
+ defer emutls_control.unlock();
+
+ const index_locked = self.object.index;
+ if (index_locked != 0) {
+ // we lost a race, but index is already initialized: nothing particular to do.
+ return index_locked;
+ }
+
+ // Store a new index atomically (for having coherent index_lockless reading).
+ @atomicStore(usize, &self.object.index, emutls_control.next_index, .Release);
+
+ // Increment the next available index
+ emutls_control.next_index += 1;
+
+ return self.object.index;
+ }
+
+ /// Simple helper for testing purpose.
+ pub fn init(comptime T: type, default_value: ?*T) emutls_control {
+ return emutls_control{
+ .size = @sizeOf(T),
+ .alignment = @alignOf(T),
+ .object = .{ .index = 0 },
+ .default_value = @ptrCast(?*c_void, default_value),
+ };
+ }
+
+ /// Get the pointer on allocated storage for emutls variable.
+ pub fn getPointer(self: *emutls_control) *c_void {
+ // ensure current_thread_storage initialization is done
+ current_thread_storage.init_once.call();
+
+ const index = self.getIndex();
+ var array = current_thread_storage.getArray(index);
+
+ return array.getPointer(index - 1, self);
+ }
+
+ /// Testing helper for retrieving typed pointer.
+ pub fn get_typed_pointer(self: *emutls_control, comptime T: type) *T {
+ assert(self.size == @sizeOf(T));
+ assert(self.alignment == @alignOf(T));
+ return @ptrCast(
+ *T,
+ @alignCast(@alignOf(T), self.getPointer()),
+ );
+ }
+};
+
+test "simple_allocator" {
+ var data1: *[64]u8 = simple_allocator.alloc([64]u8);
+ defer simple_allocator.free(data1);
+ for (data1) |*c| {
+ c.* = 0xff;
+ }
+
+ var data2: [*]u8 = simple_allocator.advancedAlloc(@alignOf(u8), 64);
+ defer simple_allocator.free(data2);
+ for (data2[0..63]) |*c| {
+ c.* = 0xff;
+ }
+}
+
+test "__emutls_get_address zeroed" {
+ var ctl = emutls_control.init(usize, null);
+ expect(ctl.object.index == 0);
+
+ // retrieve a variable from ctl
+ var x = @ptrCast(*usize, @alignCast(@alignOf(usize), __emutls_get_address(&ctl)));
+ expect(ctl.object.index != 0); // index has been allocated for this ctl
+ expect(x.* == 0); // storage has been zeroed
+
+ // modify the storage
+ x.* = 1234;
+
+ // retrieve a variable from ctl (same ctl)
+ var y = @ptrCast(*usize, @alignCast(@alignOf(usize), __emutls_get_address(&ctl)));
+
+ expect(y.* == 1234); // same content that x.*
+ expect(x == y); // same pointer
+}
+
+test "__emutls_get_address with default_value" {
+ var value: usize = 5678; // default value
+ var ctl = emutls_control.init(usize, &value);
+ expect(ctl.object.index == 0);
+
+ var x: *usize = @ptrCast(*usize, @alignCast(@alignOf(usize), __emutls_get_address(&ctl)));
+ expect(ctl.object.index != 0);
+ expect(x.* == 5678); // storage initialized with default value
+
+ // modify the storage
+ x.* = 9012;
+
+ expect(value == 5678); // the default value didn't change
+
+ var y = @ptrCast(*usize, @alignCast(@alignOf(usize), __emutls_get_address(&ctl)));
+ expect(y.* == 9012); // the modified storage persists
+}
+
+test "test default_value with differents sizes" {
+ const testType = struct {
+ fn _testType(comptime T: type, value: T) void {
+ var def: T = value;
+ var ctl = emutls_control.init(T, &def);
+ var x = ctl.get_typed_pointer(T);
+ expect(x.* == value);
+ }
+ }._testType;
+
+ testType(usize, 1234);
+ testType(u32, 1234);
+ testType(i16, -12);
+ testType(f64, -12.0);
+ testType(
+ @TypeOf("012345678901234567890123456789"),
+ "012345678901234567890123456789",
+ );
+}
diff --git a/lib/std/special/compiler_rt/extendXfYf2.zig b/lib/std/special/compiler_rt/extendXfYf2.zig
index 53783d2b13..c5b93fa51e 100644
--- a/lib/std/special/compiler_rt/extendXfYf2.zig
+++ b/lib/std/special/compiler_rt/extendXfYf2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/extendXfYf2_test.zig b/lib/std/special/compiler_rt/extendXfYf2_test.zig
index d82a8baf4c..6a3f69d8e9 100644
--- a/lib/std/special/compiler_rt/extendXfYf2_test.zig
+++ b/lib/std/special/compiler_rt/extendXfYf2_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixdfdi.zig b/lib/std/special/compiler_rt/fixdfdi.zig
index 28de1ecd23..f827f22a4a 100644
--- a/lib/std/special/compiler_rt/fixdfdi.zig
+++ b/lib/std/special/compiler_rt/fixdfdi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixdfdi_test.zig b/lib/std/special/compiler_rt/fixdfdi_test.zig
index 83835106cd..f085bdf665 100644
--- a/lib/std/special/compiler_rt/fixdfdi_test.zig
+++ b/lib/std/special/compiler_rt/fixdfdi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixdfsi.zig b/lib/std/special/compiler_rt/fixdfsi.zig
index 678c75d0c1..2e9fab2297 100644
--- a/lib/std/special/compiler_rt/fixdfsi.zig
+++ b/lib/std/special/compiler_rt/fixdfsi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixdfsi_test.zig b/lib/std/special/compiler_rt/fixdfsi_test.zig
index 8050a1b9c5..1445149546 100644
--- a/lib/std/special/compiler_rt/fixdfsi_test.zig
+++ b/lib/std/special/compiler_rt/fixdfsi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixdfti.zig b/lib/std/special/compiler_rt/fixdfti.zig
index 3d9266ae1f..88072de063 100644
--- a/lib/std/special/compiler_rt/fixdfti.zig
+++ b/lib/std/special/compiler_rt/fixdfti.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixdfti_test.zig b/lib/std/special/compiler_rt/fixdfti_test.zig
index 796855b716..3b5bac4b4e 100644
--- a/lib/std/special/compiler_rt/fixdfti_test.zig
+++ b/lib/std/special/compiler_rt/fixdfti_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixint.zig b/lib/std/special/compiler_rt/fixint.zig
index 889b599e62..2947154d20 100644
--- a/lib/std/special/compiler_rt/fixint.zig
+++ b/lib/std/special/compiler_rt/fixint.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixint_test.zig b/lib/std/special/compiler_rt/fixint_test.zig
index 49942e5382..139546c52b 100644
--- a/lib/std/special/compiler_rt/fixint_test.zig
+++ b/lib/std/special/compiler_rt/fixint_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixsfdi.zig b/lib/std/special/compiler_rt/fixsfdi.zig
index cc5731946d..9563af1a56 100644
--- a/lib/std/special/compiler_rt/fixsfdi.zig
+++ b/lib/std/special/compiler_rt/fixsfdi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixsfdi_test.zig b/lib/std/special/compiler_rt/fixsfdi_test.zig
index d93c3d4218..7c13d83da5 100644
--- a/lib/std/special/compiler_rt/fixsfdi_test.zig
+++ b/lib/std/special/compiler_rt/fixsfdi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixsfsi.zig b/lib/std/special/compiler_rt/fixsfsi.zig
index 62334574b0..f1a32d9f77 100644
--- a/lib/std/special/compiler_rt/fixsfsi.zig
+++ b/lib/std/special/compiler_rt/fixsfsi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixsfsi_test.zig b/lib/std/special/compiler_rt/fixsfsi_test.zig
index 56c28d91ab..07c080470d 100644
--- a/lib/std/special/compiler_rt/fixsfsi_test.zig
+++ b/lib/std/special/compiler_rt/fixsfsi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixsfti.zig b/lib/std/special/compiler_rt/fixsfti.zig
index 31dea953e8..75c0a2fe1d 100644
--- a/lib/std/special/compiler_rt/fixsfti.zig
+++ b/lib/std/special/compiler_rt/fixsfti.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixsfti_test.zig b/lib/std/special/compiler_rt/fixsfti_test.zig
index d0bdcc4e75..dbc30c5404 100644
--- a/lib/std/special/compiler_rt/fixsfti_test.zig
+++ b/lib/std/special/compiler_rt/fixsfti_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixtfdi.zig b/lib/std/special/compiler_rt/fixtfdi.zig
index edf70dbe49..a9e37b777f 100644
--- a/lib/std/special/compiler_rt/fixtfdi.zig
+++ b/lib/std/special/compiler_rt/fixtfdi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixtfdi_test.zig b/lib/std/special/compiler_rt/fixtfdi_test.zig
index b926f33d50..dfc08f84a3 100644
--- a/lib/std/special/compiler_rt/fixtfdi_test.zig
+++ b/lib/std/special/compiler_rt/fixtfdi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixtfsi.zig b/lib/std/special/compiler_rt/fixtfsi.zig
index cf614ec8b3..cd92a972c4 100644
--- a/lib/std/special/compiler_rt/fixtfsi.zig
+++ b/lib/std/special/compiler_rt/fixtfsi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixtfsi_test.zig b/lib/std/special/compiler_rt/fixtfsi_test.zig
index 86207f7dbc..e5605a3936 100644
--- a/lib/std/special/compiler_rt/fixtfsi_test.zig
+++ b/lib/std/special/compiler_rt/fixtfsi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixtfti.zig b/lib/std/special/compiler_rt/fixtfti.zig
index e796b86d50..cfae7c249b 100644
--- a/lib/std/special/compiler_rt/fixtfti.zig
+++ b/lib/std/special/compiler_rt/fixtfti.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixtfti_test.zig b/lib/std/special/compiler_rt/fixtfti_test.zig
index 65a64ac431..b01e3af9f9 100644
--- a/lib/std/special/compiler_rt/fixtfti_test.zig
+++ b/lib/std/special/compiler_rt/fixtfti_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixuint.zig b/lib/std/special/compiler_rt/fixuint.zig
index e10926177f..755e1b8bb2 100644
--- a/lib/std/special/compiler_rt/fixuint.zig
+++ b/lib/std/special/compiler_rt/fixuint.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunsdfdi.zig b/lib/std/special/compiler_rt/fixunsdfdi.zig
index 19f94c95a8..24a88236e0 100644
--- a/lib/std/special/compiler_rt/fixunsdfdi.zig
+++ b/lib/std/special/compiler_rt/fixunsdfdi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunsdfdi_test.zig b/lib/std/special/compiler_rt/fixunsdfdi_test.zig
index da6fef3376..b7bbe42fb9 100644
--- a/lib/std/special/compiler_rt/fixunsdfdi_test.zig
+++ b/lib/std/special/compiler_rt/fixunsdfdi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunsdfsi.zig b/lib/std/special/compiler_rt/fixunsdfsi.zig
index 2f622aff39..416ffc59af 100644
--- a/lib/std/special/compiler_rt/fixunsdfsi.zig
+++ b/lib/std/special/compiler_rt/fixunsdfsi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunsdfsi_test.zig b/lib/std/special/compiler_rt/fixunsdfsi_test.zig
index ddbb05c705..a083f97f0b 100644
--- a/lib/std/special/compiler_rt/fixunsdfsi_test.zig
+++ b/lib/std/special/compiler_rt/fixunsdfsi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunsdfti.zig b/lib/std/special/compiler_rt/fixunsdfti.zig
index f11f6b937f..02836a6f75 100644
--- a/lib/std/special/compiler_rt/fixunsdfti.zig
+++ b/lib/std/special/compiler_rt/fixunsdfti.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunsdfti_test.zig b/lib/std/special/compiler_rt/fixunsdfti_test.zig
index 11ba3bc425..dbfeb0fc4b 100644
--- a/lib/std/special/compiler_rt/fixunsdfti_test.zig
+++ b/lib/std/special/compiler_rt/fixunsdfti_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunssfdi.zig b/lib/std/special/compiler_rt/fixunssfdi.zig
index 5dd9b27d10..77077b4344 100644
--- a/lib/std/special/compiler_rt/fixunssfdi.zig
+++ b/lib/std/special/compiler_rt/fixunssfdi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunssfdi_test.zig b/lib/std/special/compiler_rt/fixunssfdi_test.zig
index 018c94ce7c..d5e04292cb 100644
--- a/lib/std/special/compiler_rt/fixunssfdi_test.zig
+++ b/lib/std/special/compiler_rt/fixunssfdi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunssfsi.zig b/lib/std/special/compiler_rt/fixunssfsi.zig
index ce983cb78a..9c63424629 100644
--- a/lib/std/special/compiler_rt/fixunssfsi.zig
+++ b/lib/std/special/compiler_rt/fixunssfsi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunssfsi_test.zig b/lib/std/special/compiler_rt/fixunssfsi_test.zig
index 98cac8a1e4..c30c1d6804 100644
--- a/lib/std/special/compiler_rt/fixunssfsi_test.zig
+++ b/lib/std/special/compiler_rt/fixunssfsi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunssfti.zig b/lib/std/special/compiler_rt/fixunssfti.zig
index bbaeafd6a8..ab5b95ec7f 100644
--- a/lib/std/special/compiler_rt/fixunssfti.zig
+++ b/lib/std/special/compiler_rt/fixunssfti.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunssfti_test.zig b/lib/std/special/compiler_rt/fixunssfti_test.zig
index b5c79906b7..b148f5a35a 100644
--- a/lib/std/special/compiler_rt/fixunssfti_test.zig
+++ b/lib/std/special/compiler_rt/fixunssfti_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunstfdi.zig b/lib/std/special/compiler_rt/fixunstfdi.zig
index 3062e5322c..2053b948e0 100644
--- a/lib/std/special/compiler_rt/fixunstfdi.zig
+++ b/lib/std/special/compiler_rt/fixunstfdi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunstfdi_test.zig b/lib/std/special/compiler_rt/fixunstfdi_test.zig
index 299c509cea..b0297d4a2f 100644
--- a/lib/std/special/compiler_rt/fixunstfdi_test.zig
+++ b/lib/std/special/compiler_rt/fixunstfdi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunstfsi.zig b/lib/std/special/compiler_rt/fixunstfsi.zig
index 6836e5df36..3c317cd7fe 100644
--- a/lib/std/special/compiler_rt/fixunstfsi.zig
+++ b/lib/std/special/compiler_rt/fixunstfsi.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunstfsi_test.zig b/lib/std/special/compiler_rt/fixunstfsi_test.zig
index 2e5139e5e2..f1cb9f6de7 100644
--- a/lib/std/special/compiler_rt/fixunstfsi_test.zig
+++ b/lib/std/special/compiler_rt/fixunstfsi_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunstfti.zig b/lib/std/special/compiler_rt/fixunstfti.zig
index da3319ee5c..b089fedd3f 100644
--- a/lib/std/special/compiler_rt/fixunstfti.zig
+++ b/lib/std/special/compiler_rt/fixunstfti.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/fixunstfti_test.zig b/lib/std/special/compiler_rt/fixunstfti_test.zig
index 2fbde63e63..fcbf9d3b25 100644
--- a/lib/std/special/compiler_rt/fixunstfti_test.zig
+++ b/lib/std/special/compiler_rt/fixunstfti_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatXisf.zig b/lib/std/special/compiler_rt/floatXisf.zig
index fcbd02239e..4ce97c98f6 100644
--- a/lib/std/special/compiler_rt/floatXisf.zig
+++ b/lib/std/special/compiler_rt/floatXisf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatdidf.zig b/lib/std/special/compiler_rt/floatdidf.zig
index 2a1ba4cadd..2e07c91dd5 100644
--- a/lib/std/special/compiler_rt/floatdidf.zig
+++ b/lib/std/special/compiler_rt/floatdidf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatdidf_test.zig b/lib/std/special/compiler_rt/floatdidf_test.zig
index a2072cc922..41b851a306 100644
--- a/lib/std/special/compiler_rt/floatdidf_test.zig
+++ b/lib/std/special/compiler_rt/floatdidf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatdisf_test.zig b/lib/std/special/compiler_rt/floatdisf_test.zig
index 6676871035..845dc7b1ae 100644
--- a/lib/std/special/compiler_rt/floatdisf_test.zig
+++ b/lib/std/special/compiler_rt/floatdisf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatditf.zig b/lib/std/special/compiler_rt/floatditf.zig
index aa945ca5dd..a06f66e71e 100644
--- a/lib/std/special/compiler_rt/floatditf.zig
+++ b/lib/std/special/compiler_rt/floatditf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatditf_test.zig b/lib/std/special/compiler_rt/floatditf_test.zig
index ff4f10927c..13796efd69 100644
--- a/lib/std/special/compiler_rt/floatditf_test.zig
+++ b/lib/std/special/compiler_rt/floatditf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatsiXf.zig b/lib/std/special/compiler_rt/floatsiXf.zig
index 5941e0ca57..50fcdd748b 100644
--- a/lib/std/special/compiler_rt/floatsiXf.zig
+++ b/lib/std/special/compiler_rt/floatsiXf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floattidf.zig b/lib/std/special/compiler_rt/floattidf.zig
index 73d86f7747..2fa5fee400 100644
--- a/lib/std/special/compiler_rt/floattidf.zig
+++ b/lib/std/special/compiler_rt/floattidf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floattidf_test.zig b/lib/std/special/compiler_rt/floattidf_test.zig
index d299ed8087..ab6311c9ff 100644
--- a/lib/std/special/compiler_rt/floattidf_test.zig
+++ b/lib/std/special/compiler_rt/floattidf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floattisf_test.zig b/lib/std/special/compiler_rt/floattisf_test.zig
index c92db9e150..2458e4bb76 100644
--- a/lib/std/special/compiler_rt/floattisf_test.zig
+++ b/lib/std/special/compiler_rt/floattisf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floattitf.zig b/lib/std/special/compiler_rt/floattitf.zig
index 87408ea445..a577b6dc10 100644
--- a/lib/std/special/compiler_rt/floattitf.zig
+++ b/lib/std/special/compiler_rt/floattitf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floattitf_test.zig b/lib/std/special/compiler_rt/floattitf_test.zig
index c4014a6298..3310875ecc 100644
--- a/lib/std/special/compiler_rt/floattitf_test.zig
+++ b/lib/std/special/compiler_rt/floattitf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatundidf.zig b/lib/std/special/compiler_rt/floatundidf.zig
index a88ca4a03d..e079dabced 100644
--- a/lib/std/special/compiler_rt/floatundidf.zig
+++ b/lib/std/special/compiler_rt/floatundidf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatundidf_test.zig b/lib/std/special/compiler_rt/floatundidf_test.zig
index c0651cb359..a0e18c4f5a 100644
--- a/lib/std/special/compiler_rt/floatundidf_test.zig
+++ b/lib/std/special/compiler_rt/floatundidf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatundisf.zig b/lib/std/special/compiler_rt/floatundisf.zig
index 67cd53b21c..ac7e576316 100644
--- a/lib/std/special/compiler_rt/floatundisf.zig
+++ b/lib/std/special/compiler_rt/floatundisf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatunditf.zig b/lib/std/special/compiler_rt/floatunditf.zig
index 014a479c5f..59c433b372 100644
--- a/lib/std/special/compiler_rt/floatunditf.zig
+++ b/lib/std/special/compiler_rt/floatunditf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatunditf_test.zig b/lib/std/special/compiler_rt/floatunditf_test.zig
index 19d1b4a2a3..e734355589 100644
--- a/lib/std/special/compiler_rt/floatunditf_test.zig
+++ b/lib/std/special/compiler_rt/floatunditf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatunsidf.zig b/lib/std/special/compiler_rt/floatunsidf.zig
index c9a31eff8e..1b700b001d 100644
--- a/lib/std/special/compiler_rt/floatunsidf.zig
+++ b/lib/std/special/compiler_rt/floatunsidf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatunsisf.zig b/lib/std/special/compiler_rt/floatunsisf.zig
index 17eae51092..1a0ef47b5c 100644
--- a/lib/std/special/compiler_rt/floatunsisf.zig
+++ b/lib/std/special/compiler_rt/floatunsisf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatunsitf.zig b/lib/std/special/compiler_rt/floatunsitf.zig
index f59446abac..3cdadfc07e 100644
--- a/lib/std/special/compiler_rt/floatunsitf.zig
+++ b/lib/std/special/compiler_rt/floatunsitf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatunsitf_test.zig b/lib/std/special/compiler_rt/floatunsitf_test.zig
index deb95ca396..7e7b8b69b9 100644
--- a/lib/std/special/compiler_rt/floatunsitf_test.zig
+++ b/lib/std/special/compiler_rt/floatunsitf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatuntidf.zig b/lib/std/special/compiler_rt/floatuntidf.zig
index adb804d0ec..6e1fe3b117 100644
--- a/lib/std/special/compiler_rt/floatuntidf.zig
+++ b/lib/std/special/compiler_rt/floatuntidf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatuntidf_test.zig b/lib/std/special/compiler_rt/floatuntidf_test.zig
index cce3893860..427c7a08f2 100644
--- a/lib/std/special/compiler_rt/floatuntidf_test.zig
+++ b/lib/std/special/compiler_rt/floatuntidf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatuntisf.zig b/lib/std/special/compiler_rt/floatuntisf.zig
index d0c9a76562..dd173945ba 100644
--- a/lib/std/special/compiler_rt/floatuntisf.zig
+++ b/lib/std/special/compiler_rt/floatuntisf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatuntisf_test.zig b/lib/std/special/compiler_rt/floatuntisf_test.zig
index 42379d8084..78d45dc5e0 100644
--- a/lib/std/special/compiler_rt/floatuntisf_test.zig
+++ b/lib/std/special/compiler_rt/floatuntisf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatuntitf.zig b/lib/std/special/compiler_rt/floatuntitf.zig
index c87ff50e9a..9759268b93 100644
--- a/lib/std/special/compiler_rt/floatuntitf.zig
+++ b/lib/std/special/compiler_rt/floatuntitf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/floatuntitf_test.zig b/lib/std/special/compiler_rt/floatuntitf_test.zig
index 62c9b631df..fd57be51e6 100644
--- a/lib/std/special/compiler_rt/floatuntitf_test.zig
+++ b/lib/std/special/compiler_rt/floatuntitf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/int.zig b/lib/std/special/compiler_rt/int.zig
index 1fb2c263e1..b852139516 100644
--- a/lib/std/special/compiler_rt/int.zig
+++ b/lib/std/special/compiler_rt/int.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/lshrdi3_test.zig b/lib/std/special/compiler_rt/lshrdi3_test.zig
index de83f0a9c8..5443fd9bce 100644
--- a/lib/std/special/compiler_rt/lshrdi3_test.zig
+++ b/lib/std/special/compiler_rt/lshrdi3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/lshrti3_test.zig b/lib/std/special/compiler_rt/lshrti3_test.zig
index f831e8b132..bfd812f028 100644
--- a/lib/std/special/compiler_rt/lshrti3_test.zig
+++ b/lib/std/special/compiler_rt/lshrti3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/modti3.zig b/lib/std/special/compiler_rt/modti3.zig
index 9c3de44395..298a488dc2 100644
--- a/lib/std/special/compiler_rt/modti3.zig
+++ b/lib/std/special/compiler_rt/modti3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/modti3_test.zig b/lib/std/special/compiler_rt/modti3_test.zig
index cad60c015e..644c9027b7 100644
--- a/lib/std/special/compiler_rt/modti3_test.zig
+++ b/lib/std/special/compiler_rt/modti3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/mulXf3.zig b/lib/std/special/compiler_rt/mulXf3.zig
index d8dac5b1bc..a4c71529d1 100644
--- a/lib/std/special/compiler_rt/mulXf3.zig
+++ b/lib/std/special/compiler_rt/mulXf3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/mulXf3_test.zig b/lib/std/special/compiler_rt/mulXf3_test.zig
index c9994b089b..272c96522d 100644
--- a/lib/std/special/compiler_rt/mulXf3_test.zig
+++ b/lib/std/special/compiler_rt/mulXf3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/muldi3.zig b/lib/std/special/compiler_rt/muldi3.zig
index 2de96ea66c..607ac489fc 100644
--- a/lib/std/special/compiler_rt/muldi3.zig
+++ b/lib/std/special/compiler_rt/muldi3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/muldi3_test.zig b/lib/std/special/compiler_rt/muldi3_test.zig
index b4962189cd..78023f514b 100644
--- a/lib/std/special/compiler_rt/muldi3_test.zig
+++ b/lib/std/special/compiler_rt/muldi3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/mulodi4.zig b/lib/std/special/compiler_rt/mulodi4.zig
index fab345fa47..ed90b4d382 100644
--- a/lib/std/special/compiler_rt/mulodi4.zig
+++ b/lib/std/special/compiler_rt/mulodi4.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/mulodi4_test.zig b/lib/std/special/compiler_rt/mulodi4_test.zig
index a96f7a1996..7d7658e192 100644
--- a/lib/std/special/compiler_rt/mulodi4_test.zig
+++ b/lib/std/special/compiler_rt/mulodi4_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/muloti4.zig b/lib/std/special/compiler_rt/muloti4.zig
index b1ad82da29..30054ac751 100644
--- a/lib/std/special/compiler_rt/muloti4.zig
+++ b/lib/std/special/compiler_rt/muloti4.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/muloti4_test.zig b/lib/std/special/compiler_rt/muloti4_test.zig
index 44ab93a069..83722df6a5 100644
--- a/lib/std/special/compiler_rt/muloti4_test.zig
+++ b/lib/std/special/compiler_rt/muloti4_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/multi3.zig b/lib/std/special/compiler_rt/multi3.zig
index fad73789ac..d417c79ff2 100644
--- a/lib/std/special/compiler_rt/multi3.zig
+++ b/lib/std/special/compiler_rt/multi3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/multi3_test.zig b/lib/std/special/compiler_rt/multi3_test.zig
index 04b70d0538..674cf1cb9b 100644
--- a/lib/std/special/compiler_rt/multi3_test.zig
+++ b/lib/std/special/compiler_rt/multi3_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/negXf2.zig b/lib/std/special/compiler_rt/negXf2.zig
index 389b26584d..8c7010cccb 100644
--- a/lib/std/special/compiler_rt/negXf2.zig
+++ b/lib/std/special/compiler_rt/negXf2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/popcountdi2.zig b/lib/std/special/compiler_rt/popcountdi2.zig
index 5bb49ce402..8495068339 100644
--- a/lib/std/special/compiler_rt/popcountdi2.zig
+++ b/lib/std/special/compiler_rt/popcountdi2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/popcountdi2_test.zig b/lib/std/special/compiler_rt/popcountdi2_test.zig
index 6ea181bf0e..d0665bf278 100644
--- a/lib/std/special/compiler_rt/popcountdi2_test.zig
+++ b/lib/std/special/compiler_rt/popcountdi2_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/shift.zig b/lib/std/special/compiler_rt/shift.zig
index 21e52d9db8..46712738ab 100644
--- a/lib/std/special/compiler_rt/shift.zig
+++ b/lib/std/special/compiler_rt/shift.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -124,7 +124,7 @@ pub fn __aeabi_llsr(a: i64, b: i32) callconv(.AAPCS) i64 {
return __lshrdi3(a, b);
}
-test "" {
+test {
_ = @import("ashrdi3_test.zig");
_ = @import("ashrti3_test.zig");
diff --git a/lib/std/special/compiler_rt/sparc.zig b/lib/std/special/compiler_rt/sparc.zig
new file mode 100644
index 0000000000..e66bb25886
--- /dev/null
+++ b/lib/std/special/compiler_rt/sparc.zig
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+//
+// SPARC uses a different naming scheme for its support routines so we map it here to the x86 name.
+
+const std = @import("std");
+const builtin = @import("builtin");
+
+// The SPARC Architecture Manual, Version 9:
+// A.13 Floating-Point Compare
+const FCMP = extern enum(i32) {
+ Equal = 0,
+ Less = 1,
+ Greater = 2,
+ Unordered = 3,
+};
+
+// Basic arithmetic
+
+pub fn _Qp_add(c: *f128, a: *f128, b: *f128) callconv(.C) void {
+ c.* = @import("addXf3.zig").__addtf3(a.*, b.*);
+}
+
+pub fn _Qp_div(c: *f128, a: *f128, b: *f128) callconv(.C) void {
+ c.* = @import("divtf3.zig").__divtf3(a.*, b.*);
+}
+
+pub fn _Qp_mul(c: *f128, a: *f128, b: *f128) callconv(.C) void {
+ c.* = @import("mulXf3.zig").__multf3(a.*, b.*);
+}
+
+pub fn _Qp_sub(c: *f128, a: *f128, b: *f128) callconv(.C) void {
+ c.* = @import("addXf3.zig").__subtf3(a.*, b.*);
+}
+
+// Comparison
+
+pub fn _Qp_cmp(a: *f128, b: *f128) callconv(.C) i32 {
+ return @enumToInt(@import("compareXf2.zig").cmp(f128, FCMP, a.*, b.*));
+}
+
+pub fn _Qp_feq(a: *f128, b: *f128) callconv(.C) bool {
+ return _Qp_cmp(a, b) == @enumToInt(FCMP.Equal);
+}
+
+pub fn _Qp_fne(a: *f128, b: *f128) callconv(.C) bool {
+ return _Qp_cmp(a, b) != @enumToInt(FCMP.Equal);
+}
+
+pub fn _Qp_flt(a: *f128, b: *f128) callconv(.C) bool {
+ return _Qp_cmp(a, b) == @enumToInt(FCMP.Less);
+}
+
+pub fn _Qp_fle(a: *f128, b: *f128) callconv(.C) bool {
+ const cmp = _Qp_cmp(a, b);
+ return cmp == @enumToInt(FCMP.Less) or cmp == @enumToInt(FCMP.Equal);
+}
+
+pub fn _Qp_fgt(a: *f128, b: *f128) callconv(.C) bool {
+ return _Qp_cmp(a, b) == @enumToInt(FCMP.Greater);
+}
+
+pub fn _Qp_fge(a: *f128, b: *f128) callconv(.C) bool {
+ const cmp = _Qp_cmp(a, b);
+ return cmp == @enumToInt(FCMP.Greater) or cmp == @enumToInt(FCMP.Equal);
+}
+
+// Casting
+
+pub fn _Qp_dtoq(c: *f128, a: f64) callconv(.C) void {
+ c.* = @import("extendXfYf2.zig").__extenddftf2(a);
+}
+
+pub fn _Qp_qtod(a: *f128) callconv(.C) f64 {
+ return @import("truncXfYf2.zig").__trunctfdf2(a.*);
+}
diff --git a/lib/std/special/compiler_rt/stack_probe.zig b/lib/std/special/compiler_rt/stack_probe.zig
index 58cce9fb6f..d0dcd70550 100644
--- a/lib/std/special/compiler_rt/stack_probe.zig
+++ b/lib/std/special/compiler_rt/stack_probe.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig
index a3885d1211..470ac17c2c 100644
--- a/lib/std/special/compiler_rt/truncXfYf2.zig
+++ b/lib/std/special/compiler_rt/truncXfYf2.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/truncXfYf2_test.zig b/lib/std/special/compiler_rt/truncXfYf2_test.zig
index 048005f86d..6426614b07 100644
--- a/lib/std/special/compiler_rt/truncXfYf2_test.zig
+++ b/lib/std/special/compiler_rt/truncXfYf2_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/udivmod.zig b/lib/std/special/compiler_rt/udivmod.zig
index aa3ca7ea30..265a365dc8 100644
--- a/lib/std/special/compiler_rt/udivmod.zig
+++ b/lib/std/special/compiler_rt/udivmod.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/udivmoddi4_test.zig b/lib/std/special/compiler_rt/udivmoddi4_test.zig
index 74a4a828e8..d3f39a0589 100644
--- a/lib/std/special/compiler_rt/udivmoddi4_test.zig
+++ b/lib/std/special/compiler_rt/udivmoddi4_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/udivmodti4.zig b/lib/std/special/compiler_rt/udivmodti4.zig
index ff33a35680..310f4dce42 100644
--- a/lib/std/special/compiler_rt/udivmodti4.zig
+++ b/lib/std/special/compiler_rt/udivmodti4.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/udivmodti4_test.zig b/lib/std/special/compiler_rt/udivmodti4_test.zig
index 1852d5c7af..667b27f0aa 100644
--- a/lib/std/special/compiler_rt/udivmodti4_test.zig
+++ b/lib/std/special/compiler_rt/udivmodti4_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/udivti3.zig b/lib/std/special/compiler_rt/udivti3.zig
index 187b4a1577..8d95624edc 100644
--- a/lib/std/special/compiler_rt/udivti3.zig
+++ b/lib/std/special/compiler_rt/udivti3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/compiler_rt/umodti3.zig b/lib/std/special/compiler_rt/umodti3.zig
index 3bc5be8ffc..98160039a1 100644
--- a/lib/std/special/compiler_rt/umodti3.zig
+++ b/lib/std/special/compiler_rt/umodti3.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/ssp.zig b/lib/std/special/ssp.zig
index 255487744d..81db44a534 100644
--- a/lib/std/special/ssp.zig
+++ b/lib/std/special/ssp.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig
index 2b2fe78262..f5a93298b5 100644
--- a/lib/std/special/test_runner.zig
+++ b/lib/std/special/test_runner.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -11,7 +11,15 @@ pub const io_mode: io.Mode = builtin.test_io_mode;
var log_err_count: usize = 0;
+var args_buffer: [std.fs.MAX_PATH_BYTES + std.mem.page_size]u8 = undefined;
+var args_allocator = std.heap.FixedBufferAllocator.init(&args_buffer);
+
pub fn main() anyerror!void {
+ const args = std.process.argsAlloc(&args_allocator.allocator) catch {
+ @panic("Too many bytes passed over the CLI to the test runner");
+ };
+ std.testing.zig_exe_path = args[1];
+
const test_fn_list = builtin.test_functions;
var ok_count: usize = 0;
var skip_count: usize = 0;
@@ -36,11 +44,11 @@ pub fn main() anyerror!void {
}
std.testing.log_level = .warn;
- var test_node = root_node.start(test_fn.name, null);
+ var test_node = root_node.start(test_fn.name, 0);
test_node.activate();
progress.refresh();
if (progress.terminal == null) {
- std.debug.print("{}/{} {}... ", .{ i + 1, test_fn_list.len, test_fn.name });
+ std.debug.print("{d}/{d} {s}... ", .{ i + 1, test_fn_list.len, test_fn.name });
}
const result = if (test_fn.async_frame_size) |size| switch (io_mode) {
.evented => blk: {
@@ -54,7 +62,7 @@ pub fn main() anyerror!void {
.blocking => {
skip_count += 1;
test_node.end();
- progress.log("{}...SKIP (async test)\n", .{test_fn.name});
+ progress.log("{s}...SKIP (async test)\n", .{test_fn.name});
if (progress.terminal == null) std.debug.print("SKIP (async test)\n", .{});
continue;
},
@@ -67,7 +75,7 @@ pub fn main() anyerror!void {
error.SkipZigTest => {
skip_count += 1;
test_node.end();
- progress.log("{}...SKIP\n", .{test_fn.name});
+ progress.log("{s}...SKIP\n", .{test_fn.name});
if (progress.terminal == null) std.debug.print("SKIP\n", .{});
},
else => {
@@ -78,15 +86,15 @@ pub fn main() anyerror!void {
}
root_node.end();
if (ok_count == test_fn_list.len) {
- std.debug.print("All {} tests passed.\n", .{ok_count});
+ std.debug.print("All {d} tests passed.\n", .{ok_count});
} else {
- std.debug.print("{} passed; {} skipped.\n", .{ ok_count, skip_count });
+ std.debug.print("{d} passed; {d} skipped.\n", .{ ok_count, skip_count });
}
if (log_err_count != 0) {
- std.debug.print("{} errors were logged.\n", .{log_err_count});
+ std.debug.print("{d} errors were logged.\n", .{log_err_count});
}
if (leaks != 0) {
- std.debug.print("{} tests leaked memory.\n", .{leaks});
+ std.debug.print("{d} tests leaked memory.\n", .{leaks});
}
if (leaks != 0 or log_err_count != 0) {
std.process.exit(1);
@@ -103,6 +111,6 @@ pub fn log(
log_err_count += 1;
}
if (@enumToInt(message_level) <= @enumToInt(std.testing.log_level)) {
- std.debug.print("[{}] ({}): " ++ format ++ "\n", .{ @tagName(scope), @tagName(message_level) } ++ args);
+ std.debug.print("[{s}] ({s}): " ++ format ++ "\n", .{ @tagName(scope), @tagName(message_level) } ++ args);
}
}
diff --git a/lib/std/spinlock.zig b/lib/std/spinlock.zig
deleted file mode 100644
index d72ac14ecf..0000000000
--- a/lib/std/spinlock.zig
+++ /dev/null
@@ -1,90 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("std.zig");
-const builtin = @import("builtin");
-
-pub const SpinLock = struct {
- state: State,
-
- const State = enum(u8) {
- Unlocked,
- Locked,
- };
-
- pub const Held = struct {
- spinlock: *SpinLock,
-
- pub fn release(self: Held) void {
- @atomicStore(State, &self.spinlock.state, .Unlocked, .Release);
- }
- };
-
- pub fn init() SpinLock {
- return SpinLock{ .state = .Unlocked };
- }
-
- pub fn deinit(self: *SpinLock) void {
- self.* = undefined;
- }
-
- pub fn tryAcquire(self: *SpinLock) ?Held {
- return switch (@atomicRmw(State, &self.state, .Xchg, .Locked, .Acquire)) {
- .Unlocked => Held{ .spinlock = self },
- .Locked => null,
- };
- }
-
- pub fn acquire(self: *SpinLock) Held {
- while (true) {
- return self.tryAcquire() orelse {
- yield();
- continue;
- };
- }
- }
-
- pub fn yield() void {
- // On native windows, SwitchToThread is too expensive,
- // and yielding for 380-410 iterations was found to be
- // a nice sweet spot. Posix systems on the other hand,
- // especially linux, perform better by yielding the thread.
- switch (builtin.os.tag) {
- .windows => loopHint(400),
- else => std.os.sched_yield() catch loopHint(1),
- }
- }
-
- /// Hint to the cpu that execution is spinning
- /// for the given amount of iterations.
- pub fn loopHint(iterations: usize) void {
- var i = iterations;
- while (i != 0) : (i -= 1) {
- switch (builtin.arch) {
- // these instructions use a memory clobber as they
- // flush the pipeline of any speculated reads/writes.
- .i386, .x86_64 => asm volatile ("pause"
- :
- :
- : "memory"
- ),
- .arm, .aarch64 => asm volatile ("yield"
- :
- :
- : "memory"
- ),
- else => std.os.sched_yield() catch {},
- }
- }
- }
-};
-
-test "spinlock" {
- var lock = SpinLock.init();
- defer lock.deinit();
-
- const held = lock.acquire();
- defer held.release();
-}
diff --git a/lib/std/start.zig b/lib/std/start.zig
index 7d3a9c45c7..0fb96c768f 100644
--- a/lib/std/start.zig
+++ b/lib/std/start.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -10,6 +10,7 @@ const std = @import("std.zig");
const builtin = std.builtin;
const assert = std.debug.assert;
const uefi = std.os.uefi;
+const tlcsprng = @import("crypto/tlcsprng.zig");
var argc_argv_ptr: [*]usize = undefined;
@@ -261,11 +262,11 @@ const bad_main_ret = "expected return type of main to be 'void', '!void', 'noret
// This is marked inline because for some reason LLVM in release mode fails to inline it,
// and we want fewer call frames in stack traces.
-inline fn initEventLoopAndCallMain() u8 {
+fn initEventLoopAndCallMain() callconv(.Inline) u8 {
if (std.event.Loop.instance) |loop| {
if (!@hasDecl(root, "event_loop")) {
loop.init() catch |err| {
- std.log.err("{}", .{@errorName(err)});
+ std.log.err("{s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
@@ -290,11 +291,11 @@ inline fn initEventLoopAndCallMain() u8 {
// and we want fewer call frames in stack traces.
// TODO This function is duplicated from initEventLoopAndCallMain instead of using generics
// because it is working around stage1 compiler bugs.
-inline fn initEventLoopAndCallWinMain() std.os.windows.INT {
+fn initEventLoopAndCallWinMain() callconv(.Inline) std.os.windows.INT {
if (std.event.Loop.instance) |loop| {
if (!@hasDecl(root, "event_loop")) {
loop.init() catch |err| {
- std.log.err("{}", .{@errorName(err)});
+ std.log.err("{s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
@@ -342,7 +343,7 @@ pub fn callMain() u8 {
},
.ErrorUnion => {
const result = root.main() catch |err| {
- std.log.err("{}", .{@errorName(err)});
+ std.log.err("{s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
diff --git a/lib/std/start_windows_tls.zig b/lib/std/start_windows_tls.zig
index 18c016d206..1ad10126d2 100644
--- a/lib/std/start_windows_tls.zig
+++ b/lib/std/start_windows_tls.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/std.zig b/lib/std/std.zig
index f6da7afc55..c0d97a9d9c 100644
--- a/lib/std/std.zig
+++ b/lib/std/std.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -8,13 +8,11 @@ pub const ArrayHashMapUnmanaged = array_hash_map.ArrayHashMapUnmanaged;
pub const ArrayList = @import("array_list.zig").ArrayList;
pub const ArrayListAligned = @import("array_list.zig").ArrayListAligned;
pub const ArrayListAlignedUnmanaged = @import("array_list.zig").ArrayListAlignedUnmanaged;
-pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled;
pub const ArrayListUnmanaged = @import("array_list.zig").ArrayListUnmanaged;
pub const AutoArrayHashMap = array_hash_map.AutoArrayHashMap;
pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;
pub const AutoHashMap = hash_map.AutoHashMap;
pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
-pub const AutoResetEvent = @import("auto_reset_event.zig").AutoResetEvent;
pub const BufMap = @import("buf_map.zig").BufMap;
pub const BufSet = @import("buf_set.zig").BufSet;
pub const ChildProcess = @import("child_process.zig").ChildProcess;
@@ -22,25 +20,22 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM
pub const DynLib = @import("dynamic_library.zig").DynLib;
pub const HashMap = hash_map.HashMap;
pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
-pub const mutex = @import("mutex.zig");
-pub const Mutex = mutex.Mutex;
+pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;
pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
-pub const Progress = @import("progress.zig").Progress;
-pub const ResetEvent = @import("reset_event.zig").ResetEvent;
+pub const Progress = @import("Progress.zig");
pub const SemanticVersion = @import("SemanticVersion.zig");
pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
-pub const SpinLock = @import("spinlock.zig").SpinLock;
pub const StringHashMap = hash_map.StringHashMap;
pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged;
pub const StringArrayHashMap = array_hash_map.StringArrayHashMap;
pub const StringArrayHashMapUnmanaged = array_hash_map.StringArrayHashMapUnmanaged;
pub const TailQueue = @import("linked_list.zig").TailQueue;
pub const Target = @import("target.zig").Target;
-pub const Thread = @import("thread.zig").Thread;
+pub const Thread = @import("Thread.zig");
pub const array_hash_map = @import("array_hash_map.zig");
pub const atomic = @import("atomic.zig");
@@ -83,6 +78,7 @@ pub const testing = @import("testing.zig");
pub const time = @import("time.zig");
pub const unicode = @import("unicode.zig");
pub const valgrind = @import("valgrind.zig");
+pub const wasm = @import("wasm.zig");
pub const zig = @import("zig.zig");
pub const start = @import("start.zig");
@@ -92,6 +88,37 @@ comptime {
_ = start;
}
-test "" {
- testing.refAllDecls(@This());
+test {
+ if (builtin.os.tag == .windows) {
+ // We only test the Windows-relevant stuff to save memory because the CI
+ // server is hitting OOM. TODO revert this after stage2 arrives.
+ _ = ChildProcess;
+ _ = DynLib;
+ _ = Progress;
+ _ = Target;
+ _ = Thread;
+
+ _ = atomic;
+ _ = build;
+ _ = builtin;
+ _ = debug;
+ _ = event;
+ _ = fs;
+ _ = heap;
+ _ = io;
+ _ = log;
+ _ = macho;
+ _ = net;
+ _ = os;
+ _ = once;
+ _ = pdb;
+ _ = process;
+ _ = testing;
+ _ = time;
+ _ = unicode;
+ _ = zig;
+ _ = start;
+ } else {
+ testing.refAllDecls(@This());
+ }
}
diff --git a/lib/std/target.zig b/lib/std/target.zig
index 08fecd7a82..ff7ee5d33c 100644
--- a/lib/std/target.zig
+++ b/lib/std/target.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -57,6 +57,9 @@ pub const Target = struct {
wasi,
emscripten,
uefi,
+ opencl,
+ glsl450,
+ vulkan,
other,
pub fn isDarwin(tag: Tag) bool {
@@ -95,7 +98,7 @@ pub const Target = struct {
win7 = 0x06010000,
win8 = 0x06020000,
win8_1 = 0x06030000,
- win10 = 0x0A000000,
+ win10 = 0x0A000000, //aka win10_th1
win10_th2 = 0x0A000001,
win10_rs1 = 0x0A000002,
win10_rs2 = 0x0A000003,
@@ -103,11 +106,34 @@ pub const Target = struct {
win10_rs4 = 0x0A000005,
win10_rs5 = 0x0A000006,
win10_19h1 = 0x0A000007,
- win10_20h1 = 0x0A000008,
+ win10_vb = 0x0A000008, //aka win10_19h2
+ win10_mn = 0x0A000009, //aka win10_20h1
+ win10_fe = 0x0A00000A, //aka win10_20h2
_,
/// Latest Windows version that the Zig Standard Library is aware of
- pub const latest = WindowsVersion.win10_20h1;
+ pub const latest = WindowsVersion.win10_fe;
+
+ /// Compared against build numbers reported by the runtime to distinguish win10 versions,
+ /// where 0x0A000000 + index corresponds to the WindowsVersion u32 value.
+ pub const known_win10_build_numbers = [_]u32{
+ 10240, //win10 aka win10_th1
+ 10586, //win10_th2
+ 14393, //win10_rs1
+ 15063, //win10_rs2
+ 16299, //win10_rs3
+ 17134, //win10_rs4
+ 17763, //win10_rs5
+ 18362, //win10_19h1
+ 18363, //win10_vb aka win10_19h2
+ 19041, //win10_mn aka win10_20h1
+ 19042, //win10_fe aka win10_20h2
+ };
+
+ /// Returns whether the first version `self` is newer (greater) than or equal to the second version `ver`.
+ pub fn isAtLeast(self: WindowsVersion, ver: WindowsVersion) bool {
+ return @enumToInt(self) >= @enumToInt(ver);
+ }
pub const Range = struct {
min: WindowsVersion,
@@ -136,14 +162,14 @@ pub const Target = struct {
) !void {
if (fmt.len > 0 and fmt[0] == 's') {
if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.latest)) {
- try std.fmt.format(out_stream, ".{}", .{@tagName(self)});
+ try std.fmt.format(out_stream, ".{s}", .{@tagName(self)});
} else {
// TODO this code path breaks zig triples, but it is used in `builtin`
try std.fmt.format(out_stream, "@intToEnum(Target.Os.WindowsVersion, 0x{X:0>8})", .{@enumToInt(self)});
}
} else {
if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.latest)) {
- try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)});
+ try std.fmt.format(out_stream, "WindowsVersion.{s}", .{@tagName(self)});
} else {
try std.fmt.format(out_stream, "WindowsVersion(0x{X:0>8})", .{@enumToInt(self)});
}
@@ -225,6 +251,9 @@ pub const Target = struct {
.wasi,
.emscripten,
.uefi,
+ .opencl, // TODO: OpenCL versions
+ .glsl450, // TODO: GLSL versions
+ .vulkan,
.other,
=> return .{ .none = {} },
@@ -237,7 +266,7 @@ pub const Target = struct {
.macos => return .{
.semver = .{
.min = .{ .major = 10, .minor = 13 },
- .max = .{ .major = 10, .minor = 15, .patch = 7 },
+ .max = .{ .major = 11, .minor = 1 },
},
},
.ios => return .{
@@ -337,6 +366,9 @@ pub const Target = struct {
};
}
+ /// On Darwin, we always link libSystem which contains libc.
+ /// Similarly on FreeBSD and NetBSD we always link system libc
+ /// since this is the stable syscall interface.
pub fn requiresLibC(os: Os) bool {
return switch (os.tag) {
.freebsd,
@@ -347,6 +379,7 @@ pub const Target = struct {
.watchos,
.dragonfly,
.openbsd,
+ .haiku,
=> true,
.linux,
@@ -359,7 +392,6 @@ pub const Target = struct {
.lv2,
.solaris,
.zos,
- .haiku,
.minix,
.rtems,
.nacl,
@@ -377,6 +409,9 @@ pub const Target = struct {
.wasi,
.emscripten,
.uefi,
+ .opencl,
+ .glsl450,
+ .vulkan,
.other,
=> false,
};
@@ -395,6 +430,7 @@ pub const Target = struct {
pub const powerpc = @import("target/powerpc.zig");
pub const riscv = @import("target/riscv.zig");
pub const sparc = @import("target/sparc.zig");
+ pub const spirv = @import("target/spirv.zig");
pub const systemz = @import("target/systemz.zig");
pub const wasm = @import("target/wasm.zig");
pub const x86 = @import("target/x86.zig");
@@ -433,7 +469,6 @@ pub const Target = struct {
.lv2,
.solaris,
.zos,
- .haiku,
.minix,
.rtems,
.nacl,
@@ -459,6 +494,7 @@ pub const Target = struct {
.kfreebsd,
.netbsd,
.hurd,
+ .haiku,
=> return .gnu,
.windows,
.uefi,
@@ -467,6 +503,10 @@ pub const Target = struct {
.wasi,
.emscripten,
=> return .musl,
+ .opencl, // TODO: SPIR-V ABIs with Linkage capability
+ .glsl450,
+ .vulkan,
+ => return .none,
}
}
@@ -502,6 +542,7 @@ pub const Target = struct {
macho,
wasm,
c,
+ spirv,
hex,
raw,
};
@@ -719,6 +760,8 @@ pub const Target = struct {
// Stage1 currently assumes that architectures above this comment
// map one-to-one with the ZigLLVM_ArchType enum.
spu_2,
+ spirv32,
+ spirv64,
pub fn isARM(arch: Arch) bool {
return switch (arch) {
@@ -833,6 +876,8 @@ pub const Target = struct {
.s390x => ._S390,
.ve => ._NONE,
.spu_2 => ._SPU_2,
+ .spirv32 => ._NONE,
+ .spirv64 => ._NONE,
};
}
@@ -891,6 +936,8 @@ pub const Target = struct {
.s390x => .Unknown,
.ve => .Unknown,
.spu_2 => .Unknown,
+ .spirv32 => .Unknown,
+ .spirv64 => .Unknown,
};
}
@@ -935,6 +982,9 @@ pub const Target = struct {
.shave,
.ve,
.spu_2,
+ // GPU bitness is opaque. For now, assume little endian.
+ .spirv32,
+ .spirv64,
=> .Little,
.arc,
@@ -991,6 +1041,7 @@ pub const Target = struct {
.wasm32,
.renderscript32,
.aarch64_32,
+ .spirv32,
=> return 32,
.aarch64,
@@ -1014,6 +1065,7 @@ pub const Target = struct {
.sparcv9,
.s390x,
.ve,
+ .spirv64,
=> return 64,
}
}
@@ -1037,6 +1089,7 @@ pub const Target = struct {
.i386, .x86_64 => "x86",
.nvptx, .nvptx64 => "nvptx",
.wasm32, .wasm64 => "wasm",
+ .spirv32, .spirv64 => "spir-v",
else => @tagName(arch),
};
}
@@ -1180,7 +1233,7 @@ pub const Target = struct {
}
pub fn linuxTripleSimple(allocator: *mem.Allocator, cpu_arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![]u8 {
- return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) });
+ return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) });
}
pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 {
@@ -1327,6 +1380,9 @@ pub const Target = struct {
.uefi,
.windows,
.emscripten,
+ .opencl,
+ .glsl450,
+ .vulkan,
.other,
=> return false,
else => return true,
@@ -1384,7 +1440,7 @@ pub const Target = struct {
if (self.abi == .android) {
const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else "";
- return print(&result, "/system/bin/linker{}", .{suffix});
+ return print(&result, "/system/bin/linker{s}", .{suffix});
}
if (self.abi.isMusl()) {
@@ -1398,7 +1454,7 @@ pub const Target = struct {
else => |arch| @tagName(arch),
};
const arch_suffix = if (is_arm and self.abi.floatAbi() == .hard) "hf" else "";
- return print(&result, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix });
+ return print(&result, "/lib/ld-musl-{s}{s}.so.1", .{ arch_part, arch_suffix });
}
switch (self.os.tag) {
@@ -1437,7 +1493,7 @@ pub const Target = struct {
};
const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008);
const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1";
- return print(&result, "/lib{}/{}", .{ lib_suffix, loader });
+ return print(&result, "/lib{s}/{s}", .{ lib_suffix, loader });
},
.powerpc => return copy(&result, "/lib/ld.so.1"),
@@ -1462,6 +1518,8 @@ pub const Target = struct {
.nvptx64,
.spu_2,
.avr,
+ .spirv32,
+ .spirv64,
=> return result,
// TODO go over each item in this list and either move it to the above list, or
@@ -1505,9 +1563,15 @@ pub const Target = struct {
.windows,
.emscripten,
.wasi,
+ .opencl,
+ .glsl450,
+ .vulkan,
.other,
=> return result,
+ // TODO revisit when multi-arch for Haiku is available
+ .haiku => return copy(&result, "/system/runtime_loader"),
+
// TODO go over each item in this list and either move it to the above list, or
// implement the standard dynamic linker path code for it.
.ananas,
@@ -1517,7 +1581,6 @@ pub const Target = struct {
.lv2,
.solaris,
.zos,
- .haiku,
.minix,
.rtems,
.nacl,
@@ -1558,6 +1621,6 @@ pub const Target = struct {
}
};
-test "" {
+test {
std.testing.refAllDecls(Target.Cpu.Arch);
}
diff --git a/lib/std/target/aarch64.zig b/lib/std/target/aarch64.zig
index abf7f09943..b64f04d7ad 100644
--- a/lib/std/target/aarch64.zig
+++ b/lib/std/target/aarch64.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/amdgpu.zig b/lib/std/target/amdgpu.zig
index 368bd80dc8..135446c7df 100644
--- a/lib/std/target/amdgpu.zig
+++ b/lib/std/target/amdgpu.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/arm.zig b/lib/std/target/arm.zig
index 7243fc3805..b717120de3 100644
--- a/lib/std/target/arm.zig
+++ b/lib/std/target/arm.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/avr.zig b/lib/std/target/avr.zig
index 4517fd5abe..9513c1861f 100644
--- a/lib/std/target/avr.zig
+++ b/lib/std/target/avr.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/bpf.zig b/lib/std/target/bpf.zig
index 5e23c233c8..73287ec6a8 100644
--- a/lib/std/target/bpf.zig
+++ b/lib/std/target/bpf.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/hexagon.zig b/lib/std/target/hexagon.zig
index 34bbf70bb4..b1f565f52d 100644
--- a/lib/std/target/hexagon.zig
+++ b/lib/std/target/hexagon.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/mips.zig b/lib/std/target/mips.zig
index ccc207ff0f..59da13ac39 100644
--- a/lib/std/target/mips.zig
+++ b/lib/std/target/mips.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/msp430.zig b/lib/std/target/msp430.zig
index 38ea358f90..c1005a1d81 100644
--- a/lib/std/target/msp430.zig
+++ b/lib/std/target/msp430.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/nvptx.zig b/lib/std/target/nvptx.zig
index 9a35edc7e9..b025fbfcf7 100644
--- a/lib/std/target/nvptx.zig
+++ b/lib/std/target/nvptx.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/powerpc.zig b/lib/std/target/powerpc.zig
index ba59ee5da9..a2040e70b9 100644
--- a/lib/std/target/powerpc.zig
+++ b/lib/std/target/powerpc.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -792,7 +792,7 @@ pub const cpu = struct {
};
pub const ppc32 = CpuModel{
.name = "ppc32",
- .llvm_name = "ppc32",
+ .llvm_name = "ppc",
.features = featureSet(&[_]Feature{
.hard_float,
}),
diff --git a/lib/std/target/riscv.zig b/lib/std/target/riscv.zig
index 3e26f0a094..a791f0105b 100644
--- a/lib/std/target/riscv.zig
+++ b/lib/std/target/riscv.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/sparc.zig b/lib/std/target/sparc.zig
index 4b6698e8de..a075160d59 100644
--- a/lib/std/target/sparc.zig
+++ b/lib/std/target/sparc.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/systemz.zig b/lib/std/target/systemz.zig
index 16b1471d55..8a78167e69 100644
--- a/lib/std/target/systemz.zig
+++ b/lib/std/target/systemz.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/wasm.zig b/lib/std/target/wasm.zig
index 6a2053c613..0a3281c692 100644
--- a/lib/std/target/wasm.zig
+++ b/lib/std/target/wasm.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/target/x86.zig b/lib/std/target/x86.zig
index d764bc2d21..1a52162969 100644
--- a/lib/std/target/x86.zig
+++ b/lib/std/target/x86.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/testing.zig b/lib/std/testing.zig
index 69df01190d..1d89155a58 100644
--- a/lib/std/testing.zig
+++ b/lib/std/testing.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -21,14 +21,18 @@ pub var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
/// TODO https://github.com/ziglang/zig/issues/5738
pub var log_level = std.log.Level.warn;
+/// This is available to any test that wants to execute Zig in a child process.
+/// It will be the same executable that is running `zig test`.
+pub var zig_exe_path: []const u8 = undefined;
+
/// This function is intended to be used only in tests. It prints diagnostics to stderr
/// and then aborts when actual_error_union is not expected_error.
pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void {
if (actual_error_union) |actual_payload| {
- std.debug.panic("expected error.{}, found {}", .{ @errorName(expected_error), actual_payload });
+ std.debug.panic("expected error.{s}, found {any}", .{ @errorName(expected_error), actual_payload });
} else |actual_error| {
if (expected_error != actual_error) {
- std.debug.panic("expected error.{}, found error.{}", .{
+ std.debug.panic("expected error.{s}, found error.{s}", .{
@errorName(expected_error),
@errorName(actual_error),
});
@@ -56,7 +60,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
.Type => {
if (actual != expected) {
- std.debug.panic("expected type {}, found type {}", .{ @typeName(expected), @typeName(actual) });
+ std.debug.panic("expected type {s}, found type {s}", .{ @typeName(expected), @typeName(actual) });
}
},
@@ -84,7 +88,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
},
.Slice => {
if (actual.ptr != expected.ptr) {
- std.debug.panic("expected slice ptr {}, found {}", .{ expected.ptr, actual.ptr });
+ std.debug.panic("expected slice ptr {*}, found {*}", .{ expected.ptr, actual.ptr });
}
if (actual.len != expected.len) {
std.debug.panic("expected slice len {}, found {}", .{ expected.len, actual.len });
@@ -115,10 +119,10 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
@compileError("Unable to compare untagged union values");
}
- const TagType = @TagType(@TypeOf(expected));
+ const Tag = std.meta.Tag(@TypeOf(expected));
- const expectedTag = @as(TagType, expected);
- const actualTag = @as(TagType, actual);
+ const expectedTag = @as(Tag, expected);
+ const actualTag = @as(Tag, actual);
expectEqual(expectedTag, actualTag);
@@ -141,11 +145,11 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
if (actual) |actual_payload| {
expectEqual(expected_payload, actual_payload);
} else {
- std.debug.panic("expected {}, found null", .{expected_payload});
+ std.debug.panic("expected {any}, found null", .{expected_payload});
}
} else {
if (actual) |actual_payload| {
- std.debug.panic("expected null, found {}", .{actual_payload});
+ std.debug.panic("expected null, found {any}", .{actual_payload});
}
}
},
@@ -155,11 +159,11 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
if (actual) |actual_payload| {
expectEqual(expected_payload, actual_payload);
} else |actual_err| {
- std.debug.panic("expected {}, found {}", .{ expected_payload, actual_err });
+ std.debug.panic("expected {any}, found {}", .{ expected_payload, actual_err });
}
} else |expected_err| {
if (actual) |actual_payload| {
- std.debug.panic("expected {}, found {}", .{ expected_err, actual_payload });
+ std.debug.panic("expected {}, found {any}", .{ expected_err, actual_payload });
} else |actual_err| {
expectEqual(expected_err, actual_err);
}
@@ -180,6 +184,22 @@ test "expectEqual.union(enum)" {
expectEqual(a10, a10);
}
+/// This function is intended to be used only in tests. When the formatted result of the template
+/// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how
+/// they are not equal, then returns an error.
+pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {
+ const result = try std.fmt.allocPrint(allocator, template, args);
+ defer allocator.free(result);
+ if (std.mem.eql(u8, result, expected)) return;
+
+ print("\n====== expected this output: =========\n", .{});
+ print("{s}", .{expected});
+ print("\n======== instead found this: =========\n", .{});
+ print("{s}", .{result});
+ print("\n======================================\n", .{});
+ return error.TestFailed;
+}
+
/// This function is intended to be used only in tests. When the actual value is not
/// within the margin of the expected value,
/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.
@@ -254,12 +274,12 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
// If the child type is u8 and no weird bytes, we could print it as strings
// Even for the length difference, it would be useful to see the values of the slices probably.
if (expected.len != actual.len) {
- std.debug.panic("slice lengths differ. expected {}, found {}", .{ expected.len, actual.len });
+ std.debug.panic("slice lengths differ. expected {d}, found {d}", .{ expected.len, actual.len });
}
var i: usize = 0;
while (i < expected.len) : (i += 1) {
if (!std.meta.eql(expected[i], actual[i])) {
- std.debug.panic("index {} incorrect. expected {}, found {}", .{ i, expected[i], actual[i] });
+ std.debug.panic("index {} incorrect. expected {any}, found {any}", .{ i, expected[i], actual[i] });
}
}
}
@@ -303,10 +323,9 @@ fn getCwdOrWasiPreopen() std.fs.Dir {
pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
- std.crypto.randomBytes(&random_bytes) catch
- @panic("unable to make tmp dir for testing: unable to get random bytes");
+ std.crypto.random.bytes(&random_bytes);
var sub_path: [TmpDir.sub_path_len]u8 = undefined;
- std.fs.base64_encoder.encode(&sub_path, &random_bytes);
+ _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
var cwd = getCwdOrWasiPreopen();
var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch
@@ -357,7 +376,7 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) void {
for (expected[0..diff_index]) |value| {
if (value == '\n') diff_line_number += 1;
}
- print("First difference occurs on line {}:\n", .{diff_line_number});
+ print("First difference occurs on line {d}:\n", .{diff_line_number});
print("expected:\n", .{});
printIndicatorLine(expected, diff_index);
@@ -413,18 +432,18 @@ fn printWithVisibleNewlines(source: []const u8) void {
while (std.mem.indexOf(u8, source[i..], "\n")) |nl| : (i += nl + 1) {
printLine(source[i .. i + nl]);
}
- print("{}␃\n", .{source[i..]}); // End of Text symbol (ETX)
+ print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX)
}
fn printLine(line: []const u8) void {
if (line.len != 0) switch (line[line.len - 1]) {
- ' ', '\t' => print("{}⏎\n", .{line}), // Carriage return symbol,
+ ' ', '\t' => print("{s}⏎\n", .{line}), // Carriage return symbol,
else => {},
};
- print("{}\n", .{line});
+ print("{s}\n", .{line});
}
-test "" {
+test {
expectEqualStrings("foo", "foo");
}
diff --git a/lib/std/testing/failing_allocator.zig b/lib/std/testing/failing_allocator.zig
index 61912e6933..570050762d 100644
--- a/lib/std/testing/failing_allocator.zig
+++ b/lib/std/testing/failing_allocator.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/thread.zig b/lib/std/thread.zig
deleted file mode 100644
index 83dfd7cb52..0000000000
--- a/lib/std/thread.zig
+++ /dev/null
@@ -1,496 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("std.zig");
-const builtin = std.builtin;
-const os = std.os;
-const mem = std.mem;
-const windows = std.os.windows;
-const c = std.c;
-const assert = std.debug.assert;
-
-const bad_startfn_ret = "expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'";
-
-pub const Thread = struct {
- data: Data,
-
- pub const use_pthreads = std.Target.current.os.tag != .windows and builtin.link_libc;
-
- /// Represents a kernel thread handle.
- /// May be an integer or a pointer depending on the platform.
- /// On Linux and POSIX, this is the same as Id.
- pub const Handle = if (use_pthreads)
- c.pthread_t
- else switch (std.Target.current.os.tag) {
- .linux => i32,
- .windows => windows.HANDLE,
- else => void,
- };
-
- /// Represents a unique ID per thread.
- /// May be an integer or pointer depending on the platform.
- /// On Linux and POSIX, this is the same as Handle.
- pub const Id = switch (std.Target.current.os.tag) {
- .windows => windows.DWORD,
- else => Handle,
- };
-
- pub const Data = if (use_pthreads)
- struct {
- handle: Thread.Handle,
- memory: []u8,
- }
- else switch (std.Target.current.os.tag) {
- .linux => struct {
- handle: Thread.Handle,
- memory: []align(mem.page_size) u8,
- },
- .windows => struct {
- handle: Thread.Handle,
- alloc_start: *c_void,
- heap_handle: windows.HANDLE,
- },
- else => struct {},
- };
-
- /// Returns the ID of the calling thread.
- /// Makes a syscall every time the function is called.
- /// On Linux and POSIX, this Id is the same as a Handle.
- pub fn getCurrentId() Id {
- if (use_pthreads) {
- return c.pthread_self();
- } else
- return switch (std.Target.current.os.tag) {
- .linux => os.linux.gettid(),
- .windows => windows.kernel32.GetCurrentThreadId(),
- else => @compileError("Unsupported OS"),
- };
- }
-
- /// Returns the handle of this thread.
- /// On Linux and POSIX, this is the same as Id.
- /// On Linux, it is possible that the thread spawned with `spawn`
- /// finishes executing entirely before the clone syscall completes. In this
- /// case, this function will return 0 rather than the no-longer-existing thread's
- /// pid.
- pub fn handle(self: Thread) Handle {
- return self.data.handle;
- }
-
- pub fn wait(self: *Thread) void {
- if (use_pthreads) {
- const err = c.pthread_join(self.data.handle, null);
- switch (err) {
- 0 => {},
- os.EINVAL => unreachable,
- os.ESRCH => unreachable,
- os.EDEADLK => unreachable,
- else => unreachable,
- }
- std.heap.c_allocator.free(self.data.memory);
- std.heap.c_allocator.destroy(self);
- } else switch (std.Target.current.os.tag) {
- .linux => {
- while (true) {
- const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst);
- if (pid_value == 0) break;
- const rc = os.linux.futex_wait(&self.data.handle, os.linux.FUTEX_WAIT, pid_value, null);
- switch (os.linux.getErrno(rc)) {
- 0 => continue,
- os.EINTR => continue,
- os.EAGAIN => continue,
- else => unreachable,
- }
- }
- os.munmap(self.data.memory);
- },
- .windows => {
- windows.WaitForSingleObjectEx(self.data.handle, windows.INFINITE, false) catch unreachable;
- windows.CloseHandle(self.data.handle);
- windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start);
- },
- else => @compileError("Unsupported OS"),
- }
- }
-
- pub const SpawnError = error{
- /// A system-imposed limit on the number of threads was encountered.
- /// There are a number of limits that may trigger this error:
- /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)),
- /// which limits the number of processes and threads for a real
- /// user ID, was reached;
- /// * the kernel's system-wide limit on the number of processes and
- /// threads, /proc/sys/kernel/threads-max, was reached (see
- /// proc(5));
- /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was
- /// reached (see proc(5)); or
- /// * the PID limit (pids.max) imposed by the cgroup "process num‐
- /// ber" (PIDs) controller was reached.
- ThreadQuotaExceeded,
-
- /// The kernel cannot allocate sufficient memory to allocate a task structure
- /// for the child, or to copy those parts of the caller's context that need to
- /// be copied.
- SystemResources,
-
- /// Not enough userland memory to spawn the thread.
- OutOfMemory,
-
- /// `mlockall` is enabled, and the memory needed to spawn the thread
- /// would exceed the limit.
- LockedMemoryLimitExceeded,
-
- Unexpected,
- };
-
- /// caller must call wait on the returned thread
- /// fn startFn(@TypeOf(context)) T
- /// where T is u8, noreturn, void, or !void
- /// caller must call wait on the returned thread
- pub fn spawn(context: anytype, comptime startFn: anytype) SpawnError!*Thread {
- if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
- // TODO compile-time call graph analysis to determine stack upper bound
- // https://github.com/ziglang/zig/issues/157
- const default_stack_size = 16 * 1024 * 1024;
-
- const Context = @TypeOf(context);
- comptime assert(@typeInfo(@TypeOf(startFn)).Fn.args[0].arg_type.? == Context);
-
- if (std.Target.current.os.tag == .windows) {
- const WinThread = struct {
- const OuterContext = struct {
- thread: Thread,
- inner: Context,
- };
- fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {
- const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
-
- switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
- .NoReturn => {
- startFn(arg);
- },
- .Void => {
- startFn(arg);
- return 0;
- },
- .Int => |info| {
- if (info.bits != 8) {
- @compileError(bad_startfn_ret);
- }
- return startFn(arg);
- },
- .ErrorUnion => |info| {
- if (info.payload != void) {
- @compileError(bad_startfn_ret);
- }
- startFn(arg) catch |err| {
- std.debug.warn("error: {}\n", .{@errorName(err)});
- if (@errorReturnTrace()) |trace| {
- std.debug.dumpStackTrace(trace.*);
- }
- };
- return 0;
- },
- else => @compileError(bad_startfn_ret),
- }
- }
- };
-
- const heap_handle = windows.kernel32.GetProcessHeap() orelse return error.OutOfMemory;
- const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
- const bytes_ptr = windows.kernel32.HeapAlloc(heap_handle, 0, byte_count) orelse return error.OutOfMemory;
- errdefer assert(windows.kernel32.HeapFree(heap_handle, 0, bytes_ptr) != 0);
- const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count];
- const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
- outer_context.* = WinThread.OuterContext{
- .thread = Thread{
- .data = Thread.Data{
- .heap_handle = heap_handle,
- .alloc_start = bytes_ptr,
- .handle = undefined,
- },
- },
- .inner = context,
- };
-
- const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);
- outer_context.thread.data.handle = windows.kernel32.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse {
- switch (windows.kernel32.GetLastError()) {
- else => |err| return windows.unexpectedError(err),
- }
- };
- return &outer_context.thread;
- }
-
- const MainFuncs = struct {
- fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 {
- const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
-
- switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
- .NoReturn => {
- startFn(arg);
- },
- .Void => {
- startFn(arg);
- return 0;
- },
- .Int => |info| {
- if (info.bits != 8) {
- @compileError(bad_startfn_ret);
- }
- return startFn(arg);
- },
- .ErrorUnion => |info| {
- if (info.payload != void) {
- @compileError(bad_startfn_ret);
- }
- startFn(arg) catch |err| {
- std.debug.warn("error: {}\n", .{@errorName(err)});
- if (@errorReturnTrace()) |trace| {
- std.debug.dumpStackTrace(trace.*);
- }
- };
- return 0;
- },
- else => @compileError(bad_startfn_ret),
- }
- }
- fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {
- const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*;
-
- switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
- .NoReturn => {
- startFn(arg);
- },
- .Void => {
- startFn(arg);
- return null;
- },
- .Int => |info| {
- if (info.bits != 8) {
- @compileError(bad_startfn_ret);
- }
- // pthreads don't support exit status, ignore value
- _ = startFn(arg);
- return null;
- },
- .ErrorUnion => |info| {
- if (info.payload != void) {
- @compileError(bad_startfn_ret);
- }
- startFn(arg) catch |err| {
- std.debug.warn("error: {}\n", .{@errorName(err)});
- if (@errorReturnTrace()) |trace| {
- std.debug.dumpStackTrace(trace.*);
- }
- };
- return null;
- },
- else => @compileError(bad_startfn_ret),
- }
- }
- };
-
- if (Thread.use_pthreads) {
- var attr: c.pthread_attr_t = undefined;
- if (c.pthread_attr_init(&attr) != 0) return error.SystemResources;
- defer assert(c.pthread_attr_destroy(&attr) == 0);
-
- const thread_obj = try std.heap.c_allocator.create(Thread);
- errdefer std.heap.c_allocator.destroy(thread_obj);
- if (@sizeOf(Context) > 0) {
- thread_obj.data.memory = try std.heap.c_allocator.allocAdvanced(
- u8,
- @alignOf(Context),
- @sizeOf(Context),
- .at_least,
- );
- errdefer std.heap.c_allocator.free(thread_obj.data.memory);
- mem.copy(u8, thread_obj.data.memory, mem.asBytes(&context));
- } else {
- thread_obj.data.memory = @as([*]u8, undefined)[0..0];
- }
-
- // Use the same set of parameters used by the libc-less impl.
- assert(c.pthread_attr_setstacksize(&attr, default_stack_size) == 0);
- assert(c.pthread_attr_setguardsize(&attr, mem.page_size) == 0);
-
- const err = c.pthread_create(
- &thread_obj.data.handle,
- &attr,
- MainFuncs.posixThreadMain,
- thread_obj.data.memory.ptr,
- );
- switch (err) {
- 0 => return thread_obj,
- os.EAGAIN => return error.SystemResources,
- os.EPERM => unreachable,
- os.EINVAL => unreachable,
- else => return os.unexpectedErrno(@intCast(usize, err)),
- }
-
- return thread_obj;
- }
-
- var guard_end_offset: usize = undefined;
- var stack_end_offset: usize = undefined;
- var thread_start_offset: usize = undefined;
- var context_start_offset: usize = undefined;
- var tls_start_offset: usize = undefined;
- const mmap_len = blk: {
- var l: usize = mem.page_size;
- // Allocate a guard page right after the end of the stack region
- guard_end_offset = l;
- // The stack itself, which grows downwards.
- l = mem.alignForward(l + default_stack_size, mem.page_size);
- stack_end_offset = l;
- // Above the stack, so that it can be in the same mmap call, put the Thread object.
- l = mem.alignForward(l, @alignOf(Thread));
- thread_start_offset = l;
- l += @sizeOf(Thread);
- // Next, the Context object.
- if (@sizeOf(Context) != 0) {
- l = mem.alignForward(l, @alignOf(Context));
- context_start_offset = l;
- l += @sizeOf(Context);
- }
- // Finally, the Thread Local Storage, if any.
- l = mem.alignForward(l, os.linux.tls.tls_image.alloc_align);
- tls_start_offset = l;
- l += os.linux.tls.tls_image.alloc_size;
- // Round the size to the page size.
- break :blk mem.alignForward(l, mem.page_size);
- };
-
- const mmap_slice = mem: {
- // Map the whole stack with no rw permissions to avoid
- // committing the whole region right away
- const mmap_slice = os.mmap(
- null,
- mmap_len,
- os.PROT_NONE,
- os.MAP_PRIVATE | os.MAP_ANONYMOUS,
- -1,
- 0,
- ) catch |err| switch (err) {
- error.MemoryMappingNotSupported => unreachable,
- error.AccessDenied => unreachable,
- error.PermissionDenied => unreachable,
- else => |e| return e,
- };
- errdefer os.munmap(mmap_slice);
-
- // Map everything but the guard page as rw
- os.mprotect(
- mmap_slice[guard_end_offset..],
- os.PROT_READ | os.PROT_WRITE,
- ) catch |err| switch (err) {
- error.AccessDenied => unreachable,
- else => |e| return e,
- };
-
- break :mem mmap_slice;
- };
-
- const mmap_addr = @ptrToInt(mmap_slice.ptr);
-
- const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
- thread_ptr.data.memory = mmap_slice;
-
- var arg: usize = undefined;
- if (@sizeOf(Context) != 0) {
- arg = mmap_addr + context_start_offset;
- const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
- context_ptr.* = context;
- }
-
- if (std.Target.current.os.tag == .linux) {
- const flags: u32 = os.CLONE_VM | os.CLONE_FS | os.CLONE_FILES |
- os.CLONE_SIGHAND | os.CLONE_THREAD | os.CLONE_SYSVSEM |
- os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID |
- os.CLONE_DETACHED | os.CLONE_SETTLS;
- // This structure is only needed when targeting i386
- var user_desc: if (std.Target.current.cpu.arch == .i386) os.linux.user_desc else void = undefined;
-
- const tls_area = mmap_slice[tls_start_offset..];
- const tp_value = os.linux.tls.prepareTLS(tls_area);
-
- const newtls = blk: {
- if (std.Target.current.cpu.arch == .i386) {
- user_desc = os.linux.user_desc{
- .entry_number = os.linux.tls.tls_image.gdt_entry_number,
- .base_addr = tp_value,
- .limit = 0xfffff,
- .seg_32bit = 1,
- .contents = 0, // Data
- .read_exec_only = 0,
- .limit_in_pages = 1,
- .seg_not_present = 0,
- .useable = 1,
- };
- break :blk @ptrToInt(&user_desc);
- } else {
- break :blk tp_value;
- }
- };
-
- const rc = os.linux.clone(
- MainFuncs.linuxThreadMain,
- mmap_addr + stack_end_offset,
- flags,
- arg,
- &thread_ptr.data.handle,
- newtls,
- &thread_ptr.data.handle,
- );
- switch (os.errno(rc)) {
- 0 => return thread_ptr,
- os.EAGAIN => return error.ThreadQuotaExceeded,
- os.EINVAL => unreachable,
- os.ENOMEM => return error.SystemResources,
- os.ENOSPC => unreachable,
- os.EPERM => unreachable,
- os.EUSERS => unreachable,
- else => |err| return os.unexpectedErrno(err),
- }
- } else {
- @compileError("Unsupported OS");
- }
- }
-
- pub const CpuCountError = error{
- PermissionDenied,
- SystemResources,
- Unexpected,
- };
-
- pub fn cpuCount() CpuCountError!usize {
- if (std.Target.current.os.tag == .linux) {
- const cpu_set = try os.sched_getaffinity(0);
- return @as(usize, os.CPU_COUNT(cpu_set)); // TODO should not need this usize cast
- }
- if (std.Target.current.os.tag == .windows) {
- return os.windows.peb().NumberOfProcessors;
- }
- if (std.Target.current.os.tag == .openbsd) {
- var count: c_int = undefined;
- var count_size: usize = @sizeOf(c_int);
- const mib = [_]c_int{ os.CTL_HW, os.HW_NCPUONLINE };
- os.sysctl(&mib, &count, &count_size, null, 0) catch |err| switch (err) {
- error.NameTooLong, error.UnknownName => unreachable,
- else => |e| return e,
- };
- return @intCast(usize, count);
- }
- var count: c_int = undefined;
- var count_len: usize = @sizeOf(c_int);
- const name = if (comptime std.Target.current.isDarwin()) "hw.logicalcpu" else "hw.ncpu";
- os.sysctlbynameZ(name, &count, &count_len, null, 0) catch |err| switch (err) {
- error.NameTooLong, error.UnknownName => unreachable,
- else => |e| return e,
- };
- return @intCast(usize, count);
- }
-};
diff --git a/lib/std/time.zig b/lib/std/time.zig
index 2f56e82f45..7435a67e3d 100644
--- a/lib/std/time.zig
+++ b/lib/std/time.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/time/epoch.zig b/lib/std/time/epoch.zig
index 3a42c85a6d..75bddc71c3 100644
--- a/lib/std/time/epoch.zig
+++ b/lib/std/time/epoch.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig
index c791e07f78..f9ad6e3eb5 100644
--- a/lib/std/unicode.zig
+++ b/lib/std/unicode.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/unicode/throughput_test.zig b/lib/std/unicode/throughput_test.zig
index 5474124fd2..8f9f9d9cb7 100644
--- a/lib/std/unicode/throughput_test.zig
+++ b/lib/std/unicode/throughput_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -45,7 +45,7 @@ fn benchmarkCodepointCount(buf: []const u8) !ResultCount {
}
pub fn main() !void {
- const stdout = std.io.getStdOut().outStream();
+ const stdout = std.io.getStdOut().writer();
const args = try std.process.argsAlloc(std.heap.page_allocator);
diff --git a/lib/std/valgrind.zig b/lib/std/valgrind.zig
index 5373a2d513..6930652fbc 100644
--- a/lib/std/valgrind.zig
+++ b/lib/std/valgrind.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -262,7 +262,7 @@ pub fn monitorCommand(command: [*]u8) bool {
pub const memcheck = @import("valgrind/memcheck.zig");
pub const callgrind = @import("valgrind/callgrind.zig");
-test "" {
+test {
_ = @import("valgrind/memcheck.zig");
_ = @import("valgrind/callgrind.zig");
}
diff --git a/lib/std/valgrind/callgrind.zig b/lib/std/valgrind/callgrind.zig
index 5e025c4ffe..3962ad3e3a 100644
--- a/lib/std/valgrind/callgrind.zig
+++ b/lib/std/valgrind/callgrind.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/valgrind/memcheck.zig b/lib/std/valgrind/memcheck.zig
index 449b4c4a46..3226beec53 100644
--- a/lib/std/valgrind/memcheck.zig
+++ b/lib/std/valgrind/memcheck.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/wasm.zig b/lib/std/wasm.zig
new file mode 100644
index 0000000000..a04378d283
--- /dev/null
+++ b/lib/std/wasm.zig
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const testing = @import("std.zig").testing;
+
+/// Wasm instruction opcodes
+///
+/// All instructions are defined as per spec:
+/// https://webassembly.github.io/spec/core/appendix/index-instructions.html
+pub const Opcode = enum(u8) {
+ @"unreachable" = 0x00,
+ nop = 0x01,
+ block = 0x02,
+ loop = 0x03,
+ @"if" = 0x04,
+ @"else" = 0x05,
+ end = 0x0B,
+ br = 0x0C,
+ br_if = 0x0D,
+ br_table = 0x0E,
+ @"return" = 0x0F,
+ call = 0x10,
+ call_indirect = 0x11,
+ drop = 0x1A,
+ select = 0x1B,
+ local_get = 0x20,
+ local_set = 0x21,
+ local_tee = 0x22,
+ global_get = 0x23,
+ global_set = 0x24,
+ i32_load = 0x28,
+ i64_load = 0x29,
+ f32_load = 0x2A,
+ f64_load = 0x2B,
+ i32_load8_s = 0x2C,
+ i32_load8_u = 0x2D,
+ i32_load16_s = 0x2E,
+ i32_load16_u = 0x2F,
+ i64_load8_s = 0x30,
+ i64_load8_u = 0x31,
+ i64_load16_s = 0x32,
+ i64_load16_u = 0x33,
+ i64_load32_s = 0x34,
+ i64_load32_u = 0x35,
+ i32_store = 0x36,
+ i64_store = 0x37,
+ f32_store = 0x38,
+ f64_store = 0x39,
+ i32_store8 = 0x3A,
+ i32_store16 = 0x3B,
+ i64_store8 = 0x3C,
+ i64_store16 = 0x3D,
+ i64_store32 = 0x3E,
+ memory_size = 0x3F,
+ memory_grow = 0x40,
+ i32_const = 0x41,
+ i64_const = 0x42,
+ f32_const = 0x43,
+ f64_const = 0x44,
+ i32_eqz = 0x45,
+ i32_eq = 0x46,
+ i32_ne = 0x47,
+ i32_lt_s = 0x48,
+ i32_lt_u = 0x49,
+ i32_gt_s = 0x4A,
+ i32_gt_u = 0x4B,
+ i32_le_s = 0x4C,
+ i32_le_u = 0x4D,
+ i32_ge_s = 0x4E,
+ i32_ge_u = 0x4F,
+ i64_eqz = 0x50,
+ i64_eq = 0x51,
+ i64_ne = 0x52,
+ i64_lt_s = 0x53,
+ i64_lt_u = 0x54,
+ i64_gt_s = 0x55,
+ i64_gt_u = 0x56,
+ i64_le_s = 0x57,
+ i64_le_u = 0x58,
+ i64_ge_s = 0x59,
+ i64_ge_u = 0x5A,
+ f32_eq = 0x5B,
+ f32_ne = 0x5C,
+ f32_lt = 0x5D,
+ f32_gt = 0x5E,
+ f32_le = 0x5F,
+ f32_ge = 0x60,
+ f64_eq = 0x61,
+ f64_ne = 0x62,
+ f64_lt = 0x63,
+ f64_gt = 0x64,
+ f64_le = 0x65,
+ f64_ge = 0x66,
+ i32_clz = 0x67,
+ i32_ctz = 0x68,
+ i32_popcnt = 0x69,
+ i32_add = 0x6A,
+ i32_sub = 0x6B,
+ i32_mul = 0x6C,
+ i32_div_s = 0x6D,
+ i32_div_u = 0x6E,
+ i32_rem_s = 0x6F,
+ i32_rem_u = 0x70,
+ i32_and = 0x71,
+ i32_or = 0x72,
+ i32_xor = 0x73,
+ i32_shl = 0x74,
+ i32_shr_s = 0x75,
+ i32_shr_u = 0x76,
+ i32_rotl = 0x77,
+ i32_rotr = 0x78,
+ i64_clz = 0x79,
+ i64_ctz = 0x7A,
+ i64_popcnt = 0x7B,
+ i64_add = 0x7C,
+ i64_sub = 0x7D,
+ i64_mul = 0x7E,
+ i64_div_s = 0x7F,
+ i64_div_u = 0x80,
+ i64_rem_s = 0x81,
+ i64_rem_u = 0x82,
+ i64_and = 0x83,
+ i64_or = 0x84,
+ i64_xor = 0x85,
+ i64_shl = 0x86,
+ i64_shr_s = 0x87,
+ i64_shr_u = 0x88,
+ i64_rotl = 0x89,
+ i64_rotr = 0x8A,
+ f32_abs = 0x8B,
+ f32_neg = 0x8C,
+ f32_ceil = 0x8D,
+ f32_floor = 0x8E,
+ f32_trunc = 0x8F,
+ f32_nearest = 0x90,
+ f32_sqrt = 0x91,
+ f32_add = 0x92,
+ f32_sub = 0x93,
+ f32_mul = 0x94,
+ f32_div = 0x95,
+ f32_min = 0x96,
+ f32_max = 0x97,
+ f32_copysign = 0x98,
+ f64_abs = 0x99,
+ f64_neg = 0x9A,
+ f64_ceil = 0x9B,
+ f64_floor = 0x9C,
+ f64_trunc = 0x9D,
+ f64_nearest = 0x9E,
+ f64_sqrt = 0x9F,
+ f64_add = 0xA0,
+ f64_sub = 0xA1,
+ f64_mul = 0xA2,
+ f64_div = 0xA3,
+ f64_min = 0xA4,
+ f64_max = 0xA5,
+ f64_copysign = 0xA6,
+ i32_wrap_i64 = 0xA7,
+ i32_trunc_f32_s = 0xA8,
+ i32_trunc_f32_u = 0xA9,
+ i32_trunc_f64_s = 0xB0,
+ i32_trunc_f64_u = 0xB1,
+ f32_convert_i32_s = 0xB2,
+ f32_convert_i32_u = 0xB3,
+ f32_convert_i64_s = 0xB4,
+ f32_convert_i64_u = 0xB5,
+ f32_demote_f64 = 0xB6,
+ f64_convert_i32_s = 0xB7,
+ f64_convert_i32_u = 0xB8,
+ f64_convert_i64_s = 0xB9,
+ f64_convert_i64_u = 0xBA,
+ f64_promote_f32 = 0xBB,
+ i32_reinterpret_f32 = 0xBC,
+ i64_reinterpret_f64 = 0xBD,
+ f32_reinterpret_i32 = 0xBE,
+ i64_reinterpret_i64 = 0xBF,
+ i32_extend8_s = 0xC0,
+ i32_extend16_s = 0xC1,
+ i64_extend8_s = 0xC2,
+ i64_extend16_s = 0xC3,
+ i64_extend32_s = 0xC4,
+ _,
+};
+
+/// Returns the integer value of an `Opcode`. Used by the Zig compiler
+/// to write instructions to the wasm binary file
+pub fn opcode(op: Opcode) u8 {
+ return @enumToInt(op);
+}
+
+test "Wasm - opcodes" {
+ // Ensure our opcodes values remain intact as certain values are skipped due to them being reserved
+ const i32_const = opcode(.i32_const);
+ const end = opcode(.end);
+ const drop = opcode(.drop);
+ const local_get = opcode(.local_get);
+ const i64_extend32_s = opcode(.i64_extend32_s);
+
+ testing.expectEqual(@as(u16, 0x41), i32_const);
+ testing.expectEqual(@as(u16, 0x0B), end);
+ testing.expectEqual(@as(u16, 0x1A), drop);
+ testing.expectEqual(@as(u16, 0x20), local_get);
+ testing.expectEqual(@as(u16, 0xC4), i64_extend32_s);
+}
+
+/// Enum representing all Wasm value types as per spec:
+/// https://webassembly.github.io/spec/core/binary/types.html
+pub const Valtype = enum(u8) {
+ i32 = 0x7F,
+ i64 = 0x7E,
+ f32 = 0x7D,
+ f64 = 0x7C,
+};
+
+/// Returns the integer value of a `Valtype`
+pub fn valtype(value: Valtype) u8 {
+ return @enumToInt(value);
+}
+
+test "Wasm - valtypes" {
+ const _i32 = valtype(.i32);
+ const _i64 = valtype(.i64);
+ const _f32 = valtype(.f32);
+ const _f64 = valtype(.f64);
+
+ testing.expectEqual(@as(u8, 0x7F), _i32);
+ testing.expectEqual(@as(u8, 0x7E), _i64);
+ testing.expectEqual(@as(u8, 0x7D), _f32);
+ testing.expectEqual(@as(u8, 0x7C), _f64);
+}
+
+/// Wasm module sections as per spec:
+/// https://webassembly.github.io/spec/core/binary/modules.html
+pub const Section = enum(u8) {
+ custom,
+ type,
+ import,
+ function,
+ table,
+ memory,
+ global,
+ @"export",
+ start,
+ element,
+ code,
+ data,
+};
+
+/// Returns the integer value of a given `Section`
+pub fn section(val: Section) u8 {
+ return @enumToInt(val);
+}
+
+/// The kind of the type when importing or exporting to/from the host environment
+/// https://webassembly.github.io/spec/core/syntax/modules.html
+pub const ExternalKind = enum(u8) {
+ function,
+ table,
+ memory,
+ global,
+};
+
+/// Returns the integer value of a given `ExternalKind`
+pub fn externalKind(val: ExternalKind) u8 {
+ return @enumToInt(val);
+}
+
+// types
+pub const element_type: u8 = 0x70;
+pub const function_type: u8 = 0x60;
+pub const result_type: u8 = 0x40;
+
+/// Represents a block which will not return a value
+pub const block_empty: u8 = 0x40;
+
+// binary constants
+pub const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm
+pub const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1
diff --git a/lib/std/zig.zig b/lib/std/zig.zig
index 06a74a9786..197d7c2c59 100644
--- a/lib/std/zig.zig
+++ b/lib/std/zig.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -8,9 +8,10 @@ const tokenizer = @import("zig/tokenizer.zig");
pub const Token = tokenizer.Token;
pub const Tokenizer = tokenizer.Tokenizer;
+pub const fmtId = @import("zig/fmt.zig").fmtId;
+pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes;
pub const parse = @import("zig/parse.zig").parse;
pub const parseStringLiteral = @import("zig/string_literal.zig").parse;
-pub const render = @import("zig/render.zig").render;
pub const ast = @import("zig/ast.zig");
pub const system = @import("zig/system.zig");
pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
@@ -139,6 +140,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
.Lib => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
},
.c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),
+ .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
.hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
.raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
}
@@ -253,6 +255,6 @@ test "parseCharLiteral" {
std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFF}x'", &bad_index));
}
-test "" {
+test {
@import("std").testing.refAllDecls(@This());
}
diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig
index 3e2f08cd06..46b58e9465 100644
--- a/lib/std/zig/ast.zig
+++ b/lib/std/zig/ast.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -9,71 +9,78 @@ const testing = std.testing;
const mem = std.mem;
const Token = std.zig.Token;
-pub const TokenIndex = usize;
-pub const NodeIndex = usize;
+pub const TokenIndex = u32;
+pub const ByteOffset = u32;
+
+pub const TokenList = std.MultiArrayList(struct {
+ tag: Token.Tag,
+ start: ByteOffset,
+});
+pub const NodeList = std.MultiArrayList(Node);
pub const Tree = struct {
/// Reference to externally-owned data.
source: []const u8,
- token_ids: []const Token.Id,
- token_locs: []const Token.Loc,
- errors: []const Error,
- root_node: *Node.Root,
- arena: std.heap.ArenaAllocator.State,
- gpa: *mem.Allocator,
+ tokens: TokenList.Slice,
+ /// The root AST node is assumed to be index 0. Since there can be no
+ /// references to the root node, this means 0 is available to indicate null.
+ nodes: NodeList.Slice,
+ extra_data: []Node.Index,
- /// translate-c uses this to avoid having to emit correct newlines
- /// TODO get rid of this hack
- generated: bool = false,
+ errors: []const Error,
- pub fn deinit(self: *Tree) void {
- self.gpa.free(self.token_ids);
- self.gpa.free(self.token_locs);
- self.gpa.free(self.errors);
- self.arena.promote(self.gpa).deinit();
- }
+ pub const Location = struct {
+ line: usize,
+ column: usize,
+ line_start: usize,
+ line_end: usize,
+ };
- pub fn renderError(self: *Tree, parse_error: *const Error, stream: anytype) !void {
- return parse_error.render(self.token_ids, stream);
+ pub fn deinit(tree: *Tree, gpa: *mem.Allocator) void {
+ tree.tokens.deinit(gpa);
+ tree.nodes.deinit(gpa);
+ gpa.free(tree.extra_data);
+ gpa.free(tree.errors);
+ tree.* = undefined;
}
- pub fn tokenSlice(self: *Tree, token_index: TokenIndex) []const u8 {
- return self.tokenSliceLoc(self.token_locs[token_index]);
- }
+ pub const RenderError = error{
+ /// Ran out of memory allocating call stack frames to complete rendering, or
+ /// ran out of memory allocating space in the output buffer.
+ OutOfMemory,
+ };
- pub fn tokenSliceLoc(self: *Tree, token: Token.Loc) []const u8 {
- return self.source[token.start..token.end];
- }
+ /// `gpa` is used for allocating the resulting formatted source code, as well as
+ /// for allocating extra stack memory if needed, because this function utilizes recursion.
+ /// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006.
+ /// Caller owns the returned slice of bytes, allocated with `gpa`.
+ pub fn render(tree: Tree, gpa: *mem.Allocator) RenderError![]u8 {
+ var buffer = std.ArrayList(u8).init(gpa);
+ defer buffer.deinit();
- pub fn getNodeSource(self: *const Tree, node: *const Node) []const u8 {
- const first_token = self.token_locs[node.firstToken()];
- const last_token = self.token_locs[node.lastToken()];
- return self.source[first_token.start..last_token.end];
+ try tree.renderToArrayList(&buffer);
+ return buffer.toOwnedSlice();
}
- pub const Location = struct {
- line: usize,
- column: usize,
- line_start: usize,
- line_end: usize,
- };
+ pub fn renderToArrayList(tree: Tree, buffer: *std.ArrayList(u8)) RenderError!void {
+ return @import("./render.zig").renderTree(buffer, tree);
+ }
- /// Return the Location of the token relative to the offset specified by `start_index`.
- pub fn tokenLocationLoc(self: *Tree, start_index: usize, token: Token.Loc) Location {
+ pub fn tokenLocation(self: Tree, start_offset: ByteOffset, token_index: TokenIndex) Location {
var loc = Location{
.line = 0,
.column = 0,
- .line_start = start_index,
+ .line_start = start_offset,
.line_end = self.source.len,
};
- if (self.generated)
- return loc;
- const token_start = token.start;
- for (self.source[start_index..]) |c, i| {
- if (i + start_index == token_start) {
- loc.line_end = i + start_index;
- while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') : (loc.line_end += 1) {}
+ const token_start = self.tokens.items(.start)[token_index];
+ for (self.source[start_offset..]) |c, i| {
+ if (i + start_offset == token_start) {
+ loc.line_end = i + start_offset;
+ while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') {
+ loc.line_end += 1;
+ }
return loc;
}
if (c == '\n') {
@@ -87,3196 +94,2878 @@ pub const Tree = struct {
return loc;
}
- pub fn tokenLocation(self: *Tree, start_index: usize, token_index: TokenIndex) Location {
- return self.tokenLocationLoc(start_index, self.token_locs[token_index]);
- }
-
- pub fn tokensOnSameLine(self: *Tree, token1_index: TokenIndex, token2_index: TokenIndex) bool {
- return self.tokensOnSameLineLoc(self.token_locs[token1_index], self.token_locs[token2_index]);
- }
-
- pub fn tokensOnSameLineLoc(self: *Tree, token1: Token.Loc, token2: Token.Loc) bool {
- return mem.indexOfScalar(u8, self.source[token1.end..token2.start], '\n') == null;
- }
-
- pub fn dump(self: *Tree) void {
- self.root_node.base.dump(0);
- }
+ pub fn tokenSlice(tree: Tree, token_index: TokenIndex) []const u8 {
+ const token_starts = tree.tokens.items(.start);
+ const token_tags = tree.tokens.items(.tag);
+ const token_tag = token_tags[token_index];
- /// Skips over comments
- pub fn prevToken(self: *Tree, token_index: TokenIndex) TokenIndex {
- var index = token_index - 1;
- while (self.token_ids[index] == Token.Id.LineComment) {
- index -= 1;
+ // Many tokens can be determined entirely by their tag.
+ if (token_tag.lexeme()) |lexeme| {
+ return lexeme;
}
- return index;
- }
- /// Skips over comments
- pub fn nextToken(self: *Tree, token_index: TokenIndex) TokenIndex {
- var index = token_index + 1;
- while (self.token_ids[index] == Token.Id.LineComment) {
- index += 1;
- }
- return index;
+ // For some tokens, re-tokenization is needed to find the end.
+ var tokenizer: std.zig.Tokenizer = .{
+ .buffer = tree.source,
+ .index = token_starts[token_index],
+ .pending_invalid_token = null,
+ };
+ const token = tokenizer.next();
+ assert(token.tag == token_tag);
+ return tree.source[token.loc.start..token.loc.end];
}
-};
-pub const Error = union(enum) {
- InvalidToken: InvalidToken,
- ExpectedContainerMembers: ExpectedContainerMembers,
- ExpectedStringLiteral: ExpectedStringLiteral,
- ExpectedIntegerLiteral: ExpectedIntegerLiteral,
- ExpectedPubItem: ExpectedPubItem,
- ExpectedIdentifier: ExpectedIdentifier,
- ExpectedStatement: ExpectedStatement,
- ExpectedVarDeclOrFn: ExpectedVarDeclOrFn,
- ExpectedVarDecl: ExpectedVarDecl,
- ExpectedFn: ExpectedFn,
- ExpectedReturnType: ExpectedReturnType,
- ExpectedAggregateKw: ExpectedAggregateKw,
- UnattachedDocComment: UnattachedDocComment,
- ExpectedEqOrSemi: ExpectedEqOrSemi,
- ExpectedSemiOrLBrace: ExpectedSemiOrLBrace,
- ExpectedSemiOrElse: ExpectedSemiOrElse,
- ExpectedLabelOrLBrace: ExpectedLabelOrLBrace,
- ExpectedLBrace: ExpectedLBrace,
- ExpectedColonOrRParen: ExpectedColonOrRParen,
- ExpectedLabelable: ExpectedLabelable,
- ExpectedInlinable: ExpectedInlinable,
- ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType,
- ExpectedCall: ExpectedCall,
- ExpectedCallOrFnProto: ExpectedCallOrFnProto,
- ExpectedSliceOrRBracket: ExpectedSliceOrRBracket,
- ExtraAlignQualifier: ExtraAlignQualifier,
- ExtraConstQualifier: ExtraConstQualifier,
- ExtraVolatileQualifier: ExtraVolatileQualifier,
- ExtraAllowZeroQualifier: ExtraAllowZeroQualifier,
- ExpectedTypeExpr: ExpectedTypeExpr,
- ExpectedPrimaryTypeExpr: ExpectedPrimaryTypeExpr,
- ExpectedParamType: ExpectedParamType,
- ExpectedExpr: ExpectedExpr,
- ExpectedPrimaryExpr: ExpectedPrimaryExpr,
- ExpectedToken: ExpectedToken,
- ExpectedCommaOrEnd: ExpectedCommaOrEnd,
- ExpectedParamList: ExpectedParamList,
- ExpectedPayload: ExpectedPayload,
- ExpectedBlockOrAssignment: ExpectedBlockOrAssignment,
- ExpectedBlockOrExpression: ExpectedBlockOrExpression,
- ExpectedExprOrAssignment: ExpectedExprOrAssignment,
- ExpectedPrefixExpr: ExpectedPrefixExpr,
- ExpectedLoopExpr: ExpectedLoopExpr,
- ExpectedDerefOrUnwrap: ExpectedDerefOrUnwrap,
- ExpectedSuffixOp: ExpectedSuffixOp,
- ExpectedBlockOrField: ExpectedBlockOrField,
- DeclBetweenFields: DeclBetweenFields,
- InvalidAnd: InvalidAnd,
- AsteriskAfterPointerDereference: AsteriskAfterPointerDereference,
-
- pub fn render(self: *const Error, tokens: []const Token.Id, stream: anytype) !void {
- switch (self.*) {
- .InvalidToken => |*x| return x.render(tokens, stream),
- .ExpectedContainerMembers => |*x| return x.render(tokens, stream),
- .ExpectedStringLiteral => |*x| return x.render(tokens, stream),
- .ExpectedIntegerLiteral => |*x| return x.render(tokens, stream),
- .ExpectedPubItem => |*x| return x.render(tokens, stream),
- .ExpectedIdentifier => |*x| return x.render(tokens, stream),
- .ExpectedStatement => |*x| return x.render(tokens, stream),
- .ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream),
- .ExpectedVarDecl => |*x| return x.render(tokens, stream),
- .ExpectedFn => |*x| return x.render(tokens, stream),
- .ExpectedReturnType => |*x| return x.render(tokens, stream),
- .ExpectedAggregateKw => |*x| return x.render(tokens, stream),
- .UnattachedDocComment => |*x| return x.render(tokens, stream),
- .ExpectedEqOrSemi => |*x| return x.render(tokens, stream),
- .ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream),
- .ExpectedSemiOrElse => |*x| return x.render(tokens, stream),
- .ExpectedLabelOrLBrace => |*x| return x.render(tokens, stream),
- .ExpectedLBrace => |*x| return x.render(tokens, stream),
- .ExpectedColonOrRParen => |*x| return x.render(tokens, stream),
- .ExpectedLabelable => |*x| return x.render(tokens, stream),
- .ExpectedInlinable => |*x| return x.render(tokens, stream),
- .ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream),
- .ExpectedCall => |*x| return x.render(tokens, stream),
- .ExpectedCallOrFnProto => |*x| return x.render(tokens, stream),
- .ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream),
- .ExtraAlignQualifier => |*x| return x.render(tokens, stream),
- .ExtraConstQualifier => |*x| return x.render(tokens, stream),
- .ExtraVolatileQualifier => |*x| return x.render(tokens, stream),
- .ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream),
- .ExpectedTypeExpr => |*x| return x.render(tokens, stream),
- .ExpectedPrimaryTypeExpr => |*x| return x.render(tokens, stream),
- .ExpectedParamType => |*x| return x.render(tokens, stream),
- .ExpectedExpr => |*x| return x.render(tokens, stream),
- .ExpectedPrimaryExpr => |*x| return x.render(tokens, stream),
- .ExpectedToken => |*x| return x.render(tokens, stream),
- .ExpectedCommaOrEnd => |*x| return x.render(tokens, stream),
- .ExpectedParamList => |*x| return x.render(tokens, stream),
- .ExpectedPayload => |*x| return x.render(tokens, stream),
- .ExpectedBlockOrAssignment => |*x| return x.render(tokens, stream),
- .ExpectedBlockOrExpression => |*x| return x.render(tokens, stream),
- .ExpectedExprOrAssignment => |*x| return x.render(tokens, stream),
- .ExpectedPrefixExpr => |*x| return x.render(tokens, stream),
- .ExpectedLoopExpr => |*x| return x.render(tokens, stream),
- .ExpectedDerefOrUnwrap => |*x| return x.render(tokens, stream),
- .ExpectedSuffixOp => |*x| return x.render(tokens, stream),
- .ExpectedBlockOrField => |*x| return x.render(tokens, stream),
- .DeclBetweenFields => |*x| return x.render(tokens, stream),
- .InvalidAnd => |*x| return x.render(tokens, stream),
- .AsteriskAfterPointerDereference => |*x| return x.render(tokens, stream),
+ pub fn extraData(tree: Tree, index: usize, comptime T: type) T {
+ const fields = std.meta.fields(T);
+ var result: T = undefined;
+ inline for (fields) |field, i| {
+ comptime assert(field.field_type == Node.Index);
+ @field(result, field.name) = tree.extra_data[index + i];
}
+ return result;
}
- pub fn loc(self: *const Error) TokenIndex {
- switch (self.*) {
- .InvalidToken => |x| return x.token,
- .ExpectedContainerMembers => |x| return x.token,
- .ExpectedStringLiteral => |x| return x.token,
- .ExpectedIntegerLiteral => |x| return x.token,
- .ExpectedPubItem => |x| return x.token,
- .ExpectedIdentifier => |x| return x.token,
- .ExpectedStatement => |x| return x.token,
- .ExpectedVarDeclOrFn => |x| return x.token,
- .ExpectedVarDecl => |x| return x.token,
- .ExpectedFn => |x| return x.token,
- .ExpectedReturnType => |x| return x.token,
- .ExpectedAggregateKw => |x| return x.token,
- .UnattachedDocComment => |x| return x.token,
- .ExpectedEqOrSemi => |x| return x.token,
- .ExpectedSemiOrLBrace => |x| return x.token,
- .ExpectedSemiOrElse => |x| return x.token,
- .ExpectedLabelOrLBrace => |x| return x.token,
- .ExpectedLBrace => |x| return x.token,
- .ExpectedColonOrRParen => |x| return x.token,
- .ExpectedLabelable => |x| return x.token,
- .ExpectedInlinable => |x| return x.token,
- .ExpectedAsmOutputReturnOrType => |x| return x.token,
- .ExpectedCall => |x| return x.node.firstToken(),
- .ExpectedCallOrFnProto => |x| return x.node.firstToken(),
- .ExpectedSliceOrRBracket => |x| return x.token,
- .ExtraAlignQualifier => |x| return x.token,
- .ExtraConstQualifier => |x| return x.token,
- .ExtraVolatileQualifier => |x| return x.token,
- .ExtraAllowZeroQualifier => |x| return x.token,
- .ExpectedTypeExpr => |x| return x.token,
- .ExpectedPrimaryTypeExpr => |x| return x.token,
- .ExpectedParamType => |x| return x.token,
- .ExpectedExpr => |x| return x.token,
- .ExpectedPrimaryExpr => |x| return x.token,
- .ExpectedToken => |x| return x.token,
- .ExpectedCommaOrEnd => |x| return x.token,
- .ExpectedParamList => |x| return x.token,
- .ExpectedPayload => |x| return x.token,
- .ExpectedBlockOrAssignment => |x| return x.token,
- .ExpectedBlockOrExpression => |x| return x.token,
- .ExpectedExprOrAssignment => |x| return x.token,
- .ExpectedPrefixExpr => |x| return x.token,
- .ExpectedLoopExpr => |x| return x.token,
- .ExpectedDerefOrUnwrap => |x| return x.token,
- .ExpectedSuffixOp => |x| return x.token,
- .ExpectedBlockOrField => |x| return x.token,
- .DeclBetweenFields => |x| return x.token,
- .InvalidAnd => |x| return x.token,
- .AsteriskAfterPointerDereference => |x| return x.token,
- }
+ pub fn rootDecls(tree: Tree) []const Node.Index {
+ // Root is always index 0.
+ const nodes_data = tree.nodes.items(.data);
+ return tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs];
}
- pub const InvalidToken = SingleTokenError("Invalid token '{}'");
- pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{}'");
- pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{}'");
- pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{}'");
- pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{}'");
- pub const ExpectedStatement = SingleTokenError("Expected statement, found '{}'");
- pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{}'");
- pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{}'");
- pub const ExpectedFn = SingleTokenError("Expected function, found '{}'");
- pub const ExpectedReturnType = SingleTokenError("Expected 'var' or return type expression, found '{}'");
- pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Id.Keyword_struct.symbol() ++ "', '" ++ Token.Id.Keyword_union.symbol() ++ "', '" ++ Token.Id.Keyword_enum.symbol() ++ "', or '" ++ Token.Id.Keyword_opaque.symbol() ++ "', found '{}'");
- pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{}'");
- pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{}'");
- pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{}'");
- pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{}'");
- pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{}'");
- pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{}'");
- pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{}'");
- pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{}'");
- pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Id.Identifier.symbol() ++ "', found '{}'");
- pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{}'");
- pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{}'");
- pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{}'");
- pub const ExpectedExpr = SingleTokenError("Expected expression, found '{}'");
- pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{}'");
- pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{}'");
- pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{}'");
- pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{}'");
- pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{}'");
- pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{}'");
- pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{}'");
- pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{}'");
- pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{}'");
- pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{}'");
- pub const ExpectedBlockOrField = SingleTokenError("Expected block or field, found '{}'");
-
- pub const ExpectedParamType = SimpleError("Expected parameter type");
- pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub");
- pub const UnattachedDocComment = SimpleError("Unattached documentation comment");
- pub const ExtraAlignQualifier = SimpleError("Extra align qualifier");
- pub const ExtraConstQualifier = SimpleError("Extra const qualifier");
- pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier");
- pub const ExtraAllowZeroQualifier = SimpleError("Extra allowzero qualifier");
- pub const DeclBetweenFields = SimpleError("Declarations are not allowed between container fields");
- pub const InvalidAnd = SimpleError("`&&` is invalid. Note that `and` is boolean AND.");
- pub const AsteriskAfterPointerDereference = SimpleError("`.*` can't be followed by `*`. Are you missing a space?");
-
- pub const ExpectedCall = struct {
- node: *Node,
-
- pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: anytype) !void {
- return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {}", .{
- @tagName(self.node.tag),
- });
- }
- };
-
- pub const ExpectedCallOrFnProto = struct {
- node: *Node,
-
- pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: anytype) !void {
- return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ " or " ++
- @tagName(Node.Tag.FnProto) ++ ", found {}", .{@tagName(self.node.tag)});
- }
- };
-
- pub const ExpectedToken = struct {
- token: TokenIndex,
- expected_id: Token.Id,
-
- pub fn render(self: *const ExpectedToken, tokens: []const Token.Id, stream: anytype) !void {
- const found_token = tokens[self.token];
- switch (found_token) {
- .Invalid => {
- return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()});
- },
- else => {
- const token_name = found_token.symbol();
- return stream.print("expected '{}', found '{}'", .{ self.expected_id.symbol(), token_name });
- },
- }
- }
- };
-
- pub const ExpectedCommaOrEnd = struct {
- token: TokenIndex,
- end_id: Token.Id,
-
- pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Id, stream: anytype) !void {
- const actual_token = tokens[self.token];
- return stream.print("expected ',' or '{}', found '{}'", .{
- self.end_id.symbol(),
- actual_token.symbol(),
- });
+ pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {
+ const token_tags = tree.tokens.items(.tag);
+ switch (parse_error.tag) {
+ .asterisk_after_ptr_deref => {
+ return stream.writeAll("'.*' cannot be followed by '*'. Are you missing a space?");
+ },
+ .decl_between_fields => {
+ return stream.writeAll("declarations are not allowed between container fields");
+ },
+ .expected_block => {
+ return stream.print("expected block or field, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_block_or_assignment => {
+ return stream.print("expected block or assignment, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_block_or_expr => {
+ return stream.print("expected block or expression, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_block_or_field => {
+ return stream.print("expected block or field, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_container_members => {
+ return stream.print("expected test, comptime, var decl, or container field, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_expr => {
+ return stream.print("expected expression, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_expr_or_assignment => {
+ return stream.print("expected expression or assignment, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_fn => {
+ return stream.print("expected function, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_inlinable => {
+ return stream.print("expected 'while' or 'for', found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_labelable => {
+ return stream.print("expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_param_list => {
+ return stream.print("expected parameter list, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_prefix_expr => {
+ return stream.print("expected prefix expression, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_primary_type_expr => {
+ return stream.print("expected primary type expression, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_pub_item => {
+ return stream.writeAll("expected function or variable declaration after pub");
+ },
+ .expected_return_type => {
+ return stream.print("expected return type expression, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_semi_or_else => {
+ return stream.print("expected ';' or 'else', found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_semi_or_lbrace => {
+ return stream.print("expected ';' or '{{', found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_statement => {
+ return stream.print("expected statement, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_string_literal => {
+ return stream.print("expected string literal, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_suffix_op => {
+ return stream.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_type_expr => {
+ return stream.print("expected type expression, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_var_decl => {
+ return stream.print("expected variable declaration, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_var_decl_or_fn => {
+ return stream.print("expected variable declaration or function, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_loop_payload => {
+ return stream.print("expected loop payload, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .expected_container => {
+ return stream.print("expected a struct, enum or union, found '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .extra_align_qualifier => {
+ return stream.writeAll("extra align qualifier");
+ },
+ .extra_allowzero_qualifier => {
+ return stream.writeAll("extra allowzero qualifier");
+ },
+ .extra_const_qualifier => {
+ return stream.writeAll("extra const qualifier");
+ },
+ .extra_volatile_qualifier => {
+ return stream.writeAll("extra volatile qualifier");
+ },
+ .invalid_align => {
+ return stream.writeAll("alignment not allowed on arrays");
+ },
+ .invalid_and => {
+ return stream.writeAll("`&&` is invalid; note that `and` is boolean AND");
+ },
+ .invalid_bit_range => {
+ return stream.writeAll("bit range not allowed on slices and arrays");
+ },
+ .invalid_token => {
+ return stream.print("invalid token '{s}'", .{
+ token_tags[parse_error.token].symbol(),
+ });
+ },
+ .same_line_doc_comment => {
+ return stream.writeAll("same line documentation comment");
+ },
+ .unattached_doc_comment => {
+ return stream.writeAll("unattached documentation comment");
+ },
+
+ .expected_token => {
+ const found_tag = token_tags[parse_error.token];
+ const expected_symbol = parse_error.extra.expected_tag.symbol();
+ switch (found_tag) {
+ .invalid => return stream.print("expected '{s}', found invalid bytes", .{
+ expected_symbol,
+ }),
+ else => return stream.print("expected '{s}', found '{s}'", .{
+ expected_symbol, found_tag.symbol(),
+ }),
+ }
+ },
}
- };
-
- fn SingleTokenError(comptime msg: []const u8) type {
- return struct {
- const ThisError = @This();
+ }
- token: TokenIndex,
+ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
+ const tags = tree.nodes.items(.tag);
+ const datas = tree.nodes.items(.data);
+ const main_tokens = tree.nodes.items(.main_token);
+ const token_tags = tree.tokens.items(.tag);
+ var end_offset: TokenIndex = 0;
+ var n = node;
+ while (true) switch (tags[n]) {
+ .root => return 0,
+
+ .test_decl,
+ .@"errdefer",
+ .@"defer",
+ .bool_not,
+ .negation,
+ .bit_not,
+ .negation_wrap,
+ .address_of,
+ .@"try",
+ .@"await",
+ .optional_type,
+ .@"switch",
+ .switch_comma,
+ .if_simple,
+ .@"if",
+ .@"suspend",
+ .@"resume",
+ .@"continue",
+ .@"break",
+ .@"return",
+ .anyframe_type,
+ .identifier,
+ .anyframe_literal,
+ .char_literal,
+ .integer_literal,
+ .float_literal,
+ .false_literal,
+ .true_literal,
+ .null_literal,
+ .undefined_literal,
+ .unreachable_literal,
+ .string_literal,
+ .multiline_string_literal,
+ .grouped_expression,
+ .builtin_call_two,
+ .builtin_call_two_comma,
+ .builtin_call,
+ .builtin_call_comma,
+ .error_set_decl,
+ .@"anytype",
+ .@"comptime",
+ .@"nosuspend",
+ .asm_simple,
+ .@"asm",
+ .array_type,
+ .array_type_sentinel,
+ .error_value,
+ => return main_tokens[n] - end_offset,
+
+ .array_init_dot,
+ .array_init_dot_comma,
+ .array_init_dot_two,
+ .array_init_dot_two_comma,
+ .struct_init_dot,
+ .struct_init_dot_comma,
+ .struct_init_dot_two,
+ .struct_init_dot_two_comma,
+ .enum_literal,
+ => return main_tokens[n] - 1 - end_offset,
+
+ .@"catch",
+ .field_access,
+ .unwrap_optional,
+ .equal_equal,
+ .bang_equal,
+ .less_than,
+ .greater_than,
+ .less_or_equal,
+ .greater_or_equal,
+ .assign_mul,
+ .assign_div,
+ .assign_mod,
+ .assign_add,
+ .assign_sub,
+ .assign_bit_shift_left,
+ .assign_bit_shift_right,
+ .assign_bit_and,
+ .assign_bit_xor,
+ .assign_bit_or,
+ .assign_mul_wrap,
+ .assign_add_wrap,
+ .assign_sub_wrap,
+ .assign,
+ .merge_error_sets,
+ .mul,
+ .div,
+ .mod,
+ .array_mult,
+ .mul_wrap,
+ .add,
+ .sub,
+ .array_cat,
+ .add_wrap,
+ .sub_wrap,
+ .bit_shift_left,
+ .bit_shift_right,
+ .bit_and,
+ .bit_xor,
+ .bit_or,
+ .@"orelse",
+ .bool_and,
+ .bool_or,
+ .slice_open,
+ .slice,
+ .slice_sentinel,
+ .deref,
+ .array_access,
+ .array_init_one,
+ .array_init_one_comma,
+ .array_init,
+ .array_init_comma,
+ .struct_init_one,
+ .struct_init_one_comma,
+ .struct_init,
+ .struct_init_comma,
+ .call_one,
+ .call_one_comma,
+ .call,
+ .call_comma,
+ .switch_range,
+ .error_union,
+ => n = datas[n].lhs,
+
+ .fn_decl,
+ .fn_proto_simple,
+ .fn_proto_multi,
+ .fn_proto_one,
+ .fn_proto,
+ => {
+ var i = main_tokens[n]; // fn token
+ while (i > 0) {
+ i -= 1;
+ switch (token_tags[i]) {
+ .keyword_extern,
+ .keyword_export,
+ .keyword_pub,
+ .keyword_threadlocal,
+ .string_literal,
+ => continue,
+
+ else => return i + 1 - end_offset,
+ }
+ }
+ return i - end_offset;
+ },
- pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: anytype) !void {
- const actual_token = tokens[self.token];
- return stream.print(msg, .{actual_token.symbol()});
- }
+ .@"usingnamespace" => {
+ const main_token = main_tokens[n];
+ if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) {
+ end_offset += 1;
+ }
+ return main_token - end_offset;
+ },
+
+ .async_call_one,
+ .async_call_one_comma,
+ .async_call,
+ .async_call_comma,
+ => {
+ end_offset += 1; // async token
+ n = datas[n].lhs;
+ },
+
+ .container_field_init,
+ .container_field_align,
+ .container_field,
+ => {
+ const name_token = main_tokens[n];
+ if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) {
+ end_offset += 1;
+ }
+ return name_token - end_offset;
+ },
+
+ .global_var_decl,
+ .local_var_decl,
+ .simple_var_decl,
+ .aligned_var_decl,
+ => {
+ var i = main_tokens[n]; // mut token
+ while (i > 0) {
+ i -= 1;
+ switch (token_tags[i]) {
+ .keyword_extern,
+ .keyword_export,
+ .keyword_comptime,
+ .keyword_pub,
+ .keyword_threadlocal,
+ .string_literal,
+ => continue,
+
+ else => return i + 1 - end_offset,
+ }
+ }
+ return i - end_offset;
+ },
+
+ .block,
+ .block_semicolon,
+ .block_two,
+ .block_two_semicolon,
+ => {
+ // Look for a label.
+ const lbrace = main_tokens[n];
+ if (token_tags[lbrace - 1] == .colon) {
+ end_offset += 2;
+ }
+ return lbrace - end_offset;
+ },
+
+ .container_decl,
+ .container_decl_trailing,
+ .container_decl_two,
+ .container_decl_two_trailing,
+ .container_decl_arg,
+ .container_decl_arg_trailing,
+ .tagged_union,
+ .tagged_union_trailing,
+ .tagged_union_two,
+ .tagged_union_two_trailing,
+ .tagged_union_enum_tag,
+ .tagged_union_enum_tag_trailing,
+ => {
+ const main_token = main_tokens[n];
+ switch (token_tags[main_token - 1]) {
+ .keyword_packed, .keyword_extern => end_offset += 1,
+ else => {},
+ }
+ return main_token - end_offset;
+ },
+
+ .ptr_type_aligned,
+ .ptr_type_sentinel,
+ .ptr_type,
+ .ptr_type_bit_range,
+ => {
+ const main_token = main_tokens[n];
+ return switch (token_tags[main_token]) {
+ .asterisk,
+ .asterisk_asterisk,
+ => switch (token_tags[main_token - 1]) {
+ .l_bracket => main_token - 1,
+ else => main_token,
+ },
+ .l_bracket => main_token,
+ else => unreachable,
+ } - end_offset;
+ },
+
+ .switch_case_one => {
+ if (datas[n].lhs == 0) {
+ return main_tokens[n] - 1 - end_offset; // else token
+ } else {
+ n = datas[n].lhs;
+ }
+ },
+ .switch_case => {
+ const extra = tree.extraData(datas[n].lhs, Node.SubRange);
+ assert(extra.end - extra.start > 0);
+ n = tree.extra_data[extra.start];
+ },
+
+ .asm_output, .asm_input => {
+ assert(token_tags[main_tokens[n] - 1] == .l_bracket);
+ return main_tokens[n] - 1 - end_offset;
+ },
+
+ .while_simple,
+ .while_cont,
+ .@"while",
+ .for_simple,
+ .@"for",
+ => {
+ // Look for a label and inline.
+ const main_token = main_tokens[n];
+ var result = main_token;
+ if (token_tags[result - 1] == .keyword_inline) {
+ result -= 1;
+ }
+ if (token_tags[result - 1] == .colon) {
+ result -= 2;
+ }
+ return result - end_offset;
+ },
};
}
- fn SimpleError(comptime msg: []const u8) type {
- return struct {
- const ThisError = @This();
-
- token: TokenIndex,
-
- pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: anytype) !void {
- return stream.writeAll(msg);
- }
+ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
+ const tags = tree.nodes.items(.tag);
+ const datas = tree.nodes.items(.data);
+ const main_tokens = tree.nodes.items(.main_token);
+ const token_starts = tree.tokens.items(.start);
+ const token_tags = tree.tokens.items(.tag);
+ var n = node;
+ var end_offset: TokenIndex = 0;
+ while (true) switch (tags[n]) {
+ .root => return @intCast(TokenIndex, tree.tokens.len - 1),
+
+ .@"usingnamespace",
+ .bool_not,
+ .negation,
+ .bit_not,
+ .negation_wrap,
+ .address_of,
+ .@"try",
+ .@"await",
+ .optional_type,
+ .@"resume",
+ .@"nosuspend",
+ .@"comptime",
+ => n = datas[n].lhs,
+
+ .test_decl,
+ .@"errdefer",
+ .@"defer",
+ .@"catch",
+ .equal_equal,
+ .bang_equal,
+ .less_than,
+ .greater_than,
+ .less_or_equal,
+ .greater_or_equal,
+ .assign_mul,
+ .assign_div,
+ .assign_mod,
+ .assign_add,
+ .assign_sub,
+ .assign_bit_shift_left,
+ .assign_bit_shift_right,
+ .assign_bit_and,
+ .assign_bit_xor,
+ .assign_bit_or,
+ .assign_mul_wrap,
+ .assign_add_wrap,
+ .assign_sub_wrap,
+ .assign,
+ .merge_error_sets,
+ .mul,
+ .div,
+ .mod,
+ .array_mult,
+ .mul_wrap,
+ .add,
+ .sub,
+ .array_cat,
+ .add_wrap,
+ .sub_wrap,
+ .bit_shift_left,
+ .bit_shift_right,
+ .bit_and,
+ .bit_xor,
+ .bit_or,
+ .@"orelse",
+ .bool_and,
+ .bool_or,
+ .anyframe_type,
+ .error_union,
+ .if_simple,
+ .while_simple,
+ .for_simple,
+ .fn_proto_simple,
+ .fn_proto_multi,
+ .ptr_type_aligned,
+ .ptr_type_sentinel,
+ .ptr_type,
+ .ptr_type_bit_range,
+ .array_type,
+ .switch_case_one,
+ .switch_case,
+ .switch_range,
+ => n = datas[n].rhs,
+
+ .field_access,
+ .unwrap_optional,
+ .grouped_expression,
+ .multiline_string_literal,
+ .error_set_decl,
+ .asm_simple,
+ .asm_output,
+ .asm_input,
+ .error_value,
+ => return datas[n].rhs + end_offset,
+
+ .@"anytype",
+ .anyframe_literal,
+ .char_literal,
+ .integer_literal,
+ .float_literal,
+ .false_literal,
+ .true_literal,
+ .null_literal,
+ .undefined_literal,
+ .unreachable_literal,
+ .identifier,
+ .deref,
+ .enum_literal,
+ .string_literal,
+ => return main_tokens[n] + end_offset,
+
+ .@"return" => if (datas[n].lhs != 0) {
+ n = datas[n].lhs;
+ } else {
+ return main_tokens[n] + end_offset;
+ },
+
+ .call, .async_call => {
+ end_offset += 1; // for the rparen
+ const params = tree.extraData(datas[n].rhs, Node.SubRange);
+ if (params.end - params.start == 0) {
+ return main_tokens[n] + end_offset;
+ }
+ n = tree.extra_data[params.end - 1]; // last parameter
+ },
+ .tagged_union_enum_tag => {
+ const members = tree.extraData(datas[n].rhs, Node.SubRange);
+ if (members.end - members.start == 0) {
+ end_offset += 4; // for the rparen + rparen + lbrace + rbrace
+ n = datas[n].lhs;
+ } else {
+ end_offset += 1; // for the rbrace
+ n = tree.extra_data[members.end - 1]; // last parameter
+ }
+ },
+ .call_comma,
+ .async_call_comma,
+ .tagged_union_enum_tag_trailing,
+ => {
+ end_offset += 2; // for the comma/semicolon + rparen/rbrace
+ const params = tree.extraData(datas[n].rhs, Node.SubRange);
+ assert(params.end > params.start);
+ n = tree.extra_data[params.end - 1]; // last parameter
+ },
+ .@"switch" => {
+ const cases = tree.extraData(datas[n].rhs, Node.SubRange);
+ if (cases.end - cases.start == 0) {
+ end_offset += 3; // rparen, lbrace, rbrace
+ n = datas[n].lhs; // condition expression
+ } else {
+ end_offset += 1; // for the rbrace
+ n = tree.extra_data[cases.end - 1]; // last case
+ }
+ },
+ .container_decl_arg => {
+ const members = tree.extraData(datas[n].rhs, Node.SubRange);
+ if (members.end - members.start == 0) {
+ end_offset += 1; // for the rparen
+ n = datas[n].lhs;
+ } else {
+ end_offset += 1; // for the rbrace
+ n = tree.extra_data[members.end - 1]; // last parameter
+ }
+ },
+ .@"asm" => {
+ const extra = tree.extraData(datas[n].rhs, Node.Asm);
+ return extra.rparen + end_offset;
+ },
+ .array_init,
+ .struct_init,
+ => {
+ const elements = tree.extraData(datas[n].rhs, Node.SubRange);
+ assert(elements.end - elements.start > 0);
+ end_offset += 1; // for the rbrace
+ n = tree.extra_data[elements.end - 1]; // last element
+ },
+ .array_init_comma,
+ .struct_init_comma,
+ .container_decl_arg_trailing,
+ .switch_comma,
+ => {
+ const members = tree.extraData(datas[n].rhs, Node.SubRange);
+ assert(members.end - members.start > 0);
+ end_offset += 2; // for the comma + rbrace
+ n = tree.extra_data[members.end - 1]; // last parameter
+ },
+ .array_init_dot,
+ .struct_init_dot,
+ .block,
+ .container_decl,
+ .tagged_union,
+ .builtin_call,
+ => {
+ assert(datas[n].rhs - datas[n].lhs > 0);
+ end_offset += 1; // for the rbrace
+ n = tree.extra_data[datas[n].rhs - 1]; // last statement
+ },
+ .array_init_dot_comma,
+ .struct_init_dot_comma,
+ .block_semicolon,
+ .container_decl_trailing,
+ .tagged_union_trailing,
+ .builtin_call_comma,
+ => {
+ assert(datas[n].rhs - datas[n].lhs > 0);
+ end_offset += 2; // for the comma/semicolon + rbrace/rparen
+ n = tree.extra_data[datas[n].rhs - 1]; // last member
+ },
+ .call_one,
+ .async_call_one,
+ .array_access,
+ => {
+ end_offset += 1; // for the rparen/rbracket
+ if (datas[n].rhs == 0) {
+ return main_tokens[n] + end_offset;
+ }
+ n = datas[n].rhs;
+ },
+ .array_init_dot_two,
+ .block_two,
+ .builtin_call_two,
+ .struct_init_dot_two,
+ .container_decl_two,
+ .tagged_union_two,
+ => {
+ if (datas[n].rhs != 0) {
+ end_offset += 1; // for the rparen/rbrace
+ n = datas[n].rhs;
+ } else if (datas[n].lhs != 0) {
+ end_offset += 1; // for the rparen/rbrace
+ n = datas[n].lhs;
+ } else {
+ switch (tags[n]) {
+ .array_init_dot_two,
+ .block_two,
+ .struct_init_dot_two,
+ => end_offset += 1, // rbrace
+ .builtin_call_two => end_offset += 2, // lparen/lbrace + rparen/rbrace
+ .container_decl_two => {
+ var i: u32 = 2; // lbrace + rbrace
+ while (token_tags[main_tokens[n] + i] == .container_doc_comment) i += 1;
+ end_offset += i;
+ },
+ .tagged_union_two => {
+ var i: u32 = 5; // (enum) {}
+ while (token_tags[main_tokens[n] + i] == .container_doc_comment) i += 1;
+ end_offset += i;
+ },
+ else => unreachable,
+ }
+ return main_tokens[n] + end_offset;
+ }
+ },
+ .array_init_dot_two_comma,
+ .builtin_call_two_comma,
+ .block_two_semicolon,
+ .struct_init_dot_two_comma,
+ .container_decl_two_trailing,
+ .tagged_union_two_trailing,
+ => {
+ end_offset += 2; // for the comma/semicolon + rbrace/rparen
+ if (datas[n].rhs != 0) {
+ n = datas[n].rhs;
+ } else if (datas[n].lhs != 0) {
+ n = datas[n].lhs;
+ } else {
+ unreachable;
+ }
+ },
+ .simple_var_decl => {
+ if (datas[n].rhs != 0) {
+ n = datas[n].rhs;
+ } else if (datas[n].lhs != 0) {
+ n = datas[n].lhs;
+ } else {
+ end_offset += 1; // from mut token to name
+ return main_tokens[n] + end_offset;
+ }
+ },
+ .aligned_var_decl => {
+ if (datas[n].rhs != 0) {
+ n = datas[n].rhs;
+ } else if (datas[n].lhs != 0) {
+ end_offset += 1; // for the rparen
+ n = datas[n].lhs;
+ } else {
+ end_offset += 1; // from mut token to name
+ return main_tokens[n] + end_offset;
+ }
+ },
+ .global_var_decl => {
+ if (datas[n].rhs != 0) {
+ n = datas[n].rhs;
+ } else {
+ const extra = tree.extraData(datas[n].lhs, Node.GlobalVarDecl);
+ if (extra.section_node != 0) {
+ end_offset += 1; // for the rparen
+ n = extra.section_node;
+ } else if (extra.align_node != 0) {
+ end_offset += 1; // for the rparen
+ n = extra.align_node;
+ } else if (extra.type_node != 0) {
+ n = extra.type_node;
+ } else {
+ end_offset += 1; // from mut token to name
+ return main_tokens[n] + end_offset;
+ }
+ }
+ },
+ .local_var_decl => {
+ if (datas[n].rhs != 0) {
+ n = datas[n].rhs;
+ } else {
+ const extra = tree.extraData(datas[n].lhs, Node.LocalVarDecl);
+ if (extra.align_node != 0) {
+ end_offset += 1; // for the rparen
+ n = extra.align_node;
+ } else if (extra.type_node != 0) {
+ n = extra.type_node;
+ } else {
+ end_offset += 1; // from mut token to name
+ return main_tokens[n] + end_offset;
+ }
+ }
+ },
+ .container_field_init => {
+ if (datas[n].rhs != 0) {
+ n = datas[n].rhs;
+ } else if (datas[n].lhs != 0) {
+ n = datas[n].lhs;
+ } else {
+ return main_tokens[n] + end_offset;
+ }
+ },
+ .container_field_align => {
+ if (datas[n].rhs != 0) {
+ end_offset += 1; // for the rparen
+ n = datas[n].rhs;
+ } else if (datas[n].lhs != 0) {
+ n = datas[n].lhs;
+ } else {
+ return main_tokens[n] + end_offset;
+ }
+ },
+ .container_field => {
+ const extra = tree.extraData(datas[n].rhs, Node.ContainerField);
+ if (extra.value_expr != 0) {
+ n = extra.value_expr;
+ } else if (extra.align_expr != 0) {
+ end_offset += 1; // for the rparen
+ n = extra.align_expr;
+ } else if (datas[n].lhs != 0) {
+ n = datas[n].lhs;
+ } else {
+ return main_tokens[n] + end_offset;
+ }
+ },
+
+ .array_init_one,
+ .struct_init_one,
+ => {
+ end_offset += 1; // rbrace
+ if (datas[n].rhs == 0) {
+ return main_tokens[n] + end_offset;
+ } else {
+ n = datas[n].rhs;
+ }
+ },
+ .slice_open,
+ .call_one_comma,
+ .async_call_one_comma,
+ .array_init_one_comma,
+ .struct_init_one_comma,
+ => {
+ end_offset += 2; // ellipsis2 + rbracket, or comma + rparen
+ n = datas[n].rhs;
+ assert(n != 0);
+ },
+ .slice => {
+ const extra = tree.extraData(datas[n].rhs, Node.Slice);
+ assert(extra.end != 0); // should have used SliceOpen
+ end_offset += 1; // rbracket
+ n = extra.end;
+ },
+ .slice_sentinel => {
+ const extra = tree.extraData(datas[n].rhs, Node.SliceSentinel);
+ assert(extra.sentinel != 0); // should have used Slice
+ end_offset += 1; // rbracket
+ n = extra.sentinel;
+ },
+
+ .@"continue" => {
+ if (datas[n].lhs != 0) {
+ return datas[n].lhs + end_offset;
+ } else {
+ return main_tokens[n] + end_offset;
+ }
+ },
+ .@"break" => {
+ if (datas[n].rhs != 0) {
+ n = datas[n].rhs;
+ } else if (datas[n].lhs != 0) {
+ return datas[n].lhs + end_offset;
+ } else {
+ return main_tokens[n] + end_offset;
+ }
+ },
+ .fn_decl => {
+ if (datas[n].rhs != 0) {
+ n = datas[n].rhs;
+ } else {
+ n = datas[n].lhs;
+ }
+ },
+ .fn_proto_one => {
+ const extra = tree.extraData(datas[n].lhs, Node.FnProtoOne);
+ // linksection, callconv, align can appear in any order, so we
+ // find the last one here.
+ var max_node: Node.Index = datas[n].rhs;
+ var max_start = token_starts[main_tokens[max_node]];
+ var max_offset: TokenIndex = 0;
+ if (extra.align_expr != 0) {
+ const start = token_starts[main_tokens[extra.align_expr]];
+ if (start > max_start) {
+ max_node = extra.align_expr;
+ max_start = start;
+ max_offset = 1; // for the rparen
+ }
+ }
+ if (extra.section_expr != 0) {
+ const start = token_starts[main_tokens[extra.section_expr]];
+ if (start > max_start) {
+ max_node = extra.section_expr;
+ max_start = start;
+ max_offset = 1; // for the rparen
+ }
+ }
+ if (extra.callconv_expr != 0) {
+ const start = token_starts[main_tokens[extra.callconv_expr]];
+ if (start > max_start) {
+ max_node = extra.callconv_expr;
+ max_start = start;
+ max_offset = 1; // for the rparen
+ }
+ }
+ n = max_node;
+ end_offset += max_offset;
+ },
+ .fn_proto => {
+ const extra = tree.extraData(datas[n].lhs, Node.FnProto);
+ // linksection, callconv, align can appear in any order, so we
+ // find the last one here.
+ var max_node: Node.Index = datas[n].rhs;
+ var max_start = token_starts[main_tokens[max_node]];
+ var max_offset: TokenIndex = 0;
+ if (extra.align_expr != 0) {
+ const start = token_starts[main_tokens[extra.align_expr]];
+ if (start > max_start) {
+ max_node = extra.align_expr;
+ max_start = start;
+ max_offset = 1; // for the rparen
+ }
+ }
+ if (extra.section_expr != 0) {
+ const start = token_starts[main_tokens[extra.section_expr]];
+ if (start > max_start) {
+ max_node = extra.section_expr;
+ max_start = start;
+ max_offset = 1; // for the rparen
+ }
+ }
+ if (extra.callconv_expr != 0) {
+ const start = token_starts[main_tokens[extra.callconv_expr]];
+ if (start > max_start) {
+ max_node = extra.callconv_expr;
+ max_start = start;
+ max_offset = 1; // for the rparen
+ }
+ }
+ n = max_node;
+ end_offset += max_offset;
+ },
+ .while_cont => {
+ const extra = tree.extraData(datas[n].rhs, Node.WhileCont);
+ assert(extra.then_expr != 0);
+ n = extra.then_expr;
+ },
+ .@"while" => {
+ const extra = tree.extraData(datas[n].rhs, Node.While);
+ assert(extra.else_expr != 0);
+ n = extra.else_expr;
+ },
+ .@"if", .@"for" => {
+ const extra = tree.extraData(datas[n].rhs, Node.If);
+ assert(extra.else_expr != 0);
+ n = extra.else_expr;
+ },
+ .@"suspend" => {
+ if (datas[n].lhs != 0) {
+ n = datas[n].lhs;
+ } else {
+ return main_tokens[n] + end_offset;
+ }
+ },
+ .array_type_sentinel => {
+ const extra = tree.extraData(datas[n].rhs, Node.ArrayTypeSentinel);
+ n = extra.elem_type;
+ },
};
}
-};
-pub const Node = struct {
- tag: Tag,
+ pub fn tokensOnSameLine(tree: Tree, token1: TokenIndex, token2: TokenIndex) bool {
+ const token_starts = tree.tokens.items(.start);
+ const source = tree.source[token_starts[token1]..token_starts[token2]];
+ return mem.indexOfScalar(u8, source, '\n') == null;
+ }
- pub const Tag = enum {
- // Top level
- Root,
- Use,
- TestDecl,
-
- // Statements
- VarDecl,
- Defer,
-
- // Infix operators
- Catch,
-
- // SimpleInfixOp
- Add,
- AddWrap,
- ArrayCat,
- ArrayMult,
- Assign,
- AssignBitAnd,
- AssignBitOr,
- AssignBitShiftLeft,
- AssignBitShiftRight,
- AssignBitXor,
- AssignDiv,
- AssignSub,
- AssignSubWrap,
- AssignMod,
- AssignAdd,
- AssignAddWrap,
- AssignMul,
- AssignMulWrap,
- BangEqual,
- BitAnd,
- BitOr,
- BitShiftLeft,
- BitShiftRight,
- BitXor,
- BoolAnd,
- BoolOr,
- Div,
- EqualEqual,
- ErrorUnion,
- GreaterOrEqual,
- GreaterThan,
- LessOrEqual,
- LessThan,
- MergeErrorSets,
- Mod,
- Mul,
- MulWrap,
- Period,
- Range,
- Sub,
- SubWrap,
- OrElse,
-
- // SimplePrefixOp
- AddressOf,
- Await,
- BitNot,
- BoolNot,
- OptionalType,
- Negation,
- NegationWrap,
- Resume,
- Try,
-
- ArrayType,
- /// ArrayType but has a sentinel node.
- ArrayTypeSentinel,
- PtrType,
- SliceType,
- /// `a[b..c]`
- Slice,
- /// `a.*`
- Deref,
- /// `a.?`
- UnwrapOptional,
- /// `a[b]`
- ArrayAccess,
- /// `T{a, b}`
- ArrayInitializer,
- /// ArrayInitializer but with `.` instead of a left-hand-side operand.
- ArrayInitializerDot,
- /// `T{.a = b}`
- StructInitializer,
- /// StructInitializer but with `.` instead of a left-hand-side operand.
- StructInitializerDot,
- /// `foo()`
- Call,
-
- // Control flow
- Switch,
- While,
- For,
- If,
- Suspend,
- Continue,
- Break,
- Return,
-
- // Type expressions
- AnyType,
- ErrorType,
- FnProto,
- AnyFrameType,
-
- // Primary expressions
- IntegerLiteral,
- FloatLiteral,
- EnumLiteral,
- StringLiteral,
- MultilineStringLiteral,
- CharLiteral,
- BoolLiteral,
- NullLiteral,
- UndefinedLiteral,
- Unreachable,
- Identifier,
- GroupedExpression,
- BuiltinCall,
- ErrorSetDecl,
- ContainerDecl,
- Asm,
- Comptime,
- Nosuspend,
- Block,
- LabeledBlock,
-
- // Misc
- DocComment,
- SwitchCase, // TODO make this not a child of AST Node
- SwitchElse, // TODO make this not a child of AST Node
- Else, // TODO make this not a child of AST Node
- Payload, // TODO make this not a child of AST Node
- PointerPayload, // TODO make this not a child of AST Node
- PointerIndexPayload, // TODO make this not a child of AST Node
- ContainerField,
- ErrorTag, // TODO make this not a child of AST Node
- FieldInitializer, // TODO make this not a child of AST Node
-
- pub fn Type(tag: Tag) type {
- return switch (tag) {
- .Root => Root,
- .Use => Use,
- .TestDecl => TestDecl,
- .VarDecl => VarDecl,
- .Defer => Defer,
- .Catch => Catch,
-
- .Add,
- .AddWrap,
- .ArrayCat,
- .ArrayMult,
- .Assign,
- .AssignBitAnd,
- .AssignBitOr,
- .AssignBitShiftLeft,
- .AssignBitShiftRight,
- .AssignBitXor,
- .AssignDiv,
- .AssignSub,
- .AssignSubWrap,
- .AssignMod,
- .AssignAdd,
- .AssignAddWrap,
- .AssignMul,
- .AssignMulWrap,
- .BangEqual,
- .BitAnd,
- .BitOr,
- .BitShiftLeft,
- .BitShiftRight,
- .BitXor,
- .BoolAnd,
- .BoolOr,
- .Div,
- .EqualEqual,
- .ErrorUnion,
- .GreaterOrEqual,
- .GreaterThan,
- .LessOrEqual,
- .LessThan,
- .MergeErrorSets,
- .Mod,
- .Mul,
- .MulWrap,
- .Period,
- .Range,
- .Sub,
- .SubWrap,
- .OrElse,
- => SimpleInfixOp,
-
- .AddressOf,
- .Await,
- .BitNot,
- .BoolNot,
- .OptionalType,
- .Negation,
- .NegationWrap,
- .Resume,
- .Try,
- => SimplePrefixOp,
-
- .Identifier,
- .BoolLiteral,
- .NullLiteral,
- .UndefinedLiteral,
- .Unreachable,
- .AnyType,
- .ErrorType,
- .IntegerLiteral,
- .FloatLiteral,
- .StringLiteral,
- .CharLiteral,
- => OneToken,
-
- .Continue,
- .Break,
- .Return,
- => ControlFlowExpression,
-
- .ArrayType => ArrayType,
- .ArrayTypeSentinel => ArrayTypeSentinel,
-
- .PtrType => PtrType,
- .SliceType => SliceType,
- .Slice => Slice,
- .Deref, .UnwrapOptional => SimpleSuffixOp,
- .ArrayAccess => ArrayAccess,
-
- .ArrayInitializer => ArrayInitializer,
- .ArrayInitializerDot => ArrayInitializerDot,
-
- .StructInitializer => StructInitializer,
- .StructInitializerDot => StructInitializerDot,
-
- .Call => Call,
- .Switch => Switch,
- .While => While,
- .For => For,
- .If => If,
- .Suspend => Suspend,
- .FnProto => FnProto,
- .AnyFrameType => AnyFrameType,
- .EnumLiteral => EnumLiteral,
- .MultilineStringLiteral => MultilineStringLiteral,
- .GroupedExpression => GroupedExpression,
- .BuiltinCall => BuiltinCall,
- .ErrorSetDecl => ErrorSetDecl,
- .ContainerDecl => ContainerDecl,
- .Asm => Asm,
- .Comptime => Comptime,
- .Nosuspend => Nosuspend,
- .Block => Block,
- .LabeledBlock => LabeledBlock,
- .DocComment => DocComment,
- .SwitchCase => SwitchCase,
- .SwitchElse => SwitchElse,
- .Else => Else,
- .Payload => Payload,
- .PointerPayload => PointerPayload,
- .PointerIndexPayload => PointerIndexPayload,
- .ContainerField => ContainerField,
- .ErrorTag => ErrorTag,
- .FieldInitializer => FieldInitializer,
- };
- }
+ pub fn getNodeSource(tree: Tree, node: Node.Index) []const u8 {
+ const token_starts = tree.tokens.items(.start);
+ const first_token = tree.firstToken(node);
+ const last_token = tree.lastToken(node);
+ const start = token_starts[first_token];
+ const end = token_starts[last_token] + tree.tokenSlice(last_token).len;
+ return tree.source[start..end];
+ }
- pub fn isBlock(tag: Tag) bool {
- return switch (tag) {
- .Block, .LabeledBlock => true,
- else => false,
- };
- }
- };
+ pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
+ assert(tree.nodes.items(.tag)[node] == .global_var_decl);
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.lhs, Node.GlobalVarDecl);
+ return tree.fullVarDecl(.{
+ .type_node = extra.type_node,
+ .align_node = extra.align_node,
+ .section_node = extra.section_node,
+ .init_node = data.rhs,
+ .mut_token = tree.nodes.items(.main_token)[node],
+ });
+ }
- /// Prefer `castTag` to this.
- pub fn cast(base: *Node, comptime T: type) ?*T {
- if (std.meta.fieldInfo(T, "base").default_value) |default_base| {
- return base.castTag(default_base.tag);
- }
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (base.tag == tag) {
- if (T == tag.Type()) {
- return @fieldParentPtr(T, "base", base);
- }
- return null;
- }
- }
- unreachable;
+ pub fn localVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
+ assert(tree.nodes.items(.tag)[node] == .local_var_decl);
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.lhs, Node.LocalVarDecl);
+ return tree.fullVarDecl(.{
+ .type_node = extra.type_node,
+ .align_node = extra.align_node,
+ .section_node = 0,
+ .init_node = data.rhs,
+ .mut_token = tree.nodes.items(.main_token)[node],
+ });
}
- pub fn castTag(base: *Node, comptime tag: Tag) ?*tag.Type() {
- if (base.tag == tag) {
- return @fieldParentPtr(tag.Type(), "base", base);
- }
- return null;
+ pub fn simpleVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
+ assert(tree.nodes.items(.tag)[node] == .simple_var_decl);
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullVarDecl(.{
+ .type_node = data.lhs,
+ .align_node = 0,
+ .section_node = 0,
+ .init_node = data.rhs,
+ .mut_token = tree.nodes.items(.main_token)[node],
+ });
}
- pub fn iterate(base: *Node, index: usize) ?*Node {
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (base.tag == tag) {
- return @fieldParentPtr(tag.Type(), "base", base).iterate(index);
- }
- }
- unreachable;
+ pub fn alignedVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
+ assert(tree.nodes.items(.tag)[node] == .aligned_var_decl);
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullVarDecl(.{
+ .type_node = 0,
+ .align_node = data.lhs,
+ .section_node = 0,
+ .init_node = data.rhs,
+ .mut_token = tree.nodes.items(.main_token)[node],
+ });
}
- pub fn firstToken(base: *const Node) TokenIndex {
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (base.tag == tag) {
- return @fieldParentPtr(tag.Type(), "base", base).firstToken();
- }
- }
- unreachable;
+ pub fn ifSimple(tree: Tree, node: Node.Index) full.If {
+ assert(tree.nodes.items(.tag)[node] == .if_simple);
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullIf(.{
+ .cond_expr = data.lhs,
+ .then_expr = data.rhs,
+ .else_expr = 0,
+ .if_token = tree.nodes.items(.main_token)[node],
+ });
}
- pub fn lastToken(base: *const Node) TokenIndex {
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (base.tag == tag) {
- return @fieldParentPtr(tag.Type(), "base", base).lastToken();
- }
- }
- unreachable;
- }
-
- pub fn requireSemiColon(base: *const Node) bool {
- var n = base;
- while (true) {
- switch (n.tag) {
- .Root,
- .ContainerField,
- .Block,
- .LabeledBlock,
- .Payload,
- .PointerPayload,
- .PointerIndexPayload,
- .Switch,
- .SwitchCase,
- .SwitchElse,
- .FieldInitializer,
- .DocComment,
- .TestDecl,
- => return false,
-
- .While => {
- const while_node = @fieldParentPtr(While, "base", n);
- if (while_node.@"else") |@"else"| {
- n = &@"else".base;
- continue;
- }
+ pub fn ifFull(tree: Tree, node: Node.Index) full.If {
+ assert(tree.nodes.items(.tag)[node] == .@"if");
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.If);
+ return tree.fullIf(.{
+ .cond_expr = data.lhs,
+ .then_expr = extra.then_expr,
+ .else_expr = extra.else_expr,
+ .if_token = tree.nodes.items(.main_token)[node],
+ });
+ }
- return !while_node.body.tag.isBlock();
- },
- .For => {
- const for_node = @fieldParentPtr(For, "base", n);
- if (for_node.@"else") |@"else"| {
- n = &@"else".base;
- continue;
- }
+ pub fn containerField(tree: Tree, node: Node.Index) full.ContainerField {
+ assert(tree.nodes.items(.tag)[node] == .container_field);
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.ContainerField);
+ return tree.fullContainerField(.{
+ .name_token = tree.nodes.items(.main_token)[node],
+ .type_expr = data.lhs,
+ .value_expr = extra.value_expr,
+ .align_expr = extra.align_expr,
+ });
+ }
- return !for_node.body.tag.isBlock();
- },
- .If => {
- const if_node = @fieldParentPtr(If, "base", n);
- if (if_node.@"else") |@"else"| {
- n = &@"else".base;
- continue;
- }
+ pub fn containerFieldInit(tree: Tree, node: Node.Index) full.ContainerField {
+ assert(tree.nodes.items(.tag)[node] == .container_field_init);
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullContainerField(.{
+ .name_token = tree.nodes.items(.main_token)[node],
+ .type_expr = data.lhs,
+ .value_expr = data.rhs,
+ .align_expr = 0,
+ });
+ }
- return !if_node.body.tag.isBlock();
- },
- .Else => {
- const else_node = @fieldParentPtr(Else, "base", n);
- n = else_node.body;
- continue;
- },
- .Defer => {
- const defer_node = @fieldParentPtr(Defer, "base", n);
- return !defer_node.expr.tag.isBlock();
- },
- .Comptime => {
- const comptime_node = @fieldParentPtr(Comptime, "base", n);
- return !comptime_node.expr.tag.isBlock();
- },
- .Suspend => {
- const suspend_node = @fieldParentPtr(Suspend, "base", n);
- if (suspend_node.body) |body| {
- return !body.tag.isBlock();
- }
+ pub fn containerFieldAlign(tree: Tree, node: Node.Index) full.ContainerField {
+ assert(tree.nodes.items(.tag)[node] == .container_field_align);
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullContainerField(.{
+ .name_token = tree.nodes.items(.main_token)[node],
+ .type_expr = data.lhs,
+ .value_expr = 0,
+ .align_expr = data.rhs,
+ });
+ }
- return true;
- },
- .Nosuspend => {
- const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n);
- return !nosuspend_node.expr.tag.isBlock();
- },
- else => return true,
- }
- }
+ pub fn fnProtoSimple(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnProto {
+ assert(tree.nodes.items(.tag)[node] == .fn_proto_simple);
+ const data = tree.nodes.items(.data)[node];
+ buffer[0] = data.lhs;
+ const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1];
+ return tree.fullFnProto(.{
+ .fn_token = tree.nodes.items(.main_token)[node],
+ .return_type = data.rhs,
+ .params = params,
+ .align_expr = 0,
+ .section_expr = 0,
+ .callconv_expr = 0,
+ });
}
- /// Asserts the node is a Block or LabeledBlock and returns the statements slice.
- pub fn blockStatements(base: *Node) []*Node {
- if (base.castTag(.Block)) |block| {
- return block.statements();
- } else if (base.castTag(.LabeledBlock)) |labeled_block| {
- return labeled_block.statements();
- } else {
- unreachable;
- }
+ pub fn fnProtoMulti(tree: Tree, node: Node.Index) full.FnProto {
+ assert(tree.nodes.items(.tag)[node] == .fn_proto_multi);
+ const data = tree.nodes.items(.data)[node];
+ const params_range = tree.extraData(data.lhs, Node.SubRange);
+ const params = tree.extra_data[params_range.start..params_range.end];
+ return tree.fullFnProto(.{
+ .fn_token = tree.nodes.items(.main_token)[node],
+ .return_type = data.rhs,
+ .params = params,
+ .align_expr = 0,
+ .section_expr = 0,
+ .callconv_expr = 0,
+ });
}
- pub fn findFirstWithId(self: *Node, id: Id) ?*Node {
- if (self.id == id) return self;
- var child_i: usize = 0;
- while (self.iterate(child_i)) |child| : (child_i += 1) {
- if (child.findFirstWithId(id)) |result| return result;
- }
- return null;
+ pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnProto {
+ assert(tree.nodes.items(.tag)[node] == .fn_proto_one);
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.lhs, Node.FnProtoOne);
+ buffer[0] = extra.param;
+ const params = if (extra.param == 0) buffer[0..0] else buffer[0..1];
+ return tree.fullFnProto(.{
+ .fn_token = tree.nodes.items(.main_token)[node],
+ .return_type = data.rhs,
+ .params = params,
+ .align_expr = extra.align_expr,
+ .section_expr = extra.section_expr,
+ .callconv_expr = extra.callconv_expr,
+ });
}
- pub fn dump(self: *Node, indent: usize) void {
- {
- var i: usize = 0;
- while (i < indent) : (i += 1) {
- std.debug.warn(" ", .{});
- }
- }
- std.debug.warn("{}\n", .{@tagName(self.tag)});
+ pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto {
+ assert(tree.nodes.items(.tag)[node] == .fn_proto);
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.lhs, Node.FnProto);
+ const params = tree.extra_data[extra.params_start..extra.params_end];
+ return tree.fullFnProto(.{
+ .fn_token = tree.nodes.items(.main_token)[node],
+ .return_type = data.rhs,
+ .params = params,
+ .align_expr = extra.align_expr,
+ .section_expr = extra.section_expr,
+ .callconv_expr = extra.callconv_expr,
+ });
+ }
- var child_i: usize = 0;
- while (self.iterate(child_i)) |child| : (child_i += 1) {
- child.dump(indent + 2);
- }
+ pub fn structInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.StructInit {
+ assert(tree.nodes.items(.tag)[node] == .struct_init_one or
+ tree.nodes.items(.tag)[node] == .struct_init_one_comma);
+ const data = tree.nodes.items(.data)[node];
+ buffer[0] = data.rhs;
+ const fields = if (data.rhs == 0) buffer[0..0] else buffer[0..1];
+ return tree.fullStructInit(.{
+ .lbrace = tree.nodes.items(.main_token)[node],
+ .fields = fields,
+ .type_expr = data.lhs,
+ });
}
- /// The decls data follows this struct in memory as an array of Node pointers.
- pub const Root = struct {
- base: Node = Node{ .tag = .Root },
- eof_token: TokenIndex,
- decls_len: NodeIndex,
+ pub fn structInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.StructInit {
+ assert(tree.nodes.items(.tag)[node] == .struct_init_dot_two or
+ tree.nodes.items(.tag)[node] == .struct_init_dot_two_comma);
+ const data = tree.nodes.items(.data)[node];
+ buffer.* = .{ data.lhs, data.rhs };
+ const fields = if (data.rhs != 0)
+ buffer[0..2]
+ else if (data.lhs != 0)
+ buffer[0..1]
+ else
+ buffer[0..0];
+ return tree.fullStructInit(.{
+ .lbrace = tree.nodes.items(.main_token)[node],
+ .fields = fields,
+ .type_expr = 0,
+ });
+ }
- /// After this the caller must initialize the decls list.
- pub fn create(allocator: *mem.Allocator, decls_len: NodeIndex, eof_token: TokenIndex) !*Root {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(Root), sizeInBytes(decls_len));
- const self = @ptrCast(*Root, bytes.ptr);
- self.* = .{
- .eof_token = eof_token,
- .decls_len = decls_len,
- };
- return self;
- }
+ pub fn structInitDot(tree: Tree, node: Node.Index) full.StructInit {
+ assert(tree.nodes.items(.tag)[node] == .struct_init_dot or
+ tree.nodes.items(.tag)[node] == .struct_init_dot_comma);
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullStructInit(.{
+ .lbrace = tree.nodes.items(.main_token)[node],
+ .fields = tree.extra_data[data.lhs..data.rhs],
+ .type_expr = 0,
+ });
+ }
- pub fn destroy(self: *Decl, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.decls_len)];
- allocator.free(bytes);
- }
+ pub fn structInit(tree: Tree, node: Node.Index) full.StructInit {
+ assert(tree.nodes.items(.tag)[node] == .struct_init or
+ tree.nodes.items(.tag)[node] == .struct_init_comma);
+ const data = tree.nodes.items(.data)[node];
+ const fields_range = tree.extraData(data.rhs, Node.SubRange);
+ return tree.fullStructInit(.{
+ .lbrace = tree.nodes.items(.main_token)[node],
+ .fields = tree.extra_data[fields_range.start..fields_range.end],
+ .type_expr = data.lhs,
+ });
+ }
- pub fn iterate(self: *const Root, index: usize) ?*Node {
- var i = index;
+ pub fn arrayInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.ArrayInit {
+ assert(tree.nodes.items(.tag)[node] == .array_init_one or
+ tree.nodes.items(.tag)[node] == .array_init_one_comma);
+ const data = tree.nodes.items(.data)[node];
+ buffer[0] = data.rhs;
+ const elements = if (data.rhs == 0) buffer[0..0] else buffer[0..1];
+ return .{
+ .ast = .{
+ .lbrace = tree.nodes.items(.main_token)[node],
+ .elements = elements,
+ .type_expr = data.lhs,
+ },
+ };
+ }
- if (i < self.decls_len) return self.declsConst()[i];
- return null;
- }
+ pub fn arrayInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ArrayInit {
+ assert(tree.nodes.items(.tag)[node] == .array_init_dot_two or
+ tree.nodes.items(.tag)[node] == .array_init_dot_two_comma);
+ const data = tree.nodes.items(.data)[node];
+ buffer.* = .{ data.lhs, data.rhs };
+ const elements = if (data.rhs != 0)
+ buffer[0..2]
+ else if (data.lhs != 0)
+ buffer[0..1]
+ else
+ buffer[0..0];
+ return .{
+ .ast = .{
+ .lbrace = tree.nodes.items(.main_token)[node],
+ .elements = elements,
+ .type_expr = 0,
+ },
+ };
+ }
- pub fn decls(self: *Root) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(Root);
- return @ptrCast([*]*Node, decls_start)[0..self.decls_len];
- }
+ pub fn arrayInitDot(tree: Tree, node: Node.Index) full.ArrayInit {
+ assert(tree.nodes.items(.tag)[node] == .array_init_dot or
+ tree.nodes.items(.tag)[node] == .array_init_dot_comma);
+ const data = tree.nodes.items(.data)[node];
+ return .{
+ .ast = .{
+ .lbrace = tree.nodes.items(.main_token)[node],
+ .elements = tree.extra_data[data.lhs..data.rhs],
+ .type_expr = 0,
+ },
+ };
+ }
- pub fn declsConst(self: *const Root) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Root);
- return @ptrCast([*]const *Node, decls_start)[0..self.decls_len];
- }
+ pub fn arrayInit(tree: Tree, node: Node.Index) full.ArrayInit {
+ assert(tree.nodes.items(.tag)[node] == .array_init or
+ tree.nodes.items(.tag)[node] == .array_init_comma);
+ const data = tree.nodes.items(.data)[node];
+ const elem_range = tree.extraData(data.rhs, Node.SubRange);
+ return .{
+ .ast = .{
+ .lbrace = tree.nodes.items(.main_token)[node],
+ .elements = tree.extra_data[elem_range.start..elem_range.end],
+ .type_expr = data.lhs,
+ },
+ };
+ }
- pub fn firstToken(self: *const Root) TokenIndex {
- if (self.decls_len == 0) return self.eof_token;
- return self.declsConst()[0].firstToken();
- }
+ pub fn arrayType(tree: Tree, node: Node.Index) full.ArrayType {
+ assert(tree.nodes.items(.tag)[node] == .array_type);
+ const data = tree.nodes.items(.data)[node];
+ return .{
+ .ast = .{
+ .lbracket = tree.nodes.items(.main_token)[node],
+ .elem_count = data.lhs,
+ .sentinel = null,
+ .elem_type = data.rhs,
+ },
+ };
+ }
- pub fn lastToken(self: *const Root) TokenIndex {
- if (self.decls_len == 0) return self.eof_token;
- return self.declsConst()[self.decls_len - 1].lastToken();
- }
+ pub fn arrayTypeSentinel(tree: Tree, node: Node.Index) full.ArrayType {
+ assert(tree.nodes.items(.tag)[node] == .array_type_sentinel);
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel);
+ return .{
+ .ast = .{
+ .lbracket = tree.nodes.items(.main_token)[node],
+ .elem_count = data.lhs,
+ .sentinel = extra.sentinel,
+ .elem_type = extra.elem_type,
+ },
+ };
+ }
- fn sizeInBytes(decls_len: NodeIndex) usize {
- return @sizeOf(Root) + @sizeOf(*Node) * @as(usize, decls_len);
- }
- };
+ pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType {
+ assert(tree.nodes.items(.tag)[node] == .ptr_type_aligned);
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullPtrType(.{
+ .main_token = tree.nodes.items(.main_token)[node],
+ .align_node = data.lhs,
+ .sentinel = 0,
+ .bit_range_start = 0,
+ .bit_range_end = 0,
+ .child_type = data.rhs,
+ });
+ }
- /// Trailed in memory by possibly many things, with each optional thing
- /// determined by a bit in `trailer_flags`.
- pub const VarDecl = struct {
- base: Node = Node{ .tag = .VarDecl },
- trailer_flags: TrailerFlags,
- mut_token: TokenIndex,
- name_token: TokenIndex,
- semicolon_token: TokenIndex,
-
- pub const TrailerFlags = std.meta.TrailerFlags(struct {
- doc_comments: *DocComment,
- visib_token: TokenIndex,
- thread_local_token: TokenIndex,
- eq_token: TokenIndex,
- comptime_token: TokenIndex,
- extern_export_token: TokenIndex,
- lib_name: *Node,
- type_node: *Node,
- align_node: *Node,
- section_node: *Node,
- init_node: *Node,
+ pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType {
+ assert(tree.nodes.items(.tag)[node] == .ptr_type_sentinel);
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullPtrType(.{
+ .main_token = tree.nodes.items(.main_token)[node],
+ .align_node = 0,
+ .sentinel = data.lhs,
+ .bit_range_start = 0,
+ .bit_range_end = 0,
+ .child_type = data.rhs,
});
+ }
- pub fn getDocComments(self: *const VarDecl) ?*DocComment {
- return self.getTrailer(.doc_comments);
- }
+ pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType {
+ assert(tree.nodes.items(.tag)[node] == .ptr_type);
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.lhs, Node.PtrType);
+ return tree.fullPtrType(.{
+ .main_token = tree.nodes.items(.main_token)[node],
+ .align_node = extra.align_node,
+ .sentinel = extra.sentinel,
+ .bit_range_start = 0,
+ .bit_range_end = 0,
+ .child_type = data.rhs,
+ });
+ }
- pub fn setDocComments(self: *VarDecl, value: *DocComment) void {
- self.setTrailer(.doc_comments, value);
- }
+ pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType {
+ assert(tree.nodes.items(.tag)[node] == .ptr_type_bit_range);
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.lhs, Node.PtrTypeBitRange);
+ return tree.fullPtrType(.{
+ .main_token = tree.nodes.items(.main_token)[node],
+ .align_node = extra.align_node,
+ .sentinel = extra.sentinel,
+ .bit_range_start = extra.bit_range_start,
+ .bit_range_end = extra.bit_range_end,
+ .child_type = data.rhs,
+ });
+ }
- pub fn getVisibToken(self: *const VarDecl) ?TokenIndex {
- return self.getTrailer(.visib_token);
- }
+ pub fn sliceOpen(tree: Tree, node: Node.Index) full.Slice {
+ assert(tree.nodes.items(.tag)[node] == .slice_open);
+ const data = tree.nodes.items(.data)[node];
+ return .{
+ .ast = .{
+ .sliced = data.lhs,
+ .lbracket = tree.nodes.items(.main_token)[node],
+ .start = data.rhs,
+ .end = 0,
+ .sentinel = 0,
+ },
+ };
+ }
- pub fn setVisibToken(self: *VarDecl, value: TokenIndex) void {
- self.setTrailer(.visib_token, value);
- }
+ pub fn slice(tree: Tree, node: Node.Index) full.Slice {
+ assert(tree.nodes.items(.tag)[node] == .slice);
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.Slice);
+ return .{
+ .ast = .{
+ .sliced = data.lhs,
+ .lbracket = tree.nodes.items(.main_token)[node],
+ .start = extra.start,
+ .end = extra.end,
+ .sentinel = 0,
+ },
+ };
+ }
- pub fn getThreadLocalToken(self: *const VarDecl) ?TokenIndex {
- return self.getTrailer(.thread_local_token);
- }
+ pub fn sliceSentinel(tree: Tree, node: Node.Index) full.Slice {
+ assert(tree.nodes.items(.tag)[node] == .slice_sentinel);
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.SliceSentinel);
+ return .{
+ .ast = .{
+ .sliced = data.lhs,
+ .lbracket = tree.nodes.items(.main_token)[node],
+ .start = extra.start,
+ .end = extra.end,
+ .sentinel = extra.sentinel,
+ },
+ };
+ }
- pub fn setThreadLocalToken(self: *VarDecl, value: TokenIndex) void {
- self.setTrailer(.thread_local_token, value);
- }
+ pub fn containerDeclTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl {
+ assert(tree.nodes.items(.tag)[node] == .container_decl_two or
+ tree.nodes.items(.tag)[node] == .container_decl_two_trailing);
+ const data = tree.nodes.items(.data)[node];
+ buffer.* = .{ data.lhs, data.rhs };
+ const members = if (data.rhs != 0)
+ buffer[0..2]
+ else if (data.lhs != 0)
+ buffer[0..1]
+ else
+ buffer[0..0];
+ return tree.fullContainerDecl(.{
+ .main_token = tree.nodes.items(.main_token)[node],
+ .enum_token = null,
+ .members = members,
+ .arg = 0,
+ });
+ }
- pub fn getEqToken(self: *const VarDecl) ?TokenIndex {
- return self.getTrailer(.eq_token);
- }
+ pub fn containerDecl(tree: Tree, node: Node.Index) full.ContainerDecl {
+ assert(tree.nodes.items(.tag)[node] == .container_decl or
+ tree.nodes.items(.tag)[node] == .container_decl_trailing);
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullContainerDecl(.{
+ .main_token = tree.nodes.items(.main_token)[node],
+ .enum_token = null,
+ .members = tree.extra_data[data.lhs..data.rhs],
+ .arg = 0,
+ });
+ }
- pub fn setEqToken(self: *VarDecl, value: TokenIndex) void {
- self.setTrailer(.eq_token, value);
- }
+ pub fn containerDeclArg(tree: Tree, node: Node.Index) full.ContainerDecl {
+ assert(tree.nodes.items(.tag)[node] == .container_decl_arg or
+ tree.nodes.items(.tag)[node] == .container_decl_arg_trailing);
+ const data = tree.nodes.items(.data)[node];
+ const members_range = tree.extraData(data.rhs, Node.SubRange);
+ return tree.fullContainerDecl(.{
+ .main_token = tree.nodes.items(.main_token)[node],
+ .enum_token = null,
+ .members = tree.extra_data[members_range.start..members_range.end],
+ .arg = data.lhs,
+ });
+ }
- pub fn getComptimeToken(self: *const VarDecl) ?TokenIndex {
- return self.getTrailer(.comptime_token);
- }
+ pub fn taggedUnionTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl {
+ assert(tree.nodes.items(.tag)[node] == .tagged_union_two or
+ tree.nodes.items(.tag)[node] == .tagged_union_two_trailing);
+ const data = tree.nodes.items(.data)[node];
+ buffer.* = .{ data.lhs, data.rhs };
+ const members = if (data.rhs != 0)
+ buffer[0..2]
+ else if (data.lhs != 0)
+ buffer[0..1]
+ else
+ buffer[0..0];
+ const main_token = tree.nodes.items(.main_token)[node];
+ return tree.fullContainerDecl(.{
+ .main_token = main_token,
+ .enum_token = main_token + 2, // union lparen enum
+ .members = members,
+ .arg = 0,
+ });
+ }
- pub fn setComptimeToken(self: *VarDecl, value: TokenIndex) void {
- self.setTrailer(.comptime_token, value);
- }
+ pub fn taggedUnion(tree: Tree, node: Node.Index) full.ContainerDecl {
+ assert(tree.nodes.items(.tag)[node] == .tagged_union or
+ tree.nodes.items(.tag)[node] == .tagged_union_trailing);
+ const data = tree.nodes.items(.data)[node];
+ const main_token = tree.nodes.items(.main_token)[node];
+ return tree.fullContainerDecl(.{
+ .main_token = main_token,
+ .enum_token = main_token + 2, // union lparen enum
+ .members = tree.extra_data[data.lhs..data.rhs],
+ .arg = 0,
+ });
+ }
- pub fn getExternExportToken(self: *const VarDecl) ?TokenIndex {
- return self.getTrailer(.extern_export_token);
- }
+ pub fn taggedUnionEnumTag(tree: Tree, node: Node.Index) full.ContainerDecl {
+ assert(tree.nodes.items(.tag)[node] == .tagged_union_enum_tag or
+ tree.nodes.items(.tag)[node] == .tagged_union_enum_tag_trailing);
+ const data = tree.nodes.items(.data)[node];
+ const members_range = tree.extraData(data.rhs, Node.SubRange);
+ const main_token = tree.nodes.items(.main_token)[node];
+ return tree.fullContainerDecl(.{
+ .main_token = main_token,
+ .enum_token = main_token + 2, // union lparen enum
+ .members = tree.extra_data[members_range.start..members_range.end],
+ .arg = data.lhs,
+ });
+ }
- pub fn setExternExportToken(self: *VarDecl, value: TokenIndex) void {
- self.setTrailer(.extern_export_token, value);
- }
+ pub fn switchCaseOne(tree: Tree, node: Node.Index) full.SwitchCase {
+ const data = &tree.nodes.items(.data)[node];
+ const values: *[1]Node.Index = &data.lhs;
+ return tree.fullSwitchCase(.{
+ .values = if (data.lhs == 0) values[0..0] else values[0..1],
+ .arrow_token = tree.nodes.items(.main_token)[node],
+ .target_expr = data.rhs,
+ });
+ }
- pub fn getLibName(self: *const VarDecl) ?*Node {
- return self.getTrailer(.lib_name);
- }
+ pub fn switchCase(tree: Tree, node: Node.Index) full.SwitchCase {
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.lhs, Node.SubRange);
+ return tree.fullSwitchCase(.{
+ .values = tree.extra_data[extra.start..extra.end],
+ .arrow_token = tree.nodes.items(.main_token)[node],
+ .target_expr = data.rhs,
+ });
+ }
- pub fn setLibName(self: *VarDecl, value: *Node) void {
- self.setTrailer(.lib_name, value);
- }
+ pub fn asmSimple(tree: Tree, node: Node.Index) full.Asm {
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullAsm(.{
+ .asm_token = tree.nodes.items(.main_token)[node],
+ .template = data.lhs,
+ .items = &.{},
+ .rparen = data.rhs,
+ });
+ }
- pub fn getTypeNode(self: *const VarDecl) ?*Node {
- return self.getTrailer(.type_node);
- }
+ pub fn asmFull(tree: Tree, node: Node.Index) full.Asm {
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.Asm);
+ return tree.fullAsm(.{
+ .asm_token = tree.nodes.items(.main_token)[node],
+ .template = data.lhs,
+ .items = tree.extra_data[extra.items_start..extra.items_end],
+ .rparen = extra.rparen,
+ });
+ }
- pub fn setTypeNode(self: *VarDecl, value: *Node) void {
- self.setTrailer(.type_node, value);
- }
+ pub fn whileSimple(tree: Tree, node: Node.Index) full.While {
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullWhile(.{
+ .while_token = tree.nodes.items(.main_token)[node],
+ .cond_expr = data.lhs,
+ .cont_expr = 0,
+ .then_expr = data.rhs,
+ .else_expr = 0,
+ });
+ }
- pub fn getAlignNode(self: *const VarDecl) ?*Node {
- return self.getTrailer(.align_node);
- }
+ pub fn whileCont(tree: Tree, node: Node.Index) full.While {
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.WhileCont);
+ return tree.fullWhile(.{
+ .while_token = tree.nodes.items(.main_token)[node],
+ .cond_expr = data.lhs,
+ .cont_expr = extra.cont_expr,
+ .then_expr = extra.then_expr,
+ .else_expr = 0,
+ });
+ }
- pub fn setAlignNode(self: *VarDecl, value: *Node) void {
- self.setTrailer(.align_node, value);
- }
+ pub fn whileFull(tree: Tree, node: Node.Index) full.While {
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.While);
+ return tree.fullWhile(.{
+ .while_token = tree.nodes.items(.main_token)[node],
+ .cond_expr = data.lhs,
+ .cont_expr = extra.cont_expr,
+ .then_expr = extra.then_expr,
+ .else_expr = extra.else_expr,
+ });
+ }
- pub fn getSectionNode(self: *const VarDecl) ?*Node {
- return self.getTrailer(.section_node);
- }
+ pub fn forSimple(tree: Tree, node: Node.Index) full.While {
+ const data = tree.nodes.items(.data)[node];
+ return tree.fullWhile(.{
+ .while_token = tree.nodes.items(.main_token)[node],
+ .cond_expr = data.lhs,
+ .cont_expr = 0,
+ .then_expr = data.rhs,
+ .else_expr = 0,
+ });
+ }
- pub fn setSectionNode(self: *VarDecl, value: *Node) void {
- self.setTrailer(.section_node, value);
- }
+ pub fn forFull(tree: Tree, node: Node.Index) full.While {
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.If);
+ return tree.fullWhile(.{
+ .while_token = tree.nodes.items(.main_token)[node],
+ .cond_expr = data.lhs,
+ .cont_expr = 0,
+ .then_expr = extra.then_expr,
+ .else_expr = extra.else_expr,
+ });
+ }
- pub fn getInitNode(self: *const VarDecl) ?*Node {
- return self.getTrailer(.init_node);
- }
+ pub fn callOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.Call {
+ const data = tree.nodes.items(.data)[node];
+ buffer.* = .{data.rhs};
+ const params = if (data.rhs != 0) buffer[0..1] else buffer[0..0];
+ return tree.fullCall(.{
+ .lparen = tree.nodes.items(.main_token)[node],
+ .fn_expr = data.lhs,
+ .params = params,
+ });
+ }
- pub fn setInitNode(self: *VarDecl, value: *Node) void {
- self.setTrailer(.init_node, value);
- }
+ pub fn callFull(tree: Tree, node: Node.Index) full.Call {
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.SubRange);
+ return tree.fullCall(.{
+ .lparen = tree.nodes.items(.main_token)[node],
+ .fn_expr = data.lhs,
+ .params = tree.extra_data[extra.start..extra.end],
+ });
+ }
- pub const RequiredFields = struct {
- mut_token: TokenIndex,
- name_token: TokenIndex,
- semicolon_token: TokenIndex,
+ fn fullVarDecl(tree: Tree, info: full.VarDecl.Ast) full.VarDecl {
+ const token_tags = tree.tokens.items(.tag);
+ var result: full.VarDecl = .{
+ .ast = info,
+ .visib_token = null,
+ .extern_export_token = null,
+ .lib_name = null,
+ .threadlocal_token = null,
+ .comptime_token = null,
};
-
- fn getTrailer(self: *const VarDecl, comptime field: TrailerFlags.FieldEnum) ?TrailerFlags.Field(field) {
- const trailers_start = @ptrCast([*]const u8, self) + @sizeOf(VarDecl);
- return self.trailer_flags.get(trailers_start, field);
- }
-
- fn setTrailer(self: *VarDecl, comptime field: TrailerFlags.FieldEnum, value: TrailerFlags.Field(field)) void {
- const trailers_start = @ptrCast([*]u8, self) + @sizeOf(VarDecl);
- self.trailer_flags.set(trailers_start, field, value);
- }
-
- pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: TrailerFlags.InitStruct) !*VarDecl {
- const trailer_flags = TrailerFlags.init(trailers);
- const bytes = try allocator.alignedAlloc(u8, @alignOf(VarDecl), sizeInBytes(trailer_flags));
- const var_decl = @ptrCast(*VarDecl, bytes.ptr);
- var_decl.* = .{
- .trailer_flags = trailer_flags,
- .mut_token = required.mut_token,
- .name_token = required.name_token,
- .semicolon_token = required.semicolon_token,
- };
- const trailers_start = bytes.ptr + @sizeOf(VarDecl);
- trailer_flags.setMany(trailers_start, trailers);
- return var_decl;
- }
-
- pub fn destroy(self: *VarDecl, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const VarDecl, index: usize) ?*Node {
- var i = index;
-
- if (self.getTypeNode()) |type_node| {
- if (i < 1) return type_node;
- i -= 1;
- }
-
- if (self.getAlignNode()) |align_node| {
- if (i < 1) return align_node;
- i -= 1;
- }
-
- if (self.getSectionNode()) |section_node| {
- if (i < 1) return section_node;
- i -= 1;
- }
-
- if (self.getInitNode()) |init_node| {
- if (i < 1) return init_node;
- i -= 1;
+ var i = info.mut_token;
+ while (i > 0) {
+ i -= 1;
+ switch (token_tags[i]) {
+ .keyword_extern, .keyword_export => result.extern_export_token = i,
+ .keyword_comptime => result.comptime_token = i,
+ .keyword_pub => result.visib_token = i,
+ .keyword_threadlocal => result.threadlocal_token = i,
+ .string_literal => result.lib_name = i,
+ else => break,
}
-
- return null;
}
+ return result;
+ }
- pub fn firstToken(self: *const VarDecl) TokenIndex {
- if (self.getVisibToken()) |visib_token| return visib_token;
- if (self.getThreadLocalToken()) |thread_local_token| return thread_local_token;
- if (self.getComptimeToken()) |comptime_token| return comptime_token;
- if (self.getExternExportToken()) |extern_export_token| return extern_export_token;
- assert(self.getLibName() == null);
- return self.mut_token;
+ fn fullIf(tree: Tree, info: full.If.Ast) full.If {
+ const token_tags = tree.tokens.items(.tag);
+ var result: full.If = .{
+ .ast = info,
+ .payload_token = null,
+ .error_token = null,
+ .else_token = undefined,
+ };
+ // if (cond_expr) |x|
+ // ^ ^
+ const payload_pipe = tree.lastToken(info.cond_expr) + 2;
+ if (token_tags[payload_pipe] == .pipe) {
+ result.payload_token = payload_pipe + 1;
}
-
- pub fn lastToken(self: *const VarDecl) TokenIndex {
- return self.semicolon_token;
+ if (info.else_expr != 0) {
+ // then_expr else |x|
+ // ^ ^
+ result.else_token = tree.lastToken(info.then_expr) + 1;
+ if (token_tags[result.else_token + 1] == .pipe) {
+ result.error_token = result.else_token + 2;
+ }
}
+ return result;
+ }
- fn sizeInBytes(trailer_flags: TrailerFlags) usize {
- return @sizeOf(VarDecl) + trailer_flags.sizeInBytes();
+ fn fullContainerField(tree: Tree, info: full.ContainerField.Ast) full.ContainerField {
+ const token_tags = tree.tokens.items(.tag);
+ var result: full.ContainerField = .{
+ .ast = info,
+ .comptime_token = null,
+ };
+ // comptime name: type = init,
+ // ^
+ if (info.name_token > 0 and token_tags[info.name_token - 1] == .keyword_comptime) {
+ result.comptime_token = info.name_token - 1;
}
- };
-
- pub const Use = struct {
- base: Node = Node{ .tag = .Use },
- doc_comments: ?*DocComment,
- visib_token: ?TokenIndex,
- use_token: TokenIndex,
- expr: *Node,
- semicolon_token: TokenIndex,
-
- pub fn iterate(self: *const Use, index: usize) ?*Node {
- var i = index;
+ return result;
+ }
- if (i < 1) return self.expr;
+ fn fullFnProto(tree: Tree, info: full.FnProto.Ast) full.FnProto {
+ const token_tags = tree.tokens.items(.tag);
+ var result: full.FnProto = .{
+ .ast = info,
+ .visib_token = null,
+ .extern_export_token = null,
+ .lib_name = null,
+ .name_token = null,
+ .lparen = undefined,
+ };
+ var i = info.fn_token;
+ while (i > 0) {
i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const Use) TokenIndex {
- if (self.visib_token) |visib_token| return visib_token;
- return self.use_token;
- }
-
- pub fn lastToken(self: *const Use) TokenIndex {
- return self.semicolon_token;
- }
- };
-
- pub const ErrorSetDecl = struct {
- base: Node = Node{ .tag = .ErrorSetDecl },
- error_token: TokenIndex,
- rbrace_token: TokenIndex,
- decls_len: NodeIndex,
-
- /// After this the caller must initialize the decls list.
- pub fn alloc(allocator: *mem.Allocator, decls_len: NodeIndex) !*ErrorSetDecl {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(ErrorSetDecl), sizeInBytes(decls_len));
- return @ptrCast(*ErrorSetDecl, bytes.ptr);
- }
-
- pub fn free(self: *ErrorSetDecl, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.decls_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const ErrorSetDecl, index: usize) ?*Node {
- var i = index;
-
- if (i < self.decls_len) return self.declsConst()[i];
- i -= self.decls_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const ErrorSetDecl) TokenIndex {
- return self.error_token;
+ switch (token_tags[i]) {
+ .keyword_extern, .keyword_export => result.extern_export_token = i,
+ .keyword_pub => result.visib_token = i,
+ .string_literal => result.lib_name = i,
+ else => break,
+ }
}
-
- pub fn lastToken(self: *const ErrorSetDecl) TokenIndex {
- return self.rbrace_token;
+ const after_fn_token = info.fn_token + 1;
+ if (token_tags[after_fn_token] == .identifier) {
+ result.name_token = after_fn_token;
+ result.lparen = after_fn_token + 1;
+ } else {
+ result.lparen = after_fn_token;
}
+ assert(token_tags[result.lparen] == .l_paren);
- pub fn decls(self: *ErrorSetDecl) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(ErrorSetDecl);
- return @ptrCast([*]*Node, decls_start)[0..self.decls_len];
- }
+ return result;
+ }
- pub fn declsConst(self: *const ErrorSetDecl) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ErrorSetDecl);
- return @ptrCast([*]const *Node, decls_start)[0..self.decls_len];
- }
+ fn fullStructInit(tree: Tree, info: full.StructInit.Ast) full.StructInit {
+ const token_tags = tree.tokens.items(.tag);
+ var result: full.StructInit = .{
+ .ast = info,
+ };
+ return result;
+ }
- fn sizeInBytes(decls_len: NodeIndex) usize {
- return @sizeOf(ErrorSetDecl) + @sizeOf(*Node) * @as(usize, decls_len);
+ fn fullPtrType(tree: Tree, info: full.PtrType.Ast) full.PtrType {
+ const token_tags = tree.tokens.items(.tag);
+ // TODO: looks like stage1 isn't quite smart enough to handle enum
+ // literals in some places here
+ const Size = std.builtin.TypeInfo.Pointer.Size;
+ const size: Size = switch (token_tags[info.main_token]) {
+ .asterisk,
+ .asterisk_asterisk,
+ => switch (token_tags[info.main_token + 1]) {
+ .r_bracket, .colon => .Many,
+ .identifier => if (token_tags[info.main_token - 1] == .l_bracket) Size.C else .One,
+ else => .One,
+ },
+ .l_bracket => Size.Slice,
+ else => unreachable,
+ };
+ var result: full.PtrType = .{
+ .size = size,
+ .allowzero_token = null,
+ .const_token = null,
+ .volatile_token = null,
+ .ast = info,
+ };
+ // We need to be careful that we don't iterate over any sub-expressions
+ // here while looking for modifiers as that could result in false
+ // positives. Therefore, start after a sentinel if there is one and
+ // skip over any align node and bit range nodes.
+ var i = if (info.sentinel != 0) tree.lastToken(info.sentinel) + 1 else info.main_token;
+ const end = tree.firstToken(info.child_type);
+ while (i < end) : (i += 1) {
+ switch (token_tags[i]) {
+ .keyword_allowzero => result.allowzero_token = i,
+ .keyword_const => result.const_token = i,
+ .keyword_volatile => result.volatile_token = i,
+ .keyword_align => {
+ assert(info.align_node != 0);
+ if (info.bit_range_end != 0) {
+ assert(info.bit_range_start != 0);
+ i = tree.lastToken(info.bit_range_end) + 1;
+ } else {
+ i = tree.lastToken(info.align_node) + 1;
+ }
+ },
+ else => {},
+ }
}
- };
+ return result;
+ }
- /// The fields and decls Node pointers directly follow this struct in memory.
- pub const ContainerDecl = struct {
- base: Node = Node{ .tag = .ContainerDecl },
- kind_token: TokenIndex,
- layout_token: ?TokenIndex,
- lbrace_token: TokenIndex,
- rbrace_token: TokenIndex,
- fields_and_decls_len: NodeIndex,
- init_arg_expr: InitArg,
-
- pub const InitArg = union(enum) {
- None,
- Enum: ?*Node,
- Type: *Node,
+ fn fullContainerDecl(tree: Tree, info: full.ContainerDecl.Ast) full.ContainerDecl {
+ const token_tags = tree.tokens.items(.tag);
+ var result: full.ContainerDecl = .{
+ .ast = info,
+ .layout_token = null,
};
-
- /// After this the caller must initialize the fields_and_decls list.
- pub fn alloc(allocator: *mem.Allocator, fields_and_decls_len: NodeIndex) !*ContainerDecl {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(ContainerDecl), sizeInBytes(fields_and_decls_len));
- return @ptrCast(*ContainerDecl, bytes.ptr);
+ switch (token_tags[info.main_token - 1]) {
+ .keyword_extern, .keyword_packed => result.layout_token = info.main_token - 1,
+ else => {},
}
+ return result;
+ }
- pub fn free(self: *ContainerDecl, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.fields_and_decls_len)];
- allocator.free(bytes);
+ fn fullSwitchCase(tree: Tree, info: full.SwitchCase.Ast) full.SwitchCase {
+ const token_tags = tree.tokens.items(.tag);
+ var result: full.SwitchCase = .{
+ .ast = info,
+ .payload_token = null,
+ };
+ if (token_tags[info.arrow_token + 1] == .pipe) {
+ result.payload_token = info.arrow_token + 2;
}
+ return result;
+ }
- pub fn iterate(self: *const ContainerDecl, index: usize) ?*Node {
- var i = index;
-
- switch (self.init_arg_expr) {
- .Type => |t| {
- if (i < 1) return t;
- i -= 1;
- },
- .None, .Enum => {},
+ fn fullAsm(tree: Tree, info: full.Asm.Ast) full.Asm {
+ const token_tags = tree.tokens.items(.tag);
+ const node_tags = tree.nodes.items(.tag);
+ var result: full.Asm = .{
+ .ast = info,
+ .volatile_token = null,
+ .inputs = &.{},
+ .outputs = &.{},
+ .first_clobber = null,
+ };
+ if (token_tags[info.asm_token + 1] == .keyword_volatile) {
+ result.volatile_token = info.asm_token + 1;
+ }
+ const outputs_end: usize = for (info.items) |item, i| {
+ switch (node_tags[item]) {
+ .asm_output => continue,
+ else => break i,
+ }
+ } else info.items.len;
+
+ result.outputs = info.items[0..outputs_end];
+ result.inputs = info.items[outputs_end..];
+
+ if (info.items.len == 0) {
+ // asm ("foo" ::: "a", "b");
+ const template_token = tree.lastToken(info.template);
+ if (token_tags[template_token + 1] == .colon and
+ token_tags[template_token + 2] == .colon and
+ token_tags[template_token + 3] == .colon and
+ token_tags[template_token + 4] == .string_literal)
+ {
+ result.first_clobber = template_token + 4;
+ }
+ } else if (result.inputs.len != 0) {
+ // asm ("foo" :: [_] "" (y) : "a", "b");
+ const last_input = result.inputs[result.inputs.len - 1];
+ const rparen = tree.lastToken(last_input);
+ if (token_tags[rparen + 1] == .colon and
+ token_tags[rparen + 2] == .string_literal)
+ {
+ result.first_clobber = rparen + 2;
}
-
- if (i < self.fields_and_decls_len) return self.fieldsAndDeclsConst()[i];
- i -= self.fields_and_decls_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const ContainerDecl) TokenIndex {
- if (self.layout_token) |layout_token| {
- return layout_token;
+ } else {
+ // asm ("foo" : [_] "" (x) :: "a", "b");
+ const last_output = result.outputs[result.outputs.len - 1];
+ const rparen = tree.lastToken(last_output);
+ if (token_tags[rparen + 1] == .colon and
+ token_tags[rparen + 2] == .colon and
+ token_tags[rparen + 3] == .string_literal)
+ {
+ result.first_clobber = rparen + 3;
}
- return self.kind_token;
}
- pub fn lastToken(self: *const ContainerDecl) TokenIndex {
- return self.rbrace_token;
- }
+ return result;
+ }
- pub fn fieldsAndDecls(self: *ContainerDecl) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(ContainerDecl);
- return @ptrCast([*]*Node, decls_start)[0..self.fields_and_decls_len];
+ fn fullWhile(tree: Tree, info: full.While.Ast) full.While {
+ const token_tags = tree.tokens.items(.tag);
+ var result: full.While = .{
+ .ast = info,
+ .inline_token = null,
+ .label_token = null,
+ .payload_token = null,
+ .else_token = undefined,
+ .error_token = null,
+ };
+ var tok_i = info.while_token - 1;
+ if (token_tags[tok_i] == .keyword_inline) {
+ result.inline_token = tok_i;
+ tok_i -= 1;
}
-
- pub fn fieldsAndDeclsConst(self: *const ContainerDecl) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ContainerDecl);
- return @ptrCast([*]const *Node, decls_start)[0..self.fields_and_decls_len];
+ if (token_tags[tok_i] == .colon and
+ token_tags[tok_i - 1] == .identifier)
+ {
+ result.label_token = tok_i - 1;
}
-
- fn sizeInBytes(fields_and_decls_len: NodeIndex) usize {
- return @sizeOf(ContainerDecl) + @sizeOf(*Node) * @as(usize, fields_and_decls_len);
+ const last_cond_token = tree.lastToken(info.cond_expr);
+ if (token_tags[last_cond_token + 2] == .pipe) {
+ result.payload_token = last_cond_token + 3;
}
- };
-
- pub const ContainerField = struct {
- base: Node = Node{ .tag = .ContainerField },
- doc_comments: ?*DocComment,
- comptime_token: ?TokenIndex,
- name_token: TokenIndex,
- type_expr: ?*Node,
- value_expr: ?*Node,
- align_expr: ?*Node,
-
- pub fn iterate(self: *const ContainerField, index: usize) ?*Node {
- var i = index;
-
- if (self.type_expr) |type_expr| {
- if (i < 1) return type_expr;
- i -= 1;
+ if (info.else_expr != 0) {
+ // then_expr else |x|
+ // ^ ^
+ result.else_token = tree.lastToken(info.then_expr) + 1;
+ if (token_tags[result.else_token + 1] == .pipe) {
+ result.error_token = result.else_token + 2;
}
-
- if (self.align_expr) |align_expr| {
- if (i < 1) return align_expr;
- i -= 1;
- }
-
- if (self.value_expr) |value_expr| {
- if (i < 1) return value_expr;
- i -= 1;
- }
-
- return null;
}
+ return result;
+ }
- pub fn firstToken(self: *const ContainerField) TokenIndex {
- return self.comptime_token orelse self.name_token;
+ fn fullCall(tree: Tree, info: full.Call.Ast) full.Call {
+ const token_tags = tree.tokens.items(.tag);
+ var result: full.Call = .{
+ .ast = info,
+ .async_token = null,
+ };
+ const maybe_async_token = tree.firstToken(info.fn_expr) - 1;
+ if (token_tags[maybe_async_token] == .keyword_async) {
+ result.async_token = maybe_async_token;
}
+ return result;
+ }
+};
- pub fn lastToken(self: *const ContainerField) TokenIndex {
- if (self.value_expr) |value_expr| {
- return value_expr.lastToken();
- }
- if (self.align_expr) |align_expr| {
- // The expression refers to what's inside the parenthesis, the
- // last token is the closing one
- return align_expr.lastToken() + 1;
- }
- if (self.type_expr) |type_expr| {
- return type_expr.lastToken();
- }
+/// Fully assembled AST node information.
+pub const full = struct {
+ pub const VarDecl = struct {
+ visib_token: ?TokenIndex,
+ extern_export_token: ?TokenIndex,
+ lib_name: ?TokenIndex,
+ threadlocal_token: ?TokenIndex,
+ comptime_token: ?TokenIndex,
+ ast: Ast,
- return self.name_token;
- }
+ pub const Ast = struct {
+ mut_token: TokenIndex,
+ type_node: Node.Index,
+ align_node: Node.Index,
+ section_node: Node.Index,
+ init_node: Node.Index,
+ };
};
- pub const ErrorTag = struct {
- base: Node = Node{ .tag = .ErrorTag },
- doc_comments: ?*DocComment,
- name_token: TokenIndex,
-
- pub fn iterate(self: *const ErrorTag, index: usize) ?*Node {
- var i = index;
-
- if (self.doc_comments) |comments| {
- if (i < 1) return &comments.base;
- i -= 1;
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const ErrorTag) TokenIndex {
- return self.name_token;
- }
+ pub const If = struct {
+ /// Points to the first token after the `|`. Will either be an identifier or
+ /// a `*` (with an identifier immediately after it).
+ payload_token: ?TokenIndex,
+ /// Points to the identifier after the `|`.
+ error_token: ?TokenIndex,
+ /// Populated only if else_expr != 0.
+ else_token: TokenIndex,
+ ast: Ast,
- pub fn lastToken(self: *const ErrorTag) TokenIndex {
- return self.name_token;
- }
+ pub const Ast = struct {
+ if_token: TokenIndex,
+ cond_expr: Node.Index,
+ then_expr: Node.Index,
+ else_expr: Node.Index,
+ };
};
- pub const OneToken = struct {
- base: Node,
- token: TokenIndex,
+ pub const While = struct {
+ ast: Ast,
+ inline_token: ?TokenIndex,
+ label_token: ?TokenIndex,
+ payload_token: ?TokenIndex,
+ error_token: ?TokenIndex,
+ /// Populated only if else_expr != 0.
+ else_token: TokenIndex,
- pub fn iterate(self: *const OneToken, index: usize) ?*Node {
- return null;
- }
+ pub const Ast = struct {
+ while_token: TokenIndex,
+ cond_expr: Node.Index,
+ cont_expr: Node.Index,
+ then_expr: Node.Index,
+ else_expr: Node.Index,
+ };
+ };
- pub fn firstToken(self: *const OneToken) TokenIndex {
- return self.token;
- }
+ pub const ContainerField = struct {
+ comptime_token: ?TokenIndex,
+ ast: Ast,
- pub fn lastToken(self: *const OneToken) TokenIndex {
- return self.token;
- }
+ pub const Ast = struct {
+ name_token: TokenIndex,
+ type_expr: Node.Index,
+ value_expr: Node.Index,
+ align_expr: Node.Index,
+ };
};
- /// The params are directly after the FnProto in memory.
- /// Next, each optional thing determined by a bit in `trailer_flags`.
pub const FnProto = struct {
- base: Node = Node{ .tag = .FnProto },
- trailer_flags: TrailerFlags,
- fn_token: TokenIndex,
- params_len: NodeIndex,
- return_type: ReturnType,
-
- pub const TrailerFlags = std.meta.TrailerFlags(struct {
- doc_comments: *DocComment,
- body_node: *Node,
- lib_name: *Node, // populated if this is an extern declaration
- align_expr: *Node, // populated if align(A) is present
- section_expr: *Node, // populated if linksection(A) is present
- callconv_expr: *Node, // populated if callconv(A) is present
- visib_token: TokenIndex,
- name_token: TokenIndex,
- var_args_token: TokenIndex,
- extern_export_inline_token: TokenIndex,
- is_extern_prototype: void, // TODO: Remove once extern fn rewriting is
- is_async: void, // TODO: remove once async fn rewriting is
- });
+ visib_token: ?TokenIndex,
+ extern_export_token: ?TokenIndex,
+ lib_name: ?TokenIndex,
+ name_token: ?TokenIndex,
+ lparen: TokenIndex,
+ ast: Ast,
- pub const RequiredFields = struct {
+ pub const Ast = struct {
fn_token: TokenIndex,
- params_len: NodeIndex,
- return_type: ReturnType,
- };
-
- pub const ReturnType = union(enum) {
- Explicit: *Node,
- InferErrorSet: *Node,
- Invalid: TokenIndex,
+ return_type: Node.Index,
+ params: []const Node.Index,
+ align_expr: Node.Index,
+ section_expr: Node.Index,
+ callconv_expr: Node.Index,
};
- pub const ParamDecl = struct {
- doc_comments: ?*DocComment,
- comptime_token: ?TokenIndex,
- noalias_token: ?TokenIndex,
+ pub const Param = struct {
+ first_doc_comment: ?TokenIndex,
name_token: ?TokenIndex,
- param_type: ParamType,
-
- pub const ParamType = union(enum) {
- any_type: *Node,
- type_expr: *Node,
- };
-
- pub fn iterate(self: *const ParamDecl, index: usize) ?*Node {
- var i = index;
+ comptime_noalias: ?TokenIndex,
+ anytype_ellipsis3: ?TokenIndex,
+ type_expr: Node.Index,
+ };
- if (i < 1) {
- switch (self.param_type) {
- .any_type, .type_expr => |node| return node,
+ /// Abstracts over the fact that anytype and ... are not included
+ /// in the params slice, since they are simple identifiers and
+ /// not sub-expressions.
+ pub const Iterator = struct {
+ tree: *const Tree,
+ fn_proto: *const FnProto,
+ param_i: usize,
+ tok_i: TokenIndex,
+ tok_flag: bool,
+
+ pub fn next(it: *Iterator) ?Param {
+ const token_tags = it.tree.tokens.items(.tag);
+ while (true) {
+ var first_doc_comment: ?TokenIndex = null;
+ var comptime_noalias: ?TokenIndex = null;
+ var name_token: ?TokenIndex = null;
+ if (!it.tok_flag) {
+ if (it.param_i >= it.fn_proto.ast.params.len) {
+ return null;
+ }
+ const param_type = it.fn_proto.ast.params[it.param_i];
+ var tok_i = it.tree.firstToken(param_type) - 1;
+ while (true) : (tok_i -= 1) switch (token_tags[tok_i]) {
+ .colon => continue,
+ .identifier => name_token = tok_i,
+ .doc_comment => first_doc_comment = tok_i,
+ .keyword_comptime, .keyword_noalias => comptime_noalias = tok_i,
+ else => break,
+ };
+ it.param_i += 1;
+ it.tok_i = it.tree.lastToken(param_type) + 1;
+ it.tok_flag = true;
+ return Param{
+ .first_doc_comment = first_doc_comment,
+ .comptime_noalias = comptime_noalias,
+ .name_token = name_token,
+ .anytype_ellipsis3 = null,
+ .type_expr = param_type,
+ };
}
- }
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const ParamDecl) TokenIndex {
- if (self.comptime_token) |comptime_token| return comptime_token;
- if (self.noalias_token) |noalias_token| return noalias_token;
- if (self.name_token) |name_token| return name_token;
- switch (self.param_type) {
- .any_type, .type_expr => |node| return node.firstToken(),
- }
- }
-
- pub fn lastToken(self: *const ParamDecl) TokenIndex {
- switch (self.param_type) {
- .any_type, .type_expr => |node| return node.lastToken(),
+ // Look for anytype and ... params afterwards.
+ if (token_tags[it.tok_i] == .comma) {
+ it.tok_i += 1;
+ } else {
+ return null;
+ }
+ if (token_tags[it.tok_i] == .doc_comment) {
+ first_doc_comment = it.tok_i;
+ while (token_tags[it.tok_i] == .doc_comment) {
+ it.tok_i += 1;
+ }
+ }
+ switch (token_tags[it.tok_i]) {
+ .ellipsis3 => {
+ it.tok_flag = false; // Next iteration should return null.
+ return Param{
+ .first_doc_comment = first_doc_comment,
+ .comptime_noalias = null,
+ .name_token = null,
+ .anytype_ellipsis3 = it.tok_i,
+ .type_expr = 0,
+ };
+ },
+ .keyword_noalias, .keyword_comptime => {
+ comptime_noalias = it.tok_i;
+ it.tok_i += 1;
+ },
+ else => {},
+ }
+ if (token_tags[it.tok_i] == .identifier and
+ token_tags[it.tok_i + 1] == .colon)
+ {
+ name_token = it.tok_i;
+ it.tok_i += 2;
+ }
+ if (token_tags[it.tok_i] == .keyword_anytype) {
+ it.tok_i += 1;
+ return Param{
+ .first_doc_comment = first_doc_comment,
+ .comptime_noalias = comptime_noalias,
+ .name_token = name_token,
+ .anytype_ellipsis3 = it.tok_i - 1,
+ .type_expr = 0,
+ };
+ }
+ it.tok_flag = false;
}
}
};
- /// For debugging purposes.
- pub fn dump(self: *const FnProto) void {
- const trailers_start = @alignCast(
- @alignOf(ParamDecl),
- @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
- );
- std.debug.print("{*} flags: {b} name_token: {} {*} params_len: {}\n", .{
- self,
- self.trailer_flags.bits,
- self.getNameToken(),
- self.trailer_flags.ptrConst(trailers_start, .name_token),
- self.params_len,
- });
- }
-
- pub fn getDocComments(self: *const FnProto) ?*DocComment {
- return self.getTrailer(.doc_comments);
- }
-
- pub fn setDocComments(self: *FnProto, value: *DocComment) void {
- self.setTrailer(.doc_comments, value);
- }
-
- pub fn getBodyNode(self: *const FnProto) ?*Node {
- return self.getTrailer(.body_node);
- }
-
- pub fn setBodyNode(self: *FnProto, value: *Node) void {
- self.setTrailer(.body_node, value);
- }
-
- pub fn getLibName(self: *const FnProto) ?*Node {
- return self.getTrailer(.lib_name);
- }
-
- pub fn setLibName(self: *FnProto, value: *Node) void {
- self.setTrailer(.lib_name, value);
- }
-
- pub fn getAlignExpr(self: *const FnProto) ?*Node {
- return self.getTrailer(.align_expr);
- }
-
- pub fn setAlignExpr(self: *FnProto, value: *Node) void {
- self.setTrailer(.align_expr, value);
- }
-
- pub fn getSectionExpr(self: *const FnProto) ?*Node {
- return self.getTrailer(.section_expr);
- }
-
- pub fn setSectionExpr(self: *FnProto, value: *Node) void {
- self.setTrailer(.section_expr, value);
- }
-
- pub fn getCallconvExpr(self: *const FnProto) ?*Node {
- return self.getTrailer(.callconv_expr);
- }
-
- pub fn setCallconvExpr(self: *FnProto, value: *Node) void {
- self.setTrailer(.callconv_expr, value);
- }
-
- pub fn getVisibToken(self: *const FnProto) ?TokenIndex {
- return self.getTrailer(.visib_token);
- }
-
- pub fn setVisibToken(self: *FnProto, value: TokenIndex) void {
- self.setTrailer(.visib_token, value);
- }
-
- pub fn getNameToken(self: *const FnProto) ?TokenIndex {
- return self.getTrailer(.name_token);
- }
-
- pub fn setNameToken(self: *FnProto, value: TokenIndex) void {
- self.setTrailer(.name_token, value);
- }
-
- pub fn getVarArgsToken(self: *const FnProto) ?TokenIndex {
- return self.getTrailer(.var_args_token);
- }
-
- pub fn setVarArgsToken(self: *FnProto, value: TokenIndex) void {
- self.setTrailer(.var_args_token, value);
- }
-
- pub fn getExternExportInlineToken(self: *const FnProto) ?TokenIndex {
- return self.getTrailer(.extern_export_inline_token);
- }
-
- pub fn setExternExportInlineToken(self: *FnProto, value: TokenIndex) void {
- self.setTrailer(.extern_export_inline_token, value);
- }
-
- pub fn getIsExternPrototype(self: *const FnProto) ?void {
- return self.getTrailer(.is_extern_prototype);
- }
-
- pub fn setIsExternPrototype(self: *FnProto, value: void) void {
- self.setTrailer(.is_extern_prototype, value);
- }
-
- pub fn getIsAsync(self: *const FnProto) ?void {
- return self.getTrailer(.is_async);
- }
-
- pub fn setIsAsync(self: *FnProto, value: void) void {
- self.setTrailer(.is_async, value);
- }
-
- fn getTrailer(self: *const FnProto, comptime field: TrailerFlags.FieldEnum) ?TrailerFlags.Field(field) {
- const trailers_start = @alignCast(
- @alignOf(ParamDecl),
- @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
- );
- return self.trailer_flags.get(trailers_start, field);
- }
-
- fn setTrailer(self: *FnProto, comptime field: TrailerFlags.FieldEnum, value: TrailerFlags.Field(field)) void {
- const trailers_start = @alignCast(
- @alignOf(ParamDecl),
- @ptrCast([*]u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
- );
- self.trailer_flags.set(trailers_start, field, value);
- }
-
- /// After this the caller must initialize the params list.
- pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: TrailerFlags.InitStruct) !*FnProto {
- const trailer_flags = TrailerFlags.init(trailers);
- const bytes = try allocator.alignedAlloc(u8, @alignOf(FnProto), sizeInBytes(
- required.params_len,
- trailer_flags,
- ));
- const fn_proto = @ptrCast(*FnProto, bytes.ptr);
- fn_proto.* = .{
- .trailer_flags = trailer_flags,
- .fn_token = required.fn_token,
- .params_len = required.params_len,
- .return_type = required.return_type,
- };
- const trailers_start = @alignCast(
- @alignOf(ParamDecl),
- bytes.ptr + @sizeOf(FnProto) + @sizeOf(ParamDecl) * required.params_len,
- );
- trailer_flags.setMany(trailers_start, trailers);
- return fn_proto;
- }
-
- pub fn destroy(self: *FnProto, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len, self.trailer_flags)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const FnProto, index: usize) ?*Node {
- var i = index;
-
- if (self.getLibName()) |lib_name| {
- if (i < 1) return lib_name;
- i -= 1;
- }
-
- const params_len: usize = if (self.params_len == 0)
- 0
- else switch (self.paramsConst()[self.params_len - 1].param_type) {
- .any_type, .type_expr => self.params_len,
+ pub fn iterate(fn_proto: FnProto, tree: Tree) Iterator {
+ return .{
+ .tree = &tree,
+ .fn_proto = &fn_proto,
+ .param_i = 0,
+ .tok_i = undefined,
+ .tok_flag = false,
};
- if (i < params_len) {
- switch (self.paramsConst()[i].param_type) {
- .any_type => |n| return n,
- .type_expr => |n| return n,
- }
- }
- i -= params_len;
-
- if (self.getAlignExpr()) |align_expr| {
- if (i < 1) return align_expr;
- i -= 1;
- }
-
- if (self.getSectionExpr()) |section_expr| {
- if (i < 1) return section_expr;
- i -= 1;
- }
-
- switch (self.return_type) {
- .Explicit, .InferErrorSet => |node| {
- if (i < 1) return node;
- i -= 1;
- },
- .Invalid => {},
- }
-
- if (self.getBodyNode()) |body_node| {
- if (i < 1) return body_node;
- i -= 1;
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const FnProto) TokenIndex {
- if (self.getVisibToken()) |visib_token| return visib_token;
- if (self.getExternExportInlineToken()) |extern_export_inline_token| return extern_export_inline_token;
- assert(self.getLibName() == null);
- return self.fn_token;
- }
-
- pub fn lastToken(self: *const FnProto) TokenIndex {
- if (self.getBodyNode()) |body_node| return body_node.lastToken();
- switch (self.return_type) {
- .Explicit, .InferErrorSet => |node| return node.lastToken(),
- .Invalid => |tok| return tok,
- }
- }
-
- pub fn params(self: *FnProto) []ParamDecl {
- const params_start = @ptrCast([*]u8, self) + @sizeOf(FnProto);
- return @ptrCast([*]ParamDecl, params_start)[0..self.params_len];
- }
-
- pub fn paramsConst(self: *const FnProto) []const ParamDecl {
- const params_start = @ptrCast([*]const u8, self) + @sizeOf(FnProto);
- return @ptrCast([*]const ParamDecl, params_start)[0..self.params_len];
- }
-
- fn sizeInBytes(params_len: NodeIndex, trailer_flags: TrailerFlags) usize {
- return @sizeOf(FnProto) + @sizeOf(ParamDecl) * @as(usize, params_len) + trailer_flags.sizeInBytes();
}
};
- pub const AnyFrameType = struct {
- base: Node = Node{ .tag = .AnyFrameType },
- anyframe_token: TokenIndex,
- result: ?Result,
+ pub const StructInit = struct {
+ ast: Ast,
- pub const Result = struct {
- arrow_token: TokenIndex,
- return_type: *Node,
+ pub const Ast = struct {
+ lbrace: TokenIndex,
+ fields: []const Node.Index,
+ type_expr: Node.Index,
};
-
- pub fn iterate(self: *const AnyFrameType, index: usize) ?*Node {
- var i = index;
-
- if (self.result) |result| {
- if (i < 1) return result.return_type;
- i -= 1;
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const AnyFrameType) TokenIndex {
- return self.anyframe_token;
- }
-
- pub fn lastToken(self: *const AnyFrameType) TokenIndex {
- if (self.result) |result| return result.return_type.lastToken();
- return self.anyframe_token;
- }
};
- /// The statements of the block follow Block directly in memory.
- pub const Block = struct {
- base: Node = Node{ .tag = .Block },
- statements_len: NodeIndex,
- lbrace: TokenIndex,
- rbrace: TokenIndex,
-
- /// After this the caller must initialize the statements list.
- pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*Block {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(Block), sizeInBytes(statements_len));
- return @ptrCast(*Block, bytes.ptr);
- }
-
- pub fn free(self: *Block, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.statements_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const Block, index: usize) ?*Node {
- var i = index;
-
- if (i < self.statements_len) return self.statementsConst()[i];
- i -= self.statements_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const Block) TokenIndex {
- return self.lbrace;
- }
-
- pub fn lastToken(self: *const Block) TokenIndex {
- return self.rbrace;
- }
-
- pub fn statements(self: *Block) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(Block);
- return @ptrCast([*]*Node, decls_start)[0..self.statements_len];
- }
-
- pub fn statementsConst(self: *const Block) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Block);
- return @ptrCast([*]const *Node, decls_start)[0..self.statements_len];
- }
-
- fn sizeInBytes(statements_len: NodeIndex) usize {
- return @sizeOf(Block) + @sizeOf(*Node) * @as(usize, statements_len);
- }
- };
-
- /// The statements of the block follow LabeledBlock directly in memory.
- pub const LabeledBlock = struct {
- base: Node = Node{ .tag = .LabeledBlock },
- statements_len: NodeIndex,
- lbrace: TokenIndex,
- rbrace: TokenIndex,
- label: TokenIndex,
-
- /// After this the caller must initialize the statements list.
- pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*LabeledBlock {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(LabeledBlock), sizeInBytes(statements_len));
- return @ptrCast(*LabeledBlock, bytes.ptr);
- }
-
- pub fn free(self: *LabeledBlock, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.statements_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const LabeledBlock, index: usize) ?*Node {
- var i = index;
-
- if (i < self.statements_len) return self.statementsConst()[i];
- i -= self.statements_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const LabeledBlock) TokenIndex {
- return self.label;
- }
-
- pub fn lastToken(self: *const LabeledBlock) TokenIndex {
- return self.rbrace;
- }
-
- pub fn statements(self: *LabeledBlock) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(LabeledBlock);
- return @ptrCast([*]*Node, decls_start)[0..self.statements_len];
- }
-
- pub fn statementsConst(self: *const LabeledBlock) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(LabeledBlock);
- return @ptrCast([*]const *Node, decls_start)[0..self.statements_len];
- }
-
- fn sizeInBytes(statements_len: NodeIndex) usize {
- return @sizeOf(LabeledBlock) + @sizeOf(*Node) * @as(usize, statements_len);
- }
- };
-
- pub const Defer = struct {
- base: Node = Node{ .tag = .Defer },
- defer_token: TokenIndex,
- payload: ?*Node,
- expr: *Node,
-
- pub fn iterate(self: *const Defer, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.expr;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const Defer) TokenIndex {
- return self.defer_token;
- }
-
- pub fn lastToken(self: *const Defer) TokenIndex {
- return self.expr.lastToken();
- }
- };
-
- pub const Comptime = struct {
- base: Node = Node{ .tag = .Comptime },
- doc_comments: ?*DocComment,
- comptime_token: TokenIndex,
- expr: *Node,
-
- pub fn iterate(self: *const Comptime, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.expr;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const Comptime) TokenIndex {
- return self.comptime_token;
- }
-
- pub fn lastToken(self: *const Comptime) TokenIndex {
- return self.expr.lastToken();
- }
- };
-
- pub const Nosuspend = struct {
- base: Node = Node{ .tag = .Nosuspend },
- nosuspend_token: TokenIndex,
- expr: *Node,
-
- pub fn iterate(self: *const Nosuspend, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.expr;
- i -= 1;
-
- return null;
- }
+ pub const ArrayInit = struct {
+ ast: Ast,
- pub fn firstToken(self: *const Nosuspend) TokenIndex {
- return self.nosuspend_token;
- }
-
- pub fn lastToken(self: *const Nosuspend) TokenIndex {
- return self.expr.lastToken();
- }
- };
-
- pub const Payload = struct {
- base: Node = Node{ .tag = .Payload },
- lpipe: TokenIndex,
- error_symbol: *Node,
- rpipe: TokenIndex,
-
- pub fn iterate(self: *const Payload, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.error_symbol;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const Payload) TokenIndex {
- return self.lpipe;
- }
-
- pub fn lastToken(self: *const Payload) TokenIndex {
- return self.rpipe;
- }
+ pub const Ast = struct {
+ lbrace: TokenIndex,
+ elements: []const Node.Index,
+ type_expr: Node.Index,
+ };
};
- pub const PointerPayload = struct {
- base: Node = Node{ .tag = .PointerPayload },
- lpipe: TokenIndex,
- ptr_token: ?TokenIndex,
- value_symbol: *Node,
- rpipe: TokenIndex,
-
- pub fn iterate(self: *const PointerPayload, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.value_symbol;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const PointerPayload) TokenIndex {
- return self.lpipe;
- }
+ pub const ArrayType = struct {
+ ast: Ast,
- pub fn lastToken(self: *const PointerPayload) TokenIndex {
- return self.rpipe;
- }
+ pub const Ast = struct {
+ lbracket: TokenIndex,
+ elem_count: Node.Index,
+ sentinel: ?Node.Index,
+ elem_type: Node.Index,
+ };
};
- pub const PointerIndexPayload = struct {
- base: Node = Node{ .tag = .PointerIndexPayload },
- lpipe: TokenIndex,
- ptr_token: ?TokenIndex,
- value_symbol: *Node,
- index_symbol: ?*Node,
- rpipe: TokenIndex,
-
- pub fn iterate(self: *const PointerIndexPayload, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.value_symbol;
- i -= 1;
-
- if (self.index_symbol) |index_symbol| {
- if (i < 1) return index_symbol;
- i -= 1;
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const PointerIndexPayload) TokenIndex {
- return self.lpipe;
- }
-
- pub fn lastToken(self: *const PointerIndexPayload) TokenIndex {
- return self.rpipe;
- }
+ pub const PtrType = struct {
+ size: std.builtin.TypeInfo.Pointer.Size,
+ allowzero_token: ?TokenIndex,
+ const_token: ?TokenIndex,
+ volatile_token: ?TokenIndex,
+ ast: Ast,
+
+ pub const Ast = struct {
+ main_token: TokenIndex,
+ align_node: Node.Index,
+ sentinel: Node.Index,
+ bit_range_start: Node.Index,
+ bit_range_end: Node.Index,
+ child_type: Node.Index,
+ };
};
- pub const Else = struct {
- base: Node = Node{ .tag = .Else },
- else_token: TokenIndex,
- payload: ?*Node,
- body: *Node,
-
- pub fn iterate(self: *const Else, index: usize) ?*Node {
- var i = index;
-
- if (self.payload) |payload| {
- if (i < 1) return payload;
- i -= 1;
- }
-
- if (i < 1) return self.body;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const Else) TokenIndex {
- return self.else_token;
- }
+ pub const Slice = struct {
+ ast: Ast,
- pub fn lastToken(self: *const Else) TokenIndex {
- return self.body.lastToken();
- }
+ pub const Ast = struct {
+ sliced: Node.Index,
+ lbracket: TokenIndex,
+ start: Node.Index,
+ end: Node.Index,
+ sentinel: Node.Index,
+ };
};
- /// The cases node pointers are found in memory after Switch.
- /// They must be SwitchCase or SwitchElse nodes.
- pub const Switch = struct {
- base: Node = Node{ .tag = .Switch },
- switch_token: TokenIndex,
- rbrace: TokenIndex,
- cases_len: NodeIndex,
- expr: *Node,
-
- /// After this the caller must initialize the fields_and_decls list.
- pub fn alloc(allocator: *mem.Allocator, cases_len: NodeIndex) !*Switch {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(Switch), sizeInBytes(cases_len));
- return @ptrCast(*Switch, bytes.ptr);
- }
-
- pub fn free(self: *Switch, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.cases_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const Switch, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.expr;
- i -= 1;
-
- if (i < self.cases_len) return self.casesConst()[i];
- i -= self.cases_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const Switch) TokenIndex {
- return self.switch_token;
- }
-
- pub fn lastToken(self: *const Switch) TokenIndex {
- return self.rbrace;
- }
-
- pub fn cases(self: *Switch) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(Switch);
- return @ptrCast([*]*Node, decls_start)[0..self.cases_len];
- }
-
- pub fn casesConst(self: *const Switch) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Switch);
- return @ptrCast([*]const *Node, decls_start)[0..self.cases_len];
- }
-
- fn sizeInBytes(cases_len: NodeIndex) usize {
- return @sizeOf(Switch) + @sizeOf(*Node) * @as(usize, cases_len);
- }
+ pub const ContainerDecl = struct {
+ layout_token: ?TokenIndex,
+ ast: Ast,
+
+ pub const Ast = struct {
+ main_token: TokenIndex,
+ /// Populated when main_token is Keyword_union.
+ enum_token: ?TokenIndex,
+ members: []const Node.Index,
+ arg: Node.Index,
+ };
};
- /// Items sub-nodes appear in memory directly following SwitchCase.
pub const SwitchCase = struct {
- base: Node = Node{ .tag = .SwitchCase },
- arrow_token: TokenIndex,
- payload: ?*Node,
- expr: *Node,
- items_len: NodeIndex,
-
- /// After this the caller must initialize the fields_and_decls list.
- pub fn alloc(allocator: *mem.Allocator, items_len: NodeIndex) !*SwitchCase {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(SwitchCase), sizeInBytes(items_len));
- return @ptrCast(*SwitchCase, bytes.ptr);
- }
-
- pub fn free(self: *SwitchCase, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.items_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const SwitchCase, index: usize) ?*Node {
- var i = index;
-
- if (i < self.items_len) return self.itemsConst()[i];
- i -= self.items_len;
-
- if (self.payload) |payload| {
- if (i < 1) return payload;
- i -= 1;
- }
-
- if (i < 1) return self.expr;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const SwitchCase) TokenIndex {
- return self.itemsConst()[0].firstToken();
- }
-
- pub fn lastToken(self: *const SwitchCase) TokenIndex {
- return self.expr.lastToken();
- }
-
- pub fn items(self: *SwitchCase) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(SwitchCase);
- return @ptrCast([*]*Node, decls_start)[0..self.items_len];
- }
-
- pub fn itemsConst(self: *const SwitchCase) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(SwitchCase);
- return @ptrCast([*]const *Node, decls_start)[0..self.items_len];
- }
-
- fn sizeInBytes(items_len: NodeIndex) usize {
- return @sizeOf(SwitchCase) + @sizeOf(*Node) * @as(usize, items_len);
- }
- };
-
- pub const SwitchElse = struct {
- base: Node = Node{ .tag = .SwitchElse },
- token: TokenIndex,
-
- pub fn iterate(self: *const SwitchElse, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const SwitchElse) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const SwitchElse) TokenIndex {
- return self.token;
- }
- };
-
- pub const While = struct {
- base: Node = Node{ .tag = .While },
- label: ?TokenIndex,
- inline_token: ?TokenIndex,
- while_token: TokenIndex,
- condition: *Node,
- payload: ?*Node,
- continue_expr: ?*Node,
- body: *Node,
- @"else": ?*Else,
-
- pub fn iterate(self: *const While, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.condition;
- i -= 1;
-
- if (self.payload) |payload| {
- if (i < 1) return payload;
- i -= 1;
- }
-
- if (self.continue_expr) |continue_expr| {
- if (i < 1) return continue_expr;
- i -= 1;
- }
-
- if (i < 1) return self.body;
- i -= 1;
-
- if (self.@"else") |@"else"| {
- if (i < 1) return &@"else".base;
- i -= 1;
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const While) TokenIndex {
- if (self.label) |label| {
- return label;
- }
-
- if (self.inline_token) |inline_token| {
- return inline_token;
- }
-
- return self.while_token;
- }
-
- pub fn lastToken(self: *const While) TokenIndex {
- if (self.@"else") |@"else"| {
- return @"else".body.lastToken();
- }
-
- return self.body.lastToken();
- }
+ /// Points to the first token after the `|`. Will either be an identifier or
+ /// a `*` (with an identifier immediately after it).
+ payload_token: ?TokenIndex,
+ ast: Ast,
+
+ pub const Ast = struct {
+ /// If empty, this is an else case
+ values: []const Node.Index,
+ arrow_token: TokenIndex,
+ target_expr: Node.Index,
+ };
};
- pub const For = struct {
- base: Node = Node{ .tag = .For },
- label: ?TokenIndex,
- inline_token: ?TokenIndex,
- for_token: TokenIndex,
- array_expr: *Node,
- payload: *Node,
- body: *Node,
- @"else": ?*Else,
-
- pub fn iterate(self: *const For, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.array_expr;
- i -= 1;
-
- if (i < 1) return self.payload;
- i -= 1;
-
- if (i < 1) return self.body;
- i -= 1;
-
- if (self.@"else") |@"else"| {
- if (i < 1) return &@"else".base;
- i -= 1;
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const For) TokenIndex {
- if (self.label) |label| {
- return label;
- }
-
- if (self.inline_token) |inline_token| {
- return inline_token;
- }
-
- return self.for_token;
- }
-
- pub fn lastToken(self: *const For) TokenIndex {
- if (self.@"else") |@"else"| {
- return @"else".body.lastToken();
- }
-
- return self.body.lastToken();
- }
+ pub const Asm = struct {
+ ast: Ast,
+ volatile_token: ?TokenIndex,
+ first_clobber: ?TokenIndex,
+ outputs: []const Node.Index,
+ inputs: []const Node.Index,
+
+ pub const Ast = struct {
+ asm_token: TokenIndex,
+ template: Node.Index,
+ items: []const Node.Index,
+ rparen: TokenIndex,
+ };
};
- pub const If = struct {
- base: Node = Node{ .tag = .If },
- if_token: TokenIndex,
- condition: *Node,
- payload: ?*Node,
- body: *Node,
- @"else": ?*Else,
-
- pub fn iterate(self: *const If, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.condition;
- i -= 1;
-
- if (self.payload) |payload| {
- if (i < 1) return payload;
- i -= 1;
- }
-
- if (i < 1) return self.body;
- i -= 1;
-
- if (self.@"else") |@"else"| {
- if (i < 1) return &@"else".base;
- i -= 1;
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const If) TokenIndex {
- return self.if_token;
- }
-
- pub fn lastToken(self: *const If) TokenIndex {
- if (self.@"else") |@"else"| {
- return @"else".body.lastToken();
- }
+ pub const Call = struct {
+ ast: Ast,
+ async_token: ?TokenIndex,
- return self.body.lastToken();
- }
+ pub const Ast = struct {
+ lparen: TokenIndex,
+ fn_expr: Node.Index,
+ params: []const Node.Index,
+ };
};
+};
- pub const Catch = struct {
- base: Node = Node{ .tag = .Catch },
- op_token: TokenIndex,
- lhs: *Node,
- rhs: *Node,
- payload: ?*Node,
-
- pub fn iterate(self: *const Catch, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.lhs;
- i -= 1;
-
- if (self.payload) |payload| {
- if (i < 1) return payload;
- i -= 1;
- }
-
- if (i < 1) return self.rhs;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const Catch) TokenIndex {
- return self.lhs.firstToken();
- }
+pub const Error = struct {
+ tag: Tag,
+ token: TokenIndex,
+ extra: union {
+ none: void,
+ expected_tag: Token.Tag,
+ } = .{ .none = {} },
- pub fn lastToken(self: *const Catch) TokenIndex {
- return self.rhs.lastToken();
- }
+ pub const Tag = enum {
+ asterisk_after_ptr_deref,
+ decl_between_fields,
+ expected_block,
+ expected_block_or_assignment,
+ expected_block_or_expr,
+ expected_block_or_field,
+ expected_container_members,
+ expected_expr,
+ expected_expr_or_assignment,
+ expected_fn,
+ expected_inlinable,
+ expected_labelable,
+ expected_param_list,
+ expected_prefix_expr,
+ expected_primary_type_expr,
+ expected_pub_item,
+ expected_return_type,
+ expected_semi_or_else,
+ expected_semi_or_lbrace,
+ expected_statement,
+ expected_string_literal,
+ expected_suffix_op,
+ expected_type_expr,
+ expected_var_decl,
+ expected_var_decl_or_fn,
+ expected_loop_payload,
+ expected_container,
+ extra_align_qualifier,
+ extra_allowzero_qualifier,
+ extra_const_qualifier,
+ extra_volatile_qualifier,
+ invalid_align,
+ invalid_and,
+ invalid_bit_range,
+ invalid_token,
+ same_line_doc_comment,
+ unattached_doc_comment,
+
+ /// `expected_tag` is populated.
+ expected_token,
};
+};
- pub const SimpleInfixOp = struct {
- base: Node,
- op_token: TokenIndex,
- lhs: *Node,
- rhs: *Node,
-
- pub fn iterate(self: *const SimpleInfixOp, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.lhs;
- i -= 1;
+pub const Node = struct {
+ tag: Tag,
+ main_token: TokenIndex,
+ data: Data,
- if (i < 1) return self.rhs;
- i -= 1;
+ pub const Index = u32;
- return null;
- }
+ comptime {
+ // Goal is to keep this under one byte for efficiency.
+ assert(@sizeOf(Tag) == 1);
+ }
- pub fn firstToken(self: *const SimpleInfixOp) TokenIndex {
- return self.lhs.firstToken();
- }
+ /// Note: The FooComma/FooSemicolon variants exist to ease the implementation of
+ /// Tree.lastToken()
+ pub const Tag = enum {
+ /// sub_list[lhs...rhs]
+ root,
+ /// `usingnamespace lhs;`. rhs unused. main_token is `usingnamespace`.
+ @"usingnamespace",
+ /// lhs is test name token (must be string literal), if any.
+ /// rhs is the body node.
+ test_decl,
+ /// lhs is the index into extra_data.
+ /// rhs is the initialization expression, if any.
+ /// main_token is `var` or `const`.
+ global_var_decl,
+ /// `var a: x align(y) = rhs`
+ /// lhs is the index into extra_data.
+ /// main_token is `var` or `const`.
+ local_var_decl,
+ /// `var a: lhs = rhs`. lhs and rhs may be unused.
+ /// Can be local or global.
+ /// main_token is `var` or `const`.
+ simple_var_decl,
+ /// `var a align(lhs) = rhs`. lhs and rhs may be unused.
+ /// Can be local or global.
+ /// main_token is `var` or `const`.
+ aligned_var_decl,
+ /// lhs is the identifier token payload if any,
+ /// rhs is the deferred expression.
+ @"errdefer",
+ /// lhs is unused.
+ /// rhs is the deferred expression.
+ @"defer",
+ /// lhs catch rhs
+ /// lhs catch |err| rhs
+ /// main_token is the `catch` keyword.
+ /// payload is determined by looking at the next token after the `catch` keyword.
+ @"catch",
+ /// `lhs.a`. main_token is the dot. rhs is the identifier token index.
+ field_access,
+ /// `lhs.?`. main_token is the dot. rhs is the `?` token index.
+ unwrap_optional,
+ /// `lhs == rhs`. main_token is op.
+ equal_equal,
+ /// `lhs != rhs`. main_token is op.
+ bang_equal,
+ /// `lhs < rhs`. main_token is op.
+ less_than,
+ /// `lhs > rhs`. main_token is op.
+ greater_than,
+ /// `lhs <= rhs`. main_token is op.
+ less_or_equal,
+ /// `lhs >= rhs`. main_token is op.
+ greater_or_equal,
+ /// `lhs *= rhs`. main_token is op.
+ assign_mul,
+ /// `lhs /= rhs`. main_token is op.
+ assign_div,
+ /// `lhs *= rhs`. main_token is op.
+ assign_mod,
+ /// `lhs += rhs`. main_token is op.
+ assign_add,
+ /// `lhs -= rhs`. main_token is op.
+ assign_sub,
+ /// `lhs <<= rhs`. main_token is op.
+ assign_bit_shift_left,
+ /// `lhs >>= rhs`. main_token is op.
+ assign_bit_shift_right,
+ /// `lhs &= rhs`. main_token is op.
+ assign_bit_and,
+ /// `lhs ^= rhs`. main_token is op.
+ assign_bit_xor,
+ /// `lhs |= rhs`. main_token is op.
+ assign_bit_or,
+ /// `lhs *%= rhs`. main_token is op.
+ assign_mul_wrap,
+ /// `lhs +%= rhs`. main_token is op.
+ assign_add_wrap,
+ /// `lhs -%= rhs`. main_token is op.
+ assign_sub_wrap,
+ /// `lhs = rhs`. main_token is op.
+ assign,
+ /// `lhs || rhs`. main_token is the `||`.
+ merge_error_sets,
+ /// `lhs * rhs`. main_token is the `*`.
+ mul,
+ /// `lhs / rhs`. main_token is the `/`.
+ div,
+ /// `lhs % rhs`. main_token is the `%`.
+ mod,
+ /// `lhs ** rhs`. main_token is the `**`.
+ array_mult,
+ /// `lhs *% rhs`. main_token is the `*%`.
+ mul_wrap,
+ /// `lhs + rhs`. main_token is the `+`.
+ add,
+ /// `lhs - rhs`. main_token is the `-`.
+ sub,
+ /// `lhs ++ rhs`. main_token is the `++`.
+ array_cat,
+ /// `lhs +% rhs`. main_token is the `+%`.
+ add_wrap,
+ /// `lhs -% rhs`. main_token is the `-%`.
+ sub_wrap,
+ /// `lhs << rhs`. main_token is the `<<`.
+ bit_shift_left,
+ /// `lhs >> rhs`. main_token is the `>>`.
+ bit_shift_right,
+ /// `lhs & rhs`. main_token is the `&`.
+ bit_and,
+ /// `lhs ^ rhs`. main_token is the `^`.
+ bit_xor,
+ /// `lhs | rhs`. main_token is the `|`.
+ bit_or,
+ /// `lhs orelse rhs`. main_token is the `orelse`.
+ @"orelse",
+ /// `lhs and rhs`. main_token is the `and`.
+ bool_and,
+ /// `lhs or rhs`. main_token is the `or`.
+ bool_or,
+ /// `op lhs`. rhs unused. main_token is op.
+ bool_not,
+ /// `op lhs`. rhs unused. main_token is op.
+ negation,
+ /// `op lhs`. rhs unused. main_token is op.
+ bit_not,
+ /// `op lhs`. rhs unused. main_token is op.
+ negation_wrap,
+ /// `op lhs`. rhs unused. main_token is op.
+ address_of,
+ /// `op lhs`. rhs unused. main_token is op.
+ @"try",
+ /// `op lhs`. rhs unused. main_token is op.
+ @"await",
+ /// `?lhs`. rhs unused. main_token is the `?`.
+ optional_type,
+ /// `[lhs]rhs`. lhs can be omitted to make it a slice.
+ array_type,
+ /// `[lhs:a]b`. `array_type_sentinel[rhs]`.
+ array_type_sentinel,
+ /// `[*]align(lhs) rhs`. lhs can be omitted.
+ /// `*align(lhs) rhs`. lhs can be omitted.
+ /// `[]rhs`.
+ /// main_token is the asterisk if a pointer or the lbracket if a slice
+ /// main_token might be a ** token, which is shared with a parent/child
+ /// pointer type and may require special handling.
+ ptr_type_aligned,
+ /// `[*:lhs]rhs`. lhs can be omitted.
+ /// `*rhs`.
+ /// `[:lhs]rhs`.
+ /// main_token is the asterisk if a pointer or the lbracket if a slice
+ /// main_token might be a ** token, which is shared with a parent/child
+ /// pointer type and may require special handling.
+ ptr_type_sentinel,
+ /// lhs is index into ptr_type. rhs is the element type expression.
+ /// main_token is the asterisk if a pointer or the lbracket if a slice
+ /// main_token might be a ** token, which is shared with a parent/child
+ /// pointer type and may require special handling.
+ ptr_type,
+ /// lhs is index into ptr_type_bit_range. rhs is the element type expression.
+ /// main_token is the asterisk if a pointer or the lbracket if a slice
+ /// main_token might be a ** token, which is shared with a parent/child
+ /// pointer type and may require special handling.
+ ptr_type_bit_range,
+ /// `lhs[rhs..]`
+ /// main_token is the lbracket.
+ slice_open,
+ /// `lhs[b..c]`. rhs is index into Slice
+ /// main_token is the lbracket.
+ slice,
+ /// `lhs[b..c :d]`. rhs is index into SliceSentinel
+ /// main_token is the lbracket.
+ slice_sentinel,
+ /// `lhs.*`. rhs is unused.
+ deref,
+ /// `lhs[rhs]`.
+ array_access,
+ /// `lhs{rhs}`. rhs can be omitted.
+ array_init_one,
+ /// `lhs{rhs,}`. rhs can *not* be omitted
+ array_init_one_comma,
+ /// `.{lhs, rhs}`. lhs and rhs can be omitted.
+ array_init_dot_two,
+ /// Same as `array_init_dot_two` except there is known to be a trailing comma
+ /// before the final rbrace.
+ array_init_dot_two_comma,
+ /// `.{a, b}`. `sub_list[lhs..rhs]`.
+ array_init_dot,
+ /// Same as `array_init_dot` except there is known to be a trailing comma
+ /// before the final rbrace.
+ array_init_dot_comma,
+ /// `lhs{a, b}`. `sub_range_list[rhs]`. lhs can be omitted which means `.{a, b}`.
+ array_init,
+ /// Same as `array_init` except there is known to be a trailing comma
+ /// before the final rbrace.
+ array_init_comma,
+ /// `lhs{.a = rhs}`. rhs can be omitted making it empty.
+ /// main_token is the lbrace.
+ struct_init_one,
+ /// `lhs{.a = rhs,}`. rhs can *not* be omitted.
+ /// main_token is the lbrace.
+ struct_init_one_comma,
+ /// `.{.a = lhs, .b = rhs}`. lhs and rhs can be omitted.
+ /// main_token is the lbrace.
+ /// No trailing comma before the rbrace.
+ struct_init_dot_two,
+ /// Same as `struct_init_dot_two` except there is known to be a trailing comma
+ /// before the final rbrace.
+ struct_init_dot_two_comma,
+ /// `.{.a = b, .c = d}`. `sub_list[lhs..rhs]`.
+ /// main_token is the lbrace.
+ struct_init_dot,
+ /// Same as `struct_init_dot` except there is known to be a trailing comma
+ /// before the final rbrace.
+ struct_init_dot_comma,
+ /// `lhs{.a = b, .c = d}`. `sub_range_list[rhs]`.
+ /// lhs can be omitted which means `.{.a = b, .c = d}`.
+ /// main_token is the lbrace.
+ struct_init,
+ /// Same as `struct_init` except there is known to be a trailing comma
+ /// before the final rbrace.
+ struct_init_comma,
+ /// `lhs(rhs)`. rhs can be omitted.
+ /// main_token is the lparen.
+ call_one,
+ /// `lhs(rhs,)`. rhs can be omitted.
+ /// main_token is the lparen.
+ call_one_comma,
+ /// `async lhs(rhs)`. rhs can be omitted.
+ async_call_one,
+ /// `async lhs(rhs,)`.
+ async_call_one_comma,
+ /// `lhs(a, b, c)`. `SubRange[rhs]`.
+ /// main_token is the `(`.
+ call,
+ /// `lhs(a, b, c,)`. `SubRange[rhs]`.
+ /// main_token is the `(`.
+ call_comma,
+ /// `async lhs(a, b, c)`. `SubRange[rhs]`.
+ /// main_token is the `(`.
+ async_call,
+ /// `async lhs(a, b, c,)`. `SubRange[rhs]`.
+ /// main_token is the `(`.
+ async_call_comma,
+ /// `switch(lhs) {}`. `SubRange[rhs]`.
+ @"switch",
+ /// Same as switch except there is known to be a trailing comma
+ /// before the final rbrace
+ switch_comma,
+ /// `lhs => rhs`. If lhs is omitted it means `else`.
+ /// main_token is the `=>`
+ switch_case_one,
+ /// `a, b, c => rhs`. `SubRange[lhs]`.
+ /// main_token is the `=>`
+ switch_case,
+ /// `lhs...rhs`.
+ switch_range,
+ /// `while (lhs) rhs`.
+ /// `while (lhs) |x| rhs`.
+ while_simple,
+ /// `while (lhs) : (a) b`. `WhileCont[rhs]`.
+ /// `while (lhs) : (a) b`. `WhileCont[rhs]`.
+ while_cont,
+ /// `while (lhs) : (a) b else c`. `While[rhs]`.
+ /// `while (lhs) |x| : (a) b else c`. `While[rhs]`.
+ /// `while (lhs) |x| : (a) b else |y| c`. `While[rhs]`.
+ @"while",
+ /// `for (lhs) rhs`.
+ for_simple,
+ /// `for (lhs) a else b`. `if_list[rhs]`.
+ @"for",
+ /// `if (lhs) rhs`.
+ /// `if (lhs) |a| rhs`.
+ if_simple,
+ /// `if (lhs) a else b`. `If[rhs]`.
+ /// `if (lhs) |x| a else b`. `If[rhs]`.
+ /// `if (lhs) |x| a else |y| b`. `If[rhs]`.
+ @"if",
+ /// `suspend lhs`. lhs can be omitted. rhs is unused.
+ @"suspend",
+ /// `resume lhs`. rhs is unused.
+ @"resume",
+ /// `continue`. lhs is token index of label if any. rhs is unused.
+ @"continue",
+ /// `break :lhs rhs`
+ /// both lhs and rhs may be omitted.
+ @"break",
+ /// `return lhs`. lhs can be omitted. rhs is unused.
+ @"return",
+ /// `fn(a: lhs) rhs`. lhs can be omitted.
+ /// anytype and ... parameters are omitted from the AST tree.
+ /// main_token is the `fn` keyword.
+ /// extern function declarations use this tag.
+ fn_proto_simple,
+ /// `fn(a: b, c: d) rhs`. `sub_range_list[lhs]`.
+ /// anytype and ... parameters are omitted from the AST tree.
+ /// main_token is the `fn` keyword.
+ /// extern function declarations use this tag.
+ fn_proto_multi,
+ /// `fn(a: b) rhs linksection(e) callconv(f)`. `FnProtoOne[lhs]`.
+ /// zero or one parameters.
+ /// anytype and ... parameters are omitted from the AST tree.
+ /// main_token is the `fn` keyword.
+ /// extern function declarations use this tag.
+ fn_proto_one,
+ /// `fn(a: b, c: d) rhs linksection(e) callconv(f)`. `FnProto[lhs]`.
+ /// anytype and ... parameters are omitted from the AST tree.
+ /// main_token is the `fn` keyword.
+ /// extern function declarations use this tag.
+ fn_proto,
+ /// lhs is the fn_proto.
+ /// rhs is the function body block.
+ /// Note that extern function declarations use the fn_proto tags rather
+ /// than this one.
+ fn_decl,
+ /// `anyframe->rhs`. main_token is `anyframe`. `lhs` is arrow token index.
+ anyframe_type,
+ /// Both lhs and rhs unused.
+ anyframe_literal,
+ /// Both lhs and rhs unused.
+ char_literal,
+ /// Both lhs and rhs unused.
+ integer_literal,
+ /// Both lhs and rhs unused.
+ float_literal,
+ /// Both lhs and rhs unused.
+ false_literal,
+ /// Both lhs and rhs unused.
+ true_literal,
+ /// Both lhs and rhs unused.
+ null_literal,
+ /// Both lhs and rhs unused.
+ undefined_literal,
+ /// Both lhs and rhs unused.
+ unreachable_literal,
+ /// Both lhs and rhs unused.
+ /// Most identifiers will not have explicit AST nodes, however for expressions
+ /// which could be one of many different kinds of AST nodes, there will be an
+ /// identifier AST node for it.
+ identifier,
+ /// lhs is the dot token index, rhs unused, main_token is the identifier.
+ enum_literal,
+ /// main_token is the string literal token
+ /// Both lhs and rhs unused.
+ string_literal,
+ /// main_token is the first token index (redundant with lhs)
+ /// lhs is the first token index; rhs is the last token index.
+ /// Could be a series of multiline_string_literal_line tokens, or a single
+ /// string_literal token.
+ multiline_string_literal,
+ /// `(lhs)`. main_token is the `(`; rhs is the token index of the `)`.
+ grouped_expression,
+ /// `@a(lhs, rhs)`. lhs and rhs may be omitted.
+ /// main_token is the builtin token.
+ builtin_call_two,
+ /// Same as builtin_call_two but there is known to be a trailing comma before the rparen.
+ builtin_call_two_comma,
+ /// `@a(b, c)`. `sub_list[lhs..rhs]`.
+ /// main_token is the builtin token.
+ builtin_call,
+ /// Same as builtin_call but there is known to be a trailing comma before the rparen.
+ builtin_call_comma,
+ /// `error{a, b}`.
+ /// rhs is the rbrace, lhs is unused.
+ error_set_decl,
+ /// `struct {}`, `union {}`, `opaque {}`, `enum {}`. `extra_data[lhs..rhs]`.
+ /// main_token is `struct`, `union`, `opaque`, `enum` keyword.
+ container_decl,
+ /// Same as ContainerDecl but there is known to be a trailing comma
+ /// or semicolon before the rbrace.
+ container_decl_trailing,
+ /// `struct {lhs, rhs}`, `union {lhs, rhs}`, `opaque {lhs, rhs}`, `enum {lhs, rhs}`.
+ /// lhs or rhs can be omitted.
+ /// main_token is `struct`, `union`, `opaque`, `enum` keyword.
+ container_decl_two,
+ /// Same as ContainerDeclTwo except there is known to be a trailing comma
+ /// or semicolon before the rbrace.
+ container_decl_two_trailing,
+ /// `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`.
+ container_decl_arg,
+ /// Same as container_decl_arg but there is known to be a trailing
+ /// comma or semicolon before the rbrace.
+ container_decl_arg_trailing,
+ /// `union(enum) {}`. `sub_list[lhs..rhs]`.
+ /// Note that tagged unions with explicitly provided enums are represented
+ /// by `container_decl_arg`.
+ tagged_union,
+ /// Same as tagged_union but there is known to be a trailing comma
+ /// or semicolon before the rbrace.
+ tagged_union_trailing,
+ /// `union(enum) {lhs, rhs}`. lhs or rhs may be omitted.
+ /// Note that tagged unions with explicitly provided enums are represented
+ /// by `container_decl_arg`.
+ tagged_union_two,
+ /// Same as tagged_union_two but there is known to be a trailing comma
+ /// or semicolon before the rbrace.
+ tagged_union_two_trailing,
+ /// `union(enum(lhs)) {}`. `SubRange[rhs]`.
+ tagged_union_enum_tag,
+ /// Same as tagged_union_enum_tag but there is known to be a trailing comma
+ /// or semicolon before the rbrace.
+ tagged_union_enum_tag_trailing,
+ /// `a: lhs = rhs,`. lhs and rhs can be omitted.
+ /// main_token is the field name identifier.
+ /// lastToken() does not include the possible trailing comma.
+ container_field_init,
+ /// `a: lhs align(rhs),`. rhs can be omitted.
+ /// main_token is the field name identifier.
+ /// lastToken() does not include the possible trailing comma.
+ container_field_align,
+ /// `a: lhs align(c) = d,`. `container_field_list[rhs]`.
+ /// main_token is the field name identifier.
+ /// lastToken() does not include the possible trailing comma.
+ container_field,
+ /// `anytype`. both lhs and rhs unused.
+ /// Used by `ContainerField`.
+ @"anytype",
+ /// `comptime lhs`. rhs unused.
+ @"comptime",
+ /// `nosuspend lhs`. rhs unused.
+ @"nosuspend",
+ /// `{lhs rhs}`. rhs or lhs can be omitted.
+ /// main_token points at the lbrace.
+ block_two,
+ /// Same as block_two but there is known to be a semicolon before the rbrace.
+ block_two_semicolon,
+ /// `{}`. `sub_list[lhs..rhs]`.
+ /// main_token points at the lbrace.
+ block,
+ /// Same as block but there is known to be a semicolon before the rbrace.
+ block_semicolon,
+ /// `asm(lhs)`. rhs is the token index of the rparen.
+ asm_simple,
+ /// `asm(lhs, a)`. `Asm[rhs]`.
+ @"asm",
+ /// `[a] "b" (c)`. lhs is 0, rhs is token index of the rparen.
+ /// `[a] "b" (-> lhs)`. rhs is token index of the rparen.
+ /// main_token is `a`.
+ asm_output,
+ /// `[a] "b" (lhs)`. rhs is token index of the rparen.
+ /// main_token is `a`.
+ asm_input,
+ /// `error.a`. lhs is token index of `.`. rhs is token index of `a`.
+ error_value,
+ /// `lhs!rhs`. main_token is the `!`.
+ error_union,
+
+ pub fn isContainerField(tag: Tag) bool {
+ return switch (tag) {
+ .container_field_init,
+ .container_field_align,
+ .container_field,
+ => true,
- pub fn lastToken(self: *const SimpleInfixOp) TokenIndex {
- return self.rhs.lastToken();
+ else => false,
+ };
}
};
- pub const SimplePrefixOp = struct {
- base: Node,
- op_token: TokenIndex,
- rhs: *Node,
-
- const Self = @This();
-
- pub fn iterate(self: *const Self, index: usize) ?*Node {
- if (index == 0) return self.rhs;
- return null;
- }
-
- pub fn firstToken(self: *const Self) TokenIndex {
- return self.op_token;
- }
-
- pub fn lastToken(self: *const Self) TokenIndex {
- return self.rhs.lastToken();
- }
+ pub const Data = struct {
+ lhs: Index,
+ rhs: Index,
};
- pub const ArrayType = struct {
- base: Node = Node{ .tag = .ArrayType },
- op_token: TokenIndex,
- rhs: *Node,
- len_expr: *Node,
-
- pub fn iterate(self: *const ArrayType, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.len_expr;
- i -= 1;
-
- if (i < 1) return self.rhs;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const ArrayType) TokenIndex {
- return self.op_token;
- }
-
- pub fn lastToken(self: *const ArrayType) TokenIndex {
- return self.rhs.lastToken();
- }
+ pub const LocalVarDecl = struct {
+ type_node: Index,
+ align_node: Index,
};
pub const ArrayTypeSentinel = struct {
- base: Node = Node{ .tag = .ArrayTypeSentinel },
- op_token: TokenIndex,
- rhs: *Node,
- len_expr: *Node,
- sentinel: *Node,
-
- pub fn iterate(self: *const ArrayTypeSentinel, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.len_expr;
- i -= 1;
-
- if (i < 1) return self.sentinel;
- i -= 1;
-
- if (i < 1) return self.rhs;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const ArrayTypeSentinel) TokenIndex {
- return self.op_token;
- }
-
- pub fn lastToken(self: *const ArrayTypeSentinel) TokenIndex {
- return self.rhs.lastToken();
- }
+ elem_type: Index,
+ sentinel: Index,
};
pub const PtrType = struct {
- base: Node = Node{ .tag = .PtrType },
- op_token: TokenIndex,
- rhs: *Node,
- /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
- /// one of these possibly-null things. Then we have them directly follow the PtrType in memory.
- ptr_info: PtrInfo = .{},
-
- pub fn iterate(self: *const PtrType, index: usize) ?*Node {
- var i = index;
-
- if (self.ptr_info.sentinel) |sentinel| {
- if (i < 1) return sentinel;
- i -= 1;
- }
-
- if (self.ptr_info.align_info) |align_info| {
- if (i < 1) return align_info.node;
- i -= 1;
- }
-
- if (i < 1) return self.rhs;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const PtrType) TokenIndex {
- return self.op_token;
- }
-
- pub fn lastToken(self: *const PtrType) TokenIndex {
- return self.rhs.lastToken();
- }
- };
-
- pub const SliceType = struct {
- base: Node = Node{ .tag = .SliceType },
- op_token: TokenIndex,
- rhs: *Node,
- /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
- /// one of these possibly-null things. Then we have them directly follow the SliceType in memory.
- ptr_info: PtrInfo = .{},
-
- pub fn iterate(self: *const SliceType, index: usize) ?*Node {
- var i = index;
-
- if (self.ptr_info.sentinel) |sentinel| {
- if (i < 1) return sentinel;
- i -= 1;
- }
-
- if (self.ptr_info.align_info) |align_info| {
- if (i < 1) return align_info.node;
- i -= 1;
- }
-
- if (i < 1) return self.rhs;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const SliceType) TokenIndex {
- return self.op_token;
- }
-
- pub fn lastToken(self: *const SliceType) TokenIndex {
- return self.rhs.lastToken();
- }
- };
-
- pub const FieldInitializer = struct {
- base: Node = Node{ .tag = .FieldInitializer },
- period_token: TokenIndex,
- name_token: TokenIndex,
- expr: *Node,
-
- pub fn iterate(self: *const FieldInitializer, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.expr;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const FieldInitializer) TokenIndex {
- return self.period_token;
- }
-
- pub fn lastToken(self: *const FieldInitializer) TokenIndex {
- return self.expr.lastToken();
- }
- };
-
- /// Elements occur directly in memory after ArrayInitializer.
- pub const ArrayInitializer = struct {
- base: Node = Node{ .tag = .ArrayInitializer },
- rtoken: TokenIndex,
- list_len: NodeIndex,
- lhs: *Node,
-
- /// After this the caller must initialize the fields_and_decls list.
- pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*ArrayInitializer {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(ArrayInitializer), sizeInBytes(list_len));
- return @ptrCast(*ArrayInitializer, bytes.ptr);
- }
-
- pub fn free(self: *ArrayInitializer, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const ArrayInitializer, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.lhs;
- i -= 1;
-
- if (i < self.list_len) return self.listConst()[i];
- i -= self.list_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const ArrayInitializer) TokenIndex {
- return self.lhs.firstToken();
- }
-
- pub fn lastToken(self: *const ArrayInitializer) TokenIndex {
- return self.rtoken;
- }
-
- pub fn list(self: *ArrayInitializer) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(ArrayInitializer);
- return @ptrCast([*]*Node, decls_start)[0..self.list_len];
- }
-
- pub fn listConst(self: *const ArrayInitializer) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ArrayInitializer);
- return @ptrCast([*]const *Node, decls_start)[0..self.list_len];
- }
-
- fn sizeInBytes(list_len: NodeIndex) usize {
- return @sizeOf(ArrayInitializer) + @sizeOf(*Node) * @as(usize, list_len);
- }
+ sentinel: Index,
+ align_node: Index,
};
- /// Elements occur directly in memory after ArrayInitializerDot.
- pub const ArrayInitializerDot = struct {
- base: Node = Node{ .tag = .ArrayInitializerDot },
- dot: TokenIndex,
- rtoken: TokenIndex,
- list_len: NodeIndex,
-
- /// After this the caller must initialize the fields_and_decls list.
- pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*ArrayInitializerDot {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(ArrayInitializerDot), sizeInBytes(list_len));
- return @ptrCast(*ArrayInitializerDot, bytes.ptr);
- }
-
- pub fn free(self: *ArrayInitializerDot, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const ArrayInitializerDot, index: usize) ?*Node {
- var i = index;
-
- if (i < self.list_len) return self.listConst()[i];
- i -= self.list_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const ArrayInitializerDot) TokenIndex {
- return self.dot;
- }
-
- pub fn lastToken(self: *const ArrayInitializerDot) TokenIndex {
- return self.rtoken;
- }
-
- pub fn list(self: *ArrayInitializerDot) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(ArrayInitializerDot);
- return @ptrCast([*]*Node, decls_start)[0..self.list_len];
- }
-
- pub fn listConst(self: *const ArrayInitializerDot) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ArrayInitializerDot);
- return @ptrCast([*]const *Node, decls_start)[0..self.list_len];
- }
-
- fn sizeInBytes(list_len: NodeIndex) usize {
- return @sizeOf(ArrayInitializerDot) + @sizeOf(*Node) * @as(usize, list_len);
- }
+ pub const PtrTypeBitRange = struct {
+ sentinel: Index,
+ align_node: Index,
+ bit_range_start: Index,
+ bit_range_end: Index,
};
- /// Elements occur directly in memory after StructInitializer.
- pub const StructInitializer = struct {
- base: Node = Node{ .tag = .StructInitializer },
- rtoken: TokenIndex,
- list_len: NodeIndex,
- lhs: *Node,
-
- /// After this the caller must initialize the fields_and_decls list.
- pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*StructInitializer {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(StructInitializer), sizeInBytes(list_len));
- return @ptrCast(*StructInitializer, bytes.ptr);
- }
-
- pub fn free(self: *StructInitializer, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const StructInitializer, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.lhs;
- i -= 1;
-
- if (i < self.list_len) return self.listConst()[i];
- i -= self.list_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const StructInitializer) TokenIndex {
- return self.lhs.firstToken();
- }
-
- pub fn lastToken(self: *const StructInitializer) TokenIndex {
- return self.rtoken;
- }
-
- pub fn list(self: *StructInitializer) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(StructInitializer);
- return @ptrCast([*]*Node, decls_start)[0..self.list_len];
- }
-
- pub fn listConst(self: *const StructInitializer) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(StructInitializer);
- return @ptrCast([*]const *Node, decls_start)[0..self.list_len];
- }
-
- fn sizeInBytes(list_len: NodeIndex) usize {
- return @sizeOf(StructInitializer) + @sizeOf(*Node) * @as(usize, list_len);
- }
- };
-
- /// Elements occur directly in memory after StructInitializerDot.
- pub const StructInitializerDot = struct {
- base: Node = Node{ .tag = .StructInitializerDot },
- dot: TokenIndex,
- rtoken: TokenIndex,
- list_len: NodeIndex,
-
- /// After this the caller must initialize the fields_and_decls list.
- pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*StructInitializerDot {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(StructInitializerDot), sizeInBytes(list_len));
- return @ptrCast(*StructInitializerDot, bytes.ptr);
- }
-
- pub fn free(self: *StructInitializerDot, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const StructInitializerDot, index: usize) ?*Node {
- var i = index;
-
- if (i < self.list_len) return self.listConst()[i];
- i -= self.list_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const StructInitializerDot) TokenIndex {
- return self.dot;
- }
-
- pub fn lastToken(self: *const StructInitializerDot) TokenIndex {
- return self.rtoken;
- }
-
- pub fn list(self: *StructInitializerDot) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(StructInitializerDot);
- return @ptrCast([*]*Node, decls_start)[0..self.list_len];
- }
-
- pub fn listConst(self: *const StructInitializerDot) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(StructInitializerDot);
- return @ptrCast([*]const *Node, decls_start)[0..self.list_len];
- }
-
- fn sizeInBytes(list_len: NodeIndex) usize {
- return @sizeOf(StructInitializerDot) + @sizeOf(*Node) * @as(usize, list_len);
- }
+ pub const SubRange = struct {
+ /// Index into sub_list.
+ start: Index,
+ /// Index into sub_list.
+ end: Index,
};
- /// Parameter nodes directly follow Call in memory.
- pub const Call = struct {
- base: Node = Node{ .tag = .Call },
- rtoken: TokenIndex,
- lhs: *Node,
- params_len: NodeIndex,
- async_token: ?TokenIndex,
-
- /// After this the caller must initialize the fields_and_decls list.
- pub fn alloc(allocator: *mem.Allocator, params_len: NodeIndex) !*Call {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(Call), sizeInBytes(params_len));
- return @ptrCast(*Call, bytes.ptr);
- }
-
- pub fn free(self: *Call, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const Call, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.lhs;
- i -= 1;
-
- if (i < self.params_len) return self.paramsConst()[i];
- i -= self.params_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const Call) TokenIndex {
- if (self.async_token) |async_token| return async_token;
- return self.lhs.firstToken();
- }
-
- pub fn lastToken(self: *const Call) TokenIndex {
- return self.rtoken;
- }
-
- pub fn params(self: *Call) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(Call);
- return @ptrCast([*]*Node, decls_start)[0..self.params_len];
- }
-
- pub fn paramsConst(self: *const Call) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Call);
- return @ptrCast([*]const *Node, decls_start)[0..self.params_len];
- }
-
- fn sizeInBytes(params_len: NodeIndex) usize {
- return @sizeOf(Call) + @sizeOf(*Node) * @as(usize, params_len);
- }
+ pub const If = struct {
+ then_expr: Index,
+ else_expr: Index,
};
- pub const ArrayAccess = struct {
- base: Node = Node{ .tag = .ArrayAccess },
- rtoken: TokenIndex,
- lhs: *Node,
- index_expr: *Node,
-
- pub fn iterate(self: *const ArrayAccess, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.lhs;
- i -= 1;
-
- if (i < 1) return self.index_expr;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const ArrayAccess) TokenIndex {
- return self.lhs.firstToken();
- }
-
- pub fn lastToken(self: *const ArrayAccess) TokenIndex {
- return self.rtoken;
- }
+ pub const ContainerField = struct {
+ value_expr: Index,
+ align_expr: Index,
};
- pub const SimpleSuffixOp = struct {
- base: Node,
- rtoken: TokenIndex,
- lhs: *Node,
-
- pub fn iterate(self: *const SimpleSuffixOp, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.lhs;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const SimpleSuffixOp) TokenIndex {
- return self.lhs.firstToken();
- }
-
- pub fn lastToken(self: *const SimpleSuffixOp) TokenIndex {
- return self.rtoken;
- }
+ pub const GlobalVarDecl = struct {
+ type_node: Index,
+ align_node: Index,
+ section_node: Index,
};
pub const Slice = struct {
- base: Node = Node{ .tag = .Slice },
- rtoken: TokenIndex,
- lhs: *Node,
- start: *Node,
- end: ?*Node,
- sentinel: ?*Node,
-
- pub fn iterate(self: *const Slice, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.lhs;
- i -= 1;
-
- if (i < 1) return self.start;
- i -= 1;
-
- if (self.end) |end| {
- if (i < 1) return end;
- i -= 1;
- }
- if (self.sentinel) |sentinel| {
- if (i < 1) return sentinel;
- i -= 1;
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const Slice) TokenIndex {
- return self.lhs.firstToken();
- }
-
- pub fn lastToken(self: *const Slice) TokenIndex {
- return self.rtoken;
- }
- };
-
- pub const GroupedExpression = struct {
- base: Node = Node{ .tag = .GroupedExpression },
- lparen: TokenIndex,
- expr: *Node,
- rparen: TokenIndex,
-
- pub fn iterate(self: *const GroupedExpression, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.expr;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const GroupedExpression) TokenIndex {
- return self.lparen;
- }
-
- pub fn lastToken(self: *const GroupedExpression) TokenIndex {
- return self.rparen;
- }
+ start: Index,
+ end: Index,
};
- /// Trailed in memory by possibly many things, with each optional thing
- /// determined by a bit in `trailer_flags`.
- /// Can be: return, break, continue
- pub const ControlFlowExpression = struct {
- base: Node,
- trailer_flags: TrailerFlags,
- ltoken: TokenIndex,
-
- pub const TrailerFlags = std.meta.TrailerFlags(struct {
- rhs: *Node,
- label: TokenIndex,
- });
-
- pub const RequiredFields = struct {
- tag: Tag,
- ltoken: TokenIndex,
- };
-
- pub fn getRHS(self: *const ControlFlowExpression) ?*Node {
- return self.getTrailer(.rhs);
- }
-
- pub fn setRHS(self: *ControlFlowExpression, value: *Node) void {
- self.setTrailer(.rhs, value);
- }
-
- pub fn getLabel(self: *const ControlFlowExpression) ?TokenIndex {
- return self.getTrailer(.label);
- }
-
- pub fn setLabel(self: *ControlFlowExpression, value: TokenIndex) void {
- self.setTrailer(.label, value);
- }
-
- fn getTrailer(self: *const ControlFlowExpression, comptime field: TrailerFlags.FieldEnum) ?TrailerFlags.Field(field) {
- const trailers_start = @ptrCast([*]const u8, self) + @sizeOf(ControlFlowExpression);
- return self.trailer_flags.get(trailers_start, field);
- }
-
- fn setTrailer(self: *ControlFlowExpression, comptime field: TrailerFlags.FieldEnum, value: TrailerFlags.Field(field)) void {
- const trailers_start = @ptrCast([*]u8, self) + @sizeOf(ControlFlowExpression);
- self.trailer_flags.set(trailers_start, field, value);
- }
-
- pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: TrailerFlags.InitStruct) !*ControlFlowExpression {
- const trailer_flags = TrailerFlags.init(trailers);
- const bytes = try allocator.alignedAlloc(u8, @alignOf(ControlFlowExpression), sizeInBytes(trailer_flags));
- const ctrl_flow_expr = @ptrCast(*ControlFlowExpression, bytes.ptr);
- ctrl_flow_expr.* = .{
- .base = .{ .tag = required.tag },
- .trailer_flags = trailer_flags,
- .ltoken = required.ltoken,
- };
- const trailers_start = bytes.ptr + @sizeOf(ControlFlowExpression);
- trailer_flags.setMany(trailers_start, trailers);
- return ctrl_flow_expr;
- }
-
- pub fn destroy(self: *ControlFlowExpression, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const ControlFlowExpression, index: usize) ?*Node {
- var i = index;
-
- if (self.getRHS()) |rhs| {
- if (i < 1) return rhs;
- i -= 1;
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const ControlFlowExpression) TokenIndex {
- return self.ltoken;
- }
-
- pub fn lastToken(self: *const ControlFlowExpression) TokenIndex {
- if (self.getRHS()) |rhs| {
- return rhs.lastToken();
- }
-
- if (self.getLabel()) |label| {
- return label;
- }
-
- return self.ltoken;
- }
-
- fn sizeInBytes(trailer_flags: TrailerFlags) usize {
- return @sizeOf(ControlFlowExpression) + trailer_flags.sizeInBytes();
- }
+ pub const SliceSentinel = struct {
+ start: Index,
+ end: Index,
+ sentinel: Index,
};
- pub const Suspend = struct {
- base: Node = Node{ .tag = .Suspend },
- suspend_token: TokenIndex,
- body: ?*Node,
-
- pub fn iterate(self: *const Suspend, index: usize) ?*Node {
- var i = index;
-
- if (self.body) |body| {
- if (i < 1) return body;
- i -= 1;
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const Suspend) TokenIndex {
- return self.suspend_token;
- }
-
- pub fn lastToken(self: *const Suspend) TokenIndex {
- if (self.body) |body| {
- return body.lastToken();
- }
-
- return self.suspend_token;
- }
+ pub const While = struct {
+ cont_expr: Index,
+ then_expr: Index,
+ else_expr: Index,
};
- pub const EnumLiteral = struct {
- base: Node = Node{ .tag = .EnumLiteral },
- dot: TokenIndex,
- name: TokenIndex,
-
- pub fn iterate(self: *const EnumLiteral, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const EnumLiteral) TokenIndex {
- return self.dot;
- }
-
- pub fn lastToken(self: *const EnumLiteral) TokenIndex {
- return self.name;
- }
+ pub const WhileCont = struct {
+ cont_expr: Index,
+ then_expr: Index,
};
- /// Parameters are in memory following BuiltinCall.
- pub const BuiltinCall = struct {
- base: Node = Node{ .tag = .BuiltinCall },
- params_len: NodeIndex,
- builtin_token: TokenIndex,
- rparen_token: TokenIndex,
-
- /// After this the caller must initialize the fields_and_decls list.
- pub fn alloc(allocator: *mem.Allocator, params_len: NodeIndex) !*BuiltinCall {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(BuiltinCall), sizeInBytes(params_len));
- return @ptrCast(*BuiltinCall, bytes.ptr);
- }
-
- pub fn free(self: *BuiltinCall, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const BuiltinCall, index: usize) ?*Node {
- var i = index;
-
- if (i < self.params_len) return self.paramsConst()[i];
- i -= self.params_len;
-
- return null;
- }
-
- pub fn firstToken(self: *const BuiltinCall) TokenIndex {
- return self.builtin_token;
- }
-
- pub fn lastToken(self: *const BuiltinCall) TokenIndex {
- return self.rparen_token;
- }
-
- pub fn params(self: *BuiltinCall) []*Node {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(BuiltinCall);
- return @ptrCast([*]*Node, decls_start)[0..self.params_len];
- }
-
- pub fn paramsConst(self: *const BuiltinCall) []const *Node {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(BuiltinCall);
- return @ptrCast([*]const *Node, decls_start)[0..self.params_len];
- }
-
- fn sizeInBytes(params_len: NodeIndex) usize {
- return @sizeOf(BuiltinCall) + @sizeOf(*Node) * @as(usize, params_len);
- }
+ pub const FnProtoOne = struct {
+ /// Populated if there is exactly 1 parameter. Otherwise there are 0 parameters.
+ param: Index,
+ /// Populated if align(A) is present.
+ align_expr: Index,
+ /// Populated if linksection(A) is present.
+ section_expr: Index,
+ /// Populated if callconv(A) is present.
+ callconv_expr: Index,
};
- /// The string literal tokens appear directly in memory after MultilineStringLiteral.
- pub const MultilineStringLiteral = struct {
- base: Node = Node{ .tag = .MultilineStringLiteral },
- lines_len: TokenIndex,
-
- /// After this the caller must initialize the lines list.
- pub fn alloc(allocator: *mem.Allocator, lines_len: NodeIndex) !*MultilineStringLiteral {
- const bytes = try allocator.alignedAlloc(u8, @alignOf(MultilineStringLiteral), sizeInBytes(lines_len));
- return @ptrCast(*MultilineStringLiteral, bytes.ptr);
- }
-
- pub fn free(self: *MultilineStringLiteral, allocator: *mem.Allocator) void {
- const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.lines_len)];
- allocator.free(bytes);
- }
-
- pub fn iterate(self: *const MultilineStringLiteral, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const MultilineStringLiteral) TokenIndex {
- return self.linesConst()[0];
- }
-
- pub fn lastToken(self: *const MultilineStringLiteral) TokenIndex {
- return self.linesConst()[self.lines_len - 1];
- }
-
- pub fn lines(self: *MultilineStringLiteral) []TokenIndex {
- const decls_start = @ptrCast([*]u8, self) + @sizeOf(MultilineStringLiteral);
- return @ptrCast([*]TokenIndex, decls_start)[0..self.lines_len];
- }
-
- pub fn linesConst(self: *const MultilineStringLiteral) []const TokenIndex {
- const decls_start = @ptrCast([*]const u8, self) + @sizeOf(MultilineStringLiteral);
- return @ptrCast([*]const TokenIndex, decls_start)[0..self.lines_len];
- }
-
- fn sizeInBytes(lines_len: NodeIndex) usize {
- return @sizeOf(MultilineStringLiteral) + @sizeOf(TokenIndex) * @as(usize, lines_len);
- }
+ pub const FnProto = struct {
+ params_start: Index,
+ params_end: Index,
+ /// Populated if align(A) is present.
+ align_expr: Index,
+ /// Populated if linksection(A) is present.
+ section_expr: Index,
+ /// Populated if callconv(A) is present.
+ callconv_expr: Index,
};
pub const Asm = struct {
- base: Node = Node{ .tag = .Asm },
- asm_token: TokenIndex,
+ items_start: Index,
+ items_end: Index,
+ /// Needed to make lastToken() work.
rparen: TokenIndex,
- volatile_token: ?TokenIndex,
- template: *Node,
- outputs: []Output,
- inputs: []Input,
- /// A clobber node must be a StringLiteral or MultilineStringLiteral.
- clobbers: []*Node,
-
- pub const Output = struct {
- lbracket: TokenIndex,
- symbolic_name: *Node,
- constraint: *Node,
- kind: Kind,
- rparen: TokenIndex,
-
- pub const Kind = union(enum) {
- Variable: *OneToken,
- Return: *Node,
- };
-
- pub fn iterate(self: *const Output, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.symbolic_name;
- i -= 1;
-
- if (i < 1) return self.constraint;
- i -= 1;
-
- switch (self.kind) {
- .Variable => |variable_name| {
- if (i < 1) return &variable_name.base;
- i -= 1;
- },
- .Return => |return_type| {
- if (i < 1) return return_type;
- i -= 1;
- },
- }
-
- return null;
- }
-
- pub fn firstToken(self: *const Output) TokenIndex {
- return self.lbracket;
- }
-
- pub fn lastToken(self: *const Output) TokenIndex {
- return self.rparen;
- }
- };
-
- pub const Input = struct {
- lbracket: TokenIndex,
- symbolic_name: *Node,
- constraint: *Node,
- expr: *Node,
- rparen: TokenIndex,
-
- pub fn iterate(self: *const Input, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.symbolic_name;
- i -= 1;
-
- if (i < 1) return self.constraint;
- i -= 1;
-
- if (i < 1) return self.expr;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const Input) TokenIndex {
- return self.lbracket;
- }
-
- pub fn lastToken(self: *const Input) TokenIndex {
- return self.rparen;
- }
- };
-
- pub fn iterate(self: *const Asm, index: usize) ?*Node {
- var i = index;
-
- if (i < self.outputs.len * 3) switch (i % 3) {
- 0 => return self.outputs[i / 3].symbolic_name,
- 1 => return self.outputs[i / 3].constraint,
- 2 => switch (self.outputs[i / 3].kind) {
- .Variable => |variable_name| return &variable_name.base,
- .Return => |return_type| return return_type,
- },
- else => unreachable,
- };
- i -= self.outputs.len * 3;
-
- if (i < self.inputs.len * 3) switch (i % 3) {
- 0 => return self.inputs[i / 3].symbolic_name,
- 1 => return self.inputs[i / 3].constraint,
- 2 => return self.inputs[i / 3].expr,
- else => unreachable,
- };
- i -= self.inputs.len * 3;
-
- return null;
- }
-
- pub fn firstToken(self: *const Asm) TokenIndex {
- return self.asm_token;
- }
-
- pub fn lastToken(self: *const Asm) TokenIndex {
- return self.rparen;
- }
- };
-
- /// TODO remove from the Node base struct
- /// TODO actually maybe remove entirely in favor of iterating backward from Node.firstToken()
- /// and forwards to find same-line doc comments.
- pub const DocComment = struct {
- base: Node = Node{ .tag = .DocComment },
- /// Points to the first doc comment token. API users are expected to iterate over the
- /// tokens array, looking for more doc comments, ignoring line comments, and stopping
- /// at the first other token.
- first_line: TokenIndex,
-
- pub fn iterate(self: *const DocComment, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const DocComment) TokenIndex {
- return self.first_line;
- }
-
- /// Returns the first doc comment line. Be careful, this may not be the desired behavior,
- /// which would require the tokens array.
- pub fn lastToken(self: *const DocComment) TokenIndex {
- return self.first_line;
- }
- };
-
- pub const TestDecl = struct {
- base: Node = Node{ .tag = .TestDecl },
- doc_comments: ?*DocComment,
- test_token: TokenIndex,
- name: *Node,
- body_node: *Node,
-
- pub fn iterate(self: *const TestDecl, index: usize) ?*Node {
- var i = index;
-
- if (i < 1) return self.body_node;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: *const TestDecl) TokenIndex {
- return self.test_token;
- }
-
- pub fn lastToken(self: *const TestDecl) TokenIndex {
- return self.body_node.lastToken();
- }
};
};
-
-pub const PtrInfo = struct {
- allowzero_token: ?TokenIndex = null,
- align_info: ?Align = null,
- const_token: ?TokenIndex = null,
- volatile_token: ?TokenIndex = null,
- sentinel: ?*Node = null,
-
- pub const Align = struct {
- node: *Node,
- bit_range: ?BitRange = null,
-
- pub const BitRange = struct {
- start: *Node,
- end: *Node,
- };
- };
-};
-
-test "iterate" {
- var root = Node.Root{
- .base = Node{ .tag = Node.Tag.Root },
- .decls_len = 0,
- .eof_token = 0,
- };
- var base = &root.base;
- testing.expect(base.iterate(0) == null);
-}
diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig
index 5c68fb3798..8d6f63f5e3 100644
--- a/lib/std/zig/cross_target.zig
+++ b/lib/std/zig/cross_target.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -130,6 +130,9 @@ pub const CrossTarget = struct {
.wasi,
.emscripten,
.uefi,
+ .opencl,
+ .glsl450,
+ .vulkan,
.other,
=> {
self.os_version_min = .{ .none = {} };
@@ -519,29 +522,29 @@ pub const CrossTarget = struct {
var result = std.ArrayList(u8).init(allocator);
defer result.deinit();
- try result.outStream().print("{}-{}", .{ arch_name, os_name });
+ try result.writer().print("{s}-{s}", .{ arch_name, os_name });
// The zig target syntax does not allow specifying a max os version with no min, so
// if either are present, we need the min.
if (self.os_version_min != null or self.os_version_max != null) {
switch (self.getOsVersionMin()) {
.none => {},
- .semver => |v| try result.outStream().print(".{}", .{v}),
- .windows => |v| try result.outStream().print("{s}", .{v}),
+ .semver => |v| try result.writer().print(".{}", .{v}),
+ .windows => |v| try result.writer().print("{s}", .{v}),
}
}
if (self.os_version_max) |max| {
switch (max) {
.none => {},
- .semver => |v| try result.outStream().print("...{}", .{v}),
- .windows => |v| try result.outStream().print("..{s}", .{v}),
+ .semver => |v| try result.writer().print("...{}", .{v}),
+ .windows => |v| try result.writer().print("..{s}", .{v}),
}
}
if (self.glibc_version) |v| {
- try result.outStream().print("-{}.{}", .{ @tagName(self.getAbi()), v });
+ try result.writer().print("-{s}.{}", .{ @tagName(self.getAbi()), v });
} else if (self.abi) |abi| {
- try result.outStream().print("-{}", .{@tagName(abi)});
+ try result.writer().print("-{s}", .{@tagName(abi)});
}
return result.toOwnedSlice();
@@ -595,7 +598,7 @@ pub const CrossTarget = struct {
.Dynamic => "",
};
- return std.fmt.allocPrint(allocator, "{}-{}{}", .{ arch, os, static_suffix });
+ return std.fmt.allocPrint(allocator, "{s}-{s}{s}", .{ arch, os, static_suffix });
}
pub const Executor = union(enum) {
@@ -730,6 +733,9 @@ pub const CrossTarget = struct {
.wasi,
.emscripten,
.uefi,
+ .opencl,
+ .glsl450,
+ .vulkan,
.other,
=> return error.InvalidOperatingSystemVersion,
@@ -790,7 +796,7 @@ test "CrossTarget.parse" {
var buf: [256]u8 = undefined;
const triple = std.fmt.bufPrint(
buf[0..],
- "native-native-{}.2.1.1",
+ "native-native-{s}.2.1.1",
.{@tagName(std.Target.current.abi)},
) catch unreachable;
diff --git a/lib/std/zig/fmt.zig b/lib/std/zig/fmt.zig
new file mode 100644
index 0000000000..2ca5279263
--- /dev/null
+++ b/lib/std/zig/fmt.zig
@@ -0,0 +1,72 @@
+const std = @import("std");
+const mem = std.mem;
+
+/// Print the string as a Zig identifier escaping it with @"" syntax if needed.
+pub fn formatId(
+ bytes: []const u8,
+ comptime fmt: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+) !void {
+ if (isValidId(bytes)) {
+ return writer.writeAll(bytes);
+ }
+ try writer.writeAll("@\"");
+ try formatEscapes(bytes, fmt, options, writer);
+ try writer.writeByte('"');
+}
+
+/// Return a Formatter for a Zig identifier
+pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) {
+ return .{ .data = bytes };
+}
+
+pub fn isValidId(bytes: []const u8) bool {
+ for (bytes) |c, i| {
+ switch (c) {
+ '_', 'a'...'z', 'A'...'Z' => {},
+ '0'...'9' => if (i == 0) return false,
+ else => return false,
+ }
+ }
+ return std.zig.Token.getKeyword(bytes) == null;
+}
+
+pub fn formatEscapes(
+ bytes: []const u8,
+ comptime fmt: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+) !void {
+ for (bytes) |byte| switch (byte) {
+ '\n' => try writer.writeAll("\\n"),
+ '\r' => try writer.writeAll("\\r"),
+ '\t' => try writer.writeAll("\\t"),
+ '\\' => try writer.writeAll("\\\\"),
+ '"' => try writer.writeAll("\\\""),
+ '\'' => try writer.writeAll("\\'"),
+ ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte),
+ // Use hex escapes for rest any unprintable characters.
+ else => {
+ try writer.writeAll("\\x");
+ try std.fmt.formatInt(byte, 16, false, .{ .width = 2, .fill = '0' }, writer);
+ },
+ };
+}
+
+/// Return a Formatter for Zig Escapes
+pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(formatEscapes) {
+ return .{ .data = bytes };
+}
+
+test "escape invalid identifiers" {
+ const expectFmt = std.testing.expectFmt;
+ try expectFmt("@\"while\"", "{}", .{fmtId("while")});
+ try expectFmt("hello", "{}", .{fmtId("hello")});
+ try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")});
+ try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")});
+ try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")});
+ try expectFmt(
+ \\" \\ hi \x07 \x11 \" derp \'"
+ , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
+}
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
index 411273b149..7a6404fbb2 100644
--- a/lib/std/zig/parse.zig
+++ b/lib/std/zig/parse.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -11,85 +11,181 @@ const Node = ast.Node;
const Tree = ast.Tree;
const AstError = ast.Error;
const TokenIndex = ast.TokenIndex;
-const NodeIndex = ast.NodeIndex;
const Token = std.zig.Token;
pub const Error = error{ParseError} || Allocator.Error;
/// Result should be freed with tree.deinit() when there are
/// no more references to any of the tokens or nodes.
-pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!*Tree {
- var token_ids = std.ArrayList(Token.Id).init(gpa);
- defer token_ids.deinit();
- var token_locs = std.ArrayList(Token.Loc).init(gpa);
- defer token_locs.deinit();
+pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {
+ var tokens = ast.TokenList{};
+ defer tokens.deinit(gpa);
// Empirically, the zig std lib has an 8:1 ratio of source bytes to token count.
const estimated_token_count = source.len / 8;
- try token_ids.ensureCapacity(estimated_token_count);
- try token_locs.ensureCapacity(estimated_token_count);
+ try tokens.ensureCapacity(gpa, estimated_token_count);
var tokenizer = std.zig.Tokenizer.init(source);
while (true) {
const token = tokenizer.next();
- try token_ids.append(token.id);
- try token_locs.append(token.loc);
- if (token.id == .Eof) break;
+ try tokens.append(gpa, .{
+ .tag = token.tag,
+ .start = @intCast(u32, token.loc.start),
+ });
+ if (token.tag == .eof) break;
}
var parser: Parser = .{
.source = source,
- .arena = std.heap.ArenaAllocator.init(gpa),
.gpa = gpa,
- .token_ids = token_ids.items,
- .token_locs = token_locs.items,
+ .token_tags = tokens.items(.tag),
+ .token_starts = tokens.items(.start),
.errors = .{},
+ .nodes = .{},
+ .extra_data = .{},
.tok_i = 0,
};
defer parser.errors.deinit(gpa);
- errdefer parser.arena.deinit();
-
- while (token_ids.items[parser.tok_i] == .LineComment) parser.tok_i += 1;
-
- const root_node = try parser.parseRoot();
+ defer parser.nodes.deinit(gpa);
+ defer parser.extra_data.deinit(gpa);
+
+ // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes.
+ // Make sure at least 1 so we can use appendAssumeCapacity on the root node below.
+ const estimated_node_count = (tokens.len + 2) / 2;
+ try parser.nodes.ensureCapacity(gpa, estimated_node_count);
+
+ // Root node must be index 0.
+ // Root <- skip ContainerMembers eof
+ parser.nodes.appendAssumeCapacity(.{
+ .tag = .root,
+ .main_token = 0,
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ });
+ const root_members = try parser.parseContainerMembers();
+ const root_decls = try root_members.toSpan(&parser);
+ if (parser.token_tags[parser.tok_i] != .eof) {
+ try parser.warnExpected(.eof);
+ }
+ parser.nodes.items(.data)[0] = .{
+ .lhs = root_decls.start,
+ .rhs = root_decls.end,
+ };
- const tree = try parser.arena.allocator.create(Tree);
- tree.* = .{
- .gpa = gpa,
+ // TODO experiment with compacting the MultiArrayList slices here
+ return Tree{
.source = source,
- .token_ids = token_ids.toOwnedSlice(),
- .token_locs = token_locs.toOwnedSlice(),
+ .tokens = tokens.toOwnedSlice(),
+ .nodes = parser.nodes.toOwnedSlice(),
+ .extra_data = parser.extra_data.toOwnedSlice(gpa),
.errors = parser.errors.toOwnedSlice(gpa),
- .root_node = root_node,
- .arena = parser.arena.state,
};
- return tree;
}
+const null_node: Node.Index = 0;
+
/// Represents in-progress parsing, will be converted to an ast.Tree after completion.
const Parser = struct {
- arena: std.heap.ArenaAllocator,
gpa: *Allocator,
source: []const u8,
- token_ids: []const Token.Id,
- token_locs: []const Token.Loc,
+ token_tags: []const Token.Tag,
+ token_starts: []const ast.ByteOffset,
tok_i: TokenIndex,
errors: std.ArrayListUnmanaged(AstError),
+ nodes: ast.NodeList,
+ extra_data: std.ArrayListUnmanaged(Node.Index),
- /// Root <- skip ContainerMembers eof
- fn parseRoot(p: *Parser) Allocator.Error!*Node.Root {
- const decls = try parseContainerMembers(p, true);
- defer p.gpa.free(decls);
+ const SmallSpan = union(enum) {
+ zero_or_one: Node.Index,
+ multi: []Node.Index,
- // parseContainerMembers will try to skip as much
- // invalid tokens as it can so this can only be the EOF
- const eof_token = p.eatToken(.Eof).?;
+ fn deinit(self: SmallSpan, gpa: *Allocator) void {
+ switch (self) {
+ .zero_or_one => {},
+ .multi => |list| gpa.free(list),
+ }
+ }
+ };
- const decls_len = @intCast(NodeIndex, decls.len);
- const node = try Node.Root.create(&p.arena.allocator, decls_len, eof_token);
- std.mem.copy(*Node, node.decls(), decls);
+ const Members = struct {
+ len: usize,
+ lhs: Node.Index,
+ rhs: Node.Index,
+ trailing: bool,
- return node;
+ fn toSpan(self: Members, p: *Parser) !Node.SubRange {
+ if (self.len <= 2) {
+ const nodes = [2]Node.Index{ self.lhs, self.rhs };
+ return p.listToSpan(nodes[0..self.len]);
+ } else {
+ return Node.SubRange{ .start = self.lhs, .end = self.rhs };
+ }
+ }
+ };
+
+ fn listToSpan(p: *Parser, list: []const Node.Index) !Node.SubRange {
+ try p.extra_data.appendSlice(p.gpa, list);
+ return Node.SubRange{
+ .start = @intCast(Node.Index, p.extra_data.items.len - list.len),
+ .end = @intCast(Node.Index, p.extra_data.items.len),
+ };
+ }
+
+ fn addNode(p: *Parser, elem: ast.NodeList.Elem) Allocator.Error!Node.Index {
+ const result = @intCast(Node.Index, p.nodes.len);
+ try p.nodes.append(p.gpa, elem);
+ return result;
+ }
+
+ fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {
+ const fields = std.meta.fields(@TypeOf(extra));
+ try p.extra_data.ensureCapacity(p.gpa, p.extra_data.items.len + fields.len);
+ const result = @intCast(u32, p.extra_data.items.len);
+ inline for (fields) |field| {
+ comptime assert(field.field_type == Node.Index);
+ p.extra_data.appendAssumeCapacity(@field(extra, field.name));
+ }
+ return result;
+ }
+
+ fn warn(p: *Parser, tag: ast.Error.Tag) error{OutOfMemory}!void {
+ @setCold(true);
+ try p.warnMsg(.{ .tag = tag, .token = p.tok_i });
+ }
+
+ fn warnExpected(p: *Parser, expected_token: Token.Tag) error{OutOfMemory}!void {
+ @setCold(true);
+ try p.warnMsg(.{
+ .tag = .expected_token,
+ .token = p.tok_i,
+ .extra = .{ .expected_tag = expected_token },
+ });
+ }
+ fn warnMsg(p: *Parser, msg: ast.Error) error{OutOfMemory}!void {
+ @setCold(true);
+ try p.errors.append(p.gpa, msg);
+ }
+
+ fn fail(p: *Parser, tag: ast.Error.Tag) error{ ParseError, OutOfMemory } {
+ @setCold(true);
+ return p.failMsg(.{ .tag = tag, .token = p.tok_i });
+ }
+
+ fn failExpected(p: *Parser, expected_token: Token.Tag) error{ ParseError, OutOfMemory } {
+ @setCold(true);
+ return p.failMsg(.{
+ .tag = .expected_token,
+ .token = p.tok_i,
+ .extra = .{ .expected_tag = expected_token },
+ });
+ }
+
+ fn failMsg(p: *Parser, msg: ast.Error) error{ ParseError, OutOfMemory } {
+ @setCold(true);
+ try p.warnMsg(msg);
+ return error.ParseError;
}
/// ContainerMembers
@@ -99,176 +195,226 @@ const Parser = struct {
/// / ContainerField COMMA ContainerMembers
/// / ContainerField
/// /
- fn parseContainerMembers(p: *Parser, top_level: bool) ![]*Node {
- var list = std.ArrayList(*Node).init(p.gpa);
+ /// TopLevelComptime <- KEYWORD_comptime BlockExpr
+ fn parseContainerMembers(p: *Parser) !Members {
+ var list = std.ArrayList(Node.Index).init(p.gpa);
defer list.deinit();
var field_state: union(enum) {
- /// no fields have been seen
+ /// No fields have been seen.
none,
- /// currently parsing fields
+ /// Currently parsing fields.
seen,
- /// saw fields and then a declaration after them.
- /// payload is first token of previous declaration.
- end: TokenIndex,
- /// ther was a declaration between fields, don't report more errors
+ /// Saw fields and then a declaration after them.
+ /// Payload is first token of previous declaration.
+ end: Node.Index,
+ /// There was a declaration between fields, don't report more errors.
err,
} = .none;
- while (true) {
- if (try p.parseContainerDocComments()) |node| {
- try list.append(node);
- continue;
- }
-
- const doc_comments = try p.parseDocComment();
+ // Skip container doc comments.
+ while (p.eatToken(.container_doc_comment)) |_| {}
- if (p.parseTestDecl() catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.ParseError => {
- p.findNextContainerMember();
- continue;
+ var trailing = false;
+ while (true) {
+ const doc_comment = try p.eatDocComments();
+
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_test => {
+ const test_decl_node = try p.expectTestDeclRecoverable();
+ if (test_decl_node != 0) {
+ if (field_state == .seen) {
+ field_state = .{ .end = test_decl_node };
+ }
+ try list.append(test_decl_node);
+ }
+ trailing = false;
},
- }) |node| {
- if (field_state == .seen) {
- field_state = .{ .end = node.firstToken() };
- }
- node.cast(Node.TestDecl).?.doc_comments = doc_comments;
- try list.append(node);
- continue;
- }
-
- if (p.parseTopLevelComptime() catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.ParseError => {
- p.findNextContainerMember();
- continue;
+ .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {
+ .identifier => {
+ p.tok_i += 1;
+ const container_field = try p.expectContainerFieldRecoverable();
+ if (container_field != 0) {
+ switch (field_state) {
+ .none => field_state = .seen,
+ .err, .seen => {},
+ .end => |node| {
+ try p.warnMsg(.{
+ .tag = .decl_between_fields,
+ .token = p.nodes.items(.main_token)[node],
+ });
+ // Continue parsing; error will be reported later.
+ field_state = .err;
+ },
+ }
+ try list.append(container_field);
+ switch (p.token_tags[p.tok_i]) {
+ .comma => {
+ p.tok_i += 1;
+ trailing = true;
+ continue;
+ },
+ .r_brace, .eof => {
+ trailing = false;
+ break;
+ },
+ else => {},
+ }
+ // There is not allowed to be a decl after a field with no comma.
+ // Report error but recover parser.
+ try p.warnExpected(.comma);
+ p.findNextContainerMember();
+ }
+ },
+ .l_brace => {
+ const comptime_token = p.nextToken();
+ const block = p.parseBlock() catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.ParseError => blk: {
+ p.findNextContainerMember();
+ break :blk null_node;
+ },
+ };
+ if (block != 0) {
+ const comptime_node = try p.addNode(.{
+ .tag = .@"comptime",
+ .main_token = comptime_token,
+ .data = .{
+ .lhs = block,
+ .rhs = undefined,
+ },
+ });
+ if (field_state == .seen) {
+ field_state = .{ .end = comptime_node };
+ }
+ try list.append(comptime_node);
+ }
+ trailing = false;
+ },
+ else => {
+ p.tok_i += 1;
+ try p.warn(.expected_block_or_field);
+ },
},
- }) |node| {
- if (field_state == .seen) {
- field_state = .{ .end = node.firstToken() };
- }
- node.cast(Node.Comptime).?.doc_comments = doc_comments;
- try list.append(node);
- continue;
- }
-
- const visib_token = p.eatToken(.Keyword_pub);
-
- if (p.parseTopLevelDecl(doc_comments, visib_token) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.ParseError => {
- p.findNextContainerMember();
- continue;
+ .keyword_pub => {
+ p.tok_i += 1;
+ const top_level_decl = try p.expectTopLevelDeclRecoverable();
+ if (top_level_decl != 0) {
+ if (field_state == .seen) {
+ field_state = .{ .end = top_level_decl };
+ }
+ try list.append(top_level_decl);
+ }
+ trailing = p.token_tags[p.tok_i - 1] == .semicolon;
},
- }) |node| {
- if (field_state == .seen) {
- field_state = .{ .end = visib_token orelse node.firstToken() };
- }
- try list.append(node);
- continue;
- }
-
- if (visib_token != null) {
- try p.errors.append(p.gpa, .{
- .ExpectedPubItem = .{ .token = p.tok_i },
- });
- // ignore this pub
- continue;
- }
-
- if (p.parseContainerField() catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.ParseError => {
- // attempt to recover
- p.findNextContainerMember();
- continue;
+ .keyword_usingnamespace => {
+ const node = try p.expectUsingNamespaceRecoverable();
+ if (node != 0) {
+ if (field_state == .seen) {
+ field_state = .{ .end = node };
+ }
+ try list.append(node);
+ }
+ trailing = p.token_tags[p.tok_i - 1] == .semicolon;
+ },
+ .keyword_const,
+ .keyword_var,
+ .keyword_threadlocal,
+ .keyword_export,
+ .keyword_extern,
+ .keyword_inline,
+ .keyword_noinline,
+ .keyword_fn,
+ => {
+ const top_level_decl = try p.expectTopLevelDeclRecoverable();
+ if (top_level_decl != 0) {
+ if (field_state == .seen) {
+ field_state = .{ .end = top_level_decl };
+ }
+ try list.append(top_level_decl);
+ }
+ trailing = p.token_tags[p.tok_i - 1] == .semicolon;
},
- }) |node| {
- switch (field_state) {
- .none => field_state = .seen,
- .err, .seen => {},
- .end => |tok| {
- try p.errors.append(p.gpa, .{
- .DeclBetweenFields = .{ .token = tok },
+ .identifier => {
+ const container_field = try p.expectContainerFieldRecoverable();
+ if (container_field != 0) {
+ switch (field_state) {
+ .none => field_state = .seen,
+ .err, .seen => {},
+ .end => |node| {
+ try p.warnMsg(.{
+ .tag = .decl_between_fields,
+ .token = p.nodes.items(.main_token)[node],
+ });
+ // Continue parsing; error will be reported later.
+ field_state = .err;
+ },
+ }
+ try list.append(container_field);
+ switch (p.token_tags[p.tok_i]) {
+ .comma => {
+ p.tok_i += 1;
+ trailing = true;
+ continue;
+ },
+ .r_brace, .eof => {
+ trailing = false;
+ break;
+ },
+ else => {},
+ }
+ // There is not allowed to be a decl after a field with no comma.
+ // Report error but recover parser.
+ try p.warnExpected(.comma);
+ p.findNextContainerMember();
+ }
+ },
+ .eof, .r_brace => {
+ if (doc_comment) |tok| {
+ try p.warnMsg(.{
+ .tag = .unattached_doc_comment,
+ .token = tok,
});
- // continue parsing, error will be reported later
- field_state = .err;
- },
- }
-
- const field = node.cast(Node.ContainerField).?;
- field.doc_comments = doc_comments;
- try list.append(node);
- const comma = p.eatToken(.Comma) orelse {
- // try to continue parsing
- const index = p.tok_i;
- p.findNextContainerMember();
- const next = p.token_ids[p.tok_i];
- switch (next) {
- .Eof => {
- // no invalid tokens were found
- if (index == p.tok_i) break;
-
- // Invalid tokens, add error and exit
- try p.errors.append(p.gpa, .{
- .ExpectedToken = .{ .token = index, .expected_id = .Comma },
- });
- break;
- },
- else => {
- if (next == .RBrace) {
- if (!top_level) break;
- _ = p.nextToken();
- }
-
- // add error and continue
- try p.errors.append(p.gpa, .{
- .ExpectedToken = .{ .token = index, .expected_id = .Comma },
- });
- continue;
- },
}
- };
- if (try p.parseAppendedDocComment(comma)) |appended_comment|
- field.doc_comments = appended_comment;
- continue;
- }
-
- // Dangling doc comment
- if (doc_comments != null) {
- try p.errors.append(p.gpa, .{
- .UnattachedDocComment = .{ .token = doc_comments.?.firstToken() },
- });
- }
-
- const next = p.token_ids[p.tok_i];
- switch (next) {
- .Eof => break,
- .Keyword_comptime => {
- _ = p.nextToken();
- try p.errors.append(p.gpa, .{
- .ExpectedBlockOrField = .{ .token = p.tok_i },
- });
+ break;
},
else => {
- const index = p.tok_i;
- if (next == .RBrace) {
- if (!top_level) break;
- _ = p.nextToken();
- }
-
- // this was likely not supposed to end yet,
- // try to find the next declaration
+ try p.warn(.expected_container_members);
+ // This was likely not supposed to end yet; try to find the next declaration.
p.findNextContainerMember();
- try p.errors.append(p.gpa, .{
- .ExpectedContainerMembers = .{ .token = index },
- });
},
}
}
- return list.toOwnedSlice();
+ switch (list.items.len) {
+ 0 => return Members{
+ .len = 0,
+ .lhs = 0,
+ .rhs = 0,
+ .trailing = trailing,
+ },
+ 1 => return Members{
+ .len = 1,
+ .lhs = list.items[0],
+ .rhs = 0,
+ .trailing = trailing,
+ },
+ 2 => return Members{
+ .len = 2,
+ .lhs = list.items[0],
+ .rhs = list.items[1],
+ .trailing = trailing,
+ },
+ else => {
+ const span = try p.listToSpan(list.items);
+ return Members{
+ .len = list.items.len,
+ .lhs = span.start,
+ .rhs = span.end,
+ .trailing = trailing,
+ };
+ },
+ }
}
/// Attempts to find next container member by searching for certain tokens
@@ -276,47 +422,52 @@ const Parser = struct {
var level: u32 = 0;
while (true) {
const tok = p.nextToken();
- switch (p.token_ids[tok]) {
- // any of these can start a new top level declaration
- .Keyword_test,
- .Keyword_comptime,
- .Keyword_pub,
- .Keyword_export,
- .Keyword_extern,
- .Keyword_inline,
- .Keyword_noinline,
- .Keyword_usingnamespace,
- .Keyword_threadlocal,
- .Keyword_const,
- .Keyword_var,
- .Keyword_fn,
- .Identifier,
+ switch (p.token_tags[tok]) {
+ // Any of these can start a new top level declaration.
+ .keyword_test,
+ .keyword_comptime,
+ .keyword_pub,
+ .keyword_export,
+ .keyword_extern,
+ .keyword_inline,
+ .keyword_noinline,
+ .keyword_usingnamespace,
+ .keyword_threadlocal,
+ .keyword_const,
+ .keyword_var,
+ .keyword_fn,
=> {
if (level == 0) {
- p.putBackToken(tok);
+ p.tok_i -= 1;
return;
}
},
- .Comma, .Semicolon => {
+ .identifier => {
+ if (p.token_tags[tok + 1] == .comma and level == 0) {
+ p.tok_i -= 1;
+ return;
+ }
+ },
+ .comma, .semicolon => {
// this decl was likely meant to end here
if (level == 0) {
return;
}
},
- .LParen, .LBracket, .LBrace => level += 1,
- .RParen, .RBracket => {
+ .l_paren, .l_bracket, .l_brace => level += 1,
+ .r_paren, .r_bracket => {
if (level != 0) level -= 1;
},
- .RBrace => {
+ .r_brace => {
if (level == 0) {
// end of container, exit
- p.putBackToken(tok);
+ p.tok_i -= 1;
return;
}
level -= 1;
},
- .Eof => {
- p.putBackToken(tok);
+ .eof => {
+ p.tok_i -= 1;
return;
},
else => {},
@@ -329,22 +480,22 @@ const Parser = struct {
var level: u32 = 0;
while (true) {
const tok = p.nextToken();
- switch (p.token_ids[tok]) {
- .LBrace => level += 1,
- .RBrace => {
+ switch (p.token_tags[tok]) {
+ .l_brace => level += 1,
+ .r_brace => {
if (level == 0) {
- p.putBackToken(tok);
+ p.tok_i -= 1;
return;
}
level -= 1;
},
- .Semicolon => {
+ .semicolon => {
if (level == 0) {
return;
}
},
- .Eof => {
- p.putBackToken(tok);
+ .eof => {
+ p.tok_i -= 1;
return;
},
else => {},
@@ -352,330 +503,337 @@ const Parser = struct {
}
}
- /// Eat a multiline container doc comment
- fn parseContainerDocComments(p: *Parser) !?*Node {
- if (p.eatToken(.ContainerDocComment)) |first_line| {
- while (p.eatToken(.ContainerDocComment)) |_| {}
- const node = try p.arena.allocator.create(Node.DocComment);
- node.* = .{ .first_line = first_line };
- return &node.base;
- }
- return null;
- }
-
- /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block
- fn parseTestDecl(p: *Parser) !?*Node {
- const test_token = p.eatToken(.Keyword_test) orelse return null;
- const name_node = try p.expectNode(parseStringLiteralSingle, .{
- .ExpectedStringLiteral = .{ .token = p.tok_i },
+ /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block
+ fn expectTestDecl(p: *Parser) !Node.Index {
+ const test_token = p.assertToken(.keyword_test);
+ const name_token = p.eatToken(.string_literal);
+ const block_node = try p.parseBlock();
+ if (block_node == 0) return p.fail(.expected_block);
+ return p.addNode(.{
+ .tag = .test_decl,
+ .main_token = test_token,
+ .data = .{
+ .lhs = name_token orelse 0,
+ .rhs = block_node,
+ },
});
- const block_node = (try p.parseBlock(null)) orelse {
- try p.errors.append(p.gpa, .{ .ExpectedLBrace = .{ .token = p.tok_i } });
- return error.ParseError;
- };
-
- const test_node = try p.arena.allocator.create(Node.TestDecl);
- test_node.* = .{
- .doc_comments = null,
- .test_token = test_token,
- .name = name_node,
- .body_node = block_node,
- };
- return &test_node.base;
}
- /// TopLevelComptime <- KEYWORD_comptime BlockExpr
- fn parseTopLevelComptime(p: *Parser) !?*Node {
- const tok = p.eatToken(.Keyword_comptime) orelse return null;
- const lbrace = p.eatToken(.LBrace) orelse {
- p.putBackToken(tok);
- return null;
- };
- p.putBackToken(lbrace);
- const block_node = try p.expectNode(parseBlockExpr, .{
- .ExpectedLabelOrLBrace = .{ .token = p.tok_i },
- });
-
- const comptime_node = try p.arena.allocator.create(Node.Comptime);
- comptime_node.* = .{
- .doc_comments = null,
- .comptime_token = tok,
- .expr = block_node,
+ fn expectTestDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
+ return p.expectTestDecl() catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.ParseError => {
+ p.findNextContainerMember();
+ return null_node;
+ },
};
- return &comptime_node.base;
}
/// TopLevelDecl
/// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
/// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
/// / KEYWORD_usingnamespace Expr SEMICOLON
- fn parseTopLevelDecl(p: *Parser, doc_comments: ?*Node.DocComment, visib_token: ?TokenIndex) !?*Node {
- var lib_name: ?*Node = null;
- const extern_export_inline_token = blk: {
- if (p.eatToken(.Keyword_export)) |token| break :blk token;
- if (p.eatToken(.Keyword_extern)) |token| {
- lib_name = try p.parseStringLiteralSingle();
- break :blk token;
+ fn expectTopLevelDecl(p: *Parser) !Node.Index {
+ const extern_export_inline_token = p.nextToken();
+ var expect_fn: bool = false;
+ var expect_var_or_fn: bool = false;
+ switch (p.token_tags[extern_export_inline_token]) {
+ .keyword_extern => {
+ _ = p.eatToken(.string_literal);
+ expect_var_or_fn = true;
+ },
+ .keyword_export => expect_var_or_fn = true,
+ .keyword_inline, .keyword_noinline => expect_fn = true,
+ else => p.tok_i -= 1,
+ }
+ const fn_proto = try p.parseFnProto();
+ if (fn_proto != 0) {
+ switch (p.token_tags[p.tok_i]) {
+ .semicolon => {
+ p.tok_i += 1;
+ return fn_proto;
+ },
+ .l_brace => {
+ const body_block = try p.parseBlock();
+ assert(body_block != 0);
+ return p.addNode(.{
+ .tag = .fn_decl,
+ .main_token = p.nodes.items(.main_token)[fn_proto],
+ .data = .{
+ .lhs = fn_proto,
+ .rhs = body_block,
+ },
+ });
+ },
+ else => {
+ // Since parseBlock only return error.ParseError on
+ // a missing '}' we can assume this function was
+ // supposed to end here.
+ try p.warn(.expected_semi_or_lbrace);
+ return null_node;
+ },
}
- if (p.eatToken(.Keyword_inline)) |token| break :blk token;
- if (p.eatToken(.Keyword_noinline)) |token| break :blk token;
- break :blk null;
- };
-
- if (try p.parseFnProto(.top_level, .{
- .doc_comments = doc_comments,
- .visib_token = visib_token,
- .extern_export_inline_token = extern_export_inline_token,
- .lib_name = lib_name,
- })) |node| {
- return node;
}
-
- if (extern_export_inline_token) |token| {
- if (p.token_ids[token] == .Keyword_inline or
- p.token_ids[token] == .Keyword_noinline)
- {
- try p.errors.append(p.gpa, .{
- .ExpectedFn = .{ .token = p.tok_i },
- });
- return error.ParseError;
- }
+ if (expect_fn) {
+ try p.warn(.expected_fn);
+ return error.ParseError;
}
- const thread_local_token = p.eatToken(.Keyword_threadlocal);
-
- if (try p.parseVarDecl(.{
- .doc_comments = doc_comments,
- .visib_token = visib_token,
- .thread_local_token = thread_local_token,
- .extern_export_token = extern_export_inline_token,
- .lib_name = lib_name,
- })) |node| {
- return node;
+ const thread_local_token = p.eatToken(.keyword_threadlocal);
+ const var_decl = try p.parseVarDecl();
+ if (var_decl != 0) {
+ const semicolon_token = try p.expectToken(.semicolon);
+ return var_decl;
}
-
if (thread_local_token != null) {
- try p.errors.append(p.gpa, .{
- .ExpectedVarDecl = .{ .token = p.tok_i },
- });
- // ignore this and try again;
- return error.ParseError;
+ return p.fail(.expected_var_decl);
}
-
- if (extern_export_inline_token) |token| {
- try p.errors.append(p.gpa, .{
- .ExpectedVarDeclOrFn = .{ .token = p.tok_i },
- });
- // ignore this and try again;
- return error.ParseError;
+ if (expect_var_or_fn) {
+ return p.fail(.expected_var_decl_or_fn);
+ }
+ if (p.token_tags[p.tok_i] != .keyword_usingnamespace) {
+ return p.fail(.expected_pub_item);
}
+ return p.expectUsingNamespace();
+ }
- const use_token = p.eatToken(.Keyword_usingnamespace) orelse return null;
- const expr = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- const semicolon_token = try p.expectToken(.Semicolon);
-
- const node = try p.arena.allocator.create(Node.Use);
- node.* = .{
- .doc_comments = doc_comments orelse try p.parseAppendedDocComment(semicolon_token),
- .visib_token = visib_token,
- .use_token = use_token,
- .expr = expr,
- .semicolon_token = semicolon_token,
+ fn expectTopLevelDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
+ return p.expectTopLevelDecl() catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.ParseError => {
+ p.findNextContainerMember();
+ return null_node;
+ },
};
+ }
- return &node.base;
+ fn expectUsingNamespace(p: *Parser) !Node.Index {
+ const usingnamespace_token = p.assertToken(.keyword_usingnamespace);
+ const expr = try p.expectExpr();
+ const semicolon_token = try p.expectToken(.semicolon);
+ return p.addNode(.{
+ .tag = .@"usingnamespace",
+ .main_token = usingnamespace_token,
+ .data = .{
+ .lhs = expr,
+ .rhs = undefined,
+ },
+ });
}
- /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
- fn parseFnProto(p: *Parser, level: enum { top_level, as_type }, fields: struct {
- doc_comments: ?*Node.DocComment = null,
- visib_token: ?TokenIndex = null,
- extern_export_inline_token: ?TokenIndex = null,
- lib_name: ?*Node = null,
- }) !?*Node {
- // TODO: Remove once extern/async fn rewriting is
- var is_async: ?void = null;
- var is_extern_prototype: ?void = null;
- const cc_token: ?TokenIndex = blk: {
- if (p.eatToken(.Keyword_extern)) |token| {
- is_extern_prototype = {};
- break :blk token;
- }
- if (p.eatToken(.Keyword_async)) |token| {
- is_async = {};
- break :blk token;
- }
- break :blk null;
- };
- const fn_token = p.eatToken(.Keyword_fn) orelse {
- if (cc_token) |token|
- p.putBackToken(token);
- return null;
+ fn expectUsingNamespaceRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
+ return p.expectUsingNamespace() catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.ParseError => {
+ p.findNextContainerMember();
+ return null_node;
+ },
};
- const name_token = p.eatToken(.Identifier);
- const lparen = try p.expectToken(.LParen);
+ }
+
+ /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
+ fn parseFnProto(p: *Parser) !Node.Index {
+ const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
+ _ = p.eatToken(.identifier);
const params = try p.parseParamDeclList();
- defer p.gpa.free(params);
- const var_args_token = p.eatToken(.Ellipsis3);
- const rparen = try p.expectToken(.RParen);
+ defer params.deinit(p.gpa);
const align_expr = try p.parseByteAlign();
const section_expr = try p.parseLinkSection();
const callconv_expr = try p.parseCallconv();
- const exclamation_token = p.eatToken(.Bang);
+ const bang_token = p.eatToken(.bang);
- const return_type_expr = (try p.parseAnyType()) orelse
- try p.expectNodeRecoverable(parseTypeExpr, .{
+ const return_type_expr = try p.parseTypeExpr();
+ if (return_type_expr == 0) {
// most likely the user forgot to specify the return type.
// Mark return type as invalid and try to continue.
- .ExpectedReturnType = .{ .token = p.tok_i },
- });
+ try p.warn(.expected_return_type);
+ }
- // TODO https://github.com/ziglang/zig/issues/3750
- const R = Node.FnProto.ReturnType;
- const return_type = if (return_type_expr == null)
- R{ .Invalid = rparen }
- else if (exclamation_token != null)
- R{ .InferErrorSet = return_type_expr.? }
- else
- R{ .Explicit = return_type_expr.? };
-
- const body_node: ?*Node = switch (level) {
- .top_level => blk: {
- if (p.eatToken(.Semicolon)) |_| {
- break :blk null;
- }
- const body_block = (try p.parseBlock(null)) orelse {
- // Since parseBlock only return error.ParseError on
- // a missing '}' we can assume this function was
- // supposed to end here.
- try p.errors.append(p.gpa, .{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } });
- break :blk null;
- };
- break :blk body_block;
+ if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
+ switch (params) {
+ .zero_or_one => |param| return p.addNode(.{
+ .tag = .fn_proto_simple,
+ .main_token = fn_token,
+ .data = .{
+ .lhs = param,
+ .rhs = return_type_expr,
+ },
+ }),
+ .multi => |list| {
+ const span = try p.listToSpan(list);
+ return p.addNode(.{
+ .tag = .fn_proto_multi,
+ .main_token = fn_token,
+ .data = .{
+ .lhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ .rhs = return_type_expr,
+ },
+ });
+ },
+ }
+ }
+ switch (params) {
+ .zero_or_one => |param| return p.addNode(.{
+ .tag = .fn_proto_one,
+ .main_token = fn_token,
+ .data = .{
+ .lhs = try p.addExtra(Node.FnProtoOne{
+ .param = param,
+ .align_expr = align_expr,
+ .section_expr = section_expr,
+ .callconv_expr = callconv_expr,
+ }),
+ .rhs = return_type_expr,
+ },
+ }),
+ .multi => |list| {
+ const span = try p.listToSpan(list);
+ return p.addNode(.{
+ .tag = .fn_proto,
+ .main_token = fn_token,
+ .data = .{
+ .lhs = try p.addExtra(Node.FnProto{
+ .params_start = span.start,
+ .params_end = span.end,
+ .align_expr = align_expr,
+ .section_expr = section_expr,
+ .callconv_expr = callconv_expr,
+ }),
+ .rhs = return_type_expr,
+ },
+ });
},
- .as_type => null,
- };
-
- const fn_proto_node = try Node.FnProto.create(&p.arena.allocator, .{
- .params_len = params.len,
- .fn_token = fn_token,
- .return_type = return_type,
- }, .{
- .doc_comments = fields.doc_comments,
- .visib_token = fields.visib_token,
- .name_token = name_token,
- .var_args_token = var_args_token,
- .extern_export_inline_token = fields.extern_export_inline_token,
- .body_node = body_node,
- .lib_name = fields.lib_name,
- .align_expr = align_expr,
- .section_expr = section_expr,
- .callconv_expr = callconv_expr,
- .is_extern_prototype = is_extern_prototype,
- .is_async = is_async,
- });
- std.mem.copy(Node.FnProto.ParamDecl, fn_proto_node.params(), params);
-
- return &fn_proto_node.base;
+ }
}
/// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
- fn parseVarDecl(p: *Parser, fields: struct {
- doc_comments: ?*Node.DocComment = null,
- visib_token: ?TokenIndex = null,
- thread_local_token: ?TokenIndex = null,
- extern_export_token: ?TokenIndex = null,
- lib_name: ?*Node = null,
- comptime_token: ?TokenIndex = null,
- }) !?*Node {
- const mut_token = p.eatToken(.Keyword_const) orelse
- p.eatToken(.Keyword_var) orelse
- return null;
+ fn parseVarDecl(p: *Parser) !Node.Index {
+ const mut_token = p.eatToken(.keyword_const) orelse
+ p.eatToken(.keyword_var) orelse
+ return null_node;
- const name_token = try p.expectToken(.Identifier);
- const type_node = if (p.eatToken(.Colon) != null)
- try p.expectNode(parseTypeExpr, .{
- .ExpectedTypeExpr = .{ .token = p.tok_i },
- })
- else
- null;
+ _ = try p.expectToken(.identifier);
+ const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr();
const align_node = try p.parseByteAlign();
const section_node = try p.parseLinkSection();
- const eq_token = p.eatToken(.Equal);
- const init_node = if (eq_token != null) blk: {
- break :blk try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
+ const init_node: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr();
+ if (section_node == 0) {
+ if (align_node == 0) {
+ return p.addNode(.{
+ .tag = .simple_var_decl,
+ .main_token = mut_token,
+ .data = .{
+ .lhs = type_node,
+ .rhs = init_node,
+ },
+ });
+ } else if (type_node == 0) {
+ return p.addNode(.{
+ .tag = .aligned_var_decl,
+ .main_token = mut_token,
+ .data = .{
+ .lhs = align_node,
+ .rhs = init_node,
+ },
+ });
+ } else {
+ return p.addNode(.{
+ .tag = .local_var_decl,
+ .main_token = mut_token,
+ .data = .{
+ .lhs = try p.addExtra(Node.LocalVarDecl{
+ .type_node = type_node,
+ .align_node = align_node,
+ }),
+ .rhs = init_node,
+ },
+ });
+ }
+ } else {
+ return p.addNode(.{
+ .tag = .global_var_decl,
+ .main_token = mut_token,
+ .data = .{
+ .lhs = try p.addExtra(Node.GlobalVarDecl{
+ .type_node = type_node,
+ .align_node = align_node,
+ .section_node = section_node,
+ }),
+ .rhs = init_node,
+ },
});
- } else null;
- const semicolon_token = try p.expectToken(.Semicolon);
-
- const doc_comments = fields.doc_comments orelse try p.parseAppendedDocComment(semicolon_token);
-
- const node = try Node.VarDecl.create(&p.arena.allocator, .{
- .mut_token = mut_token,
- .name_token = name_token,
- .semicolon_token = semicolon_token,
- }, .{
- .doc_comments = doc_comments,
- .visib_token = fields.visib_token,
- .thread_local_token = fields.thread_local_token,
- .eq_token = eq_token,
- .comptime_token = fields.comptime_token,
- .extern_export_token = fields.extern_export_token,
- .lib_name = fields.lib_name,
- .type_node = type_node,
- .align_node = align_node,
- .section_node = section_node,
- .init_node = init_node,
- });
- return &node.base;
+ }
}
/// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?
- fn parseContainerField(p: *Parser) !?*Node {
- const comptime_token = p.eatToken(.Keyword_comptime);
- const name_token = p.eatToken(.Identifier) orelse {
- if (comptime_token) |t| p.putBackToken(t);
- return null;
- };
-
- var align_expr: ?*Node = null;
- var type_expr: ?*Node = null;
- if (p.eatToken(.Colon)) |_| {
- if (p.eatToken(.Keyword_anytype) orelse p.eatToken(.Keyword_var)) |anytype_tok| {
- const node = try p.arena.allocator.create(Node.OneToken);
- node.* = .{
- .base = .{ .tag = .AnyType },
- .token = anytype_tok,
- };
- type_expr = &node.base;
- } else {
- type_expr = try p.expectNode(parseTypeExpr, .{
- .ExpectedTypeExpr = .{ .token = p.tok_i },
+ fn expectContainerField(p: *Parser) !Node.Index {
+ const comptime_token = p.eatToken(.keyword_comptime);
+ const name_token = p.assertToken(.identifier);
+
+ var align_expr: Node.Index = 0;
+ var type_expr: Node.Index = 0;
+ if (p.eatToken(.colon)) |_| {
+ if (p.eatToken(.keyword_anytype)) |anytype_tok| {
+ type_expr = try p.addNode(.{
+ .tag = .@"anytype",
+ .main_token = anytype_tok,
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
});
+ } else {
+ type_expr = try p.expectTypeExpr();
align_expr = try p.parseByteAlign();
}
}
- const value_expr = if (p.eatToken(.Equal)) |_|
- try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- })
- else
- null;
-
- const node = try p.arena.allocator.create(Node.ContainerField);
- node.* = .{
- .doc_comments = null,
- .comptime_token = comptime_token,
- .name_token = name_token,
- .type_expr = type_expr,
- .value_expr = value_expr,
- .align_expr = align_expr,
+ const value_expr: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr();
+
+ if (align_expr == 0) {
+ return p.addNode(.{
+ .tag = .container_field_init,
+ .main_token = name_token,
+ .data = .{
+ .lhs = type_expr,
+ .rhs = value_expr,
+ },
+ });
+ } else if (value_expr == 0) {
+ return p.addNode(.{
+ .tag = .container_field_align,
+ .main_token = name_token,
+ .data = .{
+ .lhs = type_expr,
+ .rhs = align_expr,
+ },
+ });
+ } else {
+ return p.addNode(.{
+ .tag = .container_field,
+ .main_token = name_token,
+ .data = .{
+ .lhs = type_expr,
+ .rhs = try p.addExtra(Node.ContainerField{
+ .value_expr = value_expr,
+ .align_expr = align_expr,
+ }),
+ },
+ });
+ }
+ }
+
+ fn expectContainerFieldRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
+ return p.expectContainerField() catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.ParseError => {
+ p.findNextContainerMember();
+ return null_node;
+ },
};
- return &node.base;
}
/// Statement
@@ -689,391 +847,1023 @@ const Parser = struct {
/// / LabeledStatement
/// / SwitchExpr
/// / AssignExpr SEMICOLON
- fn parseStatement(p: *Parser) Error!?*Node {
- const comptime_token = p.eatToken(.Keyword_comptime);
+ fn parseStatement(p: *Parser) Error!Node.Index {
+ const comptime_token = p.eatToken(.keyword_comptime);
- if (try p.parseVarDecl(.{
- .comptime_token = comptime_token,
- })) |node| {
- return node;
+ const var_decl = try p.parseVarDecl();
+ if (var_decl != 0) {
+ _ = try p.expectTokenRecoverable(.semicolon);
+ return var_decl;
}
if (comptime_token) |token| {
- const block_expr = try p.expectNode(parseBlockExprStatement, .{
- .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
+ return p.addNode(.{
+ .tag = .@"comptime",
+ .main_token = token,
+ .data = .{
+ .lhs = try p.expectBlockExprStatement(),
+ .rhs = undefined,
+ },
});
+ }
- const node = try p.arena.allocator.create(Node.Comptime);
- node.* = .{
- .doc_comments = null,
- .comptime_token = token,
- .expr = block_expr,
- };
- return &node.base;
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_nosuspend => {
+ return p.addNode(.{
+ .tag = .@"nosuspend",
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = try p.expectBlockExprStatement(),
+ .rhs = undefined,
+ },
+ });
+ },
+ .keyword_suspend => {
+ const token = p.nextToken();
+ const block_expr: Node.Index = if (p.eatToken(.semicolon) != null)
+ 0
+ else
+ try p.expectBlockExprStatement();
+ return p.addNode(.{
+ .tag = .@"suspend",
+ .main_token = token,
+ .data = .{
+ .lhs = block_expr,
+ .rhs = undefined,
+ },
+ });
+ },
+ .keyword_defer => return p.addNode(.{
+ .tag = .@"defer",
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = try p.expectBlockExprStatement(),
+ },
+ }),
+ .keyword_errdefer => return p.addNode(.{
+ .tag = .@"errdefer",
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = try p.parsePayload(),
+ .rhs = try p.expectBlockExprStatement(),
+ },
+ }),
+ .keyword_switch => return p.expectSwitchExpr(),
+ .keyword_if => return p.expectIfStatement(),
+ else => {},
}
- if (p.eatToken(.Keyword_nosuspend)) |nosuspend_token| {
- const block_expr = try p.expectNode(parseBlockExprStatement, .{
- .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
- });
+ const labeled_statement = try p.parseLabeledStatement();
+ if (labeled_statement != 0) return labeled_statement;
- const node = try p.arena.allocator.create(Node.Nosuspend);
- node.* = .{
- .nosuspend_token = nosuspend_token,
- .expr = block_expr,
- };
- return &node.base;
+ const assign_expr = try p.parseAssignExpr();
+ if (assign_expr != 0) {
+ _ = try p.expectTokenRecoverable(.semicolon);
+ return assign_expr;
}
- if (p.eatToken(.Keyword_suspend)) |suspend_token| {
- const semicolon = p.eatToken(.Semicolon);
-
- const body_node = if (semicolon == null) blk: {
- break :blk try p.expectNode(parseBlockExprStatement, .{
- .ExpectedBlockOrExpression = .{ .token = p.tok_i },
- });
- } else null;
+ return null_node;
+ }
- const node = try p.arena.allocator.create(Node.Suspend);
- node.* = .{
- .suspend_token = suspend_token,
- .body = body_node,
- };
- return &node.base;
+ fn expectStatement(p: *Parser) !Node.Index {
+ const statement = try p.parseStatement();
+ if (statement == 0) {
+ return p.fail(.expected_statement);
}
+ return statement;
+ }
- const defer_token = p.eatToken(.Keyword_defer) orelse p.eatToken(.Keyword_errdefer);
- if (defer_token) |token| {
- const payload = if (p.token_ids[token] == .Keyword_errdefer)
- try p.parsePayload()
- else
- null;
- const expr_node = try p.expectNode(parseBlockExprStatement, .{
- .ExpectedBlockOrExpression = .{ .token = p.tok_i },
- });
- const node = try p.arena.allocator.create(Node.Defer);
- node.* = .{
- .defer_token = token,
- .expr = expr_node,
- .payload = payload,
+ /// If a parse error occurs, reports an error, but then finds the next statement
+ /// and returns that one instead. If a parse error occurs but there is no following
+ /// statement, returns 0.
+ fn expectStatementRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
+ while (true) {
+ return p.expectStatement() catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.ParseError => {
+ p.findNextStmt(); // Try to skip to the next statement.
+ if (p.token_tags[p.tok_i] == .r_brace) return null_node;
+ continue;
+ },
};
- return &node.base;
- }
-
- if (try p.parseIfStatement()) |node| return node;
- if (try p.parseLabeledStatement()) |node| return node;
- if (try p.parseSwitchExpr()) |node| return node;
- if (try p.parseAssignExpr()) |node| {
- _ = try p.expectTokenRecoverable(.Semicolon);
- return node;
}
-
- return null;
}
/// IfStatement
/// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
/// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
- fn parseIfStatement(p: *Parser) !?*Node {
- const if_node = (try p.parseIfPrefix()) orelse return null;
- const if_prefix = if_node.cast(Node.If).?;
-
- const block_expr = (try p.parseBlockExpr());
- const assign_expr = if (block_expr == null)
- try p.expectNode(parseAssignExpr, .{
- .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
- })
- else
- null;
-
- const semicolon = if (assign_expr != null) p.eatToken(.Semicolon) else null;
-
- const else_node = if (semicolon == null) blk: {
- const else_token = p.eatToken(.Keyword_else) orelse break :blk null;
- const payload = try p.parsePayload();
- const else_body = try p.expectNode(parseStatement, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
-
- const node = try p.arena.allocator.create(Node.Else);
- node.* = .{
- .else_token = else_token,
- .payload = payload,
- .body = else_body,
- };
-
- break :blk node;
- } else null;
-
- if (block_expr) |body| {
- if_prefix.body = body;
- if_prefix.@"else" = else_node;
- return if_node;
- }
-
- if (assign_expr) |body| {
- if_prefix.body = body;
- if (semicolon != null) return if_node;
- if (else_node != null) {
- if_prefix.@"else" = else_node;
- return if_node;
+ fn expectIfStatement(p: *Parser) !Node.Index {
+ const if_token = p.assertToken(.keyword_if);
+ _ = try p.expectToken(.l_paren);
+ const condition = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ const then_payload = try p.parsePtrPayload();
+
+ // TODO propose to change the syntax so that semicolons are always required
+ // inside if statements, even if there is an `else`.
+ var else_required = false;
+ const then_expr = blk: {
+ const block_expr = try p.parseBlockExpr();
+ if (block_expr != 0) break :blk block_expr;
+ const assign_expr = try p.parseAssignExpr();
+ if (assign_expr == 0) {
+ return p.fail(.expected_block_or_assignment);
}
- try p.errors.append(p.gpa, .{
- .ExpectedSemiOrElse = .{ .token = p.tok_i },
+ if (p.eatToken(.semicolon)) |_| {
+ return p.addNode(.{
+ .tag = .if_simple,
+ .main_token = if_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = assign_expr,
+ },
+ });
+ }
+ else_required = true;
+ break :blk assign_expr;
+ };
+ const else_token = p.eatToken(.keyword_else) orelse {
+ if (else_required) {
+ try p.warn(.expected_semi_or_else);
+ }
+ return p.addNode(.{
+ .tag = .if_simple,
+ .main_token = if_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = then_expr,
+ },
});
- }
-
- return if_node;
+ };
+ const else_payload = try p.parsePayload();
+ const else_expr = try p.expectStatement();
+ return p.addNode(.{
+ .tag = .@"if",
+ .main_token = if_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = try p.addExtra(Node.If{
+ .then_expr = then_expr,
+ .else_expr = else_expr,
+ }),
+ },
+ });
}
/// LabeledStatement <- BlockLabel? (Block / LoopStatement)
- fn parseLabeledStatement(p: *Parser) !?*Node {
- var colon: TokenIndex = undefined;
- const label_token = p.parseBlockLabel(&colon);
-
- if (try p.parseBlock(label_token)) |node| return node;
-
- if (try p.parseLoopStatement()) |node| {
- if (node.cast(Node.For)) |for_node| {
- for_node.label = label_token;
- } else if (node.cast(Node.While)) |while_node| {
- while_node.label = label_token;
- } else unreachable;
- return node;
- }
+ fn parseLabeledStatement(p: *Parser) !Node.Index {
+ const label_token = p.parseBlockLabel();
+ const block = try p.parseBlock();
+ if (block != 0) return block;
- if (label_token != null) {
- try p.errors.append(p.gpa, .{
- .ExpectedLabelable = .{ .token = p.tok_i },
- });
- return error.ParseError;
+ const loop_stmt = try p.parseLoopStatement();
+ if (loop_stmt != 0) return loop_stmt;
+
+ if (label_token != 0) {
+ return p.fail(.expected_labelable);
}
- return null;
+ return null_node;
}
/// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
- fn parseLoopStatement(p: *Parser) !?*Node {
- const inline_token = p.eatToken(.Keyword_inline);
+ fn parseLoopStatement(p: *Parser) !Node.Index {
+ const inline_token = p.eatToken(.keyword_inline);
- if (try p.parseForStatement()) |node| {
- node.cast(Node.For).?.inline_token = inline_token;
- return node;
- }
+ const for_statement = try p.parseForStatement();
+ if (for_statement != 0) return for_statement;
- if (try p.parseWhileStatement()) |node| {
- node.cast(Node.While).?.inline_token = inline_token;
- return node;
- }
- if (inline_token == null) return null;
+ const while_statement = try p.parseWhileStatement();
+ if (while_statement != 0) return while_statement;
+
+ if (inline_token == null) return null_node;
// If we've seen "inline", there should have been a "for" or "while"
- try p.errors.append(p.gpa, .{
- .ExpectedInlinable = .{ .token = p.tok_i },
- });
- return error.ParseError;
+ return p.fail(.expected_inlinable);
}
+ /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
/// ForStatement
/// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
/// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
- fn parseForStatement(p: *Parser) !?*Node {
- const node = (try p.parseForPrefix()) orelse return null;
- const for_prefix = node.cast(Node.For).?;
-
- if (try p.parseBlockExpr()) |block_expr_node| {
- for_prefix.body = block_expr_node;
-
- if (p.eatToken(.Keyword_else)) |else_token| {
- const statement_node = try p.expectNode(parseStatement, .{
- .InvalidToken = .{ .token = p.tok_i },
+ fn parseForStatement(p: *Parser) !Node.Index {
+ const for_token = p.eatToken(.keyword_for) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const array_expr = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ const found_payload = try p.parsePtrIndexPayload();
+ if (found_payload == 0) try p.warn(.expected_loop_payload);
+
+ // TODO propose to change the syntax so that semicolons are always required
+ // inside while statements, even if there is an `else`.
+ var else_required = false;
+ const then_expr = blk: {
+ const block_expr = try p.parseBlockExpr();
+ if (block_expr != 0) break :blk block_expr;
+ const assign_expr = try p.parseAssignExpr();
+ if (assign_expr == 0) {
+ return p.fail(.expected_block_or_assignment);
+ }
+ if (p.eatToken(.semicolon)) |_| {
+ return p.addNode(.{
+ .tag = .for_simple,
+ .main_token = for_token,
+ .data = .{
+ .lhs = array_expr,
+ .rhs = assign_expr,
+ },
});
-
- const else_node = try p.arena.allocator.create(Node.Else);
- else_node.* = .{
- .else_token = else_token,
- .payload = null,
- .body = statement_node,
- };
- for_prefix.@"else" = else_node;
-
- return node;
}
+ else_required = true;
+ break :blk assign_expr;
+ };
+ const else_token = p.eatToken(.keyword_else) orelse {
+ if (else_required) {
+ try p.warn(.expected_semi_or_else);
+ }
+ return p.addNode(.{
+ .tag = .for_simple,
+ .main_token = for_token,
+ .data = .{
+ .lhs = array_expr,
+ .rhs = then_expr,
+ },
+ });
+ };
+ return p.addNode(.{
+ .tag = .@"for",
+ .main_token = for_token,
+ .data = .{
+ .lhs = array_expr,
+ .rhs = try p.addExtra(Node.If{
+ .then_expr = then_expr,
+ .else_expr = try p.expectStatement(),
+ }),
+ },
+ });
+ }
- return node;
- }
-
- for_prefix.body = try p.expectNode(parseAssignExpr, .{
- .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
+ /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
+ /// WhileStatement
+ /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
+ /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
+ fn parseWhileStatement(p: *Parser) !Node.Index {
+ const while_token = p.eatToken(.keyword_while) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const condition = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ const then_payload = try p.parsePtrPayload();
+ const cont_expr = try p.parseWhileContinueExpr();
+
+ // TODO propose to change the syntax so that semicolons are always required
+ // inside while statements, even if there is an `else`.
+ var else_required = false;
+ const then_expr = blk: {
+ const block_expr = try p.parseBlockExpr();
+ if (block_expr != 0) break :blk block_expr;
+ const assign_expr = try p.parseAssignExpr();
+ if (assign_expr == 0) {
+ return p.fail(.expected_block_or_assignment);
+ }
+ if (p.eatToken(.semicolon)) |_| {
+ if (cont_expr == 0) {
+ return p.addNode(.{
+ .tag = .while_simple,
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = assign_expr,
+ },
+ });
+ } else {
+ return p.addNode(.{
+ .tag = .while_cont,
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = try p.addExtra(Node.WhileCont{
+ .cont_expr = cont_expr,
+ .then_expr = assign_expr,
+ }),
+ },
+ });
+ }
+ }
+ else_required = true;
+ break :blk assign_expr;
+ };
+ const else_token = p.eatToken(.keyword_else) orelse {
+ if (else_required) {
+ try p.warn(.expected_semi_or_else);
+ }
+ if (cont_expr == 0) {
+ return p.addNode(.{
+ .tag = .while_simple,
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = then_expr,
+ },
+ });
+ } else {
+ return p.addNode(.{
+ .tag = .while_cont,
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = try p.addExtra(Node.WhileCont{
+ .cont_expr = cont_expr,
+ .then_expr = then_expr,
+ }),
+ },
+ });
+ }
+ };
+ const else_payload = try p.parsePayload();
+ const else_expr = try p.expectStatement();
+ return p.addNode(.{
+ .tag = .@"while",
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = try p.addExtra(Node.While{
+ .cont_expr = cont_expr,
+ .then_expr = then_expr,
+ .else_expr = else_expr,
+ }),
+ },
});
+ }
- if (p.eatToken(.Semicolon) != null) return node;
+ /// BlockExprStatement
+ /// <- BlockExpr
+ /// / AssignExpr SEMICOLON
+ fn parseBlockExprStatement(p: *Parser) !Node.Index {
+ const block_expr = try p.parseBlockExpr();
+ if (block_expr != 0) {
+ return block_expr;
+ }
+ const assign_expr = try p.parseAssignExpr();
+ if (assign_expr != 0) {
+ _ = try p.expectTokenRecoverable(.semicolon);
+ return assign_expr;
+ }
+ return null_node;
+ }
- if (p.eatToken(.Keyword_else)) |else_token| {
- const statement_node = try p.expectNode(parseStatement, .{
- .ExpectedStatement = .{ .token = p.tok_i },
- });
+ fn expectBlockExprStatement(p: *Parser) !Node.Index {
+ const node = try p.parseBlockExprStatement();
+ if (node == 0) {
+ return p.fail(.expected_block_or_expr);
+ }
+ return node;
+ }
- const else_node = try p.arena.allocator.create(Node.Else);
- else_node.* = .{
- .else_token = else_token,
- .payload = null,
- .body = statement_node,
- };
- for_prefix.@"else" = else_node;
- return node;
+ /// BlockExpr <- BlockLabel? Block
+ fn parseBlockExpr(p: *Parser) Error!Node.Index {
+ switch (p.token_tags[p.tok_i]) {
+ .identifier => {
+ if (p.token_tags[p.tok_i + 1] == .colon and
+ p.token_tags[p.tok_i + 2] == .l_brace)
+ {
+ p.tok_i += 2;
+ return p.parseBlock();
+ } else {
+ return null_node;
+ }
+ },
+ .l_brace => return p.parseBlock(),
+ else => return null_node,
}
+ }
- try p.errors.append(p.gpa, .{
- .ExpectedSemiOrElse = .{ .token = p.tok_i },
+ /// AssignExpr <- Expr (AssignOp Expr)?
+ /// AssignOp
+ /// <- ASTERISKEQUAL
+ /// / SLASHEQUAL
+ /// / PERCENTEQUAL
+ /// / PLUSEQUAL
+ /// / MINUSEQUAL
+ /// / LARROW2EQUAL
+ /// / RARROW2EQUAL
+ /// / AMPERSANDEQUAL
+ /// / CARETEQUAL
+ /// / PIPEEQUAL
+ /// / ASTERISKPERCENTEQUAL
+ /// / PLUSPERCENTEQUAL
+ /// / MINUSPERCENTEQUAL
+ /// / EQUAL
+ fn parseAssignExpr(p: *Parser) !Node.Index {
+ const expr = try p.parseExpr();
+ if (expr == 0) return null_node;
+
+ const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
+ .asterisk_equal => .assign_mul,
+ .slash_equal => .assign_div,
+ .percent_equal => .assign_mod,
+ .plus_equal => .assign_add,
+ .minus_equal => .assign_sub,
+ .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left,
+ .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right,
+ .ampersand_equal => .assign_bit_and,
+ .caret_equal => .assign_bit_xor,
+ .pipe_equal => .assign_bit_or,
+ .asterisk_percent_equal => .assign_mul_wrap,
+ .plus_percent_equal => .assign_add_wrap,
+ .minus_percent_equal => .assign_sub_wrap,
+ .equal => .assign,
+ else => return expr,
+ };
+ return p.addNode(.{
+ .tag = tag,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = expr,
+ .rhs = try p.expectExpr(),
+ },
});
+ }
- return node;
+ fn expectAssignExpr(p: *Parser) !Node.Index {
+ const expr = try p.parseAssignExpr();
+ if (expr == 0) {
+ return p.fail(.expected_expr_or_assignment);
+ }
+ return expr;
}
- /// WhileStatement
- /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
- /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
- fn parseWhileStatement(p: *Parser) !?*Node {
- const node = (try p.parseWhilePrefix()) orelse return null;
- const while_prefix = node.cast(Node.While).?;
+ /// Expr <- BoolOrExpr
+ fn parseExpr(p: *Parser) Error!Node.Index {
+ return p.parseBoolOrExpr();
+ }
- if (try p.parseBlockExpr()) |block_expr_node| {
- while_prefix.body = block_expr_node;
+ fn expectExpr(p: *Parser) Error!Node.Index {
+ const node = try p.parseExpr();
+ if (node == 0) {
+ return p.fail(.expected_expr);
+ } else {
+ return node;
+ }
+ }
- if (p.eatToken(.Keyword_else)) |else_token| {
- const payload = try p.parsePayload();
+ /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
+ fn parseBoolOrExpr(p: *Parser) Error!Node.Index {
+ var res = try p.parseBoolAndExpr();
+ if (res == 0) return null_node;
- const statement_node = try p.expectNode(parseStatement, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
+ while (true) {
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_or => {
+ const or_token = p.nextToken();
+ const rhs = try p.parseBoolAndExpr();
+ if (rhs == 0) {
+ return p.fail(.invalid_token);
+ }
+ res = try p.addNode(.{
+ .tag = .bool_or,
+ .main_token = or_token,
+ .data = .{
+ .lhs = res,
+ .rhs = rhs,
+ },
+ });
+ },
+ else => return res,
+ }
+ }
+ }
- const else_node = try p.arena.allocator.create(Node.Else);
- else_node.* = .{
- .else_token = else_token,
- .payload = payload,
- .body = statement_node,
- };
- while_prefix.@"else" = else_node;
+ /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
+ fn parseBoolAndExpr(p: *Parser) !Node.Index {
+ var res = try p.parseCompareExpr();
+ if (res == 0) return null_node;
- return node;
+ while (true) {
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_and => {
+ const and_token = p.nextToken();
+ const rhs = try p.parseCompareExpr();
+ if (rhs == 0) {
+ return p.fail(.invalid_token);
+ }
+ res = try p.addNode(.{
+ .tag = .bool_and,
+ .main_token = and_token,
+ .data = .{
+ .lhs = res,
+ .rhs = rhs,
+ },
+ });
+ },
+ .invalid_ampersands => {
+ try p.warn(.invalid_and);
+ p.tok_i += 1;
+ return p.parseCompareExpr();
+ },
+ else => return res,
}
-
- return node;
}
+ }
- while_prefix.body = try p.expectNode(parseAssignExpr, .{
- .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
+ /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
+ /// CompareOp
+ /// <- EQUALEQUAL
+ /// / EXCLAMATIONMARKEQUAL
+ /// / LARROW
+ /// / RARROW
+ /// / LARROWEQUAL
+ /// / RARROWEQUAL
+ fn parseCompareExpr(p: *Parser) !Node.Index {
+ const expr = try p.parseBitwiseExpr();
+ if (expr == 0) return null_node;
+
+ const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
+ .equal_equal => .equal_equal,
+ .bang_equal => .bang_equal,
+ .angle_bracket_left => .less_than,
+ .angle_bracket_right => .greater_than,
+ .angle_bracket_left_equal => .less_or_equal,
+ .angle_bracket_right_equal => .greater_or_equal,
+ else => return expr,
+ };
+ return p.addNode(.{
+ .tag = tag,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = expr,
+ .rhs = try p.expectBitwiseExpr(),
+ },
});
+ }
- if (p.eatToken(.Semicolon) != null) return node;
-
- if (p.eatToken(.Keyword_else)) |else_token| {
- const payload = try p.parsePayload();
+ /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
+ /// BitwiseOp
+ /// <- AMPERSAND
+ /// / CARET
+ /// / PIPE
+ /// / KEYWORD_orelse
+ /// / KEYWORD_catch Payload?
+ fn parseBitwiseExpr(p: *Parser) !Node.Index {
+ var res = try p.parseBitShiftExpr();
+ if (res == 0) return null_node;
- const statement_node = try p.expectNode(parseStatement, .{
- .ExpectedStatement = .{ .token = p.tok_i },
+ while (true) {
+ const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
+ .ampersand => .bit_and,
+ .caret => .bit_xor,
+ .pipe => .bit_or,
+ .keyword_orelse => .@"orelse",
+ .keyword_catch => {
+ const catch_token = p.nextToken();
+ _ = try p.parsePayload();
+ const rhs = try p.parseBitShiftExpr();
+ if (rhs == 0) {
+ return p.fail(.invalid_token);
+ }
+ res = try p.addNode(.{
+ .tag = .@"catch",
+ .main_token = catch_token,
+ .data = .{
+ .lhs = res,
+ .rhs = rhs,
+ },
+ });
+ continue;
+ },
+ else => return res,
+ };
+ res = try p.addNode(.{
+ .tag = tag,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = res,
+ .rhs = try p.expectBitShiftExpr(),
+ },
});
+ }
+ }
- const else_node = try p.arena.allocator.create(Node.Else);
- else_node.* = .{
- .else_token = else_token,
- .payload = payload,
- .body = statement_node,
- };
- while_prefix.@"else" = else_node;
+ fn expectBitwiseExpr(p: *Parser) Error!Node.Index {
+ const node = try p.parseBitwiseExpr();
+ if (node == 0) {
+ return p.fail(.invalid_token);
+ } else {
return node;
}
+ }
- try p.errors.append(p.gpa, .{
- .ExpectedSemiOrElse = .{ .token = p.tok_i },
- });
+ /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*
+ /// BitShiftOp
+ /// <- LARROW2
+ /// / RARROW2
+ fn parseBitShiftExpr(p: *Parser) Error!Node.Index {
+ var res = try p.parseAdditionExpr();
+ if (res == 0) return null_node;
- return node;
+ while (true) {
+ const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
+ .angle_bracket_angle_bracket_left => .bit_shift_left,
+ .angle_bracket_angle_bracket_right => .bit_shift_right,
+ else => return res,
+ };
+ res = try p.addNode(.{
+ .tag = tag,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = res,
+ .rhs = try p.expectAdditionExpr(),
+ },
+ });
+ }
}
- /// BlockExprStatement
- /// <- BlockExpr
- /// / AssignExpr SEMICOLON
- fn parseBlockExprStatement(p: *Parser) !?*Node {
- if (try p.parseBlockExpr()) |node| return node;
- if (try p.parseAssignExpr()) |node| {
- _ = try p.expectTokenRecoverable(.Semicolon);
+ fn expectBitShiftExpr(p: *Parser) Error!Node.Index {
+ const node = try p.parseBitShiftExpr();
+ if (node == 0) {
+ return p.fail(.invalid_token);
+ } else {
return node;
}
- return null;
}
- /// BlockExpr <- BlockLabel? Block
- fn parseBlockExpr(p: *Parser) Error!?*Node {
- var colon: TokenIndex = undefined;
- const label_token = p.parseBlockLabel(&colon);
- const block_node = (try p.parseBlock(label_token)) orelse {
- if (label_token) |label| {
- p.putBackToken(label + 1); // ":"
- p.putBackToken(label); // IDENTIFIER
- }
- return null;
- };
- return block_node;
- }
-
- /// AssignExpr <- Expr (AssignOp Expr)?
- fn parseAssignExpr(p: *Parser) !?*Node {
- return p.parseBinOpExpr(parseAssignOp, parseExpr, .Once);
- }
+ /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
+ /// AdditionOp
+ /// <- PLUS
+ /// / MINUS
+ /// / PLUS2
+ /// / PLUSPERCENT
+ /// / MINUSPERCENT
+ fn parseAdditionExpr(p: *Parser) Error!Node.Index {
+ var res = try p.parseMultiplyExpr();
+ if (res == 0) return null_node;
- /// Expr <- BoolOrExpr
- fn parseExpr(p: *Parser) Error!?*Node {
- return p.parsePrefixOpExpr(parseTry, parseBoolOrExpr);
+ while (true) {
+ const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
+ .plus => .add,
+ .minus => .sub,
+ .plus_plus => .array_cat,
+ .plus_percent => .add_wrap,
+ .minus_percent => .sub_wrap,
+ else => return res,
+ };
+ res = try p.addNode(.{
+ .tag = tag,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = res,
+ .rhs = try p.expectMultiplyExpr(),
+ },
+ });
+ }
}
- /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
- fn parseBoolOrExpr(p: *Parser) !?*Node {
- return p.parseBinOpExpr(
- SimpleBinOpParseFn(.Keyword_or, .BoolOr),
- parseBoolAndExpr,
- .Infinitely,
- );
+ fn expectAdditionExpr(p: *Parser) Error!Node.Index {
+ const node = try p.parseAdditionExpr();
+ if (node == 0) {
+ return p.fail(.invalid_token);
+ }
+ return node;
}
- /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
- fn parseBoolAndExpr(p: *Parser) !?*Node {
- return p.parseBinOpExpr(
- SimpleBinOpParseFn(.Keyword_and, .BoolAnd),
- parseCompareExpr,
- .Infinitely,
- );
- }
+ /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
+ /// MultiplyOp
+ /// <- PIPE2
+ /// / ASTERISK
+ /// / SLASH
+ /// / PERCENT
+ /// / ASTERISK2
+ /// / ASTERISKPERCENT
+ fn parseMultiplyExpr(p: *Parser) Error!Node.Index {
+ var res = try p.parsePrefixExpr();
+ if (res == 0) return null_node;
- /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
- fn parseCompareExpr(p: *Parser) !?*Node {
- return p.parseBinOpExpr(parseCompareOp, parseBitwiseExpr, .Once);
+ while (true) {
+ const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
+ .pipe_pipe => .merge_error_sets,
+ .asterisk => .mul,
+ .slash => .div,
+ .percent => .mod,
+ .asterisk_asterisk => .array_mult,
+ .asterisk_percent => .mul_wrap,
+ else => return res,
+ };
+ res = try p.addNode(.{
+ .tag = tag,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = res,
+ .rhs = try p.expectPrefixExpr(),
+ },
+ });
+ }
}
- /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
- fn parseBitwiseExpr(p: *Parser) !?*Node {
- return p.parseBinOpExpr(parseBitwiseOp, parseBitShiftExpr, .Infinitely);
+ fn expectMultiplyExpr(p: *Parser) Error!Node.Index {
+ const node = try p.parseMultiplyExpr();
+ if (node == 0) {
+ return p.fail(.invalid_token);
+ }
+ return node;
}
- /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*
- fn parseBitShiftExpr(p: *Parser) !?*Node {
- return p.parseBinOpExpr(parseBitShiftOp, parseAdditionExpr, .Infinitely);
+ /// PrefixExpr <- PrefixOp* PrimaryExpr
+ /// PrefixOp
+ /// <- EXCLAMATIONMARK
+ /// / MINUS
+ /// / TILDE
+ /// / MINUSPERCENT
+ /// / AMPERSAND
+ /// / KEYWORD_try
+ /// / KEYWORD_await
+ fn parsePrefixExpr(p: *Parser) Error!Node.Index {
+ const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
+ .bang => .bool_not,
+ .minus => .negation,
+ .tilde => .bit_not,
+ .minus_percent => .negation_wrap,
+ .ampersand => .address_of,
+ .keyword_try => .@"try",
+ .keyword_await => .@"await",
+ else => return p.parsePrimaryExpr(),
+ };
+ return p.addNode(.{
+ .tag = tag,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = try p.expectPrefixExpr(),
+ .rhs = undefined,
+ },
+ });
}
- /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
- fn parseAdditionExpr(p: *Parser) !?*Node {
- return p.parseBinOpExpr(parseAdditionOp, parseMultiplyExpr, .Infinitely);
+ fn expectPrefixExpr(p: *Parser) Error!Node.Index {
+ const node = try p.parsePrefixExpr();
+ if (node == 0) {
+ return p.fail(.expected_prefix_expr);
+ }
+ return node;
}
- /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
- fn parseMultiplyExpr(p: *Parser) !?*Node {
- return p.parseBinOpExpr(parseMultiplyOp, parsePrefixExpr, .Infinitely);
+ /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr
+ /// PrefixTypeOp
+ /// <- QUESTIONMARK
+ /// / KEYWORD_anyframe MINUSRARROW
+ /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
+ /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
+ /// PtrTypeStart
+ /// <- ASTERISK
+ /// / ASTERISK2
+ /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
+ /// ArrayTypeStart <- LBRACKET Expr? (COLON Expr)? RBRACKET
+ fn parseTypeExpr(p: *Parser) Error!Node.Index {
+ switch (p.token_tags[p.tok_i]) {
+ .question_mark => return p.addNode(.{
+ .tag = .optional_type,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = try p.expectTypeExpr(),
+ .rhs = undefined,
+ },
+ }),
+ .keyword_anyframe => switch (p.token_tags[p.tok_i + 1]) {
+ .arrow => return p.addNode(.{
+ .tag = .anyframe_type,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = p.nextToken(),
+ .rhs = try p.expectTypeExpr(),
+ },
+ }),
+ else => return p.parseErrorUnionExpr(),
+ },
+ .asterisk => {
+ const asterisk = p.nextToken();
+ const mods = try p.parsePtrModifiers();
+ const elem_type = try p.expectTypeExpr();
+ if (mods.bit_range_start == 0) {
+ return p.addNode(.{
+ .tag = .ptr_type_aligned,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = mods.align_node,
+ .rhs = elem_type,
+ },
+ });
+ } else {
+ return p.addNode(.{
+ .tag = .ptr_type_bit_range,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = try p.addExtra(Node.PtrTypeBitRange{
+ .sentinel = 0,
+ .align_node = mods.align_node,
+ .bit_range_start = mods.bit_range_start,
+ .bit_range_end = mods.bit_range_end,
+ }),
+ .rhs = elem_type,
+ },
+ });
+ }
+ },
+ .asterisk_asterisk => {
+ const asterisk = p.nextToken();
+ const mods = try p.parsePtrModifiers();
+ const elem_type = try p.expectTypeExpr();
+ const inner: Node.Index = inner: {
+ if (mods.bit_range_start == 0) {
+ break :inner try p.addNode(.{
+ .tag = .ptr_type_aligned,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = mods.align_node,
+ .rhs = elem_type,
+ },
+ });
+ } else {
+ break :inner try p.addNode(.{
+ .tag = .ptr_type_bit_range,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = try p.addExtra(Node.PtrTypeBitRange{
+ .sentinel = 0,
+ .align_node = mods.align_node,
+ .bit_range_start = mods.bit_range_start,
+ .bit_range_end = mods.bit_range_end,
+ }),
+ .rhs = elem_type,
+ },
+ });
+ }
+ };
+ return p.addNode(.{
+ .tag = .ptr_type_aligned,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = 0,
+ .rhs = inner,
+ },
+ });
+ },
+ .l_bracket => switch (p.token_tags[p.tok_i + 1]) {
+ .asterisk => {
+ const lbracket = p.nextToken();
+ const asterisk = p.nextToken();
+ var sentinel: Node.Index = 0;
+ prefix: {
+ if (p.eatToken(.identifier)) |ident| {
+ const token_slice = p.source[p.token_starts[ident]..][0..2];
+ if (!std.mem.eql(u8, token_slice, "c]")) {
+ p.tok_i -= 1;
+ } else {
+ break :prefix;
+ }
+ }
+ if (p.eatToken(.colon)) |_| {
+ sentinel = try p.expectExpr();
+ }
+ }
+ _ = try p.expectToken(.r_bracket);
+ const mods = try p.parsePtrModifiers();
+ const elem_type = try p.expectTypeExpr();
+ if (mods.bit_range_start == 0) {
+ if (sentinel == 0) {
+ return p.addNode(.{
+ .tag = .ptr_type_aligned,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = mods.align_node,
+ .rhs = elem_type,
+ },
+ });
+ } else if (mods.align_node == 0) {
+ return p.addNode(.{
+ .tag = .ptr_type_sentinel,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = sentinel,
+ .rhs = elem_type,
+ },
+ });
+ } else {
+ return p.addNode(.{
+ .tag = .ptr_type,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = try p.addExtra(Node.PtrType{
+ .sentinel = sentinel,
+ .align_node = mods.align_node,
+ }),
+ .rhs = elem_type,
+ },
+ });
+ }
+ } else {
+ return p.addNode(.{
+ .tag = .ptr_type_bit_range,
+ .main_token = asterisk,
+ .data = .{
+ .lhs = try p.addExtra(Node.PtrTypeBitRange{
+ .sentinel = sentinel,
+ .align_node = mods.align_node,
+ .bit_range_start = mods.bit_range_start,
+ .bit_range_end = mods.bit_range_end,
+ }),
+ .rhs = elem_type,
+ },
+ });
+ }
+ },
+ else => {
+ const lbracket = p.nextToken();
+ const len_expr = try p.parseExpr();
+ const sentinel: Node.Index = if (p.eatToken(.colon)) |_|
+ try p.expectExpr()
+ else
+ 0;
+ _ = try p.expectToken(.r_bracket);
+ const mods = try p.parsePtrModifiers();
+ const elem_type = try p.expectTypeExpr();
+ if (mods.bit_range_start != 0) {
+ try p.warnMsg(.{
+ .tag = .invalid_bit_range,
+ .token = p.nodes.items(.main_token)[mods.bit_range_start],
+ });
+ }
+ if (len_expr == 0) {
+ if (sentinel == 0) {
+ return p.addNode(.{
+ .tag = .ptr_type_aligned,
+ .main_token = lbracket,
+ .data = .{
+ .lhs = mods.align_node,
+ .rhs = elem_type,
+ },
+ });
+ } else if (mods.align_node == 0) {
+ return p.addNode(.{
+ .tag = .ptr_type_sentinel,
+ .main_token = lbracket,
+ .data = .{
+ .lhs = sentinel,
+ .rhs = elem_type,
+ },
+ });
+ } else {
+ return p.addNode(.{
+ .tag = .ptr_type,
+ .main_token = lbracket,
+ .data = .{
+ .lhs = try p.addExtra(Node.PtrType{
+ .sentinel = sentinel,
+ .align_node = mods.align_node,
+ }),
+ .rhs = elem_type,
+ },
+ });
+ }
+ } else {
+ if (mods.align_node != 0) {
+ try p.warnMsg(.{
+ .tag = .invalid_align,
+ .token = p.nodes.items(.main_token)[mods.align_node],
+ });
+ }
+ if (sentinel == 0) {
+ return p.addNode(.{
+ .tag = .array_type,
+ .main_token = lbracket,
+ .data = .{
+ .lhs = len_expr,
+ .rhs = elem_type,
+ },
+ });
+ } else {
+ return p.addNode(.{
+ .tag = .array_type_sentinel,
+ .main_token = lbracket,
+ .data = .{
+ .lhs = len_expr,
+ .rhs = try p.addExtra(.{
+ .elem_type = elem_type,
+ .sentinel = sentinel,
+ }),
+ },
+ });
+ }
+ }
+ },
+ },
+ else => return p.parseErrorUnionExpr(),
+ }
}
- /// PrefixExpr <- PrefixOp* PrimaryExpr
- fn parsePrefixExpr(p: *Parser) !?*Node {
- return p.parsePrefixOpExpr(parsePrefixOp, parsePrimaryExpr);
+ fn expectTypeExpr(p: *Parser) Error!Node.Index {
+ const node = try p.parseTypeExpr();
+ if (node == 0) {
+ return p.fail(.expected_type_expr);
+ }
+ return node;
}
/// PrimaryExpr
@@ -1088,434 +1878,601 @@ const Parser = struct {
/// / BlockLabel? LoopExpr
/// / Block
/// / CurlySuffixExpr
- fn parsePrimaryExpr(p: *Parser) !?*Node {
- if (try p.parseAsmExpr()) |node| return node;
- if (try p.parseIfExpr()) |node| return node;
-
- if (p.eatToken(.Keyword_break)) |token| {
- const label = try p.parseBreakLabel();
- const expr_node = try p.parseExpr();
- const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{
- .tag = .Break,
- .ltoken = token,
- }, .{
- .label = label,
- .rhs = expr_node,
- });
- return &node.base;
- }
-
- if (p.eatToken(.Keyword_comptime)) |token| {
- const expr_node = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- const node = try p.arena.allocator.create(Node.Comptime);
- node.* = .{
- .doc_comments = null,
- .comptime_token = token,
- .expr = expr_node,
- };
- return &node.base;
- }
-
- if (p.eatToken(.Keyword_nosuspend)) |token| {
- const expr_node = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- const node = try p.arena.allocator.create(Node.Nosuspend);
- node.* = .{
- .nosuspend_token = token,
- .expr = expr_node,
- };
- return &node.base;
- }
-
- if (p.eatToken(.Keyword_continue)) |token| {
- const label = try p.parseBreakLabel();
- const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{
- .tag = .Continue,
- .ltoken = token,
- }, .{
- .label = label,
- .rhs = null,
- });
- return &node.base;
- }
-
- if (p.eatToken(.Keyword_resume)) |token| {
- const expr_node = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- const node = try p.arena.allocator.create(Node.SimplePrefixOp);
- node.* = .{
- .base = .{ .tag = .Resume },
- .op_token = token,
- .rhs = expr_node,
- };
- return &node.base;
- }
-
- if (p.eatToken(.Keyword_return)) |token| {
- const expr_node = try p.parseExpr();
- const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{
- .tag = .Return,
- .ltoken = token,
- }, .{
- .rhs = expr_node,
- });
- return &node.base;
- }
-
- var colon: TokenIndex = undefined;
- const label = p.parseBlockLabel(&colon);
- if (try p.parseLoopExpr()) |node| {
- if (node.cast(Node.For)) |for_node| {
- for_node.label = label;
- } else if (node.cast(Node.While)) |while_node| {
- while_node.label = label;
- } else unreachable;
- return node;
- }
- if (label) |token| {
- p.putBackToken(token + 1); // ":"
- p.putBackToken(token); // IDENTIFIER
+ fn parsePrimaryExpr(p: *Parser) !Node.Index {
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_asm => return p.expectAsmExpr(),
+ .keyword_if => return p.parseIfExpr(),
+ .keyword_break => {
+ p.tok_i += 1;
+ return p.addNode(.{
+ .tag = .@"break",
+ .main_token = p.tok_i - 1,
+ .data = .{
+ .lhs = try p.parseBreakLabel(),
+ .rhs = try p.parseExpr(),
+ },
+ });
+ },
+ .keyword_continue => {
+ p.tok_i += 1;
+ return p.addNode(.{
+ .tag = .@"continue",
+ .main_token = p.tok_i - 1,
+ .data = .{
+ .lhs = try p.parseBreakLabel(),
+ .rhs = undefined,
+ },
+ });
+ },
+ .keyword_comptime => {
+ p.tok_i += 1;
+ return p.addNode(.{
+ .tag = .@"comptime",
+ .main_token = p.tok_i - 1,
+ .data = .{
+ .lhs = try p.expectExpr(),
+ .rhs = undefined,
+ },
+ });
+ },
+ .keyword_nosuspend => {
+ p.tok_i += 1;
+ return p.addNode(.{
+ .tag = .@"nosuspend",
+ .main_token = p.tok_i - 1,
+ .data = .{
+ .lhs = try p.expectExpr(),
+ .rhs = undefined,
+ },
+ });
+ },
+ .keyword_resume => {
+ p.tok_i += 1;
+ return p.addNode(.{
+ .tag = .@"resume",
+ .main_token = p.tok_i - 1,
+ .data = .{
+ .lhs = try p.expectExpr(),
+ .rhs = undefined,
+ },
+ });
+ },
+ .keyword_return => {
+ p.tok_i += 1;
+ return p.addNode(.{
+ .tag = .@"return",
+ .main_token = p.tok_i - 1,
+ .data = .{
+ .lhs = try p.parseExpr(),
+ .rhs = undefined,
+ },
+ });
+ },
+ .identifier => {
+ if (p.token_tags[p.tok_i + 1] == .colon) {
+ switch (p.token_tags[p.tok_i + 2]) {
+ .keyword_inline => {
+ p.tok_i += 3;
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_for => return p.parseForExpr(),
+ .keyword_while => return p.parseWhileExpr(),
+ else => return p.fail(.expected_inlinable),
+ }
+ },
+ .keyword_for => {
+ p.tok_i += 2;
+ return p.parseForExpr();
+ },
+ .keyword_while => {
+ p.tok_i += 2;
+ return p.parseWhileExpr();
+ },
+ .l_brace => {
+ p.tok_i += 2;
+ return p.parseBlock();
+ },
+ else => return p.parseCurlySuffixExpr(),
+ }
+ } else {
+ return p.parseCurlySuffixExpr();
+ }
+ },
+ .keyword_inline => {
+ p.tok_i += 2;
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_for => return p.parseForExpr(),
+ .keyword_while => return p.parseWhileExpr(),
+ else => return p.fail(.expected_inlinable),
+ }
+ },
+ .keyword_for => return p.parseForExpr(),
+ .keyword_while => return p.parseWhileExpr(),
+ .l_brace => return p.parseBlock(),
+ else => return p.parseCurlySuffixExpr(),
}
-
- if (try p.parseBlock(null)) |node| return node;
- if (try p.parseCurlySuffixExpr()) |node| return node;
-
- return null;
}
/// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
- fn parseIfExpr(p: *Parser) !?*Node {
+ fn parseIfExpr(p: *Parser) !Node.Index {
return p.parseIf(parseExpr);
}
/// Block <- LBRACE Statement* RBRACE
- fn parseBlock(p: *Parser, label_token: ?TokenIndex) !?*Node {
- const lbrace = p.eatToken(.LBrace) orelse return null;
-
- var statements = std.ArrayList(*Node).init(p.gpa);
- defer statements.deinit();
-
- while (true) {
- const statement = (p.parseStatement() catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.ParseError => {
- // try to skip to the next statement
- p.findNextStmt();
- continue;
+ fn parseBlock(p: *Parser) !Node.Index {
+ const lbrace = p.eatToken(.l_brace) orelse return null_node;
+
+ if (p.eatToken(.r_brace)) |_| {
+ return p.addNode(.{
+ .tag = .block_two,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = 0,
+ .rhs = 0,
},
- }) orelse break;
- try statements.append(statement);
+ });
}
- const rbrace = try p.expectToken(.RBrace);
-
- const statements_len = @intCast(NodeIndex, statements.items.len);
-
- if (label_token) |label| {
- const block_node = try Node.LabeledBlock.alloc(&p.arena.allocator, statements_len);
- block_node.* = .{
- .label = label,
- .lbrace = lbrace,
- .statements_len = statements_len,
- .rbrace = rbrace,
- };
- std.mem.copy(*Node, block_node.statements(), statements.items);
- return &block_node.base;
- } else {
- const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len);
- block_node.* = .{
- .lbrace = lbrace,
- .statements_len = statements_len,
- .rbrace = rbrace,
- };
- std.mem.copy(*Node, block_node.statements(), statements.items);
- return &block_node.base;
+ const stmt_one = try p.expectStatementRecoverable();
+ if (p.eatToken(.r_brace)) |_| {
+ const semicolon = p.token_tags[p.tok_i - 2] == .semicolon;
+ return p.addNode(.{
+ .tag = if (semicolon) .block_two_semicolon else .block_two,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = stmt_one,
+ .rhs = 0,
+ },
+ });
}
- }
-
- /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
- fn parseLoopExpr(p: *Parser) !?*Node {
- const inline_token = p.eatToken(.Keyword_inline);
-
- if (try p.parseForExpr()) |node| {
- node.cast(Node.For).?.inline_token = inline_token;
- return node;
+ const stmt_two = try p.expectStatementRecoverable();
+ if (p.eatToken(.r_brace)) |_| {
+ const semicolon = p.token_tags[p.tok_i - 2] == .semicolon;
+ return p.addNode(.{
+ .tag = if (semicolon) .block_two_semicolon else .block_two,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = stmt_one,
+ .rhs = stmt_two,
+ },
+ });
}
- if (try p.parseWhileExpr()) |node| {
- node.cast(Node.While).?.inline_token = inline_token;
- return node;
- }
+ var statements = std.ArrayList(Node.Index).init(p.gpa);
+ defer statements.deinit();
- if (inline_token == null) return null;
+ try statements.appendSlice(&.{ stmt_one, stmt_two });
- // If we've seen "inline", there should have been a "for" or "while"
- try p.errors.append(p.gpa, .{
- .ExpectedInlinable = .{ .token = p.tok_i },
+ while (true) {
+ const statement = try p.expectStatementRecoverable();
+ if (statement == 0) break;
+ try statements.append(statement);
+ if (p.token_tags[p.tok_i] == .r_brace) break;
+ }
+ _ = try p.expectToken(.r_brace);
+ const semicolon = p.token_tags[p.tok_i - 2] == .semicolon;
+ const statements_span = try p.listToSpan(statements.items);
+ return p.addNode(.{
+ .tag = if (semicolon) .block_semicolon else .block,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = statements_span.start,
+ .rhs = statements_span.end,
+ },
});
- return error.ParseError;
}
+ /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
/// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
- fn parseForExpr(p: *Parser) !?*Node {
- const node = (try p.parseForPrefix()) orelse return null;
- const for_prefix = node.cast(Node.For).?;
-
- const body_node = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- for_prefix.body = body_node;
-
- if (p.eatToken(.Keyword_else)) |else_token| {
- const body = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
+ fn parseForExpr(p: *Parser) !Node.Index {
+ const for_token = p.eatToken(.keyword_for) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const array_expr = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ const found_payload = try p.parsePtrIndexPayload();
+ if (found_payload == 0) try p.warn(.expected_loop_payload);
+
+ const then_expr = try p.expectExpr();
+ const else_token = p.eatToken(.keyword_else) orelse {
+ return p.addNode(.{
+ .tag = .for_simple,
+ .main_token = for_token,
+ .data = .{
+ .lhs = array_expr,
+ .rhs = then_expr,
+ },
});
-
- const else_node = try p.arena.allocator.create(Node.Else);
- else_node.* = .{
- .else_token = else_token,
- .payload = null,
- .body = body,
- };
-
- for_prefix.@"else" = else_node;
- }
-
- return node;
+ };
+ const else_expr = try p.expectExpr();
+ return p.addNode(.{
+ .tag = .@"for",
+ .main_token = for_token,
+ .data = .{
+ .lhs = array_expr,
+ .rhs = try p.addExtra(Node.If{
+ .then_expr = then_expr,
+ .else_expr = else_expr,
+ }),
+ },
+ });
}
+ /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
/// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
- fn parseWhileExpr(p: *Parser) !?*Node {
- const node = (try p.parseWhilePrefix()) orelse return null;
- const while_prefix = node.cast(Node.While).?;
-
- const body_node = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
+ fn parseWhileExpr(p: *Parser) !Node.Index {
+ const while_token = p.eatToken(.keyword_while) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const condition = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ const then_payload = try p.parsePtrPayload();
+ const cont_expr = try p.parseWhileContinueExpr();
+
+ const then_expr = try p.expectExpr();
+ const else_token = p.eatToken(.keyword_else) orelse {
+ if (cont_expr == 0) {
+ return p.addNode(.{
+ .tag = .while_simple,
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = then_expr,
+ },
+ });
+ } else {
+ return p.addNode(.{
+ .tag = .while_cont,
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = try p.addExtra(Node.WhileCont{
+ .cont_expr = cont_expr,
+ .then_expr = then_expr,
+ }),
+ },
+ });
+ }
+ };
+ const else_payload = try p.parsePayload();
+ const else_expr = try p.expectExpr();
+ return p.addNode(.{
+ .tag = .@"while",
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = try p.addExtra(Node.While{
+ .cont_expr = cont_expr,
+ .then_expr = then_expr,
+ .else_expr = else_expr,
+ }),
+ },
});
- while_prefix.body = body_node;
-
- if (p.eatToken(.Keyword_else)) |else_token| {
- const payload = try p.parsePayload();
- const body = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
-
- const else_node = try p.arena.allocator.create(Node.Else);
- else_node.* = .{
- .else_token = else_token,
- .payload = payload,
- .body = body,
- };
-
- while_prefix.@"else" = else_node;
- }
-
- return node;
}
/// CurlySuffixExpr <- TypeExpr InitList?
- fn parseCurlySuffixExpr(p: *Parser) !?*Node {
- const lhs = (try p.parseTypeExpr()) orelse return null;
- const suffix_op = (try p.parseInitList(lhs)) orelse return lhs;
- return suffix_op;
- }
-
/// InitList
/// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
/// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
/// / LBRACE RBRACE
- fn parseInitList(p: *Parser, lhs: *Node) !?*Node {
- const lbrace = p.eatToken(.LBrace) orelse return null;
- var init_list = std.ArrayList(*Node).init(p.gpa);
- defer init_list.deinit();
+ fn parseCurlySuffixExpr(p: *Parser) !Node.Index {
+ const lhs = try p.parseTypeExpr();
+ if (lhs == 0) return null_node;
+ const lbrace = p.eatToken(.l_brace) orelse return lhs;
+
+ // If there are 0 or 1 items, we can use ArrayInitOne/StructInitOne;
+ // otherwise we use the full ArrayInit/StructInit.
+
+ if (p.eatToken(.r_brace)) |_| {
+ return p.addNode(.{
+ .tag = .struct_init_one,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = lhs,
+ .rhs = 0,
+ },
+ });
+ }
+ const field_init = try p.parseFieldInit();
+ if (field_init != 0) {
+ const comma_one = p.eatToken(.comma);
+ if (p.eatToken(.r_brace)) |_| {
+ return p.addNode(.{
+ .tag = if (comma_one != null) .struct_init_one_comma else .struct_init_one,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = lhs,
+ .rhs = field_init,
+ },
+ });
+ }
+
+ var init_list = std.ArrayList(Node.Index).init(p.gpa);
+ defer init_list.deinit();
- if (try p.parseFieldInit()) |field_init| {
try init_list.append(field_init);
- while (p.eatToken(.Comma)) |_| {
- const next = (try p.parseFieldInit()) orelse break;
- try init_list.append(next);
- }
- const node = try Node.StructInitializer.alloc(&p.arena.allocator, init_list.items.len);
- node.* = .{
- .lhs = lhs,
- .rtoken = try p.expectToken(.RBrace),
- .list_len = init_list.items.len,
- };
- std.mem.copy(*Node, node.list(), init_list.items);
- return &node.base;
- }
- if (try p.parseExpr()) |expr| {
- try init_list.append(expr);
- while (p.eatToken(.Comma)) |_| {
- const next = (try p.parseExpr()) orelse break;
+ while (true) {
+ const next = try p.expectFieldInit();
try init_list.append(next);
+
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_brace)) |_| break;
+ continue;
+ },
+ .r_brace => break,
+ .colon, .r_paren, .r_bracket => {
+ p.tok_i -= 1;
+ return p.failExpected(.r_brace);
+ },
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
}
- const node = try Node.ArrayInitializer.alloc(&p.arena.allocator, init_list.items.len);
- node.* = .{
- .lhs = lhs,
- .rtoken = try p.expectToken(.RBrace),
- .list_len = init_list.items.len,
- };
- std.mem.copy(*Node, node.list(), init_list.items);
- return &node.base;
+ const span = try p.listToSpan(init_list.items);
+ return p.addNode(.{
+ .tag = if (p.token_tags[p.tok_i - 2] == .comma) .struct_init_comma else .struct_init,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = lhs,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
}
- const node = try p.arena.allocator.create(Node.StructInitializer);
- node.* = .{
- .lhs = lhs,
- .rtoken = try p.expectToken(.RBrace),
- .list_len = 0,
- };
- return &node.base;
- }
+ const elem_init = try p.expectExpr();
+ const comma_one = p.eatToken(.comma);
+ if (p.eatToken(.r_brace)) |_| {
+ return p.addNode(.{
+ .tag = if (comma_one != null) .array_init_one_comma else .array_init_one,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = lhs,
+ .rhs = elem_init,
+ },
+ });
+ }
+ if (comma_one == null) {
+ try p.warnExpected(.comma);
+ }
- /// InitList
- /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
- /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
- /// / LBRACE RBRACE
- fn parseAnonInitList(p: *Parser, dot: TokenIndex) !?*Node {
- const lbrace = p.eatToken(.LBrace) orelse return null;
- var init_list = std.ArrayList(*Node).init(p.gpa);
+ var init_list = std.ArrayList(Node.Index).init(p.gpa);
defer init_list.deinit();
- if (try p.parseFieldInit()) |field_init| {
- try init_list.append(field_init);
- while (p.eatToken(.Comma)) |_| {
- const next = (try p.parseFieldInit()) orelse break;
- try init_list.append(next);
- }
- const node = try Node.StructInitializerDot.alloc(&p.arena.allocator, init_list.items.len);
- node.* = .{
- .dot = dot,
- .rtoken = try p.expectToken(.RBrace),
- .list_len = init_list.items.len,
- };
- std.mem.copy(*Node, node.list(), init_list.items);
- return &node.base;
- }
+ try init_list.append(elem_init);
- if (try p.parseExpr()) |expr| {
- try init_list.append(expr);
- while (p.eatToken(.Comma)) |_| {
- const next = (try p.parseExpr()) orelse break;
- try init_list.append(next);
+ var trailing_comma = true;
+ var next = try p.parseExpr();
+ while (next != 0) : (next = try p.parseExpr()) {
+ try init_list.append(next);
+ if (p.eatToken(.comma) == null) {
+ trailing_comma = false;
+ break;
}
- const node = try Node.ArrayInitializerDot.alloc(&p.arena.allocator, init_list.items.len);
- node.* = .{
- .dot = dot,
- .rtoken = try p.expectToken(.RBrace),
- .list_len = init_list.items.len,
- };
- std.mem.copy(*Node, node.list(), init_list.items);
- return &node.base;
}
-
- const node = try p.arena.allocator.create(Node.StructInitializerDot);
- node.* = .{
- .dot = dot,
- .rtoken = try p.expectToken(.RBrace),
- .list_len = 0,
- };
- return &node.base;
- }
-
- /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr
- fn parseTypeExpr(p: *Parser) Error!?*Node {
- return p.parsePrefixOpExpr(parsePrefixTypeOp, parseErrorUnionExpr);
+ _ = try p.expectToken(.r_brace);
+ const span = try p.listToSpan(init_list.items);
+ return p.addNode(.{
+ .tag = if (trailing_comma) .array_init_comma else .array_init,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = lhs,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
}
/// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
- fn parseErrorUnionExpr(p: *Parser) !?*Node {
- const suffix_expr = (try p.parseSuffixExpr()) orelse return null;
-
- if (try SimpleBinOpParseFn(.Bang, .ErrorUnion)(p)) |node| {
- const error_union = node.castTag(.ErrorUnion).?;
- const type_expr = try p.expectNode(parseTypeExpr, .{
- .ExpectedTypeExpr = .{ .token = p.tok_i },
- });
- error_union.lhs = suffix_expr;
- error_union.rhs = type_expr;
- return node;
- }
-
- return suffix_expr;
+ fn parseErrorUnionExpr(p: *Parser) !Node.Index {
+ const suffix_expr = try p.parseSuffixExpr();
+ if (suffix_expr == 0) return null_node;
+ const bang = p.eatToken(.bang) orelse return suffix_expr;
+ return p.addNode(.{
+ .tag = .error_union,
+ .main_token = bang,
+ .data = .{
+ .lhs = suffix_expr,
+ .rhs = try p.expectTypeExpr(),
+ },
+ });
}
/// SuffixExpr
/// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
/// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
- fn parseSuffixExpr(p: *Parser) !?*Node {
- const maybe_async = p.eatToken(.Keyword_async);
- if (maybe_async) |async_token| {
- const token_fn = p.eatToken(.Keyword_fn);
- if (token_fn != null) {
- // TODO: remove this hack when async fn rewriting is
- // HACK: If we see the keyword `fn`, then we assume that
- // we are parsing an async fn proto, and not a call.
- // We therefore put back all tokens consumed by the async
- // prefix...
- p.putBackToken(token_fn.?);
- p.putBackToken(async_token);
- return p.parsePrimaryTypeExpr();
- }
- var res = try p.expectNode(parsePrimaryTypeExpr, .{
- .ExpectedPrimaryTypeExpr = .{ .token = p.tok_i },
- });
+ /// FnCallArguments <- LPAREN ExprList RPAREN
+ /// ExprList <- (Expr COMMA)* Expr?
+ fn parseSuffixExpr(p: *Parser) !Node.Index {
+ if (p.eatToken(.keyword_async)) |async_token| {
+ var res = try p.expectPrimaryTypeExpr();
- while (try p.parseSuffixOp(res)) |node| {
+ while (true) {
+ const node = try p.parseSuffixOp(res);
+ if (node == 0) break;
res = node;
}
-
- const params = (try p.parseFnCallArguments()) orelse {
- try p.errors.append(p.gpa, .{
- .ExpectedParamList = .{ .token = p.tok_i },
- });
- // ignore this, continue parsing
+ const lparen = p.nextToken();
+ if (p.token_tags[lparen] != .l_paren) {
+ p.tok_i -= 1;
+ try p.warn(.expected_param_list);
return res;
- };
- defer p.gpa.free(params.list);
- const node = try Node.Call.alloc(&p.arena.allocator, params.list.len);
- node.* = .{
- .lhs = res,
- .params_len = params.list.len,
- .async_token = async_token,
- .rtoken = params.rparen,
- };
- std.mem.copy(*Node, node.params(), params.list);
- return &node.base;
- }
- if (try p.parsePrimaryTypeExpr()) |expr| {
- var res = expr;
+ }
+ if (p.eatToken(.r_paren)) |_| {
+ return p.addNode(.{
+ .tag = .async_call_one,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = 0,
+ },
+ });
+ }
+ const param_one = try p.expectExpr();
+ const comma_one = p.eatToken(.comma);
+ if (p.eatToken(.r_paren)) |_| {
+ return p.addNode(.{
+ .tag = if (comma_one == null) .async_call_one else .async_call_one_comma,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = param_one,
+ },
+ });
+ }
+ if (comma_one == null) {
+ try p.warnExpected(.comma);
+ }
+
+ var param_list = std.ArrayList(Node.Index).init(p.gpa);
+ defer param_list.deinit();
+
+ try param_list.append(param_one);
while (true) {
- if (try p.parseSuffixOp(res)) |node| {
- res = node;
- continue;
- }
- if (try p.parseFnCallArguments()) |params| {
- defer p.gpa.free(params.list);
- const call = try Node.Call.alloc(&p.arena.allocator, params.list.len);
- call.* = .{
- .lhs = res,
- .params_len = params.list.len,
- .async_token = null,
- .rtoken = params.rparen,
- };
- std.mem.copy(*Node, call.params(), params.list);
- res = &call.base;
- continue;
+ const next = try p.expectExpr();
+ try param_list.append(next);
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_paren)) |_| {
+ const span = try p.listToSpan(param_list.items);
+ return p.addNode(.{
+ .tag = .async_call_comma,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
+ } else {
+ continue;
+ }
+ },
+ .r_paren => {
+ const span = try p.listToSpan(param_list.items);
+ return p.addNode(.{
+ .tag = .async_call,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
+ },
+ .colon, .r_brace, .r_bracket => {
+ p.tok_i -= 1;
+ return p.failExpected(.r_paren);
+ },
+ else => {
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
}
- break;
}
- return res;
}
+ var res = try p.parsePrimaryTypeExpr();
+ if (res == 0) return res;
- return null;
+ while (true) {
+ const suffix_op = try p.parseSuffixOp(res);
+ if (suffix_op != 0) {
+ res = suffix_op;
+ continue;
+ }
+ res = res: {
+ const lparen = p.eatToken(.l_paren) orelse return res;
+ if (p.eatToken(.r_paren)) |_| {
+ break :res try p.addNode(.{
+ .tag = .call_one,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = 0,
+ },
+ });
+ }
+ const param_one = try p.expectExpr();
+ const comma_one = p.eatToken(.comma);
+ if (p.eatToken(.r_paren)) |_| {
+ break :res try p.addNode(.{
+ .tag = if (comma_one == null) .call_one else .call_one_comma,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = param_one,
+ },
+ });
+ }
+ if (comma_one == null) {
+ try p.warnExpected(.comma);
+ }
+
+ var param_list = std.ArrayList(Node.Index).init(p.gpa);
+ defer param_list.deinit();
+
+ try param_list.append(param_one);
+
+ while (true) {
+ const next = try p.expectExpr();
+ try param_list.append(next);
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_paren)) |_| {
+ const span = try p.listToSpan(param_list.items);
+ break :res try p.addNode(.{
+ .tag = .call_comma,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
+ } else {
+ continue;
+ }
+ },
+ .r_paren => {
+ const span = try p.listToSpan(param_list.items);
+ break :res try p.addNode(.{
+ .tag = .call,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
+ },
+ .colon, .r_brace, .r_bracket => {
+ p.tok_i -= 1;
+ return p.failExpected(.r_paren);
+ },
+ else => {
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
+ }
+ };
+ }
}
/// PrimaryTypeExpr
@@ -1523,6 +2480,7 @@ const Parser = struct {
/// / CHAR_LITERAL
/// / ContainerDecl
/// / DOT IDENTIFIER
+ /// / DOT InitList
/// / ErrorSetDecl
/// / FLOAT
/// / FnProto
@@ -1541,260 +2499,546 @@ const Parser = struct {
/// / KEYWORD_unreachable
/// / STRINGLITERAL
/// / SwitchExpr
- fn parsePrimaryTypeExpr(p: *Parser) !?*Node {
- if (try p.parseBuiltinCall()) |node| return node;
- if (p.eatToken(.CharLiteral)) |token| {
- const node = try p.arena.allocator.create(Node.OneToken);
- node.* = .{
- .base = .{ .tag = .CharLiteral },
- .token = token,
- };
- return &node.base;
- }
- if (try p.parseContainerDecl()) |node| return node;
- if (try p.parseAnonLiteral()) |node| return node;
- if (try p.parseErrorSetDecl()) |node| return node;
- if (try p.parseFloatLiteral()) |node| return node;
- if (try p.parseFnProto(.as_type, .{})) |node| return node;
- if (try p.parseGroupedExpr()) |node| return node;
- if (try p.parseLabeledTypeExpr()) |node| return node;
- if (try p.parseIdentifier()) |node| return node;
- if (try p.parseIfTypeExpr()) |node| return node;
- if (try p.parseIntegerLiteral()) |node| return node;
- if (p.eatToken(.Keyword_comptime)) |token| {
- const expr = (try p.parseTypeExpr()) orelse return null;
- const node = try p.arena.allocator.create(Node.Comptime);
- node.* = .{
- .doc_comments = null,
- .comptime_token = token,
- .expr = expr,
- };
- return &node.base;
- }
- if (p.eatToken(.Keyword_error)) |token| {
- const period = try p.expectTokenRecoverable(.Period);
- const identifier = try p.expectNodeRecoverable(parseIdentifier, .{
- .ExpectedIdentifier = .{ .token = p.tok_i },
- });
- const global_error_set = try p.createLiteral(.ErrorType, token);
- if (period == null or identifier == null) return global_error_set;
-
- const node = try p.arena.allocator.create(Node.SimpleInfixOp);
- node.* = .{
- .base = Node{ .tag = .Period },
- .op_token = period.?,
- .lhs = global_error_set,
- .rhs = identifier.?,
- };
- return &node.base;
- }
- if (p.eatToken(.Keyword_false)) |token| return p.createLiteral(.BoolLiteral, token);
- if (p.eatToken(.Keyword_null)) |token| return p.createLiteral(.NullLiteral, token);
- if (p.eatToken(.Keyword_anyframe)) |token| {
- const node = try p.arena.allocator.create(Node.AnyFrameType);
- node.* = .{
- .anyframe_token = token,
- .result = null,
- };
- return &node.base;
- }
- if (p.eatToken(.Keyword_true)) |token| return p.createLiteral(.BoolLiteral, token);
- if (p.eatToken(.Keyword_undefined)) |token| return p.createLiteral(.UndefinedLiteral, token);
- if (p.eatToken(.Keyword_unreachable)) |token| return p.createLiteral(.Unreachable, token);
- if (try p.parseStringLiteral()) |node| return node;
- if (try p.parseSwitchExpr()) |node| return node;
-
- return null;
- }
-
/// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
- fn parseContainerDecl(p: *Parser) !?*Node {
- const layout_token = p.eatToken(.Keyword_extern) orelse
- p.eatToken(.Keyword_packed);
-
- const node = (try p.parseContainerDeclAuto()) orelse {
- if (layout_token) |token|
- p.putBackToken(token);
- return null;
- };
- node.cast(Node.ContainerDecl).?.*.layout_token = layout_token;
- return node;
- }
-
+ /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
+ /// InitList
+ /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
+ /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
+ /// / LBRACE RBRACE
/// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
- fn parseErrorSetDecl(p: *Parser) !?*Node {
- const error_token = p.eatToken(.Keyword_error) orelse return null;
- if (p.eatToken(.LBrace) == null) {
- // Might parse as `KEYWORD_error DOT IDENTIFIER` later in PrimaryTypeExpr, so don't error
- p.putBackToken(error_token);
- return null;
- }
- const decls = try p.parseErrorTagList();
- defer p.gpa.free(decls);
- const rbrace = try p.expectToken(.RBrace);
-
- const node = try Node.ErrorSetDecl.alloc(&p.arena.allocator, decls.len);
- node.* = .{
- .error_token = error_token,
- .decls_len = decls.len,
- .rbrace_token = rbrace,
- };
- std.mem.copy(*Node, node.decls(), decls);
- return &node.base;
- }
-
/// GroupedExpr <- LPAREN Expr RPAREN
- fn parseGroupedExpr(p: *Parser) !?*Node {
- const lparen = p.eatToken(.LParen) orelse return null;
- const expr = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- const rparen = try p.expectToken(.RParen);
-
- const node = try p.arena.allocator.create(Node.GroupedExpression);
- node.* = .{
- .lparen = lparen,
- .expr = expr,
- .rparen = rparen,
- };
- return &node.base;
- }
-
/// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
- fn parseIfTypeExpr(p: *Parser) !?*Node {
- return p.parseIf(parseTypeExpr);
- }
-
/// LabeledTypeExpr
/// <- BlockLabel Block
/// / BlockLabel? LoopTypeExpr
- fn parseLabeledTypeExpr(p: *Parser) !?*Node {
- var colon: TokenIndex = undefined;
- const label = p.parseBlockLabel(&colon);
+ /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
+ fn parsePrimaryTypeExpr(p: *Parser) !Node.Index {
+ switch (p.token_tags[p.tok_i]) {
+ .char_literal => return p.addNode(.{
+ .tag = .char_literal,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ .integer_literal => return p.addNode(.{
+ .tag = .integer_literal,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ .float_literal => return p.addNode(.{
+ .tag = .float_literal,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ .keyword_false => return p.addNode(.{
+ .tag = .false_literal,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ .keyword_true => return p.addNode(.{
+ .tag = .true_literal,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ .keyword_null => return p.addNode(.{
+ .tag = .null_literal,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ .keyword_undefined => return p.addNode(.{
+ .tag = .undefined_literal,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ .keyword_unreachable => return p.addNode(.{
+ .tag = .unreachable_literal,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ .keyword_anyframe => return p.addNode(.{
+ .tag = .anyframe_literal,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ .string_literal => {
+ const main_token = p.nextToken();
+ return p.addNode(.{
+ .tag = .string_literal,
+ .main_token = main_token,
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ });
+ },
- if (label) |label_token| {
- if (try p.parseBlock(label_token)) |node| return node;
- }
+ .builtin => return p.parseBuiltinCall(),
+ .keyword_fn => return p.parseFnProto(),
+ .keyword_if => return p.parseIf(parseTypeExpr),
+ .keyword_switch => return p.expectSwitchExpr(),
- if (try p.parseLoopTypeExpr()) |node| {
- switch (node.tag) {
- .For => node.cast(Node.For).?.label = label,
- .While => node.cast(Node.While).?.label = label,
- else => unreachable,
- }
- return node;
- }
+ .keyword_extern,
+ .keyword_packed,
+ => {
+ p.tok_i += 1;
+ return p.parseContainerDeclAuto();
+ },
- if (label) |token| {
- p.putBackToken(colon);
- p.putBackToken(token);
- }
- return null;
- }
+ .keyword_struct,
+ .keyword_opaque,
+ .keyword_enum,
+ .keyword_union,
+ => return p.parseContainerDeclAuto(),
+
+ .keyword_comptime => return p.addNode(.{
+ .tag = .@"comptime",
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = try p.expectTypeExpr(),
+ .rhs = undefined,
+ },
+ }),
+ .multiline_string_literal_line => {
+ const first_line = p.nextToken();
+ while (p.token_tags[p.tok_i] == .multiline_string_literal_line) {
+ p.tok_i += 1;
+ }
+ return p.addNode(.{
+ .tag = .multiline_string_literal,
+ .main_token = first_line,
+ .data = .{
+ .lhs = first_line,
+ .rhs = p.tok_i - 1,
+ },
+ });
+ },
+ .identifier => switch (p.token_tags[p.tok_i + 1]) {
+ .colon => switch (p.token_tags[p.tok_i + 2]) {
+ .keyword_inline => {
+ p.tok_i += 3;
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_for => return p.parseForTypeExpr(),
+ .keyword_while => return p.parseWhileTypeExpr(),
+ else => return p.fail(.expected_inlinable),
+ }
+ },
+ .keyword_for => {
+ p.tok_i += 2;
+ return p.parseForTypeExpr();
+ },
+ .keyword_while => {
+ p.tok_i += 2;
+ return p.parseWhileTypeExpr();
+ },
+ .l_brace => {
+ p.tok_i += 2;
+ return p.parseBlock();
+ },
+ else => return p.addNode(.{
+ .tag = .identifier,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ },
+ else => return p.addNode(.{
+ .tag = .identifier,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ }),
+ },
+ .keyword_inline => {
+ p.tok_i += 1;
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_for => return p.parseForTypeExpr(),
+ .keyword_while => return p.parseWhileTypeExpr(),
+ else => return p.fail(.expected_inlinable),
+ }
+ },
+ .keyword_for => return p.parseForTypeExpr(),
+ .keyword_while => return p.parseWhileTypeExpr(),
+ .period => switch (p.token_tags[p.tok_i + 1]) {
+ .identifier => return p.addNode(.{
+ .tag = .enum_literal,
+ .data = .{
+ .lhs = p.nextToken(), // dot
+ .rhs = undefined,
+ },
+ .main_token = p.nextToken(), // identifier
+ }),
+ .l_brace => {
+ const lbrace = p.tok_i + 1;
+ p.tok_i = lbrace + 1;
+
+ // If there are 0, 1, or 2 items, we can use ArrayInitDotTwo/StructInitDotTwo;
+ // otherwise we use the full ArrayInitDot/StructInitDot.
+
+ if (p.eatToken(.r_brace)) |_| {
+ return p.addNode(.{
+ .tag = .struct_init_dot_two,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = 0,
+ .rhs = 0,
+ },
+ });
+ }
+ const field_init_one = try p.parseFieldInit();
+ if (field_init_one != 0) {
+ const comma_one = p.eatToken(.comma);
+ if (p.eatToken(.r_brace)) |_| {
+ return p.addNode(.{
+ .tag = if (comma_one != null) .struct_init_dot_two_comma else .struct_init_dot_two,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = field_init_one,
+ .rhs = 0,
+ },
+ });
+ }
+ if (comma_one == null) {
+ try p.warnExpected(.comma);
+ }
+ const field_init_two = try p.expectFieldInit();
+ const comma_two = p.eatToken(.comma);
+ if (p.eatToken(.r_brace)) |_| {
+ return p.addNode(.{
+ .tag = if (comma_two != null) .struct_init_dot_two_comma else .struct_init_dot_two,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = field_init_one,
+ .rhs = field_init_two,
+ },
+ });
+ }
+ if (comma_two == null) {
+ try p.warnExpected(.comma);
+ }
+ var init_list = std.ArrayList(Node.Index).init(p.gpa);
+ defer init_list.deinit();
+
+ try init_list.appendSlice(&.{ field_init_one, field_init_two });
+
+ while (true) {
+ const next = try p.expectFieldInit();
+ assert(next != 0);
+ try init_list.append(next);
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_brace)) |_| break;
+ continue;
+ },
+ .r_brace => break,
+ .colon, .r_paren, .r_bracket => {
+ p.tok_i -= 1;
+ return p.failExpected(.r_brace);
+ },
+ else => {
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
+ }
+ const span = try p.listToSpan(init_list.items);
+ const trailing_comma = p.token_tags[p.tok_i - 2] == .comma;
+ return p.addNode(.{
+ .tag = if (trailing_comma) .struct_init_dot_comma else .struct_init_dot,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = span.start,
+ .rhs = span.end,
+ },
+ });
+ }
- /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
- fn parseLoopTypeExpr(p: *Parser) !?*Node {
- const inline_token = p.eatToken(.Keyword_inline);
+ const elem_init_one = try p.expectExpr();
+ const comma_one = p.eatToken(.comma);
+ if (p.eatToken(.r_brace)) |_| {
+ return p.addNode(.{
+ .tag = if (comma_one != null) .array_init_dot_two_comma else .array_init_dot_two,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = elem_init_one,
+ .rhs = 0,
+ },
+ });
+ }
+ if (comma_one == null) {
+ try p.warnExpected(.comma);
+ }
+ const elem_init_two = try p.expectExpr();
+ const comma_two = p.eatToken(.comma);
+ if (p.eatToken(.r_brace)) |_| {
+ return p.addNode(.{
+ .tag = if (comma_two != null) .array_init_dot_two_comma else .array_init_dot_two,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = elem_init_one,
+ .rhs = elem_init_two,
+ },
+ });
+ }
+ if (comma_two == null) {
+ try p.warnExpected(.comma);
+ }
+ var init_list = std.ArrayList(Node.Index).init(p.gpa);
+ defer init_list.deinit();
+
+ try init_list.appendSlice(&.{ elem_init_one, elem_init_two });
+
+ while (true) {
+ const next = try p.expectExpr();
+ if (next == 0) break;
+ try init_list.append(next);
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_brace)) |_| break;
+ continue;
+ },
+ .r_brace => break,
+ .colon, .r_paren, .r_bracket => {
+ p.tok_i -= 1;
+ return p.failExpected(.r_brace);
+ },
+ else => {
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
+ }
+ const span = try p.listToSpan(init_list.items);
+ return p.addNode(.{
+ .tag = if (p.token_tags[p.tok_i - 2] == .comma) .array_init_dot_comma else .array_init_dot,
+ .main_token = lbrace,
+ .data = .{
+ .lhs = span.start,
+ .rhs = span.end,
+ },
+ });
+ },
+ else => return null_node,
+ },
+ .keyword_error => switch (p.token_tags[p.tok_i + 1]) {
+ .l_brace => {
+ const error_token = p.tok_i;
+ p.tok_i += 2;
+
+ if (p.eatToken(.r_brace)) |rbrace| {
+ return p.addNode(.{
+ .tag = .error_set_decl,
+ .main_token = error_token,
+ .data = .{
+ .lhs = undefined,
+ .rhs = rbrace,
+ },
+ });
+ }
- if (try p.parseForTypeExpr()) |node| {
- node.cast(Node.For).?.inline_token = inline_token;
- return node;
+ while (true) {
+ const doc_comment = try p.eatDocComments();
+ const identifier = try p.expectToken(.identifier);
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_brace)) |_| break;
+ continue;
+ },
+ .r_brace => break,
+ .colon, .r_paren, .r_bracket => {
+ p.tok_i -= 1;
+ return p.failExpected(.r_brace);
+ },
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
+ }
+ return p.addNode(.{
+ .tag = .error_set_decl,
+ .main_token = error_token,
+ .data = .{
+ .lhs = undefined,
+ .rhs = p.tok_i - 1, // rbrace
+ },
+ });
+ },
+ else => {
+ const main_token = p.nextToken();
+ const period = p.eatToken(.period);
+ if (period == null) try p.warnExpected(.period);
+ const identifier = p.eatToken(.identifier);
+ if (identifier == null) try p.warnExpected(.identifier);
+ return p.addNode(.{
+ .tag = .error_value,
+ .main_token = main_token,
+ .data = .{
+ .lhs = period orelse 0,
+ .rhs = identifier orelse 0,
+ },
+ });
+ },
+ },
+ .l_paren => return p.addNode(.{
+ .tag = .grouped_expression,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = try p.expectExpr(),
+ .rhs = try p.expectToken(.r_paren),
+ },
+ }),
+ else => return null_node,
}
+ }
- if (try p.parseWhileTypeExpr()) |node| {
- node.cast(Node.While).?.inline_token = inline_token;
- return node;
+ fn expectPrimaryTypeExpr(p: *Parser) !Node.Index {
+ const node = try p.parsePrimaryTypeExpr();
+ if (node == 0) {
+ return p.fail(.expected_primary_type_expr);
}
-
- if (inline_token == null) return null;
-
- // If we've seen "inline", there should have been a "for" or "while"
- try p.errors.append(p.gpa, .{
- .ExpectedInlinable = .{ .token = p.tok_i },
- });
- return error.ParseError;
+ return node;
}
+ /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
/// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
- fn parseForTypeExpr(p: *Parser) !?*Node {
- const node = (try p.parseForPrefix()) orelse return null;
- const for_prefix = node.cast(Node.For).?;
-
- const type_expr = try p.expectNode(parseTypeExpr, .{
- .ExpectedTypeExpr = .{ .token = p.tok_i },
- });
- for_prefix.body = type_expr;
-
- if (p.eatToken(.Keyword_else)) |else_token| {
- const else_expr = try p.expectNode(parseTypeExpr, .{
- .ExpectedTypeExpr = .{ .token = p.tok_i },
+ fn parseForTypeExpr(p: *Parser) !Node.Index {
+ const for_token = p.eatToken(.keyword_for) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const array_expr = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ const found_payload = try p.parsePtrIndexPayload();
+ if (found_payload == 0) try p.warn(.expected_loop_payload);
+
+ const then_expr = try p.expectExpr();
+ const else_token = p.eatToken(.keyword_else) orelse {
+ return p.addNode(.{
+ .tag = .for_simple,
+ .main_token = for_token,
+ .data = .{
+ .lhs = array_expr,
+ .rhs = then_expr,
+ },
});
-
- const else_node = try p.arena.allocator.create(Node.Else);
- else_node.* = .{
- .else_token = else_token,
- .payload = null,
- .body = else_expr,
- };
-
- for_prefix.@"else" = else_node;
- }
-
- return node;
+ };
+ const else_expr = try p.expectTypeExpr();
+ return p.addNode(.{
+ .tag = .@"for",
+ .main_token = for_token,
+ .data = .{
+ .lhs = array_expr,
+ .rhs = try p.addExtra(Node.If{
+ .then_expr = then_expr,
+ .else_expr = else_expr,
+ }),
+ },
+ });
}
+ /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
/// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
- fn parseWhileTypeExpr(p: *Parser) !?*Node {
- const node = (try p.parseWhilePrefix()) orelse return null;
- const while_prefix = node.cast(Node.While).?;
-
- const type_expr = try p.expectNode(parseTypeExpr, .{
- .ExpectedTypeExpr = .{ .token = p.tok_i },
+ fn parseWhileTypeExpr(p: *Parser) !Node.Index {
+ const while_token = p.eatToken(.keyword_while) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const condition = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ const then_payload = try p.parsePtrPayload();
+ const cont_expr = try p.parseWhileContinueExpr();
+
+ const then_expr = try p.expectTypeExpr();
+ const else_token = p.eatToken(.keyword_else) orelse {
+ if (cont_expr == 0) {
+ return p.addNode(.{
+ .tag = .while_simple,
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = then_expr,
+ },
+ });
+ } else {
+ return p.addNode(.{
+ .tag = .while_cont,
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = try p.addExtra(Node.WhileCont{
+ .cont_expr = cont_expr,
+ .then_expr = then_expr,
+ }),
+ },
+ });
+ }
+ };
+ const else_payload = try p.parsePayload();
+ const else_expr = try p.expectTypeExpr();
+ return p.addNode(.{
+ .tag = .@"while",
+ .main_token = while_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = try p.addExtra(Node.While{
+ .cont_expr = cont_expr,
+ .then_expr = then_expr,
+ .else_expr = else_expr,
+ }),
+ },
});
- while_prefix.body = type_expr;
-
- if (p.eatToken(.Keyword_else)) |else_token| {
- const payload = try p.parsePayload();
-
- const else_expr = try p.expectNode(parseTypeExpr, .{
- .ExpectedTypeExpr = .{ .token = p.tok_i },
- });
-
- const else_node = try p.arena.allocator.create(Node.Else);
- else_node.* = .{
- .else_token = else_token,
- .payload = null,
- .body = else_expr,
- };
-
- while_prefix.@"else" = else_node;
- }
-
- return node;
}
/// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
- fn parseSwitchExpr(p: *Parser) !?*Node {
- const switch_token = p.eatToken(.Keyword_switch) orelse return null;
- _ = try p.expectToken(.LParen);
- const expr_node = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RParen);
- _ = try p.expectToken(.LBrace);
+ fn expectSwitchExpr(p: *Parser) !Node.Index {
+ const switch_token = p.assertToken(.keyword_switch);
+ _ = try p.expectToken(.l_paren);
+ const expr_node = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ _ = try p.expectToken(.l_brace);
const cases = try p.parseSwitchProngList();
- defer p.gpa.free(cases);
- const rbrace = try p.expectToken(.RBrace);
-
- const node = try Node.Switch.alloc(&p.arena.allocator, cases.len);
- node.* = .{
- .switch_token = switch_token,
- .expr = expr_node,
- .cases_len = cases.len,
- .rbrace = rbrace,
- };
- std.mem.copy(*Node, node.cases(), cases);
- return &node.base;
+ const trailing_comma = p.token_tags[p.tok_i - 1] == .comma;
+ _ = try p.expectToken(.r_brace);
+
+ return p.addNode(.{
+ .tag = if (trailing_comma) .switch_comma else .@"switch",
+ .main_token = switch_token,
+ .data = .{
+ .lhs = expr_node,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = cases.start,
+ .end = cases.end,
+ }),
+ },
+ });
}
/// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN
@@ -1802,854 +3046,395 @@ const Parser = struct {
/// AsmInput <- COLON AsmInputList AsmClobbers?
/// AsmClobbers <- COLON StringList
/// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL?
- fn parseAsmExpr(p: *Parser) !?*Node {
- const asm_token = p.eatToken(.Keyword_asm) orelse return null;
- const volatile_token = p.eatToken(.Keyword_volatile);
- _ = try p.expectToken(.LParen);
- const template = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
-
- var arena_outputs: []Node.Asm.Output = &[0]Node.Asm.Output{};
- var arena_inputs: []Node.Asm.Input = &[0]Node.Asm.Input{};
- var arena_clobbers: []*Node = &[0]*Node{};
-
- if (p.eatToken(.Colon) != null) {
- const outputs = try p.parseAsmOutputList();
- defer p.gpa.free(outputs);
- arena_outputs = try p.arena.allocator.dupe(Node.Asm.Output, outputs);
-
- if (p.eatToken(.Colon) != null) {
- const inputs = try p.parseAsmInputList();
- defer p.gpa.free(inputs);
- arena_inputs = try p.arena.allocator.dupe(Node.Asm.Input, inputs);
-
- if (p.eatToken(.Colon) != null) {
- const clobbers = try ListParseFn(*Node, parseStringLiteral)(p);
- defer p.gpa.free(clobbers);
- arena_clobbers = try p.arena.allocator.dupe(*Node, clobbers);
- }
- }
+ /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
+ /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
+ fn expectAsmExpr(p: *Parser) !Node.Index {
+ const asm_token = p.assertToken(.keyword_asm);
+ _ = p.eatToken(.keyword_volatile);
+ _ = try p.expectToken(.l_paren);
+ const template = try p.expectExpr();
+
+ if (p.eatToken(.r_paren)) |rparen| {
+ return p.addNode(.{
+ .tag = .asm_simple,
+ .main_token = asm_token,
+ .data = .{
+ .lhs = template,
+ .rhs = rparen,
+ },
+ });
}
- const node = try p.arena.allocator.create(Node.Asm);
- node.* = .{
- .asm_token = asm_token,
- .volatile_token = volatile_token,
- .template = template,
- .outputs = arena_outputs,
- .inputs = arena_inputs,
- .clobbers = arena_clobbers,
- .rparen = try p.expectToken(.RParen),
- };
-
- return &node.base;
- }
+ _ = try p.expectToken(.colon);
- /// DOT IDENTIFIER
- fn parseAnonLiteral(p: *Parser) !?*Node {
- const dot = p.eatToken(.Period) orelse return null;
+ var list = std.ArrayList(Node.Index).init(p.gpa);
+ defer list.deinit();
- // anon enum literal
- if (p.eatToken(.Identifier)) |name| {
- const node = try p.arena.allocator.create(Node.EnumLiteral);
- node.* = .{
- .dot = dot,
- .name = name,
- };
- return &node.base;
+ while (true) {
+ const output_item = try p.parseAsmOutputItem();
+ if (output_item == 0) break;
+ try list.append(output_item);
+ switch (p.token_tags[p.tok_i]) {
+ .comma => p.tok_i += 1,
+ .colon, .r_paren, .r_brace, .r_bracket => break, // All possible delimiters.
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ try p.warnExpected(.comma);
+ },
+ }
}
-
- if (try p.parseAnonInitList(dot)) |node| {
- return node;
+ if (p.eatToken(.colon)) |_| {
+ while (true) {
+ const input_item = try p.parseAsmInputItem();
+ if (input_item == 0) break;
+ try list.append(input_item);
+ switch (p.token_tags[p.tok_i]) {
+ .comma => p.tok_i += 1,
+ .colon, .r_paren, .r_brace, .r_bracket => break, // All possible delimiters.
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ try p.warnExpected(.comma);
+ },
+ }
+ }
+ if (p.eatToken(.colon)) |_| {
+ while (p.eatToken(.string_literal)) |_| {
+ switch (p.token_tags[p.tok_i]) {
+ .comma => p.tok_i += 1,
+ .colon, .r_paren, .r_brace, .r_bracket => break,
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ try p.warnExpected(.comma);
+ },
+ }
+ }
+ }
}
-
- p.putBackToken(dot);
- return null;
+ const rparen = try p.expectToken(.r_paren);
+ const span = try p.listToSpan(list.items);
+ return p.addNode(.{
+ .tag = .@"asm",
+ .main_token = asm_token,
+ .data = .{
+ .lhs = template,
+ .rhs = try p.addExtra(Node.Asm{
+ .items_start = span.start,
+ .items_end = span.end,
+ .rparen = rparen,
+ }),
+ },
+ });
}
/// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
- fn parseAsmOutputItem(p: *Parser) !?Node.Asm.Output {
- const lbracket = p.eatToken(.LBracket) orelse return null;
- const name = try p.expectNode(parseIdentifier, .{
- .ExpectedIdentifier = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RBracket);
-
- const constraint = try p.expectNode(parseStringLiteral, .{
- .ExpectedStringLiteral = .{ .token = p.tok_i },
- });
-
- _ = try p.expectToken(.LParen);
- const kind: Node.Asm.Output.Kind = blk: {
- if (p.eatToken(.Arrow) != null) {
- const return_ident = try p.expectNode(parseTypeExpr, .{
- .ExpectedTypeExpr = .{ .token = p.tok_i },
- });
- break :blk .{ .Return = return_ident };
+ fn parseAsmOutputItem(p: *Parser) !Node.Index {
+ _ = p.eatToken(.l_bracket) orelse return null_node;
+ const identifier = try p.expectToken(.identifier);
+ _ = try p.expectToken(.r_bracket);
+ _ = try p.expectToken(.string_literal);
+ _ = try p.expectToken(.l_paren);
+ const type_expr: Node.Index = blk: {
+ if (p.eatToken(.arrow)) |_| {
+ break :blk try p.expectTypeExpr();
+ } else {
+ _ = try p.expectToken(.identifier);
+ break :blk null_node;
}
- const variable = try p.expectNode(parseIdentifier, .{
- .ExpectedIdentifier = .{ .token = p.tok_i },
- });
- break :blk .{ .Variable = variable.castTag(.Identifier).? };
- };
- const rparen = try p.expectToken(.RParen);
-
- return Node.Asm.Output{
- .lbracket = lbracket,
- .symbolic_name = name,
- .constraint = constraint,
- .kind = kind,
- .rparen = rparen,
};
+ const rparen = try p.expectToken(.r_paren);
+ return p.addNode(.{
+ .tag = .asm_output,
+ .main_token = identifier,
+ .data = .{
+ .lhs = type_expr,
+ .rhs = rparen,
+ },
+ });
}
/// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
- fn parseAsmInputItem(p: *Parser) !?Node.Asm.Input {
- const lbracket = p.eatToken(.LBracket) orelse return null;
- const name = try p.expectNode(parseIdentifier, .{
- .ExpectedIdentifier = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RBracket);
-
- const constraint = try p.expectNode(parseStringLiteral, .{
- .ExpectedStringLiteral = .{ .token = p.tok_i },
- });
-
- _ = try p.expectToken(.LParen);
- const expr = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
+ fn parseAsmInputItem(p: *Parser) !Node.Index {
+ _ = p.eatToken(.l_bracket) orelse return null_node;
+ const identifier = try p.expectToken(.identifier);
+ _ = try p.expectToken(.r_bracket);
+ _ = try p.expectToken(.string_literal);
+ _ = try p.expectToken(.l_paren);
+ const expr = try p.expectExpr();
+ const rparen = try p.expectToken(.r_paren);
+ return p.addNode(.{
+ .tag = .asm_input,
+ .main_token = identifier,
+ .data = .{
+ .lhs = expr,
+ .rhs = rparen,
+ },
});
- const rparen = try p.expectToken(.RParen);
-
- return Node.Asm.Input{
- .lbracket = lbracket,
- .symbolic_name = name,
- .constraint = constraint,
- .expr = expr,
- .rparen = rparen,
- };
}
/// BreakLabel <- COLON IDENTIFIER
- fn parseBreakLabel(p: *Parser) !?TokenIndex {
- _ = p.eatToken(.Colon) orelse return null;
- const ident = try p.expectToken(.Identifier);
- return ident;
+ fn parseBreakLabel(p: *Parser) !TokenIndex {
+ _ = p.eatToken(.colon) orelse return @as(TokenIndex, 0);
+ return p.expectToken(.identifier);
}
/// BlockLabel <- IDENTIFIER COLON
- fn parseBlockLabel(p: *Parser, colon_token: *TokenIndex) ?TokenIndex {
- const identifier = p.eatToken(.Identifier) orelse return null;
- if (p.eatToken(.Colon)) |colon| {
- colon_token.* = colon;
+ fn parseBlockLabel(p: *Parser) TokenIndex {
+ if (p.token_tags[p.tok_i] == .identifier and
+ p.token_tags[p.tok_i + 1] == .colon)
+ {
+ const identifier = p.tok_i;
+ p.tok_i += 2;
return identifier;
}
- p.putBackToken(identifier);
- return null;
+ return 0;
}
/// FieldInit <- DOT IDENTIFIER EQUAL Expr
- fn parseFieldInit(p: *Parser) !?*Node {
- const period_token = p.eatToken(.Period) orelse return null;
- const name_token = p.eatToken(.Identifier) orelse {
- // Because of anon literals `.{` is also valid.
- p.putBackToken(period_token);
- return null;
- };
- const eq_token = p.eatToken(.Equal) orelse {
- // `.Name` may also be an enum literal, which is a later rule.
- p.putBackToken(name_token);
- p.putBackToken(period_token);
- return null;
- };
- const expr_node = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
+ fn parseFieldInit(p: *Parser) !Node.Index {
+ if (p.token_tags[p.tok_i + 0] == .period and
+ p.token_tags[p.tok_i + 1] == .identifier and
+ p.token_tags[p.tok_i + 2] == .equal)
+ {
+ p.tok_i += 3;
+ return p.expectExpr();
+ } else {
+ return null_node;
+ }
+ }
- const node = try p.arena.allocator.create(Node.FieldInitializer);
- node.* = .{
- .period_token = period_token,
- .name_token = name_token,
- .expr = expr_node,
- };
- return &node.base;
+ fn expectFieldInit(p: *Parser) !Node.Index {
+ _ = try p.expectToken(.period);
+ _ = try p.expectToken(.identifier);
+ _ = try p.expectToken(.equal);
+ return p.expectExpr();
}
/// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
- fn parseWhileContinueExpr(p: *Parser) !?*Node {
- _ = p.eatToken(.Colon) orelse return null;
- _ = try p.expectToken(.LParen);
- const node = try p.expectNode(parseAssignExpr, .{
- .ExpectedExprOrAssignment = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RParen);
+ fn parseWhileContinueExpr(p: *Parser) !Node.Index {
+ _ = p.eatToken(.colon) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const node = try p.parseAssignExpr();
+ if (node == 0) return p.fail(.expected_expr_or_assignment);
+ _ = try p.expectToken(.r_paren);
return node;
}
/// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
- fn parseLinkSection(p: *Parser) !?*Node {
- _ = p.eatToken(.Keyword_linksection) orelse return null;
- _ = try p.expectToken(.LParen);
- const expr_node = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RParen);
+ fn parseLinkSection(p: *Parser) !Node.Index {
+ _ = p.eatToken(.keyword_linksection) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const expr_node = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
return expr_node;
}
/// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
- fn parseCallconv(p: *Parser) !?*Node {
- _ = p.eatToken(.Keyword_callconv) orelse return null;
- _ = try p.expectToken(.LParen);
- const expr_node = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RParen);
+ fn parseCallconv(p: *Parser) !Node.Index {
+ _ = p.eatToken(.keyword_callconv) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const expr_node = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
return expr_node;
}
- /// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
- fn parseParamDecl(p: *Parser) !?Node.FnProto.ParamDecl {
- const doc_comments = try p.parseDocComment();
- const noalias_token = p.eatToken(.Keyword_noalias);
- const comptime_token = if (noalias_token == null) p.eatToken(.Keyword_comptime) else null;
- const name_token = blk: {
- const identifier = p.eatToken(.Identifier) orelse break :blk null;
- if (p.eatToken(.Colon) != null) break :blk identifier;
- p.putBackToken(identifier); // ParamType may also be an identifier
- break :blk null;
- };
- const param_type = (try p.parseParamType()) orelse {
- // Only return cleanly if no keyword, identifier, or doc comment was found
- if (noalias_token == null and
- comptime_token == null and
- name_token == null and
- doc_comments == null)
- {
- return null;
- }
- try p.errors.append(p.gpa, .{
- .ExpectedParamType = .{ .token = p.tok_i },
- });
- return error.ParseError;
- };
-
- return Node.FnProto.ParamDecl{
- .doc_comments = doc_comments,
- .comptime_token = comptime_token,
- .noalias_token = noalias_token,
- .name_token = name_token,
- .param_type = param_type,
- };
- }
-
+ /// ParamDecl
+ /// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
+ /// / DOT3
/// ParamType
/// <- Keyword_anytype
- /// / DOT3
/// / TypeExpr
- fn parseParamType(p: *Parser) !?Node.FnProto.ParamDecl.ParamType {
- // TODO cast from tuple to error union is broken
- const P = Node.FnProto.ParamDecl.ParamType;
- if (try p.parseAnyType()) |node| return P{ .any_type = node };
- if (try p.parseTypeExpr()) |node| return P{ .type_expr = node };
- return null;
- }
-
- /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
- fn parseIfPrefix(p: *Parser) !?*Node {
- const if_token = p.eatToken(.Keyword_if) orelse return null;
- _ = try p.expectToken(.LParen);
- const condition = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RParen);
- const payload = try p.parsePtrPayload();
-
- const node = try p.arena.allocator.create(Node.If);
- node.* = .{
- .if_token = if_token,
- .condition = condition,
- .payload = payload,
- .body = undefined, // set by caller
- .@"else" = null,
- };
- return &node.base;
- }
-
- /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
- fn parseWhilePrefix(p: *Parser) !?*Node {
- const while_token = p.eatToken(.Keyword_while) orelse return null;
-
- _ = try p.expectToken(.LParen);
- const condition = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RParen);
-
- const payload = try p.parsePtrPayload();
- const continue_expr = try p.parseWhileContinueExpr();
-
- const node = try p.arena.allocator.create(Node.While);
- node.* = .{
- .label = null,
- .inline_token = null,
- .while_token = while_token,
- .condition = condition,
- .payload = payload,
- .continue_expr = continue_expr,
- .body = undefined, // set by caller
- .@"else" = null,
- };
- return &node.base;
- }
-
- /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
- fn parseForPrefix(p: *Parser) !?*Node {
- const for_token = p.eatToken(.Keyword_for) orelse return null;
-
- _ = try p.expectToken(.LParen);
- const array_expr = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RParen);
-
- const payload = try p.expectNode(parsePtrIndexPayload, .{
- .ExpectedPayload = .{ .token = p.tok_i },
- });
-
- const node = try p.arena.allocator.create(Node.For);
- node.* = .{
- .label = null,
- .inline_token = null,
- .for_token = for_token,
- .array_expr = array_expr,
- .payload = payload,
- .body = undefined, // set by caller
- .@"else" = null,
- };
- return &node.base;
+ /// This function can return null nodes and then still return nodes afterwards,
+ /// such as in the case of anytype and `...`. Caller must look for rparen to find
+ /// out when there are no more param decls left.
+ fn expectParamDecl(p: *Parser) !Node.Index {
+ _ = try p.eatDocComments();
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_noalias, .keyword_comptime => p.tok_i += 1,
+ .ellipsis3 => {
+ p.tok_i += 1;
+ return null_node;
+ },
+ else => {},
+ }
+ if (p.token_tags[p.tok_i] == .identifier and
+ p.token_tags[p.tok_i + 1] == .colon)
+ {
+ p.tok_i += 2;
+ }
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_anytype => {
+ p.tok_i += 1;
+ return null_node;
+ },
+ else => return p.expectTypeExpr(),
+ }
}
/// Payload <- PIPE IDENTIFIER PIPE
- fn parsePayload(p: *Parser) !?*Node {
- const lpipe = p.eatToken(.Pipe) orelse return null;
- const identifier = try p.expectNode(parseIdentifier, .{
- .ExpectedIdentifier = .{ .token = p.tok_i },
- });
- const rpipe = try p.expectToken(.Pipe);
-
- const node = try p.arena.allocator.create(Node.Payload);
- node.* = .{
- .lpipe = lpipe,
- .error_symbol = identifier,
- .rpipe = rpipe,
- };
- return &node.base;
+ fn parsePayload(p: *Parser) !TokenIndex {
+ _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
+ const identifier = try p.expectToken(.identifier);
+ _ = try p.expectToken(.pipe);
+ return identifier;
}
/// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
- fn parsePtrPayload(p: *Parser) !?*Node {
- const lpipe = p.eatToken(.Pipe) orelse return null;
- const asterisk = p.eatToken(.Asterisk);
- const identifier = try p.expectNode(parseIdentifier, .{
- .ExpectedIdentifier = .{ .token = p.tok_i },
- });
- const rpipe = try p.expectToken(.Pipe);
-
- const node = try p.arena.allocator.create(Node.PointerPayload);
- node.* = .{
- .lpipe = lpipe,
- .ptr_token = asterisk,
- .value_symbol = identifier,
- .rpipe = rpipe,
- };
- return &node.base;
+ fn parsePtrPayload(p: *Parser) !TokenIndex {
+ _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
+ _ = p.eatToken(.asterisk);
+ const identifier = try p.expectToken(.identifier);
+ _ = try p.expectToken(.pipe);
+ return identifier;
}
/// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
- fn parsePtrIndexPayload(p: *Parser) !?*Node {
- const lpipe = p.eatToken(.Pipe) orelse return null;
- const asterisk = p.eatToken(.Asterisk);
- const identifier = try p.expectNode(parseIdentifier, .{
- .ExpectedIdentifier = .{ .token = p.tok_i },
- });
-
- const index = if (p.eatToken(.Comma) == null)
- null
- else
- try p.expectNode(parseIdentifier, .{
- .ExpectedIdentifier = .{ .token = p.tok_i },
- });
-
- const rpipe = try p.expectToken(.Pipe);
-
- const node = try p.arena.allocator.create(Node.PointerIndexPayload);
- node.* = .{
- .lpipe = lpipe,
- .ptr_token = asterisk,
- .value_symbol = identifier,
- .index_symbol = index,
- .rpipe = rpipe,
- };
- return &node.base;
+ /// Returns the first identifier token, if any.
+ fn parsePtrIndexPayload(p: *Parser) !TokenIndex {
+ _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
+ _ = p.eatToken(.asterisk);
+ const identifier = try p.expectToken(.identifier);
+ if (p.eatToken(.comma) != null) {
+ _ = try p.expectToken(.identifier);
+ }
+ _ = try p.expectToken(.pipe);
+ return identifier;
}
/// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
- fn parseSwitchProng(p: *Parser) !?*Node {
- const node = (try p.parseSwitchCase()) orelse return null;
- const arrow = try p.expectToken(.EqualAngleBracketRight);
- const payload = try p.parsePtrPayload();
- const expr = try p.expectNode(parseAssignExpr, .{
- .ExpectedExprOrAssignment = .{ .token = p.tok_i },
- });
-
- const switch_case = node.cast(Node.SwitchCase).?;
- switch_case.arrow_token = arrow;
- switch_case.payload = payload;
- switch_case.expr = expr;
-
- return node;
- }
-
/// SwitchCase
/// <- SwitchItem (COMMA SwitchItem)* COMMA?
/// / KEYWORD_else
- fn parseSwitchCase(p: *Parser) !?*Node {
- var list = std.ArrayList(*Node).init(p.gpa);
+ fn parseSwitchProng(p: *Parser) !Node.Index {
+ if (p.eatToken(.keyword_else)) |_| {
+ const arrow_token = try p.expectToken(.equal_angle_bracket_right);
+ _ = try p.parsePtrPayload();
+ return p.addNode(.{
+ .tag = .switch_case_one,
+ .main_token = arrow_token,
+ .data = .{
+ .lhs = 0,
+ .rhs = try p.expectAssignExpr(),
+ },
+ });
+ }
+ const first_item = try p.parseSwitchItem();
+ if (first_item == 0) return null_node;
+
+ if (p.eatToken(.equal_angle_bracket_right)) |arrow_token| {
+ _ = try p.parsePtrPayload();
+ return p.addNode(.{
+ .tag = .switch_case_one,
+ .main_token = arrow_token,
+ .data = .{
+ .lhs = first_item,
+ .rhs = try p.expectAssignExpr(),
+ },
+ });
+ }
+
+ var list = std.ArrayList(Node.Index).init(p.gpa);
defer list.deinit();
- if (try p.parseSwitchItem()) |first_item| {
- try list.append(first_item);
- while (p.eatToken(.Comma) != null) {
- const next_item = (try p.parseSwitchItem()) orelse break;
- try list.append(next_item);
- }
- } else if (p.eatToken(.Keyword_else)) |else_token| {
- const else_node = try p.arena.allocator.create(Node.SwitchElse);
- else_node.* = .{
- .token = else_token,
- };
- try list.append(&else_node.base);
- } else return null;
-
- const node = try Node.SwitchCase.alloc(&p.arena.allocator, list.items.len);
- node.* = .{
- .items_len = list.items.len,
- .arrow_token = undefined, // set by caller
- .payload = null,
- .expr = undefined, // set by caller
- };
- std.mem.copy(*Node, node.items(), list.items);
- return &node.base;
+ try list.append(first_item);
+ while (p.eatToken(.comma)) |_| {
+ const next_item = try p.parseSwitchItem();
+ if (next_item == 0) break;
+ try list.append(next_item);
+ }
+ const span = try p.listToSpan(list.items);
+ const arrow_token = try p.expectToken(.equal_angle_bracket_right);
+ _ = try p.parsePtrPayload();
+ return p.addNode(.{
+ .tag = .switch_case,
+ .main_token = arrow_token,
+ .data = .{
+ .lhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ .rhs = try p.expectAssignExpr(),
+ },
+ });
}
/// SwitchItem <- Expr (DOT3 Expr)?
- fn parseSwitchItem(p: *Parser) !?*Node {
- const expr = (try p.parseExpr()) orelse return null;
- if (p.eatToken(.Ellipsis3)) |token| {
- const range_end = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
+ fn parseSwitchItem(p: *Parser) !Node.Index {
+ const expr = try p.parseExpr();
+ if (expr == 0) return null_node;
+
+ if (p.eatToken(.ellipsis3)) |token| {
+ return p.addNode(.{
+ .tag = .switch_range,
+ .main_token = token,
+ .data = .{
+ .lhs = expr,
+ .rhs = try p.expectExpr(),
+ },
});
-
- const node = try p.arena.allocator.create(Node.SimpleInfixOp);
- node.* = .{
- .base = Node{ .tag = .Range },
- .op_token = token,
- .lhs = expr,
- .rhs = range_end,
- };
- return &node.base;
}
return expr;
}
- /// AssignOp
- /// <- ASTERISKEQUAL
- /// / SLASHEQUAL
- /// / PERCENTEQUAL
- /// / PLUSEQUAL
- /// / MINUSEQUAL
- /// / LARROW2EQUAL
- /// / RARROW2EQUAL
- /// / AMPERSANDEQUAL
- /// / CARETEQUAL
- /// / PIPEEQUAL
- /// / ASTERISKPERCENTEQUAL
- /// / PLUSPERCENTEQUAL
- /// / MINUSPERCENTEQUAL
- /// / EQUAL
- fn parseAssignOp(p: *Parser) !?*Node {
- const token = p.nextToken();
- const op: Node.Tag = switch (p.token_ids[token]) {
- .AsteriskEqual => .AssignMul,
- .SlashEqual => .AssignDiv,
- .PercentEqual => .AssignMod,
- .PlusEqual => .AssignAdd,
- .MinusEqual => .AssignSub,
- .AngleBracketAngleBracketLeftEqual => .AssignBitShiftLeft,
- .AngleBracketAngleBracketRightEqual => .AssignBitShiftRight,
- .AmpersandEqual => .AssignBitAnd,
- .CaretEqual => .AssignBitXor,
- .PipeEqual => .AssignBitOr,
- .AsteriskPercentEqual => .AssignMulWrap,
- .PlusPercentEqual => .AssignAddWrap,
- .MinusPercentEqual => .AssignSubWrap,
- .Equal => .Assign,
- else => {
- p.putBackToken(token);
- return null;
- },
- };
-
- const node = try p.arena.allocator.create(Node.SimpleInfixOp);
- node.* = .{
- .base = .{ .tag = op },
- .op_token = token,
- .lhs = undefined, // set by caller
- .rhs = undefined, // set by caller
- };
- return &node.base;
- }
-
- /// CompareOp
- /// <- EQUALEQUAL
- /// / EXCLAMATIONMARKEQUAL
- /// / LARROW
- /// / RARROW
- /// / LARROWEQUAL
- /// / RARROWEQUAL
- fn parseCompareOp(p: *Parser) !?*Node {
- const token = p.nextToken();
- const op: Node.Tag = switch (p.token_ids[token]) {
- .EqualEqual => .EqualEqual,
- .BangEqual => .BangEqual,
- .AngleBracketLeft => .LessThan,
- .AngleBracketRight => .GreaterThan,
- .AngleBracketLeftEqual => .LessOrEqual,
- .AngleBracketRightEqual => .GreaterOrEqual,
- else => {
- p.putBackToken(token);
- return null;
- },
- };
-
- return p.createInfixOp(token, op);
- }
-
- /// BitwiseOp
- /// <- AMPERSAND
- /// / CARET
- /// / PIPE
- /// / KEYWORD_orelse
- /// / KEYWORD_catch Payload?
- fn parseBitwiseOp(p: *Parser) !?*Node {
- const token = p.nextToken();
- const op: Node.Tag = switch (p.token_ids[token]) {
- .Ampersand => .BitAnd,
- .Caret => .BitXor,
- .Pipe => .BitOr,
- .Keyword_orelse => .OrElse,
- .Keyword_catch => {
- const payload = try p.parsePayload();
- const node = try p.arena.allocator.create(Node.Catch);
- node.* = .{
- .op_token = token,
- .lhs = undefined, // set by caller
- .rhs = undefined, // set by caller
- .payload = payload,
- };
- return &node.base;
- },
- else => {
- p.putBackToken(token);
- return null;
- },
- };
-
- return p.createInfixOp(token, op);
- }
-
- /// BitShiftOp
- /// <- LARROW2
- /// / RARROW2
- fn parseBitShiftOp(p: *Parser) !?*Node {
- const token = p.nextToken();
- const op: Node.Tag = switch (p.token_ids[token]) {
- .AngleBracketAngleBracketLeft => .BitShiftLeft,
- .AngleBracketAngleBracketRight => .BitShiftRight,
- else => {
- p.putBackToken(token);
- return null;
- },
- };
-
- return p.createInfixOp(token, op);
- }
-
- /// AdditionOp
- /// <- PLUS
- /// / MINUS
- /// / PLUS2
- /// / PLUSPERCENT
- /// / MINUSPERCENT
- fn parseAdditionOp(p: *Parser) !?*Node {
- const token = p.nextToken();
- const op: Node.Tag = switch (p.token_ids[token]) {
- .Plus => .Add,
- .Minus => .Sub,
- .PlusPlus => .ArrayCat,
- .PlusPercent => .AddWrap,
- .MinusPercent => .SubWrap,
- else => {
- p.putBackToken(token);
- return null;
- },
- };
-
- return p.createInfixOp(token, op);
- }
-
- /// MultiplyOp
- /// <- PIPE2
- /// / ASTERISK
- /// / SLASH
- /// / PERCENT
- /// / ASTERISK2
- /// / ASTERISKPERCENT
- fn parseMultiplyOp(p: *Parser) !?*Node {
- const token = p.nextToken();
- const op: Node.Tag = switch (p.token_ids[token]) {
- .PipePipe => .MergeErrorSets,
- .Asterisk => .Mul,
- .Slash => .Div,
- .Percent => .Mod,
- .AsteriskAsterisk => .ArrayMult,
- .AsteriskPercent => .MulWrap,
- else => {
- p.putBackToken(token);
- return null;
- },
- };
-
- return p.createInfixOp(token, op);
- }
-
- /// PrefixOp
- /// <- EXCLAMATIONMARK
- /// / MINUS
- /// / TILDE
- /// / MINUSPERCENT
- /// / AMPERSAND
- /// / KEYWORD_try
- /// / KEYWORD_await
- fn parsePrefixOp(p: *Parser) !?*Node {
- const token = p.nextToken();
- switch (p.token_ids[token]) {
- .Bang => return p.allocSimplePrefixOp(.BoolNot, token),
- .Minus => return p.allocSimplePrefixOp(.Negation, token),
- .Tilde => return p.allocSimplePrefixOp(.BitNot, token),
- .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token),
- .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token),
- .Keyword_try => return p.allocSimplePrefixOp(.Try, token),
- .Keyword_await => return p.allocSimplePrefixOp(.Await, token),
- else => {
- p.putBackToken(token);
- return null;
- },
- }
- }
+ const PtrModifiers = struct {
+ align_node: Node.Index,
+ bit_range_start: Node.Index,
+ bit_range_end: Node.Index,
+ };
- fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Tag, token: TokenIndex) !?*Node {
- const node = try p.arena.allocator.create(Node.SimplePrefixOp);
- node.* = .{
- .base = .{ .tag = tag },
- .op_token = token,
- .rhs = undefined, // set by caller
+ fn parsePtrModifiers(p: *Parser) !PtrModifiers {
+ var result: PtrModifiers = .{
+ .align_node = 0,
+ .bit_range_start = 0,
+ .bit_range_end = 0,
};
- return &node.base;
- }
-
- // TODO: ArrayTypeStart is either an array or a slice, but const/allowzero only work on
- // pointers. Consider updating this rule:
- // ...
- // / ArrayTypeStart
- // / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
- // / PtrTypeStart ...
-
- /// PrefixTypeOp
- /// <- QUESTIONMARK
- /// / KEYWORD_anyframe MINUSRARROW
- /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
- /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
- fn parsePrefixTypeOp(p: *Parser) !?*Node {
- if (p.eatToken(.QuestionMark)) |token| {
- const node = try p.arena.allocator.create(Node.SimplePrefixOp);
- node.* = .{
- .base = .{ .tag = .OptionalType },
- .op_token = token,
- .rhs = undefined, // set by caller
- };
- return &node.base;
- }
-
- if (p.eatToken(.Keyword_anyframe)) |token| {
- const arrow = p.eatToken(.Arrow) orelse {
- p.putBackToken(token);
- return null;
- };
- const node = try p.arena.allocator.create(Node.AnyFrameType);
- node.* = .{
- .anyframe_token = token,
- .result = .{
- .arrow_token = arrow,
- .return_type = undefined, // set by caller
- },
- };
- return &node.base;
- }
-
- if (try p.parsePtrTypeStart()) |node| {
- // If the token encountered was **, there will be two nodes instead of one.
- // The attributes should be applied to the rightmost operator.
- var ptr_info = if (node.cast(Node.PtrType)) |ptr_type|
- if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk)
- &ptr_type.rhs.cast(Node.PtrType).?.ptr_info
- else
- &ptr_type.ptr_info
- else if (node.cast(Node.SliceType)) |slice_type|
- &slice_type.ptr_info
- else
- unreachable;
-
- while (true) {
- if (p.eatToken(.Keyword_align)) |align_token| {
- const lparen = try p.expectToken(.LParen);
- const expr_node = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
-
- // Optional bit range
- const bit_range = if (p.eatToken(.Colon)) |_| bit_range_value: {
- const range_start = try p.expectNode(parseIntegerLiteral, .{
- .ExpectedIntegerLiteral = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.Colon);
- const range_end = try p.expectNode(parseIntegerLiteral, .{
- .ExpectedIntegerLiteral = .{ .token = p.tok_i },
- });
-
- break :bit_range_value ast.PtrInfo.Align.BitRange{
- .start = range_start,
- .end = range_end,
- };
- } else null;
- _ = try p.expectToken(.RParen);
-
- if (ptr_info.align_info != null) {
- try p.errors.append(p.gpa, .{
- .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
- }
-
- ptr_info.align_info = ast.PtrInfo.Align{
- .node = expr_node,
- .bit_range = bit_range,
- };
-
- continue;
- }
- if (p.eatToken(.Keyword_const)) |const_token| {
- if (ptr_info.const_token != null) {
- try p.errors.append(p.gpa, .{
- .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
- }
- ptr_info.const_token = const_token;
- continue;
- }
- if (p.eatToken(.Keyword_volatile)) |volatile_token| {
- if (ptr_info.volatile_token != null) {
- try p.errors.append(p.gpa, .{
- .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
+ var saw_const = false;
+ var saw_volatile = false;
+ var saw_allowzero = false;
+ while (true) {
+ switch (p.token_tags[p.tok_i]) {
+ .keyword_align => {
+ if (result.align_node != 0) {
+ try p.warn(.extra_align_qualifier);
}
- ptr_info.volatile_token = volatile_token;
- continue;
- }
- if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
- if (ptr_info.allowzero_token != null) {
- try p.errors.append(p.gpa, .{
- .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
+ p.tok_i += 1;
+ _ = try p.expectToken(.l_paren);
+ result.align_node = try p.expectExpr();
+
+ if (p.eatToken(.colon)) |_| {
+ result.bit_range_start = try p.expectExpr();
+ _ = try p.expectToken(.colon);
+ result.bit_range_end = try p.expectExpr();
}
- ptr_info.allowzero_token = allowzero_token;
- continue;
- }
- break;
- }
- return node;
- }
-
- if (try p.parseArrayTypeStart()) |node| {
- if (node.cast(Node.SliceType)) |slice_type| {
- // Collect pointer qualifiers in any order, but disallow duplicates
- while (true) {
- if (try p.parseByteAlign()) |align_expr| {
- if (slice_type.ptr_info.align_info != null) {
- try p.errors.append(p.gpa, .{
- .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
- }
- slice_type.ptr_info.align_info = ast.PtrInfo.Align{
- .node = align_expr,
- .bit_range = null,
- };
- continue;
- }
- if (p.eatToken(.Keyword_const)) |const_token| {
- if (slice_type.ptr_info.const_token != null) {
- try p.errors.append(p.gpa, .{
- .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
- }
- slice_type.ptr_info.const_token = const_token;
- continue;
+ _ = try p.expectToken(.r_paren);
+ },
+ .keyword_const => {
+ if (saw_const) {
+ try p.warn(.extra_const_qualifier);
}
- if (p.eatToken(.Keyword_volatile)) |volatile_token| {
- if (slice_type.ptr_info.volatile_token != null) {
- try p.errors.append(p.gpa, .{
- .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
- }
- slice_type.ptr_info.volatile_token = volatile_token;
- continue;
+ p.tok_i += 1;
+ saw_const = true;
+ },
+ .keyword_volatile => {
+ if (saw_volatile) {
+ try p.warn(.extra_volatile_qualifier);
}
- if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
- if (slice_type.ptr_info.allowzero_token != null) {
- try p.errors.append(p.gpa, .{
- .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
- });
- continue;
- }
- slice_type.ptr_info.allowzero_token = allowzero_token;
- continue;
+ p.tok_i += 1;
+ saw_volatile = true;
+ },
+ .keyword_allowzero => {
+ if (saw_allowzero) {
+ try p.warn(.extra_allowzero_qualifier);
}
- break;
- }
+ p.tok_i += 1;
+ saw_allowzero = true;
+ },
+ else => return result,
}
- return node;
}
-
- return null;
}
/// SuffixOp
@@ -2657,841 +3442,654 @@ const Parser = struct {
/// / DOT IDENTIFIER
/// / DOTASTERISK
/// / DOTQUESTIONMARK
- fn parseSuffixOp(p: *Parser, lhs: *Node) !?*Node {
- if (p.eatToken(.LBracket)) |_| {
- const index_expr = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
-
- if (p.eatToken(.Ellipsis2) != null) {
- const end_expr = try p.parseExpr();
- const sentinel: ?*Node = if (p.eatToken(.Colon) != null)
- try p.parseExpr()
- else
- null;
- const rtoken = try p.expectToken(.RBracket);
- const node = try p.arena.allocator.create(Node.Slice);
- node.* = .{
- .lhs = lhs,
- .rtoken = rtoken,
- .start = index_expr,
- .end = end_expr,
- .sentinel = sentinel,
- };
- return &node.base;
- }
-
- const rtoken = try p.expectToken(.RBracket);
- const node = try p.arena.allocator.create(Node.ArrayAccess);
- node.* = .{
- .lhs = lhs,
- .rtoken = rtoken,
- .index_expr = index_expr,
- };
- return &node.base;
- }
-
- if (p.eatToken(.PeriodAsterisk)) |period_asterisk| {
- const node = try p.arena.allocator.create(Node.SimpleSuffixOp);
- node.* = .{
- .base = .{ .tag = .Deref },
- .lhs = lhs,
- .rtoken = period_asterisk,
- };
- return &node.base;
- }
-
- if (p.eatToken(.Invalid_periodasterisks)) |period_asterisk| {
- try p.errors.append(p.gpa, .{
- .AsteriskAfterPointerDereference = .{ .token = period_asterisk },
- });
- const node = try p.arena.allocator.create(Node.SimpleSuffixOp);
- node.* = .{
- .base = .{ .tag = .Deref },
- .lhs = lhs,
- .rtoken = period_asterisk,
- };
- return &node.base;
- }
-
- if (p.eatToken(.Period)) |period| {
- if (try p.parseIdentifier()) |identifier| {
- const node = try p.arena.allocator.create(Node.SimpleInfixOp);
- node.* = .{
- .base = Node{ .tag = .Period },
- .op_token = period,
- .lhs = lhs,
- .rhs = identifier,
- };
- return &node.base;
- }
- if (p.eatToken(.QuestionMark)) |question_mark| {
- const node = try p.arena.allocator.create(Node.SimpleSuffixOp);
- node.* = .{
- .base = .{ .tag = .UnwrapOptional },
- .lhs = lhs,
- .rtoken = question_mark,
- };
- return &node.base;
- }
- try p.errors.append(p.gpa, .{
- .ExpectedSuffixOp = .{ .token = p.tok_i },
- });
- return null;
- }
-
- return null;
- }
-
- /// FnCallArguments <- LPAREN ExprList RPAREN
- /// ExprList <- (Expr COMMA)* Expr?
- fn parseFnCallArguments(p: *Parser) !?AnnotatedParamList {
- if (p.eatToken(.LParen) == null) return null;
- const list = try ListParseFn(*Node, parseExpr)(p);
- errdefer p.gpa.free(list);
- const rparen = try p.expectToken(.RParen);
- return AnnotatedParamList{ .list = list, .rparen = rparen };
- }
-
- const AnnotatedParamList = struct {
- list: []*Node,
- rparen: TokenIndex,
- };
-
- /// ArrayTypeStart <- LBRACKET Expr? (COLON Expr)? RBRACKET
- fn parseArrayTypeStart(p: *Parser) !?*Node {
- const lbracket = p.eatToken(.LBracket) orelse return null;
- const expr = try p.parseExpr();
- const sentinel = if (p.eatToken(.Colon)) |_|
- try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- })
- else
- null;
- const rbracket = try p.expectToken(.RBracket);
-
- if (expr) |len_expr| {
- if (sentinel) |s| {
- const node = try p.arena.allocator.create(Node.ArrayTypeSentinel);
- node.* = .{
- .op_token = lbracket,
- .rhs = undefined, // set by caller
- .len_expr = len_expr,
- .sentinel = s,
- };
- return &node.base;
- } else {
- const node = try p.arena.allocator.create(Node.ArrayType);
- node.* = .{
- .op_token = lbracket,
- .rhs = undefined, // set by caller
- .len_expr = len_expr,
- };
- return &node.base;
- }
- }
-
- const node = try p.arena.allocator.create(Node.SliceType);
- node.* = .{
- .op_token = lbracket,
- .rhs = undefined, // set by caller
- .ptr_info = .{ .sentinel = sentinel },
- };
- return &node.base;
- }
-
- /// PtrTypeStart
- /// <- ASTERISK
- /// / ASTERISK2
- /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
- fn parsePtrTypeStart(p: *Parser) !?*Node {
- if (p.eatToken(.Asterisk)) |asterisk| {
- const sentinel = if (p.eatToken(.Colon)) |_|
- try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- })
- else
- null;
- const node = try p.arena.allocator.create(Node.PtrType);
- node.* = .{
- .op_token = asterisk,
- .rhs = undefined, // set by caller
- .ptr_info = .{ .sentinel = sentinel },
- };
- return &node.base;
- }
-
- if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| {
- const node = try p.arena.allocator.create(Node.PtrType);
- node.* = .{
- .op_token = double_asterisk,
- .rhs = undefined, // set by caller
- };
-
- // Special case for **, which is its own token
- const child = try p.arena.allocator.create(Node.PtrType);
- child.* = .{
- .op_token = double_asterisk,
- .rhs = undefined, // set by caller
- };
- node.rhs = &child.base;
-
- return &node.base;
- }
- if (p.eatToken(.LBracket)) |lbracket| {
- const asterisk = p.eatToken(.Asterisk) orelse {
- p.putBackToken(lbracket);
- return null;
- };
- if (p.eatToken(.Identifier)) |ident| {
- const token_loc = p.token_locs[ident];
- const token_slice = p.source[token_loc.start..token_loc.end];
- if (!std.mem.eql(u8, token_slice, "c")) {
- p.putBackToken(ident);
- } else {
- _ = try p.expectToken(.RBracket);
- const node = try p.arena.allocator.create(Node.PtrType);
- node.* = .{
- .op_token = lbracket,
- .rhs = undefined, // set by caller
- };
- return &node.base;
+ fn parseSuffixOp(p: *Parser, lhs: Node.Index) !Node.Index {
+ switch (p.token_tags[p.tok_i]) {
+ .l_bracket => {
+ const lbracket = p.nextToken();
+ const index_expr = try p.expectExpr();
+
+ if (p.eatToken(.ellipsis2)) |_| {
+ const end_expr = try p.parseExpr();
+ if (end_expr == 0) {
+ _ = try p.expectToken(.r_bracket);
+ return p.addNode(.{
+ .tag = .slice_open,
+ .main_token = lbracket,
+ .data = .{
+ .lhs = lhs,
+ .rhs = index_expr,
+ },
+ });
+ }
+ if (p.eatToken(.colon)) |_| {
+ const sentinel = try p.parseExpr();
+ _ = try p.expectToken(.r_bracket);
+ return p.addNode(.{
+ .tag = .slice_sentinel,
+ .main_token = lbracket,
+ .data = .{
+ .lhs = lhs,
+ .rhs = try p.addExtra(Node.SliceSentinel{
+ .start = index_expr,
+ .end = end_expr,
+ .sentinel = sentinel,
+ }),
+ },
+ });
+ } else {
+ _ = try p.expectToken(.r_bracket);
+ return p.addNode(.{
+ .tag = .slice,
+ .main_token = lbracket,
+ .data = .{
+ .lhs = lhs,
+ .rhs = try p.addExtra(Node.Slice{
+ .start = index_expr,
+ .end = end_expr,
+ }),
+ },
+ });
+ }
}
- }
- const sentinel = if (p.eatToken(.Colon)) |_|
- try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- })
- else
- null;
- _ = try p.expectToken(.RBracket);
- const node = try p.arena.allocator.create(Node.PtrType);
- node.* = .{
- .op_token = lbracket,
- .rhs = undefined, // set by caller
- .ptr_info = .{ .sentinel = sentinel },
- };
- return &node.base;
+ _ = try p.expectToken(.r_bracket);
+ return p.addNode(.{
+ .tag = .array_access,
+ .main_token = lbracket,
+ .data = .{
+ .lhs = lhs,
+ .rhs = index_expr,
+ },
+ });
+ },
+ .period_asterisk => return p.addNode(.{
+ .tag = .deref,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = lhs,
+ .rhs = undefined,
+ },
+ }),
+ .invalid_periodasterisks => {
+ try p.warn(.asterisk_after_ptr_deref);
+ return p.addNode(.{
+ .tag = .deref,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = lhs,
+ .rhs = undefined,
+ },
+ });
+ },
+ .period => switch (p.token_tags[p.tok_i + 1]) {
+ .identifier => return p.addNode(.{
+ .tag = .field_access,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = lhs,
+ .rhs = p.nextToken(),
+ },
+ }),
+ .question_mark => return p.addNode(.{
+ .tag = .unwrap_optional,
+ .main_token = p.nextToken(),
+ .data = .{
+ .lhs = lhs,
+ .rhs = p.nextToken(),
+ },
+ }),
+ else => {
+ p.tok_i += 1;
+ try p.warn(.expected_suffix_op);
+ return null_node;
+ },
+ },
+ else => return null_node,
}
- return null;
}
- /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
- fn parseContainerDeclAuto(p: *Parser) !?*Node {
- const container_decl_type = (try p.parseContainerDeclType()) orelse return null;
- const lbrace = try p.expectToken(.LBrace);
- const members = try p.parseContainerMembers(false);
- defer p.gpa.free(members);
- const rbrace = try p.expectToken(.RBrace);
-
- const members_len = @intCast(NodeIndex, members.len);
- const node = try Node.ContainerDecl.alloc(&p.arena.allocator, members_len);
- node.* = .{
- .layout_token = null,
- .kind_token = container_decl_type.kind_token,
- .init_arg_expr = container_decl_type.init_arg_expr,
- .fields_and_decls_len = members_len,
- .lbrace_token = lbrace,
- .rbrace_token = rbrace,
- };
- std.mem.copy(*Node, node.fieldsAndDecls(), members);
- return &node.base;
- }
-
- /// Holds temporary data until we are ready to construct the full ContainerDecl AST node.
- const ContainerDeclType = struct {
- kind_token: TokenIndex,
- init_arg_expr: Node.ContainerDecl.InitArg,
- };
-
+ /// Caller must have already verified the first token.
/// ContainerDeclType
/// <- KEYWORD_struct
/// / KEYWORD_enum (LPAREN Expr RPAREN)?
/// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
/// / KEYWORD_opaque
- fn parseContainerDeclType(p: *Parser) !?ContainerDeclType {
- const kind_token = p.nextToken();
-
- const init_arg_expr = switch (p.token_ids[kind_token]) {
- .Keyword_struct, .Keyword_opaque => Node.ContainerDecl.InitArg{ .None = {} },
- .Keyword_enum => blk: {
- if (p.eatToken(.LParen) != null) {
- const expr = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RParen);
- break :blk Node.ContainerDecl.InitArg{ .Type = expr };
+ fn parseContainerDeclAuto(p: *Parser) !Node.Index {
+ const main_token = p.nextToken();
+ const arg_expr = switch (p.token_tags[main_token]) {
+ .keyword_struct, .keyword_opaque => null_node,
+ .keyword_enum => blk: {
+ if (p.eatToken(.l_paren)) |_| {
+ const expr = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ break :blk expr;
+ } else {
+ break :blk null_node;
}
- break :blk Node.ContainerDecl.InitArg{ .None = {} };
- },
- .Keyword_union => blk: {
- if (p.eatToken(.LParen) != null) {
- if (p.eatToken(.Keyword_enum) != null) {
- if (p.eatToken(.LParen) != null) {
- const expr = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
+ },
+ .keyword_union => blk: {
+ if (p.eatToken(.l_paren)) |_| {
+ if (p.eatToken(.keyword_enum)) |_| {
+ if (p.eatToken(.l_paren)) |_| {
+ const enum_tag_expr = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ _ = try p.expectToken(.r_paren);
+
+ _ = try p.expectToken(.l_brace);
+ const members = try p.parseContainerMembers();
+ const members_span = try members.toSpan(p);
+ _ = try p.expectToken(.r_brace);
+ return p.addNode(.{
+ .tag = switch (members.trailing) {
+ true => .tagged_union_enum_tag_trailing,
+ false => .tagged_union_enum_tag,
+ },
+ .main_token = main_token,
+ .data = .{
+ .lhs = enum_tag_expr,
+ .rhs = try p.addExtra(members_span),
+ },
});
- _ = try p.expectToken(.RParen);
- _ = try p.expectToken(.RParen);
- break :blk Node.ContainerDecl.InitArg{ .Enum = expr };
+ } else {
+ _ = try p.expectToken(.r_paren);
+
+ _ = try p.expectToken(.l_brace);
+ const members = try p.parseContainerMembers();
+ _ = try p.expectToken(.r_brace);
+ if (members.len <= 2) {
+ return p.addNode(.{
+ .tag = switch (members.trailing) {
+ true => .tagged_union_two_trailing,
+ false => .tagged_union_two,
+ },
+ .main_token = main_token,
+ .data = .{
+ .lhs = members.lhs,
+ .rhs = members.rhs,
+ },
+ });
+ } else {
+ const span = try members.toSpan(p);
+ return p.addNode(.{
+ .tag = switch (members.trailing) {
+ true => .tagged_union_trailing,
+ false => .tagged_union,
+ },
+ .main_token = main_token,
+ .data = .{
+ .lhs = span.start,
+ .rhs = span.end,
+ },
+ });
+ }
}
- _ = try p.expectToken(.RParen);
- break :blk Node.ContainerDecl.InitArg{ .Enum = null };
+ } else {
+ const expr = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ break :blk expr;
}
- const expr = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RParen);
- break :blk Node.ContainerDecl.InitArg{ .Type = expr };
+ } else {
+ break :blk null_node;
}
- break :blk Node.ContainerDecl.InitArg{ .None = {} };
},
else => {
- p.putBackToken(kind_token);
- return null;
+ p.tok_i -= 1;
+ return p.fail(.expected_container);
},
};
-
- return ContainerDeclType{
- .kind_token = kind_token,
- .init_arg_expr = init_arg_expr,
- };
+ _ = try p.expectToken(.l_brace);
+ const members = try p.parseContainerMembers();
+ _ = try p.expectToken(.r_brace);
+ if (arg_expr == 0) {
+ if (members.len <= 2) {
+ return p.addNode(.{
+ .tag = switch (members.trailing) {
+ true => .container_decl_two_trailing,
+ false => .container_decl_two,
+ },
+ .main_token = main_token,
+ .data = .{
+ .lhs = members.lhs,
+ .rhs = members.rhs,
+ },
+ });
+ } else {
+ const span = try members.toSpan(p);
+ return p.addNode(.{
+ .tag = switch (members.trailing) {
+ true => .container_decl_trailing,
+ false => .container_decl,
+ },
+ .main_token = main_token,
+ .data = .{
+ .lhs = span.start,
+ .rhs = span.end,
+ },
+ });
+ }
+ } else {
+ const span = try members.toSpan(p);
+ return p.addNode(.{
+ .tag = switch (members.trailing) {
+ true => .container_decl_arg_trailing,
+ false => .container_decl_arg,
+ },
+ .main_token = main_token,
+ .data = .{
+ .lhs = arg_expr,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
+ }
}
+ /// Holds temporary data until we are ready to construct the full ContainerDecl AST node.
/// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
- fn parseByteAlign(p: *Parser) !?*Node {
- _ = p.eatToken(.Keyword_align) orelse return null;
- _ = try p.expectToken(.LParen);
- const expr = try p.expectNode(parseExpr, .{
- .ExpectedExpr = .{ .token = p.tok_i },
- });
- _ = try p.expectToken(.RParen);
+ fn parseByteAlign(p: *Parser) !Node.Index {
+ _ = p.eatToken(.keyword_align) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const expr = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
return expr;
}
- /// IdentifierList <- (IDENTIFIER COMMA)* IDENTIFIER?
- /// Only ErrorSetDecl parses an IdentifierList
- fn parseErrorTagList(p: *Parser) ![]*Node {
- return ListParseFn(*Node, parseErrorTag)(p);
- }
-
/// SwitchProngList <- (SwitchProng COMMA)* SwitchProng?
- fn parseSwitchProngList(p: *Parser) ![]*Node {
- return ListParseFn(*Node, parseSwitchProng)(p);
+ fn parseSwitchProngList(p: *Parser) !Node.SubRange {
+ return ListParseFn(parseSwitchProng)(p);
}
- /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
- fn parseAsmOutputList(p: *Parser) Error![]Node.Asm.Output {
- return ListParseFn(Node.Asm.Output, parseAsmOutputItem)(p);
- }
+ /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
+ fn parseParamDeclList(p: *Parser) !SmallSpan {
+ _ = try p.expectToken(.l_paren);
+ if (p.eatToken(.r_paren)) |_| {
+ return SmallSpan{ .zero_or_one = 0 };
+ }
+ const param_one = while (true) {
+ const param = try p.expectParamDecl();
+ if (param != 0) break param;
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_paren)) |_| {
+ return SmallSpan{ .zero_or_one = 0 };
+ }
+ continue;
+ },
+ .r_paren => return SmallSpan{ .zero_or_one = 0 },
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
+ } else unreachable;
- /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
- fn parseAsmInputList(p: *Parser) Error![]Node.Asm.Input {
- return ListParseFn(Node.Asm.Input, parseAsmInputItem)(p);
- }
+ const param_two = while (true) {
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_paren)) |_| {
+ return SmallSpan{ .zero_or_one = param_one };
+ }
+ const param = try p.expectParamDecl();
+ if (param != 0) break param;
+ continue;
+ },
+ .r_paren => return SmallSpan{ .zero_or_one = param_one },
+ .colon, .r_brace, .r_bracket => {
+ p.tok_i -= 1;
+ return p.failExpected(.r_paren);
+ },
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
+ } else unreachable;
- /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
- fn parseParamDeclList(p: *Parser) ![]Node.FnProto.ParamDecl {
- return ListParseFn(Node.FnProto.ParamDecl, parseParamDecl)(p);
+ var list = std.ArrayList(Node.Index).init(p.gpa);
+ defer list.deinit();
+
+ try list.appendSlice(&.{ param_one, param_two });
+
+ while (true) {
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.token_tags[p.tok_i] == .r_paren) {
+ p.tok_i += 1;
+ return SmallSpan{ .multi = list.toOwnedSlice() };
+ }
+ const param = try p.expectParamDecl();
+ if (param != 0) {
+ try list.append(param);
+ }
+ continue;
+ },
+ .r_paren => return SmallSpan{ .multi = list.toOwnedSlice() },
+ .colon, .r_brace, .r_bracket => {
+ p.tok_i -= 1;
+ return p.failExpected(.r_paren);
+ },
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
+ }
}
- const NodeParseFn = fn (p: *Parser) Error!?*Node;
+ const NodeParseFn = fn (p: *Parser) Error!Node.Index;
- fn ListParseFn(comptime E: type, comptime nodeParseFn: anytype) ParseFn([]E) {
+ fn ListParseFn(comptime nodeParseFn: anytype) (fn (p: *Parser) Error!Node.SubRange) {
return struct {
- pub fn parse(p: *Parser) ![]E {
- var list = std.ArrayList(E).init(p.gpa);
+ pub fn parse(p: *Parser) Error!Node.SubRange {
+ var list = std.ArrayList(Node.Index).init(p.gpa);
defer list.deinit();
- while (try nodeParseFn(p)) |item| {
+ while (true) {
+ const item = try nodeParseFn(p);
+ if (item == 0) break;
+
try list.append(item);
- switch (p.token_ids[p.tok_i]) {
- .Comma => _ = p.nextToken(),
+ switch (p.token_tags[p.tok_i]) {
+ .comma => p.tok_i += 1,
// all possible delimiters
- .Colon, .RParen, .RBrace, .RBracket => break,
+ .colon, .r_paren, .r_brace, .r_bracket => break,
else => {
- // this is likely just a missing comma,
- // continue parsing this list and give an error
- try p.errors.append(p.gpa, .{
- .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma },
- });
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ try p.warnExpected(.comma);
},
}
}
- return list.toOwnedSlice();
+ return p.listToSpan(list.items);
}
}.parse;
}
- fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.Tag) NodeParseFn {
- return struct {
- pub fn parse(p: *Parser) Error!?*Node {
- const op_token = if (token == .Keyword_and) switch (p.token_ids[p.tok_i]) {
- .Keyword_and => p.nextToken(),
- .Invalid_ampersands => blk: {
- try p.errors.append(p.gpa, .{
- .InvalidAnd = .{ .token = p.tok_i },
- });
- break :blk p.nextToken();
- },
- else => return null,
- } else p.eatToken(token) orelse return null;
-
- const node = try p.arena.allocator.create(Node.SimpleInfixOp);
- node.* = .{
- .base = .{ .tag = op },
- .op_token = op_token,
- .lhs = undefined, // set by caller
- .rhs = undefined, // set by caller
- };
- return &node.base;
- }
- }.parse;
- }
-
- // Helper parsers not included in the grammar
-
- fn parseBuiltinCall(p: *Parser) !?*Node {
- const token = p.eatToken(.Builtin) orelse return null;
- const params = (try p.parseFnCallArguments()) orelse {
- try p.errors.append(p.gpa, .{
- .ExpectedParamList = .{ .token = p.tok_i },
+ /// FnCallArguments <- LPAREN ExprList RPAREN
+ /// ExprList <- (Expr COMMA)* Expr?
+ fn parseBuiltinCall(p: *Parser) !Node.Index {
+ const builtin_token = p.assertToken(.builtin);
+ if (p.token_tags[p.nextToken()] != .l_paren) {
+ p.tok_i -= 1;
+ try p.warn(.expected_param_list);
+ // Pretend this was an identifier so we can continue parsing.
+ return p.addNode(.{
+ .tag = .identifier,
+ .main_token = builtin_token,
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
});
+ }
+ if (p.eatToken(.r_paren)) |_| {
+ return p.addNode(.{
+ .tag = .builtin_call_two,
+ .main_token = builtin_token,
+ .data = .{
+ .lhs = 0,
+ .rhs = 0,
+ },
+ });
+ }
+ const param_one = try p.expectExpr();
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_paren)) |_| {
+ return p.addNode(.{
+ .tag = .builtin_call_two_comma,
+ .main_token = builtin_token,
+ .data = .{
+ .lhs = param_one,
+ .rhs = 0,
+ },
+ });
+ }
+ },
+ .r_paren => return p.addNode(.{
+ .tag = .builtin_call_two,
+ .main_token = builtin_token,
+ .data = .{
+ .lhs = param_one,
+ .rhs = 0,
+ },
+ }),
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
+ const param_two = try p.expectExpr();
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_paren)) |_| {
+ return p.addNode(.{
+ .tag = .builtin_call_two_comma,
+ .main_token = builtin_token,
+ .data = .{
+ .lhs = param_one,
+ .rhs = param_two,
+ },
+ });
+ }
+ },
+ .r_paren => return p.addNode(.{
+ .tag = .builtin_call_two,
+ .main_token = builtin_token,
+ .data = .{
+ .lhs = param_one,
+ .rhs = param_two,
+ },
+ }),
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
- // lets pretend this was an identifier so we can continue parsing
- const node = try p.arena.allocator.create(Node.OneToken);
- node.* = .{
- .base = .{ .tag = .Identifier },
- .token = token,
- };
- return &node.base;
- };
- defer p.gpa.free(params.list);
-
- const node = try Node.BuiltinCall.alloc(&p.arena.allocator, params.list.len);
- node.* = .{
- .builtin_token = token,
- .params_len = params.list.len,
- .rparen_token = params.rparen,
- };
- std.mem.copy(*Node, node.params(), params.list);
- return &node.base;
- }
-
- fn parseErrorTag(p: *Parser) !?*Node {
- const doc_comments = try p.parseDocComment(); // no need to rewind on failure
- const token = p.eatToken(.Identifier) orelse return null;
-
- const node = try p.arena.allocator.create(Node.ErrorTag);
- node.* = .{
- .doc_comments = doc_comments,
- .name_token = token,
- };
- return &node.base;
- }
-
- fn parseIdentifier(p: *Parser) !?*Node {
- const token = p.eatToken(.Identifier) orelse return null;
- const node = try p.arena.allocator.create(Node.OneToken);
- node.* = .{
- .base = .{ .tag = .Identifier },
- .token = token,
- };
- return &node.base;
- }
-
- fn parseAnyType(p: *Parser) !?*Node {
- const token = p.eatToken(.Keyword_anytype) orelse
- p.eatToken(.Keyword_var) orelse return null; // TODO remove in next release cycle
- const node = try p.arena.allocator.create(Node.OneToken);
- node.* = .{
- .base = .{ .tag = .AnyType },
- .token = token,
- };
- return &node.base;
- }
+ var list = std.ArrayList(Node.Index).init(p.gpa);
+ defer list.deinit();
- fn createLiteral(p: *Parser, tag: ast.Node.Tag, token: TokenIndex) !*Node {
- const result = try p.arena.allocator.create(Node.OneToken);
- result.* = .{
- .base = .{ .tag = tag },
- .token = token,
- };
- return &result.base;
- }
+ try list.appendSlice(&.{ param_one, param_two });
- fn parseStringLiteralSingle(p: *Parser) !?*Node {
- if (p.eatToken(.StringLiteral)) |token| {
- const node = try p.arena.allocator.create(Node.OneToken);
- node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = token,
- };
- return &node.base;
+ while (true) {
+ const param = try p.expectExpr();
+ try list.append(param);
+ switch (p.token_tags[p.nextToken()]) {
+ .comma => {
+ if (p.eatToken(.r_paren)) |_| {
+ const params = try p.listToSpan(list.items);
+ return p.addNode(.{
+ .tag = .builtin_call_comma,
+ .main_token = builtin_token,
+ .data = .{
+ .lhs = params.start,
+ .rhs = params.end,
+ },
+ });
+ }
+ continue;
+ },
+ .r_paren => {
+ const params = try p.listToSpan(list.items);
+ return p.addNode(.{
+ .tag = .builtin_call,
+ .main_token = builtin_token,
+ .data = .{
+ .lhs = params.start,
+ .rhs = params.end,
+ },
+ });
+ },
+ else => {
+ // This is likely just a missing comma;
+ // give an error but continue parsing this list.
+ p.tok_i -= 1;
+ try p.warnExpected(.comma);
+ },
+ }
}
- return null;
}
// string literal or multiline string literal
- fn parseStringLiteral(p: *Parser) !?*Node {
- if (try p.parseStringLiteralSingle()) |node| return node;
-
- if (p.eatToken(.MultilineStringLiteralLine)) |first_line| {
- const start_tok_i = p.tok_i;
- var tok_i = start_tok_i;
- var count: usize = 1; // including first_line
- while (true) : (tok_i += 1) {
- switch (p.token_ids[tok_i]) {
- .LineComment => continue,
- .MultilineStringLiteralLine => count += 1,
- else => break,
- }
- }
-
- const node = try Node.MultilineStringLiteral.alloc(&p.arena.allocator, count);
- node.* = .{ .lines_len = count };
- const lines = node.lines();
- tok_i = start_tok_i;
- lines[0] = first_line;
- count = 1;
- while (true) : (tok_i += 1) {
- switch (p.token_ids[tok_i]) {
- .LineComment => continue,
- .MultilineStringLiteralLine => {
- lines[count] = tok_i;
- count += 1;
+ fn parseStringLiteral(p: *Parser) !Node.Index {
+ switch (p.token_tags[p.tok_i]) {
+ .string_literal => {
+ const main_token = p.nextToken();
+ return p.addNode(.{
+ .tag = .string_literal,
+ .main_token = main_token,
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
},
- else => break,
+ });
+ },
+ .multiline_string_literal_line => {
+ const first_line = p.nextToken();
+ while (p.token_tags[p.tok_i] == .multiline_string_literal_line) {
+ p.tok_i += 1;
}
- }
- p.tok_i = tok_i;
- return &node.base;
+ return p.addNode(.{
+ .tag = .multiline_string_literal,
+ .main_token = first_line,
+ .data = .{
+ .lhs = first_line,
+ .rhs = p.tok_i - 1,
+ },
+ });
+ },
+ else => return null_node,
}
-
- return null;
}
- fn parseIntegerLiteral(p: *Parser) !?*Node {
- const token = p.eatToken(.IntegerLiteral) orelse return null;
- const node = try p.arena.allocator.create(Node.OneToken);
- node.* = .{
- .base = .{ .tag = .IntegerLiteral },
- .token = token,
- };
- return &node.base;
+ fn expectStringLiteral(p: *Parser) !Node.Index {
+ const node = try p.parseStringLiteral();
+ if (node == 0) {
+ return p.fail(.expected_string_literal);
+ }
+ return node;
}
- fn parseFloatLiteral(p: *Parser) !?*Node {
- const token = p.eatToken(.FloatLiteral) orelse return null;
- const node = try p.arena.allocator.create(Node.OneToken);
- node.* = .{
- .base = .{ .tag = .FloatLiteral },
- .token = token,
- };
- return &node.base;
+ fn expectIntegerLiteral(p: *Parser) !Node.Index {
+ return p.addNode(.{
+ .tag = .integer_literal,
+ .main_token = try p.expectToken(.integer_literal),
+ .data = .{
+ .lhs = undefined,
+ .rhs = undefined,
+ },
+ });
}
- fn parseTry(p: *Parser) !?*Node {
- const token = p.eatToken(.Keyword_try) orelse return null;
- const node = try p.arena.allocator.create(Node.SimplePrefixOp);
- node.* = .{
- .base = .{ .tag = .Try },
- .op_token = token,
- .rhs = undefined, // set by caller
- };
- return &node.base;
- }
+ /// KEYWORD_if LPAREN Expr RPAREN PtrPayload? Body (KEYWORD_else Payload? Body)?
+ fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !Node.Index {
+ const if_token = p.eatToken(.keyword_if) orelse return null_node;
+ _ = try p.expectToken(.l_paren);
+ const condition = try p.expectExpr();
+ _ = try p.expectToken(.r_paren);
+ const then_payload = try p.parsePtrPayload();
- /// IfPrefix Body (KEYWORD_else Payload? Body)?
- fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !?*Node {
- const node = (try p.parseIfPrefix()) orelse return null;
- const if_prefix = node.cast(Node.If).?;
+ const then_expr = try bodyParseFn(p);
+ if (then_expr == 0) return p.fail(.invalid_token);
- if_prefix.body = try p.expectNode(bodyParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
+ const else_token = p.eatToken(.keyword_else) orelse return p.addNode(.{
+ .tag = .if_simple,
+ .main_token = if_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = then_expr,
+ },
});
-
- const else_token = p.eatToken(.Keyword_else) orelse return node;
- const payload = try p.parsePayload();
- const else_expr = try p.expectNode(bodyParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
+ const else_payload = try p.parsePayload();
+ const else_expr = try bodyParseFn(p);
+ if (else_expr == 0) return p.fail(.invalid_token);
+
+ return p.addNode(.{
+ .tag = .@"if",
+ .main_token = if_token,
+ .data = .{
+ .lhs = condition,
+ .rhs = try p.addExtra(Node.If{
+ .then_expr = then_expr,
+ .else_expr = else_expr,
+ }),
+ },
});
- const else_node = try p.arena.allocator.create(Node.Else);
- else_node.* = .{
- .else_token = else_token,
- .payload = payload,
- .body = else_expr,
- };
- if_prefix.@"else" = else_node;
-
- return node;
}
- /// Eat a multiline doc comment
- fn parseDocComment(p: *Parser) !?*Node.DocComment {
- if (p.eatToken(.DocComment)) |first_line| {
- while (p.eatToken(.DocComment)) |_| {}
- const node = try p.arena.allocator.create(Node.DocComment);
- node.* = .{ .first_line = first_line };
- return node;
+ /// Skips over doc comment tokens. Returns the first one, if any.
+ fn eatDocComments(p: *Parser) !?TokenIndex {
+ if (p.eatToken(.doc_comment)) |tok| {
+ var first_line = tok;
+ if (tok > 0 and tokensOnSameLine(p, tok - 1, tok)) {
+ try p.warnMsg(.{
+ .tag = .same_line_doc_comment,
+ .token = tok,
+ });
+ first_line = p.eatToken(.doc_comment) orelse return null;
+ }
+ while (p.eatToken(.doc_comment)) |_| {}
+ return first_line;
}
return null;
}
fn tokensOnSameLine(p: *Parser, token1: TokenIndex, token2: TokenIndex) bool {
- return std.mem.indexOfScalar(u8, p.source[p.token_locs[token1].end..p.token_locs[token2].start], '\n') == null;
+ return std.mem.indexOfScalar(u8, p.source[p.token_starts[token1]..p.token_starts[token2]], '\n') == null;
}
- /// Eat a single-line doc comment on the same line as another node
- fn parseAppendedDocComment(p: *Parser, after_token: TokenIndex) !?*Node.DocComment {
- const comment_token = p.eatToken(.DocComment) orelse return null;
- if (p.tokensOnSameLine(after_token, comment_token)) {
- const node = try p.arena.allocator.create(Node.DocComment);
- node.* = .{ .first_line = comment_token };
- return node;
- }
- p.putBackToken(comment_token);
- return null;
+ fn eatToken(p: *Parser, tag: Token.Tag) ?TokenIndex {
+ return if (p.token_tags[p.tok_i] == tag) p.nextToken() else null;
}
- /// Op* Child
- fn parsePrefixOpExpr(p: *Parser, comptime opParseFn: NodeParseFn, comptime childParseFn: NodeParseFn) Error!?*Node {
- if (try opParseFn(p)) |first_op| {
- var rightmost_op = first_op;
- while (true) {
- switch (rightmost_op.tag) {
- .AddressOf,
- .Await,
- .BitNot,
- .BoolNot,
- .OptionalType,
- .Negation,
- .NegationWrap,
- .Resume,
- .Try,
- => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.SimplePrefixOp).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .ArrayType => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.ArrayType).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .ArrayTypeSentinel => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.ArrayTypeSentinel).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .SliceType => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.SliceType).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .PtrType => {
- var ptr_type = rightmost_op.cast(Node.PtrType).?;
- // If the token encountered was **, there will be two nodes
- if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) {
- rightmost_op = ptr_type.rhs;
- ptr_type = rightmost_op.cast(Node.PtrType).?;
- }
- if (try opParseFn(p)) |rhs| {
- ptr_type.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .AnyFrameType => {
- const prom = rightmost_op.cast(Node.AnyFrameType).?;
- if (try opParseFn(p)) |rhs| {
- prom.result.?.return_type = rhs;
- rightmost_op = rhs;
- } else break;
- },
- else => unreachable,
- }
- }
-
- // If any prefix op existed, a child node on the RHS is required
- switch (rightmost_op.tag) {
- .AddressOf,
- .Await,
- .BitNot,
- .BoolNot,
- .OptionalType,
- .Negation,
- .NegationWrap,
- .Resume,
- .Try,
- => {
- const prefix_op = rightmost_op.cast(Node.SimplePrefixOp).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .ArrayType => {
- const prefix_op = rightmost_op.cast(Node.ArrayType).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .ArrayTypeSentinel => {
- const prefix_op = rightmost_op.cast(Node.ArrayTypeSentinel).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .PtrType => {
- const prefix_op = rightmost_op.cast(Node.PtrType).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .SliceType => {
- const prefix_op = rightmost_op.cast(Node.SliceType).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .AnyFrameType => {
- const prom = rightmost_op.cast(Node.AnyFrameType).?;
- prom.result.?.return_type = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- else => unreachable,
- }
-
- return first_op;
- }
-
- // Otherwise, the child node is optional
- return childParseFn(p);
+ fn assertToken(p: *Parser, tag: Token.Tag) TokenIndex {
+ const token = p.nextToken();
+ assert(p.token_tags[token] == tag);
+ return token;
}
- /// Child (Op Child)*
- /// Child (Op Child)?
- fn parseBinOpExpr(
- p: *Parser,
- opParseFn: NodeParseFn,
- childParseFn: NodeParseFn,
- chain: enum {
- Once,
- Infinitely,
- },
- ) Error!?*Node {
- var res = (try childParseFn(p)) orelse return null;
-
- while (try opParseFn(p)) |node| {
- const right = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
+ fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex {
+ const token = p.nextToken();
+ if (p.token_tags[token] != tag) {
+ p.tok_i -= 1; // Go back so that we can recover properly.
+ return p.failMsg(.{
+ .tag = .expected_token,
+ .token = token,
+ .extra = .{ .expected_tag = tag },
});
- const left = res;
- res = node;
-
- if (node.castTag(.Catch)) |op| {
- op.lhs = left;
- op.rhs = right;
- } else if (node.cast(Node.SimpleInfixOp)) |op| {
- op.lhs = left;
- op.rhs = right;
- }
-
- switch (chain) {
- .Once => break,
- .Infinitely => continue,
- }
}
-
- return res;
- }
-
- fn createInfixOp(p: *Parser, op_token: TokenIndex, tag: Node.Tag) !*Node {
- const node = try p.arena.allocator.create(Node.SimpleInfixOp);
- node.* = .{
- .base = Node{ .tag = tag },
- .op_token = op_token,
- .lhs = undefined, // set by caller
- .rhs = undefined, // set by caller
- };
- return &node.base;
- }
-
- fn eatToken(p: *Parser, id: Token.Id) ?TokenIndex {
- return if (p.token_ids[p.tok_i] == id) p.nextToken() else null;
- }
-
- fn expectToken(p: *Parser, id: Token.Id) Error!TokenIndex {
- return (try p.expectTokenRecoverable(id)) orelse error.ParseError;
+ return token;
}
- fn expectTokenRecoverable(p: *Parser, id: Token.Id) !?TokenIndex {
- const token = p.nextToken();
- if (p.token_ids[token] != id) {
- try p.errors.append(p.gpa, .{
- .ExpectedToken = .{ .token = token, .expected_id = id },
- });
- // go back so that we can recover properly
- p.putBackToken(token);
+ fn expectTokenRecoverable(p: *Parser, tag: Token.Tag) !?TokenIndex {
+ if (p.token_tags[p.tok_i] != tag) {
+ try p.warnExpected(tag);
return null;
+ } else {
+ return p.nextToken();
}
- return token;
}
fn nextToken(p: *Parser) TokenIndex {
const result = p.tok_i;
p.tok_i += 1;
- assert(p.token_ids[result] != .LineComment);
- if (p.tok_i >= p.token_ids.len) return result;
-
- while (true) {
- if (p.token_ids[p.tok_i] != .LineComment) return result;
- p.tok_i += 1;
- }
- }
-
- fn putBackToken(p: *Parser, putting_back: TokenIndex) void {
- while (p.tok_i > 0) {
- p.tok_i -= 1;
- if (p.token_ids[p.tok_i] == .LineComment) continue;
- assert(putting_back == p.tok_i);
- return;
- }
- }
-
- /// TODO Delete this function. I don't like the inversion of control.
- fn expectNode(
- p: *Parser,
- parseFn: NodeParseFn,
- /// if parsing fails
- err: AstError,
- ) Error!*Node {
- return (try p.expectNodeRecoverable(parseFn, err)) orelse return error.ParseError;
- }
-
- /// TODO Delete this function. I don't like the inversion of control.
- fn expectNodeRecoverable(
- p: *Parser,
- parseFn: NodeParseFn,
- /// if parsing fails
- err: AstError,
- ) !?*Node {
- return (try parseFn(p)) orelse {
- try p.errors.append(p.gpa, err);
- return null;
- };
+ return result;
}
};
-fn ParseFn(comptime T: type) type {
- return fn (p: *Parser) Error!T;
-}
-
-test "std.zig.parser" {
+test {
_ = @import("parser_test.zig");
}
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
index 1205dbadab..2b9e3fb03c 100644
--- a/lib/std/zig/parser_test.zig
+++ b/lib/std/zig/parser_test.zig
@@ -1,277 +1,36 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
-test "zig fmt: convert var to anytype" {
- // TODO remove in next release cycle
+
+// TODO Remove this after zig 0.9.0 is released.
+test "zig fmt: rewrite inline functions as callconv(.Inline)" {
try testTransform(
- \\pub fn main(
- \\ a: var,
- \\ bar: var,
- \\) void {}
+ \\inline fn foo() void {}
+ \\
,
- \\pub fn main(
- \\ a: anytype,
- \\ bar: anytype,
- \\) void {}
+ \\fn foo() callconv(.Inline) void {}
\\
);
}
-test "zig fmt: noasync to nosuspend" {
- // TODO: remove this
- try testTransform(
- \\pub fn main() void {
- \\ noasync call();
- \\}
- ,
- \\pub fn main() void {
- \\ nosuspend call();
- \\}
+test "zig fmt: simple top level comptime block" {
+ try testCanonical(
+ \\// line comment
+ \\comptime {}
\\
);
}
-test "recovery: top level" {
- try testError(
- \\test "" {inline}
- \\test "" {inline}
- , &[_]Error{
- .ExpectedInlinable,
- .ExpectedInlinable,
- });
-}
-
-test "recovery: block statements" {
- try testError(
- \\test "" {
- \\ foo + +;
- \\ inline;
- \\}
- , &[_]Error{
- .InvalidToken,
- .ExpectedInlinable,
- });
-}
-
-test "recovery: missing comma" {
- try testError(
- \\test "" {
- \\ switch (foo) {
- \\ 2 => {}
- \\ 3 => {}
- \\ else => {
- \\ foo && bar +;
- \\ }
- \\ }
- \\}
- , &[_]Error{
- .ExpectedToken,
- .ExpectedToken,
- .InvalidAnd,
- .InvalidToken,
- });
-}
-
-test "recovery: extra qualifier" {
- try testError(
- \\const a: *const const u8;
- \\test ""
- , &[_]Error{
- .ExtraConstQualifier,
- .ExpectedLBrace,
- });
-}
-
-test "recovery: missing return type" {
- try testError(
- \\fn foo() {
- \\ a && b;
- \\}
- \\test ""
- , &[_]Error{
- .ExpectedReturnType,
- .InvalidAnd,
- .ExpectedLBrace,
- });
-}
-
-test "recovery: continue after invalid decl" {
- try testError(
- \\fn foo {
- \\ inline;
- \\}
- \\pub test "" {
- \\ async a && b;
- \\}
- , &[_]Error{
- .ExpectedToken,
- .ExpectedPubItem,
- .ExpectedParamList,
- .InvalidAnd,
- });
- try testError(
- \\threadlocal test "" {
- \\ @a && b;
- \\}
- , &[_]Error{
- .ExpectedVarDecl,
- .ExpectedParamList,
- .InvalidAnd,
- });
-}
-
-test "recovery: invalid extern/inline" {
- try testError(
- \\inline test "" { a && b; }
- , &[_]Error{
- .ExpectedFn,
- .InvalidAnd,
- });
- try testError(
- \\extern "" test "" { a && b; }
- , &[_]Error{
- .ExpectedVarDeclOrFn,
- .InvalidAnd,
- });
-}
-
-test "recovery: missing semicolon" {
- try testError(
- \\test "" {
- \\ comptime a && b
- \\ c && d
- \\ @foo
- \\}
- , &[_]Error{
- .InvalidAnd,
- .ExpectedToken,
- .InvalidAnd,
- .ExpectedToken,
- .ExpectedParamList,
- .ExpectedToken,
- });
-}
-
-test "recovery: invalid container members" {
- try testError(
- \\usingnamespace;
- \\foo+
- \\bar@,
- \\while (a == 2) { test "" {}}
- \\test "" {
- \\ a && b
- \\}
- , &[_]Error{
- .ExpectedExpr,
- .ExpectedToken,
- .ExpectedToken,
- .ExpectedContainerMembers,
- .InvalidAnd,
- .ExpectedToken,
- });
-}
-
-test "recovery: invalid parameter" {
- try testError(
- \\fn main() void {
- \\ a(comptime T: type)
- \\}
- , &[_]Error{
- .ExpectedToken,
- });
-}
-
-test "recovery: extra '}' at top level" {
- try testError(
- \\}}}
- \\test "" {
- \\ a && b;
- \\}
- , &[_]Error{
- .ExpectedContainerMembers,
- .ExpectedContainerMembers,
- .ExpectedContainerMembers,
- .InvalidAnd,
- });
-}
-
-test "recovery: mismatched bracket at top level" {
- try testError(
- \\const S = struct {
- \\ arr: 128]?G
- \\};
- , &[_]Error{
- .ExpectedToken,
- });
-}
-
-test "recovery: invalid global error set access" {
- try testError(
- \\test "" {
- \\ error && foo;
- \\}
- , &[_]Error{
- .ExpectedToken,
- .ExpectedIdentifier,
- .InvalidAnd,
- });
-}
-
-test "recovery: invalid asterisk after pointer dereference" {
- try testError(
- \\test "" {
- \\ var sequence = "repeat".*** 10;
- \\}
- , &[_]Error{
- .AsteriskAfterPointerDereference,
- });
- try testError(
- \\test "" {
- \\ var sequence = "repeat".** 10&&a;
- \\}
- , &[_]Error{
- .AsteriskAfterPointerDereference,
- .InvalidAnd,
- });
-}
-
-test "recovery: missing semicolon after if, for, while stmt" {
- try testError(
- \\test "" {
- \\ if (foo) bar
- \\ for (foo) |a| bar
- \\ while (foo) bar
- \\ a && b;
- \\}
- , &[_]Error{
- .ExpectedSemiOrElse,
- .ExpectedSemiOrElse,
- .ExpectedSemiOrElse,
- .InvalidAnd,
- });
-}
-
-test "recovery: invalid comptime" {
- try testError(
- \\comptime
- , &[_]Error{
- .ExpectedBlockOrField,
- });
-}
-
-test "recovery: missing block after for/while loops" {
- try testError(
- \\test "" { while (foo) }
- , &[_]Error{
- .ExpectedBlockOrAssignment,
- });
- try testError(
- \\test "" { for (foo) |bar| }
- , &[_]Error{
- .ExpectedBlockOrAssignment,
- });
+test "zig fmt: two spaced line comments before decl" {
+ try testCanonical(
+ \\// line comment
+ \\
+ \\// another
+ \\comptime {}
+ \\
+ );
}
test "zig fmt: respect line breaks after var declarations" {
@@ -325,6 +84,35 @@ test "zig fmt: empty file" {
);
}
+test "zig fmt: file ends in comment" {
+ try testTransform(
+ \\ //foobar
+ ,
+ \\//foobar
+ \\
+ );
+}
+
+test "zig fmt: file ends in comment after var decl" {
+ try testTransform(
+ \\const x = 42;
+ \\ //foobar
+ ,
+ \\const x = 42;
+ \\//foobar
+ \\
+ );
+}
+
+test "zig fmt: doc comments on test" {
+ try testCanonical(
+ \\/// hello
+ \\/// world
+ \\test "" {}
+ \\
+ );
+}
+
test "zig fmt: if statment" {
try testCanonical(
\\test "" {
@@ -357,7 +145,7 @@ test "zig fmt: decl between fields" {
\\ b: usize,
\\};
, &[_]Error{
- .DeclBetweenFields,
+ .decl_between_fields,
});
}
@@ -365,7 +153,7 @@ test "zig fmt: eof after missing comma" {
try testError(
\\foo()
, &[_]Error{
- .ExpectedToken,
+ .expected_token,
});
}
@@ -402,7 +190,7 @@ test "zig fmt: nosuspend await" {
);
}
-test "zig fmt: trailing comma in container declaration" {
+test "zig fmt: container declaration, single line" {
try testCanonical(
\\const X = struct { foo: i32 };
\\const X = struct { foo: i32, bar: i32 };
@@ -411,27 +199,126 @@ test "zig fmt: trailing comma in container declaration" {
\\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 };
\\
);
+}
+
+test "zig fmt: container declaration, one item, multi line trailing comma" {
try testCanonical(
\\test "" {
\\ comptime {
\\ const X = struct {
+ \\ x: i32,
+ \\ };
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: container declaration, no trailing comma on separate line" {
+ try testTransform(
+ \\test "" {
+ \\ comptime {
+ \\ const X = struct {
\\ x: i32
\\ };
\\ }
\\}
\\
+ ,
+ \\test "" {
+ \\ comptime {
+ \\ const X = struct { x: i32 };
+ \\ }
+ \\}
+ \\
);
+}
+
+test "zig fmt: container declaration, line break, no trailing comma" {
try testTransform(
\\const X = struct {
\\ foo: i32, bar: i8 };
,
+ \\const X = struct { foo: i32, bar: i8 };
+ \\
+ );
+}
+
+test "zig fmt: container declaration, transform trailing comma" {
+ try testTransform(
+ \\const X = struct {
+ \\ foo: i32, bar: i8, };
+ ,
+ \\const X = struct {
+ \\ foo: i32,
+ \\ bar: i8,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: remove empty lines at start/end of container decl" {
+ try testTransform(
+ \\const X = struct {
+ \\
+ \\ foo: i32,
+ \\
+ \\ bar: i8,
+ \\
+ \\};
+ \\
+ ,
\\const X = struct {
- \\ foo: i32, bar: i8
+ \\ foo: i32,
+ \\
+ \\ bar: i8,
\\};
\\
);
}
+test "zig fmt: remove empty lines at start/end of block" {
+ try testTransform(
+ \\test {
+ \\
+ \\ if (foo) {
+ \\ foo();
+ \\ }
+ \\
+ \\}
+ \\
+ ,
+ \\test {
+ \\ if (foo) {
+ \\ foo();
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: allow empty line before commment at start of block" {
+ try testCanonical(
+ \\test {
+ \\
+ \\ // foo
+ \\ const x = 42;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: allow empty line before commment at start of block" {
+ try testCanonical(
+ \\test {
+ \\
+ \\ // foo
+ \\ const x = 42;
+ \\}
+ \\
+ );
+}
+
test "zig fmt: trailing comma in fn parameter list" {
try testCanonical(
\\pub fn f(
@@ -480,6 +367,31 @@ test "zig fmt: comptime struct field" {
);
}
+test "zig fmt: break from block" {
+ try testCanonical(
+ \\const a = blk: {
+ \\ break :blk 42;
+ \\};
+ \\const b = blk: {
+ \\ break :blk;
+ \\};
+ \\const c = {
+ \\ break 42;
+ \\};
+ \\const d = {
+ \\ break;
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: grouped expressions (parentheses)" {
+ try testCanonical(
+ \\const r = (x + y) * (a + b);
+ \\
+ );
+}
+
test "zig fmt: c pointer type" {
try testCanonical(
\\pub extern fn repro() [*c]const u8;
@@ -535,6 +447,19 @@ test "zig fmt: anytype struct field" {
);
}
+test "zig fmt: array types last token" {
+ try testCanonical(
+ \\test {
+ \\ const x = [40]u32;
+ \\}
+ \\
+ \\test {
+ \\ const x = [40:0]u32;
+ \\}
+ \\
+ );
+}
+
test "zig fmt: sentinel-terminated array type" {
try testCanonical(
\\pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 {
@@ -553,6 +478,58 @@ test "zig fmt: sentinel-terminated slice type" {
);
}
+test "zig fmt: pointer-to-one with modifiers" {
+ try testCanonical(
+ \\const x: *u32 = undefined;
+ \\const y: *allowzero align(8) const volatile u32 = undefined;
+ \\const z: *allowzero align(8:4:2) const volatile u32 = undefined;
+ \\
+ );
+}
+
+test "zig fmt: pointer-to-many with modifiers" {
+ try testCanonical(
+ \\const x: [*]u32 = undefined;
+ \\const y: [*]allowzero align(8) const volatile u32 = undefined;
+ \\const z: [*]allowzero align(8:4:2) const volatile u32 = undefined;
+ \\
+ );
+}
+
+test "zig fmt: sentinel pointer with modifiers" {
+ try testCanonical(
+ \\const x: [*:42]u32 = undefined;
+ \\const y: [*:42]allowzero align(8) const volatile u32 = undefined;
+ \\const y: [*:42]allowzero align(8:4:2) const volatile u32 = undefined;
+ \\
+ );
+}
+
+test "zig fmt: c pointer with modifiers" {
+ try testCanonical(
+ \\const x: [*c]u32 = undefined;
+ \\const y: [*c]allowzero align(8) const volatile u32 = undefined;
+ \\const z: [*c]allowzero align(8:4:2) const volatile u32 = undefined;
+ \\
+ );
+}
+
+test "zig fmt: slice with modifiers" {
+ try testCanonical(
+ \\const x: []u32 = undefined;
+ \\const y: []allowzero align(8) const volatile u32 = undefined;
+ \\
+ );
+}
+
+test "zig fmt: sentinel slice with modifiers" {
+ try testCanonical(
+ \\const x: [:42]u32 = undefined;
+ \\const y: [:42]allowzero align(8) const volatile u32 = undefined;
+ \\
+ );
+}
+
test "zig fmt: anon literal in array" {
try testCanonical(
\\var arr: [2]Foo = .{
@@ -581,19 +558,328 @@ test "zig fmt: alignment in anonymous literal" {
);
}
-test "zig fmt: anon struct literal syntax" {
+test "zig fmt: anon struct literal 0 element" {
try testCanonical(
- \\const x = .{
- \\ .a = b,
- \\ .c = d,
- \\};
+ \\test {
+ \\ const x = .{};
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon struct literal 1 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{ .a = b };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon struct literal 1 element comma" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{
+ \\ .a = b,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon struct literal 2 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{ .a = b, .c = d };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon struct literal 2 element comma" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{
+ \\ .a = b,
+ \\ .c = d,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon struct literal 3 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{ .a = b, .c = d, .e = f };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon struct literal 3 element comma" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{
+ \\ .a = b,
+ \\ .c = d,
+ \\ .e = f,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: struct literal 0 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = X{};
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: struct literal 1 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = X{ .a = b };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: Unicode code point literal larger than u8" {
+ try testCanonical(
+ \\test {
+ \\ const x = X{
+ \\ .a = b,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: struct literal 2 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = X{ .a = b, .c = d };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: struct literal 2 element comma" {
+ try testCanonical(
+ \\test {
+ \\ const x = X{
+ \\ .a = b,
+ \\ .c = d,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: struct literal 3 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = X{ .a = b, .c = d, .e = f };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: struct literal 3 element comma" {
+ try testCanonical(
+ \\test {
+ \\ const x = X{
+ \\ .a = b,
+ \\ .c = d,
+ \\ .e = f,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon list literal 1 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{a};
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon list literal 1 element comma" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{
+ \\ a,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon list literal 2 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{ a, b };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon list literal 2 element comma" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{
+ \\ a,
+ \\ b,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon list literal 3 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{ a, b, c };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: anon list literal 3 element comma" {
+ try testCanonical(
+ \\test {
+ \\ const x = .{
+ \\ a,
+ \\ // foo
+ \\ b,
+ \\
+ \\ c,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: array literal 0 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = [_]u32{};
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: array literal 1 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = [_]u32{a};
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: array literal 1 element comma" {
+ try testCanonical(
+ \\test {
+ \\ const x = [1]u32{
+ \\ a,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: array literal 2 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = [_]u32{ a, b };
+ \\}
\\
);
}
-test "zig fmt: anon list literal syntax" {
+test "zig fmt: array literal 2 element comma" {
try testCanonical(
- \\const x = .{ a, b, c };
+ \\test {
+ \\ const x = [2]u32{
+ \\ a,
+ \\ b,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: array literal 3 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = [_]u32{ a, b, c };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: array literal 3 element comma" {
+ try testCanonical(
+ \\test {
+ \\ const x = [3]u32{
+ \\ a,
+ \\ b,
+ \\ c,
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: sentinel array literal 1 element" {
+ try testCanonical(
+ \\test {
+ \\ const x = [_:9000]u32{a};
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: slices" {
+ try testCanonical(
+ \\const a = b[0..];
+ \\const c = d[0..1];
+ \\const e = f[0..1 :0];
+ \\
+ );
+}
+
+test "zig fmt: slices with spaces in bounds" {
+ try testCanonical(
+ \\const a = b[0 + 0 ..];
+ \\const c = d[0 + 0 .. 1];
+ \\const e = f[0 .. 1 + 1 :0];
+ \\
+ );
+}
+
+test "zig fmt: block in slice expression" {
+ try testCanonical(
+ \\const a = b[{
+ \\ _ = x;
+ \\}..];
+ \\const c = d[0..{
+ \\ _ = x;
+ \\ _ = y;
+ \\}];
+ \\const e = f[0..1 :{
+ \\ _ = x;
+ \\ _ = y;
+ \\ _ = z;
+ \\}];
\\
);
}
@@ -651,6 +937,25 @@ test "zig fmt: tagged union with enum values" {
);
}
+test "zig fmt: tagged union enum tag last token" {
+ try testCanonical(
+ \\test {
+ \\ const U = union(enum(u32)) {};
+ \\}
+ \\
+ \\test {
+ \\ const U = union(enum(u32)) { foo };
+ \\}
+ \\
+ \\test {
+ \\ const U = union(enum(u32)) {
+ \\ foo,
+ \\ };
+ \\}
+ \\
+ );
+}
+
test "zig fmt: allowzero pointer" {
try testCanonical(
\\const T = [*]allowzero const u8;
@@ -729,23 +1034,6 @@ test "zig fmt: linksection" {
);
}
-test "zig fmt: correctly move doc comments on struct fields" {
- try testTransform(
- \\pub const section_64 = extern struct {
- \\ sectname: [16]u8, /// name of this section
- \\ segname: [16]u8, /// segment this section goes in
- \\};
- ,
- \\pub const section_64 = extern struct {
- \\ /// name of this section
- \\ sectname: [16]u8,
- \\ /// segment this section goes in
- \\ segname: [16]u8,
- \\};
- \\
- );
-}
-
test "zig fmt: correctly space struct fields with doc comments" {
try testTransform(
\\pub const S = struct {
@@ -934,6 +1222,32 @@ test "zig fmt: doc and line comment following 'zig fmt: on'" {
);
}
+test "zig fmt: 'zig fmt: (off|on)' works in the middle of code" {
+ try testTransform(
+ \\test "" {
+ \\ const x = 42;
+ \\
+ \\ if (foobar) |y| {
+ \\ // zig fmt: off
+ \\ }// zig fmt: on
+ \\
+ \\ const z = 420;
+ \\}
+ \\
+ ,
+ \\test "" {
+ \\ const x = 42;
+ \\
+ \\ if (foobar) |y| {
+ \\ // zig fmt: off
+ \\ }// zig fmt: on
+ \\
+ \\ const z = 420;
+ \\}
+ \\
+ );
+}
+
test "zig fmt: pointer of unknown length" {
try testCanonical(
\\fn foo(ptr: [*]u8) void {}
@@ -975,6 +1289,23 @@ test "zig fmt: 2nd arg multiline string" {
\\}
\\
);
+ try testTransform(
+ \\comptime {
+ \\ cases.addAsm("hello world linux x86_64",
+ \\ \\.text
+ \\ , "Hello, world!\n",);
+ \\}
+ ,
+ \\comptime {
+ \\ cases.addAsm(
+ \\ "hello world linux x86_64",
+ \\ \\.text
+ \\ ,
+ \\ "Hello, world!\n",
+ \\ );
+ \\}
+ \\
+ );
}
test "zig fmt: 2nd arg multiline string many args" {
@@ -1104,7 +1435,7 @@ test "zig fmt: if condition has line break but must not wrap" {
);
}
-test "zig fmt: if condition has line break but must not wrap" {
+test "zig fmt: if condition has line break but must not wrap (no fn call comma)" {
try testCanonical(
\\comptime {
\\ if (self.user_input_options.put(name, UserInputOption{
@@ -1137,31 +1468,6 @@ test "zig fmt: function call with multiline argument" {
);
}
-test "zig fmt: same-line doc comment on variable declaration" {
- try testTransform(
- \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space
- \\pub const MAP_FILE = 0x0000; /// map from file (default)
- \\
- \\pub const EMEDIUMTYPE = 124; /// Wrong medium type
- \\
- \\// nameserver query return codes
- \\pub const ENSROK = 0; /// DNS server returned answer with no data
- ,
- \\/// allocated from memory, swap space
- \\pub const MAP_ANONYMOUS = 0x1000;
- \\/// map from file (default)
- \\pub const MAP_FILE = 0x0000;
- \\
- \\/// Wrong medium type
- \\pub const EMEDIUMTYPE = 124;
- \\
- \\// nameserver query return codes
- \\/// DNS server returned answer with no data
- \\pub const ENSROK = 0;
- \\
- );
-}
-
test "zig fmt: if-else with comment before else" {
try testCanonical(
\\comptime {
@@ -1190,12 +1496,14 @@ test "zig fmt: if nested" {
\\ GE_EQUAL
\\ else
\\ GE_GREATER
+ \\ // comment
\\ else if (aInt > bInt)
\\ GE_LESS
\\ else if (aInt == bInt)
\\ GE_EQUAL
\\ else
\\ GE_GREATER;
+ \\ // comment
\\}
\\
);
@@ -1275,8 +1583,11 @@ test "zig fmt: struct literal no trailing comma" {
\\const a = foo{ .x = 1, .y = 2 };
\\const a = foo{ .x = 1,
\\ .y = 2 };
+ \\const a = foo{ .x = 1,
+ \\ .y = 2, };
,
\\const a = foo{ .x = 1, .y = 2 };
+ \\const a = foo{ .x = 1, .y = 2 };
\\const a = foo{
\\ .x = 1,
\\ .y = 2,
@@ -1291,17 +1602,26 @@ test "zig fmt: struct literal containing a multiline expression" {
\\const a = A{ .x = if (f1()) 10 else 20, };
\\const a = A{ .x = if (f1())
\\ 10 else 20 };
+ \\const a = A{ .x = if (f1())
+ \\ 10 else 20,};
\\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 };
\\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100, };
\\const a = A{ .x = if (f1())
\\ 10 else 20};
+ \\const a = A{ .x = if (f1())
+ \\ 10 else 20,};
\\const a = A{ .x = switch(g) {0 => "ok", else => "no"} };
+ \\const a = A{ .x = switch(g) {0 => "ok", else => "no"}, };
\\
,
\\const a = A{ .x = if (f1()) 10 else 20 };
\\const a = A{
\\ .x = if (f1()) 10 else 20,
\\};
+ \\const a = A{ .x = if (f1())
+ \\ 10
+ \\else
+ \\ 20 };
\\const a = A{
\\ .x = if (f1())
\\ 10
@@ -1313,12 +1633,20 @@ test "zig fmt: struct literal containing a multiline expression" {
\\ .x = if (f1()) 10 else 20,
\\ .y = f2() + 100,
\\};
+ \\const a = A{ .x = if (f1())
+ \\ 10
+ \\else
+ \\ 20 };
\\const a = A{
\\ .x = if (f1())
\\ 10
\\ else
\\ 20,
\\};
+ \\const a = A{ .x = switch (g) {
+ \\ 0 => "ok",
+ \\ else => "no",
+ \\} };
\\const a = A{
\\ .x = switch (g) {
\\ 0 => "ok",
@@ -1368,19 +1696,19 @@ test "zig fmt: array literal with hint" {
\\};
,
\\const a = []u8{
- \\ 1, 2,
+ \\ 1, 2, //
\\ 3, 4,
\\ 5, 6,
\\ 7,
\\};
\\const a = []u8{
- \\ 1, 2,
+ \\ 1, 2, //
\\ 3, 4,
\\ 5, 6,
\\ 7, 8,
\\};
\\const a = []u8{
- \\ 1, 2,
+ \\ 1, 2, //
\\ 3, 4,
\\ 5,
\\ 6, // blah
@@ -1388,21 +1716,19 @@ test "zig fmt: array literal with hint" {
\\ 8,
\\};
\\const a = []u8{
- \\ 1, 2,
+ \\ 1, 2, //
\\ 3, //
\\ 4,
- \\ 5, 6,
+ \\ 5,
+ \\ 6,
\\ 7,
\\};
\\const a = []u8{
\\ 1,
\\ 2,
- \\ 3,
- \\ 4,
- \\ 5,
- \\ 6,
- \\ 7,
- \\ 8,
+ \\ 3, 4, //
+ \\ 5, 6, //
+ \\ 7, 8, //
\\};
\\
);
@@ -1508,11 +1834,21 @@ test "zig fmt: empty block with only comment" {
);
}
-test "zig fmt: no trailing comma on struct decl" {
- try testCanonical(
+test "zig fmt: trailing commas on struct decl" {
+ try testTransform(
\\const RoundParam = struct {
\\ k: usize, s: u32, t: u32
\\};
+ \\const RoundParam = struct {
+ \\ k: usize, s: u32, t: u32,
+ \\};
+ ,
+ \\const RoundParam = struct { k: usize, s: u32, t: u32 };
+ \\const RoundParam = struct {
+ \\ k: usize,
+ \\ s: u32,
+ \\ t: u32,
+ \\};
\\
);
}
@@ -1560,11 +1896,7 @@ test "zig fmt: simple asm" {
\\ : [a] "x" (-> i32)
\\ : [a] "x" (1)
\\ );
- \\ asm ("still not real assembly"
- \\ :
- \\ :
- \\ : "a", "b"
- \\ );
+ \\ asm ("still not real assembly" ::: "a", "b");
\\}
\\
);
@@ -1581,7 +1913,7 @@ test "zig fmt: nested struct literal with one item" {
test "zig fmt: switch cases trailing comma" {
try testTransform(
- \\fn switch_cases(x: i32) void {
+ \\test "switch cases trailing comma"{
\\ switch (x) {
\\ 1,2,3 => {},
\\ 4,5, => {},
@@ -1590,7 +1922,7 @@ test "zig fmt: switch cases trailing comma" {
\\ }
\\}
,
- \\fn switch_cases(x: i32) void {
+ \\test "switch cases trailing comma" {
\\ switch (x) {
\\ 1, 2, 3 => {},
\\ 4,
@@ -1657,18 +1989,18 @@ test "zig fmt: line comment after doc comment" {
);
}
-test "zig fmt: float literal with exponent" {
+test "zig fmt: bit field alignment" {
try testCanonical(
- \\test "bit field alignment" {
+ \\test {
\\ assert(@TypeOf(&blah.b) == *align(1:3:6) const u3);
\\}
\\
);
}
-test "zig fmt: float literal with exponent" {
+test "zig fmt: nested switch" {
try testCanonical(
- \\test "aoeu" {
+ \\test {
\\ switch (state) {
\\ TermState.Start => switch (c) {
\\ '\x1b' => state = TermState.Escape,
@@ -1679,6 +2011,7 @@ test "zig fmt: float literal with exponent" {
\\
);
}
+
test "zig fmt: float literal with exponent" {
try testCanonical(
\\pub const f64_true_min = 4.94065645841246544177e-324;
@@ -2135,7 +2468,7 @@ test "zig fmt: preserve spacing" {
test "zig fmt: return types" {
try testCanonical(
\\pub fn main() !void {}
- \\pub fn main() anytype {}
+ \\pub fn main() FooBar {}
\\pub fn main() i32 {}
\\
);
@@ -2207,6 +2540,33 @@ test "zig fmt: return" {
);
}
+test "zig fmt: function attributes" {
+ try testCanonical(
+ \\export fn foo() void {}
+ \\pub export fn foo() void {}
+ \\extern fn foo() void;
+ \\pub extern fn foo() void;
+ \\extern "c" fn foo() void;
+ \\pub extern "c" fn foo() void;
+ \\noinline fn foo() void {}
+ \\pub noinline fn foo() void {}
+ \\
+ );
+}
+
+test "zig fmt: nested pointers with ** tokens" {
+ try testCanonical(
+ \\const x: *u32 = undefined;
+ \\const x: **u32 = undefined;
+ \\const x: ***u32 = undefined;
+ \\const x: ****u32 = undefined;
+ \\const x: *****u32 = undefined;
+ \\const x: ******u32 = undefined;
+ \\const x: *******u32 = undefined;
+ \\
+ );
+}
+
test "zig fmt: pointer attributes" {
try testCanonical(
\\extern fn f1(s: *align(*u8) u8) c_int;
@@ -2220,11 +2580,11 @@ test "zig fmt: pointer attributes" {
test "zig fmt: slice attributes" {
try testCanonical(
- \\extern fn f1(s: *align(*u8) u8) c_int;
- \\extern fn f2(s: **align(1) *const *volatile u8) c_int;
- \\extern fn f3(s: *align(1) const *align(1) volatile *const volatile u8) c_int;
- \\extern fn f4(s: *align(1) const volatile u8) c_int;
- \\extern fn f5(s: [*:0]align(1) const volatile u8) c_int;
+ \\extern fn f1(s: []align(*u8) u8) c_int;
+ \\extern fn f2(s: []align(1) []const []volatile u8) c_int;
+ \\extern fn f3(s: []align(1) const [:0]align(1) volatile []const volatile u8) c_int;
+ \\extern fn f4(s: []align(1) const volatile u8) c_int;
+ \\extern fn f5(s: [:0]align(1) const volatile u8) c_int;
\\
);
}
@@ -2241,7 +2601,7 @@ test "zig fmt: test declaration" {
test "zig fmt: infix operators" {
try testCanonical(
- \\test "infix operators" {
+ \\test {
\\ var i = undefined;
\\ i = 2;
\\ i *= 2;
@@ -2345,7 +2705,7 @@ test "zig fmt: call expression" {
test "zig fmt: anytype type" {
try testCanonical(
- \\fn print(args: anytype) anytype {}
+ \\fn print(args: anytype) @This() {}
\\
);
}
@@ -2355,17 +2715,17 @@ test "zig fmt: functions" {
\\extern fn puts(s: *const u8) c_int;
\\extern "c" fn puts(s: *const u8) c_int;
\\export fn puts(s: *const u8) c_int;
- \\inline fn puts(s: *const u8) c_int;
+ \\fn puts(s: *const u8) callconv(.Inline) c_int;
\\noinline fn puts(s: *const u8) c_int;
\\pub extern fn puts(s: *const u8) c_int;
\\pub extern "c" fn puts(s: *const u8) c_int;
\\pub export fn puts(s: *const u8) c_int;
- \\pub inline fn puts(s: *const u8) c_int;
+ \\pub fn puts(s: *const u8) callconv(.Inline) c_int;
\\pub noinline fn puts(s: *const u8) c_int;
\\pub extern fn puts(s: *const u8) align(2 + 2) c_int;
\\pub extern "c" fn puts(s: *const u8) align(2 + 2) c_int;
\\pub export fn puts(s: *const u8) align(2 + 2) c_int;
- \\pub inline fn puts(s: *const u8) align(2 + 2) c_int;
+ \\pub fn puts(s: *const u8) align(2 + 2) callconv(.Inline) c_int;
\\pub noinline fn puts(s: *const u8) align(2 + 2) c_int;
\\
);
@@ -2570,7 +2930,11 @@ test "zig fmt: catch" {
\\test "catch" {
\\ const a: anyerror!u8 = 0;
\\ _ = a catch return;
+ \\ _ = a catch
+ \\ return;
\\ _ = a catch |err| return;
+ \\ _ = a catch |err|
+ \\ return;
\\}
\\
);
@@ -2746,12 +3110,6 @@ test "zig fmt: for" {
\\ d => {},
\\ };
\\
- \\ for (a) |b|
- \\ switch (b) {
- \\ c => {},
- \\ d => {},
- \\ };
- \\
\\ const res = for (a) |v, i| {
\\ break v;
\\ } else {
@@ -2777,7 +3135,8 @@ test "zig fmt: for" {
\\test "fix for" {
\\ for (a) |x|
\\ f(x)
- \\ else continue;
+ \\ else
+ \\ continue;
\\}
\\
);
@@ -2948,7 +3307,7 @@ test "zig fmt: nosuspend" {
test "zig fmt: Block after if" {
try testCanonical(
- \\test "Block after if" {
+ \\test {
\\ if (true) {
\\ const a = 0;
\\ }
@@ -2961,7 +3320,7 @@ test "zig fmt: Block after if" {
);
}
-test "zig fmt: use" {
+test "zig fmt: usingnamespace" {
try testCanonical(
\\usingnamespace @import("std");
\\pub usingnamespace @import("std");
@@ -3025,10 +3384,7 @@ test "zig fmt: inline asm parameter alignment" {
\\ asm volatile (
\\ \\ foo
\\ \\ bar
- \\ :
- \\ :
- \\ : "", ""
- \\ );
+ \\ ::: "", "");
\\ asm volatile (
\\ \\ foo
\\ \\ bar
@@ -3087,16 +3443,12 @@ test "zig fmt: file ends with struct field" {
}
test "zig fmt: comment after empty comment" {
- try testTransform(
+ try testCanonical(
\\const x = true; //
\\//
\\//
\\//a
\\
- ,
- \\const x = true;
- \\//a
- \\
);
}
@@ -3113,7 +3465,8 @@ test "zig fmt: line comment in array" {
,
\\test "a" {
\\ var arr = [_]u32{
- \\ 0, // 1,
+ \\ 0,
+ \\ // 1,
\\ // 2,
\\ };
\\}
@@ -3141,7 +3494,8 @@ test "zig fmt: comment after params" {
\\
,
\\fn a(
- \\ b: u32, // c: u32,
+ \\ b: u32,
+ \\ // c: u32,
\\ // d: u32,
\\) void {}
\\
@@ -3174,13 +3528,17 @@ test "zig fmt: comment in array initializer/access" {
\\ var c = b[ //aa
\\ 0
\\ ];
- \\ var d = [_
+ \\ var d = [
+ \\ _
\\ //aa
+ \\ :
+ \\ 0
\\ ]x{ //aa
\\ //bb
\\ 9,
\\ };
- \\ var e = d[0
+ \\ var e = d[
+ \\ 0
\\ //aa
\\ ];
\\}
@@ -3199,7 +3557,8 @@ test "zig fmt: comments at several places in struct init" {
,
\\var bar = Bar{
\\ .x = 10, // test
- \\ .y = "test", // test
+ \\ .y = "test",
+ \\ // test
\\};
\\
);
@@ -3214,7 +3573,7 @@ test "zig fmt: comments at several places in struct init" {
);
}
-test "zig fmt: top level doc comments" {
+test "zig fmt: container doc comments" {
try testCanonical(
\\//! tld 1
\\//! tld 2
@@ -3235,25 +3594,25 @@ test "zig fmt: top level doc comments" {
\\ //! B tld 2
\\ //! B tld 3
\\
- \\ /// b doc
+ \\ /// B doc
\\ b: u32,
\\};
\\
\\/// C doc
- \\const C = struct {
+ \\const C = union(enum) { // comment
\\ //! C tld 1
\\ //! C tld 2
\\ //! C tld 3
+ \\};
\\
- \\ /// c1 doc
- \\ c1: u32,
- \\
- \\ //! C tld 4
- \\ //! C tld 5
- \\ //! C tld 6
+ \\/// D doc
+ \\const D = union(Foo) {
+ \\ //! D tld 1
+ \\ //! D tld 2
+ \\ //! D tld 3
\\
- \\ /// c2 doc
- \\ c2: u32,
+ \\ /// D doc
+ \\ b: u32,
\\};
\\
);
@@ -3275,8 +3634,31 @@ test "zig fmt: extern without container keyword returns error" {
\\const container = extern {};
\\
, &[_]Error{
- .ExpectedExpr,
- .ExpectedVarDeclOrFn,
+ .expected_container,
+ });
+}
+
+test "zig fmt: same line doc comment returns error" {
+ try testError(
+ \\const Foo = struct{
+ \\ bar: u32, /// comment
+ \\ foo: u32, /// comment
+ \\ /// commment
+ \\};
+ \\
+ \\const a = 42; /// comment
+ \\
+ \\extern fn foo() void; /// comment
+ \\
+ \\/// comment
+ \\
+ , &[_]Error{
+ .same_line_doc_comment,
+ .same_line_doc_comment,
+ .unattached_doc_comment,
+ .same_line_doc_comment,
+ .same_line_doc_comment,
+ .unattached_doc_comment,
});
}
@@ -3350,26 +3732,6 @@ test "zig fmt: hexadeciaml float literals with underscore separators" {
);
}
-test "zig fmt: convert async fn into callconv(.Async)" {
- try testTransform(
- \\async fn foo() void {}
- ,
- \\fn foo() callconv(.Async) void {}
- \\
- );
-}
-
-test "zig fmt: convert extern fn proto into callconv(.C)" {
- try testTransform(
- \\extern fn foo0() void {}
- \\const foo1 = extern fn () void;
- ,
- \\extern fn foo0() void {}
- \\const foo1 = fn () callconv(.C) void;
- \\
- );
-}
-
test "zig fmt: C var args" {
try testCanonical(
\\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
@@ -3458,6 +3820,54 @@ test "zig fmt: test comments in field access chain" {
);
}
+test "zig fmt: allow line break before field access" {
+ try testCanonical(
+ \\test {
+ \\ const w = foo.bar().zippy(zag).iguessthisisok();
+ \\
+ \\ const x = foo
+ \\ .bar()
+ \\ . // comment
+ \\ // comment
+ \\ swooop().zippy(zag)
+ \\ .iguessthisisok();
+ \\
+ \\ const y = view.output.root.server.input_manager.default_seat.wlr_seat.name;
+ \\
+ \\ const z = view.output.root.server
+ \\ .input_manager //
+ \\ .default_seat
+ \\ . // comment
+ \\ // another comment
+ \\ wlr_seat.name;
+ \\}
+ \\
+ );
+ try testTransform(
+ \\test {
+ \\ const x = foo.
+ \\ bar()
+ \\ .zippy(zag).iguessthisisok();
+ \\
+ \\ const z = view.output.root.server.
+ \\ input_manager.
+ \\ default_seat.wlr_seat.name;
+ \\}
+ \\
+ ,
+ \\test {
+ \\ const x = foo
+ \\ .bar()
+ \\ .zippy(zag).iguessthisisok();
+ \\
+ \\ const z = view.output.root.server
+ \\ .input_manager
+ \\ .default_seat.wlr_seat.name;
+ \\}
+ \\
+ );
+}
+
test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" {
try testCanonical(
\\fn foo() void {
@@ -3495,8 +3905,7 @@ test "zig fmt: Control flow statement as body of blockless if" {
\\
\\ const zoom_node = if (focused_node == layout_first) while (it.next()) |node| {
\\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
- \\ } else null else
- \\ focused_node;
+ \\ } else null else focused_node;
\\
\\ const zoom_node = if (focused_node == layout_first)
\\ if (it.next()) {
@@ -3513,14 +3922,13 @@ test "zig fmt: Control flow statement as body of blockless if" {
\\
\\ const zoom_node = if (focused_node == layout_first) switch (nodes) {
\\ 0 => 0,
- \\ } else
- \\ focused_node;
+ \\ } else focused_node;
\\}
\\
);
}
-test "zig fmt: " {
+test "zig fmt: regression test for #5722" {
try testCanonical(
\\pub fn sendViewTags(self: Self) void {
\\ var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32));
@@ -3580,8 +3988,8 @@ test "zig fmt: multiline string literals should play nice with array initializer
\\ 0,
\\ }}}}}}}};
\\ myFunc(.{
- \\ "aaaaaaa", "bbbbbb", "ccccc",
- \\ "dddd", ("eee"), ("fff"),
+ \\ "aaaaaaa", "bbbbbb", "ccccc",
+ \\ "dddd", ("eee"), ("fff"),
\\ ("gggg"),
\\ // Line comment
\\ \\Multiline String Literals can be quite long
@@ -3610,9 +4018,11 @@ test "zig fmt: multiline string literals should play nice with array initializer
\\ (
\\ \\ xxx
\\ ),
- \\ "xxx", "xxx",
+ \\ "xxx",
+ \\ "xxx",
\\ },
- \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, .{ "xxxxxxx", "xxx", "xxx", "xxx" },
+ \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
+ \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
\\ "aaaaaaa", "bbbbbb", "ccccc", // -
\\ "dddd", ("eee"), ("fff"),
\\ .{
@@ -3620,7 +4030,8 @@ test "zig fmt: multiline string literals should play nice with array initializer
\\ (
\\ \\ xxx
\\ ),
- \\ "xxxxxxxxxxxxxx", "xxx",
+ \\ "xxxxxxxxxxxxxx",
+ \\ "xxx",
\\ },
\\ .{
\\ (
@@ -3636,10 +4047,10 @@ test "zig fmt: multiline string literals should play nice with array initializer
);
}
-test "zig fmt: use of comments and Multiline string literals may force the parameters over multiple lines" {
+test "zig fmt: use of comments and multiline string literals may force the parameters over multiple lines" {
try testCanonical(
\\pub fn makeMemUndefined(qzz: []u8) i1 {
- \\ cases.add( // fixed bug #2032
+ \\ cases.add( // fixed bug foo
\\ "compile diagnostic string for top level decl type",
\\ \\export fn entry() void {
\\ \\ var foo: u32 = @This(){};
@@ -3661,7 +4072,7 @@ test "zig fmt: use of comments and Multiline string literals may force the param
\\const rparen = tree.prevToken(
\\// the first token for the annotation expressions is the left
\\// parenthesis, hence the need for two prevToken
- \\ if (fn_proto.getAlignExpr()) |align_expr|
+ \\if (fn_proto.getAlignExpr()) |align_expr|
\\ tree.prevToken(tree.prevToken(align_expr.firstToken()))
\\else if (fn_proto.getSectionExpr()) |section_expr|
\\ tree.prevToken(tree.prevToken(section_expr.firstToken()))
@@ -3714,7 +4125,7 @@ test "zig fmt: function params should align nicely" {
\\pub fn foo() void {
\\ cases.addRuntimeSafety("slicing operator with sentinel",
\\ \\const std = @import("std");
- \\ ++ check_panic_msg ++
+ \\ ++ check_panic_msg ++
\\ \\pub fn main() void {
\\ \\ var buf = [4]u8{'a','b','c',0};
\\ \\ const slice = buf[0..:0];
@@ -3725,6 +4136,419 @@ test "zig fmt: function params should align nicely" {
);
}
+test "zig fmt: fn proto end with anytype and comma" {
+ try testCanonical(
+ \\pub fn format(
+ \\ out_stream: anytype,
+ \\) !void {}
+ \\
+ );
+}
+
+test "zig fmt: space after top level doc comment" {
+ try testCanonical(
+ \\//! top level doc comment
+ \\
+ \\field: i32,
+ \\
+ );
+}
+
+test "zig fmt: for loop with ptr payload and index" {
+ try testCanonical(
+ \\test {
+ \\ for (self.entries.items) |*item, i| {}
+ \\ for (self.entries.items) |*item, i|
+ \\ a = b;
+ \\ for (self.entries.items) |*item, i| a = b;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: proper indent line comment after multi-line single expr while loop" {
+ try testCanonical(
+ \\test {
+ \\ while (a) : (b)
+ \\ foo();
+ \\
+ \\ // bar
+ \\ baz();
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: function with labeled block as return type" {
+ try testCanonical(
+ \\fn foo() t: {
+ \\ break :t bar;
+ \\} {
+ \\ baz();
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: extern function with missing param name" {
+ try testCanonical(
+ \\extern fn a(
+ \\ *b,
+ \\ c: *d,
+ \\) e;
+ \\extern fn f(*g, h: *i) j;
+ \\
+ );
+}
+
+test "zig fmt: line comment after multiline single expr if statement with multiline string" {
+ try testCanonical(
+ \\test {
+ \\ if (foo)
+ \\ x =
+ \\ \\hello
+ \\ \\hello
+ \\ \\
+ \\ ;
+ \\
+ \\ // bar
+ \\ baz();
+ \\
+ \\ if (foo)
+ \\ x =
+ \\ \\hello
+ \\ \\hello
+ \\ \\
+ \\ else
+ \\ y =
+ \\ \\hello
+ \\ \\hello
+ \\ \\
+ \\ ;
+ \\
+ \\ // bar
+ \\ baz();
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: respect extra newline between fn and pub usingnamespace" {
+ try testCanonical(
+ \\fn foo() void {
+ \\ bar();
+ \\}
+ \\
+ \\pub usingnamespace baz;
+ \\
+ );
+}
+
+test "zig fmt: respect extra newline between switch items" {
+ try testCanonical(
+ \\const a = switch (b) {
+ \\ .c => {},
+ \\
+ \\ .d,
+ \\ .e,
+ \\ => f,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: insert trailing comma if there are comments between switch values" {
+ try testTransform(
+ \\const a = switch (b) {
+ \\ .c => {},
+ \\
+ \\ .d, // foobar
+ \\ .e
+ \\ => f,
+ \\
+ \\ .g, .h
+ \\ // comment
+ \\ => i,
+ \\};
+ \\
+ ,
+ \\const a = switch (b) {
+ \\ .c => {},
+ \\
+ \\ .d, // foobar
+ \\ .e,
+ \\ => f,
+ \\
+ \\ .g,
+ \\ .h,
+ \\ // comment
+ \\ => i,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: error for invalid bit range" {
+ try testError(
+ \\var x: []align(0:0:0)u8 = bar;
+ , &[_]Error{
+ .invalid_bit_range,
+ });
+}
+
+test "zig fmt: error for invalid align" {
+ try testError(
+ \\var x: [10]align(10)u8 = bar;
+ , &[_]Error{
+ .invalid_align,
+ });
+}
+
+test "recovery: top level" {
+ try testError(
+ \\test "" {inline}
+ \\test "" {inline}
+ , &[_]Error{
+ .expected_inlinable,
+ .expected_inlinable,
+ });
+}
+
+test "recovery: block statements" {
+ try testError(
+ \\test "" {
+ \\ foo + +;
+ \\ inline;
+ \\}
+ , &[_]Error{
+ .invalid_token,
+ .expected_inlinable,
+ });
+}
+
+test "recovery: missing comma" {
+ try testError(
+ \\test "" {
+ \\ switch (foo) {
+ \\ 2 => {}
+ \\ 3 => {}
+ \\ else => {
+ \\ foo && bar +;
+ \\ }
+ \\ }
+ \\}
+ , &[_]Error{
+ .expected_token,
+ .expected_token,
+ .invalid_and,
+ .invalid_token,
+ });
+}
+
+test "recovery: extra qualifier" {
+ try testError(
+ \\const a: *const const u8;
+ \\test ""
+ , &[_]Error{
+ .extra_const_qualifier,
+ .expected_block,
+ });
+}
+
+test "recovery: missing return type" {
+ try testError(
+ \\fn foo() {
+ \\ a && b;
+ \\}
+ \\test ""
+ , &[_]Error{
+ .expected_return_type,
+ .invalid_and,
+ .expected_block,
+ });
+}
+
+test "recovery: continue after invalid decl" {
+ try testError(
+ \\fn foo {
+ \\ inline;
+ \\}
+ \\pub test "" {
+ \\ async a && b;
+ \\}
+ , &[_]Error{
+ .expected_token,
+ .expected_pub_item,
+ .expected_param_list,
+ .invalid_and,
+ });
+ try testError(
+ \\threadlocal test "" {
+ \\ @a && b;
+ \\}
+ , &[_]Error{
+ .expected_var_decl,
+ .expected_param_list,
+ .invalid_and,
+ });
+}
+
+test "recovery: invalid extern/inline" {
+ try testError(
+ \\inline test "" { a && b; }
+ , &[_]Error{
+ .expected_fn,
+ .invalid_and,
+ });
+ try testError(
+ \\extern "" test "" { a && b; }
+ , &[_]Error{
+ .expected_var_decl_or_fn,
+ .invalid_and,
+ });
+}
+
+test "recovery: missing semicolon" {
+ try testError(
+ \\test "" {
+ \\ comptime a && b
+ \\ c && d
+ \\ @foo
+ \\}
+ , &[_]Error{
+ .invalid_and,
+ .expected_token,
+ .invalid_and,
+ .expected_token,
+ .expected_param_list,
+ .expected_token,
+ });
+}
+
+test "recovery: invalid container members" {
+ try testError(
+ \\usingnamespace;
+ \\foo+
+ \\bar@,
+ \\while (a == 2) { test "" {}}
+ \\test "" {
+ \\ a && b
+ \\}
+ , &[_]Error{
+ .expected_expr,
+ .expected_token,
+ .expected_container_members,
+ .invalid_and,
+ .expected_token,
+ });
+}
+
+// TODO after https://github.com/ziglang/zig/issues/35 is implemented,
+// we should be able to recover from this *at any indentation level*,
+// reporting a parse error and yet also parsing all the decls even
+// inside structs.
+test "recovery: extra '}' at top level" {
+ try testError(
+ \\}}}
+ \\test "" {
+ \\ a && b;
+ \\}
+ , &[_]Error{
+ .expected_token,
+ });
+}
+
+test "recovery: mismatched bracket at top level" {
+ try testError(
+ \\const S = struct {
+ \\ arr: 128]?G
+ \\};
+ , &[_]Error{
+ .expected_token,
+ });
+}
+
+test "recovery: invalid global error set access" {
+ try testError(
+ \\test "" {
+ \\ error && foo;
+ \\}
+ , &[_]Error{
+ .expected_token,
+ .expected_token,
+ .invalid_and,
+ });
+}
+
+test "recovery: invalid asterisk after pointer dereference" {
+ try testError(
+ \\test "" {
+ \\ var sequence = "repeat".*** 10;
+ \\}
+ , &[_]Error{
+ .asterisk_after_ptr_deref,
+ });
+ try testError(
+ \\test "" {
+ \\ var sequence = "repeat".** 10&&a;
+ \\}
+ , &[_]Error{
+ .asterisk_after_ptr_deref,
+ .invalid_and,
+ });
+}
+
+test "recovery: missing semicolon after if, for, while stmt" {
+ try testError(
+ \\test "" {
+ \\ if (foo) bar
+ \\ for (foo) |a| bar
+ \\ while (foo) bar
+ \\ a && b;
+ \\}
+ , &[_]Error{
+ .expected_semi_or_else,
+ .expected_semi_or_else,
+ .expected_semi_or_else,
+ .invalid_and,
+ });
+}
+
+test "recovery: invalid comptime" {
+ try testError(
+ \\comptime
+ , &[_]Error{
+ .expected_block_or_field,
+ });
+}
+
+test "recovery: missing block after for/while loops" {
+ try testError(
+ \\test "" { while (foo) }
+ , &[_]Error{
+ .expected_block_or_assignment,
+ });
+ try testError(
+ \\test "" { for (foo) |bar| }
+ , &[_]Error{
+ .expected_block_or_assignment,
+ });
+}
+
+test "recovery: missing for payload" {
+ try testError(
+ \\comptime {
+ \\ const a = for(a) {};
+ \\ const a: for(a) {};
+ \\ for(a) {}
+ \\}
+ , &[_]Error{
+ .expected_loop_payload,
+ .expected_loop_payload,
+ .expected_loop_payload,
+ });
+}
+
const std = @import("std");
const mem = std.mem;
const warn = std.debug.warn;
@@ -3734,29 +4558,23 @@ const maxInt = std.math.maxInt;
var fixed_buffer_mem: [100 * 1024]u8 = undefined;
fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {
- const stderr = io.getStdErr().outStream();
+ const stderr = io.getStdErr().writer();
- const tree = try std.zig.parse(allocator, source);
- defer tree.deinit();
+ var tree = try std.zig.parse(allocator, source);
+ defer tree.deinit(allocator);
- for (tree.errors) |*parse_error| {
- const token = tree.token_locs[parse_error.loc()];
- const loc = tree.tokenLocation(0, parse_error.loc());
- try stderr.print("(memory buffer):{}:{}: error: ", .{ loc.line + 1, loc.column + 1 });
+ for (tree.errors) |parse_error| {
+ const token_start = tree.tokens.items(.start)[parse_error.token];
+ const loc = tree.tokenLocation(0, parse_error.token);
+ try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 });
try tree.renderError(parse_error, stderr);
- try stderr.print("\n{}\n", .{source[loc.line_start..loc.line_end]});
+ try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]});
{
var i: usize = 0;
while (i < loc.column) : (i += 1) {
try stderr.writeAll(" ");
}
- }
- {
- const caret_count = token.end - token.start;
- var i: usize = 0;
- while (i < caret_count) : (i += 1) {
- try stderr.writeAll("~");
- }
+ try stderr.writeAll("^");
}
try stderr.writeAll("\n");
}
@@ -3764,12 +4582,9 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
return error.ParseError;
}
- var buffer = std.ArrayList(u8).init(allocator);
- errdefer buffer.deinit();
-
- const outStream = buffer.outStream();
- anything_changed.* = try std.zig.render(allocator, outStream, tree);
- return buffer.toOwnedSlice();
+ const formatted = try tree.render(allocator);
+ anything_changed.* = !mem.eql(u8, formatted, source);
+ return formatted;
}
fn testTransform(source: []const u8, expected_source: []const u8) !void {
const needed_alloc_count = x: {
@@ -3800,7 +4615,7 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void {
error.OutOfMemory => {
if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
warn(
- "\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
+ "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\n",
.{
fail_index,
needed_alloc_count,
@@ -3822,14 +4637,14 @@ fn testCanonical(source: []const u8) !void {
return testTransform(source, source);
}
-const Error = @TagType(std.zig.ast.Error);
+const Error = std.zig.ast.Error.Tag;
fn testError(source: []const u8, expected_errors: []const Error) !void {
- const tree = try std.zig.parse(std.testing.allocator, source);
- defer tree.deinit();
+ var tree = try std.zig.parse(std.testing.allocator, source);
+ defer tree.deinit(std.testing.allocator);
std.testing.expect(tree.errors.len == expected_errors.len);
for (expected_errors) |expected, i| {
- std.testing.expect(expected == tree.errors[i]);
+ std.testing.expectEqual(expected, tree.errors[i].tag);
}
}
diff --git a/lib/std/zig/perf_test.zig b/lib/std/zig/perf_test.zig
index 30ae99e57c..b111170902 100644
--- a/lib/std/zig/perf_test.zig
+++ b/lib/std/zig/perf_test.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -29,7 +29,7 @@ pub fn main() !void {
const mb_per_sec = bytes_per_sec / (1024 * 1024);
var stdout_file = std.io.getStdOut();
- const stdout = stdout_file.outStream();
+ const stdout = stdout_file.writer();
try stdout.print("{:.3} MiB/s, {} KiB used \n", .{ mb_per_sec, memory_used / 1024 });
}
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
index f73979aa6b..e12f7bc733 100644
--- a/lib/std/zig/render.zig
+++ b/lib/std/zig/render.zig
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
const std = @import("../std.zig");
const assert = std.debug.assert;
const mem = std.mem;
+const Allocator = std.mem.Allocator;
const meta = std.meta;
const ast = std.zig.ast;
const Token = std.zig.Token;
@@ -13,2654 +14,2557 @@ const Token = std.zig.Token;
const indent_delta = 4;
const asm_indent_delta = 2;
-pub const Error = error{
- /// Ran out of memory allocating call stack frames to complete rendering.
- OutOfMemory,
-};
-
-/// Returns whether anything changed
-pub fn render(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool {
- // cannot render an invalid tree
- std.debug.assert(tree.errors.len == 0);
+pub const Error = ast.Tree.RenderError;
- var change_detection_stream = std.io.changeDetectionStream(tree.source, stream);
- var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, change_detection_stream.writer());
+const Ais = AutoIndentingStream(std.ArrayList(u8).Writer);
- try renderRoot(allocator, &auto_indenting_stream, tree);
+pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {
+ assert(tree.errors.len == 0); // Cannot render an invalid tree.
+ var auto_indenting_stream = Ais{
+ .indent_delta = indent_delta,
+ .underlying_writer = buffer.writer(),
+ };
+ const ais = &auto_indenting_stream;
- return change_detection_stream.changeDetected();
-}
+ // Render all the line comments at the beginning of the file.
+ const comment_end_loc = tree.tokens.items(.start)[0];
+ _ = try renderComments(ais, tree, 0, comment_end_loc);
-fn renderRoot(
- allocator: *mem.Allocator,
- ais: anytype,
- tree: *ast.Tree,
-) (@TypeOf(ais.*).Error || Error)!void {
-
- // render all the line comments at the beginning of the file
- for (tree.token_ids) |token_id, i| {
- if (token_id != .LineComment) break;
- const token_loc = tree.token_locs[i];
- try ais.writer().print("{}\n", .{mem.trimRight(u8, tree.tokenSliceLoc(token_loc), " ")});
- const next_token = tree.token_locs[i + 1];
- const loc = tree.tokenLocationLoc(token_loc.end, next_token);
- if (loc.line >= 2) {
- try ais.insertNewline();
- }
+ if (tree.tokens.items(.tag)[0] == .container_doc_comment) {
+ try renderContainerDocComments(ais, tree, 0);
}
- var decl_i: ast.NodeIndex = 0;
- const root_decls = tree.root_node.decls();
+ try renderMembers(buffer.allocator, ais, tree, tree.rootDecls());
- if (root_decls.len == 0) return;
- while (true) {
- var decl = root_decls[decl_i];
-
- // This loop does the following:
- //
- // - Iterates through line/doc comment tokens that precedes the current
- // decl.
- // - Figures out the first token index (`copy_start_token_index`) which
- // hasn't been copied to the output stream yet.
- // - Detects `zig fmt: (off|on)` in the line comment tokens, and
- // determines whether the current decl should be reformatted or not.
- //
- var token_index = decl.firstToken();
- var fmt_active = true;
- var found_fmt_directive = false;
-
- var copy_start_token_index = token_index;
-
- while (token_index != 0) {
- token_index -= 1;
- const token_id = tree.token_ids[token_index];
- switch (token_id) {
- .LineComment => {},
- .DocComment => {
- copy_start_token_index = token_index;
- continue;
- },
- else => break,
- }
+ if (ais.disabled_offset) |disabled_offset| {
+ try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..]);
+ }
+}
- const token_loc = tree.token_locs[token_index];
- if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: off")) {
- if (!found_fmt_directive) {
- fmt_active = false;
- found_fmt_directive = true;
- }
- } else if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: on")) {
- if (!found_fmt_directive) {
- fmt_active = true;
- found_fmt_directive = true;
- }
- }
- }
+/// Render all members in the given slice, keeping empty lines where appropriate
+fn renderMembers(gpa: *Allocator, ais: *Ais, tree: ast.Tree, members: []const ast.Node.Index) Error!void {
+ if (members.len == 0) return;
+ try renderMember(gpa, ais, tree, members[0], .newline);
+ for (members[1..]) |member| {
+ try renderExtraNewline(ais, tree, member);
+ try renderMember(gpa, ais, tree, member, .newline);
+ }
+}
- if (!fmt_active) {
- // Reformatting is disabled for the current decl and possibly some
- // more decls that follow.
- // Find the next `decl` for which reformatting is re-enabled.
- token_index = decl.firstToken();
-
- while (!fmt_active) {
- decl_i += 1;
- if (decl_i >= root_decls.len) {
- // If there's no next reformatted `decl`, just copy the
- // remaining input tokens and bail out.
- const start = tree.token_locs[copy_start_token_index].start;
- try copyFixingWhitespace(ais, tree.source[start..]);
- return;
+fn renderMember(gpa: *Allocator, ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const main_tokens = tree.nodes.items(.main_token);
+ const datas = tree.nodes.items(.data);
+ try renderDocComments(ais, tree, tree.firstToken(decl));
+ switch (tree.nodes.items(.tag)[decl]) {
+ .fn_decl => {
+ // Some examples:
+ // pub extern "foo" fn ...
+ // export fn ...
+ const fn_proto = datas[decl].lhs;
+ const fn_token = main_tokens[fn_proto];
+ // Go back to the first token we should render here.
+ var i = fn_token;
+ while (i > 0) {
+ i -= 1;
+ switch (token_tags[i]) {
+ .keyword_extern,
+ .keyword_export,
+ .keyword_pub,
+ .string_literal,
+ .keyword_inline,
+ .keyword_noinline,
+ => continue,
+
+ else => {
+ i += 1;
+ break;
+ },
}
- decl = root_decls[decl_i];
- var decl_first_token_index = decl.firstToken();
-
- while (token_index < decl_first_token_index) : (token_index += 1) {
- const token_id = tree.token_ids[token_index];
- switch (token_id) {
- .LineComment => {},
- .Eof => unreachable,
- else => continue,
- }
- const token_loc = tree.token_locs[token_index];
- if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: on")) {
- fmt_active = true;
- } else if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: off")) {
- fmt_active = false;
- }
+ }
+ while (i < fn_token) : (i += 1) {
+ if (token_tags[i] == .keyword_inline) {
+ // TODO remove this special case when 0.9.0 is released.
+ // See the commit that introduced this comment for more details.
+ continue;
}
+ try renderToken(ais, tree, i, .space);
}
-
- // Found the next `decl` for which reformatting is enabled. Copy
- // the input tokens before the `decl` that haven't been copied yet.
- var copy_end_token_index = decl.firstToken();
- token_index = copy_end_token_index;
- while (token_index != 0) {
- token_index -= 1;
- const token_id = tree.token_ids[token_index];
- switch (token_id) {
- .LineComment => {},
- .DocComment => {
- copy_end_token_index = token_index;
- continue;
+ assert(datas[decl].rhs != 0);
+ try renderExpression(gpa, ais, tree, fn_proto, .space);
+ return renderExpression(gpa, ais, tree, datas[decl].rhs, space);
+ },
+ .fn_proto_simple,
+ .fn_proto_multi,
+ .fn_proto_one,
+ .fn_proto,
+ => {
+ // Extern function prototypes are parsed as these tags.
+ // Go back to the first token we should render here.
+ const fn_token = main_tokens[decl];
+ var i = fn_token;
+ while (i > 0) {
+ i -= 1;
+ switch (token_tags[i]) {
+ .keyword_extern,
+ .keyword_export,
+ .keyword_pub,
+ .string_literal,
+ .keyword_inline,
+ .keyword_noinline,
+ => continue,
+
+ else => {
+ i += 1;
+ break;
},
- else => break,
}
}
-
- const start = tree.token_locs[copy_start_token_index].start;
- const end = tree.token_locs[copy_end_token_index].start;
- try copyFixingWhitespace(ais, tree.source[start..end]);
- }
-
- try renderTopLevelDecl(allocator, ais, tree, decl);
- decl_i += 1;
- if (decl_i >= root_decls.len) return;
- try renderExtraNewline(tree, ais, root_decls[decl_i]);
- }
-}
-
-fn renderExtraNewline(tree: *ast.Tree, ais: anytype, node: *ast.Node) @TypeOf(ais.*).Error!void {
- return renderExtraNewlineToken(tree, ais, node.firstToken());
-}
-
-fn renderExtraNewlineToken(
- tree: *ast.Tree,
- ais: anytype,
- first_token: ast.TokenIndex,
-) @TypeOf(ais.*).Error!void {
- var prev_token = first_token;
- if (prev_token == 0) return;
- var newline_threshold: usize = 2;
- while (tree.token_ids[prev_token - 1] == .DocComment) {
- if (tree.tokenLocation(tree.token_locs[prev_token - 1].end, prev_token).line == 1) {
- newline_threshold += 1;
- }
- prev_token -= 1;
- }
- const prev_token_end = tree.token_locs[prev_token - 1].end;
- const loc = tree.tokenLocation(prev_token_end, first_token);
- if (loc.line >= newline_threshold) {
- try ais.insertNewline();
- }
-}
-
-fn renderTopLevelDecl(allocator: *mem.Allocator, ais: anytype, tree: *ast.Tree, decl: *ast.Node) (@TypeOf(ais.*).Error || Error)!void {
- try renderContainerDecl(allocator, ais, tree, decl, .Newline);
-}
-
-fn renderContainerDecl(allocator: *mem.Allocator, ais: anytype, tree: *ast.Tree, decl: *ast.Node, space: Space) (@TypeOf(ais.*).Error || Error)!void {
- switch (decl.tag) {
- .FnProto => {
- const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
-
- try renderDocComments(tree, ais, fn_proto, fn_proto.getDocComments());
-
- if (fn_proto.getBodyNode()) |body_node| {
- try renderExpression(allocator, ais, tree, decl, .Space);
- try renderExpression(allocator, ais, tree, body_node, space);
- } else {
- try renderExpression(allocator, ais, tree, decl, .None);
- try renderToken(tree, ais, tree.nextToken(decl.lastToken()), space);
+ while (i < fn_token) : (i += 1) {
+ try renderToken(ais, tree, i, .space);
}
+ try renderExpression(gpa, ais, tree, decl, .none);
+ return renderToken(ais, tree, tree.lastToken(decl) + 1, space); // semicolon
},
- .Use => {
- const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
-
- if (use_decl.visib_token) |visib_token| {
- try renderToken(tree, ais, visib_token, .Space); // pub
+ .@"usingnamespace" => {
+ const main_token = main_tokens[decl];
+ const expr = datas[decl].lhs;
+ if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) {
+ try renderToken(ais, tree, main_token - 1, .space); // pub
}
- try renderToken(tree, ais, use_decl.use_token, .Space); // usingnamespace
- try renderExpression(allocator, ais, tree, use_decl.expr, .None);
- try renderToken(tree, ais, use_decl.semicolon_token, space); // ;
+ try renderToken(ais, tree, main_token, .space); // usingnamespace
+ try renderExpression(gpa, ais, tree, expr, .none);
+ return renderToken(ais, tree, tree.lastToken(expr) + 1, space); // ;
},
- .VarDecl => {
- const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
+ .global_var_decl => return renderVarDecl(gpa, ais, tree, tree.globalVarDecl(decl)),
+ .local_var_decl => return renderVarDecl(gpa, ais, tree, tree.localVarDecl(decl)),
+ .simple_var_decl => return renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(decl)),
+ .aligned_var_decl => return renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(decl)),
- try renderDocComments(tree, ais, var_decl, var_decl.getDocComments());
- try renderVarDecl(allocator, ais, tree, var_decl);
+ .test_decl => {
+ const test_token = main_tokens[decl];
+ try renderToken(ais, tree, test_token, .space);
+ if (token_tags[test_token + 1] == .string_literal) {
+ try renderToken(ais, tree, test_token + 1, .space);
+ }
+ try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
},
- .TestDecl => {
- const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
+ .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space),
+ .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space),
+ .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), space),
+ .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),
- try renderDocComments(tree, ais, test_decl, test_decl.doc_comments);
- try renderToken(tree, ais, test_decl.test_token, .Space);
- try renderExpression(allocator, ais, tree, test_decl.name, .Space);
- try renderExpression(allocator, ais, tree, test_decl.body_node, space);
- },
+ .root => unreachable,
+ else => unreachable,
+ }
+}
- .ContainerField => {
- const field = @fieldParentPtr(ast.Node.ContainerField, "base", decl);
+/// Render all expressions in the slice, keeping empty lines where appropriate
+fn renderExpressions(gpa: *Allocator, ais: *Ais, tree: ast.Tree, expressions: []const ast.Node.Index, space: Space) Error!void {
+ if (expressions.len == 0) return;
+ try renderExpression(gpa, ais, tree, expressions[0], space);
+ for (expressions[1..]) |expression| {
+ try renderExtraNewline(ais, tree, expression);
+ try renderExpression(gpa, ais, tree, expression, space);
+ }
+}
- try renderDocComments(tree, ais, field, field.doc_comments);
- if (field.comptime_token) |t| {
- try renderToken(tree, ais, t, .Space); // comptime
- }
+fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const main_tokens = tree.nodes.items(.main_token);
+ const node_tags = tree.nodes.items(.tag);
+ const datas = tree.nodes.items(.data);
+ switch (node_tags[node]) {
+ .identifier,
+ .integer_literal,
+ .float_literal,
+ .char_literal,
+ .true_literal,
+ .false_literal,
+ .null_literal,
+ .unreachable_literal,
+ .undefined_literal,
+ .anyframe_literal,
+ .string_literal,
+ => return renderToken(ais, tree, main_tokens[node], space),
+
+ .multiline_string_literal => {
+ var locked_indents = ais.lockOneShotIndent();
+ try ais.maybeInsertNewline();
- const src_has_trailing_comma = blk: {
- const maybe_comma = tree.nextToken(field.lastToken());
- break :blk tree.token_ids[maybe_comma] == .Comma;
- };
+ var i = datas[node].lhs;
+ while (i <= datas[node].rhs) : (i += 1) try renderToken(ais, tree, i, .newline);
- // The trailing comma is emitted at the end, but if it's not present
- // we still have to respect the specified `space` parameter
- const last_token_space: Space = if (src_has_trailing_comma) .None else space;
-
- if (field.type_expr == null and field.value_expr == null) {
- try renderToken(tree, ais, field.name_token, last_token_space); // name
- } else if (field.type_expr != null and field.value_expr == null) {
- try renderToken(tree, ais, field.name_token, .None); // name
- try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // :
-
- if (field.align_expr) |align_value_expr| {
- try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
- const lparen_token = tree.prevToken(align_value_expr.firstToken());
- const align_kw = tree.prevToken(lparen_token);
- const rparen_token = tree.nextToken(align_value_expr.lastToken());
- try renderToken(tree, ais, align_kw, .None); // align
- try renderToken(tree, ais, lparen_token, .None); // (
- try renderExpression(allocator, ais, tree, align_value_expr, .None); // alignment
- try renderToken(tree, ais, rparen_token, last_token_space); // )
- } else {
- try renderExpression(allocator, ais, tree, field.type_expr.?, last_token_space); // type
- }
- } else if (field.type_expr == null and field.value_expr != null) {
- try renderToken(tree, ais, field.name_token, .Space); // name
- try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // =
- try renderExpression(allocator, ais, tree, field.value_expr.?, last_token_space); // value
- } else {
- try renderToken(tree, ais, field.name_token, .None); // name
- try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // :
-
- if (field.align_expr) |align_value_expr| {
- try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
- const lparen_token = tree.prevToken(align_value_expr.firstToken());
- const align_kw = tree.prevToken(lparen_token);
- const rparen_token = tree.nextToken(align_value_expr.lastToken());
- try renderToken(tree, ais, align_kw, .None); // align
- try renderToken(tree, ais, lparen_token, .None); // (
- try renderExpression(allocator, ais, tree, align_value_expr, .None); // alignment
- try renderToken(tree, ais, rparen_token, .Space); // )
- } else {
- try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
- }
- try renderToken(tree, ais, tree.prevToken(field.value_expr.?.firstToken()), .Space); // =
- try renderExpression(allocator, ais, tree, field.value_expr.?, last_token_space); // value
- }
+ while (locked_indents > 0) : (locked_indents -= 1) ais.popIndent();
- if (src_has_trailing_comma) {
- const comma = tree.nextToken(field.lastToken());
- try renderToken(tree, ais, comma, space);
+ switch (space) {
+ .none, .space, .newline, .skip => {},
+ .semicolon => if (token_tags[i] == .semicolon) try renderToken(ais, tree, i, .newline),
+ .comma => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .newline),
+ .comma_space => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .space),
}
},
- .Comptime => {
- assert(!decl.requireSemiColon());
- try renderExpression(allocator, ais, tree, decl, space);
+ .error_value => {
+ try renderToken(ais, tree, main_tokens[node], .none);
+ try renderToken(ais, tree, main_tokens[node] + 1, .none);
+ return renderToken(ais, tree, main_tokens[node] + 2, space);
},
- .DocComment => {
- const comment = @fieldParentPtr(ast.Node.DocComment, "base", decl);
- const kind = tree.token_ids[comment.first_line];
- try renderToken(tree, ais, comment.first_line, .Newline);
- var tok_i = comment.first_line + 1;
- while (true) : (tok_i += 1) {
- const tok_id = tree.token_ids[tok_i];
- if (tok_id == kind) {
- try renderToken(tree, ais, tok_i, .Newline);
- } else if (tok_id == .LineComment) {
- continue;
- } else {
- break;
- }
- }
- },
- else => unreachable,
- }
-}
+ .@"anytype" => return renderToken(ais, tree, main_tokens[node], space),
-fn renderExpression(
- allocator: *mem.Allocator,
- ais: anytype,
- tree: *ast.Tree,
- base: *ast.Node,
- space: Space,
-) (@TypeOf(ais.*).Error || Error)!void {
- switch (base.tag) {
- .Identifier,
- .IntegerLiteral,
- .FloatLiteral,
- .StringLiteral,
- .CharLiteral,
- .BoolLiteral,
- .NullLiteral,
- .Unreachable,
- .ErrorType,
- .UndefinedLiteral,
+ .block_two,
+ .block_two_semicolon,
=> {
- const casted_node = base.cast(ast.Node.OneToken).?;
- return renderToken(tree, ais, casted_node.token, space);
- },
-
- .AnyType => {
- const any_type = base.castTag(.AnyType).?;
- if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) {
- // TODO remove in next release cycle
- try ais.writer().writeAll("anytype");
- if (space == .Comma) try ais.writer().writeAll(",\n");
- return;
- }
- return renderToken(tree, ais, any_type.token, space);
- },
-
- .Block, .LabeledBlock => {
- const block: struct {
- label: ?ast.TokenIndex,
- statements: []*ast.Node,
- lbrace: ast.TokenIndex,
- rbrace: ast.TokenIndex,
- } = b: {
- if (base.castTag(.Block)) |block| {
- break :b .{
- .label = null,
- .statements = block.statements(),
- .lbrace = block.lbrace,
- .rbrace = block.rbrace,
- };
- } else if (base.castTag(.LabeledBlock)) |block| {
- break :b .{
- .label = block.label,
- .statements = block.statements(),
- .lbrace = block.lbrace,
- .rbrace = block.rbrace,
- };
- } else {
- unreachable;
- }
- };
-
- if (block.label) |label| {
- try renderToken(tree, ais, label, Space.None);
- try renderToken(tree, ais, tree.nextToken(label), Space.Space);
- }
-
- if (block.statements.len == 0) {
- ais.pushIndentNextLine();
- defer ais.popIndent();
- try renderToken(tree, ais, block.lbrace, Space.None);
+ const statements = [2]ast.Node.Index{ datas[node].lhs, datas[node].rhs };
+ if (datas[node].lhs == 0) {
+ return renderBlock(gpa, ais, tree, node, statements[0..0], space);
+ } else if (datas[node].rhs == 0) {
+ return renderBlock(gpa, ais, tree, node, statements[0..1], space);
} else {
- ais.pushIndentNextLine();
- defer ais.popIndent();
-
- try renderToken(tree, ais, block.lbrace, Space.Newline);
-
- for (block.statements) |statement, i| {
- try renderStatement(allocator, ais, tree, statement);
-
- if (i + 1 < block.statements.len) {
- try renderExtraNewline(tree, ais, block.statements[i + 1]);
- }
- }
+ return renderBlock(gpa, ais, tree, node, statements[0..2], space);
}
- return renderToken(tree, ais, block.rbrace, space);
+ },
+ .block,
+ .block_semicolon,
+ => {
+ const statements = tree.extra_data[datas[node].lhs..datas[node].rhs];
+ return renderBlock(gpa, ais, tree, node, statements, space);
},
- .Defer => {
- const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
+ .@"errdefer" => {
+ const defer_token = main_tokens[node];
+ const payload_token = datas[node].lhs;
+ const expr = datas[node].rhs;
- try renderToken(tree, ais, defer_node.defer_token, Space.Space);
- if (defer_node.payload) |payload| {
- try renderExpression(allocator, ais, tree, payload, Space.Space);
+ try renderToken(ais, tree, defer_token, .space);
+ if (payload_token != 0) {
+ try renderToken(ais, tree, payload_token - 1, .none); // |
+ try renderToken(ais, tree, payload_token, .none); // identifier
+ try renderToken(ais, tree, payload_token + 1, .space); // |
}
- return renderExpression(allocator, ais, tree, defer_node.expr, space);
+ return renderExpression(gpa, ais, tree, expr, space);
},
- .Comptime => {
- const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
- try renderToken(tree, ais, comptime_node.comptime_token, Space.Space);
- return renderExpression(allocator, ais, tree, comptime_node.expr, space);
+ .@"defer" => {
+ const defer_token = main_tokens[node];
+ const expr = datas[node].rhs;
+ try renderToken(ais, tree, defer_token, .space);
+ return renderExpression(gpa, ais, tree, expr, space);
},
- .Nosuspend => {
- const nosuspend_node = @fieldParentPtr(ast.Node.Nosuspend, "base", base);
- if (mem.eql(u8, tree.tokenSlice(nosuspend_node.nosuspend_token), "noasync")) {
- // TODO: remove this
- try ais.writer().writeAll("nosuspend ");
+ .@"comptime", .@"nosuspend" => {
+ const comptime_token = main_tokens[node];
+ const block = datas[node].lhs;
+ try renderToken(ais, tree, comptime_token, .space);
+ return renderExpression(gpa, ais, tree, block, space);
+ },
+
+ .@"suspend" => {
+ const suspend_token = main_tokens[node];
+ const body = datas[node].lhs;
+ if (body != 0) {
+ try renderToken(ais, tree, suspend_token, .space);
+ return renderExpression(gpa, ais, tree, body, space);
} else {
- try renderToken(tree, ais, nosuspend_node.nosuspend_token, Space.Space);
+ return renderToken(ais, tree, suspend_token, space);
}
- return renderExpression(allocator, ais, tree, nosuspend_node.expr, space);
},
- .Suspend => {
- const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
+ .@"catch" => {
+ const main_token = main_tokens[node];
+ const fallback_first = tree.firstToken(datas[node].rhs);
- if (suspend_node.body) |body| {
- try renderToken(tree, ais, suspend_node.suspend_token, Space.Space);
- return renderExpression(allocator, ais, tree, body, space);
+ const same_line = tree.tokensOnSameLine(main_token, fallback_first);
+ const after_op_space = if (same_line) Space.space else Space.newline;
+
+ try renderExpression(gpa, ais, tree, datas[node].lhs, .space); // target
+
+ if (token_tags[fallback_first - 1] == .pipe) {
+ try renderToken(ais, tree, main_token, .space); // catch keyword
+ try renderToken(ais, tree, main_token + 1, .none); // pipe
+ try renderToken(ais, tree, main_token + 2, .none); // payload identifier
+ try renderToken(ais, tree, main_token + 3, after_op_space); // pipe
} else {
- return renderToken(tree, ais, suspend_node.suspend_token, space);
+ assert(token_tags[fallback_first - 1] == .keyword_catch);
+ try renderToken(ais, tree, main_token, after_op_space); // catch keyword
}
+
+ ais.pushIndentOneShot();
+ try renderExpression(gpa, ais, tree, datas[node].rhs, space); // fallback
},
- .Catch => {
- const infix_op_node = @fieldParentPtr(ast.Node.Catch, "base", base);
+ .field_access => {
+ const main_token = main_tokens[node];
+ const field_access = datas[node];
- const op_space = Space.Space;
- try renderExpression(allocator, ais, tree, infix_op_node.lhs, op_space);
+ try renderExpression(gpa, ais, tree, field_access.lhs, .none);
- const after_op_space = blk: {
- const same_line = tree.tokensOnSameLine(infix_op_node.op_token, tree.nextToken(infix_op_node.op_token));
- break :blk if (same_line) op_space else Space.Newline;
- };
+ // Allow a line break between the lhs and the dot if the lhs and rhs
+ // are on different lines.
+ const lhs_last_token = tree.lastToken(field_access.lhs);
+ const same_line = tree.tokensOnSameLine(lhs_last_token, main_token + 1);
+ if (!same_line) {
+ if (!hasComment(tree, lhs_last_token, main_token)) try ais.insertNewline();
+ ais.pushIndentOneShot();
+ }
- try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
+ try renderToken(ais, tree, main_token, .none);
- if (infix_op_node.payload) |payload| {
- try renderExpression(allocator, ais, tree, payload, Space.Space);
+ // This check ensures that zag() is indented in the following example:
+ // const x = foo
+ // .bar()
+ // . // comment
+ // zag();
+ if (!same_line and hasComment(tree, main_token, main_token + 1)) {
+ ais.pushIndentOneShot();
}
- ais.pushIndentOneShot();
- return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
+ return renderToken(ais, tree, field_access.rhs, space);
},
- .Add,
- .AddWrap,
- .ArrayCat,
- .ArrayMult,
- .Assign,
- .AssignBitAnd,
- .AssignBitOr,
- .AssignBitShiftLeft,
- .AssignBitShiftRight,
- .AssignBitXor,
- .AssignDiv,
- .AssignSub,
- .AssignSubWrap,
- .AssignMod,
- .AssignAdd,
- .AssignAddWrap,
- .AssignMul,
- .AssignMulWrap,
- .BangEqual,
- .BitAnd,
- .BitOr,
- .BitShiftLeft,
- .BitShiftRight,
- .BitXor,
- .BoolAnd,
- .BoolOr,
- .Div,
- .EqualEqual,
- .ErrorUnion,
- .GreaterOrEqual,
- .GreaterThan,
- .LessOrEqual,
- .LessThan,
- .MergeErrorSets,
- .Mod,
- .Mul,
- .MulWrap,
- .Period,
- .Range,
- .Sub,
- .SubWrap,
- .OrElse,
+ .error_union,
+ .switch_range,
=> {
- const infix_op_node = @fieldParentPtr(ast.Node.SimpleInfixOp, "base", base);
-
- const op_space = switch (base.tag) {
- .Period, .ErrorUnion, .Range => Space.None,
- else => Space.Space,
- };
- try renderExpression(allocator, ais, tree, infix_op_node.lhs, op_space);
-
- const after_op_space = blk: {
- const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));
- break :blk if (loc.line == 0) op_space else Space.Newline;
- };
+ const infix = datas[node];
+ try renderExpression(gpa, ais, tree, infix.lhs, .none);
+ try renderToken(ais, tree, main_tokens[node], .none);
+ return renderExpression(gpa, ais, tree, infix.rhs, space);
+ },
- {
+ .add,
+ .add_wrap,
+ .array_cat,
+ .array_mult,
+ .assign,
+ .assign_bit_and,
+ .assign_bit_or,
+ .assign_bit_shift_left,
+ .assign_bit_shift_right,
+ .assign_bit_xor,
+ .assign_div,
+ .assign_sub,
+ .assign_sub_wrap,
+ .assign_mod,
+ .assign_add,
+ .assign_add_wrap,
+ .assign_mul,
+ .assign_mul_wrap,
+ .bang_equal,
+ .bit_and,
+ .bit_or,
+ .bit_shift_left,
+ .bit_shift_right,
+ .bit_xor,
+ .bool_and,
+ .bool_or,
+ .div,
+ .equal_equal,
+ .greater_or_equal,
+ .greater_than,
+ .less_or_equal,
+ .less_than,
+ .merge_error_sets,
+ .mod,
+ .mul,
+ .mul_wrap,
+ .sub,
+ .sub_wrap,
+ .@"orelse",
+ => {
+ const infix = datas[node];
+ try renderExpression(gpa, ais, tree, infix.lhs, .space);
+ const op_token = main_tokens[node];
+ if (tree.tokensOnSameLine(op_token, op_token + 1)) {
+ try renderToken(ais, tree, op_token, .space);
+ } else {
ais.pushIndent();
- defer ais.popIndent();
- try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
+ try renderToken(ais, tree, op_token, .newline);
+ ais.popIndent();
}
ais.pushIndentOneShot();
- return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
+ return renderExpression(gpa, ais, tree, infix.rhs, space);
},
- .BitNot,
- .BoolNot,
- .Negation,
- .NegationWrap,
- .OptionalType,
- .AddressOf,
+ .bit_not,
+ .bool_not,
+ .negation,
+ .negation_wrap,
+ .optional_type,
+ .address_of,
=> {
- const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
- try renderToken(tree, ais, casted_node.op_token, Space.None);
- return renderExpression(allocator, ais, tree, casted_node.rhs, space);
+ try renderToken(ais, tree, main_tokens[node], .none);
+ return renderExpression(gpa, ais, tree, datas[node].lhs, space);
},
- .Try,
- .Resume,
- .Await,
+ .@"try",
+ .@"resume",
+ .@"await",
=> {
- const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
- try renderToken(tree, ais, casted_node.op_token, Space.Space);
- return renderExpression(allocator, ais, tree, casted_node.rhs, space);
+ try renderToken(ais, tree, main_tokens[node], .space);
+ return renderExpression(gpa, ais, tree, datas[node].lhs, space);
},
- .ArrayType => {
- const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);
- return renderArrayType(
- allocator,
- ais,
- tree,
- array_type.op_token,
- array_type.rhs,
- array_type.len_expr,
- null,
- space,
- );
+ .array_type => return renderArrayType(gpa, ais, tree, tree.arrayType(node), space),
+ .array_type_sentinel => return renderArrayType(gpa, ais, tree, tree.arrayTypeSentinel(node), space),
+
+ .ptr_type_aligned => return renderPtrType(gpa, ais, tree, tree.ptrTypeAligned(node), space),
+ .ptr_type_sentinel => return renderPtrType(gpa, ais, tree, tree.ptrTypeSentinel(node), space),
+ .ptr_type => return renderPtrType(gpa, ais, tree, tree.ptrType(node), space),
+ .ptr_type_bit_range => return renderPtrType(gpa, ais, tree, tree.ptrTypeBitRange(node), space),
+
+ .array_init_one, .array_init_one_comma => {
+ var elements: [1]ast.Node.Index = undefined;
+ return renderArrayInit(gpa, ais, tree, tree.arrayInitOne(&elements, node), space);
},
- .ArrayTypeSentinel => {
- const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base);
- return renderArrayType(
- allocator,
- ais,
- tree,
- array_type.op_token,
- array_type.rhs,
- array_type.len_expr,
- array_type.sentinel,
- space,
- );
+ .array_init_dot_two, .array_init_dot_two_comma => {
+ var elements: [2]ast.Node.Index = undefined;
+ return renderArrayInit(gpa, ais, tree, tree.arrayInitDotTwo(&elements, node), space);
+ },
+ .array_init_dot,
+ .array_init_dot_comma,
+ => return renderArrayInit(gpa, ais, tree, tree.arrayInitDot(node), space),
+ .array_init,
+ .array_init_comma,
+ => return renderArrayInit(gpa, ais, tree, tree.arrayInit(node), space),
+
+ .struct_init_one, .struct_init_one_comma => {
+ var fields: [1]ast.Node.Index = undefined;
+ return renderStructInit(gpa, ais, tree, node, tree.structInitOne(&fields, node), space);
+ },
+ .struct_init_dot_two, .struct_init_dot_two_comma => {
+ var fields: [2]ast.Node.Index = undefined;
+ return renderStructInit(gpa, ais, tree, node, tree.structInitDotTwo(&fields, node), space);
+ },
+ .struct_init_dot,
+ .struct_init_dot_comma,
+ => return renderStructInit(gpa, ais, tree, node, tree.structInitDot(node), space),
+ .struct_init,
+ .struct_init_comma,
+ => return renderStructInit(gpa, ais, tree, node, tree.structInit(node), space),
+
+ .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
+ var params: [1]ast.Node.Index = undefined;
+ return renderCall(gpa, ais, tree, tree.callOne(&params, node), space);
},
- .PtrType => {
- const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);
- const op_tok_id = tree.token_ids[ptr_type.op_token];
- switch (op_tok_id) {
- .Asterisk, .AsteriskAsterisk => try ais.writer().writeByte('*'),
- .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)
- try ais.writer().writeAll("[*c")
- else
- try ais.writer().writeAll("[*"),
- else => unreachable,
- }
- if (ptr_type.ptr_info.sentinel) |sentinel| {
- const colon_token = tree.prevToken(sentinel.firstToken());
- try renderToken(tree, ais, colon_token, Space.None); // :
- const sentinel_space = switch (op_tok_id) {
- .LBracket => Space.None,
- else => Space.Space,
- };
- try renderExpression(allocator, ais, tree, sentinel, sentinel_space);
- }
- switch (op_tok_id) {
- .Asterisk, .AsteriskAsterisk => {},
- .LBracket => try ais.writer().writeByte(']'),
- else => unreachable,
- }
- if (ptr_type.ptr_info.allowzero_token) |allowzero_token| {
- try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero
- }
- if (ptr_type.ptr_info.align_info) |align_info| {
- const lparen_token = tree.prevToken(align_info.node.firstToken());
- const align_token = tree.prevToken(lparen_token);
-
- try renderToken(tree, ais, align_token, Space.None); // align
- try renderToken(tree, ais, lparen_token, Space.None); // (
+ .call,
+ .call_comma,
+ .async_call,
+ .async_call_comma,
+ => return renderCall(gpa, ais, tree, tree.callFull(node), space),
+
+ .array_access => {
+ const suffix = datas[node];
+ const lbracket = tree.firstToken(suffix.rhs) - 1;
+ const rbracket = tree.lastToken(suffix.rhs) + 1;
+ const one_line = tree.tokensOnSameLine(lbracket, rbracket);
+ const inner_space = if (one_line) Space.none else Space.newline;
+ try renderExpression(gpa, ais, tree, suffix.lhs, .none);
+ ais.pushIndentNextLine();
+ try renderToken(ais, tree, lbracket, inner_space); // [
+ try renderExpression(gpa, ais, tree, suffix.rhs, inner_space);
+ ais.popIndent();
+ return renderToken(ais, tree, rbracket, space); // ]
+ },
- try renderExpression(allocator, ais, tree, align_info.node, Space.None);
+ .slice_open => return renderSlice(gpa, ais, tree, tree.sliceOpen(node), space),
+ .slice => return renderSlice(gpa, ais, tree, tree.slice(node), space),
+ .slice_sentinel => return renderSlice(gpa, ais, tree, tree.sliceSentinel(node), space),
- if (align_info.bit_range) |bit_range| {
- const colon1 = tree.prevToken(bit_range.start.firstToken());
- const colon2 = tree.prevToken(bit_range.end.firstToken());
+ .deref => {
+ try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
+ return renderToken(ais, tree, main_tokens[node], space);
+ },
- try renderToken(tree, ais, colon1, Space.None); // :
- try renderExpression(allocator, ais, tree, bit_range.start, Space.None);
- try renderToken(tree, ais, colon2, Space.None); // :
- try renderExpression(allocator, ais, tree, bit_range.end, Space.None);
+ .unwrap_optional => {
+ try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
+ try renderToken(ais, tree, main_tokens[node], .none);
+ return renderToken(ais, tree, datas[node].rhs, space);
+ },
- const rparen_token = tree.nextToken(bit_range.end.lastToken());
- try renderToken(tree, ais, rparen_token, Space.Space); // )
- } else {
- const rparen_token = tree.nextToken(align_info.node.lastToken());
- try renderToken(tree, ais, rparen_token, Space.Space); // )
- }
+ .@"break" => {
+ const main_token = main_tokens[node];
+ const label_token = datas[node].lhs;
+ const target = datas[node].rhs;
+ if (label_token == 0 and target == 0) {
+ try renderToken(ais, tree, main_token, space); // break keyword
+ } else if (label_token == 0 and target != 0) {
+ try renderToken(ais, tree, main_token, .space); // break keyword
+ try renderExpression(gpa, ais, tree, target, space);
+ } else if (label_token != 0 and target == 0) {
+ try renderToken(ais, tree, main_token, .space); // break keyword
+ try renderToken(ais, tree, label_token - 1, .none); // colon
+ try renderToken(ais, tree, label_token, space); // identifier
+ } else if (label_token != 0 and target != 0) {
+ try renderToken(ais, tree, main_token, .space); // break keyword
+ try renderToken(ais, tree, label_token - 1, .none); // colon
+ try renderToken(ais, tree, label_token, .space); // identifier
+ try renderExpression(gpa, ais, tree, target, space);
}
- if (ptr_type.ptr_info.const_token) |const_token| {
- try renderToken(tree, ais, const_token, Space.Space); // const
- }
- if (ptr_type.ptr_info.volatile_token) |volatile_token| {
- try renderToken(tree, ais, volatile_token, Space.Space); // volatile
- }
- return renderExpression(allocator, ais, tree, ptr_type.rhs, space);
},
- .SliceType => {
- const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base);
- try renderToken(tree, ais, slice_type.op_token, Space.None); // [
- if (slice_type.ptr_info.sentinel) |sentinel| {
- const colon_token = tree.prevToken(sentinel.firstToken());
- try renderToken(tree, ais, colon_token, Space.None); // :
- try renderExpression(allocator, ais, tree, sentinel, Space.None);
- try renderToken(tree, ais, tree.nextToken(sentinel.lastToken()), Space.None); // ]
+ .@"continue" => {
+ const main_token = main_tokens[node];
+ const label = datas[node].lhs;
+ if (label != 0) {
+ try renderToken(ais, tree, main_token, .space); // continue
+ try renderToken(ais, tree, label - 1, .none); // :
+ return renderToken(ais, tree, label, space); // label
} else {
- try renderToken(tree, ais, tree.nextToken(slice_type.op_token), Space.None); // ]
- }
-
- if (slice_type.ptr_info.allowzero_token) |allowzero_token| {
- try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero
- }
- if (slice_type.ptr_info.align_info) |align_info| {
- const lparen_token = tree.prevToken(align_info.node.firstToken());
- const align_token = tree.prevToken(lparen_token);
-
- try renderToken(tree, ais, align_token, Space.None); // align
- try renderToken(tree, ais, lparen_token, Space.None); // (
-
- try renderExpression(allocator, ais, tree, align_info.node, Space.None);
-
- if (align_info.bit_range) |bit_range| {
- const colon1 = tree.prevToken(bit_range.start.firstToken());
- const colon2 = tree.prevToken(bit_range.end.firstToken());
-
- try renderToken(tree, ais, colon1, Space.None); // :
- try renderExpression(allocator, ais, tree, bit_range.start, Space.None);
- try renderToken(tree, ais, colon2, Space.None); // :
- try renderExpression(allocator, ais, tree, bit_range.end, Space.None);
-
- const rparen_token = tree.nextToken(bit_range.end.lastToken());
- try renderToken(tree, ais, rparen_token, Space.Space); // )
- } else {
- const rparen_token = tree.nextToken(align_info.node.lastToken());
- try renderToken(tree, ais, rparen_token, Space.Space); // )
- }
- }
- if (slice_type.ptr_info.const_token) |const_token| {
- try renderToken(tree, ais, const_token, Space.Space);
- }
- if (slice_type.ptr_info.volatile_token) |volatile_token| {
- try renderToken(tree, ais, volatile_token, Space.Space);
+ return renderToken(ais, tree, main_token, space); // continue
}
- return renderExpression(allocator, ais, tree, slice_type.rhs, space);
},
- .ArrayInitializer, .ArrayInitializerDot => {
- var rtoken: ast.TokenIndex = undefined;
- var exprs: []*ast.Node = undefined;
- const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) {
- .ArrayInitializerDot => blk: {
- const casted = @fieldParentPtr(ast.Node.ArrayInitializerDot, "base", base);
- rtoken = casted.rtoken;
- exprs = casted.list();
- break :blk .{ .dot = casted.dot };
- },
- .ArrayInitializer => blk: {
- const casted = @fieldParentPtr(ast.Node.ArrayInitializer, "base", base);
- rtoken = casted.rtoken;
- exprs = casted.list();
- break :blk .{ .node = casted.lhs };
- },
- else => unreachable,
- };
-
- const lbrace = switch (lhs) {
- .dot => |dot| tree.nextToken(dot),
- .node => |node| tree.nextToken(node.lastToken()),
- };
-
- switch (lhs) {
- .dot => |dot| try renderToken(tree, ais, dot, Space.None),
- .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
- }
-
- if (exprs.len == 0) {
- try renderToken(tree, ais, lbrace, Space.None);
- return renderToken(tree, ais, rtoken, space);
- }
-
- if (exprs.len == 1 and exprs[0].tag != .MultilineStringLiteral and tree.token_ids[exprs[0].*.lastToken() + 1] == .RBrace) {
- const expr = exprs[0];
-
- try renderToken(tree, ais, lbrace, Space.None);
- try renderExpression(allocator, ais, tree, expr, Space.None);
- return renderToken(tree, ais, rtoken, space);
- }
-
- // scan to find row size
- if (rowSize(tree, exprs, rtoken) != null) {
- {
- ais.pushIndentNextLine();
- defer ais.popIndent();
- try renderToken(tree, ais, lbrace, Space.Newline);
-
- var expr_index: usize = 0;
- while (rowSize(tree, exprs[expr_index..], rtoken)) |row_size| {
- const row_exprs = exprs[expr_index..];
- // A place to store the width of each expression and its column's maximum
- var widths = try allocator.alloc(usize, row_exprs.len + row_size);
- defer allocator.free(widths);
- mem.set(usize, widths, 0);
-
- var expr_newlines = try allocator.alloc(bool, row_exprs.len);
- defer allocator.free(expr_newlines);
- mem.set(bool, expr_newlines, false);
-
- var expr_widths = widths[0 .. widths.len - row_size];
- var column_widths = widths[widths.len - row_size ..];
-
- // Find next row with trailing comment (if any) to end the current section
- var section_end = sec_end: {
- var this_line_first_expr: usize = 0;
- var this_line_size = rowSize(tree, row_exprs, rtoken);
- for (row_exprs) |expr, i| {
- // Ignore comment on first line of this section
- if (i == 0 or tree.tokensOnSameLine(row_exprs[0].firstToken(), expr.lastToken())) continue;
- // Track start of line containing comment
- if (!tree.tokensOnSameLine(row_exprs[this_line_first_expr].firstToken(), expr.lastToken())) {
- this_line_first_expr = i;
- this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rtoken);
- }
-
- const maybe_comma = expr.lastToken() + 1;
- const maybe_comment = expr.lastToken() + 2;
- if (maybe_comment < tree.token_ids.len) {
- if (tree.token_ids[maybe_comma] == .Comma and
- tree.token_ids[maybe_comment] == .LineComment and
- tree.tokensOnSameLine(expr.lastToken(), maybe_comment))
- {
- var comment_token_loc = tree.token_locs[maybe_comment];
- const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(comment_token_loc), " ").len == 2;
- if (!comment_is_empty) {
- // Found row ending in comment
- break :sec_end i - this_line_size.? + 1;
- }
- }
- }
- }
- break :sec_end row_exprs.len;
- };
- expr_index += section_end;
-
- const section_exprs = row_exprs[0..section_end];
-
- // Null stream for counting the printed length of each expression
- var line_find_stream = std.io.findByteOutStream('\n', std.io.null_out_stream);
- var counting_stream = std.io.countingOutStream(line_find_stream.writer());
- var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer());
-
- // Calculate size of columns in current section
- var column_counter: usize = 0;
- var single_line = true;
- for (section_exprs) |expr, i| {
- if (i + 1 < section_exprs.len) {
- counting_stream.bytes_written = 0;
- line_find_stream.byte_found = false;
- try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
- const width = @intCast(usize, counting_stream.bytes_written);
- expr_widths[i] = width;
- expr_newlines[i] = line_find_stream.byte_found;
-
- if (!line_find_stream.byte_found) {
- const column = column_counter % row_size;
- column_widths[column] = std.math.max(column_widths[column], width);
-
- const expr_last_token = expr.*.lastToken() + 1;
- const next_expr = section_exprs[i + 1];
- const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, next_expr.*.firstToken());
-
- column_counter += 1;
-
- if (loc.line != 0) single_line = false;
- } else {
- single_line = false;
- column_counter = 0;
- }
- } else {
- counting_stream.bytes_written = 0;
- try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
- const width = @intCast(usize, counting_stream.bytes_written);
- expr_widths[i] = width;
- expr_newlines[i] = line_find_stream.byte_found;
-
- if (!line_find_stream.byte_found) {
- const column = column_counter % row_size;
- column_widths[column] = std.math.max(column_widths[column], width);
- }
- break;
- }
- }
-
- // Render exprs in current section
- column_counter = 0;
- var last_col_index: usize = row_size - 1;
- for (section_exprs) |expr, i| {
- if (i + 1 < section_exprs.len) {
- const next_expr = section_exprs[i + 1];
- try renderExpression(allocator, ais, tree, expr, Space.None);
-
- const comma = tree.nextToken(expr.*.lastToken());
-
- if (column_counter != last_col_index) {
- if (!expr_newlines[i] and !expr_newlines[i + 1]) {
- // Neither the current or next expression is multiline
- try renderToken(tree, ais, comma, Space.Space); // ,
- assert(column_widths[column_counter % row_size] >= expr_widths[i]);
- const padding = column_widths[column_counter % row_size] - expr_widths[i];
- try ais.writer().writeByteNTimes(' ', padding);
-
- column_counter += 1;
- continue;
- }
- }
- if (single_line and row_size != 1) {
- try renderToken(tree, ais, comma, Space.Space); // ,
- continue;
- }
-
- column_counter = 0;
- try renderToken(tree, ais, comma, Space.Newline); // ,
- try renderExtraNewline(tree, ais, next_expr);
- } else {
- const maybe_comma = tree.nextToken(expr.*.lastToken());
- if (tree.token_ids[maybe_comma] == .Comma) {
- try renderExpression(allocator, ais, tree, expr, Space.None); // ,
- try renderToken(tree, ais, maybe_comma, Space.Newline); // ,
- } else {
- try renderExpression(allocator, ais, tree, expr, Space.Comma); // ,
- }
- }
- }
-
- if (expr_index == exprs.len) {
- break;
- }
- }
- }
-
- return renderToken(tree, ais, rtoken, space);
- }
-
- // Single line
- try renderToken(tree, ais, lbrace, Space.Space);
- for (exprs) |expr, i| {
- if (i + 1 < exprs.len) {
- const next_expr = exprs[i + 1];
- try renderExpression(allocator, ais, tree, expr, Space.None);
- const comma = tree.nextToken(expr.*.lastToken());
- try renderToken(tree, ais, comma, Space.Space); // ,
- } else {
- try renderExpression(allocator, ais, tree, expr, Space.Space);
- }
+ .@"return" => {
+ if (datas[node].lhs != 0) {
+ try renderToken(ais, tree, main_tokens[node], .space);
+ try renderExpression(gpa, ais, tree, datas[node].lhs, space);
+ } else {
+ try renderToken(ais, tree, main_tokens[node], space);
}
-
- return renderToken(tree, ais, rtoken, space);
},
- .StructInitializer, .StructInitializerDot => {
- var rtoken: ast.TokenIndex = undefined;
- var field_inits: []*ast.Node = undefined;
- const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) {
- .StructInitializerDot => blk: {
- const casted = @fieldParentPtr(ast.Node.StructInitializerDot, "base", base);
- rtoken = casted.rtoken;
- field_inits = casted.list();
- break :blk .{ .dot = casted.dot };
- },
- .StructInitializer => blk: {
- const casted = @fieldParentPtr(ast.Node.StructInitializer, "base", base);
- rtoken = casted.rtoken;
- field_inits = casted.list();
- break :blk .{ .node = casted.lhs };
- },
- else => unreachable,
- };
-
- const lbrace = switch (lhs) {
- .dot => |dot| tree.nextToken(dot),
- .node => |node| tree.nextToken(node.lastToken()),
- };
-
- if (field_inits.len == 0) {
- switch (lhs) {
- .dot => |dot| try renderToken(tree, ais, dot, Space.None),
- .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
- }
-
- {
- ais.pushIndentNextLine();
- defer ais.popIndent();
- try renderToken(tree, ais, lbrace, Space.None);
- }
-
- return renderToken(tree, ais, rtoken, space);
- }
-
- const src_has_trailing_comma = blk: {
- const maybe_comma = tree.prevToken(rtoken);
- break :blk tree.token_ids[maybe_comma] == .Comma;
- };
-
- const src_same_line = blk: {
- const loc = tree.tokenLocation(tree.token_locs[lbrace].end, rtoken);
- break :blk loc.line == 0;
- };
-
- const expr_outputs_one_line = blk: {
- // render field expressions until a LF is found
- for (field_inits) |field_init| {
- var find_stream = std.io.findByteOutStream('\n', std.io.null_out_stream);
- var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, find_stream.writer());
-
- try renderExpression(allocator, &auto_indenting_stream, tree, field_init, Space.None);
- if (find_stream.byte_found) break :blk false;
- }
- break :blk true;
- };
-
- if (field_inits.len == 1) blk: {
- const field_init = field_inits[0].cast(ast.Node.FieldInitializer).?;
-
- switch (field_init.expr.tag) {
- .StructInitializer,
- .StructInitializerDot,
- => break :blk,
- else => {},
- }
-
- // if the expression outputs to multiline, make this struct multiline
- if (!expr_outputs_one_line or src_has_trailing_comma) {
- break :blk;
- }
-
- switch (lhs) {
- .dot => |dot| try renderToken(tree, ais, dot, Space.None),
- .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
- }
- try renderToken(tree, ais, lbrace, Space.Space);
- try renderExpression(allocator, ais, tree, &field_init.base, Space.Space);
- return renderToken(tree, ais, rtoken, space);
- }
-
- if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) {
- // render all on one line, no trailing comma
- switch (lhs) {
- .dot => |dot| try renderToken(tree, ais, dot, Space.None),
- .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
- }
- try renderToken(tree, ais, lbrace, Space.Space);
+ .grouped_expression => {
+ try renderToken(ais, tree, main_tokens[node], .none); // lparen
+ ais.pushIndentOneShot();
+ try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
+ return renderToken(ais, tree, datas[node].rhs, space); // rparen
+ },
- for (field_inits) |field_init, i| {
- if (i + 1 < field_inits.len) {
- try renderExpression(allocator, ais, tree, field_init, Space.None);
+ .container_decl,
+ .container_decl_trailing,
+ => return renderContainerDecl(gpa, ais, tree, node, tree.containerDecl(node), space),
- const comma = tree.nextToken(field_init.lastToken());
- try renderToken(tree, ais, comma, Space.Space);
- } else {
- try renderExpression(allocator, ais, tree, field_init, Space.Space);
- }
- }
+ .container_decl_two, .container_decl_two_trailing => {
+ var buffer: [2]ast.Node.Index = undefined;
+ return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclTwo(&buffer, node), space);
+ },
+ .container_decl_arg,
+ .container_decl_arg_trailing,
+ => return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclArg(node), space),
- return renderToken(tree, ais, rtoken, space);
- }
-
- {
- switch (lhs) {
- .dot => |dot| try renderToken(tree, ais, dot, Space.None),
- .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
- }
+ .tagged_union,
+ .tagged_union_trailing,
+ => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnion(node), space),
+ .tagged_union_two, .tagged_union_two_trailing => {
+ var buffer: [2]ast.Node.Index = undefined;
+ return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionTwo(&buffer, node), space);
+ },
+ .tagged_union_enum_tag,
+ .tagged_union_enum_tag_trailing,
+ => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionEnumTag(node), space),
+
+ .error_set_decl => {
+ const error_token = main_tokens[node];
+ const lbrace = error_token + 1;
+ const rbrace = datas[node].rhs;
+
+ try renderToken(ais, tree, error_token, .none);
+
+ if (lbrace + 1 == rbrace) {
+ // There is nothing between the braces so render condensed: `error{}`
+ try renderToken(ais, tree, lbrace, .none);
+ return renderToken(ais, tree, rbrace, space);
+ } else if (lbrace + 2 == rbrace and token_tags[lbrace + 1] == .identifier) {
+ // There is exactly one member and no trailing comma or
+ // comments, so render without surrounding spaces: `error{Foo}`
+ try renderToken(ais, tree, lbrace, .none);
+ try renderToken(ais, tree, lbrace + 1, .none); // identifier
+ return renderToken(ais, tree, rbrace, space);
+ } else if (token_tags[rbrace - 1] == .comma) {
+ // There is a trailing comma so render each member on a new line.
ais.pushIndentNextLine();
- defer ais.popIndent();
-
- try renderToken(tree, ais, lbrace, Space.Newline);
-
- for (field_inits) |field_init, i| {
- if (i + 1 < field_inits.len) {
- const next_field_init = field_inits[i + 1];
- try renderExpression(allocator, ais, tree, field_init, Space.None);
-
- const comma = tree.nextToken(field_init.lastToken());
- try renderToken(tree, ais, comma, Space.Newline);
-
- try renderExtraNewline(tree, ais, next_field_init);
- } else {
- try renderExpression(allocator, ais, tree, field_init, Space.Comma);
+ try renderToken(ais, tree, lbrace, .newline);
+ var i = lbrace + 1;
+ while (i < rbrace) : (i += 1) {
+ if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i);
+ switch (token_tags[i]) {
+ .doc_comment => try renderToken(ais, tree, i, .newline),
+ .identifier => try renderToken(ais, tree, i, .comma),
+ .comma => {},
+ else => unreachable,
}
}
- }
-
- return renderToken(tree, ais, rtoken, space);
- },
-
- .Call => {
- const call = @fieldParentPtr(ast.Node.Call, "base", base);
- if (call.async_token) |async_token| {
- try renderToken(tree, ais, async_token, Space.Space);
- }
-
- try renderExpression(allocator, ais, tree, call.lhs, Space.None);
-
- const lparen = tree.nextToken(call.lhs.lastToken());
-
- if (call.params_len == 0) {
- try renderToken(tree, ais, lparen, Space.None);
- return renderToken(tree, ais, call.rtoken, space);
- }
-
- const src_has_trailing_comma = blk: {
- const maybe_comma = tree.prevToken(call.rtoken);
- break :blk tree.token_ids[maybe_comma] == .Comma;
- };
-
- if (src_has_trailing_comma) {
- {
- ais.pushIndent();
- defer ais.popIndent();
-
- try renderToken(tree, ais, lparen, Space.Newline); // (
- const params = call.params();
- for (params) |param_node, i| {
- if (i + 1 < params.len) {
- const next_node = params[i + 1];
- try renderExpression(allocator, ais, tree, param_node, Space.None);
-
- // Unindent the comma for multiline string literals
- const maybe_multiline_string = param_node.firstToken();
- const is_multiline_string = tree.token_ids[maybe_multiline_string] == .MultilineStringLiteralLine;
- if (is_multiline_string) ais.popIndent();
- defer if (is_multiline_string) ais.pushIndent();
-
- const comma = tree.nextToken(param_node.lastToken());
- try renderToken(tree, ais, comma, Space.Newline); // ,
- try renderExtraNewline(tree, ais, next_node);
- } else {
- try renderExpression(allocator, ais, tree, param_node, Space.Comma);
- }
+ ais.popIndent();
+ return renderToken(ais, tree, rbrace, space);
+ } else {
+ // There is no trailing comma so render everything on one line.
+ try renderToken(ais, tree, lbrace, .space);
+ var i = lbrace + 1;
+ while (i < rbrace) : (i += 1) {
+ switch (token_tags[i]) {
+ .doc_comment => unreachable, // TODO
+ .identifier => try renderToken(ais, tree, i, .comma_space),
+ .comma => {},
+ else => unreachable,
}
}
- return renderToken(tree, ais, call.rtoken, space);
- }
-
- try renderToken(tree, ais, lparen, Space.None); // (
-
- const params = call.params();
- for (params) |param_node, i| {
- const maybe_comment = param_node.firstToken() - 1;
- const maybe_multiline_string = param_node.firstToken();
- if (tree.token_ids[maybe_multiline_string] == .MultilineStringLiteralLine or tree.token_ids[maybe_comment] == .LineComment) {
- ais.pushIndentOneShot();
- }
-
- try renderExpression(allocator, ais, tree, param_node, Space.None);
-
- if (i + 1 < params.len) {
- const comma = tree.nextToken(param_node.lastToken());
- try renderToken(tree, ais, comma, Space.Space);
- }
- }
- return renderToken(tree, ais, call.rtoken, space); // )
- },
-
- .ArrayAccess => {
- const suffix_op = base.castTag(.ArrayAccess).?;
-
- const lbracket = tree.nextToken(suffix_op.lhs.lastToken());
- const rbracket = tree.nextToken(suffix_op.index_expr.lastToken());
-
- try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
- try renderToken(tree, ais, lbracket, Space.None); // [
-
- const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
- const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
- {
- const new_space = if (ends_with_comment) Space.Newline else Space.None;
-
- ais.pushIndent();
- defer ais.popIndent();
- try renderExpression(allocator, ais, tree, suffix_op.index_expr, new_space);
+ return renderToken(ais, tree, rbrace, space);
}
- if (starts_with_comment) try ais.maybeInsertNewline();
- return renderToken(tree, ais, rbracket, space); // ]
},
- .Slice => {
- const suffix_op = base.castTag(.Slice).?;
- try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
-
- const lbracket = tree.prevToken(suffix_op.start.firstToken());
- const dotdot = tree.nextToken(suffix_op.start.lastToken());
-
- const after_start_space_bool = nodeCausesSliceOpSpace(suffix_op.start) or
- (if (suffix_op.end) |end| nodeCausesSliceOpSpace(end) else false);
- const after_start_space = if (after_start_space_bool) Space.Space else Space.None;
- const after_op_space = if (suffix_op.end != null) after_start_space else Space.None;
-
- try renderToken(tree, ais, lbracket, Space.None); // [
- try renderExpression(allocator, ais, tree, suffix_op.start, after_start_space);
- try renderToken(tree, ais, dotdot, after_op_space); // ..
- if (suffix_op.end) |end| {
- const after_end_space = if (suffix_op.sentinel != null) Space.Space else Space.None;
- try renderExpression(allocator, ais, tree, end, after_end_space);
- }
- if (suffix_op.sentinel) |sentinel| {
- const colon = tree.prevToken(sentinel.firstToken());
- try renderToken(tree, ais, colon, Space.None); // :
- try renderExpression(allocator, ais, tree, sentinel, Space.None);
+ .builtin_call_two, .builtin_call_two_comma => {
+ if (datas[node].lhs == 0) {
+ return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{}, space);
+ } else if (datas[node].rhs == 0) {
+ return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{datas[node].lhs}, space);
+ } else {
+ return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }, space);
}
- return renderToken(tree, ais, suffix_op.rtoken, space); // ]
},
-
- .Deref => {
- const suffix_op = base.castTag(.Deref).?;
-
- try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
- return renderToken(tree, ais, suffix_op.rtoken, space); // .*
+ .builtin_call, .builtin_call_comma => {
+ const params = tree.extra_data[datas[node].lhs..datas[node].rhs];
+ return renderBuiltinCall(gpa, ais, tree, main_tokens[node], params, space);
},
- .UnwrapOptional => {
- const suffix_op = base.castTag(.UnwrapOptional).?;
- try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
- try renderToken(tree, ais, tree.prevToken(suffix_op.rtoken), Space.None); // .
- return renderToken(tree, ais, suffix_op.rtoken, space); // ?
+ .fn_proto_simple => {
+ var params: [1]ast.Node.Index = undefined;
+ return renderFnProto(gpa, ais, tree, tree.fnProtoSimple(&params, node), space);
},
-
- .Break => {
- const flow_expr = base.castTag(.Break).?;
- const maybe_rhs = flow_expr.getRHS();
- const maybe_label = flow_expr.getLabel();
-
- if (maybe_label == null and maybe_rhs == null) {
- return renderToken(tree, ais, flow_expr.ltoken, space); // break
- }
-
- try renderToken(tree, ais, flow_expr.ltoken, Space.Space); // break
- if (maybe_label) |label| {
- const colon = tree.nextToken(flow_expr.ltoken);
- try renderToken(tree, ais, colon, Space.None); // :
-
- if (maybe_rhs == null) {
- return renderToken(tree, ais, label, space); // label
- }
- try renderToken(tree, ais, label, Space.Space); // label
- }
- return renderExpression(allocator, ais, tree, maybe_rhs.?, space);
+ .fn_proto_multi => return renderFnProto(gpa, ais, tree, tree.fnProtoMulti(node), space),
+ .fn_proto_one => {
+ var params: [1]ast.Node.Index = undefined;
+ return renderFnProto(gpa, ais, tree, tree.fnProtoOne(&params, node), space);
},
-
- .Continue => {
- const flow_expr = base.castTag(.Continue).?;
- if (flow_expr.getLabel()) |label| {
- try renderToken(tree, ais, flow_expr.ltoken, Space.Space); // continue
- const colon = tree.nextToken(flow_expr.ltoken);
- try renderToken(tree, ais, colon, Space.None); // :
- return renderToken(tree, ais, label, space); // label
+ .fn_proto => return renderFnProto(gpa, ais, tree, tree.fnProto(node), space),
+
+ .anyframe_type => {
+ const main_token = main_tokens[node];
+ if (datas[node].rhs != 0) {
+ try renderToken(ais, tree, main_token, .none); // anyframe
+ try renderToken(ais, tree, main_token + 1, .none); // ->
+ return renderExpression(gpa, ais, tree, datas[node].rhs, space);
} else {
- return renderToken(tree, ais, flow_expr.ltoken, space); // continue
+ return renderToken(ais, tree, main_token, space); // anyframe
}
},
- .Return => {
- const flow_expr = base.castTag(.Return).?;
- if (flow_expr.getRHS()) |rhs| {
- try renderToken(tree, ais, flow_expr.ltoken, Space.Space);
- return renderExpression(allocator, ais, tree, rhs, space);
+ .@"switch",
+ .switch_comma,
+ => {
+ const switch_token = main_tokens[node];
+ const condition = datas[node].lhs;
+ const extra = tree.extraData(datas[node].rhs, ast.Node.SubRange);
+ const cases = tree.extra_data[extra.start..extra.end];
+ const rparen = tree.lastToken(condition) + 1;
+
+ try renderToken(ais, tree, switch_token, .space); // switch keyword
+ try renderToken(ais, tree, switch_token + 1, .none); // lparen
+ try renderExpression(gpa, ais, tree, condition, .none); // condtion expression
+ try renderToken(ais, tree, rparen, .space); // rparen
+
+ ais.pushIndentNextLine();
+ if (cases.len == 0) {
+ try renderToken(ais, tree, rparen + 1, .none); // lbrace
} else {
- return renderToken(tree, ais, flow_expr.ltoken, space);
+ try renderToken(ais, tree, rparen + 1, .newline); // lbrace
+ try renderExpressions(gpa, ais, tree, cases, .comma);
}
+ ais.popIndent();
+ return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
},
- .Payload => {
- const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
+ .switch_case_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
+ .switch_case => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
- try renderToken(tree, ais, payload.lpipe, Space.None);
- try renderExpression(allocator, ais, tree, payload.error_symbol, Space.None);
- return renderToken(tree, ais, payload.rpipe, space);
- },
+ .while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space),
+ .while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space),
+ .@"while" => return renderWhile(gpa, ais, tree, tree.whileFull(node), space),
+ .for_simple => return renderWhile(gpa, ais, tree, tree.forSimple(node), space),
+ .@"for" => return renderWhile(gpa, ais, tree, tree.forFull(node), space),
- .PointerPayload => {
- const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
+ .if_simple => return renderIf(gpa, ais, tree, tree.ifSimple(node), space),
+ .@"if" => return renderIf(gpa, ais, tree, tree.ifFull(node), space),
- try renderToken(tree, ais, payload.lpipe, Space.None);
- if (payload.ptr_token) |ptr_token| {
- try renderToken(tree, ais, ptr_token, Space.None);
- }
- try renderExpression(allocator, ais, tree, payload.value_symbol, Space.None);
- return renderToken(tree, ais, payload.rpipe, space);
- },
+ .asm_simple => return renderAsm(gpa, ais, tree, tree.asmSimple(node), space),
+ .@"asm" => return renderAsm(gpa, ais, tree, tree.asmFull(node), space),
- .PointerIndexPayload => {
- const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
+ .enum_literal => {
+ try renderToken(ais, tree, main_tokens[node] - 1, .none); // .
+ return renderToken(ais, tree, main_tokens[node], space); // name
+ },
- try renderToken(tree, ais, payload.lpipe, Space.None);
- if (payload.ptr_token) |ptr_token| {
- try renderToken(tree, ais, ptr_token, Space.None);
- }
- try renderExpression(allocator, ais, tree, payload.value_symbol, Space.None);
+ .fn_decl => unreachable,
+ .container_field => unreachable,
+ .container_field_init => unreachable,
+ .container_field_align => unreachable,
+ .root => unreachable,
+ .global_var_decl => unreachable,
+ .local_var_decl => unreachable,
+ .simple_var_decl => unreachable,
+ .aligned_var_decl => unreachable,
+ .@"usingnamespace" => unreachable,
+ .test_decl => unreachable,
+ .asm_output => unreachable,
+ .asm_input => unreachable,
+ }
+}
- if (payload.index_symbol) |index_symbol| {
- const comma = tree.nextToken(payload.value_symbol.lastToken());
+fn renderArrayType(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ array_type: ast.full.ArrayType,
+ space: Space,
+) Error!void {
+ const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
+ const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket);
+ const inner_space = if (one_line) Space.none else Space.newline;
+ ais.pushIndentNextLine();
+ try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket
+ try renderExpression(gpa, ais, tree, array_type.ast.elem_count, inner_space);
+ if (array_type.ast.sentinel) |sentinel| {
+ try renderToken(ais, tree, tree.firstToken(sentinel) - 1, inner_space); // colon
+ try renderExpression(gpa, ais, tree, sentinel, inner_space);
+ }
+ ais.popIndent();
+ try renderToken(ais, tree, rbracket, .none); // rbracket
+ return renderExpression(gpa, ais, tree, array_type.ast.elem_type, space);
+}
- try renderToken(tree, ais, comma, Space.Space);
- try renderExpression(allocator, ais, tree, index_symbol, Space.None);
+fn renderPtrType(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ ptr_type: ast.full.PtrType,
+ space: Space,
+) Error!void {
+ switch (ptr_type.size) {
+ .One => {
+ // Since ** tokens exist and the same token is shared by two
+ // nested pointer types, we check to see if we are the parent
+ // in such a relationship. If so, skip rendering anything for
+ // this pointer type and rely on the child to render our asterisk
+ // as well when it renders the ** token.
+ if (tree.tokens.items(.tag)[ptr_type.ast.main_token] == .asterisk_asterisk and
+ ptr_type.ast.main_token == tree.nodes.items(.main_token)[ptr_type.ast.child_type])
+ {
+ return renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space);
}
-
- return renderToken(tree, ais, payload.rpipe, space);
+ try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
},
-
- .GroupedExpression => {
- const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
-
- try renderToken(tree, ais, grouped_expr.lparen, Space.None);
- {
- ais.pushIndentOneShot();
- try renderExpression(allocator, ais, tree, grouped_expr.expr, Space.None);
+ .Many => {
+ if (ptr_type.ast.sentinel == 0) {
+ try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket
+ try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
+ try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket
+ } else {
+ try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket
+ try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
+ try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon
+ try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none);
+ try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket
}
- return renderToken(tree, ais, grouped_expr.rparen, space);
},
-
- .FieldInitializer => {
- const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
-
- try renderToken(tree, ais, field_init.period_token, Space.None); // .
- try renderToken(tree, ais, field_init.name_token, Space.Space); // name
- try renderToken(tree, ais, tree.nextToken(field_init.name_token), Space.Space); // =
- return renderExpression(allocator, ais, tree, field_init.expr, space);
+ .C => {
+ try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket
+ try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
+ try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // c
+ try renderToken(ais, tree, ptr_type.ast.main_token + 2, .none); // rbracket
},
+ .Slice => {
+ if (ptr_type.ast.sentinel == 0) {
+ try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket
+ try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket
+ } else {
+ try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket
+ try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon
+ try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none);
+ try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket
+ }
+ },
+ }
- .ContainerDecl => {
- const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
+ if (ptr_type.allowzero_token) |allowzero_token| {
+ try renderToken(ais, tree, allowzero_token, .space);
+ }
- if (container_decl.layout_token) |layout_token| {
- try renderToken(tree, ais, layout_token, Space.Space);
- }
+ if (ptr_type.ast.align_node != 0) {
+ const align_first = tree.firstToken(ptr_type.ast.align_node);
+ try renderToken(ais, tree, align_first - 2, .none); // align
+ try renderToken(ais, tree, align_first - 1, .none); // lparen
+ try renderExpression(gpa, ais, tree, ptr_type.ast.align_node, .none);
+ if (ptr_type.ast.bit_range_start != 0) {
+ assert(ptr_type.ast.bit_range_end != 0);
+ try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_start) - 1, .none); // colon
+ try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_start, .none);
+ try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_end) - 1, .none); // colon
+ try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_end, .none);
+ try renderToken(ais, tree, tree.lastToken(ptr_type.ast.bit_range_end) + 1, .space); // rparen
+ } else {
+ try renderToken(ais, tree, tree.lastToken(ptr_type.ast.align_node) + 1, .space); // rparen
+ }
+ }
- switch (container_decl.init_arg_expr) {
- .None => {
- try renderToken(tree, ais, container_decl.kind_token, Space.Space); // union
- },
- .Enum => |enum_tag_type| {
- try renderToken(tree, ais, container_decl.kind_token, Space.None); // union
+ if (ptr_type.const_token) |const_token| {
+ try renderToken(ais, tree, const_token, .space);
+ }
- const lparen = tree.nextToken(container_decl.kind_token);
- const enum_token = tree.nextToken(lparen);
+ if (ptr_type.volatile_token) |volatile_token| {
+ try renderToken(ais, tree, volatile_token, .space);
+ }
- try renderToken(tree, ais, lparen, Space.None); // (
- try renderToken(tree, ais, enum_token, Space.None); // enum
+ try renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space);
+}
- if (enum_tag_type) |expr| {
- try renderToken(tree, ais, tree.nextToken(enum_token), Space.None); // (
- try renderExpression(allocator, ais, tree, expr, Space.None);
+fn renderSlice(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ slice: ast.full.Slice,
+ space: Space,
+) Error!void {
+ const node_tags = tree.nodes.items(.tag);
+ const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or
+ if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false;
+ const after_start_space = if (after_start_space_bool) Space.space else Space.none;
+ const after_dots_space = if (slice.ast.end != 0) after_start_space else Space.none;
+
+ try renderExpression(gpa, ais, tree, slice.ast.sliced, .none);
+ try renderToken(ais, tree, slice.ast.lbracket, .none); // lbracket
+
+ const start_last = tree.lastToken(slice.ast.start);
+ try renderExpression(gpa, ais, tree, slice.ast.start, after_start_space);
+ try renderToken(ais, tree, start_last + 1, after_dots_space); // ellipsis2 ("..")
+ if (slice.ast.end == 0) {
+ return renderToken(ais, tree, start_last + 2, space); // rbracket
+ }
- const rparen = tree.nextToken(expr.lastToken());
- try renderToken(tree, ais, rparen, Space.None); // )
- try renderToken(tree, ais, tree.nextToken(rparen), Space.Space); // )
- } else {
- try renderToken(tree, ais, tree.nextToken(enum_token), Space.Space); // )
- }
- },
- .Type => |type_expr| {
- try renderToken(tree, ais, container_decl.kind_token, Space.None); // union
+ const end_last = tree.lastToken(slice.ast.end);
+ const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none;
+ try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space);
+ if (slice.ast.sentinel == 0) {
+ return renderToken(ais, tree, end_last + 1, space); // rbracket
+ }
- const lparen = tree.nextToken(container_decl.kind_token);
- const rparen = tree.nextToken(type_expr.lastToken());
+ try renderToken(ais, tree, end_last + 1, .none); // colon
+ try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none);
+ try renderToken(ais, tree, tree.lastToken(slice.ast.sentinel) + 1, space); // rbracket
+}
- try renderToken(tree, ais, lparen, Space.None); // (
- try renderExpression(allocator, ais, tree, type_expr, Space.None);
- try renderToken(tree, ais, rparen, Space.Space); // )
- },
- }
+fn renderAsmOutput(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ asm_output: ast.Node.Index,
+ space: Space,
+) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const node_tags = tree.nodes.items(.tag);
+ const main_tokens = tree.nodes.items(.main_token);
+ const datas = tree.nodes.items(.data);
+ assert(node_tags[asm_output] == .asm_output);
+ const symbolic_name = main_tokens[asm_output];
+
+ try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket
+ try renderToken(ais, tree, symbolic_name, .none); // ident
+ try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket
+ try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"
+ try renderToken(ais, tree, symbolic_name + 3, .none); // lparen
+
+ if (token_tags[symbolic_name + 4] == .arrow) {
+ try renderToken(ais, tree, symbolic_name + 4, .space); // ->
+ try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none);
+ return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen
+ } else {
+ try renderToken(ais, tree, symbolic_name + 4, .none); // ident
+ return renderToken(ais, tree, symbolic_name + 5, space); // rparen
+ }
+}
- if (container_decl.fields_and_decls_len == 0) {
- {
- ais.pushIndentNextLine();
- defer ais.popIndent();
- try renderToken(tree, ais, container_decl.lbrace_token, Space.None); // {
- }
- return renderToken(tree, ais, container_decl.rbrace_token, space); // }
- }
+fn renderAsmInput(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ asm_input: ast.Node.Index,
+ space: Space,
+) Error!void {
+ const node_tags = tree.nodes.items(.tag);
+ const main_tokens = tree.nodes.items(.main_token);
+ const datas = tree.nodes.items(.data);
+ assert(node_tags[asm_input] == .asm_input);
+ const symbolic_name = main_tokens[asm_input];
+
+ try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket
+ try renderToken(ais, tree, symbolic_name, .none); // ident
+ try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket
+ try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"
+ try renderToken(ais, tree, symbolic_name + 3, .none); // lparen
+ try renderExpression(gpa, ais, tree, datas[asm_input].lhs, Space.none);
+ return renderToken(ais, tree, datas[asm_input].rhs, space); // rparen
+}
- const src_has_trailing_comma = blk: {
- var maybe_comma = tree.prevToken(container_decl.lastToken());
- // Doc comments for a field may also appear after the comma, eg.
- // field_name: T, // comment attached to field_name
- if (tree.token_ids[maybe_comma] == .DocComment)
- maybe_comma = tree.prevToken(maybe_comma);
- break :blk tree.token_ids[maybe_comma] == .Comma;
- };
+fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: ast.Tree, var_decl: ast.full.VarDecl) Error!void {
+ if (var_decl.visib_token) |visib_token| {
+ try renderToken(ais, tree, visib_token, Space.space); // pub
+ }
- const fields_and_decls = container_decl.fieldsAndDecls();
+ if (var_decl.extern_export_token) |extern_export_token| {
+ try renderToken(ais, tree, extern_export_token, Space.space); // extern
- // Check if the first declaration and the { are on the same line
- const src_has_newline = !tree.tokensOnSameLine(
- container_decl.lbrace_token,
- fields_and_decls[0].firstToken(),
- );
+ if (var_decl.lib_name) |lib_name| {
+ try renderToken(ais, tree, lib_name, Space.space); // "lib"
+ }
+ }
- // We can only print all the elements in-line if all the
- // declarations inside are fields
- const src_has_only_fields = blk: {
- for (fields_and_decls) |decl| {
- if (decl.tag != .ContainerField) break :blk false;
- }
- break :blk true;
- };
+ if (var_decl.threadlocal_token) |thread_local_token| {
+ try renderToken(ais, tree, thread_local_token, Space.space); // threadlocal
+ }
- if (src_has_trailing_comma or !src_has_only_fields) {
- // One declaration per line
- ais.pushIndentNextLine();
- defer ais.popIndent();
- try renderToken(tree, ais, container_decl.lbrace_token, .Newline); // {
+ if (var_decl.comptime_token) |comptime_token| {
+ try renderToken(ais, tree, comptime_token, Space.space); // comptime
+ }
- for (fields_and_decls) |decl, i| {
- try renderContainerDecl(allocator, ais, tree, decl, .Newline);
+ try renderToken(ais, tree, var_decl.ast.mut_token, .space); // var
- if (i + 1 < fields_and_decls.len) {
- try renderExtraNewline(tree, ais, fields_and_decls[i + 1]);
- }
- }
- } else if (src_has_newline) {
- // All the declarations on the same line, but place the items on
- // their own line
- try renderToken(tree, ais, container_decl.lbrace_token, .Newline); // {
+ const name_space = if (var_decl.ast.type_node == 0 and
+ (var_decl.ast.align_node != 0 or
+ var_decl.ast.section_node != 0 or
+ var_decl.ast.init_node != 0))
+ Space.space
+ else
+ Space.none;
+ try renderToken(ais, tree, var_decl.ast.mut_token + 1, name_space); // name
- ais.pushIndent();
- defer ais.popIndent();
+ if (var_decl.ast.type_node != 0) {
+ try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // :
+ if (var_decl.ast.align_node != 0 or var_decl.ast.section_node != 0 or
+ var_decl.ast.init_node != 0)
+ {
+ try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space);
+ } else {
+ try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .none);
+ const semicolon = tree.lastToken(var_decl.ast.type_node) + 1;
+ return renderToken(ais, tree, semicolon, Space.newline); // ;
+ }
+ }
- for (fields_and_decls) |decl, i| {
- const space_after_decl: Space = if (i + 1 >= fields_and_decls.len) .Newline else .Space;
- try renderContainerDecl(allocator, ais, tree, decl, space_after_decl);
- }
- } else {
- // All the declarations on the same line
- try renderToken(tree, ais, container_decl.lbrace_token, .Space); // {
+ if (var_decl.ast.align_node != 0) {
+ const lparen = tree.firstToken(var_decl.ast.align_node) - 1;
+ const align_kw = lparen - 1;
+ const rparen = tree.lastToken(var_decl.ast.align_node) + 1;
+ try renderToken(ais, tree, align_kw, Space.none); // align
+ try renderToken(ais, tree, lparen, Space.none); // (
+ try renderExpression(gpa, ais, tree, var_decl.ast.align_node, Space.none);
+ if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) {
+ try renderToken(ais, tree, rparen, .space); // )
+ } else {
+ try renderToken(ais, tree, rparen, .none); // )
+ return renderToken(ais, tree, rparen + 1, Space.newline); // ;
+ }
+ }
- for (fields_and_decls) |decl| {
- try renderContainerDecl(allocator, ais, tree, decl, .Space);
- }
- }
+ if (var_decl.ast.section_node != 0) {
+ const lparen = tree.firstToken(var_decl.ast.section_node) - 1;
+ const section_kw = lparen - 1;
+ const rparen = tree.lastToken(var_decl.ast.section_node) + 1;
+ try renderToken(ais, tree, section_kw, Space.none); // linksection
+ try renderToken(ais, tree, lparen, Space.none); // (
+ try renderExpression(gpa, ais, tree, var_decl.ast.section_node, Space.none);
+ if (var_decl.ast.init_node != 0) {
+ try renderToken(ais, tree, rparen, .space); // )
+ } else {
+ try renderToken(ais, tree, rparen, .none); // )
+ return renderToken(ais, tree, rparen + 1, Space.newline); // ;
+ }
+ }
- return renderToken(tree, ais, container_decl.rbrace_token, space); // }
- },
+ assert(var_decl.ast.init_node != 0);
+ const eq_token = tree.firstToken(var_decl.ast.init_node) - 1;
+ const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
+ {
+ ais.pushIndent();
+ try renderToken(ais, tree, eq_token, eq_space); // =
+ ais.popIndent();
+ }
+ ais.pushIndentOneShot();
+ try renderExpression(gpa, ais, tree, var_decl.ast.init_node, .semicolon);
+}
- .ErrorSetDecl => {
- const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
+fn renderIf(gpa: *Allocator, ais: *Ais, tree: ast.Tree, if_node: ast.full.If, space: Space) Error!void {
+ return renderWhile(gpa, ais, tree, .{
+ .ast = .{
+ .while_token = if_node.ast.if_token,
+ .cond_expr = if_node.ast.cond_expr,
+ .cont_expr = 0,
+ .then_expr = if_node.ast.then_expr,
+ .else_expr = if_node.ast.else_expr,
+ },
+ .inline_token = null,
+ .label_token = null,
+ .payload_token = if_node.payload_token,
+ .else_token = if_node.else_token,
+ .error_token = if_node.error_token,
+ }, space);
+}
- const lbrace = tree.nextToken(err_set_decl.error_token);
+/// Note that this function is additionally used to render if and for expressions, with
+/// respective values set to null.
+fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full.While, space: Space) Error!void {
+ const node_tags = tree.nodes.items(.tag);
+ const token_tags = tree.tokens.items(.tag);
- if (err_set_decl.decls_len == 0) {
- try renderToken(tree, ais, err_set_decl.error_token, Space.None);
- try renderToken(tree, ais, lbrace, Space.None);
- return renderToken(tree, ais, err_set_decl.rbrace_token, space);
- }
+ if (while_node.label_token) |label| {
+ try renderToken(ais, tree, label, .none); // label
+ try renderToken(ais, tree, label + 1, .space); // :
+ }
- if (err_set_decl.decls_len == 1) blk: {
- const node = err_set_decl.decls()[0];
+ if (while_node.inline_token) |inline_token| {
+ try renderToken(ais, tree, inline_token, .space); // inline
+ }
- // if there are any doc comments or same line comments
- // don't try to put it all on one line
- if (node.cast(ast.Node.ErrorTag)) |tag| {
- if (tag.doc_comments != null) break :blk;
+ try renderToken(ais, tree, while_node.ast.while_token, .space); // if
+ try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen
+ try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition
+
+ const then_tag = node_tags[while_node.ast.then_expr];
+ if (nodeIsBlock(then_tag) and !nodeIsIf(then_tag)) {
+ if (while_node.payload_token) |payload_token| {
+ try renderToken(ais, tree, payload_token - 2, .space); // rparen
+ try renderToken(ais, tree, payload_token - 1, .none); // |
+ const ident = blk: {
+ if (token_tags[payload_token] == .asterisk) {
+ try renderToken(ais, tree, payload_token, .none); // *
+ break :blk payload_token + 1;
} else {
- break :blk;
+ break :blk payload_token;
}
-
- try renderToken(tree, ais, err_set_decl.error_token, Space.None); // error
- try renderToken(tree, ais, lbrace, Space.None); // {
- try renderExpression(allocator, ais, tree, node, Space.None);
- return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
- }
-
- try renderToken(tree, ais, err_set_decl.error_token, Space.None); // error
-
- const src_has_trailing_comma = blk: {
- const maybe_comma = tree.prevToken(err_set_decl.rbrace_token);
- break :blk tree.token_ids[maybe_comma] == .Comma;
};
-
- if (src_has_trailing_comma) {
- {
- ais.pushIndent();
- defer ais.popIndent();
-
- try renderToken(tree, ais, lbrace, Space.Newline); // {
- const decls = err_set_decl.decls();
- for (decls) |node, i| {
- if (i + 1 < decls.len) {
- try renderExpression(allocator, ais, tree, node, Space.None);
- try renderToken(tree, ais, tree.nextToken(node.lastToken()), Space.Newline); // ,
-
- try renderExtraNewline(tree, ais, decls[i + 1]);
- } else {
- try renderExpression(allocator, ais, tree, node, Space.Comma);
- }
- }
+ try renderToken(ais, tree, ident, .none); // identifier
+ const pipe = blk: {
+ if (token_tags[ident + 1] == .comma) {
+ try renderToken(ais, tree, ident + 1, .space); // ,
+ try renderToken(ais, tree, ident + 2, .none); // index
+ break :blk ident + 3;
+ } else {
+ break :blk ident + 1;
}
+ };
+ const brace_space = if (while_node.ast.cont_expr == 0 and ais.isLineOverIndented())
+ Space.newline
+ else
+ Space.space;
+ try renderToken(ais, tree, pipe, brace_space); // |
+ } else {
+ const rparen = tree.lastToken(while_node.ast.cond_expr) + 1;
+ const brace_space = if (while_node.ast.cont_expr == 0 and ais.isLineOverIndented())
+ Space.newline
+ else
+ Space.space;
+ try renderToken(ais, tree, rparen, brace_space); // rparen
+ }
+ if (while_node.ast.cont_expr != 0) {
+ const rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
+ const lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
+ try renderToken(ais, tree, lparen - 1, .space); // :
+ try renderToken(ais, tree, lparen, .none); // lparen
+ try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
+ const brace_space: Space = if (ais.isLineOverIndented()) .newline else .space;
+ try renderToken(ais, tree, rparen, brace_space); // rparen
+ }
+ if (while_node.ast.else_expr != 0) {
+ try renderExpression(gpa, ais, tree, while_node.ast.then_expr, Space.space);
+ try renderToken(ais, tree, while_node.else_token, .space); // else
+ if (while_node.error_token) |error_token| {
+ try renderToken(ais, tree, error_token - 1, .none); // |
+ try renderToken(ais, tree, error_token, .none); // identifier
+ try renderToken(ais, tree, error_token + 1, .space); // |
+ }
+ return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
+ } else {
+ return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space);
+ }
+ }
- return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
+ const rparen = tree.lastToken(while_node.ast.cond_expr) + 1;
+ const last_then_token = tree.lastToken(while_node.ast.then_expr);
+ const src_has_newline = !tree.tokensOnSameLine(rparen, last_then_token);
+
+ if (src_has_newline) {
+ if (while_node.payload_token) |payload_token| {
+ try renderToken(ais, tree, payload_token - 2, .space); // rparen
+ try renderToken(ais, tree, payload_token - 1, .none); // |
+ const ident = blk: {
+ if (token_tags[payload_token] == .asterisk) {
+ try renderToken(ais, tree, payload_token, .none); // *
+ break :blk payload_token + 1;
+ } else {
+ break :blk payload_token;
+ }
+ };
+ try renderToken(ais, tree, ident, .none); // identifier
+ const pipe = blk: {
+ if (token_tags[ident + 1] == .comma) {
+ try renderToken(ais, tree, ident + 1, .space); // ,
+ try renderToken(ais, tree, ident + 2, .none); // index
+ break :blk ident + 3;
+ } else {
+ break :blk ident + 1;
+ }
+ };
+ const after_space: Space = if (while_node.ast.cont_expr != 0) .space else .newline;
+ try renderToken(ais, tree, pipe, after_space); // |
+ } else {
+ ais.pushIndent();
+ const after_space: Space = if (while_node.ast.cont_expr != 0) .space else .newline;
+ try renderToken(ais, tree, rparen, after_space); // rparen
+ ais.popIndent();
+ }
+ if (while_node.ast.cont_expr != 0) {
+ const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
+ const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
+ try renderToken(ais, tree, cont_lparen - 1, .space); // :
+ try renderToken(ais, tree, cont_lparen, .none); // lparen
+ try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
+ try renderToken(ais, tree, cont_rparen, .newline); // rparen
+ }
+ if (while_node.ast.else_expr != 0) {
+ ais.pushIndent();
+ try renderExpression(gpa, ais, tree, while_node.ast.then_expr, Space.newline);
+ ais.popIndent();
+ const else_is_block = nodeIsBlock(node_tags[while_node.ast.else_expr]);
+ if (else_is_block) {
+ try renderToken(ais, tree, while_node.else_token, .space); // else
+ if (while_node.error_token) |error_token| {
+ try renderToken(ais, tree, error_token - 1, .none); // |
+ try renderToken(ais, tree, error_token, .none); // identifier
+ try renderToken(ais, tree, error_token + 1, .space); // |
+ }
+ return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
} else {
- try renderToken(tree, ais, lbrace, Space.Space); // {
-
- const decls = err_set_decl.decls();
- for (decls) |node, i| {
- if (i + 1 < decls.len) {
- try renderExpression(allocator, ais, tree, node, Space.None);
-
- const comma_token = tree.nextToken(node.lastToken());
- assert(tree.token_ids[comma_token] == .Comma);
- try renderToken(tree, ais, comma_token, Space.Space); // ,
- try renderExtraNewline(tree, ais, decls[i + 1]);
- } else {
- try renderExpression(allocator, ais, tree, node, Space.Space);
- }
+ if (while_node.error_token) |error_token| {
+ try renderToken(ais, tree, while_node.else_token, .space); // else
+ try renderToken(ais, tree, error_token - 1, .none); // |
+ try renderToken(ais, tree, error_token, .none); // identifier
+ try renderToken(ais, tree, error_token + 1, .space); // |
+ } else {
+ try renderToken(ais, tree, while_node.else_token, .newline); // else
}
-
- return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
+ try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space);
+ return;
}
- },
-
- .ErrorTag => {
- const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base);
-
- try renderDocComments(tree, ais, tag, tag.doc_comments);
- return renderToken(tree, ais, tag.name_token, space); // name
- },
-
- .MultilineStringLiteral => {
- const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
+ } else {
+ try renderExpressionIndented(gpa, ais, tree, while_node.ast.then_expr, space);
+ return;
+ }
+ }
- {
- const locked_indents = ais.lockOneShotIndent();
- defer {
- var i: u8 = 0;
- while (i < locked_indents) : (i += 1) ais.popIndent();
- }
- try ais.maybeInsertNewline();
+ // Render everything on a single line.
- for (multiline_str_literal.lines()) |t| try renderToken(tree, ais, t, Space.None);
+ if (while_node.payload_token) |payload_token| {
+ assert(payload_token - 2 == rparen);
+ try renderToken(ais, tree, payload_token - 2, .space); // )
+ try renderToken(ais, tree, payload_token - 1, .none); // |
+ const ident = blk: {
+ if (token_tags[payload_token] == .asterisk) {
+ try renderToken(ais, tree, payload_token, .none); // *
+ break :blk payload_token + 1;
+ } else {
+ break :blk payload_token;
}
- },
-
- .BuiltinCall => {
- const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
-
- // TODO remove after 0.7.0 release
- if (mem.eql(u8, tree.tokenSlice(builtin_call.builtin_token), "@OpaqueType"))
- return ais.writer().writeAll("opaque {}");
-
- // TODO remove after 0.7.0 release
- {
- const params = builtin_call.paramsConst();
- if (mem.eql(u8, tree.tokenSlice(builtin_call.builtin_token), "@Type") and
- params.len == 1)
- {
- if (params[0].castTag(.EnumLiteral)) |enum_literal|
- if (mem.eql(u8, tree.tokenSlice(enum_literal.name), "Opaque"))
- return ais.writer().writeAll("opaque {}");
- }
+ };
+ try renderToken(ais, tree, ident, .none); // identifier
+ const pipe = blk: {
+ if (token_tags[ident + 1] == .comma) {
+ try renderToken(ais, tree, ident + 1, .space); // ,
+ try renderToken(ais, tree, ident + 2, .none); // index
+ break :blk ident + 3;
+ } else {
+ break :blk ident + 1;
}
+ };
+ try renderToken(ais, tree, pipe, .space); // |
+ } else {
+ try renderToken(ais, tree, rparen, .space); // )
+ }
- try renderToken(tree, ais, builtin_call.builtin_token, Space.None); // @name
+ if (while_node.ast.cont_expr != 0) {
+ const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
+ const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
+ try renderToken(ais, tree, cont_lparen - 1, .space); // :
+ try renderToken(ais, tree, cont_lparen, .none); // lparen
+ try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
+ try renderToken(ais, tree, cont_rparen, .space); // rparen
+ }
- const src_params_trailing_comma = blk: {
- if (builtin_call.params_len == 0) break :blk false;
- const last_node = builtin_call.params()[builtin_call.params_len - 1];
- const maybe_comma = tree.nextToken(last_node.lastToken());
- break :blk tree.token_ids[maybe_comma] == .Comma;
- };
+ if (while_node.ast.else_expr != 0) {
+ try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space);
+ try renderToken(ais, tree, while_node.else_token, .space); // else
- const lparen = tree.nextToken(builtin_call.builtin_token);
+ if (while_node.error_token) |error_token| {
+ try renderToken(ais, tree, error_token - 1, .none); // |
+ try renderToken(ais, tree, error_token, .none); // identifier
+ try renderToken(ais, tree, error_token + 1, .space); // |
+ }
- if (!src_params_trailing_comma) {
- try renderToken(tree, ais, lparen, Space.None); // (
+ return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
+ } else {
+ return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space);
+ }
+}
- // render all on one line, no trailing comma
- const params = builtin_call.params();
- for (params) |param_node, i| {
- const maybe_comment = param_node.firstToken() - 1;
- if (param_node.*.tag == .MultilineStringLiteral or tree.token_ids[maybe_comment] == .LineComment) {
- ais.pushIndentOneShot();
- }
- try renderExpression(allocator, ais, tree, param_node, Space.None);
+fn renderContainerField(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ field: ast.full.ContainerField,
+ space: Space,
+) Error!void {
+ const main_tokens = tree.nodes.items(.main_token);
+ if (field.comptime_token) |t| {
+ try renderToken(ais, tree, t, .space); // comptime
+ }
+ if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
+ return renderTokenComma(ais, tree, field.ast.name_token, space); // name
+ }
+ if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
+ try renderToken(ais, tree, field.ast.name_token, .none); // name
+ try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
+
+ if (field.ast.align_expr != 0) {
+ try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
+ const align_token = tree.firstToken(field.ast.align_expr) - 2;
+ try renderToken(ais, tree, align_token, .none); // align
+ try renderToken(ais, tree, align_token + 1, .none); // (
+ try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
+ const rparen = tree.lastToken(field.ast.align_expr) + 1;
+ return renderTokenComma(ais, tree, rparen, space); // )
+ } else {
+ return renderExpressionComma(gpa, ais, tree, field.ast.type_expr, space); // type
+ }
+ }
+ if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
+ try renderToken(ais, tree, field.ast.name_token, .space); // name
+ try renderToken(ais, tree, field.ast.name_token + 1, .space); // =
+ return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
+ }
- if (i + 1 < params.len) {
- const comma_token = tree.nextToken(param_node.lastToken());
- try renderToken(tree, ais, comma_token, Space.Space); // ,
- }
- }
- } else {
- // one param per line
- ais.pushIndent();
- defer ais.popIndent();
- try renderToken(tree, ais, lparen, Space.Newline); // (
+ try renderToken(ais, tree, field.ast.name_token, .none); // name
+ try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
+ try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
+
+ if (field.ast.align_expr != 0) {
+ const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
+ const align_kw = lparen_token - 1;
+ const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
+ try renderToken(ais, tree, align_kw, .none); // align
+ try renderToken(ais, tree, lparen_token, .none); // (
+ try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
+ try renderToken(ais, tree, rparen_token, .space); // )
+ }
+ const eq_token = tree.firstToken(field.ast.value_expr) - 1;
+ try renderToken(ais, tree, eq_token, .space); // =
+ return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
+}
- for (builtin_call.params()) |param_node| {
- try renderExpression(allocator, ais, tree, param_node, Space.Comma);
- }
- }
+fn renderBuiltinCall(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ builtin_token: ast.TokenIndex,
+ params: []const ast.Node.Index,
+ space: Space,
+) Error!void {
+ const token_tags = tree.tokens.items(.tag);
- return renderToken(tree, ais, builtin_call.rparen_token, space); // )
- },
+ try renderToken(ais, tree, builtin_token, .none); // @name
- .FnProto => {
- const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
+ if (params.len == 0) {
+ try renderToken(ais, tree, builtin_token + 1, .none); // (
+ return renderToken(ais, tree, builtin_token + 2, space); // )
+ }
- if (fn_proto.getVisibToken()) |visib_token_index| {
- const visib_token = tree.token_ids[visib_token_index];
- assert(visib_token == .Keyword_pub or visib_token == .Keyword_export);
+ const last_param = params[params.len - 1];
+ const after_last_param_token = tree.lastToken(last_param) + 1;
- try renderToken(tree, ais, visib_token_index, Space.Space); // pub
- }
+ if (token_tags[after_last_param_token] != .comma) {
+ // Render all on one line, no trailing comma.
+ try renderToken(ais, tree, builtin_token + 1, .none); // (
- if (fn_proto.getExternExportInlineToken()) |extern_export_inline_token| {
- if (fn_proto.getIsExternPrototype() == null)
- try renderToken(tree, ais, extern_export_inline_token, Space.Space); // extern/export/inline
+ for (params) |param_node, i| {
+ const first_param_token = tree.firstToken(param_node);
+ if (token_tags[first_param_token] == .multiline_string_literal_line or
+ hasSameLineComment(tree, first_param_token - 1))
+ {
+ ais.pushIndentOneShot();
}
+ try renderExpression(gpa, ais, tree, param_node, .none);
- if (fn_proto.getLibName()) |lib_name| {
- try renderExpression(allocator, ais, tree, lib_name, Space.Space);
+ if (i + 1 < params.len) {
+ const comma_token = tree.lastToken(param_node) + 1;
+ try renderToken(ais, tree, comma_token, .space); // ,
}
+ }
+ return renderToken(ais, tree, after_last_param_token, space); // )
+ } else {
+ // Render one param per line.
+ ais.pushIndent();
+ try renderToken(ais, tree, builtin_token + 1, Space.newline); // (
+
+ for (params) |param_node| {
+ try renderExpression(gpa, ais, tree, param_node, .comma);
+ }
+ ais.popIndent();
- const lparen = if (fn_proto.getNameToken()) |name_token| blk: {
- try renderToken(tree, ais, fn_proto.fn_token, Space.Space); // fn
- try renderToken(tree, ais, name_token, Space.None); // name
- break :blk tree.nextToken(name_token);
- } else blk: {
- try renderToken(tree, ais, fn_proto.fn_token, Space.Space); // fn
- break :blk tree.nextToken(fn_proto.fn_token);
- };
- assert(tree.token_ids[lparen] == .LParen);
-
- const rparen = tree.prevToken(
- // the first token for the annotation expressions is the left
- // parenthesis, hence the need for two prevToken
- if (fn_proto.getAlignExpr()) |align_expr|
- tree.prevToken(tree.prevToken(align_expr.firstToken()))
- else if (fn_proto.getSectionExpr()) |section_expr|
- tree.prevToken(tree.prevToken(section_expr.firstToken()))
- else if (fn_proto.getCallconvExpr()) |callconv_expr|
- tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
- else switch (fn_proto.return_type) {
- .Explicit => |node| node.firstToken(),
- .InferErrorSet => |node| tree.prevToken(node.firstToken()),
- .Invalid => unreachable,
- },
- );
- assert(tree.token_ids[rparen] == .RParen);
-
- const src_params_trailing_comma = blk: {
- const maybe_comma = tree.token_ids[rparen - 1];
- break :blk maybe_comma == .Comma or maybe_comma == .LineComment;
- };
-
- if (!src_params_trailing_comma) {
- try renderToken(tree, ais, lparen, Space.None); // (
-
- // render all on one line, no trailing comma
- for (fn_proto.params()) |param_decl, i| {
- try renderParamDecl(allocator, ais, tree, param_decl, Space.None);
-
- if (i + 1 < fn_proto.params_len or fn_proto.getVarArgsToken() != null) {
- const comma = tree.nextToken(param_decl.lastToken());
- try renderToken(tree, ais, comma, Space.Space); // ,
- }
- }
- if (fn_proto.getVarArgsToken()) |var_args_token| {
- try renderToken(tree, ais, var_args_token, Space.None);
- }
- } else {
- // one param per line
- ais.pushIndent();
- defer ais.popIndent();
- try renderToken(tree, ais, lparen, Space.Newline); // (
+ return renderToken(ais, tree, after_last_param_token + 1, space); // )
+ }
+}
- for (fn_proto.params()) |param_decl| {
- try renderParamDecl(allocator, ais, tree, param_decl, Space.Comma);
- }
- if (fn_proto.getVarArgsToken()) |var_args_token| {
- try renderToken(tree, ais, var_args_token, Space.Comma);
- }
+fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: ast.Tree, fn_proto: ast.full.FnProto, space: Space) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const token_starts = tree.tokens.items(.start);
+
+ const is_inline = fn_proto.ast.fn_token > 0 and
+ token_tags[fn_proto.ast.fn_token - 1] == .keyword_inline;
+
+ const after_fn_token = fn_proto.ast.fn_token + 1;
+ const lparen = if (token_tags[after_fn_token] == .identifier) blk: {
+ try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn
+ try renderToken(ais, tree, after_fn_token, .none); // name
+ break :blk after_fn_token + 1;
+ } else blk: {
+ try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn
+ break :blk fn_proto.ast.fn_token + 1;
+ };
+ assert(token_tags[lparen] == .l_paren);
+
+ const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
+ const rparen = blk: {
+ // These may appear in any order, so we have to check the token_starts array
+ // to find out which is first.
+ var rparen = if (token_tags[maybe_bang] == .bang) maybe_bang - 1 else maybe_bang;
+ var smallest_start = token_starts[maybe_bang];
+ if (fn_proto.ast.align_expr != 0) {
+ const tok = tree.firstToken(fn_proto.ast.align_expr) - 3;
+ const start = token_starts[tok];
+ if (start < smallest_start) {
+ rparen = tok;
+ smallest_start = start;
}
-
- try renderToken(tree, ais, rparen, Space.Space); // )
-
- if (fn_proto.getAlignExpr()) |align_expr| {
- const align_rparen = tree.nextToken(align_expr.lastToken());
- const align_lparen = tree.prevToken(align_expr.firstToken());
- const align_kw = tree.prevToken(align_lparen);
-
- try renderToken(tree, ais, align_kw, Space.None); // align
- try renderToken(tree, ais, align_lparen, Space.None); // (
- try renderExpression(allocator, ais, tree, align_expr, Space.None);
- try renderToken(tree, ais, align_rparen, Space.Space); // )
+ }
+ if (fn_proto.ast.section_expr != 0) {
+ const tok = tree.firstToken(fn_proto.ast.section_expr) - 3;
+ const start = token_starts[tok];
+ if (start < smallest_start) {
+ rparen = tok;
+ smallest_start = start;
}
-
- if (fn_proto.getSectionExpr()) |section_expr| {
- const section_rparen = tree.nextToken(section_expr.lastToken());
- const section_lparen = tree.prevToken(section_expr.firstToken());
- const section_kw = tree.prevToken(section_lparen);
-
- try renderToken(tree, ais, section_kw, Space.None); // section
- try renderToken(tree, ais, section_lparen, Space.None); // (
- try renderExpression(allocator, ais, tree, section_expr, Space.None);
- try renderToken(tree, ais, section_rparen, Space.Space); // )
+ }
+ if (fn_proto.ast.callconv_expr != 0) {
+ const tok = tree.firstToken(fn_proto.ast.callconv_expr) - 3;
+ const start = token_starts[tok];
+ if (start < smallest_start) {
+ rparen = tok;
+ smallest_start = start;
}
-
- if (fn_proto.getCallconvExpr()) |callconv_expr| {
- const callconv_rparen = tree.nextToken(callconv_expr.lastToken());
- const callconv_lparen = tree.prevToken(callconv_expr.firstToken());
- const callconv_kw = tree.prevToken(callconv_lparen);
-
- try renderToken(tree, ais, callconv_kw, Space.None); // callconv
- try renderToken(tree, ais, callconv_lparen, Space.None); // (
- try renderExpression(allocator, ais, tree, callconv_expr, Space.None);
- try renderToken(tree, ais, callconv_rparen, Space.Space); // )
- } else if (fn_proto.getIsExternPrototype() != null) {
- try ais.writer().writeAll("callconv(.C) ");
- } else if (fn_proto.getIsAsync() != null) {
- try ais.writer().writeAll("callconv(.Async) ");
+ }
+ break :blk rparen;
+ };
+ assert(token_tags[rparen] == .r_paren);
+
+ // The params list is a sparse set that does *not* include anytype or ... parameters.
+
+ const trailing_comma = token_tags[rparen - 1] == .comma;
+ if (!trailing_comma and !hasComment(tree, lparen, rparen)) {
+ // Render all on one line, no trailing comma.
+ try renderToken(ais, tree, lparen, .none); // (
+
+ var param_i: usize = 0;
+ var last_param_token = lparen;
+ while (true) {
+ last_param_token += 1;
+ switch (token_tags[last_param_token]) {
+ .doc_comment => {
+ try renderToken(ais, tree, last_param_token, .newline);
+ continue;
+ },
+ .ellipsis3 => {
+ try renderToken(ais, tree, last_param_token, .none); // ...
+ break;
+ },
+ .keyword_noalias, .keyword_comptime => {
+ try renderToken(ais, tree, last_param_token, .space);
+ last_param_token += 1;
+ },
+ .identifier => {},
+ .keyword_anytype => {
+ try renderToken(ais, tree, last_param_token, .none); // anytype
+ continue;
+ },
+ .r_paren => break,
+ .comma => {
+ try renderToken(ais, tree, last_param_token, .space); // ,
+ continue;
+ },
+ else => {}, // Parameter type without a name.
}
-
- switch (fn_proto.return_type) {
- .Explicit => |node| {
- return renderExpression(allocator, ais, tree, node, space);
+ if (token_tags[last_param_token] == .identifier and
+ token_tags[last_param_token + 1] == .colon)
+ {
+ try renderToken(ais, tree, last_param_token, .none); // name
+ last_param_token += 1;
+ try renderToken(ais, tree, last_param_token, .space); // :
+ last_param_token += 1;
+ }
+ if (token_tags[last_param_token] == .keyword_anytype) {
+ try renderToken(ais, tree, last_param_token, .none); // anytype
+ continue;
+ }
+ const param = fn_proto.ast.params[param_i];
+ param_i += 1;
+ try renderExpression(gpa, ais, tree, param, .none);
+ last_param_token = tree.lastToken(param);
+ }
+ } else {
+ // One param per line.
+ ais.pushIndent();
+ try renderToken(ais, tree, lparen, .newline); // (
+
+ var param_i: usize = 0;
+ var last_param_token = lparen;
+ while (true) {
+ last_param_token += 1;
+ switch (token_tags[last_param_token]) {
+ .doc_comment => {
+ try renderToken(ais, tree, last_param_token, .newline);
+ continue;
+ },
+ .ellipsis3 => {
+ try renderToken(ais, tree, last_param_token, .comma); // ...
+ break;
},
- .InferErrorSet => |node| {
- try renderToken(tree, ais, tree.prevToken(node.firstToken()), Space.None); // !
- return renderExpression(allocator, ais, tree, node, space);
+ .keyword_noalias, .keyword_comptime => {
+ try renderToken(ais, tree, last_param_token, .space);
+ last_param_token += 1;
},
- .Invalid => unreachable,
+ .identifier => {},
+ .keyword_anytype => {
+ try renderToken(ais, tree, last_param_token, .comma); // anytype
+ if (token_tags[last_param_token + 1] == .comma)
+ last_param_token += 1;
+ continue;
+ },
+ .r_paren => break,
+ else => {}, // Parameter type without a name.
}
- },
-
- .AnyFrameType => {
- const anyframe_type = @fieldParentPtr(ast.Node.AnyFrameType, "base", base);
+ if (token_tags[last_param_token] == .identifier and
+ token_tags[last_param_token + 1] == .colon)
+ {
+ try renderToken(ais, tree, last_param_token, .none); // name
+ last_param_token += 1;
+ try renderToken(ais, tree, last_param_token, .space); // :
+ last_param_token += 1;
+ }
+ if (token_tags[last_param_token] == .keyword_anytype) {
+ try renderToken(ais, tree, last_param_token, .comma); // anytype
+ if (token_tags[last_param_token + 1] == .comma)
+ last_param_token += 1;
+ continue;
+ }
+ const param = fn_proto.ast.params[param_i];
+ param_i += 1;
+ try renderExpression(gpa, ais, tree, param, .comma);
+ last_param_token = tree.lastToken(param);
+ if (token_tags[last_param_token + 1] == .comma) last_param_token += 1;
+ }
+ ais.popIndent();
+ }
- if (anyframe_type.result) |result| {
- try renderToken(tree, ais, anyframe_type.anyframe_token, Space.None); // anyframe
- try renderToken(tree, ais, result.arrow_token, Space.None); // ->
- return renderExpression(allocator, ais, tree, result.return_type, space);
- } else {
- return renderToken(tree, ais, anyframe_type.anyframe_token, space); // anyframe
- }
- },
+ try renderToken(ais, tree, rparen, .space); // )
- .DocComment => unreachable, // doc comments are attached to nodes
+ if (fn_proto.ast.align_expr != 0) {
+ const align_lparen = tree.firstToken(fn_proto.ast.align_expr) - 1;
+ const align_rparen = tree.lastToken(fn_proto.ast.align_expr) + 1;
- .Switch => {
- const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
+ try renderToken(ais, tree, align_lparen - 1, .none); // align
+ try renderToken(ais, tree, align_lparen, .none); // (
+ try renderExpression(gpa, ais, tree, fn_proto.ast.align_expr, .none);
+ try renderToken(ais, tree, align_rparen, .space); // )
+ }
- try renderToken(tree, ais, switch_node.switch_token, Space.Space); // switch
- try renderToken(tree, ais, tree.nextToken(switch_node.switch_token), Space.None); // (
+ if (fn_proto.ast.section_expr != 0) {
+ const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;
+ const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;
- const rparen = tree.nextToken(switch_node.expr.lastToken());
- const lbrace = tree.nextToken(rparen);
+ try renderToken(ais, tree, section_lparen - 1, .none); // section
+ try renderToken(ais, tree, section_lparen, .none); // (
+ try renderExpression(gpa, ais, tree, fn_proto.ast.section_expr, .none);
+ try renderToken(ais, tree, section_rparen, .space); // )
+ }
- if (switch_node.cases_len == 0) {
- try renderExpression(allocator, ais, tree, switch_node.expr, Space.None);
- try renderToken(tree, ais, rparen, Space.Space); // )
- try renderToken(tree, ais, lbrace, Space.None); // {
- return renderToken(tree, ais, switch_node.rbrace, space); // }
- }
+ if (fn_proto.ast.callconv_expr != 0) {
+ const callconv_lparen = tree.firstToken(fn_proto.ast.callconv_expr) - 1;
+ const callconv_rparen = tree.lastToken(fn_proto.ast.callconv_expr) + 1;
- try renderExpression(allocator, ais, tree, switch_node.expr, Space.None);
- try renderToken(tree, ais, rparen, Space.Space); // )
+ try renderToken(ais, tree, callconv_lparen - 1, .none); // callconv
+ try renderToken(ais, tree, callconv_lparen, .none); // (
+ try renderExpression(gpa, ais, tree, fn_proto.ast.callconv_expr, .none);
+ try renderToken(ais, tree, callconv_rparen, .space); // )
+ } else if (is_inline) {
+ try ais.writer().writeAll("callconv(.Inline) ");
+ }
- {
- ais.pushIndentNextLine();
- defer ais.popIndent();
- try renderToken(tree, ais, lbrace, Space.Newline); // {
+ if (token_tags[maybe_bang] == .bang) {
+ try renderToken(ais, tree, maybe_bang, .none); // !
+ }
+ return renderExpression(gpa, ais, tree, fn_proto.ast.return_type, space);
+}
- const cases = switch_node.cases();
- for (cases) |node, i| {
- try renderExpression(allocator, ais, tree, node, Space.Comma);
+fn renderSwitchCase(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ switch_case: ast.full.SwitchCase,
+ space: Space,
+) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;
+
+ // Render everything before the arrow
+ if (switch_case.ast.values.len == 0) {
+ try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword
+ } else if (switch_case.ast.values.len == 1) {
+ // render on one line and drop the trailing comma if any
+ try renderExpression(gpa, ais, tree, switch_case.ast.values[0], .space);
+ } else if (trailing_comma or
+ hasComment(tree, tree.firstToken(switch_case.ast.values[0]), switch_case.ast.arrow_token))
+ {
+ // Render each value on a new line
+ try renderExpressions(gpa, ais, tree, switch_case.ast.values, .comma);
+ } else {
+ // Render on one line
+ for (switch_case.ast.values) |value_expr| {
+ try renderExpression(gpa, ais, tree, value_expr, .comma_space);
+ }
+ }
- if (i + 1 < cases.len) {
- try renderExtraNewline(tree, ais, cases[i + 1]);
- }
- }
- }
+ // Render the arrow and everything after it
+ try renderToken(ais, tree, switch_case.ast.arrow_token, .space);
- return renderToken(tree, ais, switch_node.rbrace, space); // }
- },
+ if (switch_case.payload_token) |payload_token| {
+ try renderToken(ais, tree, payload_token - 1, .none); // pipe
+ if (token_tags[payload_token] == .asterisk) {
+ try renderToken(ais, tree, payload_token, .none); // asterisk
+ try renderToken(ais, tree, payload_token + 1, .none); // identifier
+ try renderToken(ais, tree, payload_token + 2, .space); // pipe
+ } else {
+ try renderToken(ais, tree, payload_token, .none); // identifier
+ try renderToken(ais, tree, payload_token + 1, .space); // pipe
+ }
+ }
- .SwitchCase => {
- const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
+ try renderExpression(gpa, ais, tree, switch_case.ast.target_expr, space);
+}
- assert(switch_case.items_len != 0);
- const src_has_trailing_comma = blk: {
- const last_node = switch_case.items()[switch_case.items_len - 1];
- const maybe_comma = tree.nextToken(last_node.lastToken());
- break :blk tree.token_ids[maybe_comma] == .Comma;
- };
+fn renderBlock(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ block_node: ast.Node.Index,
+ statements: []const ast.Node.Index,
+ space: Space,
+) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const node_tags = tree.nodes.items(.tag);
+ const nodes_data = tree.nodes.items(.data);
+ const lbrace = tree.nodes.items(.main_token)[block_node];
+
+ if (token_tags[lbrace - 1] == .colon and
+ token_tags[lbrace - 2] == .identifier)
+ {
+ try renderToken(ais, tree, lbrace - 2, .none);
+ try renderToken(ais, tree, lbrace - 1, .space);
+ }
- if (switch_case.items_len == 1 or !src_has_trailing_comma) {
- const items = switch_case.items();
- for (items) |node, i| {
- if (i + 1 < items.len) {
- try renderExpression(allocator, ais, tree, node, Space.None);
-
- const comma_token = tree.nextToken(node.lastToken());
- try renderToken(tree, ais, comma_token, Space.Space); // ,
- try renderExtraNewline(tree, ais, items[i + 1]);
- } else {
- try renderExpression(allocator, ais, tree, node, Space.Space);
- }
- }
- } else {
- const items = switch_case.items();
- for (items) |node, i| {
- if (i + 1 < items.len) {
- try renderExpression(allocator, ais, tree, node, Space.None);
-
- const comma_token = tree.nextToken(node.lastToken());
- try renderToken(tree, ais, comma_token, Space.Newline); // ,
- try renderExtraNewline(tree, ais, items[i + 1]);
- } else {
- try renderExpression(allocator, ais, tree, node, Space.Comma);
- }
- }
+ ais.pushIndentNextLine();
+ if (statements.len == 0) {
+ try renderToken(ais, tree, lbrace, .none);
+ } else {
+ try renderToken(ais, tree, lbrace, .newline);
+ for (statements) |stmt, i| {
+ if (i != 0) try renderExtraNewline(ais, tree, stmt);
+ switch (node_tags[stmt]) {
+ .global_var_decl => try renderVarDecl(gpa, ais, tree, tree.globalVarDecl(stmt)),
+ .local_var_decl => try renderVarDecl(gpa, ais, tree, tree.localVarDecl(stmt)),
+ .simple_var_decl => try renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(stmt)),
+ .aligned_var_decl => try renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(stmt)),
+ else => try renderExpression(gpa, ais, tree, stmt, .semicolon),
}
+ }
+ }
+ ais.popIndent();
- try renderToken(tree, ais, switch_case.arrow_token, Space.Space); // =>
+ try renderToken(ais, tree, tree.lastToken(block_node), space); // rbrace
+}
- if (switch_case.payload) |payload| {
- try renderExpression(allocator, ais, tree, payload, Space.Space);
- }
+fn renderStructInit(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ struct_node: ast.Node.Index,
+ struct_init: ast.full.StructInit,
+ space: Space,
+) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ if (struct_init.ast.type_expr == 0) {
+ try renderToken(ais, tree, struct_init.ast.lbrace - 1, .none); // .
+ } else {
+ try renderExpression(gpa, ais, tree, struct_init.ast.type_expr, .none); // T
+ }
+ if (struct_init.ast.fields.len == 0) {
+ ais.pushIndentNextLine();
+ try renderToken(ais, tree, struct_init.ast.lbrace, .none); // lbrace
+ ais.popIndent();
+ return renderToken(ais, tree, struct_init.ast.lbrace + 1, space); // rbrace
+ }
- return renderExpression(allocator, ais, tree, switch_case.expr, space);
- },
- .SwitchElse => {
- const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
- return renderToken(tree, ais, switch_else.token, space);
- },
- .Else => {
- const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
+ const rbrace = tree.lastToken(struct_node);
+ const trailing_comma = token_tags[rbrace - 1] == .comma;
+ if (trailing_comma or hasComment(tree, struct_init.ast.lbrace, rbrace)) {
+ // Render one field init per line.
+ ais.pushIndentNextLine();
+ try renderToken(ais, tree, struct_init.ast.lbrace, .newline);
+
+ try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // .
+ try renderToken(ais, tree, struct_init.ast.lbrace + 2, .space); // name
+ try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // =
+ try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma);
+
+ for (struct_init.ast.fields[1..]) |field_init| {
+ const init_token = tree.firstToken(field_init);
+ try renderExtraNewlineToken(ais, tree, init_token - 3);
+ try renderToken(ais, tree, init_token - 3, .none); // .
+ try renderToken(ais, tree, init_token - 2, .space); // name
+ try renderToken(ais, tree, init_token - 1, .space); // =
+ try renderExpression(gpa, ais, tree, field_init, .comma);
+ }
- const body_is_block = nodeIsBlock(else_node.body);
- const same_line = body_is_block or tree.tokensOnSameLine(else_node.else_token, else_node.body.lastToken());
+ ais.popIndent();
+ } else {
+ // Render all on one line, no trailing comma.
+ try renderToken(ais, tree, struct_init.ast.lbrace, .space);
+
+ for (struct_init.ast.fields) |field_init| {
+ const init_token = tree.firstToken(field_init);
+ try renderToken(ais, tree, init_token - 3, .none); // .
+ try renderToken(ais, tree, init_token - 2, .space); // name
+ try renderToken(ais, tree, init_token - 1, .space); // =
+ try renderExpression(gpa, ais, tree, field_init, .comma_space);
+ }
+ }
- const after_else_space = if (same_line or else_node.payload != null) Space.Space else Space.Newline;
- try renderToken(tree, ais, else_node.else_token, after_else_space);
+ return renderToken(ais, tree, rbrace, space);
+}
- if (else_node.payload) |payload| {
- const payload_space = if (same_line) Space.Space else Space.Newline;
- try renderExpression(allocator, ais, tree, payload, payload_space);
- }
+// TODO: handle comments between elements
+fn renderArrayInit(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ array_init: ast.full.ArrayInit,
+ space: Space,
+) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const token_starts = tree.tokens.items(.start);
+
+ if (array_init.ast.type_expr == 0) {
+ try renderToken(ais, tree, array_init.ast.lbrace - 1, .none); // .
+ } else {
+ try renderExpression(gpa, ais, tree, array_init.ast.type_expr, .none); // T
+ }
- if (same_line) {
- return renderExpression(allocator, ais, tree, else_node.body, space);
- } else {
- ais.pushIndent();
- defer ais.popIndent();
- return renderExpression(allocator, ais, tree, else_node.body, space);
- }
- },
+ if (array_init.ast.elements.len == 0) {
+ ais.pushIndentNextLine();
+ try renderToken(ais, tree, array_init.ast.lbrace, .none); // lbrace
+ ais.popIndent();
+ return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace
+ }
- .While => {
- const while_node = @fieldParentPtr(ast.Node.While, "base", base);
+ const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1];
+ const last_elem_token = tree.lastToken(last_elem);
+ const trailing_comma = token_tags[last_elem_token + 1] == .comma;
+ const rbrace = if (trailing_comma) last_elem_token + 2 else last_elem_token + 1;
+ assert(token_tags[rbrace] == .r_brace);
+
+ if (array_init.ast.elements.len == 1) {
+ const only_elem = array_init.ast.elements[0];
+ const first_token = tree.firstToken(only_elem);
+ if (token_tags[first_token] != .multiline_string_literal_line and
+ !anythingBetween(tree, last_elem_token, rbrace))
+ {
+ try renderToken(ais, tree, array_init.ast.lbrace, .none);
+ try renderExpression(gpa, ais, tree, only_elem, .none);
+ return renderToken(ais, tree, rbrace, space);
+ }
+ }
- if (while_node.label) |label| {
- try renderToken(tree, ais, label, Space.None); // label
- try renderToken(tree, ais, tree.nextToken(label), Space.Space); // :
- }
+ const contains_newlines = !tree.tokensOnSameLine(array_init.ast.lbrace, rbrace);
- if (while_node.inline_token) |inline_token| {
- try renderToken(tree, ais, inline_token, Space.Space); // inline
+ if (!trailing_comma and !contains_newlines) {
+ // Render all on one line, no trailing comma.
+ if (array_init.ast.elements.len == 1) {
+ // If there is only one element, we don't use spaces
+ try renderToken(ais, tree, array_init.ast.lbrace, .none);
+ try renderExpression(gpa, ais, tree, array_init.ast.elements[0], .none);
+ } else {
+ try renderToken(ais, tree, array_init.ast.lbrace, .space);
+ for (array_init.ast.elements) |elem| {
+ try renderExpression(gpa, ais, tree, elem, .comma_space);
}
+ }
+ return renderToken(ais, tree, last_elem_token + 1, space); // rbrace
+ }
- try renderToken(tree, ais, while_node.while_token, Space.Space); // while
- try renderToken(tree, ais, tree.nextToken(while_node.while_token), Space.None); // (
- try renderExpression(allocator, ais, tree, while_node.condition, Space.None);
+ ais.pushIndentNextLine();
+ try renderToken(ais, tree, array_init.ast.lbrace, .newline);
+
+ var expr_index: usize = 0;
+ while (rowSize(tree, array_init.ast.elements[expr_index..], rbrace)) |row_size| {
+ const row_exprs = array_init.ast.elements[expr_index..];
+ // A place to store the width of each expression and its column's maximum
+ const widths = try gpa.alloc(usize, row_exprs.len + row_size);
+ defer gpa.free(widths);
+ mem.set(usize, widths, 0);
+
+ const expr_newlines = try gpa.alloc(bool, row_exprs.len);
+ defer gpa.free(expr_newlines);
+ mem.set(bool, expr_newlines, false);
+
+ const expr_widths = widths[0..row_exprs.len];
+ const column_widths = widths[row_exprs.len..];
+
+ // Find next row with trailing comment (if any) to end the current section.
+ const section_end = sec_end: {
+ var this_line_first_expr: usize = 0;
+ var this_line_size = rowSize(tree, row_exprs, rbrace);
+ for (row_exprs) |expr, i| {
+ // Ignore comment on first line of this section.
+ if (i == 0) continue;
+ const expr_last_token = tree.lastToken(expr);
+ if (tree.tokensOnSameLine(tree.firstToken(row_exprs[0]), expr_last_token))
+ continue;
+ // Track start of line containing comment.
+ if (!tree.tokensOnSameLine(tree.firstToken(row_exprs[this_line_first_expr]), expr_last_token)) {
+ this_line_first_expr = i;
+ this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rbrace);
+ }
- const cond_rparen = tree.nextToken(while_node.condition.lastToken());
+ const maybe_comma = expr_last_token + 1;
+ if (token_tags[maybe_comma] == .comma) {
+ if (hasSameLineComment(tree, maybe_comma))
+ break :sec_end i - this_line_size.? + 1;
+ }
+ }
+ break :sec_end row_exprs.len;
+ };
+ expr_index += section_end;
- const body_is_block = nodeIsBlock(while_node.body);
+ const section_exprs = row_exprs[0..section_end];
- var block_start_space: Space = undefined;
- var after_body_space: Space = undefined;
+ var sub_expr_buffer = std.ArrayList(u8).init(gpa);
+ defer sub_expr_buffer.deinit();
- if (body_is_block) {
- block_start_space = Space.BlockStart;
- after_body_space = if (while_node.@"else" == null) space else Space.SpaceOrOutdent;
- } else if (tree.tokensOnSameLine(cond_rparen, while_node.body.lastToken())) {
- block_start_space = Space.Space;
- after_body_space = if (while_node.@"else" == null) space else Space.Space;
+ var auto_indenting_stream = Ais{
+ .indent_delta = indent_delta,
+ .underlying_writer = sub_expr_buffer.writer(),
+ };
+
+ // Calculate size of columns in current section
+ var column_counter: usize = 0;
+ var single_line = true;
+ var contains_newline = false;
+ for (section_exprs) |expr, i| {
+ sub_expr_buffer.shrinkRetainingCapacity(0);
+ if (i + 1 < section_exprs.len) {
+ try renderExpression(gpa, &auto_indenting_stream, tree, expr, .none);
+ const width = sub_expr_buffer.items.len;
+ const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items, '\n') != null;
+ contains_newline = contains_newline or this_contains_newline;
+ expr_widths[i] = width;
+ expr_newlines[i] = this_contains_newline;
+
+ if (!this_contains_newline) {
+ const column = column_counter % row_size;
+ column_widths[column] = std.math.max(column_widths[column], width);
+
+ const expr_last_token = tree.lastToken(expr) + 1;
+ const next_expr = section_exprs[i + 1];
+ column_counter += 1;
+ if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(next_expr))) single_line = false;
+ } else {
+ single_line = false;
+ column_counter = 0;
+ }
} else {
- block_start_space = Space.Newline;
- after_body_space = if (while_node.@"else" == null) space else Space.Newline;
+ try renderExpression(gpa, &auto_indenting_stream, tree, expr, .none);
+ const width = sub_expr_buffer.items.len;
+ contains_newline = contains_newline or mem.indexOfScalar(u8, sub_expr_buffer.items, '\n') != null;
+ expr_widths[i] = width;
+ expr_newlines[i] = contains_newline;
+
+ if (!contains_newline) {
+ const column = column_counter % row_size;
+ column_widths[column] = std.math.max(column_widths[column], width);
+ }
+ break;
}
+ }
- {
- const rparen_space = if (while_node.payload != null or while_node.continue_expr != null) Space.Space else block_start_space;
- try renderToken(tree, ais, cond_rparen, rparen_space); // )
- }
+ // Render exprs in current section.
+ column_counter = 0;
+ var last_col_index: usize = row_size - 1;
+ for (section_exprs) |expr, i| {
+ if (i + 1 < section_exprs.len) {
+ const next_expr = section_exprs[i + 1];
+ try renderExpression(gpa, ais, tree, expr, .none);
+
+ const comma = tree.lastToken(expr) + 1;
+
+ if (column_counter != last_col_index) {
+ if (!expr_newlines[i] and !expr_newlines[i + 1]) {
+ // Neither the current or next expression is multiline
+ try renderToken(ais, tree, comma, .space); // ,
+ assert(column_widths[column_counter % row_size] >= expr_widths[i]);
+ const padding = column_widths[column_counter % row_size] - expr_widths[i];
+ try ais.writer().writeByteNTimes(' ', padding);
+
+ column_counter += 1;
+ continue;
+ }
+ }
+ if (single_line and row_size != 1) {
+ try renderToken(ais, tree, comma, .space); // ,
+ continue;
+ }
- if (while_node.payload) |payload| {
- const payload_space = if (while_node.continue_expr != null) Space.Space else block_start_space;
- try renderExpression(allocator, ais, tree, payload, payload_space);
+ column_counter = 0;
+ try renderToken(ais, tree, comma, .newline); // ,
+ try renderExtraNewline(ais, tree, next_expr);
+ } else {
+ try renderExpression(gpa, ais, tree, expr, .comma); // ,
}
+ }
- if (while_node.continue_expr) |continue_expr| {
- const rparen = tree.nextToken(continue_expr.lastToken());
- const lparen = tree.prevToken(continue_expr.firstToken());
- const colon = tree.prevToken(lparen);
-
- try renderToken(tree, ais, colon, Space.Space); // :
- try renderToken(tree, ais, lparen, Space.None); // (
-
- try renderExpression(allocator, ais, tree, continue_expr, Space.None);
+ if (expr_index == array_init.ast.elements.len)
+ break;
+ }
- try renderToken(tree, ais, rparen, block_start_space); // )
- }
+ ais.popIndent();
+ return renderToken(ais, tree, rbrace, space); // rbrace
+}
- {
- if (!body_is_block) ais.pushIndent();
- defer if (!body_is_block) ais.popIndent();
- try renderExpression(allocator, ais, tree, while_node.body, after_body_space);
- }
+fn renderContainerDecl(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ container_decl_node: ast.Node.Index,
+ container_decl: ast.full.ContainerDecl,
+ space: Space,
+) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const node_tags = tree.nodes.items(.tag);
- if (while_node.@"else") |@"else"| {
- return renderExpression(allocator, ais, tree, &@"else".base, space);
- }
- },
+ if (container_decl.layout_token) |layout_token| {
+ try renderToken(ais, tree, layout_token, .space);
+ }
- .For => {
- const for_node = @fieldParentPtr(ast.Node.For, "base", base);
+ var lbrace: ast.TokenIndex = undefined;
+ if (container_decl.ast.enum_token) |enum_token| {
+ try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
+ try renderToken(ais, tree, enum_token - 1, .none); // lparen
+ try renderToken(ais, tree, enum_token, .none); // enum
+ if (container_decl.ast.arg != 0) {
+ try renderToken(ais, tree, enum_token + 1, .none); // lparen
+ try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none);
+ const rparen = tree.lastToken(container_decl.ast.arg) + 1;
+ try renderToken(ais, tree, rparen, .none); // rparen
+ try renderToken(ais, tree, rparen + 1, .space); // rparen
+ lbrace = rparen + 2;
+ } else {
+ try renderToken(ais, tree, enum_token + 1, .space); // rparen
+ lbrace = enum_token + 2;
+ }
+ } else if (container_decl.ast.arg != 0) {
+ try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
+ try renderToken(ais, tree, container_decl.ast.main_token + 1, .none); // lparen
+ try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none);
+ const rparen = tree.lastToken(container_decl.ast.arg) + 1;
+ try renderToken(ais, tree, rparen, .space); // rparen
+ lbrace = rparen + 1;
+ } else {
+ try renderToken(ais, tree, container_decl.ast.main_token, .space); // union
+ lbrace = container_decl.ast.main_token + 1;
+ }
- if (for_node.label) |label| {
- try renderToken(tree, ais, label, Space.None); // label
- try renderToken(tree, ais, tree.nextToken(label), Space.Space); // :
- }
+ const rbrace = tree.lastToken(container_decl_node);
+ if (container_decl.ast.members.len == 0) {
+ ais.pushIndentNextLine();
+ if (token_tags[lbrace + 1] == .container_doc_comment) {
+ try renderToken(ais, tree, lbrace, .newline); // lbrace
+ try renderContainerDocComments(ais, tree, lbrace + 1);
+ } else {
+ try renderToken(ais, tree, lbrace, .none); // lbrace
+ }
+ ais.popIndent();
+ return renderToken(ais, tree, rbrace, space); // rbrace
+ }
- if (for_node.inline_token) |inline_token| {
- try renderToken(tree, ais, inline_token, Space.Space); // inline
- }
+ const src_has_trailing_comma = token_tags[rbrace - 1] == .comma;
+ if (!src_has_trailing_comma) one_line: {
+ // We can only print all the members in-line if all the members are fields.
+ for (container_decl.ast.members) |member| {
+ if (!node_tags[member].isContainerField()) break :one_line;
+ }
+ // All the declarations on the same line.
+ try renderToken(ais, tree, lbrace, .space); // lbrace
+ for (container_decl.ast.members) |member| {
+ try renderMember(gpa, ais, tree, member, .space);
+ }
+ return renderToken(ais, tree, rbrace, space); // rbrace
+ }
- try renderToken(tree, ais, for_node.for_token, Space.Space); // for
- try renderToken(tree, ais, tree.nextToken(for_node.for_token), Space.None); // (
- try renderExpression(allocator, ais, tree, for_node.array_expr, Space.None);
+ // One member per line.
+ ais.pushIndentNextLine();
+ try renderToken(ais, tree, lbrace, .newline); // lbrace
+ if (token_tags[lbrace + 1] == .container_doc_comment) {
+ try renderContainerDocComments(ais, tree, lbrace + 1);
+ }
+ try renderMembers(gpa, ais, tree, container_decl.ast.members);
+ ais.popIndent();
- const rparen = tree.nextToken(for_node.array_expr.lastToken());
+ return renderToken(ais, tree, rbrace, space); // rbrace
+}
- const body_is_block = for_node.body.tag.isBlock();
- const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());
- const body_on_same_line = body_is_block or src_one_line_to_body;
+fn renderAsm(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ asm_node: ast.full.Asm,
+ space: Space,
+) Error!void {
+ const token_tags = tree.tokens.items(.tag);
- try renderToken(tree, ais, rparen, Space.Space); // )
+ try renderToken(ais, tree, asm_node.ast.asm_token, .space); // asm
- const space_after_payload = if (body_on_same_line) Space.Space else Space.Newline;
- try renderExpression(allocator, ais, tree, for_node.payload, space_after_payload); // |x|
+ if (asm_node.volatile_token) |volatile_token| {
+ try renderToken(ais, tree, volatile_token, .space); // volatile
+ try renderToken(ais, tree, volatile_token + 1, .none); // lparen
+ } else {
+ try renderToken(ais, tree, asm_node.ast.asm_token + 1, .none); // lparen
+ }
- const space_after_body = blk: {
- if (for_node.@"else") |@"else"| {
- const src_one_line_to_else = tree.tokensOnSameLine(rparen, @"else".firstToken());
- if (body_is_block or src_one_line_to_else) {
- break :blk Space.Space;
- } else {
- break :blk Space.Newline;
- }
- } else {
- break :blk space;
+ if (asm_node.ast.items.len == 0) {
+ ais.pushIndent();
+ if (asm_node.first_clobber) |first_clobber| {
+ // asm ("foo" ::: "a", "b")
+ // asm ("foo" ::: "a", "b",)
+ try renderExpression(gpa, ais, tree, asm_node.ast.template, .space);
+ // Render the three colons.
+ try renderToken(ais, tree, first_clobber - 3, .none);
+ try renderToken(ais, tree, first_clobber - 2, .none);
+ try renderToken(ais, tree, first_clobber - 1, .space);
+
+ var tok_i = first_clobber;
+ while (true) : (tok_i += 1) {
+ try renderToken(ais, tree, tok_i, .none);
+ tok_i += 1;
+ switch (token_tags[tok_i]) {
+ .r_paren => {
+ ais.popIndent();
+ return renderToken(ais, tree, tok_i, space);
+ },
+ .comma => {
+ if (token_tags[tok_i + 1] == .r_paren) {
+ ais.popIndent();
+ return renderToken(ais, tree, tok_i + 1, space);
+ } else {
+ try renderToken(ais, tree, tok_i, .space);
+ }
+ },
+ else => unreachable,
}
- };
-
- {
- if (!body_on_same_line) ais.pushIndent();
- defer if (!body_on_same_line) ais.popIndent();
- try renderExpression(allocator, ais, tree, for_node.body, space_after_body); // { body }
}
+ } else {
+ // asm ("foo")
+ try renderExpression(gpa, ais, tree, asm_node.ast.template, .none);
+ ais.popIndent();
+ return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen
+ }
+ }
- if (for_node.@"else") |@"else"| {
- return renderExpression(allocator, ais, tree, &@"else".base, space); // else
+ ais.pushIndent();
+ try renderExpression(gpa, ais, tree, asm_node.ast.template, .newline);
+ ais.setIndentDelta(asm_indent_delta);
+ const colon1 = tree.lastToken(asm_node.ast.template) + 1;
+
+ const colon2 = if (asm_node.outputs.len == 0) colon2: {
+ try renderToken(ais, tree, colon1, .newline); // :
+ break :colon2 colon1 + 1;
+ } else colon2: {
+ try renderToken(ais, tree, colon1, .space); // :
+
+ ais.pushIndent();
+ for (asm_node.outputs) |asm_output, i| {
+ if (i + 1 < asm_node.outputs.len) {
+ const next_asm_output = asm_node.outputs[i + 1];
+ try renderAsmOutput(gpa, ais, tree, asm_output, .none);
+
+ const comma = tree.firstToken(next_asm_output) - 1;
+ try renderToken(ais, tree, comma, .newline); // ,
+ try renderExtraNewlineToken(ais, tree, tree.firstToken(next_asm_output));
+ } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) {
+ try renderAsmOutput(gpa, ais, tree, asm_output, .newline);
+ ais.popIndent();
+ ais.setIndentDelta(indent_delta);
+ ais.popIndent();
+ return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen
+ } else {
+ try renderAsmOutput(gpa, ais, tree, asm_output, .newline);
+ const comma_or_colon = tree.lastToken(asm_output) + 1;
+ ais.popIndent();
+ break :colon2 switch (token_tags[comma_or_colon]) {
+ .comma => comma_or_colon + 1,
+ else => comma_or_colon,
+ };
}
- },
-
- .If => {
- const if_node = @fieldParentPtr(ast.Node.If, "base", base);
-
- const lparen = tree.nextToken(if_node.if_token);
- const rparen = tree.nextToken(if_node.condition.lastToken());
-
- try renderToken(tree, ais, if_node.if_token, Space.Space); // if
- try renderToken(tree, ais, lparen, Space.None); // (
-
- try renderExpression(allocator, ais, tree, if_node.condition, Space.None); // condition
-
- const body_is_if_block = if_node.body.tag == .If;
- const body_is_block = nodeIsBlock(if_node.body);
-
- if (body_is_if_block) {
- try renderExtraNewline(tree, ais, if_node.body);
- } else if (body_is_block) {
- const after_rparen_space = if (if_node.payload == null) Space.BlockStart else Space.Space;
- try renderToken(tree, ais, rparen, after_rparen_space); // )
-
- if (if_node.payload) |payload| {
- try renderExpression(allocator, ais, tree, payload, Space.BlockStart); // |x|
- }
+ } else unreachable;
+ };
- if (if_node.@"else") |@"else"| {
- try renderExpression(allocator, ais, tree, if_node.body, Space.SpaceOrOutdent);
- return renderExpression(allocator, ais, tree, &@"else".base, space);
- } else {
- return renderExpression(allocator, ais, tree, if_node.body, space);
- }
+ const colon3 = if (asm_node.inputs.len == 0) colon3: {
+ try renderToken(ais, tree, colon2, .newline); // :
+ break :colon3 colon2 + 1;
+ } else colon3: {
+ try renderToken(ais, tree, colon2, .space); // :
+ ais.pushIndent();
+ for (asm_node.inputs) |asm_input, i| {
+ if (i + 1 < asm_node.inputs.len) {
+ const next_asm_input = asm_node.inputs[i + 1];
+ try renderAsmInput(gpa, ais, tree, asm_input, .none);
+
+ const first_token = tree.firstToken(next_asm_input);
+ try renderToken(ais, tree, first_token - 1, .newline); // ,
+ try renderExtraNewlineToken(ais, tree, first_token);
+ } else if (asm_node.first_clobber == null) {
+ try renderAsmInput(gpa, ais, tree, asm_input, .newline);
+ ais.popIndent();
+ ais.setIndentDelta(indent_delta);
+ ais.popIndent();
+ return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen
+ } else {
+ try renderAsmInput(gpa, ais, tree, asm_input, .newline);
+ const comma_or_colon = tree.lastToken(asm_input) + 1;
+ ais.popIndent();
+ break :colon3 switch (token_tags[comma_or_colon]) {
+ .comma => comma_or_colon + 1,
+ else => comma_or_colon,
+ };
}
+ }
+ unreachable;
+ };
- const src_has_newline = !tree.tokensOnSameLine(rparen, if_node.body.lastToken());
-
- if (src_has_newline) {
- const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space;
+ try renderToken(ais, tree, colon3, .space); // :
+ const first_clobber = asm_node.first_clobber.?;
+ var tok_i = first_clobber;
+ while (true) {
+ switch (token_tags[tok_i + 1]) {
+ .r_paren => {
+ ais.setIndentDelta(indent_delta);
+ ais.popIndent();
+ try renderToken(ais, tree, tok_i, .newline);
+ return renderToken(ais, tree, tok_i + 1, space);
+ },
+ .comma => {
+ try renderToken(ais, tree, tok_i, .none);
+ try renderToken(ais, tree, tok_i + 1, .space);
+ tok_i += 2;
+ },
+ else => unreachable,
+ }
+ } else unreachable; // TODO shouldn't need this on while(true)
+}
- {
- ais.pushIndent();
- defer ais.popIndent();
- try renderToken(tree, ais, rparen, after_rparen_space); // )
- }
+fn renderCall(
+ gpa: *Allocator,
+ ais: *Ais,
+ tree: ast.Tree,
+ call: ast.full.Call,
+ space: Space,
+) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const main_tokens = tree.nodes.items(.main_token);
- if (if_node.payload) |payload| {
- try renderExpression(allocator, ais, tree, payload, Space.Newline);
- }
+ if (call.async_token) |async_token| {
+ try renderToken(ais, tree, async_token, .space);
+ }
+ try renderExpression(gpa, ais, tree, call.ast.fn_expr, .none);
+
+ const lparen = call.ast.lparen;
+ const params = call.ast.params;
+ if (params.len == 0) {
+ ais.pushIndentNextLine();
+ try renderToken(ais, tree, lparen, .none);
+ ais.popIndent();
+ return renderToken(ais, tree, lparen + 1, space); // )
+ }
- if (if_node.@"else") |@"else"| {
- const else_is_block = nodeIsBlock(@"else".body);
+ const last_param = params[params.len - 1];
+ const after_last_param_tok = tree.lastToken(last_param) + 1;
+ if (token_tags[after_last_param_tok] == .comma) {
+ ais.pushIndentNextLine();
+ try renderToken(ais, tree, lparen, .newline); // (
+ for (params) |param_node, i| {
+ if (i + 1 < params.len) {
+ try renderExpression(gpa, ais, tree, param_node, .none);
- {
- ais.pushIndent();
- defer ais.popIndent();
- try renderExpression(allocator, ais, tree, if_node.body, Space.Newline);
- }
+ // Unindent the comma for multiline string literals.
+ const is_multiline_string =
+ token_tags[tree.firstToken(param_node)] == .multiline_string_literal_line;
+ if (is_multiline_string) ais.popIndent();
- if (else_is_block) {
- try renderToken(tree, ais, @"else".else_token, Space.Space); // else
+ const comma = tree.lastToken(param_node) + 1;
+ try renderToken(ais, tree, comma, .newline); // ,
- if (@"else".payload) |payload| {
- try renderExpression(allocator, ais, tree, payload, Space.Space);
- }
+ if (is_multiline_string) ais.pushIndent();
- return renderExpression(allocator, ais, tree, @"else".body, space);
- } else {
- const after_else_space = if (@"else".payload == null) Space.Newline else Space.Space;
- try renderToken(tree, ais, @"else".else_token, after_else_space); // else
+ try renderExtraNewline(ais, tree, params[i + 1]);
+ } else {
+ try renderExpression(gpa, ais, tree, param_node, .comma);
+ }
+ }
+ ais.popIndent();
+ return renderToken(ais, tree, after_last_param_tok + 1, space); // )
+ }
- if (@"else".payload) |payload| {
- try renderExpression(allocator, ais, tree, payload, Space.Newline);
- }
+ try renderToken(ais, tree, lparen, .none); // (
- ais.pushIndent();
- defer ais.popIndent();
- return renderExpression(allocator, ais, tree, @"else".body, space);
- }
- } else {
- ais.pushIndent();
- defer ais.popIndent();
- return renderExpression(allocator, ais, tree, if_node.body, space);
- }
- }
+ for (params) |param_node, i| {
+ const first_param_token = tree.firstToken(param_node);
+ if (token_tags[first_param_token] == .multiline_string_literal_line or
+ hasSameLineComment(tree, first_param_token - 1))
+ {
+ ais.pushIndentOneShot();
+ }
+ try renderExpression(gpa, ais, tree, param_node, .none);
+
+ if (i + 1 < params.len) {
+ const comma = tree.lastToken(param_node) + 1;
+ const next_multiline_string =
+ token_tags[tree.firstToken(params[i + 1])] == .multiline_string_literal_line;
+ const comma_space: Space = if (next_multiline_string) .none else .space;
+ try renderToken(ais, tree, comma, comma_space);
+ }
+ }
- // Single line if statement
+ return renderToken(ais, tree, after_last_param_tok, space); // )
+}
- try renderToken(tree, ais, rparen, Space.Space); // )
+/// Renders the given expression indented, popping the indent before rendering
+/// any following line comments
+fn renderExpressionIndented(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
+ const token_starts = tree.tokens.items(.start);
+ const token_tags = tree.tokens.items(.tag);
- if (if_node.payload) |payload| {
- try renderExpression(allocator, ais, tree, payload, Space.Space);
- }
+ ais.pushIndent();
- if (if_node.@"else") |@"else"| {
- try renderExpression(allocator, ais, tree, if_node.body, Space.Space);
- try renderToken(tree, ais, @"else".else_token, Space.Space);
+ var last_token = tree.lastToken(node);
+ const punctuation = switch (space) {
+ .none, .space, .newline, .skip => false,
+ .comma => true,
+ .comma_space => token_tags[last_token + 1] == .comma,
+ .semicolon => token_tags[last_token + 1] == .semicolon,
+ };
- if (@"else".payload) |payload| {
- try renderExpression(allocator, ais, tree, payload, Space.Space);
- }
+ try renderExpression(gpa, ais, tree, node, if (punctuation) .none else .skip);
- return renderExpression(allocator, ais, tree, @"else".body, space);
+ switch (space) {
+ .none, .space, .newline, .skip => {},
+ .comma => {
+ if (token_tags[last_token + 1] == .comma) {
+ try renderToken(ais, tree, last_token + 1, .skip);
+ last_token += 1;
} else {
- return renderExpression(allocator, ais, tree, if_node.body, space);
+ try ais.writer().writeByte(',');
}
},
+ .comma_space => if (token_tags[last_token + 1] == .comma) {
+ try renderToken(ais, tree, last_token + 1, .skip);
+ last_token += 1;
+ },
+ .semicolon => if (token_tags[last_token + 1] == .semicolon) {
+ try renderToken(ais, tree, last_token + 1, .skip);
+ last_token += 1;
+ },
+ }
- .Asm => {
- const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
-
- try renderToken(tree, ais, asm_node.asm_token, Space.Space); // asm
-
- if (asm_node.volatile_token) |volatile_token| {
- try renderToken(tree, ais, volatile_token, Space.Space); // volatile
- try renderToken(tree, ais, tree.nextToken(volatile_token), Space.None); // (
- } else {
- try renderToken(tree, ais, tree.nextToken(asm_node.asm_token), Space.None); // (
- }
+ ais.popIndent();
- asmblk: {
- ais.pushIndent();
- defer ais.popIndent();
+ if (space == .skip) return;
- if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
- try renderExpression(allocator, ais, tree, asm_node.template, Space.None);
- break :asmblk;
- }
+ const comment_start = token_starts[last_token] + tokenSliceForRender(tree, last_token).len;
+ const comment = try renderComments(ais, tree, comment_start, token_starts[last_token + 1]);
- try renderExpression(allocator, ais, tree, asm_node.template, Space.Newline);
+ if (!comment) switch (space) {
+ .none => {},
+ .space,
+ .comma_space,
+ => try ais.writer().writeByte(' '),
+ .newline,
+ .comma,
+ .semicolon,
+ => try ais.insertNewline(),
+ .skip => unreachable,
+ };
+}
- ais.setIndentDelta(asm_indent_delta);
- defer ais.setIndentDelta(indent_delta);
+/// Render an expression, and the comma that follows it, if it is present in the source.
+fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const maybe_comma = tree.lastToken(node) + 1;
+ if (token_tags[maybe_comma] == .comma) {
+ try renderExpression(gpa, ais, tree, node, .none);
+ return renderToken(ais, tree, maybe_comma, space);
+ } else {
+ return renderExpression(gpa, ais, tree, node, space);
+ }
+}
- const colon1 = tree.nextToken(asm_node.template.lastToken());
+fn renderTokenComma(ais: *Ais, tree: ast.Tree, token: ast.TokenIndex, space: Space) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const maybe_comma = token + 1;
+ if (token_tags[maybe_comma] == .comma) {
+ try renderToken(ais, tree, token, .none);
+ return renderToken(ais, tree, maybe_comma, space);
+ } else {
+ return renderToken(ais, tree, token, space);
+ }
+}
- const colon2 = if (asm_node.outputs.len == 0) blk: {
- try renderToken(tree, ais, colon1, Space.Newline); // :
+const Space = enum {
+ /// Output the token lexeme only.
+ none,
+ /// Output the token lexeme followed by a single space.
+ space,
+ /// Output the token lexeme followed by a newline.
+ newline,
+ /// If the next token is a comma, render it as well. If not, insert one.
+ /// In either case, a newline will be inserted afterwards.
+ comma,
+ /// Additionally consume the next token if it is a comma.
+ /// In either case, a space will be inserted afterwards.
+ comma_space,
+ /// Additionally consume the next token if it is a semicolon.
+ /// In either case, a newline will be inserted afterwards.
+ semicolon,
+ /// Skip rendering whitespace and comments. If this is used, the caller
+ /// *must* handle handle whitespace and comments manually.
+ skip,
+};
- break :blk tree.nextToken(colon1);
- } else blk: {
- try renderToken(tree, ais, colon1, Space.Space); // :
+fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Space) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const token_starts = tree.tokens.items(.start);
- ais.pushIndent();
- defer ais.popIndent();
+ const token_start = token_starts[token_index];
+ const lexeme = tokenSliceForRender(tree, token_index);
- for (asm_node.outputs) |*asm_output, i| {
- if (i + 1 < asm_node.outputs.len) {
- const next_asm_output = asm_node.outputs[i + 1];
- try renderAsmOutput(allocator, ais, tree, asm_output, Space.None);
+ try ais.writer().writeAll(lexeme);
- const comma = tree.prevToken(next_asm_output.firstToken());
- try renderToken(tree, ais, comma, Space.Newline); // ,
- try renderExtraNewlineToken(tree, ais, next_asm_output.firstToken());
- } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
- try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline);
- break :asmblk;
- } else {
- try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline);
- const comma_or_colon = tree.nextToken(asm_output.lastToken());
- break :blk switch (tree.token_ids[comma_or_colon]) {
- .Comma => tree.nextToken(comma_or_colon),
- else => comma_or_colon,
- };
- }
- }
- unreachable;
- };
+ if (space == .skip) return;
- const colon3 = if (asm_node.inputs.len == 0) blk: {
- try renderToken(tree, ais, colon2, Space.Newline); // :
- break :blk tree.nextToken(colon2);
- } else blk: {
- try renderToken(tree, ais, colon2, Space.Space); // :
- ais.pushIndent();
- defer ais.popIndent();
- for (asm_node.inputs) |*asm_input, i| {
- if (i + 1 < asm_node.inputs.len) {
- const next_asm_input = &asm_node.inputs[i + 1];
- try renderAsmInput(allocator, ais, tree, asm_input, Space.None);
-
- const comma = tree.prevToken(next_asm_input.firstToken());
- try renderToken(tree, ais, comma, Space.Newline); // ,
- try renderExtraNewlineToken(tree, ais, next_asm_input.firstToken());
- } else if (asm_node.clobbers.len == 0) {
- try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline);
- break :asmblk;
- } else {
- try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline);
- const comma_or_colon = tree.nextToken(asm_input.lastToken());
- break :blk switch (tree.token_ids[comma_or_colon]) {
- .Comma => tree.nextToken(comma_or_colon),
- else => comma_or_colon,
- };
- }
- }
- unreachable;
- };
+ if (space == .comma and token_tags[token_index + 1] != .comma) {
+ try ais.writer().writeByte(',');
+ }
- try renderToken(tree, ais, colon3, Space.Space); // :
- ais.pushIndent();
- defer ais.popIndent();
- for (asm_node.clobbers) |clobber_node, i| {
- if (i + 1 >= asm_node.clobbers.len) {
- try renderExpression(allocator, ais, tree, clobber_node, Space.Newline);
- break :asmblk;
- } else {
- try renderExpression(allocator, ais, tree, clobber_node, Space.None);
- const comma = tree.nextToken(clobber_node.lastToken());
- try renderToken(tree, ais, comma, Space.Space); // ,
- }
- }
- }
+ const comment = try renderComments(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]);
+ switch (space) {
+ .none => {},
+ .space => if (!comment) try ais.writer().writeByte(' '),
+ .newline => if (!comment) try ais.insertNewline(),
- return renderToken(tree, ais, asm_node.rparen, space);
+ .comma => if (token_tags[token_index + 1] == .comma) {
+ try renderToken(ais, tree, token_index + 1, .newline);
+ } else if (!comment) {
+ try ais.insertNewline();
},
- .EnumLiteral => {
- const enum_literal = @fieldParentPtr(ast.Node.EnumLiteral, "base", base);
+ .comma_space => if (token_tags[token_index + 1] == .comma) {
+ try renderToken(ais, tree, token_index + 1, .space);
+ } else if (!comment) {
+ try ais.writer().writeByte(' ');
+ },
- try renderToken(tree, ais, enum_literal.dot, Space.None); // .
- return renderToken(tree, ais, enum_literal.name, space); // name
+ .semicolon => if (token_tags[token_index + 1] == .semicolon) {
+ try renderToken(ais, tree, token_index + 1, .newline);
+ } else if (!comment) {
+ try ais.insertNewline();
},
- .ContainerField,
- .Root,
- .VarDecl,
- .Use,
- .TestDecl,
- => unreachable,
+ .skip => unreachable,
}
}
-fn renderArrayType(
- allocator: *mem.Allocator,
- ais: anytype,
- tree: *ast.Tree,
- lbracket: ast.TokenIndex,
- rhs: *ast.Node,
- len_expr: *ast.Node,
- opt_sentinel: ?*ast.Node,
- space: Space,
-) (@TypeOf(ais.*).Error || Error)!void {
- const rbracket = tree.nextToken(if (opt_sentinel) |sentinel|
- sentinel.lastToken()
- else
- len_expr.lastToken());
+/// Returns true if there exists a comment between the start of token
+/// `start_token` and the start of token `end_token`. This is used to determine
+/// if e.g. a fn_proto should be wrapped and have a trailing comma inserted
+/// even if there is none in the source.
+fn hasComment(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool {
+ const token_starts = tree.tokens.items(.start);
- const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
- const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
- const new_space = if (ends_with_comment) Space.Newline else Space.None;
- {
- const do_indent = (starts_with_comment or ends_with_comment);
- if (do_indent) ais.pushIndent();
- defer if (do_indent) ais.popIndent();
-
- try renderToken(tree, ais, lbracket, Space.None); // [
- try renderExpression(allocator, ais, tree, len_expr, new_space);
+ const start = token_starts[start_token];
+ const end = token_starts[end_token];
- if (starts_with_comment) {
- try ais.maybeInsertNewline();
- }
- if (opt_sentinel) |sentinel| {
- const colon_token = tree.prevToken(sentinel.firstToken());
- try renderToken(tree, ais, colon_token, Space.None); // :
- try renderExpression(allocator, ais, tree, sentinel, Space.None);
- }
- if (starts_with_comment) {
- try ais.maybeInsertNewline();
- }
- }
- try renderToken(tree, ais, rbracket, Space.None); // ]
-
- return renderExpression(allocator, ais, tree, rhs, space);
+ return mem.indexOf(u8, tree.source[start..end], "//") != null;
}
-fn renderAsmOutput(
- allocator: *mem.Allocator,
- ais: anytype,
- tree: *ast.Tree,
- asm_output: *const ast.Node.Asm.Output,
- space: Space,
-) (@TypeOf(ais.*).Error || Error)!void {
- try ais.writer().writeAll("[");
- try renderExpression(allocator, ais, tree, asm_output.symbolic_name, Space.None);
- try ais.writer().writeAll("] ");
- try renderExpression(allocator, ais, tree, asm_output.constraint, Space.None);
- try ais.writer().writeAll(" (");
-
- switch (asm_output.kind) {
- .Variable => |variable_name| {
- try renderExpression(allocator, ais, tree, &variable_name.base, Space.None);
- },
- .Return => |return_type| {
- try ais.writer().writeAll("-> ");
- try renderExpression(allocator, ais, tree, return_type, Space.None);
- },
- }
-
- return renderToken(tree, ais, asm_output.lastToken(), space); // )
-}
+/// Assumes that start is the first byte past the previous token and
+/// that end is the last byte before the next token.
+fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!bool {
+ var index: usize = start;
+ while (mem.indexOf(u8, tree.source[index..end], "//")) |offset| {
+ const comment_start = index + offset;
-fn renderAsmInput(
- allocator: *mem.Allocator,
- ais: anytype,
- tree: *ast.Tree,
- asm_input: *const ast.Node.Asm.Input,
- space: Space,
-) (@TypeOf(ais.*).Error || Error)!void {
- try ais.writer().writeAll("[");
- try renderExpression(allocator, ais, tree, asm_input.symbolic_name, Space.None);
- try ais.writer().writeAll("] ");
- try renderExpression(allocator, ais, tree, asm_input.constraint, Space.None);
- try ais.writer().writeAll(" (");
- try renderExpression(allocator, ais, tree, asm_input.expr, Space.None);
- return renderToken(tree, ais, asm_input.lastToken(), space); // )
-}
+ // If there is no newline, the comment ends with EOF
+ const newline_index = mem.indexOfScalar(u8, tree.source[comment_start..end], '\n');
+ const newline = if (newline_index) |i| comment_start + i else null;
-fn renderVarDecl(
- allocator: *mem.Allocator,
- ais: anytype,
- tree: *ast.Tree,
- var_decl: *ast.Node.VarDecl,
-) (@TypeOf(ais.*).Error || Error)!void {
- if (var_decl.getVisibToken()) |visib_token| {
- try renderToken(tree, ais, visib_token, Space.Space); // pub
- }
+ const untrimmed_comment = tree.source[comment_start .. newline orelse tree.source.len];
+ const trimmed_comment = mem.trimRight(u8, untrimmed_comment, &std.ascii.spaces);
- if (var_decl.getExternExportToken()) |extern_export_token| {
- try renderToken(tree, ais, extern_export_token, Space.Space); // extern
-
- if (var_decl.getLibName()) |lib_name| {
- try renderExpression(allocator, ais, tree, lib_name, Space.Space); // "lib"
+ // Don't leave any whitespace at the start of the file
+ if (index != 0) {
+ if (index == start and mem.containsAtLeast(u8, tree.source[index..comment_start], 2, "\n")) {
+ // Leave up to one empty line before the first comment
+ try ais.insertNewline();
+ try ais.insertNewline();
+ } else if (mem.indexOfScalar(u8, tree.source[index..comment_start], '\n') != null) {
+ // Respect the newline directly before the comment.
+ // Note: This allows an empty line between comments
+ try ais.insertNewline();
+ } else if (index == start) {
+ // Otherwise if the first comment is on the same line as
+ // the token before it, prefix it with a single space.
+ try ais.writer().writeByte(' ');
+ }
}
- }
- if (var_decl.getComptimeToken()) |comptime_token| {
- try renderToken(tree, ais, comptime_token, Space.Space); // comptime
- }
+ try ais.writer().print("{s}\n", .{trimmed_comment});
+ index = 1 + (newline orelse return true);
- if (var_decl.getThreadLocalToken()) |thread_local_token| {
- try renderToken(tree, ais, thread_local_token, Space.Space); // threadlocal
+ if (ais.disabled_offset) |disabled_offset| {
+ if (mem.eql(u8, trimmed_comment, "// zig fmt: on")) {
+ // write the source for which formatting was disabled directly
+ // to the underlying writer, fixing up invaild whitespace
+ try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..index]);
+ ais.disabled_offset = null;
+ }
+ } else if (mem.eql(u8, trimmed_comment, "// zig fmt: off")) {
+ ais.disabled_offset = index;
+ }
}
- try renderToken(tree, ais, var_decl.mut_token, Space.Space); // var
-
- const name_space = if (var_decl.getTypeNode() == null and
- (var_decl.getAlignNode() != null or
- var_decl.getSectionNode() != null or
- var_decl.getInitNode() != null))
- Space.Space
- else
- Space.None;
- try renderToken(tree, ais, var_decl.name_token, name_space);
-
- if (var_decl.getTypeNode()) |type_node| {
- try renderToken(tree, ais, tree.nextToken(var_decl.name_token), Space.Space);
- const s = if (var_decl.getAlignNode() != null or
- var_decl.getSectionNode() != null or
- var_decl.getInitNode() != null) Space.Space else Space.None;
- try renderExpression(allocator, ais, tree, type_node, s);
- }
-
- if (var_decl.getAlignNode()) |align_node| {
- const lparen = tree.prevToken(align_node.firstToken());
- const align_kw = tree.prevToken(lparen);
- const rparen = tree.nextToken(align_node.lastToken());
- try renderToken(tree, ais, align_kw, Space.None); // align
- try renderToken(tree, ais, lparen, Space.None); // (
- try renderExpression(allocator, ais, tree, align_node, Space.None);
- const s = if (var_decl.getSectionNode() != null or var_decl.getInitNode() != null) Space.Space else Space.None;
- try renderToken(tree, ais, rparen, s); // )
- }
-
- if (var_decl.getSectionNode()) |section_node| {
- const lparen = tree.prevToken(section_node.firstToken());
- const section_kw = tree.prevToken(lparen);
- const rparen = tree.nextToken(section_node.lastToken());
- try renderToken(tree, ais, section_kw, Space.None); // linksection
- try renderToken(tree, ais, lparen, Space.None); // (
- try renderExpression(allocator, ais, tree, section_node, Space.None);
- const s = if (var_decl.getInitNode() != null) Space.Space else Space.None;
- try renderToken(tree, ais, rparen, s); // )
- }
-
- if (var_decl.getInitNode()) |init_node| {
- const eq_token = var_decl.getEqToken().?;
- const eq_space = blk: {
- const loc = tree.tokenLocation(tree.token_locs[eq_token].end, tree.nextToken(eq_token));
- break :blk if (loc.line == 0) Space.Space else Space.Newline;
- };
- {
- ais.pushIndent();
- defer ais.popIndent();
- try renderToken(tree, ais, eq_token, eq_space); // =
- }
- ais.pushIndentOneShot();
- try renderExpression(allocator, ais, tree, init_node, Space.None);
+ if (index != start and mem.containsAtLeast(u8, tree.source[index - 1 .. end], 2, "\n")) {
+ try ais.insertNewline();
}
- try renderToken(tree, ais, var_decl.semicolon_token, Space.Newline);
+ return index != start;
}
-fn renderParamDecl(
- allocator: *mem.Allocator,
- ais: anytype,
- tree: *ast.Tree,
- param_decl: ast.Node.FnProto.ParamDecl,
- space: Space,
-) (@TypeOf(ais.*).Error || Error)!void {
- try renderDocComments(tree, ais, param_decl, param_decl.doc_comments);
-
- if (param_decl.comptime_token) |comptime_token| {
- try renderToken(tree, ais, comptime_token, Space.Space);
- }
- if (param_decl.noalias_token) |noalias_token| {
- try renderToken(tree, ais, noalias_token, Space.Space);
- }
- if (param_decl.name_token) |name_token| {
- try renderToken(tree, ais, name_token, Space.None);
- try renderToken(tree, ais, tree.nextToken(name_token), Space.Space); // :
- }
- switch (param_decl.param_type) {
- .any_type, .type_expr => |node| try renderExpression(allocator, ais, tree, node, space),
- }
+fn renderExtraNewline(ais: *Ais, tree: ast.Tree, node: ast.Node.Index) Error!void {
+ return renderExtraNewlineToken(ais, tree, tree.firstToken(node));
}
-fn renderStatement(
- allocator: *mem.Allocator,
- ais: anytype,
- tree: *ast.Tree,
- base: *ast.Node,
-) (@TypeOf(ais.*).Error || Error)!void {
- switch (base.tag) {
- .VarDecl => {
- const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
- try renderVarDecl(allocator, ais, tree, var_decl);
- },
- else => {
- if (base.requireSemiColon()) {
- try renderExpression(allocator, ais, tree, base, Space.None);
-
- const semicolon_index = tree.nextToken(base.lastToken());
- assert(tree.token_ids[semicolon_index] == .Semicolon);
- try renderToken(tree, ais, semicolon_index, Space.Newline);
- } else {
- try renderExpression(allocator, ais, tree, base, Space.Newline);
- }
- },
+/// Check if there is an empty line immediately before the given token. If so, render it.
+fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex) Error!void {
+ const token_starts = tree.tokens.items(.start);
+ const token_start = token_starts[token_index];
+ if (token_start == 0) return;
+ const prev_token_end = if (token_index == 0)
+ 0
+ else
+ token_starts[token_index - 1] + tokenSliceForRender(tree, token_index - 1).len;
+
+ // If there is a comment present, it will handle the empty line
+ if (mem.indexOf(u8, tree.source[prev_token_end..token_start], "//") != null) return;
+
+ // Iterate backwards to the end of the previous token, stopping if a
+ // non-whitespace character is encountered or two newlines have been found.
+ var i = token_start - 1;
+ var newlines: u2 = 0;
+ while (std.ascii.isSpace(tree.source[i])) : (i -= 1) {
+ if (tree.source[i] == '\n') newlines += 1;
+ if (newlines == 2) return ais.insertNewline();
+ if (i == prev_token_end) break;
}
}
-const Space = enum {
- None,
- Newline,
- Comma,
- Space,
- SpaceOrOutdent,
- NoNewline,
- NoComment,
- BlockStart,
-};
-
-fn renderTokenOffset(
- tree: *ast.Tree,
- ais: anytype,
- token_index: ast.TokenIndex,
- space: Space,
- token_skip_bytes: usize,
-) (@TypeOf(ais.*).Error || Error)!void {
- if (space == Space.BlockStart) {
- // If placing the lbrace on the current line would cause an uggly gap then put the lbrace on the next line
- const new_space = if (ais.isLineOverIndented()) Space.Newline else Space.Space;
- return renderToken(tree, ais, token_index, new_space);
+/// end_token is the token one past the last doc comment token. This function
+/// searches backwards from there.
+fn renderDocComments(ais: *Ais, tree: ast.Tree, end_token: ast.TokenIndex) Error!void {
+ // Search backwards for the first doc comment.
+ const token_tags = tree.tokens.items(.tag);
+ if (end_token == 0) return;
+ var tok = end_token - 1;
+ while (token_tags[tok] == .doc_comment) {
+ if (tok == 0) break;
+ tok -= 1;
+ } else {
+ tok += 1;
}
+ const first_tok = tok;
+ if (first_tok == end_token) return;
+ try renderExtraNewlineToken(ais, tree, first_tok);
- var token_loc = tree.token_locs[token_index];
- try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(token_loc)[token_skip_bytes..], " "));
-
- if (space == Space.NoComment)
- return;
-
- var next_token_id = tree.token_ids[token_index + 1];
- var next_token_loc = tree.token_locs[token_index + 1];
-
- if (space == Space.Comma) switch (next_token_id) {
- .Comma => return renderToken(tree, ais, token_index + 1, Space.Newline),
- .LineComment => {
- try ais.writer().writeAll(", ");
- return renderToken(tree, ais, token_index + 1, Space.Newline);
- },
- else => {
- if (token_index + 2 < tree.token_ids.len and
- tree.token_ids[token_index + 2] == .MultilineStringLiteralLine)
- {
- try ais.writer().writeAll(",");
- return;
- } else {
- try ais.writer().writeAll(",");
- try ais.insertNewline();
- return;
- }
- },
- };
-
- // Skip over same line doc comments
- var offset: usize = 1;
- if (next_token_id == .DocComment) {
- const loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
- if (loc.line == 0) {
- offset += 1;
- next_token_id = tree.token_ids[token_index + offset];
- next_token_loc = tree.token_locs[token_index + offset];
- }
+ while (token_tags[tok] == .doc_comment) : (tok += 1) {
+ try renderToken(ais, tree, tok, .newline);
}
+}
- if (next_token_id != .LineComment) {
- switch (space) {
- Space.None, Space.NoNewline => return,
- Space.Newline => {
- if (next_token_id == .MultilineStringLiteralLine) {
- return;
- } else {
- try ais.insertNewline();
- return;
- }
- },
- Space.Space, Space.SpaceOrOutdent => {
- if (next_token_id == .MultilineStringLiteralLine)
- return;
- try ais.writer().writeByte(' ');
- return;
- },
- Space.NoComment, Space.Comma, Space.BlockStart => unreachable,
- }
+/// start_token is first container doc comment token.
+fn renderContainerDocComments(ais: *Ais, tree: ast.Tree, start_token: ast.TokenIndex) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ var tok = start_token;
+ while (token_tags[tok] == .container_doc_comment) : (tok += 1) {
+ try renderToken(ais, tree, tok, .newline);
}
-
- while (true) {
- const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " ").len == 2;
- if (comment_is_empty) {
- switch (space) {
- Space.Newline => {
- offset += 1;
- token_loc = next_token_loc;
- next_token_id = tree.token_ids[token_index + offset];
- next_token_loc = tree.token_locs[token_index + offset];
- if (next_token_id != .LineComment) {
- try ais.insertNewline();
- return;
- }
- },
- else => break,
- }
- } else {
- break;
- }
- }
-
- var loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
- if (loc.line == 0) {
- if (tree.token_ids[token_index] != .MultilineStringLiteralLine) {
- try ais.writer().writeByte(' ');
- }
- try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " "));
- offset = 2;
- token_loc = next_token_loc;
- next_token_loc = tree.token_locs[token_index + offset];
- next_token_id = tree.token_ids[token_index + offset];
- if (next_token_id != .LineComment) {
- switch (space) {
- .None, .Space, .SpaceOrOutdent => {
- try ais.insertNewline();
- },
- .Newline => {
- if (next_token_id == .MultilineStringLiteralLine) {
- return;
- } else {
- try ais.insertNewline();
- return;
- }
- },
- .NoNewline => {},
- .NoComment, .Comma, .BlockStart => unreachable,
- }
- return;
- }
- loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
+ // Render extra newline if there is one between final container doc comment and
+ // the next token. If the next token is a doc comment, that code path
+ // will have its own logic to insert a newline.
+ if (token_tags[tok] != .doc_comment) {
+ try renderExtraNewlineToken(ais, tree, tok);
}
+}
- while (true) {
- // translate-c doesn't generate correct newlines
- // in generated code (loc.line == 0) so treat that case
- // as though there was meant to be a newline between the tokens
- var newline_count = if (loc.line <= 1) @as(u8, 1) else @as(u8, 2);
- while (newline_count > 0) : (newline_count -= 1) try ais.insertNewline();
- try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " "));
-
- offset += 1;
- token_loc = next_token_loc;
- next_token_loc = tree.token_locs[token_index + offset];
- next_token_id = tree.token_ids[token_index + offset];
- if (next_token_id != .LineComment) {
- switch (space) {
- .Newline => {
- if (next_token_id == .MultilineStringLiteralLine) {
- return;
- } else {
- try ais.insertNewline();
- return;
- }
- },
- .None, .Space, .SpaceOrOutdent => {
- try ais.insertNewline();
- },
- .NoNewline => {},
- .NoComment, .Comma, .BlockStart => unreachable,
- }
- return;
- }
- loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
+fn tokenSliceForRender(tree: ast.Tree, token_index: ast.TokenIndex) []const u8 {
+ var ret = tree.tokenSlice(token_index);
+ if (tree.tokens.items(.tag)[token_index] == .multiline_string_literal_line) {
+ assert(ret[ret.len - 1] == '\n');
+ ret.len -= 1;
}
+ return ret;
}
-fn renderToken(
- tree: *ast.Tree,
- ais: anytype,
- token_index: ast.TokenIndex,
- space: Space,
-) (@TypeOf(ais.*).Error || Error)!void {
- return renderTokenOffset(tree, ais, token_index, space, 0);
+fn hasSameLineComment(tree: ast.Tree, token_index: ast.TokenIndex) bool {
+ const token_starts = tree.tokens.items(.start);
+ const between_source = tree.source[token_starts[token_index]..token_starts[token_index + 1]];
+ for (between_source) |byte| switch (byte) {
+ '\n' => return false,
+ '/' => return true,
+ else => continue,
+ };
+ return false;
}
-fn renderDocComments(
- tree: *ast.Tree,
- ais: anytype,
- node: anytype,
- doc_comments: ?*ast.Node.DocComment,
-) (@TypeOf(ais.*).Error || Error)!void {
- const comment = doc_comments orelse return;
- return renderDocCommentsToken(tree, ais, comment, node.firstToken());
+/// Returns `true` if and only if there are any tokens or line comments between
+/// start_token and end_token.
+fn anythingBetween(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool {
+ if (start_token + 1 != end_token) return true;
+ const token_starts = tree.tokens.items(.start);
+ const between_source = tree.source[token_starts[start_token]..token_starts[start_token + 1]];
+ for (between_source) |byte| switch (byte) {
+ '/' => return true,
+ else => continue,
+ };
+ return false;
}
-fn renderDocCommentsToken(
- tree: *ast.Tree,
- ais: anytype,
- comment: *ast.Node.DocComment,
- first_token: ast.TokenIndex,
-) (@TypeOf(ais.*).Error || Error)!void {
- var tok_i = comment.first_line;
- while (true) : (tok_i += 1) {
- switch (tree.token_ids[tok_i]) {
- .DocComment, .ContainerDocComment => {
- if (comment.first_line < first_token) {
- try renderToken(tree, ais, tok_i, Space.Newline);
- } else {
- try renderToken(tree, ais, tok_i, Space.NoComment);
- try ais.insertNewline();
- }
- },
- .LineComment => continue,
- else => break,
- }
- }
+fn writeFixingWhitespace(writer: std.ArrayList(u8).Writer, slice: []const u8) Error!void {
+ for (slice) |byte| switch (byte) {
+ '\t' => try writer.writeAll(" " ** 4),
+ '\r' => {},
+ else => try writer.writeByte(byte),
+ };
}
-fn nodeIsBlock(base: *const ast.Node) bool {
- return switch (base.tag) {
- .Block,
- .LabeledBlock,
- .If,
- .For,
- .While,
- .Switch,
+fn nodeIsBlock(tag: ast.Node.Tag) bool {
+ return switch (tag) {
+ .block,
+ .block_semicolon,
+ .block_two,
+ .block_two_semicolon,
+ .@"if",
+ .if_simple,
+ .@"for",
+ .for_simple,
+ .@"while",
+ .while_simple,
+ .while_cont,
+ .@"switch",
+ .switch_comma,
=> true,
else => false,
};
}
-fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
- return switch (base.tag) {
- .Catch,
- .Add,
- .AddWrap,
- .ArrayCat,
- .ArrayMult,
- .Assign,
- .AssignBitAnd,
- .AssignBitOr,
- .AssignBitShiftLeft,
- .AssignBitShiftRight,
- .AssignBitXor,
- .AssignDiv,
- .AssignSub,
- .AssignSubWrap,
- .AssignMod,
- .AssignAdd,
- .AssignAddWrap,
- .AssignMul,
- .AssignMulWrap,
- .BangEqual,
- .BitAnd,
- .BitOr,
- .BitShiftLeft,
- .BitShiftRight,
- .BitXor,
- .BoolAnd,
- .BoolOr,
- .Div,
- .EqualEqual,
- .ErrorUnion,
- .GreaterOrEqual,
- .GreaterThan,
- .LessOrEqual,
- .LessThan,
- .MergeErrorSets,
- .Mod,
- .Mul,
- .MulWrap,
- .Range,
- .Sub,
- .SubWrap,
- .OrElse,
- => true,
-
+fn nodeIsIf(tag: ast.Node.Tag) bool {
+ return switch (tag) {
+ .@"if", .if_simple => true,
else => false,
};
}
-fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!void {
- for (slice) |byte| switch (byte) {
- '\t' => try ais.writer().writeAll(" "),
- '\r' => {},
- else => try ais.writer().writeByte(byte),
+fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool {
+ return switch (tag) {
+ .@"catch",
+ .add,
+ .add_wrap,
+ .array_cat,
+ .array_mult,
+ .assign,
+ .assign_bit_and,
+ .assign_bit_or,
+ .assign_bit_shift_left,
+ .assign_bit_shift_right,
+ .assign_bit_xor,
+ .assign_div,
+ .assign_sub,
+ .assign_sub_wrap,
+ .assign_mod,
+ .assign_add,
+ .assign_add_wrap,
+ .assign_mul,
+ .assign_mul_wrap,
+ .bang_equal,
+ .bit_and,
+ .bit_or,
+ .bit_shift_left,
+ .bit_shift_right,
+ .bit_xor,
+ .bool_and,
+ .bool_or,
+ .div,
+ .equal_equal,
+ .error_union,
+ .greater_or_equal,
+ .greater_than,
+ .less_or_equal,
+ .less_than,
+ .merge_error_sets,
+ .mod,
+ .mul,
+ .mul_wrap,
+ .sub,
+ .sub_wrap,
+ .@"orelse",
+ => true,
+
+ else => false,
};
}
// Returns the number of nodes in `expr` that are on the same line as `rtoken`,
// or null if they all are on the same line.
-fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {
- const first_token = exprs[0].firstToken();
- const first_loc = tree.tokenLocation(tree.token_locs[first_token].start, rtoken);
- if (first_loc.line == 0) {
- const maybe_comma = tree.prevToken(rtoken);
- if (tree.token_ids[maybe_comma] == .Comma)
+fn rowSize(tree: ast.Tree, exprs: []const ast.Node.Index, rtoken: ast.TokenIndex) ?usize {
+ const token_tags = tree.tokens.items(.tag);
+
+ const first_token = tree.firstToken(exprs[0]);
+ if (tree.tokensOnSameLine(first_token, rtoken)) {
+ const maybe_comma = rtoken - 1;
+ if (token_tags[maybe_comma] == .comma)
return 1;
return null; // no newlines
}
@@ -2668,9 +2572,8 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {
var count: usize = 1;
for (exprs) |expr, i| {
if (i + 1 < exprs.len) {
- const expr_last_token = expr.lastToken() + 1;
- const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, exprs[i + 1].firstToken());
- if (loc.line != 0) return count;
+ const expr_last_token = tree.lastToken(expr) + 1;
+ if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(exprs[i + 1]))) return count;
count += 1;
} else {
return count;
@@ -2678,3 +2581,150 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {
}
unreachable;
}
+
+/// Automatically inserts indentation of written data by keeping
+/// track of the current indentation level
+fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
+ return struct {
+ const Self = @This();
+ pub const Error = UnderlyingWriter.Error;
+ pub const Writer = std.io.Writer(*Self, Error, write);
+
+ underlying_writer: UnderlyingWriter,
+
+ /// Offset into the source at which formatting has been disabled with
+ /// a `zig fmt: off` comment.
+ ///
+ /// If non-null, the AutoIndentingStream will not write any bytes
+ /// to the underlying writer. It will however continue to track the
+ /// indentation level.
+ disabled_offset: ?usize = null,
+
+ indent_count: usize = 0,
+ indent_delta: usize,
+ current_line_empty: bool = true,
+ /// automatically popped when applied
+ indent_one_shot_count: usize = 0,
+ /// the most recently applied indent
+ applied_indent: usize = 0,
+ /// not used until the next line
+ indent_next_line: usize = 0,
+
+ pub fn writer(self: *Self) Writer {
+ return .{ .context = self };
+ }
+
+ pub fn write(self: *Self, bytes: []const u8) Error!usize {
+ if (bytes.len == 0)
+ return @as(usize, 0);
+
+ try self.applyIndent();
+ return self.writeNoIndent(bytes);
+ }
+
+ // Change the indent delta without changing the final indentation level
+ pub fn setIndentDelta(self: *Self, new_indent_delta: usize) void {
+ if (self.indent_delta == new_indent_delta) {
+ return;
+ } else if (self.indent_delta > new_indent_delta) {
+ assert(self.indent_delta % new_indent_delta == 0);
+ self.indent_count = self.indent_count * (self.indent_delta / new_indent_delta);
+ } else {
+ // assert that the current indentation (in spaces) in a multiple of the new delta
+ assert((self.indent_count * self.indent_delta) % new_indent_delta == 0);
+ self.indent_count = self.indent_count / (new_indent_delta / self.indent_delta);
+ }
+ self.indent_delta = new_indent_delta;
+ }
+
+ fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
+ if (bytes.len == 0)
+ return @as(usize, 0);
+
+ if (self.disabled_offset == null) try self.underlying_writer.writeAll(bytes);
+ if (bytes[bytes.len - 1] == '\n')
+ self.resetLine();
+ return bytes.len;
+ }
+
+ pub fn insertNewline(self: *Self) Error!void {
+ _ = try self.writeNoIndent("\n");
+ }
+
+ fn resetLine(self: *Self) void {
+ self.current_line_empty = true;
+ self.indent_next_line = 0;
+ }
+
+ /// Insert a newline unless the current line is blank
+ pub fn maybeInsertNewline(self: *Self) Error!void {
+ if (!self.current_line_empty)
+ try self.insertNewline();
+ }
+
+ /// Push default indentation
+ /// Doesn't actually write any indentation.
+ /// Just primes the stream to be able to write the correct indentation if it needs to.
+ pub fn pushIndent(self: *Self) void {
+ self.indent_count += 1;
+ }
+
+ /// Push an indent that is automatically popped after being applied
+ pub fn pushIndentOneShot(self: *Self) void {
+ self.indent_one_shot_count += 1;
+ self.pushIndent();
+ }
+
+ /// Turns all one-shot indents into regular indents
+ /// Returns number of indents that must now be manually popped
+ pub fn lockOneShotIndent(self: *Self) usize {
+ var locked_count = self.indent_one_shot_count;
+ self.indent_one_shot_count = 0;
+ return locked_count;
+ }
+
+ /// Push an indent that should not take effect until the next line
+ pub fn pushIndentNextLine(self: *Self) void {
+ self.indent_next_line += 1;
+ self.pushIndent();
+ }
+
+ pub fn popIndent(self: *Self) void {
+ assert(self.indent_count != 0);
+ self.indent_count -= 1;
+
+ if (self.indent_next_line > 0)
+ self.indent_next_line -= 1;
+ }
+
+ /// Writes ' ' bytes if the current line is empty
+ fn applyIndent(self: *Self) Error!void {
+ const current_indent = self.currentIndent();
+ if (self.current_line_empty and current_indent > 0) {
+ if (self.disabled_offset == null) {
+ try self.underlying_writer.writeByteNTimes(' ', current_indent);
+ }
+ self.applied_indent = current_indent;
+ }
+
+ self.indent_count -= self.indent_one_shot_count;
+ self.indent_one_shot_count = 0;
+ self.current_line_empty = false;
+ }
+
+ /// Checks to see if the most recent indentation exceeds the currently pushed indents
+ pub fn isLineOverIndented(self: *Self) bool {
+ if (self.current_line_empty) return false;
+ return self.applied_indent > self.currentIndent();
+ }
+
+ fn currentIndent(self: *Self) usize {
+ var indent_current: usize = 0;
+ if (self.indent_count > 0) {
+ const indent_count = self.indent_count - self.indent_next_line;
+ indent_current = indent_count * self.indent_delta;
+ }
+ return indent_current;
+ }
+ };
+}
diff --git a/lib/std/zig/string_literal.zig b/lib/std/zig/string_literal.zig
index b92d795eee..78d4e63bfe 100644
--- a/lib/std/zig/string_literal.zig
+++ b/lib/std/zig/string_literal.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig
index af0b000328..2d9f286dd6 100644
--- a/lib/std/zig/system.zig
+++ b/lib/std/zig/system.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -14,21 +14,24 @@ const process = std.process;
const Target = std.Target;
const CrossTarget = std.zig.CrossTarget;
const macos = @import("system/macos.zig");
-
-const is_windows = Target.current.os.tag == .windows;
+pub const windows = @import("system/windows.zig");
pub const getSDKPath = macos.getSDKPath;
pub const NativePaths = struct {
include_dirs: ArrayList([:0]u8),
lib_dirs: ArrayList([:0]u8),
+ framework_dirs: ArrayList([:0]u8),
rpaths: ArrayList([:0]u8),
warnings: ArrayList([:0]u8),
- pub fn detect(allocator: *Allocator) !NativePaths {
+ pub fn detect(allocator: *Allocator, native_info: NativeTargetInfo) !NativePaths {
+ const native_target = native_info.target;
+
var self: NativePaths = .{
.include_dirs = ArrayList([:0]u8).init(allocator),
.lib_dirs = ArrayList([:0]u8).init(allocator),
+ .framework_dirs = ArrayList([:0]u8).init(allocator),
.rpaths = ArrayList([:0]u8).init(allocator),
.warnings = ArrayList([:0]u8).init(allocator),
};
@@ -49,7 +52,7 @@ pub const NativePaths = struct {
};
try self.addIncludeDir(include_path);
} else {
- try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {}", .{word});
+ try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {s}", .{word});
break;
}
}
@@ -75,7 +78,7 @@ pub const NativePaths = struct {
const lib_path = word[2..];
try self.addLibDir(lib_path);
} else {
- try self.addWarningFmt("Unrecognized C flag from NIX_LDFLAGS: {}", .{word});
+ try self.addWarningFmt("Unrecognized C flag from NIX_LDFLAGS: {s}", .{word});
break;
}
}
@@ -88,9 +91,22 @@ pub const NativePaths = struct {
return self;
}
- if (!is_windows) {
- const triple = try Target.current.linuxTriple(allocator);
- const qual = Target.current.cpu.arch.ptrBitWidth();
+ if (comptime Target.current.isDarwin()) {
+ try self.addIncludeDir("/usr/include");
+ try self.addIncludeDir("/usr/local/include");
+
+ try self.addLibDir("/usr/lib");
+ try self.addLibDir("/usr/local/lib");
+
+ try self.addFrameworkDir("/Library/Frameworks");
+ try self.addFrameworkDir("/System/Library/Frameworks");
+
+ return self;
+ }
+
+ if (native_target.os.tag != .windows) {
+ const triple = try native_target.linuxTriple(allocator);
+ const qual = native_target.cpu.arch.ptrBitWidth();
// TODO: $ ld --verbose | grep SEARCH_DIR
// the output contains some paths that end with lib64, maybe include them too?
@@ -98,22 +114,22 @@ pub const NativePaths = struct {
// TODO: some of these are suspect and should only be added on some systems. audit needed.
try self.addIncludeDir("/usr/local/include");
- try self.addLibDirFmt("/usr/local/lib{}", .{qual});
+ try self.addLibDirFmt("/usr/local/lib{d}", .{qual});
try self.addLibDir("/usr/local/lib");
- try self.addIncludeDirFmt("/usr/include/{}", .{triple});
- try self.addLibDirFmt("/usr/lib/{}", .{triple});
+ try self.addIncludeDirFmt("/usr/include/{s}", .{triple});
+ try self.addLibDirFmt("/usr/lib/{s}", .{triple});
try self.addIncludeDir("/usr/include");
- try self.addLibDirFmt("/lib{}", .{qual});
+ try self.addLibDirFmt("/lib{d}", .{qual});
try self.addLibDir("/lib");
- try self.addLibDirFmt("/usr/lib{}", .{qual});
+ try self.addLibDirFmt("/usr/lib{d}", .{qual});
try self.addLibDir("/usr/lib");
// example: on a 64-bit debian-based linux distro, with zlib installed from apt:
// zlib.h is in /usr/include (added above)
// libz.so.1 is in /lib/x86_64-linux-gnu (added here)
- try self.addLibDirFmt("/lib/{}", .{triple});
+ try self.addLibDirFmt("/lib/{s}", .{triple});
}
return self;
@@ -122,6 +138,7 @@ pub const NativePaths = struct {
pub fn deinit(self: *NativePaths) void {
deinitArray(&self.include_dirs);
deinitArray(&self.lib_dirs);
+ deinitArray(&self.framework_dirs);
deinitArray(&self.rpaths);
deinitArray(&self.warnings);
self.* = undefined;
@@ -158,6 +175,16 @@ pub const NativePaths = struct {
return self.appendArray(&self.warnings, s);
}
+ pub fn addFrameworkDir(self: *NativePaths, s: []const u8) !void {
+ return self.appendArray(&self.framework_dirs, s);
+ }
+
+ pub fn addFrameworkDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
+ const item = try std.fmt.allocPrint0(self.framework_dirs.allocator, fmt, args);
+ errdefer self.framework_dirs.allocator.free(item);
+ try self.framework_dirs.append(item);
+ }
+
pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
const item = try std.fmt.allocPrint0(self.warnings.allocator, fmt, args);
errdefer self.warnings.allocator.free(item);
@@ -195,6 +222,7 @@ pub const NativeTargetInfo = struct {
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
DeviceBusy,
+ OSVersionDetectionFail,
};
/// Given a `CrossTarget`, which specifies in detail which parts of the target should be detected
@@ -223,77 +251,11 @@ pub const NativeTargetInfo = struct {
}
},
.windows => {
- var version_info: std.os.windows.RTL_OSVERSIONINFOW = undefined;
- version_info.dwOSVersionInfoSize = @sizeOf(@TypeOf(version_info));
-
- switch (std.os.windows.ntdll.RtlGetVersion(&version_info)) {
- .SUCCESS => {},
- else => unreachable,
- }
-
- // Starting from the system infos build a NTDDI-like version
- // constant whose format is:
- // B0 B1 B2 B3
- // `---` `` ``--> Sub-version (Starting from Windows 10 onwards)
- // \ `--> Service pack (Always zero in the constants defined)
- // `--> OS version (Major & minor)
- const os_ver: u16 =
- @intCast(u16, version_info.dwMajorVersion & 0xff) << 8 |
- @intCast(u16, version_info.dwMinorVersion & 0xff);
- const sp_ver: u8 = 0;
- const sub_ver: u8 = if (os_ver >= 0x0A00) subver: {
- // There's no other way to obtain this info beside
- // checking the build number against a known set of
- // values
- const known_build_numbers = [_]u32{
- 10240, 10586, 14393, 15063, 16299, 17134, 17763,
- 18362, 19041,
- };
- var last_idx: usize = 0;
- for (known_build_numbers) |build, i| {
- if (version_info.dwBuildNumber >= build)
- last_idx = i;
- }
- break :subver @truncate(u8, last_idx);
- } else 0;
-
- const version: u32 = @as(u32, os_ver) << 16 | @as(u32, sp_ver) << 8 | sub_ver;
-
- os.version_range.windows.max = @intToEnum(Target.Os.WindowsVersion, version);
- os.version_range.windows.min = @intToEnum(Target.Os.WindowsVersion, version);
- },
- .macos => {
- var scbuf: [32]u8 = undefined;
- var size: usize = undefined;
-
- // The osproductversion sysctl was introduced first with 10.13.4 High Sierra.
- const key_osproductversion = "kern.osproductversion"; // eg. "10.15.4"
- size = scbuf.len;
- if (std.os.sysctlbynameZ(key_osproductversion, &scbuf, &size, null, 0)) |_| {
- const string_version = scbuf[0 .. size - 1];
- if (std.builtin.Version.parse(string_version)) |ver| {
- os.version_range.semver.min = ver;
- os.version_range.semver.max = ver;
- } else |err| switch (err) {
- error.Overflow => {},
- error.InvalidCharacter => {},
- error.InvalidVersion => {},
- }
- } else |err| switch (err) {
- error.UnknownName => {
- const key_osversion = "kern.osversion"; // eg. "19E287"
- size = scbuf.len;
- std.os.sysctlbynameZ(key_osversion, &scbuf, &size, null, 0) catch {
- @panic("unable to detect macOS version: " ++ key_osversion);
- };
- if (macos.version_from_build(scbuf[0 .. size - 1])) |ver| {
- os.version_range.semver.min = ver;
- os.version_range.semver.max = ver;
- } else |_| {}
- },
- else => @panic("unable to detect macOS version: " ++ key_osproductversion),
- }
+ const detected_version = windows.detectRuntimeVersion();
+ os.version_range.windows.min = detected_version;
+ os.version_range.windows.max = detected_version;
},
+ .macos => try macos.detect(&os),
.freebsd => {
var osreldate: u32 = undefined;
var len: usize = undefined;
@@ -936,6 +898,6 @@ pub const NativeTargetInfo = struct {
}
};
-test "" {
+test {
_ = @import("system/macos.zig");
}
diff --git a/lib/std/zig/system/macos.zig b/lib/std/zig/system/macos.zig
index 6895fa3f3a..abe844d2c4 100644
--- a/lib/std/zig/system/macos.zig
+++ b/lib/std/zig/system/macos.zig
@@ -1,458 +1,408 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
const std = @import("std");
const assert = std.debug.assert;
const mem = std.mem;
+const testing = std.testing;
-pub fn version_from_build(build: []const u8) !std.builtin.Version {
- // build format:
- // 19E287 (example)
- // xxyzzz
+/// Detect macOS version.
+/// `os` is not modified in case of error.
+pub fn detect(os: *std.Target.Os) !void {
+ // Drop use of osproductversion sysctl because:
+ // 1. only available 10.13.4 High Sierra and later
+ // 2. when used from a binary built against < SDK 11.0 it returns 10.16 and masks Big Sur 11.x version
//
- // major = 10
- // minor = x - 4 = 19 - 4 = 15
- // patch = ascii(y) - 'A' = 'E' - 'A' = 69 - 65 = 4
- // answer: 10.15.4
+ // NEW APPROACH, STEP 1, parse file:
//
- // note: patch is typical but with some older releases (before 10.8) zzz is considered
-
- // never return anything below 10.0.0
- var result = std.builtin.Version{ .major = 10, .minor = 0, .patch = 0 };
+ // /System/Library/CoreServices/SystemVersion.plist
+ //
+ // NOTE: Historically `SystemVersion.plist` first appeared circa '2003
+ // with the release of Mac OS X 10.3.0 Panther.
+ //
+ // and if it contains a `10.16` value where the `16` is `>= 16` then it is non-canonical,
+ // discarded, and we move on to next step. Otherwise we accept the version.
+ //
+ // BACKGROUND: `10.(16+)` is not a proper version and does not have enough fidelity to
+ // indicate minor/point version of Big Sur and later. It is a context-sensitive result
+ // issued by the kernel for backwards compatibility purposes. Likely the kernel checks
+ // if the executable was linked against an SDK older than Big Sur.
+ //
+ // STEP 2, parse next file:
+ //
+ // /System/Library/CoreServices/.SystemVersionPlatform.plist
+ //
+ // NOTE: Historically `SystemVersionPlatform.plist` first appeared circa '2020
+ // with the release of macOS 11.0 Big Sur.
+ //
+ // Accessing the content via this path circumvents a context-sensitive result and
+ // yields a canonical Big Sur version.
+ //
+ // At this time there is no other known way for a < SDK 11.0 executable to obtain a
+ // canonical Big Sur version.
+ //
+ // This implementation uses a reasonably simplified approach to parse .plist file
+ // that while it is an xml document, we have good history on the file and its format
+ // such that I am comfortable with implementing a minimalistic parser.
+ // Things like string and general escapes are not supported.
+ const prefixSlash = "/System/Library/CoreServices/";
+ const paths = [_][]const u8{
+ prefixSlash ++ "SystemVersion.plist",
+ prefixSlash ++ ".SystemVersionPlatform.plist",
+ };
+ for (paths) |path| {
+ // approx. 4 times historical file size
+ var buf: [2048]u8 = undefined;
- // parse format-x
- var yindex: usize = 0;
- {
- while (yindex < build.len) : (yindex += 1) {
- if (build[yindex] < '0' or build[yindex] > '9') break;
+ if (std.fs.cwd().readFile(path, &buf)) |bytes| {
+ if (parseSystemVersion(bytes)) |ver| {
+ // never return non-canonical `10.(16+)`
+ if (!(ver.major == 10 and ver.minor >= 16)) {
+ os.version_range.semver.min = ver;
+ os.version_range.semver.max = ver;
+ return;
+ }
+ continue;
+ } else |err| {
+ return error.OSVersionDetectionFail;
+ }
+ } else |err| {
+ return error.OSVersionDetectionFail;
}
- if (yindex == 0) return result;
- const x = std.fmt.parseUnsigned(u16, build[0..yindex], 10) catch return error.InvalidVersion;
- if (x < 4) return result;
- result.minor = x - 4;
}
+ return error.OSVersionDetectionFail;
+}
- // parse format-y, format-z
- {
- // expect two more
- if (build.len < yindex + 2) return error.InvalidVersion;
- const y = build[yindex];
- if (y < 'A') return error.InvalidVersion;
- var zend = yindex + 1;
- while (zend < build.len) {
- if (build[zend] < '0' or build[zend] > '9') break;
- zend += 1;
- }
- if (zend == yindex + 1) return error.InvalidVersion;
- const z = std.fmt.parseUnsigned(u16, build[yindex + 1 .. zend], 10) catch return error.InvalidVersion;
-
- result.patch = switch (result.minor) {
- // TODO: compiler complains without explicit @as() coercion
- 0 => @as(u32, switch (y) { // Cheetah: 10.0
- 'K' => 0,
- 'L' => 1,
- 'P' => @as(u32, block: {
- if (z < 13) break :block 2;
- break :block 3;
- }),
- 'Q' => 4,
- else => return error.InvalidVersion,
- }),
- 1 => @as(u32, switch (y) { // Puma: 10.1
- 'G' => 0,
- 'M' => 1,
- 'P' => 2,
- 'Q' => @as(u32, block: {
- if (z < 125) break :block 3;
- break :block 4;
- }),
- 'S' => 5,
- else => return error.InvalidVersion,
- }),
- 2 => @as(u32, switch (y) { // Jaguar: 10.2
- 'C' => 0,
- 'D' => 1,
- 'F' => 2,
- 'G' => 3,
- 'I' => 4,
- 'L' => @as(u32, block: {
- if (z < 60) break :block 5;
- break :block 6;
- }),
- 'R' => @as(u32, block: {
- if (z < 73) break :block 7;
- break :block 8;
- }),
- 'S' => 8,
- else => return error.InvalidVersion,
- }),
- 3 => @as(u32, switch (y) { // Panther: 10.3
- 'B' => 0,
- 'C' => 1,
- 'D' => 2,
- 'F' => 3,
- 'H' => 4,
- 'M' => 5,
- 'R' => 6,
- 'S' => 7,
- 'U' => 8,
- 'W' => 9,
- else => return error.InvalidVersion,
- }),
- 4 => @as(u32, switch (y) { // Tiger: 10.4
- 'A' => 0,
- 'B' => 1,
- 'C',
- 'E',
- => 2,
- 'F' => 3,
- 'G' => @as(u32, block: {
- if (z >= 1454) break :block 5;
- break :block 4;
- }),
- 'H' => 5,
- 'I' => 6,
- 'J',
- 'K',
- 'N',
- => 7,
- 'L' => 8,
- 'P' => 9,
- 'R' => 10,
- 'S' => 11,
- else => return error.InvalidVersion,
- }),
- 5 => @as(u32, switch (y) { // Leopard: 10.5
- 'A' => 0,
- 'B' => 1,
- 'C' => 2,
- 'D' => 3,
- 'E' => 4,
- 'F' => 5,
- 'G' => 6,
- 'J' => 7,
- 'L' => 8,
- else => return error.InvalidVersion,
- }),
- 6 => @as(u32, switch (y) { // Snow Leopard: 10.6
- 'A' => 0,
- 'B' => 1,
- 'C' => 2,
- 'D' => 3,
- 'F' => 4,
- 'H' => 5,
- 'J' => @as(u32, block: {
- if (z < 869) break :block 6;
- break :block 7;
- }),
- 'K' => 8,
- else => return error.InvalidVersion,
- }),
- 7 => @as(u32, switch (y) { // Snow Leopard: 10.6
- 'A' => 0,
- 'B' => 1,
- 'C' => 2,
- 'D' => 3,
- 'E' => 4,
- 'G' => 5,
- else => return error.InvalidVersion,
- }),
- else => y - 'A',
- };
+fn parseSystemVersion(buf: []const u8) !std.builtin.Version {
+ var svt = SystemVersionTokenizer{ .bytes = buf };
+ try svt.skipUntilTag(.start, "dict");
+ while (true) {
+ try svt.skipUntilTag(.start, "key");
+ const content = try svt.expectContent();
+ try svt.skipUntilTag(.end, "key");
+ if (std.mem.eql(u8, content, "ProductVersion")) break;
}
- return result;
-}
+ try svt.skipUntilTag(.start, "string");
+ const ver = try svt.expectContent();
+ try svt.skipUntilTag(.end, "string");
-test "version_from_build" {
- // see https://en.wikipedia.org/wiki/MacOS_version_history#Releases
- const known = [_][2][]const u8{
- .{ "4K78", "10.0.0" },
- .{ "4L13", "10.0.1" },
- .{ "4P12", "10.0.2" },
- .{ "4P13", "10.0.3" },
- .{ "4Q12", "10.0.4" },
+ return std.builtin.Version.parse(ver);
+}
- .{ "5G64", "10.1.0" },
- .{ "5M28", "10.1.1" },
- .{ "5P48", "10.1.2" },
- .{ "5Q45", "10.1.3" },
- .{ "5Q125", "10.1.4" },
- .{ "5S60", "10.1.5" },
+const SystemVersionTokenizer = struct {
+ bytes: []const u8,
+ index: usize = 0,
+ state: State = .begin,
- .{ "6C115", "10.2.0" },
- .{ "6C115a", "10.2.0" },
- .{ "6D52", "10.2.1" },
- .{ "6F21", "10.2.2" },
- .{ "6G30", "10.2.3" },
- .{ "6G37", "10.2.3" },
- .{ "6G50", "10.2.3" },
- .{ "6I32", "10.2.4" },
- .{ "6L29", "10.2.5" },
- .{ "6L60", "10.2.6" },
- .{ "6R65", "10.2.7" },
- .{ "6R73", "10.2.8" },
- .{ "6S90", "10.2.8" },
+ fn next(self: *@This()) !?Token {
+ var mark: usize = self.index;
+ var tag = Tag{};
+ var content: []const u8 = "";
- .{ "7B85", "10.3.0" },
- .{ "7B86", "10.3.0" },
- .{ "7C107", "10.3.1" },
- .{ "7D24", "10.3.2" },
- .{ "7D28", "10.3.2" },
- .{ "7F44", "10.3.3" },
- .{ "7H63", "10.3.4" },
- .{ "7M34", "10.3.5" },
- .{ "7R28", "10.3.6" },
- .{ "7S215", "10.3.7" },
- .{ "7U16", "10.3.8" },
- .{ "7W98", "10.3.9" },
+ while (self.index < self.bytes.len) {
+ const char = self.bytes[self.index];
+ switch (self.state) {
+ .begin => switch (char) {
+ '<' => {
+ self.state = .tag0;
+ self.index += 1;
+ tag = Tag{};
+ mark = self.index;
+ },
+ '>' => {
+ return error.BadToken;
+ },
+ else => {
+ self.state = .content;
+ content = "";
+ mark = self.index;
+ },
+ },
+ .tag0 => switch (char) {
+ '<' => {
+ return error.BadToken;
+ },
+ '>' => {
+ self.state = .begin;
+ self.index += 1;
+ tag.name = self.bytes[mark..self.index];
+ return Token{ .tag = tag };
+ },
+ '"' => {
+ self.state = .tag_string;
+ self.index += 1;
+ },
+ '/' => {
+ self.state = .tag0_end_or_empty;
+ self.index += 1;
+ },
+ 'A'...'Z', 'a'...'z' => {
+ self.state = .tagN;
+ tag.kind = .start;
+ self.index += 1;
+ },
+ else => {
+ self.state = .tagN;
+ self.index += 1;
+ },
+ },
+ .tag0_end_or_empty => switch (char) {
+ '<' => {
+ return error.BadToken;
+ },
+ '>' => {
+ self.state = .begin;
+ tag.kind = .empty;
+ tag.name = self.bytes[self.index..self.index];
+ self.index += 1;
+ return Token{ .tag = tag };
+ },
+ else => {
+ self.state = .tagN;
+ tag.kind = .end;
+ mark = self.index;
+ self.index += 1;
+ },
+ },
+ .tagN => switch (char) {
+ '<' => {
+ return error.BadToken;
+ },
+ '>' => {
+ self.state = .begin;
+ tag.name = self.bytes[mark..self.index];
+ self.index += 1;
+ return Token{ .tag = tag };
+ },
+ '"' => {
+ self.state = .tag_string;
+ self.index += 1;
+ },
+ '/' => {
+ self.state = .tagN_end;
+ tag.kind = .end;
+ self.index += 1;
+ },
+ else => {
+ self.index += 1;
+ },
+ },
+ .tagN_end => switch (char) {
+ '>' => {
+ self.state = .begin;
+ tag.name = self.bytes[mark..self.index];
+ self.index += 1;
+ return Token{ .tag = tag };
+ },
+ else => {
+ return error.BadToken;
+ },
+ },
+ .tag_string => switch (char) {
+ '"' => {
+ self.state = .tagN;
+ self.index += 1;
+ },
+ else => {
+ self.index += 1;
+ },
+ },
+ .content => switch (char) {
+ '<' => {
+ self.state = .tag0;
+ content = self.bytes[mark..self.index];
+ self.index += 1;
+ tag = Tag{};
+ mark = self.index;
+ return Token{ .content = content };
+ },
+ '>' => {
+ return error.BadToken;
+ },
+ else => {
+ self.index += 1;
+ },
+ },
+ }
+ }
- .{ "8A428", "10.4.0" },
- .{ "8A432", "10.4.0" },
- .{ "8B15", "10.4.1" },
- .{ "8B17", "10.4.1" },
- .{ "8C46", "10.4.2" },
- .{ "8C47", "10.4.2" },
- .{ "8E102", "10.4.2" },
- .{ "8E45", "10.4.2" },
- .{ "8E90", "10.4.2" },
- .{ "8F46", "10.4.3" },
- .{ "8G32", "10.4.4" },
- .{ "8G1165", "10.4.4" },
- .{ "8H14", "10.4.5" },
- .{ "8G1454", "10.4.5" },
- .{ "8I127", "10.4.6" },
- .{ "8I1119", "10.4.6" },
- .{ "8J135", "10.4.7" },
- .{ "8J2135a", "10.4.7" },
- .{ "8K1079", "10.4.7" },
- .{ "8N5107", "10.4.7" },
- .{ "8L127", "10.4.8" },
- .{ "8L2127", "10.4.8" },
- .{ "8P135", "10.4.9" },
- .{ "8P2137", "10.4.9" },
- .{ "8R218", "10.4.10" },
- .{ "8R2218", "10.4.10" },
- .{ "8R2232", "10.4.10" },
- .{ "8S165", "10.4.11" },
- .{ "8S2167", "10.4.11" },
+ return null;
+ }
- .{ "9A581", "10.5.0" },
- .{ "9B18", "10.5.1" },
- .{ "9C31", "10.5.2" },
- .{ "9C7010", "10.5.2" },
- .{ "9D34", "10.5.3" },
- .{ "9E17", "10.5.4" },
- .{ "9F33", "10.5.5" },
- .{ "9G55", "10.5.6" },
- .{ "9G66", "10.5.6" },
- .{ "9J61", "10.5.7" },
- .{ "9L30", "10.5.8" },
+ fn expectContent(self: *@This()) ![]const u8 {
+ if (try self.next()) |tok| {
+ switch (tok) {
+ .content => |content| {
+ return content;
+ },
+ else => {},
+ }
+ }
+ return error.UnexpectedToken;
+ }
- .{ "10A432", "10.6.0" },
- .{ "10A433", "10.6.0" },
- .{ "10B504", "10.6.1" },
- .{ "10C540", "10.6.2" },
- .{ "10D573", "10.6.3" },
- .{ "10D575", "10.6.3" },
- .{ "10D578", "10.6.3" },
- .{ "10F569", "10.6.4" },
- .{ "10H574", "10.6.5" },
- .{ "10J567", "10.6.6" },
- .{ "10J869", "10.6.7" },
- .{ "10J3250", "10.6.7" },
- .{ "10J4138", "10.6.7" },
- .{ "10K540", "10.6.8" },
- .{ "10K549", "10.6.8" },
+ fn skipUntilTag(self: *@This(), kind: Tag.Kind, name: []const u8) !void {
+ while (try self.next()) |tok| {
+ switch (tok) {
+ .tag => |tag| {
+ if (tag.kind == kind and std.mem.eql(u8, tag.name, name)) return;
+ },
+ else => {},
+ }
+ }
+ return error.TagNotFound;
+ }
- .{ "11A511", "10.7.0" },
- .{ "11A511s", "10.7.0" },
- .{ "11A2061", "10.7.0" },
- .{ "11A2063", "10.7.0" },
- .{ "11B26", "10.7.1" },
- .{ "11B2118", "10.7.1" },
- .{ "11C74", "10.7.2" },
- .{ "11D50", "10.7.3" },
- .{ "11E53", "10.7.4" },
- .{ "11G56", "10.7.5" },
- .{ "11G63", "10.7.5" },
+ const State = enum {
+ begin,
+ tag0,
+ tag0_end_or_empty,
+ tagN,
+ tagN_end,
+ tag_string,
+ content,
+ };
- .{ "12A269", "10.8.0" },
- .{ "12B19", "10.8.1" },
- .{ "12C54", "10.8.2" },
- .{ "12C60", "10.8.2" },
- .{ "12C2034", "10.8.2" },
- .{ "12C3104", "10.8.2" },
- .{ "12D78", "10.8.3" },
- .{ "12E55", "10.8.4" },
- .{ "12E3067", "10.8.4" },
- .{ "12E4022", "10.8.4" },
- .{ "12F37", "10.8.5" },
- .{ "12F45", "10.8.5" },
- .{ "12F2501", "10.8.5" },
- .{ "12F2518", "10.8.5" },
- .{ "12F2542", "10.8.5" },
- .{ "12F2560", "10.8.5" },
+ const Token = union(enum) {
+ tag: Tag,
+ content: []const u8,
+ };
- .{ "13A603", "10.9.0" },
- .{ "13B42", "10.9.1" },
- .{ "13C64", "10.9.2" },
- .{ "13C1021", "10.9.2" },
- .{ "13D65", "10.9.3" },
- .{ "13E28", "10.9.4" },
- .{ "13F34", "10.9.5" },
- .{ "13F1066", "10.9.5" },
- .{ "13F1077", "10.9.5" },
- .{ "13F1096", "10.9.5" },
- .{ "13F1112", "10.9.5" },
- .{ "13F1134", "10.9.5" },
- .{ "13F1507", "10.9.5" },
- .{ "13F1603", "10.9.5" },
- .{ "13F1712", "10.9.5" },
- .{ "13F1808", "10.9.5" },
- .{ "13F1911", "10.9.5" },
+ const Tag = struct {
+ kind: Kind = .unknown,
+ name: []const u8 = "",
- .{ "14A389", "10.10.0" },
- .{ "14B25", "10.10.1" },
- .{ "14C109", "10.10.2" },
- .{ "14C1510", "10.10.2" },
- .{ "14C1514", "10.10.2" },
- .{ "14C2043", "10.10.2" },
- .{ "14C2513", "10.10.2" },
- .{ "14D131", "10.10.3" },
- .{ "14D136", "10.10.3" },
- .{ "14E46", "10.10.4" },
- .{ "14F27", "10.10.5" },
- .{ "14F1021", "10.10.5" },
- .{ "14F1505", "10.10.5" },
- .{ "14F1509", "10.10.5" },
- .{ "14F1605", "10.10.5" },
- .{ "14F1713", "10.10.5" },
- .{ "14F1808", "10.10.5" },
- .{ "14F1909", "10.10.5" },
- .{ "14F1912", "10.10.5" },
- .{ "14F2009", "10.10.5" },
- .{ "14F2109", "10.10.5" },
- .{ "14F2315", "10.10.5" },
- .{ "14F2411", "10.10.5" },
- .{ "14F2511", "10.10.5" },
+ const Kind = enum { unknown, start, end, empty };
+ };
+};
- .{ "15A284", "10.11.0" },
- .{ "15B42", "10.11.1" },
- .{ "15C50", "10.11.2" },
- .{ "15D21", "10.11.3" },
- .{ "15E65", "10.11.4" },
- .{ "15F34", "10.11.5" },
- .{ "15G31", "10.11.6" },
- .{ "15G1004", "10.11.6" },
- .{ "15G1011", "10.11.6" },
- .{ "15G1108", "10.11.6" },
- .{ "15G1212", "10.11.6" },
- .{ "15G1217", "10.11.6" },
- .{ "15G1421", "10.11.6" },
- .{ "15G1510", "10.11.6" },
- .{ "15G1611", "10.11.6" },
- .{ "15G17023", "10.11.6" },
- .{ "15G18013", "10.11.6" },
- .{ "15G19009", "10.11.6" },
- .{ "15G20015", "10.11.6" },
- .{ "15G21013", "10.11.6" },
- .{ "15G22010", "10.11.6" },
+test "detect" {
+ const cases = .{
+ .{
+ \\<?xml version="1.0" encoding="UTF-8"?>
+ \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ \\<plist version="1.0">
+ \\<dict>
+ \\ <key>ProductBuildVersion</key>
+ \\ <string>7B85</string>
+ \\ <key>ProductCopyright</key>
+ \\ <string>Apple Computer, Inc. 1983-2003</string>
+ \\ <key>ProductName</key>
+ \\ <string>Mac OS X</string>
+ \\ <key>ProductUserVisibleVersion</key>
+ \\ <string>10.3</string>
+ \\ <key>ProductVersion</key>
+ \\ <string>10.3</string>
+ \\</dict>
+ \\</plist>
+ ,
+ .{ .major = 10, .minor = 3 },
+ },
+ .{
+ \\<?xml version="1.0" encoding="UTF-8"?>
+ \\<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ \\<plist version="1.0">
+ \\<dict>
+ \\ <key>ProductBuildVersion</key>
+ \\ <string>7W98</string>
+ \\ <key>ProductCopyright</key>
+ \\ <string>Apple Computer, Inc. 1983-2004</string>
+ \\ <key>ProductName</key>
+ \\ <string>Mac OS X</string>
+ \\ <key>ProductUserVisibleVersion</key>
+ \\ <string>10.3.9</string>
+ \\ <key>ProductVersion</key>
+ \\ <string>10.3.9</string>
+ \\</dict>
+ \\</plist>
+ ,
+ .{ .major = 10, .minor = 3, .patch = 9 },
+ },
+ .{
+ \\<?xml version="1.0" encoding="UTF-8"?>
+ \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ \\<plist version="1.0">
+ \\<dict>
+ \\ <key>ProductBuildVersion</key>
+ \\ <string>19G68</string>
+ \\ <key>ProductCopyright</key>
+ \\ <string>1983-2020 Apple Inc.</string>
+ \\ <key>ProductName</key>
+ \\ <string>Mac OS X</string>
+ \\ <key>ProductUserVisibleVersion</key>
+ \\ <string>10.15.6</string>
+ \\ <key>ProductVersion</key>
+ \\ <string>10.15.6</string>
+ \\ <key>iOSSupportVersion</key>
+ \\ <string>13.6</string>
+ \\</dict>
+ \\</plist>
+ ,
+ .{ .major = 10, .minor = 15, .patch = 6 },
+ },
+ .{
+ \\<?xml version="1.0" encoding="UTF-8"?>
+ \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ \\<plist version="1.0">
+ \\<dict>
+ \\ <key>ProductBuildVersion</key>
+ \\ <string>20A2408</string>
+ \\ <key>ProductCopyright</key>
+ \\ <string>1983-2020 Apple Inc.</string>
+ \\ <key>ProductName</key>
+ \\ <string>macOS</string>
+ \\ <key>ProductUserVisibleVersion</key>
+ \\ <string>11.0</string>
+ \\ <key>ProductVersion</key>
+ \\ <string>11.0</string>
+ \\ <key>iOSSupportVersion</key>
+ \\ <string>14.2</string>
+ \\</dict>
+ \\</plist>
+ ,
+ .{ .major = 11, .minor = 0 },
+ },
+ .{
+ \\<?xml version="1.0" encoding="UTF-8"?>
+ \\<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ \\<plist version="1.0">
+ \\<dict>
+ \\ <key>ProductBuildVersion</key>
+ \\ <string>20C63</string>
+ \\ <key>ProductCopyright</key>
+ \\ <string>1983-2020 Apple Inc.</string>
+ \\ <key>ProductName</key>
+ \\ <string>macOS</string>
+ \\ <key>ProductUserVisibleVersion</key>
+ \\ <string>11.1</string>
+ \\ <key>ProductVersion</key>
+ \\ <string>11.1</string>
+ \\ <key>iOSSupportVersion</key>
+ \\ <string>14.3</string>
+ \\</dict>
+ \\</plist>
+ ,
+ .{ .major = 11, .minor = 1 },
+ },
+ };
- .{ "16A323", "10.12.0" },
- .{ "16B2555", "10.12.1" },
- .{ "16B2657", "10.12.1" },
- .{ "16C67", "10.12.2" },
- .{ "16C68", "10.12.2" },
- .{ "16D32", "10.12.3" },
- .{ "16E195", "10.12.4" },
- .{ "16F73", "10.12.5" },
- .{ "16F2073", "10.12.5" },
- .{ "16G29", "10.12.6" },
- .{ "16G1036", "10.12.6" },
- .{ "16G1114", "10.12.6" },
- .{ "16G1212", "10.12.6" },
- .{ "16G1314", "10.12.6" },
- .{ "16G1408", "10.12.6" },
- .{ "16G1510", "10.12.6" },
- .{ "16G1618", "10.12.6" },
- .{ "16G1710", "10.12.6" },
- .{ "16G1815", "10.12.6" },
- .{ "16G1917", "10.12.6" },
- .{ "16G1918", "10.12.6" },
- .{ "16G2016", "10.12.6" },
- .{ "16G2127", "10.12.6" },
- .{ "16G2128", "10.12.6" },
- .{ "16G2136", "10.12.6" },
+ inline for (cases) |case| {
+ const ver0 = try parseSystemVersion(case[0]);
+ const ver1: std.builtin.Version = case[1];
+ try testVersionEquality(ver1, ver0);
+ }
+}
- .{ "17A365", "10.13.0" },
- .{ "17A405", "10.13.0" },
- .{ "17B48", "10.13.1" },
- .{ "17B1002", "10.13.1" },
- .{ "17B1003", "10.13.1" },
- .{ "17C88", "10.13.2" },
- .{ "17C89", "10.13.2" },
- .{ "17C205", "10.13.2" },
- .{ "17C2205", "10.13.2" },
- .{ "17D47", "10.13.3" },
- .{ "17D2047", "10.13.3" },
- .{ "17D102", "10.13.3" },
- .{ "17D2102", "10.13.3" },
- .{ "17E199", "10.13.4" },
- .{ "17E202", "10.13.4" },
- .{ "17F77", "10.13.5" },
- .{ "17G65", "10.13.6" },
- .{ "17G2208", "10.13.6" },
- .{ "17G3025", "10.13.6" },
- .{ "17G4015", "10.13.6" },
- .{ "17G5019", "10.13.6" },
- .{ "17G6029", "10.13.6" },
- .{ "17G6030", "10.13.6" },
- .{ "17G7024", "10.13.6" },
- .{ "17G8029", "10.13.6" },
- .{ "17G8030", "10.13.6" },
- .{ "17G8037", "10.13.6" },
- .{ "17G9016", "10.13.6" },
- .{ "17G10021", "10.13.6" },
- .{ "17G11023", "10.13.6" },
- .{ "17G12034", "10.13.6" },
+fn testVersionEquality(expected: std.builtin.Version, got: std.builtin.Version) !void {
+ var b_expected: [64]u8 = undefined;
+ const s_expected: []const u8 = try std.fmt.bufPrint(b_expected[0..], "{}", .{expected});
- .{ "18A391", "10.14.0" },
- .{ "18B75", "10.14.1" },
- .{ "18B2107", "10.14.1" },
- .{ "18B3094", "10.14.1" },
- .{ "18C54", "10.14.2" },
- .{ "18D42", "10.14.3" },
- .{ "18D43", "10.14.3" },
- .{ "18D109", "10.14.3" },
- .{ "18E226", "10.14.4" },
- .{ "18E227", "10.14.4" },
- .{ "18F132", "10.14.5" },
- .{ "18G84", "10.14.6" },
- .{ "18G87", "10.14.6" },
- .{ "18G95", "10.14.6" },
- .{ "18G103", "10.14.6" },
- .{ "18G1012", "10.14.6" },
- .{ "18G2022", "10.14.6" },
- .{ "18G3020", "10.14.6" },
- .{ "18G4032", "10.14.6" },
+ var b_got: [64]u8 = undefined;
+ const s_got: []const u8 = try std.fmt.bufPrint(b_got[0..], "{}", .{got});
- .{ "19A583", "10.15.0" },
- .{ "19A602", "10.15.0" },
- .{ "19A603", "10.15.0" },
- .{ "19B88", "10.15.1" },
- .{ "19C57", "10.15.2" },
- .{ "19D76", "10.15.3" },
- .{ "19E266", "10.15.4" },
- .{ "19E287", "10.15.4" },
- };
- for (known) |pair| {
- var buf: [32]u8 = undefined;
- const ver = try version_from_build(pair[0]);
- const sver = try std.fmt.bufPrint(buf[0..], "{}.{}.{}", .{ ver.major, ver.minor, ver.patch });
- std.testing.expect(std.mem.eql(u8, sver, pair[1]));
- }
+ testing.expectEqualStrings(s_expected, s_got);
}
/// Detect SDK path on Darwin.
@@ -468,7 +418,7 @@ pub fn getSDKPath(allocator: *mem.Allocator) ![]u8 {
allocator.free(result.stdout);
}
if (result.stderr.len != 0) {
- std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {}", .{result.stderr});
+ std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {s}", .{result.stderr});
}
if (result.term.Exited != 0) {
return error.ProcessTerminated;
diff --git a/lib/std/zig/system/windows.zig b/lib/std/zig/system/windows.zig
new file mode 100644
index 0000000000..d32b28f607
--- /dev/null
+++ b/lib/std/zig/system/windows.zig
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("std");
+
+pub const WindowsVersion = std.Target.Os.WindowsVersion;
+
+/// Returns the highest known WindowsVersion deduced from reported runtime information.
+/// Discards information about in-between versions we don't differentiate.
+pub fn detectRuntimeVersion() WindowsVersion {
+ var version_info: std.os.windows.RTL_OSVERSIONINFOW = undefined;
+ version_info.dwOSVersionInfoSize = @sizeOf(@TypeOf(version_info));
+
+ switch (std.os.windows.ntdll.RtlGetVersion(&version_info)) {
+ .SUCCESS => {},
+ else => unreachable,
+ }
+
+ // Starting from the system infos build a NTDDI-like version
+ // constant whose format is:
+ // B0 B1 B2 B3
+ // `---` `` ``--> Sub-version (Starting from Windows 10 onwards)
+ // \ `--> Service pack (Always zero in the constants defined)
+ // `--> OS version (Major & minor)
+ const os_ver: u16 = @intCast(u16, version_info.dwMajorVersion & 0xff) << 8 |
+ @intCast(u16, version_info.dwMinorVersion & 0xff);
+ const sp_ver: u8 = 0;
+ const sub_ver: u8 = if (os_ver >= 0x0A00) subver: {
+ // There's no other way to obtain this info beside
+ // checking the build number against a known set of
+ // values
+ var last_idx: usize = 0;
+ for (WindowsVersion.known_win10_build_numbers) |build, i| {
+ if (version_info.dwBuildNumber >= build)
+ last_idx = i;
+ }
+ break :subver @truncate(u8, last_idx);
+ } else 0;
+
+ const version: u32 = @as(u32, os_ver) << 16 | @as(u16, sp_ver) << 8 | sub_ver;
+
+ return @intToEnum(WindowsVersion, version);
+}
diff --git a/lib/std/zig/system/x86.zig b/lib/std/zig/system/x86.zig
index cbede308dc..bda9a17c95 100644
--- a/lib/std/zig/system/x86.zig
+++ b/lib/std/zig/system/x86.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -19,11 +19,11 @@ fn setFeature(cpu: *Target.Cpu, feature: Target.x86.Feature, enabled: bool) void
if (enabled) cpu.features.addFeature(idx) else cpu.features.removeFeature(idx);
}
-inline fn bit(input: u32, offset: u5) bool {
+fn bit(input: u32, offset: u5) callconv(.Inline) bool {
return (input >> offset) & 1 != 0;
}
-inline fn hasMask(input: u32, mask: u32) bool {
+fn hasMask(input: u32, mask: u32) callconv(.Inline) bool {
return (input & mask) == mask;
}
diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig
index a0e2806e9a..88feabd021 100644
--- a/lib/std/zig/tokenizer.zig
+++ b/lib/std/zig/tokenizer.zig
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
+// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
@@ -7,7 +7,7 @@ const std = @import("../std.zig");
const mem = std.mem;
pub const Token = struct {
- id: Id,
+ tag: Tag,
loc: Loc,
pub const Loc = struct {
@@ -15,315 +15,315 @@ pub const Token = struct {
end: usize,
};
- pub const keywords = std.ComptimeStringMap(Id, .{
- .{ "align", .Keyword_align },
- .{ "allowzero", .Keyword_allowzero },
- .{ "and", .Keyword_and },
- .{ "anyframe", .Keyword_anyframe },
- .{ "anytype", .Keyword_anytype },
- .{ "asm", .Keyword_asm },
- .{ "async", .Keyword_async },
- .{ "await", .Keyword_await },
- .{ "break", .Keyword_break },
- .{ "callconv", .Keyword_callconv },
- .{ "catch", .Keyword_catch },
- .{ "comptime", .Keyword_comptime },
- .{ "const", .Keyword_const },
- .{ "continue", .Keyword_continue },
- .{ "defer", .Keyword_defer },
- .{ "else", .Keyword_else },
- .{ "enum", .Keyword_enum },
- .{ "errdefer", .Keyword_errdefer },
- .{ "error", .Keyword_error },
- .{ "export", .Keyword_export },
- .{ "extern", .Keyword_extern },
- .{ "false", .Keyword_false },
- .{ "fn", .Keyword_fn },
- .{ "for", .Keyword_for },
- .{ "if", .Keyword_if },
- .{ "inline", .Keyword_inline },
- .{ "noalias", .Keyword_noalias },
- .{ "noasync", .Keyword_nosuspend }, // TODO: remove this
- .{ "noinline", .Keyword_noinline },
- .{ "nosuspend", .Keyword_nosuspend },
- .{ "null", .Keyword_null },
- .{ "opaque", .Keyword_opaque },
- .{ "or", .Keyword_or },
- .{ "orelse", .Keyword_orelse },
- .{ "packed", .Keyword_packed },
- .{ "pub", .Keyword_pub },
- .{ "resume", .Keyword_resume },
- .{ "return", .Keyword_return },
- .{ "linksection", .Keyword_linksection },
- .{ "struct", .Keyword_struct },
- .{ "suspend", .Keyword_suspend },
- .{ "switch", .Keyword_switch },
- .{ "test", .Keyword_test },
- .{ "threadlocal", .Keyword_threadlocal },
- .{ "true", .Keyword_true },
- .{ "try", .Keyword_try },
- .{ "undefined", .Keyword_undefined },
- .{ "union", .Keyword_union },
- .{ "unreachable", .Keyword_unreachable },
- .{ "usingnamespace", .Keyword_usingnamespace },
- .{ "var", .Keyword_var },
- .{ "volatile", .Keyword_volatile },
- .{ "while", .Keyword_while },
+ pub const keywords = std.ComptimeStringMap(Tag, .{
+ .{ "align", .keyword_align },
+ .{ "allowzero", .keyword_allowzero },
+ .{ "and", .keyword_and },
+ .{ "anyframe", .keyword_anyframe },
+ .{ "anytype", .keyword_anytype },
+ .{ "asm", .keyword_asm },
+ .{ "async", .keyword_async },
+ .{ "await", .keyword_await },
+ .{ "break", .keyword_break },
+ .{ "callconv", .keyword_callconv },
+ .{ "catch", .keyword_catch },
+ .{ "comptime", .keyword_comptime },
+ .{ "const", .keyword_const },
+ .{ "continue", .keyword_continue },
+ .{ "defer", .keyword_defer },
+ .{ "else", .keyword_else },
+ .{ "enum", .keyword_enum },
+ .{ "errdefer", .keyword_errdefer },
+ .{ "error", .keyword_error },
+ .{ "export", .keyword_export },
+ .{ "extern", .keyword_extern },
+ .{ "false", .keyword_false },
+ .{ "fn", .keyword_fn },
+ .{ "for", .keyword_for },
+ .{ "if", .keyword_if },
+ .{ "inline", .keyword_inline },
+ .{ "noalias", .keyword_noalias },
+ .{ "noinline", .keyword_noinline },
+ .{ "nosuspend", .keyword_nosuspend },
+ .{ "null", .keyword_null },
+ .{ "opaque", .keyword_opaque },
+ .{ "or", .keyword_or },
+ .{ "orelse", .keyword_orelse },
+ .{ "packed", .keyword_packed },
+ .{ "pub", .keyword_pub },
+ .{ "resume", .keyword_resume },
+ .{ "return", .keyword_return },
+ .{ "linksection", .keyword_linksection },
+ .{ "struct", .keyword_struct },
+ .{ "suspend", .keyword_suspend },
+ .{ "switch", .keyword_switch },
+ .{ "test", .keyword_test },
+ .{ "threadlocal", .keyword_threadlocal },
+ .{ "true", .keyword_true },
+ .{ "try", .keyword_try },
+ .{ "undefined", .keyword_undefined },
+ .{ "union", .keyword_union },
+ .{ "unreachable", .keyword_unreachable },
+ .{ "usingnamespace", .keyword_usingnamespace },
+ .{ "var", .keyword_var },
+ .{ "volatile", .keyword_volatile },
+ .{ "while", .keyword_while },
});
- pub fn getKeyword(bytes: []const u8) ?Id {
+ pub fn getKeyword(bytes: []const u8) ?Tag {
return keywords.get(bytes);
}
- pub const Id = enum {
- Invalid,
- Invalid_ampersands,
- Invalid_periodasterisks,
- Identifier,
- StringLiteral,
- MultilineStringLiteralLine,
- CharLiteral,
- Eof,
- Builtin,
- Bang,
- Pipe,
- PipePipe,
- PipeEqual,
- Equal,
- EqualEqual,
- EqualAngleBracketRight,
- BangEqual,
- LParen,
- RParen,
- Semicolon,
- Percent,
- PercentEqual,
- LBrace,
- RBrace,
- LBracket,
- RBracket,
- Period,
- PeriodAsterisk,
- Ellipsis2,
- Ellipsis3,
- Caret,
- CaretEqual,
- Plus,
- PlusPlus,
- PlusEqual,
- PlusPercent,
- PlusPercentEqual,
- Minus,
- MinusEqual,
- MinusPercent,
- MinusPercentEqual,
- Asterisk,
- AsteriskEqual,
- AsteriskAsterisk,
- AsteriskPercent,
- AsteriskPercentEqual,
- Arrow,
- Colon,
- Slash,
- SlashEqual,
- Comma,
- Ampersand,
- AmpersandEqual,
- QuestionMark,
- AngleBracketLeft,
- AngleBracketLeftEqual,
- AngleBracketAngleBracketLeft,
- AngleBracketAngleBracketLeftEqual,
- AngleBracketRight,
- AngleBracketRightEqual,
- AngleBracketAngleBracketRight,
- AngleBracketAngleBracketRightEqual,
- Tilde,
- IntegerLiteral,
- FloatLiteral,
- LineComment,
- DocComment,
- ContainerDocComment,
- ShebangLine,
- Keyword_align,
- Keyword_allowzero,
- Keyword_and,
- Keyword_anyframe,
- Keyword_anytype,
- Keyword_asm,
- Keyword_async,
- Keyword_await,
- Keyword_break,
- Keyword_callconv,
- Keyword_catch,
- Keyword_comptime,
- Keyword_const,
- Keyword_continue,
- Keyword_defer,
- Keyword_else,
- Keyword_enum,
- Keyword_errdefer,
- Keyword_error,
- Keyword_export,
- Keyword_extern,
- Keyword_false,
- Keyword_fn,
- Keyword_for,
- Keyword_if,
- Keyword_inline,
- Keyword_noalias,
- Keyword_noinline,
- Keyword_nosuspend,
- Keyword_null,
- Keyword_opaque,
- Keyword_or,
- Keyword_orelse,
- Keyword_packed,
- Keyword_pub,
- Keyword_resume,
- Keyword_return,
- Keyword_linksection,
- Keyword_struct,
- Keyword_suspend,
- Keyword_switch,
- Keyword_test,
- Keyword_threadlocal,
- Keyword_true,
- Keyword_try,
- Keyword_undefined,
- Keyword_union,
- Keyword_unreachable,
- Keyword_usingnamespace,
- Keyword_var,
- Keyword_volatile,
- Keyword_while,
-
- pub fn symbol(id: Id) []const u8 {
- return switch (id) {
- .Invalid => "Invalid",
- .Invalid_ampersands => "&&",
- .Invalid_periodasterisks => ".**",
- .Identifier => "Identifier",
- .StringLiteral => "StringLiteral",
- .MultilineStringLiteralLine => "MultilineStringLiteralLine",
- .CharLiteral => "CharLiteral",
- .Eof => "Eof",
- .Builtin => "Builtin",
- .IntegerLiteral => "IntegerLiteral",
- .FloatLiteral => "FloatLiteral",
- .LineComment => "LineComment",
- .DocComment => "DocComment",
- .ContainerDocComment => "ContainerDocComment",
- .ShebangLine => "ShebangLine",
-
- .Bang => "!",
- .Pipe => "|",
- .PipePipe => "||",
- .PipeEqual => "|=",
- .Equal => "=",
- .EqualEqual => "==",
- .EqualAngleBracketRight => "=>",
- .BangEqual => "!=",
- .LParen => "(",
- .RParen => ")",
- .Semicolon => ";",
- .Percent => "%",
- .PercentEqual => "%=",
- .LBrace => "{",
- .RBrace => "}",
- .LBracket => "[",
- .RBracket => "]",
- .Period => ".",
- .PeriodAsterisk => ".*",
- .Ellipsis2 => "..",
- .Ellipsis3 => "...",
- .Caret => "^",
- .CaretEqual => "^=",
- .Plus => "+",
- .PlusPlus => "++",
- .PlusEqual => "+=",
- .PlusPercent => "+%",
- .PlusPercentEqual => "+%=",
- .Minus => "-",
- .MinusEqual => "-=",
- .MinusPercent => "-%",
- .MinusPercentEqual => "-%=",
- .Asterisk => "*",
- .AsteriskEqual => "*=",
- .AsteriskAsterisk => "**",
- .AsteriskPercent => "*%",
- .AsteriskPercentEqual => "*%=",
- .Arrow => "->",
- .Colon => ":",
- .Slash => "/",
- .SlashEqual => "/=",
- .Comma => ",",
- .Ampersand => "&",
- .AmpersandEqual => "&=",
- .QuestionMark => "?",
- .AngleBracketLeft => "<",
- .AngleBracketLeftEqual => "<=",
- .AngleBracketAngleBracketLeft => "<<",
- .AngleBracketAngleBracketLeftEqual => "<<=",
- .AngleBracketRight => ">",
- .AngleBracketRightEqual => ">=",
- .AngleBracketAngleBracketRight => ">>",
- .AngleBracketAngleBracketRightEqual => ">>=",
- .Tilde => "~",
- .Keyword_align => "align",
- .Keyword_allowzero => "allowzero",
- .Keyword_and => "and",
- .Keyword_anyframe => "anyframe",
- .Keyword_anytype => "anytype",
- .Keyword_asm => "asm",
- .Keyword_async => "async",
- .Keyword_await => "await",
- .Keyword_break => "break",
- .Keyword_callconv => "callconv",
- .Keyword_catch => "catch",
- .Keyword_comptime => "comptime",
- .Keyword_const => "const",
- .Keyword_continue => "continue",
- .Keyword_defer => "defer",
- .Keyword_else => "else",
- .Keyword_enum => "enum",
- .Keyword_errdefer => "errdefer",
- .Keyword_error => "error",
- .Keyword_export => "export",
- .Keyword_extern => "extern",
- .Keyword_false => "false",
- .Keyword_fn => "fn",
- .Keyword_for => "for",
- .Keyword_if => "if",
- .Keyword_inline => "inline",
- .Keyword_noalias => "noalias",
- .Keyword_noinline => "noinline",
- .Keyword_nosuspend => "nosuspend",
- .Keyword_null => "null",
- .Keyword_opaque => "opaque",
- .Keyword_or => "or",
- .Keyword_orelse => "orelse",
- .Keyword_packed => "packed",
- .Keyword_pub => "pub",
- .Keyword_resume => "resume",
- .Keyword_return => "return",
- .Keyword_linksection => "linksection",
- .Keyword_struct => "struct",
- .Keyword_suspend => "suspend",
- .Keyword_switch => "switch",
- .Keyword_test => "test",
- .Keyword_threadlocal => "threadlocal",
- .Keyword_true => "true",
- .Keyword_try => "try",
- .Keyword_undefined => "undefined",
- .Keyword_union => "union",
- .Keyword_unreachable => "unreachable",
- .Keyword_usingnamespace => "usingnamespace",
- .Keyword_var => "var",
- .Keyword_volatile => "volatile",
- .Keyword_while => "while",
+ pub const Tag = enum {
+ invalid,
+ invalid_ampersands,
+ invalid_periodasterisks,
+ identifier,
+ string_literal,
+ multiline_string_literal_line,
+ char_literal,
+ eof,
+ builtin,
+ bang,
+ pipe,
+ pipe_pipe,
+ pipe_equal,
+ equal,
+ equal_equal,
+ equal_angle_bracket_right,
+ bang_equal,
+ l_paren,
+ r_paren,
+ semicolon,
+ percent,
+ percent_equal,
+ l_brace,
+ r_brace,
+ l_bracket,
+ r_bracket,
+ period,
+ period_asterisk,
+ ellipsis2,
+ ellipsis3,
+ caret,
+ caret_equal,
+ plus,
+ plus_plus,
+ plus_equal,
+ plus_percent,
+ plus_percent_equal,
+ minus,
+ minus_equal,
+ minus_percent,
+ minus_percent_equal,
+ asterisk,
+ asterisk_equal,
+ asterisk_asterisk,
+ asterisk_percent,
+ asterisk_percent_equal,
+ arrow,
+ colon,
+ slash,
+ slash_equal,
+ comma,
+ ampersand,
+ ampersand_equal,
+ question_mark,
+ angle_bracket_left,
+ angle_bracket_left_equal,
+ angle_bracket_angle_bracket_left,
+ angle_bracket_angle_bracket_left_equal,
+ angle_bracket_right,
+ angle_bracket_right_equal,
+ angle_bracket_angle_bracket_right,
+ angle_bracket_angle_bracket_right_equal,
+ tilde,
+ integer_literal,
+ float_literal,
+ doc_comment,
+ container_doc_comment,
+ keyword_align,
+ keyword_allowzero,
+ keyword_and,
+ keyword_anyframe,
+ keyword_anytype,
+ keyword_asm,
+ keyword_async,
+ keyword_await,
+ keyword_break,
+ keyword_callconv,
+ keyword_catch,
+ keyword_comptime,
+ keyword_const,
+ keyword_continue,
+ keyword_defer,
+ keyword_else,
+ keyword_enum,
+ keyword_errdefer,
+ keyword_error,
+ keyword_export,
+ keyword_extern,
+ keyword_false,
+ keyword_fn,
+ keyword_for,
+ keyword_if,
+ keyword_inline,
+ keyword_noalias,
+ keyword_noinline,
+ keyword_nosuspend,
+ keyword_null,
+ keyword_opaque,
+ keyword_or,
+ keyword_orelse,
+ keyword_packed,
+ keyword_pub,
+ keyword_resume,
+ keyword_return,
+ keyword_linksection,
+ keyword_struct,
+ keyword_suspend,
+ keyword_switch,
+ keyword_test,
+ keyword_threadlocal,
+ keyword_true,
+ keyword_try,
+ keyword_undefined,
+ keyword_union,
+ keyword_unreachable,
+ keyword_usingnamespace,
+ keyword_var,
+ keyword_volatile,
+ keyword_while,
+
+ pub fn lexeme(tag: Tag) ?[]const u8 {
+ return switch (tag) {
+ .invalid,
+ .identifier,
+ .string_literal,
+ .multiline_string_literal_line,
+ .char_literal,
+ .eof,
+ .builtin,
+ .integer_literal,
+ .float_literal,
+ .doc_comment,
+ .container_doc_comment,
+ => null,
+
+ .invalid_ampersands => "&&",
+ .invalid_periodasterisks => ".**",
+ .bang => "!",
+ .pipe => "|",
+ .pipe_pipe => "||",
+ .pipe_equal => "|=",
+ .equal => "=",
+ .equal_equal => "==",
+ .equal_angle_bracket_right => "=>",
+ .bang_equal => "!=",
+ .l_paren => "(",
+ .r_paren => ")",
+ .semicolon => ";",
+ .percent => "%",
+ .percent_equal => "%=",
+ .l_brace => "{",
+ .r_brace => "}",
+ .l_bracket => "[",
+ .r_bracket => "]",
+ .period => ".",
+ .period_asterisk => ".*",
+ .ellipsis2 => "..",
+ .ellipsis3 => "...",
+ .caret => "^",
+ .caret_equal => "^=",
+ .plus => "+",
+ .plus_plus => "++",
+ .plus_equal => "+=",
+ .plus_percent => "+%",
+ .plus_percent_equal => "+%=",
+ .minus => "-",
+ .minus_equal => "-=",
+ .minus_percent => "-%",
+ .minus_percent_equal => "-%=",
+ .asterisk => "*",
+ .asterisk_equal => "*=",
+ .asterisk_asterisk => "**",
+ .asterisk_percent => "*%",
+ .asterisk_percent_equal => "*%=",
+ .arrow => "->",
+ .colon => ":",
+ .slash => "/",
+ .slash_equal => "/=",
+ .comma => ",",
+ .ampersand => "&",
+ .ampersand_equal => "&=",
+ .question_mark => "?",
+ .angle_bracket_left => "<",
+ .angle_bracket_left_equal => "<=",
+ .angle_bracket_angle_bracket_left => "<<",
+ .angle_bracket_angle_bracket_left_equal => "<<=",
+ .angle_bracket_right => ">",
+ .angle_bracket_right_equal => ">=",
+ .angle_bracket_angle_bracket_right => ">>",
+ .angle_bracket_angle_bracket_right_equal => ">>=",
+ .tilde => "~",
+ .keyword_align => "align",
+ .keyword_allowzero => "allowzero",
+ .keyword_and => "and",
+ .keyword_anyframe => "anyframe",
+ .keyword_anytype => "anytype",
+ .keyword_asm => "asm",
+ .keyword_async => "async",
+ .keyword_await => "await",
+ .keyword_break => "break",
+ .keyword_callconv => "callconv",
+ .keyword_catch => "catch",
+ .keyword_comptime => "comptime",
+ .keyword_const => "const",
+ .keyword_continue => "continue",
+ .keyword_defer => "defer",
+ .keyword_else => "else",
+ .keyword_enum => "enum",
+ .keyword_errdefer => "errdefer",
+ .keyword_error => "error",
+ .keyword_export => "export",
+ .keyword_extern => "extern",
+ .keyword_false => "false",
+ .keyword_fn => "fn",
+ .keyword_for => "for",
+ .keyword_if => "if",
+ .keyword_inline => "inline",
+ .keyword_noalias => "noalias",
+ .keyword_noinline => "noinline",
+ .keyword_nosuspend => "nosuspend",
+ .keyword_null => "null",
+ .keyword_opaque => "opaque",
+ .keyword_or => "or",
+ .keyword_orelse => "orelse",
+ .keyword_packed => "packed",
+ .keyword_pub => "pub",
+ .keyword_resume => "resume",
+ .keyword_return => "return",
+ .keyword_linksection => "linksection",
+ .keyword_struct => "struct",
+ .keyword_suspend => "suspend",
+ .keyword_switch => "switch",
+ .keyword_test => "test",
+ .keyword_threadlocal => "threadlocal",
+ .keyword_true => "true",
+ .keyword_try => "try",
+ .keyword_undefined => "undefined",
+ .keyword_union => "union",
+ .keyword_unreachable => "unreachable",
+ .keyword_usingnamespace => "usingnamespace",
+ .keyword_var => "var",
+ .keyword_volatile => "volatile",
+ .keyword_while => "while",
};
}
+
+ pub fn symbol(tag: Tag) []const u8 {
+ return tag.lexeme() orelse @tagName(tag);
+ }
};
};
@@ -334,7 +334,7 @@ pub const Tokenizer = struct {
/// For debugging purposes
pub fn dump(self: *Tokenizer, token: *const Token) void {
- std.debug.warn("{} \"{}\"\n", .{ @tagName(token.id), self.buffer[token.start..token.end] });
+ std.debug.warn("{s} \"{s}\"\n", .{ @tagName(token.tag), self.buffer[token.start..token.end] });
}
pub fn init(buffer: []const u8) Tokenizer {
@@ -421,7 +421,7 @@ pub const Tokenizer = struct {
const start_index = self.index;
var state: State = .start;
var result = Token{
- .id = .Eof,
+ .tag = .eof,
.loc = .{
.start = self.index,
.end = undefined,
@@ -438,14 +438,14 @@ pub const Tokenizer = struct {
},
'"' => {
state = .string_literal;
- result.id = .StringLiteral;
+ result.tag = .string_literal;
},
'\'' => {
state = .char_literal;
},
'a'...'z', 'A'...'Z', '_' => {
state = .identifier;
- result.id = .Identifier;
+ result.tag = .identifier;
},
'@' => {
state = .saw_at_sign;
@@ -460,42 +460,42 @@ pub const Tokenizer = struct {
state = .pipe;
},
'(' => {
- result.id = .LParen;
+ result.tag = .l_paren;
self.index += 1;
break;
},
')' => {
- result.id = .RParen;
+ result.tag = .r_paren;
self.index += 1;
break;
},
'[' => {
- result.id = .LBracket;
+ result.tag = .l_bracket;
self.index += 1;
break;
},
']' => {
- result.id = .RBracket;
+ result.tag = .r_bracket;
self.index += 1;
break;
},
';' => {
- result.id = .Semicolon;
+ result.tag = .semicolon;
self.index += 1;
break;
},
',' => {
- result.id = .Comma;
+ result.tag = .comma;
self.index += 1;
break;
},
'?' => {
- result.id = .QuestionMark;
+ result.tag = .question_mark;
self.index += 1;
break;
},
':' => {
- result.id = .Colon;
+ result.tag = .colon;
self.index += 1;
break;
},
@@ -519,20 +519,20 @@ pub const Tokenizer = struct {
},
'\\' => {
state = .backslash;
- result.id = .MultilineStringLiteralLine;
+ result.tag = .multiline_string_literal_line;
},
'{' => {
- result.id = .LBrace;
+ result.tag = .l_brace;
self.index += 1;
break;
},
'}' => {
- result.id = .RBrace;
+ result.tag = .r_brace;
self.index += 1;
break;
},
'~' => {
- result.id = .Tilde;
+ result.tag = .tilde;
self.index += 1;
break;
},
@@ -550,14 +550,14 @@ pub const Tokenizer = struct {
},
'0' => {
state = .zero;
- result.id = .IntegerLiteral;
+ result.tag = .integer_literal;
},
'1'...'9' => {
state = .int_literal_dec;
- result.id = .IntegerLiteral;
+ result.tag = .integer_literal;
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
self.index += 1;
break;
},
@@ -565,42 +565,42 @@ pub const Tokenizer = struct {
.saw_at_sign => switch (c) {
'"' => {
- result.id = .Identifier;
+ result.tag = .identifier;
state = .string_literal;
},
else => {
// reinterpret as a builtin
self.index -= 1;
state = .builtin;
- result.id = .Builtin;
+ result.tag = .builtin;
},
},
.ampersand => switch (c) {
'&' => {
- result.id = .Invalid_ampersands;
+ result.tag = .invalid_ampersands;
self.index += 1;
break;
},
'=' => {
- result.id = .AmpersandEqual;
+ result.tag = .ampersand_equal;
self.index += 1;
break;
},
else => {
- result.id = .Ampersand;
+ result.tag = .ampersand;
break;
},
},
.asterisk => switch (c) {
'=' => {
- result.id = .AsteriskEqual;
+ result.tag = .asterisk_equal;
self.index += 1;
break;
},
'*' => {
- result.id = .AsteriskAsterisk;
+ result.tag = .asterisk_asterisk;
self.index += 1;
break;
},
@@ -608,43 +608,43 @@ pub const Tokenizer = struct {
state = .asterisk_percent;
},
else => {
- result.id = .Asterisk;
+ result.tag = .asterisk;
break;
},
},
.asterisk_percent => switch (c) {
'=' => {
- result.id = .AsteriskPercentEqual;
+ result.tag = .asterisk_percent_equal;
self.index += 1;
break;
},
else => {
- result.id = .AsteriskPercent;
+ result.tag = .asterisk_percent;
break;
},
},
.percent => switch (c) {
'=' => {
- result.id = .PercentEqual;
+ result.tag = .percent_equal;
self.index += 1;
break;
},
else => {
- result.id = .Percent;
+ result.tag = .percent;
break;
},
},
.plus => switch (c) {
'=' => {
- result.id = .PlusEqual;
+ result.tag = .plus_equal;
self.index += 1;
break;
},
'+' => {
- result.id = .PlusPlus;
+ result.tag = .plus_plus;
self.index += 1;
break;
},
@@ -652,31 +652,31 @@ pub const Tokenizer = struct {
state = .plus_percent;
},
else => {
- result.id = .Plus;
+ result.tag = .plus;
break;
},
},
.plus_percent => switch (c) {
'=' => {
- result.id = .PlusPercentEqual;
+ result.tag = .plus_percent_equal;
self.index += 1;
break;
},
else => {
- result.id = .PlusPercent;
+ result.tag = .plus_percent;
break;
},
},
.caret => switch (c) {
'=' => {
- result.id = .CaretEqual;
+ result.tag = .caret_equal;
self.index += 1;
break;
},
else => {
- result.id = .Caret;
+ result.tag = .caret;
break;
},
},
@@ -684,8 +684,8 @@ pub const Tokenizer = struct {
.identifier => switch (c) {
'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
else => {
- if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |id| {
- result.id = id;
+ if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |tag| {
+ result.tag = tag;
}
break;
},
@@ -724,7 +724,7 @@ pub const Tokenizer = struct {
state = .char_literal_backslash;
},
'\'', 0x80...0xbf, 0xf8...0xff => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
0xc0...0xdf => { // 110xxxxx
@@ -746,7 +746,7 @@ pub const Tokenizer = struct {
.char_literal_backslash => switch (c) {
'\n' => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
'x' => {
@@ -769,7 +769,7 @@ pub const Tokenizer = struct {
}
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
},
@@ -780,7 +780,7 @@ pub const Tokenizer = struct {
seen_escape_digits = 0;
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
state = .char_literal_unicode_invalid;
},
},
@@ -791,14 +791,14 @@ pub const Tokenizer = struct {
},
'}' => {
if (seen_escape_digits == 0) {
- result.id = .Invalid;
+ result.tag = .invalid;
state = .char_literal_unicode_invalid;
} else {
state = .char_literal_end;
}
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
state = .char_literal_unicode_invalid;
},
},
@@ -813,12 +813,12 @@ pub const Tokenizer = struct {
.char_literal_end => switch (c) {
'\'' => {
- result.id = .CharLiteral;
+ result.tag = .char_literal;
self.index += 1;
break;
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
},
@@ -831,7 +831,7 @@ pub const Tokenizer = struct {
}
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
},
@@ -847,58 +847,58 @@ pub const Tokenizer = struct {
.bang => switch (c) {
'=' => {
- result.id = .BangEqual;
+ result.tag = .bang_equal;
self.index += 1;
break;
},
else => {
- result.id = .Bang;
+ result.tag = .bang;
break;
},
},
.pipe => switch (c) {
'=' => {
- result.id = .PipeEqual;
+ result.tag = .pipe_equal;
self.index += 1;
break;
},
'|' => {
- result.id = .PipePipe;
+ result.tag = .pipe_pipe;
self.index += 1;
break;
},
else => {
- result.id = .Pipe;
+ result.tag = .pipe;
break;
},
},
.equal => switch (c) {
'=' => {
- result.id = .EqualEqual;
+ result.tag = .equal_equal;
self.index += 1;
break;
},
'>' => {
- result.id = .EqualAngleBracketRight;
+ result.tag = .equal_angle_bracket_right;
self.index += 1;
break;
},
else => {
- result.id = .Equal;
+ result.tag = .equal;
break;
},
},
.minus => switch (c) {
'>' => {
- result.id = .Arrow;
+ result.tag = .arrow;
self.index += 1;
break;
},
'=' => {
- result.id = .MinusEqual;
+ result.tag = .minus_equal;
self.index += 1;
break;
},
@@ -906,19 +906,19 @@ pub const Tokenizer = struct {
state = .minus_percent;
},
else => {
- result.id = .Minus;
+ result.tag = .minus;
break;
},
},
.minus_percent => switch (c) {
'=' => {
- result.id = .MinusPercentEqual;
+ result.tag = .minus_percent_equal;
self.index += 1;
break;
},
else => {
- result.id = .MinusPercent;
+ result.tag = .minus_percent;
break;
},
},
@@ -928,24 +928,24 @@ pub const Tokenizer = struct {
state = .angle_bracket_angle_bracket_left;
},
'=' => {
- result.id = .AngleBracketLeftEqual;
+ result.tag = .angle_bracket_left_equal;
self.index += 1;
break;
},
else => {
- result.id = .AngleBracketLeft;
+ result.tag = .angle_bracket_left;
break;
},
},
.angle_bracket_angle_bracket_left => switch (c) {
'=' => {
- result.id = .AngleBracketAngleBracketLeftEqual;
+ result.tag = .angle_bracket_angle_bracket_left_equal;
self.index += 1;
break;
},
else => {
- result.id = .AngleBracketAngleBracketLeft;
+ result.tag = .angle_bracket_angle_bracket_left;
break;
},
},
@@ -955,24 +955,24 @@ pub const Tokenizer = struct {
state = .angle_bracket_angle_bracket_right;
},
'=' => {
- result.id = .AngleBracketRightEqual;
+ result.tag = .angle_bracket_right_equal;
self.index += 1;
break;
},
else => {
- result.id = .AngleBracketRight;
+ result.tag = .angle_bracket_right;
break;
},
},
.angle_bracket_angle_bracket_right => switch (c) {
'=' => {
- result.id = .AngleBracketAngleBracketRightEqual;
+ result.tag = .angle_bracket_angle_bracket_right_equal;
self.index += 1;
break;
},
else => {
- result.id = .AngleBracketAngleBracketRight;
+ result.tag = .angle_bracket_angle_bracket_right;
break;
},
},
@@ -985,30 +985,30 @@ pub const Tokenizer = struct {
state = .period_asterisk;
},
else => {
- result.id = .Period;
+ result.tag = .period;
break;
},
},
.period_2 => switch (c) {
'.' => {
- result.id = .Ellipsis3;
+ result.tag = .ellipsis3;
self.index += 1;
break;
},
else => {
- result.id = .Ellipsis2;
+ result.tag = .ellipsis2;
break;
},
},
.period_asterisk => switch (c) {
'*' => {
- result.id = .Invalid_periodasterisks;
+ result.tag = .invalid_periodasterisks;
break;
},
else => {
- result.id = .PeriodAsterisk;
+ result.tag = .period_asterisk;
break;
},
},
@@ -1016,15 +1016,14 @@ pub const Tokenizer = struct {
.slash => switch (c) {
'/' => {
state = .line_comment_start;
- result.id = .LineComment;
},
'=' => {
- result.id = .SlashEqual;
+ result.tag = .slash_equal;
self.index += 1;
break;
},
else => {
- result.id = .Slash;
+ result.tag = .slash;
break;
},
},
@@ -1033,10 +1032,13 @@ pub const Tokenizer = struct {
state = .doc_comment_start;
},
'!' => {
- result.id = .ContainerDocComment;
+ result.tag = .container_doc_comment;
state = .container_doc_comment;
},
- '\n' => break,
+ '\n' => {
+ state = .start;
+ result.loc.start = self.index + 1;
+ },
'\t', '\r' => state = .line_comment,
else => {
state = .line_comment;
@@ -1048,20 +1050,28 @@ pub const Tokenizer = struct {
state = .line_comment;
},
'\n' => {
- result.id = .DocComment;
+ result.tag = .doc_comment;
break;
},
'\t', '\r' => {
state = .doc_comment;
- result.id = .DocComment;
+ result.tag = .doc_comment;
},
else => {
state = .doc_comment;
- result.id = .DocComment;
+ result.tag = .doc_comment;
self.checkLiteralCharacter();
},
},
- .line_comment, .doc_comment, .container_doc_comment => switch (c) {
+ .line_comment => switch (c) {
+ '\n' => {
+ state = .start;
+ result.loc.start = self.index + 1;
+ },
+ '\t', '\r' => {},
+ else => self.checkLiteralCharacter(),
+ },
+ .doc_comment, .container_doc_comment => switch (c) {
'\n' => break,
'\t', '\r' => {},
else => self.checkLiteralCharacter(),
@@ -1083,7 +1093,7 @@ pub const Tokenizer = struct {
},
else => {
if (isIdentifierChar(c)) {
- result.id = .Invalid;
+ result.tag = .invalid;
}
break;
},
@@ -1093,7 +1103,7 @@ pub const Tokenizer = struct {
state = .int_literal_bin;
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
},
@@ -1104,7 +1114,7 @@ pub const Tokenizer = struct {
'0'...'1' => {},
else => {
if (isIdentifierChar(c)) {
- result.id = .Invalid;
+ result.tag = .invalid;
}
break;
},
@@ -1114,7 +1124,7 @@ pub const Tokenizer = struct {
state = .int_literal_oct;
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
},
@@ -1125,7 +1135,7 @@ pub const Tokenizer = struct {
'0'...'7' => {},
else => {
if (isIdentifierChar(c)) {
- result.id = .Invalid;
+ result.tag = .invalid;
}
break;
},
@@ -1135,7 +1145,7 @@ pub const Tokenizer = struct {
state = .int_literal_dec;
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
},
@@ -1145,16 +1155,16 @@ pub const Tokenizer = struct {
},
'.' => {
state = .num_dot_dec;
- result.id = .FloatLiteral;
+ result.tag = .float_literal;
},
'e', 'E' => {
state = .float_exponent_unsigned;
- result.id = .FloatLiteral;
+ result.tag = .float_literal;
},
'0'...'9' => {},
else => {
if (isIdentifierChar(c)) {
- result.id = .Invalid;
+ result.tag = .invalid;
}
break;
},
@@ -1164,7 +1174,7 @@ pub const Tokenizer = struct {
state = .int_literal_hex;
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
},
@@ -1174,23 +1184,23 @@ pub const Tokenizer = struct {
},
'.' => {
state = .num_dot_hex;
- result.id = .FloatLiteral;
+ result.tag = .float_literal;
},
'p', 'P' => {
state = .float_exponent_unsigned;
- result.id = .FloatLiteral;
+ result.tag = .float_literal;
},
'0'...'9', 'a'...'f', 'A'...'F' => {},
else => {
if (isIdentifierChar(c)) {
- result.id = .Invalid;
+ result.tag = .invalid;
}
break;
},
},
.num_dot_dec => switch (c) {
'.' => {
- result.id = .IntegerLiteral;
+ result.tag = .integer_literal;
self.index -= 1;
state = .start;
break;
@@ -1203,14 +1213,14 @@ pub const Tokenizer = struct {
},
else => {
if (isIdentifierChar(c)) {
- result.id = .Invalid;
+ result.tag = .invalid;
}
break;
},
},
.num_dot_hex => switch (c) {
'.' => {
- result.id = .IntegerLiteral;
+ result.tag = .integer_literal;
self.index -= 1;
state = .start;
break;
@@ -1219,12 +1229,12 @@ pub const Tokenizer = struct {
state = .float_exponent_unsigned;
},
'0'...'9', 'a'...'f', 'A'...'F' => {
- result.id = .FloatLiteral;
+ result.tag = .float_literal;
state = .float_fraction_hex;
},
else => {
if (isIdentifierChar(c)) {
- result.id = .Invalid;
+ result.tag = .invalid;
}
break;
},
@@ -1234,7 +1244,7 @@ pub const Tokenizer = struct {
state = .float_fraction_dec;
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
},
@@ -1248,7 +1258,7 @@ pub const Tokenizer = struct {
'0'...'9' => {},
else => {
if (isIdentifierChar(c)) {
- result.id = .Invalid;
+ result.tag = .invalid;
}
break;
},
@@ -1258,7 +1268,7 @@ pub const Tokenizer = struct {
state = .float_fraction_hex;
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
},
@@ -1272,7 +1282,7 @@ pub const Tokenizer = struct {
'0'...'9', 'a'...'f', 'A'...'F' => {},
else => {
if (isIdentifierChar(c)) {
- result.id = .Invalid;
+ result.tag = .invalid;
}
break;
},
@@ -1292,7 +1302,7 @@ pub const Tokenizer = struct {
state = .float_exponent_num;
},
else => {
- result.id = .Invalid;
+ result.tag = .invalid;
break;
},
},
@@ -1303,7 +1313,7 @@ pub const Tokenizer = struct {
'0'...'9' => {},
else => {
if (isIdentifierChar(c)) {
- result.id = .Invalid;
+ result.tag = .invalid;
}
break;
},
@@ -1324,21 +1334,20 @@ pub const Tokenizer = struct {
.string_literal, // find this error later
.multiline_string_literal_line,
.builtin,
+ .line_comment,
+ .line_comment_start,
=> {},
.identifier => {
- if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |id| {
- result.id = id;
+ if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |tag| {
+ result.tag = tag;
}
},
- .line_comment, .line_comment_start => {
- result.id = .LineComment;
- },
.doc_comment, .doc_comment_start => {
- result.id = .DocComment;
+ result.tag = .doc_comment;
},
.container_doc_comment => {
- result.id = .ContainerDocComment;
+ result.tag = .container_doc_comment;
},
.int_literal_dec_no_underscore,
@@ -1361,80 +1370,81 @@ pub const Tokenizer = struct {
.char_literal_unicode,
.string_literal_backslash,
=> {
- result.id = .Invalid;
+ result.tag = .invalid;
},
.equal => {
- result.id = .Equal;
+ result.tag = .equal;
},
.bang => {
- result.id = .Bang;
+ result.tag = .bang;
},
.minus => {
- result.id = .Minus;
+ result.tag = .minus;
},
.slash => {
- result.id = .Slash;
+ result.tag = .slash;
},
.zero => {
- result.id = .IntegerLiteral;
+ result.tag = .integer_literal;
},
.ampersand => {
- result.id = .Ampersand;
+ result.tag = .ampersand;
},
.period => {
- result.id = .Period;
+ result.tag = .period;
},
.period_2 => {
- result.id = .Ellipsis2;
+ result.tag = .ellipsis2;
},
.period_asterisk => {
- result.id = .PeriodAsterisk;
+ result.tag = .period_asterisk;
},
.pipe => {
- result.id = .Pipe;
+ result.tag = .pipe;
},
.angle_bracket_angle_bracket_right => {
- result.id = .AngleBracketAngleBracketRight;
+ result.tag = .angle_bracket_angle_bracket_right;
},
.angle_bracket_right => {
- result.id = .AngleBracketRight;
+ result.tag = .angle_bracket_right;
},
.angle_bracket_angle_bracket_left => {
- result.id = .AngleBracketAngleBracketLeft;
+ result.tag = .angle_bracket_angle_bracket_left;
},
.angle_bracket_left => {
- result.id = .AngleBracketLeft;
+ result.tag = .angle_bracket_left;
},
.plus_percent => {
- result.id = .PlusPercent;
+ result.tag = .plus_percent;
},
.plus => {
- result.id = .Plus;
+ result.tag = .plus;
},
.percent => {
- result.id = .Percent;
+ result.tag = .percent;
},
.caret => {
- result.id = .Caret;
+ result.tag = .caret;
},
.asterisk_percent => {
- result.id = .AsteriskPercent;
+ result.tag = .asterisk_percent;
},
.asterisk => {
- result.id = .Asterisk;
+ result.tag = .asterisk;
},
.minus_percent => {
- result.id = .MinusPercent;
+ result.tag = .minus_percent;
},
}
}
- if (result.id == .Eof) {
+ if (result.tag == .eof) {
if (self.pending_invalid_token) |token| {
self.pending_invalid_token = null;
return token;
}
+ result.loc.start = self.index;
}
result.loc.end = self.index;
@@ -1446,7 +1456,7 @@ pub const Tokenizer = struct {
const invalid_length = self.getInvalidCharacterLength();
if (invalid_length == 0) return;
self.pending_invalid_token = .{
- .id = .Invalid,
+ .tag = .invalid,
.loc = .{
.start = self.index,
.end = self.index + invalid_length,
@@ -1493,220 +1503,218 @@ pub const Tokenizer = struct {
};
test "tokenizer" {
- testTokenize("test", &[_]Token.Id{.Keyword_test});
+ testTokenize("test", &.{.keyword_test});
+}
+
+test "line comment followed by top-level comptime" {
+ testTokenize(
+ \\// line comment
+ \\comptime {}
+ \\
+ , &.{
+ .keyword_comptime,
+ .l_brace,
+ .r_brace,
+ });
}
test "tokenizer - unknown length pointer and then c pointer" {
testTokenize(
\\[*]u8
\\[*c]u8
- , &[_]Token.Id{
- .LBracket,
- .Asterisk,
- .RBracket,
- .Identifier,
- .LBracket,
- .Asterisk,
- .Identifier,
- .RBracket,
- .Identifier,
+ , &.{
+ .l_bracket,
+ .asterisk,
+ .r_bracket,
+ .identifier,
+ .l_bracket,
+ .asterisk,
+ .identifier,
+ .r_bracket,
+ .identifier,
});
}
-test "tokenizer - char literal with hex escape" {
+test "tokenizer - code point literal with hex escape" {
testTokenize(
\\'\x1b'
- , &[_]Token.Id{.CharLiteral});
+ , &.{.char_literal});
testTokenize(
\\'\x1'
- , &[_]Token.Id{ .Invalid, .Invalid });
+ , &.{ .invalid, .invalid });
}
-test "tokenizer - char literal with unicode escapes" {
+test "tokenizer - code point literal with unicode escapes" {
// Valid unicode escapes
testTokenize(
\\'\u{3}'
- , &[_]Token.Id{.CharLiteral});
+ , &.{.char_literal});
testTokenize(
\\'\u{01}'
- , &[_]Token.Id{.CharLiteral});
+ , &.{.char_literal});
testTokenize(
\\'\u{2a}'
- , &[_]Token.Id{.CharLiteral});
+ , &.{.char_literal});
testTokenize(
\\'\u{3f9}'
- , &[_]Token.Id{.CharLiteral});
+ , &.{.char_literal});
testTokenize(
\\'\u{6E09aBc1523}'
- , &[_]Token.Id{.CharLiteral});
+ , &.{.char_literal});
testTokenize(
\\"\u{440}"
- , &[_]Token.Id{.StringLiteral});
+ , &.{.string_literal});
// Invalid unicode escapes
testTokenize(
\\'\u'
- , &[_]Token.Id{.Invalid});
+ , &.{.invalid});
testTokenize(
\\'\u{{'
- , &[_]Token.Id{ .Invalid, .Invalid });
+ , &.{ .invalid, .invalid });
testTokenize(
\\'\u{}'
- , &[_]Token.Id{ .Invalid, .Invalid });
+ , &.{ .invalid, .invalid });
testTokenize(
\\'\u{s}'
- , &[_]Token.Id{ .Invalid, .Invalid });
+ , &.{ .invalid, .invalid });
testTokenize(
\\'\u{2z}'
- , &[_]Token.Id{ .Invalid, .Invalid });
+ , &.{ .invalid, .invalid });
testTokenize(
\\'\u{4a'
- , &[_]Token.Id{.Invalid});
+ , &.{.invalid});
// Test old-style unicode literals
testTokenize(
\\'\u0333'
- , &[_]Token.Id{ .Invalid, .Invalid });
+ , &.{ .invalid, .invalid });
testTokenize(
\\'\U0333'
- , &[_]Token.Id{ .Invalid, .IntegerLiteral, .Invalid });
+ , &.{ .invalid, .integer_literal, .invalid });
}
-test "tokenizer - char literal with unicode code point" {
+test "tokenizer - code point literal with unicode code point" {
testTokenize(
\\'💩'
- , &[_]Token.Id{.CharLiteral});
+ , &.{.char_literal});
}
test "tokenizer - float literal e exponent" {
- testTokenize("a = 4.94065645841246544177e-324;\n", &[_]Token.Id{
- .Identifier,
- .Equal,
- .FloatLiteral,
- .Semicolon,
+ testTokenize("a = 4.94065645841246544177e-324;\n", &.{
+ .identifier,
+ .equal,
+ .float_literal,
+ .semicolon,
});
}
test "tokenizer - float literal p exponent" {
- testTokenize("a = 0x1.a827999fcef32p+1022;\n", &[_]Token.Id{
- .Identifier,
- .Equal,
- .FloatLiteral,
- .Semicolon,
+ testTokenize("a = 0x1.a827999fcef32p+1022;\n", &.{
+ .identifier,
+ .equal,
+ .float_literal,
+ .semicolon,
});
}
test "tokenizer - chars" {
- testTokenize("'c'", &[_]Token.Id{.CharLiteral});
+ testTokenize("'c'", &.{.char_literal});
}
test "tokenizer - invalid token characters" {
- testTokenize("#", &[_]Token.Id{.Invalid});
- testTokenize("`", &[_]Token.Id{.Invalid});
- testTokenize("'c", &[_]Token.Id{.Invalid});
- testTokenize("'", &[_]Token.Id{.Invalid});
- testTokenize("''", &[_]Token.Id{ .Invalid, .Invalid });
+ testTokenize("#", &.{.invalid});
+ testTokenize("`", &.{.invalid});
+ testTokenize("'c", &.{.invalid});
+ testTokenize("'", &.{.invalid});
+ testTokenize("''", &.{ .invalid, .invalid });
}
test "tokenizer - invalid literal/comment characters" {
- testTokenize("\"\x00\"", &[_]Token.Id{
- .StringLiteral,
- .Invalid,
+ testTokenize("\"\x00\"", &.{
+ .string_literal,
+ .invalid,
});
- testTokenize("//\x00", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\x00", &.{
+ .invalid,
});
- testTokenize("//\x1f", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\x1f", &.{
+ .invalid,
});
- testTokenize("//\x7f", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\x7f", &.{
+ .invalid,
});
}
test "tokenizer - utf8" {
- testTokenize("//\xc2\x80", &[_]Token.Id{.LineComment});
- testTokenize("//\xf4\x8f\xbf\xbf", &[_]Token.Id{.LineComment});
+ testTokenize("//\xc2\x80", &.{});
+ testTokenize("//\xf4\x8f\xbf\xbf", &.{});
}
test "tokenizer - invalid utf8" {
- testTokenize("//\x80", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\x80", &.{
+ .invalid,
});
- testTokenize("//\xbf", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\xbf", &.{
+ .invalid,
});
- testTokenize("//\xf8", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\xf8", &.{
+ .invalid,
});
- testTokenize("//\xff", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\xff", &.{
+ .invalid,
});
- testTokenize("//\xc2\xc0", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\xc2\xc0", &.{
+ .invalid,
});
- testTokenize("//\xe0", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\xe0", &.{
+ .invalid,
});
- testTokenize("//\xf0", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\xf0", &.{
+ .invalid,
});
- testTokenize("//\xf0\x90\x80\xc0", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\xf0\x90\x80\xc0", &.{
+ .invalid,
});
}
test "tokenizer - illegal unicode codepoints" {
// unicode newline characters.U+0085, U+2028, U+2029
- testTokenize("//\xc2\x84", &[_]Token.Id{.LineComment});
- testTokenize("//\xc2\x85", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\xc2\x84", &.{});
+ testTokenize("//\xc2\x85", &.{
+ .invalid,
});
- testTokenize("//\xc2\x86", &[_]Token.Id{.LineComment});
- testTokenize("//\xe2\x80\xa7", &[_]Token.Id{.LineComment});
- testTokenize("//\xe2\x80\xa8", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\xc2\x86", &.{});
+ testTokenize("//\xe2\x80\xa7", &.{});
+ testTokenize("//\xe2\x80\xa8", &.{
+ .invalid,
});
- testTokenize("//\xe2\x80\xa9", &[_]Token.Id{
- .LineComment,
- .Invalid,
+ testTokenize("//\xe2\x80\xa9", &.{
+ .invalid,
});
- testTokenize("//\xe2\x80\xaa", &[_]Token.Id{.LineComment});
+ testTokenize("//\xe2\x80\xaa", &.{});
}
test "tokenizer - string identifier and builtin fns" {
testTokenize(
\\const @"if" = @import("std");
- , &[_]Token.Id{
- .Keyword_const,
- .Identifier,
- .Equal,
- .Builtin,
- .LParen,
- .StringLiteral,
- .RParen,
- .Semicolon,
+ , &.{
+ .keyword_const,
+ .identifier,
+ .equal,
+ .builtin,
+ .l_paren,
+ .string_literal,
+ .r_paren,
+ .semicolon,
});
}
test "tokenizer - multiline string literal with literal tab" {
testTokenize(
\\\\foo bar
- , &[_]Token.Id{
- .MultilineStringLiteralLine,
+ , &.{
+ .multiline_string_literal_line,
});
}
@@ -1718,32 +1726,30 @@ test "tokenizer - comments with literal tab" {
\\// foo
\\/// foo
\\/// /foo
- , &[_]Token.Id{
- .LineComment,
- .ContainerDocComment,
- .DocComment,
- .LineComment,
- .DocComment,
- .DocComment,
+ , &.{
+ .container_doc_comment,
+ .doc_comment,
+ .doc_comment,
+ .doc_comment,
});
}
test "tokenizer - pipe and then invalid" {
- testTokenize("||=", &[_]Token.Id{
- .PipePipe,
- .Equal,
+ testTokenize("||=", &.{
+ .pipe_pipe,
+ .equal,
});
}
test "tokenizer - line comment and doc comment" {
- testTokenize("//", &[_]Token.Id{.LineComment});
- testTokenize("// a / b", &[_]Token.Id{.LineComment});
- testTokenize("// /", &[_]Token.Id{.LineComment});
- testTokenize("/// a", &[_]Token.Id{.DocComment});
- testTokenize("///", &[_]Token.Id{.DocComment});
- testTokenize("////", &[_]Token.Id{.LineComment});
- testTokenize("//!", &[_]Token.Id{.ContainerDocComment});
- testTokenize("//!!", &[_]Token.Id{.ContainerDocComment});
+ testTokenize("//", &.{});
+ testTokenize("// a / b", &.{});
+ testTokenize("// /", &.{});
+ testTokenize("/// a", &.{.doc_comment});
+ testTokenize("///", &.{.doc_comment});
+ testTokenize("////", &.{});
+ testTokenize("//!", &.{.container_doc_comment});
+ testTokenize("//!!", &.{.container_doc_comment});
}
test "tokenizer - line comment followed by identifier" {
@@ -1751,304 +1757,304 @@ test "tokenizer - line comment followed by identifier" {
\\ Unexpected,
\\ // another
\\ Another,
- , &[_]Token.Id{
- .Identifier,
- .Comma,
- .LineComment,
- .Identifier,
- .Comma,
+ , &.{
+ .identifier,
+ .comma,
+ .identifier,
+ .comma,
});
}
test "tokenizer - UTF-8 BOM is recognized and skipped" {
- testTokenize("\xEF\xBB\xBFa;\n", &[_]Token.Id{
- .Identifier,
- .Semicolon,
+ testTokenize("\xEF\xBB\xBFa;\n", &.{
+ .identifier,
+ .semicolon,
});
}
test "correctly parse pointer assignment" {
- testTokenize("b.*=3;\n", &[_]Token.Id{
- .Identifier,
- .PeriodAsterisk,
- .Equal,
- .IntegerLiteral,
- .Semicolon,
+ testTokenize("b.*=3;\n", &.{
+ .identifier,
+ .period_asterisk,
+ .equal,
+ .integer_literal,
+ .semicolon,
});
}
test "correctly parse pointer dereference followed by asterisk" {
- testTokenize("\"b\".* ** 10", &[_]Token.Id{
- .StringLiteral,
- .PeriodAsterisk,
- .AsteriskAsterisk,
- .IntegerLiteral,
+ testTokenize("\"b\".* ** 10", &.{
+ .string_literal,
+ .period_asterisk,
+ .asterisk_asterisk,
+ .integer_literal,
});
- testTokenize("(\"b\".*)** 10", &[_]Token.Id{
- .LParen,
- .StringLiteral,
- .PeriodAsterisk,
- .RParen,
- .AsteriskAsterisk,
- .IntegerLiteral,
+ testTokenize("(\"b\".*)** 10", &.{
+ .l_paren,
+ .string_literal,
+ .period_asterisk,
+ .r_paren,
+ .asterisk_asterisk,
+ .integer_literal,
});
- testTokenize("\"b\".*** 10", &[_]Token.Id{
- .StringLiteral,
- .Invalid_periodasterisks,
- .AsteriskAsterisk,
- .IntegerLiteral,
+ testTokenize("\"b\".*** 10", &.{
+ .string_literal,
+ .invalid_periodasterisks,
+ .asterisk_asterisk,
+ .integer_literal,
});
}
test "tokenizer - range literals" {
- testTokenize("0...9", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
- testTokenize("'0'...'9'", &[_]Token.Id{ .CharLiteral, .Ellipsis3, .CharLiteral });
- testTokenize("0x00...0x09", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
- testTokenize("0b00...0b11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
- testTokenize("0o00...0o11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
+ testTokenize("0...9", &.{ .integer_literal, .ellipsis3, .integer_literal });
+ testTokenize("'0'...'9'", &.{ .char_literal, .ellipsis3, .char_literal });
+ testTokenize("0x00...0x09", &.{ .integer_literal, .ellipsis3, .integer_literal });
+ testTokenize("0b00...0b11", &.{ .integer_literal, .ellipsis3, .integer_literal });
+ testTokenize("0o00...0o11", &.{ .integer_literal, .ellipsis3, .integer_literal });
}
test "tokenizer - number literals decimal" {
- testTokenize("0", &[_]Token.Id{.IntegerLiteral});
- testTokenize("1", &[_]Token.Id{.IntegerLiteral});
- testTokenize("2", &[_]Token.Id{.IntegerLiteral});
- testTokenize("3", &[_]Token.Id{.IntegerLiteral});
- testTokenize("4", &[_]Token.Id{.IntegerLiteral});
- testTokenize("5", &[_]Token.Id{.IntegerLiteral});
- testTokenize("6", &[_]Token.Id{.IntegerLiteral});
- testTokenize("7", &[_]Token.Id{.IntegerLiteral});
- testTokenize("8", &[_]Token.Id{.IntegerLiteral});
- testTokenize("9", &[_]Token.Id{.IntegerLiteral});
- testTokenize("1..", &[_]Token.Id{ .IntegerLiteral, .Ellipsis2 });
- testTokenize("0a", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("9b", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1z", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1z_1", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("9z3", &[_]Token.Id{ .Invalid, .Identifier });
-
- testTokenize("0_0", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0001", &[_]Token.Id{.IntegerLiteral});
- testTokenize("01234567890", &[_]Token.Id{.IntegerLiteral});
- testTokenize("012_345_6789_0", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0_1_2_3_4_5_6_7_8_9_0", &[_]Token.Id{.IntegerLiteral});
-
- testTokenize("00_", &[_]Token.Id{.Invalid});
- testTokenize("0_0_", &[_]Token.Id{.Invalid});
- testTokenize("0__0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0_0f", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0_0_f", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0_0_f_00", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1_,", &[_]Token.Id{ .Invalid, .Comma });
-
- testTokenize("1.", &[_]Token.Id{.FloatLiteral});
- testTokenize("0.0", &[_]Token.Id{.FloatLiteral});
- testTokenize("1.0", &[_]Token.Id{.FloatLiteral});
- testTokenize("10.0", &[_]Token.Id{.FloatLiteral});
- testTokenize("0e0", &[_]Token.Id{.FloatLiteral});
- testTokenize("1e0", &[_]Token.Id{.FloatLiteral});
- testTokenize("1e100", &[_]Token.Id{.FloatLiteral});
- testTokenize("1.e100", &[_]Token.Id{.FloatLiteral});
- testTokenize("1.0e100", &[_]Token.Id{.FloatLiteral});
- testTokenize("1.0e+100", &[_]Token.Id{.FloatLiteral});
- testTokenize("1.0e-100", &[_]Token.Id{.FloatLiteral});
- testTokenize("1_0_0_0.0_0_0_0_0_1e1_0_0_0", &[_]Token.Id{.FloatLiteral});
- testTokenize("1.+", &[_]Token.Id{ .FloatLiteral, .Plus });
-
- testTokenize("1e", &[_]Token.Id{.Invalid});
- testTokenize("1.0e1f0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1.0p100", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1.0p-100", &[_]Token.Id{ .Invalid, .Identifier, .Minus, .IntegerLiteral });
- testTokenize("1.0p1f0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1.0_,", &[_]Token.Id{ .Invalid, .Comma });
- testTokenize("1_.0", &[_]Token.Id{ .Invalid, .Period, .IntegerLiteral });
- testTokenize("1._", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1.a", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1.z", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1._0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1._+", &[_]Token.Id{ .Invalid, .Identifier, .Plus });
- testTokenize("1._e", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1.0e", &[_]Token.Id{.Invalid});
- testTokenize("1.0e,", &[_]Token.Id{ .Invalid, .Comma });
- testTokenize("1.0e_", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1.0e+_", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1.0e-_", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("1.0e0_+", &[_]Token.Id{ .Invalid, .Plus });
+ testTokenize("0", &.{.integer_literal});
+ testTokenize("1", &.{.integer_literal});
+ testTokenize("2", &.{.integer_literal});
+ testTokenize("3", &.{.integer_literal});
+ testTokenize("4", &.{.integer_literal});
+ testTokenize("5", &.{.integer_literal});
+ testTokenize("6", &.{.integer_literal});
+ testTokenize("7", &.{.integer_literal});
+ testTokenize("8", &.{.integer_literal});
+ testTokenize("9", &.{.integer_literal});
+ testTokenize("1..", &.{ .integer_literal, .ellipsis2 });
+ testTokenize("0a", &.{ .invalid, .identifier });
+ testTokenize("9b", &.{ .invalid, .identifier });
+ testTokenize("1z", &.{ .invalid, .identifier });
+ testTokenize("1z_1", &.{ .invalid, .identifier });
+ testTokenize("9z3", &.{ .invalid, .identifier });
+
+ testTokenize("0_0", &.{.integer_literal});
+ testTokenize("0001", &.{.integer_literal});
+ testTokenize("01234567890", &.{.integer_literal});
+ testTokenize("012_345_6789_0", &.{.integer_literal});
+ testTokenize("0_1_2_3_4_5_6_7_8_9_0", &.{.integer_literal});
+
+ testTokenize("00_", &.{.invalid});
+ testTokenize("0_0_", &.{.invalid});
+ testTokenize("0__0", &.{ .invalid, .identifier });
+ testTokenize("0_0f", &.{ .invalid, .identifier });
+ testTokenize("0_0_f", &.{ .invalid, .identifier });
+ testTokenize("0_0_f_00", &.{ .invalid, .identifier });
+ testTokenize("1_,", &.{ .invalid, .comma });
+
+ testTokenize("1.", &.{.float_literal});
+ testTokenize("0.0", &.{.float_literal});
+ testTokenize("1.0", &.{.float_literal});
+ testTokenize("10.0", &.{.float_literal});
+ testTokenize("0e0", &.{.float_literal});
+ testTokenize("1e0", &.{.float_literal});
+ testTokenize("1e100", &.{.float_literal});
+ testTokenize("1.e100", &.{.float_literal});
+ testTokenize("1.0e100", &.{.float_literal});
+ testTokenize("1.0e+100", &.{.float_literal});
+ testTokenize("1.0e-100", &.{.float_literal});
+ testTokenize("1_0_0_0.0_0_0_0_0_1e1_0_0_0", &.{.float_literal});
+ testTokenize("1.+", &.{ .float_literal, .plus });
+
+ testTokenize("1e", &.{.invalid});
+ testTokenize("1.0e1f0", &.{ .invalid, .identifier });
+ testTokenize("1.0p100", &.{ .invalid, .identifier });
+ testTokenize("1.0p-100", &.{ .invalid, .identifier, .minus, .integer_literal });
+ testTokenize("1.0p1f0", &.{ .invalid, .identifier });
+ testTokenize("1.0_,", &.{ .invalid, .comma });
+ testTokenize("1_.0", &.{ .invalid, .period, .integer_literal });
+ testTokenize("1._", &.{ .invalid, .identifier });
+ testTokenize("1.a", &.{ .invalid, .identifier });
+ testTokenize("1.z", &.{ .invalid, .identifier });
+ testTokenize("1._0", &.{ .invalid, .identifier });
+ testTokenize("1._+", &.{ .invalid, .identifier, .plus });
+ testTokenize("1._e", &.{ .invalid, .identifier });
+ testTokenize("1.0e", &.{.invalid});
+ testTokenize("1.0e,", &.{ .invalid, .comma });
+ testTokenize("1.0e_", &.{ .invalid, .identifier });
+ testTokenize("1.0e+_", &.{ .invalid, .identifier });
+ testTokenize("1.0e-_", &.{ .invalid, .identifier });
+ testTokenize("1.0e0_+", &.{ .invalid, .plus });
}
test "tokenizer - number literals binary" {
- testTokenize("0b0", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0b1", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0b2", &[_]Token.Id{ .Invalid, .IntegerLiteral });
- testTokenize("0b3", &[_]Token.Id{ .Invalid, .IntegerLiteral });
- testTokenize("0b4", &[_]Token.Id{ .Invalid, .IntegerLiteral });
- testTokenize("0b5", &[_]Token.Id{ .Invalid, .IntegerLiteral });
- testTokenize("0b6", &[_]Token.Id{ .Invalid, .IntegerLiteral });
- testTokenize("0b7", &[_]Token.Id{ .Invalid, .IntegerLiteral });
- testTokenize("0b8", &[_]Token.Id{ .Invalid, .IntegerLiteral });
- testTokenize("0b9", &[_]Token.Id{ .Invalid, .IntegerLiteral });
- testTokenize("0ba", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0bb", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0bc", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0bd", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0be", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0bf", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0bz", &[_]Token.Id{ .Invalid, .Identifier });
-
- testTokenize("0b0000_0000", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0b1111_1111", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0b10_10_10_10", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0b0_1_0_1_0_1_0_1", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0b1.", &[_]Token.Id{ .IntegerLiteral, .Period });
- testTokenize("0b1.0", &[_]Token.Id{ .IntegerLiteral, .Period, .IntegerLiteral });
-
- testTokenize("0B0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0b_", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0b_0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0b1_", &[_]Token.Id{.Invalid});
- testTokenize("0b0__1", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0b0_1_", &[_]Token.Id{.Invalid});
- testTokenize("0b1e", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0b1p", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0b1e0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0b1p0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0b1_,", &[_]Token.Id{ .Invalid, .Comma });
+ testTokenize("0b0", &.{.integer_literal});
+ testTokenize("0b1", &.{.integer_literal});
+ testTokenize("0b2", &.{ .invalid, .integer_literal });
+ testTokenize("0b3", &.{ .invalid, .integer_literal });
+ testTokenize("0b4", &.{ .invalid, .integer_literal });
+ testTokenize("0b5", &.{ .invalid, .integer_literal });
+ testTokenize("0b6", &.{ .invalid, .integer_literal });
+ testTokenize("0b7", &.{ .invalid, .integer_literal });
+ testTokenize("0b8", &.{ .invalid, .integer_literal });
+ testTokenize("0b9", &.{ .invalid, .integer_literal });
+ testTokenize("0ba", &.{ .invalid, .identifier });
+ testTokenize("0bb", &.{ .invalid, .identifier });
+ testTokenize("0bc", &.{ .invalid, .identifier });
+ testTokenize("0bd", &.{ .invalid, .identifier });
+ testTokenize("0be", &.{ .invalid, .identifier });
+ testTokenize("0bf", &.{ .invalid, .identifier });
+ testTokenize("0bz", &.{ .invalid, .identifier });
+
+ testTokenize("0b0000_0000", &.{.integer_literal});
+ testTokenize("0b1111_1111", &.{.integer_literal});
+ testTokenize("0b10_10_10_10", &.{.integer_literal});
+ testTokenize("0b0_1_0_1_0_1_0_1", &.{.integer_literal});
+ testTokenize("0b1.", &.{ .integer_literal, .period });
+ testTokenize("0b1.0", &.{ .integer_literal, .period, .integer_literal });
+
+ testTokenize("0B0", &.{ .invalid, .identifier });
+ testTokenize("0b_", &.{ .invalid, .identifier });
+ testTokenize("0b_0", &.{ .invalid, .identifier });
+ testTokenize("0b1_", &.{.invalid});
+ testTokenize("0b0__1", &.{ .invalid, .identifier });
+ testTokenize("0b0_1_", &.{.invalid});
+ testTokenize("0b1e", &.{ .invalid, .identifier });
+ testTokenize("0b1p", &.{ .invalid, .identifier });
+ testTokenize("0b1e0", &.{ .invalid, .identifier });
+ testTokenize("0b1p0", &.{ .invalid, .identifier });
+ testTokenize("0b1_,", &.{ .invalid, .comma });
}
test "tokenizer - number literals octal" {
- testTokenize("0o0", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o1", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o2", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o3", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o4", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o5", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o6", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o7", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o8", &[_]Token.Id{ .Invalid, .IntegerLiteral });
- testTokenize("0o9", &[_]Token.Id{ .Invalid, .IntegerLiteral });
- testTokenize("0oa", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0ob", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0oc", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0od", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0oe", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0of", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0oz", &[_]Token.Id{ .Invalid, .Identifier });
-
- testTokenize("0o01234567", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o0123_4567", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o01_23_45_67", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o0_1_2_3_4_5_6_7", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0o7.", &[_]Token.Id{ .IntegerLiteral, .Period });
- testTokenize("0o7.0", &[_]Token.Id{ .IntegerLiteral, .Period, .IntegerLiteral });
-
- testTokenize("0O0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0o_", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0o_0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0o1_", &[_]Token.Id{.Invalid});
- testTokenize("0o0__1", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0o0_1_", &[_]Token.Id{.Invalid});
- testTokenize("0o1e", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0o1p", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0o1e0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0o1p0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0o_,", &[_]Token.Id{ .Invalid, .Identifier, .Comma });
+ testTokenize("0o0", &.{.integer_literal});
+ testTokenize("0o1", &.{.integer_literal});
+ testTokenize("0o2", &.{.integer_literal});
+ testTokenize("0o3", &.{.integer_literal});
+ testTokenize("0o4", &.{.integer_literal});
+ testTokenize("0o5", &.{.integer_literal});
+ testTokenize("0o6", &.{.integer_literal});
+ testTokenize("0o7", &.{.integer_literal});
+ testTokenize("0o8", &.{ .invalid, .integer_literal });
+ testTokenize("0o9", &.{ .invalid, .integer_literal });
+ testTokenize("0oa", &.{ .invalid, .identifier });
+ testTokenize("0ob", &.{ .invalid, .identifier });
+ testTokenize("0oc", &.{ .invalid, .identifier });
+ testTokenize("0od", &.{ .invalid, .identifier });
+ testTokenize("0oe", &.{ .invalid, .identifier });
+ testTokenize("0of", &.{ .invalid, .identifier });
+ testTokenize("0oz", &.{ .invalid, .identifier });
+
+ testTokenize("0o01234567", &.{.integer_literal});
+ testTokenize("0o0123_4567", &.{.integer_literal});
+ testTokenize("0o01_23_45_67", &.{.integer_literal});
+ testTokenize("0o0_1_2_3_4_5_6_7", &.{.integer_literal});
+ testTokenize("0o7.", &.{ .integer_literal, .period });
+ testTokenize("0o7.0", &.{ .integer_literal, .period, .integer_literal });
+
+ testTokenize("0O0", &.{ .invalid, .identifier });
+ testTokenize("0o_", &.{ .invalid, .identifier });
+ testTokenize("0o_0", &.{ .invalid, .identifier });
+ testTokenize("0o1_", &.{.invalid});
+ testTokenize("0o0__1", &.{ .invalid, .identifier });
+ testTokenize("0o0_1_", &.{.invalid});
+ testTokenize("0o1e", &.{ .invalid, .identifier });
+ testTokenize("0o1p", &.{ .invalid, .identifier });
+ testTokenize("0o1e0", &.{ .invalid, .identifier });
+ testTokenize("0o1p0", &.{ .invalid, .identifier });
+ testTokenize("0o_,", &.{ .invalid, .identifier, .comma });
}
test "tokenizer - number literals hexadeciaml" {
- testTokenize("0x0", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x1", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x2", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x3", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x4", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x5", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x6", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x7", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x8", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x9", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xa", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xb", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xc", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xd", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xe", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xf", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xA", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xB", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xC", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xD", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xE", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0xF", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x0z", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0xz", &[_]Token.Id{ .Invalid, .Identifier });
-
- testTokenize("0x0123456789ABCDEF", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x0123_4567_89AB_CDEF", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x01_23_45_67_89AB_CDE_F", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F", &[_]Token.Id{.IntegerLiteral});
-
- testTokenize("0X0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x_", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x_1", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x1_", &[_]Token.Id{.Invalid});
- testTokenize("0x0__1", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0_1_", &[_]Token.Id{.Invalid});
- testTokenize("0x_,", &[_]Token.Id{ .Invalid, .Identifier, .Comma });
-
- testTokenize("0x1.", &[_]Token.Id{.FloatLiteral});
- testTokenize("0x1.0", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xF.", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xF.0", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xF.F", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xF.Fp0", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xF.FP0", &[_]Token.Id{.FloatLiteral});
- testTokenize("0x1p0", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xfp0", &[_]Token.Id{.FloatLiteral});
- testTokenize("0x1.+0xF.", &[_]Token.Id{ .FloatLiteral, .Plus, .FloatLiteral });
-
- testTokenize("0x0123456.789ABCDEF", &[_]Token.Id{.FloatLiteral});
- testTokenize("0x0_123_456.789_ABC_DEF", &[_]Token.Id{.FloatLiteral});
- testTokenize("0x0_1_2_3_4_5_6.7_8_9_A_B_C_D_E_F", &[_]Token.Id{.FloatLiteral});
- testTokenize("0x0p0", &[_]Token.Id{.FloatLiteral});
- testTokenize("0x0.0p0", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xff.ffp10", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xff.ffP10", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xff.p10", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xffp10", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xff_ff.ff_ffp1_0_0_0", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xf_f_f_f.f_f_f_fp+1_000", &[_]Token.Id{.FloatLiteral});
- testTokenize("0xf_f_f_f.f_f_f_fp-1_00_0", &[_]Token.Id{.FloatLiteral});
-
- testTokenize("0x1e", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x1e0", &[_]Token.Id{.IntegerLiteral});
- testTokenize("0x1p", &[_]Token.Id{.Invalid});
- testTokenize("0xfp0z1", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0xff.ffpff", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0.p", &[_]Token.Id{.Invalid});
- testTokenize("0x0.z", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0._", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0_.0", &[_]Token.Id{ .Invalid, .Period, .IntegerLiteral });
- testTokenize("0x0_.0.0", &[_]Token.Id{ .Invalid, .Period, .FloatLiteral });
- testTokenize("0x0._0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0.0_", &[_]Token.Id{.Invalid});
- testTokenize("0x0_p0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0_.p0", &[_]Token.Id{ .Invalid, .Period, .Identifier });
- testTokenize("0x0._p0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0.0_p0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0._0p0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0.0p_0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0.0p+_0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0.0p-_0", &[_]Token.Id{ .Invalid, .Identifier });
- testTokenize("0x0.0p0_", &[_]Token.Id{ .Invalid, .Eof });
+ testTokenize("0x0", &.{.integer_literal});
+ testTokenize("0x1", &.{.integer_literal});
+ testTokenize("0x2", &.{.integer_literal});
+ testTokenize("0x3", &.{.integer_literal});
+ testTokenize("0x4", &.{.integer_literal});
+ testTokenize("0x5", &.{.integer_literal});
+ testTokenize("0x6", &.{.integer_literal});
+ testTokenize("0x7", &.{.integer_literal});
+ testTokenize("0x8", &.{.integer_literal});
+ testTokenize("0x9", &.{.integer_literal});
+ testTokenize("0xa", &.{.integer_literal});
+ testTokenize("0xb", &.{.integer_literal});
+ testTokenize("0xc", &.{.integer_literal});
+ testTokenize("0xd", &.{.integer_literal});
+ testTokenize("0xe", &.{.integer_literal});
+ testTokenize("0xf", &.{.integer_literal});
+ testTokenize("0xA", &.{.integer_literal});
+ testTokenize("0xB", &.{.integer_literal});
+ testTokenize("0xC", &.{.integer_literal});
+ testTokenize("0xD", &.{.integer_literal});
+ testTokenize("0xE", &.{.integer_literal});
+ testTokenize("0xF", &.{.integer_literal});
+ testTokenize("0x0z", &.{ .invalid, .identifier });
+ testTokenize("0xz", &.{ .invalid, .identifier });
+
+ testTokenize("0x0123456789ABCDEF", &.{.integer_literal});
+ testTokenize("0x0123_4567_89AB_CDEF", &.{.integer_literal});
+ testTokenize("0x01_23_45_67_89AB_CDE_F", &.{.integer_literal});
+ testTokenize("0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F", &.{.integer_literal});
+
+ testTokenize("0X0", &.{ .invalid, .identifier });
+ testTokenize("0x_", &.{ .invalid, .identifier });
+ testTokenize("0x_1", &.{ .invalid, .identifier });
+ testTokenize("0x1_", &.{.invalid});
+ testTokenize("0x0__1", &.{ .invalid, .identifier });
+ testTokenize("0x0_1_", &.{.invalid});
+ testTokenize("0x_,", &.{ .invalid, .identifier, .comma });
+
+ testTokenize("0x1.", &.{.float_literal});
+ testTokenize("0x1.0", &.{.float_literal});
+ testTokenize("0xF.", &.{.float_literal});
+ testTokenize("0xF.0", &.{.float_literal});
+ testTokenize("0xF.F", &.{.float_literal});
+ testTokenize("0xF.Fp0", &.{.float_literal});
+ testTokenize("0xF.FP0", &.{.float_literal});
+ testTokenize("0x1p0", &.{.float_literal});
+ testTokenize("0xfp0", &.{.float_literal});
+ testTokenize("0x1.+0xF.", &.{ .float_literal, .plus, .float_literal });
+
+ testTokenize("0x0123456.789ABCDEF", &.{.float_literal});
+ testTokenize("0x0_123_456.789_ABC_DEF", &.{.float_literal});
+ testTokenize("0x0_1_2_3_4_5_6.7_8_9_A_B_C_D_E_F", &.{.float_literal});
+ testTokenize("0x0p0", &.{.float_literal});
+ testTokenize("0x0.0p0", &.{.float_literal});
+ testTokenize("0xff.ffp10", &.{.float_literal});
+ testTokenize("0xff.ffP10", &.{.float_literal});
+ testTokenize("0xff.p10", &.{.float_literal});
+ testTokenize("0xffp10", &.{.float_literal});
+ testTokenize("0xff_ff.ff_ffp1_0_0_0", &.{.float_literal});
+ testTokenize("0xf_f_f_f.f_f_f_fp+1_000", &.{.float_literal});
+ testTokenize("0xf_f_f_f.f_f_f_fp-1_00_0", &.{.float_literal});
+
+ testTokenize("0x1e", &.{.integer_literal});
+ testTokenize("0x1e0", &.{.integer_literal});
+ testTokenize("0x1p", &.{.invalid});
+ testTokenize("0xfp0z1", &.{ .invalid, .identifier });
+ testTokenize("0xff.ffpff", &.{ .invalid, .identifier });
+ testTokenize("0x0.p", &.{.invalid});
+ testTokenize("0x0.z", &.{ .invalid, .identifier });
+ testTokenize("0x0._", &.{ .invalid, .identifier });
+ testTokenize("0x0_.0", &.{ .invalid, .period, .integer_literal });
+ testTokenize("0x0_.0.0", &.{ .invalid, .period, .float_literal });
+ testTokenize("0x0._0", &.{ .invalid, .identifier });
+ testTokenize("0x0.0_", &.{.invalid});
+ testTokenize("0x0_p0", &.{ .invalid, .identifier });
+ testTokenize("0x0_.p0", &.{ .invalid, .period, .identifier });
+ testTokenize("0x0._p0", &.{ .invalid, .identifier });
+ testTokenize("0x0.0_p0", &.{ .invalid, .identifier });
+ testTokenize("0x0._0p0", &.{ .invalid, .identifier });
+ testTokenize("0x0.0p_0", &.{ .invalid, .identifier });
+ testTokenize("0x0.0p+_0", &.{ .invalid, .identifier });
+ testTokenize("0x0.0p-_0", &.{ .invalid, .identifier });
+ testTokenize("0x0.0p0_", &.{ .invalid, .eof });
}
-fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
+fn testTokenize(source: []const u8, expected_tokens: []const Token.Tag) void {
var tokenizer = Tokenizer.init(source);
for (expected_tokens) |expected_token_id| {
const token = tokenizer.next();
- if (token.id != expected_token_id) {
- std.debug.panic("expected {}, found {}\n", .{ @tagName(expected_token_id), @tagName(token.id) });
+ if (token.tag != expected_token_id) {
+ std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.tag) });
}
}
const last_token = tokenizer.next();
- std.testing.expect(last_token.id == .Eof);
+ std.testing.expect(last_token.tag == .eof);
+ std.testing.expect(last_token.loc.start == source.len);
}