diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-09-06 19:06:09 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-09-06 19:45:02 -0700 |
| commit | b7900de1684021ff86c67105e14e34968821ea02 (patch) | |
| tree | b881a288071705ffb8cd65ede5f8e7a1452b6dce | |
| parent | 20145016ac0d098e8e63d5107a05eca376d1e7bb (diff) | |
| parent | e2bb92b2e27dc54852a0227345e294ae383358fd (diff) | |
| download | zig-b7900de1684021ff86c67105e14e34968821ea02.tar.gz zig-b7900de1684021ff86c67105e14e34968821ea02.zip | |
Merge remote-tracking branch 'origin/master' into llvm15
70 files changed, 1664 insertions, 553 deletions
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 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 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 diff --git a/ci/srht/update_download_page b/ci/srht/update_download_page index 89c26952e5..b6ff10bbaa 100755 --- a/ci/srht/update_download_page +++ b/ci/srht/update_download_page @@ -101,32 +101,48 @@ 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/ - -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/ - -## 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/ +# Update autodocs and langref directly to S3 in order to prevent the +# www.ziglang.org git repo from growing too big. + +# 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 + +$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" \ + -m "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" \ + "$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 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: 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 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" { </li> </ul> <ul> - <li>{#syntax#}[]T{#endsyntax#} - pointer to runtime-known number of items. + <li>{#syntax#}[]T{#endsyntax#} - is a slice (a fat pointer, which contains a pointer of type {#syntax#}[*]T{#endsyntax#} and a length). <ul> <li>Supports index syntax: {#syntax#}slice[i]{#endsyntax#}</li> <li>Supports slice syntax: {#syntax#}slice[start..end]{#endsyntax#}</li> diff --git a/lib/docs/index.html b/lib/docs/index.html index ddc0ecbfb2..00f570d100 100644 --- a/lib/docs/index.html +++ b/lib/docs/index.html @@ -359,7 +359,17 @@ #listFns dt { font-family: var(--mono); + display: flex; + flex-direction: colunm; + justify-content: space-between; + } + + #listFns dt .fnSignature { + overflow-x: hidden; + white-space: nowrap; + text-overflow: ellipsis; } + .argBreaker { display: none; } @@ -711,7 +721,11 @@ <h2>Examples</h2> <ul id="listFnExamples" class="examples"></ul> </div> - <div id="sectTests" class="hidden"> + <div id="sectDocTests" class="hidden"> + <h2>DocTests</h2> + <pre id="docTestsCode"></pre> + </div> + <div id="sectTests" class="hidden"> <h2>Tests</h2> <div class="table-container"> <table> diff --git a/lib/docs/main.js b/lib/docs/main.js index 6a3f2620f2..b02b061eb4 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -13,6 +13,8 @@ var zigAnalysis; const domListTypes = document.getElementById("listTypes"); const domSectTests = document.getElementById("sectTests"); const domListTests = document.getElementById("listTests"); + const domSectDocTests = document.getElementById("sectDocTests"); + const domDocTestsCode = document.getElementById("docTestsCode"); const domSectNamespaces = document.getElementById("sectNamespaces"); const domListNamespaces = document.getElementById("listNamespaces"); const domSectErrSets = document.getElementById("sectErrSets"); @@ -241,7 +243,8 @@ var zigAnalysis; return ( typeKind === typeKinds.Struct || typeKind === typeKinds.Union || - typeKind === typeKinds.Enum + typeKind === typeKinds.Enum || + typeKind === typeKinds.Opaque ); } @@ -384,6 +387,7 @@ var zigAnalysis; domSectPkgs.classList.add("hidden"); domSectTypes.classList.add("hidden"); domSectTests.classList.add("hidden"); + domSectDocTests.classList.add("hidden"); domSectNamespaces.classList.add("hidden"); domSectErrSets.classList.add("hidden"); domSectFns.classList.add("hidden"); @@ -452,6 +456,10 @@ var zigAnalysis; let lastIsDecl = isDecl(last); let lastIsType = isType(last); let lastIsContainerType = isContainerType(last); + + if (lastIsDecl){ + renderDocTest(last); + } if (lastIsContainerType) { return renderContainer(last); @@ -477,6 +485,14 @@ var zigAnalysis; return renderValue(last); } + + } + + function renderDocTest(decl) { + if (!("decltest" in decl)) return; + const astNode = zigAnalysis.astNodes[decl.decltest]; + domSectDocTests.classList.remove("hidden"); + domDocTestsCode.innerHTML = astNode.code; } function renderUnknownDecl(decl) { @@ -1405,6 +1421,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!"); } @@ -1555,6 +1595,10 @@ var zigAnalysis; return '"' + escapeHtml(expr.string) + '"'; } + case "int_big": { + return (expr.int_big.negated ? "-" : "") + expr.int_big.value; + } + case "anytype": { return "anytype"; } @@ -1581,8 +1625,7 @@ var zigAnalysis; } case typeKinds.Opaque: { let opaqueObj = typeObj; - - return opaqueObj.name; + return opaqueObj; } case typeKinds.ComptimeExpr: { return "anyopaque"; @@ -2285,7 +2328,7 @@ var zigAnalysis; return "<a style=\"float: right;\" href=\"" + sourceFileUrlTemplate.replace("{{file}}", - zigAnalysis.files[srcNode.file]).replace("{{line}}", srcNode.line) + "\">[src]</a>"; + zigAnalysis.files[srcNode.file]).replace("{{line}}", srcNode.line + 1) + "\">[src]</a>"; } function renderContainer(container) { @@ -2393,24 +2436,26 @@ var zigAnalysis; resizeDomList( domListFns, fnsList.length, - "<div><dt></dt><dd></dd></div>" + "<div><dt><div class=\"fnSignature\"></div><div></div></dt><dd></dd></div>" ); for (let i = 0; i < fnsList.length; i += 1) { let decl = fnsList[i]; let trDom = domListFns.children[i]; - let tdFnCode = trDom.children[0]; + let tdFnSignature = trDom.children[0].children[0]; + let tdFnSrc = trDom.children[0].children[1]; let tdDesc = trDom.children[1]; let declType = resolveValue(decl.value); console.assert("type" in declType.expr); - tdFnCode.innerHTML = exprName(declType.expr, { + tdFnSignature.innerHTML = exprName(declType.expr, { wantHtml: true, wantLink: true, fnDecl: decl, linkFnNameDecl: navLinkDecl(decl.name), - }) + renderSourceFileLink(decl); + }); + tdFnSrc.innerHTML = renderSourceFileLink(decl); let docs = zigAnalysis.astNodes[decl.src].docs; if (docs != null) { 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 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, 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(); 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(); 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, 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/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 2bb8c848bc..0e1817ffab 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 { @@ -5035,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 03f951d0f1..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; } @@ -2114,9 +2117,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, } @@ -2348,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. 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/src/Autodoc.zig b/src/Autodoc.zig index 0e056c093f..e6e025b5b4 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: u32 = 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)"); @@ -467,6 +481,7 @@ const DocData = struct { line: usize = 0, col: usize = 0, name: ?[]const u8 = null, + code: ?[]const u8 = null, docs: ?[]const u8 = null, fields: ?[]usize = null, // index into astNodes @"comptime": bool = false, @@ -562,7 +577,13 @@ const DocData = struct { is_extern: bool = false, }, BoundFn: struct { name: []const u8 }, - Opaque: struct { name: []const u8 }, + Opaque: struct { + name: []const u8, + src: usize, // index into astNodes + privDecls: []usize = &.{}, // index into decls + pubDecls: []usize = &.{}, // index into decls + ast: usize, + }, Frame: struct { name: []const u8 }, AnyFrame: struct { name: []const u8 }, Vector: struct { name: []const u8 }, @@ -625,7 +646,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 @@ -712,12 +733,6 @@ const DocData = struct { if (self.int.negated) try w.writeAll("-"); try jsw.emitNumber(self.int.value); }, - .int_big => { - - //@panic("TODO: json serialization of big ints!"); - //if (v.negated) try w.writeAll("-"); - //try jsw.emitNumber(v.value); - }, .builtinField => { try jsw.emitString(@tagName(self.builtinField)); }, @@ -758,8 +773,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 +788,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 +880,7 @@ fn walkInstruction( return self.walkInstruction( new_file, &root_scope, - 1, + .{}, Zir.main_struct_inst, false, ); @@ -890,14 +905,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 +943,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 +952,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 +971,7 @@ fn walkInstruction( var ptr: DocData.WalkResult = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.ptr, false, ); @@ -966,7 +981,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 +991,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 +1001,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 +1011,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 +1062,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, ); @@ -1084,15 +1100,24 @@ 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 } }, + }; }, .slice_start => { @@ -1105,14 +1130,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 +1163,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 +1205,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 +1276,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, ); @@ -1278,6 +1303,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_src, + extra.data.lhs, + false, + ); + var rhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + parent_src, + 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, @@ -1319,7 +1388,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 +1437,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 +1467,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 +1497,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 +1524,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 +1533,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 +1559,7 @@ fn walkInstruction( const elem_type_ref = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.elem_type, false, ); @@ -1499,7 +1569,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 +1577,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 +1599,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 +1628,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 +1649,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 +1673,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 +1694,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 +1712,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 +1744,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,15 +1776,17 @@ 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, ); switch (operand.expr) { .int => |*int| int.negated = true, + .int_big => |*int_big| int_big.negated = true, else => { printWithContext( file, @@ -1727,10 +1800,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 +1818,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 +1840,7 @@ fn walkInstruction( const operand = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, false, ); @@ -1782,7 +1857,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 +1886,7 @@ fn walkInstruction( const operand = try self.walkRef( file, parent_scope, - parent_line, + parent_src, un_node.operand, need_type, ); @@ -1833,10 +1909,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 +1929,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 +1948,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 +1971,7 @@ fn walkInstruction( const dest_type_walk = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.dest_type, false, ); @@ -1902,7 +1979,7 @@ fn walkInstruction( const operand = try self.walkRef( file, parent_scope, - parent_line, + parent_src, extra.data.operand, false, ); @@ -1927,10 +2004,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 +2092,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 +2143,7 @@ fn walkInstruction( return self.walkRef( file, parent_scope, - parent_line, + parent_src, getBlockInlineBreak(file.zir, inst_index), need_type, ); @@ -2092,13 +2170,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 +2192,7 @@ fn walkInstruction( const value = try self.walkRef( file, parent_scope, - parent_line, + parent_src, init_extra.data.init, need_type, ); @@ -2123,10 +2206,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 +2243,7 @@ fn walkInstruction( const value = try self.walkRef( file, parent_scope, - parent_line, + parent_src, init_extra.data.init, need_type, ); @@ -2235,7 +2319,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 +2334,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 +2365,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 +2381,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 +2409,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; } @@ -2355,36 +2439,91 @@ fn walkInstruction( return result; }, .opaque_decl => { + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ .Unanalyzed = .{} }); + + var scope: Scope = .{ + .parent = parent_scope, + .enclosing_type = type_slot_index, + }; + 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 = if (src_node) |sn| + try self.srcLocInfo(file, sn, parent_src) + else + parent_src; + _ = src_info; const decls_len = if (small.has_decls_len) blk: { const decls_len = file.zir.extra[extra_index]; extra_index += 1; break :blk decls_len; } else 0; - _ = decls_len; - - const decls_bits = file.zir.extra[extra_index]; - _ = decls_bits; - - // const sep = "=" ** 200; - // log.debug("{s}", .{sep}); - // log.debug("small = {any}", .{small}); - // log.debug("src_node = {}", .{src_node}); - // log.debug("decls_len = {}", .{decls_len}); - // log.debug("decls_bit = {}", .{decls_bits}); - // log.debug("{s}", .{sep}); - const type_slot_index = self.types.items.len - 1; - try self.types.append(self.arena, .{ .Opaque = .{ .name = "TODO" } }); + + var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; + var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; + + const decls_first_index = self.decls.items.len; + // Decl name lookahead for reserving slots in `scope` (and `decls`). + // Done to make sure that all decl refs can be resolved correctly, + // even if we haven't fully analyzed the decl yet. + { + var it = file.zir.declIterator(@intCast(u32, inst_index)); + try self.decls.resize(self.arena, decls_first_index + it.decls_len); + for (self.decls.items[decls_first_index..]) |*slot| { + slot._analyzed = false; + } + var decls_slot_index = decls_first_index; + while (it.next()) |d| : (decls_slot_index += 1) { + const decl_name_index = file.zir.extra[d.sub_index + 5]; + try scope.insertDeclRef(self.arena, decl_name_index, decls_slot_index); + } + } + + extra_index = try self.walkDecls( + file, + &scope, + src_info, + decls_first_index, + decls_len, + &decl_indexes, + &priv_decl_indexes, + extra_index, + ); + + self.types.items[type_slot_index] = .{ + .Opaque = .{ + .name = "todo_name", + .src = self_ast_node_index, + .privDecls = priv_decl_indexes.items, + .pubDecls = decl_indexes.items, + .ast = self_ast_node_index, + }, + }; + if (self.ref_paths_pending_on_types.get(type_slot_index)) |paths| { + for (paths.items) |resume_info| { + try self.tryResolveRefPath( + resume_info.file, + inst_index, + resume_info.ref_path, + ); + } + + _ = self.ref_paths_pending_on_types.remove(type_slot_index); + // TODO: we should deallocate the arraylist that holds all the + // decl paths. not doing it now since it's arena-allocated + // anyway, but maybe we should put it elsewhere. + } return DocData.WalkResult{ - .typeRef = .{ .type = @enumToInt(Ref.anyopaque_type) }, + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, .expr = .{ .type = type_slot_index }, }; }, @@ -2419,7 +2558,11 @@ fn walkInstruction( extra_index += 1; break :blk src_node; } else null; - _ = src_node; + + const src_info = if (src_node) |sn| + try self.srcLocInfo(file, sn, parent_src) + else + parent_src; const tag_type: ?Ref = if (small.has_tag_type) blk: { const tag_type = file.zir.extra[extra_index]; @@ -2470,7 +2613,7 @@ fn walkInstruction( extra_index = try self.walkDecls( file, &scope, - parent_line, + src_info, decls_first_index, decls_len, &decl_indexes, @@ -2491,7 +2634,7 @@ fn walkInstruction( try self.collectUnionFieldInfo( file, &scope, - parent_line, + src_info, fields_len, &field_type_refs, &field_name_indexes, @@ -2541,7 +2684,11 @@ fn walkInstruction( extra_index += 1; break :blk src_node; } else null; - _ = src_node; + + const src_info = if (src_node) |sn| + try self.srcLocInfo(file, sn, parent_src) + else + parent_src; const tag_type: ?Ref = if (small.has_tag_type) blk: { const tag_type = file.zir.extra[extra_index]; @@ -2592,7 +2739,7 @@ fn walkInstruction( extra_index = try self.walkDecls( file, &scope, - parent_line, + src_info, decls_first_index, decls_len, &decl_indexes, @@ -2687,7 +2834,11 @@ fn walkInstruction( extra_index += 1; break :blk src_node; } else null; - _ = src_node; + + const src_info = if (src_node) |sn| + try self.srcLocInfo(file, sn, parent_src) + else + parent_src; const fields_len = if (small.has_fields_len) blk: { const fields_len = file.zir.extra[extra_index]; @@ -2736,7 +2887,7 @@ fn walkInstruction( extra_index = try self.walkDecls( file, &scope, - parent_line, + src_info, decls_first_index, decls_len, &decl_indexes, @@ -2749,7 +2900,7 @@ fn walkInstruction( try self.collectStructFieldInfo( file, &scope, - parent_line, + src_info, fields_len, &field_type_refs, &field_name_indexes, @@ -2793,7 +2944,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,13 +2972,14 @@ 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), priv_decl_indexes: *std.ArrayListUnmanaged(usize), extra_start: usize, ) AutodocErrors!usize { + const data = file.zir.instructions.items(.data); const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; var extra_index = extra_start + bit_bags_count; var bit_bag_index: usize = extra_start; @@ -2854,7 +3006,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; @@ -2884,10 +3037,11 @@ fn walkDecls( }; _ = addrspace_inst; - // const pub_str = if (is_pub) "pub " else ""; - // const hash_bytes = @bitCast([16]u8, hash_u32s.*); + // This is known to work because decl values are always block_inlines + const value_pl_node = data[value_index].pl_node; + const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src); - var is_test = false; // we discover if it's a test by lookin at its name + var is_test = false; // we discover if it's a test by looking at its name const name: []const u8 = blk: { if (decl_name_index == 0) { break :blk if (is_exported) "usingnamespace" else "comptime"; @@ -2895,84 +3049,32 @@ fn walkDecls( is_test = true; break :blk "test"; } else if (decl_name_index == 2) { - is_test = true; - // TODO: remove temporary hack - break :blk "test"; - // // it is a decltest - // const decl_being_tested = scope.resolveDeclName(doc_comment_index); - // const ast_node_index = idx: { - // const idx = self.ast_nodes.items.len; - // const file_source = file.getSource(self.module.gpa) catch unreachable; // TODO fix this - // const source_of_decltest_function = srcloc: { - // const func_index = getBlockInlineBreak(file.zir, value_index); - // // a decltest is always a function - // const tag = file.zir.instructions.items(.tag)[Zir.refToIndex(func_index).?]; - // std.debug.assert(tag == .func_extended); - - // const pl_node = file.zir.instructions.items(.data)[Zir.refToIndex(func_index).?].pl_node; - // const extra = file.zir.extraData(Zir.Inst.ExtendedFunc, pl_node.payload_index); - // const bits = @bitCast(Zir.Inst.ExtendedFunc.Bits, extra.data.bits); - - // var extra_index_for_this_func: usize = extra.end; - // if (bits.has_lib_name) extra_index_for_this_func += 1; - // if (bits.has_cc) extra_index_for_this_func += 1; - // if (bits.has_align) extra_index_for_this_func += 1; - - // const ret_ty_body = file.zir.extra[extra_index_for_this_func..][0..extra.data.ret_body_len]; - // extra_index_for_this_func += ret_ty_body.len; - - // const body = file.zir.extra[extra_index_for_this_func..][0..extra.data.body_len]; - // extra_index_for_this_func += body.len; - - // var src_locs: Zir.Inst.Func.SrcLocs = undefined; - // if (body.len != 0) { - // src_locs = file.zir.extraData(Zir.Inst.Func.SrcLocs, extra_index_for_this_func).data; - // } else { - // src_locs = .{ - // .lbrace_line = line, - // .rbrace_line = line, - // .columns = 0, // TODO get columns when body.len == 0 - // }; - // } - // break :srcloc src_locs; - // }; - // const source_slice = slice: { - // var start_byte_offset: u32 = 0; - // var end_byte_offset: u32 = 0; - // const rbrace_col = @truncate(u16, source_of_decltest_function.columns >> 16); - // var lines: u32 = 0; - // for (file_source.bytes) |b, i| { - // if (b == '\n') { - // lines += 1; - // } - // if (lines == source_of_decltest_function.lbrace_line) { - // start_byte_offset = @intCast(u32, i); - // } - // if (lines == source_of_decltest_function.rbrace_line) { - // end_byte_offset = @intCast(u32, i) + rbrace_col; - // break; - // } - // } - // break :slice file_source.bytes[start_byte_offset..end_byte_offset]; - // }; - // try self.ast_nodes.append(self.arena, .{ - // .file = 0, - // .line = line, - // .col = 0, - // .name = try self.arena.dupe(u8, source_slice), - // }); - // break :idx idx; - // }; - // self.decls.items[decl_being_tested].decltest = ast_node_index; - // self.decls.items[decls_slot_index] = .{ - // ._analyzed = true, - // .name = "test", - // .isTest = true, - // .src = ast_node_index, - // .value = .{ .expr = .{ .type = 0 } }, - // .kind = "const", - // }; - // continue; + // it is a decltest + const decl_being_tested = scope.resolveDeclName(doc_comment_index); + const func_index = getBlockInlineBreak(file.zir, value_index); + + const pl_node = data[Zir.refToIndex(func_index).?].pl_node; + const fn_src = try self.srcLocInfo(file, pl_node.src_node, decl_src); + const tree = try file.getTree(self.module.gpa); + const test_source_code = tree.getNodeSource(fn_src.src_node); + + const ast_node_index = self.ast_nodes.items.len; + try self.ast_nodes.append(self.arena, .{ + .file = 0, + .line = 0, + .col = 0, + .code = test_source_code, + }); + self.decls.items[decl_being_tested].decltest = ast_node_index; + self.decls.items[decls_slot_index] = .{ + ._analyzed = true, + .name = "test", + .isTest = true, + .src = ast_node_index, + .value = .{ .expr = .{ .type = 0 } }, + .kind = "const", + }; + continue; } else { const raw_decl_name = file.zir.nullTerminatedString(decl_name_index); if (raw_decl_name.len == 0) { @@ -2993,8 +3095,8 @@ fn walkDecls( 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 +3107,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); @@ -3381,6 +3483,37 @@ fn tryResolveRefPath( path[i + 1] = (try self.cteTodo(child_string)).expr; continue :outer; }, + .Opaque => |t_opaque| { + for (t_opaque.pubDecls) |d| { + // TODO: this could be improved a lot + // by having our own string table! + const decl = self.decls.items[d]; + if (std.mem.eql(u8, decl.name, child_string)) { + path[i + 1] = .{ .declRef = d }; + continue :outer; + } + } + for (t_opaque.privDecls) |d| { + // TODO: this could be improved a lot + // by having our own string table! + const decl = self.decls.items[d]; + if (std.mem.eql(u8, decl.name, child_string)) { + path[i + 1] = .{ .declRef = d }; + continue :outer; + } + } + + // if we got here, our search failed + printWithContext( + file, + inst_index, + "failed to match `{s}` in opaque", + .{child_string}, + ); + + path[i + 1] = (try self.cteTodo("match failure")).expr; + continue :outer; + }, }, } } @@ -3401,7 +3534,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 +3599,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 +3608,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 +3623,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 +3640,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 +3657,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 +3674,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 +3693,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 +3715,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 +3752,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 +3818,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 +3831,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 +3853,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 +3894,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 +3906,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 +3953,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 +3976,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 +4050,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 +4060,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 +4090,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 +4202,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 +4255,24 @@ fn writePackageTableToJson( } try jsw.endObject(); } + +fn srcLocInfo( + self: Autodoc, + file: *File, + src_node: i32, + parent_src: SrcLocInfo, +) !SrcLocInfo { + const sn = @intCast(u32, @intCast(i32, parent_src.src_node) + 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 SrcLocInfo{ + .line = parent_src.line + loc.line, + .bytes = start, + .src_node = sn, + }; +} 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 7a96fd51cd..fb1638bc2a 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", .{}); } @@ -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; @@ -2849,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); } @@ -5040,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); @@ -5569,7 +5585,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); @@ -5768,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) { @@ -6047,7 +6067,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, @@ -6206,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); @@ -6308,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); @@ -6573,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); @@ -6626,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; } @@ -6657,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 @@ -7838,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) { @@ -7876,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), @@ -8072,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; @@ -8095,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), @@ -8177,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 @@ -8237,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; @@ -10379,7 +10399,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 => { @@ -15563,7 +15585,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); @@ -16661,7 +16683,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); @@ -17499,7 +17521,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"); @@ -20343,12 +20365,13 @@ fn validateRunTimeType( .Int, .Float, .ErrorSet, - .Enum, .Frame, .AnyFrame, .Void, => return true, + .Enum => return !(try sema.typeRequiresComptime(ty)), + .BoundFn, .ComptimeFloat, .ComptimeInt, @@ -20381,7 +20404,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; }, }; @@ -20489,7 +20512,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); } @@ -20509,7 +20532,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); } @@ -20528,7 +20551,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, @@ -20556,17 +20586,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(), } } @@ -20618,8 +20656,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", .{}); @@ -22998,7 +23036,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)) { @@ -23422,8 +23460,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) { @@ -23917,7 +23958,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, } }; } @@ -24075,16 +24116,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 (!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); + + 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. @@ -27579,7 +27644,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 => { @@ -27634,7 +27699,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", .{}); @@ -27866,7 +27931,7 @@ fn resolveStructFully( } // And let's not forget comptime-only status. - _ = try sema.typeRequiresComptime(block, src, ty); + _ = try sema.typeRequiresComptime(ty); } fn resolveUnionFully( @@ -27899,7 +27964,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 { @@ -28273,7 +28338,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); @@ -28610,7 +28675,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); @@ -29002,7 +29067,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; @@ -29534,7 +29599,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, @@ -29625,7 +29690,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, @@ -29641,7 +29706,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); } }, @@ -29650,14 +29715,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; } } @@ -29678,7 +29743,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; } @@ -29702,7 +29767,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; } @@ -29713,18 +29778,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); }, }; } @@ -29762,7 +29827,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; @@ -29771,7 +29836,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/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/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 95a0a8e4aa..b9637bf8e3 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -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/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..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, @@ -174,6 +182,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 +298,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/autodoc/render_source.zig b/src/autodoc/render_source.zig index cafed8d526..ceba230276 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 #ccc; + \\ border-bottom: 1px solid #ccc; + \\ background: #fafafa; + \\ } \\ \\ @media (prefers-color-scheme: dark) { \\ body{ @@ -90,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; \\ } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index badf0721c7..dc43548b80 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 }; @@ -314,6 +315,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, @@ -667,7 +677,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(); @@ -693,7 +703,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. @@ -1084,6 +1094,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(param_llvm_ty, arg_ptr, ""); + load_inst.setAlignment(alignment); + try args.append(load_inst); + } + }, }; } @@ -1191,15 +1221,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, @@ -1294,7 +1315,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, @@ -2527,6 +2548,7 @@ pub const DeclGen = struct { .multiple_llvm_ints, .multiple_llvm_float, .as_u16, + .float_array, => continue, .slice => unreachable, // extern functions do not support slice types. @@ -3093,6 +3115,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( @@ -4719,6 +4748,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 array_llvm_ty = float_ty.arrayType(field_count); + + const casted = self.builder.buildBitCast(llvm_arg, array_llvm_ty.pointerType(0), ""); + const alignment = arg_ty.abiAlignment(target); + const load_inst = self.builder.buildLoad(array_llvm_ty, casted, ""); + load_inst.setAlignment(alignment); + try llvm_args.append(load_inst); + }, }; const call = self.builder.buildCall( @@ -9240,7 +9290,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, } } @@ -9250,19 +9300,16 @@ 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]; - if (!field.ty.hasRuntimeBitsIgnoreComptime()) { - return null; - } const target = self.dg.module.getTarget(); const layout = union_ty.unionGetLayout(target); + const result_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); + 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_llvm_ty = try self.dg.lowerType(union_ty); const union_field_ptr = self.builder.buildStructGEP(union_llvm_ty, union_ptr, payload_index, ""); - const result_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); return self.builder.buildBitCast(union_field_ptr, result_llvm_ty, ""); } @@ -9832,6 +9879,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, @@ -9862,22 +9910,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) { @@ -9890,6 +9923,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, @@ -9930,6 +9964,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, } } @@ -9954,6 +9989,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), } @@ -9981,6 +10034,7 @@ const ParamTypeIterator = struct { multiple_llvm_float, slice, as_u16, + float_array, }; pub fn next(it: *ParamTypeIterator) ?Lowering { @@ -10025,22 +10079,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, - - else => false, - }; + const is_scalar = isScalar(ty); switch (it.target.cpu.arch) { .riscv32, .riscv64 => { it.zig_index += 1; @@ -10069,6 +10108,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; @@ -10123,6 +10167,7 @@ const ParamTypeIterator = struct { @panic("TODO"); }, .memory => unreachable, // handled above + .win_i128 => unreachable, // windows only .none => break, } } @@ -10155,6 +10200,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; @@ -10294,6 +10361,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/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); +} diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 51ede6f699..68eb3b0aee 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..9e7f7a5a76 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -172,18 +172,16 @@ 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: { + 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, 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; 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); 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/src/type.zig b/src/type.zig index 5c9d3f60fc..c4f1782954 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 { @@ -2398,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); } @@ -2437,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); } 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.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 }); +} 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/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); +} 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); + } +} 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..d670e0cbd4 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -113,3 +113,13 @@ 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" { + 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); +} diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 79bc1861e4..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); @@ -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); +} 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..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); } @@ -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/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 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 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/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 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 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 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' 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 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/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 {}; +} diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 71654afba4..e62c33eaa8 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 <stdint.h> - \\#include <stdlib.h> - \\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 <stdint.h> + \\#include <stdlib.h> + \\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 <stdlib.h> diff --git a/test/standalone.zig b/test/standalone.zig index 463b87556e..aa6c0f0a14 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", .{}); @@ -48,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, @@ -66,6 +71,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 <stdarg.h> + +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 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); +} diff --git a/test/translate_c.zig b/test/translate_c.zig index 17fe13330c..e54747046a 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 |
