diff --git a/CMakeLists.txt b/CMakeLists.txt index 9492d5dfa..0817c09f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -491,6 +491,7 @@ set(ZIG_STD_FILES "heap.zig" "index.zig" "io.zig" + "io/seekable_stream.zig" "json.zig" "lazy_init.zig" "linked_list.zig" diff --git a/ci/azure/linux_script b/ci/azure/linux_script index 6538c2145..0298644bf 100755 --- a/ci/azure/linux_script +++ b/ci/azure/linux_script @@ -34,6 +34,9 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then SHASUM=$(sha256sum $ARTIFACTSDIR/$TARBALL | cut '-d ' -f1) BYTESIZE=$(wc -c < $ARTIFACTSDIR/$TARBALL) + # `set -x` causes these variables to be mangled. + # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html + set +x echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" diff --git a/ci/azure/macos_script b/ci/azure/macos_script index 239c35426..da2d9351d 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -98,6 +98,9 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1) BYTESIZE=$(wc -c < $TARBALL) + # `set -x` causes these variables to be mangled. + # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html + set +x echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" diff --git a/ci/azure/windows_upload b/ci/azure/windows_upload index 8d30980fe..7d23ac7aa 100755 --- a/ci/azure/windows_upload +++ b/ci/azure/windows_upload @@ -25,6 +25,9 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then SHASUM=$(sha256sum $TARBALL | cut '-d ' -f1) BYTESIZE=$(wc -c < $TARBALL) + # `set -x` causes these variables to be mangled. + # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html + set +x echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" diff --git a/deps/lld/ELF/OutputSections.cpp b/deps/lld/ELF/OutputSections.cpp index 8253b18b4..9dd8bba26 100644 --- a/deps/lld/ELF/OutputSections.cpp +++ b/deps/lld/ELF/OutputSections.cpp @@ -95,7 +95,7 @@ void OutputSection::addSection(InputSection *IS) { Flags = IS->Flags; } else { // Otherwise, check if new type or flags are compatible with existing ones. - unsigned Mask = SHF_ALLOC | SHF_TLS | SHF_LINK_ORDER; + unsigned Mask = SHF_TLS | SHF_LINK_ORDER; if ((Flags & Mask) != (IS->Flags & Mask)) error("incompatible section flags for " + Name + "\n>>> " + toString(IS) + ": 0x" + utohexstr(IS->Flags) + "\n>>> output section " + Name + diff --git a/doc/langref.html.in b/doc/langref.html.in index 6e2d2bd9a..a1903331a 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -8,7 +8,13 @@ body{ background-color:#111; color: #bbb; - font-family: sans-serif; + font-family: system-ui, + /* Fallbacks for browsers that don't support system-ui */ + /* https://caniuse.com/#search=system-ui */ + -apple-system, /* iOS and macOS */ + Roboto, /* Android */ + "Segoe UI", /* Windows */ + sans-serif; } a { color: #88f; @@ -4264,13 +4270,21 @@ fn foo() i32 { return 1234; } {#code_end#} -

However, if the expression has type {#syntax#}void{#endsyntax#}:

+

However, if the expression has type {#syntax#}void{#endsyntax#}, there will be no error. Function return values can also be explicitly ignored by assigning them to {#syntax#}_{#endsyntax#}.

{#code_begin|test#} -test "ignoring expression value" { - foo(); +test "void is ignored" { + returnsVoid(); } -fn foo() void {} +test "explicitly ignoring expression value" { + _ = foo(); +} + +fn returnsVoid() void {} + +fn foo() i32 { + return 1234; +} {#code_end#} {#header_close#} diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index ef0b9c08f..66264666b 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -24,15 +24,15 @@ pub fn main() !void { try stdout.print("\nGuess a number between 1 and 100: "); var line_buf: [20]u8 = undefined; - const line_len = io.readLine(line_buf[0..]) catch |err| switch (err) { - error.InputTooLong => { + const line = io.readLineSlice(line_buf[0..]) catch |err| switch (err) { + error.OutOfMemory => { try stdout.print("Input too long.\n"); continue; }, - error.EndOfFile, error.StdInUnavailable => return err, + else => return err, }; - const guess = fmt.parseUnsigned(u8, line_buf[0..line_len], 10) catch { + const guess = fmt.parseUnsigned(u8, line, 10) catch { try stdout.print("Invalid number.\n"); continue; }; diff --git a/src/ir.cpp b/src/ir.cpp index 1edb12267..68a0b7f6f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -67,6 +67,8 @@ enum ConstCastResultId { struct ConstCastOnly; struct ConstCastArg { size_t arg_index; + ZigType *actual_param_type; + ZigType *expected_param_type; ConstCastOnly *child; }; @@ -8638,6 +8640,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted if (arg_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdFnArg; result.data.fn_arg.arg_index = i; + result.data.fn_arg.actual_param_type = actual_param_info->type; + result.data.fn_arg.expected_param_type = expected_param_info->type; result.data.fn_arg.child = allocate_nonzero(1); *result.data.fn_arg.child = arg_child; return result; @@ -10483,6 +10487,15 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa } break; } + case ConstCastResultIdFnArg: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("parameter %" ZIG_PRI_usize ": '%s' cannot cast into '%s'", + cast_result->data.fn_arg.arg_index, + buf_ptr(&cast_result->data.fn_arg.actual_param_type->name), + buf_ptr(&cast_result->data.fn_arg.expected_param_type->name))); + report_recursive_error(ira, source_node, cast_result->data.fn_arg.child, msg); + break; + } case ConstCastResultIdFnAlign: // TODO case ConstCastResultIdFnCC: // TODO case ConstCastResultIdFnVarArgs: // TODO @@ -10490,7 +10503,6 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa case ConstCastResultIdFnReturnType: // TODO case ConstCastResultIdFnArgCount: // TODO case ConstCastResultIdFnGenericArgCount: // TODO - case ConstCastResultIdFnArg: // TODO case ConstCastResultIdFnArgNoAlias: // TODO case ConstCastResultIdUnresolvedInferredErrSet: // TODO case ConstCastResultIdAsyncAllocatorType: // TODO diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 00023f623..bda8fa0ad 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -681,18 +681,6 @@ void ZigLLVMParseCommandLineOptions(size_t argc, const char *const *argv) { } -static_assert((Triple::ArchType)ZigLLVM_LastArchType == Triple::LastArchType, ""); -static_assert((Triple::VendorType)ZigLLVM_LastVendorType == Triple::LastVendorType, ""); -static_assert((Triple::OSType)ZigLLVM_LastOSType == Triple::LastOSType, ""); -static_assert((Triple::EnvironmentType)ZigLLVM_LastEnvironmentType == Triple::LastEnvironmentType, ""); -static_assert((Triple::SubArchType)ZigLLVM_KalimbaSubArch_v5 == Triple::KalimbaSubArch_v5, ""); - -static_assert((Triple::ObjectFormatType)ZigLLVM_UnknownObjectFormat == Triple::UnknownObjectFormat, ""); -static_assert((Triple::ObjectFormatType)ZigLLVM_COFF == Triple::COFF, ""); -static_assert((Triple::ObjectFormatType)ZigLLVM_ELF == Triple::ELF, ""); -static_assert((Triple::ObjectFormatType)ZigLLVM_MachO == Triple::MachO, ""); -static_assert((Triple::ObjectFormatType)ZigLLVM_Wasm == Triple::Wasm, ""); - const char *ZigLLVMGetArchTypeName(ZigLLVM_ArchType arch) { return (const char*)Triple::getArchTypeName((Triple::ArchType)arch).bytes_begin(); } @@ -919,3 +907,166 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ assert(false); // unreachable abort(); } + +static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); +static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); +static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); +static_assert((Triple::ArchType)ZigLLVM_aarch64 == Triple::aarch64, ""); +static_assert((Triple::ArchType)ZigLLVM_aarch64_be == Triple::aarch64_be, ""); +static_assert((Triple::ArchType)ZigLLVM_arc == Triple::arc, ""); +static_assert((Triple::ArchType)ZigLLVM_avr == Triple::avr, ""); +static_assert((Triple::ArchType)ZigLLVM_bpfel == Triple::bpfel, ""); +static_assert((Triple::ArchType)ZigLLVM_bpfeb == Triple::bpfeb, ""); +static_assert((Triple::ArchType)ZigLLVM_hexagon == Triple::hexagon, ""); +static_assert((Triple::ArchType)ZigLLVM_mips == Triple::mips, ""); +static_assert((Triple::ArchType)ZigLLVM_mipsel == Triple::mipsel, ""); +static_assert((Triple::ArchType)ZigLLVM_mips64 == Triple::mips64, ""); +static_assert((Triple::ArchType)ZigLLVM_mips64el == Triple::mips64el, ""); +static_assert((Triple::ArchType)ZigLLVM_msp430 == Triple::msp430, ""); +static_assert((Triple::ArchType)ZigLLVM_nios2 == Triple::nios2, ""); +static_assert((Triple::ArchType)ZigLLVM_ppc == Triple::ppc, ""); +static_assert((Triple::ArchType)ZigLLVM_ppc64 == Triple::ppc64, ""); +static_assert((Triple::ArchType)ZigLLVM_ppc64le == Triple::ppc64le, ""); +static_assert((Triple::ArchType)ZigLLVM_r600 == Triple::r600, ""); +static_assert((Triple::ArchType)ZigLLVM_amdgcn == Triple::amdgcn, ""); +static_assert((Triple::ArchType)ZigLLVM_riscv32 == Triple::riscv32, ""); +static_assert((Triple::ArchType)ZigLLVM_riscv64 == Triple::riscv64, ""); +static_assert((Triple::ArchType)ZigLLVM_sparc == Triple::sparc, ""); +static_assert((Triple::ArchType)ZigLLVM_sparcv9 == Triple::sparcv9, ""); +static_assert((Triple::ArchType)ZigLLVM_sparcel == Triple::sparcel, ""); +static_assert((Triple::ArchType)ZigLLVM_systemz == Triple::systemz, ""); +static_assert((Triple::ArchType)ZigLLVM_tce == Triple::tce, ""); +static_assert((Triple::ArchType)ZigLLVM_tcele == Triple::tcele, ""); +static_assert((Triple::ArchType)ZigLLVM_thumb == Triple::thumb, ""); +static_assert((Triple::ArchType)ZigLLVM_thumbeb == Triple::thumbeb, ""); +static_assert((Triple::ArchType)ZigLLVM_x86 == Triple::x86, ""); +static_assert((Triple::ArchType)ZigLLVM_x86_64 == Triple::x86_64, ""); +static_assert((Triple::ArchType)ZigLLVM_xcore == Triple::xcore, ""); +static_assert((Triple::ArchType)ZigLLVM_nvptx == Triple::nvptx, ""); +static_assert((Triple::ArchType)ZigLLVM_nvptx64 == Triple::nvptx64, ""); +static_assert((Triple::ArchType)ZigLLVM_le32 == Triple::le32, ""); +static_assert((Triple::ArchType)ZigLLVM_le64 == Triple::le64, ""); +static_assert((Triple::ArchType)ZigLLVM_amdil == Triple::amdil, ""); +static_assert((Triple::ArchType)ZigLLVM_amdil64 == Triple::amdil64, ""); +static_assert((Triple::ArchType)ZigLLVM_hsail == Triple::hsail, ""); +static_assert((Triple::ArchType)ZigLLVM_hsail64 == Triple::hsail64, ""); +static_assert((Triple::ArchType)ZigLLVM_spir == Triple::spir, ""); +static_assert((Triple::ArchType)ZigLLVM_spir64 == Triple::spir64, ""); +static_assert((Triple::ArchType)ZigLLVM_kalimba == Triple::kalimba, ""); +static_assert((Triple::ArchType)ZigLLVM_shave == Triple::shave, ""); +static_assert((Triple::ArchType)ZigLLVM_lanai == Triple::lanai, ""); +static_assert((Triple::ArchType)ZigLLVM_wasm32 == Triple::wasm32, ""); +static_assert((Triple::ArchType)ZigLLVM_wasm64 == Triple::wasm64, ""); +static_assert((Triple::ArchType)ZigLLVM_renderscript32 == Triple::renderscript32, ""); +static_assert((Triple::ArchType)ZigLLVM_renderscript64 == Triple::renderscript64, ""); +// Uncomment this when testing LLVM 8.0.0 +//static_assert((Triple::ArchType)ZigLLVM_LastArchType == Triple::LastArchType, ""); + +static_assert((Triple::SubArchType)ZigLLVM_NoSubArch == Triple::NoSubArch, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8_4a == Triple::ARMSubArch_v8_4a, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8_3a == Triple::ARMSubArch_v8_3a, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8_2a == Triple::ARMSubArch_v8_2a, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8_1a == Triple::ARMSubArch_v8_1a, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8 == Triple::ARMSubArch_v8, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8r == Triple::ARMSubArch_v8r, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8m_baseline == Triple::ARMSubArch_v8m_baseline, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v8m_mainline == Triple::ARMSubArch_v8m_mainline, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7 == Triple::ARMSubArch_v7, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7em == Triple::ARMSubArch_v7em, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7m == Triple::ARMSubArch_v7m, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7s == Triple::ARMSubArch_v7s, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7k == Triple::ARMSubArch_v7k, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v7ve == Triple::ARMSubArch_v7ve, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v6 == Triple::ARMSubArch_v6, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v6m == Triple::ARMSubArch_v6m, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v6k == Triple::ARMSubArch_v6k, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v6t2 == Triple::ARMSubArch_v6t2, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v5 == Triple::ARMSubArch_v5, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v5te == Triple::ARMSubArch_v5te, ""); +static_assert((Triple::SubArchType)ZigLLVM_ARMSubArch_v4t == Triple::ARMSubArch_v4t, ""); +static_assert((Triple::SubArchType)ZigLLVM_KalimbaSubArch_v3 == Triple::KalimbaSubArch_v3, ""); +static_assert((Triple::SubArchType)ZigLLVM_KalimbaSubArch_v4 == Triple::KalimbaSubArch_v4, ""); +static_assert((Triple::SubArchType)ZigLLVM_KalimbaSubArch_v5 == Triple::KalimbaSubArch_v5, ""); + +static_assert((Triple::VendorType)ZigLLVM_UnknownVendor == Triple::UnknownVendor, ""); +static_assert((Triple::VendorType)ZigLLVM_Apple == Triple::Apple, ""); +static_assert((Triple::VendorType)ZigLLVM_PC == Triple::PC, ""); +static_assert((Triple::VendorType)ZigLLVM_SCEI == Triple::SCEI, ""); +static_assert((Triple::VendorType)ZigLLVM_BGP == Triple::BGP, ""); +static_assert((Triple::VendorType)ZigLLVM_BGQ == Triple::BGQ, ""); +static_assert((Triple::VendorType)ZigLLVM_Freescale == Triple::Freescale, ""); +static_assert((Triple::VendorType)ZigLLVM_IBM == Triple::IBM, ""); +static_assert((Triple::VendorType)ZigLLVM_ImaginationTechnologies == Triple::ImaginationTechnologies, ""); +static_assert((Triple::VendorType)ZigLLVM_MipsTechnologies == Triple::MipsTechnologies, ""); +static_assert((Triple::VendorType)ZigLLVM_NVIDIA == Triple::NVIDIA, ""); +static_assert((Triple::VendorType)ZigLLVM_CSR == Triple::CSR, ""); +static_assert((Triple::VendorType)ZigLLVM_Myriad == Triple::Myriad, ""); +static_assert((Triple::VendorType)ZigLLVM_AMD == Triple::AMD, ""); +static_assert((Triple::VendorType)ZigLLVM_Mesa == Triple::Mesa, ""); +static_assert((Triple::VendorType)ZigLLVM_SUSE == Triple::SUSE, ""); +static_assert((Triple::VendorType)ZigLLVM_OpenEmbedded == Triple::OpenEmbedded, ""); +// Uncomment this when testing LLVM 8.0.0 +//static_assert((Triple::VendorType)ZigLLVM_LastVendorType == Triple::LastVendorType, ""); + +static_assert((Triple::OSType)ZigLLVM_UnknownOS == Triple::UnknownOS, ""); +static_assert((Triple::OSType)ZigLLVM_Ananas == Triple::Ananas, ""); +static_assert((Triple::OSType)ZigLLVM_CloudABI == Triple::CloudABI, ""); +static_assert((Triple::OSType)ZigLLVM_Darwin == Triple::Darwin, ""); +static_assert((Triple::OSType)ZigLLVM_DragonFly == Triple::DragonFly, ""); +static_assert((Triple::OSType)ZigLLVM_FreeBSD == Triple::FreeBSD, ""); +static_assert((Triple::OSType)ZigLLVM_Fuchsia == Triple::Fuchsia, ""); +static_assert((Triple::OSType)ZigLLVM_IOS == Triple::IOS, ""); +static_assert((Triple::OSType)ZigLLVM_KFreeBSD == Triple::KFreeBSD, ""); +static_assert((Triple::OSType)ZigLLVM_Linux == Triple::Linux, ""); +static_assert((Triple::OSType)ZigLLVM_Lv2 == Triple::Lv2, ""); +static_assert((Triple::OSType)ZigLLVM_MacOSX == Triple::MacOSX, ""); +static_assert((Triple::OSType)ZigLLVM_NetBSD == Triple::NetBSD, ""); +static_assert((Triple::OSType)ZigLLVM_OpenBSD == Triple::OpenBSD, ""); +static_assert((Triple::OSType)ZigLLVM_Solaris == Triple::Solaris, ""); +static_assert((Triple::OSType)ZigLLVM_Win32 == Triple::Win32, ""); +static_assert((Triple::OSType)ZigLLVM_Haiku == Triple::Haiku, ""); +static_assert((Triple::OSType)ZigLLVM_Minix == Triple::Minix, ""); +static_assert((Triple::OSType)ZigLLVM_RTEMS == Triple::RTEMS, ""); +static_assert((Triple::OSType)ZigLLVM_NaCl == Triple::NaCl, ""); +static_assert((Triple::OSType)ZigLLVM_CNK == Triple::CNK, ""); +static_assert((Triple::OSType)ZigLLVM_AIX == Triple::AIX, ""); +static_assert((Triple::OSType)ZigLLVM_CUDA == Triple::CUDA, ""); +static_assert((Triple::OSType)ZigLLVM_NVCL == Triple::NVCL, ""); +static_assert((Triple::OSType)ZigLLVM_AMDHSA == Triple::AMDHSA, ""); +static_assert((Triple::OSType)ZigLLVM_PS4 == Triple::PS4, ""); +static_assert((Triple::OSType)ZigLLVM_ELFIAMCU == Triple::ELFIAMCU, ""); +static_assert((Triple::OSType)ZigLLVM_TvOS == Triple::TvOS, ""); +static_assert((Triple::OSType)ZigLLVM_WatchOS == Triple::WatchOS, ""); +static_assert((Triple::OSType)ZigLLVM_Mesa3D == Triple::Mesa3D, ""); +static_assert((Triple::OSType)ZigLLVM_Contiki == Triple::Contiki, ""); +static_assert((Triple::OSType)ZigLLVM_AMDPAL == Triple::AMDPAL, ""); +// Uncomment this when testing LLVM 8.0.0 +//static_assert((Triple::OSType)ZigLLVM_LastOSType == Triple::LastOSType, ""); + +static_assert((Triple::EnvironmentType)ZigLLVM_UnknownEnvironment == Triple::UnknownEnvironment, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_GNU == Triple::GNU, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_GNUABIN32 == Triple::GNUABIN32, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_GNUABI64 == Triple::GNUABI64, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_GNUEABI == Triple::GNUEABI, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_GNUEABIHF == Triple::GNUEABIHF, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_GNUX32 == Triple::GNUX32, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_CODE16 == Triple::CODE16, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_EABI == Triple::EABI, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_EABIHF == Triple::EABIHF, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_Android == Triple::Android, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_Musl == Triple::Musl, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_MuslEABI == Triple::MuslEABI, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_MuslEABIHF == Triple::MuslEABIHF, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_MSVC == Triple::MSVC, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_Itanium == Triple::Itanium, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_Cygnus == Triple::Cygnus, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_CoreCLR == Triple::CoreCLR, ""); +static_assert((Triple::EnvironmentType)ZigLLVM_Simulator == Triple::Simulator, ""); +// Uncomment this when testing LLVM 8.0.0 +//static_assert((Triple::EnvironmentType)ZigLLVM_LastEnvironmentType == Triple::LastEnvironmentType, ""); + +static_assert((Triple::ObjectFormatType)ZigLLVM_UnknownObjectFormat == Triple::UnknownObjectFormat, ""); +static_assert((Triple::ObjectFormatType)ZigLLVM_COFF == Triple::COFF, ""); +static_assert((Triple::ObjectFormatType)ZigLLVM_ELF == Triple::ELF, ""); +static_assert((Triple::ObjectFormatType)ZigLLVM_MachO == Triple::MachO, ""); +static_assert((Triple::ObjectFormatType)ZigLLVM_Wasm == Triple::Wasm, ""); diff --git a/std/build.zig b/std/build.zig index 5d894eeeb..90f5bec65 100644 --- a/std/build.zig +++ b/std/build.zig @@ -150,7 +150,11 @@ pub const Builder = struct { } pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - return LibExeObjStep.createExecutable(self, name, root_src); + return LibExeObjStep.createExecutable(self, name, root_src, false); + } + + pub fn addStaticExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { + return LibExeObjStep.createExecutable(self, name, root_src, true); } pub fn addObject(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { @@ -807,6 +811,11 @@ pub const Target = union(enum) { } }; +const Pkg = struct { + name: []const u8, + path: []const u8, +}; + pub const LibExeObjStep = struct { step: Step, builder: *Builder, @@ -849,11 +858,6 @@ pub const LibExeObjStep = struct { source_files: ArrayList([]const u8), object_src: []const u8, - const Pkg = struct { - name: []const u8, - path: []const u8, - }; - const Kind = enum { Exe, Lib, @@ -891,8 +895,8 @@ pub const LibExeObjStep = struct { return self; } - pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Exe, false, builder.version(0, 0, 0))) catch unreachable; + pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, static: bool) *LibExeObjStep { + const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0))) catch unreachable; return self; } @@ -1270,6 +1274,9 @@ pub const LibExeObjStep = struct { zig_args.append("--ver-patch") catch unreachable; zig_args.append(builder.fmt("{}", self.version.patch)) catch unreachable; } + if (self.kind == Kind.Exe and self.static) { + zig_args.append("--static") catch unreachable; + } switch (self.target) { Target.Native => {}, @@ -1660,6 +1667,7 @@ pub const TestStep = struct { exec_cmd_args: ?[]const ?[]const u8, include_dirs: ArrayList([]const u8), lib_paths: ArrayList([]const u8), + packages: ArrayList(Pkg), object_files: ArrayList([]const u8), no_rosegment: bool, output_path: ?[]const u8, @@ -1680,6 +1688,7 @@ pub const TestStep = struct { .exec_cmd_args = null, .include_dirs = ArrayList([]const u8).init(builder.allocator), .lib_paths = ArrayList([]const u8).init(builder.allocator), + .packages = ArrayList(Pkg).init(builder.allocator), .object_files = ArrayList([]const u8).init(builder.allocator), .no_rosegment = false, .output_path = null, @@ -1695,6 +1704,13 @@ pub const TestStep = struct { self.lib_paths.append(path) catch unreachable; } + pub fn addPackagePath(self: *TestStep, name: []const u8, pkg_index_path: []const u8) void { + self.packages.append(Pkg{ + .name = name, + .path = pkg_index_path, + }) catch unreachable; + } + pub fn setVerbose(self: *TestStep, value: bool) void { self.verbose = value; } @@ -1871,6 +1887,13 @@ pub const TestStep = struct { try zig_args.append(lib_path); } + for (self.packages.toSliceConst()) |pkg| { + zig_args.append("--pkg-begin") catch unreachable; + zig_args.append(pkg.name) catch unreachable; + zig_args.append(builder.pathFromRoot(pkg.path)) catch unreachable; + zig_args.append("--pkg-end") catch unreachable; + } + if (self.no_rosegment) { try zig_args.append("--no-rosegment"); } diff --git a/std/debug/index.zig b/std/debug/index.zig index c31743265..4a96e9d25 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -198,49 +198,44 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, } } -pub inline fn getReturnAddress(frame_count: usize) usize { - var fp = @ptrToInt(@frameAddress()); - var i: usize = 0; - while (fp != 0 and i < frame_count) { - fp = @intToPtr(*const usize, fp).*; - i += 1; +pub const StackIterator = struct { + first_addr: ?usize, + fp: usize, + + pub fn init(first_addr: ?usize) StackIterator { + return StackIterator{ + .first_addr = first_addr, + .fp = @ptrToInt(@frameAddress()), + }; } - return @intToPtr(*const usize, fp + @sizeOf(usize)).*; -} + + fn next(self: *StackIterator) ?usize { + if (self.fp == 0) return null; + self.fp = @intToPtr(*const usize, self.fp).*; + if (self.fp == 0) return null; + + if (self.first_addr) |addr| { + while (self.fp != 0) : (self.fp = @intToPtr(*const usize, self.fp).*) { + const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; + if (addr == return_address) { + self.first_addr = null; + return return_address; + } + } + } + + const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; + return return_address; + } +}; pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { switch (builtin.os) { builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr), else => {}, } - const AddressState = union(enum) { - NotLookingForStartAddress, - LookingForStartAddress: usize, - }; - // TODO: I want to express like this: - //var addr_state = if (start_addr) |addr| AddressState { .LookingForStartAddress = addr } - // else AddressState.NotLookingForStartAddress; - var addr_state: AddressState = undefined; - if (start_addr) |addr| { - addr_state = AddressState{ .LookingForStartAddress = addr }; - } else { - addr_state = AddressState.NotLookingForStartAddress; - } - - var fp = @ptrToInt(@frameAddress()); - while (fp != 0) : (fp = @intToPtr(*const usize, fp).*) { - const return_address = @intToPtr(*const usize, fp + @sizeOf(usize)).*; - - switch (addr_state) { - AddressState.NotLookingForStartAddress => {}, - AddressState.LookingForStartAddress => |addr| { - if (return_address == addr) { - addr_state = AddressState.NotLookingForStartAddress; - } else { - continue; - } - }, - } + var it = StackIterator.init(start_addr); + while (it.next()) |return_address| { try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); } } @@ -284,7 +279,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres const mod_index = for (di.sect_contribs) |sect_contrib| { if (sect_contrib.Section > di.coff.sections.len) continue; // Remember that SectionContribEntry.Section is 1-based. - coff_section = &di.coff.sections.toSlice()[sect_contrib.Section-1]; + coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1]; const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; const vaddr_end = vaddr_start + sect_contrib.Size; @@ -414,7 +409,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres if (opt_line_info) |line_info| { try out_stream.print("\n"); - if (printLineFromFile(out_stream, line_info)) { + if (printLineFromFileAnyOs(out_stream, line_info)) { if (line_info.column == 0) { try out_stream.write("\n"); } else { @@ -598,7 +593,15 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt } else "???"; if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| { defer line_info.deinit(); - try printLineInfo(di, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color); + try printLineInfo( + out_stream, + line_info, + address, + symbol_name, + compile_unit_name, + tty_color, + printLineFromFileAnyOs, + ); } else |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { if (tty_color) { @@ -611,7 +614,15 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt } } -pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { +/// This function works in freestanding mode. +/// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void +pub fn printSourceAtAddressDwarf( + debug_info: *DwarfInfo, + out_stream: var, + address: usize, + tty_color: bool, + comptime printLineFromFile: var, +) !void { const compile_unit = findCompileUnit(debug_info, address) catch { if (tty_color) { try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); @@ -621,10 +632,18 @@ pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, addres return; }; const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); - if (getLineNumberInfoLinux(debug_info, compile_unit, address - 1)) |line_info| { + if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| { defer line_info.deinit(); const symbol_name = "???"; - try printLineInfo(debug_info, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color); + try printLineInfo( + out_stream, + line_info, + address, + symbol_name, + compile_unit_name, + tty_color, + printLineFromFile, + ); } else |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { if (tty_color) { @@ -637,14 +656,18 @@ pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, addres } } +pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { + return printSourceAtAddressDwarf(debug_info, out_stream, address, tty_color, printLineFromFileAnyOs); +} + fn printLineInfo( - debug_info: *DebugInfo, out_stream: var, line_info: LineInfo, address: usize, symbol_name: []const u8, compile_unit_name: []const u8, tty_color: bool, + comptime printLineFromFile: var, ) !void { if (tty_color) { try out_stream.print( @@ -872,55 +895,68 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { return list.toOwnedSlice(); } -fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo { - var di = DebugInfo{ - .self_exe_file = undefined, - .elf = undefined, - .debug_info = undefined, - .debug_abbrev = undefined, - .debug_str = undefined, - .debug_line = undefined, - .debug_ranges = null, - .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator), - .compile_unit_list = ArrayList(CompileUnit).init(allocator), +fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section { + const elf_header = (try elf_file.findSection(name)) orelse return null; + return DwarfInfo.Section{ + .offset = elf_header.offset, + .size = elf_header.size, }; - di.self_exe_file = try os.openSelfExe(); - errdefer di.self_exe_file.close(); +} - try di.elf.openFile(allocator, di.self_exe_file); - errdefer di.elf.close(); +/// Initialize DWARF info. The caller has the responsibility to initialize most +/// the DwarfInfo fields before calling. These fields can be left undefined: +/// * abbrev_table_list +/// * compile_unit_list +pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { + di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); + di.compile_unit_list = ArrayList(CompileUnit).init(allocator); + try scanAllCompileUnits(di); +} - di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo; - di.debug_abbrev = (try di.elf.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo; - di.debug_str = (try di.elf.findSection(".debug_str")) orelse return error.MissingDebugInfo; - di.debug_line = (try di.elf.findSection(".debug_line")) orelse return error.MissingDebugInfo; - di.debug_ranges = (try di.elf.findSection(".debug_ranges")); - try scanAllCompileUnits(&di); +pub fn openElfDebugInfo( + allocator: *mem.Allocator, + elf_seekable_stream: *DwarfSeekableStream, + elf_in_stream: *DwarfInStream, +) !DwarfInfo { + var efile: elf.Elf = undefined; + try efile.openStream(allocator, elf_seekable_stream, elf_in_stream); + errdefer efile.close(); + + var di = DwarfInfo{ + .dwarf_seekable_stream = elf_seekable_stream, + .dwarf_in_stream = elf_in_stream, + .endian = efile.endian, + .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo, + .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo, + .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo, + .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo, + .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), + .abbrev_table_list = undefined, + .compile_unit_list = undefined, + }; + try openDwarfDebugInfo(&di, allocator); return di; } -pub fn findElfSection(elf: *Elf, name: []const u8) ?*elf.Shdr { - var file_stream = elf.in_file.inStream(); - const in = &file_stream.stream; +fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DwarfInfo { + const S = struct { + var self_exe_file: os.File = undefined; + var self_exe_seekable_stream: os.File.SeekableStream = undefined; + var self_exe_in_stream: os.File.InStream = undefined; + }; + S.self_exe_file = try os.openSelfExe(); + errdefer S.self_exe_file.close(); - section_loop: for (elf.section_headers) |*elf_section| { - if (elf_section.sh_type == SHT_NULL) continue; + S.self_exe_seekable_stream = S.self_exe_file.seekableStream(); + S.self_exe_in_stream = S.self_exe_file.inStream(); - const name_offset = elf.string_section.offset + elf_section.name; - try elf.in_file.seekTo(name_offset); - - for (name) |expected_c| { - const target_c = try in.readByte(); - if (target_c == 0 or expected_c != target_c) continue :section_loop; - } - - { - const null_byte = try in.readByte(); - if (null_byte == 0) return elf_section; - } - } - - return null; + return openElfDebugInfo( + allocator, + // TODO https://github.com/ziglang/zig/issues/764 + @ptrCast(*DwarfSeekableStream, &S.self_exe_seekable_stream.stream), + // TODO https://github.com/ziglang/zig/issues/764 + @ptrCast(*DwarfInStream, &S.self_exe_in_stream.stream), + ); } fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { @@ -1000,7 +1036,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { }; } -fn printLineFromFile(out_stream: var, line_info: LineInfo) !void { +fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { var f = try os.File.openRead(line_info.file_name); defer f.close(); // TODO fstat and make sure that the file has the correct size @@ -1053,6 +1089,35 @@ const MachOFile = struct { sect_debug_line: ?*const macho.section_64, }; +pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); +pub const DwarfInStream = io.InStream(anyerror); + +pub const DwarfInfo = struct { + dwarf_seekable_stream: *DwarfSeekableStream, + dwarf_in_stream: *DwarfInStream, + endian: builtin.Endian, + debug_info: Section, + debug_abbrev: Section, + debug_str: Section, + debug_line: Section, + debug_ranges: ?Section, + abbrev_table_list: ArrayList(AbbrevTableHeader), + compile_unit_list: ArrayList(CompileUnit), + + pub const Section = struct { + offset: usize, + size: usize, + }; + + pub fn allocator(self: DwarfInfo) *mem.Allocator { + return self.abbrev_table_list.allocator; + } + + pub fn readString(self: *DwarfInfo) ![]u8 { + return readStringRaw(self.allocator(), self.dwarf_in_stream); + } +}; + pub const DebugInfo = switch (builtin.os) { builtin.Os.macosx => struct { symbols: []const MachoSymbol, @@ -1076,32 +1141,7 @@ pub const DebugInfo = switch (builtin.os) { sect_contribs: []pdb.SectionContribEntry, modules: []Module, }, - builtin.Os.linux => struct { - self_exe_file: os.File, - elf: elf.Elf, - debug_info: *elf.SectionHeader, - debug_abbrev: *elf.SectionHeader, - debug_str: *elf.SectionHeader, - debug_line: *elf.SectionHeader, - debug_ranges: ?*elf.SectionHeader, - abbrev_table_list: ArrayList(AbbrevTableHeader), - compile_unit_list: ArrayList(CompileUnit), - - pub fn allocator(self: DebugInfo) *mem.Allocator { - return self.abbrev_table_list.allocator; - } - - pub fn readString(self: *DebugInfo) ![]u8 { - var in_file_stream = self.self_exe_file.inStream(); - const in_stream = &in_file_stream.stream; - return readStringRaw(self.allocator(), in_stream); - } - - pub fn close(self: *DebugInfo) void { - self.self_exe_file.close(); - self.elf.close(); - } - }, + builtin.Os.linux => DwarfInfo, builtin.Os.freebsd => struct {}, else => @compileError("Unsupported OS"), }; @@ -1206,11 +1246,11 @@ const Die = struct { }; } - fn getAttrString(self: *const Die, st: *DebugInfo, id: u64) ![]u8 { + fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { FormValue.String => |value| value, - FormValue.StrPtr => |offset| getString(st, offset), + FormValue.StrPtr => |offset| getString(di, offset), else => error.InvalidDebugInfo, }; } @@ -1223,14 +1263,15 @@ const FileEntry = struct { len_bytes: usize, }; -const LineInfo = struct { +pub const LineInfo = struct { line: usize, column: usize, - file_name: []u8, - allocator: *mem.Allocator, + file_name: []const u8, + allocator: ?*mem.Allocator, - fn deinit(self: *const LineInfo) void { - self.allocator.free(self.file_name); + fn deinit(self: LineInfo) void { + const allocator = self.allocator orelse return; + allocator.free(self.file_name); } }; @@ -1321,10 +1362,10 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { return buf.toSlice(); } -fn getString(st: *DebugInfo, offset: u64) ![]u8 { - const pos = st.debug_str.offset + offset; - try st.self_exe_file.seekTo(pos); - return st.readString(); +fn getString(di: *DwarfInfo, offset: u64) ![]u8 { + const pos = di.debug_str.offset + offset; + try di.dwarf_seekable_stream.seekTo(pos); + return di.readString(); } fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { @@ -1371,14 +1412,7 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type return parseFormValueRefLen(allocator, in_stream, block_len); } -const ParseFormValueError = error{ - EndOfStream, - InvalidDebugInfo, - EndOfFile, - OutOfMemory, -} || std.os.File.ReadError; - -fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { +fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue { return switch (form_id) { DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), @@ -1428,25 +1462,22 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64 }; } -fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable { - const in_file = st.self_exe_file; - var in_file_stream = in_file.inStream(); - const in_stream = &in_file_stream.stream; - var result = AbbrevTable.init(st.allocator()); +fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { + var result = AbbrevTable.init(di.allocator()); while (true) { - const abbrev_code = try readULeb128(in_stream); + const abbrev_code = try readULeb128(di.dwarf_in_stream); if (abbrev_code == 0) return result; try result.append(AbbrevTableEntry{ .abbrev_code = abbrev_code, - .tag_id = try readULeb128(in_stream), - .has_children = (try in_stream.readByte()) == DW.CHILDREN_yes, - .attrs = ArrayList(AbbrevAttr).init(st.allocator()), + .tag_id = try readULeb128(di.dwarf_in_stream), + .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, + .attrs = ArrayList(AbbrevAttr).init(di.allocator()), }); const attrs = &result.items[result.len - 1].attrs; while (true) { - const attr_id = try readULeb128(in_stream); - const form_id = try readULeb128(in_stream); + const attr_id = try readULeb128(di.dwarf_in_stream); + const form_id = try readULeb128(di.dwarf_in_stream); if (attr_id == 0 and form_id == 0) break; try attrs.append(AbbrevAttr{ .attr_id = attr_id, @@ -1458,18 +1489,18 @@ fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable { /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, /// seeks in the stream and parses it. -fn getAbbrevTable(st: *DebugInfo, abbrev_offset: u64) !*const AbbrevTable { - for (st.abbrev_table_list.toSlice()) |*header| { +fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { + for (di.abbrev_table_list.toSlice()) |*header| { if (header.offset == abbrev_offset) { return &header.table; } } - try st.self_exe_file.seekTo(st.debug_abbrev.offset + abbrev_offset); - try st.abbrev_table_list.append(AbbrevTableHeader{ + try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); + try di.abbrev_table_list.append(AbbrevTableHeader{ .offset = abbrev_offset, - .table = try parseAbbrevTable(st), + .table = try parseAbbrevTable(di), }); - return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table; + return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; } fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { @@ -1479,23 +1510,20 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con return null; } -fn parseDie(st: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { - const in_file = st.self_exe_file; - var in_file_stream = in_file.inStream(); - const in_stream = &in_file_stream.stream; - const abbrev_code = try readULeb128(in_stream); +fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { + const abbrev_code = try readULeb128(di.dwarf_in_stream); const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; var result = Die{ .tag_id = table_entry.tag_id, .has_children = table_entry.has_children, - .attrs = ArrayList(Die.Attr).init(st.allocator()), + .attrs = ArrayList(Die.Attr).init(di.allocator()), }; try result.attrs.resize(table_entry.attrs.len); for (table_entry.attrs.toSliceConst()) |attr, i| { result.attrs.items[i] = Die.Attr{ .id = attr.attr_id, - .value = try parseFormValue(st.allocator(), in_stream, attr.form_id, is_64), + .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), }; } return result; @@ -1699,22 +1727,18 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u return error.MissingDebugInfo; } -fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, target_address: usize) !LineInfo { +fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); - const in_file = di.self_exe_file; const debug_line_end = di.debug_line.offset + di.debug_line.size; var this_offset = di.debug_line.offset; var this_index: usize = 0; - var in_file_stream = in_file.inStream(); - const in_stream = &in_file_stream.stream; - while (this_offset < debug_line_end) : (this_index += 1) { - try in_file.seekTo(this_offset); + try di.dwarf_seekable_stream.seekTo(this_offset); var is_64: bool = undefined; - const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64); + const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); if (unit_length == 0) return error.MissingDebugInfo; const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); @@ -1723,35 +1747,35 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ continue; } - const version = try in_stream.readInt(di.elf.endian, u16); + const version = try di.dwarf_in_stream.readInt(di.endian, u16); // TODO support 3 and 5 if (version != 2 and version != 4) return error.InvalidDebugInfo; - const prologue_length = if (is_64) try in_stream.readInt(di.elf.endian, u64) else try in_stream.readInt(di.elf.endian, u32); - const prog_start_offset = (try in_file.getPos()) + prologue_length; + const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(di.endian, u64) else try di.dwarf_in_stream.readInt(di.endian, u32); + const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length; - const minimum_instruction_length = try in_stream.readByte(); + const minimum_instruction_length = try di.dwarf_in_stream.readByte(); if (minimum_instruction_length == 0) return error.InvalidDebugInfo; if (version >= 4) { // maximum_operations_per_instruction - _ = try in_stream.readByte(); + _ = try di.dwarf_in_stream.readByte(); } - const default_is_stmt = (try in_stream.readByte()) != 0; - const line_base = try in_stream.readByteSigned(); + const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0; + const line_base = try di.dwarf_in_stream.readByteSigned(); - const line_range = try in_stream.readByte(); + const line_range = try di.dwarf_in_stream.readByte(); if (line_range == 0) return error.InvalidDebugInfo; - const opcode_base = try in_stream.readByte(); + const opcode_base = try di.dwarf_in_stream.readByte(); const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); { var i: usize = 0; while (i < opcode_base - 1) : (i += 1) { - standard_opcode_lengths[i] = try in_stream.readByte(); + standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); } } @@ -1769,9 +1793,9 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ while (true) { const file_name = try di.readString(); if (file_name.len == 0) break; - const dir_index = try readULeb128(in_stream); - const mtime = try readULeb128(in_stream); - const len_bytes = try readULeb128(in_stream); + const dir_index = try readULeb128(di.dwarf_in_stream); + const mtime = try readULeb128(di.dwarf_in_stream); + const len_bytes = try readULeb128(di.dwarf_in_stream); try file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, @@ -1780,15 +1804,15 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ }); } - try in_file.seekTo(prog_start_offset); + try di.dwarf_seekable_stream.seekTo(prog_start_offset); while (true) { - const opcode = try in_stream.readByte(); + const opcode = try di.dwarf_in_stream.readByte(); if (opcode == DW.LNS_extended_op) { - const op_size = try readULeb128(in_stream); + const op_size = try readULeb128(di.dwarf_in_stream); if (op_size < 1) return error.InvalidDebugInfo; - var sub_op = try in_stream.readByte(); + var sub_op = try di.dwarf_in_stream.readByte(); switch (sub_op) { DW.LNE_end_sequence => { prog.end_sequence = true; @@ -1796,14 +1820,14 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ return error.MissingDebugInfo; }, DW.LNE_set_address => { - const addr = try in_stream.readInt(di.elf.endian, usize); + const addr = try di.dwarf_in_stream.readInt(di.endian, usize); prog.address = addr; }, DW.LNE_define_file => { const file_name = try di.readString(); - const dir_index = try readULeb128(in_stream); - const mtime = try readULeb128(in_stream); - const len_bytes = try readULeb128(in_stream); + const dir_index = try readULeb128(di.dwarf_in_stream); + const mtime = try readULeb128(di.dwarf_in_stream); + const len_bytes = try readULeb128(di.dwarf_in_stream); try file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, @@ -1813,7 +1837,7 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ }, else => { const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; - try in_file.seekForward(fwd_amt); + try di.dwarf_seekable_stream.seekForward(fwd_amt); }, } } else if (opcode >= opcode_base) { @@ -1832,19 +1856,19 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ prog.basic_block = false; }, DW.LNS_advance_pc => { - const arg = try readULeb128(in_stream); + const arg = try readULeb128(di.dwarf_in_stream); prog.address += arg * minimum_instruction_length; }, DW.LNS_advance_line => { - const arg = try readILeb128(in_stream); + const arg = try readILeb128(di.dwarf_in_stream); prog.line += arg; }, DW.LNS_set_file => { - const arg = try readULeb128(in_stream); + const arg = try readULeb128(di.dwarf_in_stream); prog.file = arg; }, DW.LNS_set_column => { - const arg = try readULeb128(in_stream); + const arg = try readULeb128(di.dwarf_in_stream); prog.column = arg; }, DW.LNS_negate_stmt => { @@ -1858,14 +1882,14 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ prog.address += inc_addr; }, DW.LNS_fixed_advance_pc => { - const arg = try in_stream.readInt(di.elf.endian, u16); + const arg = try di.dwarf_in_stream.readInt(di.endian, u16); prog.address += arg; }, DW.LNS_set_prologue_end => {}, else => { if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; const len_bytes = standard_opcode_lengths[opcode - 1]; - try in_file.seekForward(len_bytes); + try di.dwarf_seekable_stream.seekForward(len_bytes); }, } } @@ -1877,36 +1901,33 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ return error.MissingDebugInfo; } -fn scanAllCompileUnits(st: *DebugInfo) !void { - const debug_info_end = st.debug_info.offset + st.debug_info.size; - var this_unit_offset = st.debug_info.offset; +fn scanAllCompileUnits(di: *DwarfInfo) !void { + const debug_info_end = di.debug_info.offset + di.debug_info.size; + var this_unit_offset = di.debug_info.offset; var cu_index: usize = 0; - var in_file_stream = st.self_exe_file.inStream(); - const in_stream = &in_file_stream.stream; - while (this_unit_offset < debug_info_end) { - try st.self_exe_file.seekTo(this_unit_offset); + try di.dwarf_seekable_stream.seekTo(this_unit_offset); var is_64: bool = undefined; - const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64); + const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); if (unit_length == 0) return; const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); - const version = try in_stream.readInt(st.elf.endian, u16); + const version = try di.dwarf_in_stream.readInt(di.endian, u16); if (version < 2 or version > 5) return error.InvalidDebugInfo; - const debug_abbrev_offset = if (is_64) try in_stream.readInt(st.elf.endian, u64) else try in_stream.readInt(st.elf.endian, u32); + const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(di.endian, u64) else try di.dwarf_in_stream.readInt(di.endian, u32); - const address_size = try in_stream.readByte(); + const address_size = try di.dwarf_in_stream.readByte(); if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; - const compile_unit_pos = try st.self_exe_file.getPos(); - const abbrev_table = try getAbbrevTable(st, debug_abbrev_offset); + const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); + const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset); - try st.self_exe_file.seekTo(compile_unit_pos); + try di.dwarf_seekable_stream.seekTo(compile_unit_pos); - const compile_unit_die = try st.allocator().create(try parseDie(st, abbrev_table, is_64)); + const compile_unit_die = try di.allocator().create(try parseDie(di, abbrev_table, is_64)); if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; @@ -1934,7 +1955,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void { } }; - try st.compile_unit_list.append(CompileUnit{ + try di.compile_unit_list.append(CompileUnit{ .version = version, .is_64 = is_64, .pc_range = pc_range, @@ -1947,20 +1968,18 @@ fn scanAllCompileUnits(st: *DebugInfo) !void { } } -fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit { - var in_file_stream = st.self_exe_file.inStream(); - const in_stream = &in_file_stream.stream; - for (st.compile_unit_list.toSlice()) |*compile_unit| { +fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { + for (di.compile_unit_list.toSlice()) |*compile_unit| { if (compile_unit.pc_range) |range| { if (target_address >= range.start and target_address < range.end) return compile_unit; } if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { var base_address: usize = 0; - if (st.debug_ranges) |debug_ranges| { - try st.self_exe_file.seekTo(debug_ranges.offset + ranges_offset); + if (di.debug_ranges) |debug_ranges| { + try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); while (true) { - const begin_addr = try in_stream.readIntLe(usize); - const end_addr = try in_stream.readIntLe(usize); + const begin_addr = try di.dwarf_in_stream.readIntLe(usize); + const end_addr = try di.dwarf_in_stream.readIntLe(usize); if (begin_addr == 0 and end_addr == 0) { break; } diff --git a/std/elf.zig b/std/elf.zig index e95222744..cf7c29b1e 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -353,7 +353,8 @@ pub const SectionHeader = struct { }; pub const Elf = struct { - in_file: os.File, + seekable_stream: *io.SeekableStream(anyerror, anyerror), + in_stream: *io.InStream(anyerror), auto_close_stream: bool, is_64: bool, endian: builtin.Endian, @@ -370,19 +371,24 @@ pub const Elf = struct { /// Call close when done. pub fn openPath(elf: *Elf, allocator: *mem.Allocator, path: []const u8) !void { - try elf.prealloc_file.open(path); - try elf.openFile(allocator, *elf.prealloc_file); - elf.auto_close_stream = true; + @compileError("TODO implement"); } /// Call close when done. pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: os.File) !void { - elf.allocator = allocator; - elf.in_file = file; - elf.auto_close_stream = false; + @compileError("TODO implement"); + } - var file_stream = elf.in_file.inStream(); - const in = &file_stream.stream; + pub fn openStream( + elf: *Elf, + allocator: *mem.Allocator, + seekable_stream: *io.SeekableStream(anyerror, anyerror), + in: *io.InStream(anyerror), + ) !void { + elf.auto_close_stream = false; + elf.allocator = allocator; + elf.seekable_stream = seekable_stream; + elf.in_stream = in; var magic: [4]u8 = undefined; try in.readNoEof(magic[0..]); @@ -404,7 +410,7 @@ pub const Elf = struct { if (version_byte != 1) return error.InvalidFormat; // skip over padding - try elf.in_file.seekForward(9); + try seekable_stream.seekForward(9); elf.file_type = switch (try in.readInt(elf.endian, u16)) { 1 => FileType.Relocatable, @@ -441,7 +447,7 @@ pub const Elf = struct { } // skip over flags - try elf.in_file.seekForward(4); + try seekable_stream.seekForward(4); const header_size = try in.readInt(elf.endian, u16); if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) { @@ -461,12 +467,12 @@ pub const Elf = struct { const ph_byte_count = u64(ph_entry_size) * u64(ph_entry_count); const end_ph = try math.add(u64, elf.program_header_offset, ph_byte_count); - const stream_end = try elf.in_file.getEndPos(); + const stream_end = try seekable_stream.getEndPos(); if (stream_end < end_sh or stream_end < end_ph) { return error.InvalidFormat; } - try elf.in_file.seekTo(elf.section_header_offset); + try seekable_stream.seekTo(elf.section_header_offset); elf.section_headers = try elf.allocator.alloc(SectionHeader, sh_entry_count); errdefer elf.allocator.free(elf.section_headers); @@ -521,26 +527,23 @@ pub const Elf = struct { pub fn close(elf: *Elf) void { elf.allocator.free(elf.section_headers); - if (elf.auto_close_stream) elf.in_file.close(); + if (elf.auto_close_stream) elf.prealloc_file.close(); } pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader { - var file_stream = elf.in_file.inStream(); - const in = &file_stream.stream; - section_loop: for (elf.section_headers) |*elf_section| { if (elf_section.sh_type == SHT_NULL) continue; const name_offset = elf.string_section.offset + elf_section.name; - try elf.in_file.seekTo(name_offset); + try elf.seekable_stream.seekTo(name_offset); for (name) |expected_c| { - const target_c = try in.readByte(); + const target_c = try elf.in_stream.readByte(); if (target_c == 0 or expected_c != target_c) continue :section_loop; } { - const null_byte = try in.readByte(); + const null_byte = try elf.in_stream.readByte(); if (null_byte == 0) return elf_section; } } @@ -549,7 +552,7 @@ pub const Elf = struct { } pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void { - try elf.in_file.seekTo(elf_section.offset); + try elf.seekable_stream.seekTo(elf_section.offset); } }; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index b4b2fdb01..eda0bfae0 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -2,6 +2,7 @@ const std = @import("../index.zig"); const math = std.math; const debug = std.debug; const assert = debug.assert; +const assertError = debug.assertError; const mem = std.mem; const builtin = @import("builtin"); const errol = @import("errol/index.zig"); @@ -811,13 +812,41 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned for (buf) |c| { const digit = try charToDigit(c, radix); - x = try math.mul(T, x, radix); - x = try math.add(T, x, digit); + + if (x != 0) x = try math.mul(T, x, try math.cast(T, radix)); + x = try math.add(T, x, try math.cast(T, digit)); } return x; } +test "parseUnsigned" { + assert((try parseUnsigned(u16, "050124", 10)) == 50124); + assert((try parseUnsigned(u16, "65535", 10)) == 65535); + assertError(parseUnsigned(u16, "65536", 10), error.Overflow); + + assert((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); + assertError(parseUnsigned(u64, "10000000000000000", 16), error.Overflow); + + assert((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); + + assert((try parseUnsigned(u7, "1", 10)) == 1); + assert((try parseUnsigned(u7, "1000", 2)) == 8); + + assertError(parseUnsigned(u32, "f", 10), error.InvalidCharacter); + assertError(parseUnsigned(u8, "109", 8), error.InvalidCharacter); + + assert((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); + + // these numbers should fit even though the radix itself doesn't fit in the destination type + assert((try parseUnsigned(u1, "0", 10)) == 0); + assert((try parseUnsigned(u1, "1", 10)) == 1); + assertError(parseUnsigned(u1, "2", 10), error.Overflow); + assert((try parseUnsigned(u1, "001", 16)) == 1); + assert((try parseUnsigned(u2, "3", 16)) == 3); + assertError(parseUnsigned(u2, "4", 16), error.Overflow); +} + pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { const value = switch (c) { '0'...'9' => c - '0', diff --git a/std/hash_map.zig b/std/hash_map.zig index 1b299eff7..99237047e 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -126,6 +126,14 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 }; } + pub fn getOrPutValue(self: *Self, key: K, value: V) !*KV { + const res = try self.getOrPut(key); + if (!res.found_existing) + res.kv.value = value; + + return res.kv; + } + fn ensureCapacity(self: *Self) !void { if (self.entries.len == 0) { return self.initCapacity(16); @@ -354,6 +362,12 @@ test "basic hash map usage" { gop2.kv.value = 42; assert(map.get(99).?.value == 42); + const gop3 = try map.getOrPutValue(5, 5); + assert(gop3.value == 77); + + const gop4 = try map.getOrPutValue(100, 41); + assert(gop4.value == 41); + assert(map.contains(2)); assert(map.get(2).?.value == 22); _ = map.remove(2); diff --git a/std/io.zig b/std/io.zig index 6473d993c..bdca2b03e 100644 --- a/std/io.zig +++ b/std/io.zig @@ -32,6 +32,8 @@ pub fn getStdIn() GetStdIoErrs!File { return File.openHandle(handle); } +pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; + pub fn InStream(comptime ReadError: type) type { return struct { const Self = @This(); @@ -683,25 +685,73 @@ test "import io tests" { } } -pub fn readLine(buf: []u8) !usize { - var stdin = getStdIn() catch return error.StdInUnavailable; - var adapter = stdin.inStream(); - var stream = &adapter.stream; - var index: usize = 0; +pub fn readLine(buf: *std.Buffer) ![]u8 { + var stdin = try getStdIn(); + var stdin_stream = stdin.inStream(); + return readLineFrom(&stdin_stream.stream, buf); +} + +/// Reads all characters until the next newline into buf, and returns +/// a slice of the characters read (excluding the newline character(s)). +pub fn readLineFrom(stream: var, buf: *std.Buffer) ![]u8 { + const start = buf.len(); while (true) { - const byte = stream.readByte() catch return error.EndOfFile; + const byte = try stream.readByte(); switch (byte) { '\r' => { // trash the following \n - _ = stream.readByte() catch return error.EndOfFile; - return index; - }, - '\n' => return index, - else => { - if (index == buf.len) return error.InputTooLong; - buf[index] = byte; - index += 1; + _ = try stream.readByte(); + return buf.toSlice()[start..]; }, + '\n' => return buf.toSlice()[start..], + else => try buf.appendByte(byte), } } } + +test "io.readLineFrom" { + var bytes: [128]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + + var buf = try std.Buffer.initSize(allocator, 0); + var mem_stream = SliceInStream.init( + \\Line 1 + \\Line 22 + \\Line 333 + ); + const stream = &mem_stream.stream; + + debug.assert(mem.eql(u8, "Line 1", try readLineFrom(stream, &buf))); + debug.assert(mem.eql(u8, "Line 22", try readLineFrom(stream, &buf))); + debug.assertError(readLineFrom(stream, &buf), error.EndOfStream); + debug.assert(mem.eql(u8, buf.toSlice(), "Line 1Line 22Line 333")); +} + +pub fn readLineSlice(slice: []u8) ![]u8 { + var stdin = try getStdIn(); + var stdin_stream = stdin.inStream(); + return readLineSliceFrom(&stdin_stream.stream, slice); +} + +/// Reads all characters until the next newline into slice, and returns +/// a slice of the characters read (excluding the newline character(s)). +pub fn readLineSliceFrom(stream: var, slice: []u8) ![]u8 { + // We cannot use Buffer.fromOwnedSlice, as it wants to append a null byte + // after taking ownership, which would always require an allocation. + var buf = std.Buffer{ .list = std.ArrayList(u8).fromOwnedSlice(debug.failing_allocator, slice) }; + try buf.resize(0); + return try readLineFrom(stream, &buf); +} + +test "io.readLineSliceFrom" { + var buf: [7]u8 = undefined; + var mem_stream = SliceInStream.init( + \\Line 1 + \\Line 22 + \\Line 333 + ); + const stream = &mem_stream.stream; + + debug.assert(mem.eql(u8, "Line 1", try readLineSliceFrom(stream, buf[0..]))); + debug.assertError(readLineSliceFrom(stream, buf[0..]), error.OutOfMemory); +} diff --git a/std/io/seekable_stream.zig b/std/io/seekable_stream.zig new file mode 100644 index 000000000..a766f4fb8 --- /dev/null +++ b/std/io/seekable_stream.zig @@ -0,0 +1,32 @@ +const std = @import("../index.zig"); +const InStream = std.io.InStream; + +pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type { + return struct { + const Self = @This(); + pub const SeekError = SeekErrorType; + pub const GetSeekPosError = GetSeekPosErrorType; + + seekToFn: fn (self: *Self, pos: usize) SeekError!void, + seekForwardFn: fn (self: *Self, pos: isize) SeekError!void, + + getPosFn: fn (self: *Self) GetSeekPosError!usize, + getEndPosFn: fn (self: *Self) GetSeekPosError!usize, + + pub fn seekTo(self: *Self, pos: usize) SeekError!void { + return self.seekToFn(self, pos); + } + + pub fn seekForward(self: *Self, amt: isize) SeekError!void { + return self.seekForwardFn(self, amt); + } + + pub fn getEndPos(self: *Self) GetSeekPosError!usize { + return self.getEndPosFn(self); + } + + pub fn getPos(self: *Self) GetSeekPosError!usize { + return self.getPosFn(self); + } + }; +} diff --git a/std/linked_list.zig b/std/linked_list.zig index 46cbeb03c..c3db55b5a 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -82,6 +82,28 @@ pub fn LinkedList(comptime T: type) type { list.len += 1; } + /// Concatenate list2 onto the end of list1, removing all entries from the former. + /// + /// Arguments: + /// list1: the list to concatenate onto + /// list2: the list to be concatenated + pub fn concatByMoving(list1: *Self, list2: *Self) void { + const l2_first = list2.first orelse return; + if (list1.last) |l1_last| { + l1_last.next = list2.first; + l2_first.prev = list1.last; + list1.len += list2.len; + } else { + // list1 was empty + list1.first = list2.first; + list1.len = list2.len; + } + list1.last = list2.last; + list2.first = null; + list2.last = null; + list2.len = 0; + } + /// Insert a new node at the end of the list. /// /// Arguments: @@ -247,3 +269,77 @@ test "basic linked list test" { assert(list.last.?.data == 4); assert(list.len == 2); } + +test "linked list concatenation" { + const allocator = debug.global_allocator; + var list1 = LinkedList(u32).init(); + var list2 = LinkedList(u32).init(); + + var one = try list1.createNode(1, allocator); + defer list1.destroyNode(one, allocator); + var two = try list1.createNode(2, allocator); + defer list1.destroyNode(two, allocator); + var three = try list1.createNode(3, allocator); + defer list1.destroyNode(three, allocator); + var four = try list1.createNode(4, allocator); + defer list1.destroyNode(four, allocator); + var five = try list1.createNode(5, allocator); + defer list1.destroyNode(five, allocator); + + list1.append(one); + list1.append(two); + list2.append(three); + list2.append(four); + list2.append(five); + + list1.concatByMoving(&list2); + + assert(list1.last == five); + assert(list1.len == 5); + assert(list2.first == null); + assert(list2.last == null); + assert(list2.len == 0); + + // Traverse forwards. + { + var it = list1.first; + var index: u32 = 1; + while (it) |node| : (it = node.next) { + assert(node.data == index); + index += 1; + } + } + + // Traverse backwards. + { + var it = list1.last; + var index: u32 = 1; + while (it) |node| : (it = node.prev) { + assert(node.data == (6 - index)); + index += 1; + } + } + + // Swap them back, this verifies that concating to an empty list works. + list2.concatByMoving(&list1); + + // Traverse forwards. + { + var it = list2.first; + var index: u32 = 1; + while (it) |node| : (it = node.next) { + assert(node.data == index); + index += 1; + } + } + + // Traverse backwards. + { + var it = list2.last; + var index: u32 = 1; + while (it) |node| : (it = node.prev) { + assert(node.data == (6 - index)); + index += 1; + } + } +} diff --git a/std/os/file.zig b/std/os/file.zig index 82bd24fec..2ae547c69 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -228,7 +228,14 @@ pub const File = struct { return os.isTty(self.handle); } - pub fn seekForward(self: File, amount: isize) !void { + pub const SeekError = error{ + /// TODO make this error impossible to get + Overflow, + Unseekable, + Unexpected, + }; + + pub fn seekForward(self: File, amount: isize) SeekError!void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios, Os.freebsd => { const result = posix.lseek(self.handle, amount, posix.SEEK_CUR); @@ -259,7 +266,7 @@ pub const File = struct { } } - pub fn seekTo(self: File, pos: usize) !void { + pub fn seekTo(self: File, pos: usize) SeekError!void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios, Os.freebsd => { const ipos = try math.cast(isize, pos); @@ -293,7 +300,14 @@ pub const File = struct { } } - pub fn getPos(self: File) !usize { + pub const GetSeekPosError = error{ + Overflow, + SystemResources, + Unseekable, + Unexpected, + }; + + pub fn getPos(self: File) GetSeekPosError!usize { switch (builtin.os) { Os.linux, Os.macosx, Os.ios, Os.freebsd => { const result = posix.lseek(self.handle, 0, posix.SEEK_CUR); @@ -323,13 +337,13 @@ pub const File = struct { } assert(pos >= 0); - return math.cast(usize, pos) catch error.FilePosLargerThanPointerRange; + return math.cast(usize, pos); }, else => @compileError("unsupported OS"), } } - pub fn getEndPos(self: File) !usize { + pub fn getEndPos(self: File) GetSeekPosError!usize { if (is_posix) { const stat = try os.posixFStat(self.handle); return @intCast(usize, stat.size); @@ -431,6 +445,18 @@ pub const File = struct { }; } + pub fn seekableStream(file: File) SeekableStream { + return SeekableStream{ + .file = file, + .stream = SeekableStream.Stream{ + .seekToFn = SeekableStream.seekToFn, + .seekForwardFn = SeekableStream.seekForwardFn, + .getPosFn = SeekableStream.getPosFn, + .getEndPosFn = SeekableStream.getEndPosFn, + }, + }; + } + /// Implementation of io.InStream trait for File pub const InStream = struct { file: File, @@ -458,4 +484,32 @@ pub const File = struct { return self.file.write(bytes); } }; + + /// Implementation of io.SeekableStream trait for File + pub const SeekableStream = struct { + file: File, + stream: Stream, + + pub const Stream = io.SeekableStream(SeekError, GetSeekPosError); + + pub fn seekToFn(seekable_stream: *Stream, pos: usize) SeekError!void { + const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); + return self.file.seekTo(pos); + } + + pub fn seekForwardFn(seekable_stream: *Stream, amt: isize) SeekError!void { + const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); + return self.file.seekForward(amt); + } + + pub fn getEndPosFn(seekable_stream: *Stream) GetSeekPosError!usize { + const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); + return self.file.getEndPos(); + } + + pub fn getPosFn(seekable_stream: *Stream) GetSeekPosError!usize { + const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream); + return self.file.getPos(); + } + }; }; diff --git a/std/unicode.zig b/std/unicode.zig index 559a2e739..fcb748401 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -208,7 +208,7 @@ pub const Utf8View = struct { } }; -const Utf8Iterator = struct { +pub const Utf8Iterator = struct { bytes: []const u8, i: usize, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index c5575a0c0..be839f055 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,18 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "error note for function parameter incompatibility", + \\fn do_the_thing(func: fn (arg: i32) void) void {} + \\fn bar(arg: bool) void {} + \\export fn entry() void { + \\ do_the_thing(bar); + \\} + , + ".tmp_source.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void", + ".tmp_source.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'", + ); + cases.add( "cast negative value to unsigned integer", \\comptime { @@ -5248,8 +5260,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn foo() void { \\ asm volatile ("" : : [bar]"r"(3) : ""); \\} - , - ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_int", + , + ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_int", ); cases.add( @@ -5257,7 +5269,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn foo() void { \\ asm volatile ("" : : [bar]"r"(3.17) : ""); \\} - , - ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float", + , + ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float", ); }