From 414fcea162a751435f0194ed4a01785b3a0913a0 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 24 Aug 2022 17:53:10 +0200 Subject: [PATCH 01/51] link/Wasm: handle extern variables Generate symbols for extern variables and try to resolve them. Unresolved 'data' symbols generate an error as they cannot be exported from the Wasm runtime into a Wasm module. This means, they can only be resolved by other object files such as from other Zig or C code compiled to Wasm. --- src/link/Wasm.zig | 45 ++++++++++++++++++++++++++++++---------- src/link/Wasm/Atom.zig | 5 +---- src/link/Wasm/Symbol.zig | 2 +- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index e20703cb2b371a8725ffe0cfd4da74a08ebd32e1..050d9287a5e7f0aa2e8206c74e4d2d0949e9c60f 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -607,6 +607,24 @@ fn resolveSymbolsInArchives(self: *Wasm) !void { } } +fn checkUndefinedSymbols(self: *const Wasm) !void { + var found_undefined_symbols = false; + for (self.undefs.values()) |undef| { + const symbol = undef.getSymbol(self); + if (symbol.tag == .data) { + found_undefined_symbols = true; + const file_name = if (undef.file) |file_index| name: { + break :name self.objects.items[file_index].name; + } else self.name; + log.err("could not resolve undefined symbol '{s}'", .{undef.getName(self)}); + log.err(" defined in '{s}'", .{file_name}); + } + } + if (found_undefined_symbols) { + return error.UndefinedSymbol; + } +} + pub fn deinit(self: *Wasm) void { const gpa = self.base.allocator; if (build_options.have_llvm) { @@ -783,15 +801,17 @@ pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi decl.link.wasm.clear(); - if (decl.isExtern()) { - return; - } - if (decl.val.castTag(.function)) |_| { return; } else if (decl.val.castTag(.extern_fn)) |_| { return; } + + if (decl.isExtern()) { + const variable = decl.getVariable().?; + const name = mem.sliceTo(decl.name, 0); + return self.addOrUpdateImport(name, decl.link.wasm.sym_index, variable.lib_name, null); + } const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; var code_writer = std.ArrayList(u8).init(self.base.allocator); @@ -834,19 +854,18 @@ pub fn updateDeclLineNumber(self: *Wasm, mod: *Module, decl: *const Module.Decl) } fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { - if (code.len == 0) return; const mod = self.base.options.module.?; const atom: *Atom = &decl.link.wasm; - atom.size = @intCast(u32, code.len); - atom.alignment = decl.ty.abiAlignment(self.base.options.target); const symbol = &self.symbols.items[atom.sym_index]; - const full_name = try decl.getFullyQualifiedName(mod); defer self.base.allocator.free(full_name); symbol.name = try self.string_table.put(self.base.allocator, full_name); try atom.code.appendSlice(self.base.allocator, code); - try self.resolved_symbols.put(self.base.allocator, atom.symbolLoc(), {}); + + if (code.len == 0) return; + atom.size = @intCast(u32, code.len); + atom.alignment = decl.ty.abiAlignment(self.base.options.target); } /// From a given symbol location, returns its `wasm.GlobalType`. @@ -1235,7 +1254,10 @@ pub fn addOrUpdateImport( .kind = .{ .function = ty_index }, }; } - } else @panic("TODO: Implement undefined symbols for non-function declarations"); + } else { + symbol.tag = .data; + return; // non-functions will not be imported from the runtime, but only resolved during link-time + } } /// Kind represents the type of an Atom, which is only @@ -1438,7 +1460,7 @@ fn setupImports(self: *Wasm) !void { if (std.mem.eql(u8, symbol_loc.getName(self), "__indirect_function_table")) { continue; } - if (symbol.tag == .data or !symbol.requiresImport()) { + if (!symbol.requiresImport()) { continue; } @@ -2007,6 +2029,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } try self.resolveSymbolsInArchives(); + try self.checkUndefinedSymbols(); // When we finish/error we reset the state of the linker // So we can rebuild the binary file on each incremental update diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index d5bb4509f6c86cc3a7bbf95e7146f4f3044334ca..ed512f466c44519e1d237fd8758e05cf2b180d58 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -172,10 +172,7 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa .R_WASM_MEMORY_ADDR_SLEB, .R_WASM_MEMORY_ADDR_SLEB64, => { - if (symbol.isUndefined() and symbol.isWeak()) { - return 0; - } - std.debug.assert(symbol.tag == .data); + std.debug.assert(symbol.tag == .data and !symbol.isUndefined()); const merge_segment = wasm_bin.base.options.output_mode != .Obj; const segment_info = if (self.file) |object_index| blk: { break :blk wasm_bin.objects.items[object_index].segment_info; diff --git a/src/link/Wasm/Symbol.zig b/src/link/Wasm/Symbol.zig index fa6ea89d69d9a8b110db9b43519595462f2a8cfd..5e134566053dcd1403794b5fcb3c18557d0e92f0 100644 --- a/src/link/Wasm/Symbol.zig +++ b/src/link/Wasm/Symbol.zig @@ -79,9 +79,9 @@ pub const Flag = enum(u32) { /// Verifies if the given symbol should be imported from the /// host environment or not pub fn requiresImport(self: Symbol) bool { + if (self.tag == .data) return false; if (!self.isUndefined()) return false; if (self.isWeak()) return false; - if (self.tag == .data) return false; // if (self.isDefined() and self.isWeak()) return true; //TODO: Only when building shared lib return true; -- 2.54.0 From 4f72ac265acac682541f170a1189a06350009431 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 26 Aug 2022 08:11:17 +0200 Subject: [PATCH 02/51] wasm: create relocations for extern decls This also fixes performing relocations for data symbols of which the target symbol exists in an external object file. We do this by checking if the target symbol was discarded, and if so: get the new location so that we can find the corresponding atom that belongs to said new location. Previously it would always assume the symbol would live in the same file as the atom/symbol that is doing the relocation. --- src/arch/wasm/CodeGen.zig | 4 +--- src/link/Wasm/Atom.zig | 9 +++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 95a0a8e4aa83b0adf377de3922392158782ed8d6..e09822c2083b5d28f81bbc472970cb4db984c177 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2355,7 +2355,7 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) const module = self.bin_file.base.options.module.?; const decl = module.declPtr(decl_index); - if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) { + if (!decl.ty.hasRuntimeBitsIgnoreComptime()) { return WValue{ .imm32 = 0xaaaaaaaa }; } @@ -2394,9 +2394,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { const decl_index = decl_ref_mut.data.decl_index; return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index); } - const target = self.target; - switch (ty.zigTypeTag()) { .Void => return WValue{ .none = {} }, .Int => { diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index ed512f466c44519e1d237fd8758e05cf2b180d58..9e7f7a5a76d9a1c66bad2b7e504227d8bbc4e278 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -174,13 +174,14 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa => { std.debug.assert(symbol.tag == .data and !symbol.isUndefined()); const merge_segment = wasm_bin.base.options.output_mode != .Obj; - const segment_info = if (self.file) |object_index| blk: { + const target_atom_loc = wasm_bin.discarded.get(target_loc) orelse target_loc; + const target_atom = wasm_bin.symbol_atom.get(target_atom_loc).?; + const segment_info = if (target_atom.file) |object_index| blk: { break :blk wasm_bin.objects.items[object_index].segment_info; } else wasm_bin.segment_info.items; const segment_name = segment_info[symbol.index].outputName(merge_segment); - const atom_index = wasm_bin.data_segments.get(segment_name).?; - const target_atom = wasm_bin.symbol_atom.get(target_loc).?; - const segment = wasm_bin.segments.items[atom_index]; + const segment_index = wasm_bin.data_segments.get(segment_name).?; + const segment = wasm_bin.segments.items[segment_index]; return target_atom.offset + segment.offset + (relocation.addend orelse 0); }, .R_WASM_EVENT_INDEX_LEB => return symbol.index, -- 2.54.0 From 8627858bbc2fee848e2f3e3ca64dc944f39591e5 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 26 Aug 2022 17:29:43 +0200 Subject: [PATCH 03/51] test/link: add test for extern resolution Adds a linker tests to verify extern/undefined symbols representing non-functions are being resolved correctly. --- src/arch/wasm/CodeGen.zig | 2 +- test/link.zig | 6 ++++++ test/link/wasm/extern/build.zig | 17 +++++++++++++++++ test/link/wasm/extern/foo.c | 1 + test/link/wasm/extern/main.zig | 8 ++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/link/wasm/extern/build.zig create mode 100644 test/link/wasm/extern/foo.c create mode 100644 test/link/wasm/extern/main.zig diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index e09822c2083b5d28f81bbc472970cb4db984c177..b9637bf8e31af9bcc9497777cae22958292db036 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2355,7 +2355,7 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) const module = self.bin_file.base.options.module.?; const decl = module.declPtr(decl_index); - if (!decl.ty.hasRuntimeBitsIgnoreComptime()) { + if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) { return WValue{ .imm32 = 0xaaaaaaaa }; } diff --git a/test/link.zig b/test/link.zig index 215a0511fc0f9b585bf343a2ea6e965cd8c58c91..b68353122c31e77e9fbb269198b1c222208598b4 100644 --- a/test/link.zig +++ b/test/link.zig @@ -52,6 +52,12 @@ fn addWasmCases(cases: *tests.StandaloneContext) void { .build_modes = true, .requires_stage2 = true, }); + + cases.addBuildFile("test/link/wasm/extern/build.zig", .{ + .build_modes = true, + .requires_stage2 = true, + .use_emulation = true, + }); } fn addMachOCases(cases: *tests.StandaloneContext) void { diff --git a/test/link/wasm/extern/build.zig b/test/link/wasm/extern/build.zig new file mode 100644 index 0000000000000000000000000000000000000000..88cce88d98954aa51ebddeb040c1cf189dcbd931 --- /dev/null +++ b/test/link/wasm/extern/build.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn build(b: *std.build.Builder) void { + const mode = b.standardReleaseOptions(); + const exe = b.addExecutable("extern", "main.zig"); + exe.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .wasi }); + exe.setBuildMode(mode); + exe.addCSourceFile("foo.c", &.{}); + exe.use_llvm = false; + exe.use_lld = false; + + const run = exe.runEmulatable(); + run.expectStdOutEqual("Result: 30"); + + const test_step = b.step("test", "Run linker test"); + test_step.dependOn(&run.step); +} diff --git a/test/link/wasm/extern/foo.c b/test/link/wasm/extern/foo.c new file mode 100644 index 0000000000000000000000000000000000000000..0dafd7e11241911a6f2cdf022d118f1aeec930b8 --- /dev/null +++ b/test/link/wasm/extern/foo.c @@ -0,0 +1 @@ +int foo = 30; diff --git a/test/link/wasm/extern/main.zig b/test/link/wasm/extern/main.zig new file mode 100644 index 0000000000000000000000000000000000000000..b9fa1226eb7ca6f0c57101461a9d252d0ce370a6 --- /dev/null +++ b/test/link/wasm/extern/main.zig @@ -0,0 +1,8 @@ +const std = @import("std"); + +extern const foo: u32; + +pub fn main() void { + const std_out = std.io.getStdOut(); + std_out.writer().print("Result: {d}", .{foo}) catch {}; +} -- 2.54.0 From 59e33b447b0502bdfa18909f4381360bb8f49b70 Mon Sep 17 00:00:00 2001 From: biexelar Date: Wed, 31 Aug 2022 02:31:47 +0000 Subject: [PATCH 04/51] std.Thread: fix freeAndExit on x86_64-linux (#12693) Previously, this function used incorrect registers for the munmap syscall, leading to detached threads not cleaning up. closes #12690 Co-authored-by: bxlr --- lib/std/Thread.zig | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index d52515b88d06c4be90b285e994cbba48ab976bc9..20985c36a061b7cac3d923888e1f8edb7da1f870 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -769,16 +769,13 @@ const LinuxThreadImpl = struct { ), .x86_64 => asm volatile ( \\ movq $11, %%rax - \\ movq %[ptr], %%rbx - \\ movq %[len], %%rcx \\ syscall \\ movq $60, %%rax \\ movq $1, %%rdi \\ syscall : - : [ptr] "r" (@ptrToInt(self.mapped.ptr)), - [len] "r" (self.mapped.len), - : "memory" + : [ptr] "{rdi}" (@ptrToInt(self.mapped.ptr)), + [len] "{rsi}" (self.mapped.len), ), .arm, .armeb, .thumb, .thumbeb => asm volatile ( \\ mov r7, #91 -- 2.54.0 From 9f9e51eb110289850ee30c0b4ca895a8861f4c40 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Aug 2022 22:55:30 -0700 Subject: [PATCH 05/51] CI: drone: disable failing tests See tracking issue #12689 --- ci/drone/test_linux_misc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/drone/test_linux_misc b/ci/drone/test_linux_misc index ebbeee7576ec376668c531a28aae15bb806a394c..6b64c9ff98921f213c685ad7b22d648083bb6b2e 100755 --- a/ci/drone/test_linux_misc +++ b/ci/drone/test_linux_misc @@ -16,4 +16,4 @@ $ZIG build test-universal-libc -Dskip-non-native --zig-lib-dir lib $ZIG build test-stack-traces -Dskip-non-native --zig-lib-dir lib $ZIG build test-cli -Dskip-non-native --zig-lib-dir lib $ZIG build test-asm-link -Dskip-non-native --zig-lib-dir lib -$ZIG build test-translate-c -Dskip-non-native --zig-lib-dir lib +# $ZIG build test-translate-c -Dskip-non-native --zig-lib-dir lib -- 2.54.0 From b45387f20e3abad052a11df55d2cd7bab099e5c3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 31 Aug 2022 00:57:32 +0200 Subject: [PATCH 06/51] coff: write base relocations for the dynamic linker This means we can request ASLR on by default as other COFF linkers do. Currently, we write the base relocations in bulk, however, given that there is a mechanism for padding in place in PE/COFF I believe there might be room for making it an incremental operation (write base relocation whenever we add/update a pointer that would require it). --- src/link/Coff.zig | 148 +++++++++++++++++++++++++++++++++++++++-- src/link/Coff/Atom.zig | 14 +++- 2 files changed, 154 insertions(+), 8 deletions(-) diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 36ddfc4e2a930b1778d4fcb90a4be38282a5dfc1..e302571671ef578924841eab9b368d9d90a83d38 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -50,6 +50,7 @@ text_section_index: ?u16 = null, got_section_index: ?u16 = null, rdata_section_index: ?u16 = null, data_section_index: ?u16 = null, +reloc_section_index: ?u16 = null, locals: std.ArrayListUnmanaged(coff.Symbol) = .{}, globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, @@ -98,11 +99,16 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{}, /// with `Decl` `main`, and lives as long as that `Decl`. unnamed_const_atoms: UnnamedConstTable = .{}, -/// A table of relocations indexed by the owning them `TextBlock`. -/// Note that once we refactor `TextBlock`'s lifetime and ownership rules, +/// A table of relocations indexed by the owning them `Atom`. +/// Note that once we refactor `Atom`'s lifetime and ownership rules, /// this will be a table indexed by index into the list of Atoms. relocs: RelocTable = .{}, +/// A table of base relocations indexed by the owning them `Atom`. +/// Note that once we refactor `Atom`'s lifetime and ownership rules, +/// this will be a table indexed by index into the list of Atoms. +base_relocs: BaseRelocationTable = .{}, + pub const Reloc = struct { @"type": enum { got, @@ -117,6 +123,7 @@ pub const Reloc = struct { }; const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc)); +const BaseRelocationTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32)); const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom)); const default_file_alignment: u16 = 0x200; @@ -150,7 +157,17 @@ const Section = struct { free_list: std.ArrayListUnmanaged(*Atom) = .{}, }; -pub const PtrWidth = enum { p32, p64 }; +pub const PtrWidth = enum { + p32, + p64, + + fn abiSize(pw: PtrWidth) u4 { + return switch (pw) { + .p32 => 4, + .p64 => 8, + }; + } +}; pub const SrcFn = void; pub const Export = struct { @@ -274,6 +291,14 @@ pub fn deinit(self: *Coff) void { } self.relocs.deinit(gpa); } + + { + var it = self.base_relocs.valueIterator(); + while (it.next()) |relocs| { + relocs.deinit(gpa); + } + self.base_relocs.deinit(gpa); + } } fn populateMissingMetadata(self: *Coff) !void { @@ -307,7 +332,7 @@ fn populateMissingMetadata(self: *Coff) !void { if (self.got_section_index == null) { self.got_section_index = @intCast(u16, self.sections.slice().len); - const file_size = @intCast(u32, self.base.options.symbol_count_hint); + const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); const off = self.findFreeSpace(file_size, self.page_size); log.debug("found .got free space 0x{x} to 0x{x}", .{ off, off + file_size }); var header = coff.SectionHeader{ @@ -378,6 +403,31 @@ fn populateMissingMetadata(self: *Coff) !void { try self.sections.append(gpa, .{ .header = header }); } + if (self.reloc_section_index == null) { + self.reloc_section_index = @intCast(u16, self.sections.slice().len); + const file_size = @intCast(u32, self.base.options.symbol_count_hint) * @sizeOf(coff.BaseRelocation); + const off = self.findFreeSpace(file_size, self.page_size); + log.debug("found .reloc free space 0x{x} to 0x{x}", .{ off, off + file_size }); + var header = coff.SectionHeader{ + .name = undefined, + .virtual_size = file_size, + .virtual_address = off, + .size_of_raw_data = file_size, + .pointer_to_raw_data = off, + .pointer_to_relocations = 0, + .pointer_to_linenumbers = 0, + .number_of_relocations = 0, + .number_of_linenumbers = 0, + .flags = .{ + .CNT_INITIALIZED_DATA = 1, + .MEM_PURGEABLE = 1, + .MEM_READ = 1, + }, + }; + try self.setSectionName(&header, ".reloc"); + try self.sections.append(gpa, .{ .header = header }); + } + if (self.strtab_offset == null) { try self.strtab.buffer.append(gpa, 0); self.strtab_offset = self.findFreeSpace(@intCast(u32, self.strtab.len()), 1); @@ -605,6 +655,14 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { .prev_vaddr = sym.value, }); + const target_sym = self.getSymbol(target); + switch (target_sym.section_number) { + .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"), + .ABSOLUTE => {}, + .DEBUG => unreachable, // not possible + else => try atom.addBaseRelocation(self, 0), + } + return atom; } @@ -1179,6 +1237,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try self.resolveRelocs(atom.*); } } + try self.writeBaseRelocations(); if (self.getEntryPoint()) |entry_sym_loc| { self.entry_addr = self.getSymbol(entry_sym_loc).value; @@ -1216,6 +1275,83 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v log.debug("TODO implement updateDeclLineNumber", .{}); } +/// TODO: note if we need to rewrite base relocations by dirtying any of the entries in the global table +/// TODO: note that .ABSOLUTE is used as padding within each block; we could use this fact to do +/// incremental updates and writes into the table instead of doing it all at once +fn writeBaseRelocations(self: *Coff) !void { + const gpa = self.base.allocator; + + var pages = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa); + defer { + var it = pages.valueIterator(); + while (it.next()) |inner| { + inner.deinit(); + } + pages.deinit(); + } + + var it = self.base_relocs.iterator(); + while (it.next()) |entry| { + const atom = entry.key_ptr.*; + const offsets = entry.value_ptr.*; + + for (offsets.items) |offset| { + const sym = atom.getSymbol(self); + const rva = sym.value + offset; + const page = mem.alignBackwardGeneric(u32, rva, self.page_size); + const gop = try pages.getOrPut(page); + if (!gop.found_existing) { + gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa); + } + try gop.value_ptr.append(.{ + .offset = @intCast(u12, rva - page), + .@"type" = .DIR64, + }); + } + } + + var buffer = std.ArrayList(u8).init(gpa); + defer buffer.deinit(); + + var pages_it = pages.iterator(); + while (pages_it.next()) |entry| { + // Pad to required 4byte alignment + if (!mem.isAlignedGeneric( + usize, + entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation), + @sizeOf(u32), + )) { + try entry.value_ptr.append(.{ + .offset = 0, + .@"type" = .ABSOLUTE, + }); + } + + const block_size = @intCast( + u32, + entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry), + ); + try buffer.ensureUnusedCapacity(block_size); + buffer.appendSliceAssumeCapacity(mem.asBytes(&coff.BaseRelocationDirectoryEntry{ + .page_rva = entry.key_ptr.*, + .block_size = block_size, + })); + buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entry.value_ptr.items)); + } + + const header = &self.sections.items(.header)[self.reloc_section_index.?]; + const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); + const needed_size = @intCast(u32, buffer.items.len); + assert(needed_size < sect_capacity); // TODO expand .reloc section + + try self.base.file.?.pwriteAll(buffer.items, header.pointer_to_raw_data); + + self.data_directories[@enumToInt(coff.DirectoryEntry.BASERELOC)] = .{ + .virtual_address = header.virtual_address, + .size = needed_size, + }; +} + fn writeStrtab(self: *Coff) !void { const allocated_size = self.allocatedSize(self.strtab_offset.?); const needed_size = @intCast(u32, self.strtab.len()); @@ -1277,8 +1413,8 @@ fn writeHeader(self: *Coff) !void { writer.writeAll(mem.asBytes(&coff_header)) catch unreachable; const dll_flags: coff.DllFlags = .{ - .HIGH_ENTROPY_VA = 0, //@boolToInt(self.base.options.pie), - .DYNAMIC_BASE = 0, + .HIGH_ENTROPY_VA = 1, // TODO do we want to permit non-PIE builds at all? + .DYNAMIC_BASE = 1, .TERMINAL_SERVER_AWARE = 1, // We are not a legacy app .NX_COMPAT = 1, // We are compatible with Data Execution Prevention }; diff --git a/src/link/Coff/Atom.zig b/src/link/Coff/Atom.zig index 6c085a8f588a7ae628be012ac4d32577e0ae3215..a7608d9a3463530502d182f441b3fd3df5e7481e 100644 --- a/src/link/Coff/Atom.zig +++ b/src/link/Coff/Atom.zig @@ -2,6 +2,7 @@ const Atom = @This(); const std = @import("std"); const coff = std.coff; +const log = std.log.scoped(.link); const Allocator = std.mem.Allocator; @@ -100,11 +101,20 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool { pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Reloc) !void { const gpa = coff_file.base.allocator; - // TODO causes a segfault on Windows - // log.debug("adding reloc of type {s} to target %{d}", .{ @tagName(reloc.@"type"), reloc.target.sym_index }); + log.debug(" (adding reloc of type {s} to target %{d})", .{ @tagName(reloc.@"type"), reloc.target.sym_index }); const gop = try coff_file.relocs.getOrPut(gpa, self); if (!gop.found_existing) { gop.value_ptr.* = .{}; } try gop.value_ptr.append(gpa, reloc); } + +pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void { + const gpa = coff_file.base.allocator; + log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{ offset, self.sym_index }); + const gop = try coff_file.base_relocs.getOrPut(gpa, self); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + try gop.value_ptr.append(gpa, offset); +} -- 2.54.0 From fdb88708527742e450e4c566024d9d50ce61dd8d Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 30 Aug 2022 20:45:42 -0700 Subject: [PATCH 07/51] translate-c: promote large integer macros to unsigned long long if necessary Closes #10793 Co-authored-by: Veikka Tuominen --- lib/std/zig/c_translation.zig | 2 +- test/behavior/translate_c_macros.h | 2 ++ test/behavior/translate_c_macros.zig | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/c_translation.zig b/lib/std/zig/c_translation.zig index 348e3a71331e1d6b62b5a593f1973a3384b4e252..6847a92eae9258a4da8d1aee88a94210e63f1096 100644 --- a/lib/std/zig/c_translation.zig +++ b/lib/std/zig/c_translation.zig @@ -268,7 +268,7 @@ test "sizeof" { pub const CIntLiteralRadix = enum { decimal, octal, hexadecimal }; fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime number: comptime_int, comptime radix: CIntLiteralRadix) type { - const signed_decimal = [_]type{ c_int, c_long, c_longlong }; + const signed_decimal = [_]type{ c_int, c_long, c_longlong, c_ulonglong }; const signed_oct_hex = [_]type{ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong }; const unsigned = [_]type{ c_uint, c_ulong, c_ulonglong }; diff --git a/test/behavior/translate_c_macros.h b/test/behavior/translate_c_macros.h index 526ab32abcc3ef50aae4b3e82e3d65107816617f..222a7ded6c4b061c3f9d7d7ac5057d8e6c3404ed 100644 --- a/test/behavior/translate_c_macros.h +++ b/test/behavior/translate_c_macros.h @@ -48,3 +48,5 @@ typedef _Bool uintptr_t; #define CAST_TO_BOOL(X) (_Bool)(X) #define CAST_TO_UINTPTR(X) (uintptr_t)(X) + +#define LARGE_INT 18446744073709550592 diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index 705c60aa4e2a7b848706d4dbd6aa7d1cf6db8d60..a23907c88f3b0d7e915eb30d01d120e5580e4c68 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -113,3 +113,7 @@ test "cast functions" { try expectEqual(true, h.CAST_TO_BOOL(S.foo)); try expect(h.CAST_TO_UINTPTR(S.foo) != 0); } + +test "large integer macro" { + try expectEqual(@as(c_ulonglong, 18446744073709550592), h.LARGE_INT); +} -- 2.54.0 From 973b44056185132c1abc5bd5fb1c8c2c45d3d329 Mon Sep 17 00:00:00 2001 From: Hashi364 <49736221+Kiyoshi364@users.noreply.github.com> Date: Tue, 30 Aug 2022 18:07:23 -0300 Subject: [PATCH 08/51] Using `comptime level.asText()` in log example Some recent change makes slice concatenation runtime (merge #12368), so the example needs to be explicitly made comptime. --- lib/std/log.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/log.zig b/lib/std/log.zig index 081344f2c8fb4d26cee223377efb50743e0c1661..e0e002d6001d86ce4907fafc6169928cdae4e820 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -38,7 +38,7 @@ //! return, //! } ++ "): "; //! -//! const prefix = "[" ++ level.asText() ++ "] " ++ scope_prefix; +//! const prefix = "[" ++ comptime level.asText() ++ "] " ++ scope_prefix; //! //! // Print the message to stderr, silently ignoring any errors //! std.debug.getStderrMutex().lock(); -- 2.54.0 From 26e98391322645f2d09aaaaf36d94cae06424a18 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 31 Aug 2022 16:46:51 +0300 Subject: [PATCH 09/51] disable test for non-llvm backends Follow up to fdb88708527742e450e4c566024d9d50ce61dd8d --- test/behavior/translate_c_macros.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index a23907c88f3b0d7e915eb30d01d120e5580e4c68..d670e0cbd432cb7a65996a11b464bdfc587913e1 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -115,5 +115,11 @@ test "cast functions" { } test "large integer macro" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + try expectEqual(@as(c_ulonglong, 18446744073709550592), h.LARGE_INT); } -- 2.54.0 From 540d8cd809f888042406dbcfd292c23ab71b8285 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 31 Aug 2022 19:30:51 +0200 Subject: [PATCH 10/51] coff: add import table definitions --- lib/std/coff.zig | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/lib/std/coff.zig b/lib/std/coff.zig index 1caec57c4a252abcb0587b2be2764b5acb5f0350..ad440e77572fc89cd99211f7eed655d2da4733ba 100644 --- a/lib/std/coff.zig +++ b/lib/std/coff.zig @@ -419,6 +419,91 @@ pub const DebugType = enum(u32) { EX_DLLCHARACTERISTICS = 20, }; +pub const ImportDirectoryEntry = extern struct { + /// The RVA of the import lookup table. + /// This table contains a name or ordinal for each import. + /// (The name "Characteristics" is used in Winnt.h, but no longer describes this field.) + import_lookup_table_rva: u32, + + /// The stamp that is set to zero until the image is bound. + /// After the image is bound, this field is set to the time/data stamp of the DLL. + time_date_stamp: u32, + + /// The index of the first forwarder reference. + forwarder_chain: u32, + + /// The address of an ASCII string that contains the name of the DLL. + /// This address is relative to the image base. + name_rva: u32, + + /// The RVA of the import address table. + /// The contents of this table are identical to the contents of the import lookup table until the image is bound. + import_address_table_rva: u32, +}; + +pub const ImportLookupEntry32 = struct { + pub const ByName = packed struct { + name_table_rva: u31, + flag: u1 = 0, + }; + + pub const ByOrdinal = packed struct { + ordinal_number: u16, + unused: u15 = 0, + flag: u1 = 1, + }; + + const mask = 0x80000000; + + pub fn getImportByName(raw: u32) ?ByName { + if (mask & raw != 0) return null; + return @bitCast(ByName, raw); + } + + pub fn getImportByOrdinal(raw: u32) ?ByOrdinal { + if (mask & raw == 0) return null; + return @bitCast(ByOrdinal, raw); + } +}; + +pub const ImportLookupEntry64 = struct { + pub const ByName = packed struct { + name_table_rva: u31, + unused: u32 = 0, + flag: u1 = 0, + }; + + pub const ByOrdinal = packed struct { + ordinal_number: u16, + unused: u47 = 0, + flag: u1 = 1, + }; + + const mask = 0x8000000000000000; + + pub fn getImportByName(raw: u64) ?ByName { + if (mask & raw != 0) return null; + return @bitCast(ByName, raw); + } + + pub fn getImportByOrdinal(raw: u64) ?ByOrdinal { + if (mask & raw == 0) return null; + return @bitCast(ByOrdinal, raw); + } +}; + +/// Every name ends with a NULL byte. IF the NULL byte does not fall on +/// 2byte boundary, the entry structure is padded to ensure 2byte alignment. +pub const ImportHintNameEntry = extern struct { + /// An index into the export name pointer table. + /// A match is attempted first with this value. If it fails, a binary search is performed on the DLL's export name pointer table. + hint: u16, + + /// Pointer to NULL terminated ASCII name. + /// Variable length... + name: [1]u8, +}; + pub const SectionHeader = extern struct { name: [8]u8, virtual_size: u32, -- 2.54.0 From 36ad0abd7af41ac925da6fefeda19a438205afb6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Aug 2022 13:14:51 -0700 Subject: [PATCH 11/51] CI: drone: avoid notify_lavahut task for PRs --- ci/zinc/drone.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ci/zinc/drone.yml b/ci/zinc/drone.yml index 3aa9e9af7b31c17dbbf1e8cfd1c83db1b4b09e2d..f00eb42dceb2281b6ec7f01cbfcd87e4d86e6786 100644 --- a/ci/zinc/drone.yml +++ b/ci/zinc/drone.yml @@ -75,6 +75,11 @@ steps: depends_on: - macos_package - linux_package + when: + branch: + - master + event: + - push image: ci/debian-amd64:11.1-9 environment: SRHT_OAUTH_TOKEN: -- 2.54.0 From 9a0cb34c734d83d42c70e03e4cc292860af0a17b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Aug 2022 13:15:21 -0700 Subject: [PATCH 12/51] CI: namespace manifest json files to avoid clobbering --- ci/zinc/linux_package | 2 +- ci/zinc/macos_package | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/zinc/linux_package b/ci/zinc/linux_package index f7a7dccbaecb1560b7975f04cdff5a9185a3c623..4881b3ee32a42c0bec2bf1ec8f1c10817ffc9d2c 100755 --- a/ci/zinc/linux_package +++ b/ci/zinc/linux_package @@ -29,7 +29,7 @@ tar cfJ "$TARBALL" "$BASENAME" SHASUM=$(sha256sum $TARBALL | cut '-d ' -f1) BYTESIZE=$(wc -c < $TARBALL) -MANIFEST="manifest.json" +MANIFEST="manifest-$TARGET.json" touch $MANIFEST echo "{\"tarball\": \"$TARBALL\"," >>$MANIFEST echo "\"shasum\": \"$SHASUM\"," >>$MANIFEST diff --git a/ci/zinc/macos_package b/ci/zinc/macos_package index 1ee4d5f18d460eae205819f0686354657e7f80ff..6f866d098c32f97dd0f5091cea4f093bb803d153 100755 --- a/ci/zinc/macos_package +++ b/ci/zinc/macos_package @@ -33,7 +33,7 @@ tar cfJ "$TARBALL" "$BASENAME" SHASUM=$(sha256sum $TARBALL | cut '-d ' -f1) BYTESIZE=$(wc -c < $TARBALL) -MANIFEST="manifest.json" +MANIFEST="manifest-$TARGET.json" touch $MANIFEST echo "{\"tarball\": \"$TARBALL\"," >>$MANIFEST echo "\"shasum\": \"$SHASUM\"," >>$MANIFEST -- 2.54.0 From c5f1b3224fbde8e561e240f85860dc15c88f890c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Aug 2022 13:15:42 -0700 Subject: [PATCH 13/51] CI: update_download_page: change docs deployment The original impetus for making a change here was a typo in --add-header causing the script to fail. However, upon inspection, I was alarmed that we were making a --recursive upload to the *root directory* of ziglang.org. This could result in garbage files being uploaded to the website, or important files being overwritten. As I addressed this concern, I decided to take on file compression as well. Removed compression prior to sending to S3. I am vetoing pre-compressing objects for the following reasons: * It prevents clients from working which do not support gzip encoding. * It breaks a premise that objects on S3 are stored 1-to-1 with what is on disk. * It prevents Cloudflare from using a more efficient encoding, such as brotli, which they have started doing recently. These systems such as Cloudflare or Fastly already do compression on the fly, and we should interop with these systems instead of fighting them. Cloudfront has an arbitrary limit of 9.5 MiB for auto-compression. I looked and did not see a way to increase this limit. The data.js file is currently 16 MiB. In order to fix this problem, we need to do one of the following things: * Reduce the size of data.js to less than 9.5 MiB. * Figure out how to adjust the Cloudfront settings to increase the max size for auto-compressed objects. * Migrate to Fastly. Fastly appears to not have this limitation. Note that we already plan to migrate to Fastly for the website. --- ci/srht/update_download_page | 49 ++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/ci/srht/update_download_page b/ci/srht/update_download_page index 89c26952e5238cf027e6959af9871e30a5a2e099..27a8e4a495a7f0b82f3d9176b4ba884d58311e08 100755 --- a/ci/srht/update_download_page +++ b/ci/srht/update_download_page @@ -101,32 +101,43 @@ CIDIR="$(pwd)" cd "$HOME" -# Upload new stdlib autodocs -mkdir -p docs_to_upload/documentation/master/std/ -gzip -c -9 "$ZIGDIR/docs/std/index.html" > docs_to_upload/documentation/master/std/index.html -gzip -c -9 "$ZIGDIR/docs/std/data.js" > docs_to_upload/documentation/master/std/data.js -gzip -c -9 "$ZIGDIR/docs/std/main.js" > docs_to_upload/documentation/master/std/main.js -gzip -c -9 "$LANGREF" > docs_to_upload/documentation/master/index.html -$S3CMD put -P --no-mime-magic --recursive --add-header="Content-Encoding:gzip" --add-header="Cache-Control: max-age=0, must-revalidate" "docs_to_upload/" s3://ziglang.org/ +# Update autodocs and langref directly to S3 in order to prevent the +# www.ziglang.org git repo from growing too big. -mkdir -p docs_src_to_upload/documentation/master/std/ -cp -r "$ZIGDIR/docs/std/src" docs_src_to_upload/documentation/master/std/ -$S3CMD put -P --no-mime-magic --recursive --add-header:"Content-Type:text/html" --add-header="Cache-Control: max-age=0, must-revalidate" "docs_src_to_upload/" s3://ziglang.org/ +# Please do not edit this script to pre-compress the artifacts before they hit +# S3. This prevents the website from working on browsers that do not support gzip +# encoding. Cloudfront will automatically compress files if they are less than +# 9.5 MiB, and the client advertises itself as capable of decompressing. +# The data.js file is currently 16 MiB. In order to fix this problem, we need to do +# one of the following things: +# * Reduce the size of data.js to less than 9.5 MiB. +# * Figure out how to adjust the Cloudfront settings to increase the max size for +# auto-compressed objects. +# * Migrate to fastly. +$S3CMD put -P --no-mime-magic \ + --add-header="Cache-Control: max-age=0, must-revalidate" \ + "$LANGREF" s3://ziglang.org/documentation/master/index.html -## Copy without compression: -# mkdir -p docs_to_upload/documentation/master/std/ -# cp "$ZIGDIR/docs/std/index.html" docs_to_upload/documentation/master/std/index.html -# cp "$ZIGDIR/docs/std/data.js" docs_to_upload/documentation/master/std/data.js -# cp "$ZIGDIR/docs/std/main.js" docs_to_upload/documentation/master/std/main.js -# cp "$LANGREF" docs_to_upload/documentation/master/index.html -# $S3CMD put -P --no-mime-magic --recursive --add-header="Cache-Control: max-age=0, must-revalidate" "docs_to_upload/" s3://ziglang.org/ +$S3CMD put -P --no-mime-magic \ + --add-header="Cache-Control: max-age=0, must-revalidate" \ + "$ZIGDIR/docs/std/index.html" s3://ziglang.org/documentation/master/std/index.html + +$S3CMD put -P --no-mime-magic \ + --add-header="Cache-Control: max-age=0, must-revalidate" \ + "$ZIGDIR/docs/std/main.js" s3://ziglang.org/documentation/master/std/main.js + +$S3CMD put -P --no-mime-magic \ + --add-header="Cache-Control: max-age=0, must-revalidate" \ + "$ZIGDIR/docs/std/data.js" s3://ziglang.org/documentation/master/std/data.js + +$S3CMD put -P --no-mime-magic \ + --add-header="cache-control: public, max-age=31536000, immutable" \ + "$HOME/$SRC_TARBALL" s3://ziglang.org/builds/ git clone --depth 1 git@github.com:ziglang/www.ziglang.org.git cd www.ziglang.org WWWDIR="$(pwd)" -$S3CMD put -P --no-mime-magic --add-header="cache-control: public, max-age=31536000, immutable" "$HOME/$SRC_TARBALL" s3://ziglang.org/builds/ - cd "$WWWDIR" cp "$CIDIR/out/index.json" data/releases.json git add data/releases.json -- 2.54.0 From 368a20a0513e240c3bb8b6cbfff284a915606345 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Aug 2022 14:22:01 -0700 Subject: [PATCH 14/51] CI: include source listings for autodocs --- ci/srht/update_download_page | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/ci/srht/update_download_page b/ci/srht/update_download_page index 27a8e4a495a7f0b82f3d9176b4ba884d58311e08..a3f02971b21c2cc68663a4a6d910e0228ed11894 100755 --- a/ci/srht/update_download_page +++ b/ci/srht/update_download_page @@ -118,17 +118,9 @@ $S3CMD put -P --no-mime-magic \ --add-header="Cache-Control: max-age=0, must-revalidate" \ "$LANGREF" s3://ziglang.org/documentation/master/index.html -$S3CMD put -P --no-mime-magic \ +$S3CMD put -P --no-mime-magic --recursive \ --add-header="Cache-Control: max-age=0, must-revalidate" \ - "$ZIGDIR/docs/std/index.html" s3://ziglang.org/documentation/master/std/index.html - -$S3CMD put -P --no-mime-magic \ - --add-header="Cache-Control: max-age=0, must-revalidate" \ - "$ZIGDIR/docs/std/main.js" s3://ziglang.org/documentation/master/std/main.js - -$S3CMD put -P --no-mime-magic \ - --add-header="Cache-Control: max-age=0, must-revalidate" \ - "$ZIGDIR/docs/std/data.js" s3://ziglang.org/documentation/master/std/data.js + "$ZIGDIR/docs/std/" s3://ziglang.org/documentation/master/std/ $S3CMD put -P --no-mime-magic \ --add-header="cache-control: public, max-age=31536000, immutable" \ -- 2.54.0 From 77f31ebbbe9aa3c07a7f15a652e4b3baf816d235 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Aug 2022 15:25:06 -0700 Subject: [PATCH 15/51] CI: fix content type of autodocs source listings --- ci/srht/update_download_page | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ci/srht/update_download_page b/ci/srht/update_download_page index a3f02971b21c2cc68663a4a6d910e0228ed11894..928c9977dbd843109349fe7633a3147aea819a0c 100755 --- a/ci/srht/update_download_page +++ b/ci/srht/update_download_page @@ -118,9 +118,22 @@ $S3CMD put -P --no-mime-magic \ --add-header="Cache-Control: max-age=0, must-revalidate" \ "$LANGREF" s3://ziglang.org/documentation/master/index.html +$S3CMD put -P --no-mime-magic \ + --add-header="Cache-Control: max-age=0, must-revalidate" \ + "$ZIGDIR/docs/std/index.html" s3://ziglang.org/documentation/master/std/index.html + +$S3CMD put -P --no-mime-magic \ + --add-header="Cache-Control: max-age=0, must-revalidate" \ + "$ZIGDIR/docs/std/main.js" s3://ziglang.org/documentation/master/std/main.js + +$S3CMD put -P --no-mime-magic \ + --add-header="Cache-Control: max-age=0, must-revalidate" \ + "$ZIGDIR/docs/std/data.js" s3://ziglang.org/documentation/master/std/data.js + $S3CMD put -P --no-mime-magic --recursive \ --add-header="Cache-Control: max-age=0, must-revalidate" \ - "$ZIGDIR/docs/std/" s3://ziglang.org/documentation/master/std/ + --add-header="Content-Type: text/html" \ + "$ZIGDIR/docs/std/src/" s3://ziglang.org/documentation/master/std/src/ $S3CMD put -P --no-mime-magic \ --add-header="cache-control: public, max-age=31536000, immutable" \ -- 2.54.0 From 7a733b9385e77bc6b9aa1c62ca180a6ba9f14f3f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Aug 2022 16:07:08 -0700 Subject: [PATCH 16/51] CI: macos: skip non native We already test cross compiling on x86_64-linux; no need to additionally test it in the macOS script. --- ci/azure/macos_script | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/azure/macos_script b/ci/azure/macos_script index 2bc0e75e6167f07246ca436ddaff65f0f8384791..966db008f93eba0bd03bf8ffa994ff3682ad1dac 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -52,6 +52,7 @@ stage3-release/bin/zig build test docs \ --zig-lib-dir "$(pwd)/../lib" \ -Denable-macos-sdk \ -Dstatic-llvm \ + -Dskip-non-native \ --search-prefix "$PREFIX" if [ "${BUILD_REASON}" != "PullRequest" ]; then -- 2.54.0 From 2b92c5a23e912df56885ff10d690ff7bfd1e3f47 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Wed, 31 Aug 2022 20:53:00 -0500 Subject: [PATCH 17/51] langref: tweak description of `[]T` (#12319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit saying []T is a pointer is confusing because zig docs say there are two types of pointers (*T and [*]T). It is more clear to say that []T is a slice type which contains a [*]T pointer and a length. Co-authored-by: Philipp Lühmann <47984692+luehmann@users.noreply.github.com> --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 0d10119cb735a0e87f008fde44e5664e1f693e70..162ba447003b1f284864fffd66445cfff72af793 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2631,7 +2631,7 @@ test "Conversion between vectors, arrays, and slices" {
    -
  • {#syntax#}[]T{#endsyntax#} - pointer to runtime-known number of items. +
  • {#syntax#}[]T{#endsyntax#} - is a slice (a fat pointer, which contains a pointer of type {#syntax#}[*]T{#endsyntax#} and a length).
    • Supports index syntax: {#syntax#}slice[i]{#endsyntax#}
    • Supports slice syntax: {#syntax#}slice[start..end]{#endsyntax#}
    • -- 2.54.0 From 2cd3989cb32d97c8a60d9cf4154e049815259b46 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 1 Sep 2022 13:16:06 +0300 Subject: [PATCH 18/51] Sema: add more validation to coerceVarArgParam Closes #12706 --- src/Sema.zig | 36 ++++++++++++++--- .../int_literal_passed_as_variadic_arg.zig | 11 ------ .../variadic_arg_validation.zig | 29 ++++++++++++++ test/standalone.zig | 1 + test/standalone/issue_12706/build.zig | 39 +++++++++++++++++++ test/standalone/issue_12706/main.zig | 12 ++++++ test/standalone/issue_12706/test.c | 11 ++++++ 7 files changed, 122 insertions(+), 17 deletions(-) delete mode 100644 test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig create mode 100644 test/cases/compile_errors/variadic_arg_validation.zig create mode 100644 test/standalone/issue_12706/build.zig create mode 100644 test/standalone/issue_12706/main.zig create mode 100644 test/standalone/issue_12706/test.c diff --git a/src/Sema.zig b/src/Sema.zig index 7a96fd51cd595b7ce1bd84dfe080e06a62a8aefa..e643f73962c2d212416cd716b385efee2f202d83 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -24075,16 +24075,40 @@ fn coerceVarArgParam( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { - const inst_ty = sema.typeOf(inst); if (block.is_typeof) return inst; - switch (inst_ty.zigTypeTag()) { + const coerced = switch (sema.typeOf(inst).zigTypeTag()) { // TODO consider casting to c_int/f64 if they fit - .ComptimeInt, .ComptimeFloat => return sema.fail(block, inst_src, "integer and float literals in var args function must be casted", .{}), - else => {}, + .ComptimeInt, .ComptimeFloat => return sema.fail( + block, + inst_src, + "integer and float literals passed variadic function must be casted to a fixed-size number type", + .{}, + ), + .Fn => blk: { + const fn_val = try sema.resolveConstValue(block, .unneeded, inst, undefined); + const fn_decl = fn_val.pointerDecl().?; + break :blk try sema.analyzeDeclRef(fn_decl); + }, + .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), + else => inst, + }; + + const coerced_ty = sema.typeOf(coerced); + if (!sema.validateExternType(coerced_ty, .other)) { + const msg = msg: { + const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl), coerced_ty, .other); + + try sema.addDeclaredHereNote(msg, coerced_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); } - // TODO implement more of this function. - return inst; + return coerced; } // TODO migrate callsites to use storePtr2 instead. diff --git a/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig b/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig deleted file mode 100644 index be9ffaa884c8741db68089a1253cd0cb562ff2f8..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig +++ /dev/null @@ -1,11 +0,0 @@ -extern fn printf([*:0]const u8, ...) c_int; - -pub export fn entry() void { - _ = printf("%d %d %d %d\n", 1, 2, 3, 4); -} - -// error -// backend=stage2 -// target=native -// -// :4:33: error: integer and float literals in var args function must be casted diff --git a/test/cases/compile_errors/variadic_arg_validation.zig b/test/cases/compile_errors/variadic_arg_validation.zig new file mode 100644 index 0000000000000000000000000000000000000000..830d3a087797a64aad7e7f28b51d54a331915c88 --- /dev/null +++ b/test/cases/compile_errors/variadic_arg_validation.zig @@ -0,0 +1,29 @@ +extern fn printf([*:0]const u8, ...) c_int; + +pub export fn entry() void { + _ = printf("%d %d %d %d\n", 1, 2, 3, 4); +} + +pub export fn entry1() void { + var arr: [2]u8 = undefined; + _ = printf("%d\n", arr); +} + +pub export fn entry2() void { + _ = printf("%d\n", @as(u48, 2)); +} + +pub export fn entry3() void { + _ = printf("%d\n", {}); +} + +// error +// backend=stage2 +// target=native +// +// :4:33: error: integer and float literals passed variadic function must be casted to a fixed-size number type +// :9:24: error: arrays must be passed by reference to variadic function +// :13:24: error: cannot pass 'u48' to variadic function +// :13:24: note: only integers with power of two bits are extern compatible +// :17:24: error: cannot pass 'void' to variadic function +// :17:24: note: 'void' is a zero bit type; for C 'void' use 'anyopaque' diff --git a/test/standalone.zig b/test/standalone.zig index bfd683ec4c5b7ac68f4842e1073ef3ff8b326fa7..26af0d5da565d87bdac6443ee1fc12e234372396 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -66,6 +66,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { if (builtin.os.tag == .linux) { cases.addBuildFile("test/standalone/pie/build.zig", .{}); } + cases.addBuildFile("test/standalone/issue_12706/build.zig", .{}); // Ensure the development tools are buildable. diff --git a/test/standalone/issue_12706/build.zig b/test/standalone/issue_12706/build.zig new file mode 100644 index 0000000000000000000000000000000000000000..d84160a4f499a6baf119fe7916eadffb0f545daa --- /dev/null +++ b/test/standalone/issue_12706/build.zig @@ -0,0 +1,39 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const Builder = std.build.Builder; +const CrossTarget = std.zig.CrossTarget; + +// TODO integrate this with the std.build executor API +fn isRunnableTarget(t: CrossTarget) bool { + if (t.isNative()) return true; + + return (t.getOsTag() == builtin.os.tag and + t.getCpuArch() == builtin.cpu.arch); +} + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + const target = b.standardTargetOptions(.{}); + + const exe = b.addExecutable("main", "main.zig"); + exe.setBuildMode(mode); + exe.install(); + + const c_sources = [_][]const u8{ + "test.c", + }; + + exe.addCSourceFiles(&c_sources, &.{}); + exe.linkLibC(); + + exe.setTarget(target); + b.default_step.dependOn(&exe.step); + + const test_step = b.step("test", "Test the program"); + if (isRunnableTarget(target)) { + const run_cmd = exe.run(); + test_step.dependOn(&run_cmd.step); + } else { + test_step.dependOn(&exe.step); + } +} diff --git a/test/standalone/issue_12706/main.zig b/test/standalone/issue_12706/main.zig new file mode 100644 index 0000000000000000000000000000000000000000..b40c997561677c79ce24358d7e334e98c9e5cef1 --- /dev/null +++ b/test/standalone/issue_12706/main.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +extern fn testFnPtr(n: c_int, ...) void; + +const val: c_int = 123; + +fn func(a: c_int) callconv(.C) void { + std.debug.assert(a == val); +} + +pub fn main() void { + testFnPtr(2, func, val); +} diff --git a/test/standalone/issue_12706/test.c b/test/standalone/issue_12706/test.c new file mode 100644 index 0000000000000000000000000000000000000000..30b5c6269778dbf0ea000e2f9a65cb0b5ce0f67b --- /dev/null +++ b/test/standalone/issue_12706/test.c @@ -0,0 +1,11 @@ +#include + +void testFnPtr(int n, ...) { + va_list ap; + va_start(ap, n); + + void (*fnPtr)(int) = va_arg(ap, void (*)(int)); + int arg = va_arg(ap, int); + fnPtr(arg); + va_end(ap); +} \ No newline at end of file -- 2.54.0 From 3bbf08e98a27f8c152087d54d1c4d3dc7ea474a2 Mon Sep 17 00:00:00 2001 From: Der Teufel Date: Thu, 1 Sep 2022 15:17:14 +0200 Subject: [PATCH 19/51] autodoc: Compare operators --- lib/docs/main.js | 24 ++++++++++++++++++++++++ src/Autodoc.zig | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/lib/docs/main.js b/lib/docs/main.js index 6a3f2620f27d0ca692a35c95e03197848a2f86a1..e81d53b685e2d9b954051366e9c3501dfe33126d 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -1405,6 +1405,30 @@ var zigAnalysis; operator += "**"; break; } + case "cmp_eq": { + operator += "=="; + break; + } + case "cmp_neq": { + operator += "!="; + break; + } + case "cmp_gt": { + operator += ">"; + break; + } + case "cmp_gte": { + operator += ">="; + break; + } + case "cmp_lt": { + operator += "<"; + break; + } + case "cmp_lte": { + operator += "<="; + break; + } default: console.log("operator not handled yet or doesn't exist!"); } diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 0e056c093f2614a79a41e65735cc1d2fc4248250..2e4231a40651737d5d9dac3977205aa2b8509e63 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -1278,6 +1278,50 @@ fn walkInstruction( .expr = .{ .binOpIndex = binop_index }, }; }, + // compare operators + .cmp_eq, + .cmp_neq, + .cmp_gt, + .cmp_gte, + .cmp_lt, + .cmp_lte, + => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index); + + const binop_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .binOp = .{ .lhs = 0, .rhs = 0 } }); + + var lhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + parent_line, + extra.data.lhs, + false, + ); + var rhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + parent_line, + extra.data.rhs, + false, + ); + + const lhs_index = self.exprs.items.len; + try self.exprs.append(self.arena, lhs.expr); + const rhs_index = self.exprs.items.len; + try self.exprs.append(self.arena, rhs.expr); + self.exprs.items[binop_index] = .{ .binOp = .{ + .name = @tagName(tags[inst_index]), + .lhs = lhs_index, + .rhs = rhs_index, + } }; + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.bool_type) }, + .expr = .{ .binOpIndex = binop_index }, + }; + }, // builtin functions .align_of, -- 2.54.0 From 7c91a6fe48b7c807a691fb83bf88b25291bb9298 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Sep 2022 12:57:06 -0700 Subject: [PATCH 20/51] CI: fix CLI usage of s3cmd regarding mime types for autodocs --- ci/srht/update_download_page | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/srht/update_download_page b/ci/srht/update_download_page index 928c9977dbd843109349fe7633a3147aea819a0c..b6ff10bbaac95e88a129b2713465d12472277885 100755 --- a/ci/srht/update_download_page +++ b/ci/srht/update_download_page @@ -132,7 +132,7 @@ $S3CMD put -P --no-mime-magic \ $S3CMD put -P --no-mime-magic --recursive \ --add-header="Cache-Control: max-age=0, must-revalidate" \ - --add-header="Content-Type: text/html" \ + -m "text/html" \ "$ZIGDIR/docs/std/src/" s3://ziglang.org/documentation/master/std/src/ $S3CMD put -P --no-mime-magic \ -- 2.54.0 From 36f4f32fad3e88a84b6a10d78df31a4ed2c24465 Mon Sep 17 00:00:00 2001 From: John Schmidt <3405586+schmee@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:13:07 +0200 Subject: [PATCH 21/51] Add AFNOSUPPORT error to bind (#12560) --- lib/std/os.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/std/os.zig b/lib/std/os.zig index 59f2a2173f279dfb23f2c6ee11bff28b29ce8318..a707331a4723dd41d7caddfc34eab4ef677950a9 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3447,6 +3447,9 @@ pub const BindError = error{ /// A nonexistent interface was requested or the requested address was not local. AddressNotAvailable, + /// The address is not valid for the address family of socket. + AddressFamilyNotSupported, + /// Too many symbolic links were encountered in resolving addr. SymLinkLoop, @@ -3502,6 +3505,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi .BADF => unreachable, // always a race condition if this error is returned .INVAL => unreachable, // invalid parameters .NOTSOCK => unreachable, // invalid `sockfd` + .AFNOSUPPORT => return error.AddressFamilyNotSupported, .ADDRNOTAVAIL => return error.AddressNotAvailable, .FAULT => unreachable, // invalid `addr` pointer .LOOP => return error.SymLinkLoop, -- 2.54.0 From 0d3c6b7aa847682445a0d292b7f97035a0bad052 Mon Sep 17 00:00:00 2001 From: Der Teufel Date: Wed, 31 Aug 2022 21:18:50 +0200 Subject: [PATCH 22/51] autodoc: Added int_big support --- lib/docs/main.js | 4 ++++ src/Autodoc.zig | 47 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index 6a3f2620f27d0ca692a35c95e03197848a2f86a1..7db0433b518bd83862f5fab91f4fe08a74f1eb35 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -1555,6 +1555,10 @@ var zigAnalysis; return '"' + escapeHtml(expr.string) + '"'; } + case "int_big": { + return (expr.int_big.negated ? "-" : "") + expr.int_big.value; + } + case "anytype": { return "anytype"; } diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 0e056c093f2614a79a41e65735cc1d2fc4248250..1b70ae20c778f178c806f96e1a36a262c428cfc0 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -625,7 +625,7 @@ const DocData = struct { negated: bool = false, }, int_big: struct { - value: []const u8, // direct value + value: []const u8, // string representation negated: bool = false, }, float: f64, // direct value @@ -714,9 +714,12 @@ const DocData = struct { }, .int_big => { - //@panic("TODO: json serialization of big ints!"); - //if (v.negated) try w.writeAll("-"); - //try jsw.emitNumber(v.value); + try jsw.beginObject(); + try jsw.objectField("value"); + try jsw.emitString(self.int_big.value); + try jsw.objectField("negated"); + try jsw.emitBool(self.int_big.negated); + try jsw.endObject(); }, .builtinField => { try jsw.emitString(@tagName(self.builtinField)); @@ -1084,15 +1087,32 @@ fn walkInstruction( }, .int_big => { // @check - const str = data[inst_index].str.get(file.zir); - _ = str; - printWithContext( - file, - inst_index, - "TODO: implement `{s}` for walkInstruction\n\n", - .{@tagName(tags[inst_index])}, - ); - return self.cteTodo(@tagName(tags[inst_index])); + const str = data[inst_index].str; //.get(file.zir); + const byte_count = str.len * @sizeOf(std.math.big.Limb); + const limb_bytes = file.zir.string_bytes[str.start..][0..byte_count]; + + var limbs = try self.arena.alloc(std.math.big.Limb, str.len); + std.mem.copy(u8, std.mem.sliceAsBytes(limbs), limb_bytes); + + const big_int = std.math.big.int.Const{ + .limbs = limbs, + .positive = true, + }; + + const as_string = try big_int.toStringAlloc(self.arena, 10, .lower); + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, + .expr = .{ .int_big = .{ .value = as_string } }, + }; + + // printWithContext( + // file, + // inst_index, + // "TODO: implement `{s}` for walkInstruction\n\n", + // .{@tagName(tags[inst_index])}, + // ); + // return self.cteTodo(@tagName(tags[inst_index])); }, .slice_start => { @@ -1714,6 +1734,7 @@ fn walkInstruction( ); switch (operand.expr) { .int => |*int| int.negated = true, + .int_big => |*int_big| int_big.negated = true, else => { printWithContext( file, -- 2.54.0 From 4c033eb35c20f0fb37d2e8102735aaa04c8213ae Mon Sep 17 00:00:00 2001 From: Der Teufel Date: Wed, 31 Aug 2022 21:32:58 +0200 Subject: [PATCH 23/51] autodoc: int_big cleanup --- src/Autodoc.zig | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 1b70ae20c778f178c806f96e1a36a262c428cfc0..96985cac40c889c214a22b006d8fe26d837c9371 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -713,7 +713,6 @@ const DocData = struct { try jsw.emitNumber(self.int.value); }, .int_big => { - try jsw.beginObject(); try jsw.objectField("value"); try jsw.emitString(self.int_big.value); @@ -1105,14 +1104,6 @@ fn walkInstruction( .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, .expr = .{ .int_big = .{ .value = as_string } }, }; - - // printWithContext( - // file, - // inst_index, - // "TODO: implement `{s}` for walkInstruction\n\n", - // .{@tagName(tags[inst_index])}, - // ); - // return self.cteTodo(@tagName(tags[inst_index])); }, .slice_start => { -- 2.54.0 From 0d96f1f4fb3b217e11253c33e71973c3cbfb2f96 Mon Sep 17 00:00:00 2001 From: riChar Date: Fri, 2 Sep 2022 17:52:33 +0800 Subject: [PATCH 24/51] zig fmt: remove trailing comma at the end of assembly clobber --- lib/std/zig/parser_test.zig | 22 ++++++++++++++++++++++ lib/std/zig/render.zig | 16 +++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 2bb8c848bc201ac05ad21f9f7f6e0bdad9b1f74e..ab7bea73679ba781801239a4139ac39411219bf8 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -16,6 +16,28 @@ test "zig fmt: preserves clobbers in inline asm with stray comma" { ); } +test "zig fmt: remove trailing comma at the end of assembly clobber" { + try testTransform( + \\fn foo() void { + \\ asm volatile ("" + \\ : [_] "" (-> type), + \\ : + \\ : "clobber1", "clobber2", + \\ ); + \\} + \\ + , + \\fn foo() void { + \\ asm volatile ("" + \\ : [_] "" (-> type), + \\ : + \\ : "clobber1", "clobber2" + \\ ); + \\} + \\ + ); +} + test "zig fmt: respect line breaks in struct field value declaration" { try testCanonical( \\const Foo = struct { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 03f951d0f1b377d24f7bab6d9500edf199ac3617..333fb80d88aeeac8e5f4bcca8395f920890b96b3 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -2114,9 +2114,19 @@ fn renderAsm( return renderToken(ais, tree, tok_i + 1, space); }, .comma => { - try renderToken(ais, tree, tok_i, .none); - try renderToken(ais, tree, tok_i + 1, .space); - tok_i += 2; + switch (token_tags[tok_i + 2]) { + .r_paren => { + ais.setIndentDelta(indent_delta); + ais.popIndent(); + try renderToken(ais, tree, tok_i, .newline); + return renderToken(ais, tree, tok_i + 2, space); + }, + else => { + try renderToken(ais, tree, tok_i, .none); + try renderToken(ais, tree, tok_i + 1, .space); + tok_i += 2; + }, + } }, else => unreachable, } -- 2.54.0 From f7784a081fb399025b579ee7e1337a414af16d49 Mon Sep 17 00:00:00 2001 From: riChar Date: Fri, 2 Sep 2022 17:53:48 +0800 Subject: [PATCH 25/51] stage2: fix panic when the dependency is missing --- src/Sema.zig | 4 +++- src/main.zig | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 7a96fd51cd595b7ce1bd84dfe080e06a62a8aefa..07f842751f6dd2e3914198979253429cedd780be 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10379,7 +10379,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } } unreachable; - } else unreachable; + } else { + return sema.fail(block, operand_src, "no package named '{s}' available", .{operand}); + }; return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, parent }); }, else => { diff --git a/src/main.zig b/src/main.zig index e8a16e194ada911ef797f272b5fd01a21a9cdcd7..6263a6a4026583a593a728f152d192cd3c1566be 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2750,8 +2750,22 @@ fn buildOutputType( // Transfer packages added with --pkg-begin/--pkg-end to the root package if (main_pkg) |pkg| { + var it = pkg_tree_root.table.valueIterator(); + while (it.next()) |p| { + if (p.*.parent == &pkg_tree_root) { + p.*.parent = pkg; + } + } pkg.table = pkg_tree_root.table; pkg_tree_root.table = .{}; + } else { + // Remove any dangling pointers just in case. + var it = pkg_tree_root.table.valueIterator(); + while (it.next()) |p| { + if (p.*.parent == &pkg_tree_root) { + p.*.parent = null; + } + } } const self_exe_path = try introspect.findZigExePath(arena); -- 2.54.0 From 30e85c9c805897fe445d4a0fe09aaf90955fef34 Mon Sep 17 00:00:00 2001 From: Ali Chraghi <63465728+alichraghi@users.noreply.github.com> Date: Fri, 2 Sep 2022 19:11:34 +0430 Subject: [PATCH 26/51] autodoc: highlight target line --- src/autodoc/render_source.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/autodoc/render_source.zig b/src/autodoc/render_source.zig index cafed8d5265c509a3056d54bfa9556cf7efce0ce..ce3ded927f14628dd838ffa9e8ef6fddc2bc1133 100644 --- a/src/autodoc/render_source.zig +++ b/src/autodoc/render_source.zig @@ -79,6 +79,16 @@ pub fn genHtml( \\ text-align: right; \\ color: #999; \\ } + \\ + \\ .line { + \\ width: 100%; + \\ display: inline-block; + \\ } + \\ .line:target { + \\ border-top: 1px solid #444; + \\ border-bottom: 1px solid #444; + \\ background: #333; + \\ } \\ \\ @media (prefers-color-scheme: dark) { \\ body{ -- 2.54.0 From 4462d082240d71e671a8d3d5fb3da81e70c4760e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 1 Sep 2022 13:48:36 +0300 Subject: [PATCH 27/51] stage2 llvm: fix passing packed structs to callconv(.C) functions Closes #12704 --- src/codegen/llvm.zig | 2 ++ test/behavior/packed-struct.zig | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 45426c5ee09970816c96f6a16b9e7b7ae53e1e2c..6c138df060f668129e03dfa1887fd115bbdf5124 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9853,6 +9853,8 @@ const ParamTypeIterator = struct { .AnyFrame, .Vector, => true, + .Struct => ty.containerLayout() == .Packed, + .Union => ty.containerLayout() == .Packed, else => false, }; diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index bd312e9cda79f6792989b2aeabc9817e25ed55a2..9834ba5f3d54c03f6439dd9d38524fec7f93b7a8 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -579,3 +579,29 @@ test "runtime init of unnamed packed struct type" { } }{ .x = z }).m(); } + +test "packed struct passed to callconv(.C) function" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + const S = struct { + const Packed = packed struct { + a: u16, + b: bool = true, + c: bool = true, + d: u46 = 0, + }; + + fn foo(p: Packed, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64) callconv(.C) bool { + return p.a == 12345 and p.b == true and p.c == true and p.d == 0 and a1 == 5 and a2 == 4 and a3 == 3 and a4 == 2 and a5 == 1; + } + }; + const result = S.foo(S.Packed{ + .a = 12345, + .b = true, + .c = true, + }, 5, 4, 3, 2, 1); + try expect(result); +} -- 2.54.0 From 7a8d9af4a94caeb74361c9462cd44a1e4356150b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Sep 2022 13:17:42 +0300 Subject: [PATCH 28/51] stage2 llvm: correct handling of zero-bit types in unionFieldPtr Pointers to zero-bit types are not zero-bit types so the function should return something. Closes #12716 --- src/codegen/llvm.zig | 11 ++++------- test/behavior/union.zig | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6c138df060f668129e03dfa1887fd115bbdf5124..5f8d1539a6648fb404004a0754dab4d8d46d4c6e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9051,7 +9051,7 @@ pub const FuncGen = struct { } }, }, - .Union => return self.unionFieldPtr(inst, struct_ptr, struct_ty, field_index), + .Union => return self.unionFieldPtr(inst, struct_ptr, struct_ty), else => unreachable, } } @@ -9061,16 +9061,13 @@ pub const FuncGen = struct { inst: Air.Inst.Index, union_ptr: *const llvm.Value, union_ty: Type, - field_index: c_uint, ) !?*const llvm.Value { - const union_obj = union_ty.cast(Type.Payload.Union).?.data; - const field = &union_obj.fields.values()[field_index]; const result_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); - if (!field.ty.hasRuntimeBitsIgnoreComptime()) { - return null; - } const target = self.dg.module.getTarget(); const layout = union_ty.unionGetLayout(target); + if (layout.payload_size == 0) { + return self.builder.buildBitCast(union_ptr, result_llvm_ty, ""); + } const payload_index = @boolToInt(layout.tag_align >= layout.payload_align); const union_field_ptr = self.builder.buildStructGEP(union_ptr, payload_index, ""); return self.builder.buildBitCast(union_field_ptr, result_llvm_ty, ""); diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 79bc1861e4ec422950e186875622760b0268f2c5..9053a860a6bb49870f43283447bb45a728362d7a 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1352,3 +1352,31 @@ test "@unionInit uses tag value instead of field index" { } try expect(@enumToInt(u) == 255); } + +test "union field ptr - zero sized payload" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const U = union { + foo: void, + bar: void, + fn bar(_: *void) void {} + }; + var u: U = .{ .foo = {} }; + U.bar(&u.foo); +} + +test "union field ptr - zero sized field" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const U = union { + foo: void, + bar: u32, + fn bar(_: *void) void {} + }; + var u: U = .{ .foo = {} }; + U.bar(&u.foo); +} -- 2.54.0 From 8b58dab78b8a157a39377654691324dd71f77d23 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Sep 2022 13:39:46 +0300 Subject: [PATCH 29/51] Sema: resolve lazy value before intToFloat Closes #12698 --- src/Sema.zig | 4 ++-- src/value.zig | 29 ++++++++++++++++++++++++++--- test/behavior/sizeof_and_typeof.zig | 11 +++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e643f73962c2d212416cd716b385efee2f202d83..4c83d31df832e7f3e870578514ac860a6f2452e7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17499,7 +17499,7 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { const target = sema.mod.getTarget(); - const result_val = try val.intToFloat(sema.arena, operand_ty, dest_ty, target); + const result_val = try val.intToFloatAdvanced(sema.arena, operand_ty, dest_ty, target, sema.kit(block, operand_src)); return sema.addConstant(dest_ty, result_val); } else if (dest_ty.zigTypeTag() == .ComptimeFloat) { return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime known"); @@ -22998,7 +22998,7 @@ fn coerceExtra( } break :int; }; - const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target); + const result_val = try val.intToFloatAdvanced(sema.arena, inst_ty, dest_ty, target, sema.kit(block, inst_src)); // TODO implement this compile error //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty); //if (!int_again_val.eql(val, inst_ty, mod)) { diff --git a/src/value.zig b/src/value.zig index a1961b40f7c8e93d939ebeaeafe0b6e86e51daaf..50f86c7e7961fc23d6f64e07c25eb0a9068fb726 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2940,17 +2940,24 @@ pub const Value = extern union { } pub fn intToFloat(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, target: Target) !Value { + return intToFloatAdvanced(val, arena, int_ty, float_ty, target, null) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => unreachable, + }; + } + + pub fn intToFloatAdvanced(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, target: Target, sema_kit: ?Module.WipAnalysis) !Value { if (int_ty.zigTypeTag() == .Vector) { const result_data = try arena.alloc(Value, int_ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intToFloatScalar(val.indexVectorlike(i), arena, float_ty.scalarType(), target); + scalar.* = try intToFloatScalar(val.indexVectorlike(i), arena, float_ty.scalarType(), target, sema_kit); } return Value.Tag.aggregate.create(arena, result_data); } - return intToFloatScalar(val, arena, float_ty, target); + return intToFloatScalar(val, arena, float_ty, target, sema_kit); } - pub fn intToFloatScalar(val: Value, arena: Allocator, float_ty: Type, target: Target) !Value { + pub fn intToFloatScalar(val: Value, arena: Allocator, float_ty: Type, target: Target, sema_kit: ?Module.WipAnalysis) !Value { switch (val.tag()) { .undef, .zero, .one => return val, .the_only_possible_value => return Value.initTag(.zero), // for i0, u0 @@ -2970,6 +2977,22 @@ pub const Value = extern union { const float = bigIntToFloat(limbs, false); return floatToValue(float, arena, float_ty, target); }, + .lazy_align => { + const ty = val.castTag(.lazy_align).?.data; + if (sema_kit) |sk| { + return intToFloatInner((try ty.abiAlignmentAdvanced(target, .{ .sema_kit = sk })).scalar, arena, float_ty, target); + } else { + return intToFloatInner(ty.abiAlignment(target), arena, float_ty, target); + } + }, + .lazy_size => { + const ty = val.castTag(.lazy_size).?.data; + if (sema_kit) |sk| { + return intToFloatInner((try ty.abiSizeAdvanced(target, .{ .sema_kit = sk })).scalar, arena, float_ty, target); + } else { + return intToFloatInner(ty.abiSize(target), arena, float_ty, target); + } + }, else => unreachable, } } diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 83c5d977bee82e17455a7e9dad57333e94db4737..ab2d59bf835657885caa5a5ea3918db11df24f2f 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -301,3 +301,14 @@ test "array access of generic param in typeof expression" { try expect(S.first("a") == 'a'); comptime try expect(S.first("a") == 'a'); } + +test "lazy size cast to float" { + { + const S = struct { a: u8 }; + try expect(@intToFloat(f32, @sizeOf(S)) == 1.0); + } + { + const S = struct { a: u8 }; + try expect(@as(f32, @sizeOf(S)) == 1.0); + } +} -- 2.54.0 From db54cd247ddb8bf7b4225eaaf1e7a851151f8f0b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Sep 2022 14:05:27 +0300 Subject: [PATCH 30/51] Sema: do not emit dbg_inline_end after NoReturn Closes #12698 --- src/Sema.zig | 8 ++++++-- test/standalone.zig | 2 ++ test/standalone/noreturn_call/as_arg.zig | 8 ++++++++ test/standalone/noreturn_call/inline.zig | 10 ++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 test/standalone/noreturn_call/as_arg.zig create mode 100644 test/standalone/noreturn_call/inline.zig diff --git a/src/Sema.zig b/src/Sema.zig index 4c83d31df832e7f3e870578514ac860a6f2452e7..c089794fa98ddced38fd85a9bb753af55165f0e5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5569,7 +5569,11 @@ fn zirCall( const param_ty_inst = try sema.addType(param_ty); try sema.inst_map.put(sema.gpa, inst, param_ty_inst); - resolved_args[arg_index] = try sema.resolveBody(block, args_body[arg_start..arg_end], inst); + const resolved = try sema.resolveBody(block, args_body[arg_start..arg_end], inst); + if (sema.typeOf(resolved).zigTypeTag() == .NoReturn) { + return resolved; + } + resolved_args[arg_index] = resolved; } return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src); @@ -6047,7 +6051,7 @@ fn analyzeCall( break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges); }; - if (!is_comptime_call) { + if (!is_comptime_call and sema.typeOf(result).zigTypeTag() != .NoReturn) { try sema.emitDbgInline( block, module_fn, diff --git a/test/standalone.zig b/test/standalone.zig index 26af0d5da565d87bdac6443ee1fc12e234372396..c3fbad5377168e9d92becd98b7222d94d8956e6f 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -13,6 +13,8 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.add("test/standalone/guess_number/main.zig"); cases.add("test/standalone/main_return_error/error_u8.zig"); cases.add("test/standalone/main_return_error/error_u8_non_zero.zig"); + cases.add("test/standalone/noreturn_call/inline.zig"); + cases.add("test/standalone/noreturn_call/as_arg.zig"); cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); diff --git a/test/standalone/noreturn_call/as_arg.zig b/test/standalone/noreturn_call/as_arg.zig new file mode 100644 index 0000000000000000000000000000000000000000..08a4f0bd75803280775cd7644bec88b850e0b344 --- /dev/null +++ b/test/standalone/noreturn_call/as_arg.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +fn foo() noreturn { + std.process.exit(0); +} +fn bar(_: u8, _: u8) void {} +pub fn main() void { + bar(foo(), @compileError("bad")); +} diff --git a/test/standalone/noreturn_call/inline.zig b/test/standalone/noreturn_call/inline.zig new file mode 100644 index 0000000000000000000000000000000000000000..436d97896a774f5a84fa344822c9668026c4a432 --- /dev/null +++ b/test/standalone/noreturn_call/inline.zig @@ -0,0 +1,10 @@ +pub fn main() void { + _ = bar(); +} +inline fn bar() u8 { + noret(); +} +const std = @import("std"); +inline fn noret() noreturn { + std.process.exit(0); +} -- 2.54.0 From 1c4c68e6ba058f0c320f222275335245305974a0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Sep 2022 14:16:27 +0300 Subject: [PATCH 31/51] AstGen: use reachableExpr for try operand Closes #12248 --- src/AstGen.zig | 2 +- test/cases/compile_errors/try_return.zig | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/try_return.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 79e5ad963e9efb63edf914cb35de507b7841c951..1502b970178a9c6813605f7843fd4ad48c2b1d04 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -5127,7 +5127,7 @@ fn tryExpr( else => .none, }; // This could be a pointer or value depending on the `rl` parameter. - const operand = try expr(parent_gz, scope, operand_rl, operand_node); + const operand = try reachableExpr(parent_gz, scope, operand_rl, operand_node, node); const is_inline = parent_gz.force_comptime; const is_inline_bit = @as(u2, @boolToInt(is_inline)); const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref)) << 1; diff --git a/test/cases/compile_errors/try_return.zig b/test/cases/compile_errors/try_return.zig new file mode 100644 index 0000000000000000000000000000000000000000..61e71e72b57c38a081818486971e9087771f7d99 --- /dev/null +++ b/test/cases/compile_errors/try_return.zig @@ -0,0 +1,11 @@ +pub fn foo() !void { + try return bar(); +} +pub fn bar() !void {} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: unreachable code +// :2:9: note: control flow is diverted here -- 2.54.0 From 7a51e0befe12c9d17e39f736027a205b1f19bdc6 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Sep 2022 14:59:06 +0300 Subject: [PATCH 32/51] Sema: fix noalias coercion error message Closes #11769 --- src/Sema.zig | 9 ++++++--- src/type.zig | 3 +++ .../compile_errors/noalias_param_coersion.zig | 20 +++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 test/cases/compile_errors/noalias_param_coersion.zig diff --git a/src/Sema.zig b/src/Sema.zig index c089794fa98ddced38fd85a9bb753af55165f0e5..a49aec871bb18a88f2d343a28a9b96d558c16566 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -23426,8 +23426,11 @@ const InMemoryCoercionResult = union(enum) { var index: u6 = 0; var actual_noalias = false; while (true) : (index += 1) { - if (param.actual << index != param.wanted << index) { - actual_noalias = (param.actual << index) == (1 << 31); + const actual = @truncate(u1, param.actual >> index); + const wanted = @truncate(u1, param.wanted >> index); + if (actual != wanted) { + actual_noalias = actual == 1; + break; } } if (!actual_noalias) { @@ -23921,7 +23924,7 @@ fn coerceInMemoryAllowedFns( if (dest_info.noalias_bits != src_info.noalias_bits) { return InMemoryCoercionResult{ .fn_param_noalias = .{ - .actual = dest_info.noalias_bits, + .actual = src_info.noalias_bits, .wanted = dest_info.noalias_bits, } }; } diff --git a/src/type.zig b/src/type.zig index 339485c13722f53b4cebbd5ccb2ac7ec6d3c7889..59e9b163d900caeb464f4caeb2cc62323876f985 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2042,6 +2042,9 @@ pub const Type = extern union { try writer.writeAll("fn("); for (fn_info.param_types) |param_ty, i| { if (i != 0) try writer.writeAll(", "); + if (std.math.cast(u5, i)) |index| if (@truncate(u1, fn_info.noalias_bits >> index) != 0) { + try writer.writeAll("noalias "); + }; if (param_ty.tag() == .generic_poison) { try writer.writeAll("anytype"); } else { diff --git a/test/cases/compile_errors/noalias_param_coersion.zig b/test/cases/compile_errors/noalias_param_coersion.zig new file mode 100644 index 0000000000000000000000000000000000000000..e6dfae037014753aca3b498bb1e564c5900c8c30 --- /dev/null +++ b/test/cases/compile_errors/noalias_param_coersion.zig @@ -0,0 +1,20 @@ +pub export fn entry() void { + comptime var x: fn (noalias *i32, noalias *i32) void = undefined; + x = bar; +} +pub export fn entry1() void { + comptime var x: fn (*i32, *i32) void = undefined; + x = foo; +} + +fn foo(noalias _: *i32, noalias _: *i32) void {} +fn bar(noalias _: *i32, _: *i32) void {} + +// error +// backend=stage2 +// target=native +// +// :3:9: error: expected type 'fn(noalias *i32, noalias *i32) void', found 'fn(noalias *i32, *i32) void' +// :3:9: note: regular parameter 1 cannot cast into a noalias parameter +// :7:9: error: expected type 'fn(*i32, *i32) void', found 'fn(noalias *i32, noalias *i32) void' +// :7:9: note: noalias parameter 0 cannot cast into a regular parameter -- 2.54.0 From a9cdacff95a2a6f60945c7b2a299f9f66bd94ddb Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Sep 2022 15:31:01 +0300 Subject: [PATCH 33/51] Sema: add error for enum tag value overflow Closes #12291 --- src/Sema.zig | 16 ++++++++++++++++ .../overflow_in_enum_value_allocation.zig | 6 +++--- .../specify_enum_tag_type_that_is_too_small.zig | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/overflow_in_enum_value_allocation.zig (50%) rename test/cases/compile_errors/{stage1/obj => }/specify_enum_tag_type_that_is_too_small.zig (66%) diff --git a/src/Sema.zig b/src/Sema.zig index a49aec871bb18a88f2d343a28a9b96d558c16566..1ee5bf71b1fa740b477b3609fb23bd8a5a7fdc1d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2586,6 +2586,7 @@ fn zirEnumDecl( var cur_bit_bag: u32 = undefined; var field_i: u32 = 0; var last_tag_val: ?Value = null; + var tag_val_buf: Value.Payload.U64 = undefined; while (field_i < fields_len) : (field_i += 1) { if (field_i % 32 == 0) { cur_bit_bag = sema.code.extra[bit_bag_index]; @@ -2641,6 +2642,21 @@ fn zirEnumDecl( .ty = enum_obj.tag_ty, .mod = mod, }); + } else { + tag_val_buf = .{ + .base = .{ .tag = .int_u64 }, + .data = field_i, + }; + last_tag_val = Value.initPayload(&tag_val_buf.base); + } + + if (!(try sema.intFitsInType(block, src, last_tag_val.?, enum_obj.tag_ty, null))) { + const tree = try sema.getAstTree(block); + const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, field_i); + const msg = try sema.errMsg(block, field_src, "enumeration value '{}' too large for type '{}'", .{ + last_tag_val.?.fmtValue(enum_obj.tag_ty, mod), enum_obj.tag_ty.fmt(mod), + }); + return sema.failWithOwnedErrorMsg(msg); } } return decl_val; diff --git a/test/cases/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig b/test/cases/compile_errors/overflow_in_enum_value_allocation.zig similarity index 50% rename from test/cases/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig rename to test/cases/compile_errors/overflow_in_enum_value_allocation.zig index c5dc5c1dcf8f697da9504f335a5da40150a4fbc1..2a5b55e86da148eb88a78eab5ba1bdc537a40e16 100644 --- a/test/cases/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig +++ b/test/cases/compile_errors/overflow_in_enum_value_allocation.zig @@ -2,13 +2,13 @@ const Moo = enum(u8) { Last = 255, Over, }; -pub fn main() void { +pub export fn entry() void { var y = Moo.Last; _ = y; } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:5: error: enumeration value 256 too large for type 'u8' +// :3:5: error: enumeration value '256' too large for type 'u8' diff --git a/test/cases/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig b/test/cases/compile_errors/specify_enum_tag_type_that_is_too_small.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig rename to test/cases/compile_errors/specify_enum_tag_type_that_is_too_small.zig index dc7077c8f84d0f1b21aabc89d896b3737aad7a79..d878bec18b0dac8a482d3044d4a4f1d5391775b6 100644 --- a/test/cases/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig +++ b/test/cases/compile_errors/specify_enum_tag_type_that_is_too_small.zig @@ -12,7 +12,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:5: error: enumeration value 4 too large for type 'u2' +// :6:5: error: enumeration value '4' too large for type 'u2' -- 2.54.0 From f281f3d10e4eaedc7c68afc4fcbbfd35e1f29a0f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Sep 2022 16:08:20 +0300 Subject: [PATCH 34/51] Sema: improve behavior of comptime_int backed enums --- src/Sema.zig | 7 ++++--- test/behavior/enum.zig | 7 +++++++ test/behavior/union.zig | 2 +- .../enum_backed_by_comptime_int_must_be_comptime.zig | 11 +++++++++++ 4 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 test/cases/compile_errors/enum_backed_by_comptime_int_must_be_comptime.zig diff --git a/src/Sema.zig b/src/Sema.zig index 1ee5bf71b1fa740b477b3609fb23bd8a5a7fdc1d..6966060842cba8a58a1c95fd82dd4c5d84633fc4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2565,7 +2565,7 @@ fn zirEnumDecl( } } - if (small.nonexhaustive) { + if (small.nonexhaustive and enum_obj.tag_ty.zigTypeTag() != .ComptimeInt) { if (fields_len > 1 and std.math.log2_int(u64, fields_len) == enum_obj.tag_ty.bitSize(sema.mod.getTarget())) { return sema.fail(block, src, "non-exhaustive enum specifies every value", .{}); } @@ -20363,12 +20363,13 @@ fn validateRunTimeType( .Int, .Float, .ErrorSet, - .Enum, .Frame, .AnyFrame, .Void, => return true, + .Enum => return !(try sema.typeRequiresComptime(block, src, ty)), + .BoundFn, .ComptimeFloat, .ComptimeInt, @@ -29049,7 +29050,7 @@ pub fn typeHasOnePossibleValue( }, .enum_nonexhaustive => { const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty; - if (!(try sema.typeHasRuntimeBits(block, src, tag_ty))) { + if (tag_ty.zigTypeTag() != .ComptimeInt and !(try sema.typeHasRuntimeBits(block, src, tag_ty))) { return Value.zero; } else { return null; diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 517414780b40741660b86e7116fec53853e6a29d..28c8785e64e99a719174ac91b8696d07f11b7920 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1175,3 +1175,10 @@ test "Non-exhaustive enum with nonstandard int size behaves correctly" { const E = enum(u15) { _ }; try expect(@sizeOf(E) == @sizeOf(u15)); } + +test "Non-exhaustive enum backed by comptime_int" { + const E = enum(comptime_int) { a, b, c, _ }; + comptime var e: E = .a; + e = @intToEnum(E, 378089457309184723749); + try expect(@enumToInt(e) == 378089457309184723749); +} diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 9053a860a6bb49870f43283447bb45a728362d7a..b94034adf4861d4badcea173372d37e785d064b4 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -690,7 +690,7 @@ test "union with only 1 field casted to its enum type which has enum value speci var e = Expr{ .Literal = Literal{ .Bool = true } }; comptime try expect(Tag(ExprTag) == comptime_int); - var t = @as(ExprTag, e); + comptime var t = @as(ExprTag, e); try expect(t == Expr.Literal); try expect(@enumToInt(t) == 33); comptime try expect(@enumToInt(t) == 33); diff --git a/test/cases/compile_errors/enum_backed_by_comptime_int_must_be_comptime.zig b/test/cases/compile_errors/enum_backed_by_comptime_int_must_be_comptime.zig new file mode 100644 index 0000000000000000000000000000000000000000..7dab294d4a109e7d27f7d68ce659c2adbedd9280 --- /dev/null +++ b/test/cases/compile_errors/enum_backed_by_comptime_int_must_be_comptime.zig @@ -0,0 +1,11 @@ +pub export fn entry() void { + const E = enum(comptime_int) { a, b, c, _ }; + var e: E = .a; + _ = e; +} + +// error +// backend=stage2 +// target=native +// +// :3:12: error: variable of type 'tmp.entry.E' must be const or comptime -- 2.54.0 From 081d5a96907ca844dcbfed0fce32b7ea29d933ab Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Fri, 2 Sep 2022 17:26:41 +0200 Subject: [PATCH 35/51] autodoc: correct line number implementation we also correctly take advantage of the starting byte offset of the parent decl when calling `tree.tokenLocation()`! --- lib/docs/main.js | 2 +- src/Autodoc.zig | 300 +++++++++++++++++++++++++++++------------------ 2 files changed, 184 insertions(+), 118 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index 6a3f2620f27d0ca692a35c95e03197848a2f86a1..e6a9cf3a8f285211dae1b7ad356a2db89055a9ff 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -2285,7 +2285,7 @@ var zigAnalysis; return "[src]"; + zigAnalysis.files[srcNode.file]).replace("{{line}}", srcNode.line + 1) + "\">[src]"; } function renderContainer(container) { diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 0e056c093f2614a79a41e65735cc1d2fc4248250..e574f8b22fd385ee721e833b86e48e543dea97d7 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -1,6 +1,7 @@ const builtin = @import("builtin"); const std = @import("std"); const build_options = @import("build_options"); +const Ast = std.zig.Ast; const Autodoc = @This(); const Compilation = @import("Compilation.zig"); const Module = @import("Module.zig"); @@ -47,6 +48,19 @@ const RefPathResumeInfo = struct { ref_path: []DocData.Expr, }; +/// Used to accumulate src_node offsets. +/// In ZIR, all ast node indices are relative to the parent decl. +/// More concretely, `union_decl`, `struct_decl`, `enum_decl` and `opaque_decl` +/// and the value of each of their decls participate in the relative offset +/// counting, and nothing else. +/// We keep track of the line and byte values for these instructions in order +/// to avoid tokenizing every file (on new lines) from the start every time. +const SrcLocInfo = struct { + bytes: u32 = 0, + line: usize = 0, + src_node: i32 = 0, +}; + var arena_allocator: std.heap.ArenaAllocator = undefined; pub fn init(m: *Module, doc_location: Compilation.EmitLoc) Autodoc { arena_allocator = std.heap.ArenaAllocator.init(m.gpa); @@ -202,7 +216,7 @@ pub fn generateZirData(self: *Autodoc) !void { try self.ast_nodes.append(self.arena, .{ .name = "(root)" }); try self.files.put(self.arena, file, main_type_index); - _ = try self.walkInstruction(file, &root_scope, 1, Zir.main_struct_inst, false); + _ = try self.walkInstruction(file, &root_scope, .{}, Zir.main_struct_inst, false); if (self.ref_paths_pending_on_decls.count() > 0) { @panic("some decl paths were never fully analized (pending on decls)"); @@ -758,8 +772,8 @@ const DocData = struct { const AutodocErrors = error{ OutOfMemory, CurrentWorkingDirectoryUnlinked, - Unexpected, -}; + UnexpectedEndOfFile, +} || std.fs.File.OpenError || std.fs.File.ReadError; /// Called when we need to analyze a Zir instruction. /// For example it gets called by `generateZirData` on instruction 0, @@ -773,7 +787,7 @@ fn walkInstruction( self: *Autodoc, file: *File, parent_scope: *Scope, - parent_line: usize, + parent_src: SrcLocInfo, inst_index: usize, need_type: bool, // true if the caller needs us to provide also a typeRef ) AutodocErrors!DocData.WalkResult { @@ -865,7 +879,7 @@ fn walkInstruction( return self.walkInstruction( new_file, &root_scope, - 1, + .{}, Zir.main_struct_inst, false, ); @@ -890,14 +904,14 @@ fn walkInstruction( return self.walkInstruction( new_file.file, &new_scope, - 1, + .{}, Zir.main_struct_inst, need_type, ); }, .ret_node => { const un_node = data[inst_index].un_node; - return self.walkRef(file, parent_scope, parent_line, un_node.operand, false); + return self.walkRef(file, parent_scope, parent_src, un_node.operand, false); }, .ret_load => { const un_node = data[inst_index].un_node; @@ -928,7 +942,7 @@ fn walkInstruction( } if (result_ref) |rr| { - return self.walkRef(file, parent_scope, parent_line, rr, need_type); + return self.walkRef(file, parent_scope, parent_src, rr, need_type); } return DocData.WalkResult{ @@ -937,11 +951,11 @@ fn walkInstruction( }, .closure_get => { const inst_node = data[inst_index].inst_node; - return try self.walkInstruction(file, parent_scope, parent_line, inst_node.inst, need_type); + return try self.walkInstruction(file, parent_scope, parent_src, inst_node.inst, need_type); }, .closure_capture => { const un_tok = data[inst_index].un_tok; - return try self.walkRef(file, parent_scope, parent_line, un_tok.operand, need_type); + return try self.walkRef(file, parent_scope, parent_src, un_tok.operand, need_type); }, .cmpxchg_strong, .cmpxchg_weak => { const pl_node = data[inst_index].pl_node; @@ -956,7 +970,7 @@ fn walkInstruction( var ptr: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.ptr, false, ); @@ -966,7 +980,7 @@ fn walkInstruction( var expected_value: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.expected_value, false, ); @@ -976,7 +990,7 @@ fn walkInstruction( var new_value: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.new_value, false, ); @@ -986,7 +1000,7 @@ fn walkInstruction( var success_order: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.success_order, false, ); @@ -996,7 +1010,7 @@ fn walkInstruction( var failure_order: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.failure_order, false, ); @@ -1047,10 +1061,11 @@ fn walkInstruction( }, .compile_error => { const un_node = data[inst_index].un_node; + var operand: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, false, ); @@ -1105,14 +1120,14 @@ fn walkInstruction( var lhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.lhs, false, ); var start: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.start, false, ); @@ -1138,21 +1153,21 @@ fn walkInstruction( var lhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.lhs, false, ); var start: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.start, false, ); var end: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.end, false, ); @@ -1180,28 +1195,28 @@ fn walkInstruction( var lhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.lhs, false, ); var start: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.start, false, ); var end: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.end, false, ); var sentinel: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.sentinel, false, ); @@ -1251,14 +1266,14 @@ fn walkInstruction( var lhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.lhs, false, ); var rhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.rhs, false, ); @@ -1319,7 +1334,7 @@ fn walkInstruction( const un_node = data[inst_index].un_node; const bin_index = self.exprs.items.len; try self.exprs.append(self.arena, .{ .builtin = .{ .param = 0 } }); - const param = try self.walkRef(file, parent_scope, parent_line, un_node.operand, false); + const param = try self.walkRef(file, parent_scope, parent_src, un_node.operand, false); const param_index = self.exprs.items.len; try self.exprs.append(self.arena, param.expr); @@ -1368,14 +1383,14 @@ fn walkInstruction( var lhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.lhs, false, ); var rhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.rhs, false, ); @@ -1398,14 +1413,14 @@ fn walkInstruction( var lhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.lhs, false, ); var rhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.rhs, false, ); @@ -1428,14 +1443,14 @@ fn walkInstruction( var lhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.lhs, false, ); var rhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.rhs, false, ); @@ -1455,7 +1470,7 @@ fn walkInstruction( // var operand: DocData.WalkResult = try self.walkRef( // file, - // parent_scope, parent_line, + // parent_scope, parent_src, // un_node.operand, // false, // ); @@ -1464,7 +1479,8 @@ fn walkInstruction( // }, .overflow_arithmetic_ptr => { const un_node = data[inst_index].un_node; - const elem_type_ref = try self.walkRef(file, parent_scope, parent_line, un_node.operand, false); + + const elem_type_ref = try self.walkRef(file, parent_scope, parent_src, un_node.operand, false); const type_slot_index = self.types.items.len; try self.types.append(self.arena, .{ .Pointer = .{ @@ -1489,7 +1505,7 @@ fn walkInstruction( const elem_type_ref = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.elem_type, false, ); @@ -1499,7 +1515,7 @@ fn walkInstruction( var sentinel: ?DocData.Expr = null; if (ptr.flags.has_sentinel) { const ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); - const ref_result = try self.walkRef(file, parent_scope, parent_line, ref, false); + const ref_result = try self.walkRef(file, parent_scope, parent_src, ref, false); sentinel = ref_result.expr; extra_index += 1; } @@ -1507,21 +1523,21 @@ fn walkInstruction( var @"align": ?DocData.Expr = null; if (ptr.flags.has_align) { const ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); - const ref_result = try self.walkRef(file, parent_scope, parent_line, ref, false); + const ref_result = try self.walkRef(file, parent_scope, parent_src, ref, false); @"align" = ref_result.expr; extra_index += 1; } var address_space: ?DocData.Expr = null; if (ptr.flags.has_addrspace) { const ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); - const ref_result = try self.walkRef(file, parent_scope, parent_line, ref, false); + const ref_result = try self.walkRef(file, parent_scope, parent_src, ref, false); address_space = ref_result.expr; extra_index += 1; } var bit_start: ?DocData.Expr = null; if (ptr.flags.has_bit_range) { const ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); - const ref_result = try self.walkRef(file, parent_scope, parent_line, ref, false); + const ref_result = try self.walkRef(file, parent_scope, parent_src, ref, false); address_space = ref_result.expr; extra_index += 1; } @@ -1529,7 +1545,7 @@ fn walkInstruction( var host_size: ?DocData.Expr = null; if (ptr.flags.has_bit_range) { const ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); - const ref_result = try self.walkRef(file, parent_scope, parent_line, ref, false); + const ref_result = try self.walkRef(file, parent_scope, parent_src, ref, false); host_size = ref_result.expr; } @@ -1558,9 +1574,10 @@ fn walkInstruction( }, .array_type => { const pl_node = data[inst_index].pl_node; + const bin = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index).data; - const len = try self.walkRef(file, parent_scope, parent_line, bin.lhs, false); - const child = try self.walkRef(file, parent_scope, parent_line, bin.rhs, false); + const len = try self.walkRef(file, parent_scope, parent_src, bin.lhs, false); + const child = try self.walkRef(file, parent_scope, parent_src, bin.rhs, false); const type_slot_index = self.types.items.len; try self.types.append(self.arena, .{ @@ -1578,9 +1595,9 @@ fn walkInstruction( .array_type_sentinel => { const pl_node = data[inst_index].pl_node; const extra = file.zir.extraData(Zir.Inst.ArrayTypeSentinel, pl_node.payload_index); - const len = try self.walkRef(file, parent_scope, parent_line, extra.data.len, false); - const sentinel = try self.walkRef(file, parent_scope, parent_line, extra.data.sentinel, false); - const elem_type = try self.walkRef(file, parent_scope, parent_line, extra.data.elem_type, false); + const len = try self.walkRef(file, parent_scope, parent_src, extra.data.len, false); + const sentinel = try self.walkRef(file, parent_scope, parent_src, extra.data.sentinel, false); + const elem_type = try self.walkRef(file, parent_scope, parent_src, extra.data.elem_type, false); const type_slot_index = self.types.items.len; try self.types.append(self.arena, .{ @@ -1602,10 +1619,10 @@ fn walkInstruction( const array_data = try self.arena.alloc(usize, operands.len - 1); std.debug.assert(operands.len > 0); - var array_type = try self.walkRef(file, parent_scope, parent_line, operands[0], false); + var array_type = try self.walkRef(file, parent_scope, parent_src, operands[0], false); for (operands[1..]) |op, idx| { - const wr = try self.walkRef(file, parent_scope, parent_line, op, false); + const wr = try self.walkRef(file, parent_scope, parent_src, op, false); const expr_index = self.exprs.items.len; try self.exprs.append(self.arena, wr.expr); array_data[idx] = expr_index; @@ -1623,7 +1640,7 @@ fn walkInstruction( const array_data = try self.arena.alloc(usize, operands.len); for (operands) |op, idx| { - const wr = try self.walkRef(file, parent_scope, parent_line, op, false); + const wr = try self.walkRef(file, parent_scope, parent_src, op, false); const expr_index = self.exprs.items.len; try self.exprs.append(self.arena, wr.expr); array_data[idx] = expr_index; @@ -1641,10 +1658,10 @@ fn walkInstruction( const array_data = try self.arena.alloc(usize, operands.len - 1); std.debug.assert(operands.len > 0); - var array_type = try self.walkRef(file, parent_scope, parent_line, operands[0], false); + var array_type = try self.walkRef(file, parent_scope, parent_src, operands[0], false); for (operands[1..]) |op, idx| { - const wr = try self.walkRef(file, parent_scope, parent_line, op, false); + const wr = try self.walkRef(file, parent_scope, parent_src, op, false); const expr_index = self.exprs.items.len; try self.exprs.append(self.arena, wr.expr); array_data[idx] = expr_index; @@ -1673,7 +1690,7 @@ fn walkInstruction( const array_data = try self.arena.alloc(usize, operands.len); for (operands) |op, idx| { - const wr = try self.walkRef(file, parent_scope, parent_line, op, false); + const wr = try self.walkRef(file, parent_scope, parent_src, op, false); const expr_index = self.exprs.items.len; try self.exprs.append(self.arena, wr.expr); array_data[idx] = expr_index; @@ -1705,10 +1722,11 @@ fn walkInstruction( }, .negate => { const un_node = data[inst_index].un_node; + var operand: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, need_type, ); @@ -1727,10 +1745,11 @@ fn walkInstruction( }, .size_of => { const un_node = data[inst_index].un_node; + const operand = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, false, ); @@ -1744,10 +1763,11 @@ fn walkInstruction( .bit_size_of => { // not working correctly with `align()` const un_node = data[inst_index].un_node; + const operand = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, need_type, ); @@ -1765,7 +1785,7 @@ fn walkInstruction( const operand = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, false, ); @@ -1782,7 +1802,8 @@ fn walkInstruction( const pl_node = data[inst_index].pl_node; const extra = file.zir.extraData(Zir.Inst.SwitchBlock, pl_node.payload_index); const cond_index = self.exprs.items.len; - _ = try self.walkRef(file, parent_scope, parent_line, extra.data.operand, false); + + _ = try self.walkRef(file, parent_scope, parent_src, extra.data.operand, false); const ast_index = self.ast_nodes.items.len; const type_index = self.types.items.len - 1; @@ -1810,7 +1831,7 @@ fn walkInstruction( const operand = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, need_type, ); @@ -1833,10 +1854,11 @@ fn walkInstruction( .typeof => { const un_node = data[inst_index].un_node; + const operand = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, need_type, ); @@ -1852,11 +1874,10 @@ fn walkInstruction( const pl_node = data[inst_index].pl_node; const extra = file.zir.extraData(Zir.Inst.Block, pl_node.payload_index); const body = file.zir.extra[extra.end..][extra.data.body_len - 1]; - var operand: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, data[body].@"break".operand, false, ); @@ -1872,10 +1893,11 @@ fn walkInstruction( .type_info => { // @check const un_node = data[inst_index].un_node; + const operand = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, need_type, ); @@ -1894,7 +1916,7 @@ fn walkInstruction( const dest_type_walk = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.dest_type, false, ); @@ -1902,7 +1924,7 @@ fn walkInstruction( const operand = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.operand, false, ); @@ -1927,10 +1949,11 @@ fn walkInstruction( }, .optional_type => { const un_node = data[inst_index].un_node; + const operand: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, false, ); @@ -2014,7 +2037,7 @@ fn walkInstruction( } } - break :blk try self.walkRef(file, parent_scope, parent_line, lhs_ref, false); + break :blk try self.walkRef(file, parent_scope, parent_src, lhs_ref, false); }; try path.append(self.arena, wr.expr); @@ -2065,7 +2088,7 @@ fn walkInstruction( return self.walkRef( file, parent_scope, - parent_line, + parent_src, getBlockInlineBreak(file.zir, inst_index), need_type, ); @@ -2092,13 +2115,18 @@ fn walkInstruction( Zir.Inst.FieldType, field_pl_node.payload_index, ); + const field_src = try self.srcLocInfo( + file, + field_pl_node.src_node, + parent_src, + ); // On first iteration use field info to find out the struct type if (idx == extra.end) { const wr = try self.walkRef( file, parent_scope, - parent_line, + field_src, field_extra.data.container_type, false, ); @@ -2109,7 +2137,7 @@ fn walkInstruction( const value = try self.walkRef( file, parent_scope, - parent_line, + parent_src, init_extra.data.init, need_type, ); @@ -2123,10 +2151,11 @@ fn walkInstruction( }, .struct_init_empty => { const un_node = data[inst_index].un_node; + var operand: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, false, ); @@ -2159,7 +2188,7 @@ fn walkInstruction( const value = try self.walkRef( file, parent_scope, - parent_line, + parent_src, init_extra.data.init, need_type, ); @@ -2235,7 +2264,7 @@ fn walkInstruction( const pl_node = data[inst_index].pl_node; const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index); - const callee = try self.walkRef(file, parent_scope, parent_line, extra.data.callee, need_type); + const callee = try self.walkRef(file, parent_scope, parent_src, extra.data.callee, need_type); const args_len = extra.data.flags.args_len; var args = try self.arena.alloc(DocData.Expr, args_len); @@ -2250,7 +2279,7 @@ fn walkInstruction( // to show discrepancies between the types of provided // arguments and the types declared in the function // signature for its parameters. - const wr = try self.walkRef(file, parent_scope, parent_line, ref, false); + const wr = try self.walkRef(file, parent_scope, parent_src, ref, false); args[i] = wr.expr; } @@ -2281,7 +2310,7 @@ fn walkInstruction( const result = self.analyzeFunction( file, parent_scope, - parent_line, + parent_src, inst_index, self_ast_node_index, type_slot_index, @@ -2297,7 +2326,7 @@ fn walkInstruction( const result = self.analyzeFancyFunction( file, parent_scope, - parent_line, + parent_src, inst_index, self_ast_node_index, type_slot_index, @@ -2325,7 +2354,7 @@ fn walkInstruction( var array_type: ?DocData.Expr = null; for (args) |arg, idx| { - const wr = try self.walkRef(file, parent_scope, parent_line, arg, idx == 0); + const wr = try self.walkRef(file, parent_scope, parent_src, arg, idx == 0); if (idx == 0) { array_type = wr.typeRef; } @@ -2357,12 +2386,15 @@ fn walkInstruction( .opaque_decl => { const small = @bitCast(Zir.Inst.OpaqueDecl.Small, extended.small); var extra_index: usize = extended.operand; + const src_node: ?i32 = if (small.has_src_node) blk: { const src_node = @bitCast(i32, file.zir.extra[extra_index]); extra_index += 1; break :blk src_node; } else null; - _ = src_node; + + const src_info = try self.srcLocInfo(file, src_node, parent_src); + _ = src_info; const decls_len = if (small.has_decls_len) blk: { const decls_len = file.zir.extra[extra_index]; @@ -2419,7 +2451,8 @@ fn walkInstruction( extra_index += 1; break :blk src_node; } else null; - _ = src_node; + + const src_info = try self.srcLocInfo(file, src_node, parent_src); const tag_type: ?Ref = if (small.has_tag_type) blk: { const tag_type = file.zir.extra[extra_index]; @@ -2470,7 +2503,7 @@ fn walkInstruction( extra_index = try self.walkDecls( file, &scope, - parent_line, + src_info, decls_first_index, decls_len, &decl_indexes, @@ -2491,7 +2524,7 @@ fn walkInstruction( try self.collectUnionFieldInfo( file, &scope, - parent_line, + src_info, fields_len, &field_type_refs, &field_name_indexes, @@ -2541,7 +2574,8 @@ fn walkInstruction( extra_index += 1; break :blk src_node; } else null; - _ = src_node; + + const src_info = try self.srcLocInfo(file, src_node, parent_src); const tag_type: ?Ref = if (small.has_tag_type) blk: { const tag_type = file.zir.extra[extra_index]; @@ -2592,7 +2626,7 @@ fn walkInstruction( extra_index = try self.walkDecls( file, &scope, - parent_line, + src_info, decls_first_index, decls_len, &decl_indexes, @@ -2687,7 +2721,8 @@ fn walkInstruction( extra_index += 1; break :blk src_node; } else null; - _ = src_node; + + const src_info = try self.srcLocInfo(file, src_node, parent_src); const fields_len = if (small.has_fields_len) blk: { const fields_len = file.zir.extra[extra_index]; @@ -2736,7 +2771,7 @@ fn walkInstruction( extra_index = try self.walkDecls( file, &scope, - parent_line, + src_info, decls_first_index, decls_len, &decl_indexes, @@ -2749,7 +2784,7 @@ fn walkInstruction( try self.collectStructFieldInfo( file, &scope, - parent_line, + src_info, fields_len, &field_type_refs, &field_name_indexes, @@ -2793,7 +2828,7 @@ fn walkInstruction( const extra = file.zir.extraData(Zir.Inst.UnNode, extended.operand).data; const bin_index = self.exprs.items.len; try self.exprs.append(self.arena, .{ .builtin = .{ .param = 0 } }); - const param = try self.walkRef(file, parent_scope, parent_line, extra.operand, false); + const param = try self.walkRef(file, parent_scope, parent_src, extra.operand, false); const param_index = self.exprs.items.len; try self.exprs.append(self.arena, param.expr); @@ -2821,7 +2856,7 @@ fn walkDecls( self: *Autodoc, file: *File, scope: *Scope, - parent_line: usize, + parent_src: SrcLocInfo, decls_first_index: usize, decls_len: u32, decl_indexes: *std.ArrayListUnmanaged(usize), @@ -2854,7 +2889,8 @@ fn walkDecls( // const hash_u32s = file.zir.extra[extra_index..][0..4]; extra_index += 4; - const line = parent_line + file.zir.extra[extra_index]; + + //const line = file.zir.extra[extra_index]; extra_index += 1; const decl_name_index = file.zir.extra[extra_index]; extra_index += 1; @@ -2989,12 +3025,17 @@ fn walkDecls( else null; + // This is known to work because decl values are always block_inlines + const data = file.zir.instructions.items(.data); + const value_pl_node = data[value_index].pl_node; + const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src); + // astnode const ast_node_index = idx: { const idx = self.ast_nodes.items.len; try self.ast_nodes.append(self.arena, .{ - .file = self.files.getIndex(file) orelse unreachable, - .line = line, + .file = self.files.getIndex(file).?, + .line = decl_src.line, .col = 0, .docs = doc_comment, .fields = null, // walkInstruction will fill `fields` if necessary @@ -3005,7 +3046,7 @@ fn walkDecls( const walk_result = if (is_test) // TODO: decide if tests should show up at all DocData.WalkResult{ .expr = .{ .void = .{} } } else - try self.walkInstruction(file, scope, line, value_index, true); + try self.walkInstruction(file, scope, decl_src, value_index, true); if (is_pub) { try decl_indexes.append(self.arena, decls_slot_index); @@ -3401,7 +3442,7 @@ fn analyzeFancyFunction( self: *Autodoc, file: *File, scope: *Scope, - parent_line: usize, + parent_src: SrcLocInfo, inst_index: usize, self_ast_node_index: usize, type_slot_index: usize, @@ -3466,7 +3507,7 @@ fn analyzeFancyFunction( const break_index = file.zir.extra[extra.end..][extra.data.body_len - 1]; const break_operand = data[break_index].@"break".operand; - const param_type_ref = try self.walkRef(file, scope, parent_line, break_operand, false); + const param_type_ref = try self.walkRef(file, scope, parent_src, break_operand, false); param_type_refs.appendAssumeCapacity(param_type_ref.expr); }, @@ -3475,8 +3516,8 @@ fn analyzeFancyFunction( self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items; - const inst_data = data[inst_index].pl_node; - const extra = file.zir.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.FuncFancy, pl_node.payload_index); var extra_index: usize = extra.end; @@ -3490,7 +3531,7 @@ fn analyzeFancyFunction( if (extra.data.bits.has_align_ref) { const align_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); align_index = self.exprs.items.len; - _ = try self.walkRef(file, scope, parent_line, align_ref, false); + _ = try self.walkRef(file, scope, parent_src, align_ref, false); extra_index += 1; } else if (extra.data.bits.has_align_body) { const align_body_len = file.zir.extra[extra_index]; @@ -3507,7 +3548,7 @@ fn analyzeFancyFunction( if (extra.data.bits.has_addrspace_ref) { const addrspace_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); addrspace_index = self.exprs.items.len; - _ = try self.walkRef(file, scope, parent_line, addrspace_ref, false); + _ = try self.walkRef(file, scope, parent_src, addrspace_ref, false); extra_index += 1; } else if (extra.data.bits.has_addrspace_body) { const addrspace_body_len = file.zir.extra[extra_index]; @@ -3524,7 +3565,7 @@ fn analyzeFancyFunction( if (extra.data.bits.has_section_ref) { const section_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); section_index = self.exprs.items.len; - _ = try self.walkRef(file, scope, parent_line, section_ref, false); + _ = try self.walkRef(file, scope, parent_src, section_ref, false); extra_index += 1; } else if (extra.data.bits.has_section_body) { const section_body_len = file.zir.extra[extra_index]; @@ -3541,7 +3582,7 @@ fn analyzeFancyFunction( if (extra.data.bits.has_cc_ref) { const cc_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); cc_index = self.exprs.items.len; - _ = try self.walkRef(file, scope, parent_line, cc_ref, false); + _ = try self.walkRef(file, scope, parent_src, cc_ref, false); extra_index += 1; } else if (extra.data.bits.has_cc_body) { const cc_body_len = file.zir.extra[extra_index]; @@ -3560,14 +3601,14 @@ fn analyzeFancyFunction( .none => DocData.Expr{ .void = .{} }, else => blk: { const ref = fn_info.ret_ty_ref; - const wr = try self.walkRef(file, scope, parent_line, ref, false); + const wr = try self.walkRef(file, scope, parent_src, ref, false); break :blk wr.expr; }, }, else => blk: { const last_instr_index = fn_info.ret_ty_body[fn_info.ret_ty_body.len - 1]; const break_operand = data[last_instr_index].@"break".operand; - const wr = try self.walkRef(file, scope, parent_line, break_operand, false); + const wr = try self.walkRef(file, scope, parent_src, break_operand, false); break :blk wr.expr; }, }; @@ -3582,7 +3623,7 @@ fn analyzeFancyFunction( break :blk try self.getGenericReturnType( file, scope, - parent_line, + parent_src, fn_info.body[fn_info.body.len - 1], ); } else { @@ -3619,7 +3660,7 @@ fn analyzeFunction( self: *Autodoc, file: *File, scope: *Scope, - parent_line: usize, + parent_src: SrcLocInfo, inst_index: usize, self_ast_node_index: usize, type_slot_index: usize, @@ -3685,7 +3726,7 @@ fn analyzeFunction( const break_index = file.zir.extra[extra.end..][extra.data.body_len - 1]; const break_operand = data[break_index].@"break".operand; - const param_type_ref = try self.walkRef(file, scope, parent_line, break_operand, false); + const param_type_ref = try self.walkRef(file, scope, parent_src, break_operand, false); param_type_refs.appendAssumeCapacity(param_type_ref.expr); }, @@ -3698,14 +3739,14 @@ fn analyzeFunction( .none => DocData.Expr{ .void = .{} }, else => blk: { const ref = fn_info.ret_ty_ref; - const wr = try self.walkRef(file, scope, parent_line, ref, false); + const wr = try self.walkRef(file, scope, parent_src, ref, false); break :blk wr.expr; }, }, else => blk: { const last_instr_index = fn_info.ret_ty_body[fn_info.ret_ty_body.len - 1]; const break_operand = data[last_instr_index].@"break".operand; - const wr = try self.walkRef(file, scope, parent_line, break_operand, false); + const wr = try self.walkRef(file, scope, parent_src, break_operand, false); break :blk wr.expr; }, }; @@ -3720,7 +3761,7 @@ fn analyzeFunction( break :blk try self.getGenericReturnType( file, scope, - parent_line, + parent_src, fn_info.body[fn_info.body.len - 1], ); } else { @@ -3761,11 +3802,11 @@ fn getGenericReturnType( self: *Autodoc, file: *File, scope: *Scope, - parent_line: usize, // function decl line + parent_src: SrcLocInfo, // function decl line body_end: usize, ) !DocData.Expr { // TODO: compute the correct line offset - const wr = try self.walkInstruction(file, scope, parent_line, body_end, false); + const wr = try self.walkInstruction(file, scope, parent_src, body_end, false); return wr.expr; } @@ -3773,7 +3814,7 @@ fn collectUnionFieldInfo( self: *Autodoc, file: *File, scope: *Scope, - parent_line: usize, + parent_src: SrcLocInfo, fields_len: usize, field_type_refs: *std.ArrayListUnmanaged(DocData.Expr), field_name_indexes: *std.ArrayListUnmanaged(usize), @@ -3820,7 +3861,7 @@ fn collectUnionFieldInfo( // type { - const walk_result = try self.walkRef(file, scope, parent_line, field_type, false); + const walk_result = try self.walkRef(file, scope, parent_src, field_type, false); try field_type_refs.append(self.arena, walk_result.expr); } @@ -3843,7 +3884,7 @@ fn collectStructFieldInfo( self: *Autodoc, file: *File, scope: *Scope, - parent_line: usize, + parent_src: SrcLocInfo, fields_len: usize, field_type_refs: *std.ArrayListUnmanaged(DocData.Expr), field_name_indexes: *std.ArrayListUnmanaged(usize), @@ -3917,7 +3958,7 @@ fn collectStructFieldInfo( for (fields) |field| { const type_expr = expr: { if (field.type_ref != .none) { - const walk_result = try self.walkRef(file, scope, parent_line, field.type_ref, false); + const walk_result = try self.walkRef(file, scope, parent_src, field.type_ref, false); break :expr walk_result.expr; } @@ -3927,7 +3968,7 @@ fn collectStructFieldInfo( const break_inst = body[body.len - 1]; const operand = data[break_inst].@"break".operand; - const walk_result = try self.walkRef(file, scope, parent_line, operand, false); + const walk_result = try self.walkRef(file, scope, parent_src, operand, false); break :expr walk_result.expr; }; @@ -3957,7 +3998,7 @@ fn walkRef( self: *Autodoc, file: *File, parent_scope: *Scope, - parent_line: usize, + parent_src: SrcLocInfo, ref: Ref, need_type: bool, // true when the caller needs also a typeRef for the return value ) AutodocErrors!DocData.WalkResult { @@ -4069,7 +4110,7 @@ fn walkRef( } } else { const zir_index = enum_value - Ref.typed_value_map.len; - return self.walkInstruction(file, parent_scope, parent_line, zir_index, need_type); + return self.walkInstruction(file, parent_scope, parent_src, zir_index, need_type); } } @@ -4122,3 +4163,28 @@ fn writePackageTableToJson( } try jsw.endObject(); } + +fn srcLocInfo( + self: Autodoc, + file: *File, + src_node: ?i32, + parent_src: SrcLocInfo, +) !SrcLocInfo { + if (src_node) |unwrapped_src_node| { + const sn = parent_src.src_node + unwrapped_src_node; + const tree = try file.getTree(self.module.gpa); + const node_idx = @bitCast(Ast.Node.Index, sn); + const tokens = tree.nodes.items(.main_token); + + const tok_idx = tokens[node_idx]; + const start = tree.tokens.items(.start)[tok_idx]; + const loc = tree.tokenLocation(parent_src.bytes, tok_idx); + return .{ + .line = parent_src.line + loc.line, + .bytes = start, + .src_node = sn, + }; + } else { + return parent_src; + } +} -- 2.54.0 From 0b11d8ec4d5414269f5bec3923613f5fc98abedf Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Fri, 2 Sep 2022 17:35:41 +0200 Subject: [PATCH 36/51] codeowners: mark myself as the codeowner of /src/autodoc/ --- .github/CODEOWNERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0eafbafbcea57d50862969b3bf4b3dabf95ea8a4..52306c77bd2cf372f961a0c07662302f4704a7b9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,4 @@ # Autodoc /src/Autodoc.zig @kristoff-it -/lib/docs/* @kristoff-it \ No newline at end of file +/src/autodoc/* @kristoff-it +/lib/docs/* @kristoff-it -- 2.54.0 From a77d7b740fbe52c3822567db28bade2110c9e737 Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Fri, 2 Sep 2022 17:47:04 +0200 Subject: [PATCH 37/51] autodoc: simplify int_big json stringify procedure --- src/Autodoc.zig | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 96985cac40c889c214a22b006d8fe26d837c9371..77b4cb29266fb4ba8d8cc932ce524be7763e8ed9 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -712,14 +712,6 @@ const DocData = struct { if (self.int.negated) try w.writeAll("-"); try jsw.emitNumber(self.int.value); }, - .int_big => { - try jsw.beginObject(); - try jsw.objectField("value"); - try jsw.emitString(self.int_big.value); - try jsw.objectField("negated"); - try jsw.emitBool(self.int_big.negated); - try jsw.endObject(); - }, .builtinField => { try jsw.emitString(@tagName(self.builtinField)); }, -- 2.54.0 From e72a8ed5a10539b03c5dfbf5f230322664e3d275 Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Fri, 2 Sep 2022 17:59:37 +0200 Subject: [PATCH 38/51] autodoc: fix merge mistake --- src/Autodoc.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 3f344f04213c8ca16739193791a725bef9cf9e3d..33943a30665fe4f9ae95a51b837c606dc115061a 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -1313,14 +1313,14 @@ fn walkInstruction( var lhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.lhs, false, ); var rhs: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.rhs, false, ); -- 2.54.0 From 4a08c6dd51ea41cc560dd30d51991d3d0c36ff6a Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Fri, 2 Sep 2022 18:13:17 +0200 Subject: [PATCH 39/51] autodoc: fix stage2 compile error --- src/Autodoc.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 33943a30665fe4f9ae95a51b837c606dc115061a..e6d9c843de722063f2eee58763e1e625a984dcb1 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -4227,7 +4227,7 @@ fn srcLocInfo( const tok_idx = tokens[node_idx]; const start = tree.tokens.items(.start)[tok_idx]; const loc = tree.tokenLocation(parent_src.bytes, tok_idx); - return .{ + return SrcLocInfo{ .line = parent_src.line + loc.line, .bytes = start, .src_node = sn, -- 2.54.0 From 6aee07c1446f3ce98d326998c887fbca3b7fd945 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Sep 2022 19:49:11 +0300 Subject: [PATCH 40/51] Sema: remove unused src param from typeRequiresComptime --- src/Module.zig | 2 +- src/Sema.zig | 64 +++++++++++++++++++++++++------------------------- src/type.zig | 4 ++-- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 9410c4ea4ada67224189391bc69556dadcb45bfd..c63fe43158ba45f8ec9e0b5365ebdcf356364bf5 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4635,7 +4635,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { decl.analysis = .complete; decl.generation = mod.generation; - const has_runtime_bits = try sema.fnHasRuntimeBits(&block_scope, ty_src, decl.ty); + const has_runtime_bits = try sema.fnHasRuntimeBits(decl.ty); if (has_runtime_bits) { // We don't fully codegen the decl until later, but we do need to reserve a global diff --git a/src/Sema.zig b/src/Sema.zig index 6966060842cba8a58a1c95fd82dd4c5d84633fc4..7ab9449dfb8a7e1464f1711569d9bc8dc7b098a4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2865,7 +2865,7 @@ fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const inst_data = sema.code.instructions.items(.data)[inst].node; const src = LazySrcLoc.nodeOffset(inst_data); - if (block.is_comptime or try sema.typeRequiresComptime(block, src, sema.fn_ret_ty)) { + if (block.is_comptime or try sema.typeRequiresComptime(sema.fn_ret_ty)) { const fn_ret_ty = try sema.resolveTypeFields(block, src, sema.fn_ret_ty); return sema.analyzeComptimeAlloc(block, fn_ret_ty, 0, src); } @@ -5788,7 +5788,7 @@ fn analyzeCall( var is_comptime_call = block.is_comptime or modifier == .compile_time; var comptime_only_ret_ty = false; if (!is_comptime_call) { - if (sema.typeRequiresComptime(block, func_src, func_ty_info.return_type)) |ct| { + if (sema.typeRequiresComptime(func_ty_info.return_type)) |ct| { is_comptime_call = ct; comptime_only_ret_ty = ct; } else |err| switch (err) { @@ -6226,7 +6226,7 @@ fn analyzeInlineCallArg( const param_ty = try sema.analyzeAsType(param_block, param_src, param_ty_inst); new_fn_info.param_types[arg_i.*] = param_ty; const uncasted_arg = uncasted_args[arg_i.*]; - if (try sema.typeRequiresComptime(arg_block, arg_src, param_ty)) { + if (try sema.typeRequiresComptime(param_ty)) { _ = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to parameter with comptime only type must be comptime known") catch |err| { if (err == error.AnalysisFail and sema.err != null) { try sema.addComptimeReturnTypeNote(arg_block, func, func_src, ret_ty, sema.err.?, comptime_only_ret_ty); @@ -6328,7 +6328,7 @@ fn analyzeGenericCallArg( ) !void { const is_runtime = comptime_arg.val.tag() == .generic_poison and comptime_arg.ty.hasRuntimeBits() and - !(try sema.typeRequiresComptime(block, arg_src, comptime_arg.ty)); + !(try sema.typeRequiresComptime(comptime_arg.ty)); if (is_runtime) { const param_ty = new_fn_info.param_types[runtime_i.*]; const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); @@ -6593,7 +6593,7 @@ fn instantiateGenericCall( } } else if (is_anytype) { const arg_ty = sema.typeOf(arg); - if (try sema.typeRequiresComptime(block, .unneeded, arg_ty)) { + if (try sema.typeRequiresComptime(arg_ty)) { const arg_val = try sema.resolveConstValue(block, .unneeded, arg, undefined); const child_arg = try child_sema.addConstant(arg_ty, arg_val); child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); @@ -6646,7 +6646,7 @@ fn instantiateGenericCall( const arg = child_sema.inst_map.get(inst).?; const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator); - if (try sema.typeRequiresComptime(block, .unneeded, copied_arg_ty)) { + if (try sema.typeRequiresComptime(copied_arg_ty)) { is_comptime = true; } @@ -6677,7 +6677,7 @@ fn instantiateGenericCall( // If the call evaluated to a return type that requires comptime, never mind // our generic instantiation. Instead we need to perform a comptime call. const new_fn_info = new_decl.ty.fnInfo(); - if (try sema.typeRequiresComptime(block, call_src, new_fn_info.return_type)) { + if (try sema.typeRequiresComptime(new_fn_info.return_type)) { return error.ComptimeReturn; } // Similarly, if the call evaluated to a generic type we need to instead @@ -7858,7 +7858,7 @@ fn funcCommon( } var ret_ty_requires_comptime = false; - const ret_poison = if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| rp: { + const ret_poison = if (sema.typeRequiresComptime(bare_return_type)) |ret_comptime| rp: { ret_ty_requires_comptime = ret_comptime; break :rp bare_return_type.tag() == .generic_poison; } else |err| switch (err) { @@ -8092,7 +8092,7 @@ fn analyzeParameter( cc: std.builtin.CallingConvention, has_body: bool, ) !void { - const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty); + const requires_comptime = try sema.typeRequiresComptime(param.ty); comptime_params[i] = param.is_comptime or requires_comptime; const this_generic = param.ty.tag() == .generic_poison; is_generic.* = is_generic.* or this_generic; @@ -8197,7 +8197,7 @@ fn zirParam( } }; const is_comptime = comptime_syntax or - try sema.typeRequiresComptime(block, src, param_ty); + try sema.typeRequiresComptime(param_ty); if (sema.inst_map.get(inst)) |arg| { if (is_comptime) { // We have a comptime value for this parameter so it should be elided from the @@ -8257,7 +8257,7 @@ fn zirParamAnytype( if (sema.inst_map.get(inst)) |air_ref| { const param_ty = sema.typeOf(air_ref); - if (comptime_syntax or try sema.typeRequiresComptime(block, src, param_ty)) { + if (comptime_syntax or try sema.typeRequiresComptime(param_ty)) { // We have a comptime value for this parameter so it should be elided from the // function type of the function instruction in this block. return; @@ -20368,7 +20368,7 @@ fn validateRunTimeType( .Void, => return true, - .Enum => return !(try sema.typeRequiresComptime(block, src, ty)), + .Enum => return !(try sema.typeRequiresComptime(ty)), .BoundFn, .ComptimeFloat, @@ -20402,7 +20402,7 @@ fn validateRunTimeType( .Struct, .Union => { const resolved_ty = try sema.resolveTypeFields(block, src, ty); - const needs_comptime = try sema.typeRequiresComptime(block, src, resolved_ty); + const needs_comptime = try sema.typeRequiresComptime(resolved_ty); return !needs_comptime; }, }; @@ -20510,7 +20510,7 @@ fn explainWhyTypeIsComptimeInner( .range = .type, }); - if (try sema.typeRequiresComptime(block, src, field.ty)) { + if (try sema.typeRequiresComptime(field.ty)) { try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{}); try sema.explainWhyTypeIsComptimeInner(block, src, msg, field_src_loc, field.ty, type_set); } @@ -20530,7 +20530,7 @@ fn explainWhyTypeIsComptimeInner( .range = .type, }); - if (try sema.typeRequiresComptime(block, src, field.ty)) { + if (try sema.typeRequiresComptime(field.ty)) { try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{}); try sema.explainWhyTypeIsComptimeInner(block, src, msg, field_src_loc, field.ty, type_set); } @@ -27627,7 +27627,7 @@ pub fn resolveTypeLayout( // In case of querying the ABI alignment of this optional, we will ask // for hasRuntimeBits() of the payload type, so we need "requires comptime" // to be known already before this function returns. - _ = try sema.typeRequiresComptime(block, src, payload_ty); + _ = try sema.typeRequiresComptime(payload_ty); return sema.resolveTypeLayout(block, src, payload_ty); }, .ErrorUnion => { @@ -27682,7 +27682,7 @@ fn resolveStructLayout( // for hasRuntimeBits() of each field, so we need "requires comptime" // to be known already before this function returns. for (struct_obj.fields.values()) |field, i| { - _ = sema.typeRequiresComptime(block, src, field.ty) catch |err| switch (err) { + _ = sema.typeRequiresComptime(field.ty) catch |err| switch (err) { error.AnalysisFail => { const msg = sema.err orelse return err; try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{}); @@ -27914,7 +27914,7 @@ fn resolveStructFully( } // And let's not forget comptime-only status. - _ = try sema.typeRequiresComptime(block, src, ty); + _ = try sema.typeRequiresComptime(ty); } fn resolveUnionFully( @@ -27947,7 +27947,7 @@ fn resolveUnionFully( } // And let's not forget comptime-only status. - _ = try sema.typeRequiresComptime(block, src, ty); + _ = try sema.typeRequiresComptime(ty); } pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type { @@ -29582,7 +29582,7 @@ fn typePtrOrOptionalPtrTy( /// TODO assert the return value matches `ty.comptimeOnly` /// TODO merge these implementations together with the "advanced"/sema_kit pattern seen /// elsewhere in value.zig -pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { +pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { return switch (ty.tag()) { .u1, .u8, @@ -29673,7 +29673,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ .array, .array_sentinel, .vector, - => return sema.typeRequiresComptime(block, src, ty.childType()), + => return sema.typeRequiresComptime(ty.childType()), .pointer, .single_const_pointer, @@ -29689,7 +29689,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ if (child_ty.zigTypeTag() == .Fn) { return child_ty.fnInfo().is_generic; } else { - return sema.typeRequiresComptime(block, src, child_ty); + return sema.typeRequiresComptime(child_ty); } }, @@ -29698,14 +29698,14 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ .optional_single_const_pointer, => { var buf: Type.Payload.ElemType = undefined; - return sema.typeRequiresComptime(block, src, ty.optionalChild(&buf)); + return sema.typeRequiresComptime(ty.optionalChild(&buf)); }, .tuple, .anon_struct => { const tuple = ty.tupleFields(); for (tuple.types) |field_ty, i| { const have_comptime_val = tuple.values[i].tag() != .unreachable_value; - if (!have_comptime_val and try sema.typeRequiresComptime(block, src, field_ty)) { + if (!have_comptime_val and try sema.typeRequiresComptime(field_ty)) { return true; } } @@ -29726,7 +29726,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ struct_obj.requires_comptime = .wip; for (struct_obj.fields.values()) |field| { if (field.is_comptime) continue; - if (try sema.typeRequiresComptime(block, src, field.ty)) { + if (try sema.typeRequiresComptime(field.ty)) { struct_obj.requires_comptime = .yes; return true; } @@ -29750,7 +29750,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ union_obj.requires_comptime = .wip; for (union_obj.fields.values()) |field| { - if (try sema.typeRequiresComptime(block, src, field.ty)) { + if (try sema.typeRequiresComptime(field.ty)) { union_obj.requires_comptime = .yes; return true; } @@ -29761,18 +29761,18 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ } }, - .error_union => return sema.typeRequiresComptime(block, src, ty.errorUnionPayload()), + .error_union => return sema.typeRequiresComptime(ty.errorUnionPayload()), .anyframe_T => { const child_ty = ty.castTag(.anyframe_T).?.data; - return sema.typeRequiresComptime(block, src, child_ty); + return sema.typeRequiresComptime(child_ty); }, .enum_numbered => { const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty; - return sema.typeRequiresComptime(block, src, tag_ty); + return sema.typeRequiresComptime(tag_ty); }, .enum_full, .enum_nonexhaustive => { const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty; - return sema.typeRequiresComptime(block, src, tag_ty); + return sema.typeRequiresComptime(tag_ty); }, }; } @@ -29810,7 +29810,7 @@ fn unionFieldAlignment( } /// Synchronize logic with `Type.isFnOrHasRuntimeBits`. -pub fn fnHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { +pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { const fn_info = ty.fnInfo(); if (fn_info.is_generic) return false; if (fn_info.is_var_args) return true; @@ -29819,7 +29819,7 @@ pub fn fnHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) C .Inline => return false, else => {}, } - if (try sema.typeRequiresComptime(block, src, fn_info.return_type)) { + if (try sema.typeRequiresComptime(fn_info.return_type)) { return false; } return true; diff --git a/src/type.zig b/src/type.zig index 59e9b163d900caeb464f4caeb2cc62323876f985..0d48c5e46a7398e7d128460e97f17354f2147dcf 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2401,7 +2401,7 @@ pub const Type = extern union { } else if (ty.childType().zigTypeTag() == .Fn) { return !ty.childType().fnInfo().is_generic; } else if (sema_kit) |sk| { - return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty)); + return !(try sk.sema.typeRequiresComptime(ty)); } else { return !comptimeOnly(ty); } @@ -2440,7 +2440,7 @@ pub const Type = extern union { if (ignore_comptime_only) { return true; } else if (sema_kit) |sk| { - return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, child_ty)); + return !(try sk.sema.typeRequiresComptime(child_ty)); } else { return !comptimeOnly(child_ty); } -- 2.54.0 From 10e11b60e56941cb664648dcebfd4db3d2efed30 Mon Sep 17 00:00:00 2001 From: yujiri8 Date: Fri, 2 Sep 2022 14:12:20 -0400 Subject: [PATCH 41/51] zig fmt: don't delete container doc comments Fixes #12617 --- lib/std/zig/parser_test.zig | 15 +++++++++++++++ lib/std/zig/render.zig | 9 ++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index ab7bea73679ba781801239a4139ac39411219bf8..0e1817ffabbd4944602b4e4438f8beaf06a94bd6 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -5057,6 +5057,21 @@ test "zig fmt: make single-line if no trailing comma" { ); } +test "zig fmt: preserve container doc comment in container without trailing comma" { + try testTransform( + \\const A = enum(u32) { + \\//! comment + \\_ }; + \\ + , + \\const A = enum(u32) { + \\ //! comment + \\ _, + \\}; + \\ + ); +} + test "zig fmt: make single-line if no trailing comma" { try testCanonical( \\// Test trailing comma syntax diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 333fb80d88aeeac8e5f4bcca8395f920890b96b3..bc59ddc279237d139b7f8d419fbaecc68db5f7c7 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -1933,12 +1933,15 @@ fn renderContainerDecl( break :one_line; } - // 2. A member of the container has a doc comment. + // 2. The container has a container comment. + if (token_tags[lbrace + 1] == .container_doc_comment) break :one_line; + + // 3. A member of the container has a doc comment. for (token_tags[lbrace + 1 .. rbrace - 1]) |tag| { if (tag == .doc_comment) break :one_line; } - // 3. The container has non-field members. + // 4. The container has non-field members. for (container_decl.ast.members) |member| { if (!node_tags[member].isContainerField()) break :one_line; } @@ -2358,7 +2361,7 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us } } -/// Returns true if there exists a comment between any of the tokens from +/// Returns true if there exists a line comment between any of the tokens from /// `start_token` to `end_token`. This is used to determine if e.g. a /// fn_proto should be wrapped and have a trailing comma inserted even if /// there is none in the source. -- 2.54.0 From b83c037f9ffd7a4285de41c95827615fcbdbbf2f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Sep 2022 20:26:33 +0300 Subject: [PATCH 42/51] Sema: only ABI sized packed structs are extern compatible --- src/Sema.zig | 47 +++++++---- src/arch/wasm/abi.zig | 8 ++ src/arch/x86_64/abi.zig | 12 +++ src/codegen/llvm.zig | 57 ++++++-------- test/c_abi/cfuncs.c | 78 +++++++------------ test/c_abi/main.zig | 33 +++----- ..._ABI_compatible_type_or_has_align_attr.zig | 2 +- ...non-extern_non-packed_struct_parameter.zig | 2 +- ..._non-extern_non-packed_union_parameter.zig | 2 +- 9 files changed, 116 insertions(+), 125 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 7ab9449dfb8a7e1464f1711569d9bc8dc7b098a4..539b44c6127831144d0c42c18426415acbae6a69 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5056,7 +5056,7 @@ pub fn analyzeExport( try mod.ensureDeclAnalyzed(exported_decl_index); const exported_decl = mod.declPtr(exported_decl_index); - if (!sema.validateExternType(exported_decl.ty, .other)) { + if (!try sema.validateExternType(block, src, exported_decl.ty, .other)) { const msg = msg: { const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -7896,7 +7896,7 @@ fn funcCommon( }; return sema.failWithOwnedErrorMsg(msg); } - if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !sema.validateExternType(return_type, .ret_ty)) { + if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !try sema.validateExternType(block, ret_ty_src, return_type, .ret_ty)) { const msg = msg: { const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ return_type.fmt(sema.mod), @tagName(cc_workaround), @@ -8115,7 +8115,7 @@ fn analyzeParameter( }; return sema.failWithOwnedErrorMsg(msg); } - if (!Type.fnCallingConventionAllowsZigTypes(cc) and !sema.validateExternType(param.ty, .param_ty)) { + if (!Type.fnCallingConventionAllowsZigTypes(cc) and !try sema.validateExternType(block, param_src, param.ty, .param_ty)) { const msg = msg: { const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ param.ty.fmt(sema.mod), @tagName(cc), @@ -15583,7 +15583,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) { return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); } else if (inst_data.size == .C) { - if (!sema.validateExternType(elem_ty, .other)) { + if (!try sema.validateExternType(block, elem_ty_src, elem_ty, .other)) { const msg = msg: { const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -16681,7 +16681,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in } else if (ptr_size == .Many and elem_ty.zigTypeTag() == .Opaque) { return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{}); } else if (ptr_size == .C) { - if (!sema.validateExternType(elem_ty, .other)) { + if (!try sema.validateExternType(block, src, elem_ty, .other)) { const msg = msg: { const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -20549,7 +20549,14 @@ const ExternPosition = enum { /// Returns true if `ty` is allowed in extern types. /// Does *NOT* require `ty` to be resolved in any way. -fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) bool { +/// Calls `resolveTypeLayout` for packed containers. +fn validateExternType( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + ty: Type, + position: ExternPosition, +) !bool { switch (ty.zigTypeTag()) { .Type, .ComptimeFloat, @@ -20577,17 +20584,25 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) bool { .Fn => return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention()), .Enum => { var buf: Type.Payload.Bits = undefined; - return sema.validateExternType(ty.intTagType(&buf), position); + return sema.validateExternType(block, src, ty.intTagType(&buf), position); }, .Struct, .Union => switch (ty.containerLayout()) { - .Extern, .Packed => return true, - else => return false, + .Extern => return true, + .Packed => { + const target = sema.mod.getTarget(); + const bit_size = try ty.bitSizeAdvanced(target, sema.kit(block, src)); + switch (bit_size) { + 8, 16, 32, 64, 128 => return true, + else => return false, + } + }, + .Auto => return false, }, .Array => { if (position == .ret_ty or position == .param_ty) return false; - return sema.validateExternType(ty.elemType2(), .other); + return sema.validateExternType(block, src, ty.elemType2(), .other); }, - .Vector => return sema.validateExternType(ty.elemType2(), .other), + .Vector => return sema.validateExternType(block, src, ty.elemType2(), .other), .Optional => return ty.isPtrLikeOptional(), } } @@ -20639,8 +20654,8 @@ fn explainWhyTypeIsNotExtern( try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)}); try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position); }, - .Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}), - .Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}), + .Struct => try mod.errNoteNonLazy(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}), + .Union => try mod.errNoteNonLazy(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}), .Array => { if (position == .ret_ty) { return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{}); @@ -24119,7 +24134,7 @@ fn coerceVarArgParam( }; const coerced_ty = sema.typeOf(coerced); - if (!sema.validateExternType(coerced_ty, .other)) { + if (!try sema.validateExternType(block, inst_src, coerced_ty, .other)) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -28321,7 +28336,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void }; return sema.failWithOwnedErrorMsg(msg); } - if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) { + if (struct_obj.layout == .Extern and !try sema.validateExternType(&block_scope, src, field.ty, .other)) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); const fields_src = enumFieldSrcLoc(decl, tree.*, 0, i); @@ -28658,7 +28673,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { }; return sema.failWithOwnedErrorMsg(msg); } - if (union_obj.layout == .Extern and !sema.validateExternType(field_ty, .union_field)) { + if (union_obj.layout == .Extern and !try sema.validateExternType(&block_scope, src, field_ty, .union_field)) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); diff --git a/src/arch/wasm/abi.zig b/src/arch/wasm/abi.zig index b1e99f4f926eea6eb7fd5ccf547da77d34a05f73..d54965a50ca4dbbd2ab063d964b67e68ba03ee68 100644 --- a/src/arch/wasm/abi.zig +++ b/src/arch/wasm/abi.zig @@ -23,6 +23,10 @@ pub fn classifyType(ty: Type, target: Target) [2]Class { if (!ty.hasRuntimeBitsIgnoreComptime()) return none; switch (ty.zigTypeTag()) { .Struct => { + if (ty.containerLayout() == .Packed) { + if (ty.bitSize(target) <= 64) return direct; + return .{ .direct, .direct }; + } // When the struct type is non-scalar if (ty.structFieldCount() > 1) return memory; // When the struct's alignment is non-natural @@ -57,6 +61,10 @@ pub fn classifyType(ty: Type, target: Target) [2]Class { return direct; }, .Union => { + if (ty.containerLayout() == .Packed) { + if (ty.bitSize(target) <= 64) return direct; + return .{ .direct, .direct }; + } const layout = ty.unionGetLayout(target); std.debug.assert(layout.tag_size == 0); if (ty.unionFields().count() > 1) return memory; diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 7e2025a23db7f775e073a559a7c5fa4f15bc650a..7fede95155911e1db6e6e5a8a0ad1f328aa87bb2 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -174,6 +174,12 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { // "If the size of the aggregate exceeds a single eightbyte, each is classified // separately.". const ty_size = ty.abiSize(target); + if (ty.containerLayout() == .Packed) { + assert(ty_size <= 128); + result[0] = .integer; + if (ty_size > 64) result[1] = .integer; + return result; + } if (ty_size > 64) return memory_class; @@ -284,6 +290,12 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { // "If the size of the aggregate exceeds a single eightbyte, each is classified // separately.". const ty_size = ty.abiSize(target); + if (ty.containerLayout() == .Packed) { + assert(ty_size <= 128); + result[0] = .integer; + if (ty_size > 64) result[1] = .integer; + return result; + } if (ty_size > 64) return memory_class; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5f8d1539a6648fb404004a0754dab4d8d46d4c6e..e7f4e123e3cca828b1a9116f06da49b29f4ebd7f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9674,22 +9674,7 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. } }, .C => { - const is_scalar = switch (fn_info.return_type.zigTypeTag()) { - .Void, - .Bool, - .NoReturn, - .Int, - .Float, - .Pointer, - .Optional, - .ErrorSet, - .Enum, - .AnyFrame, - .Vector, - => true, - - else => false, - }; + const is_scalar = isScalar(fn_info.return_type); switch (target.cpu.arch) { .mips, .mipsel => return dg.lowerType(fn_info.return_type), .x86_64 => switch (target.os.tag) { @@ -9837,24 +9822,7 @@ const ParamTypeIterator = struct { @panic("TODO implement async function lowering in the LLVM backend"); }, .C => { - const is_scalar = switch (ty.zigTypeTag()) { - .Void, - .Bool, - .NoReturn, - .Int, - .Float, - .Pointer, - .Optional, - .ErrorSet, - .Enum, - .AnyFrame, - .Vector, - => true, - .Struct => ty.containerLayout() == .Packed, - .Union => ty.containerLayout() == .Packed, - - else => false, - }; + const is_scalar = isScalar(ty); switch (it.target.cpu.arch) { .riscv32, .riscv64 => { it.zig_index += 1; @@ -10108,6 +10076,27 @@ fn isByRef(ty: Type) bool { } } +fn isScalar(ty: Type) bool { + return switch (ty.zigTypeTag()) { + .Void, + .Bool, + .NoReturn, + .Int, + .Float, + .Pointer, + .Optional, + .ErrorSet, + .Enum, + .AnyFrame, + .Vector, + => true, + + .Struct => ty.containerLayout() == .Packed, + .Union => ty.containerLayout() == .Packed, + else => false, + }; +} + /// This function returns true if we expect LLVM to lower x86_fp80 correctly /// and false if we expect LLVM to crash if it counters an x86_fp80 type. fn backendSupportsF80(target: std.Target) bool { diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index 391e87fc6755acff758b6f6e427b2cffc16fcdbf..439de8bbaef50f39a6c8c6e1fd57fe804d23fdf1 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -86,24 +86,8 @@ struct MedStructMixed { void zig_med_struct_mixed(struct MedStructMixed); struct MedStructMixed zig_ret_med_struct_mixed(); -struct SmallPackedStruct { - uint8_t a: 2; - uint8_t b: 2; - uint8_t c: 2; - uint8_t d: 2; - uint8_t e: 1; -}; - -struct BigPackedStruct { - uint64_t a: 64; - uint64_t b: 64; - uint64_t c: 64; - uint64_t d: 64; - uint8_t e: 8; -}; - -//void zig_small_packed_struct(struct SmallPackedStruct); // #1481 -void zig_big_packed_struct(struct BigPackedStruct); +void zig_small_packed_struct(uint8_t); +void zig_big_packed_struct(__int128); struct SplitStructInts { uint64_t a; @@ -176,13 +160,19 @@ void run_c_tests(void) { } { - struct BigPackedStruct s = {1, 2, 3, 4, 5}; + __int128 s = 0; + s |= 1 << 0; + s |= (__int128)2 << 64; zig_big_packed_struct(s); } { - struct SmallPackedStruct s = {0, 1, 2, 3, 1}; - //zig_small_packed_struct(s); + uint8_t s = 0; + s |= 0 << 0; + s |= 1 << 2; + s |= 2 << 4; + s |= 3 << 6; + zig_small_packed_struct(s); } { @@ -378,42 +368,32 @@ void c_split_struct_mixed(struct SplitStructMixed x) { assert_or_panic(y.c == 1337.0f); } -struct SmallPackedStruct c_ret_small_packed_struct() { - struct SmallPackedStruct s = { - .a = 0, - .b = 1, - .c = 2, - .d = 3, - .e = 1, - }; +uint8_t c_ret_small_packed_struct() { + uint8_t s = 0; + s |= 0 << 0; + s |= 1 << 2; + s |= 2 << 4; + s |= 3 << 6; return s; } -void c_small_packed_struct(struct SmallPackedStruct x) { - assert_or_panic(x.a == 0); - assert_or_panic(x.a == 1); - assert_or_panic(x.a == 2); - assert_or_panic(x.a == 3); - assert_or_panic(x.e == 1); +void c_small_packed_struct(uint8_t x) { + assert_or_panic(((x >> 0) & 0x3) == 0); + assert_or_panic(((x >> 2) & 0x3) == 1); + assert_or_panic(((x >> 4) & 0x3) == 2); + assert_or_panic(((x >> 6) & 0x3) == 3); } -struct BigPackedStruct c_ret_big_packed_struct() { - struct BigPackedStruct s = { - .a = 1, - .b = 2, - .c = 3, - .d = 4, - .e = 5, - }; +__int128 c_ret_big_packed_struct() { + __int128 s = 0; + s |= 1 << 0; + s |= (__int128)2 << 64; return s; } -void c_big_packed_struct(struct BigPackedStruct x) { - assert_or_panic(x.a == 1); - assert_or_panic(x.b == 2); - assert_or_panic(x.c == 3); - assert_or_panic(x.d == 4); - assert_or_panic(x.e == 5); +void c_big_packed_struct(__int128 x) { + assert_or_panic(((x >> 0) & 0xFFFFFFFFFFFFFFFF) == 1); + assert_or_panic(((x >> 64) & 0xFFFFFFFFFFFFFFFF) == 2); } struct SplitStructMixed c_ret_split_struct_mixed() { diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 145bbc384a58a008ab3ecaaf3ad6235c21788771..a34e4eda8f55c52c700bd79042f67a7017bde98e 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -263,37 +263,30 @@ const SmallPackedStruct = packed struct { b: u2, c: u2, d: u2, - e: bool, }; -const c_small_packed_struct: fn (SmallPackedStruct) callconv(.C) void = @compileError("TODO: #1481"); +extern fn c_small_packed_struct(SmallPackedStruct) void; extern fn c_ret_small_packed_struct() SmallPackedStruct; -// waiting on #1481 -//export fn zig_small_packed_struct(x: SmallPackedStruct) void { -// expect(x.a == 0) catch @panic("test failure"); -// expect(x.b == 1) catch @panic("test failure"); -// expect(x.c == 2) catch @panic("test failure"); -// expect(x.d == 3) catch @panic("test failure"); -// expect(x.e) catch @panic("test failure"); -//} +export fn zig_small_packed_struct(x: SmallPackedStruct) void { + expect(x.a == 0) catch @panic("test failure"); + expect(x.b == 1) catch @panic("test failure"); + expect(x.c == 2) catch @panic("test failure"); + expect(x.d == 3) catch @panic("test failure"); +} test "C ABI small packed struct" { - var s = SmallPackedStruct{ .a = 0, .b = 1, .c = 2, .d = 3, .e = true }; - _ = s; //c_small_packed_struct(s); // waiting on #1481 + var s = SmallPackedStruct{ .a = 0, .b = 1, .c = 2, .d = 3 }; + c_small_packed_struct(s); var s2 = c_ret_small_packed_struct(); try expect(s2.a == 0); try expect(s2.b == 1); try expect(s2.c == 2); try expect(s2.d == 3); - try expect(s2.e); } const BigPackedStruct = packed struct { a: u64, b: u64, - c: u64, - d: u64, - e: u8, }; extern fn c_big_packed_struct(BigPackedStruct) void; extern fn c_ret_big_packed_struct() BigPackedStruct; @@ -301,20 +294,14 @@ extern fn c_ret_big_packed_struct() BigPackedStruct; export fn zig_big_packed_struct(x: BigPackedStruct) void { expect(x.a == 1) catch @panic("test failure"); expect(x.b == 2) catch @panic("test failure"); - expect(x.c == 3) catch @panic("test failure"); - expect(x.d == 4) catch @panic("test failure"); - expect(x.e == 5) catch @panic("test failure"); } test "C ABI big packed struct" { - var s = BigPackedStruct{ .a = 1, .b = 2, .c = 3, .d = 4, .e = 5 }; + var s = BigPackedStruct{ .a = 1, .b = 2 }; c_big_packed_struct(s); var s2 = c_ret_big_packed_struct(); try expect(s2.a == 1); try expect(s2.b == 2); - try expect(s2.c == 3); - try expect(s2.d == 4); - try expect(s2.e == 5); } const SplitStructInt = extern struct { diff --git a/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig b/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig index 1b9118aa6408d72afe51e3cfa8b825d59a9b26ad..1472c7d5baf4c09e90e7697c766908ac91c0a561 100644 --- a/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig +++ b/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig @@ -10,5 +10,5 @@ export fn a() void { // target=native // // :3:19: error: C pointers cannot point to non-C-ABI-compatible type 'tmp.Foo' -// :3:19: note: only structs with packed or extern layout are extern compatible +// :3:19: note: only extern structs and ABI sized packed structs are extern compatible // :1:13: note: struct declared here diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig index 0007a2014e17512c53e130dea022e4b2b0903213..55ee27764112ef4470e1f4ea2204d95880d11163 100644 --- a/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig @@ -10,5 +10,5 @@ export fn entry(foo: Foo) void { _ = foo; } // target=native // // :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' -// :6:17: note: only structs with packed or extern layout are extern compatible +// :6:17: note: only extern structs and ABI sized packed structs are extern compatible // :1:13: note: struct declared here diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig index 001d235e18a1bf5978880d147b972a3bf8103e53..f848392c90921b1483d7b43d464e02c4f453ed3b 100644 --- a/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig @@ -10,5 +10,5 @@ export fn entry(foo: Foo) void { _ = foo; } // target=native // // :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' -// :6:17: note: only unions with packed or extern layout are extern compatible +// :6:17: note: only extern unions and ABI sized packed unions are extern compatible // :1:13: note: union declared here -- 2.54.0 From 0f61d1f0df887081d60558256e10944633eb868f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 3 Sep 2022 01:05:46 +0300 Subject: [PATCH 43/51] stage2 llvm: improve handling of i128 on Windows C ABI --- src/arch/x86_64/abi.zig | 12 ++++++++++-- src/codegen/llvm.zig | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 7fede95155911e1db6e6e5a8a0ad1f328aa87bb2..344fe235f3c8d965167fbdf44c3483851036b3d8 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -5,7 +5,7 @@ const assert = std.debug.assert; const Register = @import("bits.zig").Register; const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none }; +pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none, win_i128 }; pub fn classifyWindows(ty: Type, target: Target) Class { // https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017 @@ -34,7 +34,15 @@ pub fn classifyWindows(ty: Type, target: Target) Class { => switch (ty.abiSize(target)) { 0 => unreachable, 1, 2, 4, 8 => return .integer, - else => return .memory, + else => switch (ty.zigTypeTag()) { + .Int => return .win_i128, + .Struct, .Union => if (ty.containerLayout() == .Packed) { + return .win_i128; + } else { + return .memory; + }, + else => return .memory, + }, }, .Float, .Vector => return .sse, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e7f4e123e3cca828b1a9116f06da49b29f4ebd7f..004d152e1fadc1f279208da65ebefa58dea9c03b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9687,6 +9687,7 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. return dg.context.intType(@intCast(c_uint, abi_size * 8)); } }, + .win_i128 => return dg.context.intType(64).vectorType(2), .memory => return dg.context.voidType(), .sse => return dg.lowerType(fn_info.return_type), else => unreachable, @@ -9727,6 +9728,7 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. @panic("TODO"); }, .memory => unreachable, // handled above + .win_i128 => unreachable, // windows only .none => break, } } @@ -9851,6 +9853,11 @@ const ParamTypeIterator = struct { return .abi_sized_int; } }, + .win_i128 => { + it.zig_index += 1; + it.llvm_index += 1; + return .byref; + }, .memory => { it.zig_index += 1; it.llvm_index += 1; @@ -9905,6 +9912,7 @@ const ParamTypeIterator = struct { @panic("TODO"); }, .memory => unreachable, // handled above + .win_i128 => unreachable, // windows only .none => break, } } -- 2.54.0 From c7884af063791211544c6595a4900bbfcd5d96b6 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 3 Sep 2022 01:55:01 +0300 Subject: [PATCH 44/51] translate-c: do not translate packed C structs as packed Zig structs in stage2 Zig's integer backed packed structs are not compatible with C's packed structs. --- src/translate_c.zig | 4 ++++ test/run_translated_c.zig | 26 ++++++++++++++------------ test/translate_c.zig | 30 ++++++++++++++++-------------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index b0fae81475235ece607e79141bfbbc85915b8e87..faa8a456f58456c3828b3cd40f08f071b16e6a69 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1166,6 +1166,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD }); } + if (!c.zig_is_stage1 and is_packed) { + return failDecl(c, record_loc, bare_name, "cannot translate packed record union", .{}); + } + const record_payload = try c.arena.create(ast.Payload.Record); record_payload.* = .{ .base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] }, diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 4345625dc1f2d4f89f710ae530db9e6df4547532..5aa72e2d1fb97be12ff42b4abd835b3e34a5666f 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -250,18 +250,20 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\} , ""); - cases.add("struct initializer - packed", - \\#define _NO_CRT_STDIO_INLINE 1 - \\#include - \\#include - \\struct s {uint8_t x,y; - \\ uint32_t z;} __attribute__((packed)) s0 = {1, 2}; - \\int main() { - \\ /* sizeof nor offsetof currently supported */ - \\ if (((intptr_t)&s0.z - (intptr_t)&s0.x) != 2) abort(); - \\ return 0; - \\} - , ""); + if (@import("builtin").zig_backend == .stage1) { + cases.add("struct initializer - packed", + \\#define _NO_CRT_STDIO_INLINE 1 + \\#include + \\#include + \\struct s {uint8_t x,y; + \\ uint32_t z;} __attribute__((packed)) s0 = {1, 2}; + \\int main() { + \\ /* sizeof nor offsetof currently supported */ + \\ if (((intptr_t)&s0.z - (intptr_t)&s0.x) != 2) abort(); + \\ return 0; + \\} + , ""); + } cases.add("cast signed array index to unsigned", \\#include diff --git a/test/translate_c.zig b/test/translate_c.zig index 637d491f49071efbcc0d8295c3a5dde2491178ab..54b4ad60818a3fc524f5edaa3af7b35a5bb24b8a 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -728,20 +728,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("struct initializer - packed", - \\struct {int x,y,z;} __attribute__((packed)) s0 = {1, 2}; - , &[_][]const u8{ - \\const struct_unnamed_1 = packed struct { - \\ x: c_int, - \\ y: c_int, - \\ z: c_int, - \\}; - \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{ - \\ .x = @as(c_int, 1), - \\ .y = @as(c_int, 2), - \\ .z = 0, - \\}; - }); + if (builtin.zig_backend == .stage1) { + cases.add("struct initializer - packed", + \\struct {int x,y,z;} __attribute__((packed)) s0 = {1, 2}; + , &[_][]const u8{ + \\const struct_unnamed_1 = packed struct { + \\ x: c_int, + \\ y: c_int, + \\ z: c_int, + \\}; + \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{ + \\ .x = @as(c_int, 1), + \\ .y = @as(c_int, 2), + \\ .z = 0, + \\}; + }); + } // Test case temporarily disabled: // https://github.com/ziglang/zig/issues/12055 -- 2.54.0 From 5b3ca4bb53c3e56025e4cb04f11d6b6df74d9ad9 Mon Sep 17 00:00:00 2001 From: Ali Chraghi <63465728+alichraghi@users.noreply.github.com> Date: Sun, 4 Sep 2022 14:13:47 +0430 Subject: [PATCH 45/51] autodoc: fix highlighted line in light mode --- src/autodoc/render_source.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/autodoc/render_source.zig b/src/autodoc/render_source.zig index ce3ded927f14628dd838ffa9e8ef6fddc2bc1133..ceba230276ea4e334f451879c449f188341a6bd3 100644 --- a/src/autodoc/render_source.zig +++ b/src/autodoc/render_source.zig @@ -85,9 +85,9 @@ pub fn genHtml( \\ display: inline-block; \\ } \\ .line:target { - \\ border-top: 1px solid #444; - \\ border-bottom: 1px solid #444; - \\ background: #333; + \\ border-top: 1px solid #ccc; + \\ border-bottom: 1px solid #ccc; + \\ background: #fafafa; \\ } \\ \\ @media (prefers-color-scheme: dark) { @@ -100,6 +100,11 @@ pub fn genHtml( \\ background: #222; \\ border: unset; \\ } + \\ .line:target { + \\ border-top: 1px solid #444; + \\ border-bottom: 1px solid #444; + \\ background: #333; + \\ } \\ .tok-kw { \\ color: #eee; \\ } -- 2.54.0 From 349cf54b3293bc4177253e96a4f84fea0251fa08 Mon Sep 17 00:00:00 2001 From: riChar Date: Sun, 4 Sep 2022 23:44:45 +0800 Subject: [PATCH 46/51] llvm: fix the `type` parameter of `GlobalAlias` Closes 12680 --- src/codegen/llvm.zig | 24 ++++++++++++------------ test/behavior.zig | 1 + test/behavior/bugs/12680.zig | 17 +++++++++++++++++ test/behavior/bugs/12680_other_file.zig | 8 ++++++++ 4 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 test/behavior/bugs/12680.zig create mode 100644 test/behavior/bugs/12680_other_file.zig diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 004d152e1fadc1f279208da65ebefa58dea9c03b..3da66acc18ab509c782681d37b00c23c4e1f86a7 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -287,6 +287,15 @@ pub fn supportsTailCall(target: std.Target) bool { } } +/// TODO can this be done with simpler logic / different API binding? +fn deleteLlvmGlobal(llvm_global: *const llvm.Value) void { + if (llvm_global.globalGetValueType().getTypeKind() == .Function) { + llvm_global.deleteFunction(); + return; + } + return llvm_global.deleteGlobal(); +} + pub const Object = struct { gpa: Allocator, module: *Module, @@ -640,7 +649,7 @@ pub const Object = struct { const new_global_ptr = other_global.constBitCast(llvm_global.typeOf()); llvm_global.replaceAllUsesWith(new_global_ptr); - object.deleteLlvmGlobal(llvm_global); + deleteLlvmGlobal(llvm_global); entry.value_ptr.* = new_global_ptr; } object.extern_collisions.clearRetainingCapacity(); @@ -666,7 +675,7 @@ pub const Object = struct { const new_global_ptr = llvm_global.constBitCast(other_global.typeOf()); other_global.replaceAllUsesWith(new_global_ptr); llvm_global.takeName(other_global); - other_global.deleteGlobal(); + deleteLlvmGlobal(other_global); // Problem: now we need to replace in the decl_map that // the extern decl index points to this new global. However we don't // know the decl index. @@ -1184,15 +1193,6 @@ pub const Object = struct { return null; } - /// TODO can this be done with simpler logic / different API binding? - fn deleteLlvmGlobal(o: Object, llvm_global: *const llvm.Value) void { - if (o.llvm_module.getNamedFunction(llvm_global.getValueName()) != null) { - llvm_global.deleteFunction(); - return; - } - return llvm_global.deleteGlobal(); - } - pub fn updateDeclExports( self: *Object, module: *Module, @@ -1287,7 +1287,7 @@ pub const Object = struct { alias.setAliasee(llvm_global); } else { _ = self.llvm_module.addAlias( - llvm_global.typeOf(), + llvm_global.globalGetValueType(), 0, llvm_global, exp_name_z, diff --git a/test/behavior.zig b/test/behavior.zig index 8f581f372ef55698201e4c7d330592956c1a915e..4b55913af59ca7051718ff21912c8847772d80b4 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -85,6 +85,7 @@ test { _ = @import("behavior/bugs/12033.zig"); _ = @import("behavior/bugs/12430.zig"); _ = @import("behavior/bugs/12486.zig"); + _ = @import("behavior/bugs/12680.zig"); _ = @import("behavior/byteswap.zig"); _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/call.zig"); diff --git a/test/behavior/bugs/12680.zig b/test/behavior/bugs/12680.zig new file mode 100644 index 0000000000000000000000000000000000000000..c7bd8f63aa1f884ebeba138b96e3aae1c2a2b81c --- /dev/null +++ b/test/behavior/bugs/12680.zig @@ -0,0 +1,17 @@ +const std = @import("std"); +const expectEqual = std.testing.expectEqual; +const other_file = @import("12680_other_file.zig"); +const builtin = @import("builtin"); + +extern fn test_func() callconv(.C) usize; + +test "export a function twice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + // If it exports the function correctly, `test_func` and `testFunc` will points to the same address. + try expectEqual(test_func(), other_file.testFunc()); +} diff --git a/test/behavior/bugs/12680_other_file.zig b/test/behavior/bugs/12680_other_file.zig new file mode 100644 index 0000000000000000000000000000000000000000..32b9dc1b27743a7345e3e9189a79a0126aa923fb --- /dev/null +++ b/test/behavior/bugs/12680_other_file.zig @@ -0,0 +1,8 @@ +// export this function twice +pub export fn testFunc() callconv(.C) usize { + return @ptrToInt(&testFunc); +} + +comptime { + @export(testFunc, .{ .name = "test_func", .linkage = .Strong }); +} -- 2.54.0 From ae3a5ff7f962cba1eb57423e37ffaf94dc394152 Mon Sep 17 00:00:00 2001 From: Yujiri Date: Fri, 2 Sep 2022 07:51:41 +0000 Subject: [PATCH 47/51] Fix #12440: std.math.big.Rational order/orderAbs --- lib/std/math/big/rational.zig | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/std/math/big/rational.zig b/lib/std/math/big/rational.zig index 895b20d9b5436fb7b6a2e6c26046a5a905d26726..61e2194eea1549e3413324d9e5198729192fb75b 100644 --- a/lib/std/math/big/rational.zig +++ b/lib/std/math/big/rational.zig @@ -334,13 +334,13 @@ pub const Rational = struct { /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a /// > b respectively. pub fn order(a: Rational, b: Rational) !math.Order { - return cmpInternal(a, b, true); + return cmpInternal(a, b, false); } /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| == /// |b| or |a| > |b| respectively. pub fn orderAbs(a: Rational, b: Rational) !math.Order { - return cmpInternal(a, b, false); + return cmpInternal(a, b, true); } // p/q > x/y iff p*y > x*q @@ -704,6 +704,18 @@ test "big.rational order" { try testing.expect((try a.order(b)) == .eq); } +test "big.rational order/orderAbs with negative" { + var a = try Rational.init(testing.allocator); + defer a.deinit(); + var b = try Rational.init(testing.allocator); + defer b.deinit(); + + try a.setRatio(1, 1); + try b.setRatio(-2, 1); + try testing.expect((try a.order(b)) == .gt); + try testing.expect((try a.orderAbs(b)) == .lt); +} + test "big.rational add single-limb" { var a = try Rational.init(testing.allocator); defer a.deinit(); -- 2.54.0 From 9ce841a0f04be0aed70d69abad7e4aacca754e3c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 3 Sep 2022 15:47:58 +0300 Subject: [PATCH 48/51] stage2 llvm: implement aarch64 C ABI ... at least enough to pass all the current tests. --- src/arch/aarch64/abi.zig | 63 ++++++++++++++++++++++++++++ src/codegen/llvm.zig | 91 ++++++++++++++++++++++++++++++++++++++++ test/c_abi/main.zig | 2 +- test/standalone.zig | 3 ++ 4 files changed, 158 insertions(+), 1 deletion(-) diff --git a/src/arch/aarch64/abi.zig b/src/arch/aarch64/abi.zig index cb28b1fffa820cc08cf458ef4c0107797728cdac..f26a9a8a8a477dbf67972c72cbd5635fe178ac2c 100644 --- a/src/arch/aarch64/abi.zig +++ b/src/arch/aarch64/abi.zig @@ -3,6 +3,69 @@ const builtin = @import("builtin"); const bits = @import("bits.zig"); const Register = bits.Register; const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; +const Type = @import("../../type.zig").Type; + +pub const Class = enum { memory, integer, none, float_array }; + +pub fn classifyType(ty: Type, target: std.Target) [2]Class { + if (!ty.hasRuntimeBitsIgnoreComptime()) return .{ .none, .none }; + switch (ty.zigTypeTag()) { + .Struct => { + if (ty.containerLayout() == .Packed) return .{ .integer, .none }; + + if (ty.structFieldCount() <= 4) { + const fields = ty.structFields(); + var float_size: ?u64 = null; + for (fields.values()) |field| { + if (field.ty.zigTypeTag() != .Float) break; + const field_size = field.ty.bitSize(target); + const prev_size = float_size orelse { + float_size = field_size; + continue; + }; + if (field_size != prev_size) break; + } else { + return .{ .float_array, .none }; + } + } + const bit_size = ty.bitSize(target); + if (bit_size > 128) return .{ .memory, .none }; + if (bit_size > 64) return .{ .integer, .integer }; + return .{ .integer, .none }; + }, + .Union => { + const bit_size = ty.bitSize(target); + if (bit_size > 128) return .{ .memory, .none }; + if (bit_size > 64) return .{ .integer, .integer }; + return .{ .integer, .none }; + }, + .Int, .Enum, .ErrorSet, .Vector, .Float, .Bool => return .{ .integer, .none }, + .Array => return .{ .memory, .none }, + .Optional => { + std.debug.assert(ty.isPtrLikeOptional()); + return .{ .integer, .none }; + }, + .Pointer => { + std.debug.assert(!ty.isSlice()); + return .{ .integer, .none }; + }, + .ErrorUnion, + .Frame, + .AnyFrame, + .NoReturn, + .Void, + .Type, + .ComptimeFloat, + .ComptimeInt, + .Undefined, + .Null, + .BoundFn, + .Fn, + .Opaque, + .EnumLiteral, + => unreachable, + } +} const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct { pub const callee_preserved_regs = [_]Register{ diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 3da66acc18ab509c782681d37b00c23c4e1f86a7..80ffd7a6652b474497a3e299b55d183f819e426c 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -23,6 +23,7 @@ const LazySrcLoc = Module.LazySrcLoc; const CType = @import("../type.zig").CType; const x86_64_abi = @import("../arch/x86_64/abi.zig"); const wasm_c_abi = @import("../arch/wasm/abi.zig"); +const aarch64_c_abi = @import("../arch/aarch64/abi.zig"); const Error = error{ OutOfMemory, CodegenFail }; @@ -1086,6 +1087,26 @@ pub const Object = struct { try args.ensureUnusedCapacity(1); args.appendAssumeCapacity(casted); }, + .float_array => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const param_llvm_ty = try dg.lowerType(param_ty); + const param = llvm_func.getParam(llvm_arg_i); + llvm_arg_i += 1; + + const alignment = param_ty.abiAlignment(target); + const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty); + arg_ptr.setAlignment(alignment); + const casted_ptr = builder.buildBitCast(arg_ptr, param.typeOf().pointerType(0), ""); + _ = builder.buildStore(param, casted_ptr); + + if (isByRef(param_ty)) { + try args.append(arg_ptr); + } else { + const load_inst = builder.buildLoad(arg_ptr, ""); + load_inst.setAlignment(alignment); + try args.append(load_inst); + } + }, }; } @@ -3070,6 +3091,13 @@ pub const DeclGen = struct { .as_u16 => { try llvm_params.append(dg.context.intType(16)); }, + .float_array => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const float_ty = try dg.lowerType(param_ty.structFieldType(0)); + const field_count = @intCast(c_uint, param_ty.structFieldCount()); + const arr_ty = float_ty.arrayType(field_count); + try llvm_params.append(arr_ty); + }, }; return llvm.functionType( @@ -4621,6 +4649,27 @@ pub const FuncGen = struct { const casted = self.builder.buildBitCast(llvm_arg, self.dg.context.intType(16), ""); try llvm_args.append(casted); }, + .float_array => { + const arg = args[it.zig_index - 1]; + const arg_ty = self.air.typeOf(arg); + var llvm_arg = try self.resolveInst(arg); + if (!isByRef(arg_ty)) { + const p = self.buildAlloca(llvm_arg.typeOf()); + const store_inst = self.builder.buildStore(llvm_arg, p); + store_inst.setAlignment(arg_ty.abiAlignment(target)); + llvm_arg = store_inst; + } + + const float_ty = try self.dg.lowerType(arg_ty.structFieldType(0)); + const field_count = @intCast(u32, arg_ty.structFieldCount()); + const arr_ty = float_ty.arrayType(field_count); + + const casted = self.builder.buildBitCast(llvm_arg, arr_ty.pointerType(0), ""); + const alignment = arg_ty.abiAlignment(target); + const load_inst = self.builder.buildLoad(casted, ""); + load_inst.setAlignment(alignment); + try llvm_args.append(load_inst); + }, }; const call = self.builder.buildCall( @@ -9644,6 +9693,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory, }, .wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect, + .aarch64, .aarch64_be => return aarch64_c_abi.classifyType(fn_info.return_type, target)[0] == .memory, else => return false, // TODO investigate C ABI for other architectures }, else => return false, @@ -9753,6 +9803,24 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. const abi_size = scalar_type.abiSize(target); return dg.context.intType(@intCast(c_uint, abi_size * 8)); }, + .aarch64, .aarch64_be => { + if (is_scalar) { + return dg.lowerType(fn_info.return_type); + } + const classes = aarch64_c_abi.classifyType(fn_info.return_type, target); + if (classes[0] == .memory or classes[0] == .none) { + return dg.context.voidType(); + } + if (classes[0] == .float_array) { + return dg.lowerType(fn_info.return_type); + } + if (classes[1] == .none) { + const bit_size = fn_info.return_type.bitSize(target); + return dg.context.intType(@intCast(c_uint, bit_size)); + } + + return dg.context.intType(64).arrayType(2); + }, // TODO investigate C ABI for other architectures else => return dg.lowerType(fn_info.return_type), } @@ -9780,6 +9848,7 @@ const ParamTypeIterator = struct { multiple_llvm_float, slice, as_u16, + float_array, }; pub fn next(it: *ParamTypeIterator) ?Lowering { @@ -9945,6 +10014,28 @@ const ParamTypeIterator = struct { } return .abi_sized_int; }, + .aarch64, .aarch64_be => { + it.zig_index += 1; + it.llvm_index += 1; + if (is_scalar) { + return .byval; + } + const classes = aarch64_c_abi.classifyType(ty, it.target); + if (classes[0] == .memory) { + return .byref; + } + if (classes[0] == .float_array) { + return .float_array; + } + if (classes[1] == .none) { + it.llvm_types_len = 1; + } else { + it.llvm_types_len = 2; + } + it.llvm_types_buffer[0] = 64; + it.llvm_types_buffer[1] = 64; + return .multiple_llvm_ints; + }, // TODO investigate C ABI for other architectures else => { it.zig_index += 1; diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index a34e4eda8f55c52c700bd79042f67a7017bde98e..1bf58d3fcfb6293fc4d441592957e2daefc1a232 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -110,7 +110,7 @@ test "C ABI floats" { } test "C ABI long double" { - if (!builtin.cpu.arch.isWasm()) return error.SkipZigTest; + if (!builtin.cpu.arch.isWasm() and !builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; c_long_double(12.34); } diff --git a/test/standalone.zig b/test/standalone.zig index c3fbad5377168e9d92becd98b7222d94d8956e6f..e38fed2c7d302e34fc87718083c0975f68c42a0f 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -50,6 +50,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/c_abi/build.zig", .{}); } } + if (builtin.cpu.arch.isAARCH64() and builtin.zig_backend == .stage2_llvm) { + cases.addBuildFile("test/c_abi/build.zig", .{}); + } // C ABI tests only pass for the Wasm target when using stage2 cases.addBuildFile("test/c_abi/build_wasm.zig", .{ .requires_stage2 = true, -- 2.54.0 From dbd60e3d296f1c195c4b4d56ca55ecb30444d407 Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Sun, 4 Sep 2022 22:45:57 +0200 Subject: [PATCH 49/51] autodoc: add support for doc tests --- lib/docs/index.html | 6 +- lib/docs/main.js | 15 ++++ src/Autodoc.zig | 168 ++++++++++++++++---------------------------- 3 files changed, 81 insertions(+), 108 deletions(-) diff --git a/lib/docs/index.html b/lib/docs/index.html index ddc0ecbfb2668f98d676b546854b9f5d32f00ce1..83c09894aac253f4daa907a83ca2735c27cc47dd 100644 --- a/lib/docs/index.html +++ b/lib/docs/index.html @@ -711,7 +711,11 @@

      Examples

        -