| author | |
| committer | |
| log | dd9728c5a03844267bc378c326c353fd2b0e084e |
| tree | 5786bd228312976ee482a58463a798bc426d64af |
| parent | 558b0b87913dfb6e6b76f5dbe2c36b920302faab |
| parent | 10bdf73a02c90dc375985e49b08b5020cfc20b93 |
76 files changed, 7872 insertions(+), 1812 deletions(-)
CMakeLists.txt+12-1| ... | @@ -426,6 +426,7 @@ set(ZIG_SOURCES | ... | @@ -426,6 +426,7 @@ set(ZIG_SOURCES |
| 426 | ) | 426 | ) |
| 427 | set(ZIG_CPP_SOURCES | 427 | set(ZIG_CPP_SOURCES |
| 428 | "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" | 428 | "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" |
| 429 | "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" | ||
| 429 | ) | 430 | ) |
| 430 | 431 | ||
| 431 | set(ZIG_STD_FILES | 432 | set(ZIG_STD_FILES |
| ... | @@ -479,6 +480,7 @@ set(ZIG_STD_FILES | ... | @@ -479,6 +480,7 @@ set(ZIG_STD_FILES |
| 479 | "index.zig" | 480 | "index.zig" |
| 480 | "io.zig" | 481 | "io.zig" |
| 481 | "json.zig" | 482 | "json.zig" |
| 483 | "lazy_init.zig" | ||
| 482 | "linked_list.zig" | 484 | "linked_list.zig" |
| 483 | "macho.zig" | 485 | "macho.zig" |
| 484 | "math/acos.zig" | 486 | "math/acos.zig" |
| ... | @@ -488,6 +490,7 @@ set(ZIG_STD_FILES | ... | @@ -488,6 +490,7 @@ set(ZIG_STD_FILES |
| 488 | "math/atan.zig" | 490 | "math/atan.zig" |
| 489 | "math/atan2.zig" | 491 | "math/atan2.zig" |
| 490 | "math/atanh.zig" | 492 | "math/atanh.zig" |
| 493 | "math/big/index.zig" | ||
| 491 | "math/big/int.zig" | 494 | "math/big/int.zig" |
| 492 | "math/cbrt.zig" | 495 | "math/cbrt.zig" |
| 493 | "math/ceil.zig" | 496 | "math/ceil.zig" |
| ... | @@ -553,9 +556,10 @@ set(ZIG_STD_FILES | ... | @@ -553,9 +556,10 @@ set(ZIG_STD_FILES |
| 553 | "net.zig" | 556 | "net.zig" |
| 554 | "os/child_process.zig" | 557 | "os/child_process.zig" |
| 555 | "os/darwin.zig" | 558 | "os/darwin.zig" |
| 556 | "os/darwin_errno.zig" | 559 | "os/darwin/errno.zig" |
| 557 | "os/epoch.zig" | 560 | "os/epoch.zig" |
| 558 | "os/file.zig" | 561 | "os/file.zig" |
| 562 | "os/get_app_data_dir.zig" | ||
| 559 | "os/get_user_id.zig" | 563 | "os/get_user_id.zig" |
| 560 | "os/index.zig" | 564 | "os/index.zig" |
| 561 | "os/linux/errno.zig" | 565 | "os/linux/errno.zig" |
| ... | @@ -564,8 +568,14 @@ set(ZIG_STD_FILES | ... | @@ -564,8 +568,14 @@ set(ZIG_STD_FILES |
| 564 | "os/linux/x86_64.zig" | 568 | "os/linux/x86_64.zig" |
| 565 | "os/path.zig" | 569 | "os/path.zig" |
| 566 | "os/time.zig" | 570 | "os/time.zig" |
| 571 | "os/windows/advapi32.zig" | ||
| 567 | "os/windows/error.zig" | 572 | "os/windows/error.zig" |
| 568 | "os/windows/index.zig" | 573 | "os/windows/index.zig" |
| 574 | "os/windows/kernel32.zig" | ||
| 575 | "os/windows/ole32.zig" | ||
| 576 | "os/windows/shell32.zig" | ||
| 577 | "os/windows/shlwapi.zig" | ||
| 578 | "os/windows/user32.zig" | ||
| 569 | "os/windows/util.zig" | 579 | "os/windows/util.zig" |
| 570 | "os/zen.zig" | 580 | "os/zen.zig" |
| 571 | "rand/index.zig" | 581 | "rand/index.zig" |
| ... | @@ -614,6 +624,7 @@ set(ZIG_STD_FILES | ... | @@ -614,6 +624,7 @@ set(ZIG_STD_FILES |
| 614 | "zig/ast.zig" | 624 | "zig/ast.zig" |
| 615 | "zig/index.zig" | 625 | "zig/index.zig" |
| 616 | "zig/parse.zig" | 626 | "zig/parse.zig" |
| 627 | "zig/parse_string_literal.zig" | ||
| 617 | "zig/render.zig" | 628 | "zig/render.zig" |
| 618 | "zig/tokenizer.zig" | 629 | "zig/tokenizer.zig" |
| 619 | ) | 630 | ) |
README.md+5-5| ... | @@ -21,19 +21,19 @@ clarity. | ... | @@ -21,19 +21,19 @@ clarity. |
| 21 | * Compatible with C libraries with no wrapper necessary. Directly include | 21 | * Compatible with C libraries with no wrapper necessary. Directly include |
| 22 | C .h files and get access to the functions and symbols therein. | 22 | C .h files and get access to the functions and symbols therein. |
| 23 | * Provides standard library which competes with the C standard library and is | 23 | * Provides standard library which competes with the C standard library and is |
| 24 | always compiled against statically in source form. Compile units do not | 24 | always compiled against statically in source form. Zig binaries do not |
| 25 | depend on libc unless explicitly linked. | 25 | depend on libc unless explicitly linked. |
| 26 | * Nullable type instead of null pointers. | 26 | * Optional type instead of null pointers. |
| 27 | * Safe unions, tagged unions, and C ABI compatible unions. | 27 | * Safe unions, tagged unions, and C ABI compatible unions. |
| 28 | * Generics so that one can write efficient data structures that work for any | 28 | * Generics so that one can write efficient data structures that work for any |
| 29 | data type. | 29 | data type. |
| 30 | * No header files required. Top level declarations are entirely | 30 | * No header files required. Top level declarations are entirely |
| 31 | order-independent. | 31 | order-independent. |
| 32 | * Compile-time code execution. Compile-time reflection. | 32 | * Compile-time code execution. Compile-time reflection. |
| 33 | * Partial compile-time function evaluation with eliminates the need for | 33 | * Partial compile-time function evaluation which eliminates the need for |
| 34 | a preprocessor or macros. | 34 | a preprocessor or macros. |
| 35 | * The binaries produced by Zig have complete debugging information so you can, | 35 | * The binaries produced by Zig have complete debugging information so you can, |
| 36 | for example, use GDB or MSVC to debug your software. | 36 | for example, use GDB, MSVC, or LLDB to debug your software. |
| 37 | * Built-in unit tests with `zig test`. | 37 | * Built-in unit tests with `zig test`. |
| 38 | * Friendly toward package maintainers. Reproducible build, bootstrapping | 38 | * Friendly toward package maintainers. Reproducible build, bootstrapping |
| 39 | process carefully documented. Issues filed by package maintainers are | 39 | process carefully documented. Issues filed by package maintainers are |
| ... | @@ -70,7 +70,7 @@ that counts as "freestanding" for the purposes of this table. | ... | @@ -70,7 +70,7 @@ that counts as "freestanding" for the purposes of this table. |
| 70 | 70 | ||
| 71 | ## Community | 71 | ## Community |
| 72 | 72 | ||
| 73 | * IRC: `#zig` on Freenode. | 73 | * IRC: `#zig` on Freenode ([Channel Logs](https://irclog.whitequark.org/zig/)). |
| 74 | * Reddit: [/r/zig](https://www.reddit.com/r/zig) | 74 | * Reddit: [/r/zig](https://www.reddit.com/r/zig) |
| 75 | * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang) | 75 | * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang) |
| 76 | 76 |
build.zig+1-1| ... | @@ -92,7 +92,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -92,7 +92,7 @@ pub fn build(b: *Builder) !void { |
| 92 | test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes)); | 92 | test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes)); |
| 93 | 93 | ||
| 94 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); | 94 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); |
| 95 | test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); | 95 | test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes)); |
| 96 | test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); | 96 | test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); |
| 97 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); | 97 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); |
| 98 | test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); | 98 | test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); |
doc/langref.html.in+3-3| ... | @@ -1087,7 +1087,7 @@ unwrapped == 1234</code></pre> | ... | @@ -1087,7 +1087,7 @@ unwrapped == 1234</code></pre> |
| 1087 | </td> | 1087 | </td> |
| 1088 | <td> | 1088 | <td> |
| 1089 | If <code>a</code> is <code>false</code>, returns <code>false</code> | 1089 | If <code>a</code> is <code>false</code>, returns <code>false</code> |
| 1090 | without evaluating <code>b</code>. Otherwise, retuns <code>b</code>. | 1090 | without evaluating <code>b</code>. Otherwise, returns <code>b</code>. |
| 1091 | </td> | 1091 | </td> |
| 1092 | <td> | 1092 | <td> |
| 1093 | <pre><code class="zig">false and true == false</code></pre> | 1093 | <pre><code class="zig">false and true == false</code></pre> |
| ... | @@ -1102,7 +1102,7 @@ unwrapped == 1234</code></pre> | ... | @@ -1102,7 +1102,7 @@ unwrapped == 1234</code></pre> |
| 1102 | </td> | 1102 | </td> |
| 1103 | <td> | 1103 | <td> |
| 1104 | If <code>a</code> is <code>true</code>, returns <code>true</code> | 1104 | If <code>a</code> is <code>true</code>, returns <code>true</code> |
| 1105 | without evaluating <code>b</code>. Otherwise, retuns <code>b</code>. | 1105 | without evaluating <code>b</code>. Otherwise, returns <code>b</code>. |
| 1106 | </td> | 1106 | </td> |
| 1107 | <td> | 1107 | <td> |
| 1108 | <pre><code class="zig">false or true == true</code></pre> | 1108 | <pre><code class="zig">false or true == true</code></pre> |
| ... | @@ -1483,7 +1483,7 @@ test "pointer array access" { | ... | @@ -1483,7 +1483,7 @@ test "pointer array access" { |
| 1483 | } | 1483 | } |
| 1484 | 1484 | ||
| 1485 | test "pointer slicing" { | 1485 | test "pointer slicing" { |
| 1486 | // In Zig, we prefer using slices over null-terminated pointers. | 1486 | // In Zig, we prefer slices over pointers to null-terminated arrays. |
| 1487 | // You can turn an array into a slice using slice syntax: | 1487 | // You can turn an array into a slice using slice syntax: |
| 1488 | var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | 1488 | var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; |
| 1489 | const slice = array[2..4]; | 1489 | const slice = array[2..4]; |
src-self-hosted/c.zig+1| ... | @@ -4,4 +4,5 @@ pub use @cImport({ | ... | @@ -4,4 +4,5 @@ pub use @cImport({ |
| 4 | @cInclude("inttypes.h"); | 4 | @cInclude("inttypes.h"); |
| 5 | @cInclude("config.h"); | 5 | @cInclude("config.h"); |
| 6 | @cInclude("zig_llvm.h"); | 6 | @cInclude("zig_llvm.h"); |
| 7 | @cInclude("windows_sdk.h"); | ||
| 7 | }); | 8 | }); |
src-self-hosted/c_int.zig created+68| ... | @@ -0,0 +1,68 @@ | ||
| 1 | pub const CInt = struct { | ||
| 2 | id: Id, | ||
| 3 | zig_name: []const u8, | ||
| 4 | c_name: []const u8, | ||
| 5 | is_signed: bool, | ||
| 6 | |||
| 7 | pub const Id = enum { | ||
| 8 | Short, | ||
| 9 | UShort, | ||
| 10 | Int, | ||
| 11 | UInt, | ||
| 12 | Long, | ||
| 13 | ULong, | ||
| 14 | LongLong, | ||
| 15 | ULongLong, | ||
| 16 | }; | ||
| 17 | |||
| 18 | pub const list = []CInt{ | ||
| 19 | CInt{ | ||
| 20 | .id = Id.Short, | ||
| 21 | .zig_name = "c_short", | ||
| 22 | .c_name = "short", | ||
| 23 | .is_signed = true, | ||
| 24 | }, | ||
| 25 | CInt{ | ||
| 26 | .id = Id.UShort, | ||
| 27 | .zig_name = "c_ushort", | ||
| 28 | .c_name = "unsigned short", | ||
| 29 | .is_signed = false, | ||
| 30 | }, | ||
| 31 | CInt{ | ||
| 32 | .id = Id.Int, | ||
| 33 | .zig_name = "c_int", | ||
| 34 | .c_name = "int", | ||
| 35 | .is_signed = true, | ||
| 36 | }, | ||
| 37 | CInt{ | ||
| 38 | .id = Id.UInt, | ||
| 39 | .zig_name = "c_uint", | ||
| 40 | .c_name = "unsigned int", | ||
| 41 | .is_signed = false, | ||
| 42 | }, | ||
| 43 | CInt{ | ||
| 44 | .id = Id.Long, | ||
| 45 | .zig_name = "c_long", | ||
| 46 | .c_name = "long", | ||
| 47 | .is_signed = true, | ||
| 48 | }, | ||
| 49 | CInt{ | ||
| 50 | .id = Id.ULong, | ||
| 51 | .zig_name = "c_ulong", | ||
| 52 | .c_name = "unsigned long", | ||
| 53 | .is_signed = false, | ||
| 54 | }, | ||
| 55 | CInt{ | ||
| 56 | .id = Id.LongLong, | ||
| 57 | .zig_name = "c_longlong", | ||
| 58 | .c_name = "long long", | ||
| 59 | .is_signed = true, | ||
| 60 | }, | ||
| 61 | CInt{ | ||
| 62 | .id = Id.ULongLong, | ||
| 63 | .zig_name = "c_ulonglong", | ||
| 64 | .c_name = "unsigned long long", | ||
| 65 | .is_signed = false, | ||
| 66 | }, | ||
| 67 | }; | ||
| 68 | }; | ||
src-self-hosted/codegen.zig+90-10| ... | @@ -1,19 +1,22 @@ | ... | @@ -1,19 +1,22 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const Compilation = @import("compilation.zig").Compilation; | 3 | const Compilation = @import("compilation.zig").Compilation; |
| 3 | // we go through llvm instead of c for 2 reasons: | ||
| 4 | // 1. to avoid accidentally calling the non-thread-safe functions | ||
| 5 | // 2. patch up some of the types to remove nullability | ||
| 6 | const llvm = @import("llvm.zig"); | 4 | const llvm = @import("llvm.zig"); |
| 5 | const c = @import("c.zig"); | ||
| 7 | const ir = @import("ir.zig"); | 6 | const ir = @import("ir.zig"); |
| 8 | const Value = @import("value.zig").Value; | 7 | const Value = @import("value.zig").Value; |
| 9 | const Type = @import("type.zig").Type; | 8 | const Type = @import("type.zig").Type; |
| 10 | const event = std.event; | 9 | const event = std.event; |
| 11 | const assert = std.debug.assert; | 10 | const assert = std.debug.assert; |
| 11 | const DW = std.dwarf; | ||
| 12 | 12 | ||
| 13 | pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { | 13 | pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { |
| 14 | fn_val.base.ref(); | 14 | fn_val.base.ref(); |
| 15 | defer fn_val.base.deref(comp); | 15 | defer fn_val.base.deref(comp); |
| 16 | defer code.destroy(comp.a()); | 16 | defer code.destroy(comp.gpa()); |
| 17 | |||
| 18 | var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable); | ||
| 19 | errdefer output_path.deinit(); | ||
| 17 | 20 | ||
| 18 | const llvm_handle = try comp.event_loop_local.getAnyLlvmContext(); | 21 | const llvm_handle = try comp.event_loop_local.getAnyLlvmContext(); |
| 19 | defer llvm_handle.release(comp.event_loop_local); | 22 | defer llvm_handle.release(comp.event_loop_local); |
| ... | @@ -23,15 +26,59 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) | ... | @@ -23,15 +26,59 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) |
| 23 | const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory; | 26 | const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory; |
| 24 | defer llvm.DisposeModule(module); | 27 | defer llvm.DisposeModule(module); |
| 25 | 28 | ||
| 29 | llvm.SetTarget(module, comp.llvm_triple.ptr()); | ||
| 30 | llvm.SetDataLayout(module, comp.target_layout_str); | ||
| 31 | |||
| 32 | if (comp.target.getObjectFormat() == builtin.ObjectFormat.coff) { | ||
| 33 | llvm.AddModuleCodeViewFlag(module); | ||
| 34 | } else { | ||
| 35 | llvm.AddModuleDebugInfoFlag(module); | ||
| 36 | } | ||
| 37 | |||
| 26 | const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory; | 38 | const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory; |
| 27 | defer llvm.DisposeBuilder(builder); | 39 | defer llvm.DisposeBuilder(builder); |
| 28 | 40 | ||
| 41 | const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory; | ||
| 42 | defer llvm.DisposeDIBuilder(dibuilder); | ||
| 43 | |||
| 44 | // Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes | ||
| 45 | // the git revision. | ||
| 46 | const producer = try std.Buffer.allocPrint( | ||
| 47 | &code.arena.allocator, | ||
| 48 | "zig {}.{}.{}", | ||
| 49 | u32(c.ZIG_VERSION_MAJOR), | ||
| 50 | u32(c.ZIG_VERSION_MINOR), | ||
| 51 | u32(c.ZIG_VERSION_PATCH), | ||
| 52 | ); | ||
| 53 | const flags = c""; | ||
| 54 | const runtime_version = 0; | ||
| 55 | const compile_unit_file = llvm.CreateFile( | ||
| 56 | dibuilder, | ||
| 57 | comp.name.ptr(), | ||
| 58 | comp.root_package.root_src_dir.ptr(), | ||
| 59 | ) orelse return error.OutOfMemory; | ||
| 60 | const is_optimized = comp.build_mode != builtin.Mode.Debug; | ||
| 61 | const compile_unit = llvm.CreateCompileUnit( | ||
| 62 | dibuilder, | ||
| 63 | DW.LANG_C99, | ||
| 64 | compile_unit_file, | ||
| 65 | producer.ptr(), | ||
| 66 | is_optimized, | ||
| 67 | flags, | ||
| 68 | runtime_version, | ||
| 69 | c"", | ||
| 70 | 0, | ||
| 71 | !comp.strip, | ||
| 72 | ) orelse return error.OutOfMemory; | ||
| 73 | |||
| 29 | var ofile = ObjectFile{ | 74 | var ofile = ObjectFile{ |
| 30 | .comp = comp, | 75 | .comp = comp, |
| 31 | .module = module, | 76 | .module = module, |
| 32 | .builder = builder, | 77 | .builder = builder, |
| 78 | .dibuilder = dibuilder, | ||
| 33 | .context = context, | 79 | .context = context, |
| 34 | .lock = event.Lock.init(comp.loop), | 80 | .lock = event.Lock.init(comp.loop), |
| 81 | .arena = &code.arena.allocator, | ||
| 35 | }; | 82 | }; |
| 36 | 83 | ||
| 37 | try renderToLlvmModule(&ofile, fn_val, code); | 84 | try renderToLlvmModule(&ofile, fn_val, code); |
| ... | @@ -41,10 +88,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) | ... | @@ -41,10 +88,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) |
| 41 | // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); | 88 | // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); |
| 42 | //} | 89 | //} |
| 43 | 90 | ||
| 44 | // TODO | 91 | llvm.DIBuilderFinalize(dibuilder); |
| 45 | //ZigLLVMDIBuilderFinalize(g->dbuilder); | ||
| 46 | 92 | ||
| 47 | if (comp.verbose_llvm_ir) { | 93 | if (comp.verbose_llvm_ir) { |
| 94 | std.debug.warn("raw module:\n"); | ||
| 48 | llvm.DumpModule(ofile.module); | 95 | llvm.DumpModule(ofile.module); |
| 49 | } | 96 | } |
| 50 | 97 | ||
| ... | @@ -53,23 +100,56 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) | ... | @@ -53,23 +100,56 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) |
| 53 | var error_ptr: ?[*]u8 = null; | 100 | var error_ptr: ?[*]u8 = null; |
| 54 | _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); | 101 | _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); |
| 55 | } | 102 | } |
| 103 | |||
| 104 | assert(comp.emit_file_type == Compilation.Emit.Binary); // TODO support other types | ||
| 105 | |||
| 106 | const is_small = comp.build_mode == builtin.Mode.ReleaseSmall; | ||
| 107 | const is_debug = comp.build_mode == builtin.Mode.Debug; | ||
| 108 | |||
| 109 | var err_msg: [*]u8 = undefined; | ||
| 110 | // TODO integrate this with evented I/O | ||
| 111 | if (llvm.TargetMachineEmitToFile( | ||
| 112 | comp.target_machine, | ||
| 113 | module, | ||
| 114 | output_path.ptr(), | ||
| 115 | llvm.EmitBinary, | ||
| 116 | &err_msg, | ||
| 117 | is_debug, | ||
| 118 | is_small, | ||
| 119 | )) { | ||
| 120 | if (std.debug.runtime_safety) { | ||
| 121 | std.debug.panic("unable to write object file {}: {s}\n", output_path.toSliceConst(), err_msg); | ||
| 122 | } | ||
| 123 | return error.WritingObjectFileFailed; | ||
| 124 | } | ||
| 125 | //validate_inline_fns(g); TODO | ||
| 126 | fn_val.containing_object = output_path; | ||
| 127 | if (comp.verbose_llvm_ir) { | ||
| 128 | std.debug.warn("optimized module:\n"); | ||
| 129 | llvm.DumpModule(ofile.module); | ||
| 130 | } | ||
| 131 | if (comp.verbose_link) { | ||
| 132 | std.debug.warn("created {}\n", output_path.toSliceConst()); | ||
| 133 | } | ||
| 56 | } | 134 | } |
| 57 | 135 | ||
| 58 | pub const ObjectFile = struct { | 136 | pub const ObjectFile = struct { |
| 59 | comp: *Compilation, | 137 | comp: *Compilation, |
| 60 | module: llvm.ModuleRef, | 138 | module: llvm.ModuleRef, |
| 61 | builder: llvm.BuilderRef, | 139 | builder: llvm.BuilderRef, |
| 140 | dibuilder: *llvm.DIBuilder, | ||
| 62 | context: llvm.ContextRef, | 141 | context: llvm.ContextRef, |
| 63 | lock: event.Lock, | 142 | lock: event.Lock, |
| 143 | arena: *std.mem.Allocator, | ||
| 64 | 144 | ||
| 65 | fn a(self: *ObjectFile) *std.mem.Allocator { | 145 | fn gpa(self: *ObjectFile) *std.mem.Allocator { |
| 66 | return self.comp.a(); | 146 | return self.comp.gpa(); |
| 67 | } | 147 | } |
| 68 | }; | 148 | }; |
| 69 | 149 | ||
| 70 | pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void { | 150 | pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void { |
| 71 | // TODO audit more of codegen.cpp:fn_llvm_value and port more logic | 151 | // TODO audit more of codegen.cpp:fn_llvm_value and port more logic |
| 72 | const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile); | 152 | const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context); |
| 73 | const llvm_fn = llvm.AddFunction( | 153 | const llvm_fn = llvm.AddFunction( |
| 74 | ofile.module, | 154 | ofile.module, |
| 75 | fn_val.symbol_name.ptr(), | 155 | fn_val.symbol_name.ptr(), |
| ... | @@ -87,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) | ... | @@ -87,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) |
| 87 | // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack); | 167 | // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack); |
| 88 | //} | 168 | //} |
| 89 | 169 | ||
| 90 | const fn_type = fn_val.base.typeof.cast(Type.Fn).?; | 170 | const fn_type = fn_val.base.typ.cast(Type.Fn).?; |
| 91 | 171 | ||
| 92 | try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); | 172 | try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); |
| 93 | //add_uwtable_attr(g, fn_table_entry->llvm_value); | 173 | //add_uwtable_attr(g, fn_table_entry->llvm_value); |
src-self-hosted/compilation.zig+719-229| ... | @@ -21,24 +21,47 @@ const Scope = @import("scope.zig").Scope; | ... | @@ -21,24 +21,47 @@ const Scope = @import("scope.zig").Scope; |
| 21 | const Decl = @import("decl.zig").Decl; | 21 | const Decl = @import("decl.zig").Decl; |
| 22 | const ir = @import("ir.zig"); | 22 | const ir = @import("ir.zig"); |
| 23 | const Visib = @import("visib.zig").Visib; | 23 | const Visib = @import("visib.zig").Visib; |
| 24 | const ParsedFile = @import("parsed_file.zig").ParsedFile; | ||
| 25 | const Value = @import("value.zig").Value; | 24 | const Value = @import("value.zig").Value; |
| 26 | const Type = Value.Type; | 25 | const Type = Value.Type; |
| 27 | const Span = errmsg.Span; | 26 | const Span = errmsg.Span; |
| 27 | const Msg = errmsg.Msg; | ||
| 28 | const codegen = @import("codegen.zig"); | 28 | const codegen = @import("codegen.zig"); |
| 29 | const Package = @import("package.zig").Package; | ||
| 30 | const link = @import("link.zig").link; | ||
| 31 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | ||
| 32 | const CInt = @import("c_int.zig").CInt; | ||
| 29 | 33 | ||
| 30 | /// Data that is local to the event loop. | 34 | /// Data that is local to the event loop. |
| 31 | pub const EventLoopLocal = struct { | 35 | pub const EventLoopLocal = struct { |
| 32 | loop: *event.Loop, | 36 | loop: *event.Loop, |
| 33 | llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), | 37 | llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), |
| 34 | 38 | ||
| 35 | fn init(loop: *event.Loop) EventLoopLocal { | 39 | /// TODO pool these so that it doesn't have to lock |
| 40 | prng: event.Locked(std.rand.DefaultPrng), | ||
| 41 | |||
| 42 | native_libc: event.Future(LibCInstallation), | ||
| 43 | |||
| 44 | var lazy_init_targets = std.lazyInit(void); | ||
| 45 | |||
| 46 | fn init(loop: *event.Loop) !EventLoopLocal { | ||
| 47 | lazy_init_targets.get() orelse { | ||
| 48 | Target.initializeAll(); | ||
| 49 | lazy_init_targets.resolve(); | ||
| 50 | }; | ||
| 51 | |||
| 52 | var seed_bytes: [@sizeOf(u64)]u8 = undefined; | ||
| 53 | try std.os.getRandomBytes(seed_bytes[0..]); | ||
| 54 | const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big); | ||
| 55 | |||
| 36 | return EventLoopLocal{ | 56 | return EventLoopLocal{ |
| 37 | .loop = loop, | 57 | .loop = loop, |
| 38 | .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), | 58 | .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), |
| 59 | .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), | ||
| 60 | .native_libc = event.Future(LibCInstallation).init(loop), | ||
| 39 | }; | 61 | }; |
| 40 | } | 62 | } |
| 41 | 63 | ||
| 64 | /// Must be called only after EventLoop.run completes. | ||
| 42 | fn deinit(self: *EventLoopLocal) void { | 65 | fn deinit(self: *EventLoopLocal) void { |
| 43 | while (self.llvm_handle_pool.pop()) |node| { | 66 | while (self.llvm_handle_pool.pop()) |node| { |
| 44 | c.LLVMContextDispose(node.data); | 67 | c.LLVMContextDispose(node.data); |
| ... | @@ -62,6 +85,13 @@ pub const EventLoopLocal = struct { | ... | @@ -62,6 +85,13 @@ pub const EventLoopLocal = struct { |
| 62 | 85 | ||
| 63 | return LlvmHandle{ .node = node }; | 86 | return LlvmHandle{ .node = node }; |
| 64 | } | 87 | } |
| 88 | |||
| 89 | pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation { | ||
| 90 | if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr; | ||
| 91 | try await (async self.native_libc.data.findNative(self.loop) catch unreachable); | ||
| 92 | self.native_libc.resolve(); | ||
| 93 | return &self.native_libc.data; | ||
| 94 | } | ||
| 65 | }; | 95 | }; |
| 66 | 96 | ||
| 67 | pub const LlvmHandle = struct { | 97 | pub const LlvmHandle = struct { |
| ... | @@ -76,23 +106,22 @@ pub const Compilation = struct { | ... | @@ -76,23 +106,22 @@ pub const Compilation = struct { |
| 76 | event_loop_local: *EventLoopLocal, | 106 | event_loop_local: *EventLoopLocal, |
| 77 | loop: *event.Loop, | 107 | loop: *event.Loop, |
| 78 | name: Buffer, | 108 | name: Buffer, |
| 109 | llvm_triple: Buffer, | ||
| 79 | root_src_path: ?[]const u8, | 110 | root_src_path: ?[]const u8, |
| 80 | target: Target, | 111 | target: Target, |
| 112 | llvm_target: llvm.TargetRef, | ||
| 81 | build_mode: builtin.Mode, | 113 | build_mode: builtin.Mode, |
| 82 | zig_lib_dir: []const u8, | 114 | zig_lib_dir: []const u8, |
| 115 | zig_std_dir: []const u8, | ||
| 116 | |||
| 117 | /// lazily created when we need it | ||
| 118 | tmp_dir: event.Future(BuildError![]u8), | ||
| 83 | 119 | ||
| 84 | version_major: u32, | 120 | version_major: u32, |
| 85 | version_minor: u32, | 121 | version_minor: u32, |
| 86 | version_patch: u32, | 122 | version_patch: u32, |
| 87 | 123 | ||
| 88 | linker_script: ?[]const u8, | 124 | linker_script: ?[]const u8, |
| 89 | cache_dir: []const u8, | ||
| 90 | libc_lib_dir: ?[]const u8, | ||
| 91 | libc_static_lib_dir: ?[]const u8, | ||
| 92 | libc_include_dir: ?[]const u8, | ||
| 93 | msvc_lib_dir: ?[]const u8, | ||
| 94 | kernel32_lib_dir: ?[]const u8, | ||
| 95 | dynamic_linker: ?[]const u8, | ||
| 96 | out_h_path: ?[]const u8, | 125 | out_h_path: ?[]const u8, |
| 97 | 126 | ||
| 98 | is_test: bool, | 127 | is_test: bool, |
| ... | @@ -106,8 +135,16 @@ pub const Compilation = struct { | ... | @@ -106,8 +135,16 @@ pub const Compilation = struct { |
| 106 | lib_dirs: []const []const u8, | 135 | lib_dirs: []const []const u8, |
| 107 | rpath_list: []const []const u8, | 136 | rpath_list: []const []const u8, |
| 108 | assembly_files: []const []const u8, | 137 | assembly_files: []const []const u8, |
| 138 | |||
| 139 | /// paths that are explicitly provided by the user to link against | ||
| 109 | link_objects: []const []const u8, | 140 | link_objects: []const []const u8, |
| 110 | 141 | ||
| 142 | /// functions that have their own objects that we need to link | ||
| 143 | /// it uses an optional pointer so that tombstone removals are possible | ||
| 144 | fn_link_set: event.Locked(FnLinkSet), | ||
| 145 | |||
| 146 | pub const FnLinkSet = std.LinkedList(?*Value.Fn); | ||
| 147 | |||
| 111 | windows_subsystem_windows: bool, | 148 | windows_subsystem_windows: bool, |
| 112 | windows_subsystem_console: bool, | 149 | windows_subsystem_console: bool, |
| 113 | 150 | ||
| ... | @@ -141,7 +178,7 @@ pub const Compilation = struct { | ... | @@ -141,7 +178,7 @@ pub const Compilation = struct { |
| 141 | 178 | ||
| 142 | /// Before code generation starts, must wait on this group to make sure | 179 | /// Before code generation starts, must wait on this group to make sure |
| 143 | /// the build is complete. | 180 | /// the build is complete. |
| 144 | build_group: event.Group(BuildError!void), | 181 | prelink_group: event.Group(BuildError!void), |
| 145 | 182 | ||
| 146 | compile_errors: event.Locked(CompileErrList), | 183 | compile_errors: event.Locked(CompileErrList), |
| 147 | 184 | ||
| ... | @@ -149,13 +186,49 @@ pub const Compilation = struct { | ... | @@ -149,13 +186,49 @@ pub const Compilation = struct { |
| 149 | void_type: *Type.Void, | 186 | void_type: *Type.Void, |
| 150 | bool_type: *Type.Bool, | 187 | bool_type: *Type.Bool, |
| 151 | noreturn_type: *Type.NoReturn, | 188 | noreturn_type: *Type.NoReturn, |
| 189 | comptime_int_type: *Type.ComptimeInt, | ||
| 190 | u8_type: *Type.Int, | ||
| 152 | 191 | ||
| 153 | void_value: *Value.Void, | 192 | void_value: *Value.Void, |
| 154 | true_value: *Value.Bool, | 193 | true_value: *Value.Bool, |
| 155 | false_value: *Value.Bool, | 194 | false_value: *Value.Bool, |
| 156 | noreturn_value: *Value.NoReturn, | 195 | noreturn_value: *Value.NoReturn, |
| 157 | 196 | ||
| 158 | const CompileErrList = std.ArrayList(*errmsg.Msg); | 197 | target_machine: llvm.TargetMachineRef, |
| 198 | target_data_ref: llvm.TargetDataRef, | ||
| 199 | target_layout_str: [*]u8, | ||
| 200 | target_ptr_bits: u32, | ||
| 201 | |||
| 202 | /// for allocating things which have the same lifetime as this Compilation | ||
| 203 | arena_allocator: std.heap.ArenaAllocator, | ||
| 204 | |||
| 205 | root_package: *Package, | ||
| 206 | std_package: *Package, | ||
| 207 | |||
| 208 | override_libc: ?*LibCInstallation, | ||
| 209 | |||
| 210 | /// need to wait on this group before deinitializing | ||
| 211 | deinit_group: event.Group(void), | ||
| 212 | |||
| 213 | destroy_handle: promise, | ||
| 214 | |||
| 215 | have_err_ret_tracing: bool, | ||
| 216 | |||
| 217 | /// not locked because it is read-only | ||
| 218 | primitive_type_table: TypeTable, | ||
| 219 | |||
| 220 | int_type_table: event.Locked(IntTypeTable), | ||
| 221 | array_type_table: event.Locked(ArrayTypeTable), | ||
| 222 | ptr_type_table: event.Locked(PtrTypeTable), | ||
| 223 | |||
| 224 | c_int_types: [CInt.list.len]*Type.Int, | ||
| 225 | |||
| 226 | const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql); | ||
| 227 | const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql); | ||
| 228 | const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql); | ||
| 229 | const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8); | ||
| 230 | |||
| 231 | const CompileErrList = std.ArrayList(*Msg); | ||
| 159 | 232 | ||
| 160 | // TODO handle some of these earlier and report them in a way other than error codes | 233 | // TODO handle some of these earlier and report them in a way other than error codes |
| 161 | pub const BuildError = error{ | 234 | pub const BuildError = error{ |
| ... | @@ -195,12 +268,21 @@ pub const Compilation = struct { | ... | @@ -195,12 +268,21 @@ pub const Compilation = struct { |
| 195 | BufferTooSmall, | 268 | BufferTooSmall, |
| 196 | Unimplemented, // TODO remove this one | 269 | Unimplemented, // TODO remove this one |
| 197 | SemanticAnalysisFailed, // TODO remove this one | 270 | SemanticAnalysisFailed, // TODO remove this one |
| 271 | ReadOnlyFileSystem, | ||
| 272 | LinkQuotaExceeded, | ||
| 273 | EnvironmentVariableNotFound, | ||
| 274 | AppDataDirUnavailable, | ||
| 275 | LinkFailed, | ||
| 276 | LibCRequiredButNotProvidedOrFound, | ||
| 277 | LibCMissingDynamicLinker, | ||
| 278 | InvalidDarwinVersionString, | ||
| 279 | UnsupportedLinkArchitecture, | ||
| 198 | }; | 280 | }; |
| 199 | 281 | ||
| 200 | pub const Event = union(enum) { | 282 | pub const Event = union(enum) { |
| 201 | Ok, | 283 | Ok, |
| 202 | Error: BuildError, | 284 | Error: BuildError, |
| 203 | Fail: []*errmsg.Msg, | 285 | Fail: []*Msg, |
| 204 | }; | 286 | }; |
| 205 | 287 | ||
| 206 | pub const DarwinVersionMin = union(enum) { | 288 | pub const DarwinVersionMin = union(enum) { |
| ... | @@ -234,31 +316,29 @@ pub const Compilation = struct { | ... | @@ -234,31 +316,29 @@ pub const Compilation = struct { |
| 234 | event_loop_local: *EventLoopLocal, | 316 | event_loop_local: *EventLoopLocal, |
| 235 | name: []const u8, | 317 | name: []const u8, |
| 236 | root_src_path: ?[]const u8, | 318 | root_src_path: ?[]const u8, |
| 237 | target: *const Target, | 319 | target: Target, |
| 238 | kind: Kind, | 320 | kind: Kind, |
| 239 | build_mode: builtin.Mode, | 321 | build_mode: builtin.Mode, |
| 322 | is_static: bool, | ||
| 240 | zig_lib_dir: []const u8, | 323 | zig_lib_dir: []const u8, |
| 241 | cache_dir: []const u8, | ||
| 242 | ) !*Compilation { | 324 | ) !*Compilation { |
| 243 | const loop = event_loop_local.loop; | 325 | const loop = event_loop_local.loop; |
| 244 | 326 | const comp = try event_loop_local.loop.allocator.create(Compilation{ | |
| 245 | var name_buffer = try Buffer.init(loop.allocator, name); | ||
| 246 | errdefer name_buffer.deinit(); | ||
| 247 | |||
| 248 | const events = try event.Channel(Event).create(loop, 0); | ||
| 249 | errdefer events.destroy(); | ||
| 250 | |||
| 251 | const comp = try loop.allocator.create(Compilation{ | ||
| 252 | .loop = loop, | 327 | .loop = loop, |
| 328 | .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator), | ||
| 253 | .event_loop_local = event_loop_local, | 329 | .event_loop_local = event_loop_local, |
| 254 | .events = events, | 330 | .events = undefined, |
| 255 | .name = name_buffer, | ||
| 256 | .root_src_path = root_src_path, | 331 | .root_src_path = root_src_path, |
| 257 | .target = target.*, | 332 | .target = target, |
| 333 | .llvm_target = undefined, | ||
| 258 | .kind = kind, | 334 | .kind = kind, |
| 259 | .build_mode = build_mode, | 335 | .build_mode = build_mode, |
| 260 | .zig_lib_dir = zig_lib_dir, | 336 | .zig_lib_dir = zig_lib_dir, |
| 261 | .cache_dir = cache_dir, | 337 | .zig_std_dir = undefined, |
| 338 | .tmp_dir = event.Future(BuildError![]u8).init(loop), | ||
| 339 | |||
| 340 | .name = undefined, | ||
| 341 | .llvm_triple = undefined, | ||
| 262 | 342 | ||
| 263 | .version_major = 0, | 343 | .version_major = 0, |
| 264 | .version_minor = 0, | 344 | .version_minor = 0, |
| ... | @@ -273,17 +353,11 @@ pub const Compilation = struct { | ... | @@ -273,17 +353,11 @@ pub const Compilation = struct { |
| 273 | .verbose_link = false, | 353 | .verbose_link = false, |
| 274 | 354 | ||
| 275 | .linker_script = null, | 355 | .linker_script = null, |
| 276 | .libc_lib_dir = null, | ||
| 277 | .libc_static_lib_dir = null, | ||
| 278 | .libc_include_dir = null, | ||
| 279 | .msvc_lib_dir = null, | ||
| 280 | .kernel32_lib_dir = null, | ||
| 281 | .dynamic_linker = null, | ||
| 282 | .out_h_path = null, | 356 | .out_h_path = null, |
| 283 | .is_test = false, | 357 | .is_test = false, |
| 284 | .each_lib_rpath = false, | 358 | .each_lib_rpath = false, |
| 285 | .strip = false, | 359 | .strip = false, |
| 286 | .is_static = false, | 360 | .is_static = is_static, |
| 287 | .linker_rdynamic = false, | 361 | .linker_rdynamic = false, |
| 288 | .clang_argv = [][]const u8{}, | 362 | .clang_argv = [][]const u8{}, |
| 289 | .llvm_argv = [][]const u8{}, | 363 | .llvm_argv = [][]const u8{}, |
| ... | @@ -291,9 +365,10 @@ pub const Compilation = struct { | ... | @@ -291,9 +365,10 @@ pub const Compilation = struct { |
| 291 | .rpath_list = [][]const u8{}, | 365 | .rpath_list = [][]const u8{}, |
| 292 | .assembly_files = [][]const u8{}, | 366 | .assembly_files = [][]const u8{}, |
| 293 | .link_objects = [][]const u8{}, | 367 | .link_objects = [][]const u8{}, |
| 368 | .fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()), | ||
| 294 | .windows_subsystem_windows = false, | 369 | .windows_subsystem_windows = false, |
| 295 | .windows_subsystem_console = false, | 370 | .windows_subsystem_console = false, |
| 296 | .link_libs_list = ArrayList(*LinkLib).init(loop.allocator), | 371 | .link_libs_list = undefined, |
| 297 | .libc_link_lib = null, | 372 | .libc_link_lib = null, |
| 298 | .err_color = errmsg.Color.Auto, | 373 | .err_color = errmsg.Color.Auto, |
| 299 | .darwin_frameworks = [][]const u8{}, | 374 | .darwin_frameworks = [][]const u8{}, |
| ... | @@ -303,8 +378,13 @@ pub const Compilation = struct { | ... | @@ -303,8 +378,13 @@ pub const Compilation = struct { |
| 303 | .emit_file_type = Emit.Binary, | 378 | .emit_file_type = Emit.Binary, |
| 304 | .link_out_file = null, | 379 | .link_out_file = null, |
| 305 | .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), | 380 | .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), |
| 306 | .build_group = event.Group(BuildError!void).init(loop), | 381 | .prelink_group = event.Group(BuildError!void).init(loop), |
| 382 | .deinit_group = event.Group(void).init(loop), | ||
| 307 | .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), | 383 | .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), |
| 384 | .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)), | ||
| 385 | .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)), | ||
| 386 | .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)), | ||
| 387 | .c_int_types = undefined, | ||
| 308 | 388 | ||
| 309 | .meta_type = undefined, | 389 | .meta_type = undefined, |
| 310 | .void_type = undefined, | 390 | .void_type = undefined, |
| ... | @@ -314,120 +394,307 @@ pub const Compilation = struct { | ... | @@ -314,120 +394,307 @@ pub const Compilation = struct { |
| 314 | .false_value = undefined, | 394 | .false_value = undefined, |
| 315 | .noreturn_type = undefined, | 395 | .noreturn_type = undefined, |
| 316 | .noreturn_value = undefined, | 396 | .noreturn_value = undefined, |
| 397 | .comptime_int_type = undefined, | ||
| 398 | .u8_type = undefined, | ||
| 399 | |||
| 400 | .target_machine = undefined, | ||
| 401 | .target_data_ref = undefined, | ||
| 402 | .target_layout_str = undefined, | ||
| 403 | .target_ptr_bits = target.getArchPtrBitWidth(), | ||
| 404 | |||
| 405 | .root_package = undefined, | ||
| 406 | .std_package = undefined, | ||
| 407 | |||
| 408 | .override_libc = null, | ||
| 409 | .destroy_handle = undefined, | ||
| 410 | .have_err_ret_tracing = false, | ||
| 411 | .primitive_type_table = undefined, | ||
| 317 | }); | 412 | }); |
| 413 | errdefer { | ||
| 414 | comp.int_type_table.private_data.deinit(); | ||
| 415 | comp.array_type_table.private_data.deinit(); | ||
| 416 | comp.ptr_type_table.private_data.deinit(); | ||
| 417 | comp.arena_allocator.deinit(); | ||
| 418 | comp.loop.allocator.destroy(comp); | ||
| 419 | } | ||
| 420 | |||
| 421 | comp.name = try Buffer.init(comp.arena(), name); | ||
| 422 | comp.llvm_triple = try target.getTriple(comp.arena()); | ||
| 423 | comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple); | ||
| 424 | comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena()); | ||
| 425 | comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std"); | ||
| 426 | comp.primitive_type_table = TypeTable.init(comp.arena()); | ||
| 427 | |||
| 428 | const opt_level = switch (build_mode) { | ||
| 429 | builtin.Mode.Debug => llvm.CodeGenLevelNone, | ||
| 430 | else => llvm.CodeGenLevelAggressive, | ||
| 431 | }; | ||
| 432 | |||
| 433 | const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC; | ||
| 434 | |||
| 435 | // LLVM creates invalid binaries on Windows sometimes. | ||
| 436 | // See https://github.com/ziglang/zig/issues/508 | ||
| 437 | // As a workaround we do not use target native features on Windows. | ||
| 438 | var target_specific_cpu_args: ?[*]u8 = null; | ||
| 439 | var target_specific_cpu_features: ?[*]u8 = null; | ||
| 440 | errdefer llvm.DisposeMessage(target_specific_cpu_args); | ||
| 441 | errdefer llvm.DisposeMessage(target_specific_cpu_features); | ||
| 442 | if (target == Target.Native and !target.isWindows()) { | ||
| 443 | target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory; | ||
| 444 | target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory; | ||
| 445 | } | ||
| 446 | |||
| 447 | comp.target_machine = llvm.CreateTargetMachine( | ||
| 448 | comp.llvm_target, | ||
| 449 | comp.llvm_triple.ptr(), | ||
| 450 | target_specific_cpu_args orelse c"", | ||
| 451 | target_specific_cpu_features orelse c"", | ||
| 452 | opt_level, | ||
| 453 | reloc_mode, | ||
| 454 | llvm.CodeModelDefault, | ||
| 455 | ) orelse return error.OutOfMemory; | ||
| 456 | errdefer llvm.DisposeTargetMachine(comp.target_machine); | ||
| 457 | |||
| 458 | comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory; | ||
| 459 | errdefer llvm.DisposeTargetData(comp.target_data_ref); | ||
| 460 | |||
| 461 | comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory; | ||
| 462 | errdefer llvm.DisposeMessage(comp.target_layout_str); | ||
| 463 | |||
| 464 | comp.events = try event.Channel(Event).create(comp.loop, 0); | ||
| 465 | errdefer comp.events.destroy(); | ||
| 466 | |||
| 467 | if (root_src_path) |root_src| { | ||
| 468 | const dirname = std.os.path.dirname(root_src) orelse "."; | ||
| 469 | const basename = std.os.path.basename(root_src); | ||
| 470 | |||
| 471 | comp.root_package = try Package.create(comp.arena(), dirname, basename); | ||
| 472 | comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "index.zig"); | ||
| 473 | try comp.root_package.add("std", comp.std_package); | ||
| 474 | } else { | ||
| 475 | comp.root_package = try Package.create(comp.arena(), ".", ""); | ||
| 476 | } | ||
| 477 | |||
| 318 | try comp.initTypes(); | 478 | try comp.initTypes(); |
| 479 | |||
| 480 | comp.destroy_handle = try async<loop.allocator> comp.internalDeinit(); | ||
| 481 | |||
| 319 | return comp; | 482 | return comp; |
| 320 | } | 483 | } |
| 321 | 484 | ||
| 485 | /// it does ref the result because it could be an arbitrary integer size | ||
| 486 | pub async fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type { | ||
| 487 | if (name.len >= 2) { | ||
| 488 | switch (name[0]) { | ||
| 489 | 'i', 'u' => blk: { | ||
| 490 | for (name[1..]) |byte| | ||
| 491 | switch (byte) { | ||
| 492 | '0'...'9' => {}, | ||
| 493 | else => break :blk, | ||
| 494 | }; | ||
| 495 | const is_signed = name[0] == 'i'; | ||
| 496 | const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) { | ||
| 497 | error.Overflow => return error.Overflow, | ||
| 498 | error.InvalidCharacter => unreachable, // we just checked the characters above | ||
| 499 | }; | ||
| 500 | const int_type = try await (async Type.Int.get(comp, Type.Int.Key{ | ||
| 501 | .bit_count = bit_count, | ||
| 502 | .is_signed = is_signed, | ||
| 503 | }) catch unreachable); | ||
| 504 | errdefer int_type.base.base.deref(); | ||
| 505 | return &int_type.base; | ||
| 506 | }, | ||
| 507 | else => {}, | ||
| 508 | } | ||
| 509 | } | ||
| 510 | |||
| 511 | if (comp.primitive_type_table.get(name)) |entry| { | ||
| 512 | entry.value.base.ref(); | ||
| 513 | return entry.value; | ||
| 514 | } | ||
| 515 | |||
| 516 | return null; | ||
| 517 | } | ||
| 518 | |||
| 322 | fn initTypes(comp: *Compilation) !void { | 519 | fn initTypes(comp: *Compilation) !void { |
| 323 | comp.meta_type = try comp.a().create(Type.MetaType{ | 520 | comp.meta_type = try comp.arena().create(Type.MetaType{ |
| 324 | .base = Type{ | 521 | .base = Type{ |
| 522 | .name = "type", | ||
| 325 | .base = Value{ | 523 | .base = Value{ |
| 326 | .id = Value.Id.Type, | 524 | .id = Value.Id.Type, |
| 327 | .typeof = undefined, | 525 | .typ = undefined, |
| 328 | .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice | 526 | .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice |
| 329 | }, | 527 | }, |
| 330 | .id = builtin.TypeId.Type, | 528 | .id = builtin.TypeId.Type, |
| 529 | .abi_alignment = Type.AbiAlignment.init(comp.loop), | ||
| 331 | }, | 530 | }, |
| 332 | .value = undefined, | 531 | .value = undefined, |
| 333 | }); | 532 | }); |
| 334 | comp.meta_type.value = &comp.meta_type.base; | 533 | comp.meta_type.value = &comp.meta_type.base; |
| 335 | comp.meta_type.base.base.typeof = &comp.meta_type.base; | 534 | comp.meta_type.base.base.typ = &comp.meta_type.base; |
| 336 | errdefer comp.a().destroy(comp.meta_type); | 535 | assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null); |
| 337 | 536 | ||
| 338 | comp.void_type = try comp.a().create(Type.Void{ | 537 | comp.void_type = try comp.arena().create(Type.Void{ |
| 339 | .base = Type{ | 538 | .base = Type{ |
| 539 | .name = "void", | ||
| 340 | .base = Value{ | 540 | .base = Value{ |
| 341 | .id = Value.Id.Type, | 541 | .id = Value.Id.Type, |
| 342 | .typeof = &Type.MetaType.get(comp).base, | 542 | .typ = &Type.MetaType.get(comp).base, |
| 343 | .ref_count = std.atomic.Int(usize).init(1), | 543 | .ref_count = std.atomic.Int(usize).init(1), |
| 344 | }, | 544 | }, |
| 345 | .id = builtin.TypeId.Void, | 545 | .id = builtin.TypeId.Void, |
| 546 | .abi_alignment = Type.AbiAlignment.init(comp.loop), | ||
| 346 | }, | 547 | }, |
| 347 | }); | 548 | }); |
| 348 | errdefer comp.a().destroy(comp.void_type); | 549 | assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null); |
| 349 | 550 | ||
| 350 | comp.noreturn_type = try comp.a().create(Type.NoReturn{ | 551 | comp.noreturn_type = try comp.arena().create(Type.NoReturn{ |
| 351 | .base = Type{ | 552 | .base = Type{ |
| 553 | .name = "noreturn", | ||
| 352 | .base = Value{ | 554 | .base = Value{ |
| 353 | .id = Value.Id.Type, | 555 | .id = Value.Id.Type, |
| 354 | .typeof = &Type.MetaType.get(comp).base, | 556 | .typ = &Type.MetaType.get(comp).base, |
| 355 | .ref_count = std.atomic.Int(usize).init(1), | 557 | .ref_count = std.atomic.Int(usize).init(1), |
| 356 | }, | 558 | }, |
| 357 | .id = builtin.TypeId.NoReturn, | 559 | .id = builtin.TypeId.NoReturn, |
| 560 | .abi_alignment = Type.AbiAlignment.init(comp.loop), | ||
| 561 | }, | ||
| 562 | }); | ||
| 563 | assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null); | ||
| 564 | |||
| 565 | comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt{ | ||
| 566 | .base = Type{ | ||
| 567 | .name = "comptime_int", | ||
| 568 | .base = Value{ | ||
| 569 | .id = Value.Id.Type, | ||
| 570 | .typ = &Type.MetaType.get(comp).base, | ||
| 571 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 572 | }, | ||
| 573 | .id = builtin.TypeId.ComptimeInt, | ||
| 574 | .abi_alignment = Type.AbiAlignment.init(comp.loop), | ||
| 358 | }, | 575 | }, |
| 359 | }); | 576 | }); |
| 360 | errdefer comp.a().destroy(comp.noreturn_type); | 577 | assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null); |
| 361 | 578 | ||
| 362 | comp.bool_type = try comp.a().create(Type.Bool{ | 579 | comp.bool_type = try comp.arena().create(Type.Bool{ |
| 363 | .base = Type{ | 580 | .base = Type{ |
| 581 | .name = "bool", | ||
| 364 | .base = Value{ | 582 | .base = Value{ |
| 365 | .id = Value.Id.Type, | 583 | .id = Value.Id.Type, |
| 366 | .typeof = &Type.MetaType.get(comp).base, | 584 | .typ = &Type.MetaType.get(comp).base, |
| 367 | .ref_count = std.atomic.Int(usize).init(1), | 585 | .ref_count = std.atomic.Int(usize).init(1), |
| 368 | }, | 586 | }, |
| 369 | .id = builtin.TypeId.Bool, | 587 | .id = builtin.TypeId.Bool, |
| 588 | .abi_alignment = Type.AbiAlignment.init(comp.loop), | ||
| 370 | }, | 589 | }, |
| 371 | }); | 590 | }); |
| 372 | errdefer comp.a().destroy(comp.bool_type); | 591 | assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null); |
| 373 | 592 | ||
| 374 | comp.void_value = try comp.a().create(Value.Void{ | 593 | comp.void_value = try comp.arena().create(Value.Void{ |
| 375 | .base = Value{ | 594 | .base = Value{ |
| 376 | .id = Value.Id.Void, | 595 | .id = Value.Id.Void, |
| 377 | .typeof = &Type.Void.get(comp).base, | 596 | .typ = &Type.Void.get(comp).base, |
| 378 | .ref_count = std.atomic.Int(usize).init(1), | 597 | .ref_count = std.atomic.Int(usize).init(1), |
| 379 | }, | 598 | }, |
| 380 | }); | 599 | }); |
| 381 | errdefer comp.a().destroy(comp.void_value); | ||
| 382 | 600 | ||
| 383 | comp.true_value = try comp.a().create(Value.Bool{ | 601 | comp.true_value = try comp.arena().create(Value.Bool{ |
| 384 | .base = Value{ | 602 | .base = Value{ |
| 385 | .id = Value.Id.Bool, | 603 | .id = Value.Id.Bool, |
| 386 | .typeof = &Type.Bool.get(comp).base, | 604 | .typ = &Type.Bool.get(comp).base, |
| 387 | .ref_count = std.atomic.Int(usize).init(1), | 605 | .ref_count = std.atomic.Int(usize).init(1), |
| 388 | }, | 606 | }, |
| 389 | .x = true, | 607 | .x = true, |
| 390 | }); | 608 | }); |
| 391 | errdefer comp.a().destroy(comp.true_value); | ||
| 392 | 609 | ||
| 393 | comp.false_value = try comp.a().create(Value.Bool{ | 610 | comp.false_value = try comp.arena().create(Value.Bool{ |
| 394 | .base = Value{ | 611 | .base = Value{ |
| 395 | .id = Value.Id.Bool, | 612 | .id = Value.Id.Bool, |
| 396 | .typeof = &Type.Bool.get(comp).base, | 613 | .typ = &Type.Bool.get(comp).base, |
| 397 | .ref_count = std.atomic.Int(usize).init(1), | 614 | .ref_count = std.atomic.Int(usize).init(1), |
| 398 | }, | 615 | }, |
| 399 | .x = false, | 616 | .x = false, |
| 400 | }); | 617 | }); |
| 401 | errdefer comp.a().destroy(comp.false_value); | ||
| 402 | 618 | ||
| 403 | comp.noreturn_value = try comp.a().create(Value.NoReturn{ | 619 | comp.noreturn_value = try comp.arena().create(Value.NoReturn{ |
| 404 | .base = Value{ | 620 | .base = Value{ |
| 405 | .id = Value.Id.NoReturn, | 621 | .id = Value.Id.NoReturn, |
| 406 | .typeof = &Type.NoReturn.get(comp).base, | 622 | .typ = &Type.NoReturn.get(comp).base, |
| 407 | .ref_count = std.atomic.Int(usize).init(1), | 623 | .ref_count = std.atomic.Int(usize).init(1), |
| 408 | }, | 624 | }, |
| 409 | }); | 625 | }); |
| 410 | errdefer comp.a().destroy(comp.noreturn_value); | 626 | |
| 627 | for (CInt.list) |cint, i| { | ||
| 628 | const c_int_type = try comp.arena().create(Type.Int{ | ||
| 629 | .base = Type{ | ||
| 630 | .name = cint.zig_name, | ||
| 631 | .base = Value{ | ||
| 632 | .id = Value.Id.Type, | ||
| 633 | .typ = &Type.MetaType.get(comp).base, | ||
| 634 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 635 | }, | ||
| 636 | .id = builtin.TypeId.Int, | ||
| 637 | .abi_alignment = Type.AbiAlignment.init(comp.loop), | ||
| 638 | }, | ||
| 639 | .key = Type.Int.Key{ | ||
| 640 | .is_signed = cint.is_signed, | ||
| 641 | .bit_count = comp.target.cIntTypeSizeInBits(cint.id), | ||
| 642 | }, | ||
| 643 | .garbage_node = undefined, | ||
| 644 | }); | ||
| 645 | comp.c_int_types[i] = c_int_type; | ||
| 646 | assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null); | ||
| 647 | } | ||
| 648 | comp.u8_type = try comp.arena().create(Type.Int{ | ||
| 649 | .base = Type{ | ||
| 650 | .name = "u8", | ||
| 651 | .base = Value{ | ||
| 652 | .id = Value.Id.Type, | ||
| 653 | .typ = &Type.MetaType.get(comp).base, | ||
| 654 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 655 | }, | ||
| 656 | .id = builtin.TypeId.Int, | ||
| 657 | .abi_alignment = Type.AbiAlignment.init(comp.loop), | ||
| 658 | }, | ||
| 659 | .key = Type.Int.Key{ | ||
| 660 | .is_signed = false, | ||
| 661 | .bit_count = 8, | ||
| 662 | }, | ||
| 663 | .garbage_node = undefined, | ||
| 664 | }); | ||
| 665 | assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null); | ||
| 411 | } | 666 | } |
| 412 | 667 | ||
| 413 | pub fn destroy(self: *Compilation) void { | 668 | /// This function can safely use async/await, because it manages Compilation's lifetime, |
| 414 | self.noreturn_value.base.deref(self); | 669 | /// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes. |
| 415 | self.void_value.base.deref(self); | 670 | async fn internalDeinit(self: *Compilation) void { |
| 416 | self.false_value.base.deref(self); | 671 | suspend; |
| 417 | self.true_value.base.deref(self); | 672 | |
| 418 | self.noreturn_type.base.base.deref(self); | 673 | await (async self.deinit_group.wait() catch unreachable); |
| 419 | self.void_type.base.base.deref(self); | 674 | if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { |
| 420 | self.meta_type.base.base.deref(self); | 675 | // TODO evented I/O? |
| 676 | os.deleteTree(self.arena(), tmp_dir) catch {}; | ||
| 677 | } else |_| {}; | ||
| 421 | 678 | ||
| 422 | self.events.destroy(); | 679 | self.events.destroy(); |
| 423 | self.name.deinit(); | ||
| 424 | 680 | ||
| 425 | self.a().destroy(self); | 681 | llvm.DisposeMessage(self.target_layout_str); |
| 682 | llvm.DisposeTargetData(self.target_data_ref); | ||
| 683 | llvm.DisposeTargetMachine(self.target_machine); | ||
| 684 | |||
| 685 | self.primitive_type_table.deinit(); | ||
| 686 | |||
| 687 | self.arena_allocator.deinit(); | ||
| 688 | self.gpa().destroy(self); | ||
| 689 | } | ||
| 690 | |||
| 691 | pub fn destroy(self: *Compilation) void { | ||
| 692 | resume self.destroy_handle; | ||
| 426 | } | 693 | } |
| 427 | 694 | ||
| 428 | pub fn build(self: *Compilation) !void { | 695 | pub fn build(self: *Compilation) !void { |
| 429 | if (self.llvm_argv.len != 0) { | 696 | if (self.llvm_argv.len != 0) { |
| 430 | var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{ | 697 | var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{ |
| 431 | [][]const u8{"zig (LLVM option parsing)"}, | 698 | [][]const u8{"zig (LLVM option parsing)"}, |
| 432 | self.llvm_argv, | 699 | self.llvm_argv, |
| 433 | }); | 700 | }); |
| ... | @@ -436,14 +703,13 @@ pub const Compilation = struct { | ... | @@ -436,14 +703,13 @@ pub const Compilation = struct { |
| 436 | c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); | 703 | c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); |
| 437 | } | 704 | } |
| 438 | 705 | ||
| 439 | _ = try async<self.a()> self.buildAsync(); | 706 | _ = try async<self.gpa()> self.buildAsync(); |
| 440 | } | 707 | } |
| 441 | 708 | ||
| 442 | async fn buildAsync(self: *Compilation) void { | 709 | async fn buildAsync(self: *Compilation) void { |
| 443 | while (true) { | 710 | while (true) { |
| 444 | // TODO directly awaiting async should guarantee memory allocation elision | 711 | // TODO directly awaiting async should guarantee memory allocation elision |
| 445 | // TODO also async before suspending should guarantee memory allocation elision | 712 | const build_result = await (async self.compileAndLink() catch unreachable); |
| 446 | const build_result = await (async self.addRootSrc() catch unreachable); | ||
| 447 | 713 | ||
| 448 | // this makes a handy error return trace and stack trace in debug mode | 714 | // this makes a handy error return trace and stack trace in debug mode |
| 449 | if (std.debug.runtime_safety) { | 715 | if (std.debug.runtime_safety) { |
| ... | @@ -464,7 +730,7 @@ pub const Compilation = struct { | ... | @@ -464,7 +730,7 @@ pub const Compilation = struct { |
| 464 | } | 730 | } |
| 465 | } else |err| { | 731 | } else |err| { |
| 466 | // if there's an error then the compile errors have dangling references | 732 | // if there's an error then the compile errors have dangling references |
| 467 | self.a().free(compile_errors); | 733 | self.gpa().free(compile_errors); |
| 468 | 734 | ||
| 469 | await (async self.events.put(Event{ .Error = err }) catch unreachable); | 735 | await (async self.events.put(Event{ .Error = err }) catch unreachable); |
| 470 | } | 736 | } |
| ... | @@ -474,111 +740,215 @@ pub const Compilation = struct { | ... | @@ -474,111 +740,215 @@ pub const Compilation = struct { |
| 474 | } | 740 | } |
| 475 | } | 741 | } |
| 476 | 742 | ||
| 477 | async fn addRootSrc(self: *Compilation) !void { | 743 | async fn compileAndLink(self: *Compilation) !void { |
| 478 | const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); | 744 | if (self.root_src_path) |root_src_path| { |
| 479 | // TODO async/await os.path.real | 745 | // TODO async/await os.path.real |
| 480 | const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| { | 746 | const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { |
| 481 | try printError("unable to get real path '{}': {}", root_src_path, err); | 747 | try printError("unable to get real path '{}': {}", root_src_path, err); |
| 482 | return err; | 748 | return err; |
| 749 | }; | ||
| 750 | const root_scope = blk: { | ||
| 751 | errdefer self.gpa().free(root_src_real_path); | ||
| 752 | |||
| 753 | // TODO async/await readFileAlloc() | ||
| 754 | const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| { | ||
| 755 | try printError("unable to open '{}': {}", root_src_real_path, err); | ||
| 756 | return err; | ||
| 757 | }; | ||
| 758 | errdefer self.gpa().free(source_code); | ||
| 759 | |||
| 760 | const tree = try self.gpa().createOne(ast.Tree); | ||
| 761 | tree.* = try std.zig.parse(self.gpa(), source_code); | ||
| 762 | errdefer { | ||
| 763 | tree.deinit(); | ||
| 764 | self.gpa().destroy(tree); | ||
| 765 | } | ||
| 766 | |||
| 767 | break :blk try Scope.Root.create(self, tree, root_src_real_path); | ||
| 768 | }; | ||
| 769 | defer root_scope.base.deref(self); | ||
| 770 | const tree = root_scope.tree; | ||
| 771 | |||
| 772 | var error_it = tree.errors.iterator(0); | ||
| 773 | while (error_it.next()) |parse_error| { | ||
| 774 | const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error); | ||
| 775 | errdefer msg.destroy(); | ||
| 776 | |||
| 777 | try await (async self.addCompileErrorAsync(msg) catch unreachable); | ||
| 778 | } | ||
| 779 | if (tree.errors.len != 0) { | ||
| 780 | return; | ||
| 781 | } | ||
| 782 | |||
| 783 | const decls = try Scope.Decls.create(self, &root_scope.base); | ||
| 784 | defer decls.base.deref(self); | ||
| 785 | |||
| 786 | var decl_group = event.Group(BuildError!void).init(self.loop); | ||
| 787 | var decl_group_consumed = false; | ||
| 788 | errdefer if (!decl_group_consumed) decl_group.cancelAll(); | ||
| 789 | |||
| 790 | var it = tree.root_node.decls.iterator(0); | ||
| 791 | while (it.next()) |decl_ptr| { | ||
| 792 | const decl = decl_ptr.*; | ||
| 793 | switch (decl.id) { | ||
| 794 | ast.Node.Id.Comptime => { | ||
| 795 | const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl); | ||
| 796 | |||
| 797 | try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node); | ||
| 798 | }, | ||
| 799 | ast.Node.Id.VarDecl => @panic("TODO"), | ||
| 800 | ast.Node.Id.FnProto => { | ||
| 801 | const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); | ||
| 802 | |||
| 803 | const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { | ||
| 804 | try self.addCompileError(root_scope, Span{ | ||
| 805 | .first = fn_proto.fn_token, | ||
| 806 | .last = fn_proto.fn_token + 1, | ||
| 807 | }, "missing function name"); | ||
| 808 | continue; | ||
| 809 | }; | ||
| 810 | |||
| 811 | const fn_decl = try self.gpa().create(Decl.Fn{ | ||
| 812 | .base = Decl{ | ||
| 813 | .id = Decl.Id.Fn, | ||
| 814 | .name = name, | ||
| 815 | .visib = parseVisibToken(tree, fn_proto.visib_token), | ||
| 816 | .resolution = event.Future(BuildError!void).init(self.loop), | ||
| 817 | .parent_scope = &decls.base, | ||
| 818 | }, | ||
| 819 | .value = Decl.Fn.Val{ .Unresolved = {} }, | ||
| 820 | .fn_proto = fn_proto, | ||
| 821 | }); | ||
| 822 | errdefer self.gpa().destroy(fn_decl); | ||
| 823 | |||
| 824 | try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base); | ||
| 825 | }, | ||
| 826 | ast.Node.Id.TestDecl => @panic("TODO"), | ||
| 827 | else => unreachable, | ||
| 828 | } | ||
| 829 | } | ||
| 830 | decl_group_consumed = true; | ||
| 831 | try await (async decl_group.wait() catch unreachable); | ||
| 832 | |||
| 833 | // Now other code can rely on the decls scope having a complete list of names. | ||
| 834 | decls.name_future.resolve(); | ||
| 835 | } | ||
| 836 | |||
| 837 | (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) { | ||
| 838 | error.SemanticAnalysisFailed => {}, | ||
| 839 | else => return err, | ||
| 483 | }; | 840 | }; |
| 484 | errdefer self.a().free(root_src_real_path); | ||
| 485 | 841 | ||
| 486 | // TODO async/await readFileAlloc() | 842 | const any_prelink_errors = blk: { |
| 487 | const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| { | 843 | const compile_errors = await (async self.compile_errors.acquire() catch unreachable); |
| 488 | try printError("unable to open '{}': {}", root_src_real_path, err); | 844 | defer compile_errors.release(); |
| 489 | return err; | 845 | |
| 846 | break :blk compile_errors.value.len != 0; | ||
| 490 | }; | 847 | }; |
| 491 | errdefer self.a().free(source_code); | ||
| 492 | 848 | ||
| 493 | const parsed_file = try self.a().create(ParsedFile{ | 849 | if (!any_prelink_errors) { |
| 494 | .tree = undefined, | 850 | try await (async link(self) catch unreachable); |
| 495 | .realpath = root_src_real_path, | 851 | } |
| 496 | }); | 852 | } |
| 497 | errdefer self.a().destroy(parsed_file); | ||
| 498 | |||
| 499 | parsed_file.tree = try std.zig.parse(self.a(), source_code); | ||
| 500 | errdefer parsed_file.tree.deinit(); | ||
| 501 | |||
| 502 | const tree = &parsed_file.tree; | ||
| 503 | |||
| 504 | // create empty struct for it | ||
| 505 | const decls = try Scope.Decls.create(self, null); | ||
| 506 | defer decls.base.deref(self); | ||
| 507 | |||
| 508 | var decl_group = event.Group(BuildError!void).init(self.loop); | ||
| 509 | errdefer decl_group.cancelAll(); | ||
| 510 | |||
| 511 | var it = tree.root_node.decls.iterator(0); | ||
| 512 | while (it.next()) |decl_ptr| { | ||
| 513 | const decl = decl_ptr.*; | ||
| 514 | switch (decl.id) { | ||
| 515 | ast.Node.Id.Comptime => @panic("TODO"), | ||
| 516 | ast.Node.Id.VarDecl => @panic("TODO"), | ||
| 517 | ast.Node.Id.FnProto => { | ||
| 518 | const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); | ||
| 519 | |||
| 520 | const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { | ||
| 521 | try self.addCompileError(parsed_file, Span{ | ||
| 522 | .first = fn_proto.fn_token, | ||
| 523 | .last = fn_proto.fn_token + 1, | ||
| 524 | }, "missing function name"); | ||
| 525 | continue; | ||
| 526 | }; | ||
| 527 | 853 | ||
| 528 | const fn_decl = try self.a().create(Decl.Fn{ | 854 | /// caller takes ownership of resulting Code |
| 529 | .base = Decl{ | 855 | async fn genAndAnalyzeCode( |
| 530 | .id = Decl.Id.Fn, | 856 | comp: *Compilation, |
| 531 | .name = name, | 857 | scope: *Scope, |
| 532 | .visib = parseVisibToken(tree, fn_proto.visib_token), | 858 | node: *ast.Node, |
| 533 | .resolution = event.Future(BuildError!void).init(self.loop), | 859 | expected_type: ?*Type, |
| 534 | .resolution_in_progress = 0, | 860 | ) !*ir.Code { |
| 535 | .parsed_file = parsed_file, | 861 | const unanalyzed_code = try await (async ir.gen( |
| 536 | .parent_scope = &decls.base, | 862 | comp, |
| 537 | }, | 863 | node, |
| 538 | .value = Decl.Fn.Val{ .Unresolved = {} }, | 864 | scope, |
| 539 | .fn_proto = fn_proto, | 865 | ) catch unreachable); |
| 540 | }); | 866 | defer unanalyzed_code.destroy(comp.gpa()); |
| 541 | errdefer self.a().destroy(fn_decl); | 867 | |
| 542 | 868 | if (comp.verbose_ir) { | |
| 543 | try decl_group.call(addTopLevelDecl, self, &fn_decl.base); | 869 | std.debug.warn("unanalyzed:\n"); |
| 544 | }, | 870 | unanalyzed_code.dump(); |
| 545 | ast.Node.Id.TestDecl => @panic("TODO"), | 871 | } |
| 546 | else => unreachable, | 872 | |
| 547 | } | 873 | const analyzed_code = try await (async ir.analyze( |
| 874 | comp, | ||
| 875 | unanalyzed_code, | ||
| 876 | expected_type, | ||
| 877 | ) catch unreachable); | ||
| 878 | errdefer analyzed_code.destroy(comp.gpa()); | ||
| 879 | |||
| 880 | if (comp.verbose_ir) { | ||
| 881 | std.debug.warn("analyzed:\n"); | ||
| 882 | analyzed_code.dump(); | ||
| 548 | } | 883 | } |
| 549 | try await (async decl_group.wait() catch unreachable); | 884 | |
| 550 | try await (async self.build_group.wait() catch unreachable); | 885 | return analyzed_code; |
| 886 | } | ||
| 887 | |||
| 888 | async fn addCompTimeBlock( | ||
| 889 | comp: *Compilation, | ||
| 890 | scope: *Scope, | ||
| 891 | comptime_node: *ast.Node.Comptime, | ||
| 892 | ) !void { | ||
| 893 | const void_type = Type.Void.get(comp); | ||
| 894 | defer void_type.base.base.deref(comp); | ||
| 895 | |||
| 896 | const analyzed_code = (await (async genAndAnalyzeCode( | ||
| 897 | comp, | ||
| 898 | scope, | ||
| 899 | comptime_node.expr, | ||
| 900 | &void_type.base, | ||
| 901 | ) catch unreachable)) catch |err| switch (err) { | ||
| 902 | // This poison value should not cause the errdefers to run. It simply means | ||
| 903 | // that comp.compile_errors is populated. | ||
| 904 | error.SemanticAnalysisFailed => return {}, | ||
| 905 | else => return err, | ||
| 906 | }; | ||
| 907 | analyzed_code.destroy(comp.gpa()); | ||
| 551 | } | 908 | } |
| 552 | 909 | ||
| 553 | async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { | 910 | async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void { |
| 554 | const is_export = decl.isExported(&decl.parsed_file.tree); | 911 | const tree = decl.findRootScope().tree; |
| 912 | const is_export = decl.isExported(tree); | ||
| 913 | |||
| 914 | var add_to_table_resolved = false; | ||
| 915 | const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable; | ||
| 916 | errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261 | ||
| 555 | 917 | ||
| 556 | if (is_export) { | 918 | if (is_export) { |
| 557 | try self.build_group.call(verifyUniqueSymbol, self, decl); | 919 | try self.prelink_group.call(verifyUniqueSymbol, self, decl); |
| 558 | try self.build_group.call(resolveDecl, self, decl); | 920 | try self.prelink_group.call(resolveDecl, self, decl); |
| 921 | } | ||
| 922 | |||
| 923 | add_to_table_resolved = true; | ||
| 924 | try await add_to_table; | ||
| 925 | } | ||
| 926 | |||
| 927 | async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void { | ||
| 928 | const held = await (async decls.table.acquire() catch unreachable); | ||
| 929 | defer held.release(); | ||
| 930 | |||
| 931 | if (try held.value.put(decl.name, decl)) |other_decl| { | ||
| 932 | try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name); | ||
| 933 | // TODO note: other definition here | ||
| 559 | } | 934 | } |
| 560 | } | 935 | } |
| 561 | 936 | ||
| 562 | fn addCompileError(self: *Compilation, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void { | 937 | fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void { |
| 563 | const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); | 938 | const text = try std.fmt.allocPrint(self.gpa(), fmt, args); |
| 564 | errdefer self.loop.allocator.free(text); | 939 | errdefer self.gpa().free(text); |
| 940 | |||
| 941 | const msg = try Msg.createFromScope(self, root, span, text); | ||
| 942 | errdefer msg.destroy(); | ||
| 565 | 943 | ||
| 566 | try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text); | 944 | try self.prelink_group.call(addCompileErrorAsync, self, msg); |
| 567 | } | 945 | } |
| 568 | 946 | ||
| 569 | async fn addCompileErrorAsync( | 947 | async fn addCompileErrorAsync( |
| 570 | self: *Compilation, | 948 | self: *Compilation, |
| 571 | parsed_file: *ParsedFile, | 949 | msg: *Msg, |
| 572 | span: Span, | ||
| 573 | text: []u8, | ||
| 574 | ) !void { | 950 | ) !void { |
| 575 | const msg = try self.loop.allocator.create(errmsg.Msg{ | 951 | errdefer msg.destroy(); |
| 576 | .path = parsed_file.realpath, | ||
| 577 | .text = text, | ||
| 578 | .span = span, | ||
| 579 | .tree = &parsed_file.tree, | ||
| 580 | }); | ||
| 581 | errdefer self.loop.allocator.destroy(msg); | ||
| 582 | 952 | ||
| 583 | const compile_errors = await (async self.compile_errors.acquire() catch unreachable); | 953 | const compile_errors = await (async self.compile_errors.acquire() catch unreachable); |
| 584 | defer compile_errors.release(); | 954 | defer compile_errors.release(); |
| ... | @@ -592,7 +962,7 @@ pub const Compilation = struct { | ... | @@ -592,7 +962,7 @@ pub const Compilation = struct { |
| 592 | 962 | ||
| 593 | if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { | 963 | if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { |
| 594 | try self.addCompileError( | 964 | try self.addCompileError( |
| 595 | decl.parsed_file, | 965 | decl.findRootScope(), |
| 596 | decl.getSpan(), | 966 | decl.getSpan(), |
| 597 | "exported symbol collision: '{}'", | 967 | "exported symbol collision: '{}'", |
| 598 | decl.name, | 968 | decl.name, |
| ... | @@ -601,11 +971,6 @@ pub const Compilation = struct { | ... | @@ -601,11 +971,6 @@ pub const Compilation = struct { |
| 601 | } | 971 | } |
| 602 | } | 972 | } |
| 603 | 973 | ||
| 604 | pub fn link(self: *Compilation, out_file: ?[]const u8) !void { | ||
| 605 | warn("TODO link"); | ||
| 606 | return error.Todo; | ||
| 607 | } | ||
| 608 | |||
| 609 | pub fn haveLibC(self: *Compilation) bool { | 974 | pub fn haveLibC(self: *Compilation) bool { |
| 610 | return self.libc_link_lib != null; | 975 | return self.libc_link_lib != null; |
| 611 | } | 976 | } |
| ... | @@ -625,22 +990,127 @@ pub const Compilation = struct { | ... | @@ -625,22 +990,127 @@ pub const Compilation = struct { |
| 625 | } | 990 | } |
| 626 | } | 991 | } |
| 627 | 992 | ||
| 628 | const link_lib = try self.a().create(LinkLib{ | 993 | const link_lib = try self.gpa().create(LinkLib{ |
| 629 | .name = name, | 994 | .name = name, |
| 630 | .path = null, | 995 | .path = null, |
| 631 | .provided_explicitly = provided_explicitly, | 996 | .provided_explicitly = provided_explicitly, |
| 632 | .symbols = ArrayList([]u8).init(self.a()), | 997 | .symbols = ArrayList([]u8).init(self.gpa()), |
| 633 | }); | 998 | }); |
| 634 | try self.link_libs_list.append(link_lib); | 999 | try self.link_libs_list.append(link_lib); |
| 635 | if (is_libc) { | 1000 | if (is_libc) { |
| 636 | self.libc_link_lib = link_lib; | 1001 | self.libc_link_lib = link_lib; |
| 1002 | |||
| 1003 | // get a head start on looking for the native libc | ||
| 1004 | if (self.target == Target.Native and self.override_libc == null) { | ||
| 1005 | try self.deinit_group.call(startFindingNativeLibC, self); | ||
| 1006 | } | ||
| 637 | } | 1007 | } |
| 638 | return link_lib; | 1008 | return link_lib; |
| 639 | } | 1009 | } |
| 640 | 1010 | ||
| 641 | fn a(self: Compilation) *mem.Allocator { | 1011 | /// cancels itself so no need to await or cancel the promise. |
| 1012 | async fn startFindingNativeLibC(self: *Compilation) void { | ||
| 1013 | await (async self.loop.yield() catch unreachable); | ||
| 1014 | // we don't care if it fails, we're just trying to kick off the future resolution | ||
| 1015 | _ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return; | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | /// General Purpose Allocator. Must free when done. | ||
| 1019 | fn gpa(self: Compilation) *mem.Allocator { | ||
| 642 | return self.loop.allocator; | 1020 | return self.loop.allocator; |
| 643 | } | 1021 | } |
| 1022 | |||
| 1023 | /// Arena Allocator. Automatically freed when the Compilation is destroyed. | ||
| 1024 | fn arena(self: *Compilation) *mem.Allocator { | ||
| 1025 | return &self.arena_allocator.allocator; | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | /// If the temporary directory for this compilation has not been created, it creates it. | ||
| 1029 | /// Then it creates a random file name in that dir and returns it. | ||
| 1030 | pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer { | ||
| 1031 | const tmp_dir = try await (async self.getTmpDir() catch unreachable); | ||
| 1032 | const file_prefix = await (async self.getRandomFileName() catch unreachable); | ||
| 1033 | |||
| 1034 | const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix); | ||
| 1035 | defer self.gpa().free(file_name); | ||
| 1036 | |||
| 1037 | const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]); | ||
| 1038 | errdefer self.gpa().free(full_path); | ||
| 1039 | |||
| 1040 | return Buffer.fromOwnedSlice(self.gpa(), full_path); | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | /// If the temporary directory for this Compilation has not been created, creates it. | ||
| 1044 | /// Then returns it. The directory is unique to this Compilation and cleaned up when | ||
| 1045 | /// the Compilation deinitializes. | ||
| 1046 | async fn getTmpDir(self: *Compilation) ![]const u8 { | ||
| 1047 | if (await (async self.tmp_dir.start() catch unreachable)) |ptr| return ptr.*; | ||
| 1048 | self.tmp_dir.data = await (async self.getTmpDirImpl() catch unreachable); | ||
| 1049 | self.tmp_dir.resolve(); | ||
| 1050 | return self.tmp_dir.data; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | async fn getTmpDirImpl(self: *Compilation) ![]u8 { | ||
| 1054 | const comp_dir_name = await (async self.getRandomFileName() catch unreachable); | ||
| 1055 | const zig_dir_path = try getZigDir(self.gpa()); | ||
| 1056 | defer self.gpa().free(zig_dir_path); | ||
| 1057 | |||
| 1058 | const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]); | ||
| 1059 | try os.makePath(self.gpa(), tmp_dir); | ||
| 1060 | return tmp_dir; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | async fn getRandomFileName(self: *Compilation) [12]u8 { | ||
| 1064 | // here we replace the standard +/ with -_ so that it can be used in a file name | ||
| 1065 | const b64_fs_encoder = std.base64.Base64Encoder.init( | ||
| 1066 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", | ||
| 1067 | std.base64.standard_pad_char, | ||
| 1068 | ); | ||
| 1069 | |||
| 1070 | var rand_bytes: [9]u8 = undefined; | ||
| 1071 | |||
| 1072 | { | ||
| 1073 | const held = await (async self.event_loop_local.prng.acquire() catch unreachable); | ||
| 1074 | defer held.release(); | ||
| 1075 | |||
| 1076 | held.value.random.bytes(rand_bytes[0..]); | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | var result: [12]u8 = undefined; | ||
| 1080 | b64_fs_encoder.encode(result[0..], rand_bytes); | ||
| 1081 | return result; | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void { | ||
| 1085 | // TODO put the garbage somewhere | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | /// Returns a value which has been ref()'d once | ||
| 1089 | async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value { | ||
| 1090 | const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable); | ||
| 1091 | defer analyzed_code.destroy(comp.gpa()); | ||
| 1092 | |||
| 1093 | return analyzed_code.getCompTimeResult(comp); | ||
| 1094 | } | ||
| 1095 | |||
| 1096 | async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type { | ||
| 1097 | const meta_type = &Type.MetaType.get(comp).base; | ||
| 1098 | defer meta_type.base.deref(comp); | ||
| 1099 | |||
| 1100 | const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable); | ||
| 1101 | errdefer result_val.base.deref(comp); | ||
| 1102 | |||
| 1103 | return result_val.cast(Type).?; | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | /// This declaration has been blessed as going into the final code generation. | ||
| 1107 | pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { | ||
| 1108 | if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*; | ||
| 1109 | |||
| 1110 | decl.resolution.data = try await (async generateDecl(comp, decl) catch unreachable); | ||
| 1111 | decl.resolution.resolve(); | ||
| 1112 | return decl.resolution.data; | ||
| 1113 | } | ||
| 644 | }; | 1114 | }; |
| 645 | 1115 | ||
| 646 | fn printError(comptime format: []const u8, args: ...) !void { | 1116 | fn printError(comptime format: []const u8, args: ...) !void { |
| ... | @@ -660,17 +1130,6 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib | ... | @@ -660,17 +1130,6 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib |
| 660 | } | 1130 | } |
| 661 | } | 1131 | } |
| 662 | 1132 | ||
| 663 | /// This declaration has been blessed as going into the final code generation. | ||
| 664 | pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { | ||
| 665 | if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { | ||
| 666 | decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable); | ||
| 667 | decl.resolution.resolve(); | ||
| 668 | return decl.resolution.data; | ||
| 669 | } else { | ||
| 670 | return (await (async decl.resolution.get() catch unreachable)).*; | ||
| 671 | } | ||
| 672 | } | ||
| 673 | |||
| 674 | /// The function that actually does the generation. | 1133 | /// The function that actually does the generation. |
| 675 | async fn generateDecl(comp: *Compilation, decl: *Decl) !void { | 1134 | async fn generateDecl(comp: *Compilation, decl: *Decl) !void { |
| 676 | switch (decl.id) { | 1135 | switch (decl.id) { |
| ... | @@ -684,68 +1143,99 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void { | ... | @@ -684,68 +1143,99 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void { |
| 684 | } | 1143 | } |
| 685 | 1144 | ||
| 686 | async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { | 1145 | async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { |
| 687 | const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl"); | 1146 | const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable); |
| 688 | 1147 | ||
| 689 | const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope); | 1148 | const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope); |
| 690 | defer fndef_scope.base.deref(comp); | 1149 | defer fndef_scope.base.deref(comp); |
| 691 | 1150 | ||
| 692 | // TODO actually look at the return type of the AST | 1151 | const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable); |
| 693 | const return_type = &Type.Void.get(comp).base; | ||
| 694 | defer return_type.base.deref(comp); | ||
| 695 | |||
| 696 | const is_var_args = false; | ||
| 697 | const params = ([*]Type.Fn.Param)(undefined)[0..0]; | ||
| 698 | const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args); | ||
| 699 | defer fn_type.base.base.deref(comp); | 1152 | defer fn_type.base.base.deref(comp); |
| 700 | 1153 | ||
| 701 | var symbol_name = try std.Buffer.init(comp.a(), fn_decl.base.name); | 1154 | var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); |
| 702 | errdefer symbol_name.deinit(); | 1155 | var symbol_name_consumed = false; |
| 1156 | errdefer if (!symbol_name_consumed) symbol_name.deinit(); | ||
| 703 | 1157 | ||
| 1158 | // The Decl.Fn owns the initial 1 reference count | ||
| 704 | const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); | 1159 | const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); |
| 1160 | fn_decl.value = Decl.Fn.Val{ .Fn = fn_val }; | ||
| 1161 | symbol_name_consumed = true; | ||
| 1162 | |||
| 1163 | const analyzed_code = try await (async comp.genAndAnalyzeCode( | ||
| 1164 | &fndef_scope.base, | ||
| 1165 | body_node, | ||
| 1166 | fn_type.return_type, | ||
| 1167 | ) catch unreachable); | ||
| 1168 | errdefer analyzed_code.destroy(comp.gpa()); | ||
| 1169 | |||
| 1170 | // Kick off rendering to LLVM module, but it doesn't block the fn decl | ||
| 1171 | // analysis from being complete. | ||
| 1172 | try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); | ||
| 1173 | try comp.prelink_group.call(addFnToLinkSet, comp, fn_val); | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { | ||
| 1177 | fn_val.base.ref(); | ||
| 705 | defer fn_val.base.deref(comp); | 1178 | defer fn_val.base.deref(comp); |
| 706 | 1179 | ||
| 707 | fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; | 1180 | fn_val.link_set_node.data = fn_val; |
| 708 | 1181 | ||
| 709 | const unanalyzed_code = (await (async ir.gen( | 1182 | const held = await (async comp.fn_link_set.acquire() catch unreachable); |
| 710 | comp, | 1183 | defer held.release(); |
| 711 | body_node, | 1184 | |
| 712 | &fndef_scope.base, | 1185 | held.value.append(fn_val.link_set_node); |
| 713 | Span.token(body_node.lastToken()), | 1186 | } |
| 714 | fn_decl.base.parsed_file, | 1187 | |
| 715 | ) catch unreachable)) catch |err| switch (err) { | 1188 | fn getZigDir(allocator: *mem.Allocator) ![]u8 { |
| 716 | // This poison value should not cause the errdefers to run. It simply means | 1189 | return os.getAppDataDir(allocator, "zig"); |
| 717 | // that self.compile_errors is populated. | 1190 | } |
| 718 | // TODO https://github.com/ziglang/zig/issues/769 | 1191 | |
| 719 | error.SemanticAnalysisFailed => return {}, | 1192 | async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn { |
| 720 | else => return err, | 1193 | const return_type_node = switch (fn_proto.return_type) { |
| 1194 | ast.Node.FnProto.ReturnType.Explicit => |n| n, | ||
| 1195 | ast.Node.FnProto.ReturnType.InferErrorSet => |n| n, | ||
| 721 | }; | 1196 | }; |
| 722 | defer unanalyzed_code.destroy(comp.a()); | 1197 | const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable); |
| 723 | 1198 | return_type.base.deref(comp); | |
| 724 | if (comp.verbose_ir) { | 1199 | |
| 725 | std.debug.warn("unanalyzed:\n"); | 1200 | var params = ArrayList(Type.Fn.Param).init(comp.gpa()); |
| 726 | unanalyzed_code.dump(); | 1201 | var params_consumed = false; |
| 727 | } | 1202 | defer if (params_consumed) { |
| 728 | 1203 | for (params.toSliceConst()) |param| { | |
| 729 | const analyzed_code = (await (async ir.analyze( | 1204 | param.typ.base.deref(comp); |
| 730 | comp, | 1205 | } |
| 731 | fn_decl.base.parsed_file, | 1206 | params.deinit(); |
| 732 | unanalyzed_code, | ||
| 733 | null, | ||
| 734 | ) catch unreachable)) catch |err| switch (err) { | ||
| 735 | // This poison value should not cause the errdefers to run. It simply means | ||
| 736 | // that self.compile_errors is populated. | ||
| 737 | // TODO https://github.com/ziglang/zig/issues/769 | ||
| 738 | error.SemanticAnalysisFailed => return {}, | ||
| 739 | else => return err, | ||
| 740 | }; | 1207 | }; |
| 741 | errdefer analyzed_code.destroy(comp.a()); | ||
| 742 | 1208 | ||
| 743 | if (comp.verbose_ir) { | 1209 | const is_var_args = false; |
| 744 | std.debug.warn("analyzed:\n"); | 1210 | { |
| 745 | analyzed_code.dump(); | 1211 | var it = fn_proto.params.iterator(0); |
| 1212 | while (it.next()) |param_node_ptr| { | ||
| 1213 | const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?; | ||
| 1214 | const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable); | ||
| 1215 | errdefer param_type.base.deref(comp); | ||
| 1216 | try params.append(Type.Fn.Param{ | ||
| 1217 | .typ = param_type, | ||
| 1218 | .is_noalias = param_node.noalias_token != null, | ||
| 1219 | }); | ||
| 1220 | } | ||
| 746 | } | 1221 | } |
| 1222 | const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args); | ||
| 1223 | params_consumed = true; | ||
| 1224 | errdefer fn_type.base.base.deref(comp); | ||
| 747 | 1225 | ||
| 748 | // Kick off rendering to LLVM module, but it doesn't block the fn decl | 1226 | return fn_type; |
| 749 | // analysis from being complete. | 1227 | } |
| 750 | try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); | 1228 | |
| 1229 | async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { | ||
| 1230 | const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable); | ||
| 1231 | defer fn_type.base.base.deref(comp); | ||
| 1232 | |||
| 1233 | var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); | ||
| 1234 | var symbol_name_consumed = false; | ||
| 1235 | defer if (!symbol_name_consumed) symbol_name.deinit(); | ||
| 1236 | |||
| 1237 | // The Decl.Fn owns the initial 1 reference count | ||
| 1238 | const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name); | ||
| 1239 | fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val }; | ||
| 1240 | symbol_name_consumed = true; | ||
| 751 | } | 1241 | } |
src-self-hosted/decl.zig+8-6| ... | @@ -3,7 +3,6 @@ const Allocator = mem.Allocator; | ... | @@ -3,7 +3,6 @@ const Allocator = mem.Allocator; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const ast = std.zig.ast; | 4 | const ast = std.zig.ast; |
| 5 | const Visib = @import("visib.zig").Visib; | 5 | const Visib = @import("visib.zig").Visib; |
| 6 | const ParsedFile = @import("parsed_file.zig").ParsedFile; | ||
| 7 | const event = std.event; | 6 | const event = std.event; |
| 8 | const Value = @import("value.zig").Value; | 7 | const Value = @import("value.zig").Value; |
| 9 | const Token = std.zig.Token; | 8 | const Token = std.zig.Token; |
| ... | @@ -16,8 +15,6 @@ pub const Decl = struct { | ... | @@ -16,8 +15,6 @@ pub const Decl = struct { |
| 16 | name: []const u8, | 15 | name: []const u8, |
| 17 | visib: Visib, | 16 | visib: Visib, |
| 18 | resolution: event.Future(Compilation.BuildError!void), | 17 | resolution: event.Future(Compilation.BuildError!void), |
| 19 | resolution_in_progress: u8, | ||
| 20 | parsed_file: *ParsedFile, | ||
| 21 | parent_scope: *Scope, | 18 | parent_scope: *Scope, |
| 22 | 19 | ||
| 23 | pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); | 20 | pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); |
| ... | @@ -48,6 +45,10 @@ pub const Decl = struct { | ... | @@ -48,6 +45,10 @@ pub const Decl = struct { |
| 48 | } | 45 | } |
| 49 | } | 46 | } |
| 50 | 47 | ||
| 48 | pub fn findRootScope(base: *const Decl) *Scope.Root { | ||
| 49 | return base.parent_scope.findRoot(); | ||
| 50 | } | ||
| 51 | |||
| 51 | pub const Id = enum { | 52 | pub const Id = enum { |
| 52 | Var, | 53 | Var, |
| 53 | Fn, | 54 | Fn, |
| ... | @@ -61,12 +62,13 @@ pub const Decl = struct { | ... | @@ -61,12 +62,13 @@ pub const Decl = struct { |
| 61 | pub const Fn = struct { | 62 | pub const Fn = struct { |
| 62 | base: Decl, | 63 | base: Decl, |
| 63 | value: Val, | 64 | value: Val, |
| 64 | fn_proto: *const ast.Node.FnProto, | 65 | fn_proto: *ast.Node.FnProto, |
| 65 | 66 | ||
| 66 | // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous | 67 | // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous |
| 67 | pub const Val = union { | 68 | pub const Val = union(enum) { |
| 68 | Unresolved: void, | 69 | Unresolved: void, |
| 69 | Ok: *Value.Fn, | 70 | Fn: *Value.Fn, |
| 71 | FnProto: *Value.FnProto, | ||
| 70 | }; | 72 | }; |
| 71 | 73 | ||
| 72 | pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { | 74 | pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { |
src-self-hosted/errmsg.zig+203-66| ... | @@ -4,6 +4,8 @@ const os = std.os; | ... | @@ -4,6 +4,8 @@ const os = std.os; |
| 4 | const Token = std.zig.Token; | 4 | const Token = std.zig.Token; |
| 5 | const ast = std.zig.ast; | 5 | const ast = std.zig.ast; |
| 6 | const TokenIndex = std.zig.ast.TokenIndex; | 6 | const TokenIndex = std.zig.ast.TokenIndex; |
| 7 | const Compilation = @import("compilation.zig").Compilation; | ||
| 8 | const Scope = @import("scope.zig").Scope; | ||
| 7 | 9 | ||
| 8 | pub const Color = enum { | 10 | pub const Color = enum { |
| 9 | Auto, | 11 | Auto, |
| ... | @@ -16,85 +18,220 @@ pub const Span = struct { | ... | @@ -16,85 +18,220 @@ pub const Span = struct { |
| 16 | last: ast.TokenIndex, | 18 | last: ast.TokenIndex, |
| 17 | 19 | ||
| 18 | pub fn token(i: TokenIndex) Span { | 20 | pub fn token(i: TokenIndex) Span { |
| 19 | return Span { | 21 | return Span{ |
| 20 | .first = i, | 22 | .first = i, |
| 21 | .last = i, | 23 | .last = i, |
| 22 | }; | 24 | }; |
| 23 | } | 25 | } |
| 26 | |||
| 27 | pub fn node(n: *ast.Node) Span { | ||
| 28 | return Span{ | ||
| 29 | .first = n.firstToken(), | ||
| 30 | .last = n.lastToken(), | ||
| 31 | }; | ||
| 32 | } | ||
| 24 | }; | 33 | }; |
| 25 | 34 | ||
| 26 | pub const Msg = struct { | 35 | pub const Msg = struct { |
| 27 | path: []const u8, | ||
| 28 | text: []u8, | ||
| 29 | span: Span, | 36 | span: Span, |
| 30 | tree: *ast.Tree, | 37 | text: []u8, |
| 31 | }; | 38 | data: Data, |
| 39 | |||
| 40 | const Data = union(enum) { | ||
| 41 | PathAndTree: PathAndTree, | ||
| 42 | ScopeAndComp: ScopeAndComp, | ||
| 43 | }; | ||
| 44 | |||
| 45 | const PathAndTree = struct { | ||
| 46 | realpath: []const u8, | ||
| 47 | tree: *ast.Tree, | ||
| 48 | allocator: *mem.Allocator, | ||
| 49 | }; | ||
| 50 | |||
| 51 | const ScopeAndComp = struct { | ||
| 52 | root_scope: *Scope.Root, | ||
| 53 | compilation: *Compilation, | ||
| 54 | }; | ||
| 55 | |||
| 56 | pub fn destroy(self: *Msg) void { | ||
| 57 | switch (self.data) { | ||
| 58 | Data.PathAndTree => |path_and_tree| { | ||
| 59 | path_and_tree.allocator.free(self.text); | ||
| 60 | path_and_tree.allocator.destroy(self); | ||
| 61 | }, | ||
| 62 | Data.ScopeAndComp => |scope_and_comp| { | ||
| 63 | scope_and_comp.root_scope.base.deref(scope_and_comp.compilation); | ||
| 64 | scope_and_comp.compilation.gpa().free(self.text); | ||
| 65 | scope_and_comp.compilation.gpa().destroy(self); | ||
| 66 | }, | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | fn getAllocator(self: *const Msg) *mem.Allocator { | ||
| 71 | switch (self.data) { | ||
| 72 | Data.PathAndTree => |path_and_tree| { | ||
| 73 | return path_and_tree.allocator; | ||
| 74 | }, | ||
| 75 | Data.ScopeAndComp => |scope_and_comp| { | ||
| 76 | return scope_and_comp.compilation.gpa(); | ||
| 77 | }, | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | pub fn getRealPath(self: *const Msg) []const u8 { | ||
| 82 | switch (self.data) { | ||
| 83 | Data.PathAndTree => |path_and_tree| { | ||
| 84 | return path_and_tree.realpath; | ||
| 85 | }, | ||
| 86 | Data.ScopeAndComp => |scope_and_comp| { | ||
| 87 | return scope_and_comp.root_scope.realpath; | ||
| 88 | }, | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | pub fn getTree(self: *const Msg) *ast.Tree { | ||
| 93 | switch (self.data) { | ||
| 94 | Data.PathAndTree => |path_and_tree| { | ||
| 95 | return path_and_tree.tree; | ||
| 96 | }, | ||
| 97 | Data.ScopeAndComp => |scope_and_comp| { | ||
| 98 | return scope_and_comp.root_scope.tree; | ||
| 99 | }, | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Takes ownership of text | ||
| 104 | /// References root_scope, and derefs when the msg is freed | ||
| 105 | pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg { | ||
| 106 | const msg = try comp.gpa().create(Msg{ | ||
| 107 | .text = text, | ||
| 108 | .span = span, | ||
| 109 | .data = Data{ | ||
| 110 | .ScopeAndComp = ScopeAndComp{ | ||
| 111 | .root_scope = root_scope, | ||
| 112 | .compilation = comp, | ||
| 113 | }, | ||
| 114 | }, | ||
| 115 | }); | ||
| 116 | root_scope.base.ref(); | ||
| 117 | return msg; | ||
| 118 | } | ||
| 119 | |||
| 120 | pub fn createFromParseErrorAndScope( | ||
| 121 | comp: *Compilation, | ||
| 122 | root_scope: *Scope.Root, | ||
| 123 | parse_error: *const ast.Error, | ||
| 124 | ) !*Msg { | ||
| 125 | const loc_token = parse_error.loc(); | ||
| 126 | var text_buf = try std.Buffer.initSize(comp.gpa(), 0); | ||
| 127 | defer text_buf.deinit(); | ||
| 128 | |||
| 129 | var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; | ||
| 130 | try parse_error.render(&root_scope.tree.tokens, out_stream); | ||
| 131 | |||
| 132 | const msg = try comp.gpa().create(Msg{ | ||
| 133 | .text = undefined, | ||
| 134 | .span = Span{ | ||
| 135 | .first = loc_token, | ||
| 136 | .last = loc_token, | ||
| 137 | }, | ||
| 138 | .data = Data{ | ||
| 139 | .ScopeAndComp = ScopeAndComp{ | ||
| 140 | .root_scope = root_scope, | ||
| 141 | .compilation = comp, | ||
| 142 | }, | ||
| 143 | }, | ||
| 144 | }); | ||
| 145 | root_scope.base.ref(); | ||
| 146 | msg.text = text_buf.toOwnedSlice(); | ||
| 147 | return msg; | ||
| 148 | } | ||
| 149 | |||
| 150 | /// `realpath` must outlive the returned Msg | ||
| 151 | /// `tree` must outlive the returned Msg | ||
| 152 | /// Caller owns returned Msg and must free with `allocator` | ||
| 153 | /// allocator will additionally be used for printing messages later. | ||
| 154 | pub fn createFromParseError( | ||
| 155 | allocator: *mem.Allocator, | ||
| 156 | parse_error: *const ast.Error, | ||
| 157 | tree: *ast.Tree, | ||
| 158 | realpath: []const u8, | ||
| 159 | ) !*Msg { | ||
| 160 | const loc_token = parse_error.loc(); | ||
| 161 | var text_buf = try std.Buffer.initSize(allocator, 0); | ||
| 162 | defer text_buf.deinit(); | ||
| 163 | |||
| 164 | var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; | ||
| 165 | try parse_error.render(&tree.tokens, out_stream); | ||
| 166 | |||
| 167 | const msg = try allocator.create(Msg{ | ||
| 168 | .text = undefined, | ||
| 169 | .data = Data{ | ||
| 170 | .PathAndTree = PathAndTree{ | ||
| 171 | .allocator = allocator, | ||
| 172 | .realpath = realpath, | ||
| 173 | .tree = tree, | ||
| 174 | }, | ||
| 175 | }, | ||
| 176 | .span = Span{ | ||
| 177 | .first = loc_token, | ||
| 178 | .last = loc_token, | ||
| 179 | }, | ||
| 180 | }); | ||
| 181 | msg.text = text_buf.toOwnedSlice(); | ||
| 182 | errdefer allocator.destroy(msg); | ||
| 183 | |||
| 184 | return msg; | ||
| 185 | } | ||
| 186 | |||
| 187 | pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void { | ||
| 188 | const allocator = msg.getAllocator(); | ||
| 189 | const realpath = msg.getRealPath(); | ||
| 190 | const tree = msg.getTree(); | ||
| 191 | |||
| 192 | const cwd = try os.getCwd(allocator); | ||
| 193 | defer allocator.free(cwd); | ||
| 194 | |||
| 195 | const relpath = try os.path.relative(allocator, cwd, realpath); | ||
| 196 | defer allocator.free(relpath); | ||
| 197 | |||
| 198 | const path = if (relpath.len < realpath.len) relpath else realpath; | ||
| 199 | |||
| 200 | const first_token = tree.tokens.at(msg.span.first); | ||
| 201 | const last_token = tree.tokens.at(msg.span.last); | ||
| 202 | const start_loc = tree.tokenLocationPtr(0, first_token); | ||
| 203 | const end_loc = tree.tokenLocationPtr(first_token.end, last_token); | ||
| 204 | if (!color_on) { | ||
| 205 | try stream.print( | ||
| 206 | "{}:{}:{}: error: {}\n", | ||
| 207 | path, | ||
| 208 | start_loc.line + 1, | ||
| 209 | start_loc.column + 1, | ||
| 210 | msg.text, | ||
| 211 | ); | ||
| 212 | return; | ||
| 213 | } | ||
| 32 | 214 | ||
| 33 | /// `path` must outlive the returned Msg | ||
| 34 | /// `tree` must outlive the returned Msg | ||
| 35 | /// Caller owns returned Msg and must free with `allocator` | ||
| 36 | pub fn createFromParseError( | ||
| 37 | allocator: *mem.Allocator, | ||
| 38 | parse_error: *const ast.Error, | ||
| 39 | tree: *ast.Tree, | ||
| 40 | path: []const u8, | ||
| 41 | ) !*Msg { | ||
| 42 | const loc_token = parse_error.loc(); | ||
| 43 | var text_buf = try std.Buffer.initSize(allocator, 0); | ||
| 44 | defer text_buf.deinit(); | ||
| 45 | |||
| 46 | var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; | ||
| 47 | try parse_error.render(&tree.tokens, out_stream); | ||
| 48 | |||
| 49 | const msg = try allocator.create(Msg{ | ||
| 50 | .tree = tree, | ||
| 51 | .path = path, | ||
| 52 | .text = text_buf.toOwnedSlice(), | ||
| 53 | .span = Span{ | ||
| 54 | .first = loc_token, | ||
| 55 | .last = loc_token, | ||
| 56 | }, | ||
| 57 | }); | ||
| 58 | errdefer allocator.destroy(msg); | ||
| 59 | |||
| 60 | return msg; | ||
| 61 | } | ||
| 62 | |||
| 63 | pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void { | ||
| 64 | const first_token = msg.tree.tokens.at(msg.span.first); | ||
| 65 | const last_token = msg.tree.tokens.at(msg.span.last); | ||
| 66 | const start_loc = msg.tree.tokenLocationPtr(0, first_token); | ||
| 67 | const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token); | ||
| 68 | if (!color_on) { | ||
| 69 | try stream.print( | 215 | try stream.print( |
| 70 | "{}:{}:{}: error: {}\n", | 216 | "{}:{}:{}: error: {}\n{}\n", |
| 71 | msg.path, | 217 | path, |
| 72 | start_loc.line + 1, | 218 | start_loc.line + 1, |
| 73 | start_loc.column + 1, | 219 | start_loc.column + 1, |
| 74 | msg.text, | 220 | msg.text, |
| 221 | tree.source[start_loc.line_start..start_loc.line_end], | ||
| 75 | ); | 222 | ); |
| 76 | return; | 223 | try stream.writeByteNTimes(' ', start_loc.column); |
| 224 | try stream.writeByteNTimes('~', last_token.end - first_token.start); | ||
| 225 | try stream.write("\n"); | ||
| 77 | } | 226 | } |
| 78 | 227 | ||
| 79 | try stream.print( | 228 | pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void { |
| 80 | "{}:{}:{}: error: {}\n{}\n", | 229 | const color_on = switch (color) { |
| 81 | msg.path, | 230 | Color.Auto => file.isTty(), |
| 82 | start_loc.line + 1, | 231 | Color.On => true, |
| 83 | start_loc.column + 1, | 232 | Color.Off => false, |
| 84 | msg.text, | 233 | }; |
| 85 | msg.tree.source[start_loc.line_start..start_loc.line_end], | 234 | var stream = &std.io.FileOutStream.init(file).stream; |
| 86 | ); | 235 | return msg.printToStream(stream, color_on); |
| 87 | try stream.writeByteNTimes(' ', start_loc.column); | 236 | } |
| 88 | try stream.writeByteNTimes('~', last_token.end - first_token.start); | 237 | }; |
| 89 | try stream.write("\n"); | ||
| 90 | } | ||
| 91 | |||
| 92 | pub fn printToFile(file: *os.File, msg: *const Msg, color: Color) !void { | ||
| 93 | const color_on = switch (color) { | ||
| 94 | Color.Auto => file.isTty(), | ||
| 95 | Color.On => true, | ||
| 96 | Color.Off => false, | ||
| 97 | }; | ||
| 98 | var stream = &std.io.FileOutStream.init(file).stream; | ||
| 99 | return printToStream(stream, msg, color_on); | ||
| 100 | } |
src-self-hosted/ir.zig+1566-199| ... | @@ -8,10 +8,10 @@ const Value = @import("value.zig").Value; | ... | @@ -8,10 +8,10 @@ const Value = @import("value.zig").Value; |
| 8 | const Type = Value.Type; | 8 | const Type = Value.Type; |
| 9 | const assert = std.debug.assert; | 9 | const assert = std.debug.assert; |
| 10 | const Token = std.zig.Token; | 10 | const Token = std.zig.Token; |
| 11 | const ParsedFile = @import("parsed_file.zig").ParsedFile; | ||
| 12 | const Span = @import("errmsg.zig").Span; | 11 | const Span = @import("errmsg.zig").Span; |
| 13 | const llvm = @import("llvm.zig"); | 12 | const llvm = @import("llvm.zig"); |
| 14 | const ObjectFile = @import("codegen.zig").ObjectFile; | 13 | const ObjectFile = @import("codegen.zig").ObjectFile; |
| 14 | const Decl = @import("decl.zig").Decl; | ||
| 15 | 15 | ||
| 16 | pub const LVal = enum { | 16 | pub const LVal = enum { |
| 17 | None, | 17 | None, |
| ... | @@ -31,10 +31,10 @@ pub const IrVal = union(enum) { | ... | @@ -31,10 +31,10 @@ pub const IrVal = union(enum) { |
| 31 | 31 | ||
| 32 | pub fn dump(self: IrVal) void { | 32 | pub fn dump(self: IrVal) void { |
| 33 | switch (self) { | 33 | switch (self) { |
| 34 | IrVal.Unknown => typeof.dump(), | 34 | IrVal.Unknown => std.debug.warn("Unknown"), |
| 35 | IrVal.KnownType => |typeof| { | 35 | IrVal.KnownType => |typ| { |
| 36 | std.debug.warn("KnownType("); | 36 | std.debug.warn("KnownType("); |
| 37 | typeof.dump(); | 37 | typ.dump(); |
| 38 | std.debug.warn(")"); | 38 | std.debug.warn(")"); |
| 39 | }, | 39 | }, |
| 40 | IrVal.KnownValue => |value| { | 40 | IrVal.KnownValue => |value| { |
| ... | @@ -46,27 +46,28 @@ pub const IrVal = union(enum) { | ... | @@ -46,27 +46,28 @@ pub const IrVal = union(enum) { |
| 46 | } | 46 | } |
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | pub const Instruction = struct { | 49 | pub const Inst = struct { |
| 50 | id: Id, | 50 | id: Id, |
| 51 | scope: *Scope, | 51 | scope: *Scope, |
| 52 | debug_id: usize, | 52 | debug_id: usize, |
| 53 | val: IrVal, | 53 | val: IrVal, |
| 54 | ref_count: usize, | 54 | ref_count: usize, |
| 55 | span: Span, | 55 | span: Span, |
| 56 | owner_bb: *BasicBlock, | ||
| 56 | 57 | ||
| 57 | /// true if this instruction was generated by zig and not from user code | 58 | /// true if this instruction was generated by zig and not from user code |
| 58 | is_generated: bool, | 59 | is_generated: bool, |
| 59 | 60 | ||
| 60 | /// the instruction that is derived from this one in analysis | 61 | /// the instruction that is derived from this one in analysis |
| 61 | child: ?*Instruction, | 62 | child: ?*Inst, |
| 62 | 63 | ||
| 63 | /// the instruction that this one derives from in analysis | 64 | /// the instruction that this one derives from in analysis |
| 64 | parent: ?*Instruction, | 65 | parent: ?*Inst, |
| 65 | 66 | ||
| 66 | /// populated durign codegen | 67 | /// populated durign codegen |
| 67 | llvm_value: ?llvm.ValueRef, | 68 | llvm_value: ?llvm.ValueRef, |
| 68 | 69 | ||
| 69 | pub fn cast(base: *Instruction, comptime T: type) ?*T { | 70 | pub fn cast(base: *Inst, comptime T: type) ?*T { |
| 70 | if (base.id == comptime typeToId(T)) { | 71 | if (base.id == comptime typeToId(T)) { |
| 71 | return @fieldParentPtr(T, "base", base); | 72 | return @fieldParentPtr(T, "base", base); |
| 72 | } | 73 | } |
| ... | @@ -76,18 +77,18 @@ pub const Instruction = struct { | ... | @@ -76,18 +77,18 @@ pub const Instruction = struct { |
| 76 | pub fn typeToId(comptime T: type) Id { | 77 | pub fn typeToId(comptime T: type) Id { |
| 77 | comptime var i = 0; | 78 | comptime var i = 0; |
| 78 | inline while (i < @memberCount(Id)) : (i += 1) { | 79 | inline while (i < @memberCount(Id)) : (i += 1) { |
| 79 | if (T == @field(Instruction, @memberName(Id, i))) { | 80 | if (T == @field(Inst, @memberName(Id, i))) { |
| 80 | return @field(Id, @memberName(Id, i)); | 81 | return @field(Id, @memberName(Id, i)); |
| 81 | } | 82 | } |
| 82 | } | 83 | } |
| 83 | unreachable; | 84 | unreachable; |
| 84 | } | 85 | } |
| 85 | 86 | ||
| 86 | pub fn dump(base: *const Instruction) void { | 87 | pub fn dump(base: *const Inst) void { |
| 87 | comptime var i = 0; | 88 | comptime var i = 0; |
| 88 | inline while (i < @memberCount(Id)) : (i += 1) { | 89 | inline while (i < @memberCount(Id)) : (i += 1) { |
| 89 | if (base.id == @field(Id, @memberName(Id, i))) { | 90 | if (base.id == @field(Id, @memberName(Id, i))) { |
| 90 | const T = @field(Instruction, @memberName(Id, i)); | 91 | const T = @field(Inst, @memberName(Id, i)); |
| 91 | std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id)); | 92 | std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id)); |
| 92 | @fieldParentPtr(T, "base", base).dump(); | 93 | @fieldParentPtr(T, "base", base).dump(); |
| 93 | std.debug.warn(")"); | 94 | std.debug.warn(")"); |
| ... | @@ -97,32 +98,40 @@ pub const Instruction = struct { | ... | @@ -97,32 +98,40 @@ pub const Instruction = struct { |
| 97 | unreachable; | 98 | unreachable; |
| 98 | } | 99 | } |
| 99 | 100 | ||
| 100 | pub fn hasSideEffects(base: *const Instruction) bool { | 101 | pub fn hasSideEffects(base: *const Inst) bool { |
| 101 | comptime var i = 0; | 102 | comptime var i = 0; |
| 102 | inline while (i < @memberCount(Id)) : (i += 1) { | 103 | inline while (i < @memberCount(Id)) : (i += 1) { |
| 103 | if (base.id == @field(Id, @memberName(Id, i))) { | 104 | if (base.id == @field(Id, @memberName(Id, i))) { |
| 104 | const T = @field(Instruction, @memberName(Id, i)); | 105 | const T = @field(Inst, @memberName(Id, i)); |
| 105 | return @fieldParentPtr(T, "base", base).hasSideEffects(); | 106 | return @fieldParentPtr(T, "base", base).hasSideEffects(); |
| 106 | } | 107 | } |
| 107 | } | 108 | } |
| 108 | unreachable; | 109 | unreachable; |
| 109 | } | 110 | } |
| 110 | 111 | ||
| 111 | pub fn analyze(base: *Instruction, ira: *Analyze) Analyze.Error!*Instruction { | 112 | pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst { |
| 112 | comptime var i = 0; | 113 | switch (base.id) { |
| 113 | inline while (i < @memberCount(Id)) : (i += 1) { | 114 | Id.Return => return @fieldParentPtr(Return, "base", base).analyze(ira), |
| 114 | if (base.id == @field(Id, @memberName(Id, i))) { | 115 | Id.Const => return @fieldParentPtr(Const, "base", base).analyze(ira), |
| 115 | const T = @field(Instruction, @memberName(Id, i)); | 116 | Id.Call => return @fieldParentPtr(Call, "base", base).analyze(ira), |
| 116 | return @fieldParentPtr(T, "base", base).analyze(ira); | 117 | Id.DeclRef => return await (async @fieldParentPtr(DeclRef, "base", base).analyze(ira) catch unreachable), |
| 117 | } | 118 | Id.Ref => return await (async @fieldParentPtr(Ref, "base", base).analyze(ira) catch unreachable), |
| 119 | Id.DeclVar => return @fieldParentPtr(DeclVar, "base", base).analyze(ira), | ||
| 120 | Id.CheckVoidStmt => return @fieldParentPtr(CheckVoidStmt, "base", base).analyze(ira), | ||
| 121 | Id.Phi => return @fieldParentPtr(Phi, "base", base).analyze(ira), | ||
| 122 | Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira), | ||
| 123 | Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira), | ||
| 124 | Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable), | ||
| 118 | } | 125 | } |
| 119 | unreachable; | ||
| 120 | } | 126 | } |
| 121 | 127 | ||
| 122 | pub fn render(base: *Instruction, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) { | 128 | pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) { |
| 123 | switch (base.id) { | 129 | switch (base.id) { |
| 124 | Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), | 130 | Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), |
| 125 | Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), | 131 | Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), |
| 132 | Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val), | ||
| 133 | Id.DeclRef => unreachable, | ||
| 134 | Id.PtrType => unreachable, | ||
| 126 | Id.Ref => @panic("TODO"), | 135 | Id.Ref => @panic("TODO"), |
| 127 | Id.DeclVar => @panic("TODO"), | 136 | Id.DeclVar => @panic("TODO"), |
| 128 | Id.CheckVoidStmt => @panic("TODO"), | 137 | Id.CheckVoidStmt => @panic("TODO"), |
| ... | @@ -132,7 +141,22 @@ pub const Instruction = struct { | ... | @@ -132,7 +141,22 @@ pub const Instruction = struct { |
| 132 | } | 141 | } |
| 133 | } | 142 | } |
| 134 | 143 | ||
| 135 | fn getAsParam(param: *Instruction) !*Instruction { | 144 | fn ref(base: *Inst, builder: *Builder) void { |
| 145 | base.ref_count += 1; | ||
| 146 | if (base.owner_bb != builder.current_basic_block and !base.isCompTime()) { | ||
| 147 | base.owner_bb.ref(builder); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | fn copyVal(base: *Inst, comp: *Compilation) !*Value { | ||
| 152 | if (base.parent.?.ref_count == 0) { | ||
| 153 | return base.val.KnownValue.derefAndCopy(comp); | ||
| 154 | } | ||
| 155 | return base.val.KnownValue.copy(comp); | ||
| 156 | } | ||
| 157 | |||
| 158 | fn getAsParam(param: *Inst) !*Inst { | ||
| 159 | param.ref_count -= 1; | ||
| 136 | const child = param.child orelse return error.SemanticAnalysisFailed; | 160 | const child = param.child orelse return error.SemanticAnalysisFailed; |
| 137 | switch (child.val) { | 161 | switch (child.val) { |
| 138 | IrVal.Unknown => return error.SemanticAnalysisFailed, | 162 | IrVal.Unknown => return error.SemanticAnalysisFailed, |
| ... | @@ -140,28 +164,72 @@ pub const Instruction = struct { | ... | @@ -140,28 +164,72 @@ pub const Instruction = struct { |
| 140 | } | 164 | } |
| 141 | } | 165 | } |
| 142 | 166 | ||
| 167 | fn getConstVal(self: *Inst, ira: *Analyze) !*Value { | ||
| 168 | if (self.isCompTime()) { | ||
| 169 | return self.val.KnownValue; | ||
| 170 | } else { | ||
| 171 | try ira.addCompileError(self.span, "unable to evaluate constant expression"); | ||
| 172 | return error.SemanticAnalysisFailed; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | fn getAsConstType(param: *Inst, ira: *Analyze) !*Type { | ||
| 177 | const meta_type = Type.MetaType.get(ira.irb.comp); | ||
| 178 | meta_type.base.base.deref(ira.irb.comp); | ||
| 179 | |||
| 180 | const inst = try param.getAsParam(); | ||
| 181 | const casted = try ira.implicitCast(inst, &meta_type.base); | ||
| 182 | const val = try casted.getConstVal(ira); | ||
| 183 | return val.cast(Value.Type).?; | ||
| 184 | } | ||
| 185 | |||
| 186 | fn getAsConstAlign(param: *Inst, ira: *Analyze) !u32 { | ||
| 187 | return error.Unimplemented; | ||
| 188 | //const align_type = Type.Int.get_align(ira.irb.comp); | ||
| 189 | //align_type.base.base.deref(ira.irb.comp); | ||
| 190 | |||
| 191 | //const inst = try param.getAsParam(); | ||
| 192 | //const casted = try ira.implicitCast(inst, align_type); | ||
| 193 | //const val = try casted.getConstVal(ira); | ||
| 194 | |||
| 195 | //uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint); | ||
| 196 | //if (align_bytes == 0) { | ||
| 197 | // ir_add_error(ira, value, buf_sprintf("alignment must be >= 1")); | ||
| 198 | // return false; | ||
| 199 | //} | ||
| 200 | |||
| 201 | //if (!is_power_of_2(align_bytes)) { | ||
| 202 | // ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes)); | ||
| 203 | // return false; | ||
| 204 | //} | ||
| 205 | } | ||
| 206 | |||
| 143 | /// asserts that the type is known | 207 | /// asserts that the type is known |
| 144 | fn getKnownType(self: *Instruction) *Type { | 208 | fn getKnownType(self: *Inst) *Type { |
| 145 | switch (self.val) { | 209 | switch (self.val) { |
| 146 | IrVal.KnownType => |typeof| return typeof, | 210 | IrVal.KnownType => |typ| return typ, |
| 147 | IrVal.KnownValue => |value| return value.typeof, | 211 | IrVal.KnownValue => |value| return value.typ, |
| 148 | IrVal.Unknown => unreachable, | 212 | IrVal.Unknown => unreachable, |
| 149 | } | 213 | } |
| 150 | } | 214 | } |
| 151 | 215 | ||
| 152 | pub fn setGenerated(base: *Instruction) void { | 216 | pub fn setGenerated(base: *Inst) void { |
| 153 | base.is_generated = true; | 217 | base.is_generated = true; |
| 154 | } | 218 | } |
| 155 | 219 | ||
| 156 | pub fn isNoReturn(base: *const Instruction) bool { | 220 | pub fn isNoReturn(base: *const Inst) bool { |
| 157 | switch (base.val) { | 221 | switch (base.val) { |
| 158 | IrVal.Unknown => return false, | 222 | IrVal.Unknown => return false, |
| 159 | IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn, | 223 | IrVal.KnownValue => |x| return x.typ.id == Type.Id.NoReturn, |
| 160 | IrVal.KnownType => |typeof| return typeof.id == Type.Id.NoReturn, | 224 | IrVal.KnownType => |typ| return typ.id == Type.Id.NoReturn, |
| 161 | } | 225 | } |
| 162 | } | 226 | } |
| 163 | 227 | ||
| 164 | pub fn linkToParent(self: *Instruction, parent: *Instruction) void { | 228 | pub fn isCompTime(base: *const Inst) bool { |
| 229 | return base.val == IrVal.KnownValue; | ||
| 230 | } | ||
| 231 | |||
| 232 | pub fn linkToParent(self: *Inst, parent: *Inst) void { | ||
| 165 | assert(self.parent == null); | 233 | assert(self.parent == null); |
| 166 | assert(parent.child == null); | 234 | assert(parent.child == null); |
| 167 | self.parent = parent; | 235 | self.parent = parent; |
| ... | @@ -177,10 +245,89 @@ pub const Instruction = struct { | ... | @@ -177,10 +245,89 @@ pub const Instruction = struct { |
| 177 | Phi, | 245 | Phi, |
| 178 | Br, | 246 | Br, |
| 179 | AddImplicitReturnType, | 247 | AddImplicitReturnType, |
| 248 | Call, | ||
| 249 | DeclRef, | ||
| 250 | PtrType, | ||
| 251 | }; | ||
| 252 | |||
| 253 | pub const Call = struct { | ||
| 254 | base: Inst, | ||
| 255 | params: Params, | ||
| 256 | |||
| 257 | const Params = struct { | ||
| 258 | fn_ref: *Inst, | ||
| 259 | args: []*Inst, | ||
| 260 | }; | ||
| 261 | |||
| 262 | const ir_val_init = IrVal.Init.Unknown; | ||
| 263 | |||
| 264 | pub fn dump(self: *const Call) void { | ||
| 265 | std.debug.warn("#{}(", self.params.fn_ref.debug_id); | ||
| 266 | for (self.params.args) |arg| { | ||
| 267 | std.debug.warn("#{},", arg.debug_id); | ||
| 268 | } | ||
| 269 | std.debug.warn(")"); | ||
| 270 | } | ||
| 271 | |||
| 272 | pub fn hasSideEffects(self: *const Call) bool { | ||
| 273 | return true; | ||
| 274 | } | ||
| 275 | |||
| 276 | pub fn analyze(self: *const Call, ira: *Analyze) !*Inst { | ||
| 277 | const fn_ref = try self.params.fn_ref.getAsParam(); | ||
| 278 | const fn_ref_type = fn_ref.getKnownType(); | ||
| 279 | const fn_type = fn_ref_type.cast(Type.Fn) orelse { | ||
| 280 | try ira.addCompileError(fn_ref.span, "type '{}' not a function", fn_ref_type.name); | ||
| 281 | return error.SemanticAnalysisFailed; | ||
| 282 | }; | ||
| 283 | |||
| 284 | if (fn_type.params.len != self.params.args.len) { | ||
| 285 | try ira.addCompileError( | ||
| 286 | self.base.span, | ||
| 287 | "expected {} arguments, found {}", | ||
| 288 | fn_type.params.len, | ||
| 289 | self.params.args.len, | ||
| 290 | ); | ||
| 291 | return error.SemanticAnalysisFailed; | ||
| 292 | } | ||
| 293 | |||
| 294 | const args = try ira.irb.arena().alloc(*Inst, self.params.args.len); | ||
| 295 | for (self.params.args) |arg, i| { | ||
| 296 | args[i] = try arg.getAsParam(); | ||
| 297 | } | ||
| 298 | const new_inst = try ira.irb.build(Call, self.base.scope, self.base.span, Params{ | ||
| 299 | .fn_ref = fn_ref, | ||
| 300 | .args = args, | ||
| 301 | }); | ||
| 302 | new_inst.val = IrVal{ .KnownType = fn_type.return_type }; | ||
| 303 | return new_inst; | ||
| 304 | } | ||
| 305 | |||
| 306 | pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { | ||
| 307 | const fn_ref = self.params.fn_ref.llvm_value.?; | ||
| 308 | |||
| 309 | const args = try ofile.arena.alloc(llvm.ValueRef, self.params.args.len); | ||
| 310 | for (self.params.args) |arg, i| { | ||
| 311 | args[i] = arg.llvm_value.?; | ||
| 312 | } | ||
| 313 | |||
| 314 | const llvm_cc = llvm.CCallConv; | ||
| 315 | const fn_inline = llvm.FnInline.Auto; | ||
| 316 | |||
| 317 | return llvm.BuildCall( | ||
| 318 | ofile.builder, | ||
| 319 | fn_ref, | ||
| 320 | args.ptr, | ||
| 321 | @intCast(c_uint, args.len), | ||
| 322 | llvm_cc, | ||
| 323 | fn_inline, | ||
| 324 | c"", | ||
| 325 | ) orelse error.OutOfMemory; | ||
| 326 | } | ||
| 180 | }; | 327 | }; |
| 181 | 328 | ||
| 182 | pub const Const = struct { | 329 | pub const Const = struct { |
| 183 | base: Instruction, | 330 | base: Inst, |
| 184 | params: Params, | 331 | params: Params, |
| 185 | 332 | ||
| 186 | const Params = struct {}; | 333 | const Params = struct {}; |
| ... | @@ -197,7 +344,7 @@ pub const Instruction = struct { | ... | @@ -197,7 +344,7 @@ pub const Instruction = struct { |
| 197 | return false; | 344 | return false; |
| 198 | } | 345 | } |
| 199 | 346 | ||
| 200 | pub fn analyze(self: *const Const, ira: *Analyze) !*Instruction { | 347 | pub fn analyze(self: *const Const, ira: *Analyze) !*Inst { |
| 201 | const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{}); | 348 | const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{}); |
| 202 | new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() }; | 349 | new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() }; |
| 203 | return new_inst; | 350 | return new_inst; |
| ... | @@ -209,11 +356,11 @@ pub const Instruction = struct { | ... | @@ -209,11 +356,11 @@ pub const Instruction = struct { |
| 209 | }; | 356 | }; |
| 210 | 357 | ||
| 211 | pub const Return = struct { | 358 | pub const Return = struct { |
| 212 | base: Instruction, | 359 | base: Inst, |
| 213 | params: Params, | 360 | params: Params, |
| 214 | 361 | ||
| 215 | const Params = struct { | 362 | const Params = struct { |
| 216 | return_value: *Instruction, | 363 | return_value: *Inst, |
| 217 | }; | 364 | }; |
| 218 | 365 | ||
| 219 | const ir_val_init = IrVal.Init.NoReturn; | 366 | const ir_val_init = IrVal.Init.NoReturn; |
| ... | @@ -226,7 +373,7 @@ pub const Instruction = struct { | ... | @@ -226,7 +373,7 @@ pub const Instruction = struct { |
| 226 | return true; | 373 | return true; |
| 227 | } | 374 | } |
| 228 | 375 | ||
| 229 | pub fn analyze(self: *const Return, ira: *Analyze) !*Instruction { | 376 | pub fn analyze(self: *const Return, ira: *Analyze) !*Inst { |
| 230 | const value = try self.params.return_value.getAsParam(); | 377 | const value = try self.params.return_value.getAsParam(); |
| 231 | const casted_value = try ira.implicitCast(value, ira.explicit_return_type); | 378 | const casted_value = try ira.implicitCast(value, ira.explicit_return_type); |
| 232 | 379 | ||
| ... | @@ -235,25 +382,25 @@ pub const Instruction = struct { | ... | @@ -235,25 +382,25 @@ pub const Instruction = struct { |
| 235 | return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value }); | 382 | return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value }); |
| 236 | } | 383 | } |
| 237 | 384 | ||
| 238 | pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) ?llvm.ValueRef { | 385 | pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { |
| 239 | const value = self.params.return_value.llvm_value; | 386 | const value = self.params.return_value.llvm_value; |
| 240 | const return_type = self.params.return_value.getKnownType(); | 387 | const return_type = self.params.return_value.getKnownType(); |
| 241 | 388 | ||
| 242 | if (return_type.handleIsPtr()) { | 389 | if (return_type.handleIsPtr()) { |
| 243 | @panic("TODO"); | 390 | @panic("TODO"); |
| 244 | } else { | 391 | } else { |
| 245 | _ = llvm.BuildRet(ofile.builder, value); | 392 | _ = llvm.BuildRet(ofile.builder, value) orelse return error.OutOfMemory; |
| 246 | } | 393 | } |
| 247 | return null; | 394 | return null; |
| 248 | } | 395 | } |
| 249 | }; | 396 | }; |
| 250 | 397 | ||
| 251 | pub const Ref = struct { | 398 | pub const Ref = struct { |
| 252 | base: Instruction, | 399 | base: Inst, |
| 253 | params: Params, | 400 | params: Params, |
| 254 | 401 | ||
| 255 | const Params = struct { | 402 | const Params = struct { |
| 256 | target: *Instruction, | 403 | target: *Inst, |
| 257 | mut: Type.Pointer.Mut, | 404 | mut: Type.Pointer.Mut, |
| 258 | volatility: Type.Pointer.Vol, | 405 | volatility: Type.Pointer.Vol, |
| 259 | }; | 406 | }; |
| ... | @@ -266,7 +413,7 @@ pub const Instruction = struct { | ... | @@ -266,7 +413,7 @@ pub const Instruction = struct { |
| 266 | return false; | 413 | return false; |
| 267 | } | 414 | } |
| 268 | 415 | ||
| 269 | pub fn analyze(self: *const Ref, ira: *Analyze) !*Instruction { | 416 | pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst { |
| 270 | const target = try self.params.target.getAsParam(); | 417 | const target = try self.params.target.getAsParam(); |
| 271 | 418 | ||
| 272 | if (ira.getCompTimeValOrNullUndefOk(target)) |val| { | 419 | if (ira.getCompTimeValOrNullUndefOk(target)) |val| { |
| ... | @@ -275,7 +422,6 @@ pub const Instruction = struct { | ... | @@ -275,7 +422,6 @@ pub const Instruction = struct { |
| 275 | Value.Ptr.Mut.CompTimeConst, | 422 | Value.Ptr.Mut.CompTimeConst, |
| 276 | self.params.mut, | 423 | self.params.mut, |
| 277 | self.params.volatility, | 424 | self.params.volatility, |
| 278 | val.typeof.getAbiAlignment(ira.irb.comp), | ||
| 279 | ); | 425 | ); |
| 280 | } | 426 | } |
| 281 | 427 | ||
| ... | @@ -285,14 +431,13 @@ pub const Instruction = struct { | ... | @@ -285,14 +431,13 @@ pub const Instruction = struct { |
| 285 | .volatility = self.params.volatility, | 431 | .volatility = self.params.volatility, |
| 286 | }); | 432 | }); |
| 287 | const elem_type = target.getKnownType(); | 433 | const elem_type = target.getKnownType(); |
| 288 | const ptr_type = Type.Pointer.get( | 434 | const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ |
| 289 | ira.irb.comp, | 435 | .child_type = elem_type, |
| 290 | elem_type, | 436 | .mut = self.params.mut, |
| 291 | self.params.mut, | 437 | .vol = self.params.volatility, |
| 292 | self.params.volatility, | 438 | .size = Type.Pointer.Size.One, |
| 293 | Type.Pointer.Size.One, | 439 | .alignment = Type.Pointer.Align.Abi, |
| 294 | elem_type.getAbiAlignment(ira.irb.comp), | 440 | }) catch unreachable); |
| 295 | ); | ||
| 296 | // TODO: potentially set the hint that this is a stack pointer. But it might not be - this | 441 | // TODO: potentially set the hint that this is a stack pointer. But it might not be - this |
| 297 | // could be a ref of a global, for example | 442 | // could be a ref of a global, for example |
| 298 | new_inst.val = IrVal{ .KnownType = &ptr_type.base }; | 443 | new_inst.val = IrVal{ .KnownType = &ptr_type.base }; |
| ... | @@ -301,8 +446,99 @@ pub const Instruction = struct { | ... | @@ -301,8 +446,99 @@ pub const Instruction = struct { |
| 301 | } | 446 | } |
| 302 | }; | 447 | }; |
| 303 | 448 | ||
| 449 | pub const DeclRef = struct { | ||
| 450 | base: Inst, | ||
| 451 | params: Params, | ||
| 452 | |||
| 453 | const Params = struct { | ||
| 454 | decl: *Decl, | ||
| 455 | lval: LVal, | ||
| 456 | }; | ||
| 457 | |||
| 458 | const ir_val_init = IrVal.Init.Unknown; | ||
| 459 | |||
| 460 | pub fn dump(inst: *const DeclRef) void {} | ||
| 461 | |||
| 462 | pub fn hasSideEffects(inst: *const DeclRef) bool { | ||
| 463 | return false; | ||
| 464 | } | ||
| 465 | |||
| 466 | pub async fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst { | ||
| 467 | (await (async ira.irb.comp.resolveDecl(self.params.decl) catch unreachable)) catch |err| switch (err) { | ||
| 468 | error.OutOfMemory => return error.OutOfMemory, | ||
| 469 | else => return error.SemanticAnalysisFailed, | ||
| 470 | }; | ||
| 471 | switch (self.params.decl.id) { | ||
| 472 | Decl.Id.CompTime => unreachable, | ||
| 473 | Decl.Id.Var => return error.Unimplemented, | ||
| 474 | Decl.Id.Fn => { | ||
| 475 | const fn_decl = @fieldParentPtr(Decl.Fn, "base", self.params.decl); | ||
| 476 | const decl_val = switch (fn_decl.value) { | ||
| 477 | Decl.Fn.Val.Unresolved => unreachable, | ||
| 478 | Decl.Fn.Val.Fn => |fn_val| &fn_val.base, | ||
| 479 | Decl.Fn.Val.FnProto => |fn_proto| &fn_proto.base, | ||
| 480 | }; | ||
| 481 | switch (self.params.lval) { | ||
| 482 | LVal.None => { | ||
| 483 | return ira.irb.buildConstValue(self.base.scope, self.base.span, decl_val); | ||
| 484 | }, | ||
| 485 | LVal.Ptr => return error.Unimplemented, | ||
| 486 | } | ||
| 487 | }, | ||
| 488 | } | ||
| 489 | } | ||
| 490 | }; | ||
| 491 | |||
| 492 | pub const PtrType = struct { | ||
| 493 | base: Inst, | ||
| 494 | params: Params, | ||
| 495 | |||
| 496 | const Params = struct { | ||
| 497 | child_type: *Inst, | ||
| 498 | mut: Type.Pointer.Mut, | ||
| 499 | vol: Type.Pointer.Vol, | ||
| 500 | size: Type.Pointer.Size, | ||
| 501 | alignment: ?*Inst, | ||
| 502 | }; | ||
| 503 | |||
| 504 | const ir_val_init = IrVal.Init.Unknown; | ||
| 505 | |||
| 506 | pub fn dump(inst: *const PtrType) void {} | ||
| 507 | |||
| 508 | pub fn hasSideEffects(inst: *const PtrType) bool { | ||
| 509 | return false; | ||
| 510 | } | ||
| 511 | |||
| 512 | pub async fn analyze(self: *const PtrType, ira: *Analyze) !*Inst { | ||
| 513 | const child_type = try self.params.child_type.getAsConstType(ira); | ||
| 514 | // if (child_type->id == TypeTableEntryIdUnreachable) { | ||
| 515 | // ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed")); | ||
| 516 | // return ira->codegen->builtin_types.entry_invalid; | ||
| 517 | // } else if (child_type->id == TypeTableEntryIdOpaque && instruction->ptr_len == PtrLenUnknown) { | ||
| 518 | // ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque")); | ||
| 519 | // return ira->codegen->builtin_types.entry_invalid; | ||
| 520 | // } | ||
| 521 | const alignment = if (self.params.alignment) |align_inst| blk: { | ||
| 522 | const amt = try align_inst.getAsConstAlign(ira); | ||
| 523 | break :blk Type.Pointer.Align{ .Override = amt }; | ||
| 524 | } else blk: { | ||
| 525 | break :blk Type.Pointer.Align{ .Abi = {} }; | ||
| 526 | }; | ||
| 527 | const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ | ||
| 528 | .child_type = child_type, | ||
| 529 | .mut = self.params.mut, | ||
| 530 | .vol = self.params.vol, | ||
| 531 | .size = self.params.size, | ||
| 532 | .alignment = alignment, | ||
| 533 | }) catch unreachable); | ||
| 534 | ptr_type.base.base.deref(ira.irb.comp); | ||
| 535 | |||
| 536 | return ira.irb.buildConstValue(self.base.scope, self.base.span, &ptr_type.base.base); | ||
| 537 | } | ||
| 538 | }; | ||
| 539 | |||
| 304 | pub const DeclVar = struct { | 540 | pub const DeclVar = struct { |
| 305 | base: Instruction, | 541 | base: Inst, |
| 306 | params: Params, | 542 | params: Params, |
| 307 | 543 | ||
| 308 | const Params = struct { | 544 | const Params = struct { |
| ... | @@ -317,39 +553,46 @@ pub const Instruction = struct { | ... | @@ -317,39 +553,46 @@ pub const Instruction = struct { |
| 317 | return true; | 553 | return true; |
| 318 | } | 554 | } |
| 319 | 555 | ||
| 320 | pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Instruction { | 556 | pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Inst { |
| 321 | return error.Unimplemented; // TODO | 557 | return error.Unimplemented; // TODO |
| 322 | } | 558 | } |
| 323 | }; | 559 | }; |
| 324 | 560 | ||
| 325 | pub const CheckVoidStmt = struct { | 561 | pub const CheckVoidStmt = struct { |
| 326 | base: Instruction, | 562 | base: Inst, |
| 327 | params: Params, | 563 | params: Params, |
| 328 | 564 | ||
| 329 | const Params = struct { | 565 | const Params = struct { |
| 330 | target: *Instruction, | 566 | target: *Inst, |
| 331 | }; | 567 | }; |
| 332 | 568 | ||
| 333 | const ir_val_init = IrVal.Init.Unknown; | 569 | const ir_val_init = IrVal.Init.Unknown; |
| 334 | 570 | ||
| 335 | pub fn dump(inst: *const CheckVoidStmt) void {} | 571 | pub fn dump(self: *const CheckVoidStmt) void { |
| 572 | std.debug.warn("#{}", self.params.target.debug_id); | ||
| 573 | } | ||
| 336 | 574 | ||
| 337 | pub fn hasSideEffects(inst: *const CheckVoidStmt) bool { | 575 | pub fn hasSideEffects(inst: *const CheckVoidStmt) bool { |
| 338 | return true; | 576 | return true; |
| 339 | } | 577 | } |
| 340 | 578 | ||
| 341 | pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Instruction { | 579 | pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Inst { |
| 342 | return error.Unimplemented; // TODO | 580 | const target = try self.params.target.getAsParam(); |
| 581 | if (target.getKnownType().id != Type.Id.Void) { | ||
| 582 | try ira.addCompileError(self.base.span, "expression value is ignored"); | ||
| 583 | return error.SemanticAnalysisFailed; | ||
| 584 | } | ||
| 585 | return ira.irb.buildConstVoid(self.base.scope, self.base.span, true); | ||
| 343 | } | 586 | } |
| 344 | }; | 587 | }; |
| 345 | 588 | ||
| 346 | pub const Phi = struct { | 589 | pub const Phi = struct { |
| 347 | base: Instruction, | 590 | base: Inst, |
| 348 | params: Params, | 591 | params: Params, |
| 349 | 592 | ||
| 350 | const Params = struct { | 593 | const Params = struct { |
| 351 | incoming_blocks: []*BasicBlock, | 594 | incoming_blocks: []*BasicBlock, |
| 352 | incoming_values: []*Instruction, | 595 | incoming_values: []*Inst, |
| 353 | }; | 596 | }; |
| 354 | 597 | ||
| 355 | const ir_val_init = IrVal.Init.Unknown; | 598 | const ir_val_init = IrVal.Init.Unknown; |
| ... | @@ -360,18 +603,18 @@ pub const Instruction = struct { | ... | @@ -360,18 +603,18 @@ pub const Instruction = struct { |
| 360 | return false; | 603 | return false; |
| 361 | } | 604 | } |
| 362 | 605 | ||
| 363 | pub fn analyze(self: *const Phi, ira: *Analyze) !*Instruction { | 606 | pub fn analyze(self: *const Phi, ira: *Analyze) !*Inst { |
| 364 | return error.Unimplemented; // TODO | 607 | return error.Unimplemented; // TODO |
| 365 | } | 608 | } |
| 366 | }; | 609 | }; |
| 367 | 610 | ||
| 368 | pub const Br = struct { | 611 | pub const Br = struct { |
| 369 | base: Instruction, | 612 | base: Inst, |
| 370 | params: Params, | 613 | params: Params, |
| 371 | 614 | ||
| 372 | const Params = struct { | 615 | const Params = struct { |
| 373 | dest_block: *BasicBlock, | 616 | dest_block: *BasicBlock, |
| 374 | is_comptime: *Instruction, | 617 | is_comptime: *Inst, |
| 375 | }; | 618 | }; |
| 376 | 619 | ||
| 377 | const ir_val_init = IrVal.Init.NoReturn; | 620 | const ir_val_init = IrVal.Init.NoReturn; |
| ... | @@ -382,17 +625,41 @@ pub const Instruction = struct { | ... | @@ -382,17 +625,41 @@ pub const Instruction = struct { |
| 382 | return true; | 625 | return true; |
| 383 | } | 626 | } |
| 384 | 627 | ||
| 385 | pub fn analyze(self: *const Br, ira: *Analyze) !*Instruction { | 628 | pub fn analyze(self: *const Br, ira: *Analyze) !*Inst { |
| 629 | return error.Unimplemented; // TODO | ||
| 630 | } | ||
| 631 | }; | ||
| 632 | |||
| 633 | pub const CondBr = struct { | ||
| 634 | base: Inst, | ||
| 635 | params: Params, | ||
| 636 | |||
| 637 | const Params = struct { | ||
| 638 | condition: *Inst, | ||
| 639 | then_block: *BasicBlock, | ||
| 640 | else_block: *BasicBlock, | ||
| 641 | is_comptime: *Inst, | ||
| 642 | }; | ||
| 643 | |||
| 644 | const ir_val_init = IrVal.Init.NoReturn; | ||
| 645 | |||
| 646 | pub fn dump(inst: *const CondBr) void {} | ||
| 647 | |||
| 648 | pub fn hasSideEffects(inst: *const CondBr) bool { | ||
| 649 | return true; | ||
| 650 | } | ||
| 651 | |||
| 652 | pub fn analyze(self: *const CondBr, ira: *Analyze) !*Inst { | ||
| 386 | return error.Unimplemented; // TODO | 653 | return error.Unimplemented; // TODO |
| 387 | } | 654 | } |
| 388 | }; | 655 | }; |
| 389 | 656 | ||
| 390 | pub const AddImplicitReturnType = struct { | 657 | pub const AddImplicitReturnType = struct { |
| 391 | base: Instruction, | 658 | base: Inst, |
| 392 | params: Params, | 659 | params: Params, |
| 393 | 660 | ||
| 394 | pub const Params = struct { | 661 | pub const Params = struct { |
| 395 | target: *Instruction, | 662 | target: *Inst, |
| 396 | }; | 663 | }; |
| 397 | 664 | ||
| 398 | const ir_val_init = IrVal.Init.Unknown; | 665 | const ir_val_init = IrVal.Init.Unknown; |
| ... | @@ -405,12 +672,117 @@ pub const Instruction = struct { | ... | @@ -405,12 +672,117 @@ pub const Instruction = struct { |
| 405 | return true; | 672 | return true; |
| 406 | } | 673 | } |
| 407 | 674 | ||
| 408 | pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction { | 675 | pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Inst { |
| 409 | const target = try self.params.target.getAsParam(); | 676 | const target = try self.params.target.getAsParam(); |
| 410 | try ira.src_implicit_return_type_list.append(target); | 677 | try ira.src_implicit_return_type_list.append(target); |
| 411 | return ira.irb.buildConstVoid(self.base.scope, self.base.span, true); | 678 | return ira.irb.buildConstVoid(self.base.scope, self.base.span, true); |
| 412 | } | 679 | } |
| 413 | }; | 680 | }; |
| 681 | |||
| 682 | pub const TestErr = struct { | ||
| 683 | base: Inst, | ||
| 684 | params: Params, | ||
| 685 | |||
| 686 | pub const Params = struct { | ||
| 687 | target: *Inst, | ||
| 688 | }; | ||
| 689 | |||
| 690 | const ir_val_init = IrVal.Init.Unknown; | ||
| 691 | |||
| 692 | pub fn dump(inst: *const TestErr) void { | ||
| 693 | std.debug.warn("#{}", inst.params.target.debug_id); | ||
| 694 | } | ||
| 695 | |||
| 696 | pub fn hasSideEffects(inst: *const TestErr) bool { | ||
| 697 | return false; | ||
| 698 | } | ||
| 699 | |||
| 700 | pub fn analyze(self: *const TestErr, ira: *Analyze) !*Inst { | ||
| 701 | const target = try self.params.target.getAsParam(); | ||
| 702 | const target_type = target.getKnownType(); | ||
| 703 | switch (target_type.id) { | ||
| 704 | Type.Id.ErrorUnion => { | ||
| 705 | return error.Unimplemented; | ||
| 706 | // if (instr_is_comptime(value)) { | ||
| 707 | // ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad); | ||
| 708 | // if (!err_union_val) | ||
| 709 | // return ira->codegen->builtin_types.entry_invalid; | ||
| 710 | |||
| 711 | // if (err_union_val->special != ConstValSpecialRuntime) { | ||
| 712 | // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | ||
| 713 | // out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr); | ||
| 714 | // return ira->codegen->builtin_types.entry_bool; | ||
| 715 | // } | ||
| 716 | // } | ||
| 717 | |||
| 718 | // TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type; | ||
| 719 | // if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.source_node)) { | ||
| 720 | // return ira->codegen->builtin_types.entry_invalid; | ||
| 721 | // } | ||
| 722 | // if (!type_is_global_error_set(err_set_type) && | ||
| 723 | // err_set_type->data.error_set.err_count == 0) | ||
| 724 | // { | ||
| 725 | // assert(err_set_type->data.error_set.infer_fn == nullptr); | ||
| 726 | // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | ||
| 727 | // out_val->data.x_bool = false; | ||
| 728 | // return ira->codegen->builtin_types.entry_bool; | ||
| 729 | // } | ||
| 730 | |||
| 731 | // ir_build_test_err_from(&ira->new_irb, &instruction->base, value); | ||
| 732 | // return ira->codegen->builtin_types.entry_bool; | ||
| 733 | }, | ||
| 734 | Type.Id.ErrorSet => { | ||
| 735 | return ira.irb.buildConstBool(self.base.scope, self.base.span, true); | ||
| 736 | }, | ||
| 737 | else => { | ||
| 738 | return ira.irb.buildConstBool(self.base.scope, self.base.span, false); | ||
| 739 | }, | ||
| 740 | } | ||
| 741 | } | ||
| 742 | }; | ||
| 743 | |||
| 744 | pub const TestCompTime = struct { | ||
| 745 | base: Inst, | ||
| 746 | params: Params, | ||
| 747 | |||
| 748 | pub const Params = struct { | ||
| 749 | target: *Inst, | ||
| 750 | }; | ||
| 751 | |||
| 752 | const ir_val_init = IrVal.Init.Unknown; | ||
| 753 | |||
| 754 | pub fn dump(inst: *const TestCompTime) void { | ||
| 755 | std.debug.warn("#{}", inst.params.target.debug_id); | ||
| 756 | } | ||
| 757 | |||
| 758 | pub fn hasSideEffects(inst: *const TestCompTime) bool { | ||
| 759 | return false; | ||
| 760 | } | ||
| 761 | |||
| 762 | pub fn analyze(self: *const TestCompTime, ira: *Analyze) !*Inst { | ||
| 763 | const target = try self.params.target.getAsParam(); | ||
| 764 | return ira.irb.buildConstBool(self.base.scope, self.base.span, target.isCompTime()); | ||
| 765 | } | ||
| 766 | }; | ||
| 767 | |||
| 768 | pub const SaveErrRetAddr = struct { | ||
| 769 | base: Inst, | ||
| 770 | params: Params, | ||
| 771 | |||
| 772 | const Params = struct {}; | ||
| 773 | |||
| 774 | const ir_val_init = IrVal.Init.Unknown; | ||
| 775 | |||
| 776 | pub fn dump(inst: *const SaveErrRetAddr) void {} | ||
| 777 | |||
| 778 | pub fn hasSideEffects(inst: *const SaveErrRetAddr) bool { | ||
| 779 | return true; | ||
| 780 | } | ||
| 781 | |||
| 782 | pub fn analyze(self: *const SaveErrRetAddr, ira: *Analyze) !*Inst { | ||
| 783 | return ira.irb.build(Inst.SaveErrRetAddr, self.base.scope, self.base.span, Params{}); | ||
| 784 | } | ||
| 785 | }; | ||
| 414 | }; | 786 | }; |
| 415 | 787 | ||
| 416 | pub const Variable = struct { | 788 | pub const Variable = struct { |
| ... | @@ -422,8 +794,8 @@ pub const BasicBlock = struct { | ... | @@ -422,8 +794,8 @@ pub const BasicBlock = struct { |
| 422 | name_hint: [*]const u8, // must be a C string literal | 794 | name_hint: [*]const u8, // must be a C string literal |
| 423 | debug_id: usize, | 795 | debug_id: usize, |
| 424 | scope: *Scope, | 796 | scope: *Scope, |
| 425 | instruction_list: std.ArrayList(*Instruction), | 797 | instruction_list: std.ArrayList(*Inst), |
| 426 | ref_instruction: ?*Instruction, | 798 | ref_instruction: ?*Inst, |
| 427 | 799 | ||
| 428 | /// for codegen | 800 | /// for codegen |
| 429 | llvm_block: llvm.BasicBlockRef, | 801 | llvm_block: llvm.BasicBlockRef, |
| ... | @@ -435,7 +807,7 @@ pub const BasicBlock = struct { | ... | @@ -435,7 +807,7 @@ pub const BasicBlock = struct { |
| 435 | /// the basic block that this one derives from in analysis | 807 | /// the basic block that this one derives from in analysis |
| 436 | parent: ?*BasicBlock, | 808 | parent: ?*BasicBlock, |
| 437 | 809 | ||
| 438 | pub fn ref(self: *BasicBlock) void { | 810 | pub fn ref(self: *BasicBlock, builder: *Builder) void { |
| 439 | self.ref_count += 1; | 811 | self.ref_count += 1; |
| 440 | } | 812 | } |
| 441 | 813 | ||
| ... | @@ -453,7 +825,7 @@ pub const Code = struct { | ... | @@ -453,7 +825,7 @@ pub const Code = struct { |
| 453 | arena: std.heap.ArenaAllocator, | 825 | arena: std.heap.ArenaAllocator, |
| 454 | return_type: ?*Type, | 826 | return_type: ?*Type, |
| 455 | 827 | ||
| 456 | /// allocator is comp.a() | 828 | /// allocator is comp.gpa() |
| 457 | pub fn destroy(self: *Code, allocator: *Allocator) void { | 829 | pub fn destroy(self: *Code, allocator: *Allocator) void { |
| 458 | self.arena.deinit(); | 830 | self.arena.deinit(); |
| 459 | allocator.destroy(self); | 831 | allocator.destroy(self); |
| ... | @@ -470,6 +842,33 @@ pub const Code = struct { | ... | @@ -470,6 +842,33 @@ pub const Code = struct { |
| 470 | } | 842 | } |
| 471 | } | 843 | } |
| 472 | } | 844 | } |
| 845 | |||
| 846 | /// returns a ref-incremented value, or adds a compile error | ||
| 847 | pub fn getCompTimeResult(self: *Code, comp: *Compilation) !*Value { | ||
| 848 | const bb = self.basic_block_list.at(0); | ||
| 849 | for (bb.instruction_list.toSliceConst()) |inst| { | ||
| 850 | if (inst.cast(Inst.Return)) |ret_inst| { | ||
| 851 | const ret_value = ret_inst.params.return_value; | ||
| 852 | if (ret_value.isCompTime()) { | ||
| 853 | return ret_value.val.KnownValue.getRef(); | ||
| 854 | } | ||
| 855 | try comp.addCompileError( | ||
| 856 | ret_value.scope.findRoot(), | ||
| 857 | ret_value.span, | ||
| 858 | "unable to evaluate constant expression", | ||
| 859 | ); | ||
| 860 | return error.SemanticAnalysisFailed; | ||
| 861 | } else if (inst.hasSideEffects()) { | ||
| 862 | try comp.addCompileError( | ||
| 863 | inst.scope.findRoot(), | ||
| 864 | inst.span, | ||
| 865 | "unable to evaluate constant expression", | ||
| 866 | ); | ||
| 867 | return error.SemanticAnalysisFailed; | ||
| 868 | } | ||
| 869 | } | ||
| 870 | unreachable; | ||
| 871 | } | ||
| 473 | }; | 872 | }; |
| 474 | 873 | ||
| 475 | pub const Builder = struct { | 874 | pub const Builder = struct { |
| ... | @@ -477,32 +876,36 @@ pub const Builder = struct { | ... | @@ -477,32 +876,36 @@ pub const Builder = struct { |
| 477 | code: *Code, | 876 | code: *Code, |
| 478 | current_basic_block: *BasicBlock, | 877 | current_basic_block: *BasicBlock, |
| 479 | next_debug_id: usize, | 878 | next_debug_id: usize, |
| 480 | parsed_file: *ParsedFile, | 879 | root_scope: *Scope.Root, |
| 481 | is_comptime: bool, | 880 | is_comptime: bool, |
| 881 | is_async: bool, | ||
| 882 | begin_scope: ?*Scope, | ||
| 482 | 883 | ||
| 483 | pub const Error = Analyze.Error; | 884 | pub const Error = Analyze.Error; |
| 484 | 885 | ||
| 485 | pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder { | 886 | pub fn init(comp: *Compilation, root_scope: *Scope.Root, begin_scope: ?*Scope) !Builder { |
| 486 | const code = try comp.a().create(Code{ | 887 | const code = try comp.gpa().create(Code{ |
| 487 | .basic_block_list = undefined, | 888 | .basic_block_list = undefined, |
| 488 | .arena = std.heap.ArenaAllocator.init(comp.a()), | 889 | .arena = std.heap.ArenaAllocator.init(comp.gpa()), |
| 489 | .return_type = null, | 890 | .return_type = null, |
| 490 | }); | 891 | }); |
| 491 | code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); | 892 | code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); |
| 492 | errdefer code.destroy(comp.a()); | 893 | errdefer code.destroy(comp.gpa()); |
| 493 | 894 | ||
| 494 | return Builder{ | 895 | return Builder{ |
| 495 | .comp = comp, | 896 | .comp = comp, |
| 496 | .parsed_file = parsed_file, | 897 | .root_scope = root_scope, |
| 497 | .current_basic_block = undefined, | 898 | .current_basic_block = undefined, |
| 498 | .code = code, | 899 | .code = code, |
| 499 | .next_debug_id = 0, | 900 | .next_debug_id = 0, |
| 500 | .is_comptime = false, | 901 | .is_comptime = false, |
| 902 | .is_async = false, | ||
| 903 | .begin_scope = begin_scope, | ||
| 501 | }; | 904 | }; |
| 502 | } | 905 | } |
| 503 | 906 | ||
| 504 | pub fn abort(self: *Builder) void { | 907 | pub fn abort(self: *Builder) void { |
| 505 | self.code.destroy(self.comp.a()); | 908 | self.code.destroy(self.comp.gpa()); |
| 506 | } | 909 | } |
| 507 | 910 | ||
| 508 | /// Call code.destroy() when done | 911 | /// Call code.destroy() when done |
| ... | @@ -517,7 +920,7 @@ pub const Builder = struct { | ... | @@ -517,7 +920,7 @@ pub const Builder = struct { |
| 517 | .name_hint = name_hint, | 920 | .name_hint = name_hint, |
| 518 | .debug_id = self.next_debug_id, | 921 | .debug_id = self.next_debug_id, |
| 519 | .scope = scope, | 922 | .scope = scope, |
| 520 | .instruction_list = std.ArrayList(*Instruction).init(self.arena()), | 923 | .instruction_list = std.ArrayList(*Inst).init(self.arena()), |
| 521 | .child = null, | 924 | .child = null, |
| 522 | .parent = null, | 925 | .parent = null, |
| 523 | .ref_instruction = null, | 926 | .ref_instruction = null, |
| ... | @@ -537,67 +940,208 @@ pub const Builder = struct { | ... | @@ -537,67 +940,208 @@ pub const Builder = struct { |
| 537 | self.current_basic_block = basic_block; | 940 | self.current_basic_block = basic_block; |
| 538 | } | 941 | } |
| 539 | 942 | ||
| 540 | pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Instruction { | 943 | pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst { |
| 541 | switch (node.id) { | 944 | switch (node.id) { |
| 542 | ast.Node.Id.Root => unreachable, | 945 | ast.Node.Id.Root => unreachable, |
| 543 | ast.Node.Id.Use => unreachable, | 946 | ast.Node.Id.Use => unreachable, |
| 544 | ast.Node.Id.TestDecl => unreachable, | 947 | ast.Node.Id.TestDecl => unreachable, |
| 545 | ast.Node.Id.VarDecl => @panic("TODO"), | 948 | ast.Node.Id.VarDecl => return error.Unimplemented, |
| 546 | ast.Node.Id.Defer => @panic("TODO"), | 949 | ast.Node.Id.Defer => return error.Unimplemented, |
| 547 | ast.Node.Id.InfixOp => @panic("TODO"), | 950 | ast.Node.Id.InfixOp => return error.Unimplemented, |
| 548 | ast.Node.Id.PrefixOp => @panic("TODO"), | 951 | ast.Node.Id.PrefixOp => { |
| 549 | ast.Node.Id.SuffixOp => @panic("TODO"), | 952 | const prefix_op = @fieldParentPtr(ast.Node.PrefixOp, "base", node); |
| 550 | ast.Node.Id.Switch => @panic("TODO"), | 953 | switch (prefix_op.op) { |
| 551 | ast.Node.Id.While => @panic("TODO"), | 954 | ast.Node.PrefixOp.Op.AddressOf => return error.Unimplemented, |
| 552 | ast.Node.Id.For => @panic("TODO"), | 955 | ast.Node.PrefixOp.Op.ArrayType => |n| return error.Unimplemented, |
| 553 | ast.Node.Id.If => @panic("TODO"), | 956 | ast.Node.PrefixOp.Op.Await => return error.Unimplemented, |
| 554 | ast.Node.Id.ControlFlowExpression => return error.Unimplemented, | 957 | ast.Node.PrefixOp.Op.BitNot => return error.Unimplemented, |
| 555 | ast.Node.Id.Suspend => @panic("TODO"), | 958 | ast.Node.PrefixOp.Op.BoolNot => return error.Unimplemented, |
| 556 | ast.Node.Id.VarType => @panic("TODO"), | 959 | ast.Node.PrefixOp.Op.Cancel => return error.Unimplemented, |
| 557 | ast.Node.Id.ErrorType => @panic("TODO"), | 960 | ast.Node.PrefixOp.Op.OptionalType => return error.Unimplemented, |
| 558 | ast.Node.Id.FnProto => @panic("TODO"), | 961 | ast.Node.PrefixOp.Op.Negation => return error.Unimplemented, |
| 559 | ast.Node.Id.PromiseType => @panic("TODO"), | 962 | ast.Node.PrefixOp.Op.NegationWrap => return error.Unimplemented, |
| 560 | ast.Node.Id.IntegerLiteral => @panic("TODO"), | 963 | ast.Node.PrefixOp.Op.Resume => return error.Unimplemented, |
| 561 | ast.Node.Id.FloatLiteral => @panic("TODO"), | 964 | ast.Node.PrefixOp.Op.PtrType => |ptr_info| { |
| 562 | ast.Node.Id.StringLiteral => @panic("TODO"), | 965 | const inst = try await (async irb.genPtrType(prefix_op, ptr_info, scope) catch unreachable); |
| 563 | ast.Node.Id.MultilineStringLiteral => @panic("TODO"), | 966 | return irb.lvalWrap(scope, inst, lval); |
| 564 | ast.Node.Id.CharLiteral => @panic("TODO"), | 967 | }, |
| 565 | ast.Node.Id.BoolLiteral => @panic("TODO"), | 968 | ast.Node.PrefixOp.Op.SliceType => |ptr_info| return error.Unimplemented, |
| 566 | ast.Node.Id.NullLiteral => @panic("TODO"), | 969 | ast.Node.PrefixOp.Op.Try => return error.Unimplemented, |
| 567 | ast.Node.Id.UndefinedLiteral => @panic("TODO"), | 970 | } |
| 568 | ast.Node.Id.ThisLiteral => @panic("TODO"), | 971 | }, |
| 569 | ast.Node.Id.Unreachable => @panic("TODO"), | 972 | ast.Node.Id.SuffixOp => { |
| 570 | ast.Node.Id.Identifier => @panic("TODO"), | 973 | const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); |
| 974 | switch (suffix_op.op) { | ||
| 975 | @TagType(ast.Node.SuffixOp.Op).Call => |*call| { | ||
| 976 | const inst = try await (async irb.genCall(suffix_op, call, scope) catch unreachable); | ||
| 977 | return irb.lvalWrap(scope, inst, lval); | ||
| 978 | }, | ||
| 979 | @TagType(ast.Node.SuffixOp.Op).ArrayAccess => |n| return error.Unimplemented, | ||
| 980 | @TagType(ast.Node.SuffixOp.Op).Slice => |slice| return error.Unimplemented, | ||
| 981 | @TagType(ast.Node.SuffixOp.Op).ArrayInitializer => |init_list| return error.Unimplemented, | ||
| 982 | @TagType(ast.Node.SuffixOp.Op).StructInitializer => |init_list| return error.Unimplemented, | ||
| 983 | @TagType(ast.Node.SuffixOp.Op).Deref => return error.Unimplemented, | ||
| 984 | @TagType(ast.Node.SuffixOp.Op).UnwrapOptional => return error.Unimplemented, | ||
| 985 | } | ||
| 986 | }, | ||
| 987 | ast.Node.Id.Switch => return error.Unimplemented, | ||
| 988 | ast.Node.Id.While => return error.Unimplemented, | ||
| 989 | ast.Node.Id.For => return error.Unimplemented, | ||
| 990 | ast.Node.Id.If => return error.Unimplemented, | ||
| 991 | ast.Node.Id.ControlFlowExpression => { | ||
| 992 | const control_flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", node); | ||
| 993 | return await (async irb.genControlFlowExpr(control_flow_expr, scope, lval) catch unreachable); | ||
| 994 | }, | ||
| 995 | ast.Node.Id.Suspend => return error.Unimplemented, | ||
| 996 | ast.Node.Id.VarType => return error.Unimplemented, | ||
| 997 | ast.Node.Id.ErrorType => return error.Unimplemented, | ||
| 998 | ast.Node.Id.FnProto => return error.Unimplemented, | ||
| 999 | ast.Node.Id.PromiseType => return error.Unimplemented, | ||
| 1000 | ast.Node.Id.IntegerLiteral => { | ||
| 1001 | const int_lit = @fieldParentPtr(ast.Node.IntegerLiteral, "base", node); | ||
| 1002 | return irb.lvalWrap(scope, try irb.genIntLit(int_lit, scope), lval); | ||
| 1003 | }, | ||
| 1004 | ast.Node.Id.FloatLiteral => return error.Unimplemented, | ||
| 1005 | ast.Node.Id.StringLiteral => { | ||
| 1006 | const str_lit = @fieldParentPtr(ast.Node.StringLiteral, "base", node); | ||
| 1007 | const inst = try await (async irb.genStrLit(str_lit, scope) catch unreachable); | ||
| 1008 | return irb.lvalWrap(scope, inst, lval); | ||
| 1009 | }, | ||
| 1010 | ast.Node.Id.MultilineStringLiteral => return error.Unimplemented, | ||
| 1011 | ast.Node.Id.CharLiteral => return error.Unimplemented, | ||
| 1012 | ast.Node.Id.BoolLiteral => return error.Unimplemented, | ||
| 1013 | ast.Node.Id.NullLiteral => return error.Unimplemented, | ||
| 1014 | ast.Node.Id.UndefinedLiteral => return error.Unimplemented, | ||
| 1015 | ast.Node.Id.ThisLiteral => return error.Unimplemented, | ||
| 1016 | ast.Node.Id.Unreachable => return error.Unimplemented, | ||
| 1017 | ast.Node.Id.Identifier => { | ||
| 1018 | const identifier = @fieldParentPtr(ast.Node.Identifier, "base", node); | ||
| 1019 | return await (async irb.genIdentifier(identifier, scope, lval) catch unreachable); | ||
| 1020 | }, | ||
| 571 | ast.Node.Id.GroupedExpression => { | 1021 | ast.Node.Id.GroupedExpression => { |
| 572 | const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node); | 1022 | const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node); |
| 573 | return irb.genNode(grouped_expr.expr, scope, lval); | 1023 | return await (async irb.genNode(grouped_expr.expr, scope, lval) catch unreachable); |
| 574 | }, | 1024 | }, |
| 575 | ast.Node.Id.BuiltinCall => @panic("TODO"), | 1025 | ast.Node.Id.BuiltinCall => return error.Unimplemented, |
| 576 | ast.Node.Id.ErrorSetDecl => @panic("TODO"), | 1026 | ast.Node.Id.ErrorSetDecl => return error.Unimplemented, |
| 577 | ast.Node.Id.ContainerDecl => @panic("TODO"), | 1027 | ast.Node.Id.ContainerDecl => return error.Unimplemented, |
| 578 | ast.Node.Id.Asm => @panic("TODO"), | 1028 | ast.Node.Id.Asm => return error.Unimplemented, |
| 579 | ast.Node.Id.Comptime => @panic("TODO"), | 1029 | ast.Node.Id.Comptime => return error.Unimplemented, |
| 580 | ast.Node.Id.Block => { | 1030 | ast.Node.Id.Block => { |
| 581 | const block = @fieldParentPtr(ast.Node.Block, "base", node); | 1031 | const block = @fieldParentPtr(ast.Node.Block, "base", node); |
| 582 | return irb.lvalWrap(scope, try irb.genBlock(block, scope), lval); | 1032 | const inst = try await (async irb.genBlock(block, scope) catch unreachable); |
| 1033 | return irb.lvalWrap(scope, inst, lval); | ||
| 583 | }, | 1034 | }, |
| 584 | ast.Node.Id.DocComment => @panic("TODO"), | 1035 | ast.Node.Id.DocComment => return error.Unimplemented, |
| 585 | ast.Node.Id.SwitchCase => @panic("TODO"), | 1036 | ast.Node.Id.SwitchCase => return error.Unimplemented, |
| 586 | ast.Node.Id.SwitchElse => @panic("TODO"), | 1037 | ast.Node.Id.SwitchElse => return error.Unimplemented, |
| 587 | ast.Node.Id.Else => @panic("TODO"), | 1038 | ast.Node.Id.Else => return error.Unimplemented, |
| 588 | ast.Node.Id.Payload => @panic("TODO"), | 1039 | ast.Node.Id.Payload => return error.Unimplemented, |
| 589 | ast.Node.Id.PointerPayload => @panic("TODO"), | 1040 | ast.Node.Id.PointerPayload => return error.Unimplemented, |
| 590 | ast.Node.Id.PointerIndexPayload => @panic("TODO"), | 1041 | ast.Node.Id.PointerIndexPayload => return error.Unimplemented, |
| 591 | ast.Node.Id.StructField => @panic("TODO"), | 1042 | ast.Node.Id.StructField => return error.Unimplemented, |
| 592 | ast.Node.Id.UnionTag => @panic("TODO"), | 1043 | ast.Node.Id.UnionTag => return error.Unimplemented, |
| 593 | ast.Node.Id.EnumTag => @panic("TODO"), | 1044 | ast.Node.Id.EnumTag => return error.Unimplemented, |
| 594 | ast.Node.Id.ErrorTag => @panic("TODO"), | 1045 | ast.Node.Id.ErrorTag => return error.Unimplemented, |
| 595 | ast.Node.Id.AsmInput => @panic("TODO"), | 1046 | ast.Node.Id.AsmInput => return error.Unimplemented, |
| 596 | ast.Node.Id.AsmOutput => @panic("TODO"), | 1047 | ast.Node.Id.AsmOutput => return error.Unimplemented, |
| 597 | ast.Node.Id.AsyncAttribute => @panic("TODO"), | 1048 | ast.Node.Id.AsyncAttribute => return error.Unimplemented, |
| 598 | ast.Node.Id.ParamDecl => @panic("TODO"), | 1049 | ast.Node.Id.ParamDecl => return error.Unimplemented, |
| 599 | ast.Node.Id.FieldInitializer => @panic("TODO"), | 1050 | ast.Node.Id.FieldInitializer => return error.Unimplemented, |
| 1051 | } | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst { | ||
| 1055 | const fn_ref = try await (async irb.genNode(suffix_op.lhs, scope, LVal.None) catch unreachable); | ||
| 1056 | |||
| 1057 | const args = try irb.arena().alloc(*Inst, call.params.len); | ||
| 1058 | var it = call.params.iterator(0); | ||
| 1059 | var i: usize = 0; | ||
| 1060 | while (it.next()) |arg_node_ptr| : (i += 1) { | ||
| 1061 | args[i] = try await (async irb.genNode(arg_node_ptr.*, scope, LVal.None) catch unreachable); | ||
| 600 | } | 1062 | } |
| 1063 | |||
| 1064 | //bool is_async = node->data.fn_call_expr.is_async; | ||
| 1065 | //IrInstruction *async_allocator = nullptr; | ||
| 1066 | //if (is_async) { | ||
| 1067 | // if (node->data.fn_call_expr.async_allocator) { | ||
| 1068 | // async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope); | ||
| 1069 | // if (async_allocator == irb->codegen->invalid_instruction) | ||
| 1070 | // return async_allocator; | ||
| 1071 | // } | ||
| 1072 | //} | ||
| 1073 | |||
| 1074 | return irb.build(Inst.Call, scope, Span.token(suffix_op.rtoken), Inst.Call.Params{ | ||
| 1075 | .fn_ref = fn_ref, | ||
| 1076 | .args = args, | ||
| 1077 | }); | ||
| 1078 | //IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); | ||
| 1079 | //return ir_lval_wrap(irb, scope, fn_call, lval); | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | async fn genPtrType( | ||
| 1083 | irb: *Builder, | ||
| 1084 | prefix_op: *ast.Node.PrefixOp, | ||
| 1085 | ptr_info: ast.Node.PrefixOp.PtrInfo, | ||
| 1086 | scope: *Scope, | ||
| 1087 | ) !*Inst { | ||
| 1088 | // TODO port more logic | ||
| 1089 | |||
| 1090 | //assert(node->type == NodeTypePointerType); | ||
| 1091 | //PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar || | ||
| 1092 | // node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown; | ||
| 1093 | //bool is_const = node->data.pointer_type.is_const; | ||
| 1094 | //bool is_volatile = node->data.pointer_type.is_volatile; | ||
| 1095 | //AstNode *expr_node = node->data.pointer_type.op_expr; | ||
| 1096 | //AstNode *align_expr = node->data.pointer_type.align_expr; | ||
| 1097 | |||
| 1098 | //IrInstruction *align_value; | ||
| 1099 | //if (align_expr != nullptr) { | ||
| 1100 | // align_value = ir_gen_node(irb, align_expr, scope); | ||
| 1101 | // if (align_value == irb->codegen->invalid_instruction) | ||
| 1102 | // return align_value; | ||
| 1103 | //} else { | ||
| 1104 | // align_value = nullptr; | ||
| 1105 | //} | ||
| 1106 | const child_type = try await (async irb.genNode(prefix_op.rhs, scope, LVal.None) catch unreachable); | ||
| 1107 | |||
| 1108 | //uint32_t bit_offset_start = 0; | ||
| 1109 | //if (node->data.pointer_type.bit_offset_start != nullptr) { | ||
| 1110 | // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) { | ||
| 1111 | // Buf *val_buf = buf_alloc(); | ||
| 1112 | // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10); | ||
| 1113 | // exec_add_error_node(irb->codegen, irb->exec, node, | ||
| 1114 | // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); | ||
| 1115 | // return irb->codegen->invalid_instruction; | ||
| 1116 | // } | ||
| 1117 | // bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start); | ||
| 1118 | //} | ||
| 1119 | |||
| 1120 | //uint32_t bit_offset_end = 0; | ||
| 1121 | //if (node->data.pointer_type.bit_offset_end != nullptr) { | ||
| 1122 | // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_end, 32, false)) { | ||
| 1123 | // Buf *val_buf = buf_alloc(); | ||
| 1124 | // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_end, 10); | ||
| 1125 | // exec_add_error_node(irb->codegen, irb->exec, node, | ||
| 1126 | // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); | ||
| 1127 | // return irb->codegen->invalid_instruction; | ||
| 1128 | // } | ||
| 1129 | // bit_offset_end = bigint_as_unsigned(node->data.pointer_type.bit_offset_end); | ||
| 1130 | //} | ||
| 1131 | |||
| 1132 | //if ((bit_offset_start != 0 || bit_offset_end != 0) && bit_offset_start >= bit_offset_end) { | ||
| 1133 | // exec_add_error_node(irb->codegen, irb->exec, node, | ||
| 1134 | // buf_sprintf("bit offset start must be less than bit offset end")); | ||
| 1135 | // return irb->codegen->invalid_instruction; | ||
| 1136 | //} | ||
| 1137 | |||
| 1138 | return irb.build(Inst.PtrType, scope, Span.node(&prefix_op.base), Inst.PtrType.Params{ | ||
| 1139 | .child_type = child_type, | ||
| 1140 | .mut = Type.Pointer.Mut.Mut, | ||
| 1141 | .vol = Type.Pointer.Vol.Non, | ||
| 1142 | .size = Type.Pointer.Size.Many, | ||
| 1143 | .alignment = null, | ||
| 1144 | }); | ||
| 601 | } | 1145 | } |
| 602 | 1146 | ||
| 603 | fn isCompTime(irb: *Builder, target_scope: *Scope) bool { | 1147 | fn isCompTime(irb: *Builder, target_scope: *Scope) bool { |
| ... | @@ -610,15 +1154,105 @@ pub const Builder = struct { | ... | @@ -610,15 +1154,105 @@ pub const Builder = struct { |
| 610 | Scope.Id.CompTime => return true, | 1154 | Scope.Id.CompTime => return true, |
| 611 | Scope.Id.FnDef => return false, | 1155 | Scope.Id.FnDef => return false, |
| 612 | Scope.Id.Decls => unreachable, | 1156 | Scope.Id.Decls => unreachable, |
| 1157 | Scope.Id.Root => unreachable, | ||
| 613 | Scope.Id.Block, | 1158 | Scope.Id.Block, |
| 614 | Scope.Id.Defer, | 1159 | Scope.Id.Defer, |
| 615 | Scope.Id.DeferExpr, | 1160 | Scope.Id.DeferExpr, |
| 616 | => scope = scope.parent orelse return false, | 1161 | => scope = scope.parent.?, |
| 617 | } | 1162 | } |
| 618 | } | 1163 | } |
| 619 | } | 1164 | } |
| 620 | 1165 | ||
| 621 | pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Instruction { | 1166 | pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst { |
| 1167 | const int_token = irb.root_scope.tree.tokenSlice(int_lit.token); | ||
| 1168 | |||
| 1169 | var base: u8 = undefined; | ||
| 1170 | var rest: []const u8 = undefined; | ||
| 1171 | if (int_token.len >= 3 and int_token[0] == '0') { | ||
| 1172 | base = switch (int_token[1]) { | ||
| 1173 | 'b' => u8(2), | ||
| 1174 | 'o' => u8(8), | ||
| 1175 | 'x' => u8(16), | ||
| 1176 | else => unreachable, | ||
| 1177 | }; | ||
| 1178 | rest = int_token[2..]; | ||
| 1179 | } else { | ||
| 1180 | base = 10; | ||
| 1181 | rest = int_token; | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | const comptime_int_type = Type.ComptimeInt.get(irb.comp); | ||
| 1185 | defer comptime_int_type.base.base.deref(irb.comp); | ||
| 1186 | |||
| 1187 | const int_val = Value.Int.createFromString( | ||
| 1188 | irb.comp, | ||
| 1189 | &comptime_int_type.base, | ||
| 1190 | base, | ||
| 1191 | rest, | ||
| 1192 | ) catch |err| switch (err) { | ||
| 1193 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1194 | error.InvalidBase => unreachable, | ||
| 1195 | error.InvalidCharForDigit => unreachable, | ||
| 1196 | error.DigitTooLargeForBase => unreachable, | ||
| 1197 | }; | ||
| 1198 | errdefer int_val.base.deref(irb.comp); | ||
| 1199 | |||
| 1200 | const inst = try irb.build(Inst.Const, scope, Span.token(int_lit.token), Inst.Const.Params{}); | ||
| 1201 | inst.val = IrVal{ .KnownValue = &int_val.base }; | ||
| 1202 | return inst; | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst { | ||
| 1206 | const str_token = irb.root_scope.tree.tokenSlice(str_lit.token); | ||
| 1207 | const src_span = Span.token(str_lit.token); | ||
| 1208 | |||
| 1209 | var bad_index: usize = undefined; | ||
| 1210 | var buf = std.zig.parseStringLiteral(irb.comp.gpa(), str_token, &bad_index) catch |err| switch (err) { | ||
| 1211 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1212 | error.InvalidCharacter => { | ||
| 1213 | try irb.comp.addCompileError( | ||
| 1214 | irb.root_scope, | ||
| 1215 | src_span, | ||
| 1216 | "invalid character in string literal: '{c}'", | ||
| 1217 | str_token[bad_index], | ||
| 1218 | ); | ||
| 1219 | return error.SemanticAnalysisFailed; | ||
| 1220 | }, | ||
| 1221 | }; | ||
| 1222 | var buf_cleaned = false; | ||
| 1223 | errdefer if (!buf_cleaned) irb.comp.gpa().free(buf); | ||
| 1224 | |||
| 1225 | if (str_token[0] == 'c') { | ||
| 1226 | // first we add a null | ||
| 1227 | buf = try irb.comp.gpa().realloc(u8, buf, buf.len + 1); | ||
| 1228 | buf[buf.len - 1] = 0; | ||
| 1229 | |||
| 1230 | // next make an array value | ||
| 1231 | const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable); | ||
| 1232 | buf_cleaned = true; | ||
| 1233 | defer array_val.base.deref(irb.comp); | ||
| 1234 | |||
| 1235 | // then make a pointer value pointing at the first element | ||
| 1236 | const ptr_val = try await (async Value.Ptr.createArrayElemPtr( | ||
| 1237 | irb.comp, | ||
| 1238 | array_val, | ||
| 1239 | Type.Pointer.Mut.Const, | ||
| 1240 | Type.Pointer.Size.Many, | ||
| 1241 | 0, | ||
| 1242 | ) catch unreachable); | ||
| 1243 | defer ptr_val.base.deref(irb.comp); | ||
| 1244 | |||
| 1245 | return irb.buildConstValue(scope, src_span, &ptr_val.base); | ||
| 1246 | } else { | ||
| 1247 | const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable); | ||
| 1248 | buf_cleaned = true; | ||
| 1249 | defer array_val.base.deref(irb.comp); | ||
| 1250 | |||
| 1251 | return irb.buildConstValue(scope, src_span, &array_val.base); | ||
| 1252 | } | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst { | ||
| 622 | const block_scope = try Scope.Block.create(irb.comp, parent_scope); | 1256 | const block_scope = try Scope.Block.create(irb.comp, parent_scope); |
| 623 | 1257 | ||
| 624 | const outer_block_scope = &block_scope.base; | 1258 | const outer_block_scope = &block_scope.base; |
| ... | @@ -636,7 +1270,7 @@ pub const Builder = struct { | ... | @@ -636,7 +1270,7 @@ pub const Builder = struct { |
| 636 | } | 1270 | } |
| 637 | 1271 | ||
| 638 | if (block.label) |label| { | 1272 | if (block.label) |label| { |
| 639 | block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena()); | 1273 | block_scope.incoming_values = std.ArrayList(*Inst).init(irb.arena()); |
| 640 | block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); | 1274 | block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); |
| 641 | block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd"); | 1275 | block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd"); |
| 642 | block_scope.is_comptime = try irb.buildConstBool( | 1276 | block_scope.is_comptime = try irb.buildConstBool( |
| ... | @@ -647,7 +1281,7 @@ pub const Builder = struct { | ... | @@ -647,7 +1281,7 @@ pub const Builder = struct { |
| 647 | } | 1281 | } |
| 648 | 1282 | ||
| 649 | var is_continuation_unreachable = false; | 1283 | var is_continuation_unreachable = false; |
| 650 | var noreturn_return_value: ?*Instruction = null; | 1284 | var noreturn_return_value: ?*Inst = null; |
| 651 | 1285 | ||
| 652 | var stmt_it = block.statements.iterator(0); | 1286 | var stmt_it = block.statements.iterator(0); |
| 653 | while (stmt_it.next()) |statement_node_ptr| { | 1287 | while (stmt_it.next()) |statement_node_ptr| { |
| ... | @@ -655,7 +1289,7 @@ pub const Builder = struct { | ... | @@ -655,7 +1289,7 @@ pub const Builder = struct { |
| 655 | 1289 | ||
| 656 | if (statement_node.cast(ast.Node.Defer)) |defer_node| { | 1290 | if (statement_node.cast(ast.Node.Defer)) |defer_node| { |
| 657 | // defer starts a new scope | 1291 | // defer starts a new scope |
| 658 | const defer_token = irb.parsed_file.tree.tokens.at(defer_node.defer_token); | 1292 | const defer_token = irb.root_scope.tree.tokens.at(defer_node.defer_token); |
| 659 | const kind = switch (defer_token.id) { | 1293 | const kind = switch (defer_token.id) { |
| 660 | Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit, | 1294 | Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit, |
| 661 | Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit, | 1295 | Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit, |
| ... | @@ -666,7 +1300,7 @@ pub const Builder = struct { | ... | @@ -666,7 +1300,7 @@ pub const Builder = struct { |
| 666 | child_scope = &defer_child_scope.base; | 1300 | child_scope = &defer_child_scope.base; |
| 667 | continue; | 1301 | continue; |
| 668 | } | 1302 | } |
| 669 | const statement_value = try irb.genNode(statement_node, child_scope, LVal.None); | 1303 | const statement_value = try await (async irb.genNode(statement_node, child_scope, LVal.None) catch unreachable); |
| 670 | 1304 | ||
| 671 | is_continuation_unreachable = statement_value.isNoReturn(); | 1305 | is_continuation_unreachable = statement_value.isNoReturn(); |
| 672 | if (is_continuation_unreachable) { | 1306 | if (is_continuation_unreachable) { |
| ... | @@ -674,16 +1308,19 @@ pub const Builder = struct { | ... | @@ -674,16 +1308,19 @@ pub const Builder = struct { |
| 674 | noreturn_return_value = statement_value; | 1308 | noreturn_return_value = statement_value; |
| 675 | } | 1309 | } |
| 676 | 1310 | ||
| 677 | if (statement_value.cast(Instruction.DeclVar)) |decl_var| { | 1311 | if (statement_value.cast(Inst.DeclVar)) |decl_var| { |
| 678 | // variable declarations start a new scope | 1312 | // variable declarations start a new scope |
| 679 | child_scope = decl_var.params.variable.child_scope; | 1313 | child_scope = decl_var.params.variable.child_scope; |
| 680 | } else if (!is_continuation_unreachable) { | 1314 | } else if (!is_continuation_unreachable) { |
| 681 | // this statement's value must be void | 1315 | // this statement's value must be void |
| 682 | _ = irb.build( | 1316 | _ = irb.build( |
| 683 | Instruction.CheckVoidStmt, | 1317 | Inst.CheckVoidStmt, |
| 684 | child_scope, | 1318 | child_scope, |
| 685 | statement_value.span, | 1319 | Span{ |
| 686 | Instruction.CheckVoidStmt.Params{ .target = statement_value }, | 1320 | .first = statement_node.firstToken(), |
| 1321 | .last = statement_node.lastToken(), | ||
| 1322 | }, | ||
| 1323 | Inst.CheckVoidStmt.Params{ .target = statement_value }, | ||
| 687 | ); | 1324 | ); |
| 688 | } | 1325 | } |
| 689 | } | 1326 | } |
| ... | @@ -695,7 +1332,7 @@ pub const Builder = struct { | ... | @@ -695,7 +1332,7 @@ pub const Builder = struct { |
| 695 | } | 1332 | } |
| 696 | 1333 | ||
| 697 | try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); | 1334 | try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); |
| 698 | return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{ | 1335 | return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{ |
| 699 | .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(), | 1336 | .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(), |
| 700 | .incoming_values = block_scope.incoming_values.toOwnedSlice(), | 1337 | .incoming_values = block_scope.incoming_values.toOwnedSlice(), |
| 701 | }); | 1338 | }); |
| ... | @@ -706,26 +1343,216 @@ pub const Builder = struct { | ... | @@ -706,26 +1343,216 @@ pub const Builder = struct { |
| 706 | try block_scope.incoming_values.append( | 1343 | try block_scope.incoming_values.append( |
| 707 | try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true), | 1344 | try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true), |
| 708 | ); | 1345 | ); |
| 709 | _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); | 1346 | _ = try await (async irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); |
| 710 | 1347 | ||
| 711 | _ = try irb.buildGen(Instruction.Br, parent_scope, Span.token(block.rbrace), Instruction.Br.Params{ | 1348 | _ = try irb.buildGen(Inst.Br, parent_scope, Span.token(block.rbrace), Inst.Br.Params{ |
| 712 | .dest_block = block_scope.end_block, | 1349 | .dest_block = block_scope.end_block, |
| 713 | .is_comptime = block_scope.is_comptime, | 1350 | .is_comptime = block_scope.is_comptime, |
| 714 | }); | 1351 | }); |
| 715 | 1352 | ||
| 716 | try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); | 1353 | try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); |
| 717 | 1354 | ||
| 718 | return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{ | 1355 | return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{ |
| 719 | .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(), | 1356 | .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(), |
| 720 | .incoming_values = block_scope.incoming_values.toOwnedSlice(), | 1357 | .incoming_values = block_scope.incoming_values.toOwnedSlice(), |
| 721 | }); | 1358 | }); |
| 722 | } | 1359 | } |
| 723 | 1360 | ||
| 724 | _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); | 1361 | _ = try await (async irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); |
| 725 | return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true); | 1362 | return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true); |
| 726 | } | 1363 | } |
| 727 | 1364 | ||
| 728 | fn genDefersForBlock( | 1365 | pub async fn genControlFlowExpr( |
| 1366 | irb: *Builder, | ||
| 1367 | control_flow_expr: *ast.Node.ControlFlowExpression, | ||
| 1368 | scope: *Scope, | ||
| 1369 | lval: LVal, | ||
| 1370 | ) !*Inst { | ||
| 1371 | switch (control_flow_expr.kind) { | ||
| 1372 | ast.Node.ControlFlowExpression.Kind.Break => |arg| return error.Unimplemented, | ||
| 1373 | ast.Node.ControlFlowExpression.Kind.Continue => |arg| return error.Unimplemented, | ||
| 1374 | ast.Node.ControlFlowExpression.Kind.Return => { | ||
| 1375 | const src_span = Span.token(control_flow_expr.ltoken); | ||
| 1376 | if (scope.findFnDef() == null) { | ||
| 1377 | try irb.comp.addCompileError( | ||
| 1378 | irb.root_scope, | ||
| 1379 | src_span, | ||
| 1380 | "return expression outside function definition", | ||
| 1381 | ); | ||
| 1382 | return error.SemanticAnalysisFailed; | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | if (scope.findDeferExpr()) |scope_defer_expr| { | ||
| 1386 | if (!scope_defer_expr.reported_err) { | ||
| 1387 | try irb.comp.addCompileError( | ||
| 1388 | irb.root_scope, | ||
| 1389 | src_span, | ||
| 1390 | "cannot return from defer expression", | ||
| 1391 | ); | ||
| 1392 | scope_defer_expr.reported_err = true; | ||
| 1393 | } | ||
| 1394 | return error.SemanticAnalysisFailed; | ||
| 1395 | } | ||
| 1396 | |||
| 1397 | const outer_scope = irb.begin_scope.?; | ||
| 1398 | const return_value = if (control_flow_expr.rhs) |rhs| blk: { | ||
| 1399 | break :blk try await (async irb.genNode(rhs, scope, LVal.None) catch unreachable); | ||
| 1400 | } else blk: { | ||
| 1401 | break :blk try irb.buildConstVoid(scope, src_span, true); | ||
| 1402 | }; | ||
| 1403 | |||
| 1404 | const defer_counts = irb.countDefers(scope, outer_scope); | ||
| 1405 | const have_err_defers = defer_counts.error_exit != 0; | ||
| 1406 | if (have_err_defers or irb.comp.have_err_ret_tracing) { | ||
| 1407 | const err_block = try irb.createBasicBlock(scope, c"ErrRetErr"); | ||
| 1408 | const ok_block = try irb.createBasicBlock(scope, c"ErrRetOk"); | ||
| 1409 | if (!have_err_defers) { | ||
| 1410 | _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | const is_err = try irb.build( | ||
| 1414 | Inst.TestErr, | ||
| 1415 | scope, | ||
| 1416 | src_span, | ||
| 1417 | Inst.TestErr.Params{ .target = return_value }, | ||
| 1418 | ); | ||
| 1419 | |||
| 1420 | const err_is_comptime = try irb.buildTestCompTime(scope, src_span, is_err); | ||
| 1421 | |||
| 1422 | _ = try irb.buildGen(Inst.CondBr, scope, src_span, Inst.CondBr.Params{ | ||
| 1423 | .condition = is_err, | ||
| 1424 | .then_block = err_block, | ||
| 1425 | .else_block = ok_block, | ||
| 1426 | .is_comptime = err_is_comptime, | ||
| 1427 | }); | ||
| 1428 | |||
| 1429 | const ret_stmt_block = try irb.createBasicBlock(scope, c"RetStmt"); | ||
| 1430 | |||
| 1431 | try irb.setCursorAtEndAndAppendBlock(err_block); | ||
| 1432 | if (have_err_defers) { | ||
| 1433 | _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ErrorExit) catch unreachable); | ||
| 1434 | } | ||
| 1435 | if (irb.comp.have_err_ret_tracing and !irb.isCompTime(scope)) { | ||
| 1436 | _ = try irb.build(Inst.SaveErrRetAddr, scope, src_span, Inst.SaveErrRetAddr.Params{}); | ||
| 1437 | } | ||
| 1438 | _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{ | ||
| 1439 | .dest_block = ret_stmt_block, | ||
| 1440 | .is_comptime = err_is_comptime, | ||
| 1441 | }); | ||
| 1442 | |||
| 1443 | try irb.setCursorAtEndAndAppendBlock(ok_block); | ||
| 1444 | if (have_err_defers) { | ||
| 1445 | _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); | ||
| 1446 | } | ||
| 1447 | _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{ | ||
| 1448 | .dest_block = ret_stmt_block, | ||
| 1449 | .is_comptime = err_is_comptime, | ||
| 1450 | }); | ||
| 1451 | |||
| 1452 | try irb.setCursorAtEndAndAppendBlock(ret_stmt_block); | ||
| 1453 | return irb.genAsyncReturn(scope, src_span, return_value, false); | ||
| 1454 | } else { | ||
| 1455 | _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable); | ||
| 1456 | return irb.genAsyncReturn(scope, src_span, return_value, false); | ||
| 1457 | } | ||
| 1458 | }, | ||
| 1459 | } | ||
| 1460 | } | ||
| 1461 | |||
| 1462 | pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst { | ||
| 1463 | const src_span = Span.token(identifier.token); | ||
| 1464 | const name = irb.root_scope.tree.tokenSlice(identifier.token); | ||
| 1465 | |||
| 1466 | //if (buf_eql_str(variable_name, "_") && lval == LValPtr) { | ||
| 1467 | // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node); | ||
| 1468 | // const_instruction->base.value.type = get_pointer_to_type(irb->codegen, | ||
| 1469 | // irb->codegen->builtin_types.entry_void, false); | ||
| 1470 | // const_instruction->base.value.special = ConstValSpecialStatic; | ||
| 1471 | // const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard; | ||
| 1472 | // return &const_instruction->base; | ||
| 1473 | //} | ||
| 1474 | |||
| 1475 | if (await (async irb.comp.getPrimitiveType(name) catch unreachable)) |result| { | ||
| 1476 | if (result) |primitive_type| { | ||
| 1477 | defer primitive_type.base.deref(irb.comp); | ||
| 1478 | switch (lval) { | ||
| 1479 | // if (lval == LValPtr) { | ||
| 1480 | // return ir_build_ref(irb, scope, node, value, false, false); | ||
| 1481 | LVal.Ptr => return error.Unimplemented, | ||
| 1482 | LVal.None => return irb.buildConstValue(scope, src_span, &primitive_type.base), | ||
| 1483 | } | ||
| 1484 | } | ||
| 1485 | } else |err| switch (err) { | ||
| 1486 | error.Overflow => { | ||
| 1487 | try irb.comp.addCompileError(irb.root_scope, src_span, "integer too large"); | ||
| 1488 | return error.SemanticAnalysisFailed; | ||
| 1489 | }, | ||
| 1490 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | //VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); | ||
| 1494 | //if (var) { | ||
| 1495 | // IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var); | ||
| 1496 | // if (lval == LValPtr) | ||
| 1497 | // return var_ptr; | ||
| 1498 | // else | ||
| 1499 | // return ir_build_load_ptr(irb, scope, node, var_ptr); | ||
| 1500 | //} | ||
| 1501 | |||
| 1502 | if (await (async irb.findDecl(scope, name) catch unreachable)) |decl| { | ||
| 1503 | return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{ | ||
| 1504 | .decl = decl, | ||
| 1505 | .lval = lval, | ||
| 1506 | }); | ||
| 1507 | } | ||
| 1508 | |||
| 1509 | //if (node->owner->any_imports_failed) { | ||
| 1510 | // // skip the error message since we had a failing import in this file | ||
| 1511 | // // if an import breaks we don't need redundant undeclared identifier errors | ||
| 1512 | // return irb->codegen->invalid_instruction; | ||
| 1513 | //} | ||
| 1514 | |||
| 1515 | // TODO put a variable of same name with invalid type in global scope | ||
| 1516 | // so that future references to this same name will find a variable with an invalid type | ||
| 1517 | |||
| 1518 | try irb.comp.addCompileError(irb.root_scope, src_span, "unknown identifier '{}'", name); | ||
| 1519 | return error.SemanticAnalysisFailed; | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | const DeferCounts = struct { | ||
| 1523 | scope_exit: usize, | ||
| 1524 | error_exit: usize, | ||
| 1525 | }; | ||
| 1526 | |||
| 1527 | fn countDefers(irb: *Builder, inner_scope: *Scope, outer_scope: *Scope) DeferCounts { | ||
| 1528 | var result = DeferCounts{ .scope_exit = 0, .error_exit = 0 }; | ||
| 1529 | |||
| 1530 | var scope = inner_scope; | ||
| 1531 | while (scope != outer_scope) { | ||
| 1532 | switch (scope.id) { | ||
| 1533 | Scope.Id.Defer => { | ||
| 1534 | const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope); | ||
| 1535 | switch (defer_scope.kind) { | ||
| 1536 | Scope.Defer.Kind.ScopeExit => result.scope_exit += 1, | ||
| 1537 | Scope.Defer.Kind.ErrorExit => result.error_exit += 1, | ||
| 1538 | } | ||
| 1539 | scope = scope.parent orelse break; | ||
| 1540 | }, | ||
| 1541 | Scope.Id.FnDef => break, | ||
| 1542 | |||
| 1543 | Scope.Id.CompTime, | ||
| 1544 | Scope.Id.Block, | ||
| 1545 | Scope.Id.Decls, | ||
| 1546 | Scope.Id.Root, | ||
| 1547 | => scope = scope.parent orelse break, | ||
| 1548 | |||
| 1549 | Scope.Id.DeferExpr => unreachable, | ||
| 1550 | } | ||
| 1551 | } | ||
| 1552 | return result; | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | async fn genDefersForBlock( | ||
| 729 | irb: *Builder, | 1556 | irb: *Builder, |
| 730 | inner_scope: *Scope, | 1557 | inner_scope: *Scope, |
| 731 | outer_scope: *Scope, | 1558 | outer_scope: *Scope, |
| ... | @@ -743,25 +1570,26 @@ pub const Builder = struct { | ... | @@ -743,25 +1570,26 @@ pub const Builder = struct { |
| 743 | }; | 1570 | }; |
| 744 | if (generate) { | 1571 | if (generate) { |
| 745 | const defer_expr_scope = defer_scope.defer_expr_scope; | 1572 | const defer_expr_scope = defer_scope.defer_expr_scope; |
| 746 | const instruction = try irb.genNode( | 1573 | const instruction = try await (async irb.genNode( |
| 747 | defer_expr_scope.expr_node, | 1574 | defer_expr_scope.expr_node, |
| 748 | &defer_expr_scope.base, | 1575 | &defer_expr_scope.base, |
| 749 | LVal.None, | 1576 | LVal.None, |
| 750 | ); | 1577 | ) catch unreachable); |
| 751 | if (instruction.isNoReturn()) { | 1578 | if (instruction.isNoReturn()) { |
| 752 | is_noreturn = true; | 1579 | is_noreturn = true; |
| 753 | } else { | 1580 | } else { |
| 754 | _ = try irb.build( | 1581 | _ = try irb.build( |
| 755 | Instruction.CheckVoidStmt, | 1582 | Inst.CheckVoidStmt, |
| 756 | &defer_expr_scope.base, | 1583 | &defer_expr_scope.base, |
| 757 | Span.token(defer_expr_scope.expr_node.lastToken()), | 1584 | Span.token(defer_expr_scope.expr_node.lastToken()), |
| 758 | Instruction.CheckVoidStmt.Params{ .target = instruction }, | 1585 | Inst.CheckVoidStmt.Params{ .target = instruction }, |
| 759 | ); | 1586 | ); |
| 760 | } | 1587 | } |
| 761 | } | 1588 | } |
| 762 | }, | 1589 | }, |
| 763 | Scope.Id.FnDef, | 1590 | Scope.Id.FnDef, |
| 764 | Scope.Id.Decls, | 1591 | Scope.Id.Decls, |
| 1592 | Scope.Id.Root, | ||
| 765 | => return is_noreturn, | 1593 | => return is_noreturn, |
| 766 | 1594 | ||
| 767 | Scope.Id.CompTime, | 1595 | Scope.Id.CompTime, |
| ... | @@ -773,13 +1601,13 @@ pub const Builder = struct { | ... | @@ -773,13 +1601,13 @@ pub const Builder = struct { |
| 773 | } | 1601 | } |
| 774 | } | 1602 | } |
| 775 | 1603 | ||
| 776 | pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Instruction, lval: LVal) !*Instruction { | 1604 | pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Inst, lval: LVal) !*Inst { |
| 777 | switch (lval) { | 1605 | switch (lval) { |
| 778 | LVal.None => return instruction, | 1606 | LVal.None => return instruction, |
| 779 | LVal.Ptr => { | 1607 | LVal.Ptr => { |
| 780 | // We needed a pointer to a value, but we got a value. So we create | 1608 | // We needed a pointer to a value, but we got a value. So we create |
| 781 | // an instruction which just makes a const pointer of it. | 1609 | // an instruction which just makes a const pointer of it. |
| 782 | return irb.build(Instruction.Ref, scope, instruction.span, Instruction.Ref.Params{ | 1610 | return irb.build(Inst.Ref, scope, instruction.span, Inst.Ref.Params{ |
| 783 | .target = instruction, | 1611 | .target = instruction, |
| 784 | .mut = Type.Pointer.Mut.Const, | 1612 | .mut = Type.Pointer.Mut.Const, |
| 785 | .volatility = Type.Pointer.Vol.Non, | 1613 | .volatility = Type.Pointer.Vol.Non, |
| ... | @@ -799,10 +1627,10 @@ pub const Builder = struct { | ... | @@ -799,10 +1627,10 @@ pub const Builder = struct { |
| 799 | span: Span, | 1627 | span: Span, |
| 800 | params: I.Params, | 1628 | params: I.Params, |
| 801 | is_generated: bool, | 1629 | is_generated: bool, |
| 802 | ) !*Instruction { | 1630 | ) !*Inst { |
| 803 | const inst = try self.arena().create(I{ | 1631 | const inst = try self.arena().create(I{ |
| 804 | .base = Instruction{ | 1632 | .base = Inst{ |
| 805 | .id = Instruction.typeToId(I), | 1633 | .id = Inst.typeToId(I), |
| 806 | .is_generated = is_generated, | 1634 | .is_generated = is_generated, |
| 807 | .scope = scope, | 1635 | .scope = scope, |
| 808 | .debug_id = self.next_debug_id, | 1636 | .debug_id = self.next_debug_id, |
| ... | @@ -816,6 +1644,7 @@ pub const Builder = struct { | ... | @@ -816,6 +1644,7 @@ pub const Builder = struct { |
| 816 | .child = null, | 1644 | .child = null, |
| 817 | .parent = null, | 1645 | .parent = null, |
| 818 | .llvm_value = undefined, | 1646 | .llvm_value = undefined, |
| 1647 | .owner_bb = self.current_basic_block, | ||
| 819 | }, | 1648 | }, |
| 820 | .params = params, | 1649 | .params = params, |
| 821 | }); | 1650 | }); |
| ... | @@ -825,9 +1654,27 @@ pub const Builder = struct { | ... | @@ -825,9 +1654,27 @@ pub const Builder = struct { |
| 825 | inline while (i < @memberCount(I.Params)) : (i += 1) { | 1654 | inline while (i < @memberCount(I.Params)) : (i += 1) { |
| 826 | const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i))); | 1655 | const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i))); |
| 827 | switch (FieldType) { | 1656 | switch (FieldType) { |
| 828 | *Instruction => @field(inst.params, @memberName(I.Params, i)).ref_count += 1, | 1657 | *Inst => @field(inst.params, @memberName(I.Params, i)).ref(self), |
| 829 | ?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref_count += 1, | 1658 | *BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self), |
| 830 | else => {}, | 1659 | ?*Inst => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref(self), |
| 1660 | []*Inst => { | ||
| 1661 | // TODO https://github.com/ziglang/zig/issues/1269 | ||
| 1662 | for (@field(inst.params, @memberName(I.Params, i))) |other| | ||
| 1663 | other.ref(self); | ||
| 1664 | }, | ||
| 1665 | []*BasicBlock => { | ||
| 1666 | // TODO https://github.com/ziglang/zig/issues/1269 | ||
| 1667 | for (@field(inst.params, @memberName(I.Params, i))) |other| | ||
| 1668 | other.ref(self); | ||
| 1669 | }, | ||
| 1670 | Type.Pointer.Mut, | ||
| 1671 | Type.Pointer.Vol, | ||
| 1672 | Type.Pointer.Size, | ||
| 1673 | LVal, | ||
| 1674 | *Decl, | ||
| 1675 | => {}, | ||
| 1676 | // it's ok to add more types here, just make sure any instructions are ref'd appropriately | ||
| 1677 | else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)), | ||
| 831 | } | 1678 | } |
| 832 | } | 1679 | } |
| 833 | 1680 | ||
| ... | @@ -842,7 +1689,7 @@ pub const Builder = struct { | ... | @@ -842,7 +1689,7 @@ pub const Builder = struct { |
| 842 | scope: *Scope, | 1689 | scope: *Scope, |
| 843 | span: Span, | 1690 | span: Span, |
| 844 | params: I.Params, | 1691 | params: I.Params, |
| 845 | ) !*Instruction { | 1692 | ) !*Inst { |
| 846 | return self.buildExtra(I, scope, span, params, false); | 1693 | return self.buildExtra(I, scope, span, params, false); |
| 847 | } | 1694 | } |
| 848 | 1695 | ||
| ... | @@ -852,21 +1699,95 @@ pub const Builder = struct { | ... | @@ -852,21 +1699,95 @@ pub const Builder = struct { |
| 852 | scope: *Scope, | 1699 | scope: *Scope, |
| 853 | span: Span, | 1700 | span: Span, |
| 854 | params: I.Params, | 1701 | params: I.Params, |
| 855 | ) !*Instruction { | 1702 | ) !*Inst { |
| 856 | return self.buildExtra(I, scope, span, params, true); | 1703 | return self.buildExtra(I, scope, span, params, true); |
| 857 | } | 1704 | } |
| 858 | 1705 | ||
| 859 | fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Instruction { | 1706 | fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Inst { |
| 860 | const inst = try self.build(Instruction.Const, scope, span, Instruction.Const.Params{}); | 1707 | const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{}); |
| 861 | inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base }; | 1708 | inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base }; |
| 862 | return inst; | 1709 | return inst; |
| 863 | } | 1710 | } |
| 864 | 1711 | ||
| 865 | fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Instruction { | 1712 | fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Inst { |
| 866 | const inst = try self.buildExtra(Instruction.Const, scope, span, Instruction.Const.Params{}, is_generated); | 1713 | const inst = try self.buildExtra(Inst.Const, scope, span, Inst.Const.Params{}, is_generated); |
| 867 | inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base }; | 1714 | inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base }; |
| 868 | return inst; | 1715 | return inst; |
| 869 | } | 1716 | } |
| 1717 | |||
| 1718 | fn buildConstValue(self: *Builder, scope: *Scope, span: Span, v: *Value) !*Inst { | ||
| 1719 | const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{}); | ||
| 1720 | inst.val = IrVal{ .KnownValue = v.getRef() }; | ||
| 1721 | return inst; | ||
| 1722 | } | ||
| 1723 | |||
| 1724 | /// If the code is explicitly set to be comptime, then builds a const bool, | ||
| 1725 | /// otherwise builds a TestCompTime instruction. | ||
| 1726 | fn buildTestCompTime(self: *Builder, scope: *Scope, span: Span, target: *Inst) !*Inst { | ||
| 1727 | if (self.isCompTime(scope)) { | ||
| 1728 | return self.buildConstBool(scope, span, true); | ||
| 1729 | } else { | ||
| 1730 | return self.build( | ||
| 1731 | Inst.TestCompTime, | ||
| 1732 | scope, | ||
| 1733 | span, | ||
| 1734 | Inst.TestCompTime.Params{ .target = target }, | ||
| 1735 | ); | ||
| 1736 | } | ||
| 1737 | } | ||
| 1738 | |||
| 1739 | fn genAsyncReturn(irb: *Builder, scope: *Scope, span: Span, result: *Inst, is_gen: bool) !*Inst { | ||
| 1740 | _ = irb.buildGen( | ||
| 1741 | Inst.AddImplicitReturnType, | ||
| 1742 | scope, | ||
| 1743 | span, | ||
| 1744 | Inst.AddImplicitReturnType.Params{ .target = result }, | ||
| 1745 | ); | ||
| 1746 | |||
| 1747 | if (!irb.is_async) { | ||
| 1748 | return irb.buildExtra( | ||
| 1749 | Inst.Return, | ||
| 1750 | scope, | ||
| 1751 | span, | ||
| 1752 | Inst.Return.Params{ .return_value = result }, | ||
| 1753 | is_gen, | ||
| 1754 | ); | ||
| 1755 | } | ||
| 1756 | return error.Unimplemented; | ||
| 1757 | |||
| 1758 | //ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value); | ||
| 1759 | //IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, | ||
| 1760 | // get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); | ||
| 1761 | //// TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig | ||
| 1762 | //IrInstruction *replacement_value = irb->exec->coro_handle; | ||
| 1763 | //IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node, | ||
| 1764 | // promise_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr, | ||
| 1765 | // AtomicRmwOp_xchg, AtomicOrderSeqCst); | ||
| 1766 | //ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, maybe_await_handle); | ||
| 1767 | //IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle); | ||
| 1768 | //IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); | ||
| 1769 | //return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final, | ||
| 1770 | // is_comptime); | ||
| 1771 | //// the above blocks are rendered by ir_gen after the rest of codegen | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | async fn findDecl(irb: *Builder, scope: *Scope, name: []const u8) ?*Decl { | ||
| 1775 | var s = scope; | ||
| 1776 | while (true) { | ||
| 1777 | switch (s.id) { | ||
| 1778 | Scope.Id.Decls => { | ||
| 1779 | const decls = @fieldParentPtr(Scope.Decls, "base", s); | ||
| 1780 | const table = await (async decls.getTableReadOnly() catch unreachable); | ||
| 1781 | if (table.get(name)) |entry| { | ||
| 1782 | return entry.value; | ||
| 1783 | } | ||
| 1784 | }, | ||
| 1785 | Scope.Id.Root => return null, | ||
| 1786 | else => {}, | ||
| 1787 | } | ||
| 1788 | s = s.parent.?; | ||
| 1789 | } | ||
| 1790 | } | ||
| 870 | }; | 1791 | }; |
| 871 | 1792 | ||
| 872 | const Analyze = struct { | 1793 | const Analyze = struct { |
| ... | @@ -875,7 +1796,7 @@ const Analyze = struct { | ... | @@ -875,7 +1796,7 @@ const Analyze = struct { |
| 875 | const_predecessor_bb: ?*BasicBlock, | 1796 | const_predecessor_bb: ?*BasicBlock, |
| 876 | parent_basic_block: *BasicBlock, | 1797 | parent_basic_block: *BasicBlock, |
| 877 | instruction_index: usize, | 1798 | instruction_index: usize, |
| 878 | src_implicit_return_type_list: std.ArrayList(*Instruction), | 1799 | src_implicit_return_type_list: std.ArrayList(*Inst), |
| 879 | explicit_return_type: ?*Type, | 1800 | explicit_return_type: ?*Type, |
| 880 | 1801 | ||
| 881 | pub const Error = error{ | 1802 | pub const Error = error{ |
| ... | @@ -889,8 +1810,8 @@ const Analyze = struct { | ... | @@ -889,8 +1810,8 @@ const Analyze = struct { |
| 889 | OutOfMemory, | 1810 | OutOfMemory, |
| 890 | }; | 1811 | }; |
| 891 | 1812 | ||
| 892 | pub fn init(comp: *Compilation, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze { | 1813 | pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze { |
| 893 | var irb = try Builder.init(comp, parsed_file); | 1814 | var irb = try Builder.init(comp, root_scope, null); |
| 894 | errdefer irb.abort(); | 1815 | errdefer irb.abort(); |
| 895 | 1816 | ||
| 896 | return Analyze{ | 1817 | return Analyze{ |
| ... | @@ -899,7 +1820,7 @@ const Analyze = struct { | ... | @@ -899,7 +1820,7 @@ const Analyze = struct { |
| 899 | .const_predecessor_bb = null, | 1820 | .const_predecessor_bb = null, |
| 900 | .parent_basic_block = undefined, // initialized with startBasicBlock | 1821 | .parent_basic_block = undefined, // initialized with startBasicBlock |
| 901 | .instruction_index = undefined, // initialized with startBasicBlock | 1822 | .instruction_index = undefined, // initialized with startBasicBlock |
| 902 | .src_implicit_return_type_list = std.ArrayList(*Instruction).init(irb.arena()), | 1823 | .src_implicit_return_type_list = std.ArrayList(*Inst).init(irb.arena()), |
| 903 | .explicit_return_type = explicit_return_type, | 1824 | .explicit_return_type = explicit_return_type, |
| 904 | }; | 1825 | }; |
| 905 | } | 1826 | } |
| ... | @@ -908,7 +1829,7 @@ const Analyze = struct { | ... | @@ -908,7 +1829,7 @@ const Analyze = struct { |
| 908 | self.irb.abort(); | 1829 | self.irb.abort(); |
| 909 | } | 1830 | } |
| 910 | 1831 | ||
| 911 | pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Instruction) !*BasicBlock { | 1832 | pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Inst) !*BasicBlock { |
| 912 | if (old_bb.child) |child| { | 1833 | if (old_bb.child) |child| { |
| 913 | if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction) | 1834 | if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction) |
| 914 | return child; | 1835 | return child; |
| ... | @@ -968,21 +1889,478 @@ const Analyze = struct { | ... | @@ -968,21 +1889,478 @@ const Analyze = struct { |
| 968 | } | 1889 | } |
| 969 | 1890 | ||
| 970 | fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void { | 1891 | fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void { |
| 971 | return self.irb.comp.addCompileError(self.irb.parsed_file, span, fmt, args); | 1892 | return self.irb.comp.addCompileError(self.irb.root_scope, span, fmt, args); |
| 972 | } | 1893 | } |
| 973 | 1894 | ||
| 974 | fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Instruction) Analyze.Error!*Type { | 1895 | fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type { |
| 975 | // TODO actual implementation | 1896 | // TODO actual implementation |
| 976 | return &Type.Void.get(self.irb.comp).base; | 1897 | return &Type.Void.get(self.irb.comp).base; |
| 977 | } | 1898 | } |
| 978 | 1899 | ||
| 979 | fn implicitCast(self: *Analyze, target: *Instruction, optional_dest_type: ?*Type) Analyze.Error!*Instruction { | 1900 | fn implicitCast(self: *Analyze, target: *Inst, optional_dest_type: ?*Type) Analyze.Error!*Inst { |
| 980 | const dest_type = optional_dest_type orelse return target; | 1901 | const dest_type = optional_dest_type orelse return target; |
| 981 | @panic("TODO implicitCast"); | 1902 | const from_type = target.getKnownType(); |
| 1903 | if (from_type == dest_type or from_type.id == Type.Id.NoReturn) return target; | ||
| 1904 | return self.analyzeCast(target, target, dest_type); | ||
| 982 | } | 1905 | } |
| 983 | 1906 | ||
| 984 | fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Instruction) ?*Value { | 1907 | fn analyzeCast(ira: *Analyze, source_instr: *Inst, target: *Inst, dest_type: *Type) !*Inst { |
| 985 | @panic("TODO getCompTimeValOrNullUndefOk"); | 1908 | const from_type = target.getKnownType(); |
| 1909 | |||
| 1910 | //if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) { | ||
| 1911 | // return ira->codegen->invalid_instruction; | ||
| 1912 | //} | ||
| 1913 | |||
| 1914 | //// perfect match or non-const to const | ||
| 1915 | //ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, | ||
| 1916 | // source_node, false); | ||
| 1917 | //if (const_cast_result.id == ConstCastResultIdOk) { | ||
| 1918 | // return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); | ||
| 1919 | //} | ||
| 1920 | |||
| 1921 | //// widening conversion | ||
| 1922 | //if (wanted_type->id == TypeTableEntryIdInt && | ||
| 1923 | // actual_type->id == TypeTableEntryIdInt && | ||
| 1924 | // wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && | ||
| 1925 | // wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) | ||
| 1926 | //{ | ||
| 1927 | // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); | ||
| 1928 | //} | ||
| 1929 | |||
| 1930 | //// small enough unsigned ints can get casted to large enough signed ints | ||
| 1931 | //if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed && | ||
| 1932 | // actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed && | ||
| 1933 | // wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) | ||
| 1934 | //{ | ||
| 1935 | // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); | ||
| 1936 | //} | ||
| 1937 | |||
| 1938 | //// float widening conversion | ||
| 1939 | //if (wanted_type->id == TypeTableEntryIdFloat && | ||
| 1940 | // actual_type->id == TypeTableEntryIdFloat && | ||
| 1941 | // wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) | ||
| 1942 | //{ | ||
| 1943 | // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); | ||
| 1944 | //} | ||
| 1945 | |||
| 1946 | //// cast from [N]T to []const T | ||
| 1947 | //if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) { | ||
| 1948 | // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; | ||
| 1949 | // assert(ptr_type->id == TypeTableEntryIdPointer); | ||
| 1950 | // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && | ||
| 1951 | // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, | ||
| 1952 | // source_node, false).id == ConstCastResultIdOk) | ||
| 1953 | // { | ||
| 1954 | // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); | ||
| 1955 | // } | ||
| 1956 | //} | ||
| 1957 | |||
| 1958 | //// cast from *const [N]T to []const T | ||
| 1959 | //if (is_slice(wanted_type) && | ||
| 1960 | // actual_type->id == TypeTableEntryIdPointer && | ||
| 1961 | // actual_type->data.pointer.is_const && | ||
| 1962 | // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) | ||
| 1963 | //{ | ||
| 1964 | // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; | ||
| 1965 | // assert(ptr_type->id == TypeTableEntryIdPointer); | ||
| 1966 | |||
| 1967 | // TypeTableEntry *array_type = actual_type->data.pointer.child_type; | ||
| 1968 | |||
| 1969 | // if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && | ||
| 1970 | // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, | ||
| 1971 | // source_node, false).id == ConstCastResultIdOk) | ||
| 1972 | // { | ||
| 1973 | // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); | ||
| 1974 | // } | ||
| 1975 | //} | ||
| 1976 | |||
| 1977 | //// cast from [N]T to *const []const T | ||
| 1978 | //if (wanted_type->id == TypeTableEntryIdPointer && | ||
| 1979 | // wanted_type->data.pointer.is_const && | ||
| 1980 | // is_slice(wanted_type->data.pointer.child_type) && | ||
| 1981 | // actual_type->id == TypeTableEntryIdArray) | ||
| 1982 | //{ | ||
| 1983 | // TypeTableEntry *ptr_type = | ||
| 1984 | // wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; | ||
| 1985 | // assert(ptr_type->id == TypeTableEntryIdPointer); | ||
| 1986 | // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && | ||
| 1987 | // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, | ||
| 1988 | // source_node, false).id == ConstCastResultIdOk) | ||
| 1989 | // { | ||
| 1990 | // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); | ||
| 1991 | // if (type_is_invalid(cast1->value.type)) | ||
| 1992 | // return ira->codegen->invalid_instruction; | ||
| 1993 | |||
| 1994 | // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); | ||
| 1995 | // if (type_is_invalid(cast2->value.type)) | ||
| 1996 | // return ira->codegen->invalid_instruction; | ||
| 1997 | |||
| 1998 | // return cast2; | ||
| 1999 | // } | ||
| 2000 | //} | ||
| 2001 | |||
| 2002 | //// cast from [N]T to ?[]const T | ||
| 2003 | //if (wanted_type->id == TypeTableEntryIdOptional && | ||
| 2004 | // is_slice(wanted_type->data.maybe.child_type) && | ||
| 2005 | // actual_type->id == TypeTableEntryIdArray) | ||
| 2006 | //{ | ||
| 2007 | // TypeTableEntry *ptr_type = | ||
| 2008 | // wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; | ||
| 2009 | // assert(ptr_type->id == TypeTableEntryIdPointer); | ||
| 2010 | // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && | ||
| 2011 | // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, | ||
| 2012 | // source_node, false).id == ConstCastResultIdOk) | ||
| 2013 | // { | ||
| 2014 | // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); | ||
| 2015 | // if (type_is_invalid(cast1->value.type)) | ||
| 2016 | // return ira->codegen->invalid_instruction; | ||
| 2017 | |||
| 2018 | // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); | ||
| 2019 | // if (type_is_invalid(cast2->value.type)) | ||
| 2020 | // return ira->codegen->invalid_instruction; | ||
| 2021 | |||
| 2022 | // return cast2; | ||
| 2023 | // } | ||
| 2024 | //} | ||
| 2025 | |||
| 2026 | //// *[N]T to [*]T | ||
| 2027 | //if (wanted_type->id == TypeTableEntryIdPointer && | ||
| 2028 | // wanted_type->data.pointer.ptr_len == PtrLenUnknown && | ||
| 2029 | // actual_type->id == TypeTableEntryIdPointer && | ||
| 2030 | // actual_type->data.pointer.ptr_len == PtrLenSingle && | ||
| 2031 | // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray && | ||
| 2032 | // actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment && | ||
| 2033 | // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, | ||
| 2034 | // actual_type->data.pointer.child_type->data.array.child_type, source_node, | ||
| 2035 | // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) | ||
| 2036 | //{ | ||
| 2037 | // return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); | ||
| 2038 | //} | ||
| 2039 | |||
| 2040 | //// *[N]T to []T | ||
| 2041 | //if (is_slice(wanted_type) && | ||
| 2042 | // actual_type->id == TypeTableEntryIdPointer && | ||
| 2043 | // actual_type->data.pointer.ptr_len == PtrLenSingle && | ||
| 2044 | // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) | ||
| 2045 | //{ | ||
| 2046 | // TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; | ||
| 2047 | // assert(slice_ptr_type->id == TypeTableEntryIdPointer); | ||
| 2048 | // if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, | ||
| 2049 | // actual_type->data.pointer.child_type->data.array.child_type, source_node, | ||
| 2050 | // !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) | ||
| 2051 | // { | ||
| 2052 | // return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type); | ||
| 2053 | // } | ||
| 2054 | //} | ||
| 2055 | |||
| 2056 | //// cast from T to ?T | ||
| 2057 | //// note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism | ||
| 2058 | //if (wanted_type->id == TypeTableEntryIdOptional) { | ||
| 2059 | // TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; | ||
| 2060 | // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, | ||
| 2061 | // false).id == ConstCastResultIdOk) | ||
| 2062 | // { | ||
| 2063 | // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); | ||
| 2064 | // } else if (actual_type->id == TypeTableEntryIdComptimeInt || | ||
| 2065 | // actual_type->id == TypeTableEntryIdComptimeFloat) | ||
| 2066 | // { | ||
| 2067 | // if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { | ||
| 2068 | // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); | ||
| 2069 | // } else { | ||
| 2070 | // return ira->codegen->invalid_instruction; | ||
| 2071 | // } | ||
| 2072 | // } else if (wanted_child_type->id == TypeTableEntryIdPointer && | ||
| 2073 | // wanted_child_type->data.pointer.is_const && | ||
| 2074 | // (actual_type->id == TypeTableEntryIdPointer || is_container(actual_type))) | ||
| 2075 | // { | ||
| 2076 | // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_child_type, value); | ||
| 2077 | // if (type_is_invalid(cast1->value.type)) | ||
| 2078 | // return ira->codegen->invalid_instruction; | ||
| 2079 | |||
| 2080 | // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); | ||
| 2081 | // if (type_is_invalid(cast2->value.type)) | ||
| 2082 | // return ira->codegen->invalid_instruction; | ||
| 2083 | |||
| 2084 | // return cast2; | ||
| 2085 | // } | ||
| 2086 | //} | ||
| 2087 | |||
| 2088 | //// cast from null literal to maybe type | ||
| 2089 | //if (wanted_type->id == TypeTableEntryIdOptional && | ||
| 2090 | // actual_type->id == TypeTableEntryIdNull) | ||
| 2091 | //{ | ||
| 2092 | // return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); | ||
| 2093 | //} | ||
| 2094 | |||
| 2095 | //// cast from child type of error type to error type | ||
| 2096 | //if (wanted_type->id == TypeTableEntryIdErrorUnion) { | ||
| 2097 | // if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, | ||
| 2098 | // source_node, false).id == ConstCastResultIdOk) | ||
| 2099 | // { | ||
| 2100 | // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); | ||
| 2101 | // } else if (actual_type->id == TypeTableEntryIdComptimeInt || | ||
| 2102 | // actual_type->id == TypeTableEntryIdComptimeFloat) | ||
| 2103 | // { | ||
| 2104 | // if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) { | ||
| 2105 | // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); | ||
| 2106 | // } else { | ||
| 2107 | // return ira->codegen->invalid_instruction; | ||
| 2108 | // } | ||
| 2109 | // } | ||
| 2110 | //} | ||
| 2111 | |||
| 2112 | //// cast from [N]T to E![]const T | ||
| 2113 | //if (wanted_type->id == TypeTableEntryIdErrorUnion && | ||
| 2114 | // is_slice(wanted_type->data.error_union.payload_type) && | ||
| 2115 | // actual_type->id == TypeTableEntryIdArray) | ||
| 2116 | //{ | ||
| 2117 | // TypeTableEntry *ptr_type = | ||
| 2118 | // wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry; | ||
| 2119 | // assert(ptr_type->id == TypeTableEntryIdPointer); | ||
| 2120 | // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && | ||
| 2121 | // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, | ||
| 2122 | // source_node, false).id == ConstCastResultIdOk) | ||
| 2123 | // { | ||
| 2124 | // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); | ||
| 2125 | // if (type_is_invalid(cast1->value.type)) | ||
| 2126 | // return ira->codegen->invalid_instruction; | ||
| 2127 | |||
| 2128 | // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); | ||
| 2129 | // if (type_is_invalid(cast2->value.type)) | ||
| 2130 | // return ira->codegen->invalid_instruction; | ||
| 2131 | |||
| 2132 | // return cast2; | ||
| 2133 | // } | ||
| 2134 | //} | ||
| 2135 | |||
| 2136 | //// cast from error set to error union type | ||
| 2137 | //if (wanted_type->id == TypeTableEntryIdErrorUnion && | ||
| 2138 | // actual_type->id == TypeTableEntryIdErrorSet) | ||
| 2139 | //{ | ||
| 2140 | // return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type); | ||
| 2141 | //} | ||
| 2142 | |||
| 2143 | //// cast from T to E!?T | ||
| 2144 | //if (wanted_type->id == TypeTableEntryIdErrorUnion && | ||
| 2145 | // wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && | ||
| 2146 | // actual_type->id != TypeTableEntryIdOptional) | ||
| 2147 | //{ | ||
| 2148 | // TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; | ||
| 2149 | // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk || | ||
| 2150 | // actual_type->id == TypeTableEntryIdNull || | ||
| 2151 | // actual_type->id == TypeTableEntryIdComptimeInt || | ||
| 2152 | // actual_type->id == TypeTableEntryIdComptimeFloat) | ||
| 2153 | // { | ||
| 2154 | // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); | ||
| 2155 | // if (type_is_invalid(cast1->value.type)) | ||
| 2156 | // return ira->codegen->invalid_instruction; | ||
| 2157 | |||
| 2158 | // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); | ||
| 2159 | // if (type_is_invalid(cast2->value.type)) | ||
| 2160 | // return ira->codegen->invalid_instruction; | ||
| 2161 | |||
| 2162 | // return cast2; | ||
| 2163 | // } | ||
| 2164 | //} | ||
| 2165 | |||
| 2166 | // cast from comptime-known integer to another integer where the value fits | ||
| 2167 | if (target.isCompTime() and (from_type.id == Type.Id.Int or from_type.id == Type.Id.ComptimeInt)) cast: { | ||
| 2168 | const target_val = target.val.KnownValue; | ||
| 2169 | const from_int = &target_val.cast(Value.Int).?.big_int; | ||
| 2170 | const fits = fits: { | ||
| 2171 | if (dest_type.cast(Type.ComptimeInt)) |ctint| { | ||
| 2172 | break :fits true; | ||
| 2173 | } | ||
| 2174 | if (dest_type.cast(Type.Int)) |int| { | ||
| 2175 | break :fits from_int.fitsInTwosComp(int.key.is_signed, int.key.bit_count); | ||
| 2176 | } | ||
| 2177 | break :cast; | ||
| 2178 | }; | ||
| 2179 | if (!fits) { | ||
| 2180 | try ira.addCompileError( | ||
| 2181 | source_instr.span, | ||
| 2182 | "integer value '{}' cannot be stored in type '{}'", | ||
| 2183 | from_int, | ||
| 2184 | dest_type.name, | ||
| 2185 | ); | ||
| 2186 | return error.SemanticAnalysisFailed; | ||
| 2187 | } | ||
| 2188 | |||
| 2189 | const new_val = try target.copyVal(ira.irb.comp); | ||
| 2190 | new_val.setType(dest_type, ira.irb.comp); | ||
| 2191 | return ira.irb.buildConstValue(source_instr.scope, source_instr.span, new_val); | ||
| 2192 | } | ||
| 2193 | |||
| 2194 | // cast from number literal to another type | ||
| 2195 | // cast from number literal to *const integer | ||
| 2196 | //if (actual_type->id == TypeTableEntryIdComptimeFloat || | ||
| 2197 | // actual_type->id == TypeTableEntryIdComptimeInt) | ||
| 2198 | //{ | ||
| 2199 | // ensure_complete_type(ira->codegen, wanted_type); | ||
| 2200 | // if (type_is_invalid(wanted_type)) | ||
| 2201 | // return ira->codegen->invalid_instruction; | ||
| 2202 | // if (wanted_type->id == TypeTableEntryIdEnum) { | ||
| 2203 | // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value); | ||
| 2204 | // if (type_is_invalid(cast1->value.type)) | ||
| 2205 | // return ira->codegen->invalid_instruction; | ||
| 2206 | |||
| 2207 | // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); | ||
| 2208 | // if (type_is_invalid(cast2->value.type)) | ||
| 2209 | // return ira->codegen->invalid_instruction; | ||
| 2210 | |||
| 2211 | // return cast2; | ||
| 2212 | // } else if (wanted_type->id == TypeTableEntryIdPointer && | ||
| 2213 | // wanted_type->data.pointer.is_const) | ||
| 2214 | // { | ||
| 2215 | // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); | ||
| 2216 | // if (type_is_invalid(cast1->value.type)) | ||
| 2217 | // return ira->codegen->invalid_instruction; | ||
| 2218 | |||
| 2219 | // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); | ||
| 2220 | // if (type_is_invalid(cast2->value.type)) | ||
| 2221 | // return ira->codegen->invalid_instruction; | ||
| 2222 | |||
| 2223 | // return cast2; | ||
| 2224 | // } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { | ||
| 2225 | // CastOp op; | ||
| 2226 | // if ((actual_type->id == TypeTableEntryIdComptimeFloat && | ||
| 2227 | // wanted_type->id == TypeTableEntryIdFloat) || | ||
| 2228 | // (actual_type->id == TypeTableEntryIdComptimeInt && | ||
| 2229 | // wanted_type->id == TypeTableEntryIdInt)) | ||
| 2230 | // { | ||
| 2231 | // op = CastOpNumLitToConcrete; | ||
| 2232 | // } else if (wanted_type->id == TypeTableEntryIdInt) { | ||
| 2233 | // op = CastOpFloatToInt; | ||
| 2234 | // } else if (wanted_type->id == TypeTableEntryIdFloat) { | ||
| 2235 | // op = CastOpIntToFloat; | ||
| 2236 | // } else { | ||
| 2237 | // zig_unreachable(); | ||
| 2238 | // } | ||
| 2239 | // return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false); | ||
| 2240 | // } else { | ||
| 2241 | // return ira->codegen->invalid_instruction; | ||
| 2242 | // } | ||
| 2243 | //} | ||
| 2244 | |||
| 2245 | //// cast from typed number to integer or float literal. | ||
| 2246 | //// works when the number is known at compile time | ||
| 2247 | //if (instr_is_comptime(value) && | ||
| 2248 | // ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) || | ||
| 2249 | // (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat))) | ||
| 2250 | //{ | ||
| 2251 | // return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); | ||
| 2252 | //} | ||
| 2253 | |||
| 2254 | //// cast from union to the enum type of the union | ||
| 2255 | //if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) { | ||
| 2256 | // type_ensure_zero_bits_known(ira->codegen, actual_type); | ||
| 2257 | // if (type_is_invalid(actual_type)) | ||
| 2258 | // return ira->codegen->invalid_instruction; | ||
| 2259 | |||
| 2260 | // if (actual_type->data.unionation.tag_type == wanted_type) { | ||
| 2261 | // return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type); | ||
| 2262 | // } | ||
| 2263 | //} | ||
| 2264 | |||
| 2265 | //// enum to union which has the enum as the tag type | ||
| 2266 | //if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && | ||
| 2267 | // (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || | ||
| 2268 | // wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) | ||
| 2269 | //{ | ||
| 2270 | // type_ensure_zero_bits_known(ira->codegen, wanted_type); | ||
| 2271 | // if (wanted_type->data.unionation.tag_type == actual_type) { | ||
| 2272 | // return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type); | ||
| 2273 | // } | ||
| 2274 | //} | ||
| 2275 | |||
| 2276 | //// enum to &const union which has the enum as the tag type | ||
| 2277 | //if (actual_type->id == TypeTableEntryIdEnum && wanted_type->id == TypeTableEntryIdPointer) { | ||
| 2278 | // TypeTableEntry *union_type = wanted_type->data.pointer.child_type; | ||
| 2279 | // if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || | ||
| 2280 | // union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr) | ||
| 2281 | // { | ||
| 2282 | // type_ensure_zero_bits_known(ira->codegen, union_type); | ||
| 2283 | // if (union_type->data.unionation.tag_type == actual_type) { | ||
| 2284 | // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_type, value); | ||
| 2285 | // if (type_is_invalid(cast1->value.type)) | ||
| 2286 | // return ira->codegen->invalid_instruction; | ||
| 2287 | |||
| 2288 | // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); | ||
| 2289 | // if (type_is_invalid(cast2->value.type)) | ||
| 2290 | // return ira->codegen->invalid_instruction; | ||
| 2291 | |||
| 2292 | // return cast2; | ||
| 2293 | // } | ||
| 2294 | // } | ||
| 2295 | //} | ||
| 2296 | |||
| 2297 | //// cast from *T to *[1]T | ||
| 2298 | //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && | ||
| 2299 | // actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle) | ||
| 2300 | //{ | ||
| 2301 | // TypeTableEntry *array_type = wanted_type->data.pointer.child_type; | ||
| 2302 | // if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 && | ||
| 2303 | // types_match_const_cast_only(ira, array_type->data.array.child_type, | ||
| 2304 | // actual_type->data.pointer.child_type, source_node, | ||
| 2305 | // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) | ||
| 2306 | // { | ||
| 2307 | // if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) { | ||
| 2308 | // ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); | ||
| 2309 | // add_error_note(ira->codegen, msg, value->source_node, | ||
| 2310 | // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), | ||
| 2311 | // actual_type->data.pointer.alignment)); | ||
| 2312 | // add_error_note(ira->codegen, msg, source_instr->source_node, | ||
| 2313 | // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), | ||
| 2314 | // wanted_type->data.pointer.alignment)); | ||
| 2315 | // return ira->codegen->invalid_instruction; | ||
| 2316 | // } | ||
| 2317 | // return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type); | ||
| 2318 | // } | ||
| 2319 | //} | ||
| 2320 | |||
| 2321 | //// cast from T to *T where T is zero bits | ||
| 2322 | //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && | ||
| 2323 | // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, | ||
| 2324 | // actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) | ||
| 2325 | //{ | ||
| 2326 | // type_ensure_zero_bits_known(ira->codegen, actual_type); | ||
| 2327 | // if (type_is_invalid(actual_type)) { | ||
| 2328 | // return ira->codegen->invalid_instruction; | ||
| 2329 | // } | ||
| 2330 | // if (!type_has_bits(actual_type)) { | ||
| 2331 | // return ir_get_ref(ira, source_instr, value, false, false); | ||
| 2332 | // } | ||
| 2333 | //} | ||
| 2334 | |||
| 2335 | //// cast from undefined to anything | ||
| 2336 | //if (actual_type->id == TypeTableEntryIdUndefined) { | ||
| 2337 | // return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); | ||
| 2338 | //} | ||
| 2339 | |||
| 2340 | //// cast from something to const pointer of it | ||
| 2341 | //if (!type_requires_comptime(actual_type)) { | ||
| 2342 | // TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); | ||
| 2343 | // if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) { | ||
| 2344 | // return ir_analyze_cast_ref(ira, source_instr, value, wanted_type); | ||
| 2345 | // } | ||
| 2346 | //} | ||
| 2347 | |||
| 2348 | try ira.addCompileError( | ||
| 2349 | source_instr.span, | ||
| 2350 | "expected type '{}', found '{}'", | ||
| 2351 | dest_type.name, | ||
| 2352 | from_type.name, | ||
| 2353 | ); | ||
| 2354 | //ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, | ||
| 2355 | // buf_sprintf("expected type '%s', found '%s'", | ||
| 2356 | // buf_ptr(&wanted_type->name), | ||
| 2357 | // buf_ptr(&actual_type->name))); | ||
| 2358 | //report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); | ||
| 2359 | return error.SemanticAnalysisFailed; | ||
| 2360 | } | ||
| 2361 | |||
| 2362 | fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Inst) ?*Value { | ||
| 2363 | @panic("TODO"); | ||
| 986 | } | 2364 | } |
| 987 | 2365 | ||
| 988 | fn getCompTimeRef( | 2366 | fn getCompTimeRef( |
| ... | @@ -991,9 +2369,8 @@ const Analyze = struct { | ... | @@ -991,9 +2369,8 @@ const Analyze = struct { |
| 991 | ptr_mut: Value.Ptr.Mut, | 2369 | ptr_mut: Value.Ptr.Mut, |
| 992 | mut: Type.Pointer.Mut, | 2370 | mut: Type.Pointer.Mut, |
| 993 | volatility: Type.Pointer.Vol, | 2371 | volatility: Type.Pointer.Vol, |
| 994 | ptr_align: u32, | 2372 | ) Analyze.Error!*Inst { |
| 995 | ) Analyze.Error!*Instruction { | 2373 | return error.Unimplemented; |
| 996 | @panic("TODO getCompTimeRef"); | ||
| 997 | } | 2374 | } |
| 998 | }; | 2375 | }; |
| 999 | 2376 | ||
| ... | @@ -1001,43 +2378,32 @@ pub async fn gen( | ... | @@ -1001,43 +2378,32 @@ pub async fn gen( |
| 1001 | comp: *Compilation, | 2378 | comp: *Compilation, |
| 1002 | body_node: *ast.Node, | 2379 | body_node: *ast.Node, |
| 1003 | scope: *Scope, | 2380 | scope: *Scope, |
| 1004 | end_span: Span, | ||
| 1005 | parsed_file: *ParsedFile, | ||
| 1006 | ) !*Code { | 2381 | ) !*Code { |
| 1007 | var irb = try Builder.init(comp, parsed_file); | 2382 | var irb = try Builder.init(comp, scope.findRoot(), scope); |
| 1008 | errdefer irb.abort(); | 2383 | errdefer irb.abort(); |
| 1009 | 2384 | ||
| 1010 | const entry_block = try irb.createBasicBlock(scope, c"Entry"); | 2385 | const entry_block = try irb.createBasicBlock(scope, c"Entry"); |
| 1011 | entry_block.ref(); // Entry block gets a reference because we enter it to begin. | 2386 | entry_block.ref(&irb); // Entry block gets a reference because we enter it to begin. |
| 1012 | try irb.setCursorAtEndAndAppendBlock(entry_block); | 2387 | try irb.setCursorAtEndAndAppendBlock(entry_block); |
| 1013 | 2388 | ||
| 1014 | const result = try irb.genNode(body_node, scope, LVal.None); | 2389 | const result = try await (async irb.genNode(body_node, scope, LVal.None) catch unreachable); |
| 1015 | if (!result.isNoReturn()) { | 2390 | if (!result.isNoReturn()) { |
| 1016 | _ = irb.buildGen( | 2391 | // no need for save_err_ret_addr because this cannot return error |
| 1017 | Instruction.AddImplicitReturnType, | 2392 | _ = try irb.genAsyncReturn(scope, Span.token(body_node.lastToken()), result, true); |
| 1018 | scope, | ||
| 1019 | end_span, | ||
| 1020 | Instruction.AddImplicitReturnType.Params{ .target = result }, | ||
| 1021 | ); | ||
| 1022 | _ = irb.buildGen( | ||
| 1023 | Instruction.Return, | ||
| 1024 | scope, | ||
| 1025 | end_span, | ||
| 1026 | Instruction.Return.Params{ .return_value = result }, | ||
| 1027 | ); | ||
| 1028 | } | 2393 | } |
| 1029 | 2394 | ||
| 1030 | return irb.finish(); | 2395 | return irb.finish(); |
| 1031 | } | 2396 | } |
| 1032 | 2397 | ||
| 1033 | pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code { | 2398 | pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code { |
| 1034 | var ira = try Analyze.init(comp, parsed_file, expected_type); | ||
| 1035 | errdefer ira.abort(); | ||
| 1036 | |||
| 1037 | const old_entry_bb = old_code.basic_block_list.at(0); | 2399 | const old_entry_bb = old_code.basic_block_list.at(0); |
| 2400 | const root_scope = old_entry_bb.scope.findRoot(); | ||
| 2401 | |||
| 2402 | var ira = try Analyze.init(comp, root_scope, expected_type); | ||
| 2403 | errdefer ira.abort(); | ||
| 1038 | 2404 | ||
| 1039 | const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null); | 2405 | const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null); |
| 1040 | new_entry_bb.ref(); | 2406 | new_entry_bb.ref(&ira.irb); |
| 1041 | 2407 | ||
| 1042 | ira.irb.current_basic_block = new_entry_bb; | 2408 | ira.irb.current_basic_block = new_entry_bb; |
| 1043 | 2409 | ||
| ... | @@ -1051,7 +2417,8 @@ pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Co | ... | @@ -1051,7 +2417,8 @@ pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Co |
| 1051 | continue; | 2417 | continue; |
| 1052 | } | 2418 | } |
| 1053 | 2419 | ||
| 1054 | const return_inst = try old_instruction.analyze(&ira); | 2420 | const return_inst = try await (async old_instruction.analyze(&ira) catch unreachable); |
| 2421 | assert(return_inst.val != IrVal.Unknown); // at least the type should be known at this point | ||
| 1055 | return_inst.linkToParent(old_instruction); | 2422 | return_inst.linkToParent(old_instruction); |
| 1056 | // Note: if we ever modify the above to handle error.CompileError by continuing analysis, | 2423 | // Note: if we ever modify the above to handle error.CompileError by continuing analysis, |
| 1057 | // then here we want to check if ira.isCompTime() and return early if true | 2424 | // then here we want to check if ira.isCompTime() and return early if true |
src-self-hosted/libc_installation.zig created+462| ... | @@ -0,0 +1,462 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const event = std.event; | ||
| 4 | const Target = @import("target.zig").Target; | ||
| 5 | const c = @import("c.zig"); | ||
| 6 | |||
| 7 | /// See the render function implementation for documentation of the fields. | ||
| 8 | pub const LibCInstallation = struct { | ||
| 9 | include_dir: []const u8, | ||
| 10 | lib_dir: ?[]const u8, | ||
| 11 | static_lib_dir: ?[]const u8, | ||
| 12 | msvc_lib_dir: ?[]const u8, | ||
| 13 | kernel32_lib_dir: ?[]const u8, | ||
| 14 | dynamic_linker_path: ?[]const u8, | ||
| 15 | |||
| 16 | pub const FindError = error{ | ||
| 17 | OutOfMemory, | ||
| 18 | FileSystem, | ||
| 19 | UnableToSpawnCCompiler, | ||
| 20 | CCompilerExitCode, | ||
| 21 | CCompilerCrashed, | ||
| 22 | CCompilerCannotFindHeaders, | ||
| 23 | LibCRuntimeNotFound, | ||
| 24 | LibCStdLibHeaderNotFound, | ||
| 25 | LibCKernel32LibNotFound, | ||
| 26 | UnsupportedArchitecture, | ||
| 27 | }; | ||
| 28 | |||
| 29 | pub fn parse( | ||
| 30 | self: *LibCInstallation, | ||
| 31 | allocator: *std.mem.Allocator, | ||
| 32 | libc_file: []const u8, | ||
| 33 | stderr: *std.io.OutStream(std.io.FileOutStream.Error), | ||
| 34 | ) !void { | ||
| 35 | self.initEmpty(); | ||
| 36 | |||
| 37 | const keys = []const []const u8{ | ||
| 38 | "include_dir", | ||
| 39 | "lib_dir", | ||
| 40 | "static_lib_dir", | ||
| 41 | "msvc_lib_dir", | ||
| 42 | "kernel32_lib_dir", | ||
| 43 | "dynamic_linker_path", | ||
| 44 | }; | ||
| 45 | const FoundKey = struct { | ||
| 46 | found: bool, | ||
| 47 | allocated: ?[]u8, | ||
| 48 | }; | ||
| 49 | var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len; | ||
| 50 | errdefer { | ||
| 51 | self.initEmpty(); | ||
| 52 | for (found_keys) |found_key| { | ||
| 53 | if (found_key.allocated) |s| allocator.free(s); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | const contents = try std.io.readFileAlloc(allocator, libc_file); | ||
| 58 | defer allocator.free(contents); | ||
| 59 | |||
| 60 | var it = std.mem.split(contents, "\n"); | ||
| 61 | while (it.next()) |line| { | ||
| 62 | if (line.len == 0 or line[0] == '#') continue; | ||
| 63 | var line_it = std.mem.split(line, "="); | ||
| 64 | const name = line_it.next() orelse { | ||
| 65 | try stderr.print("missing equal sign after field name\n"); | ||
| 66 | return error.ParseError; | ||
| 67 | }; | ||
| 68 | const value = line_it.rest(); | ||
| 69 | inline for (keys) |key, i| { | ||
| 70 | if (std.mem.eql(u8, name, key)) { | ||
| 71 | found_keys[i].found = true; | ||
| 72 | switch (@typeInfo(@typeOf(@field(self, key)))) { | ||
| 73 | builtin.TypeId.Optional => { | ||
| 74 | if (value.len == 0) { | ||
| 75 | @field(self, key) = null; | ||
| 76 | } else { | ||
| 77 | found_keys[i].allocated = try std.mem.dupe(allocator, u8, value); | ||
| 78 | @field(self, key) = found_keys[i].allocated; | ||
| 79 | } | ||
| 80 | }, | ||
| 81 | else => { | ||
| 82 | if (value.len == 0) { | ||
| 83 | try stderr.print("field cannot be empty: {}\n", key); | ||
| 84 | return error.ParseError; | ||
| 85 | } | ||
| 86 | const dupe = try std.mem.dupe(allocator, u8, value); | ||
| 87 | found_keys[i].allocated = dupe; | ||
| 88 | @field(self, key) = dupe; | ||
| 89 | }, | ||
| 90 | } | ||
| 91 | break; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | for (found_keys) |found_key, i| { | ||
| 96 | if (!found_key.found) { | ||
| 97 | try stderr.print("missing field: {}\n", keys[i]); | ||
| 98 | return error.ParseError; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(std.io.FileOutStream.Error)) !void { | ||
| 104 | @setEvalBranchQuota(4000); | ||
| 105 | try out.print( | ||
| 106 | \\# The directory that contains `stdlib.h`. | ||
| 107 | \\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null` | ||
| 108 | \\include_dir={} | ||
| 109 | \\ | ||
| 110 | \\# The directory that contains `crt1.o`. | ||
| 111 | \\# On Linux, can be found with `cc -print-file-name=crt1.o`. | ||
| 112 | \\# Not needed when targeting MacOS. | ||
| 113 | \\lib_dir={} | ||
| 114 | \\ | ||
| 115 | \\# The directory that contains `crtbegin.o`. | ||
| 116 | \\# On Linux, can be found with `cc -print-file-name=crtbegin.o`. | ||
| 117 | \\# Not needed when targeting MacOS or Windows. | ||
| 118 | \\static_lib_dir={} | ||
| 119 | \\ | ||
| 120 | \\# The directory that contains `vcruntime.lib`. | ||
| 121 | \\# Only needed when targeting Windows. | ||
| 122 | \\msvc_lib_dir={} | ||
| 123 | \\ | ||
| 124 | \\# The directory that contains `kernel32.lib`. | ||
| 125 | \\# Only needed when targeting Windows. | ||
| 126 | \\kernel32_lib_dir={} | ||
| 127 | \\ | ||
| 128 | \\# The full path to the dynamic linker, on the target system. | ||
| 129 | \\# Only needed when targeting Linux. | ||
| 130 | \\dynamic_linker_path={} | ||
| 131 | \\ | ||
| 132 | , | ||
| 133 | self.include_dir, | ||
| 134 | self.lib_dir orelse "", | ||
| 135 | self.static_lib_dir orelse "", | ||
| 136 | self.msvc_lib_dir orelse "", | ||
| 137 | self.kernel32_lib_dir orelse "", | ||
| 138 | self.dynamic_linker_path orelse Target(Target.Native).getDynamicLinkerPath(), | ||
| 139 | ); | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Finds the default, native libc. | ||
| 143 | pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void { | ||
| 144 | self.initEmpty(); | ||
| 145 | var group = event.Group(FindError!void).init(loop); | ||
| 146 | errdefer group.cancelAll(); | ||
| 147 | var windows_sdk: ?*c.ZigWindowsSDK = null; | ||
| 148 | errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk)); | ||
| 149 | |||
| 150 | switch (builtin.os) { | ||
| 151 | builtin.Os.windows => { | ||
| 152 | var sdk: *c.ZigWindowsSDK = undefined; | ||
| 153 | switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) { | ||
| 154 | c.ZigFindWindowsSdkError.None => { | ||
| 155 | windows_sdk = sdk; | ||
| 156 | |||
| 157 | if (sdk.msvc_lib_dir_ptr) |ptr| { | ||
| 158 | self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]); | ||
| 159 | } | ||
| 160 | try group.call(findNativeKernel32LibDir, self, loop, sdk); | ||
| 161 | try group.call(findNativeIncludeDirWindows, self, loop, sdk); | ||
| 162 | try group.call(findNativeLibDirWindows, self, loop, sdk); | ||
| 163 | }, | ||
| 164 | c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory, | ||
| 165 | c.ZigFindWindowsSdkError.NotFound => return error.NotFound, | ||
| 166 | c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound, | ||
| 167 | } | ||
| 168 | }, | ||
| 169 | builtin.Os.linux => { | ||
| 170 | try group.call(findNativeIncludeDirLinux, self, loop); | ||
| 171 | try group.call(findNativeLibDirLinux, self, loop); | ||
| 172 | try group.call(findNativeStaticLibDir, self, loop); | ||
| 173 | try group.call(findNativeDynamicLinker, self, loop); | ||
| 174 | }, | ||
| 175 | builtin.Os.macosx => { | ||
| 176 | self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); | ||
| 177 | }, | ||
| 178 | else => @compileError("unimplemented: find libc for this OS"), | ||
| 179 | } | ||
| 180 | return await (async group.wait() catch unreachable); | ||
| 181 | } | ||
| 182 | |||
| 183 | async fn findNativeIncludeDirLinux(self: *LibCInstallation, loop: *event.Loop) !void { | ||
| 184 | const cc_exe = std.os.getEnvPosix("CC") orelse "cc"; | ||
| 185 | const argv = []const []const u8{ | ||
| 186 | cc_exe, | ||
| 187 | "-E", | ||
| 188 | "-Wp,-v", | ||
| 189 | "-xc", | ||
| 190 | "/dev/null", | ||
| 191 | }; | ||
| 192 | // TODO make this use event loop | ||
| 193 | const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024); | ||
| 194 | const exec_result = if (std.debug.runtime_safety) blk: { | ||
| 195 | break :blk errorable_result catch unreachable; | ||
| 196 | } else blk: { | ||
| 197 | break :blk errorable_result catch |err| switch (err) { | ||
| 198 | error.OutOfMemory => return error.OutOfMemory, | ||
| 199 | else => return error.UnableToSpawnCCompiler, | ||
| 200 | }; | ||
| 201 | }; | ||
| 202 | defer { | ||
| 203 | loop.allocator.free(exec_result.stdout); | ||
| 204 | loop.allocator.free(exec_result.stderr); | ||
| 205 | } | ||
| 206 | |||
| 207 | switch (exec_result.term) { | ||
| 208 | std.os.ChildProcess.Term.Exited => |code| { | ||
| 209 | if (code != 0) return error.CCompilerExitCode; | ||
| 210 | }, | ||
| 211 | else => { | ||
| 212 | return error.CCompilerCrashed; | ||
| 213 | }, | ||
| 214 | } | ||
| 215 | |||
| 216 | var it = std.mem.split(exec_result.stderr, "\n\r"); | ||
| 217 | var search_paths = std.ArrayList([]const u8).init(loop.allocator); | ||
| 218 | defer search_paths.deinit(); | ||
| 219 | while (it.next()) |line| { | ||
| 220 | if (line.len != 0 and line[0] == ' ') { | ||
| 221 | try search_paths.append(line); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | if (search_paths.len == 0) { | ||
| 225 | return error.CCompilerCannotFindHeaders; | ||
| 226 | } | ||
| 227 | |||
| 228 | // search in reverse order | ||
| 229 | var path_i: usize = 0; | ||
| 230 | while (path_i < search_paths.len) : (path_i += 1) { | ||
| 231 | const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1); | ||
| 232 | const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " "); | ||
| 233 | const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h"); | ||
| 234 | defer loop.allocator.free(stdlib_path); | ||
| 235 | |||
| 236 | if (try fileExists(loop.allocator, stdlib_path)) { | ||
| 237 | self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path); | ||
| 238 | return; | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | return error.LibCStdLibHeaderNotFound; | ||
| 243 | } | ||
| 244 | |||
| 245 | async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) !void { | ||
| 246 | var search_buf: [2]Search = undefined; | ||
| 247 | const searches = fillSearch(&search_buf, sdk); | ||
| 248 | |||
| 249 | var result_buf = try std.Buffer.initSize(loop.allocator, 0); | ||
| 250 | defer result_buf.deinit(); | ||
| 251 | |||
| 252 | for (searches) |search| { | ||
| 253 | result_buf.shrink(0); | ||
| 254 | const stream = &std.io.BufferOutStream.init(&result_buf).stream; | ||
| 255 | try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version); | ||
| 256 | |||
| 257 | const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h"); | ||
| 258 | defer loop.allocator.free(stdlib_path); | ||
| 259 | |||
| 260 | if (try fileExists(loop.allocator, stdlib_path)) { | ||
| 261 | self.include_dir = result_buf.toOwnedSlice(); | ||
| 262 | return; | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | return error.LibCStdLibHeaderNotFound; | ||
| 267 | } | ||
| 268 | |||
| 269 | async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void { | ||
| 270 | var search_buf: [2]Search = undefined; | ||
| 271 | const searches = fillSearch(&search_buf, sdk); | ||
| 272 | |||
| 273 | var result_buf = try std.Buffer.initSize(loop.allocator, 0); | ||
| 274 | defer result_buf.deinit(); | ||
| 275 | |||
| 276 | for (searches) |search| { | ||
| 277 | result_buf.shrink(0); | ||
| 278 | const stream = &std.io.BufferOutStream.init(&result_buf).stream; | ||
| 279 | try stream.print("{}\\Lib\\{}\\ucrt\\", search.path, search.version); | ||
| 280 | switch (builtin.arch) { | ||
| 281 | builtin.Arch.i386 => try stream.write("x86"), | ||
| 282 | builtin.Arch.x86_64 => try stream.write("x64"), | ||
| 283 | builtin.Arch.aarch64 => try stream.write("arm"), | ||
| 284 | else => return error.UnsupportedArchitecture, | ||
| 285 | } | ||
| 286 | const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib"); | ||
| 287 | defer loop.allocator.free(ucrt_lib_path); | ||
| 288 | if (try fileExists(loop.allocator, ucrt_lib_path)) { | ||
| 289 | self.lib_dir = result_buf.toOwnedSlice(); | ||
| 290 | return; | ||
| 291 | } | ||
| 292 | } | ||
| 293 | return error.LibCRuntimeNotFound; | ||
| 294 | } | ||
| 295 | |||
| 296 | async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void { | ||
| 297 | self.lib_dir = try await (async ccPrintFileName(loop, "crt1.o", true) catch unreachable); | ||
| 298 | } | ||
| 299 | |||
| 300 | async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void { | ||
| 301 | self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable); | ||
| 302 | } | ||
| 303 | |||
| 304 | async fn findNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop) FindError!void { | ||
| 305 | var dyn_tests = []DynTest{ | ||
| 306 | DynTest{ | ||
| 307 | .name = "ld-linux-x86-64.so.2", | ||
| 308 | .result = null, | ||
| 309 | }, | ||
| 310 | DynTest{ | ||
| 311 | .name = "ld-musl-x86_64.so.1", | ||
| 312 | .result = null, | ||
| 313 | }, | ||
| 314 | }; | ||
| 315 | var group = event.Group(FindError!void).init(loop); | ||
| 316 | errdefer group.cancelAll(); | ||
| 317 | for (dyn_tests) |*dyn_test| { | ||
| 318 | try group.call(testNativeDynamicLinker, self, loop, dyn_test); | ||
| 319 | } | ||
| 320 | try await (async group.wait() catch unreachable); | ||
| 321 | for (dyn_tests) |*dyn_test| { | ||
| 322 | if (dyn_test.result) |result| { | ||
| 323 | self.dynamic_linker_path = result; | ||
| 324 | return; | ||
| 325 | } | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | const DynTest = struct { | ||
| 330 | name: []const u8, | ||
| 331 | result: ?[]const u8, | ||
| 332 | }; | ||
| 333 | |||
| 334 | async fn testNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop, dyn_test: *DynTest) FindError!void { | ||
| 335 | if (await (async ccPrintFileName(loop, dyn_test.name, false) catch unreachable)) |result| { | ||
| 336 | dyn_test.result = result; | ||
| 337 | return; | ||
| 338 | } else |err| switch (err) { | ||
| 339 | error.LibCRuntimeNotFound => return, | ||
| 340 | else => return err, | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | |||
| 345 | async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void { | ||
| 346 | var search_buf: [2]Search = undefined; | ||
| 347 | const searches = fillSearch(&search_buf, sdk); | ||
| 348 | |||
| 349 | var result_buf = try std.Buffer.initSize(loop.allocator, 0); | ||
| 350 | defer result_buf.deinit(); | ||
| 351 | |||
| 352 | for (searches) |search| { | ||
| 353 | result_buf.shrink(0); | ||
| 354 | const stream = &std.io.BufferOutStream.init(&result_buf).stream; | ||
| 355 | try stream.print("{}\\Lib\\{}\\um\\", search.path, search.version); | ||
| 356 | switch (builtin.arch) { | ||
| 357 | builtin.Arch.i386 => try stream.write("x86\\"), | ||
| 358 | builtin.Arch.x86_64 => try stream.write("x64\\"), | ||
| 359 | builtin.Arch.aarch64 => try stream.write("arm\\"), | ||
| 360 | else => return error.UnsupportedArchitecture, | ||
| 361 | } | ||
| 362 | const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib"); | ||
| 363 | defer loop.allocator.free(kernel32_path); | ||
| 364 | if (try fileExists(loop.allocator, kernel32_path)) { | ||
| 365 | self.kernel32_lib_dir = result_buf.toOwnedSlice(); | ||
| 366 | return; | ||
| 367 | } | ||
| 368 | } | ||
| 369 | return error.LibCKernel32LibNotFound; | ||
| 370 | } | ||
| 371 | |||
| 372 | fn initEmpty(self: *LibCInstallation) void { | ||
| 373 | self.* = LibCInstallation{ | ||
| 374 | .include_dir = ([*]const u8)(undefined)[0..0], | ||
| 375 | .lib_dir = null, | ||
| 376 | .static_lib_dir = null, | ||
| 377 | .msvc_lib_dir = null, | ||
| 378 | .kernel32_lib_dir = null, | ||
| 379 | .dynamic_linker_path = null, | ||
| 380 | }; | ||
| 381 | } | ||
| 382 | }; | ||
| 383 | |||
| 384 | /// caller owns returned memory | ||
| 385 | async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bool) ![]u8 { | ||
| 386 | const cc_exe = std.os.getEnvPosix("CC") orelse "cc"; | ||
| 387 | const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file); | ||
| 388 | defer loop.allocator.free(arg1); | ||
| 389 | const argv = []const []const u8{ cc_exe, arg1 }; | ||
| 390 | |||
| 391 | // TODO This simulates evented I/O for the child process exec | ||
| 392 | await (async loop.yield() catch unreachable); | ||
| 393 | const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024); | ||
| 394 | const exec_result = if (std.debug.runtime_safety) blk: { | ||
| 395 | break :blk errorable_result catch unreachable; | ||
| 396 | } else blk: { | ||
| 397 | break :blk errorable_result catch |err| switch (err) { | ||
| 398 | error.OutOfMemory => return error.OutOfMemory, | ||
| 399 | else => return error.UnableToSpawnCCompiler, | ||
| 400 | }; | ||
| 401 | }; | ||
| 402 | defer { | ||
| 403 | loop.allocator.free(exec_result.stdout); | ||
| 404 | loop.allocator.free(exec_result.stderr); | ||
| 405 | } | ||
| 406 | switch (exec_result.term) { | ||
| 407 | std.os.ChildProcess.Term.Exited => |code| { | ||
| 408 | if (code != 0) return error.CCompilerExitCode; | ||
| 409 | }, | ||
| 410 | else => { | ||
| 411 | return error.CCompilerCrashed; | ||
| 412 | }, | ||
| 413 | } | ||
| 414 | var it = std.mem.split(exec_result.stdout, "\n\r"); | ||
| 415 | const line = it.next() orelse return error.LibCRuntimeNotFound; | ||
| 416 | const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound; | ||
| 417 | |||
| 418 | if (want_dirname) { | ||
| 419 | return std.mem.dupe(loop.allocator, u8, dirname); | ||
| 420 | } else { | ||
| 421 | return std.mem.dupe(loop.allocator, u8, line); | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | const Search = struct { | ||
| 426 | path: []const u8, | ||
| 427 | version: []const u8, | ||
| 428 | }; | ||
| 429 | |||
| 430 | fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search { | ||
| 431 | var search_end: usize = 0; | ||
| 432 | if (sdk.path10_ptr) |path10_ptr| { | ||
| 433 | if (sdk.version10_ptr) |ver10_ptr| { | ||
| 434 | search_buf[search_end] = Search{ | ||
| 435 | .path = path10_ptr[0..sdk.path10_len], | ||
| 436 | .version = ver10_ptr[0..sdk.version10_len], | ||
| 437 | }; | ||
| 438 | search_end += 1; | ||
| 439 | } | ||
| 440 | } | ||
| 441 | if (sdk.path81_ptr) |path81_ptr| { | ||
| 442 | if (sdk.version81_ptr) |ver81_ptr| { | ||
| 443 | search_buf[search_end] = Search{ | ||
| 444 | .path = path81_ptr[0..sdk.path81_len], | ||
| 445 | .version = ver81_ptr[0..sdk.version81_len], | ||
| 446 | }; | ||
| 447 | search_end += 1; | ||
| 448 | } | ||
| 449 | } | ||
| 450 | return search_buf[0..search_end]; | ||
| 451 | } | ||
| 452 | |||
| 453 | |||
| 454 | fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool { | ||
| 455 | if (std.os.File.access(allocator, path)) |_| { | ||
| 456 | return true; | ||
| 457 | } else |err| switch (err) { | ||
| 458 | error.NotFound, error.PermissionDenied => return false, | ||
| 459 | error.OutOfMemory => return error.OutOfMemory, | ||
| 460 | else => return error.FileSystem, | ||
| 461 | } | ||
| 462 | } | ||
src-self-hosted/link.zig created+724| ... | @@ -0,0 +1,724 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const mem = std.mem; | ||
| 3 | const c = @import("c.zig"); | ||
| 4 | const builtin = @import("builtin"); | ||
| 5 | const ObjectFormat = builtin.ObjectFormat; | ||
| 6 | const Compilation = @import("compilation.zig").Compilation; | ||
| 7 | const Target = @import("target.zig").Target; | ||
| 8 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | ||
| 9 | const assert = std.debug.assert; | ||
| 10 | |||
| 11 | const Context = struct { | ||
| 12 | comp: *Compilation, | ||
| 13 | arena: std.heap.ArenaAllocator, | ||
| 14 | args: std.ArrayList([*]const u8), | ||
| 15 | link_in_crt: bool, | ||
| 16 | |||
| 17 | link_err: error{OutOfMemory}!void, | ||
| 18 | link_msg: std.Buffer, | ||
| 19 | |||
| 20 | libc: *LibCInstallation, | ||
| 21 | out_file_path: std.Buffer, | ||
| 22 | }; | ||
| 23 | |||
| 24 | pub async fn link(comp: *Compilation) !void { | ||
| 25 | var ctx = Context{ | ||
| 26 | .comp = comp, | ||
| 27 | .arena = std.heap.ArenaAllocator.init(comp.gpa()), | ||
| 28 | .args = undefined, | ||
| 29 | .link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe, | ||
| 30 | .link_err = {}, | ||
| 31 | .link_msg = undefined, | ||
| 32 | .libc = undefined, | ||
| 33 | .out_file_path = undefined, | ||
| 34 | }; | ||
| 35 | defer ctx.arena.deinit(); | ||
| 36 | ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator); | ||
| 37 | ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator); | ||
| 38 | |||
| 39 | if (comp.link_out_file) |out_file| { | ||
| 40 | ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, out_file); | ||
| 41 | } else { | ||
| 42 | ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst()); | ||
| 43 | switch (comp.kind) { | ||
| 44 | Compilation.Kind.Exe => { | ||
| 45 | try ctx.out_file_path.append(comp.target.exeFileExt()); | ||
| 46 | }, | ||
| 47 | Compilation.Kind.Lib => { | ||
| 48 | try ctx.out_file_path.append(comp.target.libFileExt(comp.is_static)); | ||
| 49 | }, | ||
| 50 | Compilation.Kind.Obj => { | ||
| 51 | try ctx.out_file_path.append(comp.target.objFileExt()); | ||
| 52 | }, | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | // even though we're calling LLD as a library it thinks the first | ||
| 57 | // argument is its own exe name | ||
| 58 | try ctx.args.append(c"lld"); | ||
| 59 | |||
| 60 | if (comp.haveLibC()) { | ||
| 61 | ctx.libc = ctx.comp.override_libc orelse blk: { | ||
| 62 | switch (comp.target) { | ||
| 63 | Target.Native => { | ||
| 64 | break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound; | ||
| 65 | }, | ||
| 66 | else => return error.LibCRequiredButNotProvidedOrFound, | ||
| 67 | } | ||
| 68 | }; | ||
| 69 | } | ||
| 70 | |||
| 71 | try constructLinkerArgs(&ctx); | ||
| 72 | |||
| 73 | if (comp.verbose_link) { | ||
| 74 | for (ctx.args.toSliceConst()) |arg, i| { | ||
| 75 | const space = if (i == 0) "" else " "; | ||
| 76 | std.debug.warn("{}{s}", space, arg); | ||
| 77 | } | ||
| 78 | std.debug.warn("\n"); | ||
| 79 | } | ||
| 80 | |||
| 81 | const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat()); | ||
| 82 | const args_slice = ctx.args.toSlice(); | ||
| 83 | // Not evented I/O. LLD does its own multithreading internally. | ||
| 84 | if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) { | ||
| 85 | if (!ctx.link_msg.isNull()) { | ||
| 86 | // TODO capture these messages and pass them through the system, reporting them through the | ||
| 87 | // event system instead of printing them directly here. | ||
| 88 | // perhaps try to parse and understand them. | ||
| 89 | std.debug.warn("{}\n", ctx.link_msg.toSliceConst()); | ||
| 90 | } | ||
| 91 | return error.LinkFailed; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | extern fn ZigLLDLink( | ||
| 96 | oformat: c.ZigLLVM_ObjectFormatType, | ||
| 97 | args: [*]const [*]const u8, | ||
| 98 | arg_count: usize, | ||
| 99 | append_diagnostic: extern fn (*c_void, [*]const u8, usize) void, | ||
| 100 | context: *c_void, | ||
| 101 | ) bool; | ||
| 102 | |||
| 103 | extern fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) void { | ||
| 104 | const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); | ||
| 105 | ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]); | ||
| 106 | } | ||
| 107 | |||
| 108 | fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void { | ||
| 109 | if (ctx.link_msg.isNull()) { | ||
| 110 | try ctx.link_msg.resize(0); | ||
| 111 | } | ||
| 112 | try ctx.link_msg.append(msg); | ||
| 113 | } | ||
| 114 | |||
| 115 | fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType { | ||
| 116 | return switch (ofmt) { | ||
| 117 | ObjectFormat.unknown => c.ZigLLVM_UnknownObjectFormat, | ||
| 118 | ObjectFormat.coff => c.ZigLLVM_COFF, | ||
| 119 | ObjectFormat.elf => c.ZigLLVM_ELF, | ||
| 120 | ObjectFormat.macho => c.ZigLLVM_MachO, | ||
| 121 | ObjectFormat.wasm => c.ZigLLVM_Wasm, | ||
| 122 | }; | ||
| 123 | } | ||
| 124 | |||
| 125 | fn constructLinkerArgs(ctx: *Context) !void { | ||
| 126 | switch (ctx.comp.target.getObjectFormat()) { | ||
| 127 | ObjectFormat.unknown => unreachable, | ||
| 128 | ObjectFormat.coff => return constructLinkerArgsCoff(ctx), | ||
| 129 | ObjectFormat.elf => return constructLinkerArgsElf(ctx), | ||
| 130 | ObjectFormat.macho => return constructLinkerArgsMachO(ctx), | ||
| 131 | ObjectFormat.wasm => return constructLinkerArgsWasm(ctx), | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | fn constructLinkerArgsElf(ctx: *Context) !void { | ||
| 136 | // TODO commented out code in this function | ||
| 137 | //if (g->linker_script) { | ||
| 138 | // lj->args.append("-T"); | ||
| 139 | // lj->args.append(g->linker_script); | ||
| 140 | //} | ||
| 141 | |||
| 142 | //if (g->no_rosegment_workaround) { | ||
| 143 | // lj->args.append("--no-rosegment"); | ||
| 144 | //} | ||
| 145 | try ctx.args.append(c"--gc-sections"); | ||
| 146 | |||
| 147 | //lj->args.append("-m"); | ||
| 148 | //lj->args.append(getLDMOption(&g->zig_target)); | ||
| 149 | |||
| 150 | //bool is_lib = g->out_type == OutTypeLib; | ||
| 151 | //bool shared = !g->is_static && is_lib; | ||
| 152 | //Buf *soname = nullptr; | ||
| 153 | if (ctx.comp.is_static) { | ||
| 154 | if (ctx.comp.target.isArmOrThumb()) { | ||
| 155 | try ctx.args.append(c"-Bstatic"); | ||
| 156 | } else { | ||
| 157 | try ctx.args.append(c"-static"); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | //} else if (shared) { | ||
| 161 | // lj->args.append("-shared"); | ||
| 162 | |||
| 163 | // if (buf_len(&lj->out_file) == 0) { | ||
| 164 | // buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "", | ||
| 165 | // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); | ||
| 166 | // } | ||
| 167 | // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major); | ||
| 168 | //} | ||
| 169 | |||
| 170 | try ctx.args.append(c"-o"); | ||
| 171 | try ctx.args.append(ctx.out_file_path.ptr()); | ||
| 172 | |||
| 173 | if (ctx.link_in_crt) { | ||
| 174 | const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o"; | ||
| 175 | const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o"; | ||
| 176 | try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o); | ||
| 177 | try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o"); | ||
| 178 | try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino); | ||
| 179 | } | ||
| 180 | |||
| 181 | //for (size_t i = 0; i < g->rpath_list.length; i += 1) { | ||
| 182 | // Buf *rpath = g->rpath_list.at(i); | ||
| 183 | // add_rpath(lj, rpath); | ||
| 184 | //} | ||
| 185 | //if (g->each_lib_rpath) { | ||
| 186 | // for (size_t i = 0; i < g->lib_dirs.length; i += 1) { | ||
| 187 | // const char *lib_dir = g->lib_dirs.at(i); | ||
| 188 | // for (size_t i = 0; i < g->link_libs_list.length; i += 1) { | ||
| 189 | // LinkLib *link_lib = g->link_libs_list.at(i); | ||
| 190 | // if (buf_eql_str(link_lib->name, "c")) { | ||
| 191 | // continue; | ||
| 192 | // } | ||
| 193 | // bool does_exist; | ||
| 194 | // Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name)); | ||
| 195 | // if (os_file_exists(test_path, &does_exist) != ErrorNone) { | ||
| 196 | // zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path)); | ||
| 197 | // } | ||
| 198 | // if (does_exist) { | ||
| 199 | // add_rpath(lj, buf_create_from_str(lib_dir)); | ||
| 200 | // break; | ||
| 201 | // } | ||
| 202 | // } | ||
| 203 | // } | ||
| 204 | //} | ||
| 205 | |||
| 206 | //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { | ||
| 207 | // const char *lib_dir = g->lib_dirs.at(i); | ||
| 208 | // lj->args.append("-L"); | ||
| 209 | // lj->args.append(lib_dir); | ||
| 210 | //} | ||
| 211 | |||
| 212 | if (ctx.comp.haveLibC()) { | ||
| 213 | try ctx.args.append(c"-L"); | ||
| 214 | try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr); | ||
| 215 | |||
| 216 | try ctx.args.append(c"-L"); | ||
| 217 | try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr); | ||
| 218 | |||
| 219 | if (!ctx.comp.is_static) { | ||
| 220 | const dl = blk: { | ||
| 221 | if (ctx.libc.dynamic_linker_path) |dl| break :blk dl; | ||
| 222 | if (ctx.comp.target.getDynamicLinkerPath()) |dl| break :blk dl; | ||
| 223 | return error.LibCMissingDynamicLinker; | ||
| 224 | }; | ||
| 225 | try ctx.args.append(c"-dynamic-linker"); | ||
| 226 | try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | //if (shared) { | ||
| 231 | // lj->args.append("-soname"); | ||
| 232 | // lj->args.append(buf_ptr(soname)); | ||
| 233 | //} | ||
| 234 | |||
| 235 | // .o files | ||
| 236 | for (ctx.comp.link_objects) |link_object| { | ||
| 237 | const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); | ||
| 238 | try ctx.args.append(link_obj_with_null.ptr); | ||
| 239 | } | ||
| 240 | try addFnObjects(ctx); | ||
| 241 | |||
| 242 | //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { | ||
| 243 | // if (g->libc_link_lib == nullptr) { | ||
| 244 | // Buf *builtin_o_path = build_o(g, "builtin"); | ||
| 245 | // lj->args.append(buf_ptr(builtin_o_path)); | ||
| 246 | // } | ||
| 247 | |||
| 248 | // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage | ||
| 249 | // Buf *compiler_rt_o_path = build_compiler_rt(g); | ||
| 250 | // lj->args.append(buf_ptr(compiler_rt_o_path)); | ||
| 251 | //} | ||
| 252 | |||
| 253 | //for (size_t i = 0; i < g->link_libs_list.length; i += 1) { | ||
| 254 | // LinkLib *link_lib = g->link_libs_list.at(i); | ||
| 255 | // if (buf_eql_str(link_lib->name, "c")) { | ||
| 256 | // continue; | ||
| 257 | // } | ||
| 258 | // Buf *arg; | ||
| 259 | // if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") || | ||
| 260 | // buf_ends_with_str(link_lib->name, ".so")) | ||
| 261 | // { | ||
| 262 | // arg = link_lib->name; | ||
| 263 | // } else { | ||
| 264 | // arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); | ||
| 265 | // } | ||
| 266 | // lj->args.append(buf_ptr(arg)); | ||
| 267 | //} | ||
| 268 | |||
| 269 | // libc dep | ||
| 270 | if (ctx.comp.haveLibC()) { | ||
| 271 | if (ctx.comp.is_static) { | ||
| 272 | try ctx.args.append(c"--start-group"); | ||
| 273 | try ctx.args.append(c"-lgcc"); | ||
| 274 | try ctx.args.append(c"-lgcc_eh"); | ||
| 275 | try ctx.args.append(c"-lc"); | ||
| 276 | try ctx.args.append(c"-lm"); | ||
| 277 | try ctx.args.append(c"--end-group"); | ||
| 278 | } else { | ||
| 279 | try ctx.args.append(c"-lgcc"); | ||
| 280 | try ctx.args.append(c"--as-needed"); | ||
| 281 | try ctx.args.append(c"-lgcc_s"); | ||
| 282 | try ctx.args.append(c"--no-as-needed"); | ||
| 283 | try ctx.args.append(c"-lc"); | ||
| 284 | try ctx.args.append(c"-lm"); | ||
| 285 | try ctx.args.append(c"-lgcc"); | ||
| 286 | try ctx.args.append(c"--as-needed"); | ||
| 287 | try ctx.args.append(c"-lgcc_s"); | ||
| 288 | try ctx.args.append(c"--no-as-needed"); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | // crt end | ||
| 293 | if (ctx.link_in_crt) { | ||
| 294 | try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o"); | ||
| 295 | try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o"); | ||
| 296 | } | ||
| 297 | |||
| 298 | if (ctx.comp.target != Target.Native) { | ||
| 299 | try ctx.args.append(c"--allow-shlib-undefined"); | ||
| 300 | } | ||
| 301 | |||
| 302 | if (ctx.comp.target.getOs() == builtin.Os.zen) { | ||
| 303 | try ctx.args.append(c"-e"); | ||
| 304 | try ctx.args.append(c"_start"); | ||
| 305 | |||
| 306 | try ctx.args.append(c"--image-base=0x10000000"); | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void { | ||
| 311 | const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename); | ||
| 312 | const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path); | ||
| 313 | try ctx.args.append(full_path_with_null.ptr); | ||
| 314 | } | ||
| 315 | |||
| 316 | fn constructLinkerArgsCoff(ctx: *Context) !void { | ||
| 317 | try ctx.args.append(c"-NOLOGO"); | ||
| 318 | |||
| 319 | if (!ctx.comp.strip) { | ||
| 320 | try ctx.args.append(c"-DEBUG"); | ||
| 321 | } | ||
| 322 | |||
| 323 | switch (ctx.comp.target.getArch()) { | ||
| 324 | builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"), | ||
| 325 | builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"), | ||
| 326 | builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"), | ||
| 327 | else => return error.UnsupportedLinkArchitecture, | ||
| 328 | } | ||
| 329 | |||
| 330 | if (ctx.comp.windows_subsystem_windows) { | ||
| 331 | try ctx.args.append(c"/SUBSYSTEM:windows"); | ||
| 332 | } else if (ctx.comp.windows_subsystem_console) { | ||
| 333 | try ctx.args.append(c"/SUBSYSTEM:console"); | ||
| 334 | } | ||
| 335 | |||
| 336 | const is_library = ctx.comp.kind == Compilation.Kind.Lib; | ||
| 337 | |||
| 338 | const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst()); | ||
| 339 | try ctx.args.append(out_arg.ptr); | ||
| 340 | |||
| 341 | if (ctx.comp.haveLibC()) { | ||
| 342 | try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr); | ||
| 343 | try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr); | ||
| 344 | try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr); | ||
| 345 | } | ||
| 346 | |||
| 347 | if (ctx.link_in_crt) { | ||
| 348 | const lib_str = if (ctx.comp.is_static) "lib" else ""; | ||
| 349 | const d_str = if (ctx.comp.build_mode == builtin.Mode.Debug) "d" else ""; | ||
| 350 | |||
| 351 | if (ctx.comp.is_static) { | ||
| 352 | const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str); | ||
| 353 | try ctx.args.append(cmt_lib_name.ptr); | ||
| 354 | } else { | ||
| 355 | const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str); | ||
| 356 | try ctx.args.append(msvcrt_lib_name.ptr); | ||
| 357 | } | ||
| 358 | |||
| 359 | const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str); | ||
| 360 | try ctx.args.append(vcruntime_lib_name.ptr); | ||
| 361 | |||
| 362 | const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str); | ||
| 363 | try ctx.args.append(crt_lib_name.ptr); | ||
| 364 | |||
| 365 | // Visual C++ 2015 Conformance Changes | ||
| 366 | // https://msdn.microsoft.com/en-us/library/bb531344.aspx | ||
| 367 | try ctx.args.append(c"legacy_stdio_definitions.lib"); | ||
| 368 | |||
| 369 | // msvcrt depends on kernel32 | ||
| 370 | try ctx.args.append(c"kernel32.lib"); | ||
| 371 | } else { | ||
| 372 | try ctx.args.append(c"-NODEFAULTLIB"); | ||
| 373 | if (!is_library) { | ||
| 374 | try ctx.args.append(c"-ENTRY:WinMainCRTStartup"); | ||
| 375 | // TODO | ||
| 376 | //if (g->have_winmain) { | ||
| 377 | // lj->args.append("-ENTRY:WinMain"); | ||
| 378 | //} else { | ||
| 379 | // lj->args.append("-ENTRY:WinMainCRTStartup"); | ||
| 380 | //} | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | if (is_library and !ctx.comp.is_static) { | ||
| 385 | try ctx.args.append(c"-DLL"); | ||
| 386 | } | ||
| 387 | |||
| 388 | //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { | ||
| 389 | // const char *lib_dir = g->lib_dirs.at(i); | ||
| 390 | // lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir))); | ||
| 391 | //} | ||
| 392 | |||
| 393 | for (ctx.comp.link_objects) |link_object| { | ||
| 394 | const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); | ||
| 395 | try ctx.args.append(link_obj_with_null.ptr); | ||
| 396 | } | ||
| 397 | try addFnObjects(ctx); | ||
| 398 | |||
| 399 | switch (ctx.comp.kind) { | ||
| 400 | Compilation.Kind.Exe, Compilation.Kind.Lib => { | ||
| 401 | if (!ctx.comp.haveLibC()) { | ||
| 402 | @panic("TODO"); | ||
| 403 | //Buf *builtin_o_path = build_o(g, "builtin"); | ||
| 404 | //lj->args.append(buf_ptr(builtin_o_path)); | ||
| 405 | } | ||
| 406 | |||
| 407 | // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage | ||
| 408 | // TODO | ||
| 409 | //Buf *compiler_rt_o_path = build_compiler_rt(g); | ||
| 410 | //lj->args.append(buf_ptr(compiler_rt_o_path)); | ||
| 411 | }, | ||
| 412 | Compilation.Kind.Obj => {}, | ||
| 413 | } | ||
| 414 | |||
| 415 | //Buf *def_contents = buf_alloc(); | ||
| 416 | //ZigList<const char *> gen_lib_args = {0}; | ||
| 417 | //for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { | ||
| 418 | // LinkLib *link_lib = g->link_libs_list.at(lib_i); | ||
| 419 | // if (buf_eql_str(link_lib->name, "c")) { | ||
| 420 | // continue; | ||
| 421 | // } | ||
| 422 | // if (link_lib->provided_explicitly) { | ||
| 423 | // if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) { | ||
| 424 | // Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); | ||
| 425 | // lj->args.append(buf_ptr(arg)); | ||
| 426 | // } | ||
| 427 | // else { | ||
| 428 | // lj->args.append(buf_ptr(link_lib->name)); | ||
| 429 | // } | ||
| 430 | // } else { | ||
| 431 | // buf_resize(def_contents, 0); | ||
| 432 | // buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name)); | ||
| 433 | // for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) { | ||
| 434 | // Buf *symbol_name = link_lib->symbols.at(exp_i); | ||
| 435 | // buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name)); | ||
| 436 | // } | ||
| 437 | // buf_appendf(def_contents, "\n"); | ||
| 438 | |||
| 439 | // Buf *def_path = buf_alloc(); | ||
| 440 | // os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path); | ||
| 441 | // os_write_file(def_path, def_contents); | ||
| 442 | |||
| 443 | // Buf *generated_lib_path = buf_alloc(); | ||
| 444 | // os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path); | ||
| 445 | |||
| 446 | // gen_lib_args.resize(0); | ||
| 447 | // gen_lib_args.append("link"); | ||
| 448 | |||
| 449 | // coff_append_machine_arg(g, &gen_lib_args); | ||
| 450 | // gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path)))); | ||
| 451 | // gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path)))); | ||
| 452 | // Buf diag = BUF_INIT; | ||
| 453 | // if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) { | ||
| 454 | // fprintf(stderr, "%s\n", buf_ptr(&diag)); | ||
| 455 | // exit(1); | ||
| 456 | // } | ||
| 457 | // lj->args.append(buf_ptr(generated_lib_path)); | ||
| 458 | // } | ||
| 459 | //} | ||
| 460 | } | ||
| 461 | |||
| 462 | fn constructLinkerArgsMachO(ctx: *Context) !void { | ||
| 463 | try ctx.args.append(c"-demangle"); | ||
| 464 | |||
| 465 | if (ctx.comp.linker_rdynamic) { | ||
| 466 | try ctx.args.append(c"-export_dynamic"); | ||
| 467 | } | ||
| 468 | |||
| 469 | const is_lib = ctx.comp.kind == Compilation.Kind.Lib; | ||
| 470 | const shared = !ctx.comp.is_static and is_lib; | ||
| 471 | if (ctx.comp.is_static) { | ||
| 472 | try ctx.args.append(c"-static"); | ||
| 473 | } else { | ||
| 474 | try ctx.args.append(c"-dynamic"); | ||
| 475 | } | ||
| 476 | |||
| 477 | //if (is_lib) { | ||
| 478 | // if (!g->is_static) { | ||
| 479 | // lj->args.append("-dylib"); | ||
| 480 | |||
| 481 | // Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major); | ||
| 482 | // lj->args.append("-compatibility_version"); | ||
| 483 | // lj->args.append(buf_ptr(compat_vers)); | ||
| 484 | |||
| 485 | // Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, | ||
| 486 | // g->version_major, g->version_minor, g->version_patch); | ||
| 487 | // lj->args.append("-current_version"); | ||
| 488 | // lj->args.append(buf_ptr(cur_vers)); | ||
| 489 | |||
| 490 | // // TODO getting an error when running an executable when doing this rpath thing | ||
| 491 | // //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", | ||
| 492 | // // buf_ptr(g->root_out_name), g->version_major); | ||
| 493 | // //lj->args.append("-install_name"); | ||
| 494 | // //lj->args.append(buf_ptr(dylib_install_name)); | ||
| 495 | |||
| 496 | // if (buf_len(&lj->out_file) == 0) { | ||
| 497 | // buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", | ||
| 498 | // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); | ||
| 499 | // } | ||
| 500 | // } | ||
| 501 | //} | ||
| 502 | |||
| 503 | try ctx.args.append(c"-arch"); | ||
| 504 | const darwin_arch_str = try std.cstr.addNullByte( | ||
| 505 | &ctx.arena.allocator, | ||
| 506 | ctx.comp.target.getDarwinArchString(), | ||
| 507 | ); | ||
| 508 | try ctx.args.append(darwin_arch_str.ptr); | ||
| 509 | |||
| 510 | const platform = try DarwinPlatform.get(ctx.comp); | ||
| 511 | switch (platform.kind) { | ||
| 512 | DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"), | ||
| 513 | DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"), | ||
| 514 | DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"), | ||
| 515 | } | ||
| 516 | const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro); | ||
| 517 | try ctx.args.append(ver_str.ptr); | ||
| 518 | |||
| 519 | if (ctx.comp.kind == Compilation.Kind.Exe) { | ||
| 520 | if (ctx.comp.is_static) { | ||
| 521 | try ctx.args.append(c"-no_pie"); | ||
| 522 | } else { | ||
| 523 | try ctx.args.append(c"-pie"); | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | try ctx.args.append(c"-o"); | ||
| 528 | try ctx.args.append(ctx.out_file_path.ptr()); | ||
| 529 | |||
| 530 | //for (size_t i = 0; i < g->rpath_list.length; i += 1) { | ||
| 531 | // Buf *rpath = g->rpath_list.at(i); | ||
| 532 | // add_rpath(lj, rpath); | ||
| 533 | //} | ||
| 534 | //add_rpath(lj, &lj->out_file); | ||
| 535 | |||
| 536 | if (shared) { | ||
| 537 | try ctx.args.append(c"-headerpad_max_install_names"); | ||
| 538 | } else if (ctx.comp.is_static) { | ||
| 539 | try ctx.args.append(c"-lcrt0.o"); | ||
| 540 | } else { | ||
| 541 | switch (platform.kind) { | ||
| 542 | DarwinPlatform.Kind.MacOS => { | ||
| 543 | if (platform.versionLessThan(10, 5)) { | ||
| 544 | try ctx.args.append(c"-lcrt1.o"); | ||
| 545 | } else if (platform.versionLessThan(10, 6)) { | ||
| 546 | try ctx.args.append(c"-lcrt1.10.5.o"); | ||
| 547 | } else if (platform.versionLessThan(10, 8)) { | ||
| 548 | try ctx.args.append(c"-lcrt1.10.6.o"); | ||
| 549 | } | ||
| 550 | }, | ||
| 551 | DarwinPlatform.Kind.IPhoneOS => { | ||
| 552 | if (ctx.comp.target.getArch() == builtin.Arch.aarch64) { | ||
| 553 | // iOS does not need any crt1 files for arm64 | ||
| 554 | } else if (platform.versionLessThan(3, 1)) { | ||
| 555 | try ctx.args.append(c"-lcrt1.o"); | ||
| 556 | } else if (platform.versionLessThan(6, 0)) { | ||
| 557 | try ctx.args.append(c"-lcrt1.3.1.o"); | ||
| 558 | } | ||
| 559 | }, | ||
| 560 | DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed | ||
| 561 | } | ||
| 562 | } | ||
| 563 | |||
| 564 | //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { | ||
| 565 | // const char *lib_dir = g->lib_dirs.at(i); | ||
| 566 | // lj->args.append("-L"); | ||
| 567 | // lj->args.append(lib_dir); | ||
| 568 | //} | ||
| 569 | |||
| 570 | for (ctx.comp.link_objects) |link_object| { | ||
| 571 | const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); | ||
| 572 | try ctx.args.append(link_obj_with_null.ptr); | ||
| 573 | } | ||
| 574 | try addFnObjects(ctx); | ||
| 575 | |||
| 576 | //// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce | ||
| 577 | //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { | ||
| 578 | // Buf *compiler_rt_o_path = build_compiler_rt(g); | ||
| 579 | // lj->args.append(buf_ptr(compiler_rt_o_path)); | ||
| 580 | //} | ||
| 581 | |||
| 582 | if (ctx.comp.target == Target.Native) { | ||
| 583 | for (ctx.comp.link_libs_list.toSliceConst()) |lib| { | ||
| 584 | if (mem.eql(u8, lib.name, "c")) { | ||
| 585 | // on Darwin, libSystem has libc in it, but also you have to use it | ||
| 586 | // to make syscalls because the syscall numbers are not documented | ||
| 587 | // and change between versions. | ||
| 588 | // so we always link against libSystem | ||
| 589 | try ctx.args.append(c"-lSystem"); | ||
| 590 | } else { | ||
| 591 | if (mem.indexOfScalar(u8, lib.name, '/') == null) { | ||
| 592 | const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name); | ||
| 593 | try ctx.args.append(arg.ptr); | ||
| 594 | } else { | ||
| 595 | const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name); | ||
| 596 | try ctx.args.append(arg.ptr); | ||
| 597 | } | ||
| 598 | } | ||
| 599 | } | ||
| 600 | } else { | ||
| 601 | try ctx.args.append(c"-undefined"); | ||
| 602 | try ctx.args.append(c"dynamic_lookup"); | ||
| 603 | } | ||
| 604 | |||
| 605 | if (platform.kind == DarwinPlatform.Kind.MacOS) { | ||
| 606 | if (platform.versionLessThan(10, 5)) { | ||
| 607 | try ctx.args.append(c"-lgcc_s.10.4"); | ||
| 608 | } else if (platform.versionLessThan(10, 6)) { | ||
| 609 | try ctx.args.append(c"-lgcc_s.10.5"); | ||
| 610 | } | ||
| 611 | } else { | ||
| 612 | @panic("TODO"); | ||
| 613 | } | ||
| 614 | |||
| 615 | //for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) { | ||
| 616 | // lj->args.append("-framework"); | ||
| 617 | // lj->args.append(buf_ptr(g->darwin_frameworks.at(i))); | ||
| 618 | //} | ||
| 619 | } | ||
| 620 | |||
| 621 | fn constructLinkerArgsWasm(ctx: *Context) void { | ||
| 622 | @panic("TODO"); | ||
| 623 | } | ||
| 624 | |||
| 625 | fn addFnObjects(ctx: *Context) !void { | ||
| 626 | // at this point it's guaranteed nobody else has this lock, so we circumvent it | ||
| 627 | // and avoid having to be a coroutine | ||
| 628 | const fn_link_set = &ctx.comp.fn_link_set.private_data; | ||
| 629 | |||
| 630 | var it = fn_link_set.first; | ||
| 631 | while (it) |node| { | ||
| 632 | const fn_val = node.data orelse { | ||
| 633 | // handle the tombstone. See Value.Fn.destroy. | ||
| 634 | it = node.next; | ||
| 635 | fn_link_set.remove(node); | ||
| 636 | ctx.comp.gpa().destroy(node); | ||
| 637 | continue; | ||
| 638 | }; | ||
| 639 | try ctx.args.append(fn_val.containing_object.ptr()); | ||
| 640 | it = node.next; | ||
| 641 | } | ||
| 642 | } | ||
| 643 | |||
| 644 | const DarwinPlatform = struct { | ||
| 645 | kind: Kind, | ||
| 646 | major: u32, | ||
| 647 | minor: u32, | ||
| 648 | micro: u32, | ||
| 649 | |||
| 650 | const Kind = enum { | ||
| 651 | MacOS, | ||
| 652 | IPhoneOS, | ||
| 653 | IPhoneOSSimulator, | ||
| 654 | }; | ||
| 655 | |||
| 656 | fn get(comp: *Compilation) !DarwinPlatform { | ||
| 657 | var result: DarwinPlatform = undefined; | ||
| 658 | const ver_str = switch (comp.darwin_version_min) { | ||
| 659 | Compilation.DarwinVersionMin.MacOS => |ver| blk: { | ||
| 660 | result.kind = Kind.MacOS; | ||
| 661 | break :blk ver; | ||
| 662 | }, | ||
| 663 | Compilation.DarwinVersionMin.Ios => |ver| blk: { | ||
| 664 | result.kind = Kind.IPhoneOS; | ||
| 665 | break :blk ver; | ||
| 666 | }, | ||
| 667 | Compilation.DarwinVersionMin.None => blk: { | ||
| 668 | assert(comp.target.getOs() == builtin.Os.macosx); | ||
| 669 | result.kind = Kind.MacOS; | ||
| 670 | break :blk "10.10"; | ||
| 671 | }, | ||
| 672 | }; | ||
| 673 | |||
| 674 | var had_extra: bool = undefined; | ||
| 675 | try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,); | ||
| 676 | if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) { | ||
| 677 | return error.InvalidDarwinVersionString; | ||
| 678 | } | ||
| 679 | |||
| 680 | if (result.kind == Kind.IPhoneOS) { | ||
| 681 | switch (comp.target.getArch()) { | ||
| 682 | builtin.Arch.i386, | ||
| 683 | builtin.Arch.x86_64, | ||
| 684 | => result.kind = Kind.IPhoneOSSimulator, | ||
| 685 | else => {}, | ||
| 686 | } | ||
| 687 | } | ||
| 688 | return result; | ||
| 689 | } | ||
| 690 | |||
| 691 | fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool { | ||
| 692 | if (self.major < major) | ||
| 693 | return true; | ||
| 694 | if (self.major > major) | ||
| 695 | return false; | ||
| 696 | if (self.minor < minor) | ||
| 697 | return true; | ||
| 698 | return false; | ||
| 699 | } | ||
| 700 | }; | ||
| 701 | |||
| 702 | /// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the | ||
| 703 | /// grouped values as integers. Numbers which are not provided are set to 0. | ||
| 704 | /// return true if the entire string was parsed (9.2), or all groups were | ||
| 705 | /// parsed (10.3.5extrastuff). | ||
| 706 | fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void { | ||
| 707 | major.* = 0; | ||
| 708 | minor.* = 0; | ||
| 709 | micro.* = 0; | ||
| 710 | had_extra.* = false; | ||
| 711 | |||
| 712 | if (str.len == 0) | ||
| 713 | return error.InvalidDarwinVersionString; | ||
| 714 | |||
| 715 | var start_pos: usize = 0; | ||
| 716 | for ([]*u32{major, minor, micro}) |v| { | ||
| 717 | const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.'); | ||
| 718 | const end_pos = dot_pos orelse str.len; | ||
| 719 | v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString; | ||
| 720 | start_pos = (dot_pos orelse return) + 1; | ||
| 721 | if (start_pos == str.len) return; | ||
| 722 | } | ||
| 723 | had_extra.* = true; | ||
| 724 | } | ||
src-self-hosted/llvm.zig+112-3| ... | @@ -2,6 +2,12 @@ const builtin = @import("builtin"); | ... | @@ -2,6 +2,12 @@ const builtin = @import("builtin"); |
| 2 | const c = @import("c.zig"); | 2 | const c = @import("c.zig"); |
| 3 | const assert = @import("std").debug.assert; | 3 | const assert = @import("std").debug.assert; |
| 4 | 4 | ||
| 5 | // we wrap the c module for 3 reasons: | ||
| 6 | // 1. to avoid accidentally calling the non-thread-safe functions | ||
| 7 | // 2. patch up some of the types to remove nullability | ||
| 8 | // 3. some functions have been augmented by zig_llvm.cpp to be more powerful, | ||
| 9 | // such as ZigLLVMTargetMachineEmitToFile | ||
| 10 | |||
| 5 | pub const AttributeIndex = c_uint; | 11 | pub const AttributeIndex = c_uint; |
| 6 | pub const Bool = c_int; | 12 | pub const Bool = c_int; |
| 7 | 13 | ||
| ... | @@ -12,25 +18,59 @@ pub const ValueRef = removeNullability(c.LLVMValueRef); | ... | @@ -12,25 +18,59 @@ pub const ValueRef = removeNullability(c.LLVMValueRef); |
| 12 | pub const TypeRef = removeNullability(c.LLVMTypeRef); | 18 | pub const TypeRef = removeNullability(c.LLVMTypeRef); |
| 13 | pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef); | 19 | pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef); |
| 14 | pub const AttributeRef = removeNullability(c.LLVMAttributeRef); | 20 | pub const AttributeRef = removeNullability(c.LLVMAttributeRef); |
| 21 | pub const TargetRef = removeNullability(c.LLVMTargetRef); | ||
| 22 | pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef); | ||
| 23 | pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef); | ||
| 24 | pub const DIBuilder = c.ZigLLVMDIBuilder; | ||
| 15 | 25 | ||
| 26 | pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType; | ||
| 16 | pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; | 27 | pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; |
| 17 | pub const AddFunction = c.LLVMAddFunction; | 28 | pub const AddFunction = c.LLVMAddFunction; |
| 29 | pub const AddGlobal = c.LLVMAddGlobal; | ||
| 30 | pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag; | ||
| 31 | pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag; | ||
| 32 | pub const ArrayType = c.LLVMArrayType; | ||
| 18 | pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; | 33 | pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; |
| 34 | pub const ConstAllOnes = c.LLVMConstAllOnes; | ||
| 35 | pub const ConstArray = c.LLVMConstArray; | ||
| 36 | pub const ConstBitCast = c.LLVMConstBitCast; | ||
| 19 | pub const ConstInt = c.LLVMConstInt; | 37 | pub const ConstInt = c.LLVMConstInt; |
| 38 | pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision; | ||
| 39 | pub const ConstNeg = c.LLVMConstNeg; | ||
| 40 | pub const ConstNull = c.LLVMConstNull; | ||
| 20 | pub const ConstStringInContext = c.LLVMConstStringInContext; | 41 | pub const ConstStringInContext = c.LLVMConstStringInContext; |
| 21 | pub const ConstStructInContext = c.LLVMConstStructInContext; | 42 | pub const ConstStructInContext = c.LLVMConstStructInContext; |
| 43 | pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData; | ||
| 22 | pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; | 44 | pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; |
| 45 | pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit; | ||
| 46 | pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder; | ||
| 23 | pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute; | 47 | pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute; |
| 48 | pub const CreateFile = c.ZigLLVMCreateFile; | ||
| 24 | pub const CreateStringAttribute = c.LLVMCreateStringAttribute; | 49 | pub const CreateStringAttribute = c.LLVMCreateStringAttribute; |
| 50 | pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout; | ||
| 51 | pub const CreateTargetMachine = c.LLVMCreateTargetMachine; | ||
| 52 | pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize; | ||
| 25 | pub const DisposeBuilder = c.LLVMDisposeBuilder; | 53 | pub const DisposeBuilder = c.LLVMDisposeBuilder; |
| 54 | pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder; | ||
| 55 | pub const DisposeMessage = c.LLVMDisposeMessage; | ||
| 26 | pub const DisposeModule = c.LLVMDisposeModule; | 56 | pub const DisposeModule = c.LLVMDisposeModule; |
| 57 | pub const DisposeTargetData = c.LLVMDisposeTargetData; | ||
| 58 | pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine; | ||
| 27 | pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext; | 59 | pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext; |
| 28 | pub const DumpModule = c.LLVMDumpModule; | 60 | pub const DumpModule = c.LLVMDumpModule; |
| 29 | pub const FP128TypeInContext = c.LLVMFP128TypeInContext; | 61 | pub const FP128TypeInContext = c.LLVMFP128TypeInContext; |
| 30 | pub const FloatTypeInContext = c.LLVMFloatTypeInContext; | 62 | pub const FloatTypeInContext = c.LLVMFloatTypeInContext; |
| 31 | pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; | 63 | pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; |
| 64 | pub const GetHostCPUName = c.ZigLLVMGetHostCPUName; | ||
| 32 | pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; | 65 | pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; |
| 66 | pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures; | ||
| 67 | pub const GetUndef = c.LLVMGetUndef; | ||
| 33 | pub const HalfTypeInContext = c.LLVMHalfTypeInContext; | 68 | pub const HalfTypeInContext = c.LLVMHalfTypeInContext; |
| 69 | pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers; | ||
| 70 | pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters; | ||
| 71 | pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos; | ||
| 72 | pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs; | ||
| 73 | pub const InitializeAllTargets = c.LLVMInitializeAllTargets; | ||
| 34 | pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext; | 74 | pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext; |
| 35 | pub const Int128TypeInContext = c.LLVMInt128TypeInContext; | 75 | pub const Int128TypeInContext = c.LLVMInt128TypeInContext; |
| 36 | pub const Int16TypeInContext = c.LLVMInt16TypeInContext; | 76 | pub const Int16TypeInContext = c.LLVMInt16TypeInContext; |
| ... | @@ -47,13 +87,26 @@ pub const MDStringInContext = c.LLVMMDStringInContext; | ... | @@ -47,13 +87,26 @@ pub const MDStringInContext = c.LLVMMDStringInContext; |
| 47 | pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; | 87 | pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; |
| 48 | pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; | 88 | pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; |
| 49 | pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; | 89 | pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; |
| 90 | pub const PointerType = c.LLVMPointerType; | ||
| 91 | pub const SetAlignment = c.LLVMSetAlignment; | ||
| 92 | pub const SetDataLayout = c.LLVMSetDataLayout; | ||
| 93 | pub const SetGlobalConstant = c.LLVMSetGlobalConstant; | ||
| 94 | pub const SetInitializer = c.LLVMSetInitializer; | ||
| 95 | pub const SetLinkage = c.LLVMSetLinkage; | ||
| 96 | pub const SetTarget = c.LLVMSetTarget; | ||
| 97 | pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr; | ||
| 50 | pub const StructTypeInContext = c.LLVMStructTypeInContext; | 98 | pub const StructTypeInContext = c.LLVMStructTypeInContext; |
| 51 | pub const TokenTypeInContext = c.LLVMTokenTypeInContext; | 99 | pub const TokenTypeInContext = c.LLVMTokenTypeInContext; |
| 100 | pub const TypeOf = c.LLVMTypeOf; | ||
| 52 | pub const VoidTypeInContext = c.LLVMVoidTypeInContext; | 101 | pub const VoidTypeInContext = c.LLVMVoidTypeInContext; |
| 53 | pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; | 102 | pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; |
| 54 | pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; | 103 | pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; |
| 55 | pub const ConstAllOnes = c.LLVMConstAllOnes; | 104 | |
| 56 | pub const ConstNull = c.LLVMConstNull; | 105 | pub const ConstInBoundsGEP = LLVMConstInBoundsGEP; |
| 106 | pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef; | ||
| 107 | |||
| 108 | pub const GetTargetFromTriple = LLVMGetTargetFromTriple; | ||
| 109 | extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool; | ||
| 57 | 110 | ||
| 58 | pub const VerifyModule = LLVMVerifyModule; | 111 | pub const VerifyModule = LLVMVerifyModule; |
| 59 | extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; | 112 | extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; |
| ... | @@ -83,10 +136,66 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; | ... | @@ -83,10 +136,66 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; |
| 83 | pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; | 136 | pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; |
| 84 | pub const VerifierFailureAction = c.LLVMVerifierFailureAction; | 137 | pub const VerifierFailureAction = c.LLVMVerifierFailureAction; |
| 85 | 138 | ||
| 139 | pub const CodeGenLevelNone = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelNone; | ||
| 140 | pub const CodeGenLevelLess = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelLess; | ||
| 141 | pub const CodeGenLevelDefault = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault; | ||
| 142 | pub const CodeGenLevelAggressive = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive; | ||
| 143 | pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel; | ||
| 144 | |||
| 145 | pub const RelocDefault = c.LLVMRelocMode.LLVMRelocDefault; | ||
| 146 | pub const RelocStatic = c.LLVMRelocMode.LLVMRelocStatic; | ||
| 147 | pub const RelocPIC = c.LLVMRelocMode.LLVMRelocPIC; | ||
| 148 | pub const RelocDynamicNoPic = c.LLVMRelocMode.LLVMRelocDynamicNoPic; | ||
| 149 | pub const RelocMode = c.LLVMRelocMode; | ||
| 150 | |||
| 151 | pub const CodeModelDefault = c.LLVMCodeModel.LLVMCodeModelDefault; | ||
| 152 | pub const CodeModelJITDefault = c.LLVMCodeModel.LLVMCodeModelJITDefault; | ||
| 153 | pub const CodeModelSmall = c.LLVMCodeModel.LLVMCodeModelSmall; | ||
| 154 | pub const CodeModelKernel = c.LLVMCodeModel.LLVMCodeModelKernel; | ||
| 155 | pub const CodeModelMedium = c.LLVMCodeModel.LLVMCodeModelMedium; | ||
| 156 | pub const CodeModelLarge = c.LLVMCodeModel.LLVMCodeModelLarge; | ||
| 157 | pub const CodeModel = c.LLVMCodeModel; | ||
| 158 | |||
| 159 | pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly; | ||
| 160 | pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary; | ||
| 161 | pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr; | ||
| 162 | pub const EmitOutputType = c.ZigLLVM_EmitOutputType; | ||
| 163 | |||
| 164 | pub const CCallConv = c.LLVMCCallConv; | ||
| 165 | pub const FastCallConv = c.LLVMFastCallConv; | ||
| 166 | pub const ColdCallConv = c.LLVMColdCallConv; | ||
| 167 | pub const WebKitJSCallConv = c.LLVMWebKitJSCallConv; | ||
| 168 | pub const AnyRegCallConv = c.LLVMAnyRegCallConv; | ||
| 169 | pub const X86StdcallCallConv = c.LLVMX86StdcallCallConv; | ||
| 170 | pub const X86FastcallCallConv = c.LLVMX86FastcallCallConv; | ||
| 171 | pub const CallConv = c.LLVMCallConv; | ||
| 172 | |||
| 173 | pub const FnInline = extern enum { | ||
| 174 | Auto, | ||
| 175 | Always, | ||
| 176 | Never, | ||
| 177 | }; | ||
| 178 | |||
| 86 | fn removeNullability(comptime T: type) type { | 179 | fn removeNullability(comptime T: type) type { |
| 87 | comptime assert(@typeId(T) == builtin.TypeId.Optional); | 180 | comptime assert(@typeId(T) == builtin.TypeId.Optional); |
| 88 | return T.Child; | 181 | return T.Child; |
| 89 | } | 182 | } |
| 90 | 183 | ||
| 91 | pub const BuildRet = LLVMBuildRet; | 184 | pub const BuildRet = LLVMBuildRet; |
| 92 | extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef; | 185 | extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef; |
| 186 | |||
| 187 | pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile; | ||
| 188 | extern fn ZigLLVMTargetMachineEmitToFile( | ||
| 189 | targ_machine_ref: TargetMachineRef, | ||
| 190 | module_ref: ModuleRef, | ||
| 191 | filename: [*]const u8, | ||
| 192 | output_type: EmitOutputType, | ||
| 193 | error_message: *[*]u8, | ||
| 194 | is_debug: bool, | ||
| 195 | is_small: bool, | ||
| 196 | ) bool; | ||
| 197 | |||
| 198 | pub const BuildCall = ZigLLVMBuildCall; | ||
| 199 | extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef; | ||
| 200 | |||
| 201 | pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage; |
src-self-hosted/main.zig+87-60| ... | @@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal; | ... | @@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal; |
| 18 | const Compilation = @import("compilation.zig").Compilation; | 18 | const Compilation = @import("compilation.zig").Compilation; |
| 19 | const Target = @import("target.zig").Target; | 19 | const Target = @import("target.zig").Target; |
| 20 | const errmsg = @import("errmsg.zig"); | 20 | const errmsg = @import("errmsg.zig"); |
| 21 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | ||
| 21 | 22 | ||
| 22 | var stderr_file: os.File = undefined; | 23 | var stderr_file: os.File = undefined; |
| 23 | var stderr: *io.OutStream(io.FileOutStream.Error) = undefined; | 24 | var stderr: *io.OutStream(io.FileOutStream.Error) = undefined; |
| ... | @@ -28,13 +29,14 @@ const usage = | ... | @@ -28,13 +29,14 @@ const usage = |
| 28 | \\ | 29 | \\ |
| 29 | \\Commands: | 30 | \\Commands: |
| 30 | \\ | 31 | \\ |
| 31 | \\ build-exe [source] Create executable from source or object files | 32 | \\ build-exe [source] Create executable from source or object files |
| 32 | \\ build-lib [source] Create library from source or object files | 33 | \\ build-lib [source] Create library from source or object files |
| 33 | \\ build-obj [source] Create object from source or assembly | 34 | \\ build-obj [source] Create object from source or assembly |
| 34 | \\ fmt [source] Parse file and render in canonical zig format | 35 | \\ fmt [source] Parse file and render in canonical zig format |
| 35 | \\ targets List available compilation targets | 36 | \\ libc [paths_file] Display native libc paths file or validate one |
| 36 | \\ version Print version number and exit | 37 | \\ targets List available compilation targets |
| 37 | \\ zen Print zen of zig and exit | 38 | \\ version Print version number and exit |
| 39 | \\ zen Print zen of zig and exit | ||
| 38 | \\ | 40 | \\ |
| 39 | \\ | 41 | \\ |
| 40 | ; | 42 | ; |
| ... | @@ -85,6 +87,10 @@ pub fn main() !void { | ... | @@ -85,6 +87,10 @@ pub fn main() !void { |
| 85 | .name = "fmt", | 87 | .name = "fmt", |
| 86 | .exec = cmdFmt, | 88 | .exec = cmdFmt, |
| 87 | }, | 89 | }, |
| 90 | Command{ | ||
| 91 | .name = "libc", | ||
| 92 | .exec = cmdLibC, | ||
| 93 | }, | ||
| 88 | Command{ | 94 | Command{ |
| 89 | .name = "targets", | 95 | .name = "targets", |
| 90 | .exec = cmdTargets, | 96 | .exec = cmdTargets, |
| ... | @@ -130,11 +136,10 @@ const usage_build_generic = | ... | @@ -130,11 +136,10 @@ const usage_build_generic = |
| 130 | \\ --color [auto|off|on] Enable or disable colored error messages | 136 | \\ --color [auto|off|on] Enable or disable colored error messages |
| 131 | \\ | 137 | \\ |
| 132 | \\Compile Options: | 138 | \\Compile Options: |
| 139 | \\ --libc [file] Provide a file which specifies libc paths | ||
| 133 | \\ --assembly [source] Add assembly file to build | 140 | \\ --assembly [source] Add assembly file to build |
| 134 | \\ --cache-dir [path] Override the cache directory | ||
| 135 | \\ --emit [filetype] Emit a specific file format as compilation output | 141 | \\ --emit [filetype] Emit a specific file format as compilation output |
| 136 | \\ --enable-timing-info Print timing diagnostics | 142 | \\ --enable-timing-info Print timing diagnostics |
| 137 | \\ --libc-include-dir [path] Directory where libc stdlib.h resides | ||
| 138 | \\ --name [name] Override output name | 143 | \\ --name [name] Override output name |
| 139 | \\ --output [file] Override destination path | 144 | \\ --output [file] Override destination path |
| 140 | \\ --output-h [file] Override generated header file path | 145 | \\ --output-h [file] Override generated header file path |
| ... | @@ -163,12 +168,7 @@ const usage_build_generic = | ... | @@ -163,12 +168,7 @@ const usage_build_generic = |
| 163 | \\ | 168 | \\ |
| 164 | \\Link Options: | 169 | \\Link Options: |
| 165 | \\ --ar-path [path] Set the path to ar | 170 | \\ --ar-path [path] Set the path to ar |
| 166 | \\ --dynamic-linker [path] Set the path to ld.so | ||
| 167 | \\ --each-lib-rpath Add rpath for each used dynamic library | 171 | \\ --each-lib-rpath Add rpath for each used dynamic library |
| 168 | \\ --libc-lib-dir [path] Directory where libc crt1.o resides | ||
| 169 | \\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides | ||
| 170 | \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides | ||
| 171 | \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides | ||
| 172 | \\ --library [lib] Link against lib | 172 | \\ --library [lib] Link against lib |
| 173 | \\ --forbid-library [lib] Make it an error to link against lib | 173 | \\ --forbid-library [lib] Make it an error to link against lib |
| 174 | \\ --library-path [dir] Add a directory to the library search path | 174 | \\ --library-path [dir] Add a directory to the library search path |
| ... | @@ -203,14 +203,13 @@ const args_build_generic = []Flag{ | ... | @@ -203,14 +203,13 @@ const args_build_generic = []Flag{ |
| 203 | }), | 203 | }), |
| 204 | 204 | ||
| 205 | Flag.ArgMergeN("--assembly", 1), | 205 | Flag.ArgMergeN("--assembly", 1), |
| 206 | Flag.Arg1("--cache-dir"), | ||
| 207 | Flag.Option("--emit", []const []const u8{ | 206 | Flag.Option("--emit", []const []const u8{ |
| 208 | "asm", | 207 | "asm", |
| 209 | "bin", | 208 | "bin", |
| 210 | "llvm-ir", | 209 | "llvm-ir", |
| 211 | }), | 210 | }), |
| 212 | Flag.Bool("--enable-timing-info"), | 211 | Flag.Bool("--enable-timing-info"), |
| 213 | Flag.Arg1("--libc-include-dir"), | 212 | Flag.Arg1("--libc"), |
| 214 | Flag.Arg1("--name"), | 213 | Flag.Arg1("--name"), |
| 215 | Flag.Arg1("--output"), | 214 | Flag.Arg1("--output"), |
| 216 | Flag.Arg1("--output-h"), | 215 | Flag.Arg1("--output-h"), |
| ... | @@ -234,12 +233,7 @@ const args_build_generic = []Flag{ | ... | @@ -234,12 +233,7 @@ const args_build_generic = []Flag{ |
| 234 | Flag.Arg1("-mllvm"), | 233 | Flag.Arg1("-mllvm"), |
| 235 | 234 | ||
| 236 | Flag.Arg1("--ar-path"), | 235 | Flag.Arg1("--ar-path"), |
| 237 | Flag.Arg1("--dynamic-linker"), | ||
| 238 | Flag.Bool("--each-lib-rpath"), | 236 | Flag.Bool("--each-lib-rpath"), |
| 239 | Flag.Arg1("--libc-lib-dir"), | ||
| 240 | Flag.Arg1("--libc-static-lib-dir"), | ||
| 241 | Flag.Arg1("--msvc-lib-dir"), | ||
| 242 | Flag.Arg1("--kernel32-lib-dir"), | ||
| 243 | Flag.ArgMergeN("--library", 1), | 237 | Flag.ArgMergeN("--library", 1), |
| 244 | Flag.ArgMergeN("--forbid-library", 1), | 238 | Flag.ArgMergeN("--forbid-library", 1), |
| 245 | Flag.ArgMergeN("--library-path", 1), | 239 | Flag.ArgMergeN("--library-path", 1), |
| ... | @@ -363,6 +357,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co | ... | @@ -363,6 +357,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co |
| 363 | } | 357 | } |
| 364 | }; | 358 | }; |
| 365 | 359 | ||
| 360 | const is_static = flags.present("static"); | ||
| 361 | |||
| 366 | const assembly_files = flags.many("assembly"); | 362 | const assembly_files = flags.many("assembly"); |
| 367 | const link_objects = flags.many("object"); | 363 | const link_objects = flags.many("object"); |
| 368 | if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) { | 364 | if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) { |
| ... | @@ -375,21 +371,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co | ... | @@ -375,21 +371,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co |
| 375 | os.exit(1); | 371 | os.exit(1); |
| 376 | } | 372 | } |
| 377 | 373 | ||
| 378 | const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..]; | ||
| 379 | const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch { | ||
| 380 | try stderr.print("invalid cache dir: {}\n", rel_cache_dir); | ||
| 381 | os.exit(1); | ||
| 382 | }; | ||
| 383 | defer allocator.free(full_cache_dir); | ||
| 384 | |||
| 385 | const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); | 374 | const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); |
| 386 | defer allocator.free(zig_lib_dir); | 375 | defer allocator.free(zig_lib_dir); |
| 387 | 376 | ||
| 377 | var override_libc: LibCInstallation = undefined; | ||
| 378 | |||
| 388 | var loop: event.Loop = undefined; | 379 | var loop: event.Loop = undefined; |
| 389 | try loop.initMultiThreaded(allocator); | 380 | try loop.initMultiThreaded(allocator); |
| 390 | defer loop.deinit(); | 381 | defer loop.deinit(); |
| 391 | 382 | ||
| 392 | var event_loop_local = EventLoopLocal.init(&loop); | 383 | var event_loop_local = try EventLoopLocal.init(&loop); |
| 393 | defer event_loop_local.deinit(); | 384 | defer event_loop_local.deinit(); |
| 394 | 385 | ||
| 395 | var comp = try Compilation.create( | 386 | var comp = try Compilation.create( |
| ... | @@ -399,11 +390,20 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co | ... | @@ -399,11 +390,20 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co |
| 399 | Target.Native, | 390 | Target.Native, |
| 400 | out_type, | 391 | out_type, |
| 401 | build_mode, | 392 | build_mode, |
| 393 | is_static, | ||
| 402 | zig_lib_dir, | 394 | zig_lib_dir, |
| 403 | full_cache_dir, | ||
| 404 | ); | 395 | ); |
| 405 | defer comp.destroy(); | 396 | defer comp.destroy(); |
| 406 | 397 | ||
| 398 | if (flags.single("libc")) |libc_path| { | ||
| 399 | parseLibcPaths(loop.allocator, &override_libc, libc_path); | ||
| 400 | comp.override_libc = &override_libc; | ||
| 401 | } | ||
| 402 | |||
| 403 | for (flags.many("library")) |lib| { | ||
| 404 | _ = try comp.addLinkLib(lib, true); | ||
| 405 | } | ||
| 406 | |||
| 407 | comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10); | 407 | comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10); |
| 408 | comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10); | 408 | comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10); |
| 409 | comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10); | 409 | comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10); |
| ... | @@ -426,26 +426,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co | ... | @@ -426,26 +426,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co |
| 426 | comp.clang_argv = clang_argv_buf.toSliceConst(); | 426 | comp.clang_argv = clang_argv_buf.toSliceConst(); |
| 427 | 427 | ||
| 428 | comp.strip = flags.present("strip"); | 428 | comp.strip = flags.present("strip"); |
| 429 | comp.is_static = flags.present("static"); | ||
| 430 | |||
| 431 | if (flags.single("libc-lib-dir")) |libc_lib_dir| { | ||
| 432 | comp.libc_lib_dir = libc_lib_dir; | ||
| 433 | } | ||
| 434 | if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| { | ||
| 435 | comp.libc_static_lib_dir = libc_static_lib_dir; | ||
| 436 | } | ||
| 437 | if (flags.single("libc-include-dir")) |libc_include_dir| { | ||
| 438 | comp.libc_include_dir = libc_include_dir; | ||
| 439 | } | ||
| 440 | if (flags.single("msvc-lib-dir")) |msvc_lib_dir| { | ||
| 441 | comp.msvc_lib_dir = msvc_lib_dir; | ||
| 442 | } | ||
| 443 | if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| { | ||
| 444 | comp.kernel32_lib_dir = kernel32_lib_dir; | ||
| 445 | } | ||
| 446 | if (flags.single("dynamic-linker")) |dynamic_linker| { | ||
| 447 | comp.dynamic_linker = dynamic_linker; | ||
| 448 | } | ||
| 449 | 429 | ||
| 450 | comp.verbose_tokenize = flags.present("verbose-tokenize"); | 430 | comp.verbose_tokenize = flags.present("verbose-tokenize"); |
| 451 | comp.verbose_ast_tree = flags.present("verbose-ast-tree"); | 431 | comp.verbose_ast_tree = flags.present("verbose-ast-tree"); |
| ... | @@ -481,9 +461,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co | ... | @@ -481,9 +461,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co |
| 481 | } | 461 | } |
| 482 | 462 | ||
| 483 | comp.emit_file_type = emit_type; | 463 | comp.emit_file_type = emit_type; |
| 484 | comp.link_objects = link_objects; | ||
| 485 | comp.assembly_files = assembly_files; | 464 | comp.assembly_files = assembly_files; |
| 486 | comp.link_out_file = flags.single("out-file"); | 465 | comp.link_out_file = flags.single("output"); |
| 466 | comp.link_objects = link_objects; | ||
| 487 | 467 | ||
| 488 | try comp.build(); | 468 | try comp.build(); |
| 489 | const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color); | 469 | const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color); |
| ... | @@ -497,7 +477,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { | ... | @@ -497,7 +477,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { |
| 497 | 477 | ||
| 498 | switch (build_event) { | 478 | switch (build_event) { |
| 499 | Compilation.Event.Ok => { | 479 | Compilation.Event.Ok => { |
| 500 | std.debug.warn("Build succeeded\n"); | ||
| 501 | return; | 480 | return; |
| 502 | }, | 481 | }, |
| 503 | Compilation.Event.Error => |err| { | 482 | Compilation.Event.Error => |err| { |
| ... | @@ -506,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { | ... | @@ -506,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { |
| 506 | }, | 485 | }, |
| 507 | Compilation.Event.Fail => |msgs| { | 486 | Compilation.Event.Fail => |msgs| { |
| 508 | for (msgs) |msg| { | 487 | for (msgs) |msg| { |
| 509 | errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1); | 488 | defer msg.destroy(); |
| 489 | msg.printToFile(&stderr_file, color) catch os.exit(1); | ||
| 510 | } | 490 | } |
| 511 | }, | 491 | }, |
| 512 | } | 492 | } |
| ... | @@ -577,6 +557,53 @@ const Fmt = struct { | ... | @@ -577,6 +557,53 @@ const Fmt = struct { |
| 577 | } | 557 | } |
| 578 | }; | 558 | }; |
| 579 | 559 | ||
| 560 | fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void { | ||
| 561 | libc.parse(allocator, libc_paths_file, stderr) catch |err| { | ||
| 562 | stderr.print( | ||
| 563 | "Unable to parse libc path file '{}': {}.\n" ++ | ||
| 564 | "Try running `zig libc` to see an example for the native target.\n", | ||
| 565 | libc_paths_file, | ||
| 566 | @errorName(err), | ||
| 567 | ) catch os.exit(1); | ||
| 568 | os.exit(1); | ||
| 569 | }; | ||
| 570 | } | ||
| 571 | |||
| 572 | fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void { | ||
| 573 | switch (args.len) { | ||
| 574 | 0 => {}, | ||
| 575 | 1 => { | ||
| 576 | var libc_installation: LibCInstallation = undefined; | ||
| 577 | parseLibcPaths(allocator, &libc_installation, args[0]); | ||
| 578 | return; | ||
| 579 | }, | ||
| 580 | else => { | ||
| 581 | try stderr.print("unexpected extra parameter: {}\n", args[1]); | ||
| 582 | os.exit(1); | ||
| 583 | }, | ||
| 584 | } | ||
| 585 | |||
| 586 | var loop: event.Loop = undefined; | ||
| 587 | try loop.initMultiThreaded(allocator); | ||
| 588 | defer loop.deinit(); | ||
| 589 | |||
| 590 | var event_loop_local = try EventLoopLocal.init(&loop); | ||
| 591 | defer event_loop_local.deinit(); | ||
| 592 | |||
| 593 | const handle = try async<loop.allocator> findLibCAsync(&event_loop_local); | ||
| 594 | defer cancel handle; | ||
| 595 | |||
| 596 | loop.run(); | ||
| 597 | } | ||
| 598 | |||
| 599 | async fn findLibCAsync(event_loop_local: *EventLoopLocal) void { | ||
| 600 | const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| { | ||
| 601 | stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1); | ||
| 602 | os.exit(1); | ||
| 603 | }; | ||
| 604 | libc.render(stdout) catch os.exit(1); | ||
| 605 | } | ||
| 606 | |||
| 580 | fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { | 607 | fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { |
| 581 | var flags = try Args.parse(allocator, args_fmt_spec, args); | 608 | var flags = try Args.parse(allocator, args_fmt_spec, args); |
| 582 | defer flags.deinit(); | 609 | defer flags.deinit(); |
| ... | @@ -620,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { | ... | @@ -620,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { |
| 620 | 647 | ||
| 621 | var error_it = tree.errors.iterator(0); | 648 | var error_it = tree.errors.iterator(0); |
| 622 | while (error_it.next()) |parse_error| { | 649 | while (error_it.next()) |parse_error| { |
| 623 | const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>"); | 650 | const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>"); |
| 624 | defer allocator.destroy(msg); | 651 | defer msg.destroy(); |
| 625 | 652 | ||
| 626 | try errmsg.printToFile(&stderr_file, msg, color); | 653 | try msg.printToFile(&stderr_file, color); |
| 627 | } | 654 | } |
| 628 | if (tree.errors.len != 0) { | 655 | if (tree.errors.len != 0) { |
| 629 | os.exit(1); | 656 | os.exit(1); |
| ... | @@ -676,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { | ... | @@ -676,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { |
| 676 | 703 | ||
| 677 | var error_it = tree.errors.iterator(0); | 704 | var error_it = tree.errors.iterator(0); |
| 678 | while (error_it.next()) |parse_error| { | 705 | while (error_it.next()) |parse_error| { |
| 679 | const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path); | 706 | const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path); |
| 680 | defer allocator.destroy(msg); | 707 | defer msg.destroy(); |
| 681 | 708 | ||
| 682 | try errmsg.printToFile(&stderr_file, msg, color); | 709 | try msg.printToFile(&stderr_file, color); |
| 683 | } | 710 | } |
| 684 | if (tree.errors.len != 0) { | 711 | if (tree.errors.len != 0) { |
| 685 | fmt.any_error = true; | 712 | fmt.any_error = true; |
src-self-hosted/package.zig created+29| ... | @@ -0,0 +1,29 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const mem = std.mem; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const Buffer = std.Buffer; | ||
| 5 | |||
| 6 | pub const Package = struct { | ||
| 7 | root_src_dir: Buffer, | ||
| 8 | root_src_path: Buffer, | ||
| 9 | |||
| 10 | /// relative to root_src_dir | ||
| 11 | table: Table, | ||
| 12 | |||
| 13 | pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8); | ||
| 14 | |||
| 15 | /// makes internal copies of root_src_dir and root_src_path | ||
| 16 | /// allocator should be an arena allocator because Package never frees anything | ||
| 17 | pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package { | ||
| 18 | return allocator.create(Package{ | ||
| 19 | .root_src_dir = try Buffer.init(allocator, root_src_dir), | ||
| 20 | .root_src_path = try Buffer.init(allocator, root_src_path), | ||
| 21 | .table = Table.init(allocator), | ||
| 22 | }); | ||
| 23 | } | ||
| 24 | |||
| 25 | pub fn add(self: *Package, name: []const u8, package: *Package) !void { | ||
| 26 | const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package); | ||
| 27 | assert(entry == null); | ||
| 28 | } | ||
| 29 | }; | ||
src-self-hosted/parsed_file.zig deleted-6| ... | @@ -1,6 +0,0 @@ | ||
| 1 | const ast = @import("std").zig.ast; | ||
| 2 | |||
| 3 | pub const ParsedFile = struct { | ||
| 4 | tree: ast.Tree, | ||
| 5 | realpath: []const u8, | ||
| 6 | }; | ||
src-self-hosted/scope.zig+114-39| ... | @@ -8,6 +8,8 @@ const ast = std.zig.ast; | ... | @@ -8,6 +8,8 @@ const ast = std.zig.ast; |
| 8 | const Value = @import("value.zig").Value; | 8 | const Value = @import("value.zig").Value; |
| 9 | const ir = @import("ir.zig"); | 9 | const ir = @import("ir.zig"); |
| 10 | const Span = @import("errmsg.zig").Span; | 10 | const Span = @import("errmsg.zig").Span; |
| 11 | const assert = std.debug.assert; | ||
| 12 | const event = std.event; | ||
| 11 | 13 | ||
| 12 | pub const Scope = struct { | 14 | pub const Scope = struct { |
| 13 | id: Id, | 15 | id: Id, |
| ... | @@ -23,7 +25,8 @@ pub const Scope = struct { | ... | @@ -23,7 +25,8 @@ pub const Scope = struct { |
| 23 | if (base.ref_count == 0) { | 25 | if (base.ref_count == 0) { |
| 24 | if (base.parent) |parent| parent.deref(comp); | 26 | if (base.parent) |parent| parent.deref(comp); |
| 25 | switch (base.id) { | 27 | switch (base.id) { |
| 26 | Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(), | 28 | Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp), |
| 29 | Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(comp), | ||
| 27 | Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp), | 30 | Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp), |
| 28 | Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp), | 31 | Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp), |
| 29 | Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), | 32 | Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), |
| ... | @@ -33,6 +36,15 @@ pub const Scope = struct { | ... | @@ -33,6 +36,15 @@ pub const Scope = struct { |
| 33 | } | 36 | } |
| 34 | } | 37 | } |
| 35 | 38 | ||
| 39 | pub fn findRoot(base: *Scope) *Root { | ||
| 40 | var scope = base; | ||
| 41 | while (scope.parent) |parent| { | ||
| 42 | scope = parent; | ||
| 43 | } | ||
| 44 | assert(scope.id == Id.Root); | ||
| 45 | return @fieldParentPtr(Root, "base", scope); | ||
| 46 | } | ||
| 47 | |||
| 36 | pub fn findFnDef(base: *Scope) ?*FnDef { | 48 | pub fn findFnDef(base: *Scope) ?*FnDef { |
| 37 | var scope = base; | 49 | var scope = base; |
| 38 | while (true) { | 50 | while (true) { |
| ... | @@ -44,12 +56,33 @@ pub const Scope = struct { | ... | @@ -44,12 +56,33 @@ pub const Scope = struct { |
| 44 | Id.Defer, | 56 | Id.Defer, |
| 45 | Id.DeferExpr, | 57 | Id.DeferExpr, |
| 46 | Id.CompTime, | 58 | Id.CompTime, |
| 59 | Id.Root, | ||
| 60 | => scope = scope.parent orelse return null, | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | pub fn findDeferExpr(base: *Scope) ?*DeferExpr { | ||
| 66 | var scope = base; | ||
| 67 | while (true) { | ||
| 68 | switch (scope.id) { | ||
| 69 | Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base), | ||
| 70 | |||
| 71 | Id.FnDef, | ||
| 72 | Id.Decls, | ||
| 73 | => return null, | ||
| 74 | |||
| 75 | Id.Block, | ||
| 76 | Id.Defer, | ||
| 77 | Id.CompTime, | ||
| 78 | Id.Root, | ||
| 47 | => scope = scope.parent orelse return null, | 79 | => scope = scope.parent orelse return null, |
| 48 | } | 80 | } |
| 49 | } | 81 | } |
| 50 | } | 82 | } |
| 51 | 83 | ||
| 52 | pub const Id = enum { | 84 | pub const Id = enum { |
| 85 | Root, | ||
| 53 | Decls, | 86 | Decls, |
| 54 | Block, | 87 | Block, |
| 55 | FnDef, | 88 | FnDef, |
| ... | @@ -58,42 +91,82 @@ pub const Scope = struct { | ... | @@ -58,42 +91,82 @@ pub const Scope = struct { |
| 58 | DeferExpr, | 91 | DeferExpr, |
| 59 | }; | 92 | }; |
| 60 | 93 | ||
| 94 | pub const Root = struct { | ||
| 95 | base: Scope, | ||
| 96 | tree: *ast.Tree, | ||
| 97 | realpath: []const u8, | ||
| 98 | |||
| 99 | /// Creates a Root scope with 1 reference | ||
| 100 | /// Takes ownership of realpath | ||
| 101 | /// Takes ownership of tree, will deinit and destroy when done. | ||
| 102 | pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root { | ||
| 103 | const self = try comp.gpa().create(Root{ | ||
| 104 | .base = Scope{ | ||
| 105 | .id = Id.Root, | ||
| 106 | .parent = null, | ||
| 107 | .ref_count = 1, | ||
| 108 | }, | ||
| 109 | .tree = tree, | ||
| 110 | .realpath = realpath, | ||
| 111 | }); | ||
| 112 | errdefer comp.gpa().destroy(self); | ||
| 113 | |||
| 114 | return self; | ||
| 115 | } | ||
| 116 | |||
| 117 | pub fn destroy(self: *Root, comp: *Compilation) void { | ||
| 118 | comp.gpa().free(self.tree.source); | ||
| 119 | self.tree.deinit(); | ||
| 120 | comp.gpa().destroy(self.tree); | ||
| 121 | comp.gpa().free(self.realpath); | ||
| 122 | comp.gpa().destroy(self); | ||
| 123 | } | ||
| 124 | }; | ||
| 125 | |||
| 61 | pub const Decls = struct { | 126 | pub const Decls = struct { |
| 62 | base: Scope, | 127 | base: Scope, |
| 63 | table: Decl.Table, | 128 | |
| 129 | /// The lock must be respected for writing. However once name_future resolves, | ||
| 130 | /// readers can freely access it. | ||
| 131 | table: event.Locked(Decl.Table), | ||
| 132 | |||
| 133 | /// Once this future is resolved, the table is complete and available for unlocked | ||
| 134 | /// read-only access. It does not mean all the decls are resolved; it means only that | ||
| 135 | /// the table has all the names. Each decl in the table has its own resolution state. | ||
| 136 | name_future: event.Future(void), | ||
| 64 | 137 | ||
| 65 | /// Creates a Decls scope with 1 reference | 138 | /// Creates a Decls scope with 1 reference |
| 66 | pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls { | 139 | pub fn create(comp: *Compilation, parent: *Scope) !*Decls { |
| 67 | const self = try comp.a().create(Decls{ | 140 | const self = try comp.gpa().create(Decls{ |
| 68 | .base = Scope{ | 141 | .base = Scope{ |
| 69 | .id = Id.Decls, | 142 | .id = Id.Decls, |
| 70 | .parent = parent, | 143 | .parent = parent, |
| 71 | .ref_count = 1, | 144 | .ref_count = 1, |
| 72 | }, | 145 | }, |
| 73 | .table = undefined, | 146 | .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())), |
| 147 | .name_future = event.Future(void).init(comp.loop), | ||
| 74 | }); | 148 | }); |
| 75 | errdefer comp.a().destroy(self); | 149 | parent.ref(); |
| 76 | |||
| 77 | self.table = Decl.Table.init(comp.a()); | ||
| 78 | errdefer self.table.deinit(); | ||
| 79 | |||
| 80 | if (parent) |p| p.ref(); | ||
| 81 | |||
| 82 | return self; | 150 | return self; |
| 83 | } | 151 | } |
| 84 | 152 | ||
| 85 | pub fn destroy(self: *Decls) void { | 153 | pub fn destroy(self: *Decls, comp: *Compilation) void { |
| 86 | self.table.deinit(); | 154 | self.table.deinit(); |
| 87 | self.table.allocator.destroy(self); | 155 | comp.gpa().destroy(self); |
| 156 | } | ||
| 157 | |||
| 158 | pub async fn getTableReadOnly(self: *Decls) *Decl.Table { | ||
| 159 | _ = await (async self.name_future.get() catch unreachable); | ||
| 160 | return &self.table.private_data; | ||
| 88 | } | 161 | } |
| 89 | }; | 162 | }; |
| 90 | 163 | ||
| 91 | pub const Block = struct { | 164 | pub const Block = struct { |
| 92 | base: Scope, | 165 | base: Scope, |
| 93 | incoming_values: std.ArrayList(*ir.Instruction), | 166 | incoming_values: std.ArrayList(*ir.Inst), |
| 94 | incoming_blocks: std.ArrayList(*ir.BasicBlock), | 167 | incoming_blocks: std.ArrayList(*ir.BasicBlock), |
| 95 | end_block: *ir.BasicBlock, | 168 | end_block: *ir.BasicBlock, |
| 96 | is_comptime: *ir.Instruction, | 169 | is_comptime: *ir.Inst, |
| 97 | 170 | ||
| 98 | safety: Safety, | 171 | safety: Safety, |
| 99 | 172 | ||
| ... | @@ -125,8 +198,8 @@ pub const Scope = struct { | ... | @@ -125,8 +198,8 @@ pub const Scope = struct { |
| 125 | }; | 198 | }; |
| 126 | 199 | ||
| 127 | /// Creates a Block scope with 1 reference | 200 | /// Creates a Block scope with 1 reference |
| 128 | pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { | 201 | pub fn create(comp: *Compilation, parent: *Scope) !*Block { |
| 129 | const self = try comp.a().create(Block{ | 202 | const self = try comp.gpa().create(Block{ |
| 130 | .base = Scope{ | 203 | .base = Scope{ |
| 131 | .id = Id.Block, | 204 | .id = Id.Block, |
| 132 | .parent = parent, | 205 | .parent = parent, |
| ... | @@ -138,14 +211,14 @@ pub const Scope = struct { | ... | @@ -138,14 +211,14 @@ pub const Scope = struct { |
| 138 | .is_comptime = undefined, | 211 | .is_comptime = undefined, |
| 139 | .safety = Safety.Auto, | 212 | .safety = Safety.Auto, |
| 140 | }); | 213 | }); |
| 141 | errdefer comp.a().destroy(self); | 214 | errdefer comp.gpa().destroy(self); |
| 142 | 215 | ||
| 143 | if (parent) |p| p.ref(); | 216 | parent.ref(); |
| 144 | return self; | 217 | return self; |
| 145 | } | 218 | } |
| 146 | 219 | ||
| 147 | pub fn destroy(self: *Block, comp: *Compilation) void { | 220 | pub fn destroy(self: *Block, comp: *Compilation) void { |
| 148 | comp.a().destroy(self); | 221 | comp.gpa().destroy(self); |
| 149 | } | 222 | } |
| 150 | }; | 223 | }; |
| 151 | 224 | ||
| ... | @@ -157,8 +230,8 @@ pub const Scope = struct { | ... | @@ -157,8 +230,8 @@ pub const Scope = struct { |
| 157 | 230 | ||
| 158 | /// Creates a FnDef scope with 1 reference | 231 | /// Creates a FnDef scope with 1 reference |
| 159 | /// Must set the fn_val later | 232 | /// Must set the fn_val later |
| 160 | pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef { | 233 | pub fn create(comp: *Compilation, parent: *Scope) !*FnDef { |
| 161 | const self = try comp.a().create(FnDef{ | 234 | const self = try comp.gpa().create(FnDef{ |
| 162 | .base = Scope{ | 235 | .base = Scope{ |
| 163 | .id = Id.FnDef, | 236 | .id = Id.FnDef, |
| 164 | .parent = parent, | 237 | .parent = parent, |
| ... | @@ -167,13 +240,13 @@ pub const Scope = struct { | ... | @@ -167,13 +240,13 @@ pub const Scope = struct { |
| 167 | .fn_val = undefined, | 240 | .fn_val = undefined, |
| 168 | }); | 241 | }); |
| 169 | 242 | ||
| 170 | if (parent) |p| p.ref(); | 243 | parent.ref(); |
| 171 | 244 | ||
| 172 | return self; | 245 | return self; |
| 173 | } | 246 | } |
| 174 | 247 | ||
| 175 | pub fn destroy(self: *FnDef, comp: *Compilation) void { | 248 | pub fn destroy(self: *FnDef, comp: *Compilation) void { |
| 176 | comp.a().destroy(self); | 249 | comp.gpa().destroy(self); |
| 177 | } | 250 | } |
| 178 | }; | 251 | }; |
| 179 | 252 | ||
| ... | @@ -181,8 +254,8 @@ pub const Scope = struct { | ... | @@ -181,8 +254,8 @@ pub const Scope = struct { |
| 181 | base: Scope, | 254 | base: Scope, |
| 182 | 255 | ||
| 183 | /// Creates a CompTime scope with 1 reference | 256 | /// Creates a CompTime scope with 1 reference |
| 184 | pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime { | 257 | pub fn create(comp: *Compilation, parent: *Scope) !*CompTime { |
| 185 | const self = try comp.a().create(CompTime{ | 258 | const self = try comp.gpa().create(CompTime{ |
| 186 | .base = Scope{ | 259 | .base = Scope{ |
| 187 | .id = Id.CompTime, | 260 | .id = Id.CompTime, |
| 188 | .parent = parent, | 261 | .parent = parent, |
| ... | @@ -190,12 +263,12 @@ pub const Scope = struct { | ... | @@ -190,12 +263,12 @@ pub const Scope = struct { |
| 190 | }, | 263 | }, |
| 191 | }); | 264 | }); |
| 192 | 265 | ||
| 193 | if (parent) |p| p.ref(); | 266 | parent.ref(); |
| 194 | return self; | 267 | return self; |
| 195 | } | 268 | } |
| 196 | 269 | ||
| 197 | pub fn destroy(self: *CompTime, comp: *Compilation) void { | 270 | pub fn destroy(self: *CompTime, comp: *Compilation) void { |
| 198 | comp.a().destroy(self); | 271 | comp.gpa().destroy(self); |
| 199 | } | 272 | } |
| 200 | }; | 273 | }; |
| 201 | 274 | ||
| ... | @@ -212,11 +285,11 @@ pub const Scope = struct { | ... | @@ -212,11 +285,11 @@ pub const Scope = struct { |
| 212 | /// Creates a Defer scope with 1 reference | 285 | /// Creates a Defer scope with 1 reference |
| 213 | pub fn create( | 286 | pub fn create( |
| 214 | comp: *Compilation, | 287 | comp: *Compilation, |
| 215 | parent: ?*Scope, | 288 | parent: *Scope, |
| 216 | kind: Kind, | 289 | kind: Kind, |
| 217 | defer_expr_scope: *DeferExpr, | 290 | defer_expr_scope: *DeferExpr, |
| 218 | ) !*Defer { | 291 | ) !*Defer { |
| 219 | const self = try comp.a().create(Defer{ | 292 | const self = try comp.gpa().create(Defer{ |
| 220 | .base = Scope{ | 293 | .base = Scope{ |
| 221 | .id = Id.Defer, | 294 | .id = Id.Defer, |
| 222 | .parent = parent, | 295 | .parent = parent, |
| ... | @@ -225,42 +298,44 @@ pub const Scope = struct { | ... | @@ -225,42 +298,44 @@ pub const Scope = struct { |
| 225 | .defer_expr_scope = defer_expr_scope, | 298 | .defer_expr_scope = defer_expr_scope, |
| 226 | .kind = kind, | 299 | .kind = kind, |
| 227 | }); | 300 | }); |
| 228 | errdefer comp.a().destroy(self); | 301 | errdefer comp.gpa().destroy(self); |
| 229 | 302 | ||
| 230 | defer_expr_scope.base.ref(); | 303 | defer_expr_scope.base.ref(); |
| 231 | 304 | ||
| 232 | if (parent) |p| p.ref(); | 305 | parent.ref(); |
| 233 | return self; | 306 | return self; |
| 234 | } | 307 | } |
| 235 | 308 | ||
| 236 | pub fn destroy(self: *Defer, comp: *Compilation) void { | 309 | pub fn destroy(self: *Defer, comp: *Compilation) void { |
| 237 | self.defer_expr_scope.base.deref(comp); | 310 | self.defer_expr_scope.base.deref(comp); |
| 238 | comp.a().destroy(self); | 311 | comp.gpa().destroy(self); |
| 239 | } | 312 | } |
| 240 | }; | 313 | }; |
| 241 | 314 | ||
| 242 | pub const DeferExpr = struct { | 315 | pub const DeferExpr = struct { |
| 243 | base: Scope, | 316 | base: Scope, |
| 244 | expr_node: *ast.Node, | 317 | expr_node: *ast.Node, |
| 318 | reported_err: bool, | ||
| 245 | 319 | ||
| 246 | /// Creates a DeferExpr scope with 1 reference | 320 | /// Creates a DeferExpr scope with 1 reference |
| 247 | pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { | 321 | pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr { |
| 248 | const self = try comp.a().create(DeferExpr{ | 322 | const self = try comp.gpa().create(DeferExpr{ |
| 249 | .base = Scope{ | 323 | .base = Scope{ |
| 250 | .id = Id.DeferExpr, | 324 | .id = Id.DeferExpr, |
| 251 | .parent = parent, | 325 | .parent = parent, |
| 252 | .ref_count = 1, | 326 | .ref_count = 1, |
| 253 | }, | 327 | }, |
| 254 | .expr_node = expr_node, | 328 | .expr_node = expr_node, |
| 329 | .reported_err = false, | ||
| 255 | }); | 330 | }); |
| 256 | errdefer comp.a().destroy(self); | 331 | errdefer comp.gpa().destroy(self); |
| 257 | 332 | ||
| 258 | if (parent) |p| p.ref(); | 333 | parent.ref(); |
| 259 | return self; | 334 | return self; |
| 260 | } | 335 | } |
| 261 | 336 | ||
| 262 | pub fn destroy(self: *DeferExpr, comp: *Compilation) void { | 337 | pub fn destroy(self: *DeferExpr, comp: *Compilation) void { |
| 263 | comp.a().destroy(self); | 338 | comp.gpa().destroy(self); |
| 264 | } | 339 | } |
| 265 | }; | 340 | }; |
| 266 | }; | 341 | }; |
src-self-hosted/target.zig+529-27| ... | @@ -1,60 +1,562 @@ | ... | @@ -1,60 +1,562 @@ |
| 1 | const std = @import("std"); | ||
| 1 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 2 | const c = @import("c.zig"); | 3 | const llvm = @import("llvm.zig"); |
| 4 | const CInt = @import("c_int.zig").CInt; | ||
| 3 | 5 | ||
| 4 | pub const CrossTarget = struct { | 6 | pub const FloatAbi = enum { |
| 5 | arch: builtin.Arch, | 7 | Hard, |
| 6 | os: builtin.Os, | 8 | Soft, |
| 7 | environ: builtin.Environ, | 9 | SoftFp, |
| 8 | }; | 10 | }; |
| 9 | 11 | ||
| 10 | pub const Target = union(enum) { | 12 | pub const Target = union(enum) { |
| 11 | Native, | 13 | Native, |
| 12 | Cross: CrossTarget, | 14 | Cross: Cross, |
| 13 | 15 | ||
| 14 | pub fn oFileExt(self: *const Target) []const u8 { | 16 | pub const Cross = struct { |
| 15 | const environ = switch (self.*) { | 17 | arch: builtin.Arch, |
| 16 | Target.Native => builtin.environ, | 18 | os: builtin.Os, |
| 17 | Target.Cross => |t| t.environ, | 19 | environ: builtin.Environ, |
| 18 | }; | 20 | object_format: builtin.ObjectFormat, |
| 19 | return switch (environ) { | 21 | }; |
| 20 | builtin.Environ.msvc => ".obj", | 22 | |
| 23 | pub fn objFileExt(self: Target) []const u8 { | ||
| 24 | return switch (self.getObjectFormat()) { | ||
| 25 | builtin.ObjectFormat.coff => ".obj", | ||
| 21 | else => ".o", | 26 | else => ".o", |
| 22 | }; | 27 | }; |
| 23 | } | 28 | } |
| 24 | 29 | ||
| 25 | pub fn exeFileExt(self: *const Target) []const u8 { | 30 | pub fn exeFileExt(self: Target) []const u8 { |
| 26 | return switch (self.getOs()) { | 31 | return switch (self.getOs()) { |
| 27 | builtin.Os.windows => ".exe", | 32 | builtin.Os.windows => ".exe", |
| 28 | else => "", | 33 | else => "", |
| 29 | }; | 34 | }; |
| 30 | } | 35 | } |
| 31 | 36 | ||
| 32 | pub fn getOs(self: *const Target) builtin.Os { | 37 | pub fn libFileExt(self: Target, is_static: bool) []const u8 { |
| 33 | return switch (self.*) { | 38 | return switch (self.getOs()) { |
| 39 | builtin.Os.windows => if (is_static) ".lib" else ".dll", | ||
| 40 | else => if (is_static) ".a" else ".so", | ||
| 41 | }; | ||
| 42 | } | ||
| 43 | |||
| 44 | pub fn getOs(self: Target) builtin.Os { | ||
| 45 | return switch (self) { | ||
| 34 | Target.Native => builtin.os, | 46 | Target.Native => builtin.os, |
| 35 | Target.Cross => |t| t.os, | 47 | @TagType(Target).Cross => |t| t.os, |
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn getArch(self: Target) builtin.Arch { | ||
| 52 | return switch (self) { | ||
| 53 | Target.Native => builtin.arch, | ||
| 54 | @TagType(Target).Cross => |t| t.arch, | ||
| 55 | }; | ||
| 56 | } | ||
| 57 | |||
| 58 | pub fn getEnviron(self: Target) builtin.Environ { | ||
| 59 | return switch (self) { | ||
| 60 | Target.Native => builtin.environ, | ||
| 61 | @TagType(Target).Cross => |t| t.environ, | ||
| 62 | }; | ||
| 63 | } | ||
| 64 | |||
| 65 | pub fn getObjectFormat(self: Target) builtin.ObjectFormat { | ||
| 66 | return switch (self) { | ||
| 67 | Target.Native => builtin.object_format, | ||
| 68 | @TagType(Target).Cross => |t| t.object_format, | ||
| 69 | }; | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn isWasm(self: Target) bool { | ||
| 73 | return switch (self.getArch()) { | ||
| 74 | builtin.Arch.wasm32, builtin.Arch.wasm64 => true, | ||
| 75 | else => false, | ||
| 36 | }; | 76 | }; |
| 37 | } | 77 | } |
| 38 | 78 | ||
| 39 | pub fn isDarwin(self: *const Target) bool { | 79 | pub fn isDarwin(self: Target) bool { |
| 40 | return switch (self.getOs()) { | 80 | return switch (self.getOs()) { |
| 41 | builtin.Os.ios, builtin.Os.macosx => true, | 81 | builtin.Os.ios, builtin.Os.macosx => true, |
| 42 | else => false, | 82 | else => false, |
| 43 | }; | 83 | }; |
| 44 | } | 84 | } |
| 45 | 85 | ||
| 46 | pub fn isWindows(self: *const Target) bool { | 86 | pub fn isWindows(self: Target) bool { |
| 47 | return switch (self.getOs()) { | 87 | return switch (self.getOs()) { |
| 48 | builtin.Os.windows => true, | 88 | builtin.Os.windows => true, |
| 49 | else => false, | 89 | else => false, |
| 50 | }; | 90 | }; |
| 51 | } | 91 | } |
| 52 | }; | ||
| 53 | 92 | ||
| 54 | pub fn initializeAll() void { | 93 | /// TODO expose the arch and subarch separately |
| 55 | c.LLVMInitializeAllTargets(); | 94 | pub fn isArmOrThumb(self: Target) bool { |
| 56 | c.LLVMInitializeAllTargetInfos(); | 95 | return switch (self.getArch()) { |
| 57 | c.LLVMInitializeAllTargetMCs(); | 96 | builtin.Arch.armv8_3a, |
| 58 | c.LLVMInitializeAllAsmPrinters(); | 97 | builtin.Arch.armv8_2a, |
| 59 | c.LLVMInitializeAllAsmParsers(); | 98 | builtin.Arch.armv8_1a, |
| 60 | } | 99 | builtin.Arch.armv8, |
| 100 | builtin.Arch.armv8r, | ||
| 101 | builtin.Arch.armv8m_baseline, | ||
| 102 | builtin.Arch.armv8m_mainline, | ||
| 103 | builtin.Arch.armv7, | ||
| 104 | builtin.Arch.armv7em, | ||
| 105 | builtin.Arch.armv7m, | ||
| 106 | builtin.Arch.armv7s, | ||
| 107 | builtin.Arch.armv7k, | ||
| 108 | builtin.Arch.armv7ve, | ||
| 109 | builtin.Arch.armv6, | ||
| 110 | builtin.Arch.armv6m, | ||
| 111 | builtin.Arch.armv6k, | ||
| 112 | builtin.Arch.armv6t2, | ||
| 113 | builtin.Arch.armv5, | ||
| 114 | builtin.Arch.armv5te, | ||
| 115 | builtin.Arch.armv4t, | ||
| 116 | builtin.Arch.armebv8_3a, | ||
| 117 | builtin.Arch.armebv8_2a, | ||
| 118 | builtin.Arch.armebv8_1a, | ||
| 119 | builtin.Arch.armebv8, | ||
| 120 | builtin.Arch.armebv8r, | ||
| 121 | builtin.Arch.armebv8m_baseline, | ||
| 122 | builtin.Arch.armebv8m_mainline, | ||
| 123 | builtin.Arch.armebv7, | ||
| 124 | builtin.Arch.armebv7em, | ||
| 125 | builtin.Arch.armebv7m, | ||
| 126 | builtin.Arch.armebv7s, | ||
| 127 | builtin.Arch.armebv7k, | ||
| 128 | builtin.Arch.armebv7ve, | ||
| 129 | builtin.Arch.armebv6, | ||
| 130 | builtin.Arch.armebv6m, | ||
| 131 | builtin.Arch.armebv6k, | ||
| 132 | builtin.Arch.armebv6t2, | ||
| 133 | builtin.Arch.armebv5, | ||
| 134 | builtin.Arch.armebv5te, | ||
| 135 | builtin.Arch.armebv4t, | ||
| 136 | builtin.Arch.thumb, | ||
| 137 | builtin.Arch.thumbeb, | ||
| 138 | => true, | ||
| 139 | else => false, | ||
| 140 | }; | ||
| 141 | } | ||
| 142 | |||
| 143 | pub fn initializeAll() void { | ||
| 144 | llvm.InitializeAllTargets(); | ||
| 145 | llvm.InitializeAllTargetInfos(); | ||
| 146 | llvm.InitializeAllTargetMCs(); | ||
| 147 | llvm.InitializeAllAsmPrinters(); | ||
| 148 | llvm.InitializeAllAsmParsers(); | ||
| 149 | } | ||
| 150 | |||
| 151 | pub fn getTriple(self: Target, allocator: *std.mem.Allocator) !std.Buffer { | ||
| 152 | var result = try std.Buffer.initSize(allocator, 0); | ||
| 153 | errdefer result.deinit(); | ||
| 154 | |||
| 155 | // LLVM WebAssembly output support requires the target to be activated at | ||
| 156 | // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly. | ||
| 157 | // | ||
| 158 | // LLVM determines the output format based on the environment suffix, | ||
| 159 | // defaulting to an object based on the architecture. The default format in | ||
| 160 | // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to | ||
| 161 | // explicitly set this ourself in order for it to work. | ||
| 162 | // | ||
| 163 | // This is fixed in LLVM 7 and you will be able to get wasm output by | ||
| 164 | // using the target triple `wasm32-unknown-unknown-unknown`. | ||
| 165 | const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron()); | ||
| 166 | |||
| 167 | var out = &std.io.BufferOutStream.init(&result).stream; | ||
| 168 | try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name); | ||
| 169 | |||
| 170 | return result; | ||
| 171 | } | ||
| 172 | |||
| 173 | pub fn is64bit(self: Target) bool { | ||
| 174 | return self.getArchPtrBitWidth() == 64; | ||
| 175 | } | ||
| 176 | |||
| 177 | pub fn getArchPtrBitWidth(self: Target) u32 { | ||
| 178 | switch (self.getArch()) { | ||
| 179 | builtin.Arch.avr, | ||
| 180 | builtin.Arch.msp430, | ||
| 181 | => return 16, | ||
| 182 | |||
| 183 | builtin.Arch.arc, | ||
| 184 | builtin.Arch.armv8_3a, | ||
| 185 | builtin.Arch.armv8_2a, | ||
| 186 | builtin.Arch.armv8_1a, | ||
| 187 | builtin.Arch.armv8, | ||
| 188 | builtin.Arch.armv8r, | ||
| 189 | builtin.Arch.armv8m_baseline, | ||
| 190 | builtin.Arch.armv8m_mainline, | ||
| 191 | builtin.Arch.armv7, | ||
| 192 | builtin.Arch.armv7em, | ||
| 193 | builtin.Arch.armv7m, | ||
| 194 | builtin.Arch.armv7s, | ||
| 195 | builtin.Arch.armv7k, | ||
| 196 | builtin.Arch.armv7ve, | ||
| 197 | builtin.Arch.armv6, | ||
| 198 | builtin.Arch.armv6m, | ||
| 199 | builtin.Arch.armv6k, | ||
| 200 | builtin.Arch.armv6t2, | ||
| 201 | builtin.Arch.armv5, | ||
| 202 | builtin.Arch.armv5te, | ||
| 203 | builtin.Arch.armv4t, | ||
| 204 | builtin.Arch.armebv8_3a, | ||
| 205 | builtin.Arch.armebv8_2a, | ||
| 206 | builtin.Arch.armebv8_1a, | ||
| 207 | builtin.Arch.armebv8, | ||
| 208 | builtin.Arch.armebv8r, | ||
| 209 | builtin.Arch.armebv8m_baseline, | ||
| 210 | builtin.Arch.armebv8m_mainline, | ||
| 211 | builtin.Arch.armebv7, | ||
| 212 | builtin.Arch.armebv7em, | ||
| 213 | builtin.Arch.armebv7m, | ||
| 214 | builtin.Arch.armebv7s, | ||
| 215 | builtin.Arch.armebv7k, | ||
| 216 | builtin.Arch.armebv7ve, | ||
| 217 | builtin.Arch.armebv6, | ||
| 218 | builtin.Arch.armebv6m, | ||
| 219 | builtin.Arch.armebv6k, | ||
| 220 | builtin.Arch.armebv6t2, | ||
| 221 | builtin.Arch.armebv5, | ||
| 222 | builtin.Arch.armebv5te, | ||
| 223 | builtin.Arch.armebv4t, | ||
| 224 | builtin.Arch.hexagon, | ||
| 225 | builtin.Arch.le32, | ||
| 226 | builtin.Arch.mips, | ||
| 227 | builtin.Arch.mipsel, | ||
| 228 | builtin.Arch.nios2, | ||
| 229 | builtin.Arch.powerpc, | ||
| 230 | builtin.Arch.r600, | ||
| 231 | builtin.Arch.riscv32, | ||
| 232 | builtin.Arch.sparc, | ||
| 233 | builtin.Arch.sparcel, | ||
| 234 | builtin.Arch.tce, | ||
| 235 | builtin.Arch.tcele, | ||
| 236 | builtin.Arch.thumb, | ||
| 237 | builtin.Arch.thumbeb, | ||
| 238 | builtin.Arch.i386, | ||
| 239 | builtin.Arch.xcore, | ||
| 240 | builtin.Arch.nvptx, | ||
| 241 | builtin.Arch.amdil, | ||
| 242 | builtin.Arch.hsail, | ||
| 243 | builtin.Arch.spir, | ||
| 244 | builtin.Arch.kalimbav3, | ||
| 245 | builtin.Arch.kalimbav4, | ||
| 246 | builtin.Arch.kalimbav5, | ||
| 247 | builtin.Arch.shave, | ||
| 248 | builtin.Arch.lanai, | ||
| 249 | builtin.Arch.wasm32, | ||
| 250 | builtin.Arch.renderscript32, | ||
| 251 | => return 32, | ||
| 252 | |||
| 253 | builtin.Arch.aarch64, | ||
| 254 | builtin.Arch.aarch64_be, | ||
| 255 | builtin.Arch.mips64, | ||
| 256 | builtin.Arch.mips64el, | ||
| 257 | builtin.Arch.powerpc64, | ||
| 258 | builtin.Arch.powerpc64le, | ||
| 259 | builtin.Arch.riscv64, | ||
| 260 | builtin.Arch.x86_64, | ||
| 261 | builtin.Arch.nvptx64, | ||
| 262 | builtin.Arch.le64, | ||
| 263 | builtin.Arch.amdil64, | ||
| 264 | builtin.Arch.hsail64, | ||
| 265 | builtin.Arch.spir64, | ||
| 266 | builtin.Arch.wasm64, | ||
| 267 | builtin.Arch.renderscript64, | ||
| 268 | builtin.Arch.amdgcn, | ||
| 269 | builtin.Arch.bpfel, | ||
| 270 | builtin.Arch.bpfeb, | ||
| 271 | builtin.Arch.sparcv9, | ||
| 272 | builtin.Arch.s390x, | ||
| 273 | => return 64, | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | pub fn getFloatAbi(self: Target) FloatAbi { | ||
| 278 | return switch (self.getEnviron()) { | ||
| 279 | builtin.Environ.gnueabihf, | ||
| 280 | builtin.Environ.eabihf, | ||
| 281 | builtin.Environ.musleabihf, | ||
| 282 | => FloatAbi.Hard, | ||
| 283 | else => FloatAbi.Soft, | ||
| 284 | }; | ||
| 285 | } | ||
| 286 | |||
| 287 | pub fn getDynamicLinkerPath(self: Target) ?[]const u8 { | ||
| 288 | const env = self.getEnviron(); | ||
| 289 | const arch = self.getArch(); | ||
| 290 | switch (env) { | ||
| 291 | builtin.Environ.android => { | ||
| 292 | if (self.is64bit()) { | ||
| 293 | return "/system/bin/linker64"; | ||
| 294 | } else { | ||
| 295 | return "/system/bin/linker"; | ||
| 296 | } | ||
| 297 | }, | ||
| 298 | builtin.Environ.gnux32 => { | ||
| 299 | if (arch == builtin.Arch.x86_64) { | ||
| 300 | return "/libx32/ld-linux-x32.so.2"; | ||
| 301 | } | ||
| 302 | }, | ||
| 303 | builtin.Environ.musl, | ||
| 304 | builtin.Environ.musleabi, | ||
| 305 | builtin.Environ.musleabihf, | ||
| 306 | => { | ||
| 307 | if (arch == builtin.Arch.x86_64) { | ||
| 308 | return "/lib/ld-musl-x86_64.so.1"; | ||
| 309 | } | ||
| 310 | }, | ||
| 311 | else => {}, | ||
| 312 | } | ||
| 313 | switch (arch) { | ||
| 314 | builtin.Arch.i386, | ||
| 315 | builtin.Arch.sparc, | ||
| 316 | builtin.Arch.sparcel, | ||
| 317 | => return "/lib/ld-linux.so.2", | ||
| 318 | |||
| 319 | builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1", | ||
| 320 | builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1", | ||
| 321 | |||
| 322 | builtin.Arch.armv8_3a, | ||
| 323 | builtin.Arch.armv8_2a, | ||
| 324 | builtin.Arch.armv8_1a, | ||
| 325 | builtin.Arch.armv8, | ||
| 326 | builtin.Arch.armv8r, | ||
| 327 | builtin.Arch.armv8m_baseline, | ||
| 328 | builtin.Arch.armv8m_mainline, | ||
| 329 | builtin.Arch.armv7, | ||
| 330 | builtin.Arch.armv7em, | ||
| 331 | builtin.Arch.armv7m, | ||
| 332 | builtin.Arch.armv7s, | ||
| 333 | builtin.Arch.armv7k, | ||
| 334 | builtin.Arch.armv7ve, | ||
| 335 | builtin.Arch.armv6, | ||
| 336 | builtin.Arch.armv6m, | ||
| 337 | builtin.Arch.armv6k, | ||
| 338 | builtin.Arch.armv6t2, | ||
| 339 | builtin.Arch.armv5, | ||
| 340 | builtin.Arch.armv5te, | ||
| 341 | builtin.Arch.armv4t, | ||
| 342 | builtin.Arch.thumb, | ||
| 343 | => return switch (self.getFloatAbi()) { | ||
| 344 | FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3", | ||
| 345 | else => return "/lib/ld-linux.so.3", | ||
| 346 | }, | ||
| 347 | |||
| 348 | builtin.Arch.armebv8_3a, | ||
| 349 | builtin.Arch.armebv8_2a, | ||
| 350 | builtin.Arch.armebv8_1a, | ||
| 351 | builtin.Arch.armebv8, | ||
| 352 | builtin.Arch.armebv8r, | ||
| 353 | builtin.Arch.armebv8m_baseline, | ||
| 354 | builtin.Arch.armebv8m_mainline, | ||
| 355 | builtin.Arch.armebv7, | ||
| 356 | builtin.Arch.armebv7em, | ||
| 357 | builtin.Arch.armebv7m, | ||
| 358 | builtin.Arch.armebv7s, | ||
| 359 | builtin.Arch.armebv7k, | ||
| 360 | builtin.Arch.armebv7ve, | ||
| 361 | builtin.Arch.armebv6, | ||
| 362 | builtin.Arch.armebv6m, | ||
| 363 | builtin.Arch.armebv6k, | ||
| 364 | builtin.Arch.armebv6t2, | ||
| 365 | builtin.Arch.armebv5, | ||
| 366 | builtin.Arch.armebv5te, | ||
| 367 | builtin.Arch.armebv4t, | ||
| 368 | builtin.Arch.thumbeb, | ||
| 369 | => return switch (self.getFloatAbi()) { | ||
| 370 | FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3", | ||
| 371 | else => return "/lib/ld-linux.so.3", | ||
| 372 | }, | ||
| 373 | |||
| 374 | builtin.Arch.mips, | ||
| 375 | builtin.Arch.mipsel, | ||
| 376 | builtin.Arch.mips64, | ||
| 377 | builtin.Arch.mips64el, | ||
| 378 | => return null, | ||
| 379 | |||
| 380 | builtin.Arch.powerpc => return "/lib/ld.so.1", | ||
| 381 | builtin.Arch.powerpc64 => return "/lib64/ld64.so.2", | ||
| 382 | builtin.Arch.powerpc64le => return "/lib64/ld64.so.2", | ||
| 383 | builtin.Arch.s390x => return "/lib64/ld64.so.1", | ||
| 384 | builtin.Arch.sparcv9 => return "/lib64/ld-linux.so.2", | ||
| 385 | builtin.Arch.x86_64 => return "/lib64/ld-linux-x86-64.so.2", | ||
| 386 | |||
| 387 | builtin.Arch.arc, | ||
| 388 | builtin.Arch.avr, | ||
| 389 | builtin.Arch.bpfel, | ||
| 390 | builtin.Arch.bpfeb, | ||
| 391 | builtin.Arch.hexagon, | ||
| 392 | builtin.Arch.msp430, | ||
| 393 | builtin.Arch.nios2, | ||
| 394 | builtin.Arch.r600, | ||
| 395 | builtin.Arch.amdgcn, | ||
| 396 | builtin.Arch.riscv32, | ||
| 397 | builtin.Arch.riscv64, | ||
| 398 | builtin.Arch.tce, | ||
| 399 | builtin.Arch.tcele, | ||
| 400 | builtin.Arch.xcore, | ||
| 401 | builtin.Arch.nvptx, | ||
| 402 | builtin.Arch.nvptx64, | ||
| 403 | builtin.Arch.le32, | ||
| 404 | builtin.Arch.le64, | ||
| 405 | builtin.Arch.amdil, | ||
| 406 | builtin.Arch.amdil64, | ||
| 407 | builtin.Arch.hsail, | ||
| 408 | builtin.Arch.hsail64, | ||
| 409 | builtin.Arch.spir, | ||
| 410 | builtin.Arch.spir64, | ||
| 411 | builtin.Arch.kalimbav3, | ||
| 412 | builtin.Arch.kalimbav4, | ||
| 413 | builtin.Arch.kalimbav5, | ||
| 414 | builtin.Arch.shave, | ||
| 415 | builtin.Arch.lanai, | ||
| 416 | builtin.Arch.wasm32, | ||
| 417 | builtin.Arch.wasm64, | ||
| 418 | builtin.Arch.renderscript32, | ||
| 419 | builtin.Arch.renderscript64, | ||
| 420 | => return null, | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef { | ||
| 425 | var result: llvm.TargetRef = undefined; | ||
| 426 | var err_msg: [*]u8 = undefined; | ||
| 427 | if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) { | ||
| 428 | std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg); | ||
| 429 | return error.UnsupportedTarget; | ||
| 430 | } | ||
| 431 | return result; | ||
| 432 | } | ||
| 433 | |||
| 434 | pub fn cIntTypeSizeInBits(self: Target, id: CInt.Id) u32 { | ||
| 435 | const arch = self.getArch(); | ||
| 436 | switch (self.getOs()) { | ||
| 437 | builtin.Os.freestanding => switch (self.getArch()) { | ||
| 438 | builtin.Arch.msp430 => switch (id) { | ||
| 439 | CInt.Id.Short, | ||
| 440 | CInt.Id.UShort, | ||
| 441 | CInt.Id.Int, | ||
| 442 | CInt.Id.UInt, | ||
| 443 | => return 16, | ||
| 444 | CInt.Id.Long, | ||
| 445 | CInt.Id.ULong, | ||
| 446 | => return 32, | ||
| 447 | CInt.Id.LongLong, | ||
| 448 | CInt.Id.ULongLong, | ||
| 449 | => return 64, | ||
| 450 | }, | ||
| 451 | else => switch (id) { | ||
| 452 | CInt.Id.Short, | ||
| 453 | CInt.Id.UShort, | ||
| 454 | => return 16, | ||
| 455 | CInt.Id.Int, | ||
| 456 | CInt.Id.UInt, | ||
| 457 | => return 32, | ||
| 458 | CInt.Id.Long, | ||
| 459 | CInt.Id.ULong, | ||
| 460 | => return self.getArchPtrBitWidth(), | ||
| 461 | CInt.Id.LongLong, | ||
| 462 | CInt.Id.ULongLong, | ||
| 463 | => return 64, | ||
| 464 | }, | ||
| 465 | }, | ||
| 466 | |||
| 467 | builtin.Os.linux, | ||
| 468 | builtin.Os.macosx, | ||
| 469 | builtin.Os.openbsd, | ||
| 470 | builtin.Os.zen, | ||
| 471 | => switch (id) { | ||
| 472 | CInt.Id.Short, | ||
| 473 | CInt.Id.UShort, | ||
| 474 | => return 16, | ||
| 475 | CInt.Id.Int, | ||
| 476 | CInt.Id.UInt, | ||
| 477 | => return 32, | ||
| 478 | CInt.Id.Long, | ||
| 479 | CInt.Id.ULong, | ||
| 480 | => return self.getArchPtrBitWidth(), | ||
| 481 | CInt.Id.LongLong, | ||
| 482 | CInt.Id.ULongLong, | ||
| 483 | => return 64, | ||
| 484 | }, | ||
| 485 | |||
| 486 | builtin.Os.windows => switch (id) { | ||
| 487 | CInt.Id.Short, | ||
| 488 | CInt.Id.UShort, | ||
| 489 | => return 16, | ||
| 490 | CInt.Id.Int, | ||
| 491 | CInt.Id.UInt, | ||
| 492 | => return 32, | ||
| 493 | CInt.Id.Long, | ||
| 494 | CInt.Id.ULong, | ||
| 495 | CInt.Id.LongLong, | ||
| 496 | CInt.Id.ULongLong, | ||
| 497 | => return 64, | ||
| 498 | }, | ||
| 499 | |||
| 500 | builtin.Os.ananas, | ||
| 501 | builtin.Os.cloudabi, | ||
| 502 | builtin.Os.dragonfly, | ||
| 503 | builtin.Os.freebsd, | ||
| 504 | builtin.Os.fuchsia, | ||
| 505 | builtin.Os.ios, | ||
| 506 | builtin.Os.kfreebsd, | ||
| 507 | builtin.Os.lv2, | ||
| 508 | builtin.Os.netbsd, | ||
| 509 | builtin.Os.solaris, | ||
| 510 | builtin.Os.haiku, | ||
| 511 | builtin.Os.minix, | ||
| 512 | builtin.Os.rtems, | ||
| 513 | builtin.Os.nacl, | ||
| 514 | builtin.Os.cnk, | ||
| 515 | builtin.Os.aix, | ||
| 516 | builtin.Os.cuda, | ||
| 517 | builtin.Os.nvcl, | ||
| 518 | builtin.Os.amdhsa, | ||
| 519 | builtin.Os.ps4, | ||
| 520 | builtin.Os.elfiamcu, | ||
| 521 | builtin.Os.tvos, | ||
| 522 | builtin.Os.watchos, | ||
| 523 | builtin.Os.mesa3d, | ||
| 524 | builtin.Os.contiki, | ||
| 525 | builtin.Os.amdpal, | ||
| 526 | => @panic("TODO specify the C integer type sizes for this OS"), | ||
| 527 | } | ||
| 528 | } | ||
| 529 | |||
| 530 | pub fn getDarwinArchString(self: Target) []const u8 { | ||
| 531 | const arch = self.getArch(); | ||
| 532 | switch (arch) { | ||
| 533 | builtin.Arch.aarch64 => return "arm64", | ||
| 534 | builtin.Arch.thumb, | ||
| 535 | builtin.Arch.armv8_3a, | ||
| 536 | builtin.Arch.armv8_2a, | ||
| 537 | builtin.Arch.armv8_1a, | ||
| 538 | builtin.Arch.armv8, | ||
| 539 | builtin.Arch.armv8r, | ||
| 540 | builtin.Arch.armv8m_baseline, | ||
| 541 | builtin.Arch.armv8m_mainline, | ||
| 542 | builtin.Arch.armv7, | ||
| 543 | builtin.Arch.armv7em, | ||
| 544 | builtin.Arch.armv7m, | ||
| 545 | builtin.Arch.armv7s, | ||
| 546 | builtin.Arch.armv7k, | ||
| 547 | builtin.Arch.armv7ve, | ||
| 548 | builtin.Arch.armv6, | ||
| 549 | builtin.Arch.armv6m, | ||
| 550 | builtin.Arch.armv6k, | ||
| 551 | builtin.Arch.armv6t2, | ||
| 552 | builtin.Arch.armv5, | ||
| 553 | builtin.Arch.armv5te, | ||
| 554 | builtin.Arch.armv4t, | ||
| 555 | => return "arm", | ||
| 556 | builtin.Arch.powerpc => return "ppc", | ||
| 557 | builtin.Arch.powerpc64 => return "ppc64", | ||
| 558 | builtin.Arch.powerpc64le => return "ppc64le", | ||
| 559 | else => return @tagName(arch), | ||
| 560 | } | ||
| 561 | } | ||
| 562 | }; |
src-self-hosted/test.zig+90-15| ... | @@ -8,12 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic; | ... | @@ -8,12 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic; |
| 8 | const errmsg = @import("errmsg.zig"); | 8 | const errmsg = @import("errmsg.zig"); |
| 9 | const EventLoopLocal = @import("compilation.zig").EventLoopLocal; | 9 | const EventLoopLocal = @import("compilation.zig").EventLoopLocal; |
| 10 | 10 | ||
| 11 | test "compile errors" { | 11 | var ctx: TestContext = undefined; |
| 12 | var ctx: TestContext = undefined; | 12 | |
| 13 | test "stage2" { | ||
| 13 | try ctx.init(); | 14 | try ctx.init(); |
| 14 | defer ctx.deinit(); | 15 | defer ctx.deinit(); |
| 15 | 16 | ||
| 16 | try @import("../test/stage2/compile_errors.zig").addCases(&ctx); | 17 | try @import("../test/stage2/compile_errors.zig").addCases(&ctx); |
| 18 | try @import("../test/stage2/compare_output.zig").addCases(&ctx); | ||
| 17 | 19 | ||
| 18 | try ctx.run(); | 20 | try ctx.run(); |
| 19 | } | 21 | } |
| ... | @@ -25,7 +27,6 @@ pub const TestContext = struct { | ... | @@ -25,7 +27,6 @@ pub const TestContext = struct { |
| 25 | loop: std.event.Loop, | 27 | loop: std.event.Loop, |
| 26 | event_loop_local: EventLoopLocal, | 28 | event_loop_local: EventLoopLocal, |
| 27 | zig_lib_dir: []u8, | 29 | zig_lib_dir: []u8, |
| 28 | zig_cache_dir: []u8, | ||
| 29 | file_index: std.atomic.Int(usize), | 30 | file_index: std.atomic.Int(usize), |
| 30 | group: std.event.Group(error!void), | 31 | group: std.event.Group(error!void), |
| 31 | any_err: error!void, | 32 | any_err: error!void, |
| ... | @@ -38,7 +39,6 @@ pub const TestContext = struct { | ... | @@ -38,7 +39,6 @@ pub const TestContext = struct { |
| 38 | .loop = undefined, | 39 | .loop = undefined, |
| 39 | .event_loop_local = undefined, | 40 | .event_loop_local = undefined, |
| 40 | .zig_lib_dir = undefined, | 41 | .zig_lib_dir = undefined, |
| 41 | .zig_cache_dir = undefined, | ||
| 42 | .group = undefined, | 42 | .group = undefined, |
| 43 | .file_index = std.atomic.Int(usize).init(0), | 43 | .file_index = std.atomic.Int(usize).init(0), |
| 44 | }; | 44 | }; |
| ... | @@ -46,7 +46,7 @@ pub const TestContext = struct { | ... | @@ -46,7 +46,7 @@ pub const TestContext = struct { |
| 46 | try self.loop.initMultiThreaded(allocator); | 46 | try self.loop.initMultiThreaded(allocator); |
| 47 | errdefer self.loop.deinit(); | 47 | errdefer self.loop.deinit(); |
| 48 | 48 | ||
| 49 | self.event_loop_local = EventLoopLocal.init(&self.loop); | 49 | self.event_loop_local = try EventLoopLocal.init(&self.loop); |
| 50 | errdefer self.event_loop_local.deinit(); | 50 | errdefer self.event_loop_local.deinit(); |
| 51 | 51 | ||
| 52 | self.group = std.event.Group(error!void).init(&self.loop); | 52 | self.group = std.event.Group(error!void).init(&self.loop); |
| ... | @@ -55,16 +55,12 @@ pub const TestContext = struct { | ... | @@ -55,16 +55,12 @@ pub const TestContext = struct { |
| 55 | self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); | 55 | self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); |
| 56 | errdefer allocator.free(self.zig_lib_dir); | 56 | errdefer allocator.free(self.zig_lib_dir); |
| 57 | 57 | ||
| 58 | self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator); | ||
| 59 | errdefer allocator.free(self.zig_cache_dir); | ||
| 60 | |||
| 61 | try std.os.makePath(allocator, tmp_dir_name); | 58 | try std.os.makePath(allocator, tmp_dir_name); |
| 62 | errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {}; | 59 | errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {}; |
| 63 | } | 60 | } |
| 64 | 61 | ||
| 65 | fn deinit(self: *TestContext) void { | 62 | fn deinit(self: *TestContext) void { |
| 66 | std.os.deleteTree(allocator, tmp_dir_name) catch {}; | 63 | std.os.deleteTree(allocator, tmp_dir_name) catch {}; |
| 67 | allocator.free(self.zig_cache_dir); | ||
| 68 | allocator.free(self.zig_lib_dir); | 64 | allocator.free(self.zig_lib_dir); |
| 69 | self.event_loop_local.deinit(); | 65 | self.event_loop_local.deinit(); |
| 70 | self.loop.deinit(); | 66 | self.loop.deinit(); |
| ... | @@ -107,8 +103,8 @@ pub const TestContext = struct { | ... | @@ -107,8 +103,8 @@ pub const TestContext = struct { |
| 107 | Target.Native, | 103 | Target.Native, |
| 108 | Compilation.Kind.Obj, | 104 | Compilation.Kind.Obj, |
| 109 | builtin.Mode.Debug, | 105 | builtin.Mode.Debug, |
| 106 | true, // is_static | ||
| 110 | self.zig_lib_dir, | 107 | self.zig_lib_dir, |
| 111 | self.zig_cache_dir, | ||
| 112 | ); | 108 | ); |
| 113 | errdefer comp.destroy(); | 109 | errdefer comp.destroy(); |
| 114 | 110 | ||
| ... | @@ -117,6 +113,84 @@ pub const TestContext = struct { | ... | @@ -117,6 +113,84 @@ pub const TestContext = struct { |
| 117 | try self.group.call(getModuleEvent, comp, source, path, line, column, msg); | 113 | try self.group.call(getModuleEvent, comp, source, path, line, column, msg); |
| 118 | } | 114 | } |
| 119 | 115 | ||
| 116 | fn testCompareOutputLibC( | ||
| 117 | self: *TestContext, | ||
| 118 | source: []const u8, | ||
| 119 | expected_output: []const u8, | ||
| 120 | ) !void { | ||
| 121 | var file_index_buf: [20]u8 = undefined; | ||
| 122 | const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); | ||
| 123 | const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1); | ||
| 124 | |||
| 125 | const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt()); | ||
| 126 | if (std.os.path.dirname(file1_path)) |dirname| { | ||
| 127 | try std.os.makePath(allocator, dirname); | ||
| 128 | } | ||
| 129 | |||
| 130 | // TODO async I/O | ||
| 131 | try std.io.writeFile(allocator, file1_path, source); | ||
| 132 | |||
| 133 | var comp = try Compilation.create( | ||
| 134 | &self.event_loop_local, | ||
| 135 | "test", | ||
| 136 | file1_path, | ||
| 137 | Target.Native, | ||
| 138 | Compilation.Kind.Exe, | ||
| 139 | builtin.Mode.Debug, | ||
| 140 | false, | ||
| 141 | self.zig_lib_dir, | ||
| 142 | ); | ||
| 143 | errdefer comp.destroy(); | ||
| 144 | |||
| 145 | _ = try comp.addLinkLib("c", true); | ||
| 146 | comp.link_out_file = output_file; | ||
| 147 | try comp.build(); | ||
| 148 | |||
| 149 | try self.group.call(getModuleEventSuccess, comp, output_file, expected_output); | ||
| 150 | } | ||
| 151 | |||
| 152 | async fn getModuleEventSuccess( | ||
| 153 | comp: *Compilation, | ||
| 154 | exe_file: []const u8, | ||
| 155 | expected_output: []const u8, | ||
| 156 | ) !void { | ||
| 157 | // TODO this should not be necessary | ||
| 158 | const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file); | ||
| 159 | |||
| 160 | defer comp.destroy(); | ||
| 161 | const build_event = await (async comp.events.get() catch unreachable); | ||
| 162 | |||
| 163 | switch (build_event) { | ||
| 164 | Compilation.Event.Ok => { | ||
| 165 | const argv = []const []const u8{exe_file_2}; | ||
| 166 | // TODO use event loop | ||
| 167 | const child = try std.os.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024); | ||
| 168 | switch (child.term) { | ||
| 169 | std.os.ChildProcess.Term.Exited => |code| { | ||
| 170 | if (code != 0) { | ||
| 171 | return error.BadReturnCode; | ||
| 172 | } | ||
| 173 | }, | ||
| 174 | else => { | ||
| 175 | return error.Crashed; | ||
| 176 | }, | ||
| 177 | } | ||
| 178 | if (!mem.eql(u8, child.stdout, expected_output)) { | ||
| 179 | return error.OutputMismatch; | ||
| 180 | } | ||
| 181 | }, | ||
| 182 | Compilation.Event.Error => |err| return err, | ||
| 183 | Compilation.Event.Fail => |msgs| { | ||
| 184 | var stderr = try std.io.getStdErr(); | ||
| 185 | try stderr.write("build incorrectly failed:\n"); | ||
| 186 | for (msgs) |msg| { | ||
| 187 | defer msg.destroy(); | ||
| 188 | try msg.printToFile(&stderr, errmsg.Color.Auto); | ||
| 189 | } | ||
| 190 | }, | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 120 | async fn getModuleEvent( | 194 | async fn getModuleEvent( |
| 121 | comp: *Compilation, | 195 | comp: *Compilation, |
| 122 | source: []const u8, | 196 | source: []const u8, |
| ... | @@ -138,10 +212,10 @@ pub const TestContext = struct { | ... | @@ -138,10 +212,10 @@ pub const TestContext = struct { |
| 138 | Compilation.Event.Fail => |msgs| { | 212 | Compilation.Event.Fail => |msgs| { |
| 139 | assertOrPanic(msgs.len != 0); | 213 | assertOrPanic(msgs.len != 0); |
| 140 | for (msgs) |msg| { | 214 | for (msgs) |msg| { |
| 141 | if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) { | 215 | if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) { |
| 142 | const first_token = msg.tree.tokens.at(msg.span.first); | 216 | const first_token = msg.getTree().tokens.at(msg.span.first); |
| 143 | const last_token = msg.tree.tokens.at(msg.span.first); | 217 | const last_token = msg.getTree().tokens.at(msg.span.first); |
| 144 | const start_loc = msg.tree.tokenLocationPtr(0, first_token); | 218 | const start_loc = msg.getTree().tokenLocationPtr(0, first_token); |
| 145 | if (start_loc.line + 1 == line and start_loc.column + 1 == column) { | 219 | if (start_loc.line + 1 == line and start_loc.column + 1 == column) { |
| 146 | return; | 220 | return; |
| 147 | } | 221 | } |
| ... | @@ -158,7 +232,8 @@ pub const TestContext = struct { | ... | @@ -158,7 +232,8 @@ pub const TestContext = struct { |
| 158 | std.debug.warn("\n====found:========\n"); | 232 | std.debug.warn("\n====found:========\n"); |
| 159 | var stderr = try std.io.getStdErr(); | 233 | var stderr = try std.io.getStdErr(); |
| 160 | for (msgs) |msg| { | 234 | for (msgs) |msg| { |
| 161 | try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto); | 235 | defer msg.destroy(); |
| 236 | try msg.printToFile(&stderr, errmsg.Color.Auto); | ||
| 162 | } | 237 | } |
| 163 | std.debug.warn("============\n"); | 238 | std.debug.warn("============\n"); |
| 164 | return error.TestFailed; | 239 | return error.TestFailed; |
src-self-hosted/type.zig+419-92| ... | @@ -4,11 +4,17 @@ const Scope = @import("scope.zig").Scope; | ... | @@ -4,11 +4,17 @@ const Scope = @import("scope.zig").Scope; |
| 4 | const Compilation = @import("compilation.zig").Compilation; | 4 | const Compilation = @import("compilation.zig").Compilation; |
| 5 | const Value = @import("value.zig").Value; | 5 | const Value = @import("value.zig").Value; |
| 6 | const llvm = @import("llvm.zig"); | 6 | const llvm = @import("llvm.zig"); |
| 7 | const ObjectFile = @import("codegen.zig").ObjectFile; | 7 | const event = std.event; |
| 8 | const Allocator = std.mem.Allocator; | ||
| 9 | const assert = std.debug.assert; | ||
| 8 | 10 | ||
| 9 | pub const Type = struct { | 11 | pub const Type = struct { |
| 10 | base: Value, | 12 | base: Value, |
| 11 | id: Id, | 13 | id: Id, |
| 14 | name: []const u8, | ||
| 15 | abi_alignment: AbiAlignment, | ||
| 16 | |||
| 17 | pub const AbiAlignment = event.Future(error{OutOfMemory}!u32); | ||
| 12 | 18 | ||
| 13 | pub const Id = builtin.TypeId; | 19 | pub const Id = builtin.TypeId; |
| 14 | 20 | ||
| ... | @@ -42,33 +48,37 @@ pub const Type = struct { | ... | @@ -42,33 +48,37 @@ pub const Type = struct { |
| 42 | } | 48 | } |
| 43 | } | 49 | } |
| 44 | 50 | ||
| 45 | pub fn getLlvmType(base: *Type, ofile: *ObjectFile) (error{OutOfMemory}!llvm.TypeRef) { | 51 | pub fn getLlvmType( |
| 52 | base: *Type, | ||
| 53 | allocator: *Allocator, | ||
| 54 | llvm_context: llvm.ContextRef, | ||
| 55 | ) (error{OutOfMemory}!llvm.TypeRef) { | ||
| 46 | switch (base.id) { | 56 | switch (base.id) { |
| 47 | Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile), | 57 | Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context), |
| 48 | Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile), | 58 | Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context), |
| 49 | Id.Type => unreachable, | 59 | Id.Type => unreachable, |
| 50 | Id.Void => unreachable, | 60 | Id.Void => unreachable, |
| 51 | Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(ofile), | 61 | Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context), |
| 52 | Id.NoReturn => unreachable, | 62 | Id.NoReturn => unreachable, |
| 53 | Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile), | 63 | Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context), |
| 54 | Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile), | 64 | Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context), |
| 55 | Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile), | 65 | Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context), |
| 56 | Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile), | 66 | Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context), |
| 57 | Id.ComptimeFloat => unreachable, | 67 | Id.ComptimeFloat => unreachable, |
| 58 | Id.ComptimeInt => unreachable, | 68 | Id.ComptimeInt => unreachable, |
| 59 | Id.Undefined => unreachable, | 69 | Id.Undefined => unreachable, |
| 60 | Id.Null => unreachable, | 70 | Id.Null => unreachable, |
| 61 | Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile), | 71 | Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context), |
| 62 | Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile), | 72 | Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context), |
| 63 | Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile), | 73 | Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context), |
| 64 | Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile), | 74 | Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context), |
| 65 | Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile), | 75 | Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context), |
| 66 | Id.Namespace => unreachable, | 76 | Id.Namespace => unreachable, |
| 67 | Id.Block => unreachable, | 77 | Id.Block => unreachable, |
| 68 | Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(ofile), | 78 | Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context), |
| 69 | Id.ArgTuple => unreachable, | 79 | Id.ArgTuple => unreachable, |
| 70 | Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile), | 80 | Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context), |
| 71 | Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile), | 81 | Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context), |
| 72 | } | 82 | } |
| 73 | } | 83 | } |
| 74 | 84 | ||
| ... | @@ -151,8 +161,49 @@ pub const Type = struct { | ... | @@ -151,8 +161,49 @@ pub const Type = struct { |
| 151 | std.debug.warn("{}", @tagName(base.id)); | 161 | std.debug.warn("{}", @tagName(base.id)); |
| 152 | } | 162 | } |
| 153 | 163 | ||
| 154 | pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 { | 164 | fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void { |
| 155 | @panic("TODO getAbiAlignment"); | 165 | base.* = Type{ |
| 166 | .base = Value{ | ||
| 167 | .id = Value.Id.Type, | ||
| 168 | .typ = &MetaType.get(comp).base, | ||
| 169 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 170 | }, | ||
| 171 | .id = id, | ||
| 172 | .name = name, | ||
| 173 | .abi_alignment = AbiAlignment.init(comp.loop), | ||
| 174 | }; | ||
| 175 | } | ||
| 176 | |||
| 177 | /// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead. | ||
| 178 | /// Otherwise, this one will grab one from the pool and then release it. | ||
| 179 | pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 { | ||
| 180 | if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*; | ||
| 181 | |||
| 182 | { | ||
| 183 | const held = try comp.event_loop_local.getAnyLlvmContext(); | ||
| 184 | defer held.release(comp.event_loop_local); | ||
| 185 | |||
| 186 | const llvm_context = held.node.data; | ||
| 187 | |||
| 188 | base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable); | ||
| 189 | } | ||
| 190 | base.abi_alignment.resolve(); | ||
| 191 | return base.abi_alignment.data; | ||
| 192 | } | ||
| 193 | |||
| 194 | /// If you have an llvm conext handy, you can use it here. | ||
| 195 | pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 { | ||
| 196 | if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*; | ||
| 197 | |||
| 198 | base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable); | ||
| 199 | base.abi_alignment.resolve(); | ||
| 200 | return base.abi_alignment.data; | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Lower level function that does the work. See getAbiAlignment. | ||
| 204 | async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 { | ||
| 205 | const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context); | ||
| 206 | return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type)); | ||
| 156 | } | 207 | } |
| 157 | 208 | ||
| 158 | pub const Struct = struct { | 209 | pub const Struct = struct { |
| ... | @@ -160,10 +211,10 @@ pub const Type = struct { | ... | @@ -160,10 +211,10 @@ pub const Type = struct { |
| 160 | decls: *Scope.Decls, | 211 | decls: *Scope.Decls, |
| 161 | 212 | ||
| 162 | pub fn destroy(self: *Struct, comp: *Compilation) void { | 213 | pub fn destroy(self: *Struct, comp: *Compilation) void { |
| 163 | comp.a().destroy(self); | 214 | comp.gpa().destroy(self); |
| 164 | } | 215 | } |
| 165 | 216 | ||
| 166 | pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef { | 217 | pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 167 | @panic("TODO"); | 218 | @panic("TODO"); |
| 168 | } | 219 | } |
| 169 | }; | 220 | }; |
| ... | @@ -176,28 +227,23 @@ pub const Type = struct { | ... | @@ -176,28 +227,23 @@ pub const Type = struct { |
| 176 | 227 | ||
| 177 | pub const Param = struct { | 228 | pub const Param = struct { |
| 178 | is_noalias: bool, | 229 | is_noalias: bool, |
| 179 | typeof: *Type, | 230 | typ: *Type, |
| 180 | }; | 231 | }; |
| 181 | 232 | ||
| 182 | pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { | 233 | pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { |
| 183 | const result = try comp.a().create(Fn{ | 234 | const result = try comp.gpa().create(Fn{ |
| 184 | .base = Type{ | 235 | .base = undefined, |
| 185 | .base = Value{ | ||
| 186 | .id = Value.Id.Type, | ||
| 187 | .typeof = &MetaType.get(comp).base, | ||
| 188 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 189 | }, | ||
| 190 | .id = builtin.TypeId.Fn, | ||
| 191 | }, | ||
| 192 | .return_type = return_type, | 236 | .return_type = return_type, |
| 193 | .params = params, | 237 | .params = params, |
| 194 | .is_var_args = is_var_args, | 238 | .is_var_args = is_var_args, |
| 195 | }); | 239 | }); |
| 196 | errdefer comp.a().destroy(result); | 240 | errdefer comp.gpa().destroy(result); |
| 241 | |||
| 242 | result.base.init(comp, Id.Fn, "TODO fn type name"); | ||
| 197 | 243 | ||
| 198 | result.return_type.base.ref(); | 244 | result.return_type.base.ref(); |
| 199 | for (result.params) |param| { | 245 | for (result.params) |param| { |
| 200 | param.typeof.base.ref(); | 246 | param.typ.base.ref(); |
| 201 | } | 247 | } |
| 202 | return result; | 248 | return result; |
| 203 | } | 249 | } |
| ... | @@ -205,20 +251,20 @@ pub const Type = struct { | ... | @@ -205,20 +251,20 @@ pub const Type = struct { |
| 205 | pub fn destroy(self: *Fn, comp: *Compilation) void { | 251 | pub fn destroy(self: *Fn, comp: *Compilation) void { |
| 206 | self.return_type.base.deref(comp); | 252 | self.return_type.base.deref(comp); |
| 207 | for (self.params) |param| { | 253 | for (self.params) |param| { |
| 208 | param.typeof.base.deref(comp); | 254 | param.typ.base.deref(comp); |
| 209 | } | 255 | } |
| 210 | comp.a().destroy(self); | 256 | comp.gpa().destroy(self); |
| 211 | } | 257 | } |
| 212 | 258 | ||
| 213 | pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef { | 259 | pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { |
| 214 | const llvm_return_type = switch (self.return_type.id) { | 260 | const llvm_return_type = switch (self.return_type.id) { |
| 215 | Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory, | 261 | Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory, |
| 216 | else => try self.return_type.getLlvmType(ofile), | 262 | else => try self.return_type.getLlvmType(allocator, llvm_context), |
| 217 | }; | 263 | }; |
| 218 | const llvm_param_types = try ofile.a().alloc(llvm.TypeRef, self.params.len); | 264 | const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len); |
| 219 | defer ofile.a().free(llvm_param_types); | 265 | defer allocator.free(llvm_param_types); |
| 220 | for (llvm_param_types) |*llvm_param_type, i| { | 266 | for (llvm_param_types) |*llvm_param_type, i| { |
| 221 | llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile); | 267 | llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context); |
| 222 | } | 268 | } |
| 223 | 269 | ||
| 224 | return llvm.FunctionType( | 270 | return llvm.FunctionType( |
| ... | @@ -241,7 +287,7 @@ pub const Type = struct { | ... | @@ -241,7 +287,7 @@ pub const Type = struct { |
| 241 | } | 287 | } |
| 242 | 288 | ||
| 243 | pub fn destroy(self: *MetaType, comp: *Compilation) void { | 289 | pub fn destroy(self: *MetaType, comp: *Compilation) void { |
| 244 | comp.a().destroy(self); | 290 | comp.gpa().destroy(self); |
| 245 | } | 291 | } |
| 246 | }; | 292 | }; |
| 247 | 293 | ||
| ... | @@ -255,7 +301,7 @@ pub const Type = struct { | ... | @@ -255,7 +301,7 @@ pub const Type = struct { |
| 255 | } | 301 | } |
| 256 | 302 | ||
| 257 | pub fn destroy(self: *Void, comp: *Compilation) void { | 303 | pub fn destroy(self: *Void, comp: *Compilation) void { |
| 258 | comp.a().destroy(self); | 304 | comp.gpa().destroy(self); |
| 259 | } | 305 | } |
| 260 | }; | 306 | }; |
| 261 | 307 | ||
| ... | @@ -269,10 +315,10 @@ pub const Type = struct { | ... | @@ -269,10 +315,10 @@ pub const Type = struct { |
| 269 | } | 315 | } |
| 270 | 316 | ||
| 271 | pub fn destroy(self: *Bool, comp: *Compilation) void { | 317 | pub fn destroy(self: *Bool, comp: *Compilation) void { |
| 272 | comp.a().destroy(self); | 318 | comp.gpa().destroy(self); |
| 273 | } | 319 | } |
| 274 | 320 | ||
| 275 | pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef { | 321 | pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 276 | @panic("TODO"); | 322 | @panic("TODO"); |
| 277 | } | 323 | } |
| 278 | }; | 324 | }; |
| ... | @@ -287,19 +333,89 @@ pub const Type = struct { | ... | @@ -287,19 +333,89 @@ pub const Type = struct { |
| 287 | } | 333 | } |
| 288 | 334 | ||
| 289 | pub fn destroy(self: *NoReturn, comp: *Compilation) void { | 335 | pub fn destroy(self: *NoReturn, comp: *Compilation) void { |
| 290 | comp.a().destroy(self); | 336 | comp.gpa().destroy(self); |
| 291 | } | 337 | } |
| 292 | }; | 338 | }; |
| 293 | 339 | ||
| 294 | pub const Int = struct { | 340 | pub const Int = struct { |
| 295 | base: Type, | 341 | base: Type, |
| 342 | key: Key, | ||
| 343 | garbage_node: std.atomic.Stack(*Int).Node, | ||
| 344 | |||
| 345 | pub const Key = struct { | ||
| 346 | bit_count: u32, | ||
| 347 | is_signed: bool, | ||
| 348 | |||
| 349 | pub fn hash(self: *const Key) u32 { | ||
| 350 | const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 }; | ||
| 351 | return rands[@boolToInt(self.is_signed)] *% self.bit_count; | ||
| 352 | } | ||
| 353 | |||
| 354 | pub fn eql(self: *const Key, other: *const Key) bool { | ||
| 355 | return self.bit_count == other.bit_count and self.is_signed == other.is_signed; | ||
| 356 | } | ||
| 357 | }; | ||
| 358 | |||
| 359 | pub fn get_u8(comp: *Compilation) *Int { | ||
| 360 | comp.u8_type.base.base.ref(); | ||
| 361 | return comp.u8_type; | ||
| 362 | } | ||
| 363 | |||
| 364 | pub async fn get(comp: *Compilation, key: Key) !*Int { | ||
| 365 | { | ||
| 366 | const held = await (async comp.int_type_table.acquire() catch unreachable); | ||
| 367 | defer held.release(); | ||
| 368 | |||
| 369 | if (held.value.get(&key)) |entry| { | ||
| 370 | entry.value.base.base.ref(); | ||
| 371 | return entry.value; | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | const self = try comp.gpa().create(Int{ | ||
| 376 | .base = undefined, | ||
| 377 | .key = key, | ||
| 378 | .garbage_node = undefined, | ||
| 379 | }); | ||
| 380 | errdefer comp.gpa().destroy(self); | ||
| 381 | |||
| 382 | const u_or_i = "ui"[@boolToInt(key.is_signed)]; | ||
| 383 | const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", u_or_i, key.bit_count); | ||
| 384 | errdefer comp.gpa().free(name); | ||
| 385 | |||
| 386 | self.base.init(comp, Id.Int, name); | ||
| 387 | |||
| 388 | { | ||
| 389 | const held = await (async comp.int_type_table.acquire() catch unreachable); | ||
| 390 | defer held.release(); | ||
| 391 | |||
| 392 | _ = try held.value.put(&self.key, self); | ||
| 393 | } | ||
| 394 | return self; | ||
| 395 | } | ||
| 296 | 396 | ||
| 297 | pub fn destroy(self: *Int, comp: *Compilation) void { | 397 | pub fn destroy(self: *Int, comp: *Compilation) void { |
| 298 | comp.a().destroy(self); | 398 | self.garbage_node = std.atomic.Stack(*Int).Node{ |
| 399 | .data = self, | ||
| 400 | .next = undefined, | ||
| 401 | }; | ||
| 402 | comp.registerGarbage(Int, &self.garbage_node); | ||
| 299 | } | 403 | } |
| 300 | 404 | ||
| 301 | pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef { | 405 | pub async fn gcDestroy(self: *Int, comp: *Compilation) void { |
| 302 | @panic("TODO"); | 406 | { |
| 407 | const held = await (async comp.int_type_table.acquire() catch unreachable); | ||
| 408 | defer held.release(); | ||
| 409 | |||
| 410 | _ = held.value.remove(&self.key).?; | ||
| 411 | } | ||
| 412 | // we allocated the name | ||
| 413 | comp.gpa().free(self.base.name); | ||
| 414 | comp.gpa().destroy(self); | ||
| 415 | } | ||
| 416 | |||
| 417 | pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { | ||
| 418 | return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory; | ||
| 303 | } | 419 | } |
| 304 | }; | 420 | }; |
| 305 | 421 | ||
| ... | @@ -307,59 +423,239 @@ pub const Type = struct { | ... | @@ -307,59 +423,239 @@ pub const Type = struct { |
| 307 | base: Type, | 423 | base: Type, |
| 308 | 424 | ||
| 309 | pub fn destroy(self: *Float, comp: *Compilation) void { | 425 | pub fn destroy(self: *Float, comp: *Compilation) void { |
| 310 | comp.a().destroy(self); | 426 | comp.gpa().destroy(self); |
| 311 | } | 427 | } |
| 312 | 428 | ||
| 313 | pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef { | 429 | pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 314 | @panic("TODO"); | 430 | @panic("TODO"); |
| 315 | } | 431 | } |
| 316 | }; | 432 | }; |
| 317 | pub const Pointer = struct { | 433 | pub const Pointer = struct { |
| 318 | base: Type, | 434 | base: Type, |
| 319 | mut: Mut, | 435 | key: Key, |
| 320 | vol: Vol, | 436 | garbage_node: std.atomic.Stack(*Pointer).Node, |
| 321 | size: Size, | 437 | |
| 322 | alignment: u32, | 438 | pub const Key = struct { |
| 439 | child_type: *Type, | ||
| 440 | mut: Mut, | ||
| 441 | vol: Vol, | ||
| 442 | size: Size, | ||
| 443 | alignment: Align, | ||
| 444 | |||
| 445 | pub fn hash(self: *const Key) u32 { | ||
| 446 | const align_hash = switch (self.alignment) { | ||
| 447 | Align.Abi => 0xf201c090, | ||
| 448 | Align.Override => |x| x, | ||
| 449 | }; | ||
| 450 | return hash_usize(@ptrToInt(self.child_type)) *% | ||
| 451 | hash_enum(self.mut) *% | ||
| 452 | hash_enum(self.vol) *% | ||
| 453 | hash_enum(self.size) *% | ||
| 454 | align_hash; | ||
| 455 | } | ||
| 456 | |||
| 457 | pub fn eql(self: *const Key, other: *const Key) bool { | ||
| 458 | if (self.child_type != other.child_type or | ||
| 459 | self.mut != other.mut or | ||
| 460 | self.vol != other.vol or | ||
| 461 | self.size != other.size or | ||
| 462 | @TagType(Align)(self.alignment) != @TagType(Align)(other.alignment)) | ||
| 463 | { | ||
| 464 | return false; | ||
| 465 | } | ||
| 466 | switch (self.alignment) { | ||
| 467 | Align.Abi => return true, | ||
| 468 | Align.Override => |x| return x == other.alignment.Override, | ||
| 469 | } | ||
| 470 | } | ||
| 471 | }; | ||
| 323 | 472 | ||
| 324 | pub const Mut = enum { | 473 | pub const Mut = enum { |
| 325 | Mut, | 474 | Mut, |
| 326 | Const, | 475 | Const, |
| 327 | }; | 476 | }; |
| 477 | |||
| 328 | pub const Vol = enum { | 478 | pub const Vol = enum { |
| 329 | Non, | 479 | Non, |
| 330 | Volatile, | 480 | Volatile, |
| 331 | }; | 481 | }; |
| 482 | |||
| 483 | pub const Align = union(enum) { | ||
| 484 | Abi, | ||
| 485 | Override: u32, | ||
| 486 | }; | ||
| 487 | |||
| 332 | pub const Size = builtin.TypeInfo.Pointer.Size; | 488 | pub const Size = builtin.TypeInfo.Pointer.Size; |
| 333 | 489 | ||
| 334 | pub fn destroy(self: *Pointer, comp: *Compilation) void { | 490 | pub fn destroy(self: *Pointer, comp: *Compilation) void { |
| 335 | comp.a().destroy(self); | 491 | self.garbage_node = std.atomic.Stack(*Pointer).Node{ |
| 492 | .data = self, | ||
| 493 | .next = undefined, | ||
| 494 | }; | ||
| 495 | comp.registerGarbage(Pointer, &self.garbage_node); | ||
| 336 | } | 496 | } |
| 337 | 497 | ||
| 338 | pub fn get( | 498 | pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void { |
| 499 | { | ||
| 500 | const held = await (async comp.ptr_type_table.acquire() catch unreachable); | ||
| 501 | defer held.release(); | ||
| 502 | |||
| 503 | _ = held.value.remove(&self.key).?; | ||
| 504 | } | ||
| 505 | self.key.child_type.base.deref(comp); | ||
| 506 | comp.gpa().destroy(self); | ||
| 507 | } | ||
| 508 | |||
| 509 | pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 { | ||
| 510 | switch (self.key.alignment) { | ||
| 511 | Align.Abi => return await (async self.key.child_type.getAbiAlignment(comp) catch unreachable), | ||
| 512 | Align.Override => |alignment| return alignment, | ||
| 513 | } | ||
| 514 | } | ||
| 515 | |||
| 516 | pub async fn get( | ||
| 339 | comp: *Compilation, | 517 | comp: *Compilation, |
| 340 | elem_type: *Type, | 518 | key: Key, |
| 341 | mut: Mut, | 519 | ) !*Pointer { |
| 342 | vol: Vol, | 520 | var normal_key = key; |
| 343 | size: Size, | 521 | switch (key.alignment) { |
| 344 | alignment: u32, | 522 | Align.Abi => {}, |
| 345 | ) *Pointer { | 523 | Align.Override => |alignment| { |
| 346 | @panic("TODO get pointer"); | 524 | const abi_align = try await (async key.child_type.getAbiAlignment(comp) catch unreachable); |
| 525 | if (abi_align == alignment) { | ||
| 526 | normal_key.alignment = Align.Abi; | ||
| 527 | } | ||
| 528 | }, | ||
| 529 | } | ||
| 530 | { | ||
| 531 | const held = await (async comp.ptr_type_table.acquire() catch unreachable); | ||
| 532 | defer held.release(); | ||
| 533 | |||
| 534 | if (held.value.get(&normal_key)) |entry| { | ||
| 535 | entry.value.base.base.ref(); | ||
| 536 | return entry.value; | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | const self = try comp.gpa().create(Pointer{ | ||
| 541 | .base = undefined, | ||
| 542 | .key = normal_key, | ||
| 543 | .garbage_node = undefined, | ||
| 544 | }); | ||
| 545 | errdefer comp.gpa().destroy(self); | ||
| 546 | |||
| 547 | const size_str = switch (self.key.size) { | ||
| 548 | Size.One => "*", | ||
| 549 | Size.Many => "[*]", | ||
| 550 | Size.Slice => "[]", | ||
| 551 | }; | ||
| 552 | const mut_str = switch (self.key.mut) { | ||
| 553 | Mut.Const => "const ", | ||
| 554 | Mut.Mut => "", | ||
| 555 | }; | ||
| 556 | const vol_str = switch (self.key.vol) { | ||
| 557 | Vol.Volatile => "volatile ", | ||
| 558 | Vol.Non => "", | ||
| 559 | }; | ||
| 560 | const name = switch (self.key.alignment) { | ||
| 561 | Align.Abi => try std.fmt.allocPrint( | ||
| 562 | comp.gpa(), | ||
| 563 | "{}{}{}{}", | ||
| 564 | size_str, | ||
| 565 | mut_str, | ||
| 566 | vol_str, | ||
| 567 | self.key.child_type.name, | ||
| 568 | ), | ||
| 569 | Align.Override => |alignment| try std.fmt.allocPrint( | ||
| 570 | comp.gpa(), | ||
| 571 | "{}align<{}> {}{}{}", | ||
| 572 | size_str, | ||
| 573 | alignment, | ||
| 574 | mut_str, | ||
| 575 | vol_str, | ||
| 576 | self.key.child_type.name, | ||
| 577 | ), | ||
| 578 | }; | ||
| 579 | errdefer comp.gpa().free(name); | ||
| 580 | |||
| 581 | self.base.init(comp, Id.Pointer, name); | ||
| 582 | |||
| 583 | { | ||
| 584 | const held = await (async comp.ptr_type_table.acquire() catch unreachable); | ||
| 585 | defer held.release(); | ||
| 586 | |||
| 587 | _ = try held.value.put(&self.key, self); | ||
| 588 | } | ||
| 589 | return self; | ||
| 347 | } | 590 | } |
| 348 | 591 | ||
| 349 | pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef { | 592 | pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { |
| 350 | @panic("TODO"); | 593 | const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context); |
| 594 | return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory; | ||
| 351 | } | 595 | } |
| 352 | }; | 596 | }; |
| 353 | 597 | ||
| 354 | pub const Array = struct { | 598 | pub const Array = struct { |
| 355 | base: Type, | 599 | base: Type, |
| 600 | key: Key, | ||
| 601 | garbage_node: std.atomic.Stack(*Array).Node, | ||
| 602 | |||
| 603 | pub const Key = struct { | ||
| 604 | elem_type: *Type, | ||
| 605 | len: usize, | ||
| 606 | |||
| 607 | pub fn hash(self: *const Key) u32 { | ||
| 608 | return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len); | ||
| 609 | } | ||
| 610 | |||
| 611 | pub fn eql(self: *const Key, other: *const Key) bool { | ||
| 612 | return self.elem_type == other.elem_type and self.len == other.len; | ||
| 613 | } | ||
| 614 | }; | ||
| 356 | 615 | ||
| 357 | pub fn destroy(self: *Array, comp: *Compilation) void { | 616 | pub fn destroy(self: *Array, comp: *Compilation) void { |
| 358 | comp.a().destroy(self); | 617 | self.key.elem_type.base.deref(comp); |
| 618 | comp.gpa().destroy(self); | ||
| 359 | } | 619 | } |
| 360 | 620 | ||
| 361 | pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef { | 621 | pub async fn get(comp: *Compilation, key: Key) !*Array { |
| 362 | @panic("TODO"); | 622 | key.elem_type.base.ref(); |
| 623 | errdefer key.elem_type.base.deref(comp); | ||
| 624 | |||
| 625 | { | ||
| 626 | const held = await (async comp.array_type_table.acquire() catch unreachable); | ||
| 627 | defer held.release(); | ||
| 628 | |||
| 629 | if (held.value.get(&key)) |entry| { | ||
| 630 | entry.value.base.base.ref(); | ||
| 631 | return entry.value; | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 | const self = try comp.gpa().create(Array{ | ||
| 636 | .base = undefined, | ||
| 637 | .key = key, | ||
| 638 | .garbage_node = undefined, | ||
| 639 | }); | ||
| 640 | errdefer comp.gpa().destroy(self); | ||
| 641 | |||
| 642 | const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name); | ||
| 643 | errdefer comp.gpa().free(name); | ||
| 644 | |||
| 645 | self.base.init(comp, Id.Array, name); | ||
| 646 | |||
| 647 | { | ||
| 648 | const held = await (async comp.array_type_table.acquire() catch unreachable); | ||
| 649 | defer held.release(); | ||
| 650 | |||
| 651 | _ = try held.value.put(&self.key, self); | ||
| 652 | } | ||
| 653 | return self; | ||
| 654 | } | ||
| 655 | |||
| 656 | pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { | ||
| 657 | const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context); | ||
| 658 | return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory; | ||
| 363 | } | 659 | } |
| 364 | }; | 660 | }; |
| 365 | 661 | ||
| ... | @@ -367,15 +663,21 @@ pub const Type = struct { | ... | @@ -367,15 +663,21 @@ pub const Type = struct { |
| 367 | base: Type, | 663 | base: Type, |
| 368 | 664 | ||
| 369 | pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void { | 665 | pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void { |
| 370 | comp.a().destroy(self); | 666 | comp.gpa().destroy(self); |
| 371 | } | 667 | } |
| 372 | }; | 668 | }; |
| 373 | 669 | ||
| 374 | pub const ComptimeInt = struct { | 670 | pub const ComptimeInt = struct { |
| 375 | base: Type, | 671 | base: Type, |
| 376 | 672 | ||
| 673 | /// Adds 1 reference to the resulting type | ||
| 674 | pub fn get(comp: *Compilation) *ComptimeInt { | ||
| 675 | comp.comptime_int_type.base.base.ref(); | ||
| 676 | return comp.comptime_int_type; | ||
| 677 | } | ||
| 678 | |||
| 377 | pub fn destroy(self: *ComptimeInt, comp: *Compilation) void { | 679 | pub fn destroy(self: *ComptimeInt, comp: *Compilation) void { |
| 378 | comp.a().destroy(self); | 680 | comp.gpa().destroy(self); |
| 379 | } | 681 | } |
| 380 | }; | 682 | }; |
| 381 | 683 | ||
| ... | @@ -383,7 +685,7 @@ pub const Type = struct { | ... | @@ -383,7 +685,7 @@ pub const Type = struct { |
| 383 | base: Type, | 685 | base: Type, |
| 384 | 686 | ||
| 385 | pub fn destroy(self: *Undefined, comp: *Compilation) void { | 687 | pub fn destroy(self: *Undefined, comp: *Compilation) void { |
| 386 | comp.a().destroy(self); | 688 | comp.gpa().destroy(self); |
| 387 | } | 689 | } |
| 388 | }; | 690 | }; |
| 389 | 691 | ||
| ... | @@ -391,7 +693,7 @@ pub const Type = struct { | ... | @@ -391,7 +693,7 @@ pub const Type = struct { |
| 391 | base: Type, | 693 | base: Type, |
| 392 | 694 | ||
| 393 | pub fn destroy(self: *Null, comp: *Compilation) void { | 695 | pub fn destroy(self: *Null, comp: *Compilation) void { |
| 394 | comp.a().destroy(self); | 696 | comp.gpa().destroy(self); |
| 395 | } | 697 | } |
| 396 | }; | 698 | }; |
| 397 | 699 | ||
| ... | @@ -399,10 +701,10 @@ pub const Type = struct { | ... | @@ -399,10 +701,10 @@ pub const Type = struct { |
| 399 | base: Type, | 701 | base: Type, |
| 400 | 702 | ||
| 401 | pub fn destroy(self: *Optional, comp: *Compilation) void { | 703 | pub fn destroy(self: *Optional, comp: *Compilation) void { |
| 402 | comp.a().destroy(self); | 704 | comp.gpa().destroy(self); |
| 403 | } | 705 | } |
| 404 | 706 | ||
| 405 | pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef { | 707 | pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 406 | @panic("TODO"); | 708 | @panic("TODO"); |
| 407 | } | 709 | } |
| 408 | }; | 710 | }; |
| ... | @@ -411,10 +713,10 @@ pub const Type = struct { | ... | @@ -411,10 +713,10 @@ pub const Type = struct { |
| 411 | base: Type, | 713 | base: Type, |
| 412 | 714 | ||
| 413 | pub fn destroy(self: *ErrorUnion, comp: *Compilation) void { | 715 | pub fn destroy(self: *ErrorUnion, comp: *Compilation) void { |
| 414 | comp.a().destroy(self); | 716 | comp.gpa().destroy(self); |
| 415 | } | 717 | } |
| 416 | 718 | ||
| 417 | pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef { | 719 | pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 418 | @panic("TODO"); | 720 | @panic("TODO"); |
| 419 | } | 721 | } |
| 420 | }; | 722 | }; |
| ... | @@ -423,10 +725,10 @@ pub const Type = struct { | ... | @@ -423,10 +725,10 @@ pub const Type = struct { |
| 423 | base: Type, | 725 | base: Type, |
| 424 | 726 | ||
| 425 | pub fn destroy(self: *ErrorSet, comp: *Compilation) void { | 727 | pub fn destroy(self: *ErrorSet, comp: *Compilation) void { |
| 426 | comp.a().destroy(self); | 728 | comp.gpa().destroy(self); |
| 427 | } | 729 | } |
| 428 | 730 | ||
| 429 | pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef { | 731 | pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 430 | @panic("TODO"); | 732 | @panic("TODO"); |
| 431 | } | 733 | } |
| 432 | }; | 734 | }; |
| ... | @@ -435,10 +737,10 @@ pub const Type = struct { | ... | @@ -435,10 +737,10 @@ pub const Type = struct { |
| 435 | base: Type, | 737 | base: Type, |
| 436 | 738 | ||
| 437 | pub fn destroy(self: *Enum, comp: *Compilation) void { | 739 | pub fn destroy(self: *Enum, comp: *Compilation) void { |
| 438 | comp.a().destroy(self); | 740 | comp.gpa().destroy(self); |
| 439 | } | 741 | } |
| 440 | 742 | ||
| 441 | pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef { | 743 | pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 442 | @panic("TODO"); | 744 | @panic("TODO"); |
| 443 | } | 745 | } |
| 444 | }; | 746 | }; |
| ... | @@ -447,10 +749,10 @@ pub const Type = struct { | ... | @@ -447,10 +749,10 @@ pub const Type = struct { |
| 447 | base: Type, | 749 | base: Type, |
| 448 | 750 | ||
| 449 | pub fn destroy(self: *Union, comp: *Compilation) void { | 751 | pub fn destroy(self: *Union, comp: *Compilation) void { |
| 450 | comp.a().destroy(self); | 752 | comp.gpa().destroy(self); |
| 451 | } | 753 | } |
| 452 | 754 | ||
| 453 | pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef { | 755 | pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 454 | @panic("TODO"); | 756 | @panic("TODO"); |
| 455 | } | 757 | } |
| 456 | }; | 758 | }; |
| ... | @@ -459,7 +761,7 @@ pub const Type = struct { | ... | @@ -459,7 +761,7 @@ pub const Type = struct { |
| 459 | base: Type, | 761 | base: Type, |
| 460 | 762 | ||
| 461 | pub fn destroy(self: *Namespace, comp: *Compilation) void { | 763 | pub fn destroy(self: *Namespace, comp: *Compilation) void { |
| 462 | comp.a().destroy(self); | 764 | comp.gpa().destroy(self); |
| 463 | } | 765 | } |
| 464 | }; | 766 | }; |
| 465 | 767 | ||
| ... | @@ -467,7 +769,7 @@ pub const Type = struct { | ... | @@ -467,7 +769,7 @@ pub const Type = struct { |
| 467 | base: Type, | 769 | base: Type, |
| 468 | 770 | ||
| 469 | pub fn destroy(self: *Block, comp: *Compilation) void { | 771 | pub fn destroy(self: *Block, comp: *Compilation) void { |
| 470 | comp.a().destroy(self); | 772 | comp.gpa().destroy(self); |
| 471 | } | 773 | } |
| 472 | }; | 774 | }; |
| 473 | 775 | ||
| ... | @@ -475,10 +777,10 @@ pub const Type = struct { | ... | @@ -475,10 +777,10 @@ pub const Type = struct { |
| 475 | base: Type, | 777 | base: Type, |
| 476 | 778 | ||
| 477 | pub fn destroy(self: *BoundFn, comp: *Compilation) void { | 779 | pub fn destroy(self: *BoundFn, comp: *Compilation) void { |
| 478 | comp.a().destroy(self); | 780 | comp.gpa().destroy(self); |
| 479 | } | 781 | } |
| 480 | 782 | ||
| 481 | pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef { | 783 | pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 482 | @panic("TODO"); | 784 | @panic("TODO"); |
| 483 | } | 785 | } |
| 484 | }; | 786 | }; |
| ... | @@ -487,7 +789,7 @@ pub const Type = struct { | ... | @@ -487,7 +789,7 @@ pub const Type = struct { |
| 487 | base: Type, | 789 | base: Type, |
| 488 | 790 | ||
| 489 | pub fn destroy(self: *ArgTuple, comp: *Compilation) void { | 791 | pub fn destroy(self: *ArgTuple, comp: *Compilation) void { |
| 490 | comp.a().destroy(self); | 792 | comp.gpa().destroy(self); |
| 491 | } | 793 | } |
| 492 | }; | 794 | }; |
| 493 | 795 | ||
| ... | @@ -495,10 +797,10 @@ pub const Type = struct { | ... | @@ -495,10 +797,10 @@ pub const Type = struct { |
| 495 | base: Type, | 797 | base: Type, |
| 496 | 798 | ||
| 497 | pub fn destroy(self: *Opaque, comp: *Compilation) void { | 799 | pub fn destroy(self: *Opaque, comp: *Compilation) void { |
| 498 | comp.a().destroy(self); | 800 | comp.gpa().destroy(self); |
| 499 | } | 801 | } |
| 500 | 802 | ||
| 501 | pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef { | 803 | pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 502 | @panic("TODO"); | 804 | @panic("TODO"); |
| 503 | } | 805 | } |
| 504 | }; | 806 | }; |
| ... | @@ -507,11 +809,36 @@ pub const Type = struct { | ... | @@ -507,11 +809,36 @@ pub const Type = struct { |
| 507 | base: Type, | 809 | base: Type, |
| 508 | 810 | ||
| 509 | pub fn destroy(self: *Promise, comp: *Compilation) void { | 811 | pub fn destroy(self: *Promise, comp: *Compilation) void { |
| 510 | comp.a().destroy(self); | 812 | comp.gpa().destroy(self); |
| 511 | } | 813 | } |
| 512 | 814 | ||
| 513 | pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef { | 815 | pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { |
| 514 | @panic("TODO"); | 816 | @panic("TODO"); |
| 515 | } | 817 | } |
| 516 | }; | 818 | }; |
| 517 | }; | 819 | }; |
| 820 | |||
| 821 | fn hash_usize(x: usize) u32 { | ||
| 822 | return switch (@sizeOf(usize)) { | ||
| 823 | 4 => x, | ||
| 824 | 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d), | ||
| 825 | else => @compileError("implement this hash function"), | ||
| 826 | }; | ||
| 827 | } | ||
| 828 | |||
| 829 | fn hash_enum(x: var) u32 { | ||
| 830 | const rands = []u32{ | ||
| 831 | 0x85ebf64f, | ||
| 832 | 0x3fcb3211, | ||
| 833 | 0x240a4e8e, | ||
| 834 | 0x40bb0e3c, | ||
| 835 | 0x78be45af, | ||
| 836 | 0x1ca98e37, | ||
| 837 | 0xec56053a, | ||
| 838 | 0x906adc48, | ||
| 839 | 0xd4fe9763, | ||
| 840 | 0x54c80dac, | ||
| 841 | }; | ||
| 842 | comptime assert(@memberCount(@typeOf(x)) < rands.len); | ||
| 843 | return rands[@enumToInt(x)]; | ||
| 844 | } |
src-self-hosted/value.zig+401-12| ... | @@ -4,12 +4,14 @@ const Scope = @import("scope.zig").Scope; | ... | @@ -4,12 +4,14 @@ const Scope = @import("scope.zig").Scope; |
| 4 | const Compilation = @import("compilation.zig").Compilation; | 4 | const Compilation = @import("compilation.zig").Compilation; |
| 5 | const ObjectFile = @import("codegen.zig").ObjectFile; | 5 | const ObjectFile = @import("codegen.zig").ObjectFile; |
| 6 | const llvm = @import("llvm.zig"); | 6 | const llvm = @import("llvm.zig"); |
| 7 | const Buffer = std.Buffer; | ||
| 8 | const assert = std.debug.assert; | ||
| 7 | 9 | ||
| 8 | /// Values are ref-counted, heap-allocated, and copy-on-write | 10 | /// Values are ref-counted, heap-allocated, and copy-on-write |
| 9 | /// If there is only 1 ref then write need not copy | 11 | /// If there is only 1 ref then write need not copy |
| 10 | pub const Value = struct { | 12 | pub const Value = struct { |
| 11 | id: Id, | 13 | id: Id, |
| 12 | typeof: *Type, | 14 | typ: *Type, |
| 13 | ref_count: std.atomic.Int(usize), | 15 | ref_count: std.atomic.Int(usize), |
| 14 | 16 | ||
| 15 | /// Thread-safe | 17 | /// Thread-safe |
| ... | @@ -20,23 +22,37 @@ pub const Value = struct { | ... | @@ -20,23 +22,37 @@ pub const Value = struct { |
| 20 | /// Thread-safe | 22 | /// Thread-safe |
| 21 | pub fn deref(base: *Value, comp: *Compilation) void { | 23 | pub fn deref(base: *Value, comp: *Compilation) void { |
| 22 | if (base.ref_count.decr() == 1) { | 24 | if (base.ref_count.decr() == 1) { |
| 23 | base.typeof.base.deref(comp); | 25 | base.typ.base.deref(comp); |
| 24 | switch (base.id) { | 26 | switch (base.id) { |
| 25 | Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp), | 27 | Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp), |
| 26 | Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp), | 28 | Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp), |
| 29 | Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp), | ||
| 27 | Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp), | 30 | Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp), |
| 28 | Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), | 31 | Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp), |
| 29 | Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), | 32 | Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp), |
| 30 | Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp), | 33 | Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp), |
| 34 | Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp), | ||
| 35 | Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp), | ||
| 31 | } | 36 | } |
| 32 | } | 37 | } |
| 33 | } | 38 | } |
| 34 | 39 | ||
| 40 | pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void { | ||
| 41 | base.typ.base.deref(comp); | ||
| 42 | new_type.base.ref(); | ||
| 43 | base.typ = new_type; | ||
| 44 | } | ||
| 45 | |||
| 35 | pub fn getRef(base: *Value) *Value { | 46 | pub fn getRef(base: *Value) *Value { |
| 36 | base.ref(); | 47 | base.ref(); |
| 37 | return base; | 48 | return base; |
| 38 | } | 49 | } |
| 39 | 50 | ||
| 51 | pub fn cast(base: *Value, comptime T: type) ?*T { | ||
| 52 | if (base.id != @field(Id, @typeName(T))) return null; | ||
| 53 | return @fieldParentPtr(T, "base", base); | ||
| 54 | } | ||
| 55 | |||
| 40 | pub fn dump(base: *const Value) void { | 56 | pub fn dump(base: *const Value) void { |
| 41 | std.debug.warn("{}", @tagName(base.id)); | 57 | std.debug.warn("{}", @tagName(base.id)); |
| 42 | } | 58 | } |
| ... | @@ -45,30 +61,117 @@ pub const Value = struct { | ... | @@ -45,30 +61,117 @@ pub const Value = struct { |
| 45 | switch (base.id) { | 61 | switch (base.id) { |
| 46 | Id.Type => unreachable, | 62 | Id.Type => unreachable, |
| 47 | Id.Fn => @panic("TODO"), | 63 | Id.Fn => @panic("TODO"), |
| 64 | Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile), | ||
| 48 | Id.Void => return null, | 65 | Id.Void => return null, |
| 49 | Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), | 66 | Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), |
| 50 | Id.NoReturn => unreachable, | 67 | Id.NoReturn => unreachable, |
| 51 | Id.Ptr => @panic("TODO"), | 68 | Id.Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile), |
| 69 | Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile), | ||
| 70 | Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile), | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) { | ||
| 75 | if (self.ref_count.get() == 1) { | ||
| 76 | // ( Í¡° ͜ʖ Í¡°) | ||
| 77 | return self; | ||
| 78 | } | ||
| 79 | |||
| 80 | assert(self.ref_count.decr() != 1); | ||
| 81 | return self.copy(comp); | ||
| 82 | } | ||
| 83 | |||
| 84 | pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) { | ||
| 85 | switch (base.id) { | ||
| 86 | Id.Type => unreachable, | ||
| 87 | Id.Fn => unreachable, | ||
| 88 | Id.FnProto => unreachable, | ||
| 89 | Id.Void => unreachable, | ||
| 90 | Id.Bool => unreachable, | ||
| 91 | Id.NoReturn => unreachable, | ||
| 92 | Id.Ptr => unreachable, | ||
| 93 | Id.Array => unreachable, | ||
| 94 | Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base, | ||
| 52 | } | 95 | } |
| 53 | } | 96 | } |
| 54 | 97 | ||
| 98 | pub const Parent = union(enum) { | ||
| 99 | None, | ||
| 100 | BaseStruct: BaseStruct, | ||
| 101 | BaseArray: BaseArray, | ||
| 102 | BaseUnion: *Value, | ||
| 103 | BaseScalar: *Value, | ||
| 104 | |||
| 105 | pub const BaseStruct = struct { | ||
| 106 | val: *Value, | ||
| 107 | field_index: usize, | ||
| 108 | }; | ||
| 109 | |||
| 110 | pub const BaseArray = struct { | ||
| 111 | val: *Value, | ||
| 112 | elem_index: usize, | ||
| 113 | }; | ||
| 114 | }; | ||
| 115 | |||
| 55 | pub const Id = enum { | 116 | pub const Id = enum { |
| 56 | Type, | 117 | Type, |
| 57 | Fn, | 118 | Fn, |
| 58 | Void, | 119 | Void, |
| 59 | Bool, | 120 | Bool, |
| 60 | NoReturn, | 121 | NoReturn, |
| 122 | Array, | ||
| 61 | Ptr, | 123 | Ptr, |
| 124 | Int, | ||
| 125 | FnProto, | ||
| 62 | }; | 126 | }; |
| 63 | 127 | ||
| 64 | pub const Type = @import("type.zig").Type; | 128 | pub const Type = @import("type.zig").Type; |
| 65 | 129 | ||
| 130 | pub const FnProto = struct { | ||
| 131 | base: Value, | ||
| 132 | |||
| 133 | /// The main external name that is used in the .o file. | ||
| 134 | /// TODO https://github.com/ziglang/zig/issues/265 | ||
| 135 | symbol_name: Buffer, | ||
| 136 | |||
| 137 | pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto { | ||
| 138 | const self = try comp.gpa().create(FnProto{ | ||
| 139 | .base = Value{ | ||
| 140 | .id = Value.Id.FnProto, | ||
| 141 | .typ = &fn_type.base, | ||
| 142 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 143 | }, | ||
| 144 | .symbol_name = symbol_name, | ||
| 145 | }); | ||
| 146 | fn_type.base.base.ref(); | ||
| 147 | return self; | ||
| 148 | } | ||
| 149 | |||
| 150 | pub fn destroy(self: *FnProto, comp: *Compilation) void { | ||
| 151 | self.symbol_name.deinit(); | ||
| 152 | comp.gpa().destroy(self); | ||
| 153 | } | ||
| 154 | |||
| 155 | pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef { | ||
| 156 | const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); | ||
| 157 | const llvm_fn = llvm.AddFunction( | ||
| 158 | ofile.module, | ||
| 159 | self.symbol_name.ptr(), | ||
| 160 | llvm_fn_type, | ||
| 161 | ) orelse return error.OutOfMemory; | ||
| 162 | |||
| 163 | // TODO port more logic from codegen.cpp:fn_llvm_value | ||
| 164 | |||
| 165 | return llvm_fn; | ||
| 166 | } | ||
| 167 | }; | ||
| 168 | |||
| 66 | pub const Fn = struct { | 169 | pub const Fn = struct { |
| 67 | base: Value, | 170 | base: Value, |
| 68 | 171 | ||
| 69 | /// The main external name that is used in the .o file. | 172 | /// The main external name that is used in the .o file. |
| 70 | /// TODO https://github.com/ziglang/zig/issues/265 | 173 | /// TODO https://github.com/ziglang/zig/issues/265 |
| 71 | symbol_name: std.Buffer, | 174 | symbol_name: Buffer, |
| 72 | 175 | ||
| 73 | /// parent should be the top level decls or container decls | 176 | /// parent should be the top level decls or container decls |
| 74 | fndef_scope: *Scope.FnDef, | 177 | fndef_scope: *Scope.FnDef, |
| ... | @@ -79,19 +182,33 @@ pub const Value = struct { | ... | @@ -79,19 +182,33 @@ pub const Value = struct { |
| 79 | /// parent is child_scope | 182 | /// parent is child_scope |
| 80 | block_scope: *Scope.Block, | 183 | block_scope: *Scope.Block, |
| 81 | 184 | ||
| 185 | /// Path to the object file that contains this function | ||
| 186 | containing_object: Buffer, | ||
| 187 | |||
| 188 | link_set_node: *std.LinkedList(?*Value.Fn).Node, | ||
| 189 | |||
| 82 | /// Creates a Fn value with 1 ref | 190 | /// Creates a Fn value with 1 ref |
| 83 | /// Takes ownership of symbol_name | 191 | /// Takes ownership of symbol_name |
| 84 | pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn { | 192 | pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn { |
| 85 | const self = try comp.a().create(Fn{ | 193 | const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{ |
| 194 | .data = null, | ||
| 195 | .next = undefined, | ||
| 196 | .prev = undefined, | ||
| 197 | }); | ||
| 198 | errdefer comp.gpa().destroy(link_set_node); | ||
| 199 | |||
| 200 | const self = try comp.gpa().create(Fn{ | ||
| 86 | .base = Value{ | 201 | .base = Value{ |
| 87 | .id = Value.Id.Fn, | 202 | .id = Value.Id.Fn, |
| 88 | .typeof = &fn_type.base, | 203 | .typ = &fn_type.base, |
| 89 | .ref_count = std.atomic.Int(usize).init(1), | 204 | .ref_count = std.atomic.Int(usize).init(1), |
| 90 | }, | 205 | }, |
| 91 | .fndef_scope = fndef_scope, | 206 | .fndef_scope = fndef_scope, |
| 92 | .child_scope = &fndef_scope.base, | 207 | .child_scope = &fndef_scope.base, |
| 93 | .block_scope = undefined, | 208 | .block_scope = undefined, |
| 94 | .symbol_name = symbol_name, | 209 | .symbol_name = symbol_name, |
| 210 | .containing_object = Buffer.initNull(comp.gpa()), | ||
| 211 | .link_set_node = link_set_node, | ||
| 95 | }); | 212 | }); |
| 96 | fn_type.base.base.ref(); | 213 | fn_type.base.base.ref(); |
| 97 | fndef_scope.fn_val = self; | 214 | fndef_scope.fn_val = self; |
| ... | @@ -100,9 +217,19 @@ pub const Value = struct { | ... | @@ -100,9 +217,19 @@ pub const Value = struct { |
| 100 | } | 217 | } |
| 101 | 218 | ||
| 102 | pub fn destroy(self: *Fn, comp: *Compilation) void { | 219 | pub fn destroy(self: *Fn, comp: *Compilation) void { |
| 220 | // remove with a tombstone so that we do not have to grab a lock | ||
| 221 | if (self.link_set_node.data != null) { | ||
| 222 | // it's now the job of the link step to find this tombstone and | ||
| 223 | // deallocate it. | ||
| 224 | self.link_set_node.data = null; | ||
| 225 | } else { | ||
| 226 | comp.gpa().destroy(self.link_set_node); | ||
| 227 | } | ||
| 228 | |||
| 229 | self.containing_object.deinit(); | ||
| 103 | self.fndef_scope.base.deref(comp); | 230 | self.fndef_scope.base.deref(comp); |
| 104 | self.symbol_name.deinit(); | 231 | self.symbol_name.deinit(); |
| 105 | comp.a().destroy(self); | 232 | comp.gpa().destroy(self); |
| 106 | } | 233 | } |
| 107 | }; | 234 | }; |
| 108 | 235 | ||
| ... | @@ -115,7 +242,7 @@ pub const Value = struct { | ... | @@ -115,7 +242,7 @@ pub const Value = struct { |
| 115 | } | 242 | } |
| 116 | 243 | ||
| 117 | pub fn destroy(self: *Void, comp: *Compilation) void { | 244 | pub fn destroy(self: *Void, comp: *Compilation) void { |
| 118 | comp.a().destroy(self); | 245 | comp.gpa().destroy(self); |
| 119 | } | 246 | } |
| 120 | }; | 247 | }; |
| 121 | 248 | ||
| ... | @@ -134,7 +261,7 @@ pub const Value = struct { | ... | @@ -134,7 +261,7 @@ pub const Value = struct { |
| 134 | } | 261 | } |
| 135 | 262 | ||
| 136 | pub fn destroy(self: *Bool, comp: *Compilation) void { | 263 | pub fn destroy(self: *Bool, comp: *Compilation) void { |
| 137 | comp.a().destroy(self); | 264 | comp.gpa().destroy(self); |
| 138 | } | 265 | } |
| 139 | 266 | ||
| 140 | pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef { | 267 | pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef { |
| ... | @@ -156,12 +283,14 @@ pub const Value = struct { | ... | @@ -156,12 +283,14 @@ pub const Value = struct { |
| 156 | } | 283 | } |
| 157 | 284 | ||
| 158 | pub fn destroy(self: *NoReturn, comp: *Compilation) void { | 285 | pub fn destroy(self: *NoReturn, comp: *Compilation) void { |
| 159 | comp.a().destroy(self); | 286 | comp.gpa().destroy(self); |
| 160 | } | 287 | } |
| 161 | }; | 288 | }; |
| 162 | 289 | ||
| 163 | pub const Ptr = struct { | 290 | pub const Ptr = struct { |
| 164 | base: Value, | 291 | base: Value, |
| 292 | special: Special, | ||
| 293 | mut: Mut, | ||
| 165 | 294 | ||
| 166 | pub const Mut = enum { | 295 | pub const Mut = enum { |
| 167 | CompTimeConst, | 296 | CompTimeConst, |
| ... | @@ -169,8 +298,268 @@ pub const Value = struct { | ... | @@ -169,8 +298,268 @@ pub const Value = struct { |
| 169 | RunTime, | 298 | RunTime, |
| 170 | }; | 299 | }; |
| 171 | 300 | ||
| 301 | pub const Special = union(enum) { | ||
| 302 | Scalar: *Value, | ||
| 303 | BaseArray: BaseArray, | ||
| 304 | BaseStruct: BaseStruct, | ||
| 305 | HardCodedAddr: u64, | ||
| 306 | Discard, | ||
| 307 | }; | ||
| 308 | |||
| 309 | pub const BaseArray = struct { | ||
| 310 | val: *Value, | ||
| 311 | elem_index: usize, | ||
| 312 | }; | ||
| 313 | |||
| 314 | pub const BaseStruct = struct { | ||
| 315 | val: *Value, | ||
| 316 | field_index: usize, | ||
| 317 | }; | ||
| 318 | |||
| 319 | pub async fn createArrayElemPtr( | ||
| 320 | comp: *Compilation, | ||
| 321 | array_val: *Array, | ||
| 322 | mut: Type.Pointer.Mut, | ||
| 323 | size: Type.Pointer.Size, | ||
| 324 | elem_index: usize, | ||
| 325 | ) !*Ptr { | ||
| 326 | array_val.base.ref(); | ||
| 327 | errdefer array_val.base.deref(comp); | ||
| 328 | |||
| 329 | const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type; | ||
| 330 | const ptr_type = try await (async Type.Pointer.get(comp, Type.Pointer.Key{ | ||
| 331 | .child_type = elem_type, | ||
| 332 | .mut = mut, | ||
| 333 | .vol = Type.Pointer.Vol.Non, | ||
| 334 | .size = size, | ||
| 335 | .alignment = Type.Pointer.Align.Abi, | ||
| 336 | }) catch unreachable); | ||
| 337 | var ptr_type_consumed = false; | ||
| 338 | errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp); | ||
| 339 | |||
| 340 | const self = try comp.gpa().create(Value.Ptr{ | ||
| 341 | .base = Value{ | ||
| 342 | .id = Value.Id.Ptr, | ||
| 343 | .typ = &ptr_type.base, | ||
| 344 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 345 | }, | ||
| 346 | .special = Special{ | ||
| 347 | .BaseArray = BaseArray{ | ||
| 348 | .val = &array_val.base, | ||
| 349 | .elem_index = 0, | ||
| 350 | }, | ||
| 351 | }, | ||
| 352 | .mut = Mut.CompTimeConst, | ||
| 353 | }); | ||
| 354 | ptr_type_consumed = true; | ||
| 355 | errdefer comp.gpa().destroy(self); | ||
| 356 | |||
| 357 | return self; | ||
| 358 | } | ||
| 359 | |||
| 172 | pub fn destroy(self: *Ptr, comp: *Compilation) void { | 360 | pub fn destroy(self: *Ptr, comp: *Compilation) void { |
| 173 | comp.a().destroy(self); | 361 | comp.gpa().destroy(self); |
| 362 | } | ||
| 363 | |||
| 364 | pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef { | ||
| 365 | const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context); | ||
| 366 | // TODO carefully port the logic from codegen.cpp:gen_const_val_ptr | ||
| 367 | switch (self.special) { | ||
| 368 | Special.Scalar => |scalar| @panic("TODO"), | ||
| 369 | Special.BaseArray => |base_array| { | ||
| 370 | // TODO put this in one .o file only, and after that, generate extern references to it | ||
| 371 | const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?; | ||
| 372 | const ptr_bit_count = ofile.comp.target_ptr_bits; | ||
| 373 | const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory; | ||
| 374 | const indices = []llvm.ValueRef{ | ||
| 375 | llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory, | ||
| 376 | llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory, | ||
| 377 | }; | ||
| 378 | return llvm.ConstInBoundsGEP( | ||
| 379 | array_llvm_value, | ||
| 380 | &indices, | ||
| 381 | @intCast(c_uint, indices.len), | ||
| 382 | ) orelse return error.OutOfMemory; | ||
| 383 | }, | ||
| 384 | Special.BaseStruct => |base_struct| @panic("TODO"), | ||
| 385 | Special.HardCodedAddr => |addr| @panic("TODO"), | ||
| 386 | Special.Discard => unreachable, | ||
| 387 | } | ||
| 388 | } | ||
| 389 | }; | ||
| 390 | |||
| 391 | pub const Array = struct { | ||
| 392 | base: Value, | ||
| 393 | special: Special, | ||
| 394 | |||
| 395 | pub const Special = union(enum) { | ||
| 396 | Undefined, | ||
| 397 | OwnedBuffer: []u8, | ||
| 398 | Explicit: Data, | ||
| 399 | }; | ||
| 400 | |||
| 401 | pub const Data = struct { | ||
| 402 | parent: Parent, | ||
| 403 | elements: []*Value, | ||
| 404 | }; | ||
| 405 | |||
| 406 | /// Takes ownership of buffer | ||
| 407 | pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array { | ||
| 408 | const u8_type = Type.Int.get_u8(comp); | ||
| 409 | defer u8_type.base.base.deref(comp); | ||
| 410 | |||
| 411 | const array_type = try await (async Type.Array.get(comp, Type.Array.Key{ | ||
| 412 | .elem_type = &u8_type.base, | ||
| 413 | .len = buffer.len, | ||
| 414 | }) catch unreachable); | ||
| 415 | errdefer array_type.base.base.deref(comp); | ||
| 416 | |||
| 417 | const self = try comp.gpa().create(Value.Array{ | ||
| 418 | .base = Value{ | ||
| 419 | .id = Value.Id.Array, | ||
| 420 | .typ = &array_type.base, | ||
| 421 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 422 | }, | ||
| 423 | .special = Special{ .OwnedBuffer = buffer }, | ||
| 424 | }); | ||
| 425 | errdefer comp.gpa().destroy(self); | ||
| 426 | |||
| 427 | return self; | ||
| 428 | } | ||
| 429 | |||
| 430 | pub fn destroy(self: *Array, comp: *Compilation) void { | ||
| 431 | switch (self.special) { | ||
| 432 | Special.Undefined => {}, | ||
| 433 | Special.OwnedBuffer => |buf| { | ||
| 434 | comp.gpa().free(buf); | ||
| 435 | }, | ||
| 436 | Special.Explicit => {}, | ||
| 437 | } | ||
| 438 | comp.gpa().destroy(self); | ||
| 439 | } | ||
| 440 | |||
| 441 | pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef { | ||
| 442 | switch (self.special) { | ||
| 443 | Special.Undefined => { | ||
| 444 | const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); | ||
| 445 | return llvm.GetUndef(llvm_type); | ||
| 446 | }, | ||
| 447 | Special.OwnedBuffer => |buf| { | ||
| 448 | const dont_null_terminate = 1; | ||
| 449 | const llvm_str_init = llvm.ConstStringInContext( | ||
| 450 | ofile.context, | ||
| 451 | buf.ptr, | ||
| 452 | @intCast(c_uint, buf.len), | ||
| 453 | dont_null_terminate, | ||
| 454 | ) orelse return error.OutOfMemory; | ||
| 455 | const str_init_type = llvm.TypeOf(llvm_str_init); | ||
| 456 | const global = llvm.AddGlobal(ofile.module, str_init_type, c"") orelse return error.OutOfMemory; | ||
| 457 | llvm.SetInitializer(global, llvm_str_init); | ||
| 458 | llvm.SetLinkage(global, llvm.PrivateLinkage); | ||
| 459 | llvm.SetGlobalConstant(global, 1); | ||
| 460 | llvm.SetUnnamedAddr(global, 1); | ||
| 461 | llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type)); | ||
| 462 | return global; | ||
| 463 | }, | ||
| 464 | Special.Explicit => @panic("TODO"), | ||
| 465 | } | ||
| 466 | |||
| 467 | //{ | ||
| 468 | // uint64_t len = type_entry->data.array.len; | ||
| 469 | // if (const_val->data.x_array.special == ConstArraySpecialUndef) { | ||
| 470 | // return LLVMGetUndef(type_entry->type_ref); | ||
| 471 | // } | ||
| 472 | |||
| 473 | // LLVMValueRef *values = allocate<LLVMValueRef>(len); | ||
| 474 | // LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref; | ||
| 475 | // bool make_unnamed_struct = false; | ||
| 476 | // for (uint64_t i = 0; i < len; i += 1) { | ||
| 477 | // ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i]; | ||
| 478 | // LLVMValueRef val = gen_const_val(g, elem_value, ""); | ||
| 479 | // values[i] = val; | ||
| 480 | // make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val); | ||
| 481 | // } | ||
| 482 | // if (make_unnamed_struct) { | ||
| 483 | // return LLVMConstStruct(values, len, true); | ||
| 484 | // } else { | ||
| 485 | // return LLVMConstArray(element_type_ref, values, (unsigned)len); | ||
| 486 | // } | ||
| 487 | //} | ||
| 488 | } | ||
| 489 | }; | ||
| 490 | |||
| 491 | pub const Int = struct { | ||
| 492 | base: Value, | ||
| 493 | big_int: std.math.big.Int, | ||
| 494 | |||
| 495 | pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int { | ||
| 496 | const self = try comp.gpa().create(Value.Int{ | ||
| 497 | .base = Value{ | ||
| 498 | .id = Value.Id.Int, | ||
| 499 | .typ = typ, | ||
| 500 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 501 | }, | ||
| 502 | .big_int = undefined, | ||
| 503 | }); | ||
| 504 | typ.base.ref(); | ||
| 505 | errdefer comp.gpa().destroy(self); | ||
| 506 | |||
| 507 | self.big_int = try std.math.big.Int.init(comp.gpa()); | ||
| 508 | errdefer self.big_int.deinit(); | ||
| 509 | |||
| 510 | try self.big_int.setString(base, value); | ||
| 511 | |||
| 512 | return self; | ||
| 513 | } | ||
| 514 | |||
| 515 | pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef { | ||
| 516 | switch (self.base.typ.id) { | ||
| 517 | Type.Id.Int => { | ||
| 518 | const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context); | ||
| 519 | if (self.big_int.len == 0) { | ||
| 520 | return llvm.ConstNull(type_ref); | ||
| 521 | } | ||
| 522 | const unsigned_val = if (self.big_int.len == 1) blk: { | ||
| 523 | break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false)); | ||
| 524 | } else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: { | ||
| 525 | break :blk llvm.ConstIntOfArbitraryPrecision( | ||
| 526 | type_ref, | ||
| 527 | @intCast(c_uint, self.big_int.len), | ||
| 528 | @ptrCast([*]u64, self.big_int.limbs.ptr), | ||
| 529 | ); | ||
| 530 | } else { | ||
| 531 | @compileError("std.math.Big.Int.Limb size does not match LLVM"); | ||
| 532 | }; | ||
| 533 | return if (self.big_int.positive) unsigned_val else llvm.ConstNeg(unsigned_val); | ||
| 534 | }, | ||
| 535 | Type.Id.ComptimeInt => unreachable, | ||
| 536 | else => unreachable, | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | pub fn copy(old: *Int, comp: *Compilation) !*Int { | ||
| 541 | old.base.typ.base.ref(); | ||
| 542 | errdefer old.base.typ.base.deref(comp); | ||
| 543 | |||
| 544 | const new = try comp.gpa().create(Value.Int{ | ||
| 545 | .base = Value{ | ||
| 546 | .id = Value.Id.Int, | ||
| 547 | .typ = old.base.typ, | ||
| 548 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 549 | }, | ||
| 550 | .big_int = undefined, | ||
| 551 | }); | ||
| 552 | errdefer comp.gpa().destroy(new); | ||
| 553 | |||
| 554 | new.big_int = try old.big_int.clone(); | ||
| 555 | errdefer new.big_int.deinit(); | ||
| 556 | |||
| 557 | return new; | ||
| 558 | } | ||
| 559 | |||
| 560 | pub fn destroy(self: *Int, comp: *Compilation) void { | ||
| 561 | self.big_int.deinit(); | ||
| 562 | comp.gpa().destroy(self); | ||
| 174 | } | 563 | } |
| 175 | }; | 564 | }; |
| 176 | }; | 565 | }; |
src/analyze.cpp+6-5| ... | @@ -1454,7 +1454,9 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { | ... | @@ -1454,7 +1454,9 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { |
| 1454 | case TypeTableEntryIdFn: | 1454 | case TypeTableEntryIdFn: |
| 1455 | return type_entry->data.fn.fn_type_id.cc == CallingConventionC; | 1455 | return type_entry->data.fn.fn_type_id.cc == CallingConventionC; |
| 1456 | case TypeTableEntryIdPointer: | 1456 | case TypeTableEntryIdPointer: |
| 1457 | return type_allowed_in_extern(g, type_entry->data.pointer.child_type); | 1457 | if (type_size(g, type_entry) == 0) |
| 1458 | return false; | ||
| 1459 | return true; | ||
| 1458 | case TypeTableEntryIdStruct: | 1460 | case TypeTableEntryIdStruct: |
| 1459 | return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked; | 1461 | return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked; |
| 1460 | case TypeTableEntryIdOptional: | 1462 | case TypeTableEntryIdOptional: |
| ... | @@ -4377,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { | ... | @@ -4377,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 4377 | 4379 | ||
| 4378 | static ZigWindowsSDK *get_windows_sdk(CodeGen *g) { | 4380 | static ZigWindowsSDK *get_windows_sdk(CodeGen *g) { |
| 4379 | if (g->win_sdk == nullptr) { | 4381 | if (g->win_sdk == nullptr) { |
| 4380 | if (os_find_windows_sdk(&g->win_sdk)) { | 4382 | if (zig_find_windows_sdk(&g->win_sdk)) { |
| 4381 | fprintf(stderr, "unable to determine windows sdk path\n"); | 4383 | fprintf(stderr, "unable to determine windows sdk path\n"); |
| 4382 | exit(1); | 4384 | exit(1); |
| 4383 | } | 4385 | } |
| ... | @@ -4497,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) { | ... | @@ -4497,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) { |
| 4497 | ZigWindowsSDK *sdk = get_windows_sdk(g); | 4499 | ZigWindowsSDK *sdk = get_windows_sdk(g); |
| 4498 | 4500 | ||
| 4499 | if (g->msvc_lib_dir == nullptr) { | 4501 | if (g->msvc_lib_dir == nullptr) { |
| 4500 | Buf* vc_lib_dir = buf_alloc(); | 4502 | if (sdk->msvc_lib_dir_ptr == nullptr) { |
| 4501 | if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { | ||
| 4502 | fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); | 4503 | fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); |
| 4503 | exit(1); | 4504 | exit(1); |
| 4504 | } | 4505 | } |
| 4505 | g->msvc_lib_dir = vc_lib_dir; | 4506 | g->msvc_lib_dir = buf_create_from_mem(sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); |
| 4506 | } | 4507 | } |
| 4507 | 4508 | ||
| 4508 | if (g->libc_lib_dir == nullptr) { | 4509 | if (g->libc_lib_dir == nullptr) { |
src/codegen.cpp+76-36| ... | @@ -60,6 +60,33 @@ PackageTableEntry *new_anonymous_package(void) { | ... | @@ -60,6 +60,33 @@ PackageTableEntry *new_anonymous_package(void) { |
| 60 | return new_package("", ""); | 60 | return new_package("", ""); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | static const char *symbols_that_llvm_depends_on[] = { | ||
| 64 | "memcpy", | ||
| 65 | "memset", | ||
| 66 | "sqrt", | ||
| 67 | "powi", | ||
| 68 | "sin", | ||
| 69 | "cos", | ||
| 70 | "pow", | ||
| 71 | "exp", | ||
| 72 | "exp2", | ||
| 73 | "log", | ||
| 74 | "log10", | ||
| 75 | "log2", | ||
| 76 | "fma", | ||
| 77 | "fabs", | ||
| 78 | "minnum", | ||
| 79 | "maxnum", | ||
| 80 | "copysign", | ||
| 81 | "floor", | ||
| 82 | "ceil", | ||
| 83 | "trunc", | ||
| 84 | "rint", | ||
| 85 | "nearbyint", | ||
| 86 | "round", | ||
| 87 | // TODO probably all of compiler-rt needs to go here | ||
| 88 | }; | ||
| 89 | |||
| 63 | CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, | 90 | CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, |
| 64 | Buf *zig_lib_dir) | 91 | Buf *zig_lib_dir) |
| 65 | { | 92 | { |
| ... | @@ -94,6 +121,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out | ... | @@ -94,6 +121,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out |
| 94 | g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); | 121 | g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); |
| 95 | buf_resize(&g->global_asm, 0); | 122 | buf_resize(&g->global_asm, 0); |
| 96 | 123 | ||
| 124 | for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { | ||
| 125 | g->external_prototypes.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr); | ||
| 126 | } | ||
| 127 | |||
| 97 | if (root_src_path) { | 128 | if (root_src_path) { |
| 98 | Buf *src_basename = buf_alloc(); | 129 | Buf *src_basename = buf_alloc(); |
| 99 | Buf *src_dir = buf_alloc(); | 130 | Buf *src_dir = buf_alloc(); |
| ... | @@ -7419,51 +7450,60 @@ static void gen_h_file(CodeGen *g) { | ... | @@ -7419,51 +7450,60 @@ static void gen_h_file(CodeGen *g) { |
| 7419 | case TypeTableEntryIdPromise: | 7450 | case TypeTableEntryIdPromise: |
| 7420 | zig_unreachable(); | 7451 | zig_unreachable(); |
| 7421 | case TypeTableEntryIdEnum: | 7452 | case TypeTableEntryIdEnum: |
| 7422 | assert(type_entry->data.enumeration.layout == ContainerLayoutExtern); | 7453 | if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { |
| 7423 | fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name)); | 7454 | fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name)); |
| 7424 | for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { | 7455 | for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { |
| 7425 | TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; | 7456 | TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; |
| 7426 | Buf *value_buf = buf_alloc(); | 7457 | Buf *value_buf = buf_alloc(); |
| 7427 | bigint_append_buf(value_buf, &enum_field->value, 10); | 7458 | bigint_append_buf(value_buf, &enum_field->value, 10); |
| 7428 | fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); | 7459 | fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); |
| 7429 | if (field_i != type_entry->data.enumeration.src_field_count - 1) { | 7460 | if (field_i != type_entry->data.enumeration.src_field_count - 1) { |
| 7430 | fprintf(out_h, ","); | 7461 | fprintf(out_h, ","); |
| 7462 | } | ||
| 7463 | fprintf(out_h, "\n"); | ||
| 7431 | } | 7464 | } |
| 7432 | fprintf(out_h, "\n"); | 7465 | fprintf(out_h, "};\n\n"); |
| 7466 | } else { | ||
| 7467 | fprintf(out_h, "enum %s;\n", buf_ptr(&type_entry->name)); | ||
| 7433 | } | 7468 | } |
| 7434 | fprintf(out_h, "};\n\n"); | ||
| 7435 | break; | 7469 | break; |
| 7436 | case TypeTableEntryIdStruct: | 7470 | case TypeTableEntryIdStruct: |
| 7437 | assert(type_entry->data.structure.layout == ContainerLayoutExtern); | 7471 | if (type_entry->data.structure.layout == ContainerLayoutExtern) { |
| 7438 | fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name)); | 7472 | fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name)); |
| 7439 | for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { | 7473 | for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { |
| 7440 | TypeStructField *struct_field = &type_entry->data.structure.fields[field_i]; | 7474 | TypeStructField *struct_field = &type_entry->data.structure.fields[field_i]; |
| 7441 | 7475 | ||
| 7442 | Buf *type_name_buf = buf_alloc(); | 7476 | Buf *type_name_buf = buf_alloc(); |
| 7443 | get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); | 7477 | get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); |
| 7444 | 7478 | ||
| 7445 | if (struct_field->type_entry->id == TypeTableEntryIdArray) { | 7479 | if (struct_field->type_entry->id == TypeTableEntryIdArray) { |
| 7446 | fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), | 7480 | fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), |
| 7447 | buf_ptr(struct_field->name), | 7481 | buf_ptr(struct_field->name), |
| 7448 | struct_field->type_entry->data.array.len); | 7482 | struct_field->type_entry->data.array.len); |
| 7449 | } else { | 7483 | } else { |
| 7450 | fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); | 7484 | fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); |
| 7451 | } | 7485 | } |
| 7452 | 7486 | ||
| 7487 | } | ||
| 7488 | fprintf(out_h, "};\n\n"); | ||
| 7489 | } else { | ||
| 7490 | fprintf(out_h, "struct %s;\n", buf_ptr(&type_entry->name)); | ||
| 7453 | } | 7491 | } |
| 7454 | fprintf(out_h, "};\n\n"); | ||
| 7455 | break; | 7492 | break; |
| 7456 | case TypeTableEntryIdUnion: | 7493 | case TypeTableEntryIdUnion: |
| 7457 | assert(type_entry->data.unionation.layout == ContainerLayoutExtern); | 7494 | if (type_entry->data.unionation.layout == ContainerLayoutExtern) { |
| 7458 | fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name)); | 7495 | fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name)); |
| 7459 | for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { | 7496 | for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { |
| 7460 | TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; | 7497 | TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; |
| 7461 | 7498 | ||
| 7462 | Buf *type_name_buf = buf_alloc(); | 7499 | Buf *type_name_buf = buf_alloc(); |
| 7463 | get_c_type(g, gen_h, union_field->type_entry, type_name_buf); | 7500 | get_c_type(g, gen_h, union_field->type_entry, type_name_buf); |
| 7464 | fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); | 7501 | fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); |
| 7502 | } | ||
| 7503 | fprintf(out_h, "};\n\n"); | ||
| 7504 | } else { | ||
| 7505 | fprintf(out_h, "union %s;\n", buf_ptr(&type_entry->name)); | ||
| 7465 | } | 7506 | } |
| 7466 | fprintf(out_h, "};\n\n"); | ||
| 7467 | break; | 7507 | break; |
| 7468 | case TypeTableEntryIdOpaque: | 7508 | case TypeTableEntryIdOpaque: |
| 7469 | fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name)); | 7509 | fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name)); |
src/ir.cpp+63-26| ... | @@ -246,6 +246,8 @@ static void ir_ref_bb(IrBasicBlock *bb) { | ... | @@ -246,6 +246,8 @@ static void ir_ref_bb(IrBasicBlock *bb) { |
| 246 | static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) { | 246 | static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) { |
| 247 | assert(instruction->id != IrInstructionIdInvalid); | 247 | assert(instruction->id != IrInstructionIdInvalid); |
| 248 | instruction->ref_count += 1; | 248 | instruction->ref_count += 1; |
| 249 | if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction)) | ||
| 250 | ir_ref_bb(instruction->owner_bb); | ||
| 249 | } | 251 | } |
| 250 | 252 | ||
| 251 | static void ir_ref_var(VariableTableEntry *var) { | 253 | static void ir_ref_var(VariableTableEntry *var) { |
| ... | @@ -2959,16 +2961,34 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco | ... | @@ -2959,16 +2961,34 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco |
| 2959 | results[ReturnKindUnconditional] = 0; | 2961 | results[ReturnKindUnconditional] = 0; |
| 2960 | results[ReturnKindError] = 0; | 2962 | results[ReturnKindError] = 0; |
| 2961 | 2963 | ||
| 2962 | while (inner_scope != outer_scope) { | 2964 | Scope *scope = inner_scope; |
| 2963 | assert(inner_scope); | ||
| 2964 | if (inner_scope->id == ScopeIdDefer) { | ||
| 2965 | AstNode *defer_node = inner_scope->source_node; | ||
| 2966 | assert(defer_node->type == NodeTypeDefer); | ||
| 2967 | ReturnKind defer_kind = defer_node->data.defer.kind; | ||
| 2968 | results[defer_kind] += 1; | ||
| 2969 | 2965 | ||
| 2966 | while (scope != outer_scope) { | ||
| 2967 | assert(scope); | ||
| 2968 | switch (scope->id) { | ||
| 2969 | case ScopeIdDefer: { | ||
| 2970 | AstNode *defer_node = scope->source_node; | ||
| 2971 | assert(defer_node->type == NodeTypeDefer); | ||
| 2972 | ReturnKind defer_kind = defer_node->data.defer.kind; | ||
| 2973 | results[defer_kind] += 1; | ||
| 2974 | scope = scope->parent; | ||
| 2975 | continue; | ||
| 2976 | } | ||
| 2977 | case ScopeIdDecls: | ||
| 2978 | case ScopeIdFnDef: | ||
| 2979 | return; | ||
| 2980 | case ScopeIdBlock: | ||
| 2981 | case ScopeIdVarDecl: | ||
| 2982 | case ScopeIdLoop: | ||
| 2983 | case ScopeIdSuspend: | ||
| 2984 | case ScopeIdCompTime: | ||
| 2985 | scope = scope->parent; | ||
| 2986 | continue; | ||
| 2987 | case ScopeIdDeferExpr: | ||
| 2988 | case ScopeIdCImport: | ||
| 2989 | case ScopeIdCoroPrelude: | ||
| 2990 | zig_unreachable(); | ||
| 2970 | } | 2991 | } |
| 2971 | inner_scope = inner_scope->parent; | ||
| 2972 | } | 2992 | } |
| 2973 | } | 2993 | } |
| 2974 | 2994 | ||
| ... | @@ -2984,27 +3004,43 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o | ... | @@ -2984,27 +3004,43 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o |
| 2984 | if (!scope) | 3004 | if (!scope) |
| 2985 | return is_noreturn; | 3005 | return is_noreturn; |
| 2986 | 3006 | ||
| 2987 | if (scope->id == ScopeIdDefer) { | 3007 | switch (scope->id) { |
| 2988 | AstNode *defer_node = scope->source_node; | 3008 | case ScopeIdDefer: { |
| 2989 | assert(defer_node->type == NodeTypeDefer); | 3009 | AstNode *defer_node = scope->source_node; |
| 2990 | ReturnKind defer_kind = defer_node->data.defer.kind; | 3010 | assert(defer_node->type == NodeTypeDefer); |
| 2991 | if (defer_kind == ReturnKindUnconditional || | 3011 | ReturnKind defer_kind = defer_node->data.defer.kind; |
| 2992 | (gen_error_defers && defer_kind == ReturnKindError)) | 3012 | if (defer_kind == ReturnKindUnconditional || |
| 2993 | { | 3013 | (gen_error_defers && defer_kind == ReturnKindError)) |
| 2994 | AstNode *defer_expr_node = defer_node->data.defer.expr; | 3014 | { |
| 2995 | Scope *defer_expr_scope = defer_node->data.defer.expr_scope; | 3015 | AstNode *defer_expr_node = defer_node->data.defer.expr; |
| 2996 | IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); | 3016 | Scope *defer_expr_scope = defer_node->data.defer.expr_scope; |
| 2997 | if (defer_expr_value != irb->codegen->invalid_instruction) { | 3017 | IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); |
| 2998 | if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) { | 3018 | if (defer_expr_value != irb->codegen->invalid_instruction) { |
| 2999 | is_noreturn = true; | 3019 | if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) { |
| 3000 | } else { | 3020 | is_noreturn = true; |
| 3001 | ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); | 3021 | } else { |
| 3022 | ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); | ||
| 3023 | } | ||
| 3002 | } | 3024 | } |
| 3003 | } | 3025 | } |
| 3026 | scope = scope->parent; | ||
| 3027 | continue; | ||
| 3004 | } | 3028 | } |
| 3005 | 3029 | case ScopeIdDecls: | |
| 3030 | case ScopeIdFnDef: | ||
| 3031 | return is_noreturn; | ||
| 3032 | case ScopeIdBlock: | ||
| 3033 | case ScopeIdVarDecl: | ||
| 3034 | case ScopeIdLoop: | ||
| 3035 | case ScopeIdSuspend: | ||
| 3036 | case ScopeIdCompTime: | ||
| 3037 | scope = scope->parent; | ||
| 3038 | continue; | ||
| 3039 | case ScopeIdDeferExpr: | ||
| 3040 | case ScopeIdCImport: | ||
| 3041 | case ScopeIdCoroPrelude: | ||
| 3042 | zig_unreachable(); | ||
| 3006 | } | 3043 | } |
| 3007 | scope = scope->parent; | ||
| 3008 | } | 3044 | } |
| 3009 | return is_noreturn; | 3045 | return is_noreturn; |
| 3010 | } | 3046 | } |
| ... | @@ -9408,7 +9444,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc | ... | @@ -9408,7 +9444,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc |
| 9408 | if (type_is_invalid(casted_payload->value.type)) | 9444 | if (type_is_invalid(casted_payload->value.type)) |
| 9409 | return ira->codegen->invalid_instruction; | 9445 | return ira->codegen->invalid_instruction; |
| 9410 | 9446 | ||
| 9411 | ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad); | 9447 | ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefOk); |
| 9412 | if (!val) | 9448 | if (!val) |
| 9413 | return ira->codegen->invalid_instruction; | 9449 | return ira->codegen->invalid_instruction; |
| 9414 | 9450 | ||
| ... | @@ -13090,6 +13126,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal | ... | @@ -13090,6 +13126,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 13090 | impl_fn->ir_executable.parent_exec = ira->new_irb.exec; | 13126 | impl_fn->ir_executable.parent_exec = ira->new_irb.exec; |
| 13091 | impl_fn->analyzed_executable.source_node = call_instruction->base.source_node; | 13127 | impl_fn->analyzed_executable.source_node = call_instruction->base.source_node; |
| 13092 | impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; | 13128 | impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; |
| 13129 | impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota; | ||
| 13093 | impl_fn->analyzed_executable.is_generic_instantiation = true; | 13130 | impl_fn->analyzed_executable.is_generic_instantiation = true; |
| 13094 | 13131 | ||
| 13095 | ira->codegen->fn_defs.append(impl_fn); | 13132 | ira->codegen->fn_defs.append(impl_fn); |
src/link.cpp+1-1| ... | @@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) { | ... | @@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) { |
| 901 | if (strchr(buf_ptr(link_lib->name), '/') == nullptr) { | 901 | if (strchr(buf_ptr(link_lib->name), '/') == nullptr) { |
| 902 | Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); | 902 | Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); |
| 903 | lj->args.append(buf_ptr(arg)); | 903 | lj->args.append(buf_ptr(arg)); |
| 904 | } else { | 904 | } else { |
| 905 | lj->args.append(buf_ptr(link_lib->name)); | 905 | lj->args.append(buf_ptr(link_lib->name)); |
| 906 | } | 906 | } |
| 907 | } | 907 | } |
src/os.cpp+4-244| ... | @@ -26,7 +26,6 @@ | ... | @@ -26,7 +26,6 @@ |
| 26 | #include <windows.h> | 26 | #include <windows.h> |
| 27 | #include <io.h> | 27 | #include <io.h> |
| 28 | #include <fcntl.h> | 28 | #include <fcntl.h> |
| 29 | #include "windows_com.hpp" | ||
| 30 | 29 | ||
| 31 | typedef SSIZE_T ssize_t; | 30 | typedef SSIZE_T ssize_t; |
| 32 | #else | 31 | #else |
| ... | @@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) { | ... | @@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) { |
| 1115 | #endif | 1114 | #endif |
| 1116 | } | 1115 | } |
| 1117 | 1116 | ||
| 1118 | int os_find_windows_sdk(ZigWindowsSDK **out_sdk) { | ||
| 1119 | #if defined(ZIG_OS_WINDOWS) | ||
| 1120 | ZigWindowsSDK *result_sdk = allocate<ZigWindowsSDK>(1); | ||
| 1121 | buf_resize(&result_sdk->path10, 0); | ||
| 1122 | buf_resize(&result_sdk->path81, 0); | ||
| 1123 | |||
| 1124 | HKEY key; | ||
| 1125 | HRESULT rc; | ||
| 1126 | rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key); | ||
| 1127 | if (rc != ERROR_SUCCESS) { | ||
| 1128 | return ErrorFileNotFound; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | { | ||
| 1132 | DWORD tmp_buf_len = MAX_PATH; | ||
| 1133 | buf_resize(&result_sdk->path10, tmp_buf_len); | ||
| 1134 | rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path10), &tmp_buf_len); | ||
| 1135 | if (rc == ERROR_FILE_NOT_FOUND) { | ||
| 1136 | buf_resize(&result_sdk->path10, 0); | ||
| 1137 | } else { | ||
| 1138 | buf_resize(&result_sdk->path10, tmp_buf_len); | ||
| 1139 | } | ||
| 1140 | } | ||
| 1141 | { | ||
| 1142 | DWORD tmp_buf_len = MAX_PATH; | ||
| 1143 | buf_resize(&result_sdk->path81, tmp_buf_len); | ||
| 1144 | rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path81), &tmp_buf_len); | ||
| 1145 | if (rc == ERROR_FILE_NOT_FOUND) { | ||
| 1146 | buf_resize(&result_sdk->path81, 0); | ||
| 1147 | } else { | ||
| 1148 | buf_resize(&result_sdk->path81, tmp_buf_len); | ||
| 1149 | } | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | if (buf_len(&result_sdk->path10) != 0) { | ||
| 1153 | Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\*", buf_ptr(&result_sdk->path10)); | ||
| 1154 | |||
| 1155 | // enumerate files in sdk path looking for latest version | ||
| 1156 | WIN32_FIND_DATA ffd; | ||
| 1157 | HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd); | ||
| 1158 | if (hFind == INVALID_HANDLE_VALUE) { | ||
| 1159 | return ErrorFileNotFound; | ||
| 1160 | } | ||
| 1161 | int v0 = 0, v1 = 0, v2 = 0, v3 = 0; | ||
| 1162 | bool found_version_dir = false; | ||
| 1163 | for (;;) { | ||
| 1164 | if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | ||
| 1165 | int c0 = 0, c1 = 0, c2 = 0, c3 = 0; | ||
| 1166 | sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3); | ||
| 1167 | if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) { | ||
| 1168 | // Microsoft released 26624 as 10240 accidentally. | ||
| 1169 | // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive | ||
| 1170 | c2 = 26624; | ||
| 1171 | } | ||
| 1172 | if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) { | ||
| 1173 | v0 = c0, v1 = c1, v2 = c2, v3 = c3; | ||
| 1174 | buf_init_from_str(&result_sdk->version10, ffd.cFileName); | ||
| 1175 | found_version_dir = true; | ||
| 1176 | } | ||
| 1177 | } | ||
| 1178 | if (FindNextFile(hFind, &ffd) == 0) { | ||
| 1179 | FindClose(hFind); | ||
| 1180 | break; | ||
| 1181 | } | ||
| 1182 | } | ||
| 1183 | if (!found_version_dir) { | ||
| 1184 | buf_resize(&result_sdk->path10, 0); | ||
| 1185 | } | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | if (buf_len(&result_sdk->path81) != 0) { | ||
| 1189 | Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\winv*", buf_ptr(&result_sdk->path81)); | ||
| 1190 | |||
| 1191 | // enumerate files in sdk path looking for latest version | ||
| 1192 | WIN32_FIND_DATA ffd; | ||
| 1193 | HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd); | ||
| 1194 | if (hFind == INVALID_HANDLE_VALUE) { | ||
| 1195 | return ErrorFileNotFound; | ||
| 1196 | } | ||
| 1197 | int v0 = 0, v1 = 0; | ||
| 1198 | bool found_version_dir = false; | ||
| 1199 | for (;;) { | ||
| 1200 | if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | ||
| 1201 | int c0 = 0, c1 = 0; | ||
| 1202 | sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1); | ||
| 1203 | if ((c0 > v0) || (c1 > v1)) { | ||
| 1204 | v0 = c0, v1 = c1; | ||
| 1205 | buf_init_from_str(&result_sdk->version81, ffd.cFileName); | ||
| 1206 | found_version_dir = true; | ||
| 1207 | } | ||
| 1208 | } | ||
| 1209 | if (FindNextFile(hFind, &ffd) == 0) { | ||
| 1210 | FindClose(hFind); | ||
| 1211 | break; | ||
| 1212 | } | ||
| 1213 | } | ||
| 1214 | if (!found_version_dir) { | ||
| 1215 | buf_resize(&result_sdk->path81, 0); | ||
| 1216 | } | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | *out_sdk = result_sdk; | ||
| 1220 | return 0; | ||
| 1221 | #else | ||
| 1222 | return ErrorFileNotFound; | ||
| 1223 | #endif | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | int os_get_win32_vcruntime_path(Buf* output_buf, ZigLLVM_ArchType platform_type) { | ||
| 1227 | #if defined(ZIG_OS_WINDOWS) | ||
| 1228 | buf_resize(output_buf, 0); | ||
| 1229 | //COM Smart Pointerse requires explicit scope | ||
| 1230 | { | ||
| 1231 | HRESULT rc; | ||
| 1232 | rc = CoInitializeEx(NULL, COINIT_MULTITHREADED); | ||
| 1233 | if (rc != S_OK) { | ||
| 1234 | goto com_done; | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | //This COM class is installed when a VS2017 | ||
| 1238 | ISetupConfigurationPtr setup_config; | ||
| 1239 | rc = setup_config.CreateInstance(__uuidof(SetupConfiguration)); | ||
| 1240 | if (rc != S_OK) { | ||
| 1241 | goto com_done; | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | IEnumSetupInstancesPtr all_instances; | ||
| 1245 | rc = setup_config->EnumInstances(&all_instances); | ||
| 1246 | if (rc != S_OK) { | ||
| 1247 | goto com_done; | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | ISetupInstance* curr_instance; | ||
| 1251 | ULONG found_inst; | ||
| 1252 | while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) { | ||
| 1253 | BSTR bstr_inst_path; | ||
| 1254 | rc = curr_instance->GetInstallationPath(&bstr_inst_path); | ||
| 1255 | if (rc != S_OK) { | ||
| 1256 | goto com_done; | ||
| 1257 | } | ||
| 1258 | //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length | ||
| 1259 | UINT bstr_path_len = *((UINT*)bstr_inst_path - 1); | ||
| 1260 | ULONG tmp_path_len = bstr_path_len / 2 + 1; | ||
| 1261 | char* conv_path = (char*)bstr_inst_path; | ||
| 1262 | char *tmp_path = (char*)alloca(tmp_path_len); | ||
| 1263 | memset(tmp_path, 0, tmp_path_len); | ||
| 1264 | uint32_t c = 0; | ||
| 1265 | for (uint32_t i = 0; i < bstr_path_len; i += 2) { | ||
| 1266 | tmp_path[c] = conv_path[i]; | ||
| 1267 | ++c; | ||
| 1268 | assert(c != tmp_path_len); | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | buf_append_str(output_buf, tmp_path); | ||
| 1272 | buf_append_char(output_buf, '\\'); | ||
| 1273 | |||
| 1274 | Buf* tmp_buf = buf_alloc(); | ||
| 1275 | buf_append_buf(tmp_buf, output_buf); | ||
| 1276 | buf_append_str(tmp_buf, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"); | ||
| 1277 | FILE* tools_file = fopen(buf_ptr(tmp_buf), "r"); | ||
| 1278 | if (!tools_file) { | ||
| 1279 | goto com_done; | ||
| 1280 | } | ||
| 1281 | memset(tmp_path, 0, tmp_path_len); | ||
| 1282 | fgets(tmp_path, tmp_path_len, tools_file); | ||
| 1283 | strtok(tmp_path, " \r\n"); | ||
| 1284 | fclose(tools_file); | ||
| 1285 | buf_appendf(output_buf, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path); | ||
| 1286 | switch (platform_type) { | ||
| 1287 | case ZigLLVM_x86: | ||
| 1288 | buf_append_str(output_buf, "x86\\"); | ||
| 1289 | break; | ||
| 1290 | case ZigLLVM_x86_64: | ||
| 1291 | buf_append_str(output_buf, "x64\\"); | ||
| 1292 | break; | ||
| 1293 | case ZigLLVM_arm: | ||
| 1294 | buf_append_str(output_buf, "arm\\"); | ||
| 1295 | break; | ||
| 1296 | default: | ||
| 1297 | zig_panic("Attemped to use vcruntime for non-supported platform."); | ||
| 1298 | } | ||
| 1299 | buf_resize(tmp_buf, 0); | ||
| 1300 | buf_append_buf(tmp_buf, output_buf); | ||
| 1301 | buf_append_str(tmp_buf, "vcruntime.lib"); | ||
| 1302 | |||
| 1303 | if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { | ||
| 1304 | return 0; | ||
| 1305 | } | ||
| 1306 | } | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | com_done:; | ||
| 1310 | HKEY key; | ||
| 1311 | HRESULT rc; | ||
| 1312 | rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key); | ||
| 1313 | if (rc != ERROR_SUCCESS) { | ||
| 1314 | return ErrorFileNotFound; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | DWORD dw_type = 0; | ||
| 1318 | DWORD cb_data = 0; | ||
| 1319 | rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data); | ||
| 1320 | if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) { | ||
| 1321 | return ErrorFileNotFound; | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | Buf* tmp_buf = buf_alloc_fixed(cb_data); | ||
| 1325 | RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)buf_ptr(tmp_buf), &cb_data); | ||
| 1326 | //RegQueryValueExA returns the length of the string INCLUDING the null terminator | ||
| 1327 | buf_resize(tmp_buf, cb_data-1); | ||
| 1328 | buf_append_str(tmp_buf, "VC\\Lib\\"); | ||
| 1329 | switch (platform_type) { | ||
| 1330 | case ZigLLVM_x86: | ||
| 1331 | //x86 is in the root of the Lib folder | ||
| 1332 | break; | ||
| 1333 | case ZigLLVM_x86_64: | ||
| 1334 | buf_append_str(tmp_buf, "amd64\\"); | ||
| 1335 | break; | ||
| 1336 | case ZigLLVM_arm: | ||
| 1337 | buf_append_str(tmp_buf, "arm\\"); | ||
| 1338 | break; | ||
| 1339 | default: | ||
| 1340 | zig_panic("Attemped to use vcruntime for non-supported platform."); | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | buf_append_buf(output_buf, tmp_buf); | ||
| 1344 | buf_append_str(tmp_buf, "vcruntime.lib"); | ||
| 1345 | |||
| 1346 | if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { | ||
| 1347 | return 0; | ||
| 1348 | } else { | ||
| 1349 | buf_resize(output_buf, 0); | ||
| 1350 | return ErrorFileNotFound; | ||
| 1351 | } | ||
| 1352 | #else | ||
| 1353 | return ErrorFileNotFound; | ||
| 1354 | #endif | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { | 1117 | int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { |
| 1358 | #if defined(ZIG_OS_WINDOWS) | 1118 | #if defined(ZIG_OS_WINDOWS) |
| 1359 | buf_resize(output_buf, 0); | 1119 | buf_resize(output_buf, 0); |
| 1360 | buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); | 1120 | buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr); |
| 1361 | switch (platform_type) { | 1121 | switch (platform_type) { |
| 1362 | case ZigLLVM_x86: | 1122 | case ZigLLVM_x86: |
| 1363 | buf_append_str(output_buf, "x86\\"); | 1123 | buf_append_str(output_buf, "x86\\"); |
| ... | @@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch | ... | @@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch |
| 1389 | int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { | 1149 | int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { |
| 1390 | #if defined(ZIG_OS_WINDOWS) | 1150 | #if defined(ZIG_OS_WINDOWS) |
| 1391 | buf_resize(output_buf, 0); | 1151 | buf_resize(output_buf, 0); |
| 1392 | buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); | 1152 | buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr); |
| 1393 | if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) { | 1153 | if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) { |
| 1394 | return 0; | 1154 | return 0; |
| 1395 | } | 1155 | } |
| ... | @@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy | ... | @@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy |
| 1406 | #if defined(ZIG_OS_WINDOWS) | 1166 | #if defined(ZIG_OS_WINDOWS) |
| 1407 | { | 1167 | { |
| 1408 | buf_resize(output_buf, 0); | 1168 | buf_resize(output_buf, 0); |
| 1409 | buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); | 1169 | buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr); |
| 1410 | switch (platform_type) { | 1170 | switch (platform_type) { |
| 1411 | case ZigLLVM_x86: | 1171 | case ZigLLVM_x86: |
| 1412 | buf_append_str(output_buf, "x86\\"); | 1172 | buf_append_str(output_buf, "x86\\"); |
| ... | @@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy | ... | @@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy |
| 1429 | } | 1189 | } |
| 1430 | { | 1190 | { |
| 1431 | buf_resize(output_buf, 0); | 1191 | buf_resize(output_buf, 0); |
| 1432 | buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path81), buf_ptr(&sdk->version81)); | 1192 | buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr); |
| 1433 | switch (platform_type) { | 1193 | switch (platform_type) { |
| 1434 | case ZigLLVM_x86: | 1194 | case ZigLLVM_x86: |
| 1435 | buf_append_str(output_buf, "x86\\"); | 1195 | buf_append_str(output_buf, "x86\\"); |
src/os.hpp+1-9| ... | @@ -12,6 +12,7 @@ | ... | @@ -12,6 +12,7 @@ |
| 12 | #include "buffer.hpp" | 12 | #include "buffer.hpp" |
| 13 | #include "error.hpp" | 13 | #include "error.hpp" |
| 14 | #include "zig_llvm.h" | 14 | #include "zig_llvm.h" |
| 15 | #include "windows_sdk.h" | ||
| 15 | 16 | ||
| 16 | #include <stdio.h> | 17 | #include <stdio.h> |
| 17 | #include <inttypes.h> | 18 | #include <inttypes.h> |
| ... | @@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c); | ... | @@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c); |
| 79 | 80 | ||
| 80 | int os_self_exe_path(Buf *out_path); | 81 | int os_self_exe_path(Buf *out_path); |
| 81 | 82 | ||
| 82 | struct ZigWindowsSDK { | ||
| 83 | Buf path10; | ||
| 84 | Buf version10; | ||
| 85 | Buf path81; | ||
| 86 | Buf version81; | ||
| 87 | }; | ||
| 88 | |||
| 89 | int os_find_windows_sdk(ZigWindowsSDK **out_sdk); | ||
| 90 | int os_get_win32_vcruntime_path(Buf *output_buf, ZigLLVM_ArchType platform_type); | ||
| 91 | int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf); | 83 | int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf); |
| 92 | int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); | 84 | int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); |
| 93 | int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); | 85 | int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); |
src/tokenizer.cpp+13-8| ... | @@ -460,16 +460,21 @@ static const char* get_escape_shorthand(uint8_t c) { | ... | @@ -460,16 +460,21 @@ static const char* get_escape_shorthand(uint8_t c) { |
| 460 | static void invalid_char_error(Tokenize *t, uint8_t c) { | 460 | static void invalid_char_error(Tokenize *t, uint8_t c) { |
| 461 | if (c == '\r') { | 461 | if (c == '\r') { |
| 462 | tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported"); | 462 | tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported"); |
| 463 | } else if (isprint(c)) { | 463 | return; |
| 464 | } | ||
| 465 | |||
| 466 | const char *sh = get_escape_shorthand(c); | ||
| 467 | if (sh) { | ||
| 468 | tokenize_error(t, "invalid character: '%s'", sh); | ||
| 469 | return; | ||
| 470 | } | ||
| 471 | |||
| 472 | if (isprint(c)) { | ||
| 464 | tokenize_error(t, "invalid character: '%c'", c); | 473 | tokenize_error(t, "invalid character: '%c'", c); |
| 465 | } else { | 474 | return; |
| 466 | const char *sh = get_escape_shorthand(c); | ||
| 467 | if (sh) { | ||
| 468 | tokenize_error(t, "invalid character: '%s'", sh); | ||
| 469 | } else { | ||
| 470 | tokenize_error(t, "invalid character: '\\x%x'", c); | ||
| 471 | } | ||
| 472 | } | 475 | } |
| 476 | |||
| 477 | tokenize_error(t, "invalid character: '\\x%02x'", c); | ||
| 473 | } | 478 | } |
| 474 | 479 | ||
| 475 | void tokenize(Buf *buf, Tokenization *out) { | 480 | void tokenize(Buf *buf, Tokenization *out) { |
src/windows_sdk.cpp created+352| ... | @@ -0,0 +1,352 @@ | ||
| 1 | /* | ||
| 2 | * Copyright (c) 2018 Andrew Kelley | ||
| 3 | * | ||
| 4 | * This file is part of zig, which is MIT licensed. | ||
| 5 | * See http://opensource.org/licenses/MIT | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include "windows_sdk.h" | ||
| 9 | |||
| 10 | #if defined(_WIN32) | ||
| 11 | |||
| 12 | #include "windows_com.hpp" | ||
| 13 | #include <inttypes.h> | ||
| 14 | #include <assert.h> | ||
| 15 | |||
| 16 | struct ZigWindowsSDKPrivate { | ||
| 17 | ZigWindowsSDK base; | ||
| 18 | }; | ||
| 19 | |||
| 20 | enum NativeArch { | ||
| 21 | NativeArchArm, | ||
| 22 | NativeArchi386, | ||
| 23 | NativeArchx86_64, | ||
| 24 | }; | ||
| 25 | |||
| 26 | #if defined(_M_ARM) || defined(__arm_) | ||
| 27 | static const NativeArch native_arch = NativeArchArm; | ||
| 28 | #endif | ||
| 29 | #if defined(_M_IX86) || defined(__i386__) | ||
| 30 | static const NativeArch native_arch = NativeArchi386; | ||
| 31 | #endif | ||
| 32 | #if defined(_M_X64) || defined(__x86_64__) | ||
| 33 | static const NativeArch native_arch = NativeArchx86_64; | ||
| 34 | #endif | ||
| 35 | |||
| 36 | void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) { | ||
| 37 | if (sdk == nullptr) { | ||
| 38 | return; | ||
| 39 | } | ||
| 40 | free((void*)sdk->path10_ptr); | ||
| 41 | free((void*)sdk->version10_ptr); | ||
| 42 | free((void*)sdk->path81_ptr); | ||
| 43 | free((void*)sdk->version81_ptr); | ||
| 44 | free((void*)sdk->msvc_lib_dir_ptr); | ||
| 45 | } | ||
| 46 | |||
| 47 | static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) { | ||
| 48 | //COM Smart Pointers requires explicit scope | ||
| 49 | { | ||
| 50 | HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED); | ||
| 51 | if (rc != S_OK && rc != S_FALSE) { | ||
| 52 | goto com_done; | ||
| 53 | } | ||
| 54 | |||
| 55 | //This COM class is installed when a VS2017 | ||
| 56 | ISetupConfigurationPtr setup_config; | ||
| 57 | rc = setup_config.CreateInstance(__uuidof(SetupConfiguration)); | ||
| 58 | if (rc != S_OK) { | ||
| 59 | goto com_done; | ||
| 60 | } | ||
| 61 | |||
| 62 | IEnumSetupInstancesPtr all_instances; | ||
| 63 | rc = setup_config->EnumInstances(&all_instances); | ||
| 64 | if (rc != S_OK) { | ||
| 65 | goto com_done; | ||
| 66 | } | ||
| 67 | |||
| 68 | ISetupInstance* curr_instance; | ||
| 69 | ULONG found_inst; | ||
| 70 | while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) { | ||
| 71 | BSTR bstr_inst_path; | ||
| 72 | rc = curr_instance->GetInstallationPath(&bstr_inst_path); | ||
| 73 | if (rc != S_OK) { | ||
| 74 | goto com_done; | ||
| 75 | } | ||
| 76 | //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length | ||
| 77 | //TODO call an actual function to do this | ||
| 78 | UINT bstr_path_len = *((UINT*)bstr_inst_path - 1); | ||
| 79 | ULONG tmp_path_len = bstr_path_len / 2 + 1; | ||
| 80 | char* conv_path = (char*)bstr_inst_path; | ||
| 81 | // TODO don't use alloca | ||
| 82 | char *tmp_path = (char*)alloca(tmp_path_len); | ||
| 83 | memset(tmp_path, 0, tmp_path_len); | ||
| 84 | uint32_t c = 0; | ||
| 85 | for (uint32_t i = 0; i < bstr_path_len; i += 2) { | ||
| 86 | tmp_path[c] = conv_path[i]; | ||
| 87 | ++c; | ||
| 88 | assert(c != tmp_path_len); | ||
| 89 | } | ||
| 90 | char output_path[4096]; | ||
| 91 | output_path[0] = 0; | ||
| 92 | char *out_append_ptr = output_path; | ||
| 93 | |||
| 94 | out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path); | ||
| 95 | |||
| 96 | char tmp_buf[4096]; | ||
| 97 | sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"); | ||
| 98 | FILE* tools_file = fopen(tmp_buf, "rb"); | ||
| 99 | if (!tools_file) { | ||
| 100 | goto com_done; | ||
| 101 | } | ||
| 102 | memset(tmp_path, 0, tmp_path_len); | ||
| 103 | fgets(tmp_path, tmp_path_len, tools_file); | ||
| 104 | strtok(tmp_path, " \r\n"); | ||
| 105 | fclose(tools_file); | ||
| 106 | out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path); | ||
| 107 | switch (native_arch) { | ||
| 108 | case NativeArchi386: | ||
| 109 | out_append_ptr += sprintf(out_append_ptr, "x86\\"); | ||
| 110 | break; | ||
| 111 | case NativeArchx86_64: | ||
| 112 | out_append_ptr += sprintf(out_append_ptr, "x64\\"); | ||
| 113 | break; | ||
| 114 | case NativeArchArm: | ||
| 115 | out_append_ptr += sprintf(out_append_ptr, "arm\\"); | ||
| 116 | break; | ||
| 117 | } | ||
| 118 | sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib"); | ||
| 119 | |||
| 120 | if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) { | ||
| 121 | priv->base.msvc_lib_dir_ptr = strdup(output_path); | ||
| 122 | if (priv->base.msvc_lib_dir_ptr == nullptr) { | ||
| 123 | return ZigFindWindowsSdkErrorOutOfMemory; | ||
| 124 | } | ||
| 125 | priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr); | ||
| 126 | return ZigFindWindowsSdkErrorNone; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | com_done:; | ||
| 132 | HKEY key; | ||
| 133 | HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, | ||
| 134 | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key); | ||
| 135 | if (rc != ERROR_SUCCESS) { | ||
| 136 | return ZigFindWindowsSdkErrorNotFound; | ||
| 137 | } | ||
| 138 | |||
| 139 | DWORD dw_type = 0; | ||
| 140 | DWORD cb_data = 0; | ||
| 141 | rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data); | ||
| 142 | if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) { | ||
| 143 | return ZigFindWindowsSdkErrorNotFound; | ||
| 144 | } | ||
| 145 | |||
| 146 | char tmp_buf[4096]; | ||
| 147 | |||
| 148 | RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data); | ||
| 149 | // RegQueryValueExA returns the length of the string INCLUDING the null terminator | ||
| 150 | char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1); | ||
| 151 | tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\"); | ||
| 152 | switch (native_arch) { | ||
| 153 | case NativeArchi386: | ||
| 154 | //x86 is in the root of the Lib folder | ||
| 155 | break; | ||
| 156 | case NativeArchx86_64: | ||
| 157 | tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\"); | ||
| 158 | break; | ||
| 159 | case NativeArchArm: | ||
| 160 | tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\"); | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | |||
| 164 | char *output_path = strdup(tmp_buf); | ||
| 165 | if (output_path == nullptr) { | ||
| 166 | return ZigFindWindowsSdkErrorOutOfMemory; | ||
| 167 | } | ||
| 168 | |||
| 169 | tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib"); | ||
| 170 | |||
| 171 | if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) { | ||
| 172 | priv->base.msvc_lib_dir_ptr = output_path; | ||
| 173 | priv->base.msvc_lib_dir_len = strlen(output_path); | ||
| 174 | return ZigFindWindowsSdkErrorNone; | ||
| 175 | } else { | ||
| 176 | free(output_path); | ||
| 177 | return ZigFindWindowsSdkErrorNotFound; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) { | ||
| 182 | if (priv->base.path10_ptr == nullptr) | ||
| 183 | return ZigFindWindowsSdkErrorNone; | ||
| 184 | |||
| 185 | char sdk_lib_dir[4096]; | ||
| 186 | int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\*", priv->base.path10_ptr); | ||
| 187 | if (n < 0 || n >= 4096) { | ||
| 188 | return ZigFindWindowsSdkErrorPathTooLong; | ||
| 189 | } | ||
| 190 | |||
| 191 | // enumerate files in sdk path looking for latest version | ||
| 192 | WIN32_FIND_DATA ffd; | ||
| 193 | HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd); | ||
| 194 | if (hFind == INVALID_HANDLE_VALUE) { | ||
| 195 | return ZigFindWindowsSdkErrorNotFound; | ||
| 196 | } | ||
| 197 | int v0 = 0, v1 = 0, v2 = 0, v3 = 0; | ||
| 198 | for (;;) { | ||
| 199 | if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | ||
| 200 | int c0 = 0, c1 = 0, c2 = 0, c3 = 0; | ||
| 201 | sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3); | ||
| 202 | if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) { | ||
| 203 | // Microsoft released 26624 as 10240 accidentally. | ||
| 204 | // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive | ||
| 205 | c2 = 26624; | ||
| 206 | } | ||
| 207 | if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) { | ||
| 208 | v0 = c0, v1 = c1, v2 = c2, v3 = c3; | ||
| 209 | free((void*)priv->base.version10_ptr); | ||
| 210 | priv->base.version10_ptr = strdup(ffd.cFileName); | ||
| 211 | if (priv->base.version10_ptr == nullptr) { | ||
| 212 | FindClose(hFind); | ||
| 213 | return ZigFindWindowsSdkErrorOutOfMemory; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | } | ||
| 217 | if (FindNextFile(hFind, &ffd) == 0) { | ||
| 218 | FindClose(hFind); | ||
| 219 | break; | ||
| 220 | } | ||
| 221 | } | ||
| 222 | priv->base.version10_len = strlen(priv->base.version10_ptr); | ||
| 223 | return ZigFindWindowsSdkErrorNone; | ||
| 224 | } | ||
| 225 | |||
| 226 | static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) { | ||
| 227 | if (priv->base.path81_ptr == nullptr) | ||
| 228 | return ZigFindWindowsSdkErrorNone; | ||
| 229 | |||
| 230 | char sdk_lib_dir[4096]; | ||
| 231 | int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr); | ||
| 232 | if (n < 0 || n >= 4096) { | ||
| 233 | return ZigFindWindowsSdkErrorPathTooLong; | ||
| 234 | } | ||
| 235 | |||
| 236 | // enumerate files in sdk path looking for latest version | ||
| 237 | WIN32_FIND_DATA ffd; | ||
| 238 | HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd); | ||
| 239 | if (hFind == INVALID_HANDLE_VALUE) { | ||
| 240 | return ZigFindWindowsSdkErrorNotFound; | ||
| 241 | } | ||
| 242 | int v0 = 0, v1 = 0; | ||
| 243 | for (;;) { | ||
| 244 | if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | ||
| 245 | int c0 = 0, c1 = 0; | ||
| 246 | sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1); | ||
| 247 | if ((c0 > v0) || (c1 > v1)) { | ||
| 248 | v0 = c0, v1 = c1; | ||
| 249 | free((void*)priv->base.version81_ptr); | ||
| 250 | priv->base.version81_ptr = strdup(ffd.cFileName); | ||
| 251 | if (priv->base.version81_ptr == nullptr) { | ||
| 252 | FindClose(hFind); | ||
| 253 | return ZigFindWindowsSdkErrorOutOfMemory; | ||
| 254 | } | ||
| 255 | } | ||
| 256 | } | ||
| 257 | if (FindNextFile(hFind, &ffd) == 0) { | ||
| 258 | FindClose(hFind); | ||
| 259 | break; | ||
| 260 | } | ||
| 261 | } | ||
| 262 | priv->base.version81_len = strlen(priv->base.version81_ptr); | ||
| 263 | return ZigFindWindowsSdkErrorNone; | ||
| 264 | } | ||
| 265 | |||
| 266 | ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { | ||
| 267 | ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate)); | ||
| 268 | if (priv == nullptr) { | ||
| 269 | return ZigFindWindowsSdkErrorOutOfMemory; | ||
| 270 | } | ||
| 271 | |||
| 272 | HKEY key; | ||
| 273 | HRESULT rc; | ||
| 274 | rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, | ||
| 275 | KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key); | ||
| 276 | if (rc != ERROR_SUCCESS) { | ||
| 277 | zig_free_windows_sdk(&priv->base); | ||
| 278 | return ZigFindWindowsSdkErrorNotFound; | ||
| 279 | } | ||
| 280 | |||
| 281 | { | ||
| 282 | DWORD tmp_buf_len = MAX_PATH; | ||
| 283 | priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1); | ||
| 284 | if (priv->base.path10_ptr == nullptr) { | ||
| 285 | zig_free_windows_sdk(&priv->base); | ||
| 286 | return ZigFindWindowsSdkErrorOutOfMemory; | ||
| 287 | } | ||
| 288 | rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len); | ||
| 289 | if (rc == ERROR_SUCCESS) { | ||
| 290 | priv->base.path10_len = tmp_buf_len - 1; | ||
| 291 | if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') { | ||
| 292 | priv->base.path10_len -= 1; | ||
| 293 | } | ||
| 294 | } else { | ||
| 295 | free((void*)priv->base.path10_ptr); | ||
| 296 | priv->base.path10_ptr = nullptr; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | { | ||
| 300 | DWORD tmp_buf_len = MAX_PATH; | ||
| 301 | priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1); | ||
| 302 | if (priv->base.path81_ptr == nullptr) { | ||
| 303 | zig_free_windows_sdk(&priv->base); | ||
| 304 | return ZigFindWindowsSdkErrorOutOfMemory; | ||
| 305 | } | ||
| 306 | rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len); | ||
| 307 | if (rc == ERROR_SUCCESS) { | ||
| 308 | priv->base.path81_len = tmp_buf_len - 1; | ||
| 309 | if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') { | ||
| 310 | priv->base.path81_len -= 1; | ||
| 311 | } | ||
| 312 | } else { | ||
| 313 | free((void*)priv->base.path81_ptr); | ||
| 314 | priv->base.path81_ptr = nullptr; | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | { | ||
| 319 | ZigFindWindowsSdkError err = find_10_version(priv); | ||
| 320 | if (err == ZigFindWindowsSdkErrorOutOfMemory) { | ||
| 321 | zig_free_windows_sdk(&priv->base); | ||
| 322 | return err; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | { | ||
| 326 | ZigFindWindowsSdkError err = find_81_version(priv); | ||
| 327 | if (err == ZigFindWindowsSdkErrorOutOfMemory) { | ||
| 328 | zig_free_windows_sdk(&priv->base); | ||
| 329 | return err; | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | { | ||
| 334 | ZigFindWindowsSdkError err = find_msvc_lib_dir(priv); | ||
| 335 | if (err == ZigFindWindowsSdkErrorOutOfMemory) { | ||
| 336 | zig_free_windows_sdk(&priv->base); | ||
| 337 | return err; | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | *out_sdk = &priv->base; | ||
| 342 | return ZigFindWindowsSdkErrorNone; | ||
| 343 | } | ||
| 344 | |||
| 345 | #else | ||
| 346 | |||
| 347 | void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {} | ||
| 348 | ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { | ||
| 349 | return ZigFindWindowsSdkErrorNotFound; | ||
| 350 | } | ||
| 351 | |||
| 352 | #endif | ||
src/windows_sdk.h created+47| ... | @@ -0,0 +1,47 @@ | ||
| 1 | /* | ||
| 2 | * Copyright (c) 2018 Andrew Kelley | ||
| 3 | * | ||
| 4 | * This file is part of zig, which is MIT licensed. | ||
| 5 | * See http://opensource.org/licenses/MIT | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef ZIG_WINDOWS_SDK_H | ||
| 9 | #define ZIG_WINDOWS_SDK_H | ||
| 10 | |||
| 11 | #ifdef __cplusplus | ||
| 12 | #define ZIG_EXTERN_C extern "C" | ||
| 13 | #else | ||
| 14 | #define ZIG_EXTERN_C | ||
| 15 | #endif | ||
| 16 | |||
| 17 | #include <stddef.h> | ||
| 18 | |||
| 19 | struct ZigWindowsSDK { | ||
| 20 | const char *path10_ptr; | ||
| 21 | size_t path10_len; | ||
| 22 | |||
| 23 | const char *version10_ptr; | ||
| 24 | size_t version10_len; | ||
| 25 | |||
| 26 | const char *path81_ptr; | ||
| 27 | size_t path81_len; | ||
| 28 | |||
| 29 | const char *version81_ptr; | ||
| 30 | size_t version81_len; | ||
| 31 | |||
| 32 | const char *msvc_lib_dir_ptr; | ||
| 33 | size_t msvc_lib_dir_len; | ||
| 34 | }; | ||
| 35 | |||
| 36 | enum ZigFindWindowsSdkError { | ||
| 37 | ZigFindWindowsSdkErrorNone, | ||
| 38 | ZigFindWindowsSdkErrorOutOfMemory, | ||
| 39 | ZigFindWindowsSdkErrorNotFound, | ||
| 40 | ZigFindWindowsSdkErrorPathTooLong, | ||
| 41 | }; | ||
| 42 | |||
| 43 | ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk); | ||
| 44 | |||
| 45 | ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk); | ||
| 46 | |||
| 47 | #endif | ||
src/zig_llvm.cpp+5| ... | @@ -455,6 +455,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso | ... | @@ -455,6 +455,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso |
| 455 | return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder); | 455 | return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder); |
| 456 | } | 456 | } |
| 457 | 457 | ||
| 458 | void ZigLLVMDisposeDIBuilder(ZigLLVMDIBuilder *dbuilder) { | ||
| 459 | DIBuilder *di_builder = reinterpret_cast<DIBuilder *>(dbuilder); | ||
| 460 | delete di_builder; | ||
| 461 | } | ||
| 462 | |||
| 458 | void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) { | 463 | void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) { |
| 459 | unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get( | 464 | unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get( |
| 460 | line, column, reinterpret_cast<DIScope*>(scope))); | 465 | line, column, reinterpret_cast<DIScope*>(scope))); |
src/zig_llvm.h+5-1| ... | @@ -22,6 +22,9 @@ | ... | @@ -22,6 +22,9 @@ |
| 22 | #define ZIG_EXTERN_C | 22 | #define ZIG_EXTERN_C |
| 23 | #endif | 23 | #endif |
| 24 | 24 | ||
| 25 | // ATTENTION: If you modify this file, be sure to update the corresponding | ||
| 26 | // extern function declarations in the self-hosted compiler. | ||
| 27 | |||
| 25 | struct ZigLLVMDIType; | 28 | struct ZigLLVMDIType; |
| 26 | struct ZigLLVMDIBuilder; | 29 | struct ZigLLVMDIBuilder; |
| 27 | struct ZigLLVMDICompileUnit; | 30 | struct ZigLLVMDICompileUnit; |
| ... | @@ -39,7 +42,7 @@ struct ZigLLVMInsertionPoint; | ... | @@ -39,7 +42,7 @@ struct ZigLLVMInsertionPoint; |
| 39 | ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R); | 42 | ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R); |
| 40 | ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); | 43 | ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); |
| 41 | 44 | ||
| 42 | /// Caller must free memory. | 45 | /// Caller must free memory with LLVMDisposeMessage |
| 43 | ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void); | 46 | ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void); |
| 44 | ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void); | 47 | ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void); |
| 45 | 48 | ||
| ... | @@ -145,6 +148,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void); | ... | @@ -145,6 +148,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void); |
| 145 | ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void); | 148 | ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void); |
| 146 | 149 | ||
| 147 | ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved); | 150 | ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved); |
| 151 | ZIG_EXTERN_C void ZigLLVMDisposeDIBuilder(struct ZigLLVMDIBuilder *dbuilder); | ||
| 148 | ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module); | 152 | ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module); |
| 149 | ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module); | 153 | ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module); |
| 150 | 154 |
std/array_list.zig+38-11| ... | @@ -113,13 +113,12 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { | ... | @@ -113,13 +113,12 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { |
| 113 | return old_item; | 113 | return old_item; |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | pub fn removeOrError(self: *Self, n: usize) !T { | 116 | /// Removes the element at the specified index and returns it |
| 117 | if (n >= self.len) return error.OutOfBounds; | 117 | /// or an error.OutOfBounds is returned. If no error then |
| 118 | if (self.len - 1 == n) return self.pop(); | 118 | /// the empty slot is filled from the end of the list. |
| 119 | 119 | pub fn swapRemoveOrError(self: *Self, i: usize) !T { | |
| 120 | var old_item = self.at(n); | 120 | if (i >= self.len) return error.OutOfBounds; |
| 121 | try self.setOrError(n, self.pop()); | 121 | return self.swapRemove(i); |
| 122 | return old_item; | ||
| 123 | } | 122 | } |
| 124 | 123 | ||
| 125 | pub fn appendSlice(self: *Self, items: []align(A) const T) !void { | 124 | pub fn appendSlice(self: *Self, items: []align(A) const T) !void { |
| ... | @@ -192,7 +191,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { | ... | @@ -192,7 +191,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { |
| 192 | }; | 191 | }; |
| 193 | } | 192 | } |
| 194 | 193 | ||
| 195 | test "basic ArrayList test" { | 194 | test "std.ArrayList.basic" { |
| 196 | var bytes: [1024]u8 = undefined; | 195 | var bytes: [1024]u8 = undefined; |
| 197 | const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; | 196 | const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; |
| 198 | 197 | ||
| ... | @@ -279,7 +278,35 @@ test "std.ArrayList.swapRemove" { | ... | @@ -279,7 +278,35 @@ test "std.ArrayList.swapRemove" { |
| 279 | assert(list.len == 4); | 278 | assert(list.len == 4); |
| 280 | } | 279 | } |
| 281 | 280 | ||
| 282 | test "iterator ArrayList test" { | 281 | test "std.ArrayList.swapRemoveOrError" { |
| 282 | var list = ArrayList(i32).init(debug.global_allocator); | ||
| 283 | defer list.deinit(); | ||
| 284 | |||
| 285 | // Test just after initialization | ||
| 286 | assertError(list.swapRemoveOrError(0), error.OutOfBounds); | ||
| 287 | |||
| 288 | // Test after adding one item and remote it | ||
| 289 | try list.append(1); | ||
| 290 | assert((try list.swapRemoveOrError(0)) == 1); | ||
| 291 | assertError(list.swapRemoveOrError(0), error.OutOfBounds); | ||
| 292 | |||
| 293 | // Test after adding two items and remote both | ||
| 294 | try list.append(1); | ||
| 295 | try list.append(2); | ||
| 296 | assert((try list.swapRemoveOrError(1)) == 2); | ||
| 297 | assert((try list.swapRemoveOrError(0)) == 1); | ||
| 298 | assertError(list.swapRemoveOrError(0), error.OutOfBounds); | ||
| 299 | |||
| 300 | // Test out of bounds with one item | ||
| 301 | try list.append(1); | ||
| 302 | assertError(list.swapRemoveOrError(1), error.OutOfBounds); | ||
| 303 | |||
| 304 | // Test out of bounds with two items | ||
| 305 | try list.append(2); | ||
| 306 | assertError(list.swapRemoveOrError(2), error.OutOfBounds); | ||
| 307 | } | ||
| 308 | |||
| 309 | test "std.ArrayList.iterator" { | ||
| 283 | var list = ArrayList(i32).init(debug.global_allocator); | 310 | var list = ArrayList(i32).init(debug.global_allocator); |
| 284 | defer list.deinit(); | 311 | defer list.deinit(); |
| 285 | 312 | ||
| ... | @@ -308,7 +335,7 @@ test "iterator ArrayList test" { | ... | @@ -308,7 +335,7 @@ test "iterator ArrayList test" { |
| 308 | assert(it.next().? == 1); | 335 | assert(it.next().? == 1); |
| 309 | } | 336 | } |
| 310 | 337 | ||
| 311 | test "insert ArrayList test" { | 338 | test "std.ArrayList.insert" { |
| 312 | var list = ArrayList(i32).init(debug.global_allocator); | 339 | var list = ArrayList(i32).init(debug.global_allocator); |
| 313 | defer list.deinit(); | 340 | defer list.deinit(); |
| 314 | 341 | ||
| ... | @@ -322,7 +349,7 @@ test "insert ArrayList test" { | ... | @@ -322,7 +349,7 @@ test "insert ArrayList test" { |
| 322 | assert(list.items[3] == 3); | 349 | assert(list.items[3] == 3); |
| 323 | } | 350 | } |
| 324 | 351 | ||
| 325 | test "insertSlice ArrayList test" { | 352 | test "std.ArrayList.insertSlice" { |
| 326 | var list = ArrayList(i32).init(debug.global_allocator); | 353 | var list = ArrayList(i32).init(debug.global_allocator); |
| 327 | defer list.deinit(); | 354 | defer list.deinit(); |
| 328 | 355 |
std/atomic/int.zig+4| ... | @@ -25,5 +25,9 @@ pub fn Int(comptime T: type) type { | ... | @@ -25,5 +25,9 @@ pub fn Int(comptime T: type) type { |
| 25 | pub fn get(self: *Self) T { | 25 | pub fn get(self: *Self) T { |
| 26 | return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst); | 26 | return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst); |
| 27 | } | 27 | } |
| 28 | |||
| 29 | pub fn xchg(self: *Self, new_value: T) T { | ||
| 30 | return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Xchg, new_value, AtomicOrder.SeqCst); | ||
| 31 | } | ||
| 28 | }; | 32 | }; |
| 29 | } | 33 | } |
std/atomic/queue.zig+14| ... | @@ -51,6 +51,20 @@ pub fn Queue(comptime T: type) type { | ... | @@ -51,6 +51,20 @@ pub fn Queue(comptime T: type) type { |
| 51 | return head; | 51 | return head; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | pub fn unget(self: *Self, node: *Node) void { | ||
| 55 | while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} | ||
| 56 | defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); | ||
| 57 | |||
| 58 | const opt_head = self.head; | ||
| 59 | self.head = node; | ||
| 60 | if (opt_head) |head| { | ||
| 61 | head.next = node; | ||
| 62 | } else { | ||
| 63 | assert(self.tail == null); | ||
| 64 | self.tail = node; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 54 | pub fn isEmpty(self: *Self) bool { | 68 | pub fn isEmpty(self: *Self) bool { |
| 55 | return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null; | 69 | return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null; |
| 56 | } | 70 | } |
std/buffer.zig+13| ... | @@ -54,6 +54,19 @@ pub const Buffer = struct { | ... | @@ -54,6 +54,19 @@ pub const Buffer = struct { |
| 54 | return result; | 54 | return result; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: ...) !Buffer { | ||
| 58 | const countSize = struct { | ||
| 59 | fn countSize(size: *usize, bytes: []const u8) (error{}!void) { | ||
| 60 | size.* += bytes.len; | ||
| 61 | } | ||
| 62 | }.countSize; | ||
| 63 | var size: usize = 0; | ||
| 64 | std.fmt.format(&size, error{}, countSize, format, args) catch |err| switch (err) {}; | ||
| 65 | var self = try Buffer.initSize(allocator, size); | ||
| 66 | assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size); | ||
| 67 | return self; | ||
| 68 | } | ||
| 69 | |||
| 57 | pub fn deinit(self: *Buffer) void { | 70 | pub fn deinit(self: *Buffer) void { |
| 58 | self.list.deinit(); | 71 | self.list.deinit(); |
| 59 | } | 72 | } |
std/c/darwin.zig+1-1| ... | @@ -30,7 +30,7 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen | ... | @@ -30,7 +30,7 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen |
| 30 | pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; | 30 | pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; |
| 31 | pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; | 31 | pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; |
| 32 | 32 | ||
| 33 | pub use @import("../os/darwin_errno.zig"); | 33 | pub use @import("../os/darwin/errno.zig"); |
| 34 | 34 | ||
| 35 | pub const _errno = __error; | 35 | pub const _errno = __error; |
| 36 | 36 |
std/dwarf.zig+37| ... | @@ -639,3 +639,40 @@ pub const LNE_define_file = 0x03; | ... | @@ -639,3 +639,40 @@ pub const LNE_define_file = 0x03; |
| 639 | pub const LNE_set_discriminator = 0x04; | 639 | pub const LNE_set_discriminator = 0x04; |
| 640 | pub const LNE_lo_user = 0x80; | 640 | pub const LNE_lo_user = 0x80; |
| 641 | pub const LNE_hi_user = 0xff; | 641 | pub const LNE_hi_user = 0xff; |
| 642 | |||
| 643 | pub const LANG_C89 = 0x0001; | ||
| 644 | pub const LANG_C = 0x0002; | ||
| 645 | pub const LANG_Ada83 = 0x0003; | ||
| 646 | pub const LANG_C_plus_plus = 0x0004; | ||
| 647 | pub const LANG_Cobol74 = 0x0005; | ||
| 648 | pub const LANG_Cobol85 = 0x0006; | ||
| 649 | pub const LANG_Fortran77 = 0x0007; | ||
| 650 | pub const LANG_Fortran90 = 0x0008; | ||
| 651 | pub const LANG_Pascal83 = 0x0009; | ||
| 652 | pub const LANG_Modula2 = 0x000a; | ||
| 653 | pub const LANG_Java = 0x000b; | ||
| 654 | pub const LANG_C99 = 0x000c; | ||
| 655 | pub const LANG_Ada95 = 0x000d; | ||
| 656 | pub const LANG_Fortran95 = 0x000e; | ||
| 657 | pub const LANG_PLI = 0x000f; | ||
| 658 | pub const LANG_ObjC = 0x0010; | ||
| 659 | pub const LANG_ObjC_plus_plus = 0x0011; | ||
| 660 | pub const LANG_UPC = 0x0012; | ||
| 661 | pub const LANG_D = 0x0013; | ||
| 662 | pub const LANG_Python = 0x0014; | ||
| 663 | pub const LANG_Go = 0x0016; | ||
| 664 | pub const LANG_C_plus_plus_11 = 0x001a; | ||
| 665 | pub const LANG_Rust = 0x001c; | ||
| 666 | pub const LANG_C11 = 0x001d; | ||
| 667 | pub const LANG_C_plus_plus_14 = 0x0021; | ||
| 668 | pub const LANG_Fortran03 = 0x0022; | ||
| 669 | pub const LANG_Fortran08 = 0x0023; | ||
| 670 | pub const LANG_lo_user = 0x8000; | ||
| 671 | pub const LANG_hi_user = 0xffff; | ||
| 672 | pub const LANG_Mips_Assembler = 0x8001; | ||
| 673 | pub const LANG_Upc = 0x8765; | ||
| 674 | pub const LANG_HP_Bliss = 0x8003; | ||
| 675 | pub const LANG_HP_Basic91 = 0x8004; | ||
| 676 | pub const LANG_HP_Pascal91 = 0x8005; | ||
| 677 | pub const LANG_HP_IMacro = 0x8006; | ||
| 678 | pub const LANG_HP_Assembler = 0x8007; |
std/event/future.zig+31-8| ... | @@ -6,15 +6,20 @@ const AtomicOrder = builtin.AtomicOrder; | ... | @@ -6,15 +6,20 @@ const AtomicOrder = builtin.AtomicOrder; |
| 6 | const Lock = std.event.Lock; | 6 | const Lock = std.event.Lock; |
| 7 | const Loop = std.event.Loop; | 7 | const Loop = std.event.Loop; |
| 8 | 8 | ||
| 9 | /// This is a value that starts out unavailable, until a value is put(). | 9 | /// This is a value that starts out unavailable, until resolve() is called |
| 10 | /// While it is unavailable, coroutines suspend when they try to get() it, | 10 | /// While it is unavailable, coroutines suspend when they try to get() it, |
| 11 | /// and then are resumed when the value is put(). | 11 | /// and then are resumed when resolve() is called. |
| 12 | /// At this point the value remains forever available, and another put() is not allowed. | 12 | /// At this point the value remains forever available, and another resolve() is not allowed. |
| 13 | pub fn Future(comptime T: type) type { | 13 | pub fn Future(comptime T: type) type { |
| 14 | return struct { | 14 | return struct { |
| 15 | lock: Lock, | 15 | lock: Lock, |
| 16 | data: T, | 16 | data: T, |
| 17 | available: u8, // TODO make this a bool | 17 | |
| 18 | /// TODO make this an enum | ||
| 19 | /// 0 - not started | ||
| 20 | /// 1 - started | ||
| 21 | /// 2 - finished | ||
| 22 | available: u8, | ||
| 18 | 23 | ||
| 19 | const Self = this; | 24 | const Self = this; |
| 20 | const Queue = std.atomic.Queue(promise); | 25 | const Queue = std.atomic.Queue(promise); |
| ... | @@ -31,7 +36,7 @@ pub fn Future(comptime T: type) type { | ... | @@ -31,7 +36,7 @@ pub fn Future(comptime T: type) type { |
| 31 | /// available. | 36 | /// available. |
| 32 | /// Thread-safe. | 37 | /// Thread-safe. |
| 33 | pub async fn get(self: *Self) *T { | 38 | pub async fn get(self: *Self) *T { |
| 34 | if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { | 39 | if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) { |
| 35 | return &self.data; | 40 | return &self.data; |
| 36 | } | 41 | } |
| 37 | const held = await (async self.lock.acquire() catch unreachable); | 42 | const held = await (async self.lock.acquire() catch unreachable); |
| ... | @@ -43,18 +48,36 @@ pub fn Future(comptime T: type) type { | ... | @@ -43,18 +48,36 @@ pub fn Future(comptime T: type) type { |
| 43 | /// Gets the data without waiting for it. If it's available, a pointer is | 48 | /// Gets the data without waiting for it. If it's available, a pointer is |
| 44 | /// returned. Otherwise, null is returned. | 49 | /// returned. Otherwise, null is returned. |
| 45 | pub fn getOrNull(self: *Self) ?*T { | 50 | pub fn getOrNull(self: *Self) ?*T { |
| 46 | if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { | 51 | if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) { |
| 47 | return &self.data; | 52 | return &self.data; |
| 48 | } else { | 53 | } else { |
| 49 | return null; | 54 | return null; |
| 50 | } | 55 | } |
| 51 | } | 56 | } |
| 52 | 57 | ||
| 58 | /// If someone else has started working on the data, wait for them to complete | ||
| 59 | /// and return a pointer to the data. Otherwise, return null, and the caller | ||
| 60 | /// should start working on the data. | ||
| 61 | /// It's not required to call start() before resolve() but it can be useful since | ||
| 62 | /// this method is thread-safe. | ||
| 63 | pub async fn start(self: *Self) ?*T { | ||
| 64 | const state = @cmpxchgStrong(u8, &self.available, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null; | ||
| 65 | switch (state) { | ||
| 66 | 1 => { | ||
| 67 | const held = await (async self.lock.acquire() catch unreachable); | ||
| 68 | held.release(); | ||
| 69 | return &self.data; | ||
| 70 | }, | ||
| 71 | 2 => return &self.data, | ||
| 72 | else => unreachable, | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 53 | /// Make the data become available. May be called only once. | 76 | /// Make the data become available. May be called only once. |
| 54 | /// Before calling this, modify the `data` property. | 77 | /// Before calling this, modify the `data` property. |
| 55 | pub fn resolve(self: *Self) void { | 78 | pub fn resolve(self: *Self) void { |
| 56 | const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | 79 | const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst); |
| 57 | assert(prev == 0); // put() called twice | 80 | assert(prev == 0 or prev == 1); // resolve() called twice |
| 58 | Lock.Held.release(Lock.Held{ .lock = &self.lock }); | 81 | Lock.Held.release(Lock.Held{ .lock = &self.lock }); |
| 59 | } | 82 | } |
| 60 | }; | 83 | }; |
std/event/group.zig+14-2| ... | @@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp; | ... | @@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp; |
| 6 | const AtomicOrder = builtin.AtomicOrder; | 6 | const AtomicOrder = builtin.AtomicOrder; |
| 7 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 8 | 8 | ||
| 9 | /// ReturnType should be `void` or `E!void` | 9 | /// ReturnType must be `void` or `E!void` |
| 10 | pub fn Group(comptime ReturnType: type) type { | 10 | pub fn Group(comptime ReturnType: type) type { |
| 11 | return struct { | 11 | return struct { |
| 12 | coro_stack: Stack, | 12 | coro_stack: Stack, |
| ... | @@ -38,8 +38,17 @@ pub fn Group(comptime ReturnType: type) type { | ... | @@ -38,8 +38,17 @@ pub fn Group(comptime ReturnType: type) type { |
| 38 | self.alloc_stack.push(node); | 38 | self.alloc_stack.push(node); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | /// Add a node to the group. Thread-safe. Cannot fail. | ||
| 42 | /// `node.data` should be the promise handle to add to the group. | ||
| 43 | /// The node's memory should be in the coroutine frame of | ||
| 44 | /// the handle that is in the node, or somewhere guaranteed to live | ||
| 45 | /// at least as long. | ||
| 46 | pub fn addNode(self: *Self, node: *Stack.Node) void { | ||
| 47 | self.coro_stack.push(node); | ||
| 48 | } | ||
| 49 | |||
| 41 | /// This is equivalent to an async call, but the async function is added to the group, instead | 50 | /// This is equivalent to an async call, but the async function is added to the group, instead |
| 42 | /// of returning a promise. func must be async and have return type void. | 51 | /// of returning a promise. func must be async and have return type ReturnType. |
| 43 | /// Thread-safe. | 52 | /// Thread-safe. |
| 44 | pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) { | 53 | pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) { |
| 45 | const S = struct { | 54 | const S = struct { |
| ... | @@ -67,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type { | ... | @@ -67,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type { |
| 67 | 76 | ||
| 68 | /// Wait for all the calls and promises of the group to complete. | 77 | /// Wait for all the calls and promises of the group to complete. |
| 69 | /// Thread-safe. | 78 | /// Thread-safe. |
| 79 | /// Safe to call any number of times. | ||
| 70 | pub async fn wait(self: *Self) ReturnType { | 80 | pub async fn wait(self: *Self) ReturnType { |
| 71 | // TODO catch unreachable because the allocation can be grouped with | 81 | // TODO catch unreachable because the allocation can be grouped with |
| 72 | // the coro frame allocation | 82 | // the coro frame allocation |
| ... | @@ -98,6 +108,8 @@ pub fn Group(comptime ReturnType: type) type { | ... | @@ -98,6 +108,8 @@ pub fn Group(comptime ReturnType: type) type { |
| 98 | } | 108 | } |
| 99 | 109 | ||
| 100 | /// Cancel all the outstanding promises. May only be called if wait was never called. | 110 | /// Cancel all the outstanding promises. May only be called if wait was never called. |
| 111 | /// TODO These should be `cancelasync` not `cancel`. | ||
| 112 | /// See https://github.com/ziglang/zig/issues/1261 | ||
| 101 | pub fn cancelAll(self: *Self) void { | 113 | pub fn cancelAll(self: *Self) void { |
| 102 | while (self.coro_stack.pop()) |node| { | 114 | while (self.coro_stack.pop()) |node| { |
| 103 | cancel node.data; | 115 | cancel node.data; |
std/event/loop.zig+100-109| ... | @@ -12,7 +12,6 @@ pub const Loop = struct { | ... | @@ -12,7 +12,6 @@ pub const Loop = struct { |
| 12 | next_tick_queue: std.atomic.Queue(promise), | 12 | next_tick_queue: std.atomic.Queue(promise), |
| 13 | os_data: OsData, | 13 | os_data: OsData, |
| 14 | final_resume_node: ResumeNode, | 14 | final_resume_node: ResumeNode, |
| 15 | dispatch_lock: u8, // TODO make this a bool | ||
| 16 | pending_event_count: usize, | 15 | pending_event_count: usize, |
| 17 | extra_threads: []*std.os.Thread, | 16 | extra_threads: []*std.os.Thread, |
| 18 | 17 | ||
| ... | @@ -74,11 +73,10 @@ pub const Loop = struct { | ... | @@ -74,11 +73,10 @@ pub const Loop = struct { |
| 74 | /// max(thread_count - 1, 0) | 73 | /// max(thread_count - 1, 0) |
| 75 | fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void { | 74 | fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void { |
| 76 | self.* = Loop{ | 75 | self.* = Loop{ |
| 77 | .pending_event_count = 0, | 76 | .pending_event_count = 1, |
| 78 | .allocator = allocator, | 77 | .allocator = allocator, |
| 79 | .os_data = undefined, | 78 | .os_data = undefined, |
| 80 | .next_tick_queue = std.atomic.Queue(promise).init(), | 79 | .next_tick_queue = std.atomic.Queue(promise).init(), |
| 81 | .dispatch_lock = 1, // start locked so threads go directly into epoll wait | ||
| 82 | .extra_threads = undefined, | 80 | .extra_threads = undefined, |
| 83 | .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), | 81 | .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), |
| 84 | .eventfd_resume_nodes = undefined, | 82 | .eventfd_resume_nodes = undefined, |
| ... | @@ -235,8 +233,6 @@ pub const Loop = struct { | ... | @@ -235,8 +233,6 @@ pub const Loop = struct { |
| 235 | } | 233 | } |
| 236 | }, | 234 | }, |
| 237 | builtin.Os.windows => { | 235 | builtin.Os.windows => { |
| 238 | self.os_data.extra_thread_count = extra_thread_count; | ||
| 239 | |||
| 240 | self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( | 236 | self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( |
| 241 | windows.INVALID_HANDLE_VALUE, | 237 | windows.INVALID_HANDLE_VALUE, |
| 242 | null, | 238 | null, |
| ... | @@ -306,7 +302,7 @@ pub const Loop = struct { | ... | @@ -306,7 +302,7 @@ pub const Loop = struct { |
| 306 | pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { | 302 | pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { |
| 307 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | 303 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 308 | errdefer { | 304 | errdefer { |
| 309 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | 305 | self.finishOneEvent(); |
| 310 | } | 306 | } |
| 311 | try self.modFd( | 307 | try self.modFd( |
| 312 | fd, | 308 | fd, |
| ... | @@ -326,7 +322,7 @@ pub const Loop = struct { | ... | @@ -326,7 +322,7 @@ pub const Loop = struct { |
| 326 | 322 | ||
| 327 | pub fn removeFd(self: *Loop, fd: i32) void { | 323 | pub fn removeFd(self: *Loop, fd: i32) void { |
| 328 | self.removeFdNoCounter(fd); | 324 | self.removeFdNoCounter(fd); |
| 329 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | 325 | self.finishOneEvent(); |
| 330 | } | 326 | } |
| 331 | 327 | ||
| 332 | fn removeFdNoCounter(self: *Loop, fd: i32) void { | 328 | fn removeFdNoCounter(self: *Loop, fd: i32) void { |
| ... | @@ -345,14 +341,70 @@ pub const Loop = struct { | ... | @@ -345,14 +341,70 @@ pub const Loop = struct { |
| 345 | } | 341 | } |
| 346 | } | 342 | } |
| 347 | 343 | ||
| 344 | fn dispatch(self: *Loop) void { | ||
| 345 | while (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| { | ||
| 346 | const next_tick_node = self.next_tick_queue.get() orelse { | ||
| 347 | self.available_eventfd_resume_nodes.push(resume_stack_node); | ||
| 348 | return; | ||
| 349 | }; | ||
| 350 | const eventfd_node = &resume_stack_node.data; | ||
| 351 | eventfd_node.base.handle = next_tick_node.data; | ||
| 352 | switch (builtin.os) { | ||
| 353 | builtin.Os.macosx => { | ||
| 354 | const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); | ||
| 355 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; | ||
| 356 | _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { | ||
| 357 | self.next_tick_queue.unget(next_tick_node); | ||
| 358 | self.available_eventfd_resume_nodes.push(resume_stack_node); | ||
| 359 | return; | ||
| 360 | }; | ||
| 361 | }, | ||
| 362 | builtin.Os.linux => { | ||
| 363 | // the pending count is already accounted for | ||
| 364 | const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | | ||
| 365 | std.os.linux.EPOLLET; | ||
| 366 | self.modFd( | ||
| 367 | eventfd_node.eventfd, | ||
| 368 | eventfd_node.epoll_op, | ||
| 369 | epoll_events, | ||
| 370 | &eventfd_node.base, | ||
| 371 | ) catch { | ||
| 372 | self.next_tick_queue.unget(next_tick_node); | ||
| 373 | self.available_eventfd_resume_nodes.push(resume_stack_node); | ||
| 374 | return; | ||
| 375 | }; | ||
| 376 | }, | ||
| 377 | builtin.Os.windows => { | ||
| 378 | // this value is never dereferenced but we need it to be non-null so that | ||
| 379 | // the consumer code can decide whether to read the completion key. | ||
| 380 | // it has to do this for normal I/O, so we match that behavior here. | ||
| 381 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); | ||
| 382 | std.os.windowsPostQueuedCompletionStatus( | ||
| 383 | self.os_data.io_port, | ||
| 384 | undefined, | ||
| 385 | eventfd_node.completion_key, | ||
| 386 | overlapped, | ||
| 387 | ) catch { | ||
| 388 | self.next_tick_queue.unget(next_tick_node); | ||
| 389 | self.available_eventfd_resume_nodes.push(resume_stack_node); | ||
| 390 | return; | ||
| 391 | }; | ||
| 392 | }, | ||
| 393 | else => @compileError("unsupported OS"), | ||
| 394 | } | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 348 | /// Bring your own linked list node. This means it can't fail. | 398 | /// Bring your own linked list node. This means it can't fail. |
| 349 | pub fn onNextTick(self: *Loop, node: *NextTickNode) void { | 399 | pub fn onNextTick(self: *Loop, node: *NextTickNode) void { |
| 350 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | 400 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 351 | self.next_tick_queue.put(node); | 401 | self.next_tick_queue.put(node); |
| 402 | self.dispatch(); | ||
| 352 | } | 403 | } |
| 353 | 404 | ||
| 354 | pub fn run(self: *Loop) void { | 405 | pub fn run(self: *Loop) void { |
| 355 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | 406 | self.finishOneEvent(); // the reference we start with |
| 407 | |||
| 356 | self.workerRun(); | 408 | self.workerRun(); |
| 357 | for (self.extra_threads) |extra_thread| { | 409 | for (self.extra_threads) |extra_thread| { |
| 358 | extra_thread.wait(); | 410 | extra_thread.wait(); |
| ... | @@ -392,110 +444,49 @@ pub const Loop = struct { | ... | @@ -392,110 +444,49 @@ pub const Loop = struct { |
| 392 | .next = undefined, | 444 | .next = undefined, |
| 393 | .data = p, | 445 | .data = p, |
| 394 | }; | 446 | }; |
| 395 | loop.onNextTick(&my_tick_node); | 447 | self.onNextTick(&my_tick_node); |
| 396 | } | 448 | } |
| 397 | } | 449 | } |
| 398 | 450 | ||
| 399 | fn workerRun(self: *Loop) void { | 451 | fn finishOneEvent(self: *Loop) void { |
| 400 | start_over: while (true) { | 452 | if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) { |
| 401 | if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { | 453 | // cause all the threads to stop |
| 402 | while (self.next_tick_queue.get()) |next_tick_node| { | 454 | switch (builtin.os) { |
| 403 | const handle = next_tick_node.data; | 455 | builtin.Os.linux => { |
| 404 | if (self.next_tick_queue.isEmpty()) { | 456 | // writing 8 bytes to an eventfd cannot fail |
| 405 | // last node, just resume it | 457 | std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; |
| 406 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | 458 | return; |
| 407 | resume handle; | 459 | }, |
| 408 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | 460 | builtin.Os.macosx => { |
| 409 | continue :start_over; | 461 | const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); |
| 410 | } | 462 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; |
| 411 | 463 | // cannot fail because we already added it and this just enables it | |
| 412 | // non-last node, stick it in the epoll/kqueue set so that | 464 | _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; |
| 413 | // other threads can get to it | 465 | return; |
| 414 | if (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| { | 466 | }, |
| 415 | const eventfd_node = &resume_stack_node.data; | 467 | builtin.Os.windows => { |
| 416 | eventfd_node.base.handle = handle; | 468 | var i: usize = 0; |
| 417 | switch (builtin.os) { | 469 | while (i < self.extra_threads.len + 1) : (i += 1) { |
| 418 | builtin.Os.macosx => { | 470 | while (true) { |
| 419 | const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); | 471 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); |
| 420 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; | 472 | std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; |
| 421 | _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { | 473 | break; |
| 422 | // fine, we didn't need it anyway | ||
| 423 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | ||
| 424 | self.available_eventfd_resume_nodes.push(resume_stack_node); | ||
| 425 | resume handle; | ||
| 426 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | ||
| 427 | continue :start_over; | ||
| 428 | }; | ||
| 429 | }, | ||
| 430 | builtin.Os.linux => { | ||
| 431 | // the pending count is already accounted for | ||
| 432 | const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET; | ||
| 433 | self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch { | ||
| 434 | // fine, we didn't need it anyway | ||
| 435 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | ||
| 436 | self.available_eventfd_resume_nodes.push(resume_stack_node); | ||
| 437 | resume handle; | ||
| 438 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | ||
| 439 | continue :start_over; | ||
| 440 | }; | ||
| 441 | }, | ||
| 442 | builtin.Os.windows => { | ||
| 443 | // this value is never dereferenced but we need it to be non-null so that | ||
| 444 | // the consumer code can decide whether to read the completion key. | ||
| 445 | // it has to do this for normal I/O, so we match that behavior here. | ||
| 446 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); | ||
| 447 | std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, eventfd_node.completion_key, overlapped) catch { | ||
| 448 | // fine, we didn't need it anyway | ||
| 449 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | ||
| 450 | self.available_eventfd_resume_nodes.push(resume_stack_node); | ||
| 451 | resume handle; | ||
| 452 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | ||
| 453 | continue :start_over; | ||
| 454 | }; | ||
| 455 | }, | ||
| 456 | else => @compileError("unsupported OS"), | ||
| 457 | } | 474 | } |
| 458 | } else { | ||
| 459 | // threads are too busy, can't add another eventfd to wake one up | ||
| 460 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | ||
| 461 | resume handle; | ||
| 462 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | ||
| 463 | continue :start_over; | ||
| 464 | } | 475 | } |
| 465 | } | 476 | return; |
| 466 | 477 | }, | |
| 467 | const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst); | 478 | else => @compileError("unsupported OS"), |
| 468 | if (pending_event_count == 0) { | 479 | } |
| 469 | // cause all the threads to stop | 480 | } |
| 470 | switch (builtin.os) { | 481 | } |
| 471 | builtin.Os.linux => { | ||
| 472 | // writing 8 bytes to an eventfd cannot fail | ||
| 473 | std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; | ||
| 474 | return; | ||
| 475 | }, | ||
| 476 | builtin.Os.macosx => { | ||
| 477 | const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); | ||
| 478 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; | ||
| 479 | // cannot fail because we already added it and this just enables it | ||
| 480 | _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; | ||
| 481 | return; | ||
| 482 | }, | ||
| 483 | builtin.Os.windows => { | ||
| 484 | var i: usize = 0; | ||
| 485 | while (i < self.os_data.extra_thread_count) : (i += 1) { | ||
| 486 | while (true) { | ||
| 487 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); | ||
| 488 | std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; | ||
| 489 | break; | ||
| 490 | } | ||
| 491 | } | ||
| 492 | return; | ||
| 493 | }, | ||
| 494 | else => @compileError("unsupported OS"), | ||
| 495 | } | ||
| 496 | } | ||
| 497 | 482 | ||
| 498 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | 483 | fn workerRun(self: *Loop) void { |
| 484 | while (true) { | ||
| 485 | while (true) { | ||
| 486 | const next_tick_node = self.next_tick_queue.get() orelse break; | ||
| 487 | self.dispatch(); | ||
| 488 | resume next_tick_node.data; | ||
| 489 | self.finishOneEvent(); | ||
| 499 | } | 490 | } |
| 500 | 491 | ||
| 501 | switch (builtin.os) { | 492 | switch (builtin.os) { |
| ... | @@ -519,7 +510,7 @@ pub const Loop = struct { | ... | @@ -519,7 +510,7 @@ pub const Loop = struct { |
| 519 | } | 510 | } |
| 520 | resume handle; | 511 | resume handle; |
| 521 | if (resume_node_id == ResumeNode.Id.EventFd) { | 512 | if (resume_node_id == ResumeNode.Id.EventFd) { |
| 522 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | 513 | self.finishOneEvent(); |
| 523 | } | 514 | } |
| 524 | } | 515 | } |
| 525 | }, | 516 | }, |
| ... | @@ -541,7 +532,7 @@ pub const Loop = struct { | ... | @@ -541,7 +532,7 @@ pub const Loop = struct { |
| 541 | } | 532 | } |
| 542 | resume handle; | 533 | resume handle; |
| 543 | if (resume_node_id == ResumeNode.Id.EventFd) { | 534 | if (resume_node_id == ResumeNode.Id.EventFd) { |
| 544 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | 535 | self.finishOneEvent(); |
| 545 | } | 536 | } |
| 546 | } | 537 | } |
| 547 | }, | 538 | }, |
| ... | @@ -570,7 +561,7 @@ pub const Loop = struct { | ... | @@ -570,7 +561,7 @@ pub const Loop = struct { |
| 570 | } | 561 | } |
| 571 | resume handle; | 562 | resume handle; |
| 572 | if (resume_node_id == ResumeNode.Id.EventFd) { | 563 | if (resume_node_id == ResumeNode.Id.EventFd) { |
| 573 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); | 564 | self.finishOneEvent(); |
| 574 | } | 565 | } |
| 575 | }, | 566 | }, |
| 576 | else => @compileError("unsupported OS"), | 567 | else => @compileError("unsupported OS"), |
std/event/tcp.zig+2-1| ... | @@ -125,8 +125,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File | ... | @@ -125,8 +125,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File |
| 125 | test "listen on a port, send bytes, receive bytes" { | 125 | test "listen on a port, send bytes, receive bytes" { |
| 126 | if (builtin.os != builtin.Os.linux) { | 126 | if (builtin.os != builtin.Os.linux) { |
| 127 | // TODO build abstractions for other operating systems | 127 | // TODO build abstractions for other operating systems |
| 128 | return; | 128 | return error.SkipZigTest; |
| 129 | } | 129 | } |
| 130 | |||
| 130 | const MyServer = struct { | 131 | const MyServer = struct { |
| 131 | tcp_server: Server, | 132 | tcp_server: Server, |
| 132 | 133 |
std/fmt/index.zig+6-2| ... | @@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { | ... | @@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { |
| 785 | return buf[0 .. buf.len - context.remaining.len]; | 785 | return buf[0 .. buf.len - context.remaining.len]; |
| 786 | } | 786 | } |
| 787 | 787 | ||
| 788 | pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { | 788 | pub const AllocPrintError = error{OutOfMemory}; |
| 789 | |||
| 790 | pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 { | ||
| 789 | var size: usize = 0; | 791 | var size: usize = 0; |
| 790 | format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; | 792 | format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; |
| 791 | const buf = try allocator.alloc(u8, size); | 793 | const buf = try allocator.alloc(u8, size); |
| 792 | return bufPrint(buf, fmt, args); | 794 | return bufPrint(buf, fmt, args) catch |err| switch (err) { |
| 795 | error.BufferTooSmall => unreachable, // we just counted the size above | ||
| 796 | }; | ||
| 793 | } | 797 | } |
| 794 | 798 | ||
| 795 | fn countSize(size: *usize, bytes: []const u8) (error{}!void) { | 799 | fn countSize(size: *usize, bytes: []const u8) (error{}!void) { |
std/index.zig+3| ... | @@ -36,6 +36,8 @@ pub const sort = @import("sort.zig"); | ... | @@ -36,6 +36,8 @@ pub const sort = @import("sort.zig"); |
| 36 | pub const unicode = @import("unicode.zig"); | 36 | pub const unicode = @import("unicode.zig"); |
| 37 | pub const zig = @import("zig/index.zig"); | 37 | pub const zig = @import("zig/index.zig"); |
| 38 | 38 | ||
| 39 | pub const lazyInit = @import("lazy_init.zig").lazyInit; | ||
| 40 | |||
| 39 | test "std" { | 41 | test "std" { |
| 40 | // run tests from these | 42 | // run tests from these |
| 41 | _ = @import("atomic/index.zig"); | 43 | _ = @import("atomic/index.zig"); |
| ... | @@ -71,4 +73,5 @@ test "std" { | ... | @@ -71,4 +73,5 @@ test "std" { |
| 71 | _ = @import("sort.zig"); | 73 | _ = @import("sort.zig"); |
| 72 | _ = @import("unicode.zig"); | 74 | _ = @import("unicode.zig"); |
| 73 | _ = @import("zig/index.zig"); | 75 | _ = @import("zig/index.zig"); |
| 76 | _ = @import("lazy_init.zig"); | ||
| 74 | } | 77 | } |
std/lazy_init.zig created+85| ... | @@ -0,0 +1,85 @@ | ||
| 1 | const std = @import("index.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const AtomicRmwOp = builtin.AtomicRmwOp; | ||
| 5 | const AtomicOrder = builtin.AtomicOrder; | ||
| 6 | |||
| 7 | /// Thread-safe initialization of global data. | ||
| 8 | /// TODO use a mutex instead of a spinlock | ||
| 9 | pub fn lazyInit(comptime T: type) LazyInit(T) { | ||
| 10 | return LazyInit(T){ | ||
| 11 | .data = undefined, | ||
| 12 | .state = 0, | ||
| 13 | }; | ||
| 14 | } | ||
| 15 | |||
| 16 | fn LazyInit(comptime T: type) type { | ||
| 17 | return struct { | ||
| 18 | state: u8, // TODO make this an enum | ||
| 19 | data: Data, | ||
| 20 | |||
| 21 | const Self = this; | ||
| 22 | |||
| 23 | // TODO this isn't working for void, investigate and then remove this special case | ||
| 24 | const Data = if (@sizeOf(T) == 0) u8 else T; | ||
| 25 | const Ptr = if (T == void) void else *T; | ||
| 26 | |||
| 27 | /// Returns a usable pointer to the initialized data, | ||
| 28 | /// or returns null, indicating that the caller should | ||
| 29 | /// perform the initialization and then call resolve(). | ||
| 30 | pub fn get(self: *Self) ?Ptr { | ||
| 31 | while (true) { | ||
| 32 | var state = @cmpxchgWeak(u8, &self.state, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null; | ||
| 33 | switch (state) { | ||
| 34 | 0 => continue, | ||
| 35 | 1 => { | ||
| 36 | // TODO mutex instead of a spinlock | ||
| 37 | continue; | ||
| 38 | }, | ||
| 39 | 2 => { | ||
| 40 | if (@sizeOf(T) == 0) { | ||
| 41 | return T(undefined); | ||
| 42 | } else { | ||
| 43 | return &self.data; | ||
| 44 | } | ||
| 45 | }, | ||
| 46 | else => unreachable, | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn resolve(self: *Self) void { | ||
| 52 | const prev = @atomicRmw(u8, &self.state, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst); | ||
| 53 | assert(prev == 1); // resolve() called twice | ||
| 54 | } | ||
| 55 | }; | ||
| 56 | } | ||
| 57 | |||
| 58 | var global_number = lazyInit(i32); | ||
| 59 | |||
| 60 | test "std.lazyInit" { | ||
| 61 | if (global_number.get()) |_| @panic("bad") else { | ||
| 62 | global_number.data = 1234; | ||
| 63 | global_number.resolve(); | ||
| 64 | } | ||
| 65 | if (global_number.get()) |x| { | ||
| 66 | assert(x.* == 1234); | ||
| 67 | } else { | ||
| 68 | @panic("bad"); | ||
| 69 | } | ||
| 70 | if (global_number.get()) |x| { | ||
| 71 | assert(x.* == 1234); | ||
| 72 | } else { | ||
| 73 | @panic("bad"); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | var global_void = lazyInit(void); | ||
| 78 | |||
| 79 | test "std.lazyInit(void)" { | ||
| 80 | if (global_void.get()) |_| @panic("bad") else { | ||
| 81 | global_void.resolve(); | ||
| 82 | } | ||
| 83 | assert(global_void.get() != null); | ||
| 84 | assert(global_void.get() != null); | ||
| 85 | } | ||
std/macho.zig+1-1| ... | @@ -141,7 +141,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable | ... | @@ -141,7 +141,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | // Effectively a no-op, lld emits symbols in ascending order. | 143 | // Effectively a no-op, lld emits symbols in ascending order. |
| 144 | std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan); | 144 | std.sort.sort(Symbol, symbols[0..nsyms], Symbol.addressLessThan); |
| 145 | 145 | ||
| 146 | // Insert the sentinel. Since we don't know where the last function ends, | 146 | // Insert the sentinel. Since we don't know where the last function ends, |
| 147 | // we arbitrarily limit it to the start address + 4 KB. | 147 | // we arbitrarily limit it to the start address + 4 KB. |
std/math/big/int.zig+152-13| ... | @@ -60,8 +60,9 @@ pub const Int = struct { | ... | @@ -60,8 +60,9 @@ pub const Int = struct { |
| 60 | self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity); | 60 | self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | pub fn deinit(self: Int) void { | 63 | pub fn deinit(self: *Int) void { |
| 64 | self.allocator.free(self.limbs); | 64 | self.allocator.free(self.limbs); |
| 65 | self.* = undefined; | ||
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | pub fn clone(other: Int) !Int { | 68 | pub fn clone(other: Int) !Int { |
| ... | @@ -115,13 +116,63 @@ pub const Int = struct { | ... | @@ -115,13 +116,63 @@ pub const Int = struct { |
| 115 | return !r.isOdd(); | 116 | return !r.isOdd(); |
| 116 | } | 117 | } |
| 117 | 118 | ||
| 118 | fn bitcount(self: Int) usize { | 119 | // Returns the number of bits required to represent the absolute value of self. |
| 119 | const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); | 120 | fn bitCountAbs(self: Int) usize { |
| 120 | return usize(@boolToInt(!self.positive)) + u_bit_count; | 121 | return (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); |
| 121 | } | 122 | } |
| 122 | 123 | ||
| 124 | // Returns the number of bits required to represent the integer in twos-complement form. | ||
| 125 | // | ||
| 126 | // If the integer is negative the value returned is the number of bits needed by a signed | ||
| 127 | // integer to represent the value. If positive the value is the number of bits for an | ||
| 128 | // unsigned integer. Any unsigned integer will fit in the signed integer with bitcount | ||
| 129 | // one greater than the returned value. | ||
| 130 | // | ||
| 131 | // e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7. | ||
| 132 | fn bitCountTwosComp(self: Int) usize { | ||
| 133 | var bits = self.bitCountAbs(); | ||
| 134 | |||
| 135 | // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos | ||
| 136 | // complement requires one less bit. | ||
| 137 | if (!self.positive) block: { | ||
| 138 | bits += 1; | ||
| 139 | |||
| 140 | if (@popCount(self.limbs[self.len - 1]) == 1) { | ||
| 141 | for (self.limbs[0 .. self.len - 1]) |limb| { | ||
| 142 | if (@popCount(limb) != 0) { | ||
| 143 | break :block; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | bits -= 1; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | return bits; | ||
| 152 | } | ||
| 153 | |||
| 154 | pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool { | ||
| 155 | if (self.eqZero()) { | ||
| 156 | return true; | ||
| 157 | } | ||
| 158 | if (!is_signed and !self.positive) { | ||
| 159 | return false; | ||
| 160 | } | ||
| 161 | |||
| 162 | const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed); | ||
| 163 | return bit_count >= req_bits; | ||
| 164 | } | ||
| 165 | |||
| 166 | pub fn fits(self: Int, comptime T: type) bool { | ||
| 167 | return self.fitsInTwosComp(T.is_signed, T.bit_count); | ||
| 168 | } | ||
| 169 | |||
| 170 | // Returns the approximate size of the integer in the given base. Negative values accomodate for | ||
| 171 | // the minus sign. This is used for determining the number of characters needed to print the | ||
| 172 | // value. It is inexact and will exceed the given value by 1-2 digits. | ||
| 123 | pub fn sizeInBase(self: Int, base: usize) usize { | 173 | pub fn sizeInBase(self: Int, base: usize) usize { |
| 124 | return (self.bitcount() / math.log2(base)) + 1; | 174 | const bit_count = usize(@boolToInt(!self.positive)) + self.bitCountAbs(); |
| 175 | return (bit_count / math.log2(base)) + 1; | ||
| 125 | } | 176 | } |
| 126 | 177 | ||
| 127 | pub fn set(self: *Int, value: var) Allocator.Error!void { | 178 | pub fn set(self: *Int, value: var) Allocator.Error!void { |
| ... | @@ -189,9 +240,9 @@ pub const Int = struct { | ... | @@ -189,9 +240,9 @@ pub const Int = struct { |
| 189 | pub fn to(self: Int, comptime T: type) ConvertError!T { | 240 | pub fn to(self: Int, comptime T: type) ConvertError!T { |
| 190 | switch (@typeId(T)) { | 241 | switch (@typeId(T)) { |
| 191 | TypeId.Int => { | 242 | TypeId.Int => { |
| 192 | const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T; | 243 | const UT = @IntType(false, T.bit_count); |
| 193 | 244 | ||
| 194 | if (self.bitcount() > 8 * @sizeOf(UT)) { | 245 | if (self.bitCountTwosComp() > T.bit_count) { |
| 195 | return error.TargetTooSmall; | 246 | return error.TargetTooSmall; |
| 196 | } | 247 | } |
| 197 | 248 | ||
| ... | @@ -208,9 +259,17 @@ pub const Int = struct { | ... | @@ -208,9 +259,17 @@ pub const Int = struct { |
| 208 | } | 259 | } |
| 209 | 260 | ||
| 210 | if (!T.is_signed) { | 261 | if (!T.is_signed) { |
| 211 | return if (self.positive) r else error.NegativeIntoUnsigned; | 262 | return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned; |
| 212 | } else { | 263 | } else { |
| 213 | return if (self.positive) @intCast(T, r) else -@intCast(T, r); | 264 | if (self.positive) { |
| 265 | return @intCast(T, r); | ||
| 266 | } else { | ||
| 267 | if (math.cast(T, r)) |ok| { | ||
| 268 | return -ok; | ||
| 269 | } else |_| { | ||
| 270 | return @minValue(T); | ||
| 271 | } | ||
| 272 | } | ||
| 214 | } | 273 | } |
| 215 | }, | 274 | }, |
| 216 | else => { | 275 | else => { |
| ... | @@ -274,6 +333,7 @@ pub const Int = struct { | ... | @@ -274,6 +333,7 @@ pub const Int = struct { |
| 274 | self.positive = positive; | 333 | self.positive = positive; |
| 275 | } | 334 | } |
| 276 | 335 | ||
| 336 | /// TODO make this call format instead of the other way around | ||
| 277 | pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 { | 337 | pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 { |
| 278 | if (base < 2 or base > 16) { | 338 | if (base < 2 or base > 16) { |
| 279 | return error.InvalidBase; | 339 | return error.InvalidBase; |
| ... | @@ -356,6 +416,21 @@ pub const Int = struct { | ... | @@ -356,6 +416,21 @@ pub const Int = struct { |
| 356 | return s; | 416 | return s; |
| 357 | } | 417 | } |
| 358 | 418 | ||
| 419 | /// for the std lib format function | ||
| 420 | /// TODO make this non-allocating | ||
| 421 | pub fn format( | ||
| 422 | self: Int, | ||
| 423 | comptime fmt: []const u8, | ||
| 424 | context: var, | ||
| 425 | comptime FmtError: type, | ||
| 426 | output: fn (@typeOf(context), []const u8) FmtError!void, | ||
| 427 | ) FmtError!void { | ||
| 428 | // TODO look at fmt and support other bases | ||
| 429 | const str = self.toString(self.allocator, 10) catch @panic("TODO make this non allocating"); | ||
| 430 | defer self.allocator.free(str); | ||
| 431 | return output(context, str); | ||
| 432 | } | ||
| 433 | |||
| 359 | // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. | 434 | // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. |
| 360 | pub fn cmpAbs(a: Int, b: Int) i8 { | 435 | pub fn cmpAbs(a: Int, b: Int) i8 { |
| 361 | if (a.len < b.len) { | 436 | if (a.len < b.len) { |
| ... | @@ -1120,24 +1195,88 @@ test "big.int bitcount + sizeInBase" { | ... | @@ -1120,24 +1195,88 @@ test "big.int bitcount + sizeInBase" { |
| 1120 | var a = try Int.init(al); | 1195 | var a = try Int.init(al); |
| 1121 | 1196 | ||
| 1122 | try a.set(0b100); | 1197 | try a.set(0b100); |
| 1123 | debug.assert(a.bitcount() == 3); | 1198 | debug.assert(a.bitCountAbs() == 3); |
| 1124 | debug.assert(a.sizeInBase(2) >= 3); | 1199 | debug.assert(a.sizeInBase(2) >= 3); |
| 1125 | debug.assert(a.sizeInBase(10) >= 1); | 1200 | debug.assert(a.sizeInBase(10) >= 1); |
| 1126 | 1201 | ||
| 1202 | a.negate(); | ||
| 1203 | debug.assert(a.bitCountAbs() == 3); | ||
| 1204 | debug.assert(a.sizeInBase(2) >= 4); | ||
| 1205 | debug.assert(a.sizeInBase(10) >= 2); | ||
| 1206 | |||
| 1127 | try a.set(0xffffffff); | 1207 | try a.set(0xffffffff); |
| 1128 | debug.assert(a.bitcount() == 32); | 1208 | debug.assert(a.bitCountAbs() == 32); |
| 1129 | debug.assert(a.sizeInBase(2) >= 32); | 1209 | debug.assert(a.sizeInBase(2) >= 32); |
| 1130 | debug.assert(a.sizeInBase(10) >= 10); | 1210 | debug.assert(a.sizeInBase(10) >= 10); |
| 1131 | 1211 | ||
| 1132 | try a.shiftLeft(a, 5000); | 1212 | try a.shiftLeft(a, 5000); |
| 1133 | debug.assert(a.bitcount() == 5032); | 1213 | debug.assert(a.bitCountAbs() == 5032); |
| 1134 | debug.assert(a.sizeInBase(2) >= 5032); | 1214 | debug.assert(a.sizeInBase(2) >= 5032); |
| 1135 | a.positive = false; | 1215 | a.positive = false; |
| 1136 | 1216 | ||
| 1137 | debug.assert(a.bitcount() == 5033); | 1217 | debug.assert(a.bitCountAbs() == 5032); |
| 1138 | debug.assert(a.sizeInBase(2) >= 5033); | 1218 | debug.assert(a.sizeInBase(2) >= 5033); |
| 1139 | } | 1219 | } |
| 1140 | 1220 | ||
| 1221 | test "big.int bitcount/to" { | ||
| 1222 | var a = try Int.init(al); | ||
| 1223 | |||
| 1224 | try a.set(0); | ||
| 1225 | debug.assert(a.bitCountTwosComp() == 0); | ||
| 1226 | |||
| 1227 | // TODO: stack smashing | ||
| 1228 | // debug.assert((try a.to(u0)) == 0); | ||
| 1229 | // TODO: sigsegv | ||
| 1230 | // debug.assert((try a.to(i0)) == 0); | ||
| 1231 | |||
| 1232 | try a.set(-1); | ||
| 1233 | debug.assert(a.bitCountTwosComp() == 1); | ||
| 1234 | debug.assert((try a.to(i1)) == -1); | ||
| 1235 | |||
| 1236 | try a.set(-8); | ||
| 1237 | debug.assert(a.bitCountTwosComp() == 4); | ||
| 1238 | debug.assert((try a.to(i4)) == -8); | ||
| 1239 | |||
| 1240 | try a.set(127); | ||
| 1241 | debug.assert(a.bitCountTwosComp() == 7); | ||
| 1242 | debug.assert((try a.to(u7)) == 127); | ||
| 1243 | |||
| 1244 | try a.set(-128); | ||
| 1245 | debug.assert(a.bitCountTwosComp() == 8); | ||
| 1246 | debug.assert((try a.to(i8)) == -128); | ||
| 1247 | |||
| 1248 | try a.set(-129); | ||
| 1249 | debug.assert(a.bitCountTwosComp() == 9); | ||
| 1250 | debug.assert((try a.to(i9)) == -129); | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | test "big.int fits" { | ||
| 1254 | var a = try Int.init(al); | ||
| 1255 | |||
| 1256 | try a.set(0); | ||
| 1257 | debug.assert(a.fits(u0)); | ||
| 1258 | debug.assert(a.fits(i0)); | ||
| 1259 | |||
| 1260 | try a.set(255); | ||
| 1261 | debug.assert(!a.fits(u0)); | ||
| 1262 | debug.assert(!a.fits(u1)); | ||
| 1263 | debug.assert(!a.fits(i8)); | ||
| 1264 | debug.assert(a.fits(u8)); | ||
| 1265 | debug.assert(a.fits(u9)); | ||
| 1266 | debug.assert(a.fits(i9)); | ||
| 1267 | |||
| 1268 | try a.set(-128); | ||
| 1269 | debug.assert(!a.fits(i7)); | ||
| 1270 | debug.assert(a.fits(i8)); | ||
| 1271 | debug.assert(a.fits(i9)); | ||
| 1272 | debug.assert(!a.fits(u9)); | ||
| 1273 | |||
| 1274 | try a.set(0x1ffffffffeeeeeeee); | ||
| 1275 | debug.assert(!a.fits(u32)); | ||
| 1276 | debug.assert(!a.fits(u64)); | ||
| 1277 | debug.assert(a.fits(u65)); | ||
| 1278 | } | ||
| 1279 | |||
| 1141 | test "big.int string set" { | 1280 | test "big.int string set" { |
| 1142 | var a = try Int.init(al); | 1281 | var a = try Int.init(al); |
| 1143 | try a.setString(10, "120317241209124781241290847124"); | 1282 | try a.setString(10, "120317241209124781241290847124"); |
std/mem.zig+10-2| ... | @@ -35,6 +35,7 @@ pub const Allocator = struct { | ... | @@ -35,6 +35,7 @@ pub const Allocator = struct { |
| 35 | freeFn: fn (self: *Allocator, old_mem: []u8) void, | 35 | freeFn: fn (self: *Allocator, old_mem: []u8) void, |
| 36 | 36 | ||
| 37 | /// Call `destroy` with the result | 37 | /// Call `destroy` with the result |
| 38 | /// TODO this is deprecated. use createOne instead | ||
| 38 | pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) { | 39 | pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) { |
| 39 | const T = @typeOf(init); | 40 | const T = @typeOf(init); |
| 40 | if (@sizeOf(T) == 0) return &(T{}); | 41 | if (@sizeOf(T) == 0) return &(T{}); |
| ... | @@ -44,6 +45,14 @@ pub const Allocator = struct { | ... | @@ -44,6 +45,14 @@ pub const Allocator = struct { |
| 44 | return ptr; | 45 | return ptr; |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 48 | /// Call `destroy` with the result. | ||
| 49 | /// Returns undefined memory. | ||
| 50 | pub fn createOne(self: *Allocator, comptime T: type) Error!*T { | ||
| 51 | if (@sizeOf(T) == 0) return &(T{}); | ||
| 52 | const slice = try self.alloc(T, 1); | ||
| 53 | return &slice[0]; | ||
| 54 | } | ||
| 55 | |||
| 47 | /// `ptr` should be the return value of `create` | 56 | /// `ptr` should be the return value of `create` |
| 48 | pub fn destroy(self: *Allocator, ptr: var) void { | 57 | pub fn destroy(self: *Allocator, ptr: var) void { |
| 49 | const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); | 58 | const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); |
| ... | @@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { | ... | @@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { |
| 149 | @setRuntimeSafety(false); | 158 | @setRuntimeSafety(false); |
| 150 | assert(dest.len >= source.len); | 159 | assert(dest.len >= source.len); |
| 151 | var i = source.len; | 160 | var i = source.len; |
| 152 | while(i > 0){ | 161 | while (i > 0) { |
| 153 | i -= 1; | 162 | i -= 1; |
| 154 | dest[i] = source[i]; | 163 | dest[i] = source[i]; |
| 155 | } | 164 | } |
| 156 | } | 165 | } |
| 157 | 166 | ||
| 158 | |||
| 159 | pub fn set(comptime T: type, dest: []T, value: T) void { | 167 | pub fn set(comptime T: type, dest: []T, value: T) void { |
| 160 | for (dest) |*d| | 168 | for (dest) |*d| |
| 161 | d.* = value; | 169 | d.* = value; |
std/os/darwin.zig+87-1| ... | @@ -2,7 +2,7 @@ const std = @import("../index.zig"); | ... | @@ -2,7 +2,7 @@ const std = @import("../index.zig"); |
| 2 | const c = std.c; | 2 | const c = std.c; |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | 4 | ||
| 5 | pub use @import("darwin_errno.zig"); | 5 | pub use @import("darwin/errno.zig"); |
| 6 | 6 | ||
| 7 | pub const PATH_MAX = 1024; | 7 | pub const PATH_MAX = 1024; |
| 8 | 8 | ||
| ... | @@ -482,6 +482,92 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080; | ... | @@ -482,6 +482,92 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080; |
| 482 | /// data is mach absolute time units | 482 | /// data is mach absolute time units |
| 483 | pub const NOTE_MACHTIME = 0x00000100; | 483 | pub const NOTE_MACHTIME = 0x00000100; |
| 484 | 484 | ||
| 485 | pub const AF_UNSPEC: c_int = 0; | ||
| 486 | pub const AF_LOCAL: c_int = 1; | ||
| 487 | pub const AF_UNIX: c_int = AF_LOCAL; | ||
| 488 | pub const AF_INET: c_int = 2; | ||
| 489 | pub const AF_SYS_CONTROL: c_int = 2; | ||
| 490 | pub const AF_IMPLINK: c_int = 3; | ||
| 491 | pub const AF_PUP: c_int = 4; | ||
| 492 | pub const AF_CHAOS: c_int = 5; | ||
| 493 | pub const AF_NS: c_int = 6; | ||
| 494 | pub const AF_ISO: c_int = 7; | ||
| 495 | pub const AF_OSI: c_int = AF_ISO; | ||
| 496 | pub const AF_ECMA: c_int = 8; | ||
| 497 | pub const AF_DATAKIT: c_int = 9; | ||
| 498 | pub const AF_CCITT: c_int = 10; | ||
| 499 | pub const AF_SNA: c_int = 11; | ||
| 500 | pub const AF_DECnet: c_int = 12; | ||
| 501 | pub const AF_DLI: c_int = 13; | ||
| 502 | pub const AF_LAT: c_int = 14; | ||
| 503 | pub const AF_HYLINK: c_int = 15; | ||
| 504 | pub const AF_APPLETALK: c_int = 16; | ||
| 505 | pub const AF_ROUTE: c_int = 17; | ||
| 506 | pub const AF_LINK: c_int = 18; | ||
| 507 | pub const AF_XTP: c_int = 19; | ||
| 508 | pub const AF_COIP: c_int = 20; | ||
| 509 | pub const AF_CNT: c_int = 21; | ||
| 510 | pub const AF_RTIP: c_int = 22; | ||
| 511 | pub const AF_IPX: c_int = 23; | ||
| 512 | pub const AF_SIP: c_int = 24; | ||
| 513 | pub const AF_PIP: c_int = 25; | ||
| 514 | pub const AF_ISDN: c_int = 28; | ||
| 515 | pub const AF_E164: c_int = AF_ISDN; | ||
| 516 | pub const AF_KEY: c_int = 29; | ||
| 517 | pub const AF_INET6: c_int = 30; | ||
| 518 | pub const AF_NATM: c_int = 31; | ||
| 519 | pub const AF_SYSTEM: c_int = 32; | ||
| 520 | pub const AF_NETBIOS: c_int = 33; | ||
| 521 | pub const AF_PPP: c_int = 34; | ||
| 522 | pub const AF_MAX: c_int = 40; | ||
| 523 | |||
| 524 | pub const PF_UNSPEC: c_int = AF_UNSPEC; | ||
| 525 | pub const PF_LOCAL: c_int = AF_LOCAL; | ||
| 526 | pub const PF_UNIX: c_int = PF_LOCAL; | ||
| 527 | pub const PF_INET: c_int = AF_INET; | ||
| 528 | pub const PF_IMPLINK: c_int = AF_IMPLINK; | ||
| 529 | pub const PF_PUP: c_int = AF_PUP; | ||
| 530 | pub const PF_CHAOS: c_int = AF_CHAOS; | ||
| 531 | pub const PF_NS: c_int = AF_NS; | ||
| 532 | pub const PF_ISO: c_int = AF_ISO; | ||
| 533 | pub const PF_OSI: c_int = AF_ISO; | ||
| 534 | pub const PF_ECMA: c_int = AF_ECMA; | ||
| 535 | pub const PF_DATAKIT: c_int = AF_DATAKIT; | ||
| 536 | pub const PF_CCITT: c_int = AF_CCITT; | ||
| 537 | pub const PF_SNA: c_int = AF_SNA; | ||
| 538 | pub const PF_DECnet: c_int = AF_DECnet; | ||
| 539 | pub const PF_DLI: c_int = AF_DLI; | ||
| 540 | pub const PF_LAT: c_int = AF_LAT; | ||
| 541 | pub const PF_HYLINK: c_int = AF_HYLINK; | ||
| 542 | pub const PF_APPLETALK: c_int = AF_APPLETALK; | ||
| 543 | pub const PF_ROUTE: c_int = AF_ROUTE; | ||
| 544 | pub const PF_LINK: c_int = AF_LINK; | ||
| 545 | pub const PF_XTP: c_int = AF_XTP; | ||
| 546 | pub const PF_COIP: c_int = AF_COIP; | ||
| 547 | pub const PF_CNT: c_int = AF_CNT; | ||
| 548 | pub const PF_SIP: c_int = AF_SIP; | ||
| 549 | pub const PF_IPX: c_int = AF_IPX; | ||
| 550 | pub const PF_RTIP: c_int = AF_RTIP; | ||
| 551 | pub const PF_PIP: c_int = AF_PIP; | ||
| 552 | pub const PF_ISDN: c_int = AF_ISDN; | ||
| 553 | pub const PF_KEY: c_int = AF_KEY; | ||
| 554 | pub const PF_INET6: c_int = AF_INET6; | ||
| 555 | pub const PF_NATM: c_int = AF_NATM; | ||
| 556 | pub const PF_SYSTEM: c_int = AF_SYSTEM; | ||
| 557 | pub const PF_NETBIOS: c_int = AF_NETBIOS; | ||
| 558 | pub const PF_PPP: c_int = AF_PPP; | ||
| 559 | pub const PF_MAX: c_int = AF_MAX; | ||
| 560 | |||
| 561 | pub const SYSPROTO_EVENT: c_int = 1; | ||
| 562 | pub const SYSPROTO_CONTROL: c_int = 2; | ||
| 563 | |||
| 564 | pub const SOCK_STREAM: c_int = 1; | ||
| 565 | pub const SOCK_DGRAM: c_int = 2; | ||
| 566 | pub const SOCK_RAW: c_int = 3; | ||
| 567 | pub const SOCK_RDM: c_int = 4; | ||
| 568 | pub const SOCK_SEQPACKET: c_int = 5; | ||
| 569 | pub const SOCK_MAXADDRLEN: c_int = 255; | ||
| 570 | |||
| 485 | fn wstatus(x: i32) i32 { | 571 | fn wstatus(x: i32) i32 { |
| 486 | return x & 0o177; | 572 | return x & 0o177; |
| 487 | } | 573 | } |
std/os/darwin/errno.zig created+328| ... | @@ -0,0 +1,328 @@ | ||
| 1 | /// Operation not permitted | ||
| 2 | pub const EPERM = 1; | ||
| 3 | |||
| 4 | /// No such file or directory | ||
| 5 | pub const ENOENT = 2; | ||
| 6 | |||
| 7 | /// No such process | ||
| 8 | pub const ESRCH = 3; | ||
| 9 | |||
| 10 | /// Interrupted system call | ||
| 11 | pub const EINTR = 4; | ||
| 12 | |||
| 13 | /// Input/output error | ||
| 14 | pub const EIO = 5; | ||
| 15 | |||
| 16 | /// Device not configured | ||
| 17 | pub const ENXIO = 6; | ||
| 18 | |||
| 19 | /// Argument list too long | ||
| 20 | pub const E2BIG = 7; | ||
| 21 | |||
| 22 | /// Exec format error | ||
| 23 | pub const ENOEXEC = 8; | ||
| 24 | |||
| 25 | /// Bad file descriptor | ||
| 26 | pub const EBADF = 9; | ||
| 27 | |||
| 28 | /// No child processes | ||
| 29 | pub const ECHILD = 10; | ||
| 30 | |||
| 31 | /// Resource deadlock avoided | ||
| 32 | pub const EDEADLK = 11; | ||
| 33 | |||
| 34 | /// Cannot allocate memory | ||
| 35 | pub const ENOMEM = 12; | ||
| 36 | |||
| 37 | /// Permission denied | ||
| 38 | pub const EACCES = 13; | ||
| 39 | |||
| 40 | /// Bad address | ||
| 41 | pub const EFAULT = 14; | ||
| 42 | |||
| 43 | /// Block device required | ||
| 44 | pub const ENOTBLK = 15; | ||
| 45 | |||
| 46 | /// Device / Resource busy | ||
| 47 | pub const EBUSY = 16; | ||
| 48 | |||
| 49 | /// File exists | ||
| 50 | pub const EEXIST = 17; | ||
| 51 | |||
| 52 | /// Cross-device link | ||
| 53 | pub const EXDEV = 18; | ||
| 54 | |||
| 55 | /// Operation not supported by device | ||
| 56 | pub const ENODEV = 19; | ||
| 57 | |||
| 58 | /// Not a directory | ||
| 59 | pub const ENOTDIR = 20; | ||
| 60 | |||
| 61 | /// Is a directory | ||
| 62 | pub const EISDIR = 21; | ||
| 63 | |||
| 64 | /// Invalid argument | ||
| 65 | pub const EINVAL = 22; | ||
| 66 | |||
| 67 | /// Too many open files in system | ||
| 68 | pub const ENFILE = 23; | ||
| 69 | |||
| 70 | /// Too many open files | ||
| 71 | pub const EMFILE = 24; | ||
| 72 | |||
| 73 | /// Inappropriate ioctl for device | ||
| 74 | pub const ENOTTY = 25; | ||
| 75 | |||
| 76 | /// Text file busy | ||
| 77 | pub const ETXTBSY = 26; | ||
| 78 | |||
| 79 | /// File too large | ||
| 80 | pub const EFBIG = 27; | ||
| 81 | |||
| 82 | /// No space left on device | ||
| 83 | pub const ENOSPC = 28; | ||
| 84 | |||
| 85 | /// Illegal seek | ||
| 86 | pub const ESPIPE = 29; | ||
| 87 | |||
| 88 | /// Read-only file system | ||
| 89 | pub const EROFS = 30; | ||
| 90 | |||
| 91 | /// Too many links | ||
| 92 | pub const EMLINK = 31; | ||
| 93 | /// Broken pipe | ||
| 94 | |||
| 95 | // math software | ||
| 96 | pub const EPIPE = 32; | ||
| 97 | |||
| 98 | /// Numerical argument out of domain | ||
| 99 | pub const EDOM = 33; | ||
| 100 | /// Result too large | ||
| 101 | |||
| 102 | // non-blocking and interrupt i/o | ||
| 103 | pub const ERANGE = 34; | ||
| 104 | |||
| 105 | /// Resource temporarily unavailable | ||
| 106 | pub const EAGAIN = 35; | ||
| 107 | |||
| 108 | /// Operation would block | ||
| 109 | pub const EWOULDBLOCK = EAGAIN; | ||
| 110 | |||
| 111 | /// Operation now in progress | ||
| 112 | pub const EINPROGRESS = 36; | ||
| 113 | /// Operation already in progress | ||
| 114 | |||
| 115 | // ipc/network software -- argument errors | ||
| 116 | pub const EALREADY = 37; | ||
| 117 | |||
| 118 | /// Socket operation on non-socket | ||
| 119 | pub const ENOTSOCK = 38; | ||
| 120 | |||
| 121 | /// Destination address required | ||
| 122 | pub const EDESTADDRREQ = 39; | ||
| 123 | |||
| 124 | /// Message too long | ||
| 125 | pub const EMSGSIZE = 40; | ||
| 126 | |||
| 127 | /// Protocol wrong type for socket | ||
| 128 | pub const EPROTOTYPE = 41; | ||
| 129 | |||
| 130 | /// Protocol not available | ||
| 131 | pub const ENOPROTOOPT = 42; | ||
| 132 | |||
| 133 | /// Protocol not supported | ||
| 134 | pub const EPROTONOSUPPORT = 43; | ||
| 135 | |||
| 136 | /// Socket type not supported | ||
| 137 | pub const ESOCKTNOSUPPORT = 44; | ||
| 138 | |||
| 139 | /// Operation not supported | ||
| 140 | pub const ENOTSUP = 45; | ||
| 141 | |||
| 142 | /// Protocol family not supported | ||
| 143 | pub const EPFNOSUPPORT = 46; | ||
| 144 | |||
| 145 | /// Address family not supported by protocol family | ||
| 146 | pub const EAFNOSUPPORT = 47; | ||
| 147 | |||
| 148 | /// Address already in use | ||
| 149 | pub const EADDRINUSE = 48; | ||
| 150 | /// Can't assign requested address | ||
| 151 | |||
| 152 | // ipc/network software -- operational errors | ||
| 153 | pub const EADDRNOTAVAIL = 49; | ||
| 154 | |||
| 155 | /// Network is down | ||
| 156 | pub const ENETDOWN = 50; | ||
| 157 | |||
| 158 | /// Network is unreachable | ||
| 159 | pub const ENETUNREACH = 51; | ||
| 160 | |||
| 161 | /// Network dropped connection on reset | ||
| 162 | pub const ENETRESET = 52; | ||
| 163 | |||
| 164 | /// Software caused connection abort | ||
| 165 | pub const ECONNABORTED = 53; | ||
| 166 | |||
| 167 | /// Connection reset by peer | ||
| 168 | pub const ECONNRESET = 54; | ||
| 169 | |||
| 170 | /// No buffer space available | ||
| 171 | pub const ENOBUFS = 55; | ||
| 172 | |||
| 173 | /// Socket is already connected | ||
| 174 | pub const EISCONN = 56; | ||
| 175 | |||
| 176 | /// Socket is not connected | ||
| 177 | pub const ENOTCONN = 57; | ||
| 178 | |||
| 179 | /// Can't send after socket shutdown | ||
| 180 | pub const ESHUTDOWN = 58; | ||
| 181 | |||
| 182 | /// Too many references: can't splice | ||
| 183 | pub const ETOOMANYREFS = 59; | ||
| 184 | |||
| 185 | /// Operation timed out | ||
| 186 | pub const ETIMEDOUT = 60; | ||
| 187 | |||
| 188 | /// Connection refused | ||
| 189 | pub const ECONNREFUSED = 61; | ||
| 190 | |||
| 191 | /// Too many levels of symbolic links | ||
| 192 | pub const ELOOP = 62; | ||
| 193 | |||
| 194 | /// File name too long | ||
| 195 | pub const ENAMETOOLONG = 63; | ||
| 196 | |||
| 197 | /// Host is down | ||
| 198 | pub const EHOSTDOWN = 64; | ||
| 199 | |||
| 200 | /// No route to host | ||
| 201 | pub const EHOSTUNREACH = 65; | ||
| 202 | /// Directory not empty | ||
| 203 | |||
| 204 | // quotas & mush | ||
| 205 | pub const ENOTEMPTY = 66; | ||
| 206 | |||
| 207 | /// Too many processes | ||
| 208 | pub const EPROCLIM = 67; | ||
| 209 | |||
| 210 | /// Too many users | ||
| 211 | pub const EUSERS = 68; | ||
| 212 | /// Disc quota exceeded | ||
| 213 | |||
| 214 | // Network File System | ||
| 215 | pub const EDQUOT = 69; | ||
| 216 | |||
| 217 | /// Stale NFS file handle | ||
| 218 | pub const ESTALE = 70; | ||
| 219 | |||
| 220 | /// Too many levels of remote in path | ||
| 221 | pub const EREMOTE = 71; | ||
| 222 | |||
| 223 | /// RPC struct is bad | ||
| 224 | pub const EBADRPC = 72; | ||
| 225 | |||
| 226 | /// RPC version wrong | ||
| 227 | pub const ERPCMISMATCH = 73; | ||
| 228 | |||
| 229 | /// RPC prog. not avail | ||
| 230 | pub const EPROGUNAVAIL = 74; | ||
| 231 | |||
| 232 | /// Program version wrong | ||
| 233 | pub const EPROGMISMATCH = 75; | ||
| 234 | |||
| 235 | /// Bad procedure for program | ||
| 236 | pub const EPROCUNAVAIL = 76; | ||
| 237 | |||
| 238 | /// No locks available | ||
| 239 | pub const ENOLCK = 77; | ||
| 240 | |||
| 241 | /// Function not implemented | ||
| 242 | pub const ENOSYS = 78; | ||
| 243 | |||
| 244 | /// Inappropriate file type or format | ||
| 245 | pub const EFTYPE = 79; | ||
| 246 | |||
| 247 | /// Authentication error | ||
| 248 | pub const EAUTH = 80; | ||
| 249 | /// Need authenticator | ||
| 250 | |||
| 251 | // Intelligent device errors | ||
| 252 | pub const ENEEDAUTH = 81; | ||
| 253 | |||
| 254 | /// Device power is off | ||
| 255 | pub const EPWROFF = 82; | ||
| 256 | |||
| 257 | /// Device error, e.g. paper out | ||
| 258 | pub const EDEVERR = 83; | ||
| 259 | /// Value too large to be stored in data type | ||
| 260 | |||
| 261 | // Program loading errors | ||
| 262 | pub const EOVERFLOW = 84; | ||
| 263 | |||
| 264 | /// Bad executable | ||
| 265 | pub const EBADEXEC = 85; | ||
| 266 | |||
| 267 | /// Bad CPU type in executable | ||
| 268 | pub const EBADARCH = 86; | ||
| 269 | |||
| 270 | /// Shared library version mismatch | ||
| 271 | pub const ESHLIBVERS = 87; | ||
| 272 | |||
| 273 | /// Malformed Macho file | ||
| 274 | pub const EBADMACHO = 88; | ||
| 275 | |||
| 276 | /// Operation canceled | ||
| 277 | pub const ECANCELED = 89; | ||
| 278 | |||
| 279 | /// Identifier removed | ||
| 280 | pub const EIDRM = 90; | ||
| 281 | |||
| 282 | /// No message of desired type | ||
| 283 | pub const ENOMSG = 91; | ||
| 284 | |||
| 285 | /// Illegal byte sequence | ||
| 286 | pub const EILSEQ = 92; | ||
| 287 | |||
| 288 | /// Attribute not found | ||
| 289 | pub const ENOATTR = 93; | ||
| 290 | |||
| 291 | /// Bad message | ||
| 292 | pub const EBADMSG = 94; | ||
| 293 | |||
| 294 | /// Reserved | ||
| 295 | pub const EMULTIHOP = 95; | ||
| 296 | |||
| 297 | /// No message available on STREAM | ||
| 298 | pub const ENODATA = 96; | ||
| 299 | |||
| 300 | /// Reserved | ||
| 301 | pub const ENOLINK = 97; | ||
| 302 | |||
| 303 | /// No STREAM resources | ||
| 304 | pub const ENOSR = 98; | ||
| 305 | |||
| 306 | /// Not a STREAM | ||
| 307 | pub const ENOSTR = 99; | ||
| 308 | |||
| 309 | /// Protocol error | ||
| 310 | pub const EPROTO = 100; | ||
| 311 | |||
| 312 | /// STREAM ioctl timeout | ||
| 313 | pub const ETIME = 101; | ||
| 314 | |||
| 315 | /// No such policy registered | ||
| 316 | pub const ENOPOLICY = 103; | ||
| 317 | |||
| 318 | /// State not recoverable | ||
| 319 | pub const ENOTRECOVERABLE = 104; | ||
| 320 | |||
| 321 | /// Previous owner died | ||
| 322 | pub const EOWNERDEAD = 105; | ||
| 323 | |||
| 324 | /// Interface output queue is full | ||
| 325 | pub const EQFULL = 106; | ||
| 326 | |||
| 327 | /// Must be equal largest errno | ||
| 328 | pub const ELAST = 106; | ||
std/os/darwin_errno.zig deleted-328| ... | @@ -1,328 +0,0 @@ | ||
| 1 | /// Operation not permitted | ||
| 2 | pub const EPERM = 1; | ||
| 3 | |||
| 4 | /// No such file or directory | ||
| 5 | pub const ENOENT = 2; | ||
| 6 | |||
| 7 | /// No such process | ||
| 8 | pub const ESRCH = 3; | ||
| 9 | |||
| 10 | /// Interrupted system call | ||
| 11 | pub const EINTR = 4; | ||
| 12 | |||
| 13 | /// Input/output error | ||
| 14 | pub const EIO = 5; | ||
| 15 | |||
| 16 | /// Device not configured | ||
| 17 | pub const ENXIO = 6; | ||
| 18 | |||
| 19 | /// Argument list too long | ||
| 20 | pub const E2BIG = 7; | ||
| 21 | |||
| 22 | /// Exec format error | ||
| 23 | pub const ENOEXEC = 8; | ||
| 24 | |||
| 25 | /// Bad file descriptor | ||
| 26 | pub const EBADF = 9; | ||
| 27 | |||
| 28 | /// No child processes | ||
| 29 | pub const ECHILD = 10; | ||
| 30 | |||
| 31 | /// Resource deadlock avoided | ||
| 32 | pub const EDEADLK = 11; | ||
| 33 | |||
| 34 | /// Cannot allocate memory | ||
| 35 | pub const ENOMEM = 12; | ||
| 36 | |||
| 37 | /// Permission denied | ||
| 38 | pub const EACCES = 13; | ||
| 39 | |||
| 40 | /// Bad address | ||
| 41 | pub const EFAULT = 14; | ||
| 42 | |||
| 43 | /// Block device required | ||
| 44 | pub const ENOTBLK = 15; | ||
| 45 | |||
| 46 | /// Device / Resource busy | ||
| 47 | pub const EBUSY = 16; | ||
| 48 | |||
| 49 | /// File exists | ||
| 50 | pub const EEXIST = 17; | ||
| 51 | |||
| 52 | /// Cross-device link | ||
| 53 | pub const EXDEV = 18; | ||
| 54 | |||
| 55 | /// Operation not supported by device | ||
| 56 | pub const ENODEV = 19; | ||
| 57 | |||
| 58 | /// Not a directory | ||
| 59 | pub const ENOTDIR = 20; | ||
| 60 | |||
| 61 | /// Is a directory | ||
| 62 | pub const EISDIR = 21; | ||
| 63 | |||
| 64 | /// Invalid argument | ||
| 65 | pub const EINVAL = 22; | ||
| 66 | |||
| 67 | /// Too many open files in system | ||
| 68 | pub const ENFILE = 23; | ||
| 69 | |||
| 70 | /// Too many open files | ||
| 71 | pub const EMFILE = 24; | ||
| 72 | |||
| 73 | /// Inappropriate ioctl for device | ||
| 74 | pub const ENOTTY = 25; | ||
| 75 | |||
| 76 | /// Text file busy | ||
| 77 | pub const ETXTBSY = 26; | ||
| 78 | |||
| 79 | /// File too large | ||
| 80 | pub const EFBIG = 27; | ||
| 81 | |||
| 82 | /// No space left on device | ||
| 83 | pub const ENOSPC = 28; | ||
| 84 | |||
| 85 | /// Illegal seek | ||
| 86 | pub const ESPIPE = 29; | ||
| 87 | |||
| 88 | /// Read-only file system | ||
| 89 | pub const EROFS = 30; | ||
| 90 | |||
| 91 | /// Too many links | ||
| 92 | pub const EMLINK = 31; | ||
| 93 | /// Broken pipe | ||
| 94 | |||
| 95 | // math software | ||
| 96 | pub const EPIPE = 32; | ||
| 97 | |||
| 98 | /// Numerical argument out of domain | ||
| 99 | pub const EDOM = 33; | ||
| 100 | /// Result too large | ||
| 101 | |||
| 102 | // non-blocking and interrupt i/o | ||
| 103 | pub const ERANGE = 34; | ||
| 104 | |||
| 105 | /// Resource temporarily unavailable | ||
| 106 | pub const EAGAIN = 35; | ||
| 107 | |||
| 108 | /// Operation would block | ||
| 109 | pub const EWOULDBLOCK = EAGAIN; | ||
| 110 | |||
| 111 | /// Operation now in progress | ||
| 112 | pub const EINPROGRESS = 36; | ||
| 113 | /// Operation already in progress | ||
| 114 | |||
| 115 | // ipc/network software -- argument errors | ||
| 116 | pub const EALREADY = 37; | ||
| 117 | |||
| 118 | /// Socket operation on non-socket | ||
| 119 | pub const ENOTSOCK = 38; | ||
| 120 | |||
| 121 | /// Destination address required | ||
| 122 | pub const EDESTADDRREQ = 39; | ||
| 123 | |||
| 124 | /// Message too long | ||
| 125 | pub const EMSGSIZE = 40; | ||
| 126 | |||
| 127 | /// Protocol wrong type for socket | ||
| 128 | pub const EPROTOTYPE = 41; | ||
| 129 | |||
| 130 | /// Protocol not available | ||
| 131 | pub const ENOPROTOOPT = 42; | ||
| 132 | |||
| 133 | /// Protocol not supported | ||
| 134 | pub const EPROTONOSUPPORT = 43; | ||
| 135 | |||
| 136 | /// Socket type not supported | ||
| 137 | pub const ESOCKTNOSUPPORT = 44; | ||
| 138 | |||
| 139 | /// Operation not supported | ||
| 140 | pub const ENOTSUP = 45; | ||
| 141 | |||
| 142 | /// Protocol family not supported | ||
| 143 | pub const EPFNOSUPPORT = 46; | ||
| 144 | |||
| 145 | /// Address family not supported by protocol family | ||
| 146 | pub const EAFNOSUPPORT = 47; | ||
| 147 | |||
| 148 | /// Address already in use | ||
| 149 | pub const EADDRINUSE = 48; | ||
| 150 | /// Can't assign requested address | ||
| 151 | |||
| 152 | // ipc/network software -- operational errors | ||
| 153 | pub const EADDRNOTAVAIL = 49; | ||
| 154 | |||
| 155 | /// Network is down | ||
| 156 | pub const ENETDOWN = 50; | ||
| 157 | |||
| 158 | /// Network is unreachable | ||
| 159 | pub const ENETUNREACH = 51; | ||
| 160 | |||
| 161 | /// Network dropped connection on reset | ||
| 162 | pub const ENETRESET = 52; | ||
| 163 | |||
| 164 | /// Software caused connection abort | ||
| 165 | pub const ECONNABORTED = 53; | ||
| 166 | |||
| 167 | /// Connection reset by peer | ||
| 168 | pub const ECONNRESET = 54; | ||
| 169 | |||
| 170 | /// No buffer space available | ||
| 171 | pub const ENOBUFS = 55; | ||
| 172 | |||
| 173 | /// Socket is already connected | ||
| 174 | pub const EISCONN = 56; | ||
| 175 | |||
| 176 | /// Socket is not connected | ||
| 177 | pub const ENOTCONN = 57; | ||
| 178 | |||
| 179 | /// Can't send after socket shutdown | ||
| 180 | pub const ESHUTDOWN = 58; | ||
| 181 | |||
| 182 | /// Too many references: can't splice | ||
| 183 | pub const ETOOMANYREFS = 59; | ||
| 184 | |||
| 185 | /// Operation timed out | ||
| 186 | pub const ETIMEDOUT = 60; | ||
| 187 | |||
| 188 | /// Connection refused | ||
| 189 | pub const ECONNREFUSED = 61; | ||
| 190 | |||
| 191 | /// Too many levels of symbolic links | ||
| 192 | pub const ELOOP = 62; | ||
| 193 | |||
| 194 | /// File name too long | ||
| 195 | pub const ENAMETOOLONG = 63; | ||
| 196 | |||
| 197 | /// Host is down | ||
| 198 | pub const EHOSTDOWN = 64; | ||
| 199 | |||
| 200 | /// No route to host | ||
| 201 | pub const EHOSTUNREACH = 65; | ||
| 202 | /// Directory not empty | ||
| 203 | |||
| 204 | // quotas & mush | ||
| 205 | pub const ENOTEMPTY = 66; | ||
| 206 | |||
| 207 | /// Too many processes | ||
| 208 | pub const EPROCLIM = 67; | ||
| 209 | |||
| 210 | /// Too many users | ||
| 211 | pub const EUSERS = 68; | ||
| 212 | /// Disc quota exceeded | ||
| 213 | |||
| 214 | // Network File System | ||
| 215 | pub const EDQUOT = 69; | ||
| 216 | |||
| 217 | /// Stale NFS file handle | ||
| 218 | pub const ESTALE = 70; | ||
| 219 | |||
| 220 | /// Too many levels of remote in path | ||
| 221 | pub const EREMOTE = 71; | ||
| 222 | |||
| 223 | /// RPC struct is bad | ||
| 224 | pub const EBADRPC = 72; | ||
| 225 | |||
| 226 | /// RPC version wrong | ||
| 227 | pub const ERPCMISMATCH = 73; | ||
| 228 | |||
| 229 | /// RPC prog. not avail | ||
| 230 | pub const EPROGUNAVAIL = 74; | ||
| 231 | |||
| 232 | /// Program version wrong | ||
| 233 | pub const EPROGMISMATCH = 75; | ||
| 234 | |||
| 235 | /// Bad procedure for program | ||
| 236 | pub const EPROCUNAVAIL = 76; | ||
| 237 | |||
| 238 | /// No locks available | ||
| 239 | pub const ENOLCK = 77; | ||
| 240 | |||
| 241 | /// Function not implemented | ||
| 242 | pub const ENOSYS = 78; | ||
| 243 | |||
| 244 | /// Inappropriate file type or format | ||
| 245 | pub const EFTYPE = 79; | ||
| 246 | |||
| 247 | /// Authentication error | ||
| 248 | pub const EAUTH = 80; | ||
| 249 | /// Need authenticator | ||
| 250 | |||
| 251 | // Intelligent device errors | ||
| 252 | pub const ENEEDAUTH = 81; | ||
| 253 | |||
| 254 | /// Device power is off | ||
| 255 | pub const EPWROFF = 82; | ||
| 256 | |||
| 257 | /// Device error, e.g. paper out | ||
| 258 | pub const EDEVERR = 83; | ||
| 259 | /// Value too large to be stored in data type | ||
| 260 | |||
| 261 | // Program loading errors | ||
| 262 | pub const EOVERFLOW = 84; | ||
| 263 | |||
| 264 | /// Bad executable | ||
| 265 | pub const EBADEXEC = 85; | ||
| 266 | |||
| 267 | /// Bad CPU type in executable | ||
| 268 | pub const EBADARCH = 86; | ||
| 269 | |||
| 270 | /// Shared library version mismatch | ||
| 271 | pub const ESHLIBVERS = 87; | ||
| 272 | |||
| 273 | /// Malformed Macho file | ||
| 274 | pub const EBADMACHO = 88; | ||
| 275 | |||
| 276 | /// Operation canceled | ||
| 277 | pub const ECANCELED = 89; | ||
| 278 | |||
| 279 | /// Identifier removed | ||
| 280 | pub const EIDRM = 90; | ||
| 281 | |||
| 282 | /// No message of desired type | ||
| 283 | pub const ENOMSG = 91; | ||
| 284 | |||
| 285 | /// Illegal byte sequence | ||
| 286 | pub const EILSEQ = 92; | ||
| 287 | |||
| 288 | /// Attribute not found | ||
| 289 | pub const ENOATTR = 93; | ||
| 290 | |||
| 291 | /// Bad message | ||
| 292 | pub const EBADMSG = 94; | ||
| 293 | |||
| 294 | /// Reserved | ||
| 295 | pub const EMULTIHOP = 95; | ||
| 296 | |||
| 297 | /// No message available on STREAM | ||
| 298 | pub const ENODATA = 96; | ||
| 299 | |||
| 300 | /// Reserved | ||
| 301 | pub const ENOLINK = 97; | ||
| 302 | |||
| 303 | /// No STREAM resources | ||
| 304 | pub const ENOSR = 98; | ||
| 305 | |||
| 306 | /// Not a STREAM | ||
| 307 | pub const ENOSTR = 99; | ||
| 308 | |||
| 309 | /// Protocol error | ||
| 310 | pub const EPROTO = 100; | ||
| 311 | |||
| 312 | /// STREAM ioctl timeout | ||
| 313 | pub const ETIME = 101; | ||
| 314 | |||
| 315 | /// No such policy registered | ||
| 316 | pub const ENOPOLICY = 103; | ||
| 317 | |||
| 318 | /// State not recoverable | ||
| 319 | pub const ENOTRECOVERABLE = 104; | ||
| 320 | |||
| 321 | /// Previous owner died | ||
| 322 | pub const EOWNERDEAD = 105; | ||
| 323 | |||
| 324 | /// Interface output queue is full | ||
| 325 | pub const EQFULL = 106; | ||
| 326 | |||
| 327 | /// Must be equal largest errno | ||
| 328 | pub const ELAST = 106; | ||
std/os/file.zig+28-35| ... | @@ -15,7 +15,7 @@ pub const File = struct { | ... | @@ -15,7 +15,7 @@ pub const File = struct { |
| 15 | /// The OS-specific file descriptor or file handle. | 15 | /// The OS-specific file descriptor or file handle. |
| 16 | handle: os.FileHandle, | 16 | handle: os.FileHandle, |
| 17 | 17 | ||
| 18 | const OpenError = os.WindowsOpenError || os.PosixOpenError; | 18 | pub const OpenError = os.WindowsOpenError || os.PosixOpenError; |
| 19 | 19 | ||
| 20 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. | 20 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. |
| 21 | /// Call close to clean up. | 21 | /// Call close to clean up. |
| ... | @@ -109,43 +109,42 @@ pub const File = struct { | ... | @@ -109,43 +109,42 @@ pub const File = struct { |
| 109 | Unexpected, | 109 | Unexpected, |
| 110 | }; | 110 | }; |
| 111 | 111 | ||
| 112 | pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool { | 112 | pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void { |
| 113 | const path_with_null = try std.cstr.addNullByte(allocator, path); | 113 | const path_with_null = try std.cstr.addNullByte(allocator, path); |
| 114 | defer allocator.free(path_with_null); | 114 | defer allocator.free(path_with_null); |
| 115 | 115 | ||
| 116 | if (is_posix) { | 116 | if (is_posix) { |
| 117 | // mode is ignored and is always F_OK for now | ||
| 118 | const result = posix.access(path_with_null.ptr, posix.F_OK); | 117 | const result = posix.access(path_with_null.ptr, posix.F_OK); |
| 119 | const err = posix.getErrno(result); | 118 | const err = posix.getErrno(result); |
| 120 | if (err > 0) { | 119 | switch (err) { |
| 121 | return switch (err) { | 120 | 0 => return, |
| 122 | posix.EACCES => error.PermissionDenied, | 121 | posix.EACCES => return error.PermissionDenied, |
| 123 | posix.EROFS => error.PermissionDenied, | 122 | posix.EROFS => return error.PermissionDenied, |
| 124 | posix.ELOOP => error.PermissionDenied, | 123 | posix.ELOOP => return error.PermissionDenied, |
| 125 | posix.ETXTBSY => error.PermissionDenied, | 124 | posix.ETXTBSY => return error.PermissionDenied, |
| 126 | posix.ENOTDIR => error.NotFound, | 125 | posix.ENOTDIR => return error.NotFound, |
| 127 | posix.ENOENT => error.NotFound, | 126 | posix.ENOENT => return error.NotFound, |
| 128 | 127 | ||
| 129 | posix.ENAMETOOLONG => error.NameTooLong, | 128 | posix.ENAMETOOLONG => return error.NameTooLong, |
| 130 | posix.EINVAL => error.BadMode, | 129 | posix.EINVAL => unreachable, |
| 131 | posix.EFAULT => error.BadPathName, | 130 | posix.EFAULT => return error.BadPathName, |
| 132 | posix.EIO => error.Io, | 131 | posix.EIO => return error.Io, |
| 133 | posix.ENOMEM => error.SystemResources, | 132 | posix.ENOMEM => return error.SystemResources, |
| 134 | else => os.unexpectedErrorPosix(err), | 133 | else => return os.unexpectedErrorPosix(err), |
| 135 | }; | ||
| 136 | } | 134 | } |
| 137 | return true; | ||
| 138 | } else if (is_windows) { | 135 | } else if (is_windows) { |
| 139 | if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) { | 136 | if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) { |
| 140 | return true; | 137 | return; |
| 141 | } | 138 | } |
| 142 | 139 | ||
| 143 | const err = windows.GetLastError(); | 140 | const err = windows.GetLastError(); |
| 144 | return switch (err) { | 141 | switch (err) { |
| 145 | windows.ERROR.FILE_NOT_FOUND => error.NotFound, | 142 | windows.ERROR.FILE_NOT_FOUND, |
| 146 | windows.ERROR.ACCESS_DENIED => error.PermissionDenied, | 143 | windows.ERROR.PATH_NOT_FOUND, |
| 147 | else => os.unexpectedErrorWindows(err), | 144 | => return error.NotFound, |
| 148 | }; | 145 | windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, |
| 146 | else => return os.unexpectedErrorWindows(err), | ||
| 147 | } | ||
| 149 | } else { | 148 | } else { |
| 150 | @compileError("TODO implement access for this OS"); | 149 | @compileError("TODO implement access for this OS"); |
| 151 | } | 150 | } |
| ... | @@ -242,7 +241,7 @@ pub const File = struct { | ... | @@ -242,7 +241,7 @@ pub const File = struct { |
| 242 | }, | 241 | }, |
| 243 | Os.windows => { | 242 | Os.windows => { |
| 244 | var pos: windows.LARGE_INTEGER = undefined; | 243 | var pos: windows.LARGE_INTEGER = undefined; |
| 245 | if (windows.SetFilePointerEx(self.handle, 0, *pos, windows.FILE_CURRENT) == 0) { | 244 | if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) { |
| 246 | const err = windows.GetLastError(); | 245 | const err = windows.GetLastError(); |
| 247 | return switch (err) { | 246 | return switch (err) { |
| 248 | windows.ERROR.INVALID_PARAMETER => error.BadFd, | 247 | windows.ERROR.INVALID_PARAMETER => error.BadFd, |
| ... | @@ -251,13 +250,7 @@ pub const File = struct { | ... | @@ -251,13 +250,7 @@ pub const File = struct { |
| 251 | } | 250 | } |
| 252 | 251 | ||
| 253 | assert(pos >= 0); | 252 | assert(pos >= 0); |
| 254 | if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) { | 253 | return math.cast(usize, pos) catch error.FilePosLargerThanPointerRange; |
| 255 | if (pos > @maxValue(usize)) { | ||
| 256 | return error.FilePosLargerThanPointerRange; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | return usize(pos); | ||
| 261 | }, | 254 | }, |
| 262 | else => @compileError("unsupported OS"), | 255 | else => @compileError("unsupported OS"), |
| 263 | } | 256 | } |
| ... | @@ -289,7 +282,7 @@ pub const File = struct { | ... | @@ -289,7 +282,7 @@ pub const File = struct { |
| 289 | Unexpected, | 282 | Unexpected, |
| 290 | }; | 283 | }; |
| 291 | 284 | ||
| 292 | fn mode(self: *File) ModeError!os.FileMode { | 285 | pub fn mode(self: *File) ModeError!os.FileMode { |
| 293 | if (is_posix) { | 286 | if (is_posix) { |
| 294 | var stat: posix.Stat = undefined; | 287 | var stat: posix.Stat = undefined; |
| 295 | const err = posix.getErrno(posix.fstat(self.handle, &stat)); | 288 | const err = posix.getErrno(posix.fstat(self.handle, &stat)); |
| ... | @@ -364,7 +357,7 @@ pub const File = struct { | ... | @@ -364,7 +357,7 @@ pub const File = struct { |
| 364 | 357 | ||
| 365 | pub const WriteError = os.WindowsWriteError || os.PosixWriteError; | 358 | pub const WriteError = os.WindowsWriteError || os.PosixWriteError; |
| 366 | 359 | ||
| 367 | fn write(self: *File, bytes: []const u8) WriteError!void { | 360 | pub fn write(self: *File, bytes: []const u8) WriteError!void { |
| 368 | if (is_posix) { | 361 | if (is_posix) { |
| 369 | try os.posixWrite(self.handle, bytes); | 362 | try os.posixWrite(self.handle, bytes); |
| 370 | } else if (is_windows) { | 363 | } else if (is_windows) { |
std/os/get_app_data_dir.zig created+69| ... | @@ -0,0 +1,69 @@ | ||
| 1 | const std = @import("../index.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const unicode = std.unicode; | ||
| 4 | const mem = std.mem; | ||
| 5 | const os = std.os; | ||
| 6 | |||
| 7 | pub const GetAppDataDirError = error{ | ||
| 8 | OutOfMemory, | ||
| 9 | AppDataDirUnavailable, | ||
| 10 | }; | ||
| 11 | |||
| 12 | /// Caller owns returned memory. | ||
| 13 | pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { | ||
| 14 | switch (builtin.os) { | ||
| 15 | builtin.Os.windows => { | ||
| 16 | var dir_path_ptr: [*]u16 = undefined; | ||
| 17 | switch (os.windows.SHGetKnownFolderPath( | ||
| 18 | &os.windows.FOLDERID_LocalAppData, | ||
| 19 | os.windows.KF_FLAG_CREATE, | ||
| 20 | null, | ||
| 21 | &dir_path_ptr, | ||
| 22 | )) { | ||
| 23 | os.windows.S_OK => { | ||
| 24 | defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); | ||
| 25 | const global_dir = unicode.utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) { | ||
| 26 | error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, | ||
| 27 | error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable, | ||
| 28 | error.DanglingSurrogateHalf => return error.AppDataDirUnavailable, | ||
| 29 | error.OutOfMemory => return error.OutOfMemory, | ||
| 30 | }; | ||
| 31 | defer allocator.free(global_dir); | ||
| 32 | return os.path.join(allocator, global_dir, appname); | ||
| 33 | }, | ||
| 34 | os.windows.E_OUTOFMEMORY => return error.OutOfMemory, | ||
| 35 | else => return error.AppDataDirUnavailable, | ||
| 36 | } | ||
| 37 | }, | ||
| 38 | builtin.Os.macosx => { | ||
| 39 | const home_dir = os.getEnvPosix("HOME") orelse { | ||
| 40 | // TODO look in /etc/passwd | ||
| 41 | return error.AppDataDirUnavailable; | ||
| 42 | }; | ||
| 43 | return os.path.join(allocator, home_dir, "Library", "Application Support", appname); | ||
| 44 | }, | ||
| 45 | builtin.Os.linux => { | ||
| 46 | const home_dir = os.getEnvPosix("HOME") orelse { | ||
| 47 | // TODO look in /etc/passwd | ||
| 48 | return error.AppDataDirUnavailable; | ||
| 49 | }; | ||
| 50 | return os.path.join(allocator, home_dir, ".local", "share", appname); | ||
| 51 | }, | ||
| 52 | else => @compileError("Unsupported OS"), | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | fn utf16lePtrSlice(ptr: [*]const u16) []const u16 { | ||
| 57 | var index: usize = 0; | ||
| 58 | while (ptr[index] != 0) : (index += 1) {} | ||
| 59 | return ptr[0..index]; | ||
| 60 | } | ||
| 61 | |||
| 62 | test "std.os.getAppDataDir" { | ||
| 63 | var buf: [512]u8 = undefined; | ||
| 64 | const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; | ||
| 65 | |||
| 66 | // We can't actually validate the result | ||
| 67 | _ = getAppDataDir(allocator, "zig") catch return; | ||
| 68 | } | ||
| 69 | |||
std/os/index.zig+8-1| ... | @@ -11,13 +11,14 @@ const os = this; | ... | @@ -11,13 +11,14 @@ const os = this; |
| 11 | test "std.os" { | 11 | test "std.os" { |
| 12 | _ = @import("child_process.zig"); | 12 | _ = @import("child_process.zig"); |
| 13 | _ = @import("darwin.zig"); | 13 | _ = @import("darwin.zig"); |
| 14 | _ = @import("darwin_errno.zig"); | 14 | _ = @import("darwin/errno.zig"); |
| 15 | _ = @import("get_user_id.zig"); | 15 | _ = @import("get_user_id.zig"); |
| 16 | _ = @import("linux/index.zig"); | 16 | _ = @import("linux/index.zig"); |
| 17 | _ = @import("path.zig"); | 17 | _ = @import("path.zig"); |
| 18 | _ = @import("test.zig"); | 18 | _ = @import("test.zig"); |
| 19 | _ = @import("time.zig"); | 19 | _ = @import("time.zig"); |
| 20 | _ = @import("windows/index.zig"); | 20 | _ = @import("windows/index.zig"); |
| 21 | _ = @import("get_app_data_dir.zig"); | ||
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | pub const windows = @import("windows/index.zig"); | 24 | pub const windows = @import("windows/index.zig"); |
| ... | @@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError; | ... | @@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError; |
| 76 | 77 | ||
| 77 | pub const FileHandle = if (is_windows) windows.HANDLE else i32; | 78 | pub const FileHandle = if (is_windows) windows.HANDLE else i32; |
| 78 | 79 | ||
| 80 | pub const getAppDataDir = @import("get_app_data_dir.zig").getAppDataDir; | ||
| 81 | pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirError; | ||
| 82 | |||
| 79 | const debug = std.debug; | 83 | const debug = std.debug; |
| 80 | const assert = debug.assert; | 84 | const assert = debug.assert; |
| 81 | 85 | ||
| ... | @@ -494,6 +498,7 @@ pub var linux_aux_raw = []usize{0} ** 38; | ... | @@ -494,6 +498,7 @@ pub var linux_aux_raw = []usize{0} ** 38; |
| 494 | pub var posix_environ_raw: [][*]u8 = undefined; | 498 | pub var posix_environ_raw: [][*]u8 = undefined; |
| 495 | 499 | ||
| 496 | /// Caller must free result when done. | 500 | /// Caller must free result when done. |
| 501 | /// TODO make this go through libc when we have it | ||
| 497 | pub fn getEnvMap(allocator: *Allocator) !BufMap { | 502 | pub fn getEnvMap(allocator: *Allocator) !BufMap { |
| 498 | var result = BufMap.init(allocator); | 503 | var result = BufMap.init(allocator); |
| 499 | errdefer result.deinit(); | 504 | errdefer result.deinit(); |
| ... | @@ -537,6 +542,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { | ... | @@ -537,6 +542,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { |
| 537 | } | 542 | } |
| 538 | } | 543 | } |
| 539 | 544 | ||
| 545 | /// TODO make this go through libc when we have it | ||
| 540 | pub fn getEnvPosix(key: []const u8) ?[]const u8 { | 546 | pub fn getEnvPosix(key: []const u8) ?[]const u8 { |
| 541 | for (posix_environ_raw) |ptr| { | 547 | for (posix_environ_raw) |ptr| { |
| 542 | var line_i: usize = 0; | 548 | var line_i: usize = 0; |
| ... | @@ -559,6 +565,7 @@ pub const GetEnvVarOwnedError = error{ | ... | @@ -559,6 +565,7 @@ pub const GetEnvVarOwnedError = error{ |
| 559 | }; | 565 | }; |
| 560 | 566 | ||
| 561 | /// Caller must free returned memory. | 567 | /// Caller must free returned memory. |
| 568 | /// TODO make this go through libc when we have it | ||
| 562 | pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { | 569 | pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { |
| 563 | if (is_windows) { | 570 | if (is_windows) { |
| 564 | const key_with_null = try cstr.addNullByte(allocator, key); | 571 | const key_with_null = try cstr.addNullByte(allocator, key); |
std/os/test.zig+3-3| ... | @@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" { | ... | @@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" { |
| 23 | 23 | ||
| 24 | test "access file" { | 24 | test "access file" { |
| 25 | try os.makePath(a, "os_test_tmp"); | 25 | try os.makePath(a, "os_test_tmp"); |
| 26 | if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| { | 26 | if (os.File.access(a, "os_test_tmp/file.txt")) |ok| { |
| 27 | unreachable; | 27 | @panic("expected error"); |
| 28 | } else |err| { | 28 | } else |err| { |
| 29 | assert(err == error.NotFound); | 29 | assert(err == error.NotFound); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | try io.writeFile(a, "os_test_tmp/file.txt", ""); | 32 | try io.writeFile(a, "os_test_tmp/file.txt", ""); |
| 33 | assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true); | 33 | try os.File.access(a, "os_test_tmp/file.txt"); |
| 34 | try os.deleteTree(a, "os_test_tmp"); | 34 | try os.deleteTree(a, "os_test_tmp"); |
| 35 | } | 35 | } |
| 36 | 36 |
std/os/windows/advapi32.zig created+30| ... | @@ -0,0 +1,30 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub const PROV_RSA_FULL = 1; | ||
| 4 | |||
| 5 | pub const REGSAM = ACCESS_MASK; | ||
| 6 | pub const ACCESS_MASK = DWORD; | ||
| 7 | pub const PHKEY = &HKEY; | ||
| 8 | pub const HKEY = &HKEY__; | ||
| 9 | pub const HKEY__ = extern struct { | ||
| 10 | unused: c_int, | ||
| 11 | }; | ||
| 12 | pub const LSTATUS = LONG; | ||
| 13 | |||
| 14 | pub extern "advapi32" stdcallcc fn CryptAcquireContextA( | ||
| 15 | phProv: *HCRYPTPROV, | ||
| 16 | pszContainer: ?LPCSTR, | ||
| 17 | pszProvider: ?LPCSTR, | ||
| 18 | dwProvType: DWORD, | ||
| 19 | dwFlags: DWORD, | ||
| 20 | ) BOOL; | ||
| 21 | |||
| 22 | pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; | ||
| 23 | |||
| 24 | pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL; | ||
| 25 | |||
| 26 | pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM, | ||
| 27 | phkResult: &HKEY,) LSTATUS; | ||
| 28 | |||
| 29 | pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, | ||
| 30 | lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS; | ||
std/os/windows/index.zig+90-179| ... | @@ -1,188 +1,19 @@ | ... | @@ -1,188 +1,19 @@ |
| 1 | const std = @import("../../index.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | |||
| 4 | pub use @import("advapi32.zig"); | ||
| 5 | pub use @import("kernel32.zig"); | ||
| 6 | pub use @import("ole32.zig"); | ||
| 7 | pub use @import("shell32.zig"); | ||
| 8 | pub use @import("shlwapi.zig"); | ||
| 9 | pub use @import("user32.zig"); | ||
| 10 | |||
| 1 | test "import" { | 11 | test "import" { |
| 2 | _ = @import("util.zig"); | 12 | _ = @import("util.zig"); |
| 3 | } | 13 | } |
| 4 | 14 | ||
| 5 | pub const ERROR = @import("error.zig"); | 15 | pub const ERROR = @import("error.zig"); |
| 6 | 16 | ||
| 7 | pub extern "advapi32" stdcallcc fn CryptAcquireContextA( | ||
| 8 | phProv: *HCRYPTPROV, | ||
| 9 | pszContainer: ?LPCSTR, | ||
| 10 | pszProvider: ?LPCSTR, | ||
| 11 | dwProvType: DWORD, | ||
| 12 | dwFlags: DWORD, | ||
| 13 | ) BOOL; | ||
| 14 | |||
| 15 | pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; | ||
| 16 | |||
| 17 | pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL; | ||
| 18 | |||
| 19 | pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; | ||
| 20 | |||
| 21 | pub extern "kernel32" stdcallcc fn CreateDirectoryA( | ||
| 22 | lpPathName: LPCSTR, | ||
| 23 | lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, | ||
| 24 | ) BOOL; | ||
| 25 | |||
| 26 | pub extern "kernel32" stdcallcc fn CreateFileA( | ||
| 27 | lpFileName: LPCSTR, | ||
| 28 | dwDesiredAccess: DWORD, | ||
| 29 | dwShareMode: DWORD, | ||
| 30 | lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, | ||
| 31 | dwCreationDisposition: DWORD, | ||
| 32 | dwFlagsAndAttributes: DWORD, | ||
| 33 | hTemplateFile: ?HANDLE, | ||
| 34 | ) HANDLE; | ||
| 35 | |||
| 36 | pub extern "kernel32" stdcallcc fn CreatePipe( | ||
| 37 | hReadPipe: *HANDLE, | ||
| 38 | hWritePipe: *HANDLE, | ||
| 39 | lpPipeAttributes: *const SECURITY_ATTRIBUTES, | ||
| 40 | nSize: DWORD, | ||
| 41 | ) BOOL; | ||
| 42 | |||
| 43 | pub extern "kernel32" stdcallcc fn CreateProcessA( | ||
| 44 | lpApplicationName: ?LPCSTR, | ||
| 45 | lpCommandLine: LPSTR, | ||
| 46 | lpProcessAttributes: ?*SECURITY_ATTRIBUTES, | ||
| 47 | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, | ||
| 48 | bInheritHandles: BOOL, | ||
| 49 | dwCreationFlags: DWORD, | ||
| 50 | lpEnvironment: ?*c_void, | ||
| 51 | lpCurrentDirectory: ?LPCSTR, | ||
| 52 | lpStartupInfo: *STARTUPINFOA, | ||
| 53 | lpProcessInformation: *PROCESS_INFORMATION, | ||
| 54 | ) BOOL; | ||
| 55 | |||
| 56 | pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( | ||
| 57 | lpSymlinkFileName: LPCSTR, | ||
| 58 | lpTargetFileName: LPCSTR, | ||
| 59 | dwFlags: DWORD, | ||
| 60 | ) BOOLEAN; | ||
| 61 | |||
| 62 | pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; | ||
| 63 | |||
| 64 | pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; | ||
| 65 | |||
| 66 | pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; | ||
| 67 | |||
| 68 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; | ||
| 69 | |||
| 70 | pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE; | ||
| 71 | pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; | ||
| 72 | pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL; | ||
| 73 | |||
| 74 | pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; | ||
| 75 | |||
| 76 | pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; | ||
| 77 | |||
| 78 | pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; | ||
| 79 | |||
| 80 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; | ||
| 81 | |||
| 82 | pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; | ||
| 83 | |||
| 84 | pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; | ||
| 85 | |||
| 86 | pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL; | ||
| 87 | |||
| 88 | pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; | ||
| 89 | |||
| 90 | pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD; | ||
| 91 | |||
| 92 | pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; | ||
| 93 | |||
| 94 | pub extern "kernel32" stdcallcc fn GetLastError() DWORD; | ||
| 95 | |||
| 96 | pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( | ||
| 97 | in_hFile: HANDLE, | ||
| 98 | in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, | ||
| 99 | out_lpFileInformation: *c_void, | ||
| 100 | in_dwBufferSize: DWORD, | ||
| 101 | ) BOOL; | ||
| 102 | |||
| 103 | pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( | ||
| 104 | hFile: HANDLE, | ||
| 105 | lpszFilePath: LPSTR, | ||
| 106 | cchFilePath: DWORD, | ||
| 107 | dwFlags: DWORD, | ||
| 108 | ) DWORD; | ||
| 109 | |||
| 110 | pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; | ||
| 111 | pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL; | ||
| 112 | |||
| 113 | pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void; | ||
| 114 | pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; | ||
| 115 | |||
| 116 | pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; | ||
| 117 | pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; | ||
| 118 | pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; | ||
| 119 | pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; | ||
| 120 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; | ||
| 121 | pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; | ||
| 122 | pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; | ||
| 123 | |||
| 124 | pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; | ||
| 125 | |||
| 126 | pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; | ||
| 127 | |||
| 128 | pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; | ||
| 129 | |||
| 130 | pub extern "kernel32" stdcallcc fn MoveFileExA( | ||
| 131 | lpExistingFileName: LPCSTR, | ||
| 132 | lpNewFileName: LPCSTR, | ||
| 133 | dwFlags: DWORD, | ||
| 134 | ) BOOL; | ||
| 135 | |||
| 136 | pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL; | ||
| 137 | |||
| 138 | pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; | ||
| 139 | |||
| 140 | pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; | ||
| 141 | |||
| 142 | pub extern "kernel32" stdcallcc fn ReadFile( | ||
| 143 | in_hFile: HANDLE, | ||
| 144 | out_lpBuffer: *c_void, | ||
| 145 | in_nNumberOfBytesToRead: DWORD, | ||
| 146 | out_lpNumberOfBytesRead: *DWORD, | ||
| 147 | in_out_lpOverlapped: ?*OVERLAPPED, | ||
| 148 | ) BOOL; | ||
| 149 | |||
| 150 | pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL; | ||
| 151 | |||
| 152 | pub extern "kernel32" stdcallcc fn SetFilePointerEx( | ||
| 153 | in_fFile: HANDLE, | ||
| 154 | in_liDistanceToMove: LARGE_INTEGER, | ||
| 155 | out_opt_ldNewFilePointer: ?*LARGE_INTEGER, | ||
| 156 | in_dwMoveMethod: DWORD, | ||
| 157 | ) BOOL; | ||
| 158 | |||
| 159 | pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL; | ||
| 160 | |||
| 161 | pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void; | ||
| 162 | |||
| 163 | pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL; | ||
| 164 | |||
| 165 | pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; | ||
| 166 | |||
| 167 | pub extern "kernel32" stdcallcc fn WriteFile( | ||
| 168 | in_hFile: HANDLE, | ||
| 169 | in_lpBuffer: *const c_void, | ||
| 170 | in_nNumberOfBytesToWrite: DWORD, | ||
| 171 | out_lpNumberOfBytesWritten: ?*DWORD, | ||
| 172 | in_out_lpOverlapped: ?*OVERLAPPED, | ||
| 173 | ) BOOL; | ||
| 174 | |||
| 175 | //TODO: call unicode versions instead of relying on ANSI code page | ||
| 176 | pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; | ||
| 177 | |||
| 178 | pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; | ||
| 179 | |||
| 180 | pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; | ||
| 181 | |||
| 182 | pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; | ||
| 183 | |||
| 184 | pub const PROV_RSA_FULL = 1; | ||
| 185 | |||
| 186 | pub const BOOL = c_int; | 17 | pub const BOOL = c_int; |
| 187 | pub const BOOLEAN = BYTE; | 18 | pub const BOOLEAN = BYTE; |
| 188 | pub const BYTE = u8; | 19 | pub const BYTE = u8; |
| ... | @@ -204,6 +35,7 @@ pub const LPSTR = [*]CHAR; | ... | @@ -204,6 +35,7 @@ pub const LPSTR = [*]CHAR; |
| 204 | pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; | 35 | pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; |
| 205 | pub const LPVOID = *c_void; | 36 | pub const LPVOID = *c_void; |
| 206 | pub const LPWSTR = [*]WCHAR; | 37 | pub const LPWSTR = [*]WCHAR; |
| 38 | pub const LPCWSTR = [*]const WCHAR; | ||
| 207 | pub const PVOID = *c_void; | 39 | pub const PVOID = *c_void; |
| 208 | pub const PWSTR = [*]WCHAR; | 40 | pub const PWSTR = [*]WCHAR; |
| 209 | pub const SIZE_T = usize; | 41 | pub const SIZE_T = usize; |
| ... | @@ -439,3 +271,82 @@ pub const SYSTEM_INFO = extern struct { | ... | @@ -439,3 +271,82 @@ pub const SYSTEM_INFO = extern struct { |
| 439 | wProcessorLevel: WORD, | 271 | wProcessorLevel: WORD, |
| 440 | wProcessorRevision: WORD, | 272 | wProcessorRevision: WORD, |
| 441 | }; | 273 | }; |
| 274 | |||
| 275 | pub const HRESULT = c_long; | ||
| 276 | |||
| 277 | pub const KNOWNFOLDERID = GUID; | ||
| 278 | pub const GUID = extern struct { | ||
| 279 | Data1: c_ulong, | ||
| 280 | Data2: c_ushort, | ||
| 281 | Data3: c_ushort, | ||
| 282 | Data4: [8]u8, | ||
| 283 | |||
| 284 | pub fn parse(str: []const u8) GUID { | ||
| 285 | var guid: GUID = undefined; | ||
| 286 | var index: usize = 0; | ||
| 287 | assert(str[index] == '{'); | ||
| 288 | index += 1; | ||
| 289 | |||
| 290 | guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index..index + 8], 16) catch unreachable; | ||
| 291 | index += 8; | ||
| 292 | |||
| 293 | assert(str[index] == '-'); | ||
| 294 | index += 1; | ||
| 295 | |||
| 296 | guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable; | ||
| 297 | index += 4; | ||
| 298 | |||
| 299 | assert(str[index] == '-'); | ||
| 300 | index += 1; | ||
| 301 | |||
| 302 | guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable; | ||
| 303 | index += 4; | ||
| 304 | |||
| 305 | assert(str[index] == '-'); | ||
| 306 | index += 1; | ||
| 307 | |||
| 308 | guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable; | ||
| 309 | index += 2; | ||
| 310 | guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable; | ||
| 311 | index += 2; | ||
| 312 | |||
| 313 | assert(str[index] == '-'); | ||
| 314 | index += 1; | ||
| 315 | |||
| 316 | var i: usize = 2; | ||
| 317 | while (i < guid.Data4.len) : (i += 1) { | ||
| 318 | guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable; | ||
| 319 | index += 2; | ||
| 320 | } | ||
| 321 | |||
| 322 | assert(str[index] == '}'); | ||
| 323 | index += 1; | ||
| 324 | return guid; | ||
| 325 | } | ||
| 326 | }; | ||
| 327 | |||
| 328 | pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}"); | ||
| 329 | |||
| 330 | pub const KF_FLAG_DEFAULT = 0; | ||
| 331 | pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536; | ||
| 332 | pub const KF_FLAG_CREATE = 32768; | ||
| 333 | pub const KF_FLAG_DONT_VERIFY = 16384; | ||
| 334 | pub const KF_FLAG_DONT_UNEXPAND = 8192; | ||
| 335 | pub const KF_FLAG_NO_ALIAS = 4096; | ||
| 336 | pub const KF_FLAG_INIT = 2048; | ||
| 337 | pub const KF_FLAG_DEFAULT_PATH = 1024; | ||
| 338 | pub const KF_FLAG_NOT_PARENT_RELATIVE = 512; | ||
| 339 | pub const KF_FLAG_SIMPLE_IDLIST = 256; | ||
| 340 | pub const KF_FLAG_ALIAS_ONLY = -2147483648; | ||
| 341 | |||
| 342 | pub const S_OK = 0; | ||
| 343 | pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001)); | ||
| 344 | pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002)); | ||
| 345 | pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003)); | ||
| 346 | pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004)); | ||
| 347 | pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005)); | ||
| 348 | pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF)); | ||
| 349 | pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005)); | ||
| 350 | pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006)); | ||
| 351 | pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E)); | ||
| 352 | pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057)); |
std/os/windows/kernel32.zig created+162| ... | @@ -0,0 +1,162 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; | ||
| 4 | |||
| 5 | pub extern "kernel32" stdcallcc fn CreateDirectoryA( | ||
| 6 | lpPathName: LPCSTR, | ||
| 7 | lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, | ||
| 8 | ) BOOL; | ||
| 9 | |||
| 10 | pub extern "kernel32" stdcallcc fn CreateFileA( | ||
| 11 | lpFileName: LPCSTR, | ||
| 12 | dwDesiredAccess: DWORD, | ||
| 13 | dwShareMode: DWORD, | ||
| 14 | lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, | ||
| 15 | dwCreationDisposition: DWORD, | ||
| 16 | dwFlagsAndAttributes: DWORD, | ||
| 17 | hTemplateFile: ?HANDLE, | ||
| 18 | ) HANDLE; | ||
| 19 | |||
| 20 | pub extern "kernel32" stdcallcc fn CreatePipe( | ||
| 21 | hReadPipe: *HANDLE, | ||
| 22 | hWritePipe: *HANDLE, | ||
| 23 | lpPipeAttributes: *const SECURITY_ATTRIBUTES, | ||
| 24 | nSize: DWORD, | ||
| 25 | ) BOOL; | ||
| 26 | |||
| 27 | pub extern "kernel32" stdcallcc fn CreateProcessA( | ||
| 28 | lpApplicationName: ?LPCSTR, | ||
| 29 | lpCommandLine: LPSTR, | ||
| 30 | lpProcessAttributes: ?*SECURITY_ATTRIBUTES, | ||
| 31 | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, | ||
| 32 | bInheritHandles: BOOL, | ||
| 33 | dwCreationFlags: DWORD, | ||
| 34 | lpEnvironment: ?*c_void, | ||
| 35 | lpCurrentDirectory: ?LPCSTR, | ||
| 36 | lpStartupInfo: *STARTUPINFOA, | ||
| 37 | lpProcessInformation: *PROCESS_INFORMATION, | ||
| 38 | ) BOOL; | ||
| 39 | |||
| 40 | pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( | ||
| 41 | lpSymlinkFileName: LPCSTR, | ||
| 42 | lpTargetFileName: LPCSTR, | ||
| 43 | dwFlags: DWORD, | ||
| 44 | ) BOOLEAN; | ||
| 45 | |||
| 46 | pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; | ||
| 47 | |||
| 48 | pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; | ||
| 49 | |||
| 50 | pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; | ||
| 51 | |||
| 52 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; | ||
| 53 | |||
| 54 | pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE; | ||
| 55 | pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; | ||
| 56 | pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL; | ||
| 57 | |||
| 58 | pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; | ||
| 59 | |||
| 60 | pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; | ||
| 61 | |||
| 62 | pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; | ||
| 63 | |||
| 64 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; | ||
| 65 | |||
| 66 | pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; | ||
| 67 | |||
| 68 | pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; | ||
| 69 | |||
| 70 | pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL; | ||
| 71 | |||
| 72 | pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; | ||
| 73 | |||
| 74 | pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD; | ||
| 75 | |||
| 76 | pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; | ||
| 77 | |||
| 78 | pub extern "kernel32" stdcallcc fn GetLastError() DWORD; | ||
| 79 | |||
| 80 | pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( | ||
| 81 | in_hFile: HANDLE, | ||
| 82 | in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, | ||
| 83 | out_lpFileInformation: *c_void, | ||
| 84 | in_dwBufferSize: DWORD, | ||
| 85 | ) BOOL; | ||
| 86 | |||
| 87 | pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( | ||
| 88 | hFile: HANDLE, | ||
| 89 | lpszFilePath: LPSTR, | ||
| 90 | cchFilePath: DWORD, | ||
| 91 | dwFlags: DWORD, | ||
| 92 | ) DWORD; | ||
| 93 | |||
| 94 | pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; | ||
| 95 | pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL; | ||
| 96 | |||
| 97 | pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void; | ||
| 98 | pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; | ||
| 99 | |||
| 100 | pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; | ||
| 101 | pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; | ||
| 102 | pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; | ||
| 103 | pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; | ||
| 104 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; | ||
| 105 | pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; | ||
| 106 | pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; | ||
| 107 | |||
| 108 | pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; | ||
| 109 | |||
| 110 | pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; | ||
| 111 | |||
| 112 | pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; | ||
| 113 | |||
| 114 | pub extern "kernel32" stdcallcc fn MoveFileExA( | ||
| 115 | lpExistingFileName: LPCSTR, | ||
| 116 | lpNewFileName: LPCSTR, | ||
| 117 | dwFlags: DWORD, | ||
| 118 | ) BOOL; | ||
| 119 | |||
| 120 | pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL; | ||
| 121 | |||
| 122 | pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; | ||
| 123 | |||
| 124 | pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; | ||
| 125 | |||
| 126 | pub extern "kernel32" stdcallcc fn ReadFile( | ||
| 127 | in_hFile: HANDLE, | ||
| 128 | out_lpBuffer: *c_void, | ||
| 129 | in_nNumberOfBytesToRead: DWORD, | ||
| 130 | out_lpNumberOfBytesRead: *DWORD, | ||
| 131 | in_out_lpOverlapped: ?*OVERLAPPED, | ||
| 132 | ) BOOL; | ||
| 133 | |||
| 134 | pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL; | ||
| 135 | |||
| 136 | pub extern "kernel32" stdcallcc fn SetFilePointerEx( | ||
| 137 | in_fFile: HANDLE, | ||
| 138 | in_liDistanceToMove: LARGE_INTEGER, | ||
| 139 | out_opt_ldNewFilePointer: ?*LARGE_INTEGER, | ||
| 140 | in_dwMoveMethod: DWORD, | ||
| 141 | ) BOOL; | ||
| 142 | |||
| 143 | pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL; | ||
| 144 | |||
| 145 | pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void; | ||
| 146 | |||
| 147 | pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL; | ||
| 148 | |||
| 149 | pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; | ||
| 150 | |||
| 151 | pub extern "kernel32" stdcallcc fn WriteFile( | ||
| 152 | in_hFile: HANDLE, | ||
| 153 | in_lpBuffer: *const c_void, | ||
| 154 | in_nNumberOfBytesToWrite: DWORD, | ||
| 155 | out_lpNumberOfBytesWritten: ?*DWORD, | ||
| 156 | in_out_lpOverlapped: ?*OVERLAPPED, | ||
| 157 | ) BOOL; | ||
| 158 | |||
| 159 | //TODO: call unicode versions instead of relying on ANSI code page | ||
| 160 | pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; | ||
| 161 | |||
| 162 | pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; | ||
std/os/windows/ole32.zig created+18| ... | @@ -0,0 +1,18 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; | ||
| 4 | pub extern "ole32.dll" stdcallcc fn CoUninitialize() void; | ||
| 5 | pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD; | ||
| 6 | pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT; | ||
| 7 | |||
| 8 | |||
| 9 | pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED; | ||
| 10 | pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED; | ||
| 11 | pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE; | ||
| 12 | pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY; | ||
| 13 | pub const COINIT = extern enum { | ||
| 14 | COINIT_APARTMENTTHREADED = 2, | ||
| 15 | COINIT_MULTITHREADED = 0, | ||
| 16 | COINIT_DISABLE_OLE1DDE = 4, | ||
| 17 | COINIT_SPEED_OVER_MEMORY = 8, | ||
| 18 | }; | ||
std/os/windows/shell32.zig created+4| ... | @@ -0,0 +1,4 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; | ||
| 4 | |||
std/os/windows/shlwapi.zig created+4| ... | @@ -0,0 +1,4 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; | ||
| 4 | |||
std/os/windows/user32.zig created+4| ... | @@ -0,0 +1,4 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; | ||
| 4 | |||
std/special/test_runner.zig+17-3| ... | @@ -5,11 +5,25 @@ const test_fn_list = builtin.__zig_test_fn_slice; | ... | @@ -5,11 +5,25 @@ const test_fn_list = builtin.__zig_test_fn_slice; |
| 5 | const warn = std.debug.warn; | 5 | const warn = std.debug.warn; |
| 6 | 6 | ||
| 7 | pub fn main() !void { | 7 | pub fn main() !void { |
| 8 | var ok_count: usize = 0; | ||
| 9 | var skip_count: usize = 0; | ||
| 8 | for (test_fn_list) |test_fn, i| { | 10 | for (test_fn_list) |test_fn, i| { |
| 9 | warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name); | 11 | warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name); |
| 10 | 12 | ||
| 11 | try test_fn.func(); | 13 | if (test_fn.func()) |_| { |
| 12 | 14 | ok_count += 1; | |
| 13 | warn("OK\n"); | 15 | warn("OK\n"); |
| 16 | } else |err| switch (err) { | ||
| 17 | error.SkipZigTest => { | ||
| 18 | skip_count += 1; | ||
| 19 | warn("SKIP\n"); | ||
| 20 | }, | ||
| 21 | else => return err, | ||
| 22 | } | ||
| 23 | } | ||
| 24 | if (ok_count == test_fn_list.len) { | ||
| 25 | warn("All tests passed.\n"); | ||
| 26 | } else { | ||
| 27 | warn("{} passed; {} skipped.\n", ok_count, skip_count); | ||
| 14 | } | 28 | } |
| 15 | } | 29 | } |
std/unicode.zig+89| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | const std = @import("./index.zig"); | 1 | const std = @import("./index.zig"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const debug = std.debug; | 3 | const debug = std.debug; |
| 4 | const assert = std.debug.assert; | ||
| 5 | const mem = std.mem; | ||
| 3 | 6 | ||
| 4 | /// Returns how many bytes the UTF-8 representation would require | 7 | /// Returns how many bytes the UTF-8 representation would require |
| 5 | /// for the given codepoint. | 8 | /// for the given codepoint. |
| ... | @@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 { | ... | @@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 { |
| 441 | debug.assert(bytes.len == length); | 444 | debug.assert(bytes.len == length); |
| 442 | return utf8Decode(bytes); | 445 | return utf8Decode(bytes); |
| 443 | } | 446 | } |
| 447 | |||
| 448 | // TODO: make this API on top of a non-allocating Utf16LeView | ||
| 449 | pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 { | ||
| 450 | var result = std.ArrayList(u8).init(allocator); | ||
| 451 | // optimistically guess that it will all be ascii. | ||
| 452 | try result.ensureCapacity(utf16le.len); | ||
| 453 | |||
| 454 | const utf16le_as_bytes = @sliceToBytes(utf16le); | ||
| 455 | var i: usize = 0; | ||
| 456 | var out_index: usize = 0; | ||
| 457 | while (i < utf16le_as_bytes.len) : (i += 2) { | ||
| 458 | // decode | ||
| 459 | const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); | ||
| 460 | var codepoint: u32 = undefined; | ||
| 461 | if (c0 & ~u32(0x03ff) == 0xd800) { | ||
| 462 | // surrogate pair | ||
| 463 | i += 2; | ||
| 464 | if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf; | ||
| 465 | const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]); | ||
| 466 | if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; | ||
| 467 | codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); | ||
| 468 | } else if (c0 & ~u32(0x03ff) == 0xdc00) { | ||
| 469 | return error.UnexpectedSecondSurrogateHalf; | ||
| 470 | } else { | ||
| 471 | codepoint = c0; | ||
| 472 | } | ||
| 473 | |||
| 474 | // encode | ||
| 475 | const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable; | ||
| 476 | try result.resize(result.len + utf8_len); | ||
| 477 | _ = utf8Encode(codepoint, result.items[out_index..]) catch unreachable; | ||
| 478 | out_index += utf8_len; | ||
| 479 | } | ||
| 480 | |||
| 481 | return result.toOwnedSlice(); | ||
| 482 | } | ||
| 483 | |||
| 484 | test "utf16leToUtf8" { | ||
| 485 | var utf16le: [2]u16 = undefined; | ||
| 486 | const utf16le_as_bytes = @sliceToBytes(utf16le[0..]); | ||
| 487 | |||
| 488 | { | ||
| 489 | mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little); | ||
| 490 | mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little); | ||
| 491 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | ||
| 492 | assert(mem.eql(u8, utf8, "Aa")); | ||
| 493 | } | ||
| 494 | |||
| 495 | { | ||
| 496 | mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little); | ||
| 497 | mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little); | ||
| 498 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | ||
| 499 | assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf")); | ||
| 500 | } | ||
| 501 | |||
| 502 | { | ||
| 503 | // the values just outside the surrogate half range | ||
| 504 | mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little); | ||
| 505 | mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little); | ||
| 506 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | ||
| 507 | assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80")); | ||
| 508 | } | ||
| 509 | |||
| 510 | { | ||
| 511 | // smallest surrogate pair | ||
| 512 | mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little); | ||
| 513 | mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); | ||
| 514 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | ||
| 515 | assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80")); | ||
| 516 | } | ||
| 517 | |||
| 518 | { | ||
| 519 | // largest surrogate pair | ||
| 520 | mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); | ||
| 521 | mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little); | ||
| 522 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | ||
| 523 | assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf")); | ||
| 524 | } | ||
| 525 | |||
| 526 | { | ||
| 527 | mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little); | ||
| 528 | mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little); | ||
| 529 | const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le); | ||
| 530 | assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); | ||
| 531 | } | ||
| 532 | } |
std/zig/index.zig+3| ... | @@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig"); | ... | @@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig"); |
| 2 | pub const Token = tokenizer.Token; | 2 | pub const Token = tokenizer.Token; |
| 3 | pub const Tokenizer = tokenizer.Tokenizer; | 3 | pub const Tokenizer = tokenizer.Tokenizer; |
| 4 | pub const parse = @import("parse.zig").parse; | 4 | pub const parse = @import("parse.zig").parse; |
| 5 | pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral; | ||
| 5 | pub const render = @import("render.zig").render; | 6 | pub const render = @import("render.zig").render; |
| 6 | pub const ast = @import("ast.zig"); | 7 | pub const ast = @import("ast.zig"); |
| 7 | 8 | ||
| ... | @@ -10,4 +11,6 @@ test "std.zig tests" { | ... | @@ -10,4 +11,6 @@ test "std.zig tests" { |
| 10 | _ = @import("parse.zig"); | 11 | _ = @import("parse.zig"); |
| 11 | _ = @import("render.zig"); | 12 | _ = @import("render.zig"); |
| 12 | _ = @import("tokenizer.zig"); | 13 | _ = @import("tokenizer.zig"); |
| 14 | _ = @import("parse_string_literal.zig"); | ||
| 13 | } | 15 | } |
| 16 |
std/zig/parse.zig+1-1| ... | @@ -2356,7 +2356,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { | ... | @@ -2356,7 +2356,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { |
| 2356 | const token = nextToken(&tok_it, &tree); | 2356 | const token = nextToken(&tok_it, &tree); |
| 2357 | switch (token.ptr.id) { | 2357 | switch (token.ptr.id) { |
| 2358 | Token.Id.IntegerLiteral => { | 2358 | Token.Id.IntegerLiteral => { |
| 2359 | _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token.index); | 2359 | _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.IntegerLiteral, token.index); |
| 2360 | continue; | 2360 | continue; |
| 2361 | }, | 2361 | }, |
| 2362 | Token.Id.FloatLiteral => { | 2362 | Token.Id.FloatLiteral => { |
std/zig/parse_string_literal.zig created+76| ... | @@ -0,0 +1,76 @@ | ||
| 1 | const std = @import("../index.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | |||
| 4 | const State = enum { | ||
| 5 | Start, | ||
| 6 | Backslash, | ||
| 7 | }; | ||
| 8 | |||
| 9 | pub const ParseStringLiteralError = error{ | ||
| 10 | OutOfMemory, | ||
| 11 | |||
| 12 | /// When this is returned, index will be the position of the character. | ||
| 13 | InvalidCharacter, | ||
| 14 | }; | ||
| 15 | |||
| 16 | /// caller owns returned memory | ||
| 17 | pub fn parseStringLiteral( | ||
| 18 | allocator: *std.mem.Allocator, | ||
| 19 | bytes: []const u8, | ||
| 20 | bad_index: *usize, // populated if error.InvalidCharacter is returned | ||
| 21 | ) ParseStringLiteralError![]u8 { | ||
| 22 | const first_index = if (bytes[0] == 'c') usize(2) else usize(1); | ||
| 23 | assert(bytes[bytes.len - 1] == '"'); | ||
| 24 | |||
| 25 | var list = std.ArrayList(u8).init(allocator); | ||
| 26 | errdefer list.deinit(); | ||
| 27 | |||
| 28 | const slice = bytes[first_index..]; | ||
| 29 | try list.ensureCapacity(slice.len - 1); | ||
| 30 | |||
| 31 | var state = State.Start; | ||
| 32 | for (slice) |b, index| { | ||
| 33 | switch (state) { | ||
| 34 | State.Start => switch (b) { | ||
| 35 | '\\' => state = State.Backslash, | ||
| 36 | '\n' => { | ||
| 37 | bad_index.* = index; | ||
| 38 | return error.InvalidCharacter; | ||
| 39 | }, | ||
| 40 | '"' => return list.toOwnedSlice(), | ||
| 41 | else => try list.append(b), | ||
| 42 | }, | ||
| 43 | State.Backslash => switch (b) { | ||
| 44 | 'x' => @panic("TODO"), | ||
| 45 | 'u' => @panic("TODO"), | ||
| 46 | 'U' => @panic("TODO"), | ||
| 47 | 'n' => { | ||
| 48 | try list.append('\n'); | ||
| 49 | state = State.Start; | ||
| 50 | }, | ||
| 51 | 'r' => { | ||
| 52 | try list.append('\r'); | ||
| 53 | state = State.Start; | ||
| 54 | }, | ||
| 55 | '\\' => { | ||
| 56 | try list.append('\\'); | ||
| 57 | state = State.Start; | ||
| 58 | }, | ||
| 59 | 't' => { | ||
| 60 | try list.append('\t'); | ||
| 61 | state = State.Start; | ||
| 62 | }, | ||
| 63 | '"' => { | ||
| 64 | try list.append('"'); | ||
| 65 | state = State.Start; | ||
| 66 | }, | ||
| 67 | else => { | ||
| 68 | bad_index.* = index; | ||
| 69 | return error.InvalidCharacter; | ||
| 70 | }, | ||
| 71 | }, | ||
| 72 | else => unreachable, | ||
| 73 | } | ||
| 74 | } | ||
| 75 | unreachable; | ||
| 76 | } | ||
std/zig/tokenizer.zig+1| ... | @@ -73,6 +73,7 @@ pub const Token = struct { | ... | @@ -73,6 +73,7 @@ pub const Token = struct { |
| 73 | return null; | 73 | return null; |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | /// TODO remove this enum | ||
| 76 | const StrLitKind = enum { | 77 | const StrLitKind = enum { |
| 77 | Normal, | 78 | Normal, |
| 78 | C, | 79 | C, |
test/cases/cast.zig+17| ... | @@ -468,3 +468,20 @@ test "@intCast i32 to u7" { | ... | @@ -468,3 +468,20 @@ test "@intCast i32 to u7" { |
| 468 | var z = x >> @intCast(u7, y); | 468 | var z = x >> @intCast(u7, y); |
| 469 | assert(z == 0xff); | 469 | assert(z == 0xff); |
| 470 | } | 470 | } |
| 471 | |||
| 472 | test "implicit cast undefined to optional" { | ||
| 473 | assert(MakeType(void).getNull() == null); | ||
| 474 | assert(MakeType(void).getNonNull() != null); | ||
| 475 | } | ||
| 476 | |||
| 477 | fn MakeType(comptime T: type) type { | ||
| 478 | return struct { | ||
| 479 | fn getNull() ?T { | ||
| 480 | return null; | ||
| 481 | } | ||
| 482 | |||
| 483 | fn getNonNull() ?T { | ||
| 484 | return T(undefined); | ||
| 485 | } | ||
| 486 | }; | ||
| 487 | } |
test/cases/defer.zig+15| ... | @@ -61,3 +61,18 @@ test "defer and labeled break" { | ... | @@ -61,3 +61,18 @@ test "defer and labeled break" { |
| 61 | 61 | ||
| 62 | assert(i == 1); | 62 | assert(i == 1); |
| 63 | } | 63 | } |
| 64 | |||
| 65 | test "errdefer does not apply to fn inside fn" { | ||
| 66 | if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad); | ||
| 67 | } | ||
| 68 | |||
| 69 | fn testNestedFnErrDefer() error!void { | ||
| 70 | var a: i32 = 0; | ||
| 71 | errdefer a += 1; | ||
| 72 | const S = struct { | ||
| 73 | fn baz() error { | ||
| 74 | return error.Bad; | ||
| 75 | } | ||
| 76 | }; | ||
| 77 | return S.baz(); | ||
| 78 | } |
test/cases/eval.zig+10| ... | @@ -642,3 +642,13 @@ test "@tagName of @typeId" { | ... | @@ -642,3 +642,13 @@ test "@tagName of @typeId" { |
| 642 | const str = @tagName(@typeId(u8)); | 642 | const str = @tagName(@typeId(u8)); |
| 643 | assert(std.mem.eql(u8, str, "Int")); | 643 | assert(std.mem.eql(u8, str, "Int")); |
| 644 | } | 644 | } |
| 645 | |||
| 646 | test "setting backward branch quota just before a generic fn call" { | ||
| 647 | @setEvalBranchQuota(1001); | ||
| 648 | loopNTimes(1001); | ||
| 649 | } | ||
| 650 | |||
| 651 | fn loopNTimes(comptime n: usize) void { | ||
| 652 | comptime var i = 0; | ||
| 653 | inline while (i < n) : (i += 1) {} | ||
| 654 | } |
test/gen_h.zig+47| ... | @@ -76,4 +76,51 @@ pub fn addCases(cases: *tests.GenHContext) void { | ... | @@ -76,4 +76,51 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 76 | \\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]); | 76 | \\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]); |
| 77 | \\ | 77 | \\ |
| 78 | ); | 78 | ); |
| 79 | |||
| 80 | cases.add("ptr to zig struct", | ||
| 81 | \\const S = struct { | ||
| 82 | \\ a: u8, | ||
| 83 | \\}; | ||
| 84 | \\ | ||
| 85 | \\export fn a(s: *S) u8 { | ||
| 86 | \\ return s.a; | ||
| 87 | \\} | ||
| 88 | |||
| 89 | , | ||
| 90 | \\struct S; | ||
| 91 | \\TEST_EXPORT uint8_t a(struct S * s); | ||
| 92 | \\ | ||
| 93 | ); | ||
| 94 | |||
| 95 | cases.add("ptr to zig union", | ||
| 96 | \\const U = union(enum) { | ||
| 97 | \\ A: u8, | ||
| 98 | \\ B: u16, | ||
| 99 | \\}; | ||
| 100 | \\ | ||
| 101 | \\export fn a(s: *U) u8 { | ||
| 102 | \\ return s.A; | ||
| 103 | \\} | ||
| 104 | |||
| 105 | , | ||
| 106 | \\union U; | ||
| 107 | \\TEST_EXPORT uint8_t a(union U * s); | ||
| 108 | \\ | ||
| 109 | ); | ||
| 110 | |||
| 111 | cases.add("ptr to zig enum", | ||
| 112 | \\const E = enum(u8) { | ||
| 113 | \\ A, | ||
| 114 | \\ B, | ||
| 115 | \\}; | ||
| 116 | \\ | ||
| 117 | \\export fn a(s: *E) u8 { | ||
| 118 | \\ return @enumToInt(s.*); | ||
| 119 | \\} | ||
| 120 | |||
| 121 | , | ||
| 122 | \\enum E; | ||
| 123 | \\TEST_EXPORT uint8_t a(enum E * s); | ||
| 124 | \\ | ||
| 125 | ); | ||
| 79 | } | 126 | } |
test/stage2/compare_output.zig created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; | ||
| 3 | |||
| 4 | pub fn addCases(ctx: *TestContext) !void { | ||
| 5 | try ctx.testCompareOutputLibC( | ||
| 6 | \\extern fn puts([*]const u8) void; | ||
| 7 | \\export fn main() c_int { | ||
| 8 | \\ puts(c"Hello, world!"); | ||
| 9 | \\ return 0; | ||
| 10 | \\} | ||
| 11 | , "Hello, world!" ++ std.cstr.line_sep); | ||
| 12 | } | ||
test/stage2/compile_errors.zig+18| ... | @@ -9,4 +9,22 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -9,4 +9,22 @@ pub fn addCases(ctx: *TestContext) !void { |
| 9 | try ctx.testCompileError( | 9 | try ctx.testCompileError( |
| 10 | \\fn() void {} | 10 | \\fn() void {} |
| 11 | , "1.zig", 1, 1, "missing function name"); | 11 | , "1.zig", 1, 1, "missing function name"); |
| 12 | |||
| 13 | try ctx.testCompileError( | ||
| 14 | \\comptime { | ||
| 15 | \\ return; | ||
| 16 | \\} | ||
| 17 | , "1.zig", 2, 5, "return expression outside function definition"); | ||
| 18 | |||
| 19 | try ctx.testCompileError( | ||
| 20 | \\export fn entry() void { | ||
| 21 | \\ defer return; | ||
| 22 | \\} | ||
| 23 | , "1.zig", 2, 11, "cannot return from defer expression"); | ||
| 24 | |||
| 25 | try ctx.testCompileError( | ||
| 26 | \\export fn entry() c_int { | ||
| 27 | \\ return 36893488147419103232; | ||
| 28 | \\} | ||
| 29 | , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'"); | ||
| 12 | } | 30 | } |
test/tests.zig+4-7| ... | @@ -89,12 +89,13 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: | ... | @@ -89,12 +89,13 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: |
| 89 | return cases.step; | 89 | return cases.step; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { | 92 | pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { |
| 93 | const cases = b.allocator.create(BuildExamplesContext{ | 93 | const cases = b.allocator.create(BuildExamplesContext{ |
| 94 | .b = b, | 94 | .b = b, |
| 95 | .step = b.step("test-build-examples", "Build the examples"), | 95 | .step = b.step("test-build-examples", "Build the examples"), |
| 96 | .test_index = 0, | 96 | .test_index = 0, |
| 97 | .test_filter = test_filter, | 97 | .test_filter = test_filter, |
| 98 | .modes = modes, | ||
| 98 | }) catch unreachable; | 99 | }) catch unreachable; |
| 99 | 100 | ||
| 100 | build_examples.addCases(cases); | 101 | build_examples.addCases(cases); |
| ... | @@ -697,6 +698,7 @@ pub const BuildExamplesContext = struct { | ... | @@ -697,6 +698,7 @@ pub const BuildExamplesContext = struct { |
| 697 | step: *build.Step, | 698 | step: *build.Step, |
| 698 | test_index: usize, | 699 | test_index: usize, |
| 699 | test_filter: ?[]const u8, | 700 | test_filter: ?[]const u8, |
| 701 | modes: []const Mode, | ||
| 700 | 702 | ||
| 701 | pub fn addC(self: *BuildExamplesContext, root_src: []const u8) void { | 703 | pub fn addC(self: *BuildExamplesContext, root_src: []const u8) void { |
| 702 | self.addAllArgs(root_src, true); | 704 | self.addAllArgs(root_src, true); |
| ... | @@ -739,12 +741,7 @@ pub const BuildExamplesContext = struct { | ... | @@ -739,12 +741,7 @@ pub const BuildExamplesContext = struct { |
| 739 | pub fn addAllArgs(self: *BuildExamplesContext, root_src: []const u8, link_libc: bool) void { | 741 | pub fn addAllArgs(self: *BuildExamplesContext, root_src: []const u8, link_libc: bool) void { |
| 740 | const b = self.b; | 742 | const b = self.b; |
| 741 | 743 | ||
| 742 | for ([]Mode{ | 744 | for (self.modes) |mode| { |
| 743 | Mode.Debug, | ||
| 744 | Mode.ReleaseSafe, | ||
| 745 | Mode.ReleaseFast, | ||
| 746 | Mode.ReleaseSmall, | ||
| 747 | }) |mode| { | ||
| 748 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, @tagName(mode)) catch unreachable; | 745 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, @tagName(mode)) catch unreachable; |
| 749 | if (self.test_filter) |filter| { | 746 | if (self.test_filter) |filter| { |
| 750 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; | 747 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; |