zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit a2abdb185f9e47b663edce1bdfa3fa525502f321 (tree)
parent a6bf37f8ca5a2eabc7cacb22696d2a2c622a993d
Author: Andrew Kelley <superjoe30@gmail.com>
Date:   Sun, 16 Sep 2018 10:51:58 -0400

Merge remote-tracking branch 'origin/master' into llvm7

Diffstat:
MCMakeLists.txt | 14+++++++++++++-
Mbuild.zig | 36+++++++++++++++++++++++++++---------
Mci/appveyor/build_script.bat | 2+-
Mci/travis_linux_script | 4++--
Mci/travis_osx_script | 4++--
Mdoc/docgen.zig | 257++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mdoc/langref.html.in | 1551++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc-self-hosted/main.zig | 2+-
Msrc-self-hosted/type.zig | 12------------
Msrc/all_types.hpp | 325++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/analyze.cpp | 508+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/analyze.hpp | 9+++++----
Msrc/ast_render.cpp | 7-------
Asrc/blake2.h | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/blake2b.c | 539+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/buffer.hpp | 4++++
Asrc/cache_hash.cpp | 469+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cache_hash.hpp | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/codegen.cpp | 530++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/codegen.hpp | 6+++---
Asrc/compiler.cpp | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/compiler.hpp | 17+++++++++++++++++
Msrc/error.cpp | 5+++++
Msrc/error.hpp | 5+++++
Msrc/ir.cpp | 585+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/link.cpp | 79++++++++++++++++++-------------------------------------------------------------
Dsrc/link.hpp | 17-----------------
Msrc/main.cpp | 145+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/os.cpp | 599+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/os.hpp | 99+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/parser.cpp | 9+--------
Msrc/target.cpp | 16++++++++++++++++
Msrc/target.hpp | 1+
Msrc/tokenizer.cpp | 2--
Msrc/tokenizer.hpp | 1-
Msrc/util.cpp | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/util.hpp | 13+++++++++++++
Msrc/zig_llvm.cpp | 9++++++++-
Msrc/zig_llvm.h | 3++-
Mstd/array_list.zig | 2+-
Mstd/atomic/int.zig | 2+-
Mstd/atomic/queue.zig | 4++--
Mstd/atomic/stack.zig | 2+-
Mstd/build.zig | 28+++++++++++++++++++++++++++-
Mstd/coff.zig | 52+++++++++++++++++++++++-----------------------------
Mstd/crypto/blake2.zig | 4++--
Mstd/crypto/hmac.zig | 2+-
Mstd/crypto/md5.zig | 2+-
Mstd/crypto/poly1305.zig | 2+-
Mstd/crypto/sha1.zig | 2+-
Mstd/crypto/sha2.zig | 4++--
Mstd/crypto/sha3.zig | 2+-
Mstd/crypto/x25519.zig | 2+-
Mstd/debug/index.zig | 29+++++++++++++++--------------
Mstd/event/channel.zig | 2+-
Mstd/event/fs.zig | 88++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mstd/event/future.zig | 2+-
Mstd/event/group.zig | 2+-
Mstd/event/locked.zig | 2+-
Mstd/event/loop.zig | 44++++++++++++++++++++++++++++++--------------
Mstd/event/rwlocked.zig | 2+-
Mstd/event/tcp.zig | 3++-
Mstd/fmt/index.zig | 2+-
Mstd/hash/crc.zig | 4++--
Mstd/hash/fnv.zig | 2+-
Mstd/hash/siphash.zig | 2+-
Mstd/hash_map.zig | 4+---
Mstd/heap.zig | 2+-
Mstd/io.zig | 12++++++------
Mstd/lazy_init.zig | 2+-
Mstd/linked_list.zig | 2+-
Mstd/math/complex/cosh.zig | 4++--
Mstd/math/complex/exp.zig | 9++++-----
Mstd/math/complex/index.zig | 2+-
Mstd/math/complex/sinh.zig | 4++--
Mstd/mem.zig | 2+-
Mstd/net.zig | 2+-
Mstd/os/child_process.zig | 12++++++++++--
Mstd/os/index.zig | 38++++++++++++++++++++------------------
Mstd/os/windows/kernel32.zig | 1-
Mstd/os/windows/util.zig | 7+++++--
Mstd/pdb.zig | 161++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mstd/segmented_list.zig | 2+-
Mstd/zig/ast.zig | 4++--
Mstd/zig/bench.zig | 2+-
Mstd/zig/parser_test.zig | 2+-
Mstd/zig/render.zig | 2+-
Mtest/behavior.zig | 3+++
Mtest/cases/align.zig | 7+++++++
Atest/cases/bugs/1322.zig | 19+++++++++++++++++++
Atest/cases/bugs/1381.zig | 21+++++++++++++++++++++
Atest/cases/bugs/1442.zig | 11+++++++++++
Mtest/cases/cast.zig | 15+++++++++++++--
Mtest/cases/eval.zig | 4++--
Mtest/cases/misc.zig | 3---
Mtest/cases/reflection.zig | 2+-
Mtest/cases/struct.zig | 4++--
Mtest/cases/this.zig | 13++-----------
Mtest/cases/type_info.zig | 4++--
Mtest/cases/union.zig | 39+++++++++++++++++++++++++++++++++++++++
Mtest/compile_errors.zig | 47+++++++++++++++++++++++++++++------------------
Mtest/standalone/brace_expansion/main.zig | 2+-
102 files changed, 4814 insertions(+), 2216 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -393,7 +393,7 @@ if(MSVC) ) else() set_target_properties(embedded_softfloat PROPERTIES - COMPILE_FLAGS "-std=c99" + COMPILE_FLAGS "-std=c99 -O3" ) endif() target_include_directories(embedded_softfloat PUBLIC @@ -412,7 +412,9 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/bigint.cpp" "${CMAKE_SOURCE_DIR}/src/buffer.cpp" "${CMAKE_SOURCE_DIR}/src/c_tokenizer.cpp" + "${CMAKE_SOURCE_DIR}/src/cache_hash.cpp" "${CMAKE_SOURCE_DIR}/src/codegen.cpp" + "${CMAKE_SOURCE_DIR}/src/compiler.cpp" "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" "${CMAKE_SOURCE_DIR}/src/error.cpp" "${CMAKE_SOURCE_DIR}/src/ir.cpp" @@ -427,6 +429,9 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/util.cpp" "${CMAKE_SOURCE_DIR}/src/translate_c.cpp" ) +set(BLAKE_SOURCES + "${CMAKE_SOURCE_DIR}/src/blake2b.c" +) set(ZIG_CPP_SOURCES "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" @@ -793,6 +798,7 @@ else() set(EXE_CFLAGS "${EXE_CFLAGS} -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fno-exceptions -fno-rtti -Werror=strict-prototypes -Werror=old-style-definition -Werror=type-limits -Wno-missing-braces") endif() +set(BLAKE_CFLAGS "-std=c99") set(EXE_LDFLAGS " ") if(MINGW) @@ -814,6 +820,11 @@ set_target_properties(zig_cpp PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} ) +add_library(embedded_blake STATIC ${BLAKE_SOURCES}) +set_target_properties(embedded_blake PROPERTIES + COMPILE_FLAGS "${BLAKE_CFLAGS} -O3" +) + add_executable(zig ${ZIG_SOURCES}) set_target_properties(zig PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} @@ -822,6 +833,7 @@ set_target_properties(zig PROPERTIES target_link_libraries(zig LINK_PUBLIC zig_cpp + embedded_blake ${SOFTFLOAT_LIBRARIES} ${CLANG_LIBRARIES} ${LLD_LIBRARIES} diff --git a/build.zig b/build.zig @@ -16,11 +16,12 @@ pub fn build(b: *Builder) !void { var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe); + const langref_out_path = os.path.join(b.allocator, b.cache_root, "langref.html") catch unreachable; var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{ docgen_exe.getOutputPath(), rel_zig_exe, "doc" ++ os.path.sep_str ++ "langref.html.in", - os.path.join(b.allocator, b.cache_root, "langref.html") catch unreachable, + langref_out_path, }); docgen_cmd.step.dependOn(&docgen_exe.step); @@ -61,6 +62,9 @@ pub fn build(b: *Builder) !void { b.default_step.dependOn(&exe.step); const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; + const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release; + const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release; + const skip_release_safe = b.option(bool, "skip-release-safe", "Main test suite skips release-safe builds") orelse skip_release; const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false; if (!skip_self_hosted) { test_step.dependOn(&exe.step); @@ -76,15 +80,29 @@ pub fn build(b: *Builder) !void { const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); test_stage2_step.dependOn(&test_stage2.step); - test_step.dependOn(test_stage2_step); - const all_modes = []builtin.Mode{ - builtin.Mode.Debug, - builtin.Mode.ReleaseSafe, - builtin.Mode.ReleaseFast, - builtin.Mode.ReleaseSmall, - }; - const modes = if (skip_release) []builtin.Mode{builtin.Mode.Debug} else all_modes; + // TODO see https://github.com/ziglang/zig/issues/1364 + if (false) { + test_step.dependOn(test_stage2_step); + } + + var chosen_modes: [4]builtin.Mode = undefined; + var chosen_mode_index: usize = 0; + chosen_modes[chosen_mode_index] = builtin.Mode.Debug; + chosen_mode_index += 1; + if (!skip_release_safe) { + chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe; + chosen_mode_index += 1; + } + if (!skip_release_fast) { + chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseFast; + chosen_mode_index += 1; + } + if (!skip_release_small) { + chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSmall; + chosen_mode_index += 1; + } + const modes = chosen_modes[0..chosen_mode_index]; test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", modes)); diff --git a/ci/appveyor/build_script.bat b/ci/appveyor/build_script.bat @@ -23,4 +23,4 @@ cd %ZIGBUILDDIR% cmake.exe .. -Thost=x64 -G"Visual Studio 14 2015 Win64" "-DCMAKE_INSTALL_PREFIX=%ZIGBUILDDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release || exit /b msbuild /p:Configuration=Release INSTALL.vcxproj || exit /b -bin\zig.exe build --build-file ..\build.zig test || exit /b +bin\zig.exe build --build-file ..\build.zig test -Dskip-release || exit /b diff --git a/ci/travis_linux_script b/ci/travis_linux_script @@ -8,9 +8,9 @@ export CXX=clang++-7.0 echo $PATH mkdir build cd build -cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) +cmake .. -DCMAKE_BUILD_TYPE=Release make -j2 install -./zig build --build-file ../build.zig test +./zig build --build-file ../build.zig test -Dskip-release-small if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then mkdir $TRAVIS_BUILD_DIR/artifacts diff --git a/ci/travis_osx_script b/ci/travis_osx_script @@ -5,8 +5,8 @@ set -e mkdir build cd build -cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@7/ -DCMAKE_INSTALL_PREFIX=$(pwd) +cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@7/ -DCMAKE_BUILD_TYPE=Release make VERBOSE=1 make install -./zig build --build-file ../build.zig test +./zig build --build-file ../build.zig test -Dskip-release-small diff --git a/doc/docgen.zig b/doc/docgen.zig @@ -11,6 +11,7 @@ const max_doc_file_size = 10 * 1024 * 1024; const exe_ext = std.build.Target(std.build.Target.Native).exeFileExt(); const obj_ext = std.build.Target(std.build.Target.Native).oFileExt(); const tmp_dir_name = "docgen_tmp"; +const test_out_path = tmp_dir_name ++ os.path.sep_str ++ "test" ++ exe_ext; pub fn main() !void { var direct_allocator = std.heap.DirectAllocator.init(); @@ -299,6 +300,7 @@ const Node = union(enum) { SeeAlso: []const SeeAlsoItem, Code: Code, Link: Link, + Syntax: Token, }; const Toc = struct { @@ -529,6 +531,17 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { }, }); tokenizer.code_node_count += 1; + } else if (mem.eql(u8, tag_name, "syntax")) { + _ = try eatToken(tokenizer, Token.Id.BracketClose); + const content_tok = try eatToken(tokenizer, Token.Id.Content); + _ = try eatToken(tokenizer, Token.Id.BracketOpen); + const end_syntax_tag = try eatToken(tokenizer, Token.Id.TagContent); + const end_tag_name = tokenizer.buffer[end_syntax_tag.start..end_syntax_tag.end]; + if (!mem.eql(u8, end_tag_name, "endsyntax")) { + return parseError(tokenizer, end_syntax_tag, "invalid token inside syntax: {}", end_tag_name); + } + _ = try eatToken(tokenizer, Token.Id.BracketClose); + try nodes.append(Node{ .Syntax = content_tok }); } else { return parseError(tokenizer, tag_token, "unrecognized tag name: {}", tag_name); } @@ -570,6 +583,11 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 { var buf_adapter = io.BufferOutStream.init(&buf); var out = &buf_adapter.stream; + try writeEscaped(out, input); + return buf.toOwnedSlice(); +} + +fn writeEscaped(out: var, input: []const u8) !void { for (input) |c| { try switch (c) { '&' => out.write("&amp;"), @@ -579,7 +597,6 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 { else => out.writeByte(c), }; } - return buf.toOwnedSlice(); } //#define VT_RED "\x1b[31;1m" @@ -686,6 +703,230 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 { return buf.toOwnedSlice(); } +const builtin_types = [][]const u8{ + "f16", "f32", "f64", "f128", "c_longdouble", "c_short", + "c_ushort", "c_int", "c_uint", "c_long", "c_ulong", "c_longlong", + "c_ulonglong", "c_char", "c_void", "void", "bool", "isize", + "usize", "noreturn", "type", "error", "comptime_int", "comptime_float", +}; + +fn isType(name: []const u8) bool { + for (builtin_types) |t| { + if (mem.eql(u8, t, name)) + return true; + } + return false; +} + +fn tokenizeAndPrint(allocator: *mem.Allocator, docgen_tokenizer: *Tokenizer, out: var, source_token: Token) !void { + const raw_src = docgen_tokenizer.buffer[source_token.start..source_token.end]; + const src = mem.trim(u8, raw_src, " \n"); + try out.write("<code class=\"zig\">"); + var tokenizer = std.zig.Tokenizer.init(src); + var index: usize = 0; + var next_tok_is_fn = false; + while (true) { + const prev_tok_was_fn = next_tok_is_fn; + next_tok_is_fn = false; + + const token = tokenizer.next(); + try writeEscaped(out, src[index..token.start]); + switch (token.id) { + std.zig.Token.Id.Eof => break, + + std.zig.Token.Id.Keyword_align, + std.zig.Token.Id.Keyword_and, + std.zig.Token.Id.Keyword_asm, + std.zig.Token.Id.Keyword_async, + std.zig.Token.Id.Keyword_await, + std.zig.Token.Id.Keyword_break, + std.zig.Token.Id.Keyword_cancel, + std.zig.Token.Id.Keyword_catch, + std.zig.Token.Id.Keyword_comptime, + std.zig.Token.Id.Keyword_const, + std.zig.Token.Id.Keyword_continue, + std.zig.Token.Id.Keyword_defer, + std.zig.Token.Id.Keyword_else, + std.zig.Token.Id.Keyword_enum, + std.zig.Token.Id.Keyword_errdefer, + std.zig.Token.Id.Keyword_error, + std.zig.Token.Id.Keyword_export, + std.zig.Token.Id.Keyword_extern, + std.zig.Token.Id.Keyword_for, + std.zig.Token.Id.Keyword_if, + std.zig.Token.Id.Keyword_inline, + std.zig.Token.Id.Keyword_nakedcc, + std.zig.Token.Id.Keyword_noalias, + std.zig.Token.Id.Keyword_or, + std.zig.Token.Id.Keyword_orelse, + std.zig.Token.Id.Keyword_packed, + std.zig.Token.Id.Keyword_promise, + std.zig.Token.Id.Keyword_pub, + std.zig.Token.Id.Keyword_resume, + std.zig.Token.Id.Keyword_return, + std.zig.Token.Id.Keyword_section, + std.zig.Token.Id.Keyword_stdcallcc, + std.zig.Token.Id.Keyword_struct, + std.zig.Token.Id.Keyword_suspend, + std.zig.Token.Id.Keyword_switch, + std.zig.Token.Id.Keyword_test, + std.zig.Token.Id.Keyword_try, + std.zig.Token.Id.Keyword_union, + std.zig.Token.Id.Keyword_unreachable, + std.zig.Token.Id.Keyword_use, + std.zig.Token.Id.Keyword_var, + std.zig.Token.Id.Keyword_volatile, + std.zig.Token.Id.Keyword_while, + => { + try out.write("<span class=\"tok-kw\">"); + try writeEscaped(out, src[token.start..token.end]); + try out.write("</span>"); + }, + + std.zig.Token.Id.Keyword_fn => { + try out.write("<span class=\"tok-kw\">"); + try writeEscaped(out, src[token.start..token.end]); + try out.write("</span>"); + next_tok_is_fn = true; + }, + + std.zig.Token.Id.Keyword_undefined, + std.zig.Token.Id.Keyword_null, + std.zig.Token.Id.Keyword_true, + std.zig.Token.Id.Keyword_false, + std.zig.Token.Id.Keyword_this, + => { + try out.write("<span class=\"tok-null\">"); + try writeEscaped(out, src[token.start..token.end]); + try out.write("</span>"); + }, + + std.zig.Token.Id.StringLiteral, + std.zig.Token.Id.MultilineStringLiteralLine, + std.zig.Token.Id.CharLiteral, + => { + try out.write("<span class=\"tok-str\">"); + try writeEscaped(out, src[token.start..token.end]); + try out.write("</span>"); + }, + + std.zig.Token.Id.Builtin => { + try out.write("<span class=\"tok-builtin\">"); + try writeEscaped(out, src[token.start..token.end]); + try out.write("</span>"); + }, + + std.zig.Token.Id.LineComment, + std.zig.Token.Id.DocComment, + => { + try out.write("<span class=\"tok-comment\">"); + try writeEscaped(out, src[token.start..token.end]); + try out.write("</span>"); + }, + + std.zig.Token.Id.Identifier => { + if (prev_tok_was_fn) { + try out.write("<span class=\"tok-fn\">"); + try writeEscaped(out, src[token.start..token.end]); + try out.write("</span>"); + } else { + const is_int = blk: { + if (src[token.start] != 'i' and src[token.start] != 'u') + break :blk false; + var i = token.start + 1; + if (i == token.end) + break :blk false; + while (i != token.end) : (i += 1) { + if (src[i] < '0' or src[i] > '9') + break :blk false; + } + break :blk true; + }; + if (is_int or isType(src[token.start..token.end])) { + try out.write("<span class=\"tok-type\">"); + try writeEscaped(out, src[token.start..token.end]); + try out.write("</span>"); + } else { + try writeEscaped(out, src[token.start..token.end]); + } + } + }, + + std.zig.Token.Id.IntegerLiteral, + std.zig.Token.Id.FloatLiteral, + => { + try out.write("<span class=\"tok-number\">"); + try writeEscaped(out, src[token.start..token.end]); + try out.write("</span>"); + }, + + std.zig.Token.Id.Bang, + std.zig.Token.Id.Pipe, + std.zig.Token.Id.PipePipe, + std.zig.Token.Id.PipeEqual, + std.zig.Token.Id.Equal, + std.zig.Token.Id.EqualEqual, + std.zig.Token.Id.EqualAngleBracketRight, + std.zig.Token.Id.BangEqual, + std.zig.Token.Id.LParen, + std.zig.Token.Id.RParen, + std.zig.Token.Id.Semicolon, + std.zig.Token.Id.Percent, + std.zig.Token.Id.PercentEqual, + std.zig.Token.Id.LBrace, + std.zig.Token.Id.RBrace, + std.zig.Token.Id.LBracket, + std.zig.Token.Id.RBracket, + std.zig.Token.Id.Period, + std.zig.Token.Id.Ellipsis2, + std.zig.Token.Id.Ellipsis3, + std.zig.Token.Id.Caret, + std.zig.Token.Id.CaretEqual, + std.zig.Token.Id.Plus, + std.zig.Token.Id.PlusPlus, + std.zig.Token.Id.PlusEqual, + std.zig.Token.Id.PlusPercent, + std.zig.Token.Id.PlusPercentEqual, + std.zig.Token.Id.Minus, + std.zig.Token.Id.MinusEqual, + std.zig.Token.Id.MinusPercent, + std.zig.Token.Id.MinusPercentEqual, + std.zig.Token.Id.Asterisk, + std.zig.Token.Id.AsteriskEqual, + std.zig.Token.Id.AsteriskAsterisk, + std.zig.Token.Id.AsteriskPercent, + std.zig.Token.Id.AsteriskPercentEqual, + std.zig.Token.Id.Arrow, + std.zig.Token.Id.Colon, + std.zig.Token.Id.Slash, + std.zig.Token.Id.SlashEqual, + std.zig.Token.Id.Comma, + std.zig.Token.Id.Ampersand, + std.zig.Token.Id.AmpersandEqual, + std.zig.Token.Id.QuestionMark, + std.zig.Token.Id.AngleBracketLeft, + std.zig.Token.Id.AngleBracketLeftEqual, + std.zig.Token.Id.AngleBracketAngleBracketLeft, + std.zig.Token.Id.AngleBracketAngleBracketLeftEqual, + std.zig.Token.Id.AngleBracketRight, + std.zig.Token.Id.AngleBracketRightEqual, + std.zig.Token.Id.AngleBracketAngleBracketRight, + std.zig.Token.Id.AngleBracketAngleBracketRightEqual, + std.zig.Token.Id.Tilde, + std.zig.Token.Id.BracketStarBracket, + => try writeEscaped(out, src[token.start..token.end]), + + std.zig.Token.Id.Invalid => return parseError( + docgen_tokenizer, + source_token, + "syntax error", + ), + } + index = token.end; + } + try out.write("</code>"); +} + fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void { var code_progress_index: usize = 0; @@ -725,17 +966,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } try out.write("</ul>\n"); }, + Node.Syntax => |content_tok| { + try tokenizeAndPrint(allocator, tokenizer, out, content_tok); + }, Node.Code => |code| { code_progress_index += 1; warn("docgen example code {}/{}...", code_progress_index, tokenizer.code_node_count); const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end]; const trimmed_raw_source = mem.trim(u8, raw_source, " \n"); - const escaped_source = try escapeHtml(allocator, trimmed_raw_source); if (!code.is_inline) { try out.print("<p class=\"file\">{}.zig</p>", code.name); } - try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source); + try out.write("<pre>"); + try tokenizeAndPrint(allocator, tokenizer, out, code.source_token); + try out.write("</pre>"); const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name); const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext); try io.writeFile(tmp_source_file_name, trimmed_raw_source); @@ -821,6 +1066,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var zig_exe, "test", tmp_source_file_name, + "--output", + test_out_path, }); try out.print("<pre><code class=\"shell\">$ zig test {}.zig", code.name); switch (code.mode) { @@ -863,6 +1110,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var "--color", "on", tmp_source_file_name, + "--output", + test_out_path, }); try out.print("<pre><code class=\"shell\">$ zig test {}.zig", code.name); switch (code.mode) { @@ -918,6 +1167,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var zig_exe, "test", tmp_source_file_name, + "--output", + test_out_path, }); switch (code.mode) { builtin.Mode.Debug => {}, diff --git a/doc/langref.html.in b/doc/langref.html.in @@ -5,9 +5,6 @@ <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> <title>Documentation - The Zig Programming Language</title> <style type="text/css"> -.hljs{display:block;overflow-x:auto;padding:0.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:bold}.hljs-number,.hljs-literal,.hljs-variable,.hljs-template-variable,.hljs-tag .hljs-attr{color:#008080}.hljs-string,.hljs-doctag{color:#d14}.hljs-title,.hljs-section,.hljs-selector-id{color:#900;font-weight:bold}.hljs-subst{font-weight:normal}.hljs-type,.hljs-class .hljs-title{color:#458;font-weight:bold}.hljs-tag,.hljs-name,.hljs-attribute{color:#000080;font-weight:normal}.hljs-regexp,.hljs-link{color:#009926}.hljs-symbol,.hljs-bullet{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:bold}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} - </style> - <style type="text/css"> table, th, td { border-collapse: collapse; border: 1px solid grey; @@ -39,11 +36,46 @@ pre > code { display: block; overflow: auto; + + overflow-x: auto; + padding: 0.5em; + color: #333; + background: #f8f8f8; } .table-wrapper { width: 100%; overflow-y: auto; } + + .tok-kw { + color: #333; + font-weight: bold; + } + .tok-str { + color: #d14; + } + .tok-builtin { + color: #0086b3; + } + .tok-comment { + color: #998; + font-style: italic; + } + .tok-fn { + color: #900; + font-weight: bold; + } + .tok-null { + color: #008080; + } + .tok-number { + color: #008080; + } + .tok-type { + color: #458; + font-weight: bold; + } + /* Desktop */ @media screen and (min-width: 56.25em) { #nav { @@ -129,8 +161,8 @@ pub fn main() void { } {#code_end#} <p> - Note that we also left off the <code class="zig">!</code> from the return type. - In Zig, if your main function cannot fail, you must use the <code class="zig">void</code> return type. + Note that we also left off the {#syntax#}!{#endsyntax#} from the return type. + In Zig, if your main function cannot fail, you must use the {#syntax#}void{#endsyntax#} return type. </p> {#see_also|Values|@import|Errors|Root Source File#} {#header_close#} @@ -149,14 +181,14 @@ test "comments" { } {#code_end#} <p> - There are no multiline comments in Zig (e.g. like <code>/* */</code> + There are no multiline comments in Zig (e.g. like <code class="c">/* */</code> comments in C). This helps allow Zig to have the property that each line of code can be tokenized out of context. </p> {#header_open|Doc comments#} <p> A doc comment is one that begins with exactly three slashes (i.e. - <code class="zig">///</code> but not <code class="zig">////</code>); + {#syntax#}///{#endsyntax#} but not {#syntax#}////{#endsyntax#}); multiple doc comments in a row are merged together to form a multiline doc comment. The doc comment documents whatever immediately follows it. </p> @@ -248,169 +280,169 @@ pub fn main() void { </th> </tr> <tr> - <td><code>i8</code></td> - <td><code>int8_t</code></td> + <td>{#syntax#}i8{#endsyntax#}</td> + <td><code class="c">int8_t</code></td> <td>signed 8-bit integer</td> </tr> <tr> - <td><code>u8</code></td> - <td><code>uint8_t</code></td> + <td>{#syntax#}u8{#endsyntax#}</td> + <td><code class="c">uint8_t</code></td> <td>unsigned 8-bit integer</td> </tr> <tr> - <td><code>i16</code></td> - <td><code>int16_t</code></td> + <td>{#syntax#}i16{#endsyntax#}</td> + <td><code class="c">int16_t</code></td> <td>signed 16-bit integer</td> </tr> <tr> - <td><code>u16</code></td> - <td><code>uint16_t</code></td> + <td>{#syntax#}u16{#endsyntax#}</td> + <td><code class="c">uint16_t</code></td> <td>unsigned 16-bit integer</td> </tr> <tr> - <td><code>i32</code></td> - <td><code>int32_t</code></td> + <td>{#syntax#}i32{#endsyntax#}</td> + <td><code class="c">int32_t</code></td> <td>signed 32-bit integer</td> </tr> <tr> - <td><code>u32</code></td> - <td><code>uint32_t</code></td> + <td>{#syntax#}u32{#endsyntax#}</td> + <td><code class="c">uint32_t</code></td> <td>unsigned 32-bit integer</td> </tr> <tr> - <td><code>i64</code></td> - <td><code>int64_t</code></td> + <td>{#syntax#}i64{#endsyntax#}</td> + <td><code class="c">int64_t</code></td> <td>signed 64-bit integer</td> </tr> <tr> - <td><code>u64</code></td> - <td><code>uint64_t</code></td> + <td>{#syntax#}u64{#endsyntax#}</td> + <td><code class="c">uint64_t</code></td> <td>unsigned 64-bit integer</td> </tr> <tr> - <td><code>i128</code></td> - <td><code>__int128</code></td> + <td>{#syntax#}i128{#endsyntax#}</td> + <td><code class="c">__int128</code></td> <td>signed 128-bit integer</td> </tr> <tr> - <td><code>u128</code></td> - <td><code>unsigned __int128</code></td> + <td>{#syntax#}u128{#endsyntax#}</td> + <td><code class="c">unsigned __int128</code></td> <td>unsigned 128-bit integer</td> </tr> <tr> - <td><code>isize</code></td> - <td><code>intptr_t</code></td> + <td>{#syntax#}isize{#endsyntax#}</td> + <td><code class="c">intptr_t</code></td> <td>signed pointer sized integer</td> </tr> <tr> - <td><code>usize</code></td> - <td><code>uintptr_t</code></td> + <td>{#syntax#}usize{#endsyntax#}</td> + <td><code class="c">uintptr_t</code></td> <td>unsigned pointer sized integer</td> </tr> <tr> - <td><code>c_short</code></td> - <td><code>short</code></td> + <td>{#syntax#}c_short{#endsyntax#}</td> + <td><code class="c">short</code></td> <td>for ABI compatibility with C</td> </tr> <tr> - <td><code>c_ushort</code></td> - <td><code>unsigned short</code></td> + <td>{#syntax#}c_ushort{#endsyntax#}</td> + <td><code class="c">unsigned short</code></td> <td>for ABI compatibility with C</td> </tr> <tr> - <td><code>c_int</code></td> - <td><code>int</code></td> + <td>{#syntax#}c_int{#endsyntax#}</td> + <td><code class="c">int</code></td> <td>for ABI compatibility with C</td> </tr> <tr> - <td><code>c_uint</code></td> - <td><code>unsigned int</code></td> + <td>{#syntax#}c_uint{#endsyntax#}</td> + <td><code class="c">unsigned int</code></td> <td>for ABI compatibility with C</td> </tr> <tr> - <td><code>c_long</code></td> - <td><code>long</code></td> + <td>{#syntax#}c_long{#endsyntax#}</td> + <td><code class="c">long</code></td> <td>for ABI compatibility with C</td> </tr> <tr> - <td><code>c_ulong</code></td> - <td><code>unsigned long</code></td> + <td>{#syntax#}c_ulong{#endsyntax#}</td> + <td><code class="c">unsigned long</code></td> <td>for ABI compatibility with C</td> </tr> <tr> - <td><code>c_longlong</code></td> - <td><code>long long</code></td> + <td>{#syntax#}c_longlong{#endsyntax#}</td> + <td><code class="c">long long</code></td> <td>for ABI compatibility with C</td> </tr> <tr> - <td><code>c_ulonglong</code></td> - <td><code>unsigned long long</code></td> + <td>{#syntax#}c_ulonglong{#endsyntax#}</td> + <td><code class="c">unsigned long long</code></td> <td>for ABI compatibility with C</td> </tr> <tr> - <td><code>c_longdouble</code></td> - <td><code>long double</code></td> + <td>{#syntax#}c_longdouble{#endsyntax#}</td> + <td><code class="c">long double</code></td> <td>for ABI compatibility with C</td> </tr> <tr> - <td><code>c_void</code></td> - <td><code>void</code></td> + <td>{#syntax#}c_void{#endsyntax#}</td> + <td><code class="c">void</code></td> <td>for ABI compatibility with C</td> </tr> <tr> - <td><code>f16</code></td> - <td><code>float</code></td> + <td>{#syntax#}f16{#endsyntax#}</td> + <td><code class="c">float</code></td> <td>16-bit floating point (10-bit mantissa) IEEE-754-2008 binary16</td> </tr> <tr> - <td><code>f32</code></td> - <td><code>float</code></td> + <td>{#syntax#}f32{#endsyntax#}</td> + <td><code class="c">float</code></td> <td>32-bit floating point (23-bit mantissa) IEEE-754-2008 binary32</td> </tr> <tr> - <td><code>f64</code></td> - <td><code>double</code></td> + <td>{#syntax#}f64{#endsyntax#}</td> + <td><code class="c">double</code></td> <td>64-bit floating point (52-bit mantissa) IEEE-754-2008 binary64</td> </tr> <tr> - <td><code>f128</code></td> + <td>{#syntax#}f128{#endsyntax#}</td> <td>(none)</td> <td>128-bit floating point (112-bit mantissa) IEEE-754-2008 binary128</td> </tr> <tr> - <td><code>bool</code></td> - <td><code>bool</code></td> - <td><code>true</code> or <code>false</code></td> + <td>{#syntax#}bool{#endsyntax#}</td> + <td><code class="c">bool</code></td> + <td>{#syntax#}true{#endsyntax#} or {#syntax#}false{#endsyntax#}</td> </tr> <tr> - <td><code>void</code></td> + <td>{#syntax#}void{#endsyntax#}</td> <td>(none)</td> <td>0 bit type</td> </tr> <tr> - <td><code>noreturn</code></td> + <td>{#syntax#}noreturn{#endsyntax#}</td> <td>(none)</td> - <td>the type of <code>break</code>, <code>continue</code>, <code>return</code>, <code>unreachable</code>, and <code>while (true) {}</code></td> + <td>the type of {#syntax#}break{#endsyntax#}, {#syntax#}continue{#endsyntax#}, {#syntax#}return{#endsyntax#}, {#syntax#}unreachable{#endsyntax#}, and {#syntax#}while (true) {}{#endsyntax#}</td> </tr> <tr> - <td><code>type</code></td> + <td>{#syntax#}type{#endsyntax#}</td> <td>(none)</td> <td>the type of types</td> </tr> <tr> - <td><code>error</code></td> + <td>{#syntax#}error{#endsyntax#}</td> <td>(none)</td> <td>an error code</td> </tr> <tr> - <td><code>comptime_int</code></td> + <td>{#syntax#}comptime_int{#endsyntax#}</td> <td>(none)</td> <td>Only allowed for {#link|comptime#}-known values. The type of integer literals.</td> </tr> <tr> - <td><code>comptime_float</code></td> + <td>{#syntax#}comptime_float{#endsyntax#}</td> <td>(none)</td> <td>Only allowed for {#link|comptime#}-known values. The type of float literals.</td> </tr> @@ -419,7 +451,7 @@ pub fn main() void { <p> In addition to the integer types above, arbitrary bit-width integers can be referenced by using an identifier of <code>i</code> or </code>u</code> followed by digits. For example, the identifier - <code>i7</code> refers to a signed 7-bit integer. + {#syntax#}i7{#endsyntax#} refers to a signed 7-bit integer. </p> {#see_also|Integers|Floats|void|Errors#} {#header_close#} @@ -435,24 +467,20 @@ pub fn main() void { </th> </tr> <tr> - <td><code>true</code> and <code>false</code></td> - <td><code>bool</code> values</td> + <td>{#syntax#}true{#endsyntax#} and {#syntax#}false{#endsyntax#}</td> + <td>{#syntax#}bool{#endsyntax#} values</td> </tr> <tr> - <td><code>null</code></td> - <td>used to set an optional type to <code>null</code></td> + <td>{#syntax#}null{#endsyntax#}</td> + <td>used to set an optional type to {#syntax#}null{#endsyntax#}</td> </tr> <tr> - <td><code>undefined</code></td> + <td>{#syntax#}undefined{#endsyntax#}</td> <td>used to leave a value unspecified</td> </tr> - <tr> - <td><code>this</code></td> - <td>refers to the thing in immediate scope</td> - </tr> </table> </div> - {#see_also|Optionals|this#} + {#see_also|Optionals#} {#header_close#} {#header_open|String Literals#} {#code_begin|test#} @@ -487,52 +515,52 @@ test "string literals" { </th> </tr> <tr> - <td><code>\n</code></td> + <td><code>\n</code></td> <td>Newline</td> </tr> <tr> - <td><code>\r</code></td> + <td><code>\r</code></td> <td>Carriage Return</td> </tr> <tr> - <td><code>\t</code></td> + <td><code>\t</code></td> <td>Tab</td> </tr> <tr> - <td><code>\\</code></td> + <td><code>\\</code></td> <td>Backslash</td> </tr> <tr> - <td><code>\'</code></td> + <td><code>\'</code></td> <td>Single Quote</td> </tr> <tr> - <td><code>\"</code></td> + <td><code>\"</code></td> <td>Double Quote</td> </tr> <tr> - <td><code>\xNN</code></td> + <td><code>\xNN</code></td> <td>hexadecimal 8-bit character code (2 digits)</td> </tr> <tr> - <td><code>\uNNNN</code></td> + <td><code>\uNNNN</code></td> <td>hexadecimal 16-bit Unicode character code UTF-8 encoded (4 digits)</td> </tr> <tr> - <td><code>\UNNNNNN</code></td> + <td><code>\UNNNNNN</code></td> <td>hexadecimal 24-bit Unicode character code UTF-8 encoded (6 digits)</td> </tr> </table> </div> - <p>Note that the maximum valid Unicode point is <code>0x10ffff</code>.</p> + <p>Note that the maximum valid Unicode point is {#syntax#}0x10ffff{#endsyntax#}.</p> {#header_close#} {#header_open|Multiline String Literals#} <p> Multiline string literals have no escapes and can span across multiple lines. - To start a multiline string literal, use the <code>\\</code> token. Just like a comment, + To start a multiline string literal, use the {#syntax#}\\{#endsyntax#} token. Just like a comment, the string literal goes until the end of the line. The end of the line is not included in the string literal. - However, if the next line begins with <code>\\</code> then a newline is appended and + However, if the next line begins with {#syntax#}\\{#endsyntax#} then a newline is appended and the string literal continues. </p> {#code_begin|syntax#} @@ -546,7 +574,7 @@ const hello_world_in_c = ; {#code_end#} <p> - For a multiline C string literal, prepend <code>c</code> to each <code>\\</code>: + For a multiline C string literal, prepend <code>c</code> to each {#syntax#}\\{#endsyntax#}: </p> {#code_begin|syntax#} const c_string_literal = @@ -559,14 +587,14 @@ const c_string_literal = ; {#code_end#} <p> - In this example the variable <code>c_string_literal</code> has type <code>[*]const char</code> and + In this example the variable {#syntax#}c_string_literal{#endsyntax#} has type {#syntax#}[*]const char{#endsyntax#} and has a terminating null byte. </p> {#see_also|@embedFile#} {#header_close#} {#header_close#} {#header_open|Assignment#} - <p>Use the <code>const</code> keyword to assign a value to an identifier:</p> + <p>Use the {#syntax#}const{#endsyntax#} keyword to assign a value to an identifier:</p> {#code_begin|test_err|cannot assign to constant#} const x = 1234; @@ -582,8 +610,8 @@ test "assignment" { foo(); } {#code_end#} - <p><code>const</code> applies to all of the bytes that the identifier immediately addresses. {#link|Pointers#} have their own const-ness.</p> - <p>If you need a variable that you can modify, use the <code>var</code> keyword:</p> + <p>{#syntax#}const{#endsyntax#} applies to all of the bytes that the identifier immediately addresses. {#link|Pointers#} have their own const-ness.</p> + <p>If you need a variable that you can modify, use the {#syntax#}var{#endsyntax#} keyword:</p> {#code_begin|test#} const assert = @import("std").debug.assert; @@ -604,7 +632,7 @@ test "initialization" { } {#code_end#} {#header_open|undefined#} - <p>Use <code>undefined</code> to leave variables uninitialized:</p> + <p>Use {#syntax#}undefined{#endsyntax#} to leave variables uninitialized:</p> {#code_begin|test#} const assert = @import("std").debug.assert; @@ -615,14 +643,14 @@ test "init with undefined" { } {#code_end#} <p> - <code>undefined</code> can be {#link|implicitly cast|Implicit Casts#} to any type. - Once this happens, it is no longer possible to detect that the value is <code>undefined</code>. - <code>undefined</code> means the value could be anything, even something that is nonsense - according to the type. Translated into English, <code>undefined</code> means "Not a meaningful + {#syntax#}undefined{#endsyntax#} can be {#link|implicitly cast|Implicit Casts#} to any type. + Once this happens, it is no longer possible to detect that the value is {#syntax#}undefined{#endsyntax#}. + {#syntax#}undefined{#endsyntax#} means the value could be anything, even something that is nonsense + according to the type. Translated into English, {#syntax#}undefined{#endsyntax#} means "Not a meaningful value. Using this value would be a bug. The value will be unused, or overwritten before being used." </p> <p> - In {#link|Debug#} mode, Zig writes <code>0xaa</code> bytes to undefined memory. This is to catch + In {#link|Debug#} mode, Zig writes {#syntax#}0xaa{#endsyntax#} bytes to undefined memory. This is to catch bugs early, and to help detect use of undefined memory in a debugger. </p> {#header_close#} @@ -653,14 +681,14 @@ fn divide(a: i32, b: i32) i32 { } {#code_end#} <p> - In this function, values <code>a</code> and <code>b</code> are known only at runtime, + In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime, and thus this division operation is vulnerable to both integer overflow and division by zero. </p> <p> - Operators such as <code>+</code> and <code>-</code> cause undefined behavior on - integer overflow. Also available are operations such as <code>+%</code> and - <code>-%</code> which are defined to have wrapping arithmetic on all targets. + Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on + integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and + {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets. </p> {#see_also|Integer Overflow|Division by Zero|Wrapping Operations#} {#header_close#} @@ -668,15 +696,15 @@ fn divide(a: i32, b: i32) i32 { {#header_open|Floats#} <p>Zig has the following floating point types:</p> <ul> - <li><code>f16</code> - IEEE-754-2008 binary16</li> - <li><code>f32</code> - IEEE-754-2008 binary32</li> - <li><code>f64</code> - IEEE-754-2008 binary64</li> - <li><code>f128</code> - IEEE-754-2008 binary128</li> - <li><code>c_longdouble</code> - matches <code>long double</code> for the target C ABI</li> + <li>{#syntax#}f16{#endsyntax#} - IEEE-754-2008 binary16</li> + <li>{#syntax#}f32{#endsyntax#} - IEEE-754-2008 binary32</li> + <li>{#syntax#}f64{#endsyntax#} - IEEE-754-2008 binary64</li> + <li>{#syntax#}f128{#endsyntax#} - IEEE-754-2008 binary128</li> + <li>{#syntax#}c_longdouble{#endsyntax#} - matches <code class="c">long double</code> for the target C ABI</li> </ul> {#header_open|Float Literals#} <p> - Float literals have type <code>comptime_float</code> which is guaranteed to hold at least all possible values + Float literals have type {#syntax#}comptime_float{#endsyntax#} which is guaranteed to hold at least all possible values that the largest other floating point type can hold. Float literals {#link|implicitly cast|Implicit Casts#} to any other type. </p> {#code_begin|syntax#} @@ -690,8 +718,8 @@ const yet_another_hex_float = 0x103.70P-5; {#code_end#} {#header_close#} {#header_open|Floating Point Operations#} - <p>By default floating point operations use <code>Strict</code> mode, - but you can switch to <code>Optimized</code> mode on a per-block basis:</p> + <p>By default floating point operations use {#syntax#}Strict{#endsyntax#} mode, + but you can switch to {#syntax#}Optimized{#endsyntax#} mode on a per-block basis:</p> {#code_begin|obj|foo#} {#code_release_fast#} const builtin = @import("builtin"); @@ -702,7 +730,7 @@ export fn foo_strict(x: f64) f64 { } export fn foo_optimized(x: f64) f64 { - @setFloatMode(this, builtin.FloatMode.Optimized); + @setFloatMode(builtin.FloatMode.Optimized); return x + big - big; } {#code_end#} @@ -744,8 +772,8 @@ pub fn main() void { </th> </tr> <tr> - <td><pre><code class="zig">a + b -a += b</code></pre></td> + <td><pre>{#syntax#}a + b +a += b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -760,12 +788,12 @@ a += b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">2 + 5 == 7</code></pre> + <pre>{#syntax#}2 + 5 == 7{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a +% b -a +%= b</code></pre></td> + <td><pre>{#syntax#}a +% b +a +%= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -779,12 +807,12 @@ a +%= b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">u32(@maxValue(u32)) +% 1 == 0</code></pre> + <pre>{#syntax#}u32(@maxValue(u32)) +% 1 == 0{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a - b -a -= b</code></pre></td> + <td><pre>{#syntax#}a - b +a -= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -799,12 +827,12 @@ a -= b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">2 - 5 == -3</code></pre> + <pre>{#syntax#}2 - 5 == -3{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a -% b -a -%= b</code></pre></td> + <td><pre>{#syntax#}a -% b +a -%= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -818,11 +846,11 @@ a -%= b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">u32(0) -% 1 == @maxValue(u32)</code></pre> + <pre>{#syntax#}u32(0) -% 1 == @maxValue(u32){#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">-a<code></pre></td> + <td><pre>{#syntax#}-a{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -836,11 +864,11 @@ a -%= b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">-1 == 0 - 1</code></pre> + <pre>{#syntax#}-1 == 0 - 1{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">-%a<code></pre></td> + <td><pre>{#syntax#}-%a{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -853,12 +881,12 @@ a -%= b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">-%i32(@minValue(i32)) == @minValue(i32)</code></pre> + <pre>{#syntax#}-%i32(@minValue(i32)) == @minValue(i32){#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a * b -a *= b</code></pre></td> + <td><pre>{#syntax#}a * b +a *= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -873,12 +901,12 @@ a *= b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">2 * 5 == 10</code></pre> + <pre>{#syntax#}2 * 5 == 10{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a *% b -a *%= b</code></pre></td> + <td><pre>{#syntax#}a *% b +a *%= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -892,12 +920,12 @@ a *%= b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">u8(200) *% 2 == 144</code></pre> + <pre>{#syntax#}u8(200) *% 2 == 144{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a / b -a /= b</code></pre></td> + <td><pre>{#syntax#}a / b +a /= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -912,18 +940,18 @@ a /= b</code></pre></td> <li>For non-compile-time-known signed integers, must use {#link|@divTrunc#}, {#link|@divFloor#}, or - {#link|@divExact#} instead of <code>/</code>. + {#link|@divExact#} instead of {#syntax#}/{#endsyntax#}. </li> <li>Invokes {#link|Peer Type Resolution#} for the operands.</li> </ul> </td> <td> - <pre><code class="zig">10 / 5 == 2</code></pre> + <pre>{#syntax#}10 / 5 == 2{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a % b -a %= b</code></pre></td> + <td><pre>{#syntax#}a % b +a %= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -936,18 +964,18 @@ a %= b</code></pre></td> <li>Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.Optimized Mode|Floating Point Operations#}.</li> <li>For non-compile-time-known signed integers, must use {#link|@rem#} or - {#link|@mod#} instead of <code>%</code>. + {#link|@mod#} instead of {#syntax#}%{#endsyntax#}. </li> <li>Invokes {#link|Peer Type Resolution#} for the operands.</li> </ul> </td> <td> - <pre><code class="zig">10 % 3 == 1</code></pre> + <pre>{#syntax#}10 % 3 == 1{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a &lt;&lt; b -a &lt;&lt;= b</code></pre></td> + <td><pre>{#syntax#}a << b +a <<= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -955,18 +983,18 @@ a &lt;&lt;= b</code></pre></td> </td> <td>Bit Shift Left. <ul> - <li><code>b</code> must be {#link|comptime-known|comptime#} or have a type with log2 number of bits as <code>a</code>.</li> + <li>{#syntax#}b{#endsyntax#} must be {#link|comptime-known|comptime#} or have a type with log2 number of bits as {#syntax#}a{#endsyntax#}.</li> <li>See also {#link|@shlExact#}.</li> <li>See also {#link|@shlWithOverflow#}.</li> </ul> </td> <td> - <pre><code class="zig">1 &lt;&lt; 8 == 256</code></pre> + <pre>{#syntax#}1 << 8 == 256{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a &gt;&gt; b -a &gt;&gt;= b</code></pre></td> + <td><pre>{#syntax#}a >> b +a >>= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -974,17 +1002,17 @@ a &gt;&gt;= b</code></pre></td> </td> <td>Bit Shift Right. <ul> - <li><code>b</code> must be {#link|comptime-known|comptime#} or have a type with log2 number of bits as <code>a</code>.</li> + <li>{#syntax#}b{#endsyntax#} must be {#link|comptime-known|comptime#} or have a type with log2 number of bits as {#syntax#}a{#endsyntax#}.</li> <li>See also {#link|@shrExact#}.</li> </ul> </td> <td> - <pre><code class="zig">10 &gt;&gt; 1 == 5</code></pre> + <pre>{#syntax#}10 >> 1 == 5{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a &amp; b -a &amp;= b</code></pre></td> + <td><pre>{#syntax#}a & b +a &= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -996,12 +1024,12 @@ a &amp;= b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">0b011 &amp; 0b101 == 0b001</code></pre> + <pre>{#syntax#}0b011 &amp; 0b101 == 0b001{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a | b -a |= b</code></pre></td> + <td><pre>{#syntax#}a | b +a |= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -1013,12 +1041,12 @@ a |= b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">0b010 | 0b100 == 0b110</code></pre> + <pre>{#syntax#}0b010 | 0b100 == 0b110{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a ^ b -a ^= b</code></pre></td> + <td><pre>{#syntax#}a ^ b +a ^= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -1030,11 +1058,11 @@ a ^= b</code></pre></td> </ul> </td> <td> - <pre><code class="zig">0b011 ^ 0b101 == 0b110</code></pre> + <pre>{#syntax#}0b011 ^ 0b101 == 0b110{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">~a<code></pre></td> + <td><pre>{#syntax#}~a{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -1044,29 +1072,29 @@ a ^= b</code></pre></td> Bitwise NOT. </td> <td> - <pre><code class="zig">~u8(0b0101111) == 0b1010000</code></pre> + <pre>{#syntax#}~u8(0b0101111) == 0b1010000{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a orelse b</code></pre></td> + <td><pre>{#syntax#}a orelse b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Optionals#}</li> </ul> </td> - <td>If <code>a</code> is <code>null</code>, - returns <code>b</code> ("default value"), - otherwise returns the unwrapped value of <code>a</code>. - Note that <code>b</code> may be a value of type {#link|noreturn#}. + <td>If {#syntax#}a{#endsyntax#} is {#syntax#}null{#endsyntax#}, + returns {#syntax#}b{#endsyntax#} ("default value"), + otherwise returns the unwrapped value of {#syntax#}a{#endsyntax#}. + Note that {#syntax#}b{#endsyntax#} may be a value of type {#link|noreturn#}. </td> <td> - <pre><code class="zig">const value: ?u32 = null; + <pre>{#syntax#}const value: ?u32 = null; const unwrapped = value orelse 1234; -unwrapped == 1234</code></pre> +unwrapped == 1234{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a.?</code></pre></td> + <td><pre>{#syntax#}a.?{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Optionals#}</li> @@ -1074,65 +1102,65 @@ unwrapped == 1234</code></pre> </td> <td> Equivalent to: - <pre><code class="zig">a orelse unreachable</code></pre> + <pre>{#syntax#}a orelse unreachable{#endsyntax#}</pre> </td> <td> - <pre><code class="zig">const value: ?u32 = 5678; -value.? == 5678</code></pre> + <pre>{#syntax#}const value: ?u32 = 5678; +value.? == 5678{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a catch b -a catch |err| b</code></pre></td> + <td><pre>{#syntax#}a catch b +a catch |err| b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Error Unions|Errors#}</li> </ul> </td> - <td>If <code>a</code> is an <code>error</code>, - returns <code>b</code> ("default value"), - otherwise returns the unwrapped value of <code>a</code>. - Note that <code>b</code> may be a value of type {#link|noreturn#}. - <code>err</code> is the <code>error</code> and is in scope of the expression <code>b</code>. + <td>If {#syntax#}a{#endsyntax#} is an {#syntax#}error{#endsyntax#}, + returns {#syntax#}b{#endsyntax#} ("default value"), + otherwise returns the unwrapped value of {#syntax#}a{#endsyntax#}. + Note that {#syntax#}b{#endsyntax#} may be a value of type {#link|noreturn#}. + {#syntax#}err{#endsyntax#} is the {#syntax#}error{#endsyntax#} and is in scope of the expression {#syntax#}b{#endsyntax#}. </td> <td> - <pre><code class="zig">const value: error!u32 = error.Broken; + <pre>{#syntax#}const value: error!u32 = error.Broken; const unwrapped = value catch 1234; -unwrapped == 1234</code></pre> +unwrapped == 1234{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a and b<code></pre></td> + <td><pre>{#syntax#}a and b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|bool|Primitive Types#}</li> </ul> </td> <td> - If <code>a</code> is <code>false</code>, returns <code>false</code> - without evaluating <code>b</code>. Otherwise, returns <code>b</code>. + If {#syntax#}a{#endsyntax#} is {#syntax#}false{#endsyntax#}, returns {#syntax#}false{#endsyntax#} + without evaluating {#syntax#}b{#endsyntax#}. Otherwise, returns {#syntax#}b{#endsyntax#}. </td> <td> - <pre><code class="zig">false and true == false</code></pre> + <pre>{#syntax#}false and true == false{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a or b<code></pre></td> + <td><pre>{#syntax#}a or b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|bool|Primitive Types#}</li> </ul> </td> <td> - If <code>a</code> is <code>true</code>, returns <code>true</code> - without evaluating <code>b</code>. Otherwise, returns <code>b</code>. + If {#syntax#}a{#endsyntax#} is {#syntax#}true{#endsyntax#}, returns {#syntax#}true{#endsyntax#} + without evaluating {#syntax#}b{#endsyntax#}. Otherwise, returns {#syntax#}b{#endsyntax#}. </td> <td> - <pre><code class="zig">false or true == true</code></pre> + <pre>{#syntax#}false or true == true{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">!a<code></pre></td> + <td><pre>{#syntax#}!a{#endsyntax#}</pre></td> <td> <ul> <li>{#link|bool|Primitive Types#}</li> @@ -1142,11 +1170,11 @@ unwrapped == 1234</code></pre> Boolean NOT. </td> <td> - <pre><code class="zig">!false == true</code></pre> + <pre>{#syntax#}!false == true{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a == b<code></pre></td> + <td><pre>{#syntax#}a == b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -1156,30 +1184,30 @@ unwrapped == 1234</code></pre> </ul> </td> <td> - Returns <code>true</code> if a and b are equal, otherwise returns <code>false</code>. + Returns {#syntax#}true{#endsyntax#} if a and b are equal, otherwise returns {#syntax#}false{#endsyntax#}. Invokes {#link|Peer Type Resolution#} for the operands. </td> <td> - <pre><code class="zig">(1 == 1) == true</code></pre> + <pre>{#syntax#}(1 == 1) == true{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a == null<code></pre></td> + <td><pre>{#syntax#}a == null{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Optionals#}</li> </ul> </td> <td> - Returns <code>true</code> if a is <code>null</code>, otherwise returns <code>false</code>. + Returns {#syntax#}true{#endsyntax#} if a is {#syntax#}null{#endsyntax#}, otherwise returns {#syntax#}false{#endsyntax#}. </td> <td> - <pre><code class="zig">const value: ?u32 = null; -value == null</code></pre> + <pre>{#syntax#}const value: ?u32 = null; +value == null{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a != b<code></pre></td> + <td><pre>{#syntax#}a != b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -1189,15 +1217,15 @@ value == null</code></pre> </ul> </td> <td> - Returns <code>false</code> if a and b are equal, otherwise returns <code>true</code>. + Returns {#syntax#}false{#endsyntax#} if a and b are equal, otherwise returns {#syntax#}true{#endsyntax#}. Invokes {#link|Peer Type Resolution#} for the operands. </td> <td> - <pre><code class="zig">(1 != 1) == false</code></pre> + <pre>{#syntax#}(1 != 1) == false{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a &gt; b<code></pre></td> + <td><pre>{#syntax#}a > b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -1205,15 +1233,15 @@ value == null</code></pre> </ul> </td> <td> - Returns <code>true</code> if a is greater than b, otherwise returns <code>false</code>. + Returns {#syntax#}true{#endsyntax#} if a is greater than b, otherwise returns {#syntax#}false{#endsyntax#}. Invokes {#link|Peer Type Resolution#} for the operands. </td> <td> - <pre><code class="zig">(2 &gt; 1) == true</code></pre> + <pre>{#syntax#}(2 > 1) == true{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a &gt;= b<code></pre></td> + <td><pre>{#syntax#}a >= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -1221,15 +1249,15 @@ value == null</code></pre> </ul> </td> <td> - Returns <code>true</code> if a is greater than or equal to b, otherwise returns <code>false</code>. + Returns {#syntax#}true{#endsyntax#} if a is greater than or equal to b, otherwise returns {#syntax#}false{#endsyntax#}. Invokes {#link|Peer Type Resolution#} for the operands. </td> <td> - <pre><code class="zig">(2 &gt;= 1) == true</code></pre> + <pre>{#syntax#}(2 >= 1) == true{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a &lt; b<code></pre></td> + <td><pre>{#syntax#}a < b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -1237,15 +1265,15 @@ value == null</code></pre> </ul> </td> <td> - Returns <code>true</code> if a is less than b, otherwise returns <code>false</code>. + Returns {#syntax#}true{#endsyntax#} if a is less than b, otherwise returns {#syntax#}false{#endsyntax#}. Invokes {#link|Peer Type Resolution#} for the operands. </td> <td> - <pre><code class="zig">(1 &lt; 2) == true</code></pre> + <pre>{#syntax#}(1 < 2) == true{#endsyntax#}></pre> </td> </tr> <tr> - <td><pre><code class="zig">a &lt;= b<code></pre></td> + <td><pre>{#syntax#}a <= b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Integers#}</li> @@ -1253,15 +1281,15 @@ value == null</code></pre> </ul> </td> <td> - Returns <code>true</code> if a is less than or equal to b, otherwise returns <code>false</code>. + Returns {#syntax#}true{#endsyntax#} if a is less than or equal to b, otherwise returns {#syntax#}false{#endsyntax#}. Invokes {#link|Peer Type Resolution#} for the operands. </td> <td> - <pre><code class="zig">(1 &lt;= 2) == true</code></pre> + <pre>{#syntax#}(1 <= 2) == true{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a ++ b<code></pre></td> + <td><pre>{#syntax#}a ++ b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Arrays#}</li> @@ -1270,19 +1298,19 @@ value == null</code></pre> <td> Array concatenation. <ul> - <li>Only available when <code>a</code> and <code>b</code> are {#link|compile-time known|comptime#}. + <li>Only available when {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are {#link|compile-time known|comptime#}. </ul> </td> <td> - <pre><code class="zig">const mem = @import("std").mem; + <pre>{#syntax#}const mem = @import("std").mem; const array1 = []u32{1,2}; const array2 = []u32{3,4}; const together = array1 ++ array2; -mem.eql(u32, together, []u32{1,2,3,4})</code></pre> +mem.eql(u32, together, []u32{1,2,3,4}){#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a ** b<code></pre></td> + <td><pre>{#syntax#}a ** b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Arrays#}</li> @@ -1291,17 +1319,17 @@ mem.eql(u32, together, []u32{1,2,3,4})</code></pre> <td> Array multiplication. <ul> - <li>Only available when <code>a</code> and <code>b</code> are {#link|compile-time known|comptime#}. + <li>Only available when {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are {#link|compile-time known|comptime#}. </ul> </td> <td> - <pre><code class="zig">const mem = @import("std").mem; + <pre>{#syntax#}const mem = @import("std").mem; const pattern = "ab" ** 3; -mem.eql(u8, pattern, "ababab")</code></pre> +mem.eql(u8, pattern, "ababab"){#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a.*<code></pre></td> + <td><pre>{#syntax#}a.*{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Pointers#}</li> @@ -1311,13 +1339,13 @@ mem.eql(u8, pattern, "ababab")</code></pre> Pointer dereference. </td> <td> - <pre><code class="zig">const x: u32 = 1234; -const ptr = &amp;x; -x.* == 1234</code></pre> + <pre>{#syntax#}const x: u32 = 1234; +const ptr = &x; +x.* == 1234{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">&amp;a<code></pre></td> + <td><pre>{#syntax#}&amp;a{#endsyntax#}</pre></td> <td> All types </td> @@ -1325,13 +1353,13 @@ x.* == 1234</code></pre> Address of. </td> <td> - <pre><code class="zig">const x: u32 = 1234; -const ptr = &amp;x; -x.* == 1234</code></pre> + <pre>{#syntax#}const x: u32 = 1234; +const ptr = &x; +x.* == 1234{#endsyntax#}</pre> </td> </tr> <tr> - <td><pre><code class="zig">a || b<code></pre></td> + <td><pre>{#syntax#}a || b{#endsyntax#}</pre></td> <td> <ul> <li>{#link|Error Set Type#}</li> @@ -1341,30 +1369,30 @@ x.* == 1234</code></pre> {#link|Merging Error Sets#} </td> <td> - <pre><code class="zig">const A = error{One}; + <pre>{#syntax#}const A = error{One}; const B = error{Two}; -(A || B) == error{One, Two}</code></pre> +(A || B) == error{One, Two}{#endsyntax#}</pre> </td> </tr> </table> </div> {#header_close#} {#header_open|Precedence#} - <pre><code>x() x[] x.y + <pre>{#syntax#}x() x[] x.y a!b -!x -x -%x ~x &amp;x ?x +!x -x -%x ~x &x ?x x{} x.* x.? ! * / % ** *% || + - ++ +% -% -&lt;&lt; &gt;&gt; -&amp; +<< >> +& ^ | -== != &lt; &gt; &lt;= &gt;= +== != < > <= >= and or orelse catch -= *= /= %= += -= &lt;&lt;= &gt;&gt;= &amp;= ^= |=</code></pre> += *= /= %= += -= <<= >>= &= ^= |={#endsyntax#}</pre> {#header_close#} {#header_close#} {#header_open|Arrays#} @@ -1613,7 +1641,7 @@ test "pointer child type" { </p> <p> Alignment depends on the CPU architecture, but is always a power of two, and - less than <code>1 &lt;&lt; 29</code>. + less than {#syntax#}1 << 29{#endsyntax#}. </p> <p> In Zig, a pointer type has an alignment value. If the value is equal to the @@ -1633,8 +1661,8 @@ test "variable alignment" { } } {#code_end#} - <p>In the same way that a <code>*i32</code> can be {#link|implicitly cast|Implicit Casts#} to a - <code>*const i32</code>, a pointer with a larger alignment can be implicitly + <p>In the same way that a {#syntax#}*i32{#endsyntax#} can be {#link|implicitly cast|Implicit Casts#} to a + {#syntax#}*const i32{#endsyntax#}, a pointer with a larger alignment can be implicitly cast to a pointer with a smaller alignment, but not vice versa. </p> <p> @@ -1689,14 +1717,14 @@ fn foo(bytes: []u8) u32 { {#header_open|Type Based Alias Analysis#} <p>Zig uses Type Based Alias Analysis (also known as Strict Aliasing) to perform some optimizations. This means that pointers of different types must - not alias the same memory, with the exception of <code>u8</code>. Pointers to - <code>u8</code> can alias any memory. + not alias the same memory, with the exception of {#syntax#}u8{#endsyntax#}. Pointers to + {#syntax#}u8{#endsyntax#} can alias any memory. </p> <p>As an example, this code produces undefined behavior:</p> - <pre><code class="zig">@ptrCast(*u32, f32(12.34)).*</code></pre> + <pre>{#syntax#}@ptrCast(*u32, f32(12.34)).*{#endsyntax#}</pre> <p>Instead, use {#link|@bitCast#}: - <pre><code class="zig">@bitCast(u32, f32(12.34))</code></pre> - <p>As an added benefit, the <code>@bitCast</code> version works at compile-time.</p> + <pre>{#syntax#}@bitCast(u32, f32(12.34)){#endsyntax#}</pre> + <p>As an added benefit, the {#syntax#}@bitCast{#endsyntax#} version works at compile-time.</p> {#see_also|Slices|Memory#} {#header_close#} {#header_close#} @@ -1924,9 +1952,9 @@ test "linked list" { <ul> <li>If the struct is in the initialization expression of a variable, it gets named after that variable.</li> - <li>If the struct is in the <code>return</code> expression, it gets named after + <li>If the struct is in the {#syntax#}return{#endsyntax#} expression, it gets named after the function it is returning from, with the parameter values serialized.</li> - <li>Otherwise, the struct gets a same such as <code>(anonymous struct at file.zig:7:38)</code>.</li> + <li>Otherwise, the struct gets a same such as {#syntax#}(anonymous struct at file.zig:7:38){#endsyntax#}.</li> </ul> {#code_begin|exe|struct_name#} const std = @import("std"); @@ -2058,7 +2086,7 @@ const Foo = enum { A, B, C }; export fn entry(foo: Foo) void { } {#code_end#} <p> - For a C-ABI-compatible enum, use <code class="zig">extern enum</code>: + For a C-ABI-compatible enum, use {#syntax#}extern enum{#endsyntax#}: </p> {#code_begin|obj#} const Foo = extern enum { A, B, C }; @@ -2067,7 +2095,7 @@ export fn entry(foo: Foo) void { } {#header_close#} {#header_open|packed enum#} <p>By default, the size of enums is not guaranteed.</p> - <p><code>packed enum</code> causes the size of the enum to be the same as the size of the integer tag type + <p>{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the integer tag type of the enum:</p> {#code_begin|test#} const std = @import("std"); @@ -2218,7 +2246,7 @@ test "access variable after block scope" { x += 1; } {#code_end#} - <p>Blocks are expressions. When labeled, <code>break</code> can be used + <p>Blocks are expressions. When labeled, {#syntax#}break{#endsyntax#} can be used to return a value from the block: </p> {#code_begin|test#} @@ -2236,7 +2264,7 @@ test "labeled break from labeled block expression" { assert(y == 124); } {#code_end#} - <p>Here, <code>blk</code> can be any name.</p> + <p>Here, {#syntax#}blk{#endsyntax#} can be any name.</p> {#see_also|Labeled while|Labeled for#} {#header_close#} {#header_open|switch#} @@ -2352,7 +2380,7 @@ test "while basic" { } {#code_end#} <p> - Use <code>break</code> to exit a while loop early. + Use {#syntax#}break{#endsyntax#} to exit a while loop early. </p> {#code_begin|test|while#} const assert = @import("std").debug.assert; @@ -2368,7 +2396,7 @@ test "while break" { } {#code_end#} <p> - Use <code>continue</code> to jump back to the beginning of the loop. + Use {#syntax#}continue{#endsyntax#} to jump back to the beginning of the loop. </p> {#code_begin|test|while#} const assert = @import("std").debug.assert; @@ -2386,7 +2414,7 @@ test "while continue" { {#code_end#} <p> While loops support a continue expression which is executed when the loop - is continued. The <code>continue</code> keyword respects this expression. + is continued. The {#syntax#}continue{#endsyntax#} keyword respects this expression. </p> {#code_begin|test|while#} const assert = @import("std").debug.assert; @@ -2408,13 +2436,13 @@ test "while loop continue expression, more complicated" { {#code_end#} <p> While loops are expressions. The result of the expression is the - result of the <code>else</code> clause of a while loop, which is executed when + result of the {#syntax#}else{#endsyntax#} clause of a while loop, which is executed when the condition of the while loop is tested as false. </p> <p> - <code>break</code>, like <code>return</code>, accepts a value - parameter. This is the result of the <code>while</code> expression. - When you <code>break</code> from a while loop, the <code>else</code> branch is not + {#syntax#}break{#endsyntax#}, like {#syntax#}return{#endsyntax#}, accepts a value + parameter. This is the result of the {#syntax#}while{#endsyntax#} expression. + When you {#syntax#}break{#endsyntax#} from a while loop, the {#syntax#}else{#endsyntax#} branch is not evaluated. </p> {#code_begin|test|while#} @@ -2435,8 +2463,8 @@ fn rangeHasNumber(begin: usize, end: usize, number: usize) bool { } {#code_end#} {#header_open|Labeled while#} - <p>When a <code>while</code> loop is labeled, it can be referenced from a <code>break</code> - or <code>continue</code> from within a nested loop:</p> + <p>When a {#syntax#}while{#endsyntax#} loop is labeled, it can be referenced from a {#syntax#}break{#endsyntax#} + or {#syntax#}continue{#endsyntax#} from within a nested loop:</p> {#code_begin|test#} test "nested break" { outer: while (true) { @@ -2463,11 +2491,11 @@ test "nested continue" { exits. </p> <p> - When the <code>|x|</code> syntax is present on a <code>while</code> expression, + When the {#syntax#}|x|{#endsyntax#} syntax is present on a {#syntax#}while{#endsyntax#} expression, the while condition must have an {#link|Optional Type#}. </p> <p> - The <code>else</code> branch is allowed on optional iteration. In this case, it will + The {#syntax#}else{#endsyntax#} branch is allowed on optional iteration. In this case, it will be executed on the first null value encountered. </p> {#code_begin|test|while#} @@ -2509,7 +2537,7 @@ fn eventuallyNullSequence() ?u32 { the loop is finished. </p> <p> - When the <code>else |x|</code> syntax is present on a <code>while</code> expression, + When the {#syntax#}else |x|{#endsyntax#} syntax is present on a {#syntax#}while{#endsyntax#} expression, the while condition must have an {#link|Error Union Type#}. </p> {#code_begin|test|while#} @@ -2565,7 +2593,7 @@ fn typeNameLength(comptime T: type) usize { } {#code_end#} <p> - It is recommended to use <code>inline</code> loops only for one of these reasons: + It is recommended to use {#syntax#}inline{#endsyntax#} loops only for one of these reasons: </p> <ul> <li>You need the loop to execute at {#link|comptime#} for the semantics to work.</li> @@ -2643,8 +2671,8 @@ test "for else" { } {#code_end#} {#header_open|Labeled for#} - <p>When a <code>for</code> loop is labeled, it can be referenced from a <code>break</code> - or <code>continue</code> from within a nested loop:</p> + <p>When a {#syntax#}for{#endsyntax#} loop is labeled, it can be referenced from a {#syntax#}break{#endsyntax#} + or {#syntax#}continue{#endsyntax#} from within a nested loop:</p> {#code_begin|test#} const std = @import("std"); const assert = std.debug.assert; @@ -2704,7 +2732,7 @@ fn typeNameLength(comptime T: type) usize { } {#code_end#} <p> - It is recommended to use <code>inline</code> loops only for one of these reasons: + It is recommended to use {#syntax#}inline{#endsyntax#} loops only for one of these reasons: </p> <ul> <li>You need the loop to execute at {#link|comptime#} for the semantics to work.</li> @@ -2904,13 +2932,13 @@ test "errdefer unwinding" { {#header_close#} {#header_open|unreachable#} <p> - In <code>Debug</code> and <code>ReleaseSafe</code> mode, and when using <code>zig test</code>, - <code>unreachable</code> emits a call to <code>panic</code> with the message <code>reached unreachable code</code>. + In {#syntax#}Debug{#endsyntax#} and {#syntax#}ReleaseSafe{#endsyntax#} mode, and when using <code>zig test</code>, + {#syntax#}unreachable{#endsyntax#} emits a call to {#syntax#}panic{#endsyntax#} with the message <code>reached unreachable code</code>. </p> <p> - In <code>ReleaseFast</code> mode, the optimizer uses the assumption that <code>unreachable</code> code - will never be hit to perform optimizations. However, <code>zig test</code> even in <code>ReleaseFast</code> mode - still emits <code>unreachable</code> as calls to <code>panic</code>. + In {#syntax#}ReleaseFast{#endsyntax#} mode, the optimizer uses the assumption that {#syntax#}unreachable{#endsyntax#} code + will never be hit to perform optimizations. However, <code>zig test</code> even in {#syntax#}ReleaseFast{#endsyntax#} mode + still emits {#syntax#}unreachable{#endsyntax#} as calls to {#syntax#}panic{#endsyntax#}. </p> {#header_open|Basics#} {#code_begin|test#} @@ -2956,17 +2984,17 @@ test "type of unreachable" { {#header_close#} {#header_open|noreturn#} <p> - <code>noreturn</code> is the type of: + {#syntax#}noreturn{#endsyntax#} is the type of: </p> <ul> - <li><code>break</code></li> - <li><code>continue</code></li> - <li><code>return</code></li> - <li><code>unreachable</code></li> - <li><code>while (true) {}</code></li> + <li>{#syntax#}break{#endsyntax#}</li> + <li>{#syntax#}continue{#endsyntax#}</li> + <li>{#syntax#}return{#endsyntax#}</li> + <li>{#syntax#}unreachable{#endsyntax#}</li> + <li>{#syntax#}while (true) {}{#endsyntax#}</li> </ul> - <p>When resolving types together, such as <code>if</code> clauses or <code>switch</code> prongs, - the <code>noreturn</code> type is compatible with every other type. Consider: + <p>When resolving types together, such as {#syntax#}if{#endsyntax#} clauses or {#syntax#}switch{#endsyntax#} prongs, + the {#syntax#}noreturn{#endsyntax#} type is compatible with every other type. Consider: </p> {#code_begin|test#} fn foo(condition: bool, b: u32) void { @@ -2977,7 +3005,7 @@ test "noreturn" { foo(false, 1); } {#code_end#} - <p>Another use case for <code>noreturn</code> is the <code>exit</code> function:</p> + <p>Another use case for {#syntax#}noreturn{#endsyntax#} is the {#syntax#}exit{#endsyntax#} function:</p> {#code_begin|test#} {#target_windows#} pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: c_uint) noreturn; @@ -3106,7 +3134,7 @@ test "fn reflection" { </p> <p> The number of unique error values across the entire compilation should determine the size of the error set type. - However right now it is hard coded to be a <code>u16</code>. See <a href="https://github.com/ziglang/zig/issues/786">#768</a>. + However right now it is hard coded to be a {#syntax#}u16{#endsyntax#}. See <a href="https://github.com/ziglang/zig/issues/786">#768</a>. </p> <p> You can {#link|implicitly cast|Implicit Casts#} an error from a subset to its superset: @@ -3169,7 +3197,7 @@ const err = (error {FileNotFound}).FileNotFound; This becomes useful when using {#link|Inferred Error Sets#}. </p> {#header_open|The Global Error Set#} - <p><code>error</code> refers to the global error set. + <p>{#syntax#}error{#endsyntax#} refers to the global error set. This is the error set that contains all errors in the entire compilation unit. It is a superset of all other error sets and a subset of none of them. </p> @@ -3188,7 +3216,7 @@ const err = (error {FileNotFound}).FileNotFound; {#header_close#} {#header_open|Error Union Type#} <p> - An error set type and normal type can be combined with the <code>!</code> + An error set type and normal type can be combined with the {#syntax#}!{#endsyntax#} binary operator to form an error union type. You are likely to use an error union type more often than an error set type by itself. </p> @@ -3235,14 +3263,14 @@ test "parse u64" { } {#code_end#} <p> - Notice the return type is <code>!u64</code>. This means that the function + Notice the return type is {#syntax#}!u64{#endsyntax#}. This means that the function either returns an unsigned 64 bit integer, or an error. We left off the error set - to the left of the <code>!</code>, so the error set is inferred. + to the left of the {#syntax#}!{#endsyntax#}, so the error set is inferred. </p> <p> Within the function definition, you can see some return statements that return - an error, and at the bottom a return statement that returns a <code>u64</code>. - Both types {#link|implicitly cast|Implicit Casts#} to <code>error!u64</code>. + an error, and at the bottom a return statement that returns a {#syntax#}u64{#endsyntax#}. + Both types {#link|implicitly cast|Implicit Casts#} to {#syntax#}error!u64{#endsyntax#}. </p> <p> What it looks like to use this function varies depending on what you're @@ -3255,7 +3283,7 @@ test "parse u64" { <li>You want to take a different action for each possible error.</li> </ul> {#header_open|catch#} - <p>If you want to provide a default value, you can use the <code>catch</code> binary operator:</p> + <p>If you want to provide a default value, you can use the {#syntax#}catch{#endsyntax#} binary operator:</p> {#code_begin|syntax#} fn doAThing(str: []u8) void { const number = parseU64(str, 10) catch 13; @@ -3263,9 +3291,9 @@ fn doAThing(str: []u8) void { } {#code_end#} <p> - In this code, <code>number</code> will be equal to the successfully parsed string, or - a default value of 13. The type of the right hand side of the binary <code>catch</code> operator must - match the unwrapped error union type, or be of type <code>noreturn</code>. + In this code, {#syntax#}number{#endsyntax#} will be equal to the successfully parsed string, or + a default value of 13. The type of the right hand side of the binary {#syntax#}catch{#endsyntax#} operator must + match the unwrapped error union type, or be of type {#syntax#}noreturn{#endsyntax#}. </p> {#header_close#} {#header_open|try#} @@ -3278,7 +3306,7 @@ fn doAThing(str: []u8) !void { } {#code_end#} <p> - There is a shortcut for this. The <code>try</code> expression: + There is a shortcut for this. The {#syntax#}try{#endsyntax#} expression: </p> {#code_begin|syntax#} fn doAThing(str: []u8) !void { @@ -3287,7 +3315,7 @@ fn doAThing(str: []u8) !void { } {#code_end#} <p> - <code>try</code> evaluates an error union expression. If it is an error, it returns + {#syntax#}try{#endsyntax#} evaluates an error union expression. If it is an error, it returns from the current function with the same error. Otherwise, the expression results in the unwrapped value. </p> @@ -3299,7 +3327,7 @@ fn doAThing(str: []u8) !void { {#code_begin|syntax#}const number = parseU64("1234", 10) catch unreachable;{#code_end#} <p> Here we know for sure that "1234" will parse successfully. So we put the - <code>unreachable</code> value on the right hand side. <code>unreachable</code> generates + {#syntax#}unreachable{#endsyntax#} value on the right hand side. {#syntax#}unreachable{#endsyntax#} generates a panic in Debug and ReleaseSafe modes and undefined behavior in ReleaseFast mode. So, while we're debugging the application, if there <em>was</em> a surprise error here, the application would crash appropriately. @@ -3324,7 +3352,7 @@ fn doAThing(str: []u8) void { {#header_open|errdefer#} <p> The other component to error handling is defer statements. - In addition to an unconditional {#link|defer#}, Zig has <code>errdefer</code>, + In addition to an unconditional {#link|defer#}, Zig has {#syntax#}errdefer{#endsyntax#}, which evaluates the deferred expression on block exit path if and only if the function returned with an error from the block. </p> @@ -3362,7 +3390,7 @@ fn createFoo(param: i32) !Foo { <ul> <li>These primitives give enough expressiveness that it's completely practical to have failing to check for an error be a compile error. If you really want - to ignore the error, you can add <code>catch unreachable</code> and + to ignore the error, you can add {#syntax#}catch unreachable{#endsyntax#} and get the added benefit of crashing in Debug and ReleaseSafe modes if your assumption was wrong. </li> <li> @@ -3373,7 +3401,7 @@ fn createFoo(param: i32) !Foo { </ul> {#see_also|defer|if|switch#} - <p>An error union is created with the <code>!</code> binary operator. + <p>An error union is created with the {#syntax#}!{#endsyntax#} binary operator. You can use compile-time reflection to access the child type of an error union:</p> {#code_begin|test#} const assert = @import("std").debug.assert; @@ -3396,15 +3424,15 @@ test "error union" { {#code_end#} {#header_open|Merging Error Sets#} <p> - Use the <code>||</code> operator to merge two error sets together. The resulting + Use the {#syntax#}||{#endsyntax#} operator to merge two error sets together. The resulting error set contains the errors of both error sets. Doc comments from the left-hand side override doc comments from the right-hand side. In this example, the doc - comments for <code>C.PathNotFound</code> is <code>A doc comment</code>. + comments for {#syntax#}C.PathNotFound{#endsyntax#} is <code>A doc comment</code>. </p> <p> This is especially useful for functions which return different error sets depending on {#link|comptime#} branches. For example, the Zig standard library uses - <code>LinuxFileOpenError || WindowsFileOpenError</code> for the error set of opening + {#syntax#}LinuxFileOpenError || WindowsFileOpenError{#endsyntax#} for the error set of opening files. </p> {#code_begin|test#} @@ -3537,8 +3565,8 @@ fn bang2() !void { Look closely at this example. This is no stack trace. </p> <p> - You can see that the final error bubbled up was <code>PermissionDenied</code>, - but the original error that started this whole thing was <code>FileNotFound</code>. In the <code>bar</code> function, the code handles the original error code, + You can see that the final error bubbled up was {#syntax#}PermissionDenied{#endsyntax#}, + but the original error that started this whole thing was {#syntax#}FileNotFound{#endsyntax#}. In the {#syntax#}bar{#endsyntax#} function, the code handles the original error code, and then returns another one, from the switch statement. Error Return Traces make this clear, whereas a stack trace would look like this: </p> {#code_begin|exe_err#} @@ -3584,7 +3612,7 @@ fn bang2() void { {#code_end#} <p> Here, the stack trace does not explain how the control - flow in <code>bar</code> got to the <code>hello()</code> call. + flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call. One would have to open a debugger or further instrument the application in order to find out. The error return trace, on the other hand, shows exactly how the error bubbled up. @@ -3603,8 +3631,8 @@ fn bang2() void { </p> <ul> <li>Return an error from main</li> - <li>An error makes its way to <code>catch unreachable</code> and you have not overridden the default panic handler</li> - <li>Use {#link|errorReturnTrace#} to access the current return trace. You can use <code>std.debug.dumpStackTrace</code> to print it. This function returns comptime-known {#link|null#} when building without error return tracing support.</li> + <li>An error makes its way to {#syntax#}catch unreachable{#endsyntax#} and you have not overridden the default panic handler</li> + <li>Use {#link|errorReturnTrace#} to access the current return trace. You can use {#syntax#}std.debug.dumpStackTrace{#endsyntax#} to print it. This function returns comptime-known {#link|null#} when building without error return tracing support.</li> </ul> {#header_open|Implementation Details#} <p> @@ -3615,7 +3643,7 @@ fn bang2() void { <li>when returning errors</li> </ul> <p> - For the case when no errors are returned, the cost is a single memory write operation, only in the first non-failable function in the call graph that calls a failable function, i.e. when a function returning <code>void</code> calls a function returning <code>error</code>. + For the case when no errors are returned, the cost is a single memory write operation, only in the first non-failable function in the call graph that calls a failable function, i.e. when a function returning {#syntax#}void{#endsyntax#} calls a function returning {#syntax#}error{#endsyntax#}. This is to initialize this struct in the stack memory: </p> {#code_begin|syntax#} @@ -3628,13 +3656,13 @@ pub const StackTrace = struct { Here, N is the maximum function call depth as determined by call graph analysis. Recursion is ignored and counts for 2. </p> <p> - A pointer to <code>StackTrace</code> is passed as a secret parameter to every function that can return an error, but it's always the first parameter, so it can likely sit in a register and stay there. + A pointer to {#syntax#}StackTrace{#endsyntax#} is passed as a secret parameter to every function that can return an error, but it's always the first parameter, so it can likely sit in a register and stay there. </p> <p> That's it for the path when no errors occur. It's practically free in terms of performance. </p> <p> - When generating the code for a function that returns an error, just before the <code>return</code> statement (only for the <code>return</code> statements that return errors), Zig generates a call to this function: + When generating the code for a function that returns an error, just before the {#syntax#}return{#endsyntax#} statement (only for the {#syntax#}return{#endsyntax#} statements that return errors), Zig generates a call to this function: </p> {#code_begin|syntax#} // marked as "no-inline" in LLVM IR @@ -3649,7 +3677,7 @@ fn __zig_return_error(stack_trace: *StackTrace) void { <p> As for code size cost, 1 function call before a return statement is no big deal. Even so, I have <a href="https://github.com/ziglang/zig/issues/690">a plan</a> to make the call to - <code>__zig_return_error</code> a tail call, which brings the code size cost down to actually zero. What is a return statement in code without error return tracing can become a jump instruction in code with error return tracing. + {#syntax#}__zig_return_error{#endsyntax#} a tail call, which brings the code size cost down to actually zero. What is a return statement in code without error return tracing can become a jump instruction in code with error return tracing. </p> {#header_close#} {#header_close#} @@ -3671,7 +3699,7 @@ const normal_int: i32 = 1234; const optional_int: ?i32 = 5678; {#code_end#} <p> - Now the variable <code>optional_int</code> could be an <code>i32</code>, or <code>null</code>. + Now the variable {#syntax#}optional_int{#endsyntax#} could be an {#syntax#}i32{#endsyntax#}, or {#syntax#}null{#endsyntax#}. </p> <p> Instead of integers, let's talk about pointers. Null references are the source of many runtime @@ -3712,8 +3740,8 @@ fn doAThing() ?*Foo { {#code_end#} <p> Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr" - is <code>*u8</code> <em>not</em> <code>?*u8</code>. The <code>orelse</code> keyword - unwrapped the optional type and therefore <code>ptr</code> is guaranteed to be non-null everywhere + is {#syntax#}*u8{#endsyntax#} <em>not</em> {#syntax#}?*u8{#endsyntax#}. The {#syntax#}orelse{#endsyntax#} keyword + unwrapped the optional type and therefore {#syntax#}ptr{#endsyntax#} is guaranteed to be non-null everywhere it is used in the function. </p> <p> @@ -3744,7 +3772,7 @@ fn doAThing(optional_foo: ?*Foo) void { {#code_end#} <p> Once again, the notable thing here is that inside the if block, - <code>foo</code> is no longer an optional pointer, it is a pointer, which + {#syntax#}foo{#endsyntax#} is no longer an optional pointer, it is a pointer, which cannot be null. </p> <p> @@ -3755,7 +3783,7 @@ fn doAThing(optional_foo: ?*Foo) void { cannot be null. </p> {#header_open|Optional Type#} - <p>An optional is created by putting <code>?</code> in front of a type. You can use compile-time + <p>An optional is created by putting {#syntax#}?{#endsyntax#} in front of a type. You can use compile-time reflection to access the child type of an optional:</p> {#code_begin|test#} const assert = @import("std").debug.assert; @@ -3774,7 +3802,7 @@ test "optional type" { {#header_close#} {#header_open|null#} <p> - Just like {#link|undefined#}, <code>null</code> has its own type, and the only way to use it is to + Just like {#link|undefined#}, {#syntax#}null{#endsyntax#} has its own type, and the only way to use it is to cast it to a different type: </p> {#code_begin|syntax#} @@ -3822,9 +3850,9 @@ test "implicit cast - invoke a type as a function" { of the qualifiers, no matter how nested the qualifiers are: </p> <ul> - <li><code>const</code> - non-const to const is allowed</li> - <li><code>volatile</code> - non-volatile to volatile is allowed</li> - <li><code>align</code> - bigger to smaller alignment is allowed </li> + <li>{#syntax#}const{#endsyntax#} - non-const to const is allowed</li> + <li>{#syntax#}volatile{#endsyntax#} - non-volatile to volatile is allowed</li> + <li>{#syntax#}align{#endsyntax#} - bigger to smaller alignment is allowed </li> <li>{#link|error sets|Error Set Type#} to supersets is allowed</li> </ul> <p> @@ -4072,7 +4100,7 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) error![]u8 { {#header_open|void#} <p> - <code>void</code> represents a type that has no value. Code that makes use of void values is + {#syntax#}void{#endsyntax#} represents a type that has no value. Code that makes use of void values is not included in the final generated code: </p> {#code_begin|syntax#} @@ -4082,7 +4110,7 @@ export fn entry() void { x = y; } {#code_end#} - <p>When this turns into LLVM IR, there is no code generated in the body of <code>entry</code>, + <p>When this turns into LLVM IR, there is no code generated in the body of {#syntax#}entry{#endsyntax#}, even in debug mode. For example, on x86_64:</p> <pre><code>0000000000000010 &lt;entry&gt;: 10: 55 push %rbp @@ -4092,9 +4120,9 @@ export fn entry() void { <p>These assembly instructions do not have any code associated with the void values - they only perform the function call prologue and epilog.</p> <p> - <code>void</code> can be useful for instantiating generic types. For example, given a - <code>Map(Key, Value)</code>, one can pass <code>void</code> for the <code>Value</code> - type to make it into a <code>Set</code>: + {#syntax#}void{#endsyntax#} can be useful for instantiating generic types. For example, given a + {#syntax#}Map(Key, Value){#endsyntax#}, one can pass {#syntax#}void{#endsyntax#} for the {#syntax#}Value{#endsyntax#} + type to make it into a {#syntax#}Set{#endsyntax#}: </p> {#code_begin|test#} const std = @import("std"); @@ -4123,17 +4151,17 @@ fn eql_i32(a: i32, b: i32) bool { } {#code_end#} <p>Note that this is different than using a dummy value for the hash map value. - By using <code>void</code> as the type of the value, the hash map entry type has no value field, and + By using {#syntax#}void{#endsyntax#} as the type of the value, the hash map entry type has no value field, and thus the hash map takes up less space. Further, all the code that deals with storing and loading the value is deleted, as seen above. </p> <p> - <code>void</code> is distinct from <code>c_void</code>, which is defined like this: - <code>pub const c_void = @OpaqueType();</code>. - <code>void</code> has a known size of 0 bytes, and <code>c_void</code> has an unknown, but non-zero, size. + {#syntax#}void{#endsyntax#} is distinct from {#syntax#}c_void{#endsyntax#}, which is defined like this: + {#syntax#}pub const c_void = @OpaqueType();{#endsyntax#}. + {#syntax#}void{#endsyntax#} has a known size of 0 bytes, and {#syntax#}c_void{#endsyntax#} has an unknown, but non-zero, size. </p> <p> - Expressions of type <code>void</code> are the only ones whose value can be ignored. For example: + Expressions of type {#syntax#}void{#endsyntax#} are the only ones whose value can be ignored. For example: </p> {#code_begin|test_err|expression value is ignored#} test "ignoring expression value" { @@ -4144,7 +4172,7 @@ fn foo() i32 { return 1234; } {#code_end#} - <p>However, if the expression has type <code>void</code>:</p> + <p>However, if the expression has type {#syntax#}void{#endsyntax#}:</p> {#code_begin|test#} test "ignoring expression value" { foo(); @@ -4154,11 +4182,6 @@ fn foo() void {} {#code_end#} {#header_close#} - {#header_open|this#} - <p>TODO: example of this referring to Self struct</p> - <p>TODO: example of this referring to recursion function</p> - <p>TODO: example of this referring to basic block for @setRuntimeSafety</p> - {#header_close#} {#header_open|comptime#} <p> Zig places importance on the concept of whether an expression is known at compile-time. @@ -4184,10 +4207,10 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 { <p> In Zig, types are first-class citizens. They can be assigned to variables, passed as parameters to functions, and returned from functions. However, they can only be used in expressions which are known at <em>compile-time</em>, - which is why the parameter <code>T</code> in the above snippet must be marked with <code>comptime</code>. + which is why the parameter {#syntax#}T{#endsyntax#} in the above snippet must be marked with {#syntax#}comptime{#endsyntax#}. </p> <p> - A <code>comptime</code> parameter means that: + A {#syntax#}comptime{#endsyntax#} parameter means that: </p> <ul> <li>At the callsite, the value must be known at compile-time, or it is a compile error.</li> @@ -4232,7 +4255,7 @@ test "try to compare bools" { } {#code_end#} <p> - On the flip side, inside the function definition with the <code>comptime</code> parameter, the + On the flip side, inside the function definition with the {#syntax#}comptime{#endsyntax#} parameter, the value is known at compile-time. This means that we actually could make this work for the bool type if we wanted to: </p> @@ -4251,12 +4274,12 @@ test "try to compare bools" { } {#code_end#} <p> - This works because Zig implicitly inlines <code>if</code> expressions when the condition + This works because Zig implicitly inlines {#syntax#}if{#endsyntax#} expressions when the condition is known at compile-time, and the compiler guarantees that it will skip analysis of the branch not taken. </p> <p> - This means that the actual function generated for <code>max</code> in this situation looks like + This means that the actual function generated for {#syntax#}max{#endsyntax#} in this situation looks like this: </p> {#code_begin|syntax#} @@ -4269,18 +4292,18 @@ fn max(a: bool, b: bool) bool { the necessary run-time code to accomplish the task. </p> <p> - This works the same way for <code>switch</code> expressions - they are implicitly inlined + This works the same way for {#syntax#}switch{#endsyntax#} expressions - they are implicitly inlined when the target expression is compile-time known. </p> {#header_close#} {#header_open|Compile-Time Variables#} <p> - In Zig, the programmer can label variables as <code>comptime</code>. This guarantees to the compiler + In Zig, the programmer can label variables as {#syntax#}comptime{#endsyntax#}. This guarantees to the compiler that every load and store of the variable is performed at compile-time. Any violation of this results in a compile error. </p> <p> - This combined with the fact that we can <code>inline</code> loops allows us to write + This combined with the fact that we can {#syntax#}inline{#endsyntax#} loops allows us to write a function which is partially evaluated at compile-time and partially at run-time. </p> <p> @@ -4323,8 +4346,8 @@ test "perform fn" { <p> This example is a bit contrived, because the compile-time evaluation component is unnecessary; this code would work fine if it was all done at run-time. But it does end up generating - different code. In this example, the function <code>performFn</code> is generated three different times, - for the different values of <code>prefix_char</code> provided: + different code. In this example, the function {#syntax#}performFn{#endsyntax#} is generated three different times, + for the different values of {#syntax#}prefix_char{#endsyntax#} provided: </p> {#code_begin|syntax#} // From the line: @@ -4365,7 +4388,7 @@ fn performFn(start_value: i32) i32 { {#header_open|Compile-Time Expressions#} <p> In Zig, it matters whether a given expression is known at compile-time or run-time. A programmer can - use a <code>comptime</code> expression to guarantee that the expression will be evaluated at compile-time. + use a {#syntax#}comptime{#endsyntax#} expression to guarantee that the expression will be evaluated at compile-time. If this cannot be accomplished, the compiler will emit an error. For example: </p> {#code_begin|test_err|unable to evaluate constant expression#} @@ -4378,16 +4401,16 @@ test "foo" { } {#code_end#} <p> - It doesn't make sense that a program could call <code>exit()</code> (or any other external function) - at compile-time, so this is a compile error. However, a <code>comptime</code> expression does much + It doesn't make sense that a program could call {#syntax#}exit(){#endsyntax#} (or any other external function) + at compile-time, so this is a compile error. However, a {#syntax#}comptime{#endsyntax#} expression does much more than sometimes cause a compile error. </p> <p> - Within a <code>comptime</code> expression: + Within a {#syntax#}comptime{#endsyntax#} expression: </p> <ul> - <li>All variables are <code>comptime</code> variables.</li> - <li>All <code>if</code>, <code>while</code>, <code>for</code>, and <code>switch</code> + <li>All variables are {#syntax#}comptime{#endsyntax#} variables.</li> + <li>All {#syntax#}if{#endsyntax#}, {#syntax#}while{#endsyntax#}, {#syntax#}for{#endsyntax#}, and {#syntax#}switch{#endsyntax#} expressions are evaluated at compile-time, or emit a compile error if this is not possible.</li> <li>All function calls cause the compiler to interpret the function at compile-time, emitting a compile error if the function tries to do something that has global run-time side effects.</li> @@ -4464,7 +4487,7 @@ test "fibonacci" { {#link|@setEvalBranchQuota#} to change the default number 1000 to something else. </p> <p> - What if we fix the base case, but put the wrong value in the <code>assert</code> line? + What if we fix the base case, but put the wrong value in the {#syntax#}assert{#endsyntax#} line? </p> {#code_begin|test_err|encountered @panic at compile-time#} const assert = @import("std").debug.assert; @@ -4481,16 +4504,16 @@ test "fibonacci" { } {#code_end#} <p> - What happened is Zig started interpreting the <code>assert</code> function with the - parameter <code>ok</code> set to <code>false</code>. When the interpreter hit - <code>unreachable</code> it emitted a compile error, because reaching unreachable + What happened is Zig started interpreting the {#syntax#}assert{#endsyntax#} function with the + parameter {#syntax#}ok{#endsyntax#} set to {#syntax#}false{#endsyntax#}. When the interpreter hit + {#syntax#}unreachable{#endsyntax#} it emitted a compile error, because reaching unreachable code is undefined behavior, and undefined behavior causes a compile error if it is detected at compile-time. </p> <p> In the global scope (outside of any function), all expressions are implicitly - <code>comptime</code> expressions. This means that we can use functions to + {#syntax#}comptime{#endsyntax#} expressions. This means that we can use functions to initialize complex static data. For example: </p> {#code_begin|test#} @@ -4538,7 +4561,7 @@ test "variable values" { @1 = internal unnamed_addr constant i32 1060</code></pre> <p> Note that we did not have to do anything special with the syntax of these functions. For example, - we could call the <code>sum</code> function as is with a slice of numbers whose length and values were + we could call the {#syntax#}sum{#endsyntax#} function as is with a slice of numbers whose length and values were only known at run-time. </p> {#header_close#} @@ -4550,8 +4573,8 @@ test "variable values" { generic data structure. </p> <p> - Here is an example of a generic <code>List</code> data structure, that we will instantiate with - the type <code>i32</code>. In Zig we refer to the type as <code>List(i32)</code>. + Here is an example of a generic {#syntax#}List{#endsyntax#} data structure, that we will instantiate with + the type {#syntax#}i32{#endsyntax#}. In Zig we refer to the type as {#syntax#}List(i32){#endsyntax#}. </p> {#code_begin|syntax#} fn List(comptime T: type) type { @@ -4562,8 +4585,8 @@ fn List(comptime T: type) type { } {#code_end#} <p> - That's it. It's a function that returns an anonymous <code>struct</code>. For the purposes of error messages - and debugging, Zig infers the name <code>"List(i32)"</code> from the function name and parameters invoked when creating + That's it. It's a function that returns an anonymous {#syntax#}struct{#endsyntax#}. For the purposes of error messages + and debugging, Zig infers the name {#syntax#}"List(i32)"{#endsyntax#} from the function name and parameters invoked when creating the anonymous struct. </p> <p> @@ -4579,13 +4602,13 @@ const Node = struct { <p> This works because all top level declarations are order-independent, and as long as there isn't an actual infinite regression, values can refer to themselves, directly or indirectly. In this case, - <code>Node</code> refers to itself as a pointer, which is not actually an infinite regression, so + {#syntax#}Node{#endsyntax#} refers to itself as a pointer, which is not actually an infinite regression, so it works fine. </p> {#header_close#} {#header_open|Case Study: printf in Zig#} <p> - Putting all of this together, let's see how <code>printf</code> works in Zig. + Putting all of this together, let's see how {#syntax#}printf{#endsyntax#} works in Zig. </p> {#code_begin|exe|printf#} const warn = @import("std").debug.warn; @@ -4686,7 +4709,7 @@ pub fn printf(self: *OutStream, arg0: i32, arg1: []const u8) !void { } {#code_end#} <p> - <code>printValue</code> is a function that takes a parameter of any type, and does different things depending + {#syntax#}printValue{#endsyntax#} is a function that takes a parameter of any type, and does different things depending on the type: </p> {#code_begin|syntax#} @@ -4702,7 +4725,7 @@ pub fn printValue(self: *OutStream, value: var) !void { } {#code_end#} <p> - And now, what happens if we give too many arguments to <code>printf</code>? + And now, what happens if we give too many arguments to {#syntax#}printf{#endsyntax#}? </p> {#code_begin|test_err|Unused arguments#} const warn = @import("std").debug.warn; @@ -4720,7 +4743,7 @@ test "printf too many arguments" { </p> <p> Zig doesn't care whether the format argument is a string literal, - only that it is a compile-time known value that is implicitly castable to a <code>[]const u8</code>: + only that it is a compile-time known value that is implicitly castable to a {#syntax#}[]const u8{#endsyntax#}: </p> {#code_begin|exe|printf#} const warn = @import("std").debug.warn; @@ -4774,16 +4797,16 @@ pub fn main() void { </p> {#header_open|Minimal Coroutine Example#} <p> - Declare a coroutine with the <code>async</code> keyword. + Declare a coroutine with the {#syntax#}async{#endsyntax#} keyword. The expression in angle brackets must evaluate to a struct which has these fields: </p> <ul> - <li><code>allocFn: fn (self: *Allocator, byte_count: usize, alignment: u29) Error![]u8</code> - where <code>Error</code> can be any error set.</li> - <li><code>freeFn: fn (self: *Allocator, old_mem: []u8) void</code></li> + <li>{#syntax#}allocFn: fn (self: *Allocator, byte_count: usize, alignment: u29) Error![]u8{#endsyntax#} - where {#syntax#}Error{#endsyntax#} can be any error set.</li> + <li>{#syntax#}freeFn: fn (self: *Allocator, old_mem: []u8) void{#endsyntax#}</li> </ul> <p> - You may notice that this corresponds to the <code>std.mem.Allocator</code> interface. + You may notice that this corresponds to the {#syntax#}std.mem.Allocator{#endsyntax#} interface. This makes it convenient to integrate with existing allocators. Note, however, that the language feature does not depend on the standard library, and any struct which has these fields is allowed. @@ -4793,13 +4816,13 @@ pub fn main() void { the function generic. Zig will infer the allocator type when the async function is called. </p> <p> - Call a coroutine with the <code>async</code> keyword. Here, the expression in angle brackets + Call a coroutine with the {#syntax#}async{#endsyntax#} keyword. Here, the expression in angle brackets is a pointer to the allocator struct that the coroutine expects. </p> <p> - The result of an async function call is a <code>promise->T</code> type, where <code>T</code> + The result of an async function call is a {#syntax#}promise->T{#endsyntax#} type, where {#syntax#}T{#endsyntax#} is the return type of the async function. Once a promise has been created, it must be - consumed, either with <code>cancel</code> or <code>await</code>: + consumed, either with {#syntax#}cancel{#endsyntax#} or {#syntax#}await{#endsyntax#}: </p> <p> Async functions start executing when created, so in the following example, the entire @@ -4888,18 +4911,18 @@ async fn testSuspendBlock() void { {#code_end#} <p> Every suspend point in an async function represents a point at which the coroutine - could be destroyed. If that happens, <code>defer</code> expressions that are in - scope are run, as well as <code>errdefer</code> expressions. + could be destroyed. If that happens, {#syntax#}defer{#endsyntax#} expressions that are in + scope are run, as well as {#syntax#}errdefer{#endsyntax#} expressions. </p> <p> {#link|Await#} counts as a suspend point. </p> {#header_open|Resuming from Suspend Blocks#} <p> - Upon entering a <code>suspend</code> block, the coroutine is already considered + Upon entering a {#syntax#}suspend{#endsyntax#} block, the coroutine is already considered suspended, and can be resumed. For example, if you started another kernel thread, - and had that thread call <code>resume</code> on the promise handle provided by the - <code>suspend</code> block, the new thread would begin executing after the suspend + and had that thread call {#syntax#}resume{#endsyntax#} on the promise handle provided by the + {#syntax#}suspend{#endsyntax#} block, the new thread would begin executing after the suspend block, while the old thread continued executing the suspend block. </p> <p> @@ -4934,26 +4957,26 @@ async fn testResumeFromSuspend(my_result: *i32) void { {#header_close#} {#header_open|Await#} <p> - The <code>await</code> keyword is used to coordinate with an async function's - <code>return</code> statement. + The {#syntax#}await{#endsyntax#} keyword is used to coordinate with an async function's + {#syntax#}return{#endsyntax#} statement. </p> <p> - <code>await</code> is valid only in an <code>async</code> function, and it takes + {#syntax#}await{#endsyntax#} is valid only in an {#syntax#}async{#endsyntax#} function, and it takes as an operand a promise handle. If the async function associated with the promise handle has already returned, - then <code>await</code> destroys the target async function, and gives the return value. - Otherwise, <code>await</code> suspends the current async function, registering its + then {#syntax#}await{#endsyntax#} destroys the target async function, and gives the return value. + Otherwise, {#syntax#}await{#endsyntax#} suspends the current async function, registering its promise handle with the target coroutine. It becomes the target coroutine's responsibility to have ensured that it will be resumed or destroyed. When the target coroutine reaches its return statement, it gives the return value to the awaiter, destroys itself, and then resumes the awaiter. </p> <p> - A promise handle must be consumed exactly once after it is created, either by <code>cancel</code> or <code>await</code>. + A promise handle must be consumed exactly once after it is created, either by {#syntax#}cancel{#endsyntax#} or {#syntax#}await{#endsyntax#}. </p> <p> - <code>await</code> counts as a suspend point, and therefore at every <code>await</code>, - a coroutine can be potentially destroyed, which would run <code>defer</code> and <code>errdefer</code> expressions. + {#syntax#}await{#endsyntax#} counts as a suspend point, and therefore at every {#syntax#}await{#endsyntax#}, + a coroutine can be potentially destroyed, which would run {#syntax#}defer{#endsyntax#} and {#syntax#}errdefer{#endsyntax#} expressions. </p> {#code_begin|test#} const std = @import("std"); @@ -4997,9 +5020,9 @@ fn seq(c: u8) void { } {#code_end#} <p> - In general, <code>suspend</code> is lower level than <code>await</code>. Most application - code will use only <code>async</code> and <code>await</code>, but event loop - implementations will make use of <code>suspend</code> internally. + In general, {#syntax#}suspend{#endsyntax#} is lower level than {#syntax#}await{#endsyntax#}. Most application + code will use only {#syntax#}async{#endsyntax#} and {#syntax#}await{#endsyntax#}, but event loop + implementations will make use of {#syntax#}suspend{#endsyntax#} internally. </p> {#header_close#} {#header_open|Open Issues#} @@ -5029,36 +5052,36 @@ fn seq(c: u8) void { {#header_open|Builtin Functions#} <p> Builtin functions are provided by the compiler and are prefixed with <code>@</code>. - The <code>comptime</code> keyword on a parameter means that the parameter must be known + The {#syntax#}comptime{#endsyntax#} keyword on a parameter means that the parameter must be known at compile time. </p> {#header_open|@addWithOverflow#} - <pre><code class="zig">@addWithOverflow(comptime T: type, a: T, b: T, result: *T) bool</code></pre> + <pre>{#syntax#}@addWithOverflow(comptime T: type, a: T, b: T, result: *T) bool{#endsyntax#}</pre> <p> - Performs <code>result.* = a + b</code>. If overflow or underflow occurs, - stores the overflowed bits in <code>result</code> and returns <code>true</code>. - If no overflow or underflow occurs, returns <code>false</code>. + Performs {#syntax#}result.* = a + b{#endsyntax#}. If overflow or underflow occurs, + stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}. + If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}. </p> {#header_close#} {#header_open|@ArgType#} - <pre><code class="zig">@ArgType(comptime T: type, comptime n: usize) type</code></pre> + <pre>{#syntax#}@ArgType(comptime T: type, comptime n: usize) type{#endsyntax#}</pre> <p> - This builtin function takes a function type and returns the type of the parameter at index <code>n</code>. + This builtin function takes a function type and returns the type of the parameter at index {#syntax#}n{#endsyntax#}. </p> <p> - <code>T</code> must be a function type. + {#syntax#}T{#endsyntax#} must be a function type. </p> <p> Note: This function is deprecated. Use {#link|@typeInfo#} instead. </p> {#header_close#} {#header_open|@atomicLoad#} - <pre><code class="zig">@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T</code></pre> + <pre>{#syntax#}@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre> <p> This builtin function atomically dereferences a pointer and returns the value. </p> <p> - <code>T</code> must be a pointer type, a <code>bool</code>, + {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, or an integer whose bit count meets these requirements: </p> <ul> @@ -5072,12 +5095,12 @@ fn seq(c: u8) void { </p> {#header_close#} {#header_open|@atomicRmw#} - <pre><code class="zig">@atomicRmw(comptime T: type, ptr: *T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T</code></pre> + <pre>{#syntax#}@atomicRmw(comptime T: type, ptr: *T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre> <p> This builtin function atomically modifies memory and then returns the previous value. </p> <p> - <code>T</code> must be a pointer type, a <code>bool</code>, + {#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, or an integer whose bit count meets these requirements: </p> <ul> @@ -5091,29 +5114,29 @@ fn seq(c: u8) void { </p> {#header_close#} {#header_open|@bitCast#} - <pre><code class="zig">@bitCast(comptime DestType: type, value: var) DestType</code></pre> + <pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre> <p> Converts a value of one type to another type. </p> <p> - Asserts that <code>@sizeOf(@typeOf(value)) == @sizeOf(DestType)</code>. + Asserts that {#syntax#}@sizeOf(@typeOf(value)) == @sizeOf(DestType){#endsyntax#}. </p> <p> - Asserts that <code>@typeId(DestType) != @import("builtin").TypeId.Pointer</code>. Use <code>@ptrCast</code> or <code>@intToPtr</code> if you need this. + Asserts that {#syntax#}@typeId(DestType) != @import("builtin").TypeId.Pointer{#endsyntax#}. Use {#syntax#}@ptrCast{#endsyntax#} or {#syntax#}@intToPtr{#endsyntax#} if you need this. </p> <p> Can be used for these things for example: </p> <ul> - <li>Convert <code>f32</code> to <code>u32</code> bits</li> - <li>Convert <code>i32</code> to <code>u32</code> preserving twos complement</li> + <li>Convert {#syntax#}f32{#endsyntax#} to {#syntax#}u32{#endsyntax#} bits</li> + <li>Convert {#syntax#}i32{#endsyntax#} to {#syntax#}u32{#endsyntax#} preserving twos complement</li> </ul> <p> - Works at compile-time if <code>value</code> is known at compile time. It's a compile error to bitcast a struct to a scalar type of the same size since structs have undefined layout. However if the struct is packed then it works. + Works at compile-time if {#syntax#}value{#endsyntax#} is known at compile time. It's a compile error to bitcast a struct to a scalar type of the same size since structs have undefined layout. However if the struct is packed then it works. </p> {#header_close#} {#header_open|@breakpoint#} - <pre><code class="zig">@breakpoint()</code></pre> + <pre>{#syntax#}@breakpoint(){#endsyntax#}</pre> <p> This function inserts a platform-specific debug trap instruction which causes debuggers to break there. @@ -5124,10 +5147,10 @@ fn seq(c: u8) void { {#header_close#} {#header_open|@alignCast#} - <pre><code class="zig">@alignCast(comptime alignment: u29, ptr: var) var</code></pre> + <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: var) var{#endsyntax#}</pre> <p> - <code>ptr</code> can be <code>*T</code>, <code>fn()</code>, <code>?*T</code>, - <code>?fn()</code>, or <code>[]T</code>. It returns the same type as <code>ptr</code> + {#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}fn(){#endsyntax#}, {#syntax#}?*T{#endsyntax#}, + {#syntax#}?fn(){#endsyntax#}, or {#syntax#}[]T{#endsyntax#}. It returns the same type as {#syntax#}ptr{#endsyntax#} except with the alignment adjusted to the new value. </p> <p>A {#link|pointer alignment safety check|Incorrect Pointer Alignment#} is added @@ -5135,16 +5158,16 @@ fn seq(c: u8) void { {#header_close#} {#header_open|@alignOf#} - <pre><code class="zig">@alignOf(comptime T: type) (number literal)</code></pre> + <pre>{#syntax#}@alignOf(comptime T: type) comptime_int{#endsyntax#}</pre> <p> This function returns the number of bytes that this type should be aligned to for the current target to match the C ABI. When the child type of a pointer has this alignment, the alignment can be omitted from the type. </p> - <pre><code class="zig">const assert = @import("std").debug.assert; + <pre>{#syntax#}const assert = @import("std").debug.assert; comptime { assert(*u32 == *align(@alignOf(u32)) u32); -}</code></pre> +}{#endsyntax#}</pre> <p> The result is a target-specific compile time constant. It is guaranteed to be less than or equal to {#link|@sizeOf(T)|@sizeOf#}. @@ -5153,21 +5176,21 @@ comptime { {#header_close#} {#header_open|@boolToInt#} - <pre><code class="zig">@boolToInt(value: bool) u1</code></pre> + <pre>{#syntax#}@boolToInt(value: bool) u1{#endsyntax#}</pre> <p> - Converts <code>true</code> to <code>u1(1)</code> and <code>false</code> to - <code>u1(0)</code>. + Converts {#syntax#}true{#endsyntax#} to {#syntax#}u1(1){#endsyntax#} and {#syntax#}false{#endsyntax#} to + {#syntax#}u1(0){#endsyntax#}. </p> <p> - If the value is known at compile-time, the return type is <code>comptime_int</code> - instead of <code>u1</code>. + If the value is known at compile-time, the return type is {#syntax#}comptime_int{#endsyntax#} + instead of {#syntax#}u1{#endsyntax#}. </p> {#header_close#} {#header_open|@bytesToSlice#} - <pre><code class="zig">@bytesToSlice(comptime Element: type, bytes: []u8) []Element</code></pre> + <pre>{#syntax#}@bytesToSlice(comptime Element: type, bytes: []u8) []Element{#endsyntax#}</pre> <p> - Converts a slice of bytes or array of bytes into a slice of <code>Element</code>. + Converts a slice of bytes or array of bytes into a slice of {#syntax#}Element{#endsyntax#}. The resulting slice has the same {#link|pointer|Pointers#} properties as the parameter. </p> <p> @@ -5177,12 +5200,12 @@ comptime { {#header_close#} {#header_open|@cDefine#} - <pre><code class="zig">@cDefine(comptime name: []u8, value)</code></pre> + <pre>{#syntax#}@cDefine(comptime name: []u8, value){#endsyntax#}</pre> <p> - This function can only occur inside <code>@cImport</code>. + This function can only occur inside {#syntax#}@cImport{#endsyntax#}. </p> <p> - This appends <code>#define $name $value</code> to the <code>@cImport</code> + This appends <code>#define $name $value</code> to the {#syntax#}@cImport{#endsyntax#} temporary buffer. </p> <p> @@ -5192,72 +5215,72 @@ comptime { <p> Use the void value, like this: </p> - <pre><code class="zig">@cDefine("_GNU_SOURCE", {})</code></pre> + <pre>{#syntax#}@cDefine("_GNU_SOURCE", {}){#endsyntax#}</pre> {#see_also|Import from C Header File|@cInclude|@cImport|@cUndef|void#} {#header_close#} {#header_open|@cImport#} - <pre><code class="zig">@cImport(expression) (namespace)</code></pre> + <pre>{#syntax#}@cImport(expression) (namespace){#endsyntax#}</pre> <p> This function parses C code and imports the functions, types, variables, and compatible macro definitions into the result namespace. </p> <p> - <code>expression</code> is interpreted at compile time. The builtin functions - <code>@cInclude</code>, <code>@cDefine</code>, and <code>@cUndef</code> work + {#syntax#}expression{#endsyntax#} is interpreted at compile time. The builtin functions + {#syntax#}@cInclude{#endsyntax#}, {#syntax#}@cDefine{#endsyntax#}, and {#syntax#}@cUndef{#endsyntax#} work within this expression, appending to a temporary buffer which is then parsed as C code. </p> <p> - Usually you should only have one <code>@cImport</code> in your entire application, because it saves the compiler + Usually you should only have one {#syntax#}@cImport{#endsyntax#} in your entire application, because it saves the compiler from invoking clang multiple times, and prevents inline functions from being duplicated. </p> <p> - Reasons for having multiple <code>@cImport</code> expressions would be: + Reasons for having multiple {#syntax#}@cImport{#endsyntax#} expressions would be: </p> <ul> - <li>To avoid a symbol collision, for example if foo.h and bar.h both <code>#define CONNECTION_COUNT</code></li> + <li>To avoid a symbol collision, for example if foo.h and bar.h both <code>#define CONNECTION_COUNT</code></li> <li>To analyze the C code with different preprocessor defines</li> </ul> {#see_also|Import from C Header File|@cInclude|@cDefine|@cUndef#} {#header_close#} {#header_open|@cInclude#} - <pre><code class="zig">@cInclude(comptime path: []u8)</code></pre> + <pre>{#syntax#}@cInclude(comptime path: []u8){#endsyntax#}</pre> <p> - This function can only occur inside <code>@cImport</code>. + This function can only occur inside {#syntax#}@cImport{#endsyntax#}. </p> <p> - This appends <code>#include <$path>\n</code> to the <code>c_import</code> + This appends <code>#include <$path>\n</code> to the {#syntax#}c_import{#endsyntax#} temporary buffer. </p> {#see_also|Import from C Header File|@cImport|@cDefine|@cUndef#} {#header_close#} {#header_open|@cUndef#} - <pre><code class="zig">@cUndef(comptime name: []u8)</code></pre> + <pre>{#syntax#}@cUndef(comptime name: []u8){#endsyntax#}</pre> <p> - This function can only occur inside <code>@cImport</code>. + This function can only occur inside {#syntax#}@cImport{#endsyntax#}. </p> <p> - This appends <code>#undef $name</code> to the <code>@cImport</code> + This appends <code>#undef $name</code> to the {#syntax#}@cImport{#endsyntax#} temporary buffer. </p> {#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#} {#header_close#} {#header_open|@clz#} - <pre><code class="zig">@clz(x: T) U</code></pre> + <pre>{#syntax#}@clz(x: T) U{#endsyntax#}</pre> <p> - This function counts the number of leading zeroes in <code>x</code> which is an integer - type <code>T</code>. + This function counts the number of leading zeroes in {#syntax#}x{#endsyntax#} which is an integer + type {#syntax#}T{#endsyntax#}. </p> <p> - The return type <code>U</code> is an unsigned integer with the minimum number - of bits that can represent the value <code>T.bit_count</code>. + The return type {#syntax#}U{#endsyntax#} is an unsigned integer with the minimum number + of bits that can represent the value {#syntax#}T.bit_count{#endsyntax#}. </p> <p> - If <code>x</code> is zero, <code>@clz</code> returns <code>T.bit_count</code>. + If {#syntax#}x{#endsyntax#} is zero, {#syntax#}@clz{#endsyntax#} returns {#syntax#}T.bit_count{#endsyntax#}. </p> {#see_also|@ctz|@popCount#} {#header_close#} {#header_open|@cmpxchgStrong#} - <pre><code class="zig">@cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T</code></pre> + <pre>{#syntax#}@cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T{#endsyntax#}</pre> <p> This function performs a strong atomic compare exchange operation. It's the equivalent of this code, except atomic: @@ -5278,13 +5301,13 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v more efficiently in machine instructions. </p> <p> - <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>. + {#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("builtin").AtomicOrder{#endsyntax#}. </p> - <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p> + <p>{#syntax#}@typeOf(ptr).alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p> {#see_also|Compile Variables|cmpxchgWeak#} {#header_close#} {#header_open|@cmpxchgWeak#} - <pre><code class="zig">@cmpxchgWeak(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T</code></pre> + <pre>{#syntax#}@cmpxchgWeak(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T{#endsyntax#}</pre> <p> This function performs a weak atomic compare exchange operation. It's the equivalent of this code, except atomic: @@ -5301,30 +5324,30 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val } {#code_end#} <p> - If you are using cmpxchg in a loop, the sporadic failure will be no problem, and <code>cmpxchgWeak</code> + If you are using cmpxchg in a loop, the sporadic failure will be no problem, and {#syntax#}cmpxchgWeak{#endsyntax#} is the better choice, because it can be implemented more efficiently in machine instructions. However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}. </p> <p> - <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>. + {#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("builtin").AtomicOrder{#endsyntax#}. </p> - <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p> + <p>{#syntax#}@typeOf(ptr).alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p> {#see_also|Compile Variables|cmpxchgStrong#} {#header_close#} {#header_open|@compileError#} - <pre><code class="zig">@compileError(comptime msg: []u8)</code></pre> + <pre>{#syntax#}@compileError(comptime msg: []u8){#endsyntax#}</pre> <p> This function, when semantically analyzed, causes a compile error with the - message <code>msg</code>. + message {#syntax#}msg{#endsyntax#}. </p> <p> There are several ways that code avoids being semantically checked, such as - using <code>if</code> or <code>switch</code> with compile time constants, - and <code>comptime</code> functions. + using {#syntax#}if{#endsyntax#} or {#syntax#}switch{#endsyntax#} with compile time constants, + and {#syntax#}comptime{#endsyntax#} functions. </p> {#header_close#} {#header_open|@compileLog#} - <pre><code class="zig">@compileLog(args: ...)</code></pre> + <pre>{#syntax#}@compileLog(args: ...){#endsyntax#}</pre> <p> This function prints the arguments passed to it at compile-time. </p> @@ -5359,7 +5382,7 @@ test "main" { will ouput: </p> <p> - If all <code>@compileLog</code> calls are removed or + If all {#syntax#}@compileLog{#endsyntax#} calls are removed or not encountered by analysis, the program compiles successfully and the generated executable prints: </p> @@ -5378,84 +5401,88 @@ test "main" { {#code_end#} {#header_close#} {#header_open|@ctz#} - <pre><code class="zig">@ctz(x: T) U</code></pre> + <pre>{#syntax#}@ctz(x: T) U{#endsyntax#}</pre> <p> - This function counts the number of trailing zeroes in <code>x</code> which is an integer - type <code>T</code>. + This function counts the number of trailing zeroes in {#syntax#}x{#endsyntax#} which is an integer + type {#syntax#}T{#endsyntax#}. </p> <p> - The return type <code>U</code> is an unsigned integer with the minimum number - of bits that can represent the value <code>T.bit_count</code>. + The return type {#syntax#}U{#endsyntax#} is an unsigned integer with the minimum number + of bits that can represent the value {#syntax#}T.bit_count{#endsyntax#}. </p> <p> - If <code>x</code> is zero, <code>@ctz</code> returns <code>T.bit_count</code>. + If {#syntax#}x{#endsyntax#} is zero, {#syntax#}@ctz{#endsyntax#} returns {#syntax#}T.bit_count{#endsyntax#}. </p> {#see_also|@clz|@popCount#} {#header_close#} {#header_open|@divExact#} - <pre><code class="zig">@divExact(numerator: T, denominator: T) T</code></pre> + <pre>{#syntax#}@divExact(numerator: T, denominator: T) T{#endsyntax#}</pre> <p> - Exact division. Caller guarantees <code>denominator != 0</code> and - <code>@divTrunc(numerator, denominator) * denominator == numerator</code>. + Exact division. Caller guarantees {#syntax#}denominator != 0{#endsyntax#} and + {#syntax#}@divTrunc(numerator, denominator) * denominator == numerator{#endsyntax#}. </p> <ul> - <li><code>@divExact(6, 3) == 2</code></li> - <li><code>@divExact(a, b) * b == a</code></li> + <li>{#syntax#}@divExact(6, 3) == 2{#endsyntax#}</li> + <li>{#syntax#}@divExact(a, b) * b == a{#endsyntax#}</li> </ul> - <p>For a function that returns a possible error code, use <code>@import("std").math.divExact</code>.</p> + <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divExact{#endsyntax#}.</p> {#see_also|@divTrunc|@divFloor#} {#header_close#} {#header_open|@divFloor#} - <pre><code class="zig">@divFloor(numerator: T, denominator: T) T</code></pre> + <pre>{#syntax#}@divFloor(numerator: T, denominator: T) T{#endsyntax#}</pre> <p> Floored division. Rounds toward negative infinity. For unsigned integers it is - the same as <code>numerator / denominator</code>. Caller guarantees <code>denominator != 0</code> and - <code>!(@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1)</code>. + the same as {#syntax#}numerator / denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator != 0{#endsyntax#} and + {#syntax#}!(@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1){#endsyntax#}. </p> <ul> - <li><code>@divFloor(-5, 3) == -2</code></li> - <li><code>@divFloor(a, b) + @mod(a, b) == a</code></li> + <li>{#syntax#}@divFloor(-5, 3) == -2{#endsyntax#}</li> + <li>{#syntax#}@divFloor(a, b) + @mod(a, b) == a{#endsyntax#}</li> </ul> - <p>For a function that returns a possible error code, use <code>@import("std").math.divFloor</code>.</p> + <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divFloor{#endsyntax#}.</p> {#see_also|@divTrunc|@divExact#} {#header_close#} {#header_open|@divTrunc#} - <pre><code class="zig">@divTrunc(numerator: T, denominator: T) T</code></pre> + <pre>{#syntax#}@divTrunc(numerator: T, denominator: T) T{#endsyntax#}</pre> <p> Truncated division. Rounds toward zero. For unsigned integers it is - the same as <code>numerator / denominator</code>. Caller guarantees <code>denominator != 0</code> and - <code>!(@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1)</code>. + the same as {#syntax#}numerator / denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator != 0{#endsyntax#} and + {#syntax#}!(@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1){#endsyntax#}. </p> <ul> - <li><code>@divTrunc(-5, 3) == -1</code></li> - <li><code>@divTrunc(a, b) + @rem(a, b) == a</code></li> + <li>{#syntax#}@divTrunc(-5, 3) == -1{#endsyntax#}</li> + <li>{#syntax#}@divTrunc(a, b) + @rem(a, b) == a{#endsyntax#}</li> </ul> - <p>For a function that returns a possible error code, use <code>@import("std").math.divTrunc</code>.</p> + <p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divTrunc{#endsyntax#}.</p> {#see_also|@divFloor|@divExact#} {#header_close#} {#header_open|@embedFile#} - <pre><code class="zig">@embedFile(comptime path: []const u8) [X]u8</code></pre> + <pre>{#syntax#}@embedFile(comptime path: []const u8) [X]u8{#endsyntax#}</pre> <p> This function returns a compile time constant fixed-size array with length - equal to the byte count of the file given by <code>path</code>. The contents of the array + equal to the byte count of the file given by {#syntax#}path{#endsyntax#}. The contents of the array are the contents of the file. </p> <p> - <code>path</code> is absolute or relative to the current file, just like <code>@import</code>. + {#syntax#}path{#endsyntax#} is absolute or relative to the current file, just like {#syntax#}@import{#endsyntax#}. </p> {#see_also|@import#} {#header_close#} {#header_open|@enumToInt#} - <pre><code class="zig">@enumToInt(enum_value: var) var</code></pre> + <pre>{#syntax#}@enumToInt(enum_value: var) var{#endsyntax#}</pre> <p> Converts an enumeration value into its integer tag type. </p> + <p> + If the enum has only 1 possible value, the resut is a {#syntax#}comptime_int{#endsyntax#} + known at {#link|comptime#}. + </p> {#see_also|@intToEnum#} {#header_close#} {#header_open|@errSetCast#} - <pre><code class="zig">@errSetCast(comptime T: DestType, value: var) DestType</code></pre> + <pre>{#syntax#}@errSetCast(comptime T: DestType, value: var) DestType{#endsyntax#}</pre> <p> Converts an error value from one error set to another error set. Attempting to convert an error which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}. @@ -5463,24 +5490,24 @@ test "main" { {#header_close#} {#header_open|@errorName#} - <pre><code class="zig">@errorName(err: error) []u8</code></pre> + <pre>{#syntax#}@errorName(err: error) []u8{#endsyntax#}</pre> <p> This function returns the string representation of an error. If an error declaration is: </p> - <pre><code class="zig">error OutOfMem</code></pre> + <pre>{#syntax#}error OutOfMem{#endsyntax#}</pre> <p> - Then the string representation is <code>"OutOfMem"</code>. + Then the string representation is {#syntax#}"OutOfMem"{#endsyntax#}. </p> <p> - If there are no calls to <code>@errorName</code> in an entire application, - or all calls have a compile-time known value for <code>err</code>, then no + If there are no calls to {#syntax#}@errorName{#endsyntax#} in an entire application, + or all calls have a compile-time known value for {#syntax#}err{#endsyntax#}, then no error name table will be generated. </p> {#header_close#} {#header_open|@errorReturnTrace#} - <pre><code class="zig">@errorReturnTrace() ?*builtin.StackTrace</code></pre> + <pre>{#syntax#}@errorReturnTrace() ?*builtin.StackTrace{#endsyntax#}</pre> <p> If the binary is built with error return tracing, and this function is invoked in a function that calls a function with an error or error union return type, returns a @@ -5489,13 +5516,13 @@ test "main" { {#header_close#} {#header_open|@errorToInt#} - <pre><code class="zig">@errorToInt(err: var) @IntType(false, @sizeOf(error) * 8)</code></pre> + <pre>{#syntax#}@errorToInt(err: var) @IntType(false, @sizeOf(error) * 8){#endsyntax#}</pre> <p> Supports the following types: </p> <ul> <li>error unions</li> - <li><code>E!void</code></li> + <li>{#syntax#}E!void{#endsyntax#}</li> </ul> <p> Converts an error to the integer representation of an error. @@ -5508,38 +5535,41 @@ test "main" { {#header_close#} {#header_open|@export#} - <pre><code class="zig">@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8</code></pre> + <pre>{#syntax#}@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8{#endsyntax#}</pre> <p> Creates a symbol in the output object file. </p> {#header_close#} {#header_open|@fence#} - <pre><code class="zig">@fence(order: AtomicOrder)</code></pre> + <pre>{#syntax#}@fence(order: AtomicOrder){#endsyntax#}</pre> <p> - The <code>fence</code> function is used to introduce happens-before edges between operations. + The {#syntax#}fence{#endsyntax#} function is used to introduce happens-before edges between operations. </p> <p> - <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>. + {#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("builtin").AtomicOrder{#endsyntax#}. </p> {#see_also|Compile Variables#} {#header_close#} {#header_open|@field#} - <pre><code class="zig">@field(lhs: var, comptime field_name: []const u8) (field)</code></pre> - <p>Preforms field access equivalent to <code>lhs.-&gtfield_name-&lt</code>.</p> + <pre>{#syntax#}@field(lhs: var, comptime field_name: []const u8) (field){#endsyntax#}</pre> + <p>Preforms field access equivalent to {#syntax#}lhs.field_name{#endsyntax#}, except instead + of the field {#syntax#}"field_name"{#endsyntax#}, it accesses the field named by the string + value of {#syntax#}field_name{#endsyntax#}. + </p> {#header_close#} {#header_open|@fieldParentPtr#} - <pre><code class="zig">@fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8, - field_ptr: *T) *ParentType</code></pre> + <pre>{#syntax#}@fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8, + field_ptr: *T) *ParentType{#endsyntax#}</pre> <p> Given a pointer to a field, returns the base pointer of a struct. </p> {#header_close#} {#header_open|@floatCast#} - <pre><code class="zig">@floatCast(comptime DestType: type, value: var) DestType</code></pre> + <pre>{#syntax#}@floatCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre> <p> Convert from one float type to another. This cast is safe, but may cause the numeric value to lose precision. @@ -5547,7 +5577,7 @@ test "main" { {#header_close#} {#header_open|@floatToInt#} - <pre><code class="zig">@floatToInt(comptime DestType: type, float: var) DestType</code></pre> + <pre>{#syntax#}@floatToInt(comptime DestType: type, float: var) DestType{#endsyntax#}</pre> <p> Converts the integer part of a floating point number to the destination type. </p> @@ -5559,7 +5589,7 @@ test "main" { {#header_close#} {#header_open|@frameAddress#} - <pre><code class="zig">@frameAddress()</code></pre> + <pre>{#syntax#}@frameAddress(){#endsyntax#}</pre> <p> This function returns the base pointer of the current stack frame. </p> @@ -5573,9 +5603,9 @@ test "main" { </p> {#header_close#} {#header_open|@handle#} - <pre><code class="zig">@handle()</code></pre> + <pre>{#syntax#}@handle(){#endsyntax#}</pre> <p> - This function returns a <code>promise->T</code> type, where <code>T</code> + This function returns a {#syntax#}promise->T{#endsyntax#} type, where {#syntax#}T{#endsyntax#} is the return type of the async function in scope. </p> <p> @@ -5583,27 +5613,27 @@ test "main" { </p> {#header_close#} {#header_open|@import#} - <pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre> + <pre>{#syntax#}@import(comptime path: []u8) (namespace){#endsyntax#}</pre> <p> - This function finds a zig file corresponding to <code>path</code> and imports all the + This function finds a zig file corresponding to {#syntax#}path{#endsyntax#} and imports all the public top level declarations into the resulting namespace. </p> <p> - <code>path</code> can be a relative or absolute path, or it can be the name of a package. - If it is a relative path, it is relative to the file that contains the <code>@import</code> + {#syntax#}path{#endsyntax#} can be a relative or absolute path, or it can be the name of a package. + If it is a relative path, it is relative to the file that contains the {#syntax#}@import{#endsyntax#} function call. </p> <p> The following packages are always available: </p> <ul> - <li><code>@import("std")</code> - Zig Standard Library</li> - <li><code>@import("builtin")</code> - Compiler-provided types and variables</li> + <li>{#syntax#}@import("std"){#endsyntax#} - Zig Standard Library</li> + <li>{#syntax#}@import("builtin"){#endsyntax#} - Compiler-provided types and variables</li> </ul> {#see_also|Compile Variables|@embedFile#} {#header_close#} {#header_open|@inlineCall#} - <pre><code class="zig">@inlineCall(function: X, args: ...) Y</code></pre> + <pre>{#syntax#}@inlineCall(function: X, args: ...) Y{#endsyntax#}</pre> <p> This calls a function, in the same way that invoking an expression with parentheses does: </p> @@ -5617,14 +5647,14 @@ test "inline function call" { fn add(a: i32, b: i32) i32 { return a + b; } {#code_end#} <p> - Unlike a normal function call, however, <code>@inlineCall</code> guarantees that the call + Unlike a normal function call, however, {#syntax#}@inlineCall{#endsyntax#} guarantees that the call will be inlined. If the call cannot be inlined, a compile error is emitted. </p> {#see_also|@noInlineCall#} {#header_close#} {#header_open|@intCast#} - <pre><code class="zig">@intCast(comptime DestType: type, int: var) DestType</code></pre> + <pre>{#syntax#}@intCast(comptime DestType: type, int: var) DestType{#endsyntax#}</pre> <p> Converts an integer to another integer while keeping the same numerical value. Attempting to convert a number which is out of range of the destination type results in @@ -5633,7 +5663,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } {#header_close#} {#header_open|@intToEnum#} - <pre><code class="zig">@intToEnum(comptime DestType: type, int_value: @TagType(DestType)) DestType</code></pre> + <pre>{#syntax#}@intToEnum(comptime DestType: type, int_value: @TagType(DestType)) DestType{#endsyntax#}</pre> <p> Converts an integer into an {#link|enum#} value. </p> @@ -5645,7 +5675,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } {#header_close#} {#header_open|@intToError#} - <pre><code class="zig">@intToError(value: @IntType(false, @sizeOf(error) * 8)) error</code></pre> + <pre>{#syntax#}@intToError(value: @IntType(false, @sizeOf(error) * 8)) error{#endsyntax#}</pre> <p> Converts from the integer representation of an error into the global error set type. </p> @@ -5661,36 +5691,36 @@ fn add(a: i32, b: i32) i32 { return a + b; } {#header_close#} {#header_open|@intToFloat#} - <pre><code class="zig">@intToFloat(comptime DestType: type, int: var) DestType</code></pre> + <pre>{#syntax#}@intToFloat(comptime DestType: type, int: var) DestType{#endsyntax#}</pre> <p> Converts an integer to the closest floating point representation. To convert the other way, use {#link|@floatToInt#}. This cast is always safe. </p> {#header_close#} {#header_open|@intToPtr#} - <pre><code class="zig">@intToPtr(comptime DestType: type, int: usize) DestType</code></pre> + <pre>{#syntax#}@intToPtr(comptime DestType: type, int: usize) DestType{#endsyntax#}</pre> <p> Converts an integer to a pointer. To convert the other way, use {#link|@ptrToInt#}. </p> {#header_close#} {#header_open|@IntType#} - <pre><code class="zig">@IntType(comptime is_signed: bool, comptime bit_count: u32) type</code></pre> + <pre>{#syntax#}@IntType(comptime is_signed: bool, comptime bit_count: u32) type{#endsyntax#}</pre> <p> This function returns an integer type with the given signness and bit count. </p> {#header_close#} {#header_open|@maxValue#} - <pre><code class="zig">@maxValue(comptime T: type) (number literal)</code></pre> + <pre>{#syntax#}@maxValue(comptime T: type) comptime_int{#endsyntax#}</pre> <p> - This function returns the maximum value of the integer type <code>T</code>. + This function returns the maximum value of the integer type {#syntax#}T{#endsyntax#}. </p> <p> The result is a compile time constant. </p> {#header_close#} {#header_open|@memberCount#} - <pre><code class="zig">@memberCount(comptime T: type) (number literal)</code></pre> + <pre>{#syntax#}@memberCount(comptime T: type) comptime_int{#endsyntax#}</pre> <p> This function returns the number of members in a struct, enum, or union type. </p> @@ -5702,7 +5732,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } </p> {#header_close#} {#header_open|@memberName#} - <pre><code class="zig">@memberName(comptime T: type, comptime index: usize) [N]u8</code></pre> + <pre>{#syntax#}@memberName(comptime T: type, comptime index: usize) [N]u8{#endsyntax#}</pre> <p>Returns the field name of a struct, union, or enum.</p> <p> The result is a compile time constant. @@ -5712,46 +5742,46 @@ fn add(a: i32, b: i32) i32 { return a + b; } </p> {#header_close#} {#header_open|@memberType#} - <pre><code class="zig">@memberType(comptime T: type, comptime index: usize) type</code></pre> + <pre>{#syntax#}@memberType(comptime T: type, comptime index: usize) type{#endsyntax#}</pre> <p>Returns the field type of a struct or union.</p> {#header_close#} {#header_open|@memcpy#} - <pre><code class="zig">@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize)</code></pre> + <pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize){#endsyntax#}</pre> <p> - This function copies bytes from one region of memory to another. <code>dest</code> and - <code>source</code> are both pointers and must not overlap. + This function copies bytes from one region of memory to another. {#syntax#}dest{#endsyntax#} and + {#syntax#}source{#endsyntax#} are both pointers and must not overlap. </p> <p> This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this: </p> - <pre><code class="zig">for (source[0...byte_count]) |b, i| dest[i] = b;</code></pre> + <pre>{#syntax#}for (source[0...byte_count]) |b, i| dest[i] = b;{#endsyntax#}</pre> <p> The optimizer is intelligent enough to turn the above snippet into a memcpy. </p> <p>There is also a standard library function for this:</p> - <pre><code class="zig">const mem = @import("std").mem; -mem.copy(u8, dest[0...byte_count], source[0...byte_count]);</code></pre> + <pre>{#syntax#}const mem = @import("std").mem; +mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#}</pre> {#header_close#} {#header_open|@memset#} - <pre><code class="zig">@memset(dest: [*]u8, c: u8, byte_count: usize)</code></pre> + <pre>{#syntax#}@memset(dest: [*]u8, c: u8, byte_count: usize){#endsyntax#}</pre> <p> - This function sets a region of memory to <code>c</code>. <code>dest</code> is a pointer. + This function sets a region of memory to {#syntax#}c{#endsyntax#}. {#syntax#}dest{#endsyntax#} is a pointer. </p> <p> This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this: </p> - <pre><code class="zig">for (dest[0...byte_count]) |*b| b.* = c;</code></pre> + <pre>{#syntax#}for (dest[0...byte_count]) |*b| b.* = c;{#endsyntax#}</pre> <p> The optimizer is intelligent enough to turn the above snippet into a memset. </p> <p>There is also a standard library function for this:</p> - <pre><code>const mem = @import("std").mem; -mem.set(u8, dest, c);</code></pre> + <pre>{#syntax#}const mem = @import("std").mem; +mem.set(u8, dest, c);{#endsyntax#}</pre> {#header_close#} {#header_open|@minValue#} - <pre><code class="zig">@minValue(comptime T: type) (number literal)</code></pre> + <pre>{#syntax#}@minValue(comptime T: type) comptime_int{#endsyntax#}</pre> <p> This function returns the minimum value of the integer type T. </p> @@ -5760,31 +5790,31 @@ mem.set(u8, dest, c);</code></pre> </p> {#header_close#} {#header_open|@mod#} - <pre><code class="zig">@mod(numerator: T, denominator: T) T</code></pre> + <pre>{#syntax#}@mod(numerator: T, denominator: T) T{#endsyntax#}</pre> <p> Modulus division. For unsigned integers this is the same as - <code>numerator % denominator</code>. Caller guarantees <code>denominator &gt; 0</code>. + {#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator &gt; 0{#endsyntax#}. </p> <ul> - <li><code>@mod(-5, 3) == 1</code></li> - <li><code>@divFloor(a, b) + @mod(a, b) == a</code></li> + <li>{#syntax#}@mod(-5, 3) == 1{#endsyntax#}</li> + <li>{#syntax#}@divFloor(a, b) + @mod(a, b) == a{#endsyntax#}</li> </ul> - <p>For a function that returns an error code, see <code>@import("std").math.mod</code>.</p> + <p>For a function that returns an error code, see {#syntax#}@import("std").math.mod{#endsyntax#}.</p> {#see_also|@rem#} {#header_close#} {#header_open|@mulWithOverflow#} - <pre><code class="zig">@mulWithOverflow(comptime T: type, a: T, b: T, result: *T) bool</code></pre> + <pre>{#syntax#}@mulWithOverflow(comptime T: type, a: T, b: T, result: *T) bool{#endsyntax#}</pre> <p> - Performs <code>result.* = a * b</code>. If overflow or underflow occurs, - stores the overflowed bits in <code>result</code> and returns <code>true</code>. - If no overflow or underflow occurs, returns <code>false</code>. + Performs {#syntax#}result.* = a * b{#endsyntax#}. If overflow or underflow occurs, + stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}. + If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}. </p> {#header_close#} {#header_open|@newStackCall#} - <pre><code class="zig">@newStackCall(new_stack: []u8, function: var, args: ...) var</code></pre> + <pre>{#syntax#}@newStackCall(new_stack: []u8, function: var, args: ...) var{#endsyntax#}</pre> <p> This calls a function, in the same way that invoking an expression with parentheses does. However, - instead of using the same stack as the caller, the function uses the stack provided in the <code>new_stack</code> + instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#} parameter. </p> {#code_begin|test#} @@ -5817,7 +5847,7 @@ fn targetFunction(x: i32) usize { {#code_end#} {#header_close#} {#header_open|@noInlineCall#} - <pre><code class="zig">@noInlineCall(function: var, args: ...) var</code></pre> + <pre>{#syntax#}@noInlineCall(function: var, args: ...) var{#endsyntax#}</pre> <p> This calls a function, in the same way that invoking an expression with parentheses does: </p> @@ -5833,19 +5863,19 @@ fn add(a: i32, b: i32) i32 { } {#code_end#} <p> - Unlike a normal function call, however, <code>@noInlineCall</code> guarantees that the call + Unlike a normal function call, however, {#syntax#}@noInlineCall{#endsyntax#} guarantees that the call will not be inlined. If the call must be inlined, a compile error is emitted. </p> {#see_also|@inlineCall#} {#header_close#} {#header_open|@offsetOf#} - <pre><code class="zig">@offsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)</code></pre> + <pre>{#syntax#}@offsetOf(comptime T: type, comptime field_name: [] const u8) comptime_int{#endsyntax#}</pre> <p> This function returns the byte offset of a field relative to its containing struct. </p> {#header_close#} {#header_open|@OpaqueType#} - <pre><code class="zig">@OpaqueType() type</code></pre> + <pre>{#syntax#}@OpaqueType() type{#endsyntax#}</pre> <p> Creates a new type with an unknown size and alignment. </p> @@ -5868,14 +5898,14 @@ test "call foo" { {#code_end#} {#header_close#} {#header_open|@panic#} - <pre><code class="zig">@panic(message: []const u8) noreturn</code></pre> + <pre>{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}</pre> <p> Invokes the panic handler function. By default the panic handler function - calls the public <code>panic</code> function exposed in the root source file, or - if there is not one specified, invokes the one provided in <code>std/special/panic.zig</code>. + calls the public {#syntax#}panic{#endsyntax#} function exposed in the root source file, or + if there is not one specified, invokes the one provided in {#syntax#}std/special/panic.zig{#endsyntax#}. </p> - <p>Generally it is better to use <code>@import("std").debug.panic</code>. - However, <code>@panic</code> can be useful for 2 scenarios: + <p>Generally it is better to use {#syntax#}@import("std").debug.panic{#endsyntax#}. + However, {#syntax#}@panic{#endsyntax#} can be useful for 2 scenarios: </p> <ul> <li>From library code, calling the programmer's panic function if they exposed one in the root source file.</li> @@ -5884,50 +5914,50 @@ test "call foo" { {#see_also|Root Source File#} {#header_close#} {#header_open|@popCount#} - <pre><code class="zig">@popCount(integer: var) var</code></pre> + <pre>{#syntax#}@popCount(integer: var) var{#endsyntax#}</pre> <p>Counts the number of bits set in an integer.</p> <p> - If <code>integer</code> is known at {#link|comptime#}, the return type is <code>comptime_int</code>. + If {#syntax#}integer{#endsyntax#} is known at {#link|comptime#}, the return type is {#syntax#}comptime_int{#endsyntax#}. Otherwise, the return type is an unsigned integer with the minimum number of bits that can represent the bit count of the integer type. </p> {#see_also|@ctz|@clz#} {#header_close#} {#header_open|@ptrCast#} - <pre><code class="zig">@ptrCast(comptime DestType: type, value: var) DestType</code></pre> + <pre>{#syntax#}@ptrCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre> <p> Converts a pointer of one type to a pointer of another type. </p> {#header_close#} {#header_open|@ptrToInt#} - <pre><code class="zig">@ptrToInt(value: var) usize</code></pre> + <pre>{#syntax#}@ptrToInt(value: var) usize{#endsyntax#}</pre> <p> - Converts <code>value</code> to a <code>usize</code> which is the address of the pointer. <code>value</code> can be one of these types: + Converts {#syntax#}value{#endsyntax#} to a {#syntax#}usize{#endsyntax#} which is the address of the pointer. {#syntax#}value{#endsyntax#} can be one of these types: </p> <ul> - <li><code>*T</code></li> - <li><code>?*T</code></li> - <li><code>fn()</code></li> - <li><code>?fn()</code></li> + <li>{#syntax#}*T{#endsyntax#}</li> + <li>{#syntax#}?*T{#endsyntax#}</li> + <li>{#syntax#}fn(){#endsyntax#}</li> + <li>{#syntax#}?fn(){#endsyntax#}</li> </ul> <p>To convert the other way, use {#link|@intToPtr#}</p> {#header_close#} {#header_open|@rem#} - <pre><code class="zig">@rem(numerator: T, denominator: T) T</code></pre> + <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre> <p> Remainder division. For unsigned integers this is the same as - <code>numerator % denominator</code>. Caller guarantees <code>denominator &gt; 0</code>. + {#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator > 0{#endsyntax#}. </p> <ul> - <li><code>@rem(-5, 3) == -2</code></li> - <li><code>@divTrunc(a, b) + @rem(a, b) == a</code></li> + <li>{#syntax#}@rem(-5, 3) == -2{#endsyntax#}</li> + <li>{#syntax#}@divTrunc(a, b) + @rem(a, b) == a{#endsyntax#}</li> </ul> - <p>For a function that returns an error code, see <code>@import("std").math.rem</code>.</p> + <p>For a function that returns an error code, see {#syntax#}@import("std").math.rem{#endsyntax#}.</p> {#see_also|@mod#} {#header_close#} {#header_open|@returnAddress#} - <pre><code class="zig">@returnAddress()</code></pre> + <pre>{#syntax#}@returnAddress(){#endsyntax#}</pre> <p> This function returns a pointer to the return address of the current stack frame. @@ -5941,32 +5971,32 @@ test "call foo" { </p> {#header_close#} {#header_open|@setAlignStack#} - <pre><code class="zig">@setAlignStack(comptime alignment: u29)</code></pre> + <pre>{#syntax#}@setAlignStack(comptime alignment: u29){#endsyntax#}</pre> <p> - Ensures that a function will have a stack alignment of at least <code>alignment</code> bytes. + Ensures that a function will have a stack alignment of at least {#syntax#}alignment{#endsyntax#} bytes. </p> {#header_close#} {#header_open|@setCold#} - <pre><code class="zig">@setCold(is_cold: bool)</code></pre> + <pre>{#syntax#}@setCold(is_cold: bool){#endsyntax#}</pre> <p> Tells the optimizer that a function is rarely called. </p> {#header_close#} {#header_open|@setRuntimeSafety#} - <pre><code class="zig">@setRuntimeSafety(safety_on: bool)</code></pre> + <pre>{#syntax#}@setRuntimeSafety(safety_on: bool){#endsyntax#}</pre> <p> Sets whether runtime safety checks are on for the scope that contains the function call. </p> {#header_close#} {#header_open|@setEvalBranchQuota#} - <pre><code class="zig">@setEvalBranchQuota(new_quota: usize)</code></pre> + <pre>{#syntax#}@setEvalBranchQuota(new_quota: usize){#endsyntax#}</pre> <p> Changes the maximum number of backwards branches that compile-time code execution can use before giving up and making a compile error. </p> <p> - If the <code>new_quota</code> is smaller than the default quota (<code>1000</code>) or + If the {#syntax#}new_quota{#endsyntax#} is smaller than the default quota ({#syntax#}1000{#endsyntax#}) or a previously explicitly set quota, it is ignored. </p> <p> @@ -5980,7 +6010,7 @@ test "foo" { } } {#code_end#} - <p>Now we use <code class="zig">@setEvalBranchQuota</code>:</p> + <p>Now we use {#syntax#}@setEvalBranchQuota{#endsyntax#}:</p> {#code_begin|test#} test "foo" { comptime { @@ -5994,19 +6024,22 @@ test "foo" { {#see_also|comptime#} {#header_close#} {#header_open|@setFloatMode#} - <pre><code class="zig">@setFloatMode(scope, mode: @import("builtin").FloatMode)</code></pre> + <pre>{#syntax#}@setFloatMode(mode: @import("builtin").FloatMode){#endsyntax#}</pre> <p> - Sets the floating point mode for a given scope. Possible values are: + Sets the floating point mode of the current scope. Possible values are: </p> {#code_begin|syntax#} pub const FloatMode = enum { - Optimized, Strict, + Optimized, }; {#code_end#} <ul> <li> - <code>Optimized</code> - Floating point operations may do all of the following: + {#syntax#}Strict{#endsyntax#} (default) - Floating point operations follow strict IEEE compliance. + </li> + <li> + {#syntax#}Optimized{#endsyntax#} - Floating point operations may do all of the following: <ul> <li>Assume the arguments and result are not NaN. Optimizations are required to retain defined behavior over NaNs, but the value of the result is undefined.</li> <li>Assume the arguments and result are not +/-Inf. Optimizations are required to retain defined behavior over +/-Inf, but the value of the result is undefined.</li> @@ -6017,61 +6050,62 @@ pub const FloatMode = enum { </ul> This is equivalent to <code>-ffast-math</code> in GCC. </li> - <li> - <code>Strict</code> (default) - Floating point operations follow strict IEEE compliance. - </li> </ul> + <p> + The floating point mode is inherited by child scopes, and can be overridden in any scope. + You can set the floating point mode in a struct or module scope by using a comptime block. + </p> {#see_also|Floating Point Operations#} {#header_close#} {#header_open|@setGlobalLinkage#} - <pre><code class="zig">@setGlobalLinkage(global_variable_name, comptime linkage: GlobalLinkage)</code></pre> + <pre>{#syntax#}@setGlobalLinkage(global_variable_name, comptime linkage: GlobalLinkage){#endsyntax#}</pre> <p> - <code>GlobalLinkage</code> can be found with <code>@import("builtin").GlobalLinkage</code>. + {#syntax#}GlobalLinkage{#endsyntax#} can be found with {#syntax#}@import("builtin").GlobalLinkage{#endsyntax#}. </p> {#see_also|Compile Variables#} {#header_close#} {#header_open|@shlExact#} - <pre><code class="zig">@shlExact(value: T, shift_amt: Log2T) T</code></pre> + <pre>{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre> <p> - Performs the left shift operation (<code>&lt;&lt;</code>). Caller guarantees + Performs the left shift operation ({#syntax#}<<{#endsyntax#}). Caller guarantees that the shift will not shift any 1 bits out. </p> <p> - The type of <code>shift_amt</code> is an unsigned integer with <code>log2(T.bit_count)</code> bits. - This is because <code>shift_amt &gt;= T.bit_count</code> is undefined behavior. + The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits. + This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior. </p> {#see_also|@shrExact|@shlWithOverflow#} {#header_close#} {#header_open|@shlWithOverflow#} - <pre><code class="zig">@shlWithOverflow(comptime T: type, a: T, shift_amt: Log2T, result: *T) bool</code></pre> + <pre>{#syntax#}@shlWithOverflow(comptime T: type, a: T, shift_amt: Log2T, result: *T) bool{#endsyntax#}</pre> <p> - Performs <code>result.* = a &lt;&lt; b</code>. If overflow or underflow occurs, - stores the overflowed bits in <code>result</code> and returns <code>true</code>. - If no overflow or underflow occurs, returns <code>false</code>. + Performs {#syntax#}result.* = a << b{#endsyntax#}. If overflow or underflow occurs, + stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}. + If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}. </p> <p> - The type of <code>shift_amt</code> is an unsigned integer with <code>log2(T.bit_count)</code> bits. - This is because <code>shift_amt &gt;= T.bit_count</code> is undefined behavior. + The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits. + This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior. </p> {#see_also|@shlExact|@shrExact#} {#header_close#} {#header_open|@shrExact#} - <pre><code class="zig">@shrExact(value: T, shift_amt: Log2T) T</code></pre> + <pre>{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre> <p> - Performs the right shift operation (<code>&gt;&gt;</code>). Caller guarantees + Performs the right shift operation ({#syntax#}>>{#endsyntax#}). Caller guarantees that the shift will not shift any 1 bits out. </p> <p> - The type of <code>shift_amt</code> is an unsigned integer with <code>log2(T.bit_count)</code> bits. - This is because <code>shift_amt &gt;= T.bit_count</code> is undefined behavior. + The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits. + This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior. </p> {#see_also|@shlExact|@shlWithOverflow#} {#header_close#} {#header_open|@sizeOf#} - <pre><code class="zig">@sizeOf(comptime T: type) comptime_int</code></pre> + <pre>{#syntax#}@sizeOf(comptime T: type) comptime_int{#endsyntax#}</pre> <p> - This function returns the number of bytes it takes to store <code>T</code> in memory. + This function returns the number of bytes it takes to store {#syntax#}T{#endsyntax#} in memory. </p> <p> The result is a target-specific compile time constant. @@ -6079,39 +6113,39 @@ pub const FloatMode = enum { {#header_close#} {#header_open|@sliceToBytes#} - <pre><code class="zig">@sliceToBytes(value: var) []u8</code></pre> + <pre>{#syntax#}@sliceToBytes(value: var) []u8{#endsyntax#}</pre> <p> - Converts a slice or array to a slice of <code>u8</code>. The resulting slice has the same + Converts a slice or array to a slice of {#syntax#}u8{#endsyntax#}. The resulting slice has the same {#link|pointer|Pointers#} properties as the parameter. </p> {#header_close#} {#header_open|@sqrt#} - <pre><code class="zig">@sqrt(comptime T: type, value: T) T</code></pre> + <pre>{#syntax#}@sqrt(comptime T: type, value: T) T{#endsyntax#}</pre> <p> Performs the square root of a floating point number. Uses a dedicated hardware instruction when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO. </p> <p> - This is a low-level intrinsic. Most code can use <code>std.math.sqrt</code> instead. + This is a low-level intrinsic. Most code can use {#syntax#}std.math.sqrt{#endsyntax#} instead. </p> {#header_close#} {#header_open|@subWithOverflow#} - <pre><code class="zig">@subWithOverflow(comptime T: type, a: T, b: T, result: *T) bool</code></pre> + <pre>{#syntax#}@subWithOverflow(comptime T: type, a: T, b: T, result: *T) bool{#endsyntax#}</pre> <p> - Performs <code>result.* = a - b</code>. If overflow or underflow occurs, - stores the overflowed bits in <code>result</code> and returns <code>true</code>. - If no overflow or underflow occurs, returns <code>false</code>. + Performs {#syntax#}result.* = a - b{#endsyntax#}. If overflow or underflow occurs, + stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}. + If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}. </p> {#header_close#} {#header_open|@tagName#} - <pre><code class="zig">@tagName(value: var) []const u8</code></pre> + <pre>{#syntax#}@tagName(value: var) []const u8{#endsyntax#}</pre> <p> Converts an enum value or union value to a slice of bytes representing the name. </p> {#header_close#} {#header_open|@TagType#} - <pre><code class="zig">@TagType(T: type) type</code></pre> + <pre>{#syntax#}@TagType(T: type) type{#endsyntax#}</pre> <p> For an enum, returns the integer type that is used to store the enumeration value. </p> @@ -6119,8 +6153,43 @@ pub const FloatMode = enum { For a union, returns the enum type that is used to store the tag value. </p> {#header_close#} + {#header_open|@This#} + <pre>{#syntax#}@This() type{#endsyntax#}</pre> + <p> + Returns the innermost struct or union that this function call is inside. + This can be useful for an anonymous struct that needs to refer to itself: + </p> + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +test "@This()" { + var items = []i32{ 1, 2, 3, 4 }; + const list = List(i32){ .items = items[0..] }; + assert(list.length() == 4); +} + +fn List(comptime T: type) type { + return struct { + const Self = @This(); + + items: []T, + + fn length(self: Self) usize { + return self.items.len; + } + }; +} + {#code_end#} + <p> + When {#syntax#}@This(){#endsyntax#} is used at global scope, it returns a reference to the + current import. There is a proposal to remove the import type and use an empty struct + type instead. See + <a href="https://github.com/ziglang/zig/issues/1047">#1047</a> for details. + </p> + {#header_close#} {#header_open|@truncate#} - <pre><code class="zig">@truncate(comptime T: type, integer) T</code></pre> + <pre>{#syntax#}@truncate(comptime T: type, integer) T{#endsyntax#}</pre> <p> This function truncates bits from an integer type, resulting in a smaller integer type. @@ -6129,14 +6198,14 @@ pub const FloatMode = enum { The following produces a crash in debug mode and undefined behavior in release mode: </p> - <pre><code class="zig">const a: u16 = 0xabcd; -const b: u8 = u8(a);</code></pre> + <pre>{#syntax#}const a: u16 = 0xabcd; +const b: u8 = u8(a);{#endsyntax#}</pre> <p> However this is well defined and working code: </p> - <pre><code class="zig">const a: u16 = 0xabcd; + <pre>{#syntax#}const a: u16 = 0xabcd; const b: u8 = @truncate(u8, a); -// b is now 0xcd</code></pre> +// b is now 0xcd{#endsyntax#}</pre> <p> This function always truncates the significant bits of the integer, regardless of endianness on the target platform. @@ -6144,7 +6213,7 @@ const b: u8 = @truncate(u8, a); {#header_close#} {#header_open|@typeId#} - <pre><code class="zig">@typeId(comptime T: type) @import("builtin").TypeId</code></pre> + <pre>{#syntax#}@typeId(comptime T: type) @import("builtin").TypeId{#endsyntax#}</pre> <p> Returns which kind of type something is. Possible values: </p> @@ -6178,7 +6247,7 @@ pub const TypeId = enum { {#code_end#} {#header_close#} {#header_open|@typeInfo#} - <pre><code class="zig">@typeInfo(comptime T: type) @import("builtin").TypeInfo</code></pre> + <pre>{#syntax#}@typeInfo(comptime T: type) @import("builtin").TypeInfo{#endsyntax#}</pre> <p> Returns information on the type. Returns a value of the following union: </p> @@ -6361,14 +6430,14 @@ pub const TypeInfo = union(TypeId) { {#code_end#} {#header_close#} {#header_open|@typeName#} - <pre><code class="zig">@typeName(T: type) []u8</code></pre> + <pre>{#syntax#}@typeName(T: type) []u8{#endsyntax#}</pre> <p> This function returns the string representation of a type. </p> {#header_close#} {#header_open|@typeOf#} - <pre><code class="zig">@typeOf(expression) type</code></pre> + <pre>{#syntax#}@typeOf(expression) type{#endsyntax#}</pre> <p> This function returns a compile-time constant, which is the type of the expression passed as an argument. The expression is evaluated. @@ -6545,11 +6614,11 @@ pub fn main() void { {#header_open|Default Operations#} <p>The following operators can cause integer overflow:</p> <ul> - <li><code>+</code> (addition)</li> - <li><code>-</code> (subtraction)</li> - <li><code>-</code> (negation)</li> - <li><code>*</code> (multiplication)</li> - <li><code>/</code> (division)</li> + <li>{#syntax#}+{#endsyntax#} (addition)</li> + <li>{#syntax#}-{#endsyntax#} (subtraction)</li> + <li>{#syntax#}-{#endsyntax#} (negation)</li> + <li>{#syntax#}*{#endsyntax#} (multiplication)</li> + <li>{#syntax#}/{#endsyntax#} (division)</li> <li>{#link|@divTrunc#} (division)</li> <li>{#link|@divFloor#} (division)</li> <li>{#link|@divExact#} (division)</li> @@ -6575,13 +6644,13 @@ pub fn main() void { {#header_open|Standard Library Math Functions#} <p>These functions provided by the standard library return possible errors.</p> <ul> - <li><code>@import("std").math.add</code></li> - <li><code>@import("std").math.sub</code></li> - <li><code>@import("std").math.mul</code></li> - <li><code>@import("std").math.divTrunc</code></li> - <li><code>@import("std").math.divFloor</code></li> - <li><code>@import("std").math.divExact</code></li> - <li><code>@import("std").math.shl</code></li> + <li>{#syntax#}@import("std").math.add{#endsyntax#}</li> + <li>{#syntax#}@import("std").math.sub{#endsyntax#}</li> + <li>{#syntax#}@import("std").math.mul{#endsyntax#}</li> + <li>{#syntax#}@import("std").math.divTrunc{#endsyntax#}</li> + <li>{#syntax#}@import("std").math.divFloor{#endsyntax#}</li> + <li>{#syntax#}@import("std").math.divExact{#endsyntax#}</li> + <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li> </ul> <p>Example of catching an overflow for addition:</p> {#code_begin|exe_err#} @@ -6601,7 +6670,7 @@ pub fn main() !void { {#header_close#} {#header_open|Builtin Overflow Functions#} <p> - These builtins return a <code>bool</code> of whether or not overflow + These builtins return a {#syntax#}bool{#endsyntax#} of whether or not overflow occurred, as well as returning the overflowed bits: </p> <ul> @@ -6632,10 +6701,10 @@ pub fn main() void { These operations have guaranteed wraparound semantics. </p> <ul> - <li><code>+%</code> (wraparound addition)</li> - <li><code>-%</code> (wraparound subtraction)</li> - <li><code>-%</code> (wraparound negation)</li> - <li><code>*%</code> (wraparound multiplication)</li> + <li>{#syntax#}+%{#endsyntax#} (wraparound addition)</li> + <li>{#syntax#}-%{#endsyntax#} (wraparound subtraction)</li> + <li>{#syntax#}-%{#endsyntax#} (wraparound negation)</li> + <li>{#syntax#}*%{#endsyntax#} (wraparound multiplication)</li> </ul> {#code_begin|test#} const assert = @import("std").debug.assert; @@ -6787,7 +6856,7 @@ pub fn main() void { } {#code_end#} <p>One way to avoid this crash is to test for null instead of assuming non-null, with - the <code>if</code> expression:</p> + the {#syntax#}if{#endsyntax#} expression:</p> {#code_begin|exe|test#} const warn = @import("std").debug.warn; pub fn main() void { @@ -6827,7 +6896,7 @@ fn getNumberOrFail() !i32 { } {#code_end#} <p>One way to avoid this crash is to test for an error instead of assuming a successful result, with - the <code>if</code> expression:</p> + the {#syntax#}if{#endsyntax#} expression:</p> {#code_begin|exe#} const warn = @import("std").debug.warn; @@ -6991,7 +7060,7 @@ fn bar(f: *Foo) void { } {#code_end#} <p> - This safety is not available for <code>extern</code> or <code>packed</code> unions. + This safety is not available for {#syntax#}extern{#endsyntax#} or {#syntax#}packed{#endsyntax#} unions. </p> <p> To change the active field of a union, assign the entire union, like this: @@ -7056,7 +7125,7 @@ fn bar(f: *Foo) void { {#header_close#} {#header_open|Compile Variables#} <p> - Compile variables are accessible by importing the <code>"builtin"</code> package, + Compile variables are accessible by importing the {#syntax#}"builtin"{#endsyntax#} package, which the compiler makes available to every Zig source file. It contains compile-time constants such as the current target, endianness, and release mode. </p> @@ -7065,7 +7134,7 @@ const builtin = @import("builtin"); const separator = if (builtin.os == builtin.Os.windows) '\\' else '/'; {#code_end#} <p> - Example of what is imported with <code>@import("builtin")</code>: + Example of what is imported with {#syntax#}@import("builtin"){#endsyntax#}: </p> {#builtin#} {#see_also|Build Mode#} @@ -7104,16 +7173,16 @@ const separator = if (builtin.os == builtin.Os.windows) '\\' else '/'; These have guaranteed C ABI compatibility and can be used like any other type. </p> <ul> - <li><code>c_short</code></li> - <li><code>c_ushort</code></li> - <li><code>c_int</code></li> - <li><code>c_uint</code></li> - <li><code>c_long</code></li> - <li><code>c_ulong</code></li> - <li><code>c_longlong</code></li> - <li><code>c_ulonglong</code></li> - <li><code>c_longdouble</code></li> - <li><code>c_void</code></li> + <li>{#syntax#}c_short{#endsyntax#}</li> + <li>{#syntax#}c_ushort{#endsyntax#}</li> + <li>{#syntax#}c_int{#endsyntax#}</li> + <li>{#syntax#}c_uint{#endsyntax#}</li> + <li>{#syntax#}c_long{#endsyntax#}</li> + <li>{#syntax#}c_ulong{#endsyntax#}</li> + <li>{#syntax#}c_longlong{#endsyntax#}</li> + <li>{#syntax#}c_ulonglong{#endsyntax#}</li> + <li>{#syntax#}c_longdouble{#endsyntax#}</li> + <li>{#syntax#}c_void{#endsyntax#}</li> </ul> {#see_also|Primitive Types#} {#header_close#} @@ -7135,7 +7204,7 @@ pub fn main() void { {#header_close#} {#header_open|Import from C Header File#} <p> - The <code>@cImport</code> builtin function can be used + The {#syntax#}@cImport{#endsyntax#} builtin function can be used to directly import symbols from .h files: </p> {#code_begin|exe#} @@ -7150,7 +7219,7 @@ pub fn main() void { } {#code_end#} <p> - The <code>@cImport</code> function takes an expression as a parameter. + The {#syntax#}@cImport{#endsyntax#} function takes an expression as a parameter. This expression is evaluated at compile-time and is used to control preprocessor directives and include multiple .h files: </p> @@ -7174,7 +7243,7 @@ const c = @cImport({ {#header_open|Exporting a C Library#} <p> One of the primary use cases for Zig is exporting a library with the C ABI for other programming languages - to call into. The <code>export</code> keyword in front of functions, variables, and types causes them to + to call into. The {#syntax#}export{#endsyntax#} keyword in front of functions, variables, and types causes them to be part of the library API: </p> <p class="file">mathtest.zig</p> @@ -7423,7 +7492,7 @@ Environments: coreclr opencl</code></pre> <p> - The Zig Standard Library (<code>@import("std")</code>) has architecture, environment, and operating sytsem + The Zig Standard Library ({#syntax#}@import("std"){#endsyntax#}) has architecture, environment, and operating sytsem abstractions, and thus takes additional work to support more platforms. Not all standard library code requires operating system abstractions, however, so things such as generic data structures work an all above platforms. @@ -7460,25 +7529,25 @@ coding style. {#header_close#} {#header_open|Names#} <p> - Roughly speaking: <code>camelCaseFunctionName</code>, <code>TitleCaseTypeName</code>, - <code>snake_case_variable_name</code>. More precisely: + Roughly speaking: {#syntax#}camelCaseFunctionName{#endsyntax#}, {#syntax#}TitleCaseTypeName{#endsyntax#}, + {#syntax#}snake_case_variable_name{#endsyntax#}. More precisely: </p> <ul> <li> - If <code>x</code> is a <code>struct</code> (or an alias of a <code>struct</code>), - then <code>x</code> should be <code>TitleCase</code>. + If {#syntax#}x{#endsyntax#} is a {#syntax#}struct{#endsyntax#} (or an alias of a {#syntax#}struct{#endsyntax#}), + then {#syntax#}x{#endsyntax#} should be {#syntax#}TitleCase{#endsyntax#}. </li> <li> - If <code>x</code> otherwise identifies a type, <code>x</code> should have <code>snake_case</code>. + If {#syntax#}x{#endsyntax#} otherwise identifies a type, {#syntax#}x{#endsyntax#} should have {#syntax#}snake_case{#endsyntax#}. </li> <li> - If <code>x</code> is callable, and <code>x</code>'s return type is <code>type</code>, then <code>x</code> should be <code>TitleCase</code>. + If {#syntax#}x{#endsyntax#} is callable, and {#syntax#}x{#endsyntax#}'s return type is {#syntax#}type{#endsyntax#}, then {#syntax#}x{#endsyntax#} should be {#syntax#}TitleCase{#endsyntax#}. </li> <li> - If <code>x</code> is otherwise callable, then <code>x</code> should be <code>camelCase</code>. + If {#syntax#}x{#endsyntax#} is otherwise callable, then {#syntax#}x{#endsyntax#} should be {#syntax#}camelCase{#endsyntax#}. </li> <li> - Otherwise, <code>x</code> should be <code>snake_case</code>. + Otherwise, {#syntax#}x{#endsyntax#} should be {#syntax#}snake_case{#endsyntax#}. </li> </ul> <p> @@ -7490,7 +7559,7 @@ coding style. <p> These are general rules of thumb; if it makes sense to do something different, do what makes sense. For example, if there is an established convention such as - <code>ENOENT</code>, follow the established convention. + {#syntax#}ENOENT{#endsyntax#}, follow the established convention. </p> {#header_close#} {#header_open|Examples#} @@ -7704,7 +7773,7 @@ ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" GroupedExpression = "(" Expression ")" -KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" | "suspend" +KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "unreachable" | "suspend" ErrorSetDecl = "error" "{" list(Symbol, ",") "}" @@ -7728,141 +7797,5 @@ ContainerDecl = option("extern" | "packed") </ul> {#header_close#} </div> - <script> -/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ -!function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return w(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||w(i))return i}function o(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset<r[0].offset?e:r:"start"===r[0].event?e:r:e.length?e:r}function o(e){function r(e){return" "+e.nodeName+'="'+n(e.value).replace('"',"&quot;")+'"'}s+="<"+t(e)+E.map.call(e.attributes,r).join("")+">"}function u(e){s+="</"+t(e)+">"}function c(e){("start"===e.event?o:u)(e.node)}for(var l=0,s="",f=[];e.length||r.length;){var g=i();if(s+=n(a.substring(l,g[0].offset)),l=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===l);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return s+n(a.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(n){return o(e,{v:null},n)})),e.cached_variants||e.eW&&[o(e)]||[e]}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var o={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");o[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):x(a.k).forEach(function(e){u(e,a.k[e])}),a.k=o}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]),a.c=Array.prototype.concat.apply([],a.c.map(function(e){return l("self"===e?a:e)})),a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var c=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=c.length?t(c.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function l(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":I.classPrefix,i='<span class="'+a,o=t?"":C;return i+=e+'">',i+n+o}function h(){var e,t,r,a;if(!E.k)return n(k);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(k);r;)a+=n(k.substring(t,r.index)),e=l(E,r),e?(B+=e[1],a+=p(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(k);return a+n(k.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!y[E.sL])return n(k);var t=e?f(E.sL,k,!0,x[E.sL]):g(k,E.sL.length?E.sL:void 0);return E.r>0&&(B+=t.r),e&&(x[E.sL]=t.top),p(t.language,t.value,!1,!0)}function b(){L+=null!=E.sL?d():h(),k=""}function v(e){L+=e.cN?p(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(k+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?k+=n:(t.eB&&(k+=n),b(),t.rB||t.eB||(k=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?k+=n:(a.rE||a.eE||(k+=n),b(),a.eE&&(k=n));do E.cN&&(L+=C),E.skip||(B+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"<unnamed>")+'"');return k+=n,n.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,E=i||N,x={},L="";for(R=E;R!==N;R=R.parent)R.cN&&(L=p(R.cN,"",!0)+L);var k="",B=0;try{for(var M,j,O=0;;){if(E.t.lastIndex=O,M=E.t.exec(t),!M)break;j=m(t.substring(O,M.index),M[0]),O=M.index+j}for(m(t.substr(O)),R=E;R.parent;R=R.parent)R.cN&&(L+=C);return{r:B,value:L,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function g(e,t){t=t||I.languages||x(y);var r={r:0,value:n(e)},a=r;return t.filter(w).forEach(function(n){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function p(e){return I.tabReplace||I.useBR?e.replace(M,function(e,n){return I.useBR&&"\n"===e?"<br>":I.tabReplace?n.replace(/\t/g,I.tabReplace):""}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function d(e){var n,t,r,o,l,s=i(e);a(s)||(I.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")):n=e,l=n.textContent,r=s?f(s,l,!0):g(l),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),l)),r.value=p(r.value),e.innerHTML=r.value,e.className=h(e.className,s,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function b(e){I=o(I,e)}function v(){if(!v.called){v.called=!0;var e=document.querySelectorAll("pre code");E.forEach.call(e,d)}}function m(){addEventListener("DOMContentLoaded",v,!1),addEventListener("load",v,!1)}function N(n,t){var r=y[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function R(){return x(y)}function w(e){return e=(e||"").toLowerCase(),y[e]||y[L[e]]}var E=[],x=Object.keys,y={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="</span>",I={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=f,e.highlightAuto=g,e.fixMarkup=p,e.highlightBlock=d,e.configure=b,e.initHighlighting=v,e.initHighlightingOnLoad=m,e.registerLanguage=N,e.listLanguages=R,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("cpp",function(t){var e={cN:"keyword",b:"\\b[a-z\\d_]*_t\\b"},r={cN:"string",v:[{b:'(u8?|U)?L?"',e:'"',i:"\\n",c:[t.BE]},{b:'(u8?|U)?R"',e:'"',c:[t.BE]},{b:"'\\\\?.",e:"'",i:"."}]},s={cN:"number",v:[{b:"\\b(0b[01']+)"},{b:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{b:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],r:0},i={cN:"meta",b:/#\s*[a-z]+\b/,e:/$/,k:{"meta-keyword":"if else elif endif define undef warning error line pragma ifdef ifndef include"},c:[{b:/\\\n/,r:0},t.inherit(r,{cN:"meta-string"}),{cN:"meta-string",b:/<[^\n>]*>/,e:/$/,i:"\\n"},t.CLCM,t.CBCM]},a=t.IR+"\\s*\\(",c={keyword:"int float while private char catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and or not",built_in:"std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr",literal:"true false nullptr NULL"},n=[e,t.CLCM,t.CBCM,s,r];return{aliases:["c","cc","h","c++","h++","hpp"],k:c,i:"</",c:n.concat([i,{b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:c,c:["self",e]},{b:t.IR+"::",k:c},{v:[{b:/=/,e:/;/},{b:/\(/,e:/\)/},{bK:"new throw return else",e:/;/}],k:c,c:n.concat([{b:/\(/,e:/\)/,k:c,c:n.concat(["self"]),r:0}]),r:0},{cN:"function",b:"("+t.IR+"[\\*&\\s]+)+"+a,rB:!0,e:/[{;=]/,eE:!0,k:c,i:/[^\w\s\*&]/,c:[{b:a,rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:c,r:0,c:[t.CLCM,t.CBCM,r,s,e]},t.CLCM,t.CBCM,i]},{cN:"class",bK:"class struct",e:/[{;:]/,c:[{b:/</,e:/>/,c:["self"]},t.TM]}]),exports:{preprocessor:i,strings:r,k:c}}});hljs.registerLanguage("llvm",function(e){var n="([-a-zA-Z$._][\\w\\-$.]*)";return{k:"begin end true false declare define global constant private linker_private internal available_externally linkonce linkonce_odr weak weak_odr appending dllimport dllexport common default hidden protected extern_weak external thread_local zeroinitializer undef null to tail target triple datalayout volatile nuw nsw nnan ninf nsz arcp fast exact inbounds align addrspace section alias module asm sideeffect gc dbg linker_private_weak attributes blockaddress initialexec localdynamic localexec prefix unnamed_addr ccc fastcc coldcc x86_stdcallcc x86_fastcallcc arm_apcscc arm_aapcscc arm_aapcs_vfpcc ptx_device ptx_kernel intel_ocl_bicc msp430_intrcc spir_func spir_kernel x86_64_sysvcc x86_64_win64cc x86_thiscallcc cc c signext zeroext inreg sret nounwind noreturn noalias nocapture byval nest readnone readonly inlinehint noinline alwaysinline optsize ssp sspreq noredzone noimplicitfloat naked builtin cold nobuiltin noduplicate nonlazybind optnone returns_twice sanitize_address sanitize_memory sanitize_thread sspstrong uwtable returned type opaque eq ne slt sgt sle sge ult ugt ule uge oeq one olt ogt ole oge ord uno ueq une x acq_rel acquire alignstack atomic catch cleanup filter inteldialect max min monotonic nand personality release seq_cst singlethread umax umin unordered xchg add fadd sub fsub mul fmul udiv sdiv fdiv urem srem frem shl lshr ashr and or xor icmp fcmp phi call trunc zext sext fptrunc fpext uitofp sitofp fptoui fptosi inttoptr ptrtoint bitcast addrspacecast select va_arg ret br switch invoke unwind unreachable indirectbr landingpad resume malloc alloca free load store getelementptr extractelement insertelement shufflevector getresult extractvalue insertvalue atomicrmw cmpxchg fence argmemonly double",c:[{cN:"keyword",b:"i\\d+"},e.C(";","\\n",{r:0}),e.QSM,{cN:"string",v:[{b:'"',e:'[^\\\\]"'}],r:0},{cN:"title",v:[{b:"@"+n},{b:"@\\d+"},{b:"!"+n},{b:"!\\d+"+n}]},{cN:"symbol",v:[{b:"%"+n},{b:"%\\d+"},{b:"#\\d+"}]},{cN:"number",v:[{b:"0[xX][a-fA-F0-9]+"},{b:"-?\\d+(?:[.]\\d+)?(?:[eE][-+]?\\d+(?:[.]\\d+)?)?"}],r:0}]}});hljs.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},s={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/\b-?[a-z\._]+\b/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"meta",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,s,a,t]}});hljs.registerLanguage("shell",function(s){return{aliases:["console"],c:[{cN:"meta",b:"^\\s{0,3}[\\w\\d\\[\\]()@-]*[>%$#]",starts:{e:"$",sL:"bash"}}]}}); - </script> - <script> -hljs.registerLanguage("zig", function(t) { - var e = { - cN: "keyword", - b: "\\b[a-z\\d_]*_t\\b" - }, - r = { - cN: "string", - v: [{ - b: '(u8?|U)?L?"', - e: '"', - i: "\\n", - c: [t.BE] - }, { - b: '(u8?|U)?R"', - e: '"', - c: [t.BE] - }, { - b: "'\\\\?.", - e: "'", - i: "." - }] - }, - s = { - cN: "number", - v: [{ - b: "\\b(0b[01']+)" - }, { - b: "(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)" - }, { - b: "(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" - }], - r: 0 - }, - i = { - cN: "meta", - b: /#\s*[a-z]+\b/, - e: /$/, - k: { - "meta-keyword": "zzzzzzdisable" - }, - c: [{ - b: /\\\n/, - r: 0 - }, t.inherit(r, { - cN: "meta-string" - }), { - cN: "meta-string", - b: /<[^\n>]*>/, - e: /$/, - i: "\\n" - }, t.CLCM, t.CBCM] - }, - a = t.IR + "\\s*\\(", - c = { - keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume suspend cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz popCount import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum handle", - literal: "true false null undefined" - }, - n = [e, t.CLCM, t.CBCM, s, r]; - return { - aliases: ["c", "cc", "h", "c++", "h++", "hpp"], - k: c, - i: "</", - c: n.concat([i, { - b: "\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<", - e: ">", - k: c, - c: ["self", e] - }, { - b: t.IR + "::", - k: c - }, { - v: [{ - b: /=/, - e: /;/ - }, { - b: /\(/, - e: /\)/ - }, { - bK: "new throw return else", - e: /;/ - }], - k: c, - c: n.concat([{ - b: /\(/, - e: /\)/, - k: c, - c: n.concat(["self"]), - r: 0 - }]), - r: 0 - }, { - cN: "function", - b: "(" + t.IR + "[\\*&\\s]+)+" + a, - rB: !0, - e: /[{;=]/, - eE: !0, - k: c, - i: /[^\w\s\*&]/, - c: [{ - b: a, - rB: !0, - c: [t.TM], - r: 0 - }, { - cN: "params", - b: /\(/, - e: /\)/, - k: c, - r: 0, - c: [t.CLCM, t.CBCM, r, s, e] - }, t.CLCM, t.CBCM, i] - }, { - cN: "class", - bK: "class struct", - e: /[{;:]/, - c: [{ - b: /</, - e: />/, - c: ["self"] - }, t.TM] - }]), - exports: { - preprocessor: i, - strings: r, - k: c - } - } -}); - hljs.initHighlightingOnLoad(); - </script> </body> </html> diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig @@ -737,7 +737,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void { file_path, max_src_size, )) catch |err| switch (err) { - error.IsDir => { + error.IsDir, error.AccessDenied => { // TODO make event based (and dir.next()) var dir = try std.os.Dir.open(fmt.loop.allocator, file_path); defer dir.close(); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig @@ -40,7 +40,6 @@ pub const Type = struct { Id.Enum => @fieldParentPtr(Enum, "base", base).destroy(comp), Id.Union => @fieldParentPtr(Union, "base", base).destroy(comp), Id.Namespace => @fieldParentPtr(Namespace, "base", base).destroy(comp), - Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp), Id.BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(comp), Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(comp), Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp), @@ -74,7 +73,6 @@ pub const Type = struct { Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context), Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context), Id.Namespace => unreachable, - Id.Block => unreachable, Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context), Id.ArgTuple => unreachable, Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context), @@ -90,7 +88,6 @@ pub const Type = struct { Id.Undefined, Id.Null, Id.Namespace, - Id.Block, Id.BoundFn, Id.ArgTuple, Id.Opaque, @@ -124,7 +121,6 @@ pub const Type = struct { Id.Undefined, Id.Null, Id.Namespace, - Id.Block, Id.BoundFn, Id.ArgTuple, Id.Opaque, @@ -1012,14 +1008,6 @@ pub const Type = struct { } }; - pub const Block = struct { - base: Type, - - pub fn destroy(self: *Block, comp: *Compilation) void { - comp.gpa().destroy(self); - } - }; - pub const BoundFn = struct { base: Type, diff --git a/src/all_types.hpp b/src/all_types.hpp @@ -10,6 +10,7 @@ #include "list.hpp" #include "buffer.hpp" +#include "cache_hash.hpp" #include "zig_llvm.h" #include "hash_map.hpp" #include "errmsg.hpp" @@ -282,7 +283,6 @@ struct ConstExprValue { ConstArrayValue x_array; ConstPtrValue x_ptr; ImportTableEntry *x_import; - Scope *x_block; ConstArgTuple x_arg_tuple; // populated if special == ConstValSpecialRuntime @@ -412,7 +412,6 @@ enum NodeType { NodeTypeBoolLiteral, NodeTypeNullLiteral, NodeTypeUndefinedLiteral, - NodeTypeThisLiteral, NodeTypeUnreachable, NodeTypeIfBoolExpr, NodeTypeWhileExpr, @@ -1013,13 +1012,13 @@ enum PtrLen { struct ZigTypePointer { ZigType *child_type; + ZigType *slice_parent; PtrLen ptr_len; - bool is_const; - bool is_volatile; - uint32_t alignment; + uint32_t explicit_alignment; // 0 means use ABI alignment uint32_t bit_offset; uint32_t unaligned_bit_count; - ZigType *slice_parent; + bool is_const; + bool is_volatile; }; struct ZigTypeInt { @@ -1047,32 +1046,35 @@ struct TypeStructField { size_t unaligned_bit_count; AstNode *decl_node; }; + +enum ResolveStatus { + ResolveStatusUnstarted, + ResolveStatusInvalid, + ResolveStatusZeroBitsKnown, + ResolveStatusAlignmentKnown, + ResolveStatusSizeKnown, +}; + struct ZigTypeStruct { AstNode *decl_node; - ContainerLayout layout; - uint32_t src_field_count; - uint32_t gen_field_count; TypeStructField *fields; - uint64_t size_bytes; - bool is_invalid; // true if any fields are invalid - bool is_slice; ScopeDecls *decls_scope; + uint64_t size_bytes; + HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name; - // set this flag temporarily to detect infinite loops - bool embedded_in_current; - bool reported_infinite_err; - // whether we've finished resolving it - bool complete; + uint32_t src_field_count; + uint32_t gen_field_count; + + uint32_t abi_alignment; // known after ResolveStatusAlignmentKnown + ContainerLayout layout; + ResolveStatus resolve_status; + bool is_slice; + bool resolve_loop_flag; // set this flag temporarily to detect infinite loops + bool reported_infinite_err; // whether any of the fields require comptime - // the value is not valid until zero_bits_known == true + // known after ResolveStatusZeroBitsKnown bool requires_comptime; - - bool zero_bits_loop_flag; - bool zero_bits_known; - uint32_t abi_alignment; // also figured out with zero_bits pass - - HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name; }; struct ZigTypeOptional { @@ -1204,7 +1206,6 @@ enum ZigTypeId { ZigTypeIdUnion, ZigTypeIdFn, ZigTypeIdNamespace, - ZigTypeIdBlock, ZigTypeIdBoundFn, ZigTypeIdArgTuple, ZigTypeIdOpaque, @@ -1412,6 +1413,7 @@ enum BuiltinFnId { BuiltinFnIdSetEvalBranchQuota, BuiltinFnIdAlignCast, BuiltinFnIdOpaqueType, + BuiltinFnIdThis, BuiltinFnIdSetAlignStack, BuiltinFnIdArgType, BuiltinFnIdExport, @@ -1550,22 +1552,50 @@ struct LinkLib { bool provided_explicitly; }; +// When adding fields, check if they should be added to the hash computation in build_with_cache struct CodeGen { + //////////////////////////// Runtime State LLVMModuleRef module; ZigList<ErrorMsg*> errors; LLVMBuilderRef builder; ZigLLVMDIBuilder *dbuilder; ZigLLVMDICompileUnit *compile_unit; ZigLLVMDIFile *compile_unit_file; - - ZigList<LinkLib *> link_libs_list; LinkLib *libc_link_lib; - - // add -framework [name] args to linker - ZigList<Buf *> darwin_frameworks; - // add -rpath [name] args to linker - ZigList<Buf *> rpath_list; - + LLVMTargetDataRef target_data_ref; + LLVMTargetMachineRef target_machine; + ZigLLVMDIFile *dummy_di_file; + LLVMValueRef cur_ret_ptr; + LLVMValueRef cur_fn_val; + LLVMValueRef cur_err_ret_trace_val_arg; + LLVMValueRef cur_err_ret_trace_val_stack; + LLVMValueRef memcpy_fn_val; + LLVMValueRef memset_fn_val; + LLVMValueRef trap_fn_val; + LLVMValueRef return_address_fn_val; + LLVMValueRef frame_address_fn_val; + LLVMValueRef coro_destroy_fn_val; + LLVMValueRef coro_id_fn_val; + LLVMValueRef coro_alloc_fn_val; + LLVMValueRef coro_size_fn_val; + LLVMValueRef coro_begin_fn_val; + LLVMValueRef coro_suspend_fn_val; + LLVMValueRef coro_end_fn_val; + LLVMValueRef coro_free_fn_val; + LLVMValueRef coro_resume_fn_val; + LLVMValueRef coro_save_fn_val; + LLVMValueRef coro_promise_fn_val; + LLVMValueRef coro_alloc_helper_fn_val; + LLVMValueRef coro_frame_fn_val; + LLVMValueRef merge_err_ret_traces_fn_val; + LLVMValueRef add_error_return_trace_addr_fn_val; + LLVMValueRef stacksave_fn_val; + LLVMValueRef stackrestore_fn_val; + LLVMValueRef write_register_fn_val; + LLVMValueRef sp_md_node; + LLVMValueRef err_name_table; + LLVMValueRef safety_crash_err_fn; + LLVMValueRef return_err_fn; // reminder: hash tables must be initialized before use HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table; @@ -1582,15 +1612,29 @@ struct CodeGen { HashMap<Buf *, ConstExprValue *, buf_hash, buf_eql_buf> string_literals_table; HashMap<const ZigType *, ConstExprValue *, type_ptr_hash, type_ptr_eql> type_info_cache; - ZigList<ImportTableEntry *> import_queue; size_t import_queue_index; ZigList<Tld *> resolve_queue; size_t resolve_queue_index; ZigList<AstNode *> use_queue; size_t use_queue_index; + ZigList<TimeEvent> timing_events; + ZigList<ZigLLVMDIType **> error_di_types; + ZigList<AstNode *> tld_ref_source_node_stack; + ZigList<ZigFn *> inline_fns; + ZigList<ZigFn *> test_fns; + ZigList<ZigLLVMDIEnumerator *> err_enumerators; + ZigList<ErrorTableEntry *> errors_by_index; + size_t largest_err_name_len; - uint32_t next_unresolved_index; + PackageTableEntry *std_package; + PackageTableEntry *panic_package; + PackageTableEntry *test_runner_package; + PackageTableEntry *compile_var_package; + ImportTableEntry *compile_var_import; + ImportTableEntry *root_import; + ImportTableEntry *bootstrap_import; + ImportTableEntry *test_runner_import; struct { ZigType *entry_bool; @@ -1626,163 +1670,122 @@ struct CodeGen { ZigType *entry_arg_tuple; ZigType *entry_promise; } builtin_types; + ZigType *align_amt_type; + ZigType *stack_trace_type; + ZigType *ptr_to_stack_trace_type; + ZigType *err_tag_type; + ZigType *test_fn_type; - EmitFileType emit_file_type; - ZigTarget zig_target; - LLVMTargetDataRef target_data_ref; - unsigned pointer_size_bytes; - bool is_big_endian; - bool is_static; - bool strip_debug_symbols; - bool want_h_file; - bool have_pub_main; - bool have_c_main; - bool have_winmain; - bool have_winmain_crt_startup; - bool have_dllmain_crt_startup; - bool have_pub_panic; - Buf *libc_lib_dir; - Buf *libc_static_lib_dir; - Buf *libc_include_dir; - Buf *msvc_lib_dir; - Buf *kernel32_lib_dir; - Buf *zig_lib_dir; - Buf *zig_std_dir; - Buf *zig_c_headers_dir; - Buf *zig_std_special_dir; - Buf *dynamic_linker; - Buf *ar_path; - ZigWindowsSDK *win_sdk; Buf triple_str; - BuildMode build_mode; - bool is_test_build; - bool have_err_ret_tracing; - uint32_t target_os_index; - uint32_t target_arch_index; - uint32_t target_environ_index; - uint32_t target_oformat_index; - LLVMTargetMachineRef target_machine; - ZigLLVMDIFile *dummy_di_file; - bool is_native_target; - PackageTableEntry *root_package; - PackageTableEntry *std_package; - PackageTableEntry *panic_package; - PackageTableEntry *test_runner_package; - PackageTableEntry *compile_var_package; - ImportTableEntry *compile_var_import; - Buf *root_out_name; - bool windows_subsystem_windows; - bool windows_subsystem_console; - Buf *mmacosx_version_min; - Buf *mios_version_min; - bool linker_rdynamic; - const char *linker_script; + Buf global_asm; + Buf *out_h_path; + Buf artifact_dir; + Buf output_file_path; + Buf o_file_output_path; + Buf *wanted_output_file_path; + Buf cache_dir; + + IrInstruction *invalid_instruction; + + ConstExprValue const_void_val; + ConstExprValue panic_msg_vals[PanicMsgIdCount]; // The function definitions this module includes. ZigList<ZigFn *> fn_defs; size_t fn_defs_index; ZigList<TldVar *> global_vars; - OutType out_type; ZigFn *cur_fn; ZigFn *main_fn; ZigFn *panic_fn; - LLVMValueRef cur_ret_ptr; - LLVMValueRef cur_fn_val; - LLVMValueRef cur_err_ret_trace_val_arg; - LLVMValueRef cur_err_ret_trace_val_stack; + AstNode *root_export_decl; + + CacheHash cache_hash; + ErrColor err_color; + uint32_t next_unresolved_index; + unsigned pointer_size_bytes; + uint32_t target_os_index; + uint32_t target_arch_index; + uint32_t target_environ_index; + uint32_t target_oformat_index; + bool is_big_endian; + bool want_h_file; + bool have_pub_main; + bool have_c_main; + bool have_winmain; + bool have_winmain_crt_startup; + bool have_dllmain_crt_startup; + bool have_pub_panic; + bool have_err_ret_tracing; bool c_want_stdint; bool c_want_stdbool; - AstNode *root_export_decl; - size_t version_major; - size_t version_minor; - size_t version_patch; bool verbose_tokenize; bool verbose_ast; bool verbose_link; bool verbose_ir; bool verbose_llvm_ir; bool verbose_cimport; - ErrColor err_color; - ImportTableEntry *root_import; - ImportTableEntry *bootstrap_import; - ImportTableEntry *test_runner_import; - LLVMValueRef trap_fn_val; - LLVMValueRef return_address_fn_val; - LLVMValueRef frame_address_fn_val; - LLVMValueRef coro_destroy_fn_val; - LLVMValueRef coro_id_fn_val; - LLVMValueRef coro_alloc_fn_val; - LLVMValueRef coro_size_fn_val; - LLVMValueRef coro_begin_fn_val; - LLVMValueRef coro_suspend_fn_val; - LLVMValueRef coro_end_fn_val; - LLVMValueRef coro_free_fn_val; - LLVMValueRef coro_resume_fn_val; - LLVMValueRef coro_save_fn_val; - LLVMValueRef coro_promise_fn_val; - LLVMValueRef coro_alloc_helper_fn_val; - LLVMValueRef coro_frame_fn_val; - LLVMValueRef merge_err_ret_traces_fn_val; - LLVMValueRef add_error_return_trace_addr_fn_val; - LLVMValueRef stacksave_fn_val; - LLVMValueRef stackrestore_fn_val; - LLVMValueRef write_register_fn_val; bool error_during_imports; + bool generate_error_name_table; + bool enable_cache; + bool enable_time_report; - LLVMValueRef sp_md_node; - - const char **clang_argv; - size_t clang_argv_len; + //////////////////////////// Participates in Input Parameter Cache Hash + ZigList<LinkLib *> link_libs_list; + // add -framework [name] args to linker + ZigList<Buf *> darwin_frameworks; + // add -rpath [name] args to linker + ZigList<Buf *> rpath_list; + ZigList<Buf *> forbidden_libs; + ZigList<Buf *> link_objects; + ZigList<Buf *> assembly_files; ZigList<const char *> lib_dirs; - const char **llvm_argv; - size_t llvm_argv_len; - - ZigList<ZigFn *> test_fns; - ZigType *test_fn_type; + size_t version_major; + size_t version_minor; + size_t version_patch; + const char *linker_script; + EmitFileType emit_file_type; + BuildMode build_mode; + OutType out_type; + ZigTarget zig_target; + bool is_static; + bool strip_debug_symbols; + bool is_test_build; + bool is_native_target; + bool windows_subsystem_windows; + bool windows_subsystem_console; + bool linker_rdynamic; + bool no_rosegment_workaround; bool each_lib_rpath; - ZigType *err_tag_type; - ZigList<ZigLLVMDIEnumerator *> err_enumerators; - ZigList<ErrorTableEntry *> errors_by_index; - bool generate_error_name_table; - LLVMValueRef err_name_table; - size_t largest_err_name_len; - LLVMValueRef safety_crash_err_fn; - - LLVMValueRef return_err_fn; - - IrInstruction *invalid_instruction; - ConstExprValue const_void_val; - - ConstExprValue panic_msg_vals[PanicMsgIdCount]; - - Buf global_asm; - ZigList<Buf *> link_objects; - ZigList<Buf *> assembly_files; - + Buf *mmacosx_version_min; + Buf *mios_version_min; + Buf *root_out_name; Buf *test_filter; Buf *test_name_prefix; + PackageTableEntry *root_package; - ZigList<TimeEvent> timing_events; - - Buf cache_dir; - Buf *out_h_path; - - ZigList<ZigFn *> inline_fns; - ZigList<AstNode *> tld_ref_source_node_stack; - - ZigType *align_amt_type; - ZigType *stack_trace_type; - ZigType *ptr_to_stack_trace_type; + const char **llvm_argv; + size_t llvm_argv_len; - ZigList<ZigLLVMDIType **> error_di_types; + const char **clang_argv; + size_t clang_argv_len; - ZigList<Buf *> forbidden_libs; + //////////////////////////// Unsorted - bool no_rosegment_workaround; + Buf *libc_lib_dir; + Buf *libc_static_lib_dir; + Buf *libc_include_dir; + Buf *msvc_lib_dir; + Buf *kernel32_lib_dir; + Buf *zig_lib_dir; + Buf *zig_std_dir; + Buf *zig_c_headers_dir; + Buf *zig_std_special_dir; + Buf *dynamic_linker; + ZigWindowsSDK *win_sdk; }; enum VarLinkage { @@ -3285,8 +3288,8 @@ static const size_t stack_trace_ptr_count = 30; enum FloatMode { - FloatModeOptimized, FloatModeStrict, + FloatModeOptimized, }; enum FnWalkId { diff --git a/src/analyze.cpp b/src/analyze.cpp @@ -23,6 +23,7 @@ static Error resolve_enum_type(CodeGen *g, ZigType *enum_type); static Error resolve_struct_type(CodeGen *g, ZigType *struct_type); static Error ATTRIBUTE_MUST_USE resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type); +static Error ATTRIBUTE_MUST_USE resolve_struct_alignment(CodeGen *g, ZigType *struct_type); static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type); static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type); static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry); @@ -246,7 +247,6 @@ AstNode *type_decl_node(ZigType *type_entry) { case ZigTypeIdErrorSet: case ZigTypeIdFn: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: @@ -255,18 +255,42 @@ AstNode *type_decl_node(ZigType *type_entry) { zig_unreachable(); } -bool type_is_complete(ZigType *type_entry) { +bool type_is_resolved(ZigType *type_entry, ResolveStatus status) { switch (type_entry->id) { case ZigTypeIdInvalid: zig_unreachable(); case ZigTypeIdStruct: - return type_entry->data.structure.complete; + return type_entry->data.structure.resolve_status >= status; case ZigTypeIdEnum: - return type_entry->data.enumeration.complete; + switch (status) { + case ResolveStatusUnstarted: + return true; + case ResolveStatusInvalid: + zig_unreachable(); + case ResolveStatusZeroBitsKnown: + return type_entry->data.enumeration.zero_bits_known; + case ResolveStatusAlignmentKnown: + return type_entry->data.enumeration.zero_bits_known; + case ResolveStatusSizeKnown: + return type_entry->data.enumeration.complete; + } + zig_unreachable(); case ZigTypeIdUnion: - return type_entry->data.unionation.complete; + switch (status) { + case ResolveStatusUnstarted: + return true; + case ResolveStatusInvalid: + zig_unreachable(); + case ResolveStatusZeroBitsKnown: + return type_entry->data.unionation.zero_bits_known; + case ResolveStatusAlignmentKnown: + return type_entry->data.unionation.zero_bits_known; + case ResolveStatusSizeKnown: + return type_entry->data.unionation.complete; + } + zig_unreachable(); case ZigTypeIdOpaque: - return false; + return status < ResolveStatusSizeKnown; case ZigTypeIdMetaType: case ZigTypeIdVoid: case ZigTypeIdBool: @@ -284,7 +308,6 @@ bool type_is_complete(ZigType *type_entry) { case ZigTypeIdErrorSet: case ZigTypeIdFn: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: @@ -293,44 +316,10 @@ bool type_is_complete(ZigType *type_entry) { zig_unreachable(); } -bool type_has_zero_bits_known(ZigType *type_entry) { - switch (type_entry->id) { - case ZigTypeIdInvalid: - zig_unreachable(); - case ZigTypeIdStruct: - return type_entry->data.structure.zero_bits_known; - case ZigTypeIdEnum: - return type_entry->data.enumeration.zero_bits_known; - case ZigTypeIdUnion: - return type_entry->data.unionation.zero_bits_known; - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdUnreachable: - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdArray: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdOptional: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdFn: - case ZigTypeIdNamespace: - case ZigTypeIdBlock: - case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: - case ZigTypeIdOpaque: - case ZigTypeIdPromise: - return true; - } - zig_unreachable(); +bool type_is_complete(ZigType *type_entry) { + return type_is_resolved(type_entry, ResolveStatusSizeKnown); } - uint64_t type_size(CodeGen *g, ZigType *type_entry) { assert(type_is_complete(type_entry)); @@ -379,7 +368,7 @@ uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) { Result<bool> type_is_copyable(CodeGen *g, ZigType *type_entry) { Error err; - if ((err = type_ensure_zero_bits_known(g, type_entry))) + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) return err; if (!type_has_bits(type_entry)) @@ -434,10 +423,15 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons assert(!type_is_invalid(child_type)); assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque); + if (byte_alignment != 0) { + uint32_t abi_alignment = get_abi_alignment(g, child_type); + if (byte_alignment == abi_alignment) + byte_alignment = 0; + } + TypeId type_id = {}; ZigType **parent_pointer = nullptr; - uint32_t abi_alignment = get_abi_alignment(g, child_type); - if (unaligned_bit_count != 0 || is_volatile || byte_alignment != abi_alignment || ptr_len != PtrLenSingle) { + if (unaligned_bit_count != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle) { type_id.id = ZigTypeIdPointer; type_id.data.pointer.child_type = child_type; type_id.data.pointer.is_const = is_const; @@ -454,12 +448,12 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons assert(bit_offset == 0); parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)]; if (*parent_pointer) { - assert((*parent_pointer)->data.pointer.alignment == byte_alignment); + assert((*parent_pointer)->data.pointer.explicit_alignment == 0); return *parent_pointer; } } - assertNoError(type_ensure_zero_bits_known(g, child_type)); + assert(type_is_resolved(child_type, ResolveStatusZeroBitsKnown)); ZigType *entry = new_type_table_entry(ZigTypeIdPointer); entry->is_copyable = true; @@ -468,11 +462,14 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons const char *const_str = is_const ? "const " : ""; const char *volatile_str = is_volatile ? "volatile " : ""; buf_resize(&entry->name, 0); - if (unaligned_bit_count == 0 && byte_alignment == abi_alignment) { + if (unaligned_bit_count == 0 && byte_alignment == 0) { buf_appendf(&entry->name, "%s%s%s%s", star_str, const_str, volatile_str, buf_ptr(&child_type->name)); } else if (unaligned_bit_count == 0) { buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s", star_str, byte_alignment, const_str, volatile_str, buf_ptr(&child_type->name)); + } else if (byte_alignment == 0) { + buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str, + bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name)); } else { buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str, byte_alignment, bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name)); @@ -483,8 +480,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons entry->zero_bits = !type_has_bits(child_type); if (!entry->zero_bits) { - assert(byte_alignment > 0); - if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != abi_alignment || + if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != 0 || ptr_len != PtrLenSingle) { ZigType *peer_type = get_pointer_to_type(g, child_type, false); @@ -508,7 +504,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons entry->data.pointer.child_type = child_type; entry->data.pointer.is_const = is_const; entry->data.pointer.is_volatile = is_volatile; - entry->data.pointer.alignment = byte_alignment; + entry->data.pointer.explicit_alignment = byte_alignment; entry->data.pointer.bit_offset = bit_offset; entry->data.pointer.unaligned_bit_count = unaligned_bit_count; @@ -521,8 +517,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons } ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) { - return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, - get_abi_alignment(g, child_type), 0, 0); + return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0); } ZigType *get_promise_frame_type(CodeGen *g, ZigType *return_type) { @@ -803,8 +798,7 @@ static void slice_type_common_init(CodeGen *g, ZigType *pointer_type, ZigType *e entry->data.structure.fields_by_name.put(ptr_field_name, &entry->data.structure.fields[slice_ptr_index]); entry->data.structure.fields_by_name.put(len_field_name, &entry->data.structure.fields[slice_len_index]); - assert(type_has_zero_bits_known(pointer_type->data.pointer.child_type)); - if (pointer_type->data.pointer.child_type->zero_bits) { + if (!type_has_bits(pointer_type->data.pointer.child_type)) { entry->data.structure.gen_field_count = 1; entry->data.structure.fields[slice_ptr_index].gen_index = SIZE_MAX; entry->data.structure.fields[slice_len_index].gen_index = 0; @@ -829,20 +823,18 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + name_offset); ZigType *child_type = ptr_type->data.pointer.child_type; - uint32_t abi_alignment = get_abi_alignment(g, child_type); if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile || - ptr_type->data.pointer.alignment != abi_alignment) + ptr_type->data.pointer.explicit_alignment != 0) { ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false, - PtrLenUnknown, abi_alignment, 0, 0); + PtrLenUnknown, 0, 0, 0); ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type); slice_type_common_init(g, ptr_type, entry); entry->type_ref = peer_slice_type->type_ref; entry->di_type = peer_slice_type->di_type; - entry->data.structure.complete = true; - entry->data.structure.zero_bits_known = true; + entry->data.structure.resolve_status = ResolveStatusSizeKnown; entry->data.structure.abi_alignment = peer_slice_type->data.structure.abi_alignment; *parent_pointer = entry; @@ -854,15 +846,15 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { if (is_slice(child_type)) { ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry; assert(child_ptr_type->id == ZigTypeIdPointer); - ZigType *grand_child_type = child_ptr_type->data.pointer.child_type; if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile || - child_ptr_type->data.pointer.alignment != get_abi_alignment(g, grand_child_type)) + child_ptr_type->data.pointer.explicit_alignment != 0) { + ZigType *grand_child_type = child_ptr_type->data.pointer.child_type; ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false, - PtrLenUnknown, get_abi_alignment(g, grand_child_type), 0, 0); + PtrLenUnknown, 0, 0, 0); ZigType *bland_child_slice = get_slice_type(g, bland_child_ptr_type); ZigType *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false, - PtrLenUnknown, get_abi_alignment(g, bland_child_slice), 0, 0); + PtrLenUnknown, 0, 0, 0); ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type); entry->type_ref = peer_slice_type->type_ref; @@ -964,8 +956,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { } - entry->data.structure.complete = true; - entry->data.structure.zero_bits_known = true; + entry->data.structure.resolve_status = ResolveStatusSizeKnown; *parent_pointer = entry; return entry; @@ -1370,7 +1361,7 @@ static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) { ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, - PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); + PtrLenUnknown, 0, 0, 0); ZigType *str_type = get_slice_type(g, ptr_type); IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr); if (type_is_invalid(instr->value.type)) @@ -1414,7 +1405,6 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) { case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: @@ -1455,7 +1445,6 @@ static bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) { case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: @@ -1581,7 +1570,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc return g->builtin_types.entry_invalid; } if (!calling_convention_allows_zig_types(fn_type_id.cc)) { - if ((err = type_ensure_zero_bits_known(g, type_entry))) + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) return g->builtin_types.entry_invalid; if (!type_has_bits(type_entry)) { add_node_error(g, param_node->data.param_decl.type, @@ -1613,7 +1602,6 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdMetaType: case ZigTypeIdVoid: @@ -1630,7 +1618,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdPromise: - if ((err = type_ensure_zero_bits_known(g, type_entry))) + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) return g->builtin_types.entry_invalid; if (type_requires_comptime(type_entry)) { add_node_error(g, param_node->data.param_decl.type, @@ -1703,7 +1691,6 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdMetaType: case ZigTypeIdUnreachable: @@ -1721,7 +1708,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdPromise: - if ((err = type_ensure_zero_bits_known(g, fn_type_id.return_type))) + if ((err = type_resolve(g, fn_type_id.return_type, ResolveStatusZeroBitsKnown))) return g->builtin_types.entry_invalid; if (type_requires_comptime(fn_type_id.return_type)) { return get_generic_fn_type(g, &fn_type_id); @@ -1747,7 +1734,7 @@ bool type_is_invalid(ZigType *type_entry) { case ZigTypeIdInvalid: return true; case ZigTypeIdStruct: - return type_entry->data.structure.is_invalid; + return type_entry->data.structure.resolve_status == ResolveStatusInvalid; case ZigTypeIdEnum: return type_entry->data.enumeration.is_invalid; case ZigTypeIdUnion: @@ -1862,8 +1849,7 @@ ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_na struct_type->data.structure.src_field_count = field_count; struct_type->data.structure.gen_field_count = 0; - struct_type->data.structure.zero_bits_known = true; - struct_type->data.structure.complete = true; + struct_type->data.structure.resolve_status = ResolveStatusSizeKnown; struct_type->data.structure.fields = allocate<TypeStructField>(field_count); struct_type->data.structure.fields_by_name.init(field_count); @@ -1935,26 +1921,29 @@ ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_na static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { assert(struct_type->id == ZigTypeIdStruct); - if (struct_type->data.structure.complete) + Error err; + + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + if (struct_type->data.structure.resolve_status >= ResolveStatusSizeKnown) return ErrorNone; - Error err; - if ((err = resolve_struct_zero_bits(g, struct_type))) + if ((err = resolve_struct_alignment(g, struct_type))) return err; AstNode *decl_node = struct_type->data.structure.decl_node; - if (struct_type->data.structure.embedded_in_current) { - struct_type->data.structure.is_invalid = true; - if (!struct_type->data.structure.reported_infinite_err) { - struct_type->data.structure.reported_infinite_err = true; + if (struct_type->data.structure.resolve_loop_flag) { + if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; add_node_error(g, decl_node, - buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name))); + buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name))); } return ErrorSemanticAnalyzeFail; } - assert(!struct_type->data.structure.zero_bits_loop_flag); + struct_type->data.structure.resolve_loop_flag = true; + assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0); assert(decl_node->type == NodeTypeContainerDecl); @@ -1963,9 +1952,6 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { size_t gen_field_count = struct_type->data.structure.gen_field_count; LLVMTypeRef *element_types = allocate<LLVMTypeRef>(gen_field_count); - // this field should be set to true only during the recursive calls to resolve_struct_type - struct_type->data.structure.embedded_in_current = true; - Scope *scope = &struct_type->data.structure.decls_scope->base; size_t gen_field_index = 0; @@ -1979,7 +1965,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { ZigType *field_type = type_struct_field->type_entry; if ((err = ensure_complete_type(g, field_type))) { - struct_type->data.structure.is_invalid = true; + struct_type->data.structure.resolve_status = ResolveStatusInvalid; break; } @@ -1989,7 +1975,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { add_node_error(g, field_source_node, buf_sprintf("extern structs cannot contain fields of type '%s'", buf_ptr(&field_type->name))); - struct_type->data.structure.is_invalid = true; + struct_type->data.structure.resolve_status = ResolveStatusInvalid; break; } } @@ -2005,7 +1991,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { add_node_error(g, field_source_node, buf_sprintf("packed structs cannot contain fields of type '%s'", buf_ptr(&field_type->name))); - struct_type->data.structure.is_invalid = true; + struct_type->data.structure.resolve_status = ResolveStatusInvalid; break; } @@ -2056,12 +2042,13 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { gen_field_index += 1; } - struct_type->data.structure.embedded_in_current = false; - struct_type->data.structure.complete = true; + struct_type->data.structure.resolve_loop_flag = false; - if (struct_type->data.structure.is_invalid) + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) return ErrorSemanticAnalyzeFail; + struct_type->data.structure.resolve_status = ResolveStatusSizeKnown; + if (struct_type->zero_bits) { struct_type->type_ref = LLVMVoidType(); @@ -2123,7 +2110,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { assert(field_type->type_ref); assert(struct_type->type_ref); - assert(struct_type->data.structure.complete); + assert(struct_type->data.structure.resolve_status == ResolveStatusSizeKnown); uint64_t debug_size_in_bits; uint64_t debug_align_in_bits; uint64_t debug_offset_in_bits; @@ -2450,6 +2437,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { ZigType *tag_int_type; if (enum_type->data.enumeration.layout == ContainerLayoutExtern) { tag_int_type = get_c_int_type(g, CIntTypeInt); + } else if (enum_type->data.enumeration.layout == ContainerLayoutAuto && field_count == 1) { + tag_int_type = g->builtin_types.entry_num_lit_int; } else { tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); } @@ -2513,7 +2502,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { continue; } assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == ZigTypeIdInt); + assert(result_inst->value.type->id == ZigTypeIdInt || + result_inst->value.type->id == ZigTypeIdComptimeInt); auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); if (entry == nullptr) { bigint_init_bigint(&type_enum_field->value, &result_inst->value.data.x_bigint); @@ -2574,30 +2564,18 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { Error err; - if (struct_type->data.structure.is_invalid) + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) return ErrorSemanticAnalyzeFail; - - if (struct_type->data.structure.zero_bits_known) + if (struct_type->data.structure.resolve_status >= ResolveStatusZeroBitsKnown) return ErrorNone; - if (struct_type->data.structure.zero_bits_loop_flag) { - // If we get here it's due to recursion. This is a design flaw in the compiler, - // we should be able to still figure out alignment, but here we give up and say that - // the alignment is pointer width, then assert that the first field is within that - // alignment - struct_type->data.structure.zero_bits_known = true; - struct_type->data.structure.zero_bits_loop_flag = false; - if (struct_type->data.structure.abi_alignment == 0) { - if (struct_type->data.structure.layout == ContainerLayoutPacked) { - struct_type->data.structure.abi_alignment = 1; - } else { - struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0)); - } - } + if (struct_type->data.structure.resolve_loop_flag) { + struct_type->data.structure.resolve_status = ResolveStatusZeroBitsKnown; + struct_type->data.structure.resolve_loop_flag = false; return ErrorNone; } - struct_type->data.structure.zero_bits_loop_flag = true; + struct_type->data.structure.resolve_loop_flag = true; AstNode *decl_node = struct_type->data.structure.decl_node; assert(decl_node->type == NodeTypeContainerDecl); @@ -2620,7 +2598,7 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { if (field_node->data.struct_field.type == nullptr) { add_node_error(g, field_node, buf_sprintf("struct field missing type")); - struct_type->data.structure.is_invalid = true; + struct_type->data.structure.resolve_status = ResolveStatusInvalid; continue; } @@ -2629,7 +2607,7 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { ErrorMsg *msg = add_node_error(g, field_node, buf_sprintf("duplicate struct field: '%s'", buf_ptr(type_struct_field->name))); add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); - struct_type->data.structure.is_invalid = true; + struct_type->data.structure.resolve_status = ResolveStatusInvalid; continue; } @@ -2643,8 +2621,8 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { buf_sprintf("enums, not structs, support field assignment")); } - if ((err = type_ensure_zero_bits_known(g, field_type))) { - struct_type->data.structure.is_invalid = true; + if ((err = type_resolve(g, field_type, ResolveStatusZeroBitsKnown))) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; continue; } @@ -2655,36 +2633,87 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { if (!type_has_bits(field_type)) continue; - if (gen_field_index == 0) { - if (struct_type->data.structure.layout == ContainerLayoutPacked) { - struct_type->data.structure.abi_alignment = 1; - } else if (struct_type->data.structure.abi_alignment == 0) { - // Alignment of structs is the alignment of the first field, for now. - // TODO change this when we re-order struct fields (issue #168) - struct_type->data.structure.abi_alignment = get_abi_alignment(g, field_type); - assert(struct_type->data.structure.abi_alignment != 0); - } else { - // due to a design flaw in the compiler we assumed that alignment was - // pointer width, so we assert that this wasn't violated. - if (get_abi_alignment(g, field_type) > struct_type->data.structure.abi_alignment) { - zig_panic("compiler design flaw: incorrect alignment assumption"); - } - } - } - type_struct_field->gen_index = gen_field_index; gen_field_index += 1; } - struct_type->data.structure.zero_bits_loop_flag = false; + struct_type->data.structure.resolve_loop_flag = false; struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index; struct_type->zero_bits = (gen_field_index == 0); - struct_type->data.structure.zero_bits_known = true; - if (struct_type->data.structure.is_invalid) { + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + + struct_type->data.structure.resolve_status = ResolveStatusZeroBitsKnown; + return ErrorNone; +} + +static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { + assert(struct_type->id == ZigTypeIdStruct); + + Error err; + + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) + return ErrorSemanticAnalyzeFail; + if (struct_type->data.structure.resolve_status >= ResolveStatusAlignmentKnown) + return ErrorNone; + + if ((err = resolve_struct_zero_bits(g, struct_type))) + return err; + + AstNode *decl_node = struct_type->data.structure.decl_node; + + if (struct_type->data.structure.resolve_loop_flag) { + if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + add_node_error(g, decl_node, + buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name))); + } + return ErrorSemanticAnalyzeFail; + } + + struct_type->data.structure.resolve_loop_flag = true; + assert(decl_node->type == NodeTypeContainerDecl); + assert(struct_type->di_type); + + if (struct_type->data.structure.layout == ContainerLayoutPacked) { + struct_type->data.structure.abi_alignment = 1; + } + + size_t field_count = struct_type->data.structure.src_field_count; + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = &struct_type->data.structure.fields[i]; + + // If this assertion trips, look up the call stack. Probably something is + // calling type_resolve with ResolveStatusAlignmentKnown when it should only + // be resolving ResolveStatusZeroBitsKnown + assert(field->type_entry != nullptr); + + if (!type_has_bits(field->type_entry)) + continue; + + // alignment of structs is the alignment of the most-aligned field + if (struct_type->data.structure.layout != ContainerLayoutPacked) { + if ((err = type_resolve(g, field->type_entry, ResolveStatusAlignmentKnown))) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + break; + } + + uint32_t this_field_align = get_abi_alignment(g, field->type_entry); + assert(this_field_align != 0); + if (this_field_align > struct_type->data.structure.abi_alignment) { + struct_type->data.structure.abi_alignment = this_field_align; + } + } + } + + struct_type->data.structure.resolve_loop_flag = false; + + if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) { return ErrorSemanticAnalyzeFail; } + struct_type->data.structure.resolve_status = ResolveStatusAlignmentKnown; return ErrorNone; } @@ -2776,6 +2805,8 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { union_type->data.unionation.is_invalid = true; return ErrorSemanticAnalyzeFail; } + } else if (auto_layout && field_count == 1) { + tag_int_type = g->builtin_types.entry_num_lit_int; } else { tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); } @@ -2809,6 +2840,10 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); return ErrorSemanticAnalyzeFail; } + if ((err = type_resolve(g, enum_type, ResolveStatusAlignmentKnown))) { + assert(g->errors.length != 0); + return err; + } tag_type = enum_type; abi_alignment_so_far = get_abi_alignment(g, enum_type); // this populates src_field_count covered_enum_fields = allocate<bool>(enum_type->data.enumeration.src_field_count); @@ -2846,7 +2881,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { } } else { field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); - if ((err = type_ensure_zero_bits_known(g, field_type))) { + if ((err = type_resolve(g, field_type, ResolveStatusAlignmentKnown))) { union_type->data.unionation.is_invalid = true; continue; } @@ -3109,7 +3144,7 @@ static void typecheck_panic_fn(CodeGen *g, ZigFn *panic_fn) { return wrong_panic_prototype(g, proto_node, fn_type); } ZigType *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, - PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); + PtrLenUnknown, 0, 0, 0); ZigType *const_u8_slice = get_slice_type(g, const_u8_ptr); if (fn_type_id->param_info[0].type != const_u8_slice) { return wrong_panic_prototype(g, proto_node, fn_type); @@ -3428,7 +3463,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: - case NodeTypeThisLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: case NodeTypePointerType: @@ -3488,7 +3522,6 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdBlock: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed", @@ -3789,34 +3822,6 @@ ZigFn *scope_fn_entry(Scope *scope) { return nullptr; } -ZigFn *scope_get_fn_if_root(Scope *scope) { - assert(scope); - scope = scope->parent; - while (scope) { - switch (scope->id) { - case ScopeIdBlock: - return nullptr; - case ScopeIdDecls: - case ScopeIdDefer: - case ScopeIdDeferExpr: - case ScopeIdVarDecl: - case ScopeIdCImport: - case ScopeIdLoop: - case ScopeIdSuspend: - case ScopeIdCompTime: - case ScopeIdCoroPrelude: - case ScopeIdRuntime: - scope = scope->parent; - continue; - case ScopeIdFnDef: - ScopeFnDef *fn_scope = (ScopeFnDef *)scope; - return fn_scope->fn_entry; - } - zig_unreachable(); - } - return nullptr; -} - TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name) { assert(enum_type->id == ZigTypeIdEnum); if (enum_type->data.enumeration.src_field_count == 0) @@ -3829,7 +3834,7 @@ TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name) { TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name) { assert(type_entry->id == ZigTypeIdStruct); - assert(type_entry->data.structure.complete); + assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown)); if (type_entry->data.structure.src_field_count == 0) return nullptr; auto entry = type_entry->data.structure.fields_by_name.maybe_get(name); @@ -3898,7 +3903,6 @@ static bool is_container(ZigType *type_entry) { case ZigTypeIdErrorSet: case ZigTypeIdFn: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: @@ -3957,7 +3961,6 @@ void resolve_container_type(CodeGen *g, ZigType *type_entry) { case ZigTypeIdErrorSet: case ZigTypeIdFn: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdInvalid: case ZigTypeIdArgTuple: @@ -3983,14 +3986,17 @@ bool type_is_codegen_pointer(ZigType *type) { return get_codegen_ptr_type(type) == type; } -uint32_t get_ptr_align(ZigType *type) { +uint32_t get_ptr_align(CodeGen *g, ZigType *type) { ZigType *ptr_type = get_codegen_ptr_type(type); if (ptr_type->id == ZigTypeIdPointer) { - return ptr_type->data.pointer.alignment; + return (ptr_type->data.pointer.explicit_alignment == 0) ? + get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment; } else if (ptr_type->id == ZigTypeIdFn) { - return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment; + return (ptr_type->data.fn.fn_type_id.alignment == 0) ? + LLVMABIAlignmentOfType(g->target_data_ref, ptr_type->data.fn.raw_type_ref) : + ptr_type->data.fn.fn_type_id.alignment; } else if (ptr_type->id == ZigTypeIdPromise) { - return 1; + return get_coro_frame_align_bytes(g); } else { zig_unreachable(); } @@ -4060,6 +4066,7 @@ static void define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) { } bool resolve_inferred_error_set(CodeGen *g, ZigType *err_set_type, AstNode *source_node) { + assert(err_set_type->id == ZigTypeIdErrorSet); ZigFn *infer_fn = err_set_type->data.error_set.infer_fn; if (infer_fn != nullptr) { if (infer_fn->anal_state == FnAnalStateInvalid) { @@ -4417,7 +4424,6 @@ bool handle_is_ptr(ZigType *type_entry) { case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: @@ -4832,8 +4838,6 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return const_val->data.x_err_set->value ^ 2630160122; case ZigTypeIdNamespace: return hash_ptr(const_val->data.x_import); - case ZigTypeIdBlock: - return hash_ptr(const_val->data.x_block); case ZigTypeIdBoundFn: case ZigTypeIdInvalid: case ZigTypeIdUnreachable: @@ -4894,7 +4898,6 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdFn: - case ZigTypeIdBlock: case ZigTypeIdOpaque: case ZigTypeIdPromise: case ZigTypeIdErrorSet: @@ -4961,7 +4964,6 @@ static bool return_type_is_cacheable(ZigType *return_type) { case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdFn: - case ZigTypeIdBlock: case ZigTypeIdOpaque: case ZigTypeIdPromise: case ZigTypeIdErrorSet: @@ -5057,8 +5059,8 @@ bool fn_eval_eql(Scope *a, Scope *b) { bool type_has_bits(ZigType *type_entry) { assert(type_entry); - assert(type_entry->id != ZigTypeIdInvalid); - assert(type_has_zero_bits_known(type_entry)); + assert(!type_is_invalid(type_entry)); + assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown)); return !type_entry->zero_bits; } @@ -5073,17 +5075,16 @@ bool type_requires_comptime(ZigType *type_entry) { case ZigTypeIdNull: case ZigTypeIdMetaType: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: return true; case ZigTypeIdArray: return type_requires_comptime(type_entry->data.array.child_type); case ZigTypeIdStruct: - assert(type_has_zero_bits_known(type_entry)); + assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown)); return type_entry->data.structure.requires_comptime; case ZigTypeIdUnion: - assert(type_has_zero_bits_known(type_entry)); + assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown)); return type_entry->data.unionation.requires_comptime; case ZigTypeIdOptional: return type_requires_comptime(type_entry->data.maybe.child_type); @@ -5159,7 +5160,7 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { const_val->special = ConstValSpecialStatic; // TODO make this `[*]null u8` instead of `[*]u8` const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, - PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); + PtrLenUnknown, 0, 0, 0); const_val->data.x_ptr.special = ConstPtrSpecialBaseArray; const_val->data.x_ptr.data.base_array.array_val = array_val; const_val->data.x_ptr.data.base_array.elem_index = 0; @@ -5304,8 +5305,7 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr assert(array_val->type->id == ZigTypeIdArray); ZigType *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type, - is_const, false, PtrLenUnknown, get_abi_alignment(g, array_val->type->data.array.child_type), - 0, 0); + is_const, false, PtrLenUnknown, 0, 0, 0); const_val->special = ConstValSpecialStatic; const_val->type = get_slice_type(g, ptr_type); @@ -5330,7 +5330,7 @@ void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue const_val->special = ConstValSpecialStatic; const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false, - ptr_len, get_abi_alignment(g, child_type), 0, 0); + ptr_len, 0, 0, 0); const_val->data.x_ptr.special = ConstPtrSpecialBaseArray; const_val->data.x_ptr.data.base_array.array_val = array_val; const_val->data.x_ptr.data.base_array.elem_index = elem_index; @@ -5429,32 +5429,46 @@ ConstExprValue *create_const_vals(size_t count) { } Error ensure_complete_type(CodeGen *g, ZigType *type_entry) { - if (type_is_invalid(type_entry)) - return ErrorSemanticAnalyzeFail; - if (type_entry->id == ZigTypeIdStruct) { - if (!type_entry->data.structure.complete) - return resolve_struct_type(g, type_entry); - } else if (type_entry->id == ZigTypeIdEnum) { - if (!type_entry->data.enumeration.complete) - return resolve_enum_type(g, type_entry); - } else if (type_entry->id == ZigTypeIdUnion) { - if (!type_entry->data.unionation.complete) - return resolve_union_type(g, type_entry); - } - return ErrorNone; + return type_resolve(g, type_entry, ResolveStatusSizeKnown); } -Error type_ensure_zero_bits_known(CodeGen *g, ZigType *type_entry) { - if (type_is_invalid(type_entry)) +Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) { + if (type_is_invalid(ty)) return ErrorSemanticAnalyzeFail; - if (type_entry->id == ZigTypeIdStruct) { - return resolve_struct_zero_bits(g, type_entry); - } else if (type_entry->id == ZigTypeIdEnum) { - return resolve_enum_zero_bits(g, type_entry); - } else if (type_entry->id == ZigTypeIdUnion) { - return resolve_union_zero_bits(g, type_entry); + switch (status) { + case ResolveStatusUnstarted: + return ErrorNone; + case ResolveStatusInvalid: + zig_unreachable(); + case ResolveStatusZeroBitsKnown: + if (ty->id == ZigTypeIdStruct) { + return resolve_struct_zero_bits(g, ty); + } else if (ty->id == ZigTypeIdEnum) { + return resolve_enum_zero_bits(g, ty); + } else if (ty->id == ZigTypeIdUnion) { + return resolve_union_zero_bits(g, ty); + } + return ErrorNone; + case ResolveStatusAlignmentKnown: + if (ty->id == ZigTypeIdStruct) { + return resolve_struct_alignment(g, ty); + } else if (ty->id == ZigTypeIdEnum) { + return resolve_enum_zero_bits(g, ty); + } else if (ty->id == ZigTypeIdUnion) { + return resolve_union_zero_bits(g, ty); + } + return ErrorNone; + case ResolveStatusSizeKnown: + if (ty->id == ZigTypeIdStruct) { + return resolve_struct_type(g, ty); + } else if (ty->id == ZigTypeIdEnum) { + return resolve_enum_type(g, ty); + } else if (ty->id == ZigTypeIdUnion) { + return resolve_union_type(g, ty); + } + return ErrorNone; } - return ErrorNone; + zig_unreachable(); } bool ir_get_var_is_comptime(ZigVar *var) { @@ -5605,8 +5619,6 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { zig_panic("TODO"); case ZigTypeIdNamespace: return a->data.x_import == b->data.x_import; - case ZigTypeIdBlock: - return a->data.x_block == b->data.x_block; case ZigTypeIdArgTuple: return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index && a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index; @@ -5785,12 +5797,6 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case ZigTypeIdPointer: return render_const_val_ptr(g, buf, const_val, type_entry); - case ZigTypeIdBlock: - { - AstNode *node = const_val->data.x_block->source_node; - buf_appendf(buf, "(scope:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", node->line + 1, node->column + 1); - return; - } case ZigTypeIdArray: { ZigType *child_type = type_entry->data.array.child_type; @@ -5882,12 +5888,23 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case ZigTypeIdErrorUnion: { - buf_appendf(buf, "(error union %s constant)", buf_ptr(&type_entry->name)); + buf_appendf(buf, "%s(", buf_ptr(&type_entry->name)); + if (const_val->data.x_err_union.err == nullptr) { + render_const_value(g, buf, const_val->data.x_err_union.payload); + } else { + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name), + buf_ptr(&const_val->data.x_err_union.err->name)); + } + buf_appendf(buf, ")"); return; } case ZigTypeIdUnion: { - buf_appendf(buf, "(union %s constant)", buf_ptr(&type_entry->name)); + uint64_t tag = bigint_as_unsigned(&const_val->data.x_union.tag); + TypeUnionField *field = &type_entry->data.unionation.fields[tag]; + buf_appendf(buf, "%s { .%s = ", buf_ptr(&type_entry->name), buf_ptr(field->name)); + render_const_value(g, buf, const_val->data.x_union.payload); + buf_append_str(buf, "}"); return; } case ZigTypeIdErrorSet: @@ -5959,7 +5976,6 @@ uint32_t type_id_hash(TypeId x) { case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: @@ -6006,7 +6022,6 @@ bool type_id_eql(TypeId a, TypeId b) { case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: @@ -6132,7 +6147,6 @@ static const ZigTypeId all_type_ids[] = { ZigTypeIdUnion, ZigTypeIdFn, ZigTypeIdNamespace, - ZigTypeIdBlock, ZigTypeIdBoundFn, ZigTypeIdArgTuple, ZigTypeIdOpaque, @@ -6194,16 +6208,14 @@ size_t type_id_index(ZigType *entry) { return 18; case ZigTypeIdNamespace: return 19; - case ZigTypeIdBlock: - return 20; case ZigTypeIdBoundFn: - return 21; + return 20; case ZigTypeIdArgTuple: - return 22; + return 21; case ZigTypeIdOpaque: - return 23; + return 22; case ZigTypeIdPromise: - return 24; + return 23; } zig_unreachable(); } @@ -6252,8 +6264,6 @@ const char *type_id_name(ZigTypeId id) { return "Fn"; case ZigTypeIdNamespace: return "Namespace"; - case ZigTypeIdBlock: - return "Block"; case ZigTypeIdBoundFn: return "BoundFn"; case ZigTypeIdArgTuple: @@ -6278,6 +6288,12 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { if (is_libc && g->libc_link_lib != nullptr) return g->libc_link_lib; + if (g->enable_cache && is_libc && g->zig_target.os != OsMacOSX && g->zig_target.os != OsIOS) { + fprintf(stderr, "TODO linking against libc is currently incompatible with `--cache on`.\n" + "Zig is not yet capable of determining whether the libc installation has changed on subsequent builds.\n"); + exit(1); + } + for (size_t i = 0; i < g->link_libs_list.length; i += 1) { LinkLib *existing_lib = g->link_libs_list.at(i); if (buf_eql_buf(existing_lib->name, name)) { @@ -6295,7 +6311,7 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { } uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry) { - assertNoError(type_ensure_zero_bits_known(g, type_entry)); + assert(type_is_resolved(type_entry, ResolveStatusAlignmentKnown)); if (type_entry->zero_bits) return 0; // We need to make this function work without requiring ensure_complete_type @@ -6310,10 +6326,6 @@ uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry) { return 1; } else { uint32_t llvm_alignment = LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref); - // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw - if (type_entry->id == ZigTypeIdPromise && llvm_alignment < 8) { - return 8; - } return llvm_alignment; } } @@ -6351,7 +6363,10 @@ bool type_is_global_error_set(ZigType *err_set_type) { } uint32_t get_coro_frame_align_bytes(CodeGen *g) { - return g->pointer_size_bytes * 2; + uint32_t a = g->pointer_size_bytes * 2; + // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw + if (a < 8) a = 8; + return a; } bool type_can_fail(ZigType *type_entry) { @@ -6387,6 +6402,14 @@ not_integer: return nullptr; } +Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents) { + if (g->enable_cache) { + return cache_add_file_fetch(&g->cache_hash, resolved_path, contents); + } else { + return os_fetch_file_path(resolved_path, contents, false); + } +} + X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { size_t ty_size = type_size(g, ty); if (get_codegen_ptr_type(ty) != nullptr) @@ -6467,4 +6490,3 @@ bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { ty->id == ZigTypeIdUnreachable || get_codegen_ptr_type(ty) != nullptr); } - diff --git a/src/analyze.hpp b/src/analyze.hpp @@ -54,14 +54,14 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so bool type_is_codegen_pointer(ZigType *type); ZigType *get_codegen_ptr_type(ZigType *type); -uint32_t get_ptr_align(ZigType *type); +uint32_t get_ptr_align(CodeGen *g, ZigType *type); bool get_ptr_const(ZigType *type); ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry); ZigType *container_ref_type(ZigType *type_entry); bool type_is_complete(ZigType *type_entry); +bool type_is_resolved(ZigType *type_entry, ResolveStatus status); bool type_is_invalid(ZigType *type_entry); bool type_is_global_error_set(ZigType *err_set_type); -bool type_has_zero_bits_known(ZigType *type_entry); void resolve_container_type(CodeGen *g, ZigType *type_entry); ScopeDecls *get_container_scope(ZigType *type_entry); TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name); @@ -87,10 +87,9 @@ ZigFn *create_fn(AstNode *proto_node); ZigFn *create_fn_raw(FnInline inline_value, GlobalLinkageId linkage); void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_count_alloc); AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index); -ZigFn *scope_get_fn_if_root(Scope *scope); bool type_requires_comptime(ZigType *type_entry); Error ATTRIBUTE_MUST_USE ensure_complete_type(CodeGen *g, ZigType *type_entry); -Error ATTRIBUTE_MUST_USE type_ensure_zero_bits_known(CodeGen *g, ZigType *type_entry); +Error ATTRIBUTE_MUST_USE type_resolve(CodeGen *g, ZigType *type_entry, ResolveStatus status); void complete_enum(CodeGen *g, ZigType *enum_type); bool ir_get_var_is_comptime(ZigVar *var); bool const_values_equal(ConstExprValue *a, ConstExprValue *b); @@ -209,6 +208,8 @@ ZigType *get_primitive_type(CodeGen *g, Buf *name); bool calling_convention_allows_zig_types(CallingConvention cc); const char *calling_convention_name(CallingConvention cc); +Error ATTRIBUTE_MUST_USE file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents); + void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty); bool type_is_c_abi_int(CodeGen *g, ZigType *ty); diff --git a/src/ast_render.cpp b/src/ast_render.cpp @@ -193,8 +193,6 @@ static const char *node_type_str(NodeType node_type) { return "NullLiteral"; case NodeTypeUndefinedLiteral: return "UndefinedLiteral"; - case NodeTypeThisLiteral: - return "ThisLiteral"; case NodeTypeIfBoolExpr: return "IfBoolExpr"; case NodeTypeWhileExpr: @@ -897,11 +895,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } break; } - case NodeTypeThisLiteral: - { - fprintf(ar->f, "this"); - break; - } case NodeTypeBoolLiteral: { const char *bool_str = node->data.bool_literal.value ? "true" : "false"; diff --git a/src/blake2.h b/src/blake2.h @@ -0,0 +1,196 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2_H +#define BLAKE2_H + +#include <stddef.h> +#include <stdint.h> + +#if defined(_MSC_VER) +#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) +#else +#define BLAKE2_PACKED(x) x __attribute__((packed)) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + + enum blake2s_constant + { + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32, + BLAKE2S_KEYBYTES = 32, + BLAKE2S_SALTBYTES = 8, + BLAKE2S_PERSONALBYTES = 8 + }; + + enum blake2b_constant + { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 + }; + + typedef struct blake2s_state__ + { + uint32_t h[8]; + uint32_t t[2]; + uint32_t f[2]; + uint8_t buf[BLAKE2S_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; + } blake2s_state; + + typedef struct blake2b_state__ + { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; + } blake2b_state; + + typedef struct blake2sp_state__ + { + blake2s_state S[8][1]; + blake2s_state R[1]; + uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; + size_t buflen; + size_t outlen; + } blake2sp_state; + + typedef struct blake2bp_state__ + { + blake2b_state S[4][1]; + blake2b_state R[1]; + uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + } blake2bp_state; + + + BLAKE2_PACKED(struct blake2s_param__ + { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint16_t xof_length; /* 14 */ + uint8_t node_depth; /* 15 */ + uint8_t inner_length; /* 16 */ + /* uint8_t reserved[0]; */ + uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ + uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ + }); + + typedef struct blake2s_param__ blake2s_param; + + BLAKE2_PACKED(struct blake2b_param__ + { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint32_t xof_length; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ + }); + + typedef struct blake2b_param__ blake2b_param; + + typedef struct blake2xs_state__ + { + blake2s_state S[1]; + blake2s_param P[1]; + } blake2xs_state; + + typedef struct blake2xb_state__ + { + blake2b_state S[1]; + blake2b_param P[1]; + } blake2xb_state; + + /* Padded structs result in a compile-time error */ + enum { + BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), + BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) + }; + + /* Streaming API */ + int blake2s_init( blake2s_state *S, size_t outlen ); + int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); + int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); + int blake2s_final( blake2s_state *S, void *out, size_t outlen ); + + int blake2b_init( blake2b_state *S, size_t outlen ); + int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); + int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); + int blake2b_final( blake2b_state *S, void *out, size_t outlen ); + + int blake2sp_init( blake2sp_state *S, size_t outlen ); + int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); + int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); + + int blake2bp_init( blake2bp_state *S, size_t outlen ); + int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); + int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); + + /* Variable output length API */ + int blake2xs_init( blake2xs_state *S, const size_t outlen ); + int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); + int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); + int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); + + int blake2xb_init( blake2xb_state *S, const size_t outlen ); + int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); + int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); + int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); + + /* Simple API */ + int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + /* This is simply an alias for blake2b */ + int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + +#if defined(__cplusplus) +} +#endif + +#endif + diff --git a/src/blake2b.c b/src/blake2b.c @@ -0,0 +1,539 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include "blake2.h" +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2_IMPL_H +#define BLAKE2_IMPL_H + +#include <stdint.h> +#include <string.h> + +#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) + #if defined(_MSC_VER) + #define BLAKE2_INLINE __inline + #elif defined(__GNUC__) + #define BLAKE2_INLINE __inline__ + #else + #define BLAKE2_INLINE + #endif +#else + #define BLAKE2_INLINE inline +#endif + +static BLAKE2_INLINE uint32_t load32( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint32_t )( p[0] ) << 0) | + (( uint32_t )( p[1] ) << 8) | + (( uint32_t )( p[2] ) << 16) | + (( uint32_t )( p[3] ) << 24) ; +#endif +} + +static BLAKE2_INLINE uint64_t load64( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint64_t )( p[0] ) << 0) | + (( uint64_t )( p[1] ) << 8) | + (( uint64_t )( p[2] ) << 16) | + (( uint64_t )( p[3] ) << 24) | + (( uint64_t )( p[4] ) << 32) | + (( uint64_t )( p[5] ) << 40) | + (( uint64_t )( p[6] ) << 48) | + (( uint64_t )( p[7] ) << 56) ; +#endif +} + +static BLAKE2_INLINE uint16_t load16( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint16_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return ( uint16_t )((( uint32_t )( p[0] ) << 0) | + (( uint32_t )( p[1] ) << 8)); +#endif +} + +static BLAKE2_INLINE void store16( void *dst, uint16_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + *p++ = ( uint8_t )w; w >>= 8; + *p++ = ( uint8_t )w; +#endif +} + +static BLAKE2_INLINE void store32( void *dst, uint32_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); +#endif +} + +static BLAKE2_INLINE void store64( void *dst, uint64_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); + p[6] = (uint8_t)(w >> 48); + p[7] = (uint8_t)(w >> 56); +#endif +} + +static BLAKE2_INLINE uint64_t load48( const void *src ) +{ + const uint8_t *p = ( const uint8_t * )src; + return (( uint64_t )( p[0] ) << 0) | + (( uint64_t )( p[1] ) << 8) | + (( uint64_t )( p[2] ) << 16) | + (( uint64_t )( p[3] ) << 24) | + (( uint64_t )( p[4] ) << 32) | + (( uint64_t )( p[5] ) << 40) ; +} + +static BLAKE2_INLINE void store48( void *dst, uint64_t w ) +{ + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); +} + +static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) +{ + return ( w >> c ) | ( w << ( 32 - c ) ); +} + +static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) +{ + return ( w >> c ) | ( w << ( 64 - c ) ); +} + +/* prevents compiler optimizing out memset() */ +static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) +{ + static void *(*const volatile memset_v)(void *, int, size_t) = &memset; + memset_v(v, 0, n); +} + +#endif + +static const uint64_t blake2b_IV[8] = +{ + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +}; + +static const uint8_t blake2b_sigma[12][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } +}; + + +static void blake2b_set_lastnode( blake2b_state *S ) +{ + S->f[1] = (uint64_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2b_is_lastblock( const blake2b_state *S ) +{ + return S->f[0] != 0; +} + +static void blake2b_set_lastblock( blake2b_state *S ) +{ + if( S->last_node ) blake2b_set_lastnode( S ); + + S->f[0] = (uint64_t)-1; +} + +static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + +static void blake2b_init0( blake2b_state *S ) +{ + size_t i; + memset( S, 0, sizeof( blake2b_state ) ); + + for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; +} + +/* init xors IV with input parameter block */ +int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) +{ + const uint8_t *p = ( const uint8_t * )( P ); + size_t i; + + blake2b_init0( S ); + + /* IV XOR ParamBlock */ + for( i = 0; i < 8; ++i ) + S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); + + S->outlen = P->digest_length; + return 0; +} + + + +int blake2b_init( blake2b_state *S, size_t outlen ) +{ + blake2b_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2b_init_param( S, P ); +} + + +int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) +{ + blake2b_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + if( blake2b_init_param( S, P ) < 0 ) return -1; + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset( block, 0, BLAKE2B_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); + secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2*i+0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2*i+1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while(0) + +static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) +{ + uint64_t m[16]; + uint64_t v[16]; + size_t i; + + for( i = 0; i < 16; ++i ) { + m[i] = load64( block + i * sizeof( m[i] ) ); + } + + for( i = 0; i < 8; ++i ) { + v[i] = S->h[i]; + } + + v[ 8] = blake2b_IV[0]; + v[ 9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + ROUND( 10 ); + ROUND( 11 ); + + for( i = 0; i < 8; ++i ) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + if( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + if( inlen > fill ) + { + S->buflen = 0; + memcpy( S->buf + left, in, fill ); /* Fill buffer */ + blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); + blake2b_compress( S, S->buf ); /* Compress */ + in += fill; inlen -= fill; + while(inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress( S, in ); + in += BLAKE2B_BLOCKBYTES; + inlen -= BLAKE2B_BLOCKBYTES; + } + } + memcpy( S->buf + S->buflen, in, inlen ); + S->buflen += inlen; + } + return 0; +} + +int blake2b_final( blake2b_state *S, void *out, size_t outlen ) +{ + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; + size_t i; + + if( out == NULL || outlen < S->outlen ) + return -1; + + if( blake2b_is_lastblock( S ) ) + return -1; + + blake2b_increment_counter( S, S->buflen ); + blake2b_set_lastblock( S ); + memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ + blake2b_compress( S, S->buf ); + + for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ + store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + + memcpy( out, buffer, S->outlen ); + secure_zero_memory(buffer, sizeof(buffer)); + return 0; +} + +/* inlen, at least, should be uint64_t. Others can be size_t. */ +int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + blake2b_state S[1]; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if( NULL == key && keylen > 0 ) return -1; + + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + if( keylen > BLAKE2B_KEYBYTES ) return -1; + + if( keylen > 0 ) + { + if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; + } + else + { + if( blake2b_init( S, outlen ) < 0 ) return -1; + } + + blake2b_update( S, ( const uint8_t * )in, inlen ); + blake2b_final( S, out, outlen ); + return 0; +} + +int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { + return blake2b(out, outlen, in, inlen, key, keylen); +} + +#if defined(SUPERCOP) +int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) +{ + return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); +} +#endif + +#if defined(BLAKE2B_SELFTEST) +#include <string.h> +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2B_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); + + if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2b_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2b_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif + diff --git a/src/buffer.hpp b/src/buffer.hpp @@ -78,6 +78,10 @@ static inline Buf *buf_create_from_mem(const char *ptr, size_t len) { return buf; } +static inline Buf *buf_create_from_slice(Slice<uint8_t> slice) { + return buf_create_from_mem((const char *)slice.ptr, slice.len); +} + static inline Buf *buf_create_from_str(const char *str) { return buf_create_from_mem(str, strlen(str)); } diff --git a/src/cache_hash.cpp b/src/cache_hash.cpp @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2018 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "cache_hash.hpp" +#include "all_types.hpp" +#include "buffer.hpp" +#include "os.hpp" + +#include <stdio.h> + +void cache_init(CacheHash *ch, Buf *manifest_dir) { + int rc = blake2b_init(&ch->blake, 48); + assert(rc == 0); + ch->files = {}; + ch->manifest_dir = manifest_dir; + ch->manifest_file_path = nullptr; + ch->manifest_dirty = false; +} + +void cache_str(CacheHash *ch, const char *ptr) { + assert(ch->manifest_file_path == nullptr); + assert(ptr != nullptr); + // + 1 to include the null byte + blake2b_update(&ch->blake, ptr, strlen(ptr) + 1); +} + +void cache_int(CacheHash *ch, int x) { + assert(ch->manifest_file_path == nullptr); + // + 1 to include the null byte + uint8_t buf[sizeof(int) + 1]; + memcpy(buf, &x, sizeof(int)); + buf[sizeof(int)] = 0; + blake2b_update(&ch->blake, buf, sizeof(int) + 1); +} + +void cache_usize(CacheHash *ch, size_t x) { + assert(ch->manifest_file_path == nullptr); + // + 1 to include the null byte + uint8_t buf[sizeof(size_t) + 1]; + memcpy(buf, &x, sizeof(size_t)); + buf[sizeof(size_t)] = 0; + blake2b_update(&ch->blake, buf, sizeof(size_t) + 1); +} + +void cache_bool(CacheHash *ch, bool x) { + assert(ch->manifest_file_path == nullptr); + blake2b_update(&ch->blake, &x, 1); +} + +void cache_buf(CacheHash *ch, Buf *buf) { + assert(ch->manifest_file_path == nullptr); + assert(buf != nullptr); + // + 1 to include the null byte + blake2b_update(&ch->blake, buf_ptr(buf), buf_len(buf) + 1); +} + +void cache_buf_opt(CacheHash *ch, Buf *buf) { + assert(ch->manifest_file_path == nullptr); + if (buf == nullptr) { + cache_str(ch, ""); + cache_str(ch, ""); + } else { + cache_buf(ch, buf); + } +} + +void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len) { + assert(ch->manifest_file_path == nullptr); + for (size_t i = 0; i < len; i += 1) { + LinkLib *lib = ptr[i]; + if (lib->provided_explicitly) { + cache_buf(ch, lib->name); + } + } + cache_str(ch, ""); +} + +void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len) { + assert(ch->manifest_file_path == nullptr); + for (size_t i = 0; i < len; i += 1) { + Buf *buf = ptr[i]; + cache_buf(ch, buf); + } + cache_str(ch, ""); +} + +void cache_list_of_file(CacheHash *ch, Buf **ptr, size_t len) { + assert(ch->manifest_file_path == nullptr); + + for (size_t i = 0; i < len; i += 1) { + Buf *buf = ptr[i]; + cache_file(ch, buf); + } + cache_str(ch, ""); +} + +void cache_list_of_str(CacheHash *ch, const char **ptr, size_t len) { + assert(ch->manifest_file_path == nullptr); + + for (size_t i = 0; i < len; i += 1) { + const char *s = ptr[i]; + cache_str(ch, s); + } + cache_str(ch, ""); +} + +void cache_file(CacheHash *ch, Buf *file_path) { + assert(ch->manifest_file_path == nullptr); + assert(file_path != nullptr); + Buf *resolved_path = buf_alloc(); + *resolved_path = os_path_resolve(&file_path, 1); + CacheHashFile *chf = ch->files.add_one(); + chf->path = resolved_path; + cache_buf(ch, resolved_path); +} + +void cache_file_opt(CacheHash *ch, Buf *file_path) { + assert(ch->manifest_file_path == nullptr); + if (file_path == nullptr) { + cache_str(ch, ""); + cache_str(ch, ""); + } else { + cache_file(ch, file_path); + } +} + +// Ported from std/base64.zig +static uint8_t base64_fs_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; +static void base64_encode(Slice<uint8_t> dest, Slice<uint8_t> source) { + size_t dest_len = ((source.len + 2) / 3) * 4; + assert(dest.len == dest_len); + + size_t i = 0; + size_t out_index = 0; + for (; i + 2 < source.len; i += 3) { + dest.ptr[out_index] = base64_fs_alphabet[(source.ptr[i] >> 2) & 0x3f]; + out_index += 1; + + dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i] & 0x3) << 4) | ((source.ptr[i + 1] & 0xf0) >> 4)]; + out_index += 1; + + dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i + 1] & 0xf) << 2) | ((source.ptr[i + 2] & 0xc0) >> 6)]; + out_index += 1; + + dest.ptr[out_index] = base64_fs_alphabet[source.ptr[i + 2] & 0x3f]; + out_index += 1; + } + + // Assert that we never need pad characters. + assert(i == source.len); +} + +// Ported from std/base64.zig +static Error base64_decode(Slice<uint8_t> dest, Slice<uint8_t> source) { + assert(source.len % 4 == 0); + assert(dest.len == (source.len / 4) * 3); + + // In Zig this is comptime computed. In C++ it's not worth it to do that. + uint8_t char_to_index[256]; + bool char_in_alphabet[256] = {0}; + for (size_t i = 0; i < 64; i += 1) { + uint8_t c = base64_fs_alphabet[i]; + assert(!char_in_alphabet[c]); + char_in_alphabet[c] = true; + char_to_index[c] = i; + } + + size_t src_cursor = 0; + size_t dest_cursor = 0; + + for (;src_cursor < source.len; src_cursor += 4) { + if (!char_in_alphabet[source.ptr[src_cursor + 0]]) return ErrorInvalidFormat; + if (!char_in_alphabet[source.ptr[src_cursor + 1]]) return ErrorInvalidFormat; + if (!char_in_alphabet[source.ptr[src_cursor + 2]]) return ErrorInvalidFormat; + if (!char_in_alphabet[source.ptr[src_cursor + 3]]) return ErrorInvalidFormat; + dest.ptr[dest_cursor + 0] = (char_to_index[source.ptr[src_cursor + 0]] << 2) | (char_to_index[source.ptr[src_cursor + 1]] >> 4); + dest.ptr[dest_cursor + 1] = (char_to_index[source.ptr[src_cursor + 1]] << 4) | (char_to_index[source.ptr[src_cursor + 2]] >> 2); + dest.ptr[dest_cursor + 2] = (char_to_index[source.ptr[src_cursor + 2]] << 6) | (char_to_index[source.ptr[src_cursor + 3]]); + dest_cursor += 3; + } + + assert(src_cursor == source.len); + assert(dest_cursor == dest.len); + return ErrorNone; +} + +static Error hash_file(uint8_t *digest, OsFile handle, Buf *contents) { + Error err; + + if (contents) { + buf_resize(contents, 0); + } + + blake2b_state blake; + int rc = blake2b_init(&blake, 48); + assert(rc == 0); + + for (;;) { + uint8_t buf[4096]; + size_t amt = 4096; + if ((err = os_file_read(handle, buf, &amt))) + return err; + if (amt == 0) { + rc = blake2b_final(&blake, digest, 48); + assert(rc == 0); + return ErrorNone; + } + blake2b_update(&blake, buf, amt); + if (contents) { + buf_append_mem(contents, (char*)buf, amt); + } + } +} + +static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf, Buf *contents) { + Error err; + + assert(chf->path != nullptr); + + OsFile this_file; + if ((err = os_file_open_r(chf->path, &this_file))) + return err; + + if ((err = os_file_mtime(this_file, &chf->mtime))) { + os_file_close(this_file); + return err; + } + + if ((err = hash_file(chf->bin_digest, this_file, contents))) { + os_file_close(this_file); + return err; + } + os_file_close(this_file); + + blake2b_update(&ch->blake, chf->bin_digest, 48); + + return ErrorNone; +} + +Error cache_hit(CacheHash *ch, Buf *out_digest) { + Error err; + + uint8_t bin_digest[48]; + int rc = blake2b_final(&ch->blake, bin_digest, 48); + assert(rc == 0); + + if (ch->files.length == 0) { + buf_resize(out_digest, 64); + base64_encode(buf_to_slice(out_digest), {bin_digest, 48}); + return ErrorNone; + } + + Buf b64_digest = BUF_INIT; + buf_resize(&b64_digest, 64); + base64_encode(buf_to_slice(&b64_digest), {bin_digest, 48}); + + rc = blake2b_init(&ch->blake, 48); + assert(rc == 0); + blake2b_update(&ch->blake, bin_digest, 48); + + ch->manifest_file_path = buf_alloc(); + os_path_join(ch->manifest_dir, &b64_digest, ch->manifest_file_path); + + buf_append_str(ch->manifest_file_path, ".txt"); + + if ((err = os_make_path(ch->manifest_dir))) + return err; + + if ((err = os_file_open_lock_rw(ch->manifest_file_path, &ch->manifest_file))) + return err; + + Buf line_buf = BUF_INIT; + buf_resize(&line_buf, 512); + if ((err = os_file_read_all(ch->manifest_file, &line_buf))) { + os_file_close(ch->manifest_file); + return err; + } + + size_t input_file_count = ch->files.length; + bool any_file_changed = false; + size_t file_i = 0; + SplitIterator line_it = memSplit(buf_to_slice(&line_buf), str("\n")); + for (;; file_i += 1) { + Optional<Slice<uint8_t>> opt_line = SplitIterator_next(&line_it); + if (!opt_line.is_some) + break; + + CacheHashFile *chf; + if (file_i < input_file_count) { + chf = &ch->files.at(file_i); + } else if (any_file_changed) { + // cache miss. + // keep the the manifest file open with the rw lock + // reset the hash + rc = blake2b_init(&ch->blake, 48); + assert(rc == 0); + blake2b_update(&ch->blake, bin_digest, 48); + ch->files.resize(input_file_count); + // bring the hash up to the input file hashes + for (file_i = 0; file_i < input_file_count; file_i += 1) { + blake2b_update(&ch->blake, ch->files.at(file_i).bin_digest, 48); + } + // caller can notice that out_digest is unmodified. + return ErrorNone; + } else { + chf = ch->files.add_one(); + chf->path = nullptr; + } + + SplitIterator it = memSplit(opt_line.value, str(" ")); + + Optional<Slice<uint8_t>> opt_mtime_sec = SplitIterator_next(&it); + if (!opt_mtime_sec.is_some) { + os_file_close(ch->manifest_file); + return ErrorInvalidFormat; + } + chf->mtime.sec = strtoull((const char *)opt_mtime_sec.value.ptr, nullptr, 10); + + Optional<Slice<uint8_t>> opt_mtime_nsec = SplitIterator_next(&it); + if (!opt_mtime_nsec.is_some) { + os_file_close(ch->manifest_file); + return ErrorInvalidFormat; + } + chf->mtime.nsec = strtoull((const char *)opt_mtime_nsec.value.ptr, nullptr, 10); + + Optional<Slice<uint8_t>> opt_digest = SplitIterator_next(&it); + if (!opt_digest.is_some) { + os_file_close(ch->manifest_file); + return ErrorInvalidFormat; + } + if ((err = base64_decode({chf->bin_digest, 48}, opt_digest.value))) { + os_file_close(ch->manifest_file); + return ErrorInvalidFormat; + } + + Slice<uint8_t> file_path = SplitIterator_rest(&it); + if (file_path.len == 0) { + os_file_close(ch->manifest_file); + return ErrorInvalidFormat; + } + Buf *this_path = buf_create_from_slice(file_path); + if (chf->path != nullptr && !buf_eql_buf(this_path, chf->path)) { + os_file_close(ch->manifest_file); + return ErrorInvalidFormat; + } + chf->path = this_path; + + // if the mtime matches we can trust the digest + OsFile this_file; + if ((err = os_file_open_r(chf->path, &this_file))) { + os_file_close(ch->manifest_file); + return err; + } + OsTimeStamp actual_mtime; + if ((err = os_file_mtime(this_file, &actual_mtime))) { + os_file_close(this_file); + os_file_close(ch->manifest_file); + return err; + } + if (chf->mtime.sec == actual_mtime.sec && chf->mtime.nsec == actual_mtime.nsec) { + os_file_close(this_file); + } else { + // we have to recompute the digest. + // later we'll rewrite the manifest with the new mtime/digest values + ch->manifest_dirty = true; + chf->mtime = actual_mtime; + + uint8_t actual_digest[48]; + if ((err = hash_file(actual_digest, this_file, nullptr))) { + os_file_close(this_file); + os_file_close(ch->manifest_file); + return err; + } + os_file_close(this_file); + if (memcmp(chf->bin_digest, actual_digest, 48) != 0) { + memcpy(chf->bin_digest, actual_digest, 48); + // keep going until we have the input file digests + any_file_changed = true; + } + } + if (!any_file_changed) { + blake2b_update(&ch->blake, chf->bin_digest, 48); + } + } + if (file_i < input_file_count) { + // manifest file is empty or missing entries, so this is a cache miss + ch->manifest_dirty = true; + for (; file_i < input_file_count; file_i += 1) { + CacheHashFile *chf = &ch->files.at(file_i); + if ((err = populate_file_hash(ch, chf, nullptr))) { + os_file_close(ch->manifest_file); + return err; + } + } + return ErrorNone; + } + // Cache Hit + return cache_final(ch, out_digest); +} + +Error cache_add_file_fetch(CacheHash *ch, Buf *resolved_path, Buf *contents) { + Error err; + + assert(ch->manifest_file_path != nullptr); + CacheHashFile *chf = ch->files.add_one(); + chf->path = resolved_path; + if ((err = populate_file_hash(ch, chf, contents))) { + os_file_close(ch->manifest_file); + return err; + } + + return ErrorNone; +} + +Error cache_add_file(CacheHash *ch, Buf *path) { + Buf *resolved_path = buf_alloc(); + *resolved_path = os_path_resolve(&path, 1); + return cache_add_file_fetch(ch, resolved_path, nullptr); +} + +static Error write_manifest_file(CacheHash *ch) { + Error err; + Buf contents = BUF_INIT; + buf_resize(&contents, 0); + uint8_t encoded_digest[65]; + encoded_digest[64] = 0; + for (size_t i = 0; i < ch->files.length; i += 1) { + CacheHashFile *chf = &ch->files.at(i); + base64_encode({encoded_digest, 64}, {chf->bin_digest, 48}); + buf_appendf(&contents, "%" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %s %s\n", + chf->mtime.sec, chf->mtime.nsec, encoded_digest, buf_ptr(chf->path)); + } + if ((err = os_file_overwrite(ch->manifest_file, &contents))) + return err; + + return ErrorNone; +} + +Error cache_final(CacheHash *ch, Buf *out_digest) { + Error err; + + assert(ch->manifest_file_path != nullptr); + + if (ch->manifest_dirty) { + if ((err = write_manifest_file(ch))) { + fprintf(stderr, "Warning: Unable to write cache file '%s': %s\n", + buf_ptr(ch->manifest_file_path), err_str(err)); + } + } + // We don't close the manifest file yet, because we want to + // keep it locked until the API user is done using it. + + uint8_t bin_digest[48]; + int rc = blake2b_final(&ch->blake, bin_digest, 48); + assert(rc == 0); + buf_resize(out_digest, 64); + base64_encode(buf_to_slice(out_digest), {bin_digest, 48}); + + return ErrorNone; +} + +void cache_release(CacheHash *ch) { + assert(ch->manifest_file_path != nullptr); + os_file_close(ch->manifest_file); +} diff --git a/src/cache_hash.hpp b/src/cache_hash.hpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_CACHE_HASH_HPP +#define ZIG_CACHE_HASH_HPP + +#include "blake2.h" +#include "os.hpp" + +struct LinkLib; + +struct CacheHashFile { + Buf *path; + OsTimeStamp mtime; + uint8_t bin_digest[48]; + Buf *contents; +}; + +struct CacheHash { + blake2b_state blake; + ZigList<CacheHashFile> files; + Buf *manifest_dir; + Buf *manifest_file_path; + OsFile manifest_file; + bool manifest_dirty; +}; + +// Always call this first to set up. +void cache_init(CacheHash *ch, Buf *manifest_dir); + +// Next, use the hash population functions to add the initial parameters. +void cache_str(CacheHash *ch, const char *ptr); +void cache_int(CacheHash *ch, int x); +void cache_bool(CacheHash *ch, bool x); +void cache_usize(CacheHash *ch, size_t x); +void cache_buf(CacheHash *ch, Buf *buf); +void cache_buf_opt(CacheHash *ch, Buf *buf); +void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len); +void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len); +void cache_list_of_file(CacheHash *ch, Buf **ptr, size_t len); +void cache_list_of_str(CacheHash *ch, const char **ptr, size_t len); +void cache_file(CacheHash *ch, Buf *path); +void cache_file_opt(CacheHash *ch, Buf *path); + +// Then call cache_hit when you're ready to see if you can skip the next step. +// out_b64_digest will be left unchanged if it was a cache miss. +// If you got a cache hit, the next step is cache_release. +// From this point on, there is a lock on the input params. Release +// the lock with cache_release. +Error ATTRIBUTE_MUST_USE cache_hit(CacheHash *ch, Buf *out_b64_digest); + +// If you did not get a cache hit, call this function for every file +// that is depended on, and then finish with cache_final. +Error ATTRIBUTE_MUST_USE cache_add_file(CacheHash *ch, Buf *path); + +// This variant of cache_add_file returns the file contents. +// Also the file path argument must be already resolved. +Error ATTRIBUTE_MUST_USE cache_add_file_fetch(CacheHash *ch, Buf *resolved_path, Buf *contents); + +// out_b64_digest will be the same thing that cache_hit returns if you got a cache hit +Error ATTRIBUTE_MUST_USE cache_final(CacheHash *ch, Buf *out_b64_digest); + +// Until this function is called, no one will be able to get a lock on your input params. +void cache_release(CacheHash *ch); + + +#endif diff --git a/src/codegen.cpp b/src/codegen.cpp @@ -8,12 +8,12 @@ #include "analyze.hpp" #include "ast_render.hpp" #include "codegen.hpp" +#include "compiler.hpp" #include "config.h" #include "errmsg.hpp" #include "error.hpp" #include "hash_map.hpp" #include "ir.hpp" -#include "link.hpp" #include "os.hpp" #include "translate_c.hpp" #include "target.hpp" @@ -183,14 +183,14 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out return g; } -void codegen_destroy(CodeGen *codegen) { - LLVMDisposeTargetMachine(codegen->target_machine); -} - void codegen_set_output_h_path(CodeGen *g, Buf *h_path) { g->out_h_path = h_path; } +void codegen_set_output_path(CodeGen *g, Buf *path) { + g->wanted_output_file_path = path; +} + void codegen_set_clang_argv(CodeGen *g, const char **args, size_t len) { g->clang_argv = args; g->clang_argv_len = len; @@ -243,10 +243,6 @@ void codegen_set_out_name(CodeGen *g, Buf *out_name) { g->root_out_name = out_name; } -void codegen_set_cache_dir(CodeGen *g, Buf cache_dir) { - g->cache_dir = cache_dir; -} - void codegen_set_libc_lib_dir(CodeGen *g, Buf *libc_lib_dir) { g->libc_lib_dir = libc_lib_dir; } @@ -779,7 +775,8 @@ static LLVMValueRef gen_store_untyped(CodeGen *g, LLVMValueRef value, LLVMValueR static LLVMValueRef gen_store(CodeGen *g, LLVMValueRef value, LLVMValueRef ptr, ZigType *ptr_type) { assert(ptr_type->id == ZigTypeIdPointer); - return gen_store_untyped(g, value, ptr, ptr_type->data.pointer.alignment, ptr_type->data.pointer.is_volatile); + uint32_t alignment = get_ptr_align(g, ptr_type); + return gen_store_untyped(g, value, ptr, alignment, ptr_type->data.pointer.is_volatile); } static LLVMValueRef gen_load_untyped(CodeGen *g, LLVMValueRef ptr, uint32_t alignment, bool is_volatile, @@ -797,7 +794,8 @@ static LLVMValueRef gen_load_untyped(CodeGen *g, LLVMValueRef ptr, uint32_t alig static LLVMValueRef gen_load(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, const char *name) { assert(ptr_type->id == ZigTypeIdPointer); - return gen_load_untyped(g, ptr, ptr_type->data.pointer.alignment, ptr_type->data.pointer.is_volatile, name); + uint32_t alignment = get_ptr_align(g, ptr_type); + return gen_load_untyped(g, ptr, alignment, ptr_type->data.pointer.is_volatile, name); } static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, ZigType *type, ZigType *ptr_type) { @@ -1772,7 +1770,7 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_ty ZigType *usize = g->builtin_types.entry_usize; uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref); - uint64_t align_bytes = ptr_type->data.pointer.alignment; + uint64_t align_bytes = get_ptr_align(g, ptr_type); assert(size_bytes > 0); assert(align_bytes > 0); @@ -3162,7 +3160,8 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, assert(var->value->type == init_value->value.type); ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, PtrLenSingle, var->align_bytes, 0, 0); - gen_assign_raw(g, var->value_ref, var_ptr_type, ir_llvm_value(g, init_value)); + LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); + gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); } else { bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); if (want_safe) { @@ -4022,7 +4021,7 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I LLVMValueRef ptr_val; if (target_type->id == ZigTypeIdPointer) { - align_bytes = target_type->data.pointer.alignment; + align_bytes = get_ptr_align(g, target_type); ptr_val = target_val; } else if (target_type->id == ZigTypeIdFn) { align_bytes = target_type->data.fn.fn_type_id.alignment; @@ -4030,7 +4029,7 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I } else if (target_type->id == ZigTypeIdOptional && target_type->data.maybe.child_type->id == ZigTypeIdPointer) { - align_bytes = target_type->data.maybe.child_type->data.pointer.alignment; + align_bytes = get_ptr_align(g, target_type->data.maybe.child_type); ptr_val = target_val; } else if (target_type->id == ZigTypeIdOptional && target_type->data.maybe.child_type->id == ZigTypeIdFn) @@ -4043,7 +4042,7 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I zig_panic("TODO audit this function"); } else if (target_type->id == ZigTypeIdStruct && target_type->data.structure.is_slice) { ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry; - align_bytes = slice_ptr_type->data.pointer.alignment; + align_bytes = get_ptr_align(g, slice_ptr_type); size_t ptr_index = target_type->data.structure.fields[slice_ptr_index].gen_index; LLVMValueRef ptr_val_ptr = LLVMBuildStructGEP(g->builder, target_val, (unsigned)ptr_index, ""); @@ -4195,7 +4194,7 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrIns ZigType *ptr_type = instruction->dest_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); - ZigLLVMBuildMemSet(g->builder, dest_ptr_casted, char_val, len_val, ptr_type->data.pointer.alignment, ptr_type->data.pointer.is_volatile); + ZigLLVMBuildMemSet(g->builder, dest_ptr_casted, char_val, len_val, get_ptr_align(g, ptr_type), ptr_type->data.pointer.is_volatile); return nullptr; } @@ -4216,9 +4215,8 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrIns assert(src_ptr_type->id == ZigTypeIdPointer); bool is_volatile = (dest_ptr_type->data.pointer.is_volatile || src_ptr_type->data.pointer.is_volatile); - - ZigLLVMBuildMemCpy(g->builder, dest_ptr_casted, dest_ptr_type->data.pointer.alignment, - src_ptr_casted, src_ptr_type->data.pointer.alignment, len_val, is_volatile); + ZigLLVMBuildMemCpy(g->builder, dest_ptr_casted, get_ptr_align(g, dest_ptr_type), + src_ptr_casted, get_ptr_align(g, src_ptr_type), len_val, is_volatile); return nullptr; } @@ -4629,7 +4627,6 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, IrInstructionUnionTag *instruction) { ZigType *union_type = instruction->value->value.type; - assert(union_type->data.unionation.gen_tag_index != SIZE_MAX); ZigType *tag_type = union_type->data.unionation.tag_type; if (!type_has_bits(tag_type)) @@ -4639,6 +4636,7 @@ static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, Ir if (union_type->data.unionation.gen_field_count == 0) return union_val; + assert(union_type->data.unionation.gen_tag_index != SIZE_MAX); LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_val, union_type->data.unionation.gen_tag_index, ""); ZigType *ptr_type = get_pointer_to_type(g, tag_type, false); @@ -5393,7 +5391,6 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdVoid: @@ -5774,12 +5771,24 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c LLVMValueRef tag_value = bigint_to_llvm_const(type_entry->data.unionation.tag_type->type_ref, &const_val->data.x_union.tag); - LLVMValueRef fields[2]; + LLVMValueRef fields[3]; fields[type_entry->data.unionation.gen_union_index] = union_value_ref; fields[type_entry->data.unionation.gen_tag_index] = tag_value; if (make_unnamed_struct) { - return LLVMConstStruct(fields, 2, false); + LLVMValueRef result = LLVMConstStruct(fields, 2, false); + uint64_t last_field_offset = LLVMOffsetOfElement(g->target_data_ref, LLVMTypeOf(result), 1); + uint64_t end_offset = last_field_offset + + LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(fields[1])); + uint64_t expected_sz = LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); + unsigned pad_sz = expected_sz - end_offset; + if (pad_sz != 0) { + fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)); + result = LLVMConstStruct(fields, 3, false); + } + uint64_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result)); + assert(actual_sz == expected_sz); + return result; } else { return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); } @@ -5789,9 +5798,16 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case ZigTypeIdEnum: return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_enum_tag); case ZigTypeIdFn: - assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); - assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); - return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry); + if (const_val->data.x_ptr.special == ConstPtrSpecialFunction) { + assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); + return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry); + } else if (const_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; + uint64_t addr = const_val->data.x_ptr.data.hard_coded_addr.addr; + return LLVMConstIntToPtr(LLVMConstInt(usize_type_ref, addr, false), type_entry->type_ref); + } else { + zig_unreachable(); + } case ZigTypeIdPointer: return gen_const_val_ptr(g, const_val, name); case ZigTypeIdErrorUnion: @@ -5819,13 +5835,29 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c err_payload_value = gen_const_val(g, payload_val, ""); make_unnamed_struct = is_llvm_value_unnamed_type(payload_val->type, err_payload_value); } - LLVMValueRef fields[] = { - err_tag_value, - err_payload_value, - }; if (make_unnamed_struct) { - return LLVMConstStruct(fields, 2, false); + uint64_t payload_off = LLVMOffsetOfElement(g->target_data_ref, type_entry->type_ref, 1); + uint64_t err_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(err_tag_value)); + unsigned pad_sz = payload_off - err_sz; + if (pad_sz == 0) { + LLVMValueRef fields[] = { + err_tag_value, + err_payload_value, + }; + return LLVMConstStruct(fields, 2, false); + } else { + LLVMValueRef fields[] = { + err_tag_value, + LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)), + err_payload_value, + }; + return LLVMConstStruct(fields, 3, false); + } } else { + LLVMValueRef fields[] = { + err_tag_value, + err_payload_value, + }; return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); } } @@ -5840,7 +5872,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: @@ -5958,13 +5989,6 @@ static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val, // TODO ^^ make an actual global variable } -static void ensure_cache_dir(CodeGen *g) { - int err; - if ((err = os_make_path(&g->cache_dir))) { - zig_panic("unable to make cache dir: %s", err_str(err)); - } -} - static void validate_inline_fns(CodeGen *g) { for (size_t i = 0; i < g->inline_fns.length; i += 1) { ZigFn *fn_entry = g->inline_fns.at(i); @@ -5979,8 +6003,6 @@ static void validate_inline_fns(CodeGen *g) { static void do_code_gen(CodeGen *g) { assert(!g->errors.length); - codegen_add_time_event(g, "Code Generation"); - { // create debug type for error sets assert(g->err_enumerators.length == g->errors_by_index.length); @@ -6283,45 +6305,18 @@ static void do_code_gen(CodeGen *g) { char *error = nullptr; LLVMVerifyModule(g->module, LLVMAbortProcessAction, &error); #endif +} - codegen_add_time_event(g, "LLVM Emit Output"); - - char *err_msg = nullptr; - Buf *o_basename = buf_create_from_buf(g->root_out_name); - - switch (g->emit_file_type) { - case EmitFileTypeBinary: - { - const char *o_ext = target_o_file_ext(&g->zig_target); - buf_append_str(o_basename, o_ext); - break; - } - case EmitFileTypeAssembly: - { - const char *asm_ext = target_asm_file_ext(&g->zig_target); - buf_append_str(o_basename, asm_ext); - break; - } - case EmitFileTypeLLVMIr: - { - const char *llvm_ir_ext = target_llvm_ir_file_ext(&g->zig_target); - buf_append_str(o_basename, llvm_ir_ext); - break; - } - default: - zig_unreachable(); - } - - Buf *output_path = buf_alloc(); - os_path_join(&g->cache_dir, o_basename, output_path); - ensure_cache_dir(g); - +static void zig_llvm_emit_output(CodeGen *g) { bool is_small = g->build_mode == BuildModeSmallRelease; + Buf *output_path = &g->o_file_output_path; + char *err_msg = nullptr; switch (g->emit_file_type) { case EmitFileTypeBinary: if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), - ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small)) + ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small, + g->enable_time_report)) { zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg); } @@ -6331,22 +6326,22 @@ static void do_code_gen(CodeGen *g) { case EmitFileTypeAssembly: if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), - ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug, is_small)) + ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug, is_small, + g->enable_time_report)) { zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg); } validate_inline_fns(g); - g->link_objects.append(output_path); break; case EmitFileTypeLLVMIr: if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), - ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug, is_small)) + ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug, is_small, + g->enable_time_report)) { zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg); } validate_inline_fns(g); - g->link_objects.append(output_path); break; default: @@ -6400,12 +6395,6 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_namespace = entry; } { - ZigType *entry = new_type_table_entry(ZigTypeIdBlock); - buf_init_from_str(&entry->name, "(block)"); - entry->zero_bits = true; - g->builtin_types.entry_block = entry; - } - { ZigType *entry = new_type_table_entry(ZigTypeIdComptimeFloat); buf_init_from_str(&entry->name, "comptime_float"); entry->zero_bits = true; @@ -6651,7 +6640,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1); create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1); - create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2); + create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 1); create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1); create_builtin_fn(g, BuiltinFnIdPtrCast, "ptrCast", 2); create_builtin_fn(g, BuiltinFnIdBitCast, "bitCast", 2); @@ -6685,6 +6674,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1); create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2); + create_builtin_fn(g, BuiltinFnIdThis, "This", 0); } static const char *bool_to_str(bool b) { @@ -6866,7 +6856,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " Union: Union,\n" " Fn: Fn,\n" " Namespace: void,\n" - " Block: void,\n" " BoundFn: Fn,\n" " ArgTuple: void,\n" " Opaque: void,\n" @@ -7037,11 +7026,11 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { { buf_appendf(contents, "pub const FloatMode = enum {\n" - " Optimized,\n" " Strict,\n" + " Optimized,\n" "};\n\n"); - assert(FloatModeOptimized == 0); - assert(FloatModeStrict == 1); + assert(FloatModeStrict == 0); + assert(FloatModeOptimized == 1); } { buf_appendf(contents, @@ -7049,8 +7038,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " Big,\n" " Little,\n" "};\n\n"); - assert(FloatModeOptimized == 0); - assert(FloatModeStrict == 1); + //assert(EndianBig == 0); + //assert(EndianLittle == 1); } { const char *endian_str = g->is_big_endian ? "Endian.Big" : "Endian.Little"; @@ -7071,36 +7060,84 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { return contents; } -static void define_builtin_compile_vars(CodeGen *g) { +static Error define_builtin_compile_vars(CodeGen *g) { if (g->std_package == nullptr) - return; + return ErrorNone; - const char *builtin_zig_basename = "builtin.zig"; - Buf *builtin_zig_path = buf_alloc(); - os_path_join(&g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); + Error err; - Buf *contents = codegen_generate_builtin_source(g); - ensure_cache_dir(g); - os_write_file(builtin_zig_path, contents); + Buf *manifest_dir = buf_alloc(); + os_path_join(get_stage1_cache_path(), buf_create_from_str("builtin"), manifest_dir); - Buf *resolved_path = buf_alloc(); - Buf *resolve_paths[] = {builtin_zig_path}; - *resolved_path = os_path_resolve(resolve_paths, 1); + CacheHash cache_hash; + cache_init(&cache_hash, manifest_dir); + + Buf *compiler_id; + if ((err = get_compiler_id(&compiler_id))) + return err; + + // Only a few things affect builtin.zig + cache_buf(&cache_hash, compiler_id); + cache_int(&cache_hash, g->build_mode); + cache_bool(&cache_hash, g->is_test_build); + cache_int(&cache_hash, g->zig_target.arch.arch); + cache_int(&cache_hash, g->zig_target.arch.sub_arch); + cache_int(&cache_hash, g->zig_target.vendor); + cache_int(&cache_hash, g->zig_target.os); + cache_int(&cache_hash, g->zig_target.env_type); + cache_int(&cache_hash, g->zig_target.oformat); + cache_bool(&cache_hash, g->have_err_ret_tracing); + cache_bool(&cache_hash, g->libc_link_lib != nullptr); + + Buf digest = BUF_INIT; + buf_resize(&digest, 0); + if ((err = cache_hit(&cache_hash, &digest))) + return err; + + // We should always get a cache hit because there are no + // files in the input hash. + assert(buf_len(&digest) != 0); + + Buf *this_dir = buf_alloc(); + os_path_join(manifest_dir, &digest, this_dir); + + if ((err = os_make_path(this_dir))) + return err; + + const char *builtin_zig_basename = "builtin.zig"; + Buf *builtin_zig_path = buf_alloc(); + os_path_join(this_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); + + bool hit; + if ((err = os_file_exists(builtin_zig_path, &hit))) + return err; + Buf *contents; + if (hit) { + contents = buf_alloc(); + if ((err = os_fetch_file_path(builtin_zig_path, contents, false))) { + fprintf(stderr, "Unable to open '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err)); + exit(1); + } + } else { + contents = codegen_generate_builtin_source(g); + os_write_file(builtin_zig_path, contents); + } assert(g->root_package); assert(g->std_package); - g->compile_var_package = new_package(buf_ptr(&g->cache_dir), builtin_zig_basename); + g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename); g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); - g->compile_var_import = add_source_file(g, g->compile_var_package, resolved_path, contents); + g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents); scan_import(g, g->compile_var_import); + + return ErrorNone; } static void init(CodeGen *g) { if (g->module) return; - if (g->llvm_argv_len > 0) { const char **args = allocate_nonzero<const char *>(g->llvm_argv_len + 2); args[0] = "zig (LLVM option parsing)"; @@ -7207,7 +7244,11 @@ static void init(CodeGen *g) { g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease && g->build_mode != BuildModeSmallRelease; define_builtin_fns(g); - define_builtin_compile_vars(g); + Error err; + if ((err = define_builtin_compile_vars(g))) { + fprintf(stderr, "Unable to create builtin.zig: %s\n", err_str(err)); + exit(1); + } } void codegen_translate_c(CodeGen *g, Buf *full_path) { @@ -7253,8 +7294,8 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package Buf *resolved_path = buf_alloc(); *resolved_path = os_path_resolve(resolve_paths, 1); Buf *import_code = buf_alloc(); - int err; - if ((err = os_fetch_file_path(resolved_path, import_code, false))) { + Error err; + if ((err = file_fetch(g, resolved_path, import_code))) { zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err)); } @@ -7327,23 +7368,32 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { g->test_runner_import = add_special_code(g, g->test_runner_package, "test_runner.zig"); } -static void gen_root_source(CodeGen *g) { +static Buf *get_resolved_root_src_path(CodeGen *g) { + // TODO memoize if (buf_len(&g->root_package->root_src_path) == 0) - return; + return nullptr; - codegen_add_time_event(g, "Semantic Analysis"); - - Buf *rel_full_path = buf_alloc(); - os_path_join(&g->root_package->root_src_dir, &g->root_package->root_src_path, rel_full_path); + Buf rel_full_path = BUF_INIT; + os_path_join(&g->root_package->root_src_dir, &g->root_package->root_src_path, &rel_full_path); Buf *resolved_path = buf_alloc(); - Buf *resolve_paths[] = {rel_full_path}; + Buf *resolve_paths[] = {&rel_full_path}; *resolved_path = os_path_resolve(resolve_paths, 1); + return resolved_path; +} + +static void gen_root_source(CodeGen *g) { + Buf *resolved_path = get_resolved_root_src_path(g); + if (resolved_path == nullptr) + return; + Buf *source_code = buf_alloc(); int err; - if ((err = os_fetch_file_path(rel_full_path, source_code, true))) { - fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err)); + // No need for using the caching system for this file fetch because it is handled + // separately. + if ((err = os_fetch_file_path(resolved_path, source_code, true))) { + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(resolved_path), err_str(err)); exit(1); } @@ -7408,6 +7458,8 @@ static void gen_global_asm(CodeGen *g) { int err; for (size_t i = 0; i < g->assembly_files.length; i += 1) { Buf *asm_file = g->assembly_files.at(i); + // No need to use the caching system for these fetches because they + // are handled separately. if ((err = os_fetch_file_path(asm_file, &contents, false))) { zig_panic("Unable to read %s: %s", buf_ptr(asm_file), err_str(err)); } @@ -7448,7 +7500,6 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdErrorUnion: @@ -7627,7 +7678,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu case ZigTypeIdMetaType: case ZigTypeIdBoundFn: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: @@ -7671,19 +7721,11 @@ static Buf *preprocessor_mangle(Buf *src) { } static void gen_h_file(CodeGen *g) { - if (!g->want_h_file) - return; - GenH gen_h_data = {0}; GenH *gen_h = &gen_h_data; - codegen_add_time_event(g, "Generate .h"); - assert(!g->is_test_build); - - if (!g->out_h_path) { - g->out_h_path = buf_sprintf("%s.h", buf_ptr(g->root_out_name)); - } + assert(g->out_h_path != nullptr); FILE *out_h = fopen(buf_ptr(g->out_h_path), "wb"); if (!out_h) @@ -7788,7 +7830,6 @@ static void gen_h_file(CodeGen *g) { case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOptional: @@ -7886,14 +7927,231 @@ void codegen_add_time_event(CodeGen *g, const char *name) { g->timing_events.append({os_get_time(), name}); } -void codegen_build(CodeGen *g) { +static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) { + if (buf_len(&pkg->root_src_path) == 0) + return; + + Buf *rel_full_path = buf_alloc(); + os_path_join(&pkg->root_src_dir, &pkg->root_src_path, rel_full_path); + cache_file(ch, rel_full_path); + + auto it = pkg->package_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + cache_buf(ch, entry->key); + add_cache_pkg(g, ch, entry->value); + } +} + +// Called before init() +static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { + Error err; + + Buf *compiler_id; + if ((err = get_compiler_id(&compiler_id))) + return err; + + CacheHash *ch = &g->cache_hash; + cache_init(ch, manifest_dir); + + add_cache_pkg(g, ch, g->root_package); + if (g->linker_script != nullptr) { + cache_file(ch, buf_create_from_str(g->linker_script)); + } + cache_buf(ch, compiler_id); + cache_buf(ch, g->root_out_name); + cache_list_of_link_lib(ch, g->link_libs_list.items, g->link_libs_list.length); + cache_list_of_buf(ch, g->darwin_frameworks.items, g->darwin_frameworks.length); + cache_list_of_buf(ch, g->rpath_list.items, g->rpath_list.length); + cache_list_of_buf(ch, g->forbidden_libs.items, g->forbidden_libs.length); + cache_list_of_file(ch, g->link_objects.items, g->link_objects.length); + cache_list_of_file(ch, g->assembly_files.items, g->assembly_files.length); + cache_int(ch, g->emit_file_type); + cache_int(ch, g->build_mode); + cache_int(ch, g->out_type); + cache_int(ch, g->zig_target.arch.arch); + cache_int(ch, g->zig_target.arch.sub_arch); + cache_int(ch, g->zig_target.vendor); + cache_int(ch, g->zig_target.os); + cache_int(ch, g->zig_target.env_type); + cache_int(ch, g->zig_target.oformat); + cache_bool(ch, g->is_static); + cache_bool(ch, g->strip_debug_symbols); + cache_bool(ch, g->is_test_build); + cache_bool(ch, g->is_native_target); + cache_bool(ch, g->windows_subsystem_windows); + cache_bool(ch, g->windows_subsystem_console); + cache_bool(ch, g->linker_rdynamic); + cache_bool(ch, g->no_rosegment_workaround); + cache_bool(ch, g->each_lib_rpath); + cache_buf_opt(ch, g->mmacosx_version_min); + cache_buf_opt(ch, g->mios_version_min); + cache_usize(ch, g->version_major); + cache_usize(ch, g->version_minor); + cache_usize(ch, g->version_patch); + cache_buf_opt(ch, g->test_filter); + cache_buf_opt(ch, g->test_name_prefix); + cache_list_of_str(ch, g->llvm_argv, g->llvm_argv_len); + cache_list_of_str(ch, g->clang_argv, g->clang_argv_len); + cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length); + + buf_resize(digest, 0); + if ((err = cache_hit(ch, digest))) + return err; + + return ErrorNone; +} + +static void resolve_out_paths(CodeGen *g) { + Buf *o_basename = buf_create_from_buf(g->root_out_name); + + switch (g->emit_file_type) { + case EmitFileTypeBinary: + { + const char *o_ext = target_o_file_ext(&g->zig_target); + buf_append_str(o_basename, o_ext); + break; + } + case EmitFileTypeAssembly: + { + const char *asm_ext = target_asm_file_ext(&g->zig_target); + buf_append_str(o_basename, asm_ext); + break; + } + case EmitFileTypeLLVMIr: + { + const char *llvm_ir_ext = target_llvm_ir_file_ext(&g->zig_target); + buf_append_str(o_basename, llvm_ir_ext); + break; + } + default: + zig_unreachable(); + } + + if (g->enable_cache || g->out_type != OutTypeObj) { + os_path_join(&g->artifact_dir, o_basename, &g->o_file_output_path); + } else if (g->wanted_output_file_path != nullptr && g->out_type == OutTypeObj) { + buf_init_from_buf(&g->o_file_output_path, g->wanted_output_file_path); + } else { + buf_init_from_buf(&g->o_file_output_path, o_basename); + } + + if (g->out_type == OutTypeObj) { + buf_init_from_buf(&g->output_file_path, &g->o_file_output_path); + } else if (g->out_type == OutTypeExe) { + if (!g->enable_cache && g->wanted_output_file_path != nullptr) { + buf_init_from_buf(&g->output_file_path, g->wanted_output_file_path); + } else { + assert(g->root_out_name); + + Buf basename = BUF_INIT; + buf_init_from_buf(&basename, g->root_out_name); + buf_append_str(&basename, target_exe_file_ext(&g->zig_target)); + if (g->enable_cache || g->is_test_build) { + os_path_join(&g->artifact_dir, &basename, &g->output_file_path); + } else { + buf_init_from_buf(&g->output_file_path, &basename); + } + } + } else if (g->out_type == OutTypeLib) { + if (!g->enable_cache && g->wanted_output_file_path != nullptr) { + buf_init_from_buf(&g->output_file_path, g->wanted_output_file_path); + } else { + Buf basename = BUF_INIT; + buf_init_from_buf(&basename, g->root_out_name); + buf_append_str(&basename, target_lib_file_ext(&g->zig_target, g->is_static, + g->version_major, g->version_minor, g->version_patch)); + if (g->enable_cache) { + os_path_join(&g->artifact_dir, &basename, &g->output_file_path); + } else { + buf_init_from_buf(&g->output_file_path, &basename); + } + } + } else { + zig_unreachable(); + } + + if (g->want_h_file && !g->out_h_path) { + assert(g->root_out_name); + Buf *h_basename = buf_sprintf("%s.h", buf_ptr(g->root_out_name)); + if (g->enable_cache) { + g->out_h_path = buf_alloc(); + os_path_join(&g->artifact_dir, h_basename, g->out_h_path); + } else { + g->out_h_path = h_basename; + } + } +} + + +void codegen_build_and_link(CodeGen *g) { + Error err; assert(g->out_type != OutTypeUnknown); - init(g); - gen_global_asm(g); - gen_root_source(g); - do_code_gen(g); - gen_h_file(g); + Buf *stage1_dir = get_stage1_cache_path(); + Buf *artifact_dir = buf_alloc(); + Buf digest = BUF_INIT; + if (g->enable_cache) { + codegen_add_time_event(g, "Check Cache"); + + Buf *manifest_dir = buf_alloc(); + os_path_join(stage1_dir, buf_create_from_str("build"), manifest_dir); + + if ((err = check_cache(g, manifest_dir, &digest))) { + fprintf(stderr, "Unable to check cache: %s\n", err_str(err)); + exit(1); + } + + os_path_join(stage1_dir, buf_create_from_str("artifact"), artifact_dir); + } + + if (g->enable_cache && buf_len(&digest) != 0) { + os_path_join(artifact_dir, &digest, &g->artifact_dir); + resolve_out_paths(g); + } else { + init(g); + + codegen_add_time_event(g, "Semantic Analysis"); + + gen_global_asm(g); + gen_root_source(g); + + if (g->enable_cache) { + if ((err = cache_final(&g->cache_hash, &digest))) { + fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); + exit(1); + } + os_path_join(artifact_dir, &digest, &g->artifact_dir); + } else { + buf_init_from_buf(&g->artifact_dir, &g->cache_dir); + } + if ((err = os_make_path(&g->artifact_dir))) { + fprintf(stderr, "Unable to create artifact directory: %s\n", err_str(err)); + exit(1); + } + resolve_out_paths(g); + + codegen_add_time_event(g, "Code Generation"); + do_code_gen(g); + codegen_add_time_event(g, "LLVM Emit Output"); + zig_llvm_emit_output(g); + + if (g->want_h_file) { + codegen_add_time_event(g, "Generate .h"); + gen_h_file(g); + } + if (g->out_type != OutTypeObj) { + codegen_link(g); + } + } + + if (g->enable_cache) { + cache_release(&g->cache_hash); + } + codegen_add_time_event(g, "Done"); } PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path) { diff --git a/src/codegen.hpp b/src/codegen.hpp @@ -16,7 +16,6 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, Buf *zig_lib_dir); -void codegen_destroy(CodeGen *codegen); void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); @@ -47,11 +46,12 @@ void codegen_set_linker_script(CodeGen *g, const char *linker_script); void codegen_set_test_filter(CodeGen *g, Buf *filter); void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix); void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch); -void codegen_set_cache_dir(CodeGen *g, Buf cache_dir); void codegen_set_output_h_path(CodeGen *g, Buf *h_path); +void codegen_set_output_path(CodeGen *g, Buf *path); void codegen_add_time_event(CodeGen *g, const char *name); void codegen_print_timing_report(CodeGen *g, FILE *f); -void codegen_build(CodeGen *g); +void codegen_link(CodeGen *g); +void codegen_build_and_link(CodeGen *g); PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path); void codegen_add_assembly(CodeGen *g, Buf *path); diff --git a/src/compiler.cpp b/src/compiler.cpp @@ -0,0 +1,66 @@ +#include "cache_hash.hpp" + +#include <stdio.h> + +static Buf saved_compiler_id = BUF_INIT; +static Buf saved_app_data_dir = BUF_INIT; +static Buf saved_stage1_path = BUF_INIT; + +Buf *get_stage1_cache_path() { + if (saved_stage1_path.list.length != 0) { + return &saved_stage1_path; + } + Error err; + if ((err = os_get_app_data_dir(&saved_app_data_dir, "zig"))) { + fprintf(stderr, "Unable to get app data dir: %s\n", err_str(err)); + exit(1); + } + os_path_join(&saved_app_data_dir, buf_create_from_str("stage1"), &saved_stage1_path); + return &saved_stage1_path; +} + +Error get_compiler_id(Buf **result) { + if (saved_compiler_id.list.length != 0) { + *result = &saved_compiler_id; + return ErrorNone; + } + + Error err; + Buf *stage1_dir = get_stage1_cache_path(); + Buf *manifest_dir = buf_alloc(); + os_path_join(stage1_dir, buf_create_from_str("exe"), manifest_dir); + + CacheHash cache_hash; + CacheHash *ch = &cache_hash; + cache_init(ch, manifest_dir); + Buf self_exe_path = BUF_INIT; + if ((err = os_self_exe_path(&self_exe_path))) + return err; + + cache_file(ch, &self_exe_path); + + buf_resize(&saved_compiler_id, 0); + if ((err = cache_hit(ch, &saved_compiler_id))) + return err; + if (buf_len(&saved_compiler_id) != 0) { + cache_release(ch); + *result = &saved_compiler_id; + return ErrorNone; + } + ZigList<Buf *> lib_paths = {}; + if ((err = os_self_exe_shared_libs(lib_paths))) + return err; + for (size_t i = 0; i < lib_paths.length; i += 1) { + Buf *lib_path = lib_paths.at(i); + if ((err = cache_add_file(ch, lib_path))) + return err; + } + if ((err = cache_final(ch, &saved_compiler_id))) + return err; + + cache_release(ch); + + *result = &saved_compiler_id; + return ErrorNone; +} + diff --git a/src/compiler.hpp b/src/compiler.hpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2018 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_COMPILER_HPP +#define ZIG_COMPILER_HPP + +#include "buffer.hpp" +#include "error.hpp" + +Buf *get_stage1_cache_path(); +Error get_compiler_id(Buf **result); + +#endif diff --git a/src/error.cpp b/src/error.cpp @@ -27,6 +27,11 @@ const char *err_str(int err) { case ErrorNegativeDenominator: return "negative denominator"; case ErrorShiftedOutOneBits: return "exact shift shifted out one bits"; case ErrorCCompileErrors: return "C compile errors"; + case ErrorEndOfFile: return "end of file"; + case ErrorIsDir: return "is directory"; + case ErrorUnsupportedOperatingSystem: return "unsupported operating system"; + case ErrorSharingViolation: return "sharing violation"; + case ErrorPipeBusy: return "pipe busy"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp @@ -27,6 +27,11 @@ enum Error { ErrorNegativeDenominator, ErrorShiftedOutOneBits, ErrorCCompileErrors, + ErrorEndOfFile, + ErrorIsDir, + ErrorUnsupportedOperatingSystem, + ErrorSharingViolation, + ErrorPipeBusy, }; const char *err_str(int err); diff --git a/src/ir.cpp b/src/ir.cpp @@ -40,6 +40,7 @@ struct IrAnalyze { enum ConstCastResultId { ConstCastResultIdOk, + ConstCastResultIdInvalid, ConstCastResultIdErrSet, ConstCastResultIdErrSetGlobal, ConstCastResultIdPointerChild, @@ -1029,12 +1030,6 @@ static IrInstruction *ir_create_const_fn(IrBuilder *irb, Scope *scope, AstNode * return &const_instruction->base; } -static IrInstruction *ir_build_const_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry) { - IrInstruction *instruction = ir_create_const_fn(irb, scope, source_node, fn_entry); - ir_instruction_append(irb->current_basic_block, instruction); - return instruction; -} - static IrInstruction *ir_build_const_import(IrBuilder *irb, Scope *scope, AstNode *source_node, ImportTableEntry *import) { IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_namespace; @@ -1043,16 +1038,6 @@ static IrInstruction *ir_build_const_import(IrBuilder *irb, Scope *scope, AstNod return &const_instruction->base; } -static IrInstruction *ir_build_const_scope(IrBuilder *irb, Scope *parent_scope, AstNode *source_node, - Scope *target_scope) -{ - IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, parent_scope, source_node); - const_instruction->base.value.type = irb->codegen->builtin_types.entry_block; - const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_block = target_scope; - return &const_instruction->base; -} - static IrInstruction *ir_build_const_bool(IrBuilder *irb, Scope *scope, AstNode *source_node, bool value) { IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_bool; @@ -1577,13 +1562,11 @@ static IrInstruction *ir_build_set_runtime_safety(IrBuilder *irb, Scope *scope, } static IrInstruction *ir_build_set_float_mode(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *scope_value, IrInstruction *mode_value) + IrInstruction *mode_value) { IrInstructionSetFloatMode *instruction = ir_build_instruction<IrInstructionSetFloatMode>(irb, scope, source_node); - instruction->scope_value = scope_value; instruction->mode_value = mode_value; - ir_ref_instruction(scope_value, irb->current_basic_block); ir_ref_instruction(mode_value, irb->current_basic_block); return &instruction->base; @@ -3894,6 +3877,21 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); } +static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *node) { + for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) { + if (it_scope->id == ScopeIdDecls) { + ScopeDecls *decls_scope = (ScopeDecls *)it_scope; + ZigType *container_type = decls_scope->container_type; + if (container_type != nullptr) { + return ir_build_const_type(irb, orig_scope, node, container_type); + } else { + return ir_build_const_import(irb, orig_scope, node, decls_scope->import); + } + } + } + zig_unreachable(); +} + static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeFnCallExpr); @@ -3959,12 +3957,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_instruction) - return arg1_value; - - IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value); + IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, set_float_mode, lval); } case BuiltinFnIdSizeof: @@ -4837,6 +4830,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node); return ir_lval_wrap(irb, scope, opaque_type, lval); } + case BuiltinFnIdThis: + { + IrInstruction *this_inst = ir_gen_this(irb, scope, node); + return ir_lval_wrap(irb, scope, this_inst, lval); + } case BuiltinFnIdSetAlignStack: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -5688,33 +5686,6 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } -static IrInstruction *ir_gen_this_literal(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeThisLiteral); - - if (!scope->parent) - return ir_build_const_import(irb, scope, node, node->owner); - - ZigFn *fn_entry = scope_get_fn_if_root(scope); - if (fn_entry) - return ir_build_const_fn(irb, scope, node, fn_entry); - - while (scope->id != ScopeIdBlock && scope->id != ScopeIdDecls) { - scope = scope->parent; - } - - if (scope->id == ScopeIdDecls) { - ScopeDecls *decls_scope = (ScopeDecls *)scope; - ZigType *container_type = decls_scope->container_type; - assert(container_type); - return ir_build_const_type(irb, scope, node, container_type); - } - - if (scope->id == ScopeIdBlock) - return ir_build_const_scope(irb, scope, node, scope); - - zig_unreachable(); -} - static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBoolLiteral); return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value); @@ -7292,8 +7263,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); } - case NodeTypeThisLiteral: - return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); case NodeTypeBoolLiteral: return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); case NodeTypeArrayType: @@ -7522,8 +7491,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec if (type_has_bits(return_type)) { IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, - false, false, PtrLenUnknown, get_abi_alignment(irb->codegen, irb->codegen->builtin_types.entry_u8), - 0, 0)); + false, false, PtrLenUnknown, 0, 0, 0)); IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, @@ -7576,8 +7544,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, - false, false, PtrLenUnknown, get_abi_alignment(irb->codegen, irb->codegen->builtin_types.entry_u8), - 0, 0)); + false, false, PtrLenUnknown, 0, 0, 0)); IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); @@ -8548,6 +8515,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted ConstCastOnly result = {}; result.id = ConstCastResultIdOk; + Error err; + if (wanted_type == actual_type) return result; @@ -8560,6 +8529,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable); + if (child.id == ConstCastResultIdInvalid) + return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdNullWrapPtr; result.data.null_wrap_ptr_child = allocate_nonzero<ConstCastOnly>(1); @@ -8576,7 +8547,6 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) { - assert(actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment); return result; } @@ -8584,6 +8554,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const); + if (child.id == ConstCastResultIdInvalid) + return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdPointerChild; result.data.pointer_mismatch = allocate_nonzero<ConstCastPointerMismatch>(1); @@ -8592,12 +8564,20 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type; return result; } + if ((err = type_resolve(g, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { + result.id = ConstCastResultIdInvalid; + return result; + } + if ((err = type_resolve(g, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { + result.id = ConstCastResultIdInvalid; + return result; + } if ((actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) && (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) && actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset && actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count && - actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment) + get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type)) { return result; } @@ -8607,14 +8587,24 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted if (is_slice(wanted_type) && is_slice(actual_type)) { ZigType *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; ZigType *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; + if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { + result.id = ConstCastResultIdInvalid; + return result; + } + if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { + result.id = ConstCastResultIdInvalid; + return result; + } if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) && actual_ptr_type->data.pointer.bit_offset == wanted_ptr_type->data.pointer.bit_offset && actual_ptr_type->data.pointer.unaligned_bit_count == wanted_ptr_type->data.pointer.unaligned_bit_count && - actual_ptr_type->data.pointer.alignment >= wanted_ptr_type->data.pointer.alignment) + get_ptr_align(g, actual_ptr_type) >= get_ptr_align(g, wanted_ptr_type)) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type, actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); + if (child.id == ConstCastResultIdInvalid) + return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdSliceChild; result.data.slice_mismatch = allocate_nonzero<ConstCastSliceMismatch>(1); @@ -8630,6 +8620,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted if (wanted_type->id == ZigTypeIdOptional && actual_type->id == ZigTypeIdOptional) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type->data.maybe.child_type, source_node, wanted_is_mutable); + if (child.id == ConstCastResultIdInvalid) + return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdOptionalChild; result.data.optional = allocate_nonzero<ConstCastOptionalMismatch>(1); @@ -8644,6 +8636,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorUnion) { ConstCastOnly payload_child = types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type->data.error_union.payload_type, source_node, wanted_is_mutable); + if (payload_child.id == ConstCastResultIdInvalid) + return payload_child; if (payload_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdErrorUnionPayload; result.data.error_union_payload = allocate_nonzero<ConstCastErrUnionPayloadMismatch>(1); @@ -8654,6 +8648,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted } ConstCastOnly error_set_child = types_match_const_cast_only(ira, wanted_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type, source_node, wanted_is_mutable); + if (error_set_child.id == ConstCastResultIdInvalid) + return error_set_child; if (error_set_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdErrorUnionErrorSet; result.data.error_union_error_set = allocate_nonzero<ConstCastErrUnionErrSetMismatch>(1); @@ -8741,6 +8737,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type, source_node, false); + if (child.id == ConstCastResultIdInvalid) + return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdFnReturnType; result.data.return_type = allocate_nonzero<ConstCastOnly>(1); @@ -8753,6 +8751,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted actual_type->data.fn.fn_type_id.async_allocator_type, wanted_type->data.fn.fn_type_id.async_allocator_type, source_node, false); + if (child.id == ConstCastResultIdInvalid) + return child; if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdAsyncAllocatorType; result.data.async_allocator_type = allocate_nonzero<ConstCastOnly>(1); @@ -8777,6 +8777,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, expected_param_info->type, source_node, false); + if (arg_child.id == ConstCastResultIdInvalid) + return arg_child; if (arg_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdFnArg; result.data.fn_arg.arg_index = i; @@ -9270,7 +9272,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdUnion && (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) { - if ((err = type_ensure_zero_bits_known(ira->codegen, cur_type))) + if ((err = type_resolve(ira->codegen, cur_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (cur_type->data.unionation.tag_type == prev_type) { continue; @@ -9280,7 +9282,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdUnion && (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) { - if ((err = type_ensure_zero_bits_known(ira->codegen, prev_type))) + if ((err = type_resolve(ira->codegen, prev_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (prev_type->data.unionation.tag_type == cur_type) { prev_inst = cur_inst; @@ -9306,8 +9308,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT ZigType *ptr_type = get_pointer_to_type_extra( ira->codegen, prev_inst->value.type->data.array.child_type, true, false, PtrLenUnknown, - get_abi_alignment(ira->codegen, prev_inst->value.type->data.array.child_type), - 0, 0); + 0, 0, 0); ZigType *slice_type = get_slice_type(ira->codegen, ptr_type); if (err_set_type != nullptr) { return get_error_union_type(ira->codegen, err_set_type, slice_type); @@ -9504,7 +9505,16 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInstruction *value, ZigType *wanted_type) { assert(value->value.type->id == ZigTypeIdPointer); - wanted_type = adjust_ptr_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment); + + Error err; + + if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_instruction; + } + + wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); if (instr_is_comptime(value)) { ConstExprValue *pointee = ir_const_ptr_pointee(ira, &value->value, source_instr->source_node); @@ -9532,7 +9542,15 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { - wanted_type = adjust_slice_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment); + Error err; + + if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_instruction; + } + + wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); if (instr_is_comptime(value)) { ConstExprValue *pointee = ir_const_ptr_pointee(ira, &value->value, source_instr->source_node); @@ -9719,8 +9737,7 @@ static ZigType *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction, ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile) { IrInstruction *const_instr = ir_get_const_ptr(ira, instruction, pointee, - pointee_type, ptr_mut, ptr_is_const, ptr_is_volatile, - get_abi_alignment(ira->codegen, pointee_type)); + pointee_type, ptr_mut, ptr_is_const, ptr_is_volatile, 0); ir_link_new_instruction(const_instr, instruction); return const_instr->value.type; } @@ -10037,20 +10054,24 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value, bool is_const, bool is_volatile) { + Error err; + if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; + if ((err = type_resolve(ira->codegen, value->value.type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_instruction; + if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefOk); if (!val) return ira->codegen->invalid_instruction; return ir_get_const_ptr(ira, source_instruction, val, value->value.type, - ConstPtrMutComptimeConst, is_const, is_volatile, - get_abi_alignment(ira->codegen, value->value.type)); + ConstPtrMutComptimeConst, is_const, is_volatile, 0); } ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, - is_const, is_volatile, PtrLenSingle, get_abi_alignment(ira->codegen, value->value.type), 0, 0); + is_const, is_volatile, PtrLenSingle, 0, 0, 0); IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope, source_instruction->source_node, value, is_const, is_volatile); new_instruction->value.type = ptr_type; @@ -10113,7 +10134,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour IrInstruction *target, ZigType *wanted_type) { Error err; - assert(wanted_type->id == ZigTypeIdInt); + assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt); ZigType *actual_type = target->value.type; if ((err = ensure_complete_type(ira->codegen, actual_type))) @@ -10139,6 +10160,18 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour return result; } + // If there is only one possible tag, then we know at comptime what it is. + if (actual_type->data.enumeration.layout == ContainerLayoutAuto && + actual_type->data.enumeration.src_field_count == 1) + { + assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int); + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + init_const_bigint(&result->value, wanted_type, + &actual_type->data.enumeration.fields[0].value); + return result; + } + IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, source_instr->source_node, target); result->value.type = wanted_type; @@ -10164,6 +10197,19 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou return result; } + // If there is only 1 possible tag, then we know at comptime what it is. + if (wanted_type->data.enumeration.layout == ContainerLayoutAuto && + wanted_type->data.enumeration.src_field_count == 1) + { + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + result->value.special = ConstValSpecialStatic; + result->value.type = wanted_type; + TypeEnumField *enum_field = target->value.type->data.unionation.fields[0].enum_field; + bigint_init_bigint(&result->value.data.x_enum_tag, &enum_field->value); + return result; + } + IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope, source_instr->source_node, target); result->value.type = wanted_type; @@ -10192,9 +10238,9 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so return ira->codegen->invalid_instruction; TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag); assert(union_field != nullptr); - if ((err = type_ensure_zero_bits_known(ira->codegen, union_field->type_entry))) + if ((err = type_resolve(ira->codegen, union_field->type_entry, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - if (!union_field->type_entry->zero_bits) { + if (type_has_bits(union_field->type_entry)) { AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at( union_field->enum_field->decl_index); ErrorMsg *msg = ir_add_error(ira, source_instr, @@ -10497,7 +10543,10 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdPointer); - wanted_type = adjust_ptr_align(ira->codegen, wanted_type, target->value.type->data.pointer.alignment); + Error err; + if ((err = type_resolve(ira->codegen, target->value.type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_instruction; + wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, target->value.type)); ZigType *array_type = wanted_type->data.pointer.child_type; assert(array_type->id == ZigTypeIdArray); assert(array_type->data.array.len == 1); @@ -10544,6 +10593,8 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa switch (cast_result->id) { case ConstCastResultIdOk: zig_unreachable(); + case ConstCastResultIdInvalid: + zig_unreachable(); case ConstCastResultIdOptionalChild: { ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("optional type child '%s' cannot cast into optional type child '%s'", @@ -10643,6 +10694,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // perfect match or non-const to const ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, source_node, false); + if (const_cast_result.id == ConstCastResultIdInvalid) + return ira->codegen->invalid_instruction; if (const_cast_result.id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } @@ -10758,13 +10811,19 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.pointer.ptr_len == PtrLenUnknown && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == ZigTypeIdArray && - actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment && - types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node, - !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + actual_type->data.pointer.child_type->id == ZigTypeIdArray) { - return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); + if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_instruction; + if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_instruction; + if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type) && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); + } } // *[N]T to []T @@ -10818,16 +10877,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_child_type->data.pointer.ptr_len == PtrLenUnknown && actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == ZigTypeIdArray && - actual_type->data.pointer.alignment >= wanted_child_type->data.pointer.alignment && - types_match_const_cast_only(ira, wanted_child_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node, - !wanted_child_type->data.pointer.is_const).id == ConstCastResultIdOk) + actual_type->data.pointer.child_type->id == ZigTypeIdArray) { - IrInstruction *cast1 = ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_child_type); - if (type_is_invalid(cast1->value.type)) + if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; - return ir_analyze_maybe_wrap(ira, source_instr, cast1, wanted_type); + if ((err = type_resolve(ira->codegen, wanted_child_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_instruction; + if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_child_type) && + types_match_const_cast_only(ira, wanted_child_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node, + !wanted_child_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + IrInstruction *cast1 = ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, + wanted_child_type); + if (type_is_invalid(cast1->value.type)) + return ira->codegen->invalid_instruction; + return ir_analyze_maybe_wrap(ira, source_instr, cast1, wanted_type); + } } } @@ -10970,7 +11036,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // cast from union to the enum type of the union if (actual_type->id == ZigTypeIdUnion && wanted_type->id == ZigTypeIdEnum) { - if ((err = type_ensure_zero_bits_known(ira->codegen, actual_type))) + if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; if (actual_type->data.unionation.tag_type == wanted_type) { @@ -10983,7 +11049,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) { - if ((err = type_ensure_zero_bits_known(ira->codegen, wanted_type))) + if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; if (wanted_type->data.unionation.tag_type == actual_type) { @@ -10997,7 +11063,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr) { - if ((err = type_ensure_zero_bits_known(ira->codegen, union_type))) + if ((err = type_resolve(ira->codegen, union_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; if (union_type->data.unionation.tag_type == actual_type) { @@ -11024,14 +11090,24 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { - if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) { + if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_instruction; + } + if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_instruction; + } + uint32_t wanted_align = get_ptr_align(ira->codegen, wanted_type); + uint32_t actual_align = get_ptr_align(ira->codegen, actual_type); + if (wanted_align > actual_align) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); add_error_note(ira->codegen, msg, value->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), - actual_type->data.pointer.alignment)); + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), actual_align)); add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), - wanted_type->data.pointer.alignment)); + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), wanted_align)); return ira->codegen->invalid_instruction; } return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type); @@ -11043,7 +11119,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { - if ((err = type_ensure_zero_bits_known(ira->codegen, actual_type))) { + if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) { return ira->codegen->invalid_instruction; } if (!type_has_bits(actual_type)) { @@ -11289,8 +11365,7 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { return nullptr; ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, - get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); + true, false, PtrLenUnknown, 0, 0, 0); ZigType *str_type = get_slice_type(ira->codegen, ptr_type); IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type); if (type_is_invalid(casted_value->value.type)) @@ -11580,8 +11655,6 @@ static ZigType *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op ZigType *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; - if ((err = type_ensure_zero_bits_known(ira->codegen, resolved_type))) - return resolved_type; bool operator_allowed; switch (resolved_type->id) { @@ -11603,7 +11676,6 @@ static ZigType *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op case ZigTypeIdFn: case ZigTypeIdOpaque: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: @@ -11638,6 +11710,9 @@ static ZigType *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; + if ((err = type_resolve(ira->codegen, resolved_type, ResolveStatusZeroBitsKnown))) + return resolved_type; + bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type); if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); @@ -12324,7 +12399,7 @@ static ZigType *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruc out_array_val = out_val; } else if (is_slice(op1_type) || is_slice(op2_type)) { ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, - true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, child_type), 0, 0); + true, false, PtrLenUnknown, 0, 0, 0); result_type = get_slice_type(ira->codegen, ptr_type); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; @@ -12345,8 +12420,7 @@ static ZigType *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruc new_len += 1; // null byte // TODO make this `[*]null T` instead of `[*]T` - result_type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, - PtrLenUnknown, get_abi_alignment(ira->codegen, child_type), 0, 0); + result_type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; @@ -12444,10 +12518,22 @@ static ZigType *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp * if (type_is_invalid(op1_type)) return ira->codegen->builtin_types.entry_invalid; + if (op1_type->id != ZigTypeIdErrorSet) { + ir_add_error(ira, instruction->op1, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&op1_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + ZigType *op2_type = ir_resolve_type(ira, instruction->op2->other); if (type_is_invalid(op2_type)) return ira->codegen->builtin_types.entry_invalid; + if (op2_type->id != ZigTypeIdErrorSet) { + ir_add_error(ira, instruction->op2, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&op2_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_global_error_set(op1_type) || type_is_global_error_set(op2_type)) { @@ -12559,7 +12645,7 @@ static ZigType *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDec if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; } else { - if ((err = type_ensure_zero_bits_known(ira->codegen, result_type))) { + if ((err = type_resolve(ira->codegen, result_type, ResolveStatusZeroBitsKnown))) { result_type = ira->codegen->builtin_types.entry_invalid; } } @@ -12627,6 +12713,11 @@ static ZigType *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDec } if (decl_var_instruction->align_value == nullptr) { + if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) { + var->value->type = ira->codegen->builtin_types.entry_invalid; + decl_var_instruction->base.other = &decl_var_instruction->base; + return ira->codegen->builtin_types.entry_void; + } var->align_bytes = get_abi_alignment(ira->codegen, result_type); } else { if (!ir_resolve_align(ira, decl_var_instruction->align_value->other, &var->align_bytes)) { @@ -12792,7 +12883,6 @@ static ZigType *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExpor case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: @@ -12817,7 +12907,6 @@ static ZigType *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExpor case ZigTypeIdErrorSet: zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name)); case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: @@ -13098,7 +13187,6 @@ static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) { static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) { - Error err; while (var->next_var != nullptr) { var = var->next_var; } @@ -13156,8 +13244,6 @@ no_mem_slot: instruction->scope, instruction->source_node, var); var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); - if ((err = type_ensure_zero_bits_known(ira->codegen, var->value->type))) - return ira->codegen->invalid_instruction; bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; @@ -13354,8 +13440,7 @@ static ZigType *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instr IrInstruction *casted_new_stack = nullptr; if (call_instruction->new_stack != nullptr) { ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - false, false, PtrLenUnknown, - get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); + false, false, PtrLenUnknown, 0, 0, 0); ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); IrInstruction *new_stack = call_instruction->new_stack->other; if (type_is_invalid(new_stack->value.type)) @@ -13534,7 +13619,7 @@ static ZigType *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instr inst_fn_type_id.return_type = specified_return_type; } - if ((err = type_ensure_zero_bits_known(ira->codegen, specified_return_type))) + if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (type_requires_comptime(specified_return_type)) { @@ -13875,7 +13960,6 @@ static ZigType *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instru case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: @@ -14211,7 +14295,7 @@ static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { ptr_type->data.pointer.child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, ptr_len, - ptr_type->data.pointer.alignment, + ptr_type->data.pointer.explicit_alignment, ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count); } @@ -14263,7 +14347,7 @@ static ZigType *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionEle return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, elem_ptr_instruction->ptr_len, - ptr_type->data.pointer.alignment, 0, 0); + ptr_type->data.pointer.explicit_alignment, 0, 0); } else { uint64_t elem_val_scalar; if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar)) @@ -14335,7 +14419,7 @@ static ZigType *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionEle uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type); uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type); - uint64_t ptr_align = return_type->data.pointer.alignment; + uint64_t ptr_align = get_ptr_align(ira->codegen, return_type); if (instr_is_comptime(casted_elem_index)) { uint64_t index = bigint_as_unsigned(&casted_elem_index->value.data.x_bigint); if (array_type->id == ZigTypeIdArray) { @@ -14652,9 +14736,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ } ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, - is_const, is_volatile, - PtrLenSingle, - get_abi_alignment(ira->codegen, field_type), 0, 0); + is_const, is_volatile, PtrLenSingle, 0, 0, 0); IrInstruction *result = ir_get_const(ira, source_instr); ConstExprValue *const_val = &result->value; @@ -14668,7 +14750,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, - PtrLenSingle, get_abi_alignment(ira->codegen, field->type_entry), 0, 0); + PtrLenSingle, 0, 0, 0); return result; } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, @@ -15001,9 +15083,14 @@ static ZigType *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFi } else if (buf_eql_str(field_name, "alignment")) { bool ptr_is_const = true; bool ptr_is_volatile = false; + if ((err = type_resolve(ira->codegen, child_type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->builtin_types.entry_invalid; + } return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int, - child_type->data.pointer.alignment, false), + get_ptr_align(ira->codegen, child_type), false), ira->codegen->builtin_types.entry_num_lit_int, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { @@ -15233,7 +15320,6 @@ static ZigType *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeO case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdMetaType: case ZigTypeIdVoid: @@ -15342,6 +15428,7 @@ static ZigType *ir_analyze_instruction_set_cold(IrAnalyze *ira, IrInstructionSet ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; } + static ZigType *ir_analyze_instruction_set_runtime_safety(IrAnalyze *ira, IrInstructionSetRuntimeSafety *set_runtime_safety_instruction) { @@ -15402,14 +15489,6 @@ static ZigType *ir_analyze_instruction_set_runtime_safety(IrAnalyze *ira, static ZigType *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, IrInstructionSetFloatMode *instruction) { - IrInstruction *target_instruction = instruction->scope_value->other; - ZigType *target_type = target_instruction->value.type; - if (type_is_invalid(target_type)) - return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *target_val = ir_resolve_const(ira, target_instruction, UndefBad); - if (!target_val) - return ira->codegen->builtin_types.entry_invalid; - if (ira->new_irb.exec->is_inline) { // ignore setFloatMode when running functions at compile time ir_build_const_from(ira, &instruction->base); @@ -15418,40 +15497,34 @@ static ZigType *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, bool *fast_math_on_ptr; AstNode **fast_math_set_node_ptr; - if (target_type->id == ZigTypeIdBlock) { - ScopeBlock *block_scope = (ScopeBlock *)target_val->data.x_block; - fast_math_on_ptr = &block_scope->fast_math_on; - fast_math_set_node_ptr = &block_scope->fast_math_set_node; - } else if (target_type->id == ZigTypeIdFn) { - assert(target_val->data.x_ptr.special == ConstPtrSpecialFunction); - ZigFn *target_fn = target_val->data.x_ptr.data.fn.fn_entry; - assert(target_fn->def_scope); - fast_math_on_ptr = &target_fn->def_scope->fast_math_on; - fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node; - } else if (target_type->id == ZigTypeIdMetaType) { - ScopeDecls *decls_scope; - ZigType *type_arg = target_val->data.x_type; - if (type_arg->id == ZigTypeIdStruct) { - decls_scope = type_arg->data.structure.decls_scope; - } else if (type_arg->id == ZigTypeIdEnum) { - decls_scope = type_arg->data.enumeration.decls_scope; - } else if (type_arg->id == ZigTypeIdUnion) { - decls_scope = type_arg->data.unionation.decls_scope; + + Scope *scope = instruction->base.scope; + while (scope != nullptr) { + if (scope->id == ScopeIdBlock) { + ScopeBlock *block_scope = (ScopeBlock *)scope; + fast_math_on_ptr = &block_scope->fast_math_on; + fast_math_set_node_ptr = &block_scope->fast_math_set_node; + break; + } else if (scope->id == ScopeIdFnDef) { + ScopeFnDef *def_scope = (ScopeFnDef *)scope; + ZigFn *target_fn = def_scope->fn_entry; + assert(target_fn->def_scope != nullptr); + fast_math_on_ptr = &target_fn->def_scope->fast_math_on; + fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node; + break; + } else if (scope->id == ScopeIdDecls) { + ScopeDecls *decls_scope = (ScopeDecls *)scope; + fast_math_on_ptr = &decls_scope->fast_math_on; + fast_math_set_node_ptr = &decls_scope->fast_math_set_node; + break; } else { - ir_add_error_node(ira, target_instruction->source_node, - buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&type_arg->name))); - return ira->codegen->builtin_types.entry_invalid; + scope = scope->parent; + continue; } - fast_math_on_ptr = &decls_scope->fast_math_on; - fast_math_set_node_ptr = &decls_scope->fast_math_set_node; - } else { - ir_add_error_node(ira, target_instruction->source_node, - buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&target_type->name))); - return ira->codegen->builtin_types.entry_invalid; } + assert(scope != nullptr); IrInstruction *float_mode_value = instruction->mode_value->other; - FloatMode float_mode_scalar; if (!ir_resolve_float_mode(ira, float_mode_value, &float_mode_scalar)) return ira->codegen->builtin_types.entry_invalid; @@ -15474,7 +15547,7 @@ static ZigType *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstructionSliceType *slice_type_instruction) { Error err; - uint32_t align_bytes; + uint32_t align_bytes = 0; if (slice_type_instruction->align_value != nullptr) { if (!ir_resolve_align(ira, slice_type_instruction->align_value->other, &align_bytes)) return ira->codegen->builtin_types.entry_invalid; @@ -15484,12 +15557,6 @@ static ZigType *ir_analyze_instruction_slice_type(IrAnalyze *ira, if (type_is_invalid(child_type)) return ira->codegen->builtin_types.entry_invalid; - if (slice_type_instruction->align_value == nullptr) { - if ((err = type_ensure_zero_bits_known(ira->codegen, child_type))) - return ira->codegen->builtin_types.entry_invalid; - align_bytes = get_abi_alignment(ira->codegen, child_type); - } - bool is_const = slice_type_instruction->is_const; bool is_volatile = slice_type_instruction->is_volatile; @@ -15499,7 +15566,6 @@ static ZigType *ir_analyze_instruction_slice_type(IrAnalyze *ira, case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdBlock: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: ir_add_error_node(ira, slice_type_instruction->base.source_node, @@ -15525,7 +15591,7 @@ static ZigType *ir_analyze_instruction_slice_type(IrAnalyze *ira, case ZigTypeIdBoundFn: case ZigTypeIdPromise: { - if ((err = type_ensure_zero_bits_known(ira->codegen, child_type))) + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0); @@ -15610,7 +15676,6 @@ static ZigType *ir_analyze_instruction_array_type(IrAnalyze *ira, case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdBlock: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: ir_add_error_node(ira, array_type_instruction->base.source_node, @@ -15681,7 +15746,6 @@ static ZigType *ir_analyze_instruction_size_of(IrAnalyze *ira, case ZigTypeIdUnreachable: case ZigTypeIdUndefined: case ZigTypeIdNull: - case ZigTypeIdBlock: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: case ZigTypeIdBoundFn: @@ -15767,9 +15831,7 @@ static ZigType *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, } ZigType *child_type = type_entry->data.maybe.child_type; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - PtrLenSingle, - get_abi_alignment(ira->codegen, child_type), 0, 0); + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); @@ -16139,7 +16201,7 @@ static ZigType *ir_analyze_instruction_switch_target(IrAnalyze *ira, return tag_type; } case ZigTypeIdEnum: { - if ((err = type_ensure_zero_bits_known(ira->codegen, target_type))) + if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (target_type->data.enumeration.src_field_count < 2) { TypeEnumField *only_field = &target_type->data.enumeration.fields[0]; @@ -16167,7 +16229,6 @@ static ZigType *ir_analyze_instruction_switch_target(IrAnalyze *ira, case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdOptional: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: @@ -16231,6 +16292,8 @@ static ZigType *ir_analyze_instruction_union_tag(IrAnalyze *ira, IrInstructionUn } static ZigType *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructionImport *import_instruction) { + Error err; + IrInstruction *name_value = import_instruction->name->other; Buf *import_target_str = ir_resolve_str(ira, name_value); if (!import_target_str) @@ -16274,8 +16337,7 @@ static ZigType *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructionImpor return ira->codegen->builtin_types.entry_namespace; } - int err; - if ((err = os_fetch_file_path(resolved_path, import_code, true))) { + if ((err = file_fetch(ira->codegen, resolved_path, import_code))) { if (err == ErrorFileNotFound) { ir_add_error_node(ira, source_node, buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); @@ -16286,6 +16348,7 @@ static ZigType *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructionImpor return ira->codegen->builtin_types.entry_invalid; } } + ImportTableEntry *target_import = add_source_file(ira->codegen, target_package, resolved_path, import_code); scan_import(ira->codegen, target_import); @@ -16367,7 +16430,7 @@ static ZigType *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruc if (casted_field_value == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; - if ((err = type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type))) + if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); @@ -16686,7 +16749,6 @@ static ZigType *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_instruc case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: @@ -16768,7 +16830,7 @@ static ZigType *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErr return ira->codegen->builtin_types.entry_invalid; ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); + true, false, PtrLenUnknown, 0, 0, 0); ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); if (casted_value->value.special == ConstValSpecialStatic) { ErrorTableEntry *err = casted_value->value.data.x_err_set; @@ -16795,7 +16857,7 @@ static ZigType *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructi assert(target->value.type->id == ZigTypeIdEnum); if (instr_is_comptime(target)) { - if ((err = type_ensure_zero_bits_known(ira->codegen, target->value.type))) + if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint); ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name); @@ -16810,8 +16872,7 @@ static ZigType *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructi ZigType *u8_ptr_type = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, - get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), - 0, 0); + 0, 0, 0); result->value.type = get_slice_type(ira->codegen, u8_ptr_type); return result->value.type; } @@ -17174,8 +17235,7 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco ZigType *u8_ptr = get_pointer_to_type_extra( ira->codegen, ira->codegen->builtin_types.entry_u8, true, false, PtrLenUnknown, - get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), - 0, 0); + 0, 0, 0); fn_def_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { fn_def_fields[6].data.x_optional = create_const_vals(1); @@ -17295,7 +17355,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty ensure_field_index(result->type, "alignment", 3); fields[3].special = ConstValSpecialStatic; fields[3].type = get_int_type(ira->codegen, false, 29); - bigint_init_unsigned(&fields[3].data.x_bigint, attrs_type->data.pointer.alignment); + bigint_init_unsigned(&fields[3].data.x_bigint, get_ptr_align(ira->codegen, attrs_type)); // child: type ensure_field_index(result->type, "child", 4); fields[4].special = ConstValSpecialStatic; @@ -17349,7 +17409,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: *out = nullptr; @@ -17959,6 +18018,12 @@ static ZigType *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTy } static ZigType *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) { + if (ira->codegen->enable_cache) { + ir_add_error(ira, &instruction->base, + buf_sprintf("TODO @cImport is incompatible with --cache on. The cache system currently is unable to detect subsequent changes in .h files.")); + return ira->codegen->builtin_types.entry_invalid; + } + AstNode *node = instruction->base.source_node; assert(node->type == NodeTypeFnCallExpr); AstNode *block_node = node->data.fn_call_expr.params.at(0); @@ -18105,7 +18170,7 @@ static ZigType *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstructionE // load from file system into const expr Buf *file_contents = buf_alloc(); int err; - if ((err = os_fetch_file_path(&file_path, file_contents, false))) { + if ((err = file_fetch(ira->codegen, &file_path, file_contents))) { if (err == ErrorFileNotFound) { ir_add_error(ira, instruction->name, buf_sprintf("unable to find '%s'", buf_ptr(&file_path))); return ira->codegen->builtin_types.entry_invalid; @@ -18115,9 +18180,6 @@ static ZigType *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstructionE } } - // TODO add dependency on the file we embedded so that we know if it changes - // we'll have to invalidate the cache - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); init_const_str_lit(ira->codegen, out_val, file_contents); @@ -18383,7 +18445,21 @@ static ZigType *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructio return dest_type; } +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { + Error err; + + if (ty->id == ZigTypeIdPointer) { + if ((err = type_resolve(ira->codegen, ty->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return err; + } + + *result_align = get_ptr_align(ira->codegen, ty); + return ErrorNone; +} + static ZigType *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { + Error err; + ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->other); if (type_is_invalid(dest_child_type)) return ira->codegen->builtin_types.entry_invalid; @@ -18398,15 +18474,23 @@ static ZigType *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionF if (target->value.type->id == ZigTypeIdPointer) { src_ptr_const = target->value.type->data.pointer.is_const; src_ptr_volatile = target->value.type->data.pointer.is_volatile; - src_ptr_align = target->value.type->data.pointer.alignment; + + if ((err = resolve_ptr_align(ira, target->value.type, &src_ptr_align))) + return ira->codegen->builtin_types.entry_invalid; } else if (is_slice(target->value.type)) { ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; src_ptr_const = src_ptr_type->data.pointer.is_const; src_ptr_volatile = src_ptr_type->data.pointer.is_volatile; - src_ptr_align = src_ptr_type->data.pointer.alignment; + + if ((err = resolve_ptr_align(ira, src_ptr_type, &src_ptr_align))) + return ira->codegen->builtin_types.entry_invalid; } else { src_ptr_const = true; src_ptr_volatile = false; + + if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusAlignmentKnown))) + return ira->codegen->builtin_types.entry_invalid; + src_ptr_align = get_abi_alignment(ira->codegen, target->value.type); } @@ -18464,6 +18548,8 @@ static ZigType *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionF } static ZigType *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) { + Error err; + IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -18476,9 +18562,13 @@ static ZigType *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToB ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; + uint32_t alignment; + if ((err = resolve_ptr_align(ira, src_ptr_type, &alignment))) + return ira->codegen->builtin_types.entry_invalid; + ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, src_ptr_type->data.pointer.is_const, src_ptr_type->data.pointer.is_volatile, PtrLenUnknown, - src_ptr_type->data.pointer.alignment, 0, 0); + alignment, 0, 0); ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); @@ -18636,6 +18726,8 @@ static ZigType *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoo } static ZigType *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) { + Error err; + IrInstruction *dest_ptr = instruction->dest_ptr->other; if (type_is_invalid(dest_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -18654,8 +18746,13 @@ static ZigType *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemse ZigType *usize = ira->codegen->builtin_types.entry_usize; ZigType *u8 = ira->codegen->builtin_types.entry_u8; - uint32_t dest_align = (dest_uncasted_type->id == ZigTypeIdPointer) ? - dest_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8); + uint32_t dest_align; + if (dest_uncasted_type->id == ZigTypeIdPointer) { + if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align))) + return ira->codegen->builtin_types.entry_invalid; + } else { + dest_align = get_abi_alignment(ira->codegen, u8); + } ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, PtrLenUnknown, dest_align, 0, 0); @@ -18728,6 +18825,8 @@ static ZigType *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemse } static ZigType *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcpy *instruction) { + Error err; + IrInstruction *dest_ptr = instruction->dest_ptr->other; if (type_is_invalid(dest_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -18747,10 +18846,22 @@ static ZigType *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcp dest_uncasted_type->data.pointer.is_volatile; bool src_is_volatile = (src_uncasted_type->id == ZigTypeIdPointer) && src_uncasted_type->data.pointer.is_volatile; - uint32_t dest_align = (dest_uncasted_type->id == ZigTypeIdPointer) ? - dest_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8); - uint32_t src_align = (src_uncasted_type->id == ZigTypeIdPointer) ? - src_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8); + + uint32_t dest_align; + if (dest_uncasted_type->id == ZigTypeIdPointer) { + if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align))) + return ira->codegen->builtin_types.entry_invalid; + } else { + dest_align = get_abi_alignment(ira->codegen, u8); + } + + uint32_t src_align; + if (src_uncasted_type->id == ZigTypeIdPointer) { + if ((err = resolve_ptr_align(ira, src_uncasted_type, &src_align))) + return ira->codegen->builtin_types.entry_invalid; + } else { + src_align = get_abi_alignment(ira->codegen, u8); + } ZigType *usize = ira->codegen->builtin_types.entry_usize; ZigType *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, @@ -18895,17 +19006,13 @@ static ZigType *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice ZigType *return_type; if (array_type->id == ZigTypeIdArray) { - uint32_t byte_alignment = ptr_type->data.pointer.alignment; - if (array_type->data.array.len == 0 && byte_alignment == 0) { - byte_alignment = get_abi_alignment(ira->codegen, array_type->data.array.child_type); - } bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic && ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, ptr_type->data.pointer.is_const || is_comptime_const, ptr_type->data.pointer.is_volatile, PtrLenUnknown, - byte_alignment, 0, 0); + ptr_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { @@ -18915,7 +19022,7 @@ static ZigType *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice main_type->data.pointer.child_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, PtrLenUnknown, - array_type->data.pointer.alignment, 0, 0); + array_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer")); @@ -18925,7 +19032,7 @@ static ZigType *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, PtrLenUnknown, - array_type->data.pointer.alignment, 0, 0); + array_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); if (!end) { ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); @@ -19306,7 +19413,7 @@ static ZigType *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAli return ira->codegen->builtin_types.entry_invalid; ZigType *type_entry = ir_resolve_type(ira, type_value); - if ((err = type_ensure_zero_bits_known(ira->codegen, type_entry))) + if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusAlignmentKnown))) return ira->codegen->builtin_types.entry_invalid; switch (type_entry->id) { @@ -19319,7 +19426,6 @@ static ZigType *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAli case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdVoid: @@ -19351,6 +19457,8 @@ static ZigType *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAli } static ZigType *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstructionOverflowOp *instruction) { + Error err; + IrInstruction *type_value = instruction->type_value->other; if (type_is_invalid(type_value->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -19394,10 +19502,13 @@ static ZigType *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstruction ZigType *expected_ptr_type; if (result_ptr->value.type->id == ZigTypeIdPointer) { + uint32_t alignment; + if ((err = resolve_ptr_align(ira, result_ptr->value.type, &alignment))) + return ira->codegen->builtin_types.entry_invalid; expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type, false, result_ptr->value.type->data.pointer.is_volatile, PtrLenSingle, - result_ptr->value.type->data.pointer.alignment, 0, 0); + alignment, 0, 0); } else { expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false); } @@ -19559,8 +19670,7 @@ static ZigType *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, } ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - PtrLenSingle, - get_abi_alignment(ira->codegen, payload_type), 0, 0); + PtrLenSingle, 0, 0, 0); if (instr_is_comptime(value)) { ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); if (!ptr_val) @@ -19639,7 +19749,7 @@ static ZigType *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnP ZigType *param_type = ir_resolve_type(ira, param_type_value); if (type_is_invalid(param_type)) return ira->codegen->builtin_types.entry_invalid; - if ((err = type_ensure_zero_bits_known(ira->codegen, param_type))) + if ((err = type_resolve(ira->codegen, param_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (type_requires_comptime(param_type)) { if (!calling_convention_allows_zig_types(fn_type_id.cc)) { @@ -19914,7 +20024,7 @@ static ZigType *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic } ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); + true, false, PtrLenUnknown, 0, 0, 0); ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type); if (type_is_invalid(casted_msg->value.type)) @@ -19927,6 +20037,8 @@ static ZigType *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic } static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint32_t align_bytes, bool safety_check_on) { + Error err; + ZigType *target_type = target->value.type; assert(!type_is_invalid(target_type)); @@ -19935,7 +20047,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 if (target_type->id == ZigTypeIdPointer) { result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes); - old_align_bytes = target_type->data.pointer.alignment; + if ((err = resolve_ptr_align(ira, target_type, &old_align_bytes))) + return ira->codegen->invalid_instruction; } else if (target_type->id == ZigTypeIdFn) { FnTypeId fn_type_id = target_type->data.fn.fn_type_id; old_align_bytes = fn_type_id.alignment; @@ -19945,7 +20058,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 target_type->data.maybe.child_type->id == ZigTypeIdPointer) { ZigType *ptr_type = target_type->data.maybe.child_type; - old_align_bytes = ptr_type->data.pointer.alignment; + if ((err = resolve_ptr_align(ira, ptr_type, &old_align_bytes))) + return ira->codegen->invalid_instruction; ZigType *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes); result_type = get_optional_type(ira->codegen, better_ptr_type); @@ -19959,7 +20073,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 result_type = get_optional_type(ira->codegen, fn_type); } else if (is_slice(target_type)) { ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry; - old_align_bytes = slice_ptr_type->data.pointer.alignment; + if ((err = resolve_ptr_align(ira, slice_ptr_type, &old_align_bytes))) + return ira->codegen->invalid_instruction; ZigType *result_ptr_type = adjust_ptr_align(ira->codegen, slice_ptr_type, align_bytes); result_type = get_slice_type(ira->codegen, result_ptr_type); } else { @@ -20038,8 +20153,13 @@ static ZigType *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtr return dest_type; } - uint32_t src_align_bytes = get_ptr_align(src_type); - uint32_t dest_align_bytes = get_ptr_align(dest_type); + uint32_t src_align_bytes; + if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes))) + return ira->codegen->builtin_types.entry_invalid; + + uint32_t dest_align_bytes; + if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes))) + return ira->codegen->builtin_types.entry_invalid; if (dest_align_bytes > src_align_bytes) { ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("cast increases pointer alignment")); @@ -20056,7 +20176,7 @@ static ZigType *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtr // Keep the bigger alignment, it can only help- // unless the target is zero bits. - if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type))) + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; IrInstruction *result; @@ -20080,7 +20200,6 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: @@ -20147,7 +20266,6 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: @@ -20227,7 +20345,6 @@ static ZigType *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBit case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: @@ -20253,7 +20370,6 @@ static ZigType *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBit case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdNamespace: - case ZigTypeIdBlock: case ZigTypeIdUnreachable: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: @@ -20308,7 +20424,7 @@ static ZigType *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionI return ira->codegen->builtin_types.entry_invalid; } - if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type))) + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; if (!type_has_bits(dest_type)) { ir_add_error(ira, dest_type_value, @@ -20459,12 +20575,15 @@ static ZigType *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtr if (instruction->align_value != nullptr) { if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes)) return ira->codegen->builtin_types.entry_invalid; + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->builtin_types.entry_invalid; } else { - if ((err = type_ensure_zero_bits_known(ira->codegen, child_type))) + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; - align_bytes = get_abi_alignment(ira->codegen, child_type); + align_bytes = 0; } + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_pointer_to_type_extra(ira->codegen, child_type, instruction->is_const, instruction->is_volatile, @@ -21108,7 +21227,7 @@ static ZigType *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstruction return ira->codegen->builtin_types.entry_invalid; } - if ((err = type_ensure_zero_bits_known(ira->codegen, target->value.type))) + if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; ZigType *tag_type = target->value.type->data.enumeration.tag_int_type; @@ -21131,7 +21250,7 @@ static ZigType *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstruction return ira->codegen->builtin_types.entry_invalid; } - if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type))) + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) return ira->codegen->builtin_types.entry_invalid; ZigType *tag_type = dest_type->data.enumeration.tag_int_type; diff --git a/src/link.cpp b/src/link.cpp @@ -5,7 +5,6 @@ * See http://opensource.org/licenses/MIT */ -#include "link.hpp" #include "os.hpp" #include "config.h" #include "codegen.hpp" @@ -13,7 +12,6 @@ struct LinkJob { CodeGen *codegen; - Buf out_file; ZigList<const char *> args; bool link_in_crt; HashMap<Buf *, bool, buf_hash, buf_eql_buf> rpath_table; @@ -44,8 +42,6 @@ static Buf *build_o_raw(CodeGen *parent_gen, const char *oname, Buf *full_path) child_gen->verbose_llvm_ir = parent_gen->verbose_llvm_ir; child_gen->verbose_cimport = parent_gen->verbose_cimport; - codegen_set_cache_dir(child_gen, parent_gen->cache_dir); - codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); codegen_set_is_static(child_gen, parent_gen->is_static); @@ -62,16 +58,9 @@ static Buf *build_o_raw(CodeGen *parent_gen, const char *oname, Buf *full_path) new_link_lib->provided_explicitly = link_lib->provided_explicitly; } - codegen_build(child_gen); - const char *o_ext = target_o_file_ext(&child_gen->zig_target); - Buf *o_out_name = buf_sprintf("%s%s", oname, o_ext); - Buf *output_path = buf_alloc(); - os_path_join(&parent_gen->cache_dir, o_out_name, output_path); - codegen_link(child_gen, buf_ptr(output_path)); - - codegen_destroy(child_gen); - - return output_path; + child_gen->enable_cache = true; + codegen_build_and_link(child_gen); + return &child_gen->output_file_path; } static Buf *build_o(CodeGen *parent_gen, const char *oname) { @@ -239,15 +228,15 @@ static void construct_linker_job_elf(LinkJob *lj) { } else if (shared) { lj->args.append("-shared"); - if (buf_len(&lj->out_file) == 0) { - buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "", + if (buf_len(&g->output_file_path) == 0) { + buf_appendf(&g->output_file_path, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); } soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major); } lj->args.append("-o"); - lj->args.append(buf_ptr(&lj->out_file)); + lj->args.append(buf_ptr(&g->output_file_path)); if (lj->link_in_crt) { const char *crt1o; @@ -399,7 +388,7 @@ static void construct_linker_job_wasm(LinkJob *lj) { lj->args.append("--relocatable"); // So lld doesn't look for _start. lj->args.append("-o"); - lj->args.append(buf_ptr(&lj->out_file)); + lj->args.append(buf_ptr(&g->output_file_path)); // .o files for (size_t i = 0; i < g->link_objects.length; i += 1) { @@ -480,7 +469,7 @@ static void construct_linker_job_coff(LinkJob *lj) { // } //} - lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&lj->out_file)))); + lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->output_file_path)))); if (g->libc_link_lib != nullptr) { lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->msvc_lib_dir)))); @@ -587,11 +576,11 @@ static void construct_linker_job_coff(LinkJob *lj) { buf_appendf(def_contents, "\n"); Buf *def_path = buf_alloc(); - os_path_join(&g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path); + os_path_join(&g->artifact_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path); os_write_file(def_path, def_contents); Buf *generated_lib_path = buf_alloc(); - os_path_join(&g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path); + os_path_join(&g->artifact_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path); gen_lib_args.resize(0); gen_lib_args.append("link"); @@ -799,8 +788,8 @@ static void construct_linker_job_macho(LinkJob *lj) { //lj->args.append("-install_name"); //lj->args.append(buf_ptr(dylib_install_name)); - if (buf_len(&lj->out_file) == 0) { - buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", + if (buf_len(&g->output_file_path) == 0) { + buf_appendf(&g->output_file_path, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); } } @@ -834,13 +823,13 @@ static void construct_linker_job_macho(LinkJob *lj) { } lj->args.append("-o"); - lj->args.append(buf_ptr(&lj->out_file)); + lj->args.append(buf_ptr(&g->output_file_path)); for (size_t i = 0; i < g->rpath_list.length; i += 1) { Buf *rpath = g->rpath_list.at(i); add_rpath(lj, rpath); } - add_rpath(lj, &lj->out_file); + add_rpath(lj, &g->output_file_path); if (shared) { lj->args.append("-headerpad_max_install_names"); @@ -944,7 +933,8 @@ static void construct_linker_job(LinkJob *lj) { } } -void codegen_link(CodeGen *g, const char *out_file) { +void codegen_link(CodeGen *g) { + assert(g->out_type != OutTypeObj); codegen_add_time_event(g, "Build Dependencies"); LinkJob lj = {0}; @@ -955,11 +945,6 @@ void codegen_link(CodeGen *g, const char *out_file) { lj.rpath_table.init(4); lj.codegen = g; - if (out_file) { - buf_init_from_str(&lj.out_file, out_file); - } else { - buf_resize(&lj.out_file, 0); - } if (g->verbose_llvm_ir) { fprintf(stderr, "\nOptimization:\n"); @@ -968,35 +953,9 @@ void codegen_link(CodeGen *g, const char *out_file) { LLVMDumpModule(g->module); } - bool override_out_file = (buf_len(&lj.out_file) != 0); - if (!override_out_file) { - assert(g->root_out_name); - - buf_init_from_buf(&lj.out_file, g->root_out_name); - if (g->out_type == OutTypeExe) { - buf_append_str(&lj.out_file, target_exe_file_ext(&g->zig_target)); - } - } - - if (g->out_type == OutTypeObj) { - if (override_out_file) { - assert(g->link_objects.length == 1); - Buf *o_file_path = g->link_objects.at(0); - int err; - if ((err = os_rename(o_file_path, &lj.out_file))) { - zig_panic("unable to rename object file %s into final output %s: %s", buf_ptr(o_file_path), buf_ptr(&lj.out_file), err_str(err)); - } - } - return; - } - if (g->out_type == OutTypeLib && g->is_static) { - // invoke `ar` - // example: - // # static link into libfoo.a - // ar rcs libfoo.a foo1.o foo2.o - zig_panic("TODO invoke ar"); - return; + fprintf(stderr, "Zig does not yet support creating static libraries\nSee https://github.com/ziglang/zig/issues/1493\n"); + exit(1); } lj.link_in_crt = (g->libc_link_lib != nullptr && g->out_type == OutTypeExe); @@ -1019,6 +978,4 @@ void codegen_link(CodeGen *g, const char *out_file) { fprintf(stderr, "%s\n", buf_ptr(&diag)); exit(1); } - - codegen_add_time_event(g, "Done"); } diff --git a/src/link.hpp b/src/link.hpp @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_LINK_HPP -#define ZIG_LINK_HPP - -#include "all_types.hpp" - -void codegen_link(CodeGen *g, const char *out_file); - - -#endif - diff --git a/src/main.cpp b/src/main.cpp @@ -8,9 +8,9 @@ #include "ast_render.hpp" #include "buffer.hpp" #include "codegen.hpp" +#include "compiler.hpp" #include "config.h" #include "error.hpp" -#include "link.hpp" #include "os.hpp" #include "target.hpp" @@ -24,6 +24,7 @@ static int usage(const char *arg0) { " build-lib [source] create library from source or object files\n" " build-obj [source] create object from source or assembly\n" " builtin show the source code of that @import(\"builtin\")\n" + " id print the base64-encoded compiler id\n" " run [source] create executable and run immediately\n" " translate-c [source] convert c code to zig code\n" " targets list available compilation targets\n" @@ -33,9 +34,10 @@ static int usage(const char *arg0) { "Compile Options:\n" " --assembly [source] add assembly file to build\n" " --cache-dir [path] override the cache directory\n" + " --cache [auto|off|on] build to the global cache and print output path to stdout\n" " --color [auto|off|on] enable or disable colored error messages\n" " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n" - " --enable-timing-info print timing diagnostics\n" + " -ftime-report print timing diagnostics\n" " --libc-include-dir [path] directory where libc stdlib.h resides\n" " --name [name] override output name\n" " --output [file] override destination path\n" @@ -256,6 +258,24 @@ static void add_package(CodeGen *g, CliPkg *cli_pkg, PackageTableEntry *pkg) { } } +enum CacheOpt { + CacheOptAuto, + CacheOptOn, + CacheOptOff, +}; + +static bool get_cache_opt(CacheOpt opt, bool default_value) { + switch (opt) { + case CacheOptAuto: + return default_value; + case CacheOptOn: + return true; + case CacheOptOff: + return false; + } + zig_unreachable(); +} + int main(int argc, char **argv) { if (argc == 2 && strcmp(argv[1], "BUILD_INFO") == 0) { printf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", @@ -270,6 +290,17 @@ int main(int argc, char **argv) { return 0; } + if (argc == 2 && strcmp(argv[1], "id") == 0) { + Error err; + Buf *compiler_id; + if ((err = get_compiler_id(&compiler_id))) { + fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err)); + return EXIT_FAILURE; + } + printf("%s\n", buf_ptr(compiler_id)); + return EXIT_SUCCESS; + } + os_init(); char *arg0 = argv[0]; @@ -289,6 +320,7 @@ int main(int argc, char **argv) { bool verbose_llvm_ir = false; bool verbose_cimport = false; ErrColor color = ErrColorAuto; + CacheOpt enable_cache = CacheOptAuto; const char *libc_lib_dir = nullptr; const char *libc_static_lib_dir = nullptr; const char *libc_include_dir = nullptr; @@ -325,8 +357,7 @@ int main(int argc, char **argv) { CliPkg *cur_pkg = allocate<CliPkg>(1); BuildMode build_mode = BuildModeDebug; ZigList<const char *> test_exec_args = {0}; - int comptime_args_end = 0; - int runtime_args_start = argc; + int runtime_args_start = -1; bool no_rosegment_workaround = false; if (argc >= 2 && strcmp(argv[1], "build") == 0) { @@ -370,8 +401,9 @@ int main(int argc, char **argv) { Buf *build_runner_path = buf_alloc(); os_path_join(special_dir, buf_create_from_str("build_runner.zig"), build_runner_path); - CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, zig_lib_dir_buf); + g->enable_time_report = timing_info; + buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); codegen_set_out_name(g, buf_create_from_str("build")); Buf *build_file_buf = buf_create_from_str(build_file); @@ -380,6 +412,7 @@ int main(int argc, char **argv) { Buf build_file_dirname = BUF_INIT; os_path_split(&build_file_abs, &build_file_dirname, &build_file_basename); + Buf full_cache_dir = BUF_INIT; if (cache_dir == nullptr) { os_path_join(&build_file_dirname, buf_create_from_str(default_zig_cache_name), &full_cache_dir); @@ -388,10 +421,6 @@ int main(int argc, char **argv) { full_cache_dir = os_path_resolve(&cache_dir_buf, 1); } - Buf *path_to_build_exe = buf_alloc(); - os_path_join(&full_cache_dir, buf_create_from_str("build"), path_to_build_exe); - codegen_set_cache_dir(g, full_cache_dir); - args.items[1] = buf_ptr(&build_file_dirname); args.items[2] = buf_ptr(&full_cache_dir); @@ -459,15 +488,14 @@ int main(int argc, char **argv) { PackageTableEntry *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname), buf_ptr(&build_file_basename)); g->root_package->package_table.put(buf_create_from_str("@build"), build_pkg); - codegen_build(g); - codegen_link(g, buf_ptr(path_to_build_exe)); - codegen_destroy(g); + g->enable_cache = get_cache_opt(enable_cache, true); + codegen_build_and_link(g); Termination term; - os_spawn_process(buf_ptr(path_to_build_exe), args, &term); + os_spawn_process(buf_ptr(&g->output_file_path), args, &term); if (term.how != TerminationIdClean || term.code != 0) { fprintf(stderr, "\nBuild failed. The following command failed:\n"); - fprintf(stderr, "%s", buf_ptr(path_to_build_exe)); + fprintf(stderr, "%s", buf_ptr(&g->output_file_path)); for (size_t i = 0; i < args.length; i += 1) { fprintf(stderr, " %s", args.at(i)); } @@ -476,15 +504,11 @@ int main(int argc, char **argv) { return (term.how == TerminationIdClean) ? term.code : -1; } - for (int i = 1; i < argc; i += 1, comptime_args_end += 1) { + for (int i = 1; i < argc; i += 1) { char *arg = argv[i]; if (arg[0] == '-') { - if (strcmp(arg, "--") == 0) { - // ignore -- from both compile and runtime arg sets - runtime_args_start = i + 1; - break; - } else if (strcmp(arg, "--release-fast") == 0) { + if (strcmp(arg, "--release-fast") == 0) { build_mode = BuildModeFastRelease; } else if (strcmp(arg, "--release-safe") == 0) { build_mode = BuildModeSafeRelease; @@ -516,7 +540,7 @@ int main(int argc, char **argv) { no_rosegment_workaround = true; } else if (strcmp(arg, "--each-lib-rpath") == 0) { each_lib_rpath = true; - } else if (strcmp(arg, "--enable-timing-info") == 0) { + } else if (strcmp(arg, "-ftime-report") == 0) { timing_info = true; } else if (strcmp(arg, "--test-cmd-bin") == 0) { test_exec_args.append(nullptr); @@ -562,6 +586,17 @@ int main(int argc, char **argv) { fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n"); return usage(arg0); } + } else if (strcmp(arg, "--cache") == 0) { + if (strcmp(argv[i], "auto") == 0) { + enable_cache = CacheOptAuto; + } else if (strcmp(argv[i], "on") == 0) { + enable_cache = CacheOptOn; + } else if (strcmp(argv[i], "off") == 0) { + enable_cache = CacheOptOff; + } else { + fprintf(stderr, "--cache options are 'auto', 'on', or 'off'\n"); + return usage(arg0); + } } else if (strcmp(arg, "--emit") == 0) { if (strcmp(argv[i], "asm") == 0) { emit_file_type = EmitFileTypeAssembly; @@ -681,6 +716,10 @@ int main(int argc, char **argv) { case CmdTest: if (!in_file) { in_file = arg; + if (cmd == CmdRun) { + runtime_args_start = i + 1; + break; // rest of the args are for the program + } } else { fprintf(stderr, "Unexpected extra parameter: %s\n", arg); return usage(arg0); @@ -790,32 +829,18 @@ int main(int argc, char **argv) { Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf; - Buf full_cache_dir = BUF_INIT; - Buf *run_exec_path = buf_alloc(); - if (cmd == CmdRun) { - if (buf_out_name == nullptr) { - buf_out_name = buf_create_from_str("run"); - } - - Buf *global_cache_dir = buf_alloc(); - os_get_global_cache_directory(global_cache_dir); - os_path_join(global_cache_dir, buf_out_name, run_exec_path); - full_cache_dir = os_path_resolve(&global_cache_dir, 1); - - out_file = buf_ptr(run_exec_path); - } else { - Buf *resolve_paths = buf_create_from_str((cache_dir == nullptr) ? default_zig_cache_name : cache_dir); - full_cache_dir = os_path_resolve(&resolve_paths, 1); + if (cmd == CmdRun && buf_out_name == nullptr) { + buf_out_name = buf_create_from_str("run"); } - Buf *zig_lib_dir_buf = resolve_zig_lib_dir(); CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf); + g->enable_time_report = timing_info; + buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); codegen_set_out_name(g, buf_out_name); codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); codegen_set_is_test(g, cmd == CmdTest); codegen_set_linker_script(g, linker_script); - codegen_set_cache_dir(g, full_cache_dir); if (each_lib_rpath) codegen_set_each_lib_rpath(g, each_lib_rpath); @@ -885,6 +910,8 @@ int main(int argc, char **argv) { codegen_set_test_name_prefix(g, buf_create_from_str(test_name_prefix)); } + if (out_file) + codegen_set_output_path(g, buf_create_from_str(out_file)); if (out_file_h) codegen_set_output_h_path(g, buf_create_from_str(out_file_h)); @@ -904,8 +931,8 @@ int main(int argc, char **argv) { if (cmd == CmdBuild || cmd == CmdRun) { codegen_set_emit_file_type(g, emit_file_type); - codegen_build(g); - codegen_link(g, out_file); + g->enable_cache = get_cache_opt(enable_cache, cmd == CmdRun); + codegen_build_and_link(g); if (timing_info) codegen_print_timing_report(g, stdout); @@ -915,12 +942,26 @@ int main(int argc, char **argv) { args.append(argv[i]); } + const char *exec_path = buf_ptr(&g->output_file_path); + args.append(nullptr); + + os_execv(exec_path, args.items); + + args.pop(); Termination term; - os_spawn_process(buf_ptr(run_exec_path), args, &term); + os_spawn_process(exec_path, args, &term); return term.code; + } else if (cmd == CmdBuild) { + if (g->enable_cache) { + printf("%s\n", buf_ptr(&g->output_file_path)); + if (g->out_h_path != nullptr) { + printf("%s\n", buf_ptr(g->out_h_path)); + } + } + return EXIT_SUCCESS; + } else { + zig_unreachable(); } - - return EXIT_SUCCESS; } else if (cmd == CmdTranslateC) { codegen_translate_c(g, in_file_buf); ast_render(g, stdout, g->root_import->root, 4); @@ -933,11 +974,16 @@ int main(int argc, char **argv) { ZigTarget native; get_native_target(&native); - ZigTarget *non_null_target = target ? target : &native; + g->enable_cache = get_cache_opt(enable_cache, false); + codegen_build_and_link(g); - Buf *test_exe_name = buf_sprintf("test%s", target_exe_file_ext(non_null_target)); + if (timing_info) { + codegen_print_timing_report(g, stdout); + } + + Buf *test_exe_path_unresolved = &g->output_file_path; Buf *test_exe_path = buf_alloc(); - os_path_join(&full_cache_dir, test_exe_name, test_exe_path); + *test_exe_path = os_path_resolve(&test_exe_path_unresolved, 1); for (size_t i = 0; i < test_exec_args.length; i += 1) { if (test_exec_args.items[i] == nullptr) { @@ -945,9 +991,6 @@ int main(int argc, char **argv) { } } - codegen_build(g); - codegen_link(g, buf_ptr(test_exe_path)); - if (!target_can_exec(&native, target)) { fprintf(stderr, "Created %s but skipping execution because it is non-native.\n", buf_ptr(test_exe_path)); @@ -969,8 +1012,6 @@ int main(int argc, char **argv) { if (term.how != TerminationIdClean || term.code != 0) { fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); fprintf(stderr, "%s\n", buf_ptr(test_exe_path)); - } else if (timing_info) { - codegen_print_timing_report(g, stdout); } return (term.how == TerminationIdClean) ? term.code : -1; } else { diff --git a/src/os.cpp b/src/os.cpp @@ -24,6 +24,7 @@ #endif #include <windows.h> +#include <shlobj.h> #include <io.h> #include <fcntl.h> @@ -40,6 +41,10 @@ typedef SSIZE_T ssize_t; #endif +#if defined(ZIG_OS_LINUX) +#include <link.h> +#endif + #if defined(__MACH__) #include <mach/clock.h> @@ -57,54 +62,6 @@ static clock_serv_t cclock; #include <errno.h> #include <time.h> -// Ported from std/mem.zig. -// Coordinate struct fields with memSplit function -struct SplitIterator { - size_t index; - Slice<uint8_t> buffer; - Slice<uint8_t> split_bytes; -}; - -// Ported from std/mem.zig. -static bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte) { - for (size_t i = 0; i < self->split_bytes.len; i += 1) { - if (byte == self->split_bytes.ptr[i]) { - return true; - } - } - return false; -} - -// Ported from std/mem.zig. -static Optional<Slice<uint8_t>> SplitIterator_next(SplitIterator *self) { - // move to beginning of token - while (self->index < self->buffer.len && - SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) - { - self->index += 1; - } - size_t start = self->index; - if (start == self->buffer.len) { - return {}; - } - - // move to end of token - while (self->index < self->buffer.len && - !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) - { - self->index += 1; - } - size_t end = self->index; - - return Optional<Slice<uint8_t>>::some(self->buffer.slice(start, end)); -} - -// Ported from std/mem.zig -static SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) { - return SplitIterator{0, buffer, split_bytes}; -} - - #if defined(ZIG_OS_POSIX) static void populate_termination(Termination *term, int status) { if (WIFEXITED(status)) { @@ -765,7 +722,7 @@ Buf os_path_resolve(Buf **paths_ptr, size_t paths_len) { #endif } -int os_fetch_file(FILE *f, Buf *out_buf, bool skip_shebang) { +Error os_fetch_file(FILE *f, Buf *out_buf, bool skip_shebang) { static const ssize_t buf_size = 0x2000; buf_resize(out_buf, buf_size); ssize_t actual_buf_len = 0; @@ -801,7 +758,7 @@ int os_fetch_file(FILE *f, Buf *out_buf, bool skip_shebang) { if (amt_read != buf_size) { if (feof(f)) { buf_resize(out_buf, actual_buf_len); - return 0; + return ErrorNone; } else { return ErrorFileSystem; } @@ -813,13 +770,13 @@ int os_fetch_file(FILE *f, Buf *out_buf, bool skip_shebang) { zig_unreachable(); } -int os_file_exists(Buf *full_path, bool *result) { +Error os_file_exists(Buf *full_path, bool *result) { #if defined(ZIG_OS_WINDOWS) *result = GetFileAttributes(buf_ptr(full_path)) != INVALID_FILE_ATTRIBUTES; - return 0; + return ErrorNone; #else *result = access(buf_ptr(full_path), F_OK) != -1; - return 0; + return ErrorNone; #endif } @@ -878,13 +835,15 @@ static int os_exec_process_posix(const char *exe, ZigList<const char *> &args, FILE *stdout_f = fdopen(stdout_pipe[0], "rb"); FILE *stderr_f = fdopen(stderr_pipe[0], "rb"); - os_fetch_file(stdout_f, out_stdout, false); - os_fetch_file(stderr_f, out_stderr, false); + Error err1 = os_fetch_file(stdout_f, out_stdout, false); + Error err2 = os_fetch_file(stderr_f, out_stderr, false); fclose(stdout_f); fclose(stderr_f); - return 0; + if (err1) return err1; + if (err2) return err2; + return ErrorNone; } } #endif @@ -1016,6 +975,22 @@ static int os_exec_process_windows(const char *exe, ZigList<const char *> &args, } #endif +Error os_execv(const char *exe, const char **argv) { +#if defined(ZIG_OS_WINDOWS) + return ErrorUnsupportedOperatingSystem; +#else + execv(exe, (char *const *)argv); + switch (errno) { + case ENOMEM: + return ErrorSystemResources; + case EIO: + return ErrorFileSystem; + default: + return ErrorUnexpected; + } +#endif +} + int os_exec_process(const char *exe, ZigList<const char *> &args, Termination *term, Buf *out_stderr, Buf *out_stdout) { @@ -1092,7 +1067,7 @@ int os_copy_file(Buf *src_path, Buf *dest_path) { } } -int os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang) { +Error os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang) { FILE *f = fopen(buf_ptr(full_path), "rb"); if (!f) { switch (errno) { @@ -1111,7 +1086,7 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang) { return ErrorFileSystem; } } - int result = os_fetch_file(f, out_contents, skip_shebang); + Error result = os_fetch_file(f, out_contents, skip_shebang); fclose(f); return result; } @@ -1282,44 +1257,6 @@ int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path) { #endif } -#if defined(ZIG_OS_POSIX) -int os_get_global_cache_directory(Buf *out_tmp_path) { - const char *tmp_dir = getenv("TMPDIR"); - if (!tmp_dir) { - tmp_dir = P_tmpdir; - } - - Buf *tmp_dir_buf = buf_create_from_str(tmp_dir); - Buf *cache_dirname_buf = buf_create_from_str("zig-cache"); - - buf_resize(out_tmp_path, 0); - os_path_join(tmp_dir_buf, cache_dirname_buf, out_tmp_path); - - buf_deinit(tmp_dir_buf); - buf_deinit(cache_dirname_buf); - return 0; -} -#endif - -#if defined(ZIG_OS_WINDOWS) -int os_get_global_cache_directory(Buf *out_tmp_path) { - char tmp_dir[MAX_PATH + 1]; - if (GetTempPath(MAX_PATH, tmp_dir) == 0) { - zig_panic("GetTempPath failed"); - } - - Buf *tmp_dir_buf = buf_create_from_str(tmp_dir); - Buf *cache_dirname_buf = buf_create_from_str("zig-cache"); - - buf_resize(out_tmp_path, 0); - os_path_join(tmp_dir_buf, cache_dirname_buf, out_tmp_path); - - buf_deinit(tmp_dir_buf); - buf_deinit(cache_dirname_buf); - return 0; -} -#endif - int os_delete_file(Buf *path) { if (remove(buf_ptr(path))) { return ErrorFileSystem; @@ -1368,16 +1305,16 @@ double os_get_time(void) { #endif } -int os_make_path(Buf *path) { +Error os_make_path(Buf *path) { Buf resolved_path = os_path_resolve(&path, 1); size_t end_index = buf_len(&resolved_path); - int err; + Error err; while (true) { if ((err = os_make_dir(buf_slice(&resolved_path, 0, end_index)))) { if (err == ErrorPathAlreadyExists) { if (end_index == buf_len(&resolved_path)) - return 0; + return ErrorNone; } else if (err == ErrorFileNotFound) { // march end_index backward until next path component while (true) { @@ -1391,7 +1328,7 @@ int os_make_path(Buf *path) { } } if (end_index == buf_len(&resolved_path)) - return 0; + return ErrorNone; // march end_index forward until next path component while (true) { end_index += 1; @@ -1399,10 +1336,10 @@ int os_make_path(Buf *path) { break; } } - return 0; + return ErrorNone; } -int os_make_dir(Buf *path) { +Error os_make_dir(Buf *path) { #if defined(ZIG_OS_WINDOWS) if (!CreateDirectory(buf_ptr(path), NULL)) { if (GetLastError() == ERROR_ALREADY_EXISTS) @@ -1413,7 +1350,7 @@ int os_make_dir(Buf *path) { return ErrorAccess; return ErrorUnexpected; } - return 0; + return ErrorNone; #else if (mkdir(buf_ptr(path), 0755) == -1) { if (errno == EEXIST) @@ -1424,7 +1361,7 @@ int os_make_dir(Buf *path) { return ErrorAccess; return ErrorUnexpected; } - return 0; + return ErrorNone; #endif } @@ -1447,7 +1384,7 @@ int os_init(void) { return 0; } -int os_self_exe_path(Buf *out_path) { +Error os_self_exe_path(Buf *out_path) { #if defined(ZIG_OS_WINDOWS) buf_resize(out_path, 256); for (;;) { @@ -1457,7 +1394,7 @@ int os_self_exe_path(Buf *out_path) { } if (copied_amt < buf_len(out_path)) { buf_resize(out_path, copied_amt); - return 0; + return ErrorNone; } buf_resize(out_path, buf_len(out_path) * 2); } @@ -1480,27 +1417,21 @@ int os_self_exe_path(Buf *out_path) { char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); if (!real_path) { buf_init_from_buf(out_path, tmp); - return 0; + return ErrorNone; } // Resize out_path for the correct length. buf_resize(out_path, strlen(buf_ptr(out_path))); - return 0; + return ErrorNone; #elif defined(ZIG_OS_LINUX) - buf_resize(out_path, 256); - for (;;) { - ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path)); - if (amt == -1) { - return ErrorUnexpected; - } - if (amt == (ssize_t)buf_len(out_path)) { - buf_resize(out_path, buf_len(out_path) * 2); - continue; - } - buf_resize(out_path, amt); - return 0; + buf_resize(out_path, PATH_MAX); + ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path)); + if (amt == -1) { + return ErrorUnexpected; } + buf_resize(out_path, amt); + return ErrorNone; #endif return ErrorFileNotFound; } @@ -1685,3 +1616,431 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy return ErrorFileNotFound; #endif } + +#if defined(ZIG_OS_WINDOWS) +// Ported from std/unicode.zig +struct Utf16LeIterator { + uint8_t *bytes; + size_t i; +}; + +// Ported from std/unicode.zig +static Utf16LeIterator Utf16LeIterator_init(WCHAR *ptr) { + return {(uint8_t*)ptr, 0}; +} + +// Ported from std/unicode.zig +static Optional<uint32_t> Utf16LeIterator_nextCodepoint(Utf16LeIterator *it) { + if (it->bytes[it->i] == 0 && it->bytes[it->i + 1] == 0) + return {}; + uint32_t c0 = ((uint32_t)it->bytes[it->i]) | (((uint32_t)it->bytes[it->i + 1]) << 8); + if (c0 & ~((uint32_t)0x03ff) == 0xd800) { + // surrogate pair + it->i += 2; + assert(it->bytes[it->i] != 0 || it->bytes[it->i + 1] != 0); + uint32_t c1 = ((uint32_t)it->bytes[it->i]) | (((uint32_t)it->bytes[it->i + 1]) << 8); + assert(c1 & ~((uint32_t)0x03ff) == 0xdc00); + it->i += 2; + return Optional<uint32_t>::some(0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff))); + } else { + assert(c0 & ~((uint32_t)0x03ff) != 0xdc00); + it->i += 2; + return Optional<uint32_t>::some(c0); + } +} + +// Ported from std/unicode.zig +static uint8_t utf8CodepointSequenceLength(uint32_t c) { + if (c < 0x80) return 1; + if (c < 0x800) return 2; + if (c < 0x10000) return 3; + if (c < 0x110000) return 4; + zig_unreachable(); +} + +// Ported from std/unicode.zig +static size_t utf8Encode(uint32_t c, Slice<uint8_t> out) { + size_t length = utf8CodepointSequenceLength(c); + assert(out.len >= length); + switch (length) { + // The pattern for each is the same + // - Increasing the initial shift by 6 each time + // - Each time after the first shorten the shifted + // value to a max of 0b111111 (63) + case 1: + out.ptr[0] = c; // Can just do 0 + codepoint for initial range + break; + case 2: + out.ptr[0] = 0b11000000 | (c >> 6); + out.ptr[1] = 0b10000000 | (c & 0b111111); + break; + case 3: + assert(!(0xd800 <= c && c <= 0xdfff)); + out.ptr[0] = 0b11100000 | (c >> 12); + out.ptr[1] = 0b10000000 | ((c >> 6) & 0b111111); + out.ptr[2] = 0b10000000 | (c & 0b111111); + break; + case 4: + out.ptr[0] = 0b11110000 | (c >> 18); + out.ptr[1] = 0b10000000 | ((c >> 12) & 0b111111); + out.ptr[2] = 0b10000000 | ((c >> 6) & 0b111111); + out.ptr[3] = 0b10000000 | (c & 0b111111); + break; + default: + zig_unreachable(); + } + return length; +} + +// Ported from std.unicode.utf16leToUtf8Alloc +static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) { + // optimistically guess that it will all be ascii. + buf_resize(out, 0); + size_t out_index = 0; + Utf16LeIterator it = Utf16LeIterator_init(utf16le); + for (;;) { + Optional<uint32_t> opt_codepoint = Utf16LeIterator_nextCodepoint(&it); + if (!opt_codepoint.is_some) break; + uint32_t codepoint = opt_codepoint.value; + + size_t utf8_len = utf8CodepointSequenceLength(codepoint); + buf_resize(out, buf_len(out) + utf8_len); + utf8Encode(codepoint, {(uint8_t*)buf_ptr(out)+out_index, buf_len(out)-out_index}); + out_index += utf8_len; + } +} +#endif + +// Ported from std.os.getAppDataDir +Error os_get_app_data_dir(Buf *out_path, const char *appname) { +#if defined(ZIG_OS_WINDOWS) + Error err; + WCHAR *dir_path_ptr; + switch (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &dir_path_ptr)) { + case S_OK: + // defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); + utf16le_ptr_to_utf8(out_path, dir_path_ptr); + CoTaskMemFree(dir_path_ptr); + buf_appendf(out_path, "\\%s", appname); + return ErrorNone; + case E_OUTOFMEMORY: + return ErrorNoMem; + default: + return ErrorUnexpected; + } + zig_unreachable(); +#elif defined(ZIG_OS_DARWIN) + const char *home_dir = getenv("HOME"); + if (home_dir == nullptr) { + // TODO use /etc/passwd + return ErrorFileNotFound; + } + buf_resize(out_path, 0); + buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname); + return ErrorNone; +#elif defined(ZIG_OS_LINUX) + const char *home_dir = getenv("HOME"); + if (home_dir == nullptr) { + // TODO use /etc/passwd + return ErrorFileNotFound; + } + buf_resize(out_path, 0); + buf_appendf(out_path, "%s/.local/share/%s", home_dir, appname); + return ErrorNone; +#endif +} + + +#if defined(ZIG_OS_LINUX) +static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) { + ZigList<Buf *> *libs = reinterpret_cast< ZigList<Buf *> *>(data); + if (info->dlpi_name[0] == '/') { + libs->append(buf_create_from_str(info->dlpi_name)); + } + return 0; +} +#endif + +Error os_self_exe_shared_libs(ZigList<Buf *> &paths) { +#if defined(ZIG_OS_LINUX) + paths.resize(0); + dl_iterate_phdr(self_exe_shared_libs_callback, &paths); + return ErrorNone; +#elif defined(ZIG_OS_DARWIN) + paths.resize(0); + uint32_t img_count = _dyld_image_count(); + for (uint32_t i = 0; i != img_count; i += 1) { + const char *name = _dyld_get_image_name(i); + paths.append(buf_create_from_str(name)); + } + return ErrorNone; +#elif defined(ZIG_OS_WINDOWS) + // zig is built statically on windows, so we can return an empty list + paths.resize(0); + return ErrorNone; +#else +#error unimplemented +#endif +} + +Error os_file_open_r(Buf *full_path, OsFile *out_file) { +#if defined(ZIG_OS_WINDOWS) + // TODO use CreateFileW + HANDLE result = CreateFileA(buf_ptr(full_path), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + + if (result == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError(); + switch (err) { + case ERROR_SHARING_VIOLATION: + return ErrorSharingViolation; + case ERROR_ALREADY_EXISTS: + return ErrorPathAlreadyExists; + case ERROR_FILE_EXISTS: + return ErrorPathAlreadyExists; + case ERROR_FILE_NOT_FOUND: + return ErrorFileNotFound; + case ERROR_PATH_NOT_FOUND: + return ErrorFileNotFound; + case ERROR_ACCESS_DENIED: + return ErrorAccess; + case ERROR_PIPE_BUSY: + return ErrorPipeBusy; + default: + return ErrorUnexpected; + } + } + + *out_file = result; + return ErrorNone; +#else + for (;;) { + int fd = open(buf_ptr(full_path), O_RDONLY|O_CLOEXEC); + if (fd == -1) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + zig_unreachable(); + case EFAULT: + zig_unreachable(); + case EACCES: + return ErrorAccess; + case EISDIR: + return ErrorIsDir; + case ENOENT: + return ErrorFileNotFound; + default: + return ErrorFileSystem; + } + } + *out_file = fd; + return ErrorNone; + } +#endif +} + +Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { +#if defined(ZIG_OS_WINDOWS) + for (;;) { + HANDLE result = CreateFileA(buf_ptr(full_path), GENERIC_READ | GENERIC_WRITE, + 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + + if (result == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError(); + switch (err) { + case ERROR_SHARING_VIOLATION: + // TODO wait for the lock instead of sleeping + Sleep(10); + continue; + case ERROR_ALREADY_EXISTS: + return ErrorPathAlreadyExists; + case ERROR_FILE_EXISTS: + return ErrorPathAlreadyExists; + case ERROR_FILE_NOT_FOUND: + return ErrorFileNotFound; + case ERROR_PATH_NOT_FOUND: + return ErrorFileNotFound; + case ERROR_ACCESS_DENIED: + return ErrorAccess; + case ERROR_PIPE_BUSY: + return ErrorPipeBusy; + default: + return ErrorUnexpected; + } + } + *out_file = result; + return ErrorNone; + } +#else + int fd; + for (;;) { + fd = open(buf_ptr(full_path), O_RDWR|O_CLOEXEC|O_CREAT, 0666); + if (fd == -1) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + zig_unreachable(); + case EFAULT: + zig_unreachable(); + case EACCES: + return ErrorAccess; + case EISDIR: + return ErrorIsDir; + case ENOENT: + return ErrorFileNotFound; + default: + return ErrorFileSystem; + } + } + break; + } + for (;;) { + struct flock lock; + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + if (fcntl(fd, F_SETLKW, &lock) == -1) { + switch (errno) { + case EINTR: + continue; + case EBADF: + zig_unreachable(); + case EFAULT: + zig_unreachable(); + case EINVAL: + zig_unreachable(); + default: + close(fd); + return ErrorFileSystem; + } + } + break; + } + *out_file = fd; + return ErrorNone; +#endif +} + +Error os_file_mtime(OsFile file, OsTimeStamp *mtime) { +#if defined(ZIG_OS_WINDOWS) + FILETIME last_write_time; + if (!GetFileTime(file, nullptr, nullptr, &last_write_time)) + return ErrorUnexpected; + mtime->sec = last_write_time.dwLowDateTime | (last_write_time.dwHighDateTime << 32); + mtime->nsec = 0; + return ErrorNone; +#elif defined(ZIG_OS_LINUX) + struct stat statbuf; + if (fstat(file, &statbuf) == -1) + return ErrorFileSystem; + + mtime->sec = statbuf.st_mtim.tv_sec; + mtime->nsec = statbuf.st_mtim.tv_nsec; + return ErrorNone; +#elif defined(ZIG_OS_DARWIN) + struct stat statbuf; + if (fstat(file, &statbuf) == -1) + return ErrorFileSystem; + + mtime->sec = statbuf.st_mtimespec.tv_sec; + mtime->nsec = statbuf.st_mtimespec.tv_nsec; + return ErrorNone; +#else +#error unimplemented +#endif +} + +Error os_file_read(OsFile file, void *ptr, size_t *len) { +#if defined(ZIG_OS_WINDOWS) + DWORD amt_read; + if (ReadFile(file, ptr, *len, &amt_read, nullptr) == 0) + return ErrorUnexpected; + *len = amt_read; + return ErrorNone; +#else + for (;;) { + ssize_t rc = read(file, ptr, *len); + if (rc == -1) { + switch (errno) { + case EINTR: + continue; + case EBADF: + zig_unreachable(); + case EFAULT: + zig_unreachable(); + case EISDIR: + zig_unreachable(); + default: + return ErrorFileSystem; + } + } + *len = rc; + return ErrorNone; + } +#endif +} + +Error os_file_read_all(OsFile file, Buf *contents) { + Error err; + size_t index = 0; + for (;;) { + size_t amt = buf_len(contents) - index; + + if (amt < 4096) { + buf_resize(contents, buf_len(contents) + (4096 - amt)); + amt = buf_len(contents) - index; + } + + if ((err = os_file_read(file, buf_ptr(contents) + index, &amt))) + return err; + + if (amt == 0) { + buf_resize(contents, index); + return ErrorNone; + } + + index += amt; + } +} + +Error os_file_overwrite(OsFile file, Buf *contents) { +#if defined(ZIG_OS_WINDOWS) + if (SetFilePointer(file, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + return ErrorFileSystem; + if (!SetEndOfFile(file)) + return ErrorFileSystem; + if (!WriteFile(file, buf_ptr(contents), buf_len(contents), nullptr, nullptr)) + return ErrorFileSystem; + return ErrorNone; +#else + if (lseek(file, 0, SEEK_SET) == -1) + return ErrorFileSystem; + if (ftruncate(file, 0) == -1) + return ErrorFileSystem; + for (;;) { + if (write(file, buf_ptr(contents), buf_len(contents)) == -1) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + zig_unreachable(); + case EBADF: + zig_unreachable(); + default: + return ErrorFileSystem; + } + } + return ErrorNone; + } +#endif +} + +void os_file_close(OsFile file) { +#if defined(ZIG_OS_WINDOWS) + CloseHandle(file); +#else + close(file); +#endif +} diff --git a/src/os.hpp b/src/os.hpp @@ -13,10 +13,43 @@ #include "error.hpp" #include "zig_llvm.h" #include "windows_sdk.h" +#include "result.hpp" #include <stdio.h> #include <inttypes.h> +#if defined(__APPLE__) +#define ZIG_OS_DARWIN +#elif defined(_WIN32) +#define ZIG_OS_WINDOWS +#elif defined(__linux__) +#define ZIG_OS_LINUX +#else +#define ZIG_OS_UNKNOWN +#endif + +#if defined(__x86_64__) +#define ZIG_ARCH_X86_64 +#else +#define ZIG_ARCH_UNKNOWN +#endif + +#if defined(ZIG_OS_WINDOWS) +#define ZIG_PRI_usize "I64u" +#define ZIG_PRI_u64 "I64u" +#define ZIG_PRI_llu "I64u" +#define ZIG_PRI_x64 "I64x" +#define OS_SEP "\\" +#define ZIG_OS_SEP_CHAR '\\' +#else +#define ZIG_PRI_usize "zu" +#define ZIG_PRI_u64 PRIu64 +#define ZIG_PRI_llu "llu" +#define ZIG_PRI_x64 PRIx64 +#define OS_SEP "/" +#define ZIG_OS_SEP_CHAR '/' +#endif + enum TermColor { TermColorRed, TermColorGreen, @@ -38,11 +71,23 @@ struct Termination { int code; }; +#if defined(ZIG_OS_WINDOWS) +#define OsFile void * +#else +#define OsFile int +#endif + +struct OsTimeStamp { + uint64_t sec; + uint64_t nsec; +}; + int os_init(void); void os_spawn_process(const char *exe, ZigList<const char *> &args, Termination *term); int os_exec_process(const char *exe, ZigList<const char *> &args, Termination *term, Buf *out_stderr, Buf *out_stdout); +Error os_execv(const char *exe, const char **argv); void os_path_dirname(Buf *full_path, Buf *out_dirname); void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); @@ -52,16 +97,22 @@ int os_path_real(Buf *rel_path, Buf *out_abs_path); Buf os_path_resolve(Buf **paths_ptr, size_t paths_len); bool os_path_is_absolute(Buf *path); -int os_get_global_cache_directory(Buf *out_tmp_path); +Error ATTRIBUTE_MUST_USE os_make_path(Buf *path); +Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path); -int os_make_path(Buf *path); -int os_make_dir(Buf *path); +Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file); +Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file); +Error ATTRIBUTE_MUST_USE os_file_mtime(OsFile file, OsTimeStamp *mtime); +Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len); +Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents); +Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents); +void os_file_close(OsFile file); void os_write_file(Buf *full_path, Buf *contents); int os_copy_file(Buf *src_path, Buf *dest_path); -int os_fetch_file(FILE *file, Buf *out_contents, bool skip_shebang); -int os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang); +Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents, bool skip_shebang); +Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang); int os_get_cwd(Buf *out_cwd); @@ -71,49 +122,21 @@ void os_stderr_set_color(TermColor color); int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path); int os_delete_file(Buf *path); -int os_file_exists(Buf *full_path, bool *result); +Error ATTRIBUTE_MUST_USE os_file_exists(Buf *full_path, bool *result); int os_rename(Buf *src_path, Buf *dest_path); double os_get_time(void); bool os_is_sep(uint8_t c); -int os_self_exe_path(Buf *out_path); +Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path); + +Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname); int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf); int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); -#if defined(__APPLE__) -#define ZIG_OS_DARWIN -#elif defined(_WIN32) -#define ZIG_OS_WINDOWS -#elif defined(__linux__) -#define ZIG_OS_LINUX -#else -#define ZIG_OS_UNKNOWN -#endif - -#if defined(__x86_64__) -#define ZIG_ARCH_X86_64 -#else -#define ZIG_ARCH_UNKNOWN -#endif - -#if defined(ZIG_OS_WINDOWS) -#define ZIG_PRI_usize "I64u" -#define ZIG_PRI_u64 "I64u" -#define ZIG_PRI_llu "I64u" -#define ZIG_PRI_x64 "I64x" -#define OS_SEP "\\" -#define ZIG_OS_SEP_CHAR '\\' -#else -#define ZIG_PRI_usize "zu" -#define ZIG_PRI_u64 PRIu64 -#define ZIG_PRI_llu "llu" -#define ZIG_PRI_x64 PRIx64 -#define OS_SEP "/" -#define ZIG_OS_SEP_CHAR '/' -#endif +Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths); #endif diff --git a/src/parser.cpp b/src/parser.cpp @@ -700,7 +700,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b /* PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType -KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" | "suspend" +KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "unreachable" | "suspend" ErrorSetDecl = "error" "{" list(Symbol, ",") "}" */ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) { @@ -756,10 +756,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token); *token_index += 1; return node; - } else if (token->id == TokenIdKeywordThis) { - AstNode *node = ast_create_node(pc, NodeTypeThisLiteral, token); - *token_index += 1; - return node; } else if (token->id == TokenIdKeywordUnreachable) { AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token); *token_index += 1; @@ -3021,9 +3017,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeUndefinedLiteral: // none break; - case NodeTypeThisLiteral: - // none - break; case NodeTypeIfBoolExpr: visit_field(&node->data.if_bool_expr.condition, visit, context); visit_field(&node->data.if_bool_expr.then_block, visit, context); diff --git a/src/target.cpp b/src/target.cpp @@ -812,6 +812,22 @@ const char *target_exe_file_ext(ZigTarget *target) { } } +const char *target_lib_file_ext(ZigTarget *target, bool is_static, size_t version_major, size_t version_minor, size_t version_patch) { + if (target->os == OsWindows) { + if (is_static) { + return ".lib"; + } else { + return ".dll"; + } + } else { + if (is_static) { + return ".a"; + } else { + return buf_ptr(buf_sprintf(".so.%zu", version_major)); + } + } +} + enum FloatAbi { FloatAbiHard, FloatAbiSoft, diff --git a/src/target.hpp b/src/target.hpp @@ -114,6 +114,7 @@ const char *target_o_file_ext(ZigTarget *target); const char *target_asm_file_ext(ZigTarget *target); const char *target_llvm_ir_file_ext(ZigTarget *target); const char *target_exe_file_ext(ZigTarget *target); +const char *target_lib_file_ext(ZigTarget *target, bool is_static, size_t version_major, size_t version_minor, size_t version_patch); Buf *target_dynamic_linker(ZigTarget *target); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp @@ -146,7 +146,6 @@ static const struct ZigKeyword zig_keywords[] = { {"suspend", TokenIdKeywordSuspend}, {"switch", TokenIdKeywordSwitch}, {"test", TokenIdKeywordTest}, - {"this", TokenIdKeywordThis}, {"true", TokenIdKeywordTrue}, {"try", TokenIdKeywordTry}, {"undefined", TokenIdKeywordUndefined}, @@ -1588,7 +1587,6 @@ const char * token_name(TokenId id) { case TokenIdKeywordStruct: return "struct"; case TokenIdKeywordSwitch: return "switch"; case TokenIdKeywordTest: return "test"; - case TokenIdKeywordThis: return "this"; case TokenIdKeywordTrue: return "true"; case TokenIdKeywordTry: return "try"; case TokenIdKeywordUndefined: return "undefined"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp @@ -87,7 +87,6 @@ enum TokenId { TokenIdKeywordSuspend, TokenIdKeywordSwitch, TokenIdKeywordTest, - TokenIdKeywordThis, TokenIdKeywordTrue, TokenIdKeywordTry, TokenIdKeywordUndefined, diff --git a/src/util.cpp b/src/util.cpp @@ -43,3 +43,52 @@ uint32_t ptr_hash(const void *ptr) { bool ptr_eq(const void *a, const void *b) { return a == b; } + +// Ported from std/mem.zig. +bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte) { + for (size_t i = 0; i < self->split_bytes.len; i += 1) { + if (byte == self->split_bytes.ptr[i]) { + return true; + } + } + return false; +} + +// Ported from std/mem.zig. +Optional<Slice<uint8_t>> SplitIterator_next(SplitIterator *self) { + // move to beginning of token + while (self->index < self->buffer.len && + SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) + { + self->index += 1; + } + size_t start = self->index; + if (start == self->buffer.len) { + return {}; + } + + // move to end of token + while (self->index < self->buffer.len && + !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) + { + self->index += 1; + } + size_t end = self->index; + + return Optional<Slice<uint8_t>>::some(self->buffer.slice(start, end)); +} + +// Ported from std/mem.zig +Slice<uint8_t> SplitIterator_rest(SplitIterator *self) { + // move to beginning of token + size_t index = self->index; + while (index < self->buffer.len && SplitIterator_isSplitByte(self, self->buffer.ptr[index])) { + index += 1; + } + return self->buffer.sliceFrom(index); +} + +// Ported from std/mem.zig +SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) { + return SplitIterator{0, buffer, split_bytes}; +} diff --git a/src/util.hpp b/src/util.hpp @@ -254,4 +254,17 @@ static inline void memCopy(Slice<T> dest, Slice<T> src) { memcpy(dest.ptr, src.ptr, src.len * sizeof(T)); } +// Ported from std/mem.zig. +// Coordinate struct fields with memSplit function +struct SplitIterator { + size_t index; + Slice<uint8_t> buffer; + Slice<uint8_t> split_bytes; +}; + +bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte); +Optional< Slice<uint8_t> > SplitIterator_next(SplitIterator *self); +Slice<uint8_t> SplitIterator_rest(SplitIterator *self); +SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes); + #endif diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp @@ -30,6 +30,7 @@ #include <llvm/PassRegistry.h> #include <llvm/Support/FileSystem.h> #include <llvm/Support/TargetParser.h> +#include <llvm/Support/Timer.h> #include <llvm/Support/raw_ostream.h> #include <llvm/Target/TargetMachine.h> #include <llvm/Transforms/Coroutines.h> @@ -82,8 +83,11 @@ static const bool assertions_on = false; #endif bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, - const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small) + const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, + bool is_small, bool time_report) { + TimePassesIsEnabled = time_report; + std::error_code EC; raw_fd_ostream dest(filename, EC, sys::fs::F_None); if (EC) { @@ -183,6 +187,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM } } + if (time_report) { + TimerGroup::printAll(errs()); + } return false; } diff --git a/src/zig_llvm.h b/src/zig_llvm.h @@ -55,7 +55,8 @@ enum ZigLLVM_EmitOutputType { }; ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, - const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small); + const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, + bool is_small, bool time_report); ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref); diff --git a/std/array_list.zig b/std/array_list.zig @@ -11,7 +11,7 @@ pub fn ArrayList(comptime T: type) type { pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return struct { - const Self = this; + const Self = @This(); /// Use toSlice instead of slicing this directly, because if you don't /// specify the end position of the slice, this will potentially give diff --git a/std/atomic/int.zig b/std/atomic/int.zig @@ -6,7 +6,7 @@ pub fn Int(comptime T: type) type { return struct { unprotected_value: T, - pub const Self = this; + pub const Self = @This(); pub fn init(init_val: T) Self { return Self{ .unprotected_value = init_val }; diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig @@ -12,7 +12,7 @@ pub fn Queue(comptime T: type) type { tail: ?*Node, mutex: std.Mutex, - pub const Self = this; + pub const Self = @This(); pub const Node = std.LinkedList(T).Node; pub fn init() Self { @@ -114,7 +114,7 @@ pub fn Queue(comptime T: type) type { fn dumpRecursive(optional_node: ?*Node, indent: usize) void { var stderr_file = std.io.getStdErr() catch return; - const stderr = &std.io.FileOutStream.init(&stderr_file).stream; + const stderr = &std.io.FileOutStream.init(stderr_file).stream; stderr.writeByteNTimes(' ', indent) catch return; if (optional_node) |node| { std.debug.warn("0x{x}={}\n", @ptrToInt(node), node.data); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig @@ -9,7 +9,7 @@ pub fn Stack(comptime T: type) type { root: ?*Node, lock: u8, - pub const Self = this; + pub const Self = @This(); pub const Node = struct { next: ?*Node, diff --git a/std/build.zig b/std/build.zig @@ -232,6 +232,8 @@ pub const Builder = struct { } pub fn make(self: *Builder, step_names: []const []const u8) !void { + try self.makePath(self.cache_root); + var wanted_steps = ArrayList(*Step).init(self.allocator); defer wanted_steps.deinit(); @@ -1641,6 +1643,7 @@ pub const TestStep = struct { lib_paths: ArrayList([]const u8), object_files: ArrayList([]const u8), no_rosegment: bool, + output_path: ?[]const u8, pub fn init(builder: *Builder, root_src: []const u8) TestStep { const step_name = builder.fmt("test {}", root_src); @@ -1659,6 +1662,7 @@ pub const TestStep = struct { .lib_paths = ArrayList([]const u8).init(builder.allocator), .object_files = ArrayList([]const u8).init(builder.allocator), .no_rosegment = false, + .output_path = null, }; } @@ -1682,6 +1686,24 @@ pub const TestStep = struct { self.build_mode = mode; } + pub fn setOutputPath(self: *TestStep, file_path: []const u8) void { + self.output_path = file_path; + + // catch a common mistake + if (mem.eql(u8, self.builder.pathFromRoot(file_path), self.builder.pathFromRoot("."))) { + debug.panic("setOutputPath wants a file path, not a directory\n"); + } + } + + pub fn getOutputPath(self: *TestStep) []const u8 { + if (self.output_path) |output_path| { + return output_path; + } else { + const basename = self.builder.fmt("test{}", self.target.exeFileExt()); + return os.path.join(self.builder.allocator, self.builder.cache_root, basename) catch unreachable; + } + } + pub fn linkSystemLibrary(self: *TestStep, name: []const u8) void { self.link_libs.put(name) catch unreachable; } @@ -1746,6 +1768,10 @@ pub const TestStep = struct { builtin.Mode.ReleaseSmall => try zig_args.append("--release-small"), } + const output_path = builder.pathFromRoot(self.getOutputPath()); + try zig_args.append("--output"); + try zig_args.append(output_path); + switch (self.target) { Target.Native => {}, Target.Cross => |cross_target| { @@ -1864,7 +1890,7 @@ const InstallArtifactStep = struct { artifact: *LibExeObjStep, dest_file: []const u8, - const Self = this; + const Self = @This(); pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { const dest_dir = switch (artifact.kind) { diff --git a/std/coff.zig b/std/coff.zig @@ -8,9 +8,9 @@ const ArrayList = std.ArrayList; // CoffHeader.machine values // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx -const IMAGE_FILE_MACHINE_I386 = 0x014c; -const IMAGE_FILE_MACHINE_IA64 = 0x0200; -const IMAGE_FILE_MACHINE_AMD64 = 0x8664; +const IMAGE_FILE_MACHINE_I386 = 0x014c; +const IMAGE_FILE_MACHINE_IA64 = 0x0200; +const IMAGE_FILE_MACHINE_AMD64 = 0x8664; // OptionalHeader.magic values // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx @@ -20,7 +20,7 @@ const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b; const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; const DEBUG_DIRECTORY = 6; -pub const CoffError = error { +pub const CoffError = error{ InvalidPEMagic, InvalidPEHeader, InvalidMachine, @@ -56,24 +56,21 @@ pub const Coff = struct { var pe_header_magic: [4]u8 = undefined; try in.readNoEof(pe_header_magic[0..]); - if (!mem.eql(u8, pe_header_magic, []u8{'P', 'E', 0, 0})) + if (!mem.eql(u8, pe_header_magic, []u8{ 'P', 'E', 0, 0 })) return error.InvalidPEHeader; - self.coff_header = CoffHeader { + self.coff_header = CoffHeader{ .machine = try in.readIntLe(u16), - .number_of_sections = try in.readIntLe(u16), - .timedate_stamp = try in.readIntLe(u32), - .pointer_to_symbol_table = try in.readIntLe(u32), - .number_of_symbols = try in.readIntLe(u32), - .size_of_optional_header = try in.readIntLe(u16), - .characteristics = try in.readIntLe(u16), + .number_of_sections = try in.readIntLe(u16), + .timedate_stamp = try in.readIntLe(u32), + .pointer_to_symbol_table = try in.readIntLe(u32), + .number_of_symbols = try in.readIntLe(u32), + .size_of_optional_header = try in.readIntLe(u16), + .characteristics = try in.readIntLe(u16), }; switch (self.coff_header.machine) { - IMAGE_FILE_MACHINE_I386, - IMAGE_FILE_MACHINE_AMD64, - IMAGE_FILE_MACHINE_IA64 - => {}, + IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_IA64 => {}, else => return error.InvalidMachine, } @@ -89,11 +86,9 @@ pub const Coff = struct { var skip_size: u16 = undefined; if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32); - } - else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + } 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 + } else return error.InvalidPEMagic; try self.in_file.seekForward(skip_size); @@ -103,7 +98,7 @@ pub const Coff = struct { return error.InvalidPEHeader; for (self.pe_header.data_directory) |*data_dir| { - data_dir.* = OptionalHeader.DataDirectory { + data_dir.* = OptionalHeader.DataDirectory{ .virtual_address = try in.readIntLe(u32), .size = try in.readIntLe(u32), }; @@ -114,7 +109,7 @@ pub const Coff = struct { try self.loadSections(); const header = (self.getSection(".rdata") orelse return error.MissingCoffSection).header; - // The linker puts a chunk that contains the .pdb path right after the + // The linker puts a chunk that contains the .pdb path right after the // debug_directory. 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; @@ -159,10 +154,10 @@ pub const Coff = struct { var i: u16 = 0; while (i < self.coff_header.number_of_sections) : (i += 1) { try in.readNoEof(name[0..]); - try self.sections.append(Section { - .header = SectionHeader { + try self.sections.append(Section{ + .header = SectionHeader{ .name = name, - .misc = SectionHeader.Misc { .physical_address = try in.readIntLe(u32) }, + .misc = SectionHeader.Misc{ .physical_address = try in.readIntLe(u32) }, .virtual_address = try in.readIntLe(u32), .size_of_raw_data = try in.readIntLe(u32), .pointer_to_raw_data = try in.readIntLe(u32), @@ -184,7 +179,6 @@ pub const Coff = struct { } return null; } - }; const CoffHeader = struct { @@ -194,13 +188,13 @@ const CoffHeader = struct { pointer_to_symbol_table: u32, number_of_symbols: u32, size_of_optional_header: u16, - characteristics: u16 + characteristics: u16, }; const OptionalHeader = struct { const DataDirectory = struct { virtual_address: u32, - size: u32 + size: u32, }; magic: u16, @@ -214,7 +208,7 @@ pub const Section = struct { const SectionHeader = struct { const Misc = union { physical_address: u32, - virtual_size: u32 + virtual_size: u32, }; name: [8]u8, diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig @@ -33,7 +33,7 @@ pub const Blake2s256 = Blake2s(256); fn Blake2s(comptime out_len: usize) type { return struct { - const Self = this; + const Self = @This(); const block_length = 64; const digest_length = out_len / 8; @@ -266,7 +266,7 @@ pub const Blake2b512 = Blake2b(512); fn Blake2b(comptime out_len: usize) type { return struct { - const Self = this; + const Self = @This(); const block_length = 128; const digest_length = out_len / 8; diff --git a/std/crypto/hmac.zig b/std/crypto/hmac.zig @@ -9,7 +9,7 @@ pub const HmacSha256 = Hmac(crypto.Sha256); pub fn Hmac(comptime Hash: type) type { return struct { - const Self = this; + const Self = @This(); pub const mac_length = Hash.digest_length; pub const minimum_key_length = 0; diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig @@ -28,7 +28,7 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundPar } pub const Md5 = struct { - const Self = this; + const Self = @This(); const block_length = 64; const digest_length = 16; diff --git a/std/crypto/poly1305.zig b/std/crypto/poly1305.zig @@ -10,7 +10,7 @@ const readInt = std.mem.readInt; const writeInt = std.mem.writeInt; pub const Poly1305 = struct { - const Self = this; + const Self = @This(); pub const mac_length = 16; pub const minimum_key_length = 32; diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig @@ -25,7 +25,7 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam { } pub const Sha1 = struct { - const Self = this; + const Self = @This(); const block_length = 64; const digest_length = 20; diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig @@ -77,7 +77,7 @@ pub const Sha256 = Sha2_32(Sha256Params); fn Sha2_32(comptime params: Sha2Params32) type { return struct { - const Self = this; + const Self = @This(); const block_length = 64; const digest_length = params.out_len / 8; @@ -418,7 +418,7 @@ pub const Sha512 = Sha2_64(Sha512Params); fn Sha2_64(comptime params: Sha2Params64) type { return struct { - const Self = this; + const Self = @This(); const block_length = 128; const digest_length = params.out_len / 8; diff --git a/std/crypto/sha3.zig b/std/crypto/sha3.zig @@ -12,7 +12,7 @@ pub const Sha3_512 = Keccak(512, 0x06); fn Keccak(comptime bits: usize, comptime delim: u8) type { return struct { - const Self = this; + const Self = @This(); const block_length = 200; const digest_length = bits / 8; diff --git a/std/crypto/x25519.zig b/std/crypto/x25519.zig @@ -115,7 +115,7 @@ pub const X25519 = struct { return !zerocmp(u8, out); } - pub fn createPublicKey(public_key: [] u8, private_key: []const u8) bool { + pub fn createPublicKey(public_key: []u8, private_key: []const u8) bool { var base_point = []u8{9} ++ []u8{0} ** 31; return create(public_key, private_key, base_point); } diff --git a/std/debug/index.zig b/std/debug/index.zig @@ -242,9 +242,12 @@ pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color } } -pub fn writeCurrentStackTraceWindows(out_stream: var, debug_info: *DebugInfo, - tty_color: bool, start_addr: ?usize) !void -{ +pub fn writeCurrentStackTraceWindows( + out_stream: var, + debug_info: *DebugInfo, + tty_color: bool, + start_addr: ?usize, +) !void { var addr_buf: [1024]usize = undefined; const casted_len = @intCast(u32, addr_buf.len); // TODO shouldn't need this cast const n = windows.RtlCaptureStackBackTrace(0, casted_len, @ptrCast(**c_void, &addr_buf), null); @@ -391,7 +394,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres break :subsections null; } }; - + if (tty_color) { setTtyColor(TtyColor.White); if (opt_line_info) |li| { @@ -438,7 +441,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres } } -const TtyColor = enum{ +const TtyColor = enum { Red, Green, Cyan, @@ -465,18 +468,16 @@ fn setTtyColor(tty_color: TtyColor) void { // TODO handle errors switch (tty_color) { TtyColor.Red => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED|windows.FOREGROUND_INTENSITY); + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY); }, TtyColor.Green => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN|windows.FOREGROUND_INTENSITY); + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY); }, TtyColor.Cyan => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, - windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY); + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); }, TtyColor.White, TtyColor.Bold => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, - windows.FOREGROUND_RED|windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY); + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); }, TtyColor.Dim => { _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY); @@ -915,7 +916,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { } else { return error.MissingDebugInfo; }; - const syms = @ptrCast([*]macho.nlist_64, hdr_base + symtab.symoff)[0..symtab.nsyms]; + const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize]; const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); @@ -1496,14 +1497,14 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u const segcmd = while (ncmd != 0) : (ncmd -= 1) { const lc = @ptrCast(*const std.macho.load_command, ptr); switch (lc.cmd) { - std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, ptr), + std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)), else => {}, } ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 } else { return error.MissingDebugInfo; }; - const sections = @alignCast(@alignOf(macho.section_64), @ptrCast([*]const macho.section_64, ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects]; + const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects]; for (sections) |*sect| { if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG) diff --git a/std/event/channel.zig b/std/event/channel.zig @@ -25,7 +25,7 @@ pub fn Channel(comptime T: type) type { buffer_index: usize, buffer_len: usize, - const SelfChannel = this; + const SelfChannel = @This(); const GetNode = struct { tick_node: *Loop.NextTickNode, data: Data, diff --git a/std/event/fs.zig b/std/event/fs.zig @@ -109,30 +109,28 @@ pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, off .base = Loop.ResumeNode{ .id = Loop.ResumeNode.Id.Basic, .handle = @handle(), + .overlapped = windows.OVERLAPPED{ + .Internal = 0, + .InternalHigh = 0, + .Offset = @truncate(u32, offset), + .OffsetHigh = @truncate(u32, offset >> 32), + .hEvent = null, + }, }, }; - const completion_key = @ptrToInt(&resume_node.base); - // TODO support concurrent async ops on the file handle - // we can do this by ignoring completion key and using @fieldParentPtr with the *Overlapped - _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, completion_key, undefined); - var overlapped = windows.OVERLAPPED{ - .Internal = 0, - .InternalHigh = 0, - .Offset = @truncate(u32, offset), - .OffsetHigh = @truncate(u32, offset >> 32), - .hEvent = null, - }; + // TODO only call create io completion port once per fd + _ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined); loop.beginOneEvent(); errdefer loop.finishOneEvent(); errdefer { - _ = windows.CancelIoEx(fd, &overlapped); + _ = windows.CancelIoEx(fd, &resume_node.base.overlapped); } suspend { - _ = windows.WriteFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &overlapped); + _ = windows.WriteFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &resume_node.base.overlapped); } var bytes_transferred: windows.DWORD = undefined; - if (windows.GetOverlappedResult(fd, &overlapped, &bytes_transferred, windows.FALSE) == 0) { + if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.IO_PENDING => unreachable, @@ -243,37 +241,36 @@ pub async fn preadWindows(loop: *Loop, fd: os.FileHandle, data: []u8, offset: u6 .base = Loop.ResumeNode{ .id = Loop.ResumeNode.Id.Basic, .handle = @handle(), + .overlapped = windows.OVERLAPPED{ + .Internal = 0, + .InternalHigh = 0, + .Offset = @truncate(u32, offset), + .OffsetHigh = @truncate(u32, offset >> 32), + .hEvent = null, + }, }, }; - const completion_key = @ptrToInt(&resume_node.base); - // TODO support concurrent async ops on the file handle - // we can do this by ignoring completion key and using @fieldParentPtr with the *Overlapped - _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, completion_key, undefined); - var overlapped = windows.OVERLAPPED{ - .Internal = 0, - .InternalHigh = 0, - .Offset = @truncate(u32, offset), - .OffsetHigh = @truncate(u32, offset >> 32), - .hEvent = null, - }; + // TODO only call create io completion port once per fd + _ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined); loop.beginOneEvent(); errdefer loop.finishOneEvent(); errdefer { - _ = windows.CancelIoEx(fd, &overlapped); + _ = windows.CancelIoEx(fd, &resume_node.base.overlapped); } suspend { - _ = windows.ReadFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &overlapped); + _ = windows.ReadFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &resume_node.base.overlapped); } var bytes_transferred: windows.DWORD = undefined; - if (windows.GetOverlappedResult(fd, &overlapped, &bytes_transferred, windows.FALSE) == 0) { + if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { const err = windows.GetLastError(); - return switch (err) { + switch (err) { windows.ERROR.IO_PENDING => unreachable, - windows.ERROR.OPERATION_ABORTED => error.OperationAborted, - windows.ERROR.BROKEN_PIPE => error.BrokenPipe, - else => os.unexpectedErrorWindows(err), - }; + windows.ERROR.OPERATION_ABORTED => return error.OperationAborted, + windows.ERROR.BROKEN_PIPE => return error.BrokenPipe, + windows.ERROR.HANDLE_EOF => return usize(bytes_transferred), + else => return os.unexpectedErrorWindows(err), + } } return usize(bytes_transferred); } @@ -727,7 +724,7 @@ pub fn Watch(comptime V: type) type { const FileToHandle = std.AutoHashMap([]const u8, promise); - const Self = this; + const Self = @This(); pub const Event = struct { id: Id, @@ -1074,23 +1071,22 @@ pub fn Watch(comptime V: type) type { .base = Loop.ResumeNode{ .id = Loop.ResumeNode.Id.Basic, .handle = @handle(), + .overlapped = windows.OVERLAPPED{ + .Internal = 0, + .InternalHigh = 0, + .Offset = 0, + .OffsetHigh = 0, + .hEvent = null, + }, }, }; - const completion_key = @ptrToInt(&resume_node.base); - var overlapped = windows.OVERLAPPED{ - .Internal = 0, - .InternalHigh = 0, - .Offset = 0, - .OffsetHigh = 0, - .hEvent = null, - }; var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined; // TODO handle this error not in the channel but in the setup _ = os.windowsCreateIoCompletionPort( dir_handle, self.channel.loop.os_data.io_port, - completion_key, + undefined, undefined, ) catch |err| { await (async self.channel.put(err) catch unreachable); @@ -1103,7 +1099,7 @@ pub fn Watch(comptime V: type) type { self.channel.loop.beginOneEvent(); errdefer self.channel.loop.finishOneEvent(); errdefer { - _ = windows.CancelIoEx(dir_handle, &overlapped); + _ = windows.CancelIoEx(dir_handle, &resume_node.base.overlapped); } suspend { _ = windows.ReadDirectoryChangesW( @@ -1116,13 +1112,13 @@ pub fn Watch(comptime V: type) type { 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) - &overlapped, + &resume_node.base.overlapped, null, // completion routine - unused because we use IOCP ); } } var bytes_transferred: windows.DWORD = undefined; - if (windows.GetOverlappedResult(dir_handle, &overlapped, &bytes_transferred, windows.FALSE) == 0) { + if (windows.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { const errno = windows.GetLastError(); const err = switch (errno) { else => os.unexpectedErrorWindows(errno), diff --git a/std/event/future.zig b/std/event/future.zig @@ -21,7 +21,7 @@ pub fn Future(comptime T: type) type { /// 2 - finished available: u8, - const Self = this; + const Self = @This(); const Queue = std.atomic.Queue(promise); pub fn init(loop: *Loop) Self { diff --git a/std/event/group.zig b/std/event/group.zig @@ -13,7 +13,7 @@ pub fn Group(comptime ReturnType: type) type { alloc_stack: Stack, lock: Lock, - const Self = this; + const Self = @This(); const Error = switch (@typeInfo(ReturnType)) { builtin.TypeId.ErrorUnion => |payload| payload.error_set, diff --git a/std/event/locked.zig b/std/event/locked.zig @@ -10,7 +10,7 @@ pub fn Locked(comptime T: type) type { lock: Lock, private_data: T, - const Self = this; + const Self = @This(); pub const HeldLock = struct { value: *T, diff --git a/std/event/loop.zig b/std/event/loop.zig @@ -27,6 +27,19 @@ pub const Loop = struct { pub const ResumeNode = struct { id: Id, handle: promise, + overlapped: Overlapped, + + pub const overlapped_init = switch (builtin.os) { + builtin.Os.windows => windows.OVERLAPPED{ + .Internal = 0, + .InternalHigh = 0, + .Offset = 0, + .OffsetHigh = 0, + .hEvent = null, + }, + else => {}, + }; + pub const Overlapped = @typeOf(overlapped_init); pub const Id = enum { Basic, @@ -101,6 +114,7 @@ pub const Loop = struct { .final_resume_node = ResumeNode{ .id = ResumeNode.Id.Stop, .handle = undefined, + .overlapped = ResumeNode.overlapped_init, }, }; const extra_thread_count = thread_count - 1; @@ -153,6 +167,7 @@ pub const Loop = struct { .base = ResumeNode{ .id = ResumeNode.Id.EventFd, .handle = undefined, + .overlapped = ResumeNode.overlapped_init, }, .eventfd = try os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), .epoll_op = posix.EPOLL_CTL_ADD, @@ -225,6 +240,7 @@ pub const Loop = struct { .base = ResumeNode{ .id = ResumeNode.Id.EventFd, .handle = undefined, + .overlapped = ResumeNode.overlapped_init, }, // this one is for sending events .kevent = posix.Kevent{ @@ -311,6 +327,7 @@ pub const Loop = struct { .base = ResumeNode{ .id = ResumeNode.Id.EventFd, .handle = undefined, + .overlapped = ResumeNode.overlapped_init, }, // this one is for sending events .completion_key = @ptrToInt(&eventfd_node.data.base), @@ -325,8 +342,8 @@ pub const Loop = struct { var i: usize = 0; while (i < extra_thread_index) : (i += 1) { while (true) { - const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); - os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; + const overlapped = &self.final_resume_node.overlapped; + os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, undefined, overlapped) catch continue; break; } } @@ -398,6 +415,7 @@ pub const Loop = struct { .base = ResumeNode{ .id = ResumeNode.Id.Basic, .handle = @handle(), + .overlapped = ResumeNode.overlapped_init, }, }; try self.linuxAddFd(fd, &resume_node.base, flags); @@ -413,6 +431,7 @@ pub const Loop = struct { .base = ResumeNode{ .id = ResumeNode.Id.Basic, .handle = @handle(), + .overlapped = ResumeNode.overlapped_init, }, .kev = undefined, }; @@ -489,15 +508,11 @@ pub const Loop = struct { }; }, builtin.Os.windows => { - // this value is never dereferenced but we need it to be non-null so that - // the consumer code can decide whether to read the completion key. - // it has to do this for normal I/O, so we match that behavior here. - const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); os.windowsPostQueuedCompletionStatus( self.os_data.io_port, undefined, - eventfd_node.completion_key, - overlapped, + undefined, + &eventfd_node.base.overlapped, ) catch { self.next_tick_queue.unget(next_tick_node); self.available_eventfd_resume_nodes.push(resume_stack_node); @@ -606,8 +621,8 @@ pub const Loop = struct { var i: usize = 0; while (i < self.extra_threads.len + 1) : (i += 1) { while (true) { - const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); - os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; + const overlapped = &self.final_resume_node.overlapped; + os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, undefined, overlapped) catch continue; break; } } @@ -680,17 +695,18 @@ pub const Loop = struct { }, builtin.Os.windows => { var completion_key: usize = undefined; - while (true) { + const overlapped = while (true) { var nbytes: windows.DWORD = undefined; var overlapped: ?*windows.OVERLAPPED = undefined; switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { os.WindowsWaitResult.Aborted => return, os.WindowsWaitResult.Normal => {}, + os.WindowsWaitResult.EOF => {}, os.WindowsWaitResult.Cancelled => continue, } - if (overlapped != null) break; - } - const resume_node = @intToPtr(*ResumeNode, completion_key); + if (overlapped) |o| break o; + } else unreachable; // TODO else unreachable should not be necessary + const resume_node = @fieldParentPtr(ResumeNode, "overlapped", overlapped); const handle = resume_node.handle; const resume_node_id = resume_node.id; switch (resume_node_id) { diff --git a/std/event/rwlocked.zig b/std/event/rwlocked.zig @@ -10,7 +10,7 @@ pub fn RwLocked(comptime T: type) type { lock: RwLock, locked_data: T, - const Self = this; + const Self = @This(); pub const HeldReadLock = struct { value: *const T, diff --git a/std/event/tcp.zig b/std/event/tcp.zig @@ -32,6 +32,7 @@ pub const Server = struct { .listen_resume_node = event.Loop.ResumeNode{ .id = event.Loop.ResumeNode.Id.Basic, .handle = undefined, + .overlapped = event.Loop.ResumeNode.overlapped_init, }, }; } @@ -131,7 +132,7 @@ test "listen on a port, send bytes, receive bytes" { const MyServer = struct { tcp_server: Server, - const Self = this; + const Self = @This(); async<*mem.Allocator> fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: *const std.os.File) void { const self = @fieldParentPtr(Self, "tcp_server", tcp_server); var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 diff --git a/std/fmt/index.zig b/std/fmt/index.zig @@ -1183,7 +1183,7 @@ test "fmt.format" { //custom type format { const Vec2 = struct { - const SelfType = this; + const SelfType = @This(); x: f32, y: f32, diff --git a/std/hash/crc.zig b/std/hash/crc.zig @@ -20,7 +20,7 @@ pub const Crc32 = Crc32WithPoly(Polynomial.IEEE); // slicing-by-8 crc32 implementation. pub fn Crc32WithPoly(comptime poly: u32) type { return struct { - const Self = this; + const Self = @This(); const lookup_tables = comptime block: { @setEvalBranchQuota(20000); var tables: [8][256]u32 = undefined; @@ -117,7 +117,7 @@ test "crc32 castagnoli" { // half-byte lookup table implementation. pub fn Crc32SmallWithPoly(comptime poly: u32) type { return struct { - const Self = this; + const Self = @This(); const lookup_table = comptime block: { var table: [16]u32 = undefined; diff --git a/std/hash/fnv.zig b/std/hash/fnv.zig @@ -13,7 +13,7 @@ pub const Fnv1a_128 = Fnv1a(u128, 0x1000000000000000000013b, 0x6c62272e07bb01426 fn Fnv1a(comptime T: type, comptime prime: T, comptime offset: T) type { return struct { - const Self = this; + const Self = @This(); value: T, diff --git a/std/hash/siphash.zig b/std/hash/siphash.zig @@ -25,7 +25,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) debug.assert(c_rounds > 0 and d_rounds > 0); return struct { - const Self = this; + const Self = @This(); const digest_size = 64; const block_size = 64; diff --git a/std/hash_map.zig b/std/hash_map.zig @@ -22,7 +22,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 // this is used to detect bugs where a hashtable is edited while an iterator is running. modification_count: debug_u32, - const Self = this; + const Self = @This(); pub const KV = struct { key: K, @@ -472,7 +472,6 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng), builtin.TypeId.Namespace, - builtin.TypeId.Block, builtin.TypeId.BoundFn, builtin.TypeId.ComptimeFloat, builtin.TypeId.ComptimeInt, @@ -517,7 +516,6 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool { builtin.TypeId.ComptimeFloat, builtin.TypeId.ComptimeInt, builtin.TypeId.Namespace, - builtin.TypeId.Block, builtin.TypeId.Promise, builtin.TypeId.Enum, builtin.TypeId.BoundFn, diff --git a/std/heap.zig b/std/heap.zig @@ -385,7 +385,7 @@ pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) Stack pub fn StackFallbackAllocator(comptime size: usize) type { return struct { - const Self = this; + const Self = @This(); buffer: [size]u8, allocator: Allocator, diff --git a/std/io.zig b/std/io.zig @@ -76,7 +76,7 @@ pub const FileOutStream = struct { pub fn InStream(comptime ReadError: type) type { return struct { - const Self = this; + const Self = @This(); pub const Error = ReadError; /// Return the number of bytes read. If the number read is smaller than buf.len, it @@ -218,7 +218,7 @@ pub fn InStream(comptime ReadError: type) type { pub fn OutStream(comptime WriteError: type) type { return struct { - const Self = this; + const Self = @This(); pub const Error = WriteError; writeFn: fn (self: *Self, bytes: []const u8) Error!void, @@ -291,7 +291,7 @@ pub fn BufferedInStream(comptime Error: type) type { pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) type { return struct { - const Self = this; + const Self = @This(); const Stream = InStream(Error); pub stream: Stream, @@ -361,7 +361,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) /// This makes look-ahead style parsing much easier. pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) type { return struct { - const Self = this; + const Self = @This(); pub const Error = InStreamError; pub const Stream = InStream(Error); @@ -424,7 +424,7 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ } pub const SliceInStream = struct { - const Self = this; + const Self = @This(); pub const Error = error{}; pub const Stream = InStream(Error); @@ -505,7 +505,7 @@ pub fn BufferedOutStream(comptime Error: type) type { pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamError: type) type { return struct { - const Self = this; + const Self = @This(); pub const Stream = OutStream(Error); pub const Error = OutStreamError; diff --git a/std/lazy_init.zig b/std/lazy_init.zig @@ -18,7 +18,7 @@ fn LazyInit(comptime T: type) type { state: u8, // TODO make this an enum data: Data, - const Self = this; + const Self = @This(); // TODO this isn't working for void, investigate and then remove this special case const Data = if (@sizeOf(T) == 0) u8 else T; diff --git a/std/linked_list.zig b/std/linked_list.zig @@ -7,7 +7,7 @@ const Allocator = mem.Allocator; /// Generic doubly linked list. pub fn LinkedList(comptime T: type) type { return struct { - const Self = this; + const Self = @This(); /// Node inside the linked list wrapping the actual data. pub const Node = struct { diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig @@ -44,7 +44,7 @@ fn cosh32(z: *const Complex(f32)) Complex(f32) { else if (ix < 0x4340b1e7) { const v = Complex(f32).new(math.fabs(x), y); const r = ldexp_cexp(v, -1); - return Complex(f32).new(x, y * math.copysign(f32, 1, x)); + return Complex(f32).new(r.re, r.im * math.copysign(f32, 1, x)); } // x >= 192.7: result always overflows else { @@ -112,7 +112,7 @@ fn cosh64(z: *const Complex(f64)) Complex(f64) { else if (ix < 0x4096bbaa) { const v = Complex(f64).new(math.fabs(x), y); const r = ldexp_cexp(v, -1); - return Complex(f64).new(x, y * math.copysign(f64, 1, x)); + return Complex(f64).new(r.re, r.im * math.copysign(f64, 1, x)); } // x >= 1455: result always overflows else { diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig @@ -69,7 +69,7 @@ fn exp64(z: Complex(f64)) Complex(f64) { const y = z.im; const fy = @bitCast(u64, y); - const hy = u32(fy >> 32) & 0x7fffffff; + const hy = @intCast(u32, (fy >> 32) & 0x7fffffff); const ly = @truncate(u32, fy); // cexp(x + i0) = exp(x) + i0 @@ -78,7 +78,7 @@ fn exp64(z: Complex(f64)) Complex(f64) { } const fx = @bitCast(u64, x); - const hx = u32(fx >> 32); + const hx = @intCast(u32, fx >> 32); const lx = @truncate(u32, fx); // cexp(0 + iy) = cos(y) + isin(y) @@ -101,8 +101,7 @@ fn exp64(z: Complex(f64)) Complex(f64) { // 709.7 <= x <= 1454.3 so must scale if (hx >= exp_overflow and hx <= cexp_overflow) { - const r = ldexp_cexp(z, 0); - return r.*; + return ldexp_cexp(z, 0); } // - x < exp_overflow => exp(x) won't overflow (common) // - x > cexp_overflow, so exp(x) * s overflows for s > 0 // - x = +-inf @@ -124,7 +123,7 @@ test "complex.cexp32" { } test "complex.cexp64" { - const a = Complex(f32).new(5, 3); + const a = Complex(f64).new(5, 3); const c = exp(a); debug.assert(math.approxEq(f64, c.re, -146.927917, epsilon)); diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig @@ -25,7 +25,7 @@ pub const tan = @import("tan.zig").tan; pub fn Complex(comptime T: type) type { return struct { - const Self = this; + const Self = @This(); re: T, im: T, diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig @@ -44,7 +44,7 @@ fn sinh32(z: Complex(f32)) Complex(f32) { else if (ix < 0x4340b1e7) { const v = Complex(f32).new(math.fabs(x), y); const r = ldexp_cexp(v, -1); - return Complex(f32).new(x * math.copysign(f32, 1, x), y); + return Complex(f32).new(r.re * math.copysign(f32, 1, x), r.im); } // x >= 192.7: result always overflows else { @@ -111,7 +111,7 @@ fn sinh64(z: Complex(f64)) Complex(f64) { else if (ix < 0x4096bbaa) { const v = Complex(f64).new(math.fabs(x), y); const r = ldexp_cexp(v, -1); - return Complex(f64).new(x * math.copysign(f64, 1, x), y); + return Complex(f64).new(r.re * math.copysign(f64, 1, x), r.im); } // x >= 1455: result always overflows else { diff --git a/std/mem.zig b/std/mem.zig @@ -3,7 +3,7 @@ const debug = std.debug; const assert = debug.assert; const math = std.math; const builtin = @import("builtin"); -const mem = this; +const mem = @This(); pub const Allocator = struct { pub const Error = error{OutOfMemory}; diff --git a/std/net.zig b/std/net.zig @@ -1,7 +1,7 @@ const std = @import("index.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; -const net = this; +const net = @This(); const posix = std.os.posix; const mem = std.mem; diff --git a/std/os/child_process.zig b/std/os/child_process.zig @@ -658,8 +658,16 @@ fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, c // environment variables to programs that were not, which seems unlikely. // More investigation is needed. if (windows.CreateProcessW( - app_name, cmd_line, null, null, windows.TRUE, windows.CREATE_UNICODE_ENVIRONMENT, - @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation, + app_name, + cmd_line, + null, + null, + windows.TRUE, + windows.CREATE_UNICODE_ENVIRONMENT, + @ptrCast(?*c_void, envp_ptr), + cwd_ptr, + lpStartupInfo, + lpProcessInformation, ) == 0) { const err = windows.GetLastError(); switch (err) { diff --git a/std/os/index.zig b/std/os/index.zig @@ -6,7 +6,7 @@ const is_posix = switch (builtin.os) { builtin.Os.linux, builtin.Os.macosx => true, else => false, }; -const os = this; +const os = @This(); test "std.os" { _ = @import("child_process.zig"); @@ -343,23 +343,25 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); const rc = posix.write(fd, bytes.ptr + index, amt_to_write); const write_err = posix.getErrno(rc); - if (write_err > 0) { - return switch (write_err) { - posix.EINTR => continue, - posix.EINVAL, posix.EFAULT => unreachable, - posix.EAGAIN => PosixWriteError.WouldBlock, - posix.EBADF => PosixWriteError.FileClosed, - posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired, - posix.EDQUOT => PosixWriteError.DiskQuota, - posix.EFBIG => PosixWriteError.FileTooBig, - posix.EIO => PosixWriteError.InputOutput, - posix.ENOSPC => PosixWriteError.NoSpaceLeft, - posix.EPERM => PosixWriteError.AccessDenied, - posix.EPIPE => PosixWriteError.BrokenPipe, - else => unexpectedErrorPosix(write_err), - }; + switch (write_err) { + 0 => { + index += rc; + continue; + }, + posix.EINTR => continue, + posix.EINVAL => unreachable, + posix.EFAULT => unreachable, + posix.EAGAIN => return PosixWriteError.WouldBlock, + posix.EBADF => return PosixWriteError.FileClosed, + posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired, + posix.EDQUOT => return PosixWriteError.DiskQuota, + posix.EFBIG => return PosixWriteError.FileTooBig, + posix.EIO => return PosixWriteError.InputOutput, + posix.ENOSPC => return PosixWriteError.NoSpaceLeft, + posix.EPERM => return PosixWriteError.AccessDenied, + posix.EPIPE => return PosixWriteError.BrokenPipe, + else => return unexpectedErrorPosix(write_err), } - index += rc; } } @@ -1614,7 +1616,7 @@ pub const Dir = struct { return null; } const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); - if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'})) + if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' })) continue; // Trust that Windows gives us valid UTF-16LE const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig @@ -206,7 +206,6 @@ pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2; pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1; pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; - pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct { dwSize: COORD, dwCursorPosition: COORD, diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig @@ -52,7 +52,8 @@ pub const WriteError = error{ }; pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), null, null) == 0) { + var bytes_written: windows.DWORD = undefined; + if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), &bytes_written, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, @@ -222,7 +223,7 @@ pub fn windowsFindFirstFile( dir_path: []const u8, find_file_data: *windows.WIN32_FIND_DATAW, ) !windows.HANDLE { - const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{'\\', '*', 0}); + const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{ '\\', '*', 0 }); const handle = windows.FindFirstFileW(&dir_path_w, find_file_data); if (handle == windows.INVALID_HANDLE_VALUE) { @@ -277,6 +278,7 @@ pub const WindowsWaitResult = enum { Normal, Aborted, Cancelled, + EOF, }; pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult { @@ -285,6 +287,7 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t switch (err) { windows.ERROR.ABANDONED_WAIT_0 => return WindowsWaitResult.Aborted, windows.ERROR.OPERATION_ABORTED => return WindowsWaitResult.Cancelled, + windows.ERROR.HANDLE_EOF => return WindowsWaitResult.EOF, else => { if (std.debug.runtime_safety) { std.debug.panic("unexpected error: {}\n", err); diff --git a/std/pdb.zig b/std/pdb.zig @@ -64,19 +64,35 @@ pub const ModInfo = packed struct { }; pub const SectionMapHeader = packed struct { - Count: u16, /// Number of segment descriptors - LogCount: u16, /// Number of logical segment descriptors + /// Number of segment descriptors + Count: u16, + + /// Number of logical segment descriptors + LogCount: u16, }; pub const SectionMapEntry = packed struct { - Flags: u16 , /// See the SectionMapEntryFlags enum below. - Ovl: u16 , /// Logical overlay number - Group: u16 , /// Group index into descriptor array. - Frame: u16 , - SectionName: u16 , /// Byte index of segment / group name in string table, or 0xFFFF. - ClassName: u16 , /// Byte index of class in string table, or 0xFFFF. - Offset: u32 , /// Byte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group. - SectionLength: u32 , /// Byte count of the segment or group. + /// See the SectionMapEntryFlags enum below. + Flags: u16, + + /// Logical overlay number + Ovl: u16, + + /// Group index into descriptor array. + Group: u16, + Frame: u16, + + /// Byte index of segment / group name in string table, or 0xFFFF. + SectionName: u16, + + /// Byte index of class in string table, or 0xFFFF. + ClassName: u16, + + /// Byte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group. + Offset: u32, + + /// Byte count of the segment or group. + SectionLength: u32, }; pub const StreamType = enum(u16) { @@ -290,13 +306,13 @@ pub const SymbolKind = packed enum(u16) { pub const TypeIndex = u32; pub const ProcSym = packed struct { - Parent: u32 , - End: u32 , - Next: u32 , - CodeSize: u32 , - DbgStart: u32 , - DbgEnd: u32 , - FunctionType: TypeIndex , + Parent: u32, + End: u32, + Next: u32, + CodeSize: u32, + DbgStart: u32, + DbgEnd: u32, + FunctionType: TypeIndex, CodeOffset: u32, Segment: u16, Flags: ProcSymFlags, @@ -315,25 +331,34 @@ pub const ProcSymFlags = packed struct { HasOptimizedDebugInfo: bool, }; -pub const SectionContrSubstreamVersion = enum(u32) { - Ver60 = 0xeffe0000 + 19970605, - V2 = 0xeffe0000 + 20140516 +pub const SectionContrSubstreamVersion = enum(u32) { + Ver60 = 0xeffe0000 + 19970605, + V2 = 0xeffe0000 + 20140516, }; pub const RecordPrefix = packed struct { - RecordLen: u16, /// Record length, starting from &RecordKind. - RecordKind: SymbolKind, /// Record kind enum (SymRecordKind or TypeRecordKind) + /// Record length, starting from &RecordKind. + RecordLen: u16, + + /// Record kind enum (SymRecordKind or TypeRecordKind) + RecordKind: SymbolKind, }; pub const LineFragmentHeader = packed struct { - RelocOffset: u32, /// Code offset of line contribution. - RelocSegment: u16, /// Code segment of line contribution. + /// Code offset of line contribution. + RelocOffset: u32, + + /// Code segment of line contribution. + RelocSegment: u16, Flags: LineFlags, - CodeSize: u32, /// Code size of this line contribution. + + /// Code size of this line contribution. + CodeSize: u32, }; pub const LineFlags = packed struct { - LF_HaveColumns: bool, /// CV_LINES_HAVE_COLUMNS + /// CV_LINES_HAVE_COLUMNS + LF_HaveColumns: bool, unused: u15, }; @@ -348,12 +373,14 @@ pub const LineBlockFragmentHeader = packed struct { /// table of the actual name. NameIndex: u32, NumLines: u32, - BlockSize: u32, /// code size of block, in bytes -}; + /// code size of block, in bytes + BlockSize: u32, +}; pub const LineNumberEntry = packed struct { - Offset: u32, /// Offset to start of code bytes for line number + /// Offset to start of code bytes for line number + Offset: u32, Flags: u32, /// TODO runtime crash when I make the actual type of Flags this @@ -371,42 +398,53 @@ pub const ColumnNumberEntry = packed struct { /// Checksum bytes follow. pub const FileChecksumEntryHeader = packed struct { - FileNameOffset: u32, /// Byte offset of filename in global string table. - ChecksumSize: u8, /// Number of bytes of checksum. - ChecksumKind: u8, /// FileChecksumKind + /// Byte offset of filename in global string table. + FileNameOffset: u32, + + /// Number of bytes of checksum. + ChecksumSize: u8, + + /// FileChecksumKind + ChecksumKind: u8, }; pub const DebugSubsectionKind = packed enum(u32) { - None = 0, - Symbols = 0xf1, - Lines = 0xf2, - StringTable = 0xf3, - FileChecksums = 0xf4, - FrameData = 0xf5, - InlineeLines = 0xf6, - CrossScopeImports = 0xf7, - CrossScopeExports = 0xf8, - - // These appear to relate to .Net assembly info. - ILLines = 0xf9, - FuncMDTokenMap = 0xfa, - TypeMDTokenMap = 0xfb, - MergedAssemblyInput = 0xfc, - - CoffSymbolRVA = 0xfd, + None = 0, + Symbols = 0xf1, + Lines = 0xf2, + StringTable = 0xf3, + FileChecksums = 0xf4, + FrameData = 0xf5, + InlineeLines = 0xf6, + CrossScopeImports = 0xf7, + CrossScopeExports = 0xf8, + + // These appear to relate to .Net assembly info. + ILLines = 0xf9, + FuncMDTokenMap = 0xfa, + TypeMDTokenMap = 0xfb, + MergedAssemblyInput = 0xfc, + + CoffSymbolRVA = 0xfd, }; - pub const DebugSubsectionHeader = packed struct { - Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum - Length: u32, /// number of bytes occupied by this record. -}; + /// codeview::DebugSubsectionKind enum + Kind: DebugSubsectionKind, + /// number of bytes occupied by this record. + Length: u32, +}; pub const PDBStringTableHeader = packed struct { - Signature: u32, /// PDBStringTableSignature - HashVersion: u32, /// 1 or 2 - ByteSize: u32, /// Number of bytes of names buffer. + /// PDBStringTableSignature + Signature: u32, + + /// 1 or 2 + HashVersion: u32, + + /// Number of bytes of names buffer. + ByteSize: u32, }; pub const Pdb = struct { @@ -456,7 +494,7 @@ const Msf = struct { switch (superblock.BlockSize) { // llvm only supports 4096 but we can handle any of these values 512, 1024, 2048, 4096 => {}, - else => return error.InvalidDebugInfo + else => return error.InvalidDebugInfo, } if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos()) @@ -536,7 +574,6 @@ const SuperBlock = packed struct { /// The number of ulittle32_t’s in this array is given by /// ceil(NumDirectoryBytes / BlockSize). BlockMapAddr: u32, - }; const MsfStream = struct { @@ -552,14 +589,12 @@ const MsfStream = struct { pub const Stream = io.InStream(Error); fn init(block_size: u32, block_count: u32, pos: usize, file: os.File, allocator: *mem.Allocator) !MsfStream { - var stream = MsfStream { + var stream = MsfStream{ .in_file = file, .pos = 0, .blocks = try allocator.alloc(u32, block_count), .block_size = block_size, - .stream = Stream { - .readFn = readFn, - }, + .stream = Stream{ .readFn = readFn }, }; var file_stream = io.FileInStream.init(file); @@ -597,7 +632,7 @@ const MsfStream = struct { var size: usize = 0; for (buffer) |*byte| { - byte.* = try in.readByte(); + byte.* = try in.readByte(); offset += 1; size += 1; diff --git a/std/segmented_list.zig b/std/segmented_list.zig @@ -75,7 +75,7 @@ const Allocator = std.mem.Allocator; /// size is small. `prealloc_item_count` must be 0, or a power of 2. pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type { return struct { - const Self = this; + const Self = @This(); const prealloc_exp = blk: { // we don't use the prealloc_exp constant when prealloc_item_count is 0. assert(prealloc_item_count != 0); diff --git a/std/zig/ast.zig b/std/zig/ast.zig @@ -231,7 +231,7 @@ pub const Error = union(enum) { fn SingleTokenError(comptime msg: []const u8) type { return struct { - const ThisError = this; + const ThisError = @This(); token: TokenIndex, @@ -244,7 +244,7 @@ pub const Error = union(enum) { fn SimpleError(comptime msg: []const u8) type { return struct { - const ThisError = this; + const ThisError = @This(); token: TokenIndex, diff --git a/std/zig/bench.zig b/std/zig/bench.zig @@ -24,7 +24,7 @@ pub fn main() !void { const mb_per_sec = bytes_per_sec / (1024 * 1024); var stdout_file = try std.io.getStdOut(); - const stdout = &std.io.FileOutStream.init(&stdout_file).stream; + const stdout = &std.io.FileOutStream.init(stdout_file).stream; try stdout.print("{.3} MiB/s, {} KiB used \n", mb_per_sec, memory_used / 1024); } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig @@ -1354,7 +1354,7 @@ test "zig fmt: indexing" { test "zig fmt: struct declaration" { try testCanonical( \\const S = struct { - \\ const Self = this; + \\ const Self = @This(); \\ f1: u8, \\ pub f3: u8, \\ diff --git a/std/zig/render.zig b/std/zig/render.zig @@ -20,7 +20,7 @@ pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@typeOf( // make a passthrough stream that checks whether something changed const MyStream = struct { - const MyStream = this; + const MyStream = @This(); const StreamError = @typeOf(stream).Child.Error; const Stream = std.io.OutStream(StreamError); diff --git a/test/behavior.zig b/test/behavior.zig @@ -10,7 +10,10 @@ comptime { _ = @import("cases/bool.zig"); _ = @import("cases/bugs/1111.zig"); _ = @import("cases/bugs/1277.zig"); + _ = @import("cases/bugs/1322.zig"); + _ = @import("cases/bugs/1381.zig"); _ = @import("cases/bugs/1421.zig"); + _ = @import("cases/bugs/1442.zig"); _ = @import("cases/bugs/394.zig"); _ = @import("cases/bugs/655.zig"); _ = @import("cases/bugs/656.zig"); diff --git a/test/cases/align.zig b/test/cases/align.zig @@ -212,3 +212,10 @@ fn fnWithAlignedStack() i32 { @setAlignStack(256); return 1234; } + +test "alignment of structs" { + assert(@alignOf(struct { + a: i32, + b: *i32, + }) == @alignOf(usize)); +} diff --git a/test/cases/bugs/1322.zig b/test/cases/bugs/1322.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +const B = union(enum) { + c: C, + None, +}; + +const A = struct { + b: B, +}; + +const C = struct {}; + +test "tagged union with all void fields but a meaningful tag" { + var a: A = A{ .b = B{ .c = C{} } }; + std.debug.assert(@TagType(B)(a.b) == @TagType(B).c); + a = A{ .b = B.None }; + std.debug.assert(@TagType(B)(a.b) == @TagType(B).None); +} diff --git a/test/cases/bugs/1381.zig b/test/cases/bugs/1381.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +const B = union(enum) { + D: u8, + E: u16, +}; + +const A = union(enum) { + B: B, + C: u8, +}; + +test "union that needs padding bytes inside an array" { + var as = []A{ + A{ .B = B{ .D = 1 } }, + A{ .B = B{ .D = 1 } }, + }; + + const a = as[0].B; + std.debug.assertOrPanic(a.D == 1); +} diff --git a/test/cases/bugs/1442.zig b/test/cases/bugs/1442.zig @@ -0,0 +1,11 @@ +const std = @import("std"); + +const Union = union(enum) { + Text: []const u8, + Color: u32, +}; + +test "const error union field alignment" { + var union_or_err: error!Union = Union{ .Color = 1234 }; + std.debug.assertOrPanic((union_or_err catch unreachable).Color == 1234); +} diff --git a/test/cases/cast.zig b/test/cases/cast.zig @@ -64,7 +64,7 @@ test "implicitly cast a container to a const pointer of it" { fn Struct(comptime T: type) type { return struct { - const Self = this; + const Self = @This(); x: T, fn pointer(self: *const Self) Self { @@ -106,7 +106,7 @@ const Enum = enum { test "implicitly cast indirect pointer to maybe-indirect pointer" { const S = struct { - const Self = this; + const Self = @This(); x: u8, fn constConst(p: *const *const Self) u8 { return p.*.x; @@ -526,3 +526,14 @@ test "*usize to *void" { var v = @ptrCast(*void, &i); v.* = {}; } + +test "compile time int to ptr of function" { + foobar(FUNCTION_CONSTANT); +} + +pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, @maxValue(usize)); +pub const PFN_void = extern fn (*c_void) void; + +fn foobar(func: PFN_void) void { + std.debug.assert(@ptrToInt(func) == @maxValue(usize)); +} diff --git a/test/cases/eval.zig b/test/cases/eval.zig @@ -275,7 +275,7 @@ test "eval @setFloatMode at compile-time" { } fn fnWithFloatMode() f32 { - @setFloatMode(this, builtin.FloatMode.Strict); + @setFloatMode(builtin.FloatMode.Strict); return 1234.0; } @@ -628,7 +628,7 @@ test "call method with comptime pass-by-non-copying-value self parameter" { const S = struct { a: u8, - fn b(comptime s: this) u8 { + fn b(comptime s: @This()) u8 { return s.a; } }; diff --git a/test/cases/misc.zig b/test/cases/misc.zig @@ -510,9 +510,6 @@ test "@typeId" { assert(@typeId(AUnion) == Tid.Union); assert(@typeId(fn () void) == Tid.Fn); assert(@typeId(@typeOf(builtin)) == Tid.Namespace); - assert(@typeId(@typeOf(x: { - break :x this; - })) == Tid.Block); // TODO bound fn // TODO arg tuple // TODO opaque diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig @@ -1,6 +1,6 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; -const reflection = this; +const reflection = @This(); test "reflection: array, pointer, optional, error union type child" { comptime { diff --git a/test/cases/struct.zig b/test/cases/struct.zig @@ -423,10 +423,10 @@ fn alloc(comptime T: type) []T { test "call method with mutable reference to struct with no fields" { const S = struct { - fn doC(s: *const this) bool { + fn doC(s: *const @This()) bool { return true; } - fn do(s: *this) bool { + fn do(s: *@This()) bool { return true; } }; diff --git a/test/cases/this.zig b/test/cases/this.zig @@ -1,10 +1,10 @@ const assert = @import("std").debug.assert; -const module = this; +const module = @This(); fn Point(comptime T: type) type { return struct { - const Self = this; + const Self = @This(); x: T, y: T, @@ -19,11 +19,6 @@ fn add(x: i32, y: i32) i32 { return x + y; } -fn factorial(x: i32) i32 { - const selfFn = this; - return if (x == 0) 1 else x * selfFn(x - 1); -} - test "this refer to module call private fn" { assert(module.add(1, 2) == 3); } @@ -37,7 +32,3 @@ test "this refer to container" { assert(pt.x == 13); assert(pt.y == 35); } - -test "this refer to fn" { - assert(factorial(5) == 120); -} diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig @@ -166,7 +166,7 @@ fn testUnion() void { assert(TypeId(typeinfo_info) == TypeId.Union); assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); assert(typeinfo_info.Union.tag_type.? == TypeId); - assert(typeinfo_info.Union.fields.len == 25); + assert(typeinfo_info.Union.fields.len == 24); assert(typeinfo_info.Union.fields[4].enum_field != null); assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4); assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); @@ -217,7 +217,7 @@ fn testStruct() void { } const TestStruct = packed struct { - const Self = this; + const Self = @This(); fieldA: usize, fieldB: void, diff --git a/test/cases/union.zig b/test/cases/union.zig @@ -324,3 +324,42 @@ test "tagged union with no payloads" { @TagType(UnionEnumNoPayloads).B => {}, } } + +test "union with only 1 field casted to its enum type" { + const Literal = union(enum) { + Number: f64, + Bool: bool, + }; + + const Expr = union(enum) { + Literal: Literal, + }; + + var e = Expr{ .Literal = Literal{ .Bool = true } }; + const Tag = @TagType(Expr); + comptime assert(@TagType(Tag) == comptime_int); + var t = Tag(e); + assert(t == Expr.Literal); +} + +test "union with only 1 field casted to its enum type which has enum value specified" { + const Literal = union(enum) { + Number: f64, + Bool: bool, + }; + + const Tag = enum { + Literal = 33, + }; + + const Expr = union(Tag) { + Literal: Literal, + }; + + var e = Expr{ .Literal = Literal{ .Bool = true } }; + comptime assert(@TagType(Tag) == comptime_int); + var t = Tag(e); + assert(t == Expr.Literal); + assert(@enumToInt(t) == 33); + comptime assert(@enumToInt(t) == 33); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig @@ -2,6 +2,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( + "non error sets used in merge error sets operator", + \\export fn foo() void { + \\ const Errors = u8 || u16; + \\} + \\export fn bar() void { + \\ const Errors = error{} || u16; + \\} + , + ".tmp_source.zig:2:20: error: expected error set type, found 'u8'", + ".tmp_source.zig:5:31: error: expected error set type, found 'u16'", + ); + + cases.add( "variable initialization compile error then referenced", \\fn Undeclared() type { \\ return T; @@ -3431,7 +3444,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:8:26: error: expected type '*const u3', found '*align(1:3:6) const u3'", + ".tmp_source.zig:8:26: error: expected type '*const u3', found '*align(:3:6) const u3'", ); cases.add( @@ -3800,11 +3813,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return struct { \\ b: B(), \\ - \\ const Self = this; + \\ const Self = @This(); \\ \\ fn B() type { \\ return struct { - \\ const Self = this; + \\ const Self = @This(); \\ }; \\ } \\ }; @@ -3983,8 +3996,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "@setFloatMode twice for same scope", \\export fn foo() void { - \\ @setFloatMode(this, @import("builtin").FloatMode.Optimized); - \\ @setFloatMode(this, @import("builtin").FloatMode.Optimized); + \\ @setFloatMode(@import("builtin").FloatMode.Optimized); + \\ @setFloatMode(@import("builtin").FloatMode.Optimized); \\} , ".tmp_source.zig:3:5: error: float mode set twice for same scope", @@ -4301,12 +4314,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var a = undefined; \\ var b = 1; \\ var c = 1.0; - \\ var d = this; - \\ var e = null; - \\ var f = opaque.*; - \\ var g = i32; - \\ var h = @import("std",); - \\ var i = (Foo {}).bar; + \\ var d = null; + \\ var e = opaque.*; + \\ var f = i32; + \\ var g = @import("std",); + \\ var h = (Foo {}).bar; \\ \\ var z: noreturn = return; \\} @@ -4319,13 +4331,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:7:4: error: variable of type '(undefined)' must be const or comptime", ".tmp_source.zig:8:4: error: variable of type 'comptime_int' must be const or comptime", ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime", - ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime", - ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime", - ".tmp_source.zig:12:4: error: variable of type 'Opaque' not allowed", - ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime", - ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime", - ".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", - ".tmp_source.zig:17:4: error: unreachable code", + ".tmp_source.zig:10:4: error: variable of type '(null)' must be const or comptime", + ".tmp_source.zig:11:4: error: variable of type 'Opaque' not allowed", + ".tmp_source.zig:12:4: error: variable of type 'type' must be const or comptime", + ".tmp_source.zig:13:4: error: variable of type '(namespace)' must be const or comptime", + ".tmp_source.zig:14:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", + ".tmp_source.zig:16:4: error: unreachable code", ); cases.add( diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig @@ -191,7 +191,7 @@ pub fn main() !void { var stdin_buf = try Buffer.initSize(global_allocator, 0); defer stdin_buf.deinit(); - var stdin_adapter = io.FileInStream.init(&stdin_file); + var stdin_adapter = io.FileInStream.init(stdin_file); try stdin_adapter.stream.readAllBuffer(&stdin_buf, @maxValue(usize)); var result_buf = try Buffer.initSize(global_allocator, 0);