| author | |
| committer | |
| log | a8e964eadd3496330043985cacaaee7db92886c6 |
| tree | 57768a2f87b76b9f80231ab924d2bdb481a22d2a |
| parent | ba71b96fe6c0e01b8445a2f7bd49541a07c360db |
Frontend improvements:
* When compiling in `zig test` mode, put a task on the work queue to
analyze the main package root file. Normally, start code does
`_ = import("root");` to make Zig analyze the user's code, however in
the case of `zig test`, the root source file is the test runner.
Without this change, no tests are picked up.
* In the main pipeline, once semantic analysis is finished, if there
are no compile errors, populate the `test_functions` Decl with the
set of test functions picked up from semantic analysis.
* Value: add `array` and `slice` Tags.
LLVM backend improvements:
* Fix incremental updates of globals. Previously the
value of a global would not get replaced with a new value.
* Fix LLVM type of arrays. They were incorrectly sending
the ABI size as the element count.
* Remove the FuncGen parameter from genTypedValue. This function is for
generating global constants and there is no function available when
it is being called.
- The `ref_val` case is now commented out. I'd like to eliminate
`ref_val` as one of the possible Value Tags. Instead it should
always be done via `decl_ref`.
* Implement constant value generation for slices, arrays, and structs.
* Constant value generation for functions supports the `decl_ref` tag.6 files changed, 332 insertions(+), 82 deletions(-)
src/Compilation.zig+15-19| ... | @@ -1709,7 +1709,9 @@ pub fn update(self: *Compilation) !void { | ... | @@ -1709,7 +1709,9 @@ pub fn update(self: *Compilation) !void { |
| 1709 | // in the start code, but when using the stage1 backend that won't happen, | 1709 | // in the start code, but when using the stage1 backend that won't happen, |
| 1710 | // so in order to run AstGen on the root source file we put it into the | 1710 | // so in order to run AstGen on the root source file we put it into the |
| 1711 | // import_table here. | 1711 | // import_table here. |
| 1712 | if (use_stage1) { | 1712 | // Likewise, in the case of `zig test`, the test runner is the root source file, |
| 1713 | // and so there is nothing to import the main file. | ||
| 1714 | if (use_stage1 or self.bin_file.options.is_test) { | ||
| 1713 | _ = try module.importPkg(module.main_pkg); | 1715 | _ = try module.importPkg(module.main_pkg); |
| 1714 | } | 1716 | } |
| 1715 | 1717 | ||
| ... | @@ -1725,6 +1727,9 @@ pub fn update(self: *Compilation) !void { | ... | @@ -1725,6 +1727,9 @@ pub fn update(self: *Compilation) !void { |
| 1725 | 1727 | ||
| 1726 | if (!use_stage1) { | 1728 | if (!use_stage1) { |
| 1727 | try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg }); | 1729 | try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg }); |
| 1730 | if (self.bin_file.options.is_test) { | ||
| 1731 | try self.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg }); | ||
| 1732 | } | ||
| 1728 | } | 1733 | } |
| 1729 | } | 1734 | } |
| 1730 | 1735 | ||
| ... | @@ -2053,24 +2058,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor | ... | @@ -2053,24 +2058,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 2053 | assert(decl.has_tv); | 2058 | assert(decl.has_tv); |
| 2054 | assert(decl.ty.hasCodeGenBits()); | 2059 | assert(decl.ty.hasCodeGenBits()); |
| 2055 | 2060 | ||
| 2056 | self.bin_file.updateDecl(module, decl) catch |err| switch (err) { | 2061 | try module.linkerUpdateDecl(decl); |
| 2057 | error.OutOfMemory => return error.OutOfMemory, | ||
| 2058 | error.AnalysisFail => { | ||
| 2059 | decl.analysis = .codegen_failure; | ||
| 2060 | continue; | ||
| 2061 | }, | ||
| 2062 | else => { | ||
| 2063 | try module.failed_decls.ensureUnusedCapacity(gpa, 1); | ||
| 2064 | module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create( | ||
| 2065 | gpa, | ||
| 2066 | decl.srcLoc(), | ||
| 2067 | "unable to codegen: {s}", | ||
| 2068 | .{@errorName(err)}, | ||
| 2069 | )); | ||
| 2070 | decl.analysis = .codegen_failure_retryable; | ||
| 2071 | continue; | ||
| 2072 | }, | ||
| 2073 | }; | ||
| 2074 | }, | 2062 | }, |
| 2075 | }, | 2063 | }, |
| 2076 | .codegen_func => |func| switch (func.owner_decl.analysis) { | 2064 | .codegen_func => |func| switch (func.owner_decl.analysis) { |
| ... | @@ -2396,6 +2384,14 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor | ... | @@ -2396,6 +2384,14 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 2396 | }; | 2384 | }; |
| 2397 | }, | 2385 | }, |
| 2398 | }; | 2386 | }; |
| 2387 | |||
| 2388 | if (self.bin_file.options.is_test and self.totalErrorCount() == 0) { | ||
| 2389 | // The `test_functions` decl has been intentionally postponed until now, | ||
| 2390 | // at which point we must populate it with the list of test functions that | ||
| 2391 | // have been discovered and not filtered out. | ||
| 2392 | const mod = self.bin_file.options.module.?; | ||
| 2393 | try mod.populateTestFunctions(); | ||
| 2394 | } | ||
| 2399 | } | 2395 | } |
| 2400 | 2396 | ||
| 2401 | const AstGenSrc = union(enum) { | 2397 | const AstGenSrc = union(enum) { |
src/Module.zig+122-13| ... | @@ -112,6 +112,8 @@ compile_log_text: ArrayListUnmanaged(u8) = .{}, | ... | @@ -112,6 +112,8 @@ compile_log_text: ArrayListUnmanaged(u8) = .{}, |
| 112 | 112 | ||
| 113 | emit_h: ?*GlobalEmitH, | 113 | emit_h: ?*GlobalEmitH, |
| 114 | 114 | ||
| 115 | test_functions: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, | ||
| 116 | |||
| 115 | /// A `Module` has zero or one of these depending on whether `-femit-h` is enabled. | 117 | /// A `Module` has zero or one of these depending on whether `-femit-h` is enabled. |
| 116 | pub const GlobalEmitH = struct { | 118 | pub const GlobalEmitH = struct { |
| 117 | /// Where to put the output. | 119 | /// Where to put the output. |
| ... | @@ -282,6 +284,7 @@ pub const Decl = struct { | ... | @@ -282,6 +284,7 @@ pub const Decl = struct { |
| 282 | pub fn destroy(decl: *Decl, module: *Module) void { | 284 | pub fn destroy(decl: *Decl, module: *Module) void { |
| 283 | const gpa = module.gpa; | 285 | const gpa = module.gpa; |
| 284 | log.debug("destroy {*} ({s})", .{ decl, decl.name }); | 286 | log.debug("destroy {*} ({s})", .{ decl, decl.name }); |
| 287 | _ = module.test_functions.swapRemove(decl); | ||
| 285 | if (decl.deletion_flag) { | 288 | if (decl.deletion_flag) { |
| 286 | assert(module.deletion_set.swapRemove(decl)); | 289 | assert(module.deletion_set.swapRemove(decl)); |
| 287 | } | 290 | } |
| ... | @@ -3319,6 +3322,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi | ... | @@ -3319,6 +3322,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi |
| 3319 | // the test name filter. | 3322 | // the test name filter. |
| 3320 | if (!mod.comp.bin_file.options.is_test) break :blk false; | 3323 | if (!mod.comp.bin_file.options.is_test) break :blk false; |
| 3321 | if (decl_pkg != mod.main_pkg) break :blk false; | 3324 | if (decl_pkg != mod.main_pkg) break :blk false; |
| 3325 | try mod.test_functions.put(gpa, new_decl, {}); | ||
| 3322 | break :blk true; | 3326 | break :blk true; |
| 3323 | }, | 3327 | }, |
| 3324 | else => blk: { | 3328 | else => blk: { |
| ... | @@ -3326,6 +3330,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi | ... | @@ -3326,6 +3330,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi |
| 3326 | if (!mod.comp.bin_file.options.is_test) break :blk false; | 3330 | if (!mod.comp.bin_file.options.is_test) break :blk false; |
| 3327 | if (decl_pkg != mod.main_pkg) break :blk false; | 3331 | if (decl_pkg != mod.main_pkg) break :blk false; |
| 3328 | // TODO check the name against --test-filter | 3332 | // TODO check the name against --test-filter |
| 3333 | try mod.test_functions.put(gpa, new_decl, {}); | ||
| 3329 | break :blk true; | 3334 | break :blk true; |
| 3330 | }, | 3335 | }, |
| 3331 | }; | 3336 | }; |
| ... | @@ -3765,17 +3770,38 @@ pub fn createAnonymousDeclNamed( | ... | @@ -3765,17 +3770,38 @@ pub fn createAnonymousDeclNamed( |
| 3765 | scope: *Scope, | 3770 | scope: *Scope, |
| 3766 | typed_value: TypedValue, | 3771 | typed_value: TypedValue, |
| 3767 | name: [:0]u8, | 3772 | name: [:0]u8, |
| 3773 | ) !*Decl { | ||
| 3774 | return mod.createAnonymousDeclFromDeclNamed(scope.ownerDecl().?, typed_value, name); | ||
| 3775 | } | ||
| 3776 | |||
| 3777 | pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl { | ||
| 3778 | return mod.createAnonymousDeclFromDecl(scope.ownerDecl().?, typed_value); | ||
| 3779 | } | ||
| 3780 | |||
| 3781 | pub fn createAnonymousDeclFromDecl(mod: *Module, owner_decl: *Decl, tv: TypedValue) !*Decl { | ||
| 3782 | const name_index = mod.getNextAnonNameIndex(); | ||
| 3783 | const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ | ||
| 3784 | owner_decl.name, name_index, | ||
| 3785 | }); | ||
| 3786 | return mod.createAnonymousDeclFromDeclNamed(owner_decl, tv, name); | ||
| 3787 | } | ||
| 3788 | |||
| 3789 | /// Takes ownership of `name` even if it returns an error. | ||
| 3790 | pub fn createAnonymousDeclFromDeclNamed( | ||
| 3791 | mod: *Module, | ||
| 3792 | owner_decl: *Decl, | ||
| 3793 | typed_value: TypedValue, | ||
| 3794 | name: [:0]u8, | ||
| 3768 | ) !*Decl { | 3795 | ) !*Decl { |
| 3769 | errdefer mod.gpa.free(name); | 3796 | errdefer mod.gpa.free(name); |
| 3770 | 3797 | ||
| 3771 | const scope_decl = scope.ownerDecl().?; | 3798 | const namespace = owner_decl.namespace; |
| 3772 | const namespace = scope_decl.namespace; | ||
| 3773 | try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1); | 3799 | try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1); |
| 3774 | 3800 | ||
| 3775 | const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node); | 3801 | const new_decl = try mod.allocateNewDecl(namespace, owner_decl.src_node); |
| 3776 | 3802 | ||
| 3777 | new_decl.name = name; | 3803 | new_decl.name = name; |
| 3778 | new_decl.src_line = scope_decl.src_line; | 3804 | new_decl.src_line = owner_decl.src_line; |
| 3779 | new_decl.ty = typed_value.ty; | 3805 | new_decl.ty = typed_value.ty; |
| 3780 | new_decl.val = typed_value.val; | 3806 | new_decl.val = typed_value.val; |
| 3781 | new_decl.has_tv = true; | 3807 | new_decl.has_tv = true; |
| ... | @@ -3796,15 +3822,6 @@ pub fn createAnonymousDeclNamed( | ... | @@ -3796,15 +3822,6 @@ pub fn createAnonymousDeclNamed( |
| 3796 | return new_decl; | 3822 | return new_decl; |
| 3797 | } | 3823 | } |
| 3798 | 3824 | ||
| 3799 | pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl { | ||
| 3800 | const scope_decl = scope.ownerDecl().?; | ||
| 3801 | const name_index = mod.getNextAnonNameIndex(); | ||
| 3802 | const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ | ||
| 3803 | scope_decl.name, name_index, | ||
| 3804 | }); | ||
| 3805 | return mod.createAnonymousDeclNamed(scope, typed_value, name); | ||
| 3806 | } | ||
| 3807 | |||
| 3808 | pub fn getNextAnonNameIndex(mod: *Module) usize { | 3825 | pub fn getNextAnonNameIndex(mod: *Module) usize { |
| 3809 | return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); | 3826 | return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); |
| 3810 | } | 3827 | } |
| ... | @@ -4801,3 +4818,95 @@ pub fn processExports(mod: *Module) !void { | ... | @@ -4801,3 +4818,95 @@ pub fn processExports(mod: *Module) !void { |
| 4801 | }; | 4818 | }; |
| 4802 | } | 4819 | } |
| 4803 | } | 4820 | } |
| 4821 | |||
| 4822 | pub fn populateTestFunctions(mod: *Module) !void { | ||
| 4823 | const gpa = mod.gpa; | ||
| 4824 | const builtin_pkg = mod.main_pkg.table.get("builtin").?; | ||
| 4825 | const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file; | ||
| 4826 | const builtin_namespace = builtin_file.root_decl.?.namespace; | ||
| 4827 | const decl = builtin_namespace.decls.get("test_functions").?; | ||
| 4828 | var buf: Type.Payload.ElemType = undefined; | ||
| 4829 | const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType(); | ||
| 4830 | |||
| 4831 | const array_decl = d: { | ||
| 4832 | // Add mod.test_functions to an array decl then make the test_functions | ||
| 4833 | // decl reference it as a slice. | ||
| 4834 | var new_decl_arena = std.heap.ArenaAllocator.init(gpa); | ||
| 4835 | errdefer new_decl_arena.deinit(); | ||
| 4836 | const arena = &new_decl_arena.allocator; | ||
| 4837 | |||
| 4838 | const test_fn_vals = try arena.alloc(Value, mod.test_functions.count()); | ||
| 4839 | const array_decl = try mod.createAnonymousDeclFromDecl(decl, .{ | ||
| 4840 | .ty = try Type.Tag.array.create(arena, .{ | ||
| 4841 | .len = test_fn_vals.len, | ||
| 4842 | .elem_type = try tmp_test_fn_ty.copy(arena), | ||
| 4843 | }), | ||
| 4844 | .val = try Value.Tag.array.create(arena, test_fn_vals), | ||
| 4845 | }); | ||
| 4846 | for (mod.test_functions.keys()) |test_decl, i| { | ||
| 4847 | const test_name_slice = mem.sliceTo(test_decl.name, 0); | ||
| 4848 | const test_name_decl = n: { | ||
| 4849 | var name_decl_arena = std.heap.ArenaAllocator.init(gpa); | ||
| 4850 | errdefer name_decl_arena.deinit(); | ||
| 4851 | const bytes = try name_decl_arena.allocator.dupe(u8, test_name_slice); | ||
| 4852 | const test_name_decl = try mod.createAnonymousDeclFromDecl(array_decl, .{ | ||
| 4853 | .ty = try Type.Tag.array_u8.create(&name_decl_arena.allocator, bytes.len), | ||
| 4854 | .val = try Value.Tag.bytes.create(&name_decl_arena.allocator, bytes), | ||
| 4855 | }); | ||
| 4856 | try test_name_decl.finalizeNewArena(&name_decl_arena); | ||
| 4857 | break :n test_name_decl; | ||
| 4858 | }; | ||
| 4859 | try mod.linkerUpdateDecl(test_name_decl); | ||
| 4860 | |||
| 4861 | const field_vals = try arena.create([3]Value); | ||
| 4862 | field_vals.* = .{ | ||
| 4863 | try Value.Tag.slice.create(arena, .{ | ||
| 4864 | .ptr = try Value.Tag.decl_ref.create(arena, test_name_decl), | ||
| 4865 | .len = try Value.Tag.int_u64.create(arena, test_name_slice.len), | ||
| 4866 | }), // name | ||
| 4867 | try Value.Tag.decl_ref.create(arena, test_decl), // func | ||
| 4868 | Value.initTag(.null_value), // async_frame_size | ||
| 4869 | }; | ||
| 4870 | test_fn_vals[i] = try Value.Tag.@"struct".create(arena, field_vals); | ||
| 4871 | } | ||
| 4872 | |||
| 4873 | try array_decl.finalizeNewArena(&new_decl_arena); | ||
| 4874 | break :d array_decl; | ||
| 4875 | }; | ||
| 4876 | try mod.linkerUpdateDecl(array_decl); | ||
| 4877 | |||
| 4878 | { | ||
| 4879 | var arena_instance = decl.value_arena.?.promote(gpa); | ||
| 4880 | defer decl.value_arena.?.* = arena_instance.state; | ||
| 4881 | const arena = &arena_instance.allocator; | ||
| 4882 | |||
| 4883 | decl.ty = try Type.Tag.const_slice.create(arena, try tmp_test_fn_ty.copy(arena)); | ||
| 4884 | decl.val = try Value.Tag.slice.create(arena, .{ | ||
| 4885 | .ptr = try Value.Tag.decl_ref.create(arena, array_decl), | ||
| 4886 | .len = try Value.Tag.int_u64.create(arena, mod.test_functions.count()), | ||
| 4887 | }); | ||
| 4888 | } | ||
| 4889 | try mod.linkerUpdateDecl(decl); | ||
| 4890 | } | ||
| 4891 | |||
| 4892 | pub fn linkerUpdateDecl(mod: *Module, decl: *Decl) !void { | ||
| 4893 | mod.comp.bin_file.updateDecl(mod, decl) catch |err| switch (err) { | ||
| 4894 | error.OutOfMemory => return error.OutOfMemory, | ||
| 4895 | error.AnalysisFail => { | ||
| 4896 | decl.analysis = .codegen_failure; | ||
| 4897 | return; | ||
| 4898 | }, | ||
| 4899 | else => { | ||
| 4900 | const gpa = mod.gpa; | ||
| 4901 | try mod.failed_decls.ensureUnusedCapacity(gpa, 1); | ||
| 4902 | mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | ||
| 4903 | gpa, | ||
| 4904 | decl.srcLoc(), | ||
| 4905 | "unable to codegen: {s}", | ||
| 4906 | .{@errorName(err)}, | ||
| 4907 | )); | ||
| 4908 | decl.analysis = .codegen_failure_retryable; | ||
| 4909 | return; | ||
| 4910 | }, | ||
| 4911 | }; | ||
| 4912 | } |
src/codegen/llvm.zig+91-43| ... | @@ -500,7 +500,18 @@ pub const DeclGen = struct { | ... | @@ -500,7 +500,18 @@ pub const DeclGen = struct { |
| 500 | } else if (decl.val.castTag(.extern_fn)) |extern_fn| { | 500 | } else if (decl.val.castTag(.extern_fn)) |extern_fn| { |
| 501 | _ = try self.resolveLlvmFunction(extern_fn.data); | 501 | _ = try self.resolveLlvmFunction(extern_fn.data); |
| 502 | } else { | 502 | } else { |
| 503 | _ = try self.resolveGlobalDecl(decl); | 503 | const global = try self.resolveGlobalDecl(decl); |
| 504 | assert(decl.has_tv); | ||
| 505 | const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { | ||
| 506 | const variable = payload.data; | ||
| 507 | break :init_val variable.init; | ||
| 508 | } else init_val: { | ||
| 509 | global.setGlobalConstant(.True); | ||
| 510 | break :init_val decl.val; | ||
| 511 | }; | ||
| 512 | |||
| 513 | const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val }); | ||
| 514 | llvm.setInitializer(global, llvm_init); | ||
| 504 | } | 515 | } |
| 505 | } | 516 | } |
| 506 | 517 | ||
| ... | @@ -548,25 +559,11 @@ pub const DeclGen = struct { | ... | @@ -548,25 +559,11 @@ pub const DeclGen = struct { |
| 548 | } | 559 | } |
| 549 | 560 | ||
| 550 | fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value { | 561 | fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value { |
| 551 | if (self.llvmModule().getNamedGlobal(decl.name)) |val| return val; | 562 | const llvm_module = self.object.llvm_module; |
| 552 | 563 | if (llvm_module.getNamedGlobal(decl.name)) |val| return val; | |
| 553 | assert(decl.has_tv); | ||
| 554 | |||
| 555 | // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`. | 564 | // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`. |
| 556 | const llvm_type = try self.llvmType(decl.ty); | 565 | const llvm_type = try self.llvmType(decl.ty); |
| 557 | const global = self.llvmModule().addGlobal(llvm_type, decl.name); | 566 | return llvm_module.addGlobal(llvm_type, decl.name); |
| 558 | const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { | ||
| 559 | const variable = payload.data; | ||
| 560 | break :init_val variable.init; | ||
| 561 | } else init_val: { | ||
| 562 | global.setGlobalConstant(.True); | ||
| 563 | break :init_val decl.val; | ||
| 564 | }; | ||
| 565 | |||
| 566 | const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val }, null); | ||
| 567 | llvm.setInitializer(global, llvm_init); | ||
| 568 | |||
| 569 | return global; | ||
| 570 | } | 567 | } |
| 571 | 568 | ||
| 572 | fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type { | 569 | fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type { |
| ... | @@ -596,7 +593,8 @@ pub const DeclGen = struct { | ... | @@ -596,7 +593,8 @@ pub const DeclGen = struct { |
| 596 | }, | 593 | }, |
| 597 | .Array => { | 594 | .Array => { |
| 598 | const elem_type = try self.llvmType(t.elemType()); | 595 | const elem_type = try self.llvmType(t.elemType()); |
| 599 | return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); | 596 | const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null); |
| 597 | return elem_type.arrayType(@intCast(c_uint, total_len)); | ||
| 600 | }, | 598 | }, |
| 601 | .Optional => { | 599 | .Optional => { |
| 602 | if (!t.isPtrLikeOptional()) { | 600 | if (!t.isPtrLikeOptional()) { |
| ... | @@ -674,8 +672,7 @@ pub const DeclGen = struct { | ... | @@ -674,8 +672,7 @@ pub const DeclGen = struct { |
| 674 | } | 672 | } |
| 675 | } | 673 | } |
| 676 | 674 | ||
| 677 | // TODO: figure out a way to remove the FuncGen argument | 675 | fn genTypedValue(self: *DeclGen, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value { |
| 678 | fn genTypedValue(self: *DeclGen, tv: TypedValue, fg: ?*FuncGen) error{ OutOfMemory, CodegenFail }!*const llvm.Value { | ||
| 679 | const llvm_type = try self.llvmType(tv.ty); | 676 | const llvm_type = try self.llvmType(tv.ty); |
| 680 | 677 | ||
| 681 | if (tv.val.isUndef()) | 678 | if (tv.val.isUndef()) |
| ... | @@ -711,20 +708,36 @@ pub const DeclGen = struct { | ... | @@ -711,20 +708,36 @@ pub const DeclGen = struct { |
| 711 | usize_type.constNull(), | 708 | usize_type.constNull(), |
| 712 | }; | 709 | }; |
| 713 | 710 | ||
| 714 | // TODO: consider using buildInBoundsGEP2 for opaque pointers | 711 | return val.constInBoundsGEP(&indices, indices.len); |
| 715 | return fg.?.builder.buildInBoundsGEP(val, &indices, 2, ""); | ||
| 716 | }, | 712 | }, |
| 717 | .ref_val => { | 713 | .ref_val => { |
| 718 | const elem_value = tv.val.castTag(.ref_val).?.data; | 714 | //const elem_value = tv.val.castTag(.ref_val).?.data; |
| 719 | const elem_type = tv.ty.castPointer().?.data; | 715 | //const elem_type = tv.ty.castPointer().?.data; |
| 720 | const alloca = fg.?.buildAlloca(try self.llvmType(elem_type)); | 716 | //const alloca = fg.?.buildAlloca(try self.llvmType(elem_type)); |
| 721 | _ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca); | 717 | //_ = fg.?.builder.buildStore(try self.genTypedValue(.{ .ty = elem_type, .val = elem_value }, fg), alloca); |
| 722 | return alloca; | 718 | //return alloca; |
| 719 | // TODO eliminate the ref_val Value Tag | ||
| 720 | return self.todo("implement const of pointer tag ref_val", .{}); | ||
| 723 | }, | 721 | }, |
| 724 | .variable => { | 722 | .variable => { |
| 725 | const variable = tv.val.castTag(.variable).?.data; | 723 | const variable = tv.val.castTag(.variable).?.data; |
| 726 | return self.resolveGlobalDecl(variable.owner_decl); | 724 | return self.resolveGlobalDecl(variable.owner_decl); |
| 727 | }, | 725 | }, |
| 726 | .slice => { | ||
| 727 | const slice = tv.val.castTag(.slice).?.data; | ||
| 728 | var buf: Type.Payload.ElemType = undefined; | ||
| 729 | const fields: [2]*const llvm.Value = .{ | ||
| 730 | try self.genTypedValue(.{ | ||
| 731 | .ty = tv.ty.slicePtrFieldType(&buf), | ||
| 732 | .val = slice.ptr, | ||
| 733 | }), | ||
| 734 | try self.genTypedValue(.{ | ||
| 735 | .ty = Type.initTag(.usize), | ||
| 736 | .val = slice.len, | ||
| 737 | }), | ||
| 738 | }; | ||
| 739 | return self.context.constStruct(&fields, fields.len, .False); | ||
| 740 | }, | ||
| 728 | else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), | 741 | else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), |
| 729 | }, | 742 | }, |
| 730 | .Array => { | 743 | .Array => { |
| ... | @@ -734,10 +747,28 @@ pub const DeclGen = struct { | ... | @@ -734,10 +747,28 @@ pub const DeclGen = struct { |
| 734 | return self.todo("handle other sentinel values", .{}); | 747 | return self.todo("handle other sentinel values", .{}); |
| 735 | } else false; | 748 | } else false; |
| 736 | 749 | ||
| 737 | return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel)); | 750 | return self.context.constString( |
| 738 | } else { | 751 | payload.data.ptr, |
| 739 | return self.todo("handle more array values", .{}); | 752 | @intCast(c_uint, payload.data.len), |
| 753 | llvm.Bool.fromBool(!zero_sentinel), | ||
| 754 | ); | ||
| 755 | } | ||
| 756 | if (tv.val.castTag(.array)) |payload| { | ||
| 757 | const gpa = self.gpa; | ||
| 758 | const elem_ty = tv.ty.elemType(); | ||
| 759 | const elem_vals = payload.data; | ||
| 760 | const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len); | ||
| 761 | defer gpa.free(llvm_elems); | ||
| 762 | for (elem_vals) |elem_val, i| { | ||
| 763 | llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_val }); | ||
| 764 | } | ||
| 765 | const llvm_elem_ty = try self.llvmType(elem_ty); | ||
| 766 | return llvm_elem_ty.constArray( | ||
| 767 | llvm_elems.ptr, | ||
| 768 | @intCast(c_uint, llvm_elems.len), | ||
| 769 | ); | ||
| 740 | } | 770 | } |
| 771 | return self.todo("handle more array values", .{}); | ||
| 741 | }, | 772 | }, |
| 742 | .Optional => { | 773 | .Optional => { |
| 743 | if (!tv.ty.isPtrLikeOptional()) { | 774 | if (!tv.ty.isPtrLikeOptional()) { |
| ... | @@ -750,26 +781,25 @@ pub const DeclGen = struct { | ... | @@ -750,26 +781,25 @@ pub const DeclGen = struct { |
| 750 | llvm_child_type.constNull(), | 781 | llvm_child_type.constNull(), |
| 751 | self.context.intType(1).constNull(), | 782 | self.context.intType(1).constNull(), |
| 752 | }; | 783 | }; |
| 753 | return self.context.constStruct(&optional_values, 2, .False); | 784 | return self.context.constStruct(&optional_values, optional_values.len, .False); |
| 754 | } else { | 785 | } else { |
| 755 | var optional_values: [2]*const llvm.Value = .{ | 786 | var optional_values: [2]*const llvm.Value = .{ |
| 756 | try self.genTypedValue(.{ .ty = child_type, .val = tv.val }, fg), | 787 | try self.genTypedValue(.{ .ty = child_type, .val = tv.val }), |
| 757 | self.context.intType(1).constAllOnes(), | 788 | self.context.intType(1).constAllOnes(), |
| 758 | }; | 789 | }; |
| 759 | return self.context.constStruct(&optional_values, 2, .False); | 790 | return self.context.constStruct(&optional_values, optional_values.len, .False); |
| 760 | } | 791 | } |
| 761 | } else { | 792 | } else { |
| 762 | return self.todo("implement const of optional pointer", .{}); | 793 | return self.todo("implement const of optional pointer", .{}); |
| 763 | } | 794 | } |
| 764 | }, | 795 | }, |
| 765 | .Fn => { | 796 | .Fn => { |
| 766 | const fn_decl = if (tv.val.castTag(.extern_fn)) |extern_fn| | 797 | const fn_decl = switch (tv.val.tag()) { |
| 767 | extern_fn.data | 798 | .extern_fn => tv.val.castTag(.extern_fn).?.data, |
| 768 | else if (tv.val.castTag(.function)) |func_payload| | 799 | .function => tv.val.castTag(.function).?.data.owner_decl, |
| 769 | func_payload.data.owner_decl | 800 | .decl_ref => tv.val.castTag(.decl_ref).?.data, |
| 770 | else | 801 | else => unreachable, |
| 771 | unreachable; | 802 | }; |
| 772 | |||
| 773 | return self.resolveLlvmFunction(fn_decl); | 803 | return self.resolveLlvmFunction(fn_decl); |
| 774 | }, | 804 | }, |
| 775 | .ErrorSet => { | 805 | .ErrorSet => { |
| ... | @@ -793,11 +823,29 @@ pub const DeclGen = struct { | ... | @@ -793,11 +823,29 @@ pub const DeclGen = struct { |
| 793 | 823 | ||
| 794 | if (!payload_type.hasCodeGenBits()) { | 824 | if (!payload_type.hasCodeGenBits()) { |
| 795 | // We use the error type directly as the type. | 825 | // We use the error type directly as the type. |
| 796 | return self.genTypedValue(.{ .ty = error_type, .val = sub_val }, fg); | 826 | return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); |
| 797 | } | 827 | } |
| 798 | 828 | ||
| 799 | return self.todo("implement error union const of type '{}'", .{tv.ty}); | 829 | return self.todo("implement error union const of type '{}'", .{tv.ty}); |
| 800 | }, | 830 | }, |
| 831 | .Struct => { | ||
| 832 | const fields_len = tv.ty.structFieldCount(); | ||
| 833 | const field_vals = tv.val.castTag(.@"struct").?.data; | ||
| 834 | const gpa = self.gpa; | ||
| 835 | const llvm_fields = try gpa.alloc(*const llvm.Value, fields_len); | ||
| 836 | defer gpa.free(llvm_fields); | ||
| 837 | for (llvm_fields) |*llvm_field, i| { | ||
| 838 | llvm_field.* = try self.genTypedValue(.{ | ||
| 839 | .ty = tv.ty.structFieldType(i), | ||
| 840 | .val = field_vals[i], | ||
| 841 | }); | ||
| 842 | } | ||
| 843 | return self.context.constStruct( | ||
| 844 | llvm_fields.ptr, | ||
| 845 | @intCast(c_uint, llvm_fields.len), | ||
| 846 | .False, | ||
| 847 | ); | ||
| 848 | }, | ||
| 801 | else => return self.todo("implement const of type '{}'", .{tv.ty}), | 849 | else => return self.todo("implement const of type '{}'", .{tv.ty}), |
| 802 | } | 850 | } |
| 803 | } | 851 | } |
| ... | @@ -869,7 +917,7 @@ pub const FuncGen = struct { | ... | @@ -869,7 +917,7 @@ pub const FuncGen = struct { |
| 869 | 917 | ||
| 870 | fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value { | 918 | fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value { |
| 871 | if (self.air.value(inst)) |val| { | 919 | if (self.air.value(inst)) |val| { |
| 872 | return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val }, self); | 920 | return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val }); |
| 873 | } | 921 | } |
| 874 | const inst_index = Air.refToIndex(inst).?; | 922 | const inst_index = Air.refToIndex(inst).?; |
| 875 | if (self.func_inst_table.get(inst_index)) |value| return value; | 923 | if (self.func_inst_table.get(inst_index)) |value| return value; |
src/codegen/llvm/bindings.zig+14-2| ... | @@ -49,7 +49,12 @@ pub const Context = opaque { | ... | @@ -49,7 +49,12 @@ pub const Context = opaque { |
| 49 | extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value; | 49 | extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value; |
| 50 | 50 | ||
| 51 | pub const constStruct = LLVMConstStructInContext; | 51 | pub const constStruct = LLVMConstStructInContext; |
| 52 | extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: Bool) *const Value; | 52 | extern fn LLVMConstStructInContext( |
| 53 | C: *const Context, | ||
| 54 | ConstantVals: [*]const *const Value, | ||
| 55 | Count: c_uint, | ||
| 56 | Packed: Bool, | ||
| 57 | ) *const Value; | ||
| 53 | 58 | ||
| 54 | pub const createBasicBlock = LLVMCreateBasicBlockInContext; | 59 | pub const createBasicBlock = LLVMCreateBasicBlockInContext; |
| 55 | extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; | 60 | extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; |
| ... | @@ -100,6 +105,13 @@ pub const Value = opaque { | ... | @@ -100,6 +105,13 @@ pub const Value = opaque { |
| 100 | 105 | ||
| 101 | pub const setAliasee = LLVMAliasSetAliasee; | 106 | pub const setAliasee = LLVMAliasSetAliasee; |
| 102 | extern fn LLVMAliasSetAliasee(Alias: *const Value, Aliasee: *const Value) void; | 107 | extern fn LLVMAliasSetAliasee(Alias: *const Value, Aliasee: *const Value) void; |
| 108 | |||
| 109 | pub const constInBoundsGEP = LLVMConstInBoundsGEP; | ||
| 110 | extern fn LLVMConstInBoundsGEP( | ||
| 111 | ConstantVal: *const Value, | ||
| 112 | ConstantIndices: [*]const *const Value, | ||
| 113 | NumIndices: c_uint, | ||
| 114 | ) *const Value; | ||
| 103 | }; | 115 | }; |
| 104 | 116 | ||
| 105 | pub const Type = opaque { | 117 | pub const Type = opaque { |
| ... | @@ -113,7 +125,7 @@ pub const Type = opaque { | ... | @@ -113,7 +125,7 @@ pub const Type = opaque { |
| 113 | extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value; | 125 | extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value; |
| 114 | 126 | ||
| 115 | pub const constArray = LLVMConstArray; | 127 | pub const constArray = LLVMConstArray; |
| 116 | extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value; | 128 | extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]*const Value, Length: c_uint) *const Value; |
| 117 | 129 | ||
| 118 | pub const getUndef = LLVMGetUndef; | 130 | pub const getUndef = LLVMGetUndef; |
| 119 | extern fn LLVMGetUndef(Ty: *const Type) *const Value; | 131 | extern fn LLVMGetUndef(Ty: *const Type) *const Value; |
src/type.zig+22| ... | @@ -1526,6 +1526,8 @@ pub const Type = extern union { | ... | @@ -1526,6 +1526,8 @@ pub const Type = extern union { |
| 1526 | .var_args_param => unreachable, | 1526 | .var_args_param => unreachable, |
| 1527 | 1527 | ||
| 1528 | .@"struct" => { | 1528 | .@"struct" => { |
| 1529 | const s = self.castTag(.@"struct").?.data; | ||
| 1530 | assert(s.status == .have_layout); | ||
| 1529 | @panic("TODO abiSize struct"); | 1531 | @panic("TODO abiSize struct"); |
| 1530 | }, | 1532 | }, |
| 1531 | .enum_simple, .enum_full, .enum_nonexhaustive => { | 1533 | .enum_simple, .enum_full, .enum_nonexhaustive => { |
| ... | @@ -2768,6 +2770,26 @@ pub const Type = extern union { | ... | @@ -2768,6 +2770,26 @@ pub const Type = extern union { |
| 2768 | } | 2770 | } |
| 2769 | } | 2771 | } |
| 2770 | 2772 | ||
| 2773 | pub fn structFieldCount(ty: Type) usize { | ||
| 2774 | switch (ty.tag()) { | ||
| 2775 | .@"struct" => { | ||
| 2776 | const struct_obj = ty.castTag(.@"struct").?.data; | ||
| 2777 | return struct_obj.fields.count(); | ||
| 2778 | }, | ||
| 2779 | else => unreachable, | ||
| 2780 | } | ||
| 2781 | } | ||
| 2782 | |||
| 2783 | pub fn structFieldType(ty: Type, index: usize) Type { | ||
| 2784 | switch (ty.tag()) { | ||
| 2785 | .@"struct" => { | ||
| 2786 | const struct_obj = ty.castTag(.@"struct").?.data; | ||
| 2787 | return struct_obj.fields.values()[index].ty; | ||
| 2788 | }, | ||
| 2789 | else => unreachable, | ||
| 2790 | } | ||
| 2791 | } | ||
| 2792 | |||
| 2771 | pub fn declSrcLoc(ty: Type) Module.SrcLoc { | 2793 | pub fn declSrcLoc(ty: Type) Module.SrcLoc { |
| 2772 | switch (ty.tag()) { | 2794 | switch (ty.tag()) { |
| 2773 | .enum_full, .enum_nonexhaustive => { | 2795 | .enum_full, .enum_nonexhaustive => { |
src/value.zig+68-5| ... | @@ -112,6 +112,10 @@ pub const Value = extern union { | ... | @@ -112,6 +112,10 @@ pub const Value = extern union { |
| 112 | /// This value is repeated some number of times. The amount of times to repeat | 112 | /// This value is repeated some number of times. The amount of times to repeat |
| 113 | /// is stored externally. | 113 | /// is stored externally. |
| 114 | repeated, | 114 | repeated, |
| 115 | /// Each element stored as a `Value`. | ||
| 116 | array, | ||
| 117 | /// Pointer and length as sub `Value` objects. | ||
| 118 | slice, | ||
| 115 | float_16, | 119 | float_16, |
| 116 | float_32, | 120 | float_32, |
| 117 | float_64, | 121 | float_64, |
| ... | @@ -217,6 +221,9 @@ pub const Value = extern union { | ... | @@ -217,6 +221,9 @@ pub const Value = extern union { |
| 217 | .enum_literal, | 221 | .enum_literal, |
| 218 | => Payload.Bytes, | 222 | => Payload.Bytes, |
| 219 | 223 | ||
| 224 | .array => Payload.Array, | ||
| 225 | .slice => Payload.Slice, | ||
| 226 | |||
| 220 | .enum_field_index => Payload.U32, | 227 | .enum_field_index => Payload.U32, |
| 221 | 228 | ||
| 222 | .ty => Payload.Ty, | 229 | .ty => Payload.Ty, |
| ... | @@ -442,6 +449,28 @@ pub const Value = extern union { | ... | @@ -442,6 +449,28 @@ pub const Value = extern union { |
| 442 | }; | 449 | }; |
| 443 | return Value{ .ptr_otherwise = &new_payload.base }; | 450 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 444 | }, | 451 | }, |
| 452 | .array => { | ||
| 453 | const payload = self.castTag(.array).?; | ||
| 454 | const new_payload = try allocator.create(Payload.Array); | ||
| 455 | new_payload.* = .{ | ||
| 456 | .base = payload.base, | ||
| 457 | .data = try allocator.alloc(Value, payload.data.len), | ||
| 458 | }; | ||
| 459 | std.mem.copy(Value, new_payload.data, payload.data); | ||
| 460 | return Value{ .ptr_otherwise = &new_payload.base }; | ||
| 461 | }, | ||
| 462 | .slice => { | ||
| 463 | const payload = self.castTag(.slice).?; | ||
| 464 | const new_payload = try allocator.create(Payload.Slice); | ||
| 465 | new_payload.* = .{ | ||
| 466 | .base = payload.base, | ||
| 467 | .data = .{ | ||
| 468 | .ptr = try payload.data.ptr.copy(allocator), | ||
| 469 | .len = try payload.data.len.copy(allocator), | ||
| 470 | }, | ||
| 471 | }; | ||
| 472 | return Value{ .ptr_otherwise = &new_payload.base }; | ||
| 473 | }, | ||
| 445 | .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16), | 474 | .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16), |
| 446 | .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32), | 475 | .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32), |
| 447 | .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64), | 476 | .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64), |
| ... | @@ -605,6 +634,8 @@ pub const Value = extern union { | ... | @@ -605,6 +634,8 @@ pub const Value = extern union { |
| 605 | try out_stream.writeAll("(repeated) "); | 634 | try out_stream.writeAll("(repeated) "); |
| 606 | val = val.castTag(.repeated).?.data; | 635 | val = val.castTag(.repeated).?.data; |
| 607 | }, | 636 | }, |
| 637 | .array => return out_stream.writeAll("(array)"), | ||
| 638 | .slice => return out_stream.writeAll("(slice)"), | ||
| 608 | .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}), | 639 | .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}), |
| 609 | .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), | 640 | .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), |
| 610 | .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}), | 641 | .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}), |
| ... | @@ -729,6 +760,8 @@ pub const Value = extern union { | ... | @@ -729,6 +760,8 @@ pub const Value = extern union { |
| 729 | .field_ptr, | 760 | .field_ptr, |
| 730 | .bytes, | 761 | .bytes, |
| 731 | .repeated, | 762 | .repeated, |
| 763 | .array, | ||
| 764 | .slice, | ||
| 732 | .float_16, | 765 | .float_16, |
| 733 | .float_32, | 766 | .float_32, |
| 734 | .float_64, | 767 | .float_64, |
| ... | @@ -1075,6 +1108,8 @@ pub const Value = extern union { | ... | @@ -1075,6 +1108,8 @@ pub const Value = extern union { |
| 1075 | return orderAgainstZero(lhs).compare(op); | 1108 | return orderAgainstZero(lhs).compare(op); |
| 1076 | } | 1109 | } |
| 1077 | 1110 | ||
| 1111 | /// TODO we can't compare value equality without also knowing the type to treat | ||
| 1112 | /// the values as | ||
| 1078 | pub fn eql(a: Value, b: Value) bool { | 1113 | pub fn eql(a: Value, b: Value) bool { |
| 1079 | const a_tag = a.tag(); | 1114 | const a_tag = a.tag(); |
| 1080 | const b_tag = b.tag(); | 1115 | const b_tag = b.tag(); |
| ... | @@ -1109,6 +1144,8 @@ pub const Value = extern union { | ... | @@ -1109,6 +1144,8 @@ pub const Value = extern union { |
| 1109 | return @truncate(u32, self.hash()); | 1144 | return @truncate(u32, self.hash()); |
| 1110 | } | 1145 | } |
| 1111 | 1146 | ||
| 1147 | /// TODO we can't hash without also knowing the type of the value. | ||
| 1148 | /// we have to hash as if there were a canonical value memory layout. | ||
| 1112 | pub fn hash(self: Value) u64 { | 1149 | pub fn hash(self: Value) u64 { |
| 1113 | var hasher = std.hash.Wyhash.init(0); | 1150 | var hasher = std.hash.Wyhash.init(0); |
| 1114 | 1151 | ||
| ... | @@ -1203,6 +1240,15 @@ pub const Value = extern union { | ... | @@ -1203,6 +1240,15 @@ pub const Value = extern union { |
| 1203 | const payload = self.castTag(.bytes).?; | 1240 | const payload = self.castTag(.bytes).?; |
| 1204 | hasher.update(payload.data); | 1241 | hasher.update(payload.data); |
| 1205 | }, | 1242 | }, |
| 1243 | .repeated => { | ||
| 1244 | @panic("TODO Value.hash for repeated"); | ||
| 1245 | }, | ||
| 1246 | .array => { | ||
| 1247 | @panic("TODO Value.hash for array"); | ||
| 1248 | }, | ||
| 1249 | .slice => { | ||
| 1250 | @panic("TODO Value.hash for slice"); | ||
| 1251 | }, | ||
| 1206 | .int_u64 => { | 1252 | .int_u64 => { |
| 1207 | const payload = self.castTag(.int_u64).?; | 1253 | const payload = self.castTag(.int_u64).?; |
| 1208 | std.hash.autoHash(&hasher, payload.data); | 1254 | std.hash.autoHash(&hasher, payload.data); |
| ... | @@ -1211,10 +1257,6 @@ pub const Value = extern union { | ... | @@ -1211,10 +1257,6 @@ pub const Value = extern union { |
| 1211 | const payload = self.castTag(.int_i64).?; | 1257 | const payload = self.castTag(.int_i64).?; |
| 1212 | std.hash.autoHash(&hasher, payload.data); | 1258 | std.hash.autoHash(&hasher, payload.data); |
| 1213 | }, | 1259 | }, |
| 1214 | .repeated => { | ||
| 1215 | const payload = self.castTag(.repeated).?; | ||
| 1216 | std.hash.autoHash(&hasher, payload.data.hash()); | ||
| 1217 | }, | ||
| 1218 | .ref_val => { | 1260 | .ref_val => { |
| 1219 | const payload = self.castTag(.ref_val).?; | 1261 | const payload = self.castTag(.ref_val).?; |
| 1220 | std.hash.autoHash(&hasher, payload.data.hash()); | 1262 | std.hash.autoHash(&hasher, payload.data.hash()); |
| ... | @@ -1340,6 +1382,8 @@ pub const Value = extern union { | ... | @@ -1340,6 +1382,8 @@ pub const Value = extern union { |
| 1340 | return switch (val.tag()) { | 1382 | return switch (val.tag()) { |
| 1341 | .empty_array => 0, | 1383 | .empty_array => 0, |
| 1342 | .bytes => val.castTag(.bytes).?.data.len, | 1384 | .bytes => val.castTag(.bytes).?.data.len, |
| 1385 | .array => val.castTag(.array).?.data.len, | ||
| 1386 | .slice => val.castTag(.slice).?.data.len.toUnsignedInt(), | ||
| 1343 | .ref_val => sliceLen(val.castTag(.ref_val).?.data), | 1387 | .ref_val => sliceLen(val.castTag(.ref_val).?.data), |
| 1344 | .decl_ref => { | 1388 | .decl_ref => { |
| 1345 | const decl = val.castTag(.decl_ref).?.data; | 1389 | const decl = val.castTag(.decl_ref).?.data; |
| ... | @@ -1364,6 +1408,9 @@ pub const Value = extern union { | ... | @@ -1364,6 +1408,9 @@ pub const Value = extern union { |
| 1364 | // No matter the index; all the elements are the same! | 1408 | // No matter the index; all the elements are the same! |
| 1365 | .repeated => return self.castTag(.repeated).?.data, | 1409 | .repeated => return self.castTag(.repeated).?.data, |
| 1366 | 1410 | ||
| 1411 | .array => return self.castTag(.array).?.data[index], | ||
| 1412 | .slice => return self.castTag(.slice).?.data.ptr.elemValue(allocator, index), | ||
| 1413 | |||
| 1367 | else => unreachable, | 1414 | else => unreachable, |
| 1368 | } | 1415 | } |
| 1369 | } | 1416 | } |
| ... | @@ -1450,7 +1497,8 @@ pub const Value = extern union { | ... | @@ -1450,7 +1497,8 @@ pub const Value = extern union { |
| 1450 | } | 1497 | } |
| 1451 | 1498 | ||
| 1452 | /// Valid for all types. Asserts the value is not undefined. | 1499 | /// Valid for all types. Asserts the value is not undefined. |
| 1453 | pub fn isType(self: Value) bool { | 1500 | /// TODO this function is a code smell and should be deleted |
| 1501 | fn isType(self: Value) bool { | ||
| 1454 | return switch (self.tag()) { | 1502 | return switch (self.tag()) { |
| 1455 | .ty, | 1503 | .ty, |
| 1456 | .int_type, | 1504 | .int_type, |
| ... | @@ -1528,6 +1576,8 @@ pub const Value = extern union { | ... | @@ -1528,6 +1576,8 @@ pub const Value = extern union { |
| 1528 | .field_ptr, | 1576 | .field_ptr, |
| 1529 | .bytes, | 1577 | .bytes, |
| 1530 | .repeated, | 1578 | .repeated, |
| 1579 | .array, | ||
| 1580 | .slice, | ||
| 1531 | .float_16, | 1581 | .float_16, |
| 1532 | .float_32, | 1582 | .float_32, |
| 1533 | .float_64, | 1583 | .float_64, |
| ... | @@ -1638,6 +1688,19 @@ pub const Value = extern union { | ... | @@ -1638,6 +1688,19 @@ pub const Value = extern union { |
| 1638 | data: []const u8, | 1688 | data: []const u8, |
| 1639 | }; | 1689 | }; |
| 1640 | 1690 | ||
| 1691 | pub const Array = struct { | ||
| 1692 | base: Payload, | ||
| 1693 | data: []Value, | ||
| 1694 | }; | ||
| 1695 | |||
| 1696 | pub const Slice = struct { | ||
| 1697 | base: Payload, | ||
| 1698 | data: struct { | ||
| 1699 | ptr: Value, | ||
| 1700 | len: Value, | ||
| 1701 | }, | ||
| 1702 | }; | ||
| 1703 | |||
| 1641 | pub const Ty = struct { | 1704 | pub const Ty = struct { |
| 1642 | base: Payload, | 1705 | base: Payload, |
| 1643 | data: Type, | 1706 | data: Type, |