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 e20703cb2b..050d9287a5 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 d5bb4509f6..ed512f466c 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 fa6ea89d69..5e13456605 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; 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 95a0a8e4aa..e09822c208 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 ed512f466c..9e7f7a5a76 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, 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 e09822c208..b9637bf8e3 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 215a0511fc..b68353122c 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 0000000000..88cce88d98 --- /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 0000000000..0dafd7e112 --- /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 0000000000..b9fa1226eb --- /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 {}; +} 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 d52515b88d..20985c36a0 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 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 ebbeee7576..6b64c9ff98 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 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 36ddfc4e2a..e302571671 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 6c085a8f58..a7608d9a34 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); +} 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 348e3a7133..6847a92eae 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 526ab32abc..222a7ded6c 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 705c60aa4e..a23907c88f 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); +} 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 081344f2c8..e0e002d600 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(); 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 a23907c88f..d670e0cbd4 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); } 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 1caec57c4a..ad440e7757 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, 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 3aa9e9af7b..f00eb42dce 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: 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 f7a7dccbae..4881b3ee32 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 1ee4d5f18d..6f866d098c 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 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 89c26952e5..27a8e4a495 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 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 27a8e4a495..a3f02971b2 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" \ 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 a3f02971b2..928c9977db 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" \ 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 2bc0e75e61..966db008f9 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 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 0d10119cb7..162ba44700 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#}
    • 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 7a96fd51cd..e643f73962 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 be9ffaa884..0000000000 --- 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 0000000000..830d3a0877 --- /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 bfd683ec4c..26af0d5da5 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 0000000000..d84160a4f4 --- /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 0000000000..b40c997561 --- /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 0000000000..30b5c62697 --- /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 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 6a3f2620f2..e81d53b685 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 0e056c093f..2e4231a406 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, 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 928c9977db..b6ff10bbaa 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 \ 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 59f2a2173f..a707331a47 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, 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 6a3f2620f2..7db0433b51 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 0e056c093f..1b70ae20c7 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, 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 1b70ae20c7..96985cac40 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 => { 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 2bb8c848bc..ab7bea7367 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 03f951d0f1..333fb80d88 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, } 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 7a96fd51cd..07f842751f 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 e8a16e194a..6263a6a402 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); 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 cafed8d526..ce3ded927f 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{ 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 45426c5ee0..6c138df060 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 bd312e9cda..9834ba5f3d 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); +} 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 6c138df060..5f8d1539a6 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 79bc1861e4..9053a860a6 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); +} 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 e643f73962..4c83d31df8 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 a1961b40f7..50f86c7e79 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 83c5d977be..ab2d59bf83 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); + } +} 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 4c83d31df8..c089794fa9 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 26af0d5da5..c3fbad5377 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 0000000000..08a4f0bd75 --- /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 0000000000..436d97896a --- /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); +} 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 79e5ad963e..1502b97017 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 0000000000..61e71e72b5 --- /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 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 c089794fa9..a49aec871b 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 339485c137..59e9b163d9 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 0000000000..e6dfae0370 --- /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 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 a49aec871b..1ee5bf71b1 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 c5dc5c1dcf..2a5b55e86d 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 dc7077c8f8..d878bec18b 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' 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 1ee5bf71b1..6966060842 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 517414780b..28c8785e64 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 9053a860a6..b94034adf4 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 0000000000..7dab294d4a --- /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 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 6a3f2620f2..e6a9cf3a8f 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 0e056c093f..e574f8b22f 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; + } +} 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 0eafbafbce..52306c77bd 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 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 96985cac40..77b4cb2926 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)); }, 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 3f344f0421..33943a3066 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, ); 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 33943a3066..e6d9c843de 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, 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 9410c4ea4a..c63fe43158 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 6966060842..7ab9449dfb 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 59e9b163d9..0d48c5e46a 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); } 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 ab7bea7367..0e1817ffab 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 333fb80d88..bc59ddc279 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. 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 7ab9449dfb..539b44c612 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 b1e99f4f92..d54965a50c 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 7e2025a23d..7fede95155 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 5f8d1539a6..e7f4e123e3 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 391e87fc67..439de8bbae 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 145bbc384a..a34e4eda8f 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 1b9118aa64..1472c7d5ba 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 0007a2014e..55ee277641 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 001d235e18..f848392c90 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 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 7fede95155..344fe235f3 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 e7f4e123e3..004d152e1f 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, } } 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 b0fae81475..faa8a456f5 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 4345625dc1..5aa72e2d1f 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 637d491f49..54b4ad6081 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 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 ce3ded927f..ceba230276 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; \\ } 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 004d152e1f..3da66acc18 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 8f581f372e..4b55913af5 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 0000000000..c7bd8f63aa --- /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 0000000000..32b9dc1b27 --- /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 }); +} 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 895b20d9b5..61e2194eea 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(); 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 cb28b1fffa..f26a9a8a8a 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 3da66acc18..80ffd7a665 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 a34e4eda8f..1bf58d3fcf 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 c3fbad5377..e38fed2c7d 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, 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 ddc0ecbfb2..83c09894aa 100644 --- a/lib/docs/index.html +++ b/lib/docs/index.html @@ -711,7 +711,11 @@

      Examples

        -