[
  {
    "path": ".arret-root",
    "content": ""
  },
  {
    "path": ".buildkite/build-and-test.sh",
    "content": "#!/bin/sh\nset -eu\n\n# Deny warnings on CI\nexport RUSTFLAGS=\"-D warnings\"\n\necho '--- :cargo: Compiling debug'\ncargo build\n\necho '--- :pray: Testing debug'\ncargo test\n\necho '--- :keyboard: Testing driver'\n./driver/tests/integration/run.sh target/debug/arret\n"
  },
  {
    "path": ".buildkite/llvm-assert.Dockerfile",
    "content": "ARG LLVM_VERSION=11.1.0\nARG LLVM_ROOT=/opt/llvm-11\n\n##\n\nFROM fedora:33 AS fedora-common\n\nRUN dnf install -y gcc-c++\n# `dnf clean all` happens in later stages\n\n##\n\nFROM fedora-common AS llvm-build\nARG LLVM_VERSION\nARG LLVM_ROOT\n\nRUN dnf install -y file cmake ninja-build xz && \\\n  dnf clean all\n\nWORKDIR /usr/src\n\nRUN curl https://github.com/llvm/llvm-project/releases/download/llvmorg-11.1.0/llvm-11.1.0.src.tar.xz -sSL | \\\n  tar -Jx --no-same-owner\n\nWORKDIR /usr/src/llvm-build\n\n# We need to be careful to use less than 4GiB on our build agents\nRUN cmake \\\n  -GNinja \\\n  -DCMAKE_BUILD_TYPE=Release \\\n  -DCMAKE_INSTALL_PREFIX=${LLVM_ROOT} \\\n  -DLLVM_ENABLE_ASSERTIONS=ON \\\n  -DLLVM_TARGETS_TO_BUILD=AArch64 \\\n  -DLLVM_ENABLE_WARNINGS=OFF \\\n  # Disable a spammy ABI change warning on GCC 7 that `ENABLE_WARNINGS=OFF`\n  # doesn't suppress.\n  -DCMAKE_CXX_FLAGS=-Wno-psabi \\\n  -DLLVM_USE_LINKER=gold \\\n  ../llvm-${LLVM_VERSION}.src\n\nRUN ninja install\n\n##\n\nFROM fedora-common\nARG LLVM_ROOT\n\nCOPY --from=llvm-build ${LLVM_ROOT} ${LLVM_ROOT}\n\nRUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.53.0 --profile=minimal --component rustfmt\n\nENV PATH \"/root/.cargo/bin:${PATH}\"\nENV LLVM_SYS_100_PREFIX \"${LLVM_ROOT}\"\n"
  },
  {
    "path": ".buildkite/pipeline.yml",
    "content": "cached-ecr-build-env: &cached-ecr-build-env\n  plugins:\n    - seek-oss/docker-ecr-cache#v1.11.0:\n        target: build-env\n        cache-on:\n          - Cargo.lock\n    - docker#v3.9.0\n\ndocker-ghcr-login: &docker-ghcr-login\n  docker-login#v2.0.1:\n    server: ghcr.io\n    username: etaoins\n    password-env: CR_PAT\n\nsteps:\n  - label: ':muscle: Test ARM64'\n    agents: { queue: arm64 }\n    command:\n      - ./.buildkite/build-and-test.sh\n\n      - \"echo '--- :sleuth_or_spy: Checking release'\"\n      - RUSTFLAGS=\"-Copt-level=0\" cargo check --release\n    <<: *cached-ecr-build-env\n\n  - label: ':ubuntu: Test AMD64'\n    agents: { queue: amd64 }\n    command:\n      - ./.buildkite/build-and-test.sh\n    <<: *cached-ecr-build-env\n\n  - label: ':fedora: Test LLVM assert'\n    branches: '!master'\n    agents: { queue: arm64 }\n    command:\n      - \"echo '--- :prettier: Checking rustfmt'\"\n      - cargo fmt -- --check\n\n      - ./.buildkite/build-and-test.sh\n    plugins:\n      - seek-oss/docker-ecr-cache#v1.11.0:\n          dockerfile: ./.buildkite/llvm-assert.Dockerfile\n      - docker#v3.9.0\n\n  - label: ':typescript: Test VS Code extension'\n    agents: { queue: amd64 }\n    command:\n      - ./.buildkite/vscode-extension-tests.sh\n    plugins:\n      - seek-oss/docker-ecr-cache#v1.11.0:\n          dockerfile: ./editors/code/Dockerfile\n          ecr-name: build-cache/arret/vscode-extension\n          cache-on:\n            - ./editors/code/yarn.lock\n            - ./editors/code/src/test/vsCodeVersion.ts\n      - docker#v3.9.0:\n          volumes:\n            - '/workdir/editors/code/node_modules'\n            - '/workdir/editors/code/.vscode-test'\n\n  - wait\n\n  - label: ':mechanical_arm: Push ARM64 REPL image'\n    key: 'push-arm64-repl-image'\n    branches: 'master'\n    agents: { queue: arm64 }\n    plugins:\n      - *docker-ghcr-login\n      - docker-compose#v3.9.0:\n          push:\n            - repl:ghcr.io/etaoins/arret-repl-arm64\n          env:\n            - BUILDKITE_COMMIT\n\n  - label: ':rocket: Push AMD64 REPL image'\n    key: 'push-amd64-repl-image'\n    branches: 'master'\n    agents: { queue: amd64 }\n    plugins:\n      - *docker-ghcr-login\n      - docker-compose#v3.9.0:\n          push:\n            - repl:ghcr.io/etaoins/arret-repl-amd64\n          env:\n            - BUILDKITE_COMMIT\n\n  - label: ':docker: Update multiarch manifest'\n    branches: 'master'\n    agents: { queue: arm64 }\n    depends_on:\n      - 'push-amd64-repl-image'\n      - 'push-arm64-repl-image'\n    command:\n      - ./.buildkite/update-multiarch-manifest.sh\n    plugins:\n      - *docker-ghcr-login\n\n  - label: ':rust: Check (Rust Beta)'\n    branches: 'master'\n    agents: { queue: arm64 }\n    command:\n      - \"echo '--- :rust: Installing Rust beta'\"\n      - rustup default beta\n\n      - \"echo '--- :male-detective: Checking debug'\"\n      - cargo check\n\n      - \"echo '--- :female-detective: Checking release'\"\n      - RUSTFLAGS=\"-Copt-level=0\" cargo check --release\n    <<: *cached-ecr-build-env\n\n  - label: ':books: Update Rustdoc'\n    branches: 'master'\n    agents: { queue: arm64 }\n    command:\n      - \"echo '--- :book: Building rustdoc'\"\n      - cargo doc --no-deps\n\n      - \"echo '--- :rust: Installing awscli'\"\n      - apt-get update\n      - DEBIAN_FRONTEND=noninteractive apt-get -y install awscli\n\n      - ./.buildkite/sync-rustdoc.sh\n    <<: *cached-ecr-build-env\n    concurrency_group: 'update-rustdoc'\n    concurrency: 1\n"
  },
  {
    "path": ".buildkite/sync-rustdoc.sh",
    "content": "#!/bin/sh\n\nset -eu\n\nS3_BUCKET_NAME=arret-lang-rustdoc\nCLOUDFRONT_DISTRIBUTION_ID=E1FFCMKSLRZAZ\n\necho '--- :s3: Updating S3'\naws s3 sync --only-show-errors --delete --cache-control \"max-age=3600\" \\\n\ttarget/doc/ \"s3://${S3_BUCKET_NAME}\"\n\necho '--- :cloudfront: Invalidating CloudFront'\naws cloudfront create-invalidation \\\n\t--distribution-id \"${CLOUDFRONT_DISTRIBUTION_ID}\" \\\n\t--paths '/*'\n\n"
  },
  {
    "path": ".buildkite/update-multiarch-manifest.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\n# Needed for `docker manifest`\nexport DOCKER_CLI_EXPERIMENTAL=enabled\n\nmanifest=ghcr.io/etaoins/arret-repl\n\ndocker manifest create -a \"${manifest}\" \"${manifest}-arm64\" \"${manifest}-amd64\"\ndocker manifest push \"${manifest}\""
  },
  {
    "path": ".buildkite/vscode-extension-tests.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\nexport DISPLAY=':99.0'\n/usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &\n\ncd editors/code\nyarn test\nyarn lint\nyarn vscode:package\n"
  },
  {
    "path": ".dockerignore",
    "content": "/target/\n/.git\n\n# These are build files that aren't needed inside the Docker container. Ignore them so we don't\n# trigger a recompile when experimenting with Docker\n/.dockerignore\n/docker-compose.yml\n/Dockerfile\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "*\t@etaoins\n"
  },
  {
    "path": ".github/renovate.json",
    "content": "{\n  \"extends\": [\"config:base\", \"docker:disable\"],\n  \"cargo\": {\n    \"enabled\": true\n  },\n  \"timezone\": \"Australia/Melbourne\",\n  \"schedule\": [\"before 5am every 2 weeks on Sunday\"],\n  \"prCreation\": \"not-pending\",\n  \"packageRules\": [\n    {\n      \"managers\": [\"npm\"],\n      \"depTypeList\": [\"devDependencies\"],\n      \"packagePatterns\": [\"^@typescript-eslint/\", \"^eslint-\"],\n      \"packageNames\": [\"eslint\"],\n      \"groupName\": \"eslint deps\"\n    },\n    {\n      \"managers\": [\"npm\"],\n      \"depTypeList\": [\"devDependencies\"],\n      \"packageNames\": [\"vsce\", \"vscode-test\", \"@types/vscode\"],\n      \"groupName\": \"VS Code deps\"\n    },\n    {\n      \"managers\": [\"npm\"],\n      \"depTypeList\": [\"devDependencies\"],\n      \"packagePatterns\": [\"^@types/\"],\n      \"groupName\": \"npm Definitely Typed deps\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "/target/\n/rls/\n**/*.rs.bk\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"singleQuote\": true,\n  \"trailingComma\": \"all\"\n}\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nmembers = [\n\t\"syntax\",\n\t\"compiler\",\n\t\"driver\",\n\t\"lsp-server\",\n\t\"runtime\",\n\t\"runtime-syntax\",\n\t\"rfi-derive\",\n\t\"stdlib/rust\"\n]"
  },
  {
    "path": "Dockerfile",
    "content": "FROM ubuntu:20.04 AS build-env\n\nRUN \\\n  apt-get update && \\\n  apt-get -y install --no-install-recommends ca-certificates curl gcc zlib1g-dev libstdc++-9-dev llvm-10 llvm-10-dev && \\\n  apt-get clean\nENV LLVM_SYS_10_PREFIX /usr/lib/llvm-10\n\nRUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.53.0 --profile=minimal\nENV PATH \"/root/.cargo/bin:${PATH}\"\n\n# These are the minimum required files for `cargo fetch`\n# This allows the `cargo fetch` to be cached between other source code changes\nADD Cargo.toml Cargo.lock /opt/arret/\nADD syntax/Cargo.toml /opt/arret/syntax/\nADD runtime/Cargo.toml /opt/arret/runtime/\nADD runtime-syntax/Cargo.toml /opt/arret/runtime-syntax/\nADD rfi-derive/Cargo.toml /opt/arret/rfi-derive/\nADD stdlib/rust/Cargo.toml /opt/arret/stdlib/rust/\nADD compiler/Cargo.toml /opt/arret/compiler/\nADD driver/Cargo.toml /opt/arret/driver/\nADD lsp-server/Cargo.toml /opt/arret/lsp-server/\n\nWORKDIR /opt/arret\n\nRUN cargo fetch\nADD . /opt/arret\n\n###\n\nFROM build-env as full-compiler\nRUN cargo build --release\n\n###\n\nFROM ubuntu:20.04 AS repl\n\nARG vcs_ref\n\nCOPY --from=full-compiler /opt/arret/.arret-root /opt/arret/.arret-root\nCOPY --from=full-compiler /opt/arret/stdlib/arret /opt/arret/stdlib/arret\nCOPY --from=full-compiler /opt/arret/target/release/arret /opt/arret/target/release/arret\nCOPY --from=full-compiler /opt/arret/target/release/*.so /opt/arret/target/release/\n\nRUN groupadd arret && useradd -r -g arret arret\nUSER arret:arret\n\nWORKDIR /opt/arret\nENTRYPOINT [\"/opt/arret/target/release/arret\"]\nCMD [\"repl\"]\n\n# Label the commit that was used to build this\nLABEL \\\n  org.label-schema.vcs-ref=$vcs_ref \\\n  org.label-schema.vcs-url=\"https://github.com/etaoins/arret\"\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Arret\n\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Build status](https://badge.buildkite.com/bcda02e06b6795e669edae4264bdecbb11ff98b4f5afb1fa4b.svg?branch=master)](https://buildkite.com/arret/arret)\n\n## Overview\n\nArret is pure functional, strongly typed language with Lisp-like syntax.\nIt aims to combine the expressiveness of Lisp with guarantees provided by functional programming.\nThe [language design documentation](./docs/language-design.md) has a high-level summary of the language's design choices.\n\nThe Arret compiler and parts of its standard library are written in Rust.\nThe mechanism for calling Rust code from Arret is referred to as the Rust Function Interface or RFI.\nDocumentation for the [`arret_runtime` crate](https://rustdoc.arret-lang.org/arret_runtime/index.html) describes the core concepts of the RFI.\n\n## Installation\n\n### Docker REPL Image\n\nThere is a public Docker image at `ghcr.io/etaoins/arret-repl` that runs the Arret REPL.\nWhenever `cargo run repl` appears in the documentation this command can be used instead:\n\n```shell\n> docker run -ti ghcr.io/etaoins/arret-repl\n```\n\nIt can also evaluate single file programs:\n\n```shell\n> cat hello-world.arret\n(import [stdlib base])\n(defn main! ()\n  (println! \"Hello, world!\"))\n\n> docker run -i ghcr.io/etaoins/arret-repl eval - < hello-world.arret\nHello, world!\n```\n\n### Build Requirements\n\n1. A Unix-like host running on ARM64, x86-64 or x86-32.\n   These are the platforms supporting lazy compilation with LLVM's ORC JIT.\n1. [LLVM](http://releases.llvm.org) 10 or 11\n1. [Rust](https://www.rust-lang.org)\n\n### Building with rustup and Cargo\n\n```shell\n> curl https://sh.rustup.rs -sSf | sh\n> cd ~/path/to/repo/root\n> cargo run repl\n```\n\n## Usage\n\n### REPL\n\nThe REPL provides an interactive environment for exploring Arret.\nIt's supported as a first class environment in Arret; the REPL is just as powerful as the compiler.\n\n```text\n> cargo run repl\narret> (length '(1 2 3 4 5))\n=> 5\narret> (defn identity #{T} ([x T]) -> T x)\ndefined\narret> /type identity\n=> (All #{T} T -> T)\narret> (identity \"Hello, world!\")\n=> \"Hello, world!\"\narret> /type (identity [one two three])\n=> (Vector 'one 'two 'three)\narret> /quit\n```\n\n### Compiler\n\nCompiled programs have a `(main!)` function as their entry point:\n\n```clojure\n(import [stdlib base])\n\n(defn main! ()\n  (println! \"Hello, world!\"))\n```\n\nThese can be compiled to a static binary by running Arret with the path name:\n\n```sh\n> cargo run compile hello-world.arret\n> ./hello-world\n\"Hello, world!\"\n```\n\n### Editors\n\nA basic [Visual Studio Code](https://code.visualstudio.com) extension is bundled in [editors/code](./editors/code).\nThis uses the [Language Server](https://microsoft.github.io/language-server-protocol/) from the [lsp-server crate](./lsp-server).\n\n```sh\n# Install `arret-lsp-server`\ncargo install --path lsp-server\n\n# Install the Visual Studio code extension\ncd editors/code\nyarn\nyarn vscode:install\n```\n\n## Examples\n\nThe Arret language is still rapidly evolving.\nThis makes it impractical to provide accurate documentation of the language and standard library.\nHowever, the test programs in [run-pass](compiler/tests/run-pass) give examples of working Arret code.\n"
  },
  {
    "path": "compiler/Cargo.toml",
    "content": "[package]\nname = \"arret-compiler\"\nversion = \"0.1.0\"\nedition = \"2018\"\nauthors = [\"Ryan Cumming <etaoins@gmail.com>\"]\n\n[lib]\npath = \"lib.rs\"\ncrate-type = [\"lib\"]\n\n[dependencies]\nllvm-sys = \"100\"\nlibc = \"0.2\"\nlibloading = \"0.7\"\narret-syntax = { path = \"../syntax\" }\narret-runtime = { path = \"../runtime\" }\narret-runtime-syntax = { path = \"../runtime-syntax\" }\ncodespan-reporting = \"0.11\"\ncrossbeam-channel = \"0.5\"\ntermcolor = \"1\"\n\n[dev-dependencies]\ntempfile = \"3\"\nnum_cpus = \"1.13\""
  },
  {
    "path": "compiler/arret_root.rs",
    "content": "use std::{env, path};\n\nconst ARRET_ROOT_ENV_VAR: &str = \"ARRET_ROOT\";\n\nfn is_arret_root(path: &path::Path) -> bool {\n    path.join(\"./.arret-root\").is_file()\n}\n\npub struct InvalidOptionError {\n    invalid_path: path::PathBuf,\n}\n\nimpl InvalidOptionError {\n    /// Path to the invalid Arret root\n    pub fn invalid_path(&self) -> &path::Path {\n        &self.invalid_path\n    }\n}\n\npub struct InvalidEnvVarError {\n    invalid_path: path::PathBuf,\n}\n\nimpl InvalidEnvVarError {\n    /// Environment variable that contained the invalid root\n    pub fn env_var_name(&self) -> &'static str {\n        ARRET_ROOT_ENV_VAR\n    }\n\n    /// Path to the invalid Arret root\n    pub fn invalid_path(&self) -> &path::Path {\n        &self.invalid_path\n    }\n}\n\npub enum FindArretRootError {\n    /// Explicitly specified option was not an Arret root\n    InvalidOption(InvalidOptionError),\n    /// Environment variable with the given name is not an Arret root\n    InvalidEnvVar(InvalidEnvVarError),\n    /// Heuristic search failed\n    NotFound,\n}\n\n/// Attempts to find the path to Arret root directory\n///\n/// The search order is:\n/// 1. The `arret_root_option` parameter\n/// 2. The `ARRET_ROOT` environment variable\n/// 3. The path this binary was originally built in and all of its parents\n/// 4. The current directory and all of its parents\npub fn find_arret_root(\n    arret_root_option: Option<&str>,\n) -> Result<path::PathBuf, FindArretRootError> {\n    if let Some(arg_root) = arret_root_option {\n        let arg_path = path::PathBuf::from(arg_root);\n        if !is_arret_root(&arg_path) {\n            return Err(FindArretRootError::InvalidOption(InvalidOptionError {\n                invalid_path: arg_path,\n            }));\n        }\n\n        return Ok(arg_path);\n    }\n\n    if let Some(env_root) = env::var_os(ARRET_ROOT_ENV_VAR) {\n        let env_path = path::PathBuf::from(env_root);\n        if !is_arret_root(&env_path) {\n            return Err(FindArretRootError::InvalidEnvVar(InvalidEnvVarError {\n                invalid_path: env_path,\n            }));\n        }\n\n        return Ok(env_path);\n    }\n\n    if let Some(manifest_dir) = option_env!(\"CARGO_MANIFEST_DIR\") {\n        for candidate in path::Path::new(manifest_dir).ancestors() {\n            if is_arret_root(candidate) {\n                return Ok(candidate.to_owned());\n            }\n        }\n    }\n\n    let current_dir = env::current_dir().expect(\"Cannot determine current directory\");\n    for candidate in path::Path::new(&current_dir).ancestors() {\n        if is_arret_root(candidate) {\n            return Ok(candidate.to_owned());\n        }\n    }\n\n    Err(FindArretRootError::NotFound)\n}\n"
  },
  {
    "path": "compiler/codegen/alloc/core.rs",
    "content": "use std::{mem, ptr};\n\nuse llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::{LLVMAttributeFunctionIndex, LLVMAttributeReturnIndex, LLVMIntPredicate};\n\nuse arret_runtime::boxed;\n\nuse crate::codegen::alloc::{ActiveAlloc, AllocAtom, BoxSource};\nuse crate::codegen::mod_gen::ModCtx;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::libcstr;\n\nfn init_alloced_box_header(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    alloced_box: LLVMValueRef,\n    header: boxed::Header,\n) {\n    unsafe {\n        let header_ptr = LLVMBuildStructGEP(builder, alloced_box, 0, libcstr!(\"header_ptr\"));\n        LLVMBuildStore(builder, tcx.llvm_box_header(header), header_ptr);\n    }\n}\n\nfn gen_stack_alloced_box<T: boxed::ConstTagged>(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    llvm_type: LLVMTypeRef,\n    value_name: &[u8],\n) -> LLVMValueRef {\n    unsafe {\n        let type_tag = T::TYPE_TAG;\n\n        let alloced_box = LLVMBuildAlloca(builder, llvm_type, value_name.as_ptr() as *const _);\n        LLVMSetAlignment(alloced_box, mem::align_of::<T>() as u32);\n\n        init_alloced_box_header(\n            tcx,\n            builder,\n            alloced_box,\n            boxed::Header::new(type_tag, boxed::AllocType::Stack),\n        );\n\n        alloced_box\n    }\n}\n\nfn gen_heap_alloced_box<T: boxed::ConstTagged>(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    active_alloc: &mut ActiveAlloc<'_>,\n    box_size: boxed::BoxSize,\n    llvm_type: LLVMTypeRef,\n    value_name: &[u8],\n) -> LLVMValueRef {\n    unsafe {\n        assert!(\n            !active_alloc.is_empty(),\n            \"attempt to create heap box with empty active heap allocation\"\n        );\n\n        let cell_count = box_size.cell_count();\n\n        let slot_index = active_alloc.used_cells;\n        let llvm_slot = if slot_index == 0 {\n            active_alloc.box_slots\n        } else {\n            let gep_indices = &mut [LLVMConstInt(\n                LLVMInt32TypeInContext(tcx.llx),\n                slot_index as u64,\n                0,\n            )];\n\n            LLVMBuildInBoundsGEP(\n                builder,\n                active_alloc.box_slots,\n                gep_indices.as_mut_ptr(),\n                gep_indices.len() as u32,\n                libcstr!(\"slot\"),\n            )\n        };\n\n        active_alloc.used_cells += cell_count;\n        assert!(active_alloc.used_cells <= active_alloc.total_cells);\n\n        let type_tag = T::TYPE_TAG;\n        let alloced_box = LLVMBuildBitCast(\n            builder,\n            llvm_slot,\n            LLVMPointerType(llvm_type, 0),\n            value_name.as_ptr() as *const _,\n        );\n\n        init_alloced_box_header(\n            tcx,\n            builder,\n            alloced_box,\n            boxed::Header::new(type_tag, box_size.to_heap_alloc_type()),\n        );\n\n        alloced_box\n    }\n}\n\npub fn gen_alloced_box_with_llvm_type<T: boxed::ConstTagged>(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    active_alloc: &mut ActiveAlloc<'_>,\n    box_source: BoxSource,\n    llvm_type: LLVMTypeRef,\n    value_name: &[u8],\n) -> LLVMValueRef {\n    match box_source {\n        BoxSource::Stack => gen_stack_alloced_box::<T>(tcx, builder, llvm_type, value_name),\n        BoxSource::Heap(box_size) => {\n            gen_heap_alloced_box::<T>(tcx, builder, active_alloc, box_size, llvm_type, value_name)\n        }\n    }\n}\n\npub fn gen_alloced_box<T: boxed::ConstTagged>(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    active_alloc: &mut ActiveAlloc<'_>,\n    box_source: BoxSource,\n    value_name: &[u8],\n) -> LLVMValueRef {\n    let llvm_type = tcx.boxed_abi_to_llvm_struct_type(&T::TYPE_TAG.into());\n\n    gen_alloced_box_with_llvm_type::<T>(\n        tcx,\n        builder,\n        active_alloc,\n        box_source,\n        llvm_type,\n        value_name,\n    )\n}\n\n/// Allocates cells by invoking a function at runtime\n///\n/// This is the slow path; it is only used when our current heap segment is full.\nfn gen_runtime_heap_alloc(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    builder: LLVMBuilderRef,\n    llvm_task: LLVMValueRef,\n    required_cells: usize,\n) -> LLVMValueRef {\n    use arret_runtime::abitype;\n\n    unsafe {\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n        let llvm_param_types = &mut [tcx.task_llvm_ptr_type(), llvm_i32];\n\n        let alloc_cells_llvm_type = LLVMFunctionType(\n            tcx.boxed_abi_to_llvm_ptr_type(&abitype::BoxedAbiType::Any),\n            llvm_param_types.as_mut_ptr(),\n            llvm_param_types.len() as u32,\n            0,\n        );\n\n        let alloc_cells_fun = mcx.get_function_or_insert(\n            alloc_cells_llvm_type,\n            b\"arret_runtime_alloc_cells\\0\",\n            |alloc_cells_fun| {\n                LLVMAddAttributeAtIndex(\n                    alloc_cells_fun,\n                    LLVMAttributeFunctionIndex,\n                    tcx.llvm_enum_attr_for_name(\"cold\", 0),\n                );\n                LLVMAddAttributeAtIndex(\n                    alloc_cells_fun,\n                    LLVMAttributeReturnIndex,\n                    tcx.llvm_boxed_align_attr(),\n                );\n                LLVMAddAttributeAtIndex(\n                    alloc_cells_fun,\n                    LLVMAttributeReturnIndex,\n                    tcx.llvm_noalias_attr(),\n                );\n            },\n        );\n\n        let alloc_cells_args = &mut [llvm_task, LLVMConstInt(llvm_i32, required_cells as u64, 0)];\n\n        let runtime_box_slots = LLVMBuildCall(\n            builder,\n            alloc_cells_fun,\n            alloc_cells_args.as_mut_ptr(),\n            alloc_cells_args.len() as u32,\n            libcstr!(\"runtime_box_slots\"),\n        );\n\n        // We can dereference the entire allocation immediately\n        let dereferenceable_attr = tcx.llvm_enum_attr_for_name(\n            \"dereferenceable\",\n            (mem::size_of::<boxed::Any>() * required_cells) as u64,\n        );\n        LLVMAddCallSiteAttribute(\n            runtime_box_slots,\n            LLVMAttributeReturnIndex,\n            dereferenceable_attr,\n        );\n\n        runtime_box_slots\n    }\n}\n\n/// Generates an `ActiveAlloc` containing the required allocations for the passed `AllocAtom`\n///\n/// This will first attempt a bump allocation on the task's current segment. If that fails it will\n/// fallback to the runtime.\npub fn atom_into_active_alloc<'op>(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    builder: LLVMBuilderRef,\n    llvm_task: LLVMValueRef,\n    atom: AllocAtom<'op>,\n) -> ActiveAlloc<'op> {\n    use arret_runtime::abitype;\n\n    let required_cells = atom\n        .box_sources\n        .iter()\n        .map(|box_source| match box_source {\n            BoxSource::Stack => 0,\n            BoxSource::Heap(box_size) => box_size.cell_count(),\n        })\n        .sum();\n\n    if required_cells == 0 {\n        return ActiveAlloc {\n            box_slots: ptr::null_mut(),\n            total_cells: 0,\n            used_cells: 0,\n\n            box_source_iter: atom.box_sources.into_iter(),\n            cond_plan_iter: atom.cond_plans.into_iter(),\n        };\n    }\n\n    unsafe {\n        let function = LLVMGetBasicBlockParent(LLVMGetInsertBlock(builder));\n\n        let mut bump_alloc_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, function, libcstr!(\"bump_alloc\"));\n\n        let mut runtime_alloc_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, function, libcstr!(\"runtime_alloc\"));\n\n        let cont_block = LLVMAppendBasicBlockInContext(tcx.llx, function, libcstr!(\"alloc_cont\"));\n\n        let seg_next_ptr = LLVMBuildStructGEP(builder, llvm_task, 0, libcstr!(\"seg_next_ptr\"));\n        let mut seg_old_next = LLVMBuildLoad(builder, seg_next_ptr, libcstr!(\"seg_old_next\"));\n\n        let gep_indices = &mut [LLVMConstInt(\n            LLVMInt32TypeInContext(tcx.llx),\n            required_cells as u64,\n            0,\n        )];\n        let seg_new_next = LLVMBuildInBoundsGEP(\n            builder,\n            seg_old_next,\n            gep_indices.as_mut_ptr(),\n            gep_indices.len() as u32,\n            libcstr!(\"seg_new_next\"),\n        );\n\n        let seg_end_ptr = LLVMBuildStructGEP(builder, llvm_task, 1, libcstr!(\"seg_end_ptr\"));\n        let seg_end = LLVMBuildLoad(builder, seg_end_ptr, libcstr!(\"seg_end\"));\n\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n        let seg_new_next_int = LLVMBuildPtrToInt(\n            builder,\n            seg_new_next,\n            llvm_i64,\n            libcstr!(\"seg_new_next_int\"),\n        );\n        let seg_end_int = LLVMBuildPtrToInt(builder, seg_end, llvm_i64, libcstr!(\"seg_end_int\"));\n\n        let seg_has_space = LLVMBuildICmp(\n            builder,\n            LLVMIntPredicate::LLVMIntULE,\n            seg_new_next_int,\n            seg_end_int,\n            libcstr!(\"seg_has_space\"),\n        );\n\n        LLVMBuildCondBr(\n            builder,\n            seg_has_space,\n            bump_alloc_block,\n            runtime_alloc_block,\n        );\n\n        // Bump alloc succeeded; update the segment\n        LLVMPositionBuilderAtEnd(builder, bump_alloc_block);\n        LLVMBuildStore(builder, seg_new_next, seg_next_ptr);\n        LLVMBuildBr(builder, cont_block);\n\n        // Bump alloc failed; call the runtime\n        LLVMPositionBuilderAtEnd(builder, runtime_alloc_block);\n        let mut runtime_box_slots =\n            gen_runtime_heap_alloc(tcx, mcx, builder, llvm_task, required_cells);\n        LLVMBuildBr(builder, cont_block);\n\n        LLVMPositionBuilderAtEnd(builder, cont_block);\n        let box_slots = LLVMBuildPhi(\n            builder,\n            tcx.boxed_abi_to_llvm_ptr_type(&abitype::BoxedAbiType::Any),\n            libcstr!(\"box_slots\"),\n        );\n\n        LLVMAddIncoming(\n            box_slots,\n            &mut seg_old_next as *mut _,\n            &mut bump_alloc_block as *mut _,\n            1,\n        );\n        LLVMAddIncoming(\n            box_slots,\n            &mut runtime_box_slots as *mut _,\n            &mut runtime_alloc_block as *mut _,\n            1,\n        );\n\n        ActiveAlloc {\n            box_slots,\n            total_cells: required_cells,\n            used_cells: 0,\n\n            box_source_iter: atom.box_sources.into_iter(),\n            cond_plan_iter: atom.cond_plans.into_iter(),\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/alloc/mod.rs",
    "content": "use std::vec;\n\nuse llvm_sys::prelude::*;\n\nuse arret_runtime::boxed;\n\nuse crate::mir::ops;\n\npub mod core;\npub mod plan;\npub mod types;\n\n/// Indicates where memory for a box allocation should come from\n#[derive(PartialEq, Debug, Clone, Copy)]\npub enum BoxSource {\n    Stack,\n    Heap(boxed::BoxSize),\n}\n\n/// Contains the sub-plans for a conditional branch\n#[derive(PartialEq, Debug)]\npub struct CondPlan<'op> {\n    pub true_subplan: Vec<AllocAtom<'op>>,\n    pub false_subplan: Vec<AllocAtom<'op>>,\n}\n\n/// Represents a sequence of MIR ops that begin and end with the heap in a consistent state\n#[derive(PartialEq, Debug, Default)]\npub struct AllocAtom<'op> {\n    box_sources: Vec<BoxSource>,\n    cond_plans: Vec<CondPlan<'op>>,\n\n    ops_base: &'op [ops::Op],\n    ops_count: usize,\n}\n\nimpl<'op> AllocAtom<'op> {\n    /// Creates a new `AllocAtom` with its ops starting at the specified slice\n    fn new(ops_base: &'op [ops::Op]) -> Self {\n        Self {\n            ops_base,\n            ..Default::default()\n        }\n    }\n\n    pub fn ops(&self) -> &'op [ops::Op] {\n        &self.ops_base[0..self.ops_count]\n    }\n\n    /// Increments the used size of our ops by one\n    fn push_op(&mut self) {\n        self.ops_count += 1\n    }\n\n    fn is_empty(&self) -> bool {\n        self.ops_count == 0\n    }\n}\n\npub struct ActiveAlloc<'op> {\n    box_slots: LLVMValueRef,\n    total_cells: usize,\n    used_cells: usize,\n\n    box_source_iter: vec::IntoIter<BoxSource>,\n    cond_plan_iter: vec::IntoIter<CondPlan<'op>>,\n}\n\nimpl<'op> ActiveAlloc<'op> {\n    pub fn is_empty(&self) -> bool {\n        self.total_cells == self.used_cells\n    }\n\n    pub fn next_box_source(&mut self) -> BoxSource {\n        self.box_source_iter.next().unwrap()\n    }\n\n    pub fn next_cond_plan(&mut self) -> CondPlan<'op> {\n        self.cond_plan_iter.next().unwrap()\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/alloc/plan.rs",
    "content": "use arret_runtime::boxed;\n\nuse crate::codegen::alloc::{AllocAtom, BoxSource, CondPlan};\nuse crate::codegen::analysis::escape::{CaptureKind, Captures};\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::mir::ops;\n\nstruct AllocInfo {\n    output_reg: ops::RegId,\n    box_size: boxed::BoxSize,\n}\n\n/// Determines if an op requires the heap to be in a consistent state before it's executed\n///\n/// Our `AllocAtom`s cannot span these operations\nfn op_needs_heap_checkpoint(tcx: &mut TargetCtx, op: &ops::Op) -> bool {\n    use crate::mir::ops::OpKind;\n\n    match op.kind() {\n        OpKind::Ret(_)\n        | OpKind::RetVoid\n        | OpKind::Unreachable\n        | OpKind::Call(_, _)\n        | OpKind::Panic(_)\n        | OpKind::Int64CheckedAdd(_, _)\n        | OpKind::Int64CheckedSub(_, _)\n        | OpKind::Int64CheckedMul(_, _)\n        | OpKind::Int64CheckedDiv(_, _)\n        | OpKind::Int64CheckedRem(_, _) => true,\n        OpKind::Cond(cond_op) => cond_op\n            .true_ops\n            .iter()\n            .chain(cond_op.false_ops.iter())\n            // We additionally need to make sure we don't allocate in our branches. Otherwise we\n            // might need to plan an allocation of a dynamic size to cover each branch. Instead\n            // just start a new atom for each branch.\n            .any(|op| op_needs_heap_checkpoint(tcx, op) || op_alloc_info(tcx, op).is_some()),\n        _ => false,\n    }\n}\n\n/// Returns the output reg for an allocating op, or `None` otherwise\nfn op_alloc_info(tcx: &mut TargetCtx, op: &ops::Op) -> Option<AllocInfo> {\n    use crate::mir::ops::OpKind;\n\n    match op.kind() {\n        OpKind::AllocBoxedInt(output_reg, _) => Some(AllocInfo {\n            output_reg: *output_reg,\n            box_size: boxed::Int::size(),\n        }),\n        OpKind::AllocBoxedFloat(output_reg, _) => Some(AllocInfo {\n            output_reg: *output_reg,\n            box_size: boxed::Float::size(),\n        }),\n        OpKind::AllocBoxedChar(output_reg, _) => Some(AllocInfo {\n            output_reg: *output_reg,\n            box_size: boxed::Char::size(),\n        }),\n        OpKind::AllocBoxedSym(output_reg, _) => Some(AllocInfo {\n            output_reg: *output_reg,\n            box_size: boxed::Sym::size(),\n        }),\n        OpKind::AllocBoxedPair(output_reg, _) => Some(AllocInfo {\n            output_reg: *output_reg,\n            box_size: boxed::Pair::<boxed::Any>::size(),\n        }),\n        OpKind::AllocBoxedFunThunk(output_reg, _) => Some(AllocInfo {\n            output_reg: *output_reg,\n            box_size: boxed::FunThunk::size(),\n        }),\n        OpKind::AllocBoxedRecord(output_reg, box_record_op) => {\n            let record_storage = tcx\n                .target_record_struct(&box_record_op.record_struct)\n                .record_storage;\n\n            Some(AllocInfo {\n                output_reg: *output_reg,\n                box_size: record_storage.box_size(),\n            })\n        }\n        _ => None,\n    }\n}\n\npub fn plan_allocs<'op>(\n    tcx: &mut TargetCtx,\n    captures: &Captures,\n    ops: &'op [ops::Op],\n) -> Vec<AllocAtom<'op>> {\n    use std::mem;\n\n    let mut atoms = vec![];\n    let mut current_atom = AllocAtom::new(&ops[0..]);\n\n    for (i, op) in ops.iter().enumerate() {\n        let checkpointing_op = op_needs_heap_checkpoint(tcx, op);\n\n        if checkpointing_op && !current_atom.is_empty() {\n            atoms.push(mem::replace(&mut current_atom, AllocAtom::new(&ops[i..])));\n        }\n\n        if let ops::OpKind::Cond(ops::CondOp {\n            true_ops,\n            false_ops,\n            ..\n        }) = op.kind()\n        {\n            current_atom.cond_plans.push(CondPlan {\n                true_subplan: plan_allocs(tcx, captures, true_ops),\n                false_subplan: plan_allocs(tcx, captures, false_ops),\n            });\n        } else if let Some(AllocInfo {\n            output_reg,\n            box_size,\n        }) = op_alloc_info(tcx, op)\n        {\n            if captures.get(output_reg) == CaptureKind::Never {\n                current_atom.box_sources.push(BoxSource::Stack);\n            } else {\n                current_atom.box_sources.push(BoxSource::Heap(box_size));\n            }\n        }\n\n        current_atom.push_op();\n\n        if checkpointing_op {\n            atoms.push(mem::replace(\n                &mut current_atom,\n                AllocAtom::new(&ops[i + 1..]),\n            ));\n        }\n    }\n\n    if !current_atom.is_empty() {\n        atoms.push(current_atom);\n    }\n\n    atoms\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    /// Plans allocations assuming the native data layout\n    fn plan_native_allocs(ops: &[ops::Op]) -> Vec<AllocAtom<'_>> {\n        use llvm_sys::target_machine::*;\n\n        use crate::codegen::target_machine::create_target_machine;\n        use crate::codegen::test::initialise_test_llvm;\n\n        initialise_test_llvm();\n\n        let target_machine = create_target_machine(\n            None,\n            LLVMRelocMode::LLVMRelocDynamicNoPic,\n            LLVMCodeModel::LLVMCodeModelDefault,\n        );\n\n        let mut tcx = TargetCtx::new(target_machine, false);\n        let atoms = plan_allocs(&mut tcx, &Captures::new(), ops);\n\n        unsafe {\n            LLVMDisposeTargetMachine(target_machine);\n        }\n\n        atoms\n    }\n\n    #[test]\n    fn empty_ops() {\n        let actual_atoms = plan_native_allocs(&[]);\n        assert_eq!(0, actual_atoms.len());\n    }\n\n    #[test]\n    fn condless_allocs() {\n        let reg1 = ops::RegId::alloc();\n        let reg2 = ops::RegId::alloc();\n        let reg3 = ops::RegId::alloc();\n        let reg4 = ops::RegId::alloc();\n\n        let input_ops = [\n            ops::OpKind::AllocBoxedInt(reg1, reg1).into(),\n            ops::OpKind::ConstBoxedTrue(reg2, ()).into(),\n            ops::OpKind::RetVoid.into(),\n            ops::OpKind::AllocBoxedInt(reg3, reg3).into(),\n            ops::OpKind::AllocBoxedInt(reg4, reg4).into(),\n        ];\n\n        let expected_atoms = vec![\n            AllocAtom {\n                box_sources: vec![BoxSource::Stack],\n                cond_plans: vec![],\n                ops_base: &input_ops[0..],\n                ops_count: 2,\n            },\n            AllocAtom {\n                box_sources: vec![],\n                cond_plans: vec![],\n                ops_base: &input_ops[2..],\n                ops_count: 1,\n            },\n            AllocAtom {\n                box_sources: vec![BoxSource::Stack, BoxSource::Stack],\n                cond_plans: vec![],\n                ops_base: &input_ops[3..],\n                ops_count: 2,\n            },\n        ];\n\n        let actual_atoms = plan_native_allocs(&input_ops);\n\n        assert_eq!(expected_atoms, actual_atoms);\n    }\n\n    #[test]\n    fn non_allocating_cond() {\n        let output_reg = ops::RegId::alloc();\n        let true_result_reg = ops::RegId::alloc();\n        let false_result_reg = ops::RegId::alloc();\n\n        let test_reg = ops::RegId::alloc();\n\n        let true_ops = Box::new([ops::OpKind::ConstBoxedNil(true_result_reg, ()).into()]);\n        let false_ops = Box::new([ops::OpKind::ConstBoxedNil(false_result_reg, ()).into()]);\n\n        let input_ops = [\n            ops::OpKind::AllocBoxedInt(test_reg, test_reg).into(),\n            ops::OpKind::Cond(ops::CondOp {\n                reg_phi: Some(ops::RegPhi {\n                    output_reg,\n                    true_result_reg,\n                    false_result_reg,\n                }),\n                test_reg,\n                true_ops,\n                false_ops,\n            })\n            .into(),\n        ];\n\n        let actual_atoms = plan_native_allocs(&input_ops);\n        // We should place the `AllocBoxedInt` and `Cond` in the same atom\n        assert_eq!(1, actual_atoms.len());\n    }\n\n    #[test]\n    fn allocating_cond() {\n        let output_reg = ops::RegId::alloc();\n        let test_reg = ops::RegId::alloc();\n        let true_result_reg = ops::RegId::alloc();\n        let false_result_reg = ops::RegId::alloc();\n\n        let true_ops = Box::new([ops::OpKind::ConstBoxedNil(true_result_reg, ()).into()]);\n        let false_ops =\n            Box::new([ops::OpKind::AllocBoxedInt(false_result_reg, false_result_reg).into()]);\n\n        let input_ops = [\n            ops::OpKind::AllocBoxedInt(test_reg, test_reg).into(),\n            ops::OpKind::Cond(ops::CondOp {\n                reg_phi: Some(ops::RegPhi {\n                    output_reg,\n                    true_result_reg,\n                    false_result_reg,\n                }),\n                test_reg,\n                true_ops,\n                false_ops,\n            })\n            .into(),\n        ];\n\n        let actual_atoms = plan_native_allocs(&input_ops);\n        // We should place the `AllocBoxedInt` and `Cond` in different atoms\n        assert_eq!(2, actual_atoms.len());\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/alloc/types.rs",
    "content": "use llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::LLVMAttributeReturnIndex;\n\nuse arret_runtime::boxed;\n\nuse crate::codegen::alloc::core::{gen_alloced_box, gen_alloced_box_with_llvm_type};\nuse crate::codegen::alloc::{ActiveAlloc, BoxSource};\nuse crate::codegen::mod_gen::ModCtx;\nuse crate::codegen::record_struct;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::libcstr;\nuse crate::mir::ops::RecordStructId;\n\npub struct PairInput {\n    pub llvm_head: LLVMValueRef,\n    pub llvm_rest: LLVMValueRef,\n    pub llvm_list_len: LLVMValueRef,\n}\n\npub struct FunThunkInput {\n    pub llvm_captures: LLVMValueRef,\n    pub llvm_entry_point: LLVMValueRef,\n}\n\npub struct RecordInput<'rs> {\n    pub record_struct: &'rs RecordStructId,\n    pub llvm_fields: Box<[LLVMValueRef]>,\n}\n\npub fn gen_alloc_int(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    active_alloc: &mut ActiveAlloc<'_>,\n    box_source: BoxSource,\n    llvm_int_value: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let alloced_int =\n            gen_alloced_box::<boxed::Int>(tcx, builder, active_alloc, box_source, b\"alloced_int\\0\");\n\n        let value_ptr = LLVMBuildStructGEP(builder, alloced_int, 1, libcstr!(\"value_ptr\"));\n        LLVMBuildStore(builder, llvm_int_value, value_ptr);\n\n        alloced_int\n    }\n}\n\npub fn gen_alloc_char(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    active_alloc: &mut ActiveAlloc<'_>,\n    box_source: BoxSource,\n    llvm_char_value: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let alloced_char = gen_alloced_box::<boxed::Char>(\n            tcx,\n            builder,\n            active_alloc,\n            box_source,\n            b\"alloced_char\\0\",\n        );\n\n        let value_ptr = LLVMBuildStructGEP(builder, alloced_char, 1, libcstr!(\"value_ptr\"));\n        LLVMBuildStore(builder, llvm_char_value, value_ptr);\n\n        alloced_char\n    }\n}\n\npub fn gen_alloc_sym(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    active_alloc: &mut ActiveAlloc<'_>,\n    box_source: BoxSource,\n    llvm_interned_sym: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let alloced_sym =\n            gen_alloced_box::<boxed::Sym>(tcx, builder, active_alloc, box_source, b\"alloced_sym\\0\");\n\n        let interned_sym_ptr =\n            LLVMBuildStructGEP(builder, alloced_sym, 1, libcstr!(\"interned_sym_ptr\"));\n        LLVMBuildStore(builder, llvm_interned_sym, interned_sym_ptr);\n\n        alloced_sym\n    }\n}\n\npub fn gen_alloc_float(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    active_alloc: &mut ActiveAlloc<'_>,\n    box_source: BoxSource,\n    llvm_float_value: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let alloced_float = gen_alloced_box::<boxed::Float>(\n            tcx,\n            builder,\n            active_alloc,\n            box_source,\n            b\"alloced_float\\0\",\n        );\n\n        let value_ptr = LLVMBuildStructGEP(builder, alloced_float, 1, libcstr!(\"value_ptr\"));\n        LLVMBuildStore(builder, llvm_float_value, value_ptr);\n\n        alloced_float\n    }\n}\n\npub fn gen_alloc_boxed_pair(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    active_alloc: &mut ActiveAlloc<'_>,\n    box_source: BoxSource,\n    input: &PairInput,\n) -> LLVMValueRef {\n    let PairInput {\n        llvm_head,\n        llvm_rest,\n        llvm_list_len,\n    } = input;\n\n    unsafe {\n        let alloced_pair = gen_alloced_box::<boxed::Pair>(\n            tcx,\n            builder,\n            active_alloc,\n            box_source,\n            b\"alloced_pair\\0\",\n        );\n\n        let list_len_ptr = LLVMBuildStructGEP(builder, alloced_pair, 1, libcstr!(\"list_len_ptr\"));\n        LLVMBuildStore(builder, *llvm_list_len, list_len_ptr);\n\n        let head_ptr = LLVMBuildStructGEP(builder, alloced_pair, 2, libcstr!(\"head_ptr\"));\n        LLVMBuildStore(builder, *llvm_head, head_ptr);\n\n        let rest_ptr = LLVMBuildStructGEP(builder, alloced_pair, 3, libcstr!(\"rest_ptr\"));\n        LLVMBuildStore(builder, *llvm_rest, rest_ptr);\n\n        alloced_pair\n    }\n}\n\npub fn gen_alloc_boxed_fun_thunk(\n    tcx: &mut TargetCtx,\n    builder: LLVMBuilderRef,\n    active_alloc: &mut ActiveAlloc<'_>,\n    box_source: BoxSource,\n    input: &FunThunkInput,\n) -> LLVMValueRef {\n    let FunThunkInput {\n        llvm_captures,\n        llvm_entry_point,\n    } = input;\n\n    unsafe {\n        let alloced_fun_thunk = gen_alloced_box::<boxed::FunThunk>(\n            tcx,\n            builder,\n            active_alloc,\n            box_source,\n            b\"alloced_fun_thunk\\0\",\n        );\n\n        let captures_ptr =\n            LLVMBuildStructGEP(builder, alloced_fun_thunk, 1, libcstr!(\"captures_ptr\"));\n        LLVMBuildStore(builder, *llvm_captures, captures_ptr);\n\n        let entry_point_ptr =\n            LLVMBuildStructGEP(builder, alloced_fun_thunk, 2, libcstr!(\"entry_point_ptr\"));\n        LLVMBuildStore(builder, *llvm_entry_point, entry_point_ptr);\n\n        alloced_fun_thunk\n    }\n}\n\npub fn gen_alloc_boxed_record(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    builder: LLVMBuilderRef,\n    active_alloc: &mut ActiveAlloc<'_>,\n    box_source: BoxSource,\n    input: &RecordInput<'_>,\n) -> LLVMValueRef {\n    let RecordInput {\n        record_struct,\n        llvm_fields,\n    } = input;\n\n    let record_class_id = mcx.record_class_id_for_struct(record_struct);\n\n    unsafe {\n        let llvm_i8 = LLVMInt8TypeInContext(tcx.llx);\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n\n        let record_struct::TargetRecordStruct {\n            data_layout,\n            record_storage,\n            llvm_data_type,\n            ..\n        } = *tcx.target_record_struct(record_struct);\n\n        let may_contain_gc_refs = record_struct\n            .field_abi_types\n            .iter()\n            .zip(llvm_fields.iter())\n            .any(|(field_abi_type, llvm_field)| {\n                field_abi_type.may_contain_gc_refs() && LLVMIsConstant(*llvm_field) == 0\n            });\n\n        let boxed_record_name = format!(\"alloced_{}_record\\0\", record_struct.source_name);\n\n        let llvm_box_type = tcx.record_struct_llvm_box_type(record_struct);\n        let alloced_boxed_record = gen_alloced_box_with_llvm_type::<boxed::Record>(\n            tcx,\n            builder,\n            active_alloc,\n            box_source,\n            llvm_box_type,\n            boxed_record_name.as_bytes(),\n        );\n\n        let may_contain_gc_refs_ptr = LLVMBuildStructGEP(\n            builder,\n            alloced_boxed_record,\n            record_struct::CONTAINS_GC_REFS_INDEX,\n            libcstr!(\"may_contain_gc_refs_ptr\"),\n        );\n        let llvm_may_contain_gc_refs = LLVMConstInt(llvm_i8, may_contain_gc_refs as u64, 1);\n        LLVMBuildStore(builder, llvm_may_contain_gc_refs, may_contain_gc_refs_ptr);\n\n        let record_class_id_ptr = LLVMBuildStructGEP(\n            builder,\n            alloced_boxed_record,\n            record_struct::RECORD_CLASS_ID_INDEX,\n            libcstr!(\"record_class_id_ptr\"),\n        );\n\n        let llvm_record_class_id = LLVMConstInt(\n            tcx.record_class_id_llvm_type(),\n            u64::from(record_class_id),\n            1,\n        );\n        LLVMBuildStore(builder, llvm_record_class_id, record_class_id_ptr);\n\n        // This is used by both inline and external records\n        let record_data_gep_indices = &mut [\n            LLVMConstInt(llvm_i32, 0, 0),\n            LLVMConstInt(llvm_i32, u64::from(record_struct::DATA_INDEX), 0),\n        ];\n\n        let (llvm_record_data_ptr, inline_byte_len) = match (record_storage, box_source) {\n            (boxed::RecordStorage::Inline(_), _) => {\n                let llvm_inline_record_data_ptr = LLVMBuildInBoundsGEP(\n                    builder,\n                    alloced_boxed_record,\n                    record_data_gep_indices.as_mut_ptr(),\n                    record_data_gep_indices.len() as u32,\n                    libcstr!(\"inline_record_data\"),\n                );\n\n                let inline_byte_len = match data_layout {\n                    Some(data_layout) => data_layout.size(),\n                    None => 0,\n                };\n\n                (llvm_inline_record_data_ptr, inline_byte_len)\n            }\n            (boxed::RecordStorage::External, BoxSource::Stack) => {\n                // Allocate the record data\n                let llvm_stack_record_data_ptr =\n                    LLVMBuildAlloca(builder, llvm_data_type, libcstr!(\"stack_record_data\"));\n\n                // Update our record data pointer\n                let llvm_record_data_ptr_ptr = LLVMBuildInBoundsGEP(\n                    builder,\n                    alloced_boxed_record,\n                    record_data_gep_indices.as_mut_ptr(),\n                    record_data_gep_indices.len() as u32,\n                    libcstr!(\"record_data_ptr_ptr\"),\n                );\n\n                LLVMBuildStore(\n                    builder,\n                    llvm_stack_record_data_ptr,\n                    llvm_record_data_ptr_ptr,\n                );\n\n                (\n                    llvm_stack_record_data_ptr,\n                    boxed::Record::EXTERNAL_INLINE_LEN as usize,\n                )\n            }\n            (boxed::RecordStorage::External, BoxSource::Heap(_)) => {\n                let data_layout = data_layout.unwrap();\n\n                let llvm_i8 = LLVMInt8TypeInContext(tcx.llx);\n                let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n                let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n                let llvm_param_types = &mut [llvm_i64, llvm_i32];\n\n                let alloc_record_data_llvm_type = LLVMFunctionType(\n                    LLVMPointerType(llvm_i8, 0),\n                    llvm_param_types.as_mut_ptr(),\n                    llvm_param_types.len() as u32,\n                    0,\n                );\n\n                let alloc_record_data_fun = mcx.get_function_or_insert(\n                    alloc_record_data_llvm_type,\n                    b\"arret_runtime_alloc_record_data\\0\",\n                    |alloc_record_data_fun| {\n                        LLVMAddAttributeAtIndex(\n                            alloc_record_data_fun,\n                            LLVMAttributeReturnIndex,\n                            tcx.llvm_noalias_attr(),\n                        );\n                    },\n                );\n\n                let alloc_record_data_args = &mut [\n                    LLVMConstInt(llvm_i64, data_layout.size() as u64, 0),\n                    LLVMConstInt(llvm_i32, data_layout.align() as u64, 0),\n                ];\n\n                let llvm_untyped_record_data_ptr = LLVMBuildCall(\n                    builder,\n                    alloc_record_data_fun,\n                    alloc_record_data_args.as_mut_ptr(),\n                    alloc_record_data_args.len() as u32,\n                    libcstr!(\"external_record_data\"),\n                );\n\n                // Convert the record data pointer to the correct type\n                let llvm_typed_record_data_ptr = LLVMBuildBitCast(\n                    builder,\n                    llvm_untyped_record_data_ptr,\n                    LLVMPointerType(llvm_data_type, 0),\n                    libcstr!(\"typed_record_data_ptr\"),\n                );\n\n                // Save the record data pointer\n                let llvm_record_data_ptr_ptr = LLVMBuildInBoundsGEP(\n                    builder,\n                    alloced_boxed_record,\n                    record_data_gep_indices.as_mut_ptr(),\n                    record_data_gep_indices.len() as u32,\n                    libcstr!(\"record_data_ptr_ptr\"),\n                );\n                LLVMBuildStore(\n                    builder,\n                    llvm_typed_record_data_ptr,\n                    llvm_record_data_ptr_ptr,\n                );\n\n                // Save the compact layout\n                let record_compact_layout_gep_indices = &mut [\n                    LLVMConstInt(llvm_i32, 0, 0),\n                    LLVMConstInt(\n                        llvm_i32,\n                        u64::from(record_struct::EXTERNAL_COMPACT_LAYOUT_INDEX),\n                        0,\n                    ),\n                ];\n\n                let llvm_record_compact_layout_ptr = LLVMBuildInBoundsGEP(\n                    builder,\n                    alloced_boxed_record,\n                    record_compact_layout_gep_indices.as_mut_ptr(),\n                    record_compact_layout_gep_indices.len() as u32,\n                    libcstr!(\"record_compact_layout_ptr\"),\n                );\n                LLVMBuildStore(\n                    builder,\n                    LLVMConstInt(\n                        llvm_i64,\n                        boxed::RecordData::alloc_layout_to_compact(Some(data_layout)),\n                        0,\n                    ),\n                    llvm_record_compact_layout_ptr,\n                );\n\n                (\n                    llvm_typed_record_data_ptr,\n                    boxed::Record::EXTERNAL_INLINE_LEN as usize,\n                )\n            }\n        };\n\n        let inline_byte_len_ptr = LLVMBuildStructGEP(\n            builder,\n            alloced_boxed_record,\n            record_struct::IS_INLINE_INDEX,\n            libcstr!(\"inline_byte_len_ptr\"),\n        );\n        let llvm_inline_byte_len = LLVMConstInt(llvm_i8, inline_byte_len as u64, 1);\n        LLVMBuildStore(builder, llvm_inline_byte_len, inline_byte_len_ptr);\n\n        for (field_index, llvm_field) in llvm_fields.iter().enumerate() {\n            let field_gep_indices = &mut [\n                LLVMConstInt(llvm_i32, 0, 0),\n                LLVMConstInt(llvm_i32, field_index as u64, 0),\n            ];\n\n            let llvm_field_ptr = LLVMBuildInBoundsGEP(\n                builder,\n                llvm_record_data_ptr,\n                field_gep_indices.as_mut_ptr(),\n                field_gep_indices.len() as u32,\n                libcstr!(\"init_record_field_ptr\"),\n            );\n\n            LLVMBuildStore(builder, *llvm_field, llvm_field_ptr);\n        }\n\n        let boxed_record_name = format!(\"alloced_{}_record\\0\", record_struct.source_name);\n\n        LLVMBuildBitCast(\n            builder,\n            alloced_boxed_record,\n            tcx.boxed_abi_to_llvm_ptr_type(&boxed::TypeTag::Record.into()),\n            boxed_record_name.as_ptr() as *const _,\n        )\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/analysis/escape.rs",
    "content": "use std::collections::{HashMap, HashSet};\n\nuse arret_runtime::abitype::{AbiType, ParamAbiType, ParamCapture, RetAbiType};\n\nuse crate::codegen::GenAbi;\nuse crate::mir::ops;\n\n/// Describes the capture behaviour of a function parameter\n#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]\npub enum CaptureKind {\n    /// This reg is always captured\n    Always = 2,\n    /// This reg is captured if the function's return value is captured\n    ViaRet = 1,\n    /// This reg is never captured\n    Never = 0,\n}\n\nimpl CaptureKind {\n    /// Calculates the capture of a passed parameter based on the the call's return capture\n    fn capture_for_call_param(self, return_capture: CaptureKind) -> CaptureKind {\n        match self {\n            CaptureKind::Always => CaptureKind::Always,\n            CaptureKind::ViaRet => return_capture,\n            CaptureKind::Never => CaptureKind::Never,\n        }\n    }\n}\n\n/// Tracks the captures for all regs in a function\n#[derive(Debug)]\npub struct Captures {\n    inner: HashMap<ops::RegId, CaptureKind>,\n}\n\nimpl Captures {\n    pub fn new() -> Captures {\n        Captures {\n            inner: HashMap::new(),\n        }\n    }\n\n    /// Adds the capture of a reg to the capture state\n    ///\n    /// If there is an existing capture the \"stronger\" capture kind of the two will be used.\n    pub fn add(&mut self, reg_id: ops::RegId, capture: CaptureKind) {\n        use std::cmp::max;\n\n        // It's not worthwhile to track never captures; that's the default capture type\n        if capture != CaptureKind::Never {\n            self.inner\n                .entry(reg_id)\n                .and_modify(|e| {\n                    *e = max(*e, capture);\n                })\n                .or_insert(capture);\n        }\n    }\n\n    pub fn get(&self, reg_id: ops::RegId) -> CaptureKind {\n        self.inner\n            .get(&reg_id)\n            .cloned()\n            .unwrap_or(CaptureKind::Never)\n    }\n}\n\n/// Infers if a function can capture a parameter based on its return type\n///\n/// This is used for Rust functions where we don't have precise capture information. This uses a\n/// very conservative algorithm where any function returning a box is assumed to capture all of its\n/// arguments.\npub fn infer_param_capture_kind(\n    ret_abi_type: &RetAbiType,\n    param_abi_type: &ParamAbiType,\n) -> CaptureKind {\n    let returns_box = matches!(ret_abi_type, RetAbiType::Inhabited(AbiType::Boxed(_)));\n\n    match param_abi_type.capture {\n        ParamCapture::Auto => {\n            if returns_box {\n                CaptureKind::ViaRet\n            } else {\n                CaptureKind::Never\n            }\n        }\n        ParamCapture::Always => CaptureKind::Always,\n        ParamCapture::Never => CaptureKind::Never,\n    }\n}\n\nfn add_static_symbol_call_captures(\n    captures: &mut Captures,\n    return_capture: CaptureKind,\n    static_symbol_abi: &GenAbi,\n    args: &[ops::RegId],\n) {\n    let arg_iter = args.iter();\n\n    assert_eq!(arg_iter.len(), static_symbol_abi.params.len());\n    for (arg_reg, param_abi_type) in arg_iter.zip(static_symbol_abi.params.iter()) {\n        let param_capture = infer_param_capture_kind(&static_symbol_abi.ret, param_abi_type);\n\n        captures.add(\n            *arg_reg,\n            param_capture.capture_for_call_param(return_capture),\n        );\n    }\n}\n\nstruct ProgramCaptureCtx<'of> {\n    private_funs: &'of HashMap<ops::PrivateFunId, ops::Fun>,\n    private_fun_captures: HashMap<ops::PrivateFunId, Captures>,\n\n    // Funs we're currently recursing in to\n    recursing_private_funs: HashSet<ops::PrivateFunId>,\n}\n\nimpl<'of> ProgramCaptureCtx<'of> {\n    fn add_op_captures(&mut self, captures: &mut Captures, ret_type: &RetAbiType, op: &ops::Op) {\n        use crate::mir::ops::OpKind;\n\n        match op.kind() {\n            OpKind::Ret(ret_reg) => {\n                if let RetAbiType::Inhabited(AbiType::Boxed(_)) = ret_type {\n                    // `Ret` captures boxes unconditionally\n                    captures.add(*ret_reg, CaptureKind::ViaRet);\n                }\n            }\n            OpKind::CastBoxed(reg, ops::CastBoxedOp { from_reg, .. })\n            | OpKind::Alias(reg, from_reg)\n            | OpKind::LoadBoxedPairHead(reg, from_reg)\n            | OpKind::LoadBoxedPairRest(reg, from_reg)\n            | OpKind::LoadBoxedVectorMember(\n                reg,\n                ops::LoadBoxedVectorMemberOp {\n                    vector_reg: from_reg,\n                    ..\n                },\n            ) => {\n                captures.add(*from_reg, captures.get(*reg));\n            }\n            OpKind::AllocBoxedPair(\n                reg,\n                ops::BoxPairOp {\n                    head_reg, rest_reg, ..\n                },\n            ) => {\n                let output_capture = captures.get(*reg);\n                captures.add(*head_reg, output_capture);\n                captures.add(*rest_reg, output_capture);\n            }\n            OpKind::Cond(ops::CondOp {\n                reg_phi,\n                true_ops,\n                false_ops,\n                ..\n            }) => {\n                if let Some(reg_phi) = reg_phi {\n                    let output_capture = captures.get(reg_phi.output_reg);\n\n                    // Propagate captures through the phi\n                    captures.add(reg_phi.true_result_reg, output_capture);\n                    captures.add(reg_phi.false_result_reg, output_capture);\n                }\n\n                for op in true_ops.iter().rev().chain(false_ops.iter().rev()) {\n                    self.add_op_captures(captures, ret_type, op);\n                }\n            }\n            OpKind::Call(reg, ops::CallOp { callee, args, .. }) => {\n                let return_capture = captures.get(*reg);\n\n                match callee {\n                    ops::Callee::StaticSymbol(ops::StaticSymbol { abi, .. }) => {\n                        add_static_symbol_call_captures(captures, return_capture, abi, args);\n                    }\n                    ops::Callee::PrivateFun(private_fun_id) => {\n                        let ops_fun = &self.private_funs[private_fun_id];\n\n                        if !self.recursing_private_funs.contains(private_fun_id) {\n                            let callee_captures = self.captures_for_private_fun_id(*private_fun_id);\n\n                            for (arg_reg, param_reg) in args.iter().zip(ops_fun.param_regs.iter()) {\n                                captures.add(*arg_reg, callee_captures.get(*param_reg));\n                            }\n                        } else {\n                            // This is part of a recursive loop; assume everything is captured.\n\n                            // While this seems like an easy way out this is probably the right\n                            // thing to do. If there are many loop iterations at runtime we do\n                            // not want to allocate boxes on the stack. This both prevents tail\n                            // recursion and can lead to a stack overflow. By claiming that\n                            // everything is captured we force them to be heap allocated.\n                            for arg_reg in args.iter() {\n                                captures.add(*arg_reg, CaptureKind::Always);\n                            }\n                        }\n                    }\n                    ops::Callee::BoxedFunThunk(_) => {\n                        // We know nothing about the actual captures. We need to assume the worst.\n                        for arg_reg in args.iter() {\n                            captures.add(*arg_reg, CaptureKind::Always);\n                        }\n                    }\n                };\n            }\n            OpKind::TailCall(_, ops::TailCallOp { args, .. }) => {\n                // This is the same justification as the recursive case in `OpKind::Call`\n                for arg_reg in args.iter() {\n                    captures.add(*arg_reg, CaptureKind::Always);\n                }\n            }\n            OpKind::MakeCallback(_, ops::MakeCallbackOp { callee, .. })\n            | OpKind::AllocBoxedFunThunk(_, ops::BoxFunThunkOp { callee, .. })\n            | OpKind::ConstBoxedFunThunk(_, ops::BoxFunThunkOp { callee, .. }) => {\n                // We don't actually care about these captures; we just pull them in for dependencies\n                if let ops::Callee::PrivateFun(private_fun_id) = callee {\n                    // If we're already recursing we'll only loop if we re-enter\n                    if !self.recursing_private_funs.contains(private_fun_id) {\n                        self.captures_for_private_fun_id(*private_fun_id);\n                    }\n                }\n            }\n            OpKind::AllocBoxedRecord(reg, ops::BoxRecordOp { field_regs, .. }) => {\n                let output_capture = captures.get(*reg);\n\n                for field_reg in field_regs.iter() {\n                    captures.add(*field_reg, output_capture);\n                }\n            }\n            OpKind::LoadBoxedRecordField(\n                reg,\n                ops::LoadBoxedRecordFieldOp {\n                    record_reg,\n                    record_struct,\n                    field_index,\n                },\n            ) => {\n                let output_capture = captures.get(*reg);\n\n                // Don't capture the record if we're loading a non-GCed value\n                if record_struct.field_abi_types[*field_index].may_contain_gc_refs() {\n                    captures.add(*record_reg, output_capture);\n                }\n            }\n            _ => {}\n        }\n    }\n\n    fn captures_for_private_fun_id(&mut self, private_fun_id: ops::PrivateFunId) -> &Captures {\n        if self.private_fun_captures.contains_key(&private_fun_id) {\n            return &self.private_fun_captures[&private_fun_id];\n        }\n\n        self.recursing_private_funs.insert(private_fun_id);\n\n        let ops_fun = &self.private_funs[&private_fun_id];\n        let captures = self.calc_fun_captures(ops_fun);\n\n        self.recursing_private_funs.remove(&private_fun_id);\n        self.private_fun_captures\n            .entry(private_fun_id)\n            .or_insert(captures)\n    }\n\n    fn calc_fun_captures(&mut self, fun: &ops::Fun) -> Captures {\n        let mut captures = Captures::new();\n\n        for op in fun.ops.iter().rev() {\n            self.add_op_captures(&mut captures, &fun.abi.ret, op);\n        }\n\n        captures\n    }\n}\n\npub struct ProgramCaptures {\n    pub entry_fun_captures: Captures,\n    pub private_fun_captures: HashMap<ops::PrivateFunId, Captures>,\n}\n\n/// Calculates the captured registers for the passed fun and every fun it references\npub fn calc_program_captures(\n    private_funs: &HashMap<ops::PrivateFunId, ops::Fun>,\n    entry_fun: &ops::Fun,\n) -> ProgramCaptures {\n    let mut ctx = ProgramCaptureCtx {\n        private_funs,\n        private_fun_captures: HashMap::new(),\n\n        recursing_private_funs: HashSet::new(),\n    };\n\n    let entry_fun_captures = ctx.calc_fun_captures(entry_fun);\n\n    ProgramCaptures {\n        private_fun_captures: ctx.private_fun_captures,\n        entry_fun_captures,\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use arret_runtime::boxed;\n\n    use crate::source::EMPTY_SPAN;\n\n    fn calc_single_fun_captures(fun: &ops::Fun) -> Captures {\n        calc_program_captures(&HashMap::new(), fun).entry_fun_captures\n    }\n\n    #[test]\n    fn infer_param_capture() {\n        // Boxed return type can capture boxed parameter\n        assert_eq!(\n            CaptureKind::ViaRet,\n            infer_param_capture_kind(&boxed::TypeTag::Int.into(), &boxed::TypeTag::Int.into())\n        );\n\n        // Unboxed return type cannot capture boxed parameter\n        assert_eq!(\n            CaptureKind::Never,\n            infer_param_capture_kind(&AbiType::Bool.into(), &boxed::TypeTag::Int.into())\n        );\n    }\n\n    #[test]\n    fn empty_fun_captures() {\n        let param_reg = ops::RegId::alloc();\n\n        let test_fun = ops::Fun {\n            span: EMPTY_SPAN,\n            source_name: None,\n\n            abi: ops::OpsAbi {\n                call_conv: ops::CallConv::FastCc,\n                params: Box::new([boxed::TypeTag::Int.into()]),\n                ret: RetAbiType::Void,\n            },\n            param_regs: Box::new([param_reg]),\n            ops: Box::new([]),\n        };\n\n        let captures = calc_single_fun_captures(&test_fun);\n        assert_eq!(CaptureKind::Never, captures.get(param_reg));\n    }\n\n    #[test]\n    fn capture_param_via_ret() {\n        let capture_reg = ops::RegId::alloc();\n\n        let test_fun = ops::Fun {\n            span: EMPTY_SPAN,\n            source_name: None,\n\n            abi: ops::OpsAbi {\n                call_conv: ops::CallConv::FastCc,\n                params: Box::new([boxed::TypeTag::Int.into()]),\n                ret: boxed::TypeTag::Int.into(),\n            },\n            param_regs: Box::new([capture_reg]),\n            ops: Box::new([ops::OpKind::Ret(capture_reg).into()]),\n        };\n\n        let captures = calc_single_fun_captures(&test_fun);\n        assert_eq!(CaptureKind::ViaRet, captures.get(capture_reg));\n    }\n\n    #[test]\n    fn capture_param_via_pair() {\n        let param_reg = ops::RegId::alloc();\n        let ret_reg = ops::RegId::alloc();\n\n        let test_fun = ops::Fun {\n            span: EMPTY_SPAN,\n            source_name: None,\n\n            abi: ops::OpsAbi {\n                call_conv: ops::CallConv::FastCc,\n                params: Box::new([boxed::TypeTag::Int.into()]),\n                ret: boxed::TypeTag::Pair.into(),\n            },\n            param_regs: Box::new([param_reg]),\n            ops: Box::new([\n                ops::OpKind::AllocBoxedPair(\n                    ret_reg,\n                    ops::BoxPairOp {\n                        head_reg: param_reg,\n                        rest_reg: param_reg,\n                        list_len_reg: param_reg,\n                    },\n                )\n                .into(),\n                ops::OpKind::Ret(ret_reg).into(),\n            ]),\n        };\n\n        let captures = calc_single_fun_captures(&test_fun);\n        assert_eq!(CaptureKind::ViaRet, captures.get(param_reg));\n        assert_eq!(CaptureKind::ViaRet, captures.get(ret_reg));\n    }\n\n    #[test]\n    fn capture_param_via_box_thunk_call() {\n        let param_reg = ops::RegId::alloc();\n        let ret_reg = ops::RegId::alloc();\n\n        let test_fun = ops::Fun {\n            span: EMPTY_SPAN,\n            source_name: None,\n\n            abi: ops::OpsAbi {\n                call_conv: ops::CallConv::FastCc,\n                params: Box::new([boxed::TypeTag::Int.into()]),\n                ret: boxed::TypeTag::Pair.into(),\n            },\n            param_regs: Box::new([param_reg]),\n            ops: Box::new([\n                ops::OpKind::Call(\n                    ret_reg,\n                    ops::CallOp {\n                        callee: ops::Callee::BoxedFunThunk(param_reg),\n                        impure: true,\n                        args: Box::new([param_reg, param_reg, param_reg]),\n                    },\n                )\n                .into(),\n                ops::OpKind::Ret(ret_reg).into(),\n            ]),\n        };\n\n        let captures = calc_single_fun_captures(&test_fun);\n        assert_eq!(CaptureKind::Always, captures.get(param_reg));\n        assert_eq!(CaptureKind::ViaRet, captures.get(ret_reg));\n    }\n\n    #[test]\n    fn capture_param_via_static_symbol_call() {\n        // These are passed to the first call with an unused ret\n        let param_reg1 = ops::RegId::alloc();\n        let param_reg2 = ops::RegId::alloc();\n        let param_reg3 = ops::RegId::alloc();\n\n        // These are passed to the second call which does have its ret captured\n        let param_reg4 = ops::RegId::alloc();\n        let param_reg5 = ops::RegId::alloc();\n        let param_reg6 = ops::RegId::alloc();\n\n        let unused_reg = ops::RegId::alloc();\n        let ret_reg = ops::RegId::alloc();\n\n        let static_symbol_abi = GenAbi {\n            takes_task: false,\n            params: Box::new([\n                ParamAbiType {\n                    abi_type: boxed::TypeTag::Int.into(),\n                    capture: ParamCapture::Never,\n                },\n                ParamAbiType {\n                    abi_type: boxed::TypeTag::Int.into(),\n                    capture: ParamCapture::Auto,\n                },\n                ParamAbiType {\n                    abi_type: boxed::TypeTag::Int.into(),\n                    capture: ParamCapture::Always,\n                },\n            ]),\n            ret: boxed::TypeTag::Int.into(),\n        };\n\n        let static_symbol = ops::StaticSymbol {\n            symbol: \"test\",\n            impure: true,\n            abi: static_symbol_abi,\n        };\n\n        let test_fun = ops::Fun {\n            span: EMPTY_SPAN,\n            source_name: None,\n\n            abi: ops::OpsAbi {\n                call_conv: ops::CallConv::FastCc,\n                params: Box::new([\n                    boxed::TypeTag::Int.into(),\n                    boxed::TypeTag::Int.into(),\n                    boxed::TypeTag::Int.into(),\n                    boxed::TypeTag::Int.into(),\n                    boxed::TypeTag::Int.into(),\n                    boxed::TypeTag::Int.into(),\n                ]),\n                ret: boxed::TypeTag::Int.into(),\n            },\n            param_regs: Box::new([param_reg1]),\n            ops: Box::new([\n                ops::OpKind::Call(\n                    unused_reg,\n                    ops::CallOp {\n                        callee: ops::Callee::StaticSymbol(static_symbol.clone()),\n                        impure: true,\n                        args: Box::new([param_reg1, param_reg2, param_reg3]),\n                    },\n                )\n                .into(),\n                ops::OpKind::Call(\n                    ret_reg,\n                    ops::CallOp {\n                        callee: ops::Callee::StaticSymbol(static_symbol),\n                        impure: true,\n                        args: Box::new([param_reg4, param_reg5, param_reg6]),\n                    },\n                )\n                .into(),\n                ops::OpKind::Ret(ret_reg).into(),\n            ]),\n        };\n\n        let captures = calc_single_fun_captures(&test_fun);\n\n        assert_eq!(CaptureKind::Never, captures.get(param_reg1));\n        assert_eq!(CaptureKind::Never, captures.get(param_reg2));\n        assert_eq!(CaptureKind::Always, captures.get(param_reg3));\n\n        assert_eq!(CaptureKind::Never, captures.get(param_reg4));\n        assert_eq!(CaptureKind::ViaRet, captures.get(param_reg5));\n        assert_eq!(CaptureKind::Always, captures.get(param_reg6));\n\n        assert_eq!(CaptureKind::Never, captures.get(unused_reg));\n        assert_eq!(CaptureKind::ViaRet, captures.get(ret_reg));\n    }\n\n    #[test]\n    fn capture_param_via_cond() {\n        let param_reg = ops::RegId::alloc();\n        let ret_reg = ops::RegId::alloc();\n\n        let test_fun = ops::Fun {\n            span: EMPTY_SPAN,\n            source_name: None,\n\n            abi: ops::OpsAbi {\n                call_conv: ops::CallConv::FastCc,\n                params: Box::new([boxed::TypeTag::Int.into()]),\n                ret: boxed::TypeTag::Pair.into(),\n            },\n            param_regs: Box::new([param_reg]),\n            ops: Box::new([\n                ops::OpKind::Cond(ops::CondOp {\n                    reg_phi: Some(ops::RegPhi {\n                        output_reg: ret_reg,\n                        true_result_reg: param_reg,\n                        false_result_reg: param_reg,\n                    }),\n                    test_reg: param_reg,\n                    true_ops: Box::new([]),\n                    false_ops: Box::new([]),\n                })\n                .into(),\n                ops::OpKind::Ret(ret_reg).into(),\n            ]),\n        };\n\n        let captures = calc_single_fun_captures(&test_fun);\n        assert_eq!(CaptureKind::ViaRet, captures.get(param_reg));\n        assert_eq!(CaptureKind::ViaRet, captures.get(ret_reg));\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/analysis/mod.rs",
    "content": "pub mod escape;\npub mod names;\n\nuse std::collections::{BTreeMap, HashMap};\nuse std::rc::Rc;\n\nuse arret_runtime::intern;\n\nuse crate::codegen::analysis::escape::Captures;\nuse crate::mir::ops;\n\npub struct AnalysedMod<'of> {\n    private_funs: HashMap<ops::PrivateFunId, AnalysedFun<'of>>,\n    entry_fun: AnalysedFun<'of>,\n    global_interned_names: BTreeMap<Rc<str>, intern::InternedSym>,\n}\n\npub struct AnalysedFun<'of> {\n    pub ops_fun: &'of ops::Fun,\n    pub captures: Captures,\n}\n\nimpl<'of> AnalysedMod<'of> {\n    pub fn new(\n        private_funs: &'of HashMap<ops::PrivateFunId, ops::Fun>,\n        entry_fun: &'of ops::Fun,\n    ) -> AnalysedMod<'of> {\n        // This also determines which private funs are used; private_fun_captures won't contain\n        // entries for unused funs\n        let escape::ProgramCaptures {\n            private_fun_captures,\n            entry_fun_captures,\n        } = escape::calc_program_captures(private_funs, entry_fun);\n\n        let private_funs = private_fun_captures\n            .into_iter()\n            .map(|(private_fun_id, captures)| {\n                (\n                    private_fun_id,\n                    AnalysedFun {\n                        ops_fun: &private_funs[&private_fun_id],\n                        captures,\n                    },\n                )\n            })\n            .collect();\n\n        let global_interned_names =\n            names::calc_program_global_interned_names(&private_funs, entry_fun);\n\n        AnalysedMod {\n            private_funs,\n            entry_fun: AnalysedFun {\n                ops_fun: entry_fun,\n                captures: entry_fun_captures,\n            },\n            global_interned_names,\n        }\n    }\n\n    pub fn private_funs(&self) -> impl Iterator<Item = (&ops::PrivateFunId, &AnalysedFun<'of>)> {\n        self.private_funs.iter()\n    }\n\n    pub fn entry_fun(&self) -> &AnalysedFun<'of> {\n        &self.entry_fun\n    }\n\n    pub fn global_interned_names(&self) -> &BTreeMap<Rc<str>, intern::InternedSym> {\n        &self.global_interned_names\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/analysis/names.rs",
    "content": "use std::collections::{BTreeMap, BTreeSet, HashMap};\nuse std::rc::Rc;\n\nuse arret_runtime::intern;\n\nuse super::AnalysedFun;\nuse crate::mir::ops;\n\nfn add_op_global_interned_names(names: &mut BTreeSet<Rc<str>>, op: &ops::Op) {\n    use ops::OpKind;\n\n    match op.kind() {\n        OpKind::ConstInternedSym(_, name) | OpKind::ConstBoxedSym(_, name) => {\n            if intern::InternedSym::try_from_inline_name(name).is_none() {\n                names.insert(name.clone());\n            }\n        }\n        OpKind::Cond(ops::CondOp {\n            true_ops,\n            false_ops,\n            ..\n        }) => {\n            for op in true_ops.iter().rev().chain(false_ops.iter().rev()) {\n                add_op_global_interned_names(names, op);\n            }\n        }\n        _ => {}\n    }\n}\n\nfn add_fun_global_interned_names(fun: &ops::Fun, names: &mut BTreeSet<Rc<str>>) {\n    for op in fun.ops.iter() {\n        add_op_global_interned_names(names, op);\n    }\n}\n\n/// Finds all global interned names in the program and returns them in sorted order\npub fn calc_program_global_interned_names(\n    private_funs: &HashMap<ops::PrivateFunId, AnalysedFun<'_>>,\n    entry_fun: &ops::Fun,\n) -> BTreeMap<Rc<str>, intern::InternedSym> {\n    let mut names: BTreeSet<Rc<str>> = BTreeSet::new();\n\n    for fun in private_funs\n        .values()\n        .map(|af| af.ops_fun)\n        .chain(std::iter::once(entry_fun))\n    {\n        add_fun_global_interned_names(fun, &mut names);\n    }\n\n    names\n        .into_iter()\n        .enumerate()\n        .map(|(idx, name)| (name, intern::InternedSym::from_global_index(idx as u32)))\n        .collect()\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use arret_runtime::abitype::RetAbiType;\n    use arret_runtime::boxed;\n\n    use crate::source::EMPTY_SPAN;\n\n    #[test]\n    fn simple_global_interned_names() {\n        let param_reg = ops::RegId::alloc();\n\n        let inline_reg = ops::RegId::alloc();\n        let alpha_reg = ops::RegId::alloc();\n        let beta_reg = ops::RegId::alloc();\n        let gamma_reg = ops::RegId::alloc();\n\n        let test_fun = ops::Fun {\n            span: EMPTY_SPAN,\n            source_name: None,\n\n            abi: ops::OpsAbi {\n                call_conv: ops::CallConv::FastCc,\n                params: Box::new([boxed::TypeTag::Int.into()]),\n                ret: RetAbiType::Void,\n            },\n            param_regs: Box::new([]),\n            ops: Box::new([\n                ops::OpKind::ConstBoxedSym(inline_reg, \"inline\".into()).into(),\n                ops::OpKind::Cond(ops::CondOp {\n                    reg_phi: None,\n                    test_reg: param_reg,\n                    true_ops: Box::new([ops::OpKind::ConstBoxedSym(\n                        beta_reg,\n                        \"beta NOT INLINE\".into(),\n                    )\n                    .into()]),\n                    false_ops: Box::new([ops::OpKind::ConstInternedSym(\n                        gamma_reg,\n                        \"gamma NOT INLINE\".into(),\n                    )\n                    .into()]),\n                })\n                .into(),\n                ops::OpKind::ConstBoxedSym(alpha_reg, \"alpha NOT INLINE\".into()).into(),\n            ]),\n        };\n\n        let global_interned_names = calc_program_global_interned_names(&HashMap::new(), &test_fun);\n\n        assert_eq!(\n            [\n                (\"alpha NOT INLINE\", 0u32),\n                (\"beta NOT INLINE\", 1u32),\n                (\"gamma NOT INLINE\", 2u32)\n            ]\n            .iter()\n            .map(|(name, idx)| ((*name).into(), intern::InternedSym::from_global_index(*idx)))\n            .collect::<BTreeMap<Rc<str>, _>>(),\n            global_interned_names\n        );\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/box_layout.rs",
    "content": "use llvm_sys::core::*;\nuse llvm_sys::prelude::*;\n\nuse arret_runtime::abitype::{BoxedAbiType, EncodeBoxedAbiType, TOP_LIST_BOXED_ABI_TYPE};\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::TypeTag;\n\nuse crate::codegen::record_struct;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::codegen::GenAbi;\n\n/// Represents the runtime layout of a boxed data structure\n///\n/// There are many boxed ABI types that can correspond to the same type name and layout. For\n/// example, all boxed pair types currently share a layout.\n#[derive(Clone, Hash, PartialEq, Eq)]\npub enum BoxLayout {\n    Any,\n    Bool,\n    Num,\n    List,\n    Union,\n    ConstTagged(boxed::TypeTag),\n}\n\nimpl BoxLayout {\n    /// Returns a NULL terminated type name for the box layout\n    ///\n    /// This is used to make the LLVM IR more descriptive. Some boxes with identical layouts have\n    /// distinct enum values and names for the purposes of making LLVM IR more readable.\n    pub fn type_name(&self) -> &'static [u8] {\n        match self {\n            BoxLayout::Any => b\"boxed_any\\0\",\n            BoxLayout::Bool => b\"boxed_bool\\0\",\n            BoxLayout::Num => b\"boxed_num\\0\",\n            BoxLayout::List => b\"boxed_list\\0\",\n            BoxLayout::Union => b\"boxed_union\\0\",\n            BoxLayout::ConstTagged(TypeTag::Nil) => b\"boxed_nil\\0\",\n            BoxLayout::ConstTagged(TypeTag::True) => b\"boxed_true\\0\",\n            BoxLayout::ConstTagged(TypeTag::False) => b\"boxed_false\\0\",\n            BoxLayout::ConstTagged(TypeTag::Int) => b\"boxed_int\\0\",\n            BoxLayout::ConstTagged(TypeTag::Float) => b\"boxed_float\\0\",\n            BoxLayout::ConstTagged(TypeTag::Char) => b\"boxed_char\\0\",\n            BoxLayout::ConstTagged(TypeTag::Set) => b\"boxed_set\\0\",\n            BoxLayout::ConstTagged(TypeTag::Str) => b\"boxed_str\\0\",\n            BoxLayout::ConstTagged(TypeTag::Sym) => b\"boxed_sym\\0\",\n            BoxLayout::ConstTagged(TypeTag::FunThunk) => b\"boxed_fun_thunk\\0\",\n            BoxLayout::ConstTagged(TypeTag::Pair) => b\"boxed_pair\\0\",\n            BoxLayout::ConstTagged(TypeTag::Vector) => b\"boxed_vector\\0\",\n            BoxLayout::ConstTagged(TypeTag::Record) => b\"boxed_record\\0\",\n            BoxLayout::ConstTagged(TypeTag::Map) => b\"boxed_map\\0\",\n        }\n    }\n\n    /// Appends member types to the passed `Vec`\n    ///\n    /// This presumes `members` already contains the box header\n    pub fn append_members(&self, tcx: &mut TargetCtx, members: &mut Vec<LLVMTypeRef>) {\n        unsafe {\n            match self {\n                BoxLayout::Any => {\n                    use std::mem;\n\n                    let llvm_byte = LLVMInt8TypeInContext(tcx.llx);\n                    let padding_bytes =\n                        mem::size_of::<boxed::Any>() - mem::size_of::<boxed::Header>();\n\n                    members.push(LLVMArrayType(llvm_byte, padding_bytes as u32));\n                }\n                BoxLayout::ConstTagged(TypeTag::Int) => {\n                    members.push(LLVMInt64TypeInContext(tcx.llx));\n                }\n                BoxLayout::ConstTagged(TypeTag::Float) => {\n                    members.push(LLVMDoubleTypeInContext(tcx.llx));\n                }\n                BoxLayout::ConstTagged(TypeTag::Char) => {\n                    members.push(LLVMInt32TypeInContext(tcx.llx));\n                }\n                BoxLayout::ConstTagged(TypeTag::Str) => {\n                    members.push(LLVMInt8TypeInContext(tcx.llx));\n                }\n                BoxLayout::ConstTagged(TypeTag::Sym) => {\n                    members.push(LLVMInt64TypeInContext(tcx.llx));\n                }\n                BoxLayout::ConstTagged(TypeTag::FunThunk) => {\n                    members.extend_from_slice(&[\n                        tcx.captures_llvm_type(),\n                        LLVMPointerType(tcx.fun_abi_to_llvm_type(&GenAbi::thunk_abi()), 0),\n                    ]);\n                }\n                BoxLayout::ConstTagged(TypeTag::Pair) => {\n                    let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n                    let llvm_any_ptr = tcx.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any);\n                    let llvm_any_list_ptr =\n                        tcx.boxed_abi_to_llvm_ptr_type(&TOP_LIST_BOXED_ABI_TYPE);\n\n                    members.extend_from_slice(&[llvm_i64, llvm_any_ptr, llvm_any_list_ptr]);\n                }\n                BoxLayout::ConstTagged(TypeTag::Record) => {\n                    record_struct::append_common_internal_members(tcx, members);\n                }\n                BoxLayout::List => {\n                    members.push(LLVMInt64TypeInContext(tcx.llx));\n                }\n                BoxLayout::ConstTagged(TypeTag::Set) => {\n                    let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n                    let llvm_any_ptr = tcx.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any);\n\n                    members.extend_from_slice(&[llvm_i32, llvm_any_ptr, llvm_any_ptr, llvm_any_ptr])\n                }\n                BoxLayout::ConstTagged(TypeTag::Vector) => {\n                    // inline_len\n                    members.push(LLVMInt32TypeInContext(tcx.llx));\n                }\n                BoxLayout::ConstTagged(TypeTag::Nil)\n                | BoxLayout::ConstTagged(TypeTag::True)\n                | BoxLayout::ConstTagged(TypeTag::False)\n                | BoxLayout::ConstTagged(TypeTag::Map)\n                | BoxLayout::Bool\n                | BoxLayout::Num\n                | BoxLayout::Union => {}\n            };\n        }\n    }\n}\n\nimpl From<&BoxedAbiType> for BoxLayout {\n    fn from(boxed_abi_type: &BoxedAbiType) -> BoxLayout {\n        match boxed_abi_type {\n            BoxedAbiType::Any => BoxLayout::Any,\n\n            BoxedAbiType::List(_) => BoxLayout::List,\n            &boxed::Num::BOXED_ABI_TYPE => BoxLayout::Num,\n            &boxed::Bool::BOXED_ABI_TYPE => BoxLayout::Bool,\n            BoxedAbiType::Union(_, _) => BoxLayout::Union,\n\n            BoxedAbiType::UniqueTagged(type_tag) => BoxLayout::ConstTagged(*type_tag),\n            BoxedAbiType::Pair(_) => BoxLayout::ConstTagged(TypeTag::Pair),\n            BoxedAbiType::Set(_) => BoxLayout::ConstTagged(TypeTag::Set),\n            BoxedAbiType::Vector(_) => BoxLayout::ConstTagged(TypeTag::Vector),\n            BoxedAbiType::Map(_, _) => BoxLayout::ConstTagged(TypeTag::Map),\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/callee.rs",
    "content": "use std::ffi;\n\nuse llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::{LLVMAttributeFunctionIndex, LLVMCallConv};\n\nuse crate::codegen::mod_gen::ModCtx;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::libcstr;\nuse crate::mir::ops;\n\npub fn gen_static_symbol_entry_point(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    static_symbol: &ops::StaticSymbol,\n) -> LLVMValueRef {\n    use crate::codegen::analysis::escape::{infer_param_capture_kind, CaptureKind};\n    use arret_runtime::abitype::{AbiType, RetAbiType};\n\n    let ops::StaticSymbol {\n        abi,\n        impure,\n        symbol,\n    } = static_symbol;\n\n    let function_type = tcx.fun_abi_to_llvm_type(abi);\n    let function_name = ffi::CString::new(*symbol).unwrap();\n\n    unsafe {\n        mcx.get_function_or_insert(\n            function_type,\n            function_name.as_bytes_with_nul(),\n            |function| {\n                let param_attr_offset = abi.takes_task as usize;\n\n                for (index, param_abi_type) in abi.params.iter().enumerate() {\n                    if let AbiType::Boxed(_) = param_abi_type.abi_type {\n                        let no_capture = infer_param_capture_kind(&abi.ret, param_abi_type)\n                            == CaptureKind::Never;\n\n                        tcx.add_boxed_param_attrs(\n                            function,\n                            (param_attr_offset + index) as u32,\n                            no_capture,\n                        )\n                    }\n                }\n\n                if !impure {\n                    let speculatable_attr = tcx.llvm_enum_attr_for_name(\"speculatable\", 0);\n                    LLVMAddAttributeAtIndex(\n                        function,\n                        LLVMAttributeFunctionIndex,\n                        speculatable_attr,\n                    );\n                }\n\n                match abi.ret {\n                    RetAbiType::Inhabited(AbiType::Boxed(_)) => {\n                        tcx.add_boxed_return_attrs(function);\n                    }\n                    RetAbiType::Never => {\n                        let noreturn_attr = tcx.llvm_enum_attr_for_name(\"noreturn\", 0);\n                        LLVMAddAttributeAtIndex(\n                            function,\n                            LLVMAttributeFunctionIndex,\n                            noreturn_attr,\n                        );\n                    }\n                    _ => {}\n                }\n            },\n        )\n    }\n}\n\npub fn gen_boxed_fun_thunk_entry_point(\n    builder: LLVMBuilderRef,\n    llvm_fun_thunk: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let entry_ptr =\n            LLVMBuildStructGEP(builder, llvm_fun_thunk, 2, libcstr!(\"fun_thunk_entry_ptr\"));\n\n        LLVMBuildLoad(builder, entry_ptr, libcstr!(\"fun_thunk_entry\"))\n    }\n}\n\npub fn callee_takes_task(callee: &ops::Callee) -> bool {\n    match callee {\n        ops::Callee::BoxedFunThunk(_) => true,\n        ops::Callee::PrivateFun(_) => true,\n        ops::Callee::StaticSymbol(ops::StaticSymbol { abi, .. }) => abi.takes_task,\n    }\n}\n\npub fn callee_call_conv(mcx: &mut ModCtx<'_, '_, '_>, callee: &ops::Callee) -> u32 {\n    match callee {\n        ops::Callee::BoxedFunThunk(_) | ops::Callee::StaticSymbol(_) => {\n            LLVMCallConv::LLVMCCallConv as u32\n        }\n        ops::Callee::PrivateFun(private_fun_id) => unsafe {\n            LLVMGetFunctionCallConv(mcx.llvm_private_fun(*private_fun_id))\n        },\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/const_gen.rs",
    "content": "use std::rc::Rc;\nuse std::{iter, mem};\n\nuse llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::{LLVMLinkage, LLVMUnnamedAddr};\n\nuse arret_runtime::boxed;\n\nuse crate::codegen::mod_gen::ModCtx;\nuse crate::codegen::record_struct;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::libcstr;\nuse crate::mir::ops;\n\npub fn annotate_private_global(llvm_global: LLVMValueRef) {\n    unsafe {\n        LLVMSetUnnamedAddress(llvm_global, LLVMUnnamedAddr::LLVMGlobalUnnamedAddr);\n        LLVMSetGlobalConstant(llvm_global, 1);\n        LLVMSetLinkage(llvm_global, LLVMLinkage::LLVMPrivateLinkage)\n    }\n}\n\npub fn gen_boxed_pair(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    llvm_head: LLVMValueRef,\n    llvm_rest: LLVMValueRef,\n    llvm_list_len: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let type_tag = boxed::TypeTag::Pair;\n        let llvm_type = tcx.boxed_abi_to_llvm_struct_type(&type_tag.into());\n\n        let members = &mut [\n            tcx.llvm_box_header(type_tag.to_const_header()),\n            llvm_list_len,\n            llvm_head,\n            llvm_rest,\n        ];\n\n        let llvm_value =\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n        let global = LLVMAddGlobal(mcx.module, llvm_type, libcstr!(\"const_pair\"));\n        LLVMSetInitializer(global, llvm_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::Pair>() as u32);\n\n        annotate_private_global(global);\n        global\n    }\n}\n\nfn gen_boxed_external_str(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    value: &str,\n) -> LLVMValueRef {\n    unsafe {\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n        let shared_str_members = &mut [\n            // ref_count\n            LLVMConstInt(llvm_i64, std::u64::MAX, 0),\n            // len\n            LLVMConstInt(llvm_i64, value.len() as u64, 0),\n            // data\n            LLVMConstStringInContext(tcx.llx, value.as_ptr() as *mut _, value.len() as u32, 1),\n        ];\n\n        let shared_str_llvm_value = LLVMConstStructInContext(\n            tcx.llx,\n            shared_str_members.as_mut_ptr(),\n            shared_str_members.len() as u32,\n            0,\n        );\n\n        let shared_str_global = LLVMAddGlobal(\n            mcx.module,\n            LLVMTypeOf(shared_str_llvm_value),\n            libcstr!(\"shared_str\"),\n        );\n        LLVMSetInitializer(shared_str_global, shared_str_llvm_value);\n        annotate_private_global(shared_str_global);\n\n        let type_tag = boxed::TypeTag::Str;\n        let external_llvm_type = tcx.boxed_external_str_llvm_type();\n        let llvm_i8 = LLVMInt8TypeInContext(tcx.llx);\n\n        let external_members = &mut [\n            tcx.llvm_box_header(type_tag.to_const_header()),\n            LLVMConstInt(llvm_i8, boxed::Str::EXTERNAL_INLINE_BYTE_LEN as u64, 0),\n            LLVMConstBitCast(\n                shared_str_global,\n                LLVMPointerType(tcx.shared_str_llvm_type(), 0),\n            ),\n        ];\n\n        let external_llvm_value = LLVMConstNamedStruct(\n            external_llvm_type,\n            external_members.as_mut_ptr(),\n            external_members.len() as u32,\n        );\n\n        let global = LLVMAddGlobal(mcx.module, external_llvm_type, libcstr!(\"const_str\"));\n        LLVMSetInitializer(global, external_llvm_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::Str>() as u32);\n        annotate_private_global(global);\n\n        LLVMConstBitCast(global, tcx.boxed_abi_to_llvm_ptr_type(&type_tag.into()))\n    }\n}\n\nfn gen_boxed_inline_str(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    value: &str,\n) -> LLVMValueRef {\n    unsafe {\n        const MAX_INLINE_BYTES: usize = boxed::Str::MAX_INLINE_BYTES;\n\n        let mut inline_buffer: [u8; MAX_INLINE_BYTES] = [0; MAX_INLINE_BYTES];\n        inline_buffer[0..value.len()].copy_from_slice(value.as_bytes());\n\n        let type_tag = boxed::TypeTag::Str;\n        let inline_llvm_type = tcx.boxed_inline_str_llvm_type();\n        let llvm_i8 = LLVMInt8TypeInContext(tcx.llx);\n\n        let members = &mut [\n            tcx.llvm_box_header(type_tag.to_const_header()),\n            LLVMConstInt(llvm_i8, value.len() as u64, 0),\n            LLVMConstStringInContext(\n                tcx.llx,\n                inline_buffer.as_mut_ptr() as *mut _,\n                MAX_INLINE_BYTES as u32,\n                1,\n            ),\n        ];\n\n        let inline_llvm_value =\n            LLVMConstNamedStruct(inline_llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n        let global = LLVMAddGlobal(mcx.module, inline_llvm_type, libcstr!(\"const_str\"));\n        LLVMSetInitializer(global, inline_llvm_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::Str>() as u32);\n        annotate_private_global(global);\n\n        LLVMConstBitCast(global, tcx.boxed_abi_to_llvm_ptr_type(&type_tag.into()))\n    }\n}\n\npub fn gen_boxed_str(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    value: &str,\n) -> LLVMValueRef {\n    match boxed::Str::storage_for_byte_len(value.len()) {\n        boxed::StrStorage::Inline(_) => gen_boxed_inline_str(tcx, mcx, value),\n        boxed::StrStorage::External => gen_boxed_external_str(tcx, mcx, value),\n    }\n}\n\npub fn gen_boxed_sym(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    value: &str,\n) -> LLVMValueRef {\n    let interned_sym = mcx.intern_name(value);\n\n    unsafe {\n        let type_tag = boxed::TypeTag::Sym;\n        let boxed_llvm_type = tcx.boxed_abi_to_llvm_struct_type(&type_tag.into());\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n        let members = &mut [\n            tcx.llvm_box_header(type_tag.to_const_header()),\n            LLVMConstInt(llvm_i64, interned_sym.to_raw_u64(), 0),\n        ];\n        let boxed_llvm_value =\n            LLVMConstNamedStruct(boxed_llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n        let global = LLVMAddGlobal(mcx.module, boxed_llvm_type, libcstr!(\"const_sym\"));\n        LLVMSetInitializer(global, boxed_llvm_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::Sym>() as u32);\n        annotate_private_global(global);\n\n        global\n    }\n}\n\n/// Generates a table of global interned names\n///\n/// `names` must be pre-sorted\npub fn gen_global_interned_names<'a>(\n    tcx: &mut TargetCtx,\n    llvm_module: LLVMModuleRef,\n    names: impl ExactSizeIterator<Item = &'a Rc<str>>,\n) -> LLVMValueRef {\n    unsafe {\n        let names_len = names.len();\n        if names_len == 0 {\n            return LLVMConstPointerNull(LLVMPointerType(tcx.global_interned_name_llvm_type(), 0));\n        }\n\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n        let global_interned_name_llvm_type = tcx.global_interned_name_llvm_type();\n\n        let first_element_gep_indices =\n            &mut [LLVMConstInt(llvm_i32, 0, 0), LLVMConstInt(llvm_i32, 0, 0)];\n\n        let mut llvm_names: Vec<LLVMValueRef> = names\n            .map(|name| {\n                let llvm_name_string = LLVMConstStringInContext(\n                    tcx.llx,\n                    name.as_bytes().as_ptr() as *mut _,\n                    name.len() as u32,\n                    1,\n                );\n\n                let name_global_name = format!(\"global_interned_name_{}\\0\", name);\n\n                let llvm_name_global = LLVMAddGlobal(\n                    llvm_module,\n                    LLVMTypeOf(llvm_name_string),\n                    name_global_name.as_ptr() as *const _,\n                );\n                LLVMSetInitializer(llvm_name_global, llvm_name_string);\n                annotate_private_global(llvm_name_global);\n\n                let llvm_name_string_ptr = LLVMConstGEP(\n                    llvm_name_global,\n                    first_element_gep_indices.as_mut_ptr(),\n                    first_element_gep_indices.len() as u32,\n                );\n\n                let llvm_name_members = &mut [\n                    LLVMConstInt(llvm_i64, name.len() as u64, 0),\n                    llvm_name_string_ptr,\n                ];\n\n                LLVMConstNamedStruct(\n                    global_interned_name_llvm_type,\n                    llvm_name_members.as_mut_ptr(),\n                    llvm_name_members.len() as u32,\n                )\n            })\n            .collect();\n\n        let llvm_names_array = LLVMConstArray(\n            global_interned_name_llvm_type,\n            llvm_names.as_mut_ptr(),\n            llvm_names.len() as u32,\n        );\n\n        let global_names_members = &mut [\n            // len\n            LLVMConstInt(llvm_i32, names_len as u64, 0),\n            // names\n            llvm_names_array,\n        ];\n\n        let llvm_global_names = LLVMConstStructInContext(\n            tcx.llx,\n            global_names_members.as_mut_ptr(),\n            global_names_members.len() as u32,\n            0,\n        );\n\n        let global = LLVMAddGlobal(\n            llvm_module,\n            LLVMTypeOf(llvm_global_names),\n            libcstr!(\"global_interned_names\"),\n        );\n\n        LLVMSetInitializer(global, llvm_global_names);\n        annotate_private_global(global);\n\n        global\n    }\n}\n\npub fn gen_boxed_int(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    value: i64,\n) -> LLVMValueRef {\n    unsafe {\n        let type_tag = boxed::TypeTag::Int;\n        let llvm_type = tcx.boxed_abi_to_llvm_struct_type(&type_tag.into());\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n        let box_name = format!(\"const_int_{}\\0\", value);\n\n        let global = mcx.get_global_or_insert(llvm_type, box_name.as_bytes(), || {\n            let members = &mut [\n                tcx.llvm_box_header(type_tag.to_const_header()),\n                LLVMConstInt(llvm_i64, value as u64, 1),\n            ];\n\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32)\n        });\n\n        LLVMSetAlignment(global, mem::align_of::<boxed::Int>() as u32);\n        annotate_private_global(global);\n        global\n    }\n}\n\npub fn gen_boxed_float(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    value: f64,\n) -> LLVMValueRef {\n    unsafe {\n        let type_tag = boxed::TypeTag::Float;\n        let llvm_type = tcx.boxed_abi_to_llvm_struct_type(&type_tag.into());\n        let llvm_double = LLVMDoubleTypeInContext(tcx.llx);\n\n        let members = &mut [\n            tcx.llvm_box_header(type_tag.to_const_header()),\n            LLVMConstReal(llvm_double, value),\n        ];\n\n        let llvm_value =\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n        let global = LLVMAddGlobal(mcx.module, llvm_type, libcstr!(\"const_float\"));\n        LLVMSetInitializer(global, llvm_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::Float>() as u32);\n\n        annotate_private_global(global);\n        global\n    }\n}\n\npub fn gen_boxed_char(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    value: char,\n) -> LLVMValueRef {\n    unsafe {\n        let type_tag = boxed::TypeTag::Char;\n        let llvm_type = tcx.boxed_abi_to_llvm_struct_type(&type_tag.into());\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n\n        let box_name = format!(\"const_char_{}\\0\", value);\n\n        let global = mcx.get_global_or_insert(llvm_type, box_name.as_bytes(), || {\n            let members = &mut [\n                tcx.llvm_box_header(type_tag.to_const_header()),\n                LLVMConstInt(llvm_i32, value as u64, 1),\n            ];\n\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32)\n        });\n\n        LLVMSetAlignment(global, mem::align_of::<boxed::Char>() as u32);\n        annotate_private_global(global);\n        global\n    }\n}\n\npub fn gen_boxed_nil(tcx: &mut TargetCtx, mcx: &mut ModCtx<'_, '_, '_>) -> LLVMValueRef {\n    tcx.ptr_to_singleton_box(mcx.module, boxed::TypeTag::Nil, b\"ARRET_NIL\\0\")\n}\n\npub fn gen_boxed_fun_thunk(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    llvm_captures: LLVMValueRef,\n    llvm_entry_point: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let type_tag = boxed::TypeTag::FunThunk;\n        let llvm_type = tcx.boxed_abi_to_llvm_struct_type(&type_tag.into());\n\n        let members = &mut [\n            tcx.llvm_box_header(type_tag.to_const_header()),\n            llvm_captures,\n            llvm_entry_point,\n        ];\n\n        let llvm_value =\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n        let global = LLVMAddGlobal(mcx.module, llvm_type, libcstr!(\"const_fun_thunk\"));\n        LLVMSetInitializer(global, llvm_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::FunThunk>() as u32);\n\n        annotate_private_global(global);\n        global\n    }\n}\n\npub fn gen_boxed_record(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    record_struct: &ops::RecordStructId,\n    llvm_fields: &[LLVMValueRef],\n) -> LLVMValueRef {\n    let type_tag = boxed::TypeTag::Record;\n    let record_class_id = mcx.record_class_id_for_struct(record_struct);\n\n    let record_struct::TargetRecordStruct {\n        data_layout,\n        record_storage,\n        llvm_data_type,\n        ..\n    } = *tcx.target_record_struct(record_struct);\n\n    let llvm_box_type = tcx.record_struct_llvm_box_type(record_struct);\n\n    unsafe {\n        let box_name = format!(\"const_{}\\0\", record_struct.source_name);\n\n        let llvm_data_struct = LLVMConstNamedStruct(\n            llvm_data_type,\n            llvm_fields.as_ptr() as *mut _,\n            llvm_fields.len() as u32,\n        );\n\n        let llvm_box_header = tcx.llvm_box_header(type_tag.to_const_header());\n\n        // Constant records by definition cannot have GC refs\n        let llvm_i8 = LLVMInt8TypeInContext(tcx.llx);\n        let llvm_has_gc_refs = LLVMConstInt(llvm_i8, 0, 1);\n\n        let llvm_record_class_id = LLVMConstInt(\n            tcx.record_class_id_llvm_type(),\n            u64::from(record_class_id),\n            1,\n        );\n\n        let llvm_box_value = if let boxed::RecordStorage::Inline(_) = record_storage {\n            let llvm_inline_byte_len = LLVMConstInt(\n                llvm_i8,\n                match data_layout {\n                    Some(data_layout) => data_layout.size() as u64,\n                    None => 0,\n                },\n                1,\n            );\n\n            let inline_box_members = &mut [\n                llvm_box_header,\n                llvm_inline_byte_len,\n                llvm_has_gc_refs,\n                llvm_record_class_id,\n                llvm_data_struct,\n            ];\n\n            LLVMConstNamedStruct(\n                llvm_box_type,\n                inline_box_members.as_mut_ptr(),\n                inline_box_members.len() as u32,\n            )\n        } else {\n            // Create a global containing our data and return a pointer to it\n            let data_global_name = format!(\"const_{}_data\\0\", record_struct.source_name);\n\n            let data_global = LLVMAddGlobal(\n                mcx.module,\n                llvm_data_type,\n                data_global_name.as_ptr() as *const _,\n            );\n            LLVMSetInitializer(data_global, llvm_data_struct);\n            annotate_private_global(data_global);\n\n            let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n            let external_box_members = &mut [\n                llvm_box_header,\n                LLVMConstInt(llvm_i8, boxed::Record::EXTERNAL_INLINE_LEN as u64, 1),\n                llvm_has_gc_refs,\n                llvm_record_class_id,\n                data_global,\n                LLVMConstInt(llvm_i64, 0, 0),\n            ];\n\n            LLVMConstNamedStruct(\n                llvm_box_type,\n                external_box_members.as_mut_ptr(),\n                external_box_members.len() as u32,\n            )\n        };\n\n        let global = LLVMAddGlobal(mcx.module, llvm_box_type, box_name.as_ptr() as *const _);\n        LLVMSetInitializer(global, llvm_box_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::Record>() as u32);\n        annotate_private_global(global);\n\n        LLVMConstBitCast(global, tcx.boxed_abi_to_llvm_ptr_type(&type_tag.into()))\n    }\n}\n\nfn gen_persistent_vector_leaf(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    llvm_elements: &[LLVMValueRef],\n) -> LLVMValueRef {\n    use arret_runtime::abitype::BoxedAbiType;\n    use arret_runtime::persistent::vector::GLOBAL_CONSTANT_REFCOUNT;\n    use arret_runtime::persistent::vector::NODE_SIZE;\n\n    unsafe {\n        let llvm_type = tcx.persistent_vector_leaf_llvm_type();\n        let llvm_any_ptr = tcx.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any);\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n        let mut padded_llvm_elements: Vec<LLVMValueRef> = llvm_elements\n            .iter()\n            .copied()\n            .chain(iter::repeat(LLVMGetUndef(llvm_any_ptr)).take(NODE_SIZE - llvm_elements.len()))\n            .collect();\n\n        let mut members = vec![\n            LLVMConstInt(llvm_i64, GLOBAL_CONSTANT_REFCOUNT as u64, 0),\n            LLVMConstArray(\n                llvm_any_ptr,\n                padded_llvm_elements.as_mut_ptr(),\n                padded_llvm_elements.len() as u32,\n            ),\n        ];\n\n        let global = LLVMAddGlobal(mcx.module, llvm_type, libcstr!(\"const_vector_leaf\"));\n\n        let llvm_value =\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n        LLVMSetInitializer(global, llvm_value);\n        annotate_private_global(global);\n        global\n    }\n}\n\nfn gen_boxed_external_vector(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    llvm_elements_iter: impl ExactSizeIterator<Item = LLVMValueRef>,\n) -> LLVMValueRef {\n    use arret_runtime::persistent::vector::NODE_SIZE;\n\n    let llvm_elements: Vec<LLVMValueRef> = llvm_elements_iter.collect();\n\n    unsafe {\n        let type_tag = boxed::TypeTag::Vector;\n        let llvm_type = tcx.boxed_external_vector_llvm_type();\n\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n        let llvm_persistent_vector_leaf_ptr =\n            LLVMPointerType(tcx.persistent_vector_leaf_llvm_type(), 0);\n\n        let (root_ptr, tail_ptr) = if llvm_elements.len() > NODE_SIZE {\n            // Need a root a tail\n            (\n                gen_persistent_vector_leaf(tcx, mcx, &llvm_elements[0..NODE_SIZE]),\n                gen_persistent_vector_leaf(tcx, mcx, &llvm_elements[NODE_SIZE..]),\n            )\n        } else {\n            // Need just the tail\n            (\n                LLVMConstPointerNull(llvm_persistent_vector_leaf_ptr),\n                gen_persistent_vector_leaf(tcx, mcx, &llvm_elements),\n            )\n        };\n\n        let mut members = [\n            tcx.llvm_box_header(type_tag.to_const_header()),\n            LLVMConstInt(\n                llvm_i32,\n                boxed::Vector::<boxed::Any>::EXTERNAL_INLINE_LEN as u64,\n                0,\n            ),\n            LLVMConstInt(llvm_i64, llvm_elements.len() as u64, 0),\n            root_ptr,\n            tail_ptr,\n        ];\n\n        let llvm_value =\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n        let global = LLVMAddGlobal(mcx.module, llvm_type, libcstr!(\"const_vector\"));\n        LLVMSetInitializer(global, llvm_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::Vector>() as u32);\n\n        annotate_private_global(global);\n        global\n    }\n}\n\nfn gen_boxed_inline_vector(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    llvm_elements: impl ExactSizeIterator<Item = LLVMValueRef>,\n) -> LLVMValueRef {\n    use arret_runtime::abitype::BoxedAbiType;\n\n    let elements_len = llvm_elements.len();\n\n    unsafe {\n        let type_tag = boxed::TypeTag::Vector;\n        let llvm_type = tcx.boxed_inline_vector_llvm_type();\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n        let llvm_any_ptr = tcx.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any);\n\n        let mut members: Vec<LLVMValueRef> = vec![\n            tcx.llvm_box_header(type_tag.to_const_header()),\n            LLVMConstInt(llvm_i32, elements_len as u64, 0),\n        ];\n\n        members.extend(\n            llvm_elements.chain(\n                iter::repeat(LLVMGetUndef(llvm_any_ptr))\n                    .take(boxed::Vector::<boxed::Any>::MAX_INLINE_LEN - elements_len),\n            ),\n        );\n\n        let llvm_value =\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n        let global = LLVMAddGlobal(mcx.module, llvm_type, libcstr!(\"const_vector\"));\n        LLVMSetInitializer(global, llvm_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::Vector>() as u32);\n\n        annotate_private_global(global);\n        global\n    }\n}\n\npub fn gen_boxed_vector(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    llvm_elements: impl ExactSizeIterator<Item = LLVMValueRef>,\n) -> LLVMValueRef {\n    use arret_runtime::persistent::vector::NODE_SIZE;\n\n    let elements_len = llvm_elements.len();\n\n    if elements_len <= boxed::Vector::<boxed::Any>::MAX_INLINE_LEN {\n        gen_boxed_inline_vector(tcx, mcx, llvm_elements)\n    } else if elements_len <= (NODE_SIZE * 2) {\n        gen_boxed_external_vector(tcx, mcx, llvm_elements)\n    } else {\n        todo!(\"generating constant vector of length {}\", elements_len);\n    }\n}\n\npub fn gen_boxed_set(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    llvm_elements: impl ExactSizeIterator<Item = LLVMValueRef>,\n) -> LLVMValueRef {\n    use arret_runtime::abitype::BoxedAbiType;\n\n    let elements_len = llvm_elements.len();\n\n    if elements_len > boxed::Set::<boxed::Any>::MAX_INLINE_LEN {\n        todo!(\"generating constant set of length {}\", elements_len);\n    }\n\n    unsafe {\n        let type_tag = boxed::TypeTag::Set;\n        let llvm_type = tcx.boxed_abi_to_llvm_struct_type(&type_tag.into());\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n        let llvm_any_ptr = tcx.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any);\n\n        let mut members: Vec<LLVMValueRef> = vec![\n            tcx.llvm_box_header(type_tag.to_const_header()),\n            LLVMConstInt(llvm_i32, elements_len as u64, 0),\n        ];\n\n        members.extend(\n            llvm_elements.chain(\n                iter::repeat(LLVMGetUndef(llvm_any_ptr))\n                    .take(boxed::Set::<boxed::Any>::MAX_INLINE_LEN - elements_len),\n            ),\n        );\n\n        let llvm_value =\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n        let global = LLVMAddGlobal(mcx.module, llvm_type, libcstr!(\"const_set\"));\n        LLVMSetInitializer(global, llvm_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::Set>() as u32);\n\n        annotate_private_global(global);\n        global\n    }\n}\n\npub fn gen_boxed_map(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    llvm_elements: impl ExactSizeIterator<Item = (LLVMValueRef, LLVMValueRef)>,\n) -> LLVMValueRef {\n    if llvm_elements.len() > 0 {\n        todo!(\"generating non-empty map\");\n    }\n\n    unsafe {\n        let type_tag = boxed::TypeTag::Map;\n        let llvm_type = tcx.boxed_abi_to_llvm_struct_type(&type_tag.into());\n\n        let mut members: Vec<LLVMValueRef> = vec![tcx.llvm_box_header(type_tag.to_const_header())];\n\n        let llvm_value =\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n        let global = LLVMAddGlobal(mcx.module, llvm_type, libcstr!(\"const_map\"));\n        LLVMSetInitializer(global, llvm_value);\n        LLVMSetAlignment(global, mem::align_of::<boxed::Map>() as u32);\n\n        annotate_private_global(global);\n        global\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/debug_info.rs",
    "content": "use std::collections::HashMap;\nuse std::os::unix::ffi::OsStrExt;\nuse std::{env, ffi, ptr};\n\nuse codespan_reporting::files::Files as _;\n\nuse llvm_sys::core::*;\nuse llvm_sys::debuginfo::*;\nuse llvm_sys::prelude::*;\n\nuse arret_syntax::datum::DataStr;\nuse arret_syntax::span::{FileId, Span};\n\nuse crate::source::SourceLoader;\n\npub struct DebugInfoBuilder<'sl> {\n    pub llvm_dib: LLVMDIBuilderRef,\n\n    source_loader: &'sl SourceLoader,\n    current_dir: ffi::OsString,\n    file_metadata: HashMap<FileId, LLVMMetadataRef>,\n}\n\nimpl<'sl> DebugInfoBuilder<'sl> {\n    pub fn new(\n        source_loader: &'sl SourceLoader,\n        optimised: bool,\n        main_span: Span,\n        module: LLVMModuleRef,\n    ) -> DebugInfoBuilder<'sl> {\n        // This is needed for all of our file metadata so the debugger can resolve relative paths\n        let current_dir = env::current_dir()\n            .ok()\n            .map(|current_dir| current_dir.as_os_str().to_owned())\n            .unwrap_or_else(ffi::OsString::new);\n\n        let llvm_dib = unsafe { LLVMCreateDIBuilderDisallowUnresolved(module) };\n\n        let mut di_builder = DebugInfoBuilder {\n            llvm_dib,\n\n            source_loader,\n            current_dir,\n            file_metadata: HashMap::new(),\n        };\n\n        di_builder.add_compile_unit_metadata(optimised, main_span);\n        di_builder\n    }\n\n    fn add_compile_unit_metadata(&mut self, optimised: bool, main_span: Span) {\n        let main_file_id = if let Some(file_id) = main_span.file_id() {\n            file_id\n        } else {\n            return;\n        };\n\n        let main_file_metadata = self.file_metadata(main_file_id);\n\n        let producer = b\"arret\\0\";\n\n        unsafe {\n            // This is implicitly added to the LLVM module\n            LLVMDIBuilderCreateCompileUnit(\n                self.llvm_dib,\n                LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageC,\n                main_file_metadata,\n                producer.as_ptr() as *const _,\n                producer.len(),\n                optimised as i32,                                 // `isOptimized`\n                ptr::null(),                                      // `Flags`\n                0,                                                // `FlagsLen`\n                0,                                                // `RuntimeVer`\n                ptr::null(),                                      // `SplitName`\n                0,                                                // `SplitNameLen`\n                LLVMDWARFEmissionKind::LLVMDWARFEmissionKindFull, // `LLVMDWARFEmissionKind::LLVMDWARFEmissionKindLineTablesOnly`,\n                0,                                                // `DWOId`\n                0,                                                // `SplitDebugInlining`\n                0,                                                // `DebugInfoForProfiling`\n            );\n        }\n    }\n\n    pub fn file_metadata(&mut self, file_id: FileId) -> LLVMMetadataRef {\n        if let Some(metadata) = self.file_metadata.get(&file_id) {\n            return *metadata;\n        }\n\n        let filename = self.source_loader.files().name(file_id).unwrap();\n\n        let metadata = unsafe {\n            LLVMDIBuilderCreateFile(\n                self.llvm_dib,\n                filename.as_ptr() as *const _,\n                filename.len(),\n                self.current_dir.as_bytes().as_ptr() as *const _,\n                self.current_dir.as_bytes().len(),\n            )\n        };\n\n        self.file_metadata.insert(file_id, metadata);\n        metadata\n    }\n\n    /// Returns a subroutine type containing no parameters\n    pub fn placeholder_subroutine_type(\n        &mut self,\n        file_metadata: LLVMMetadataRef,\n    ) -> LLVMMetadataRef {\n        // This includes no parameter types\n        unsafe {\n            LLVMDIBuilderCreateSubroutineType(\n                self.llvm_dib,\n                file_metadata,\n                ptr::null_mut(),\n                0,\n                LLVMDIFlagZero,\n            )\n        }\n    }\n\n    pub fn add_function_debug_info(\n        &mut self,\n        span: Span,\n        source_name: Option<&DataStr>,\n        llvm_function: LLVMValueRef,\n    ) {\n        let file_id = if let Some(file_id) = span.file_id() {\n            file_id\n        } else {\n            return;\n        };\n\n        let location = if let Ok(location) = self\n            .source_loader\n            .files()\n            .location(file_id, span.start() as usize)\n        {\n            location\n        } else {\n            return;\n        };\n\n        let line_index = location.line_number - 1;\n\n        let file_metadata = self.file_metadata(file_id);\n\n        unsafe {\n            let mut linkage_name_len: usize = 0;\n            let linkage_name_ptr = LLVMGetValueName2(llvm_function, &mut linkage_name_len);\n\n            let function_metadata = LLVMDIBuilderCreateFunction(\n                self.llvm_dib,\n                file_metadata, // `Scope`\n                source_name\n                    .map(|source_name| source_name.as_ptr() as *const _)\n                    .unwrap_or(linkage_name_ptr),\n                source_name\n                    .as_ref()\n                    .map(|source_name| source_name.len())\n                    .unwrap_or(linkage_name_len),\n                linkage_name_ptr,\n                linkage_name_len,\n                file_metadata,\n                line_index as u32,\n                self.placeholder_subroutine_type(file_metadata),\n                source_name.is_none() as i32, // `IsLocalToUnit`\n                1,                            // `IsDefinition`\n                line_index as u32,            // `ScopeLine`\n                LLVMDIFlagZero,\n                1, // `IsOptimized`\n            );\n\n            LLVMSetSubprogram(llvm_function, function_metadata);\n        }\n    }\n\n    pub fn finalise(&mut self) {\n        unsafe {\n            LLVMDIBuilderFinalize(self.llvm_dib);\n        }\n    }\n}\n\nimpl Drop for DebugInfoBuilder<'_> {\n    fn drop(&mut self) {\n        unsafe { LLVMDisposeDIBuilder(self.llvm_dib) }\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/fun_gen.rs",
    "content": "use std::collections::HashMap;\nuse std::ffi;\n\nuse llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::LLVMCallConv;\n\nuse crate::mir::ops;\n\nuse crate::codegen::analysis::escape::{CaptureKind, Captures};\nuse crate::codegen::mod_gen::ModCtx;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::codegen::GenAbi;\nuse crate::libcstr;\n\npub(crate) struct FunCtx {\n    pub regs: HashMap<ops::RegId, LLVMValueRef>,\n\n    pub function: LLVMValueRef,\n    pub builder: LLVMBuilderRef,\n    pub current_task: LLVMValueRef,\n}\n\nimpl FunCtx {\n    pub(crate) fn new(\n        function: LLVMValueRef,\n        builder: LLVMBuilderRef,\n        current_task: LLVMValueRef,\n    ) -> FunCtx {\n        FunCtx {\n            regs: HashMap::new(),\n\n            function,\n            builder,\n            current_task,\n        }\n    }\n}\n\nimpl Drop for FunCtx {\n    fn drop(&mut self) {\n        unsafe {\n            LLVMDisposeBuilder(self.builder);\n        }\n    }\n}\n\npub(crate) fn declare_fun(\n    tcx: &mut TargetCtx,\n    llvm_module: LLVMModuleRef,\n    fun: &ops::Fun,\n) -> LLVMValueRef {\n    let gen_abi: GenAbi = (&fun.abi).into();\n    let function_type = tcx.fun_abi_to_llvm_type(&gen_abi);\n\n    let fun_symbol = fun\n        .source_name\n        .as_ref()\n        .map(|source_name| ffi::CString::new(source_name.as_bytes()).unwrap())\n        .unwrap_or_else(|| ffi::CString::new(\"anon_fun\").unwrap());\n\n    unsafe {\n        let llvm_fun = LLVMAddFunction(llvm_module, fun_symbol.as_ptr() as *const _, function_type);\n\n        let llvm_call_conv = match fun.abi.call_conv {\n            ops::CallConv::Ccc => LLVMCallConv::LLVMCCallConv,\n            ops::CallConv::FastCc => LLVMCallConv::LLVMFastCallConv,\n        };\n        LLVMSetFunctionCallConv(llvm_fun, llvm_call_conv as u32);\n\n        llvm_fun\n    }\n}\n\npub(crate) fn define_fun(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fun: &ops::Fun,\n    captures: &Captures,\n    llvm_fun: LLVMValueRef,\n) {\n    use crate::codegen::alloc::plan::plan_allocs;\n    use crate::codegen::op_gen;\n    use arret_runtime::abitype::{AbiType, RetAbiType};\n\n    let alloc_plan = plan_allocs(tcx, captures, &fun.ops);\n\n    unsafe {\n        let builder = LLVMCreateBuilderInContext(tcx.llx);\n        let bb = LLVMAppendBasicBlockInContext(tcx.llx, llvm_fun, libcstr!(\"entry\"));\n        LLVMPositionBuilderAtEnd(builder, bb);\n\n        let mut fcx = FunCtx::new(llvm_fun, builder, LLVMGetParam(llvm_fun, 0));\n        fcx.regs.reserve(fun.param_regs.len());\n\n        for (param_index, (reg, param_abi_type)) in\n            fun.param_regs.iter().zip(fun.abi.params.iter()).enumerate()\n        {\n            // Our implicit task param shifts our params by 1\n            let llvm_offset = (1 + param_index) as u32;\n            fcx.regs.insert(*reg, LLVMGetParam(llvm_fun, llvm_offset));\n\n            if let AbiType::Boxed(_) = param_abi_type {\n                let no_capture = captures.get(*reg) == CaptureKind::Never;\n                tcx.add_boxed_param_attrs(llvm_fun, llvm_offset, no_capture);\n            }\n        }\n\n        if let RetAbiType::Inhabited(AbiType::Boxed(_)) = fun.abi.ret {\n            tcx.add_boxed_return_attrs(llvm_fun);\n        }\n\n        for alloc_atom in alloc_plan {\n            op_gen::gen_alloc_atom(tcx, mcx, &mut fcx, alloc_atom);\n        }\n\n        mcx.optimise_function(llvm_fun);\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/jit.rs",
    "content": "use std::collections::HashMap;\nuse std::{alloc, env, ffi, io, ptr};\n\nuse llvm_sys::core::*;\nuse llvm_sys::execution_engine::*;\nuse llvm_sys::orc::*;\nuse llvm_sys::target_machine::*;\n\nuse arret_runtime::boxed;\nuse arret_runtime::class_map::ClassMap;\nuse arret_runtime::intern::Interner;\n\nuse crate::codegen::analysis::AnalysedMod;\nuse crate::codegen::mod_gen::{gen_mod, GeneratedMod};\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::mir::ops;\nuse crate::mir::printer::print_fun;\n\nextern \"C\" fn orc_sym_resolve(name_ptr: *const libc::c_char, jcx_void: *mut libc::c_void) -> u64 {\n    unsafe {\n        let jcx: &JitCtx = &*(jcx_void as *mut _);\n        let name = ffi::CStr::from_ptr(name_ptr);\n        jcx.symbols\n            .get(name)\n            .cloned()\n            .unwrap_or_else(|| panic!(\"unable to lookup symbol {:?}\", name))\n    }\n}\n\npub struct JitCtx {\n    tcx: TargetCtx,\n    orc: LLVMOrcJITStackRef,\n    target_machine: LLVMTargetMachineRef,\n    symbols: HashMap<ffi::CString, u64>,\n    record_struct_class_ids: HashMap<ops::RecordStructId, boxed::RecordClassId>,\n\n    module_counter: usize,\n}\n\npub struct RegisteredRecordStruct {\n    /// Allocation layout of the record's data\n    pub data_layout: Option<alloc::Layout>,\n    /// Record class ID that was dynamically registered in the class map\n    pub record_class_id: boxed::RecordClassId,\n}\n\nimpl JitCtx {\n    pub fn new(optimising: bool) -> JitCtx {\n        #[allow(clippy::fn_to_numeric_cast)]\n        unsafe {\n            use crate::codegen::target_machine::create_target_machine;\n            use arret_runtime::compiler_support;\n\n            LLVMLinkInMCJIT();\n\n            let target_machine = create_target_machine(\n                None, // Can't cross compile in the JIT\n                LLVMRelocMode::LLVMRelocDefault,\n                LLVMCodeModel::LLVMCodeModelJITDefault,\n            );\n            let orc = LLVMOrcCreateInstance(target_machine);\n\n            let mut jcx = JitCtx {\n                tcx: TargetCtx::new(target_machine, optimising),\n                orc,\n                target_machine,\n                symbols: HashMap::new(),\n                record_struct_class_ids: HashMap::new(),\n\n                module_counter: 0,\n            };\n\n            jcx.add_symbol(b\"ARRET_TRUE\\0\", &boxed::TRUE_INSTANCE as *const _ as u64);\n            jcx.add_symbol(b\"ARRET_FALSE\\0\", &boxed::FALSE_INSTANCE as *const _ as u64);\n            jcx.add_symbol(b\"ARRET_NIL\\0\", &boxed::NIL_INSTANCE as *const _ as u64);\n            jcx.add_symbol(\n                b\"arret_runtime_alloc_cells\\0\",\n                compiler_support::alloc_cells as u64,\n            );\n            jcx.add_symbol(\n                b\"arret_runtime_alloc_record_data\\0\",\n                compiler_support::alloc_record_data as u64,\n            );\n            jcx.add_symbol(b\"arret_runtime_equals\\0\", compiler_support::equals as u64);\n            jcx.add_symbol(\n                b\"arret_runtime_panic_with_string\\0\",\n                compiler_support::panic_with_string as u64,\n            );\n\n            jcx\n        }\n    }\n\n    pub fn compile_fun(\n        &mut self,\n        private_funs: &HashMap<ops::PrivateFunId, ops::Fun>,\n        interner: &mut Interner,\n        fun: &ops::Fun,\n    ) -> u64 {\n        if env::var_os(\"ARRET_DUMP_MIR\").is_some() {\n            print_fun(&mut io::stdout().lock(), private_funs, fun, None).unwrap();\n        }\n\n        let tcx = &mut self.tcx;\n\n        self.module_counter += 1;\n\n        let module_counter = self.module_counter;\n        let module_name = fun\n            .source_name\n            .as_ref()\n            .map(|source_name| format!(\"JIT Module #{} for `{}`\\0\", module_counter, source_name))\n            .unwrap_or_else(|| format!(\"Anonymous JIT Module #{}\\0\", module_counter));\n\n        // Create the module\n        let analysed_mod = AnalysedMod::new(private_funs, fun);\n\n        unsafe {\n            // Generate our Arret funs\n            let GeneratedMod {\n                llvm_module,\n                llvm_entry_fun,\n                ..\n            } = gen_mod(\n                tcx,\n                module_name.as_bytes(),\n                &analysed_mod,\n                Some(interner),\n                self.record_struct_class_ids.clone(),\n                None,\n            );\n\n            // We need to take ownership before we transfer the module to ORC\n            let mut function_name_len: usize = 0;\n            let function_name_ptr = LLVMGetValueName2(llvm_entry_fun, &mut function_name_len);\n            let function_name = ffi::CStr::from_ptr(function_name_ptr).to_owned();\n\n            tcx.finish_module(llvm_module);\n\n            let mut orc_module: LLVMOrcModuleHandle = 0;\n            if !LLVMOrcAddEagerlyCompiledIR(\n                self.orc,\n                &mut orc_module,\n                llvm_module,\n                Some(orc_sym_resolve),\n                self as *mut JitCtx as *mut _,\n            )\n            .is_null()\n            {\n                panic!(\"Unable to add module\");\n            }\n\n            let mut target_address: LLVMOrcTargetAddress = 0;\n            if !LLVMOrcGetSymbolAddressIn(\n                self.orc,\n                &mut target_address,\n                orc_module,\n                function_name.as_ptr() as *const _,\n            )\n            .is_null()\n            {\n                panic!(\"Unable to get symbol address\")\n            }\n\n            target_address\n        }\n    }\n\n    pub fn add_symbol(&mut self, unmangled_name: &[u8], address: u64) {\n        unsafe {\n            let mut mangled_pointer: *mut libc::c_char = ptr::null_mut();\n            LLVMOrcGetMangledSymbol(\n                self.orc,\n                &mut mangled_pointer,\n                unmangled_name.as_ptr() as *const _,\n            );\n\n            let mangled_string = ffi::CStr::from_ptr(mangled_pointer);\n            self.symbols.insert(mangled_string.to_owned(), address);\n\n            LLVMOrcDisposeMangledSymbol(mangled_pointer);\n        }\n    }\n\n    pub fn register_record_struct(\n        &mut self,\n        record_struct: &ops::RecordStructId,\n        class_map: &mut ClassMap,\n    ) -> RegisteredRecordStruct {\n        let target_record_struct = self.tcx.target_record_struct(record_struct);\n        let record_class_id =\n            class_map.push_dynamic_class(target_record_struct.classmap_class.clone());\n\n        self.record_struct_class_ids\n            .insert(record_struct.clone(), record_class_id);\n\n        RegisteredRecordStruct {\n            data_layout: target_record_struct.data_layout,\n            record_class_id,\n        }\n    }\n}\n\nimpl Drop for JitCtx {\n    fn drop(&mut self) {\n        unsafe {\n            LLVMDisposeTargetMachine(self.target_machine);\n            LLVMOrcDisposeInstance(self.orc);\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/libcstr.rs",
    "content": "/// Builds a static NULL terminated `*const libc::c_char` with the given contents\n#[macro_export]\nmacro_rules! libcstr {\n    ($s:expr) => {\n        concat!($s, \"\\0\").as_ptr() as *const libc::c_char\n    };\n}\n"
  },
  {
    "path": "compiler/codegen/math_gen.rs",
    "content": "use llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::LLVMIntPredicate;\n\nuse crate::codegen::fun_gen::FunCtx;\nuse crate::codegen::mod_gen::ModCtx;\nuse crate::codegen::panic_gen::gen_panic;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::libcstr;\n\npub struct CheckedIntOp {\n    math_intrinsic_name: &'static [u8],\n    result_name: &'static [u8],\n    panic_message: &'static str,\n}\n\npub const CHECKED_ADD: CheckedIntOp = CheckedIntOp {\n    math_intrinsic_name: b\"llvm.sadd.with.overflow.i64\\0\",\n    result_name: b\"sum\\0\",\n    panic_message: \"attempt to add with overflow\",\n};\n\npub const CHECKED_SUB: CheckedIntOp = CheckedIntOp {\n    math_intrinsic_name: b\"llvm.ssub.with.overflow.i64\\0\",\n    result_name: b\"difference\\0\",\n    panic_message: \"attempt to subtract with overflow\",\n};\n\npub const CHECKED_MUL: CheckedIntOp = CheckedIntOp {\n    math_intrinsic_name: b\"llvm.smul.with.overflow.i64\\0\",\n    result_name: b\"product\\0\",\n    panic_message: \"attempt to multiply with overflow\",\n};\n\npub(crate) fn gen_checked_int_math(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fcx: &mut FunCtx,\n    int_op: &'static CheckedIntOp,\n    llvm_lhs: LLVMValueRef,\n    llvm_rhs: LLVMValueRef,\n) -> LLVMValueRef {\n    let CheckedIntOp {\n        math_intrinsic_name,\n        result_name,\n        panic_message,\n    } = int_op;\n\n    unsafe {\n        let llvm_i1 = LLVMInt1TypeInContext(tcx.llx);\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n        let mut return_type_members = [llvm_i64, llvm_i1];\n\n        let llvm_return_type = LLVMStructTypeInContext(\n            tcx.llx,\n            return_type_members.as_mut_ptr(),\n            return_type_members.len() as u32,\n            0,\n        );\n\n        let llvm_param_types = &mut [llvm_i64, llvm_i64];\n\n        let math_intrinsic_llvm_type = LLVMFunctionType(\n            llvm_return_type,\n            llvm_param_types.as_mut_ptr(),\n            llvm_param_types.len() as u32,\n            0,\n        );\n\n        let math_intrinsic_fun =\n            mcx.get_function_or_insert(math_intrinsic_llvm_type, math_intrinsic_name, |_| {});\n\n        let math_intrinsic_args = &mut [llvm_lhs, llvm_rhs];\n\n        let llvm_result_with_overflow = LLVMBuildCall(\n            fcx.builder,\n            math_intrinsic_fun,\n            math_intrinsic_args.as_mut_ptr(),\n            math_intrinsic_args.len() as u32,\n            libcstr!(\"result_with_overflow\"),\n        );\n\n        let llvm_math_result = LLVMBuildExtractValue(\n            fcx.builder,\n            llvm_result_with_overflow,\n            0,\n            result_name.as_ptr() as *const _,\n        );\n\n        let llvm_overflow = LLVMBuildExtractValue(\n            fcx.builder,\n            llvm_result_with_overflow,\n            1,\n            libcstr!(\"overflow_flag\"),\n        );\n\n        let overflow_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"overflow\"));\n\n        let cont_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"no_overflow\"));\n\n        LLVMBuildCondBr(fcx.builder, llvm_overflow, overflow_block, cont_block);\n\n        LLVMPositionBuilderAtEnd(fcx.builder, overflow_block);\n        gen_panic(tcx, mcx, fcx, panic_message);\n\n        LLVMPositionBuilderAtEnd(fcx.builder, cont_block);\n        llvm_math_result\n    }\n}\n\npub(crate) fn gen_checked_int_rem(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fcx: &mut FunCtx,\n    llvm_numer: LLVMValueRef,\n    llvm_denom: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n        let denom_is_zero = LLVMBuildICmp(\n            fcx.builder,\n            LLVMIntPredicate::LLVMIntEQ,\n            llvm_denom,\n            LLVMConstInt(llvm_i64, 0, 0),\n            libcstr!(\"denom_is_zero\"),\n        );\n\n        let rem_by_zero_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"rem_by_zero\"));\n\n        let valid_rem_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"valid_rem\"));\n\n        LLVMBuildCondBr(\n            fcx.builder,\n            denom_is_zero,\n            rem_by_zero_block,\n            valid_rem_block,\n        );\n\n        LLVMPositionBuilderAtEnd(fcx.builder, rem_by_zero_block);\n        gen_panic(tcx, mcx, fcx, \"division by zero\");\n\n        LLVMPositionBuilderAtEnd(fcx.builder, valid_rem_block);\n        LLVMBuildSRem(fcx.builder, llvm_numer, llvm_denom, libcstr!(\"rem\"))\n    }\n}\n\npub(crate) fn gen_checked_int_div(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fcx: &mut FunCtx,\n    llvm_numer: LLVMValueRef,\n    llvm_denom: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n        // Build our blocks\n        let div_by_zero_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"div_by_zero\"));\n\n        let non_zero_denom_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"non_zero_denom\"));\n\n        let neg_one_denom_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"neg_one_denom\"));\n\n        let valid_div_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"valid_div_block\"));\n\n        // Test if the denominator is 0\n        let denom_is_zero = LLVMBuildICmp(\n            fcx.builder,\n            LLVMIntPredicate::LLVMIntEQ,\n            llvm_denom,\n            LLVMConstInt(llvm_i64, 0, 0),\n            libcstr!(\"denom_is_zero\"),\n        );\n\n        // If the denominator 0 then raise a divide by zero error\n        LLVMBuildCondBr(\n            fcx.builder,\n            denom_is_zero,\n            div_by_zero_block,\n            non_zero_denom_block,\n        );\n\n        // Test if the denominator is -1\n        LLVMPositionBuilderAtEnd(fcx.builder, non_zero_denom_block);\n\n        let denom_is_neg_one = LLVMBuildICmp(\n            fcx.builder,\n            LLVMIntPredicate::LLVMIntEQ,\n            llvm_denom,\n            LLVMConstInt(llvm_i64, std::mem::transmute(-1i64), 0),\n            libcstr!(\"denom_is_neg_one\"),\n        );\n\n        // If the denominator in -1 then we need to test the numerator\n        LLVMBuildCondBr(\n            fcx.builder,\n            denom_is_neg_one,\n            neg_one_denom_block,\n            valid_div_block,\n        );\n\n        // Test if the numerator is i64::MIN\n        LLVMPositionBuilderAtEnd(fcx.builder, neg_one_denom_block);\n\n        let numer_is_int_min = LLVMBuildICmp(\n            fcx.builder,\n            LLVMIntPredicate::LLVMIntEQ,\n            llvm_numer,\n            LLVMConstInt(llvm_i64, std::mem::transmute(std::i64::MIN), 0),\n            libcstr!(\"numer_is_int_min\"),\n        );\n\n        // If denominator is -1 and numerator is i64::MIN then raise a divide by zero error\n        LLVMBuildCondBr(\n            fcx.builder,\n            numer_is_int_min,\n            div_by_zero_block,\n            valid_div_block,\n        );\n\n        // Build the common panic block\n        LLVMPositionBuilderAtEnd(fcx.builder, div_by_zero_block);\n        gen_panic(tcx, mcx, fcx, \"division by zero\");\n\n        LLVMPositionBuilderAtEnd(fcx.builder, valid_div_block);\n        LLVMBuildSDiv(fcx.builder, llvm_numer, llvm_denom, libcstr!(\"quot\"))\n    }\n}\n\npub(crate) fn gen_float_sqrt(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fcx: &mut FunCtx,\n    llvm_radicand: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let llvm_double = LLVMDoubleTypeInContext(tcx.llx);\n\n        let llvm_param_types = &mut [llvm_double];\n\n        let double_sqrt_llvm_type = LLVMFunctionType(\n            llvm_double,\n            llvm_param_types.as_mut_ptr(),\n            llvm_param_types.len() as u32,\n            0,\n        );\n\n        let double_sqrt_fun =\n            mcx.get_function_or_insert(double_sqrt_llvm_type, b\"llvm.sqrt.f64\\0\", |_| {});\n\n        let llvm_sqrt_args = &mut [llvm_radicand];\n\n        LLVMBuildCall(\n            fcx.builder,\n            double_sqrt_fun,\n            llvm_sqrt_args.as_mut_ptr(),\n            llvm_sqrt_args.len() as u32,\n            libcstr!(\"sqrt\"),\n        )\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/mod.rs",
    "content": "mod alloc;\nmod analysis;\nmod box_layout;\nmod callee;\nmod const_gen;\nmod debug_info;\nmod fun_gen;\npub(crate) mod jit;\nmod libcstr;\nmod math_gen;\nmod mod_gen;\nmod op_gen;\nmod panic_gen;\npub(crate) mod program;\nmod range_md;\nmod record_struct;\npub(crate) mod target_gen;\nmod target_machine;\nmod vector_gen;\n\nuse crate::mir::ops::OpsAbi;\nuse arret_runtime::abitype;\n\n#[derive(Debug, PartialEq, Clone)]\npub struct GenAbi {\n    pub takes_task: bool,\n    pub params: Box<[abitype::ParamAbiType]>,\n    pub ret: abitype::RetAbiType,\n}\n\nimpl GenAbi {\n    pub fn thunk_abi() -> GenAbi {\n        GenAbi {\n            takes_task: true,\n            params: Box::new([\n                abitype::BoxedAbiType::Any.into(),\n                abitype::TOP_LIST_BOXED_ABI_TYPE.into(),\n            ]),\n            ret: abitype::BoxedAbiType::Any.into(),\n        }\n    }\n}\n\nimpl<'a> From<&'a OpsAbi> for GenAbi {\n    fn from(ops_abi: &'a OpsAbi) -> GenAbi {\n        GenAbi {\n            takes_task: true,\n            params: ops_abi\n                .params\n                .iter()\n                .map(|abi_type| abi_type.clone().into())\n                .collect(),\n            ret: ops_abi.ret.clone(),\n        }\n    }\n}\n\n/// Initialises LLVM\n///\n/// This must be called before anything else in this module. It can only be called from a single\n/// thread at once.\npub fn initialise_llvm(support_cross_compilation: bool) {\n    use llvm_sys::target::*;\n\n    unsafe {\n        if support_cross_compilation {\n            LLVM_InitializeAllTargetInfos();\n            LLVM_InitializeAllTargets();\n            LLVM_InitializeAllTargetMCs();\n            LLVM_InitializeAllAsmPrinters();\n        } else {\n            LLVM_InitializeNativeTarget();\n            LLVM_InitializeNativeAsmPrinter();\n        }\n    }\n}\n\n#[cfg(test)]\npub(crate) mod test {\n    use super::*;\n    use std::sync::Once;\n\n    static INITIALISE_TEST_LLVM: Once = Once::new();\n\n    pub fn initialise_test_llvm() {\n        INITIALISE_TEST_LLVM.call_once(|| {\n            initialise_llvm(false);\n        });\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/mod_gen.rs",
    "content": "use std::collections::HashMap;\n\nuse llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::target::*;\nuse llvm_sys::target_machine::*;\nuse llvm_sys::LLVMLinkage;\n\nuse arret_runtime::boxed::RecordClassId;\nuse arret_runtime::intern;\n\nuse crate::codegen::analysis::AnalysedMod;\nuse crate::codegen::debug_info::DebugInfoBuilder;\nuse crate::codegen::record_struct;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::mir::ops;\nuse crate::source::SourceLoader;\n\npub struct ModCtx<'am, 'sl, 'interner> {\n    pub module: LLVMModuleRef,\n\n    analysed_mod: &'am AnalysedMod<'am>,\n    di_builder: Option<DebugInfoBuilder<'sl>>,\n    llvm_private_funs: HashMap<ops::PrivateFunId, LLVMValueRef>,\n\n    jit_interner: Option<&'interner mut intern::Interner>,\n\n    has_jit_record_struct_class_ids: bool,\n    record_struct_class_ids: HashMap<ops::RecordStructId, RecordClassId>,\n    record_structs: Vec<ops::RecordStructId>,\n\n    record_class_id_llvm_values: Vec<LLVMValueRef>,\n\n    function_pass_manager: LLVMPassManagerRef,\n}\n\npub struct GeneratedMod {\n    pub llvm_module: LLVMModuleRef,\n    pub llvm_entry_fun: LLVMValueRef,\n    pub llvm_global_interned_names: LLVMValueRef,\n    pub llvm_classmap_classes: LLVMValueRef,\n}\n\nimpl<'am, 'sl, 'interner> ModCtx<'am, 'sl, 'interner> {\n    /// Constructs a new module context with the given name\n    ///\n    /// Note that the module name in LLVM is not arbitrary. For instance, in the ORC JIT it will\n    /// shadow exported symbol names. This identifier should be as unique and descriptive as\n    /// possible.\n    fn new(\n        tcx: &mut TargetCtx,\n        name: &[u8],\n        analysed_mod: &'am AnalysedMod<'am>,\n        jit_interner: Option<&'interner mut intern::Interner>,\n        jit_record_struct_class_ids: HashMap<ops::RecordStructId, RecordClassId>,\n        debug_source_loader: Option<&'sl SourceLoader>,\n    ) -> Self {\n        use crate::codegen::fun_gen::declare_fun;\n        use llvm_sys::transforms::pass_manager_builder::*;\n\n        // Hoist these out of the unsafe block\n        let module;\n        let function_pass_manager;\n        unsafe {\n            module = LLVMModuleCreateWithNameInContext(name.as_ptr() as *const _, tcx.llx);\n            LLVMSetModuleDataLayout(module, tcx.target_data());\n\n            let target_triple = LLVMGetTargetMachineTriple(tcx.target_machine());\n            LLVMSetTarget(module, target_triple);\n            LLVMDisposeMessage(target_triple);\n\n            function_pass_manager = LLVMCreateFunctionPassManagerForModule(module);\n\n            if tcx.optimising() {\n                let fpmb = LLVMPassManagerBuilderCreate();\n                LLVMPassManagerBuilderSetOptLevel(fpmb, 2);\n                LLVMPassManagerBuilderPopulateFunctionPassManager(fpmb, function_pass_manager);\n                LLVMPassManagerBuilderDispose(fpmb);\n            }\n        }\n\n        let di_builder = debug_source_loader.map(|source_loader| {\n            DebugInfoBuilder::new(\n                source_loader,\n                tcx.optimising(),\n                analysed_mod.entry_fun().ops_fun.span,\n                module,\n            )\n        });\n\n        // Forward declare all our private funs\n        // Analysis has determined all of these are used\n        let llvm_private_funs = analysed_mod\n            .private_funs()\n            .map(|(private_fun_id, analysed_fun)| {\n                let llvm_fun = declare_fun(tcx, module, analysed_fun.ops_fun);\n                (*private_fun_id, llvm_fun)\n            })\n            .collect();\n\n        ModCtx {\n            module,\n\n            analysed_mod,\n            di_builder,\n            llvm_private_funs,\n\n            jit_interner,\n\n            has_jit_record_struct_class_ids: !jit_record_struct_class_ids.is_empty(),\n            record_struct_class_ids: jit_record_struct_class_ids,\n            record_structs: vec![],\n            record_class_id_llvm_values: vec![],\n\n            function_pass_manager,\n        }\n    }\n\n    pub fn intern_name(&mut self, name: &str) -> intern::InternedSym {\n        if let Some(ref mut jit_interner) = self.jit_interner {\n            jit_interner.intern_static(name)\n        } else if let Some(interned_sym) = intern::InternedSym::try_from_inline_name(name) {\n            interned_sym\n        } else {\n            *self\n                .analysed_mod\n                .global_interned_names()\n                .get(name)\n                .expect(\"encountered name not found during analysis\")\n        }\n    }\n\n    pub fn record_class_id_for_struct(\n        &mut self,\n        record_struct: &ops::RecordStructId,\n    ) -> RecordClassId {\n        if let Some(record_class_id) = self.record_struct_class_ids.get(record_struct) {\n            return *record_class_id;\n        }\n\n        let record_class_id = self.record_structs.len() as u32;\n        self.record_structs.push(record_struct.clone());\n\n        self.record_struct_class_ids\n            .insert(record_struct.clone(), record_class_id);\n        record_class_id\n    }\n\n    pub fn add_record_class_id_range_metadata(&mut self, record_class_id_llvm_value: LLVMValueRef) {\n        // This is a bit of a hack - we don't know the range of the record class IDs until we\n        // finish generating the module.\n        self.record_class_id_llvm_values\n            .push(record_class_id_llvm_value);\n    }\n\n    pub fn llvm_private_fun(&self, private_fun_id: ops::PrivateFunId) -> LLVMValueRef {\n        self.llvm_private_funs[&private_fun_id]\n    }\n\n    pub fn get_global_or_insert<F>(\n        &mut self,\n        llvm_type: LLVMTypeRef,\n        name: &[u8],\n        initial_value: F,\n    ) -> LLVMValueRef\n    where\n        F: FnOnce() -> LLVMValueRef,\n    {\n        unsafe {\n            let global = LLVMGetNamedGlobal(self.module, name.as_ptr() as *const _);\n\n            if !global.is_null() {\n                return global;\n            }\n\n            let global = LLVMAddGlobal(self.module, llvm_type, name.as_ptr() as *const _);\n            LLVMSetInitializer(global, initial_value());\n\n            global\n        }\n    }\n\n    pub fn get_function_or_insert<F>(\n        &mut self,\n        function_type: LLVMTypeRef,\n        name: &[u8],\n        initialise: F,\n    ) -> LLVMValueRef\n    where\n        F: FnOnce(LLVMValueRef),\n    {\n        unsafe {\n            let function = LLVMGetNamedFunction(self.module, name.as_ptr() as *const _);\n\n            if !function.is_null() {\n                return function;\n            }\n\n            let function = LLVMAddFunction(self.module, name.as_ptr() as *const _, function_type);\n\n            initialise(function);\n            function\n        }\n    }\n\n    pub fn optimise_function(&mut self, function: LLVMValueRef) {\n        unsafe {\n            LLVMRunFunctionPassManager(self.function_pass_manager, function);\n        }\n    }\n\n    fn finalise_record_class_id_range_metadata(&mut self, tcx: &mut TargetCtx) {\n        unsafe {\n            if self.has_jit_record_struct_class_ids {\n                // These are from a distinct range; it's too much effort to include them in the JIT\n                // case so just skip generating metadata.\n                return;\n            }\n\n            let mut llvm_range_values = [\n                LLVMValueAsMetadata(LLVMConstInt(tcx.record_class_id_llvm_type(), 0, 0)),\n                LLVMValueAsMetadata(LLVMConstInt(\n                    tcx.record_class_id_llvm_type(),\n                    self.record_structs.len() as u64,\n                    0,\n                )),\n            ];\n\n            let range_md_kind_id = tcx.llvm_md_kind_id_for_name(\"range\");\n            let record_class_id_range_md = LLVMMDNodeInContext2(\n                tcx.llx,\n                llvm_range_values.as_mut_ptr(),\n                llvm_range_values.len(),\n            );\n\n            for llvm_value in self.record_class_id_llvm_values.iter() {\n                LLVMSetMetadata(\n                    *llvm_value,\n                    range_md_kind_id,\n                    LLVMMetadataAsValue(tcx.llx, record_class_id_range_md),\n                );\n            }\n        }\n    }\n\n    /// Finalise the module and return the LLVMModuleRef\n    ///\n    /// This will verify the module's correctness and dump the LLVM IR to stdout if the\n    /// `ARRET_DUMP_LLVM` environment variable is set\n    fn into_generated_mod(mut self, tcx: &mut TargetCtx) -> GeneratedMod {\n        use crate::codegen::analysis::AnalysedFun;\n        use crate::codegen::const_gen::gen_global_interned_names;\n        use crate::codegen::fun_gen::{declare_fun, define_fun};\n\n        // Define our entry fun\n        let AnalysedFun {\n            ops_fun: entry_ops_fun,\n            captures: entry_captures,\n        } = self.analysed_mod.entry_fun();\n\n        let llvm_entry_fun = declare_fun(tcx, self.module, entry_ops_fun);\n        define_fun(\n            tcx,\n            &mut self,\n            entry_ops_fun,\n            entry_captures,\n            llvm_entry_fun,\n        );\n\n        if let Some(ref mut di_builder) = self.di_builder {\n            di_builder.add_function_debug_info(\n                entry_ops_fun.span,\n                entry_ops_fun.source_name.as_ref(),\n                llvm_entry_fun,\n            );\n        }\n\n        // Define all of our private funs\n        for (private_fun_id, analysed_fun) in self.analysed_mod.private_funs() {\n            let AnalysedFun { ops_fun, captures } = analysed_fun;\n            let llvm_fun = self.llvm_private_funs[private_fun_id];\n\n            define_fun(tcx, &mut self, ops_fun, captures, llvm_fun);\n\n            if let Some(ref mut di_builder) = self.di_builder {\n                di_builder.add_function_debug_info(\n                    ops_fun.span,\n                    ops_fun.source_name.as_ref(),\n                    llvm_fun,\n                );\n            }\n\n            unsafe {\n                LLVMSetLinkage(llvm_fun, LLVMLinkage::LLVMPrivateLinkage);\n            }\n        }\n\n        let llvm_global_interned_names = gen_global_interned_names(\n            tcx,\n            self.module,\n            self.analysed_mod.global_interned_names().keys(),\n        );\n\n        let llvm_classmap_classes =\n            record_struct::gen_classmap_classes(tcx, self.module, &self.record_structs);\n\n        self.finalise_record_class_id_range_metadata(tcx);\n\n        if let Some(ref mut di_builder) = self.di_builder {\n            di_builder.finalise();\n        }\n\n        GeneratedMod {\n            llvm_module: self.module,\n            llvm_entry_fun,\n            llvm_global_interned_names,\n            llvm_classmap_classes,\n        }\n    }\n}\n\npub fn gen_mod<'am, 'sl, 'interner>(\n    tcx: &mut TargetCtx,\n    name: &[u8],\n    analysed_mod: &'am AnalysedMod<'am>,\n    jit_interner: Option<&'interner mut intern::Interner>,\n    jit_record_struct_class_ids: HashMap<ops::RecordStructId, RecordClassId>,\n    debug_source_loader: Option<&'sl SourceLoader>,\n) -> GeneratedMod {\n    ModCtx::new(\n        tcx,\n        name,\n        analysed_mod,\n        jit_interner,\n        jit_record_struct_class_ids,\n        debug_source_loader,\n    )\n    .into_generated_mod(tcx)\n}\n\nimpl Drop for ModCtx<'_, '_, '_> {\n    fn drop(&mut self) {\n        unsafe {\n            LLVMDisposePassManager(self.function_pass_manager);\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/op_gen.rs",
    "content": "use llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::{LLVMCallConv, LLVMIntPredicate, LLVMRealPredicate};\n\nuse arret_runtime::boxed;\n\nuse crate::mir::ops::*;\n\nuse crate::codegen::fun_gen::FunCtx;\nuse crate::codegen::math_gen;\nuse crate::codegen::mod_gen::ModCtx;\nuse crate::codegen::panic_gen::gen_panic;\nuse crate::codegen::record_struct;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::codegen::{alloc, const_gen};\nuse crate::libcstr;\n\nfn comparison_to_llvm_int_pred(comparison: Comparison) -> LLVMIntPredicate {\n    match comparison {\n        Comparison::Lt => LLVMIntPredicate::LLVMIntSLT,\n        Comparison::Le => LLVMIntPredicate::LLVMIntSLE,\n        Comparison::Eq => LLVMIntPredicate::LLVMIntEQ,\n        Comparison::Gt => LLVMIntPredicate::LLVMIntSGT,\n        Comparison::Ge => LLVMIntPredicate::LLVMIntSGE,\n    }\n}\n\nfn comparison_to_llvm_real_pred(comparison: Comparison) -> LLVMRealPredicate {\n    match comparison {\n        Comparison::Lt => LLVMRealPredicate::LLVMRealOLT,\n        Comparison::Le => LLVMRealPredicate::LLVMRealOLE,\n        Comparison::Eq => LLVMRealPredicate::LLVMRealOEQ,\n        Comparison::Gt => LLVMRealPredicate::LLVMRealOGT,\n        Comparison::Ge => LLVMRealPredicate::LLVMRealOGE,\n    }\n}\n\nfn gen_int_compare(\n    fcx: &mut FunCtx,\n    reg: RegId,\n    comparison: Comparison,\n    lhs_reg: RegId,\n    rhs_reg: RegId,\n    reg_name: &str,\n) {\n    unsafe {\n        fcx.regs.insert(\n            reg,\n            LLVMBuildICmp(\n                fcx.builder,\n                comparison_to_llvm_int_pred(comparison),\n                fcx.regs[&lhs_reg],\n                fcx.regs[&rhs_reg],\n                reg_name.as_ptr() as *const _,\n            ),\n        );\n    }\n}\n\nfn gen_op(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fcx: &mut FunCtx,\n    active_alloc: &mut alloc::ActiveAlloc<'_>,\n    op: &Op,\n) {\n    unsafe {\n        match &op.kind {\n            OpKind::ConstBoxedNil(reg, _) => {\n                let llvm_value = const_gen::gen_boxed_nil(tcx, mcx);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedTrue(reg, _) => {\n                let llvm_value =\n                    tcx.ptr_to_singleton_box(mcx.module, boxed::TypeTag::True, b\"ARRET_TRUE\\0\");\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedFalse(reg, _) => {\n                let llvm_value =\n                    tcx.ptr_to_singleton_box(mcx.module, boxed::TypeTag::False, b\"ARRET_FALSE\\0\");\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstInt64(reg, value) => {\n                let llvm_value = LLVMConstInt(LLVMInt64TypeInContext(tcx.llx), *value as u64, 1);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstFloat(reg, value) => {\n                let llvm_value = LLVMConstReal(LLVMDoubleTypeInContext(tcx.llx), *value);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstChar(reg, value) => {\n                let llvm_value = LLVMConstInt(LLVMInt32TypeInContext(tcx.llx), *value as u64, 1);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBool(reg, value) => {\n                let llvm_value = LLVMConstInt(LLVMInt1TypeInContext(tcx.llx), *value as u64, 1);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstInternedSym(reg, value) => {\n                let interned_sym = mcx.intern_name(value);\n                let llvm_value = LLVMConstInt(\n                    LLVMInt64TypeInContext(tcx.llx),\n                    interned_sym.to_raw_u64(),\n                    1,\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstTypeTag(reg, type_tag) => {\n                let llvm_value = LLVMConstInt(LLVMInt8TypeInContext(tcx.llx), *type_tag as u64, 1);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstRecordClassId(reg, record_struct) => {\n                let record_class_id = mcx.record_class_id_for_struct(record_struct);\n                let llvm_value = LLVMConstInt(\n                    tcx.record_class_id_llvm_type(),\n                    u64::from(record_class_id),\n                    1,\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedInt(reg, value) => {\n                let llvm_value = const_gen::gen_boxed_int(tcx, mcx, *value);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedFloat(reg, value) => {\n                let llvm_value = const_gen::gen_boxed_float(tcx, mcx, *value);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedChar(reg, value) => {\n                let llvm_value = const_gen::gen_boxed_char(tcx, mcx, *value);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedFunThunk(\n                reg,\n                BoxFunThunkOp {\n                    captures_reg,\n                    callee,\n                },\n            ) => {\n                let llvm_entry_point = gen_callee_entry_point(tcx, mcx, fcx, callee);\n                let llvm_env = fcx.regs[captures_reg];\n\n                let llvm_value =\n                    const_gen::gen_boxed_fun_thunk(tcx, mcx, llvm_env, llvm_entry_point);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedPair(\n                reg,\n                BoxPairOp {\n                    head_reg,\n                    rest_reg,\n                    list_len_reg,\n                },\n            ) => {\n                let llvm_head = fcx.regs[head_reg];\n                let llvm_rest = fcx.regs[rest_reg];\n                let llvm_list_len = fcx.regs[list_len_reg];\n\n                let llvm_value =\n                    const_gen::gen_boxed_pair(tcx, mcx, llvm_head, llvm_rest, llvm_list_len);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedStr(reg, value) => {\n                let llvm_value = const_gen::gen_boxed_str(tcx, mcx, value.as_ref());\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedSym(reg, value) => {\n                let llvm_value = const_gen::gen_boxed_sym(tcx, mcx, value.as_ref());\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedVector(reg, elements) => {\n                let llvm_value = const_gen::gen_boxed_vector(\n                    tcx,\n                    mcx,\n                    elements.iter().map(|element| fcx.regs[element]),\n                );\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedSet(reg, elements) => {\n                let llvm_value = const_gen::gen_boxed_set(\n                    tcx,\n                    mcx,\n                    elements.iter().map(|element| fcx.regs[element]),\n                );\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstBoxedMap(reg, entries) => {\n                let llvm_value = const_gen::gen_boxed_map(\n                    tcx,\n                    mcx,\n                    entries\n                        .iter()\n                        .map(|(key, value)| (fcx.regs[key], fcx.regs[value])),\n                );\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::ConstCastBoxed(reg, CastBoxedOp { from_reg, to_type }) => {\n                let from_llvm_value = fcx.regs[from_reg];\n                let to_llvm_type = tcx.boxed_abi_to_llvm_ptr_type(to_type);\n                let to_llvm_value = LLVMConstBitCast(from_llvm_value, to_llvm_type);\n\n                fcx.regs.insert(*reg, to_llvm_value);\n            }\n            OpKind::CastBoxed(reg, CastBoxedOp { from_reg, to_type }) => {\n                let from_llvm_value = fcx.regs[from_reg];\n                let to_llvm_type = tcx.boxed_abi_to_llvm_ptr_type(to_type);\n\n                let to_llvm_value = LLVMBuildBitCast(\n                    fcx.builder,\n                    from_llvm_value,\n                    to_llvm_type,\n                    libcstr!(\"box_bitcast\"),\n                );\n                fcx.regs.insert(*reg, to_llvm_value);\n            }\n            OpKind::Alias(reg, from_reg) => {\n                let from_llvm_value = fcx.regs[from_reg];\n                fcx.regs.insert(*reg, from_llvm_value);\n            }\n            OpKind::Call(reg, CallOp { callee, args, .. }) => {\n                use crate::codegen::callee;\n\n                let llvm_fun = gen_callee_entry_point(tcx, mcx, fcx, callee);\n                let takes_task = callee::callee_takes_task(callee);\n\n                let task_reg_iter = Some(fcx.current_task).filter(|_| takes_task).into_iter();\n                let mut llvm_args = task_reg_iter\n                    .chain(args.iter().map(|param_reg| fcx.regs[param_reg]))\n                    .collect::<Vec<LLVMValueRef>>();\n\n                let llvm_ret = LLVMBuildCall(\n                    fcx.builder,\n                    llvm_fun,\n                    llvm_args.as_mut_ptr(),\n                    llvm_args.len() as u32,\n                    libcstr!(\"\"),\n                );\n\n                let call_conv = callee::callee_call_conv(mcx, callee);\n                LLVMSetInstructionCallConv(llvm_ret, call_conv);\n\n                fcx.regs.insert(*reg, llvm_ret);\n            }\n            OpKind::TailCall(reg, TailCallOp { args, .. }) => {\n                let mut llvm_args = std::iter::once(fcx.current_task)\n                    .chain(args.iter().map(|param_reg| fcx.regs[param_reg]))\n                    .collect::<Vec<LLVMValueRef>>();\n\n                let llvm_ret = LLVMBuildCall(\n                    fcx.builder,\n                    fcx.function,\n                    llvm_args.as_mut_ptr(),\n                    llvm_args.len() as u32,\n                    libcstr!(\"\"),\n                );\n\n                LLVMSetTailCall(llvm_ret, 1);\n                LLVMSetInstructionCallConv(llvm_ret, LLVMCallConv::LLVMFastCallConv as u32);\n\n                fcx.regs.insert(*reg, llvm_ret);\n            }\n            OpKind::Ret(reg) => {\n                let llvm_value = fcx.regs[reg];\n                LLVMBuildRet(fcx.builder, llvm_value);\n            }\n            OpKind::RetVoid => {\n                LLVMBuildRetVoid(fcx.builder);\n            }\n            OpKind::Unreachable => {\n                LLVMBuildUnreachable(fcx.builder);\n            }\n            OpKind::Panic(message) => {\n                gen_panic(tcx, mcx, fcx, message);\n            }\n            OpKind::LoadBoxedTypeTag(\n                reg,\n                LoadBoxedTypeTagOp {\n                    subject_reg,\n                    possible_type_tags,\n                },\n            ) => {\n                use crate::codegen::range_md::int_range_md_node;\n\n                let llvm_any = fcx.regs[subject_reg];\n                let gep_indices = &mut [\n                    LLVMConstInt(LLVMInt32TypeInContext(tcx.llx), 0, 0),\n                    LLVMConstInt(LLVMInt32TypeInContext(tcx.llx), 0, 0),\n                    LLVMConstInt(LLVMInt32TypeInContext(tcx.llx), 0, 0),\n                ];\n\n                let llvm_type_tag_ptr = LLVMBuildInBoundsGEP(\n                    fcx.builder,\n                    llvm_any,\n                    gep_indices.as_mut_ptr(),\n                    gep_indices.len() as u32,\n                    libcstr!(\"type_tag_ptr\"),\n                );\n\n                let llvm_type_tag =\n                    LLVMBuildLoad(fcx.builder, llvm_type_tag_ptr, libcstr!(\"type_tag\"));\n\n                let llvm_i8 = LLVMInt8TypeInContext(tcx.llx);\n                let possible_type_tag_md = int_range_md_node(\n                    tcx.llx,\n                    llvm_i8,\n                    possible_type_tags\n                        .into_iter()\n                        .map(|type_tag| type_tag as i64),\n                );\n\n                let range_md_kind_id = tcx.llvm_md_kind_id_for_name(\"range\");\n                LLVMSetMetadata(\n                    llvm_type_tag,\n                    range_md_kind_id,\n                    LLVMMetadataAsValue(tcx.llx, possible_type_tag_md),\n                );\n\n                tcx.add_invariant_load_metadata(llvm_type_tag);\n                fcx.regs.insert(*reg, llvm_type_tag);\n            }\n            OpKind::LoadBoxedListLen(\n                reg,\n                LoadBoxedListLenOp {\n                    list_reg,\n                    min_list_len,\n                },\n            ) => {\n                let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n                let llvm_list = fcx.regs[list_reg];\n                let list_len_ptr =\n                    LLVMBuildStructGEP(fcx.builder, llvm_list, 1, libcstr!(\"list_len_ptr\"));\n\n                let llvm_list_len = LLVMBuildLoad(fcx.builder, list_len_ptr, libcstr!(\"list_len\"));\n                tcx.add_invariant_load_metadata(llvm_list_len);\n\n                // Every list element needs at least one pair. This means there's a maximum list\n                // length that can fit in our address space.\n                let max_list_len = std::u64::MAX / std::mem::size_of::<boxed::Pair>() as u64;\n\n                let mut llvm_range_values = [\n                    LLVMValueAsMetadata(LLVMConstInt(llvm_i64, *min_list_len as u64, 0)),\n                    LLVMValueAsMetadata(LLVMConstInt(llvm_i64, max_list_len + 1, 0)),\n                ];\n\n                let range_md_kind_id = tcx.llvm_md_kind_id_for_name(\"list_len_range\");\n                let list_len_range_md = LLVMMDNodeInContext2(\n                    tcx.llx,\n                    llvm_range_values.as_mut_ptr(),\n                    llvm_range_values.len(),\n                );\n                LLVMSetMetadata(\n                    llvm_list_len,\n                    range_md_kind_id,\n                    LLVMMetadataAsValue(tcx.llx, list_len_range_md),\n                );\n\n                fcx.regs.insert(*reg, llvm_list_len);\n            }\n            OpKind::LoadBoxedPairHead(reg, pair_reg) => {\n                let llvm_pair = fcx.regs[pair_reg];\n                let head_ptr = LLVMBuildStructGEP(fcx.builder, llvm_pair, 2, libcstr!(\"head_ptr\"));\n\n                let llvm_head = LLVMBuildLoad(fcx.builder, head_ptr, libcstr!(\"head\"));\n                tcx.add_invariant_load_metadata(llvm_head);\n                tcx.add_boxed_load_metadata(llvm_head);\n\n                fcx.regs.insert(*reg, llvm_head);\n            }\n            OpKind::LoadBoxedPairRest(reg, pair_reg) => {\n                let llvm_pair = fcx.regs[pair_reg];\n                let head_ptr = LLVMBuildStructGEP(fcx.builder, llvm_pair, 3, libcstr!(\"rest_ptr\"));\n\n                let llvm_rest = LLVMBuildLoad(fcx.builder, head_ptr, libcstr!(\"rest\"));\n                tcx.add_invariant_load_metadata(llvm_rest);\n                tcx.add_boxed_load_metadata(llvm_rest);\n\n                fcx.regs.insert(*reg, llvm_rest);\n            }\n            OpKind::LoadBoxedIntValue(reg, boxed_int_reg) => {\n                let llvm_boxed_int = fcx.regs[boxed_int_reg];\n                let value_ptr =\n                    LLVMBuildStructGEP(fcx.builder, llvm_boxed_int, 1, libcstr!(\"int_value_ptr\"));\n\n                let llvm_value = LLVMBuildLoad(fcx.builder, value_ptr, libcstr!(\"int_value\"));\n                tcx.add_invariant_load_metadata(llvm_value);\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::LoadBoxedSymInterned(reg, boxed_sym_reg) => {\n                let llvm_boxed_sym = fcx.regs[boxed_sym_reg];\n                let value_ptr = LLVMBuildStructGEP(\n                    fcx.builder,\n                    llvm_boxed_sym,\n                    1,\n                    libcstr!(\"interned_sym_ptr\"),\n                );\n\n                let llvm_value = LLVMBuildLoad(fcx.builder, value_ptr, libcstr!(\"interned_sym\"));\n                tcx.add_invariant_load_metadata(llvm_value);\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::LoadBoxedFloatValue(reg, boxed_float_reg) => {\n                let llvm_boxed_float = fcx.regs[boxed_float_reg];\n                let value_ptr = LLVMBuildStructGEP(\n                    fcx.builder,\n                    llvm_boxed_float,\n                    1,\n                    libcstr!(\"float_value_ptr\"),\n                );\n\n                let llvm_value = LLVMBuildLoad(fcx.builder, value_ptr, libcstr!(\"float_value\"));\n                tcx.add_invariant_load_metadata(llvm_value);\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::LoadBoxedCharValue(reg, boxed_char_reg) => {\n                let llvm_boxed_char = fcx.regs[boxed_char_reg];\n                let value_ptr =\n                    LLVMBuildStructGEP(fcx.builder, llvm_boxed_char, 1, libcstr!(\"char_value_ptr\"));\n\n                let llvm_value = LLVMBuildLoad(fcx.builder, value_ptr, libcstr!(\"char_value\"));\n\n                tcx.add_invariant_load_metadata(llvm_value);\n                tcx.add_char_codepoint_range_metadata(llvm_value);\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::LoadBoxedFunThunkCaptures(reg, boxed_fun_thunk_reg) => {\n                let llvm_boxed_fun_thunk = fcx.regs[boxed_fun_thunk_reg];\n\n                let captures_ptr = LLVMBuildStructGEP(\n                    fcx.builder,\n                    llvm_boxed_fun_thunk,\n                    1,\n                    libcstr!(\"boxed_fun_thunk_captures_ptr\"),\n                );\n                let llvm_value = LLVMBuildLoad(\n                    fcx.builder,\n                    captures_ptr,\n                    libcstr!(\"boxed_fun_thunk_captures\"),\n                );\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::LoadBoxedRecordClassId(reg, boxed_record_reg) => {\n                let llvm_boxed_record = fcx.regs[boxed_record_reg];\n                let value_ptr = LLVMBuildStructGEP(\n                    fcx.builder,\n                    llvm_boxed_record,\n                    record_struct::RECORD_CLASS_ID_INDEX,\n                    libcstr!(\"record_class_id_ptr\"),\n                );\n\n                let llvm_value = LLVMBuildLoad(fcx.builder, value_ptr, libcstr!(\"record_class_id\"));\n\n                mcx.add_record_class_id_range_metadata(llvm_value);\n                tcx.add_invariant_load_metadata(llvm_value);\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::LoadBoxedRecordField(reg, load_boxed_record_field_op) => {\n                let LoadBoxedRecordFieldOp {\n                    record_reg,\n                    record_struct,\n                    field_index,\n                } = load_boxed_record_field_op;\n\n                let record_struct::TargetRecordStruct { record_storage, .. } =\n                    *tcx.target_record_struct(record_struct);\n\n                let boxed_record_name = format!(\"boxed_{}_record\\0\", record_struct.source_name);\n\n                let boxed_record_ptr_type =\n                    LLVMPointerType(tcx.record_struct_llvm_box_type(record_struct), 0);\n\n                let llvm_boxed_record = LLVMBuildBitCast(\n                    fcx.builder,\n                    fcx.regs[record_reg],\n                    boxed_record_ptr_type,\n                    boxed_record_name.as_ptr() as *const _,\n                );\n\n                let field_ptr = record_struct::gen_record_field_ptr(\n                    tcx,\n                    fcx.builder,\n                    record_storage,\n                    llvm_boxed_record,\n                    *field_index,\n                    b\"record_field_ptr\\0\",\n                );\n\n                let llvm_value =\n                    LLVMBuildLoad(fcx.builder, field_ptr, libcstr!(\"record_field_value\"));\n                tcx.add_invariant_load_metadata(llvm_value);\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::LoadBoxedVectorLen(reg, vector_reg) => {\n                use crate::codegen::vector_gen::load_boxed_vector_len;\n\n                let llvm_boxed_vector = fcx.regs[vector_reg];\n                let llvm_vector_len = load_boxed_vector_len(tcx, fcx, llvm_boxed_vector);\n\n                fcx.regs.insert(*reg, llvm_vector_len);\n            }\n            OpKind::LoadBoxedVectorMember(\n                reg,\n                LoadBoxedVectorMemberOp {\n                    vector_reg,\n                    known_vector_len,\n                    member_index,\n                },\n            ) => {\n                use crate::codegen::vector_gen::load_boxed_vector_member;\n\n                let llvm_boxed_vector = fcx.regs[vector_reg];\n\n                let llvm_vector_member = load_boxed_vector_member(\n                    tcx,\n                    fcx,\n                    llvm_boxed_vector,\n                    *known_vector_len,\n                    *member_index,\n                );\n\n                fcx.regs.insert(*reg, llvm_vector_member);\n            }\n            OpKind::Cond(cond_op) => {\n                let cond_alloc_plan = active_alloc.next_cond_plan();\n                gen_cond(tcx, mcx, fcx, cond_op, cond_alloc_plan);\n            }\n            OpKind::AllocBoxedInt(reg, int_reg) => {\n                let box_source = active_alloc.next_box_source();\n\n                let llvm_int = fcx.regs[int_reg];\n                let llvm_alloced = alloc::types::gen_alloc_int(\n                    tcx,\n                    fcx.builder,\n                    active_alloc,\n                    box_source,\n                    llvm_int,\n                );\n\n                fcx.regs.insert(*reg, llvm_alloced);\n            }\n            OpKind::AllocBoxedFloat(reg, float_reg) => {\n                let box_source = active_alloc.next_box_source();\n\n                let llvm_float = fcx.regs[float_reg];\n                let llvm_alloced = alloc::types::gen_alloc_float(\n                    tcx,\n                    fcx.builder,\n                    active_alloc,\n                    box_source,\n                    llvm_float,\n                );\n\n                fcx.regs.insert(*reg, llvm_alloced);\n            }\n            OpKind::AllocBoxedChar(reg, char_reg) => {\n                let box_source = active_alloc.next_box_source();\n\n                let llvm_char = fcx.regs[char_reg];\n                let llvm_alloced = alloc::types::gen_alloc_char(\n                    tcx,\n                    fcx.builder,\n                    active_alloc,\n                    box_source,\n                    llvm_char,\n                );\n\n                fcx.regs.insert(*reg, llvm_alloced);\n            }\n            OpKind::AllocBoxedSym(reg, interned_sym_reg) => {\n                let box_source = active_alloc.next_box_source();\n\n                let llvm_interned_sym = fcx.regs[interned_sym_reg];\n                let llvm_alloced = alloc::types::gen_alloc_sym(\n                    tcx,\n                    fcx.builder,\n                    active_alloc,\n                    box_source,\n                    llvm_interned_sym,\n                );\n\n                fcx.regs.insert(*reg, llvm_alloced);\n            }\n            OpKind::AllocBoxedPair(\n                reg,\n                BoxPairOp {\n                    head_reg,\n                    rest_reg,\n                    list_len_reg,\n                },\n            ) => {\n                let box_source = active_alloc.next_box_source();\n\n                let input = alloc::types::PairInput {\n                    llvm_head: fcx.regs[head_reg],\n                    llvm_rest: fcx.regs[rest_reg],\n                    llvm_list_len: fcx.regs[list_len_reg],\n                };\n\n                let llvm_value = alloc::types::gen_alloc_boxed_pair(\n                    tcx,\n                    fcx.builder,\n                    active_alloc,\n                    box_source,\n                    &input,\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::AllocBoxedFunThunk(\n                reg,\n                BoxFunThunkOp {\n                    captures_reg,\n                    callee,\n                },\n            ) => {\n                let box_source = active_alloc.next_box_source();\n\n                let input = alloc::types::FunThunkInput {\n                    llvm_captures: fcx.regs[captures_reg],\n                    llvm_entry_point: gen_callee_entry_point(tcx, mcx, fcx, callee),\n                };\n\n                let llvm_value = alloc::types::gen_alloc_boxed_fun_thunk(\n                    tcx,\n                    fcx.builder,\n                    active_alloc,\n                    box_source,\n                    &input,\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::IntCompare(\n                reg,\n                CompareOp {\n                    comparison,\n                    lhs_reg,\n                    rhs_reg,\n                },\n            ) => {\n                let reg_name = if comparison == &Comparison::Eq {\n                    \"int_equal\\0\"\n                } else {\n                    \"int_compare\\0\"\n                };\n\n                gen_int_compare(fcx, *reg, *comparison, *lhs_reg, *rhs_reg, reg_name)\n            }\n            OpKind::BoolEqual(reg, BinaryOp { lhs_reg, rhs_reg }) => gen_int_compare(\n                fcx,\n                *reg,\n                Comparison::Eq,\n                *lhs_reg,\n                *rhs_reg,\n                \"bool_equal\\0\",\n            ),\n            OpKind::CharEqual(reg, BinaryOp { lhs_reg, rhs_reg }) => gen_int_compare(\n                fcx,\n                *reg,\n                Comparison::Eq,\n                *lhs_reg,\n                *rhs_reg,\n                \"char_equal\\0\",\n            ),\n            OpKind::InternedSymEqual(reg, BinaryOp { lhs_reg, rhs_reg }) => gen_int_compare(\n                fcx,\n                *reg,\n                Comparison::Eq,\n                *lhs_reg,\n                *rhs_reg,\n                \"interned_sym_equal\\0\",\n            ),\n            OpKind::TypeTagEqual(reg, BinaryOp { lhs_reg, rhs_reg }) => gen_int_compare(\n                fcx,\n                *reg,\n                Comparison::Eq,\n                *lhs_reg,\n                *rhs_reg,\n                \"type_tag_equal\\0\",\n            ),\n            OpKind::RecordClassIdEqual(reg, BinaryOp { lhs_reg, rhs_reg }) => gen_int_compare(\n                fcx,\n                *reg,\n                Comparison::Eq,\n                *lhs_reg,\n                *rhs_reg,\n                \"record_class_id_equal\\0\",\n            ),\n            OpKind::FloatCompare(\n                reg,\n                CompareOp {\n                    comparison,\n                    lhs_reg,\n                    rhs_reg,\n                },\n            ) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let reg_name = if comparison == &Comparison::Eq {\n                    \"float_equal\\0\"\n                } else {\n                    \"float_compare\\0\"\n                };\n\n                let llvm_value = LLVMBuildFCmp(\n                    fcx.builder,\n                    comparison_to_llvm_real_pred(*comparison),\n                    llvm_lhs,\n                    llvm_rhs,\n                    reg_name.as_ptr() as *const _,\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::BoxIdentical(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n                let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n                let i64_lhs =\n                    LLVMBuildPtrToInt(fcx.builder, llvm_lhs, llvm_i64, libcstr!(\"lhs_as_int\"));\n\n                let i64_rhs =\n                    LLVMBuildPtrToInt(fcx.builder, llvm_rhs, llvm_i64, libcstr!(\"rhs_as_int\"));\n\n                let llvm_value = LLVMBuildICmp(\n                    fcx.builder,\n                    LLVMIntPredicate::LLVMIntEQ,\n                    i64_lhs,\n                    i64_rhs,\n                    libcstr!(\"box_identical\"),\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64ToFloat(reg, int64_reg) => {\n                let llvm_i64 = fcx.regs[int64_reg];\n\n                let llvm_double = LLVMBuildSIToFP(\n                    fcx.builder,\n                    llvm_i64,\n                    LLVMDoubleTypeInContext(tcx.llx),\n                    libcstr!(\"i64_as_double\"),\n                );\n\n                fcx.regs.insert(*reg, llvm_double);\n            }\n            OpKind::FloatAdd(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value =\n                    LLVMBuildFAdd(fcx.builder, llvm_lhs, llvm_rhs, libcstr!(\"float_sum\"));\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64Add(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value = LLVMBuildNUWAdd(fcx.builder, llvm_lhs, llvm_rhs, libcstr!(\"sum\"));\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64CheckedAdd(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value = math_gen::gen_checked_int_math(\n                    tcx,\n                    mcx,\n                    fcx,\n                    &math_gen::CHECKED_ADD,\n                    llvm_lhs,\n                    llvm_rhs,\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::FloatMul(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value =\n                    LLVMBuildFMul(fcx.builder, llvm_lhs, llvm_rhs, libcstr!(\"float_product\"));\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64CheckedMul(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value = math_gen::gen_checked_int_math(\n                    tcx,\n                    mcx,\n                    fcx,\n                    &math_gen::CHECKED_MUL,\n                    llvm_lhs,\n                    llvm_rhs,\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::FloatSub(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value = LLVMBuildFSub(\n                    fcx.builder,\n                    llvm_lhs,\n                    llvm_rhs,\n                    libcstr!(\"float_difference\"),\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64CheckedSub(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value = math_gen::gen_checked_int_math(\n                    tcx,\n                    mcx,\n                    fcx,\n                    &math_gen::CHECKED_SUB,\n                    llvm_lhs,\n                    llvm_rhs,\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::FloatDiv(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value =\n                    LLVMBuildFDiv(fcx.builder, llvm_lhs, llvm_rhs, libcstr!(\"float_quotient\"));\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64Div(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_numer = fcx.regs[lhs_reg];\n                let llvm_denom = fcx.regs[rhs_reg];\n\n                let llvm_value =\n                    LLVMBuildSDiv(fcx.builder, llvm_numer, llvm_denom, libcstr!(\"quot\"));\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64CheckedDiv(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_numer = fcx.regs[lhs_reg];\n                let llvm_denom = fcx.regs[rhs_reg];\n\n                let llvm_value =\n                    math_gen::gen_checked_int_div(tcx, mcx, fcx, llvm_numer, llvm_denom);\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64Rem(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_numer = fcx.regs[lhs_reg];\n                let llvm_denom = fcx.regs[rhs_reg];\n\n                let llvm_value =\n                    LLVMBuildSRem(fcx.builder, llvm_numer, llvm_denom, libcstr!(\"rem\"));\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64CheckedRem(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_numer = fcx.regs[lhs_reg];\n                let llvm_denom = fcx.regs[rhs_reg];\n\n                let llvm_value =\n                    math_gen::gen_checked_int_rem(tcx, mcx, fcx, llvm_numer, llvm_denom);\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::FloatSqrt(reg, radicand) => {\n                let llvm_radicand = fcx.regs[radicand];\n\n                let llvm_value = math_gen::gen_float_sqrt(tcx, mcx, fcx, llvm_radicand);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64BitwiseAnd(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value = LLVMBuildAnd(fcx.builder, llvm_lhs, llvm_rhs, libcstr!(\"int_and\"));\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64BitwiseOr(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value = LLVMBuildOr(fcx.builder, llvm_lhs, llvm_rhs, libcstr!(\"int_or\"));\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64BitwiseXor(reg, BinaryOp { lhs_reg, rhs_reg }) => {\n                let llvm_lhs = fcx.regs[lhs_reg];\n                let llvm_rhs = fcx.regs[rhs_reg];\n\n                let llvm_value = LLVMBuildXor(fcx.builder, llvm_lhs, llvm_rhs, libcstr!(\"int_xor\"));\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64BitwiseNot(reg, int_reg) => {\n                let llvm_int = fcx.regs[int_reg];\n\n                let llvm_value = LLVMBuildNot(fcx.builder, llvm_int, libcstr!(\"int_not\"));\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64ShiftLeft(reg, ShiftOp { int_reg, bit_count }) => {\n                let llvm_int = fcx.regs[int_reg];\n\n                let llvm_value = LLVMBuildShl(\n                    fcx.builder,\n                    llvm_int,\n                    LLVMConstInt(LLVMInt64TypeInContext(tcx.llx), *bit_count as u64, 0),\n                    libcstr!(\"int_shl\"),\n                );\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64ArithmeticShiftRight(reg, ShiftOp { int_reg, bit_count }) => {\n                let llvm_int = fcx.regs[int_reg];\n\n                let llvm_value = LLVMBuildAShr(\n                    fcx.builder,\n                    llvm_int,\n                    LLVMConstInt(LLVMInt64TypeInContext(tcx.llx), *bit_count as u64, 0),\n                    libcstr!(\"int_ashr\"),\n                );\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::Int64LogicalShiftRight(reg, ShiftOp { int_reg, bit_count }) => {\n                let llvm_int = fcx.regs[int_reg];\n\n                let llvm_value = LLVMBuildLShr(\n                    fcx.builder,\n                    llvm_int,\n                    LLVMConstInt(LLVMInt64TypeInContext(tcx.llx), *bit_count as u64, 0),\n                    libcstr!(\"int_lshr\"),\n                );\n\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::MakeCallback(\n                reg,\n                MakeCallbackOp {\n                    callee,\n                    captures_reg,\n                },\n            ) => {\n                let llvm_captures = fcx.regs[captures_reg];\n                let llvm_entry_point = gen_callee_entry_point(tcx, mcx, fcx, callee);\n                let entry_point_llvm_type = LLVMTypeOf(llvm_entry_point);\n\n                let callback_type = tcx.callback_llvm_type(entry_point_llvm_type);\n\n                let llvm_undef = LLVMGetUndef(callback_type);\n                let llvm_with_captures = LLVMBuildInsertValue(\n                    fcx.builder,\n                    llvm_undef,\n                    llvm_captures,\n                    0,\n                    libcstr!(\"captures\"),\n                );\n                let llvm_callback = LLVMBuildInsertValue(\n                    fcx.builder,\n                    llvm_with_captures,\n                    llvm_entry_point,\n                    1,\n                    libcstr!(\"callback\"),\n                );\n\n                fcx.regs.insert(*reg, llvm_callback);\n            }\n            OpKind::ConstBoxedRecord(\n                reg,\n                BoxRecordOp {\n                    record_struct,\n                    field_regs,\n                },\n            ) => {\n                let llvm_fields: Box<[LLVMValueRef]> = field_regs\n                    .iter()\n                    .map(|field_reg| fcx.regs[field_reg])\n                    .collect();\n\n                let llvm_value = const_gen::gen_boxed_record(tcx, mcx, record_struct, &llvm_fields);\n                fcx.regs.insert(*reg, llvm_value);\n            }\n            OpKind::AllocBoxedRecord(\n                reg,\n                BoxRecordOp {\n                    record_struct,\n                    field_regs,\n                },\n            ) => {\n                let box_source = active_alloc.next_box_source();\n\n                let llvm_fields = field_regs\n                    .iter()\n                    .map(|field_reg| fcx.regs[field_reg])\n                    .collect();\n\n                let input = alloc::types::RecordInput {\n                    record_struct,\n                    llvm_fields,\n                };\n\n                let llvm_value = alloc::types::gen_alloc_boxed_record(\n                    tcx,\n                    mcx,\n                    fcx.builder,\n                    active_alloc,\n                    box_source,\n                    &input,\n                );\n                fcx.regs.insert(*reg, llvm_value);\n            }\n        }\n    }\n}\n\nfn gen_cond_branch(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fcx: &mut FunCtx,\n    block: LLVMBasicBlockRef,\n    alloc_plan: Vec<alloc::AllocAtom<'_>>,\n    cont_block: LLVMBasicBlockRef,\n) {\n    unsafe {\n        LLVMPositionBuilderAtEnd(fcx.builder, block);\n\n        // We can't branch if we terminated\n        let will_terminate = alloc_plan\n            .last()\n            .and_then(|alloc_atom| alloc_atom.ops().last())\n            .filter(|op| op.kind().is_terminator())\n            .is_some();\n\n        for alloc_atom in alloc_plan {\n            gen_alloc_atom(tcx, mcx, fcx, alloc_atom);\n        }\n\n        if !will_terminate {\n            LLVMBuildBr(fcx.builder, cont_block);\n        }\n    }\n}\n\nfn gen_cond(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fcx: &mut FunCtx,\n    cond_op: &CondOp,\n    cond_alloc_plan: alloc::CondPlan<'_>,\n) {\n    let CondOp {\n        reg_phi, test_reg, ..\n    } = cond_op;\n\n    let alloc::CondPlan {\n        true_subplan: true_alloc_subplan,\n        false_subplan: false_alloc_subplan,\n    } = cond_alloc_plan;\n\n    unsafe {\n        let true_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"cond_true\"));\n\n        let false_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"cond_false\"));\n\n        let cont_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"cond_cont\"));\n\n        let test_llvm = fcx.regs[test_reg];\n        LLVMBuildCondBr(fcx.builder, test_llvm, true_block, false_block);\n\n        gen_cond_branch(tcx, mcx, fcx, true_block, true_alloc_subplan, cont_block);\n        let mut final_true_block = LLVMGetInsertBlock(fcx.builder);\n\n        gen_cond_branch(tcx, mcx, fcx, false_block, false_alloc_subplan, cont_block);\n        let mut final_false_block = LLVMGetInsertBlock(fcx.builder);\n\n        LLVMPositionBuilderAtEnd(fcx.builder, cont_block);\n\n        if let Some(RegPhi {\n            output_reg,\n            true_result_reg,\n            false_result_reg,\n        }) = reg_phi\n        {\n            let mut true_result_llvm = fcx.regs[true_result_reg];\n            let mut false_result_llvm = fcx.regs[false_result_reg];\n\n            let phi_value = LLVMBuildPhi(\n                fcx.builder,\n                LLVMTypeOf(true_result_llvm),\n                libcstr!(\"cond_phi\"),\n            );\n\n            LLVMAddIncoming(\n                phi_value,\n                &mut true_result_llvm as *mut _,\n                &mut final_true_block as *mut _,\n                1,\n            );\n            LLVMAddIncoming(\n                phi_value,\n                &mut false_result_llvm as *mut _,\n                &mut final_false_block as *mut _,\n                1,\n            );\n\n            fcx.regs.insert(*output_reg, phi_value);\n        }\n    }\n}\n\nfn gen_callee_entry_point(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fcx: &mut FunCtx,\n    callee: &Callee,\n) -> LLVMValueRef {\n    use crate::codegen::callee::*;\n\n    match callee {\n        Callee::PrivateFun(private_fun_id) => mcx.llvm_private_fun(*private_fun_id),\n        Callee::BoxedFunThunk(fun_thunk_reg) => {\n            let llvm_fun_thunk = fcx.regs[fun_thunk_reg];\n            gen_boxed_fun_thunk_entry_point(fcx.builder, llvm_fun_thunk)\n        }\n        Callee::StaticSymbol(static_symbol) => {\n            gen_static_symbol_entry_point(tcx, mcx, static_symbol)\n        }\n    }\n}\n\npub(crate) fn gen_alloc_atom(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fcx: &mut FunCtx,\n    alloc_atom: alloc::AllocAtom<'_>,\n) {\n    let ops = alloc_atom.ops();\n    let mut active_alloc =\n        alloc::core::atom_into_active_alloc(tcx, mcx, fcx.builder, fcx.current_task, alloc_atom);\n\n    for op in ops {\n        gen_op(tcx, mcx, fcx, &mut active_alloc, op);\n    }\n\n    assert!(\n        active_alloc.is_empty(),\n        \"did not consume entire active heap allocation\"\n    );\n}\n"
  },
  {
    "path": "compiler/codegen/panic_gen.rs",
    "content": "use llvm_sys::core::*;\nuse llvm_sys::LLVMAttributeFunctionIndex;\n\nuse crate::codegen::const_gen::annotate_private_global;\nuse crate::codegen::fun_gen::FunCtx;\nuse crate::codegen::mod_gen::ModCtx;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::libcstr;\n\npub(crate) fn gen_panic(\n    tcx: &mut TargetCtx,\n    mcx: &mut ModCtx<'_, '_, '_>,\n    fcx: &mut FunCtx,\n    message: &str,\n) {\n    unsafe {\n        let llvm_i8 = LLVMInt8TypeInContext(tcx.llx);\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n\n        let llvm_param_types = &mut [\n            tcx.task_llvm_ptr_type(),\n            LLVMPointerType(llvm_i8, 0),\n            llvm_i32,\n        ];\n\n        let panic_with_string_llvm_type = LLVMFunctionType(\n            LLVMVoidTypeInContext(tcx.llx),\n            llvm_param_types.as_mut_ptr(),\n            llvm_param_types.len() as u32,\n            0,\n        );\n\n        let panic_with_string_fun = mcx.get_function_or_insert(\n            panic_with_string_llvm_type,\n            b\"arret_runtime_panic_with_string\\0\",\n            |panic_with_string_fun| {\n                LLVMAddAttributeAtIndex(\n                    panic_with_string_fun,\n                    LLVMAttributeFunctionIndex,\n                    tcx.llvm_enum_attr_for_name(\"cold\", 0),\n                );\n\n                LLVMAddAttributeAtIndex(\n                    panic_with_string_fun,\n                    LLVMAttributeFunctionIndex,\n                    tcx.llvm_enum_attr_for_name(\"noreturn\", 0),\n                );\n            },\n        );\n\n        let llvm_message_string =\n            LLVMConstStringInContext(tcx.llx, message.as_ptr() as *mut _, message.len() as u32, 1);\n\n        let llvm_message_global = LLVMAddGlobal(\n            mcx.module,\n            LLVMTypeOf(llvm_message_string),\n            libcstr!(\"panic_message\"),\n        );\n        LLVMSetInitializer(llvm_message_global, llvm_message_string);\n        annotate_private_global(llvm_message_global);\n\n        let llvm_first_byte_gep_indices =\n            &mut [LLVMConstInt(llvm_i32, 0, 0), LLVMConstInt(llvm_i32, 0, 0)];\n\n        let message_pointer = LLVMConstInBoundsGEP(\n            llvm_message_global,\n            llvm_first_byte_gep_indices.as_mut_ptr(),\n            llvm_first_byte_gep_indices.len() as u32,\n        );\n\n        let panic_with_string_args = &mut [\n            fcx.current_task,\n            message_pointer,\n            LLVMConstInt(llvm_i32, message.len() as u64, 0),\n        ];\n\n        LLVMBuildCall(\n            fcx.builder,\n            panic_with_string_fun,\n            panic_with_string_args.as_mut_ptr(),\n            panic_with_string_args.len() as u32,\n            libcstr!(\"\"),\n        );\n\n        LLVMBuildUnreachable(fcx.builder);\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/program.rs",
    "content": "use std::collections::HashMap;\nuse std::ffi::{CStr, CString};\nuse std::sync::Arc;\nuse std::{env, fs, io, path, process, ptr};\n\nuse llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::target_machine::*;\nuse llvm_sys::LLVMLinkage;\n\nuse crate::codegen::analysis::AnalysedMod;\nuse crate::codegen::mod_gen::{gen_mod, GeneratedMod};\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::context::LinkedLibrary;\nuse crate::libcstr;\nuse crate::mir;\nuse crate::SourceLoader;\n\n#[derive(Copy, Clone, PartialEq)]\npub enum OutputType {\n    None,\n    LlvmIr,\n    Assembly,\n    Object,\n    Executable,\n}\n\n#[derive(Copy, Clone, PartialEq)]\npub struct Options<'target> {\n    target_triple: Option<&'target str>,\n    output_type: OutputType,\n    llvm_opt: bool,\n}\n\nimpl<'target> Options<'target> {\n    pub fn new() -> Options<'static> {\n        Options {\n            target_triple: None,\n            output_type: OutputType::Executable,\n            llvm_opt: true,\n        }\n    }\n\n    pub fn with_target_triple(self, target_triple: Option<&'target str>) -> Options<'target> {\n        Options {\n            target_triple,\n            ..self\n        }\n    }\n\n    pub fn with_llvm_opt(self, llvm_opt: bool) -> Options<'target> {\n        Options { llvm_opt, ..self }\n    }\n\n    pub fn with_output_type(self, output_type: OutputType) -> Options<'target> {\n        Options {\n            output_type,\n            ..self\n        }\n    }\n\n    pub fn output_type(&self) -> OutputType {\n        self.output_type\n    }\n}\n\nimpl Default for Options<'static> {\n    fn default() -> Options<'static> {\n        Options::new()\n    }\n}\n\nfn arret_main_llvm_type(tcx: &mut TargetCtx) -> LLVMTypeRef {\n    unsafe {\n        let llvm_arg_types = &mut [tcx.task_llvm_ptr_type()];\n\n        LLVMFunctionType(\n            LLVMVoidTypeInContext(tcx.llx),\n            llvm_arg_types.as_mut_ptr(),\n            llvm_arg_types.len() as u32,\n            0,\n        )\n    }\n}\n\nfn c_main_llvm_type(tcx: &mut TargetCtx) -> LLVMTypeRef {\n    unsafe {\n        let llvm_argc_type = LLVMInt32TypeInContext(tcx.llx);\n        let llvm_argv_type = LLVMPointerType(LLVMPointerType(LLVMInt8TypeInContext(tcx.llx), 0), 0);\n        let llvm_ret_type = LLVMInt32TypeInContext(tcx.llx);\n\n        let llvm_arg_types = &mut [llvm_argc_type, llvm_argv_type];\n\n        LLVMFunctionType(\n            llvm_ret_type,\n            llvm_arg_types.as_mut_ptr(),\n            llvm_arg_types.len() as u32,\n            0,\n        )\n    }\n}\n\nfn program_to_module(\n    tcx: &mut TargetCtx,\n    program: &mir::BuiltProgram,\n    debug_source_loader: Option<&SourceLoader>,\n) -> LLVMModuleRef {\n    unsafe {\n        let analysed_mod = AnalysedMod::new(&program.private_funs, &program.main);\n\n        // Build our Arret funs\n        let GeneratedMod {\n            llvm_module,\n            llvm_entry_fun: llvm_arret_main,\n            llvm_global_interned_names,\n            llvm_classmap_classes,\n        } = gen_mod(\n            tcx,\n            b\"program\\0\",\n            &analysed_mod,\n            None,\n            HashMap::new(),\n            debug_source_loader,\n        );\n\n        LLVMSetLinkage(llvm_arret_main, LLVMLinkage::LLVMPrivateLinkage);\n\n        // Declare our C main\n        let builder = LLVMCreateBuilderInContext(tcx.llx);\n        let c_main = LLVMAddFunction(llvm_module, libcstr!(\"main\"), c_main_llvm_type(tcx));\n\n        let bb = LLVMAppendBasicBlockInContext(tcx.llx, c_main, libcstr!(\"entry\"));\n        LLVMPositionBuilderAtEnd(builder, bb);\n\n        let classmap_class_ptr_type = LLVMPointerType(tcx.classmap_class_llvm_type(), 0);\n\n        // Declare arret_runtime_launch_task\n        let launch_task_llvm_arg_types = &mut [\n            LLVMTypeOf(llvm_global_interned_names),\n            classmap_class_ptr_type,\n            LLVMPointerType(arret_main_llvm_type(tcx), 0),\n        ];\n\n        let launch_task_llvm_type = LLVMFunctionType(\n            LLVMVoidTypeInContext(tcx.llx),\n            launch_task_llvm_arg_types.as_mut_ptr(),\n            launch_task_llvm_arg_types.len() as u32,\n            0,\n        );\n\n        // And launch the task from C main\n        let launch_task_llvm_fun = LLVMAddFunction(\n            llvm_module,\n            libcstr!(\"arret_runtime_launch_task\"),\n            launch_task_llvm_type,\n        );\n\n        let launch_task_llvm_args = &mut [\n            llvm_global_interned_names,\n            llvm_classmap_classes,\n            llvm_arret_main,\n        ];\n\n        LLVMBuildCall(\n            builder,\n            launch_task_llvm_fun,\n            launch_task_llvm_args.as_mut_ptr(),\n            launch_task_llvm_args.len() as u32,\n            libcstr!(\"\"),\n        );\n\n        LLVMBuildRet(builder, LLVMConstInt(LLVMInt32TypeInContext(tcx.llx), 0, 0));\n        LLVMDisposeBuilder(builder);\n\n        llvm_module\n    }\n}\n\nfn target_triple_to_cc_args(target_triple: &str) -> Vec<&str> {\n    // Try to use -m32 when possible for compatibility with GCC\n    if (cfg!(target_arch = \"x86_64\") && target_triple.starts_with(\"i686-\"))\n        || (cfg!(target_arch = \"aarch64\") && target_triple.starts_with(\"arm\"))\n    {\n        vec![\"-m32\"]\n    } else {\n        vec![\"-target\", target_triple]\n    }\n}\n\n/// Generates code for the program with the given output type\n///\n/// `codegen::initialise_llvm()` must be called before this.\npub fn gen_program(\n    options: Options<'_>,\n    linked_libraries: &[Arc<LinkedLibrary>],\n    program: &mir::BuiltProgram,\n    output_file: &path::Path,\n    debug_source_loader: Option<&SourceLoader>,\n) {\n    use crate::codegen::target_machine::create_target_machine;\n\n    if env::var_os(\"ARRET_DUMP_MIR\").is_some() {\n        mir::print_program(&mut io::stdout().lock(), program, debug_source_loader).unwrap();\n    }\n\n    let Options {\n        target_triple,\n        output_type,\n        llvm_opt,\n    } = options;\n\n    let llvm_output_path = if output_type == OutputType::Executable {\n        // When outputting an executable this is an intermediate file that we pass to our linker\n        output_file.with_extension(\"o\")\n    } else {\n        // Otherwise this is the final destination\n        output_file.to_owned()\n    };\n\n    let llvm_output_path_cstring = CString::new(llvm_output_path.to_str().unwrap()).unwrap();\n\n    let target_machine = create_target_machine(\n        target_triple,\n        LLVMRelocMode::LLVMRelocDynamicNoPic,\n        LLVMCodeModel::LLVMCodeModelDefault,\n    );\n\n    let mut tcx = TargetCtx::new(target_machine, llvm_opt);\n    let module = program_to_module(&mut tcx, program, debug_source_loader);\n    tcx.finish_module(module);\n\n    unsafe {\n        let mut error: *mut libc::c_char = ptr::null_mut();\n\n        let llvm_code_gen_file_type = match output_type {\n            OutputType::None => {\n                LLVMDisposeTargetMachine(target_machine);\n                return;\n            }\n            OutputType::LlvmIr => {\n                if LLVMPrintModuleToFile(\n                    module,\n                    llvm_output_path_cstring.as_ptr() as *mut _,\n                    &mut error as *mut _,\n                ) != 0\n                {\n                    panic!(\n                        \"LLVMPrintModuleToFile: {}\",\n                        CStr::from_ptr(error).to_str().unwrap()\n                    );\n                }\n                return;\n            }\n            OutputType::Assembly => LLVMCodeGenFileType::LLVMAssemblyFile,\n            OutputType::Object | OutputType::Executable => LLVMCodeGenFileType::LLVMObjectFile,\n        };\n\n        if LLVMTargetMachineEmitToFile(\n            target_machine,\n            module,\n            llvm_output_path_cstring.as_ptr() as *mut _,\n            llvm_code_gen_file_type,\n            &mut error as *mut _,\n        ) != 0\n        {\n            panic!(\n                \"LLVMTargetMachineEmitToFile: {}\",\n                CStr::from_ptr(error).to_str().unwrap()\n            );\n        }\n        LLVMDisposeTargetMachine(target_machine);\n    }\n\n    if output_type == OutputType::Executable {\n        let target_args = match target_triple {\n            Some(triple) => target_triple_to_cc_args(triple),\n            None => vec![],\n        };\n\n        let status = process::Command::new(\"cc\")\n            .arg(llvm_output_path.clone())\n            .args(target_args)\n            .arg(\"-o\")\n            .arg(output_file)\n            .args(linked_libraries.iter().map(|l| l.target_path()))\n            .arg(\"-pthread\")\n            .arg(\"-ldl\")\n            .arg(\"-lm\")\n            .status()\n            .unwrap();\n\n        let _ = fs::remove_file(llvm_output_path);\n\n        if !status.success() {\n            panic!(\"Error invoking linker\");\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/range_md.rs",
    "content": "use std::iter;\nuse std::ops::Range;\n\nuse llvm_sys::core::*;\nuse llvm_sys::prelude::*;\n\ntype Int = i64;\n\nstruct IntRangeIter<I>\nwhere\n    I: Iterator<Item = Int>,\n{\n    inner_iter: iter::Peekable<I>,\n}\n\nimpl<I> Iterator for IntRangeIter<I>\nwhere\n    I: Iterator<Item = Int>,\n{\n    type Item = Range<Int>;\n\n    fn next(&mut self) -> Option<Range<Int>> {\n        let range_start = self.inner_iter.next()?;\n\n        let mut range_length: Int = 1;\n        while self.inner_iter.peek() == Some(&(range_start + range_length)) {\n            self.inner_iter.next();\n            range_length += 1;\n        }\n\n        Some(range_start..range_start + (range_length as Int))\n    }\n}\n\n/// Finds ranges of consecutive integers from a sorted iterator\nfn find_int_ranges(input: impl Iterator<Item = Int>) -> impl Iterator<Item = Range<Int>> {\n    IntRangeIter {\n        inner_iter: input.peekable(),\n    }\n}\n\n/// Generates a range metadata node from a sorted iterator of possible values\n///\n/// This does not handle minimum or maximum values of `Int` correctly!\npub fn int_range_md_node(\n    llx: LLVMContextRef,\n    llvm_int_type: LLVMTypeRef,\n    input: impl Iterator<Item = Int>,\n) -> LLVMMetadataRef {\n    unsafe {\n        let mut llvm_range_values: Vec<LLVMMetadataRef> = find_int_ranges(input)\n            .flat_map(|range| iter::once(range.start).chain(iter::once(range.end)))\n            .map(|value| LLVMConstInt(llvm_int_type, value as u64, 0))\n            .map(|value| LLVMValueAsMetadata(value))\n            .collect();\n\n        LLVMMDNodeInContext2(llx, llvm_range_values.as_mut_ptr(), llvm_range_values.len())\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn empty() {\n        let iter = find_int_ranges([].iter().cloned());\n        assert_eq!(0, iter.count());\n    }\n\n    #[test]\n    fn single_value() {\n        let mut iter = find_int_ranges([-5].iter().cloned());\n\n        assert_eq!(Some(-5..-4), iter.next());\n        assert_eq!(None, iter.next());\n    }\n\n    #[test]\n    fn single_range() {\n        let mut iter = find_int_ranges([-1, 0, 1].iter().cloned());\n\n        assert_eq!(Some(-1..2), iter.next());\n        assert_eq!(None, iter.next());\n    }\n\n    #[test]\n    fn multi_range() {\n        let mut iter = find_int_ranges([-5, -1, 0, 1, 90, 91, 92].iter().cloned());\n\n        assert_eq!(Some(-5..-4), iter.next());\n        assert_eq!(Some(-1..2), iter.next());\n        assert_eq!(Some(90..93), iter.next());\n        assert_eq!(None, iter.next());\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/record_struct.rs",
    "content": "use std::alloc;\n\nuse llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::target::*;\n\nuse arret_runtime::boxed;\nuse arret_runtime::class_map;\n\nuse crate::codegen::const_gen::annotate_private_global;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::libcstr;\nuse crate::mir::ops;\n\npub const IS_INLINE_INDEX: u32 = 1;\npub const CONTAINS_GC_REFS_INDEX: u32 = 2;\npub const RECORD_CLASS_ID_INDEX: u32 = 3;\npub const DATA_INDEX: u32 = 4;\npub const EXTERNAL_COMPACT_LAYOUT_INDEX: u32 = 5;\n\n/// Adds internal member fields common to all inline and external records\npub fn append_common_internal_members(tcx: &mut TargetCtx, members: &mut Vec<LLVMTypeRef>) {\n    unsafe {\n        members.extend_from_slice(&[\n            // is_inline\n            LLVMInt8TypeInContext(tcx.llx),\n            // may_contain_gc_refs\n            LLVMInt8TypeInContext(tcx.llx),\n            // record_class_id\n            tcx.record_class_id_llvm_type(),\n        ]);\n    }\n}\n\n#[derive(Clone)]\npub struct TargetRecordStruct {\n    pub data_layout: Option<alloc::Layout>,\n    pub record_storage: boxed::RecordStorage,\n    pub llvm_data_type: LLVMTypeRef,\n    pub classmap_class: class_map::BoxedClass,\n}\n\nimpl TargetRecordStruct {\n    pub fn from_mir_record_struct(\n        tcx: &mut TargetCtx,\n        record_struct: &ops::RecordStructId,\n    ) -> Self {\n        let mut members: Box<[LLVMTypeRef]> = record_struct\n            .field_abi_types\n            .iter()\n            .map(|abi_type| tcx.abi_to_llvm_type(abi_type))\n            .collect();\n\n        let record_data_name = format!(\"{}_data\\0\", record_struct.source_name);\n\n        unsafe {\n            let llvm_data_type =\n                LLVMStructCreateNamed(tcx.llx, record_data_name.as_ptr() as *const _);\n\n            LLVMStructSetBody(\n                llvm_data_type,\n                members.as_mut_ptr(),\n                members.len() as u32,\n                0,\n            );\n\n            let data_layout = if members.is_empty() {\n                None\n            } else {\n                // Convert our LLVM layout information to Rust's `std::alloc::Layout`\n                let align = LLVMABIAlignmentOfType(tcx.target_data(), llvm_data_type) as usize;\n                let size = LLVMABISizeOfType(tcx.target_data(), llvm_data_type) as usize;\n\n                Some(alloc::Layout::from_size_align_unchecked(size, align))\n            };\n\n            let record_storage = boxed::Record::storage_for_data_layout(data_layout);\n\n            let classmap_class = class_map::BoxedClass::from_fields(\n                record_struct\n                    .field_abi_types\n                    .iter()\n                    .enumerate()\n                    .map(|(index, field_abi_type)| {\n                        let field_type = class_map::FieldType::from_abi_type(field_abi_type);\n                        let offset =\n                            LLVMOffsetOfElement(tcx.target_data(), llvm_data_type, index as u32)\n                                as usize;\n\n                        class_map::Field::new(field_type, offset)\n                    }),\n            );\n\n            Self {\n                data_layout,\n                record_storage,\n                llvm_data_type,\n                classmap_class,\n            }\n        }\n    }\n}\n\npub fn gen_classmap_classes(\n    tcx: &mut TargetCtx,\n    llvm_module: LLVMModuleRef,\n    record_structs: &[ops::RecordStructId],\n) -> LLVMValueRef {\n    if record_structs.is_empty() {\n        return unsafe { LLVMConstPointerNull(LLVMPointerType(tcx.classmap_class_llvm_type(), 0)) };\n    }\n\n    let llvm_classmap_field_type = tcx.classmap_field_llvm_type();\n    let llvm_i8 = unsafe { LLVMInt8TypeInContext(tcx.llx) };\n    let llvm_i32 = unsafe { LLVMInt32TypeInContext(tcx.llx) };\n\n    let llvm_first_element_gep_indices =\n        unsafe { &mut [LLVMConstInt(llvm_i32, 0, 0), LLVMConstInt(llvm_i32, 0, 0)] };\n\n    let mut llvm_classmap_classes: Vec<LLVMValueRef> = record_structs\n        .iter()\n        .map(|record_struct| {\n            let classmap_class = tcx\n                .target_record_struct(record_struct)\n                .classmap_class\n                .as_ref();\n\n            if classmap_class.is_empty() {\n                return unsafe {\n                    LLVMConstPointerNull(LLVMPointerType(llvm_classmap_field_type, 0))\n                };\n            }\n\n            let mut llvm_classmap_class_fields: Vec<LLVMValueRef> = classmap_class\n                .field_iter()\n                .map(|field| unsafe {\n                    // This is the layout of `class_map::Field`\n                    let llvm_offset = LLVMConstInt(llvm_i32, field.offset() as u64, 0);\n                    let llvm_field_type = LLVMConstInt(llvm_i8, field.field_type() as u64, 0);\n                    let llvm_is_last = LLVMConstInt(llvm_i8, field.is_last() as u64, 0);\n\n                    let members = &mut [llvm_offset, llvm_field_type, llvm_is_last];\n\n                    LLVMConstNamedStruct(\n                        llvm_classmap_field_type,\n                        members.as_mut_ptr(),\n                        members.len() as u32,\n                    )\n                })\n                .collect();\n\n            unsafe {\n                let llvm_classmap_class = LLVMConstArray(\n                    llvm_classmap_field_type,\n                    llvm_classmap_class_fields.as_mut_ptr(),\n                    llvm_classmap_class_fields.len() as u32,\n                );\n\n                let classmap_class_global_name =\n                    format!(\"{}_classmap\\0\", record_struct.source_name);\n\n                let llvm_classmap_class_global = LLVMAddGlobal(\n                    llvm_module,\n                    LLVMTypeOf(llvm_classmap_class),\n                    classmap_class_global_name.as_ptr() as *const _,\n                );\n\n                LLVMSetInitializer(llvm_classmap_class_global, llvm_classmap_class);\n                annotate_private_global(llvm_classmap_class_global);\n\n                LLVMConstInBoundsGEP(\n                    llvm_classmap_class_global,\n                    llvm_first_element_gep_indices.as_mut_ptr(),\n                    llvm_first_element_gep_indices.len() as u32,\n                )\n            }\n        })\n        .collect();\n\n    unsafe {\n        let llvm_classmap = LLVMConstArray(\n            LLVMPointerType(llvm_classmap_field_type, 0),\n            llvm_classmap_classes.as_mut_ptr(),\n            llvm_classmap_classes.len() as u32,\n        );\n\n        let llvm_classmap_global = LLVMAddGlobal(\n            llvm_module,\n            LLVMTypeOf(llvm_classmap),\n            libcstr!(\"classmap_classes\"),\n        );\n\n        LLVMSetInitializer(llvm_classmap_global, llvm_classmap);\n        annotate_private_global(llvm_classmap_global);\n\n        LLVMConstInBoundsGEP(\n            llvm_classmap_global,\n            llvm_first_element_gep_indices.as_mut_ptr(),\n            llvm_first_element_gep_indices.len() as u32,\n        )\n    }\n}\n\npub fn gen_record_field_ptr(\n    tcx: &TargetCtx,\n    builder: LLVMBuilderRef,\n    record_storage: boxed::RecordStorage,\n    llvm_boxed_record: LLVMValueRef,\n    field_index: usize,\n    pointer_name: &[u8],\n) -> LLVMValueRef {\n    unsafe {\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n\n        match record_storage {\n            boxed::RecordStorage::Inline(_) => {\n                let field_gep_indices = &mut [\n                    LLVMConstInt(llvm_i32, 0, 0),\n                    LLVMConstInt(llvm_i32, u64::from(DATA_INDEX), 0),\n                    LLVMConstInt(llvm_i32, field_index as u64, 0),\n                ];\n\n                LLVMBuildInBoundsGEP(\n                    builder,\n                    llvm_boxed_record,\n                    field_gep_indices.as_mut_ptr(),\n                    field_gep_indices.len() as u32,\n                    pointer_name.as_ptr() as *const _,\n                )\n            }\n            boxed::RecordStorage::External => {\n                let data_ptr_gep_indices = &mut [\n                    LLVMConstInt(llvm_i32, 0, 0),\n                    LLVMConstInt(llvm_i32, u64::from(DATA_INDEX), 0),\n                ];\n\n                let llvm_record_data_ptr_ptr = LLVMBuildInBoundsGEP(\n                    builder,\n                    llvm_boxed_record,\n                    data_ptr_gep_indices.as_mut_ptr(),\n                    data_ptr_gep_indices.len() as u32,\n                    libcstr!(\"record_data_ptr_ptr\"),\n                );\n\n                let llvm_record_data_ptr = LLVMBuildLoad(\n                    builder,\n                    llvm_record_data_ptr_ptr,\n                    libcstr!(\"record_data_ptr\"),\n                );\n                tcx.add_invariant_load_metadata(llvm_record_data_ptr);\n\n                let field_gep_indices = &mut [\n                    LLVMConstInt(llvm_i32, 0, 0),\n                    LLVMConstInt(llvm_i32, field_index as u64, 0),\n                ];\n\n                LLVMBuildInBoundsGEP(\n                    builder,\n                    llvm_record_data_ptr,\n                    field_gep_indices.as_mut_ptr(),\n                    field_gep_indices.len() as u32,\n                    pointer_name.as_ptr() as *const _,\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/target_gen.rs",
    "content": "use std::collections::HashMap;\n\nuse llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::target::*;\nuse llvm_sys::target_machine::*;\nuse llvm_sys::{LLVMAttributeReturnIndex, LLVMLinkage};\n\nuse arret_runtime::abitype::{AbiType, BoxedAbiType, RetAbiType};\nuse arret_runtime::boxed;\nuse arret_runtime::callback::EntryPointAbiType as CallbackEntryPointAbiType;\n\nuse crate::codegen::box_layout::BoxLayout;\nuse crate::codegen::record_struct;\nuse crate::codegen::GenAbi;\nuse crate::libcstr;\nuse crate::mir::ops;\n\nfn llvm_enum_attr_for_name(\n    llx: LLVMContextRef,\n    attr_name: &str,\n    attr_value: u64,\n) -> LLVMAttributeRef {\n    unsafe {\n        let kind_id =\n            LLVMGetEnumAttributeKindForName(attr_name.as_ptr() as *const _, attr_name.len());\n        LLVMCreateEnumAttribute(llx, kind_id, attr_value)\n    }\n}\n\nfn llvm_md_kind_id_for_name(llx: LLVMContextRef, md_name: &str) -> u32 {\n    unsafe { LLVMGetMDKindIDInContext(llx, md_name.as_ptr() as *const _, md_name.len() as u32) }\n}\n\nfn llvm_i64_md_node(llx: LLVMContextRef, values: &[u64]) -> LLVMMetadataRef {\n    unsafe {\n        let llvm_i64 = LLVMInt64TypeInContext(llx);\n\n        let mut node_values: Vec<LLVMMetadataRef> = values\n            .iter()\n            .map(|value| LLVMConstInt(llvm_i64, *value as u64, 0))\n            .map(|value| LLVMValueAsMetadata(value))\n            .collect();\n\n        LLVMMDNodeInContext2(llx, node_values.as_mut_ptr(), node_values.len())\n    }\n}\n\n#[derive(Default)]\nstruct CachedTypes {\n    task: Option<LLVMTypeRef>,\n    box_header: Option<LLVMTypeRef>,\n\n    boxed: HashMap<BoxLayout, LLVMTypeRef>,\n\n    shared_str: Option<LLVMTypeRef>,\n    boxed_inline_str: Option<LLVMTypeRef>,\n    boxed_external_str: Option<LLVMTypeRef>,\n\n    persistent_vector_leaf: Option<LLVMTypeRef>,\n    boxed_inline_vector: Option<LLVMTypeRef>,\n    boxed_external_vector: Option<LLVMTypeRef>,\n\n    global_interned_name: Option<LLVMTypeRef>,\n\n    record_struct_box: HashMap<ops::RecordStructId, LLVMTypeRef>,\n    classmap_field: Option<LLVMTypeRef>,\n}\n\n/// Context for building against a given target machine\n///\n/// During compilation there will typically be two instances of `TargetCtx`: one for the eval JIT\n/// and one for generating the program.\n///\n/// This has a number of responsibilities:\n///\n/// 1. Storing information about the target machine and its data layout\n/// 2. Wrapping the global `LLVMContextRef`\n/// 3. Caching complex types, attributes and metadata nodes\n/// 4. Optimising modules\n///\n/// These are only vaguely related; this is a bit of a God Object.\npub struct TargetCtx {\n    pub llx: LLVMContextRef,\n    target_machine: LLVMTargetMachineRef,\n    target_data: LLVMTargetDataRef,\n\n    optimising: bool,\n    module_pass_manager: LLVMPassManagerRef,\n\n    boxed_dereferenceable_attr: LLVMAttributeRef,\n    boxed_align_attr: LLVMAttributeRef,\n    readonly_attr: LLVMAttributeRef,\n    noalias_attr: LLVMAttributeRef,\n    nocapture_attr: LLVMAttributeRef,\n\n    invariant_load_md_kind_id: u32,\n    dereferenceable_md_kind_id: u32,\n    align_md_kind_id: u32,\n\n    empty_md_node: LLVMMetadataRef,\n    boxed_dereferenceable_md_node: LLVMMetadataRef,\n    boxed_align_md_node: LLVMMetadataRef,\n\n    cached_types: CachedTypes,\n    target_record_structs: HashMap<ops::RecordStructId, record_struct::TargetRecordStruct>,\n}\n\nimpl TargetCtx {\n    /// Construct a new `TargetCtx`\n    ///\n    /// `target_machine` remains owned by the caller and must outlive this instance.\n    pub fn new(target_machine: LLVMTargetMachineRef, optimising: bool) -> TargetCtx {\n        use llvm_sys::transforms::pass_manager_builder::*;\n        use std::mem;\n\n        unsafe {\n            let llx = LLVMContextCreate();\n            let module_pass_manager = LLVMCreatePassManager();\n            let target_data = LLVMCreateTargetDataLayout(target_machine);\n\n            if optimising {\n                let fpmb = LLVMPassManagerBuilderCreate();\n                LLVMPassManagerBuilderSetOptLevel(fpmb, 2);\n                LLVMPassManagerBuilderPopulateModulePassManager(fpmb, module_pass_manager);\n                LLVMPassManagerBuilderDispose(fpmb);\n            }\n\n            TargetCtx {\n                llx,\n                target_machine,\n                target_data,\n\n                optimising,\n                module_pass_manager,\n\n                boxed_dereferenceable_attr: llvm_enum_attr_for_name(\n                    llx,\n                    \"dereferenceable\",\n                    mem::size_of::<boxed::Any>() as u64,\n                ),\n                boxed_align_attr: llvm_enum_attr_for_name(\n                    llx,\n                    \"align\",\n                    mem::align_of::<boxed::Any>() as u64,\n                ),\n                readonly_attr: llvm_enum_attr_for_name(llx, \"readonly\", 0),\n                noalias_attr: llvm_enum_attr_for_name(llx, \"noalias\", 0),\n                nocapture_attr: llvm_enum_attr_for_name(llx, \"nocapture\", 0),\n\n                invariant_load_md_kind_id: llvm_md_kind_id_for_name(llx, \"invariant.load\"),\n                dereferenceable_md_kind_id: llvm_md_kind_id_for_name(llx, \"dereferenceable\"),\n                align_md_kind_id: llvm_md_kind_id_for_name(llx, \"align\"),\n\n                empty_md_node: llvm_i64_md_node(llx, &[]),\n                boxed_dereferenceable_md_node: llvm_i64_md_node(\n                    llx,\n                    &[mem::size_of::<boxed::Any>() as u64],\n                ),\n                boxed_align_md_node: llvm_i64_md_node(llx, &[mem::align_of::<boxed::Any>() as u64]),\n\n                cached_types: Default::default(),\n                target_record_structs: HashMap::new(),\n            }\n        }\n    }\n\n    pub fn optimising(&self) -> bool {\n        self.optimising\n    }\n\n    pub fn target_machine(&self) -> LLVMTargetMachineRef {\n        self.target_machine\n    }\n\n    pub fn target_data(&self) -> LLVMTargetDataRef {\n        self.target_data\n    }\n\n    pub fn task_llvm_ptr_type(&mut self) -> LLVMTypeRef {\n        let llvm_any_ptr = self.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any);\n        let llx = self.llx;\n        *self.cached_types.task.get_or_insert_with(|| unsafe {\n            let members = &mut [llvm_any_ptr, llvm_any_ptr];\n\n            let llvm_type = LLVMStructCreateNamed(llx, libcstr!(\"task\"));\n            LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n            LLVMPointerType(llvm_type, 0)\n        })\n    }\n\n    pub fn global_interned_name_llvm_type(&mut self) -> LLVMTypeRef {\n        let llx = self.llx;\n\n        *self\n            .cached_types\n            .global_interned_name\n            .get_or_insert_with(|| unsafe {\n                let llvm_i64 = LLVMInt64TypeInContext(llx);\n                let llvm_i8 = LLVMInt8TypeInContext(llx);\n                let members = &mut [llvm_i64, LLVMPointerType(llvm_i8, 0)];\n\n                let llvm_type = LLVMStructCreateNamed(llx, libcstr!(\"global_interned_name\"));\n                LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n                llvm_type\n            })\n    }\n\n    pub fn classmap_field_llvm_type(&mut self) -> LLVMTypeRef {\n        let llx = self.llx;\n\n        *self\n            .cached_types\n            .classmap_field\n            .get_or_insert_with(|| unsafe {\n                let llvm_i32 = LLVMInt32TypeInContext(llx);\n                let llvm_i8 = LLVMInt8TypeInContext(llx);\n                let members = &mut [llvm_i32, llvm_i8, llvm_i8];\n\n                let llvm_type = LLVMStructCreateNamed(llx, libcstr!(\"classmap_field\"));\n                LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n                llvm_type\n            })\n    }\n\n    pub fn classmap_class_llvm_type(&mut self) -> LLVMTypeRef {\n        unsafe { LLVMPointerType(self.classmap_field_llvm_type(), 0) }\n    }\n\n    pub fn captures_llvm_type(&mut self) -> LLVMTypeRef {\n        self.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any)\n    }\n\n    pub fn record_class_id_llvm_type(&self) -> LLVMTypeRef {\n        unsafe { LLVMInt32TypeInContext(self.llx) }\n    }\n\n    fn box_header_llvm_type(&mut self) -> LLVMTypeRef {\n        let llx = self.llx;\n        *self.cached_types.box_header.get_or_insert_with(|| unsafe {\n            let llvm_i8 = LLVMInt8TypeInContext(llx);\n            let members = &mut [llvm_i8, llvm_i8];\n\n            let llvm_type = LLVMStructCreateNamed(llx, libcstr!(\"box_header\"));\n            LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n            llvm_type\n        })\n    }\n\n    pub fn shared_str_llvm_type(&mut self) -> LLVMTypeRef {\n        let llx = self.llx;\n\n        *self.cached_types.shared_str.get_or_insert_with(|| unsafe {\n            let llvm_i8 = LLVMInt8TypeInContext(llx);\n            let llvm_i64 = LLVMInt64TypeInContext(llx);\n\n            let members = &mut [\n                // ref_count\n                llvm_i64,\n                // len\n                llvm_i64,\n                // data\n                LLVMArrayType(llvm_i8, 0),\n            ];\n\n            let llvm_type = LLVMStructCreateNamed(llx, libcstr!(\"shared_str\"));\n            LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n            llvm_type\n        })\n    }\n\n    pub fn boxed_external_str_llvm_type(&mut self) -> LLVMTypeRef {\n        let llx = self.llx;\n        let llvm_header = self.box_header_llvm_type();\n        let shared_str_llvm_type = self.shared_str_llvm_type();\n\n        *self\n            .cached_types\n            .boxed_external_str\n            .get_or_insert_with(|| unsafe {\n                let llvm_i8 = LLVMInt8TypeInContext(llx);\n                let members = &mut [\n                    llvm_header,\n                    llvm_i8,\n                    LLVMPointerType(shared_str_llvm_type, 0),\n                ];\n\n                let llvm_type = LLVMStructCreateNamed(llx, libcstr!(\"boxed_external_str\"));\n                LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n                llvm_type\n            })\n    }\n\n    pub fn boxed_inline_str_llvm_type(&mut self) -> LLVMTypeRef {\n        let llx = self.llx;\n        let llvm_header = self.box_header_llvm_type();\n\n        *self\n            .cached_types\n            .boxed_inline_str\n            .get_or_insert_with(|| unsafe {\n                let llvm_i8 = LLVMInt8TypeInContext(llx);\n                let members = &mut [\n                    llvm_header,\n                    llvm_i8,\n                    LLVMArrayType(llvm_i8, boxed::Str::MAX_INLINE_BYTES as u32),\n                ];\n\n                let llvm_type = LLVMStructCreateNamed(llx, libcstr!(\"boxed_inline_str\"));\n                LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n                llvm_type\n            })\n    }\n\n    pub fn persistent_vector_leaf_llvm_type(&mut self) -> LLVMTypeRef {\n        use arret_runtime::persistent::vector::NODE_SIZE;\n\n        let llx = self.llx;\n        let llvm_any_ptr = self.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any);\n\n        *self\n            .cached_types\n            .persistent_vector_leaf\n            .get_or_insert_with(|| unsafe {\n                let llvm_i64 = LLVMInt64TypeInContext(llx);\n\n                let mut members = [llvm_i64, LLVMArrayType(llvm_any_ptr, NODE_SIZE as u32)];\n\n                let llvm_type = LLVMStructCreateNamed(llx, libcstr!(\"persistent_vector_leaf\"));\n                LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n                llvm_type\n            })\n    }\n\n    pub fn boxed_external_vector_llvm_type(&mut self) -> LLVMTypeRef {\n        let llx = self.llx;\n        let llvm_header = self.box_header_llvm_type();\n        let persistent_vector_leaf_type = self.persistent_vector_leaf_llvm_type();\n\n        *self\n            .cached_types\n            .boxed_external_vector\n            .get_or_insert_with(|| unsafe {\n                let llvm_i32 = LLVMInt32TypeInContext(llx);\n                let llvm_i64 = LLVMInt64TypeInContext(llx);\n                let persistent_vector_leaf_ptr = LLVMPointerType(persistent_vector_leaf_type, 0);\n\n                let members = &mut [\n                    llvm_header,\n                    llvm_i32,\n                    llvm_i64,\n                    persistent_vector_leaf_ptr,\n                    persistent_vector_leaf_ptr,\n                ];\n\n                let llvm_type = LLVMStructCreateNamed(llx, libcstr!(\"boxed_external_vector\"));\n                LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n                llvm_type\n            })\n    }\n\n    pub fn boxed_inline_vector_llvm_type(&mut self) -> LLVMTypeRef {\n        let llx = self.llx;\n        let llvm_header = self.box_header_llvm_type();\n        let llvm_any_ptr = self.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any);\n\n        *self\n            .cached_types\n            .boxed_inline_vector\n            .get_or_insert_with(|| unsafe {\n                let llvm_i32 = LLVMInt32TypeInContext(llx);\n\n                let members = &mut [\n                    llvm_header,\n                    llvm_i32,\n                    llvm_any_ptr,\n                    llvm_any_ptr,\n                    llvm_any_ptr,\n                ];\n\n                let llvm_type = LLVMStructCreateNamed(llx, libcstr!(\"boxed_inline_vector\"));\n                LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n                llvm_type\n            })\n    }\n\n    pub fn boxed_abi_to_llvm_struct_type(&mut self, boxed_abi_type: &BoxedAbiType) -> LLVMTypeRef {\n        let box_layout: BoxLayout = boxed_abi_type.into();\n\n        if let Some(llvm_struct) = self.cached_types.boxed.get(&box_layout) {\n            return *llvm_struct;\n        }\n\n        unsafe {\n            let llvm_header = self.box_header_llvm_type();\n            let mut members = vec![llvm_header];\n\n            box_layout.append_members(self, &mut members);\n\n            let llvm_type =\n                LLVMStructCreateNamed(self.llx, box_layout.type_name().as_ptr() as *const _);\n            LLVMStructSetBody(llvm_type, members.as_mut_ptr(), members.len() as u32, 0);\n\n            self.cached_types.boxed.insert(box_layout, llvm_type);\n            llvm_type\n        }\n    }\n\n    fn callback_entry_point_llvm_type(\n        &mut self,\n        entry_point_abi_type: &CallbackEntryPointAbiType,\n    ) -> LLVMTypeRef {\n        let mut llvm_param_types = vec![\n            self.task_llvm_ptr_type(),\n            self.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any),\n        ];\n\n        llvm_param_types.extend(\n            entry_point_abi_type\n                .params\n                .iter()\n                .map(|abi_type| self.abi_to_llvm_type(abi_type)),\n        );\n\n        let llvm_ret_type = self.ret_abi_to_llvm_type(&entry_point_abi_type.ret);\n\n        unsafe {\n            LLVMPointerType(\n                LLVMFunctionType(\n                    llvm_ret_type,\n                    llvm_param_types.as_mut_ptr(),\n                    llvm_param_types.len() as u32,\n                    0,\n                ),\n                0,\n            )\n        }\n    }\n\n    pub fn callback_llvm_type(&mut self, entry_point_llvm_type: LLVMTypeRef) -> LLVMTypeRef {\n        let mut members = [\n            self.boxed_abi_to_llvm_ptr_type(&BoxedAbiType::Any),\n            entry_point_llvm_type,\n        ];\n\n        unsafe { LLVMStructTypeInContext(self.llx, members.as_mut_ptr(), members.len() as u32, 0) }\n    }\n\n    pub fn boxed_abi_to_llvm_ptr_type(&mut self, boxed_abi_type: &BoxedAbiType) -> LLVMTypeRef {\n        unsafe { LLVMPointerType(self.boxed_abi_to_llvm_struct_type(boxed_abi_type), 0) }\n    }\n\n    pub fn abi_to_llvm_type(&mut self, abi_type: &AbiType) -> LLVMTypeRef {\n        unsafe {\n            match abi_type {\n                AbiType::Bool => LLVMInt1TypeInContext(self.llx),\n                AbiType::Int => LLVMInt64TypeInContext(self.llx),\n                AbiType::Char => LLVMInt32TypeInContext(self.llx),\n                AbiType::Float => LLVMDoubleTypeInContext(self.llx),\n                AbiType::InternedSym => LLVMInt64TypeInContext(self.llx),\n                AbiType::Boxed(boxed) => self.boxed_abi_to_llvm_ptr_type(boxed),\n                AbiType::Callback(entry_point_abi_type) => {\n                    let entry_point_llvm_type =\n                        self.callback_entry_point_llvm_type(entry_point_abi_type);\n                    self.callback_llvm_type(entry_point_llvm_type)\n                }\n            }\n        }\n    }\n\n    fn ret_abi_to_llvm_type(&mut self, ret_abi_type: &RetAbiType) -> LLVMTypeRef {\n        match ret_abi_type {\n            RetAbiType::Inhabited(abi_type) => self.abi_to_llvm_type(abi_type),\n            RetAbiType::Void | RetAbiType::Never => unsafe { LLVMVoidTypeInContext(self.llx) },\n        }\n    }\n\n    pub fn fun_abi_to_llvm_type(&mut self, fun_abi: &GenAbi) -> LLVMTypeRef {\n        let mut llvm_param_types = vec![];\n\n        if fun_abi.takes_task {\n            llvm_param_types.push(self.task_llvm_ptr_type());\n        }\n\n        llvm_param_types.extend(\n            fun_abi\n                .params\n                .iter()\n                .map(|param_abi_type| self.abi_to_llvm_type(&param_abi_type.abi_type)),\n        );\n\n        let llvm_ret_type = self.ret_abi_to_llvm_type(&fun_abi.ret);\n\n        unsafe {\n            LLVMFunctionType(\n                llvm_ret_type,\n                llvm_param_types.as_mut_ptr(),\n                llvm_param_types.len() as u32,\n                0,\n            )\n        }\n    }\n\n    pub fn target_record_struct<'a>(\n        &'a mut self,\n        mir_record_struct: &ops::RecordStructId,\n    ) -> &'a record_struct::TargetRecordStruct {\n        if self.target_record_structs.contains_key(mir_record_struct) {\n            return &self.target_record_structs[mir_record_struct];\n        }\n\n        let target_record_struct =\n            record_struct::TargetRecordStruct::from_mir_record_struct(self, mir_record_struct);\n\n        self.target_record_structs\n            .entry(mir_record_struct.clone())\n            .or_insert(target_record_struct)\n    }\n\n    pub fn record_struct_llvm_box_type(\n        &mut self,\n        record_struct: &ops::RecordStructId,\n    ) -> LLVMTypeRef {\n        if let Some(record_struct_box_type) = self.cached_types.record_struct_box.get(record_struct)\n        {\n            return *record_struct_box_type;\n        }\n\n        let record_struct::TargetRecordStruct {\n            llvm_data_type,\n            record_storage,\n            ..\n        } = *self.target_record_struct(record_struct);\n\n        let llvm_header = self.box_header_llvm_type();\n\n        let mut members = vec![llvm_header];\n        record_struct::append_common_internal_members(self, &mut members);\n\n        match record_storage {\n            boxed::RecordStorage::Inline(_) => {\n                members.push(llvm_data_type);\n            }\n            boxed::RecordStorage::External => unsafe {\n                members.extend(&[\n                    LLVMPointerType(llvm_data_type, 0),\n                    LLVMInt64TypeInContext(self.llx),\n                ]);\n            },\n        }\n\n        let box_name = format!(\"boxed_{}\\0\", record_struct.source_name);\n\n        let record_struct_box_type = unsafe {\n            let record_struct_box_type =\n                LLVMStructCreateNamed(self.llx, box_name.as_ptr() as *const _);\n\n            LLVMStructSetBody(\n                record_struct_box_type,\n                members.as_mut_ptr(),\n                members.len() as u32,\n                0,\n            );\n\n            record_struct_box_type\n        };\n\n        self.cached_types\n            .record_struct_box\n            .insert(record_struct.clone(), record_struct_box_type);\n\n        record_struct_box_type\n    }\n\n    pub fn ptr_to_singleton_box(\n        &mut self,\n        module: LLVMModuleRef,\n        type_tag: boxed::TypeTag,\n        name: &[u8],\n    ) -> LLVMValueRef {\n        use std::mem;\n\n        unsafe {\n            let global = LLVMGetNamedGlobal(module, name.as_ptr() as *const _);\n            if !global.is_null() {\n                return global;\n            }\n\n            let llvm_type = self.boxed_abi_to_llvm_struct_type(&type_tag.into());\n            let global = LLVMAddGlobal(module, llvm_type, name.as_ptr() as *const _);\n\n            let members = &mut [self.llvm_box_header(type_tag.to_const_header())];\n\n            let llvm_value =\n                LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32);\n\n            LLVMSetInitializer(global, llvm_value);\n            LLVMSetAlignment(global, mem::align_of::<boxed::Any>() as u32);\n            LLVMSetGlobalConstant(global, 1);\n            LLVMSetLinkage(global, LLVMLinkage::LLVMAvailableExternallyLinkage);\n\n            global\n        }\n    }\n\n    pub fn llvm_box_header(&mut self, header: boxed::Header) -> LLVMValueRef {\n        unsafe {\n            let llvm_i8 = LLVMInt8TypeInContext(self.llx);\n            let llvm_type = self.box_header_llvm_type();\n\n            let members = &mut [\n                LLVMConstInt(llvm_i8, header.type_tag() as u64, 0),\n                LLVMConstInt(llvm_i8, header.alloc_type() as u64, 0),\n            ];\n\n            LLVMConstNamedStruct(llvm_type, members.as_mut_ptr(), members.len() as u32)\n        }\n    }\n\n    pub fn llvm_enum_attr_for_name(\n        &mut self,\n        attr_name: &str,\n        attr_value: u64,\n    ) -> LLVMAttributeRef {\n        llvm_enum_attr_for_name(self.llx, attr_name, attr_value)\n    }\n\n    pub fn llvm_md_kind_id_for_name(&mut self, md_name: &str) -> u32 {\n        llvm_md_kind_id_for_name(self.llx, md_name)\n    }\n\n    pub fn llvm_boxed_align_attr(&self) -> LLVMAttributeRef {\n        self.boxed_align_attr\n    }\n\n    pub fn llvm_noalias_attr(&self) -> LLVMAttributeRef {\n        self.noalias_attr\n    }\n\n    pub fn add_invariant_load_metadata(&self, loaded_value: LLVMValueRef) {\n        unsafe {\n            LLVMSetMetadata(\n                loaded_value,\n                self.invariant_load_md_kind_id,\n                LLVMMetadataAsValue(self.llx, self.empty_md_node),\n            );\n        }\n    }\n\n    /// Adds range metadata to annotate our native `Char` type with valid Unicode codepoint ranges\n    pub fn add_char_codepoint_range_metadata(&mut self, llvm_value: LLVMValueRef) {\n        unsafe {\n            // Valid Unicode codepoints are effectively 21bit values with a hole in the middle\n            let llvm_char_type = LLVMInt32TypeInContext(self.llx);\n            let mut llvm_range_values: Vec<LLVMMetadataRef> = [0x0000, 0xD800, 0xE000, 0x11_0000]\n                .iter()\n                .map(|value| LLVMConstInt(llvm_char_type, *value as u64, 0))\n                .map(|value| LLVMValueAsMetadata(value))\n                .collect();\n\n            let codepoint_range_md = LLVMMDNodeInContext2(\n                self.llx,\n                llvm_range_values.as_mut_ptr(),\n                llvm_range_values.len(),\n            );\n            let range_md_kind_id = self.llvm_md_kind_id_for_name(\"range\");\n\n            LLVMSetMetadata(\n                llvm_value,\n                range_md_kind_id,\n                LLVMMetadataAsValue(self.llx, codepoint_range_md),\n            );\n        }\n    }\n\n    pub fn add_boxed_param_attrs(\n        &mut self,\n        function: LLVMValueRef,\n        param_index: u32,\n        no_capture: bool,\n    ) {\n        unsafe {\n            for &common_attr in &[\n                self.boxed_dereferenceable_attr,\n                self.boxed_align_attr,\n                self.readonly_attr,\n                self.noalias_attr,\n            ] {\n                // Parameters are offset by 1\n                LLVMAddAttributeAtIndex(function, param_index + 1, common_attr);\n            }\n\n            if no_capture {\n                LLVMAddAttributeAtIndex(function, param_index + 1, self.nocapture_attr);\n            }\n        }\n    }\n\n    pub fn add_boxed_return_attrs(&mut self, function: LLVMValueRef) {\n        unsafe {\n            for &common_attr in &[\n                self.boxed_dereferenceable_attr,\n                self.boxed_align_attr,\n                self.noalias_attr,\n            ] {\n                LLVMAddAttributeAtIndex(function, LLVMAttributeReturnIndex, common_attr);\n            }\n        }\n    }\n\n    pub fn add_boxed_load_metadata(&mut self, loaded_value: LLVMValueRef) {\n        unsafe {\n            for &(kind_id, md_node) in &[\n                (\n                    self.dereferenceable_md_kind_id,\n                    self.boxed_dereferenceable_md_node,\n                ),\n                (self.align_md_kind_id, self.boxed_align_md_node),\n            ] {\n                LLVMSetMetadata(\n                    loaded_value,\n                    kind_id,\n                    LLVMMetadataAsValue(self.llx, md_node),\n                );\n            }\n        }\n    }\n\n    pub fn finish_module(&mut self, module: LLVMModuleRef) {\n        use llvm_sys::analysis::*;\n        use std::{env, ptr};\n\n        unsafe {\n            let mut error: *mut libc::c_char = ptr::null_mut();\n\n            // Dump\n            if env::var_os(\"ARRET_DUMP_LLVM\").is_some() {\n                LLVMDumpModule(module);\n            }\n\n            // Verify\n            LLVMVerifyModule(\n                module,\n                LLVMVerifierFailureAction::LLVMAbortProcessAction,\n                &mut error as *mut _,\n            );\n            LLVMDisposeMessage(error);\n\n            // Optimise\n            LLVMRunPassManager(self.module_pass_manager, module);\n        }\n    }\n}\n\nimpl Drop for TargetCtx {\n    fn drop(&mut self) {\n        unsafe {\n            LLVMDisposeTargetData(self.target_data);\n            LLVMDisposePassManager(self.module_pass_manager);\n            LLVMContextDispose(self.llx);\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/target_machine.rs",
    "content": "use std::{ffi, ptr};\n\nuse llvm_sys::core::*;\nuse llvm_sys::target_machine::*;\n\nenum TripleString {\n    Cross(ffi::CString),\n    LlvmDefault(*mut libc::c_char),\n}\n\nimpl TripleString {\n    fn as_ptr(&self) -> *const libc::c_char {\n        match self {\n            TripleString::Cross(cross_triple) => cross_triple.as_ptr() as *const _,\n            TripleString::LlvmDefault(llvm_default) => *llvm_default,\n        }\n    }\n}\n\nimpl Drop for TripleString {\n    fn drop(&mut self) {\n        if let TripleString::LlvmDefault(llvm_default) = self {\n            unsafe {\n                LLVMDisposeMessage(*llvm_default);\n            }\n        }\n    }\n}\n\npub fn create_target_machine(\n    cross_triple: Option<&str>,\n    reloc_mode: LLVMRelocMode,\n    code_model: LLVMCodeModel,\n) -> LLVMTargetMachineRef {\n    let cross_triple = cross_triple.map(|cross_triple| ffi::CString::new(cross_triple).unwrap());\n\n    unsafe {\n        let mut target: LLVMTargetRef = ptr::null_mut();\n\n        let triple_string = cross_triple\n            .map(|cross_triple| TripleString::Cross(ffi::CString::new(cross_triple).unwrap()))\n            .unwrap_or_else(|| TripleString::LlvmDefault(LLVMGetDefaultTargetTriple()));\n\n        let mut error: *mut libc::c_char = ptr::null_mut();\n        if LLVMGetTargetFromTriple(triple_string.as_ptr(), &mut target, &mut error as *mut _) != 0 {\n            panic!(\n                \"LLVMGetTargetFromTriple({:?}): {}\",\n                ffi::CStr::from_ptr(triple_string.as_ptr())\n                    .to_str()\n                    .unwrap(),\n                ffi::CStr::from_ptr(error).to_str().unwrap()\n            );\n        }\n\n        LLVMCreateTargetMachine(\n            target,\n            triple_string.as_ptr(),\n            ptr::null(),\n            ptr::null(),\n            LLVMCodeGenOptLevel::LLVMCodeGenLevelDefault,\n            reloc_mode,\n            code_model,\n        )\n    }\n}\n"
  },
  {
    "path": "compiler/codegen/vector_gen.rs",
    "content": "use llvm_sys::core::*;\nuse llvm_sys::prelude::*;\nuse llvm_sys::LLVMIntPredicate;\n\nuse arret_runtime::boxed;\n\nuse crate::codegen::fun_gen::FunCtx;\nuse crate::codegen::target_gen::TargetCtx;\nuse crate::libcstr;\n\nfn load_boxed_external_vector_len(\n    tcx: &mut TargetCtx,\n    fcx: &mut FunCtx,\n    llvm_boxed_vector: LLVMValueRef,\n) -> LLVMValueRef {\n    unsafe {\n        let boxed_external_vector_ptr_type =\n            LLVMPointerType(tcx.boxed_external_vector_llvm_type(), 0);\n\n        let llvm_boxed_external_vector = LLVMBuildBitCast(\n            fcx.builder,\n            llvm_boxed_vector,\n            boxed_external_vector_ptr_type,\n            libcstr!(\"boxed_external_vector\"),\n        );\n\n        let vector_external_len_ptr = LLVMBuildStructGEP(\n            fcx.builder,\n            llvm_boxed_external_vector,\n            2,\n            libcstr!(\"vector_external_len_ptr\"),\n        );\n\n        let llvm_vector_external_len = LLVMBuildLoad(\n            fcx.builder,\n            vector_external_len_ptr,\n            libcstr!(\"vector_external_len\"),\n        );\n        tcx.add_invariant_load_metadata(llvm_vector_external_len);\n\n        llvm_vector_external_len\n    }\n}\n\npub(crate) fn load_boxed_vector_len(\n    tcx: &mut TargetCtx,\n    fcx: &mut FunCtx,\n    llvm_boxed_vector: LLVMValueRef,\n) -> LLVMValueRef {\n    use arret_runtime::boxed::Vector;\n\n    unsafe {\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n        let llvm_i64 = LLVMInt64TypeInContext(tcx.llx);\n\n        let vector_inline_len_ptr = LLVMBuildStructGEP(\n            fcx.builder,\n            llvm_boxed_vector,\n            1,\n            libcstr!(\"vector_inline_len_ptr\"),\n        );\n\n        let llvm_vector_inline_len = LLVMBuildLoad(\n            fcx.builder,\n            vector_inline_len_ptr,\n            libcstr!(\"vector_inline_len\"),\n        );\n        tcx.add_invariant_load_metadata(llvm_vector_inline_len);\n\n        let mut llvm_range_values = [\n            LLVMValueAsMetadata(LLVMConstInt(llvm_i32, 0, 0)),\n            LLVMValueAsMetadata(LLVMConstInt(\n                llvm_i32,\n                (Vector::<boxed::Any>::EXTERNAL_INLINE_LEN + 1) as u64,\n                0,\n            )),\n        ];\n\n        let range_md_kind_id = tcx.llvm_md_kind_id_for_name(\"vector_inline_len_range\");\n        let vector_inline_len_range_md = LLVMMDNodeInContext2(\n            tcx.llx,\n            llvm_range_values.as_mut_ptr(),\n            llvm_range_values.len(),\n        );\n        LLVMSetMetadata(\n            llvm_vector_inline_len,\n            range_md_kind_id,\n            LLVMMetadataAsValue(tcx.llx, vector_inline_len_range_md),\n        );\n\n        let llvm_vector_is_external = LLVMBuildICmp(\n            fcx.builder,\n            LLVMIntPredicate::LLVMIntEQ,\n            llvm_vector_inline_len,\n            LLVMConstInt(\n                llvm_i32,\n                Vector::<boxed::Any>::EXTERNAL_INLINE_LEN as u64,\n                0,\n            ),\n            libcstr!(\"vector_is_external\"),\n        );\n\n        let mut external_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"external_vector\"));\n\n        let mut inline_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"inline_vector\"));\n\n        let cont_block =\n            LLVMAppendBasicBlockInContext(tcx.llx, fcx.function, libcstr!(\"vector_len_cont\"));\n\n        LLVMBuildCondBr(\n            fcx.builder,\n            llvm_vector_is_external,\n            external_block,\n            inline_block,\n        );\n\n        let mut llvm_external_vector_len = {\n            LLVMPositionBuilderAtEnd(fcx.builder, external_block);\n            let llvm_value = load_boxed_external_vector_len(tcx, fcx, llvm_boxed_vector);\n\n            LLVMBuildBr(fcx.builder, cont_block);\n            llvm_value\n        };\n\n        let mut llvm_vector_inline_len_ext = {\n            LLVMPositionBuilderAtEnd(fcx.builder, inline_block);\n            let llvm_value = LLVMBuildZExt(\n                fcx.builder,\n                llvm_vector_inline_len,\n                llvm_i64,\n                libcstr!(\"vector_inline_len_ext\"),\n            );\n\n            LLVMBuildBr(fcx.builder, cont_block);\n            llvm_value\n        };\n\n        LLVMPositionBuilderAtEnd(fcx.builder, cont_block);\n        let phi_value = LLVMBuildPhi(fcx.builder, llvm_i64, libcstr!(\"vector_len\"));\n\n        LLVMAddIncoming(\n            phi_value,\n            &mut llvm_external_vector_len as *mut _,\n            &mut external_block as *mut _,\n            1,\n        );\n        LLVMAddIncoming(\n            phi_value,\n            &mut llvm_vector_inline_len_ext as *mut _,\n            &mut inline_block as *mut _,\n            1,\n        );\n\n        phi_value\n    }\n}\n\nfn load_boxed_inline_vector_member(\n    tcx: &mut TargetCtx,\n    fcx: &mut FunCtx,\n    llvm_boxed_vector: LLVMValueRef,\n    member_index: usize,\n) -> LLVMValueRef {\n    unsafe {\n        let boxed_inline_vector_ptr_type = LLVMPointerType(tcx.boxed_inline_vector_llvm_type(), 0);\n\n        let llvm_boxed_inline_vector = LLVMBuildBitCast(\n            fcx.builder,\n            llvm_boxed_vector,\n            boxed_inline_vector_ptr_type,\n            libcstr!(\"boxed_inline_vector\"),\n        );\n\n        let value_ptr = LLVMBuildStructGEP(\n            fcx.builder,\n            llvm_boxed_inline_vector,\n            // Skip the header and inline len\n            (2 + member_index) as u32,\n            libcstr!(\"vector_member_ptr\"),\n        );\n\n        let llvm_value = LLVMBuildLoad(fcx.builder, value_ptr, libcstr!(\"vector_member\"));\n\n        tcx.add_invariant_load_metadata(llvm_value);\n        tcx.add_boxed_load_metadata(llvm_value);\n        llvm_value\n    }\n}\n\nfn load_boxed_external_vector_member(\n    tcx: &mut TargetCtx,\n    fcx: &mut FunCtx,\n    llvm_boxed_vector: LLVMValueRef,\n    known_vector_len: usize,\n    member_index: usize,\n) -> LLVMValueRef {\n    use arret_runtime::persistent::vector::NODE_SIZE;\n\n    const TREE_PTR_INDEX: u32 = 3;\n    const TAIL_PTR_INDEX: u32 = 4;\n\n    let (node_gep_index, element_array_index) = if known_vector_len <= NODE_SIZE {\n        (TAIL_PTR_INDEX, member_index as u64)\n    } else if known_vector_len <= (NODE_SIZE * 2) {\n        if member_index < NODE_SIZE {\n            (TREE_PTR_INDEX, member_index as u64)\n        } else {\n            (TAIL_PTR_INDEX, (member_index - NODE_SIZE) as u64)\n        }\n    } else {\n        todo!(\"loading member of vector of length {}\", known_vector_len);\n    };\n\n    unsafe {\n        let llvm_i32 = LLVMInt32TypeInContext(tcx.llx);\n\n        let boxed_external_vector_ptr_type =\n            LLVMPointerType(tcx.boxed_external_vector_llvm_type(), 0);\n\n        let llvm_boxed_external_vector = LLVMBuildBitCast(\n            fcx.builder,\n            llvm_boxed_vector,\n            boxed_external_vector_ptr_type,\n            libcstr!(\"boxed_external_vector\"),\n        );\n\n        let vector_node_ptr_ptr = LLVMBuildStructGEP(\n            fcx.builder,\n            llvm_boxed_external_vector,\n            node_gep_index,\n            libcstr!(\"vector_node_ptr_ptr\"),\n        );\n\n        let vector_node_ptr = LLVMBuildLoad(\n            fcx.builder,\n            vector_node_ptr_ptr,\n            libcstr!(\"vector_node_ptr\"),\n        );\n        tcx.add_invariant_load_metadata(vector_node_ptr);\n\n        let element_ptr_gep_indices = &mut [\n            LLVMConstInt(llvm_i32, 0, 0),\n            // Skip the refcount\n            LLVMConstInt(llvm_i32, 1, 0),\n            LLVMConstInt(llvm_i32, element_array_index, 0),\n        ];\n\n        let vector_node_element_ptr = LLVMBuildInBoundsGEP(\n            fcx.builder,\n            vector_node_ptr,\n            element_ptr_gep_indices.as_mut_ptr(),\n            element_ptr_gep_indices.len() as u32,\n            libcstr!(\"vector_node_element_ptr\"),\n        );\n\n        let llvm_value = LLVMBuildLoad(\n            fcx.builder,\n            vector_node_element_ptr,\n            libcstr!(\"vector_member\"),\n        );\n\n        tcx.add_invariant_load_metadata(llvm_value);\n        tcx.add_boxed_load_metadata(llvm_value);\n        llvm_value\n    }\n}\n\npub(crate) fn load_boxed_vector_member(\n    tcx: &mut TargetCtx,\n    fcx: &mut FunCtx,\n    llvm_boxed_vector: LLVMValueRef,\n    known_vector_len: usize,\n    member_index: usize,\n) -> LLVMValueRef {\n    if known_vector_len <= boxed::Vector::<boxed::Any>::MAX_INLINE_LEN {\n        load_boxed_inline_vector_member(tcx, fcx, llvm_boxed_vector, member_index)\n    } else {\n        load_boxed_external_vector_member(\n            tcx,\n            fcx,\n            llvm_boxed_vector,\n            known_vector_len,\n            member_index,\n        )\n    }\n}\n"
  },
  {
    "path": "compiler/context.rs",
    "content": "use crate::hir::PackagePaths;\nuse crate::rfi;\nuse crate::source::SourceLoader;\n\nuse std::collections::{HashMap, HashSet};\nuse std::sync::Arc;\nuse std::{hash, path};\n\nuse codespan_reporting::diagnostic::Diagnostic;\n\nuse arret_syntax::datum::Datum;\nuse arret_syntax::span::{FileId, Span};\n\nuse crate::hir;\nuse crate::hir::exports::Exports;\nuse crate::hir::import;\nuse crate::hir::loader::{LoadedModule, ModuleName};\nuse crate::hir::lowering::LoweredModule;\nuse crate::promise::PromiseMap;\nuse crate::reporting::diagnostic_for_syntax_error;\nuse crate::reporting::errors_to_diagnostics;\nuse crate::source::SourceFile;\nuse crate::ty;\nuse crate::typeck::infer;\n\nnew_global_id_type!(\n    ModuleId,\n    u32,\n    std::sync::atomic::AtomicU32,\n    std::num::NonZeroU32\n);\n\npub(crate) type ModuleImports = HashMap<ModuleName, Arc<Module>>;\n\npub struct LinkedLibrary {\n    _loaded: libloading::Library,\n    target_path: Box<path::Path>,\n}\n\nimpl LinkedLibrary {\n    pub fn target_path(&self) -> &path::Path {\n        &self.target_path\n    }\n}\n\n/// Module being compiled until type inference\n///\n/// This represents both Arret and RFI libraries\npub(crate) struct Module {\n    pub module_id: ModuleId,\n\n    pub imports: ModuleImports,\n    pub defs: Vec<hir::Def<hir::Inferred>>,\n    pub inferred_locals: Arc<HashMap<hir::LocalId, ty::Ref<ty::Poly>>>,\n    pub exports: Exports,\n    pub main_local_id: Option<hir::LocalId>,\n\n    pub linked_library: Option<Arc<LinkedLibrary>>,\n}\n\nimpl PartialEq for Module {\n    fn eq(&self, other: &Self) -> bool {\n        self.module_id == other.module_id\n    }\n}\n\nimpl Eq for Module {}\n\nimpl hash::Hash for Module {\n    fn hash<H: hash::Hasher>(&self, state: &mut H) {\n        state.write_u32(self.module_id.get());\n    }\n}\n\ntype CachedModule = Result<Arc<Module>, Vec<Diagnostic<FileId>>>;\ntype UncachedModule = Result<Module, Vec<Diagnostic<FileId>>>;\n\n/// Finds all transitive dependencies for a set of imports\n///\n/// This is inclusive of the imports themselves.\nfn transitive_deps(imports: &ModuleImports) -> HashSet<Arc<Module>> {\n    let mut all_deps: HashSet<Arc<Module>> = imports.values().cloned().collect();\n\n    for import in imports.values() {\n        all_deps.extend(transitive_deps(&import.imports).into_iter());\n    }\n\n    all_deps\n}\n\npub(crate) fn prims_to_module(exports: Exports) -> Module {\n    Module {\n        module_id: ModuleId::alloc(),\n\n        imports: HashMap::new(),\n        defs: vec![],\n        inferred_locals: Arc::new(HashMap::new()),\n        exports,\n        main_local_id: None,\n\n        linked_library: None,\n    }\n}\n\nfn rfi_library_to_module(span: Span, rfi_library: rfi::Library) -> Module {\n    use crate::hir::var_id::LocalIdAlloc;\n    use crate::ty::Ty;\n\n    use arret_syntax::datum::DataStr;\n\n    let rfi::Library {\n        loaded,\n        target_path,\n        exported_funs,\n    } = rfi_library;\n\n    let mut lia = LocalIdAlloc::new();\n\n    let mut exports = HashMap::with_capacity(exported_funs.len());\n    let mut defs = Vec::with_capacity(exported_funs.len());\n    let mut inferred_locals = HashMap::with_capacity(exported_funs.len());\n\n    for (fun_name, rust_fun) in exported_funs.into_vec().into_iter() {\n        let local_id = lia.alloc_mut();\n        let arret_type: ty::Ref<ty::Poly> =\n            Ty::Fun(Box::new(rust_fun.arret_fun_type().clone())).into();\n\n        let fun_name_data_str: DataStr = fun_name.into();\n\n        let def = hir::Def::<hir::Inferred> {\n            span,\n            macro_invocation_span: None,\n            destruc: hir::destruc::Destruc::Scalar(\n                span,\n                hir::destruc::Scalar::new(\n                    Some(local_id),\n                    fun_name_data_str.clone(),\n                    arret_type.clone(),\n                ),\n            ),\n            value_expr: hir::Expr {\n                result_ty: arret_type.clone(),\n                kind: hir::ExprKind::RustFun(rust_fun),\n            },\n        };\n\n        defs.push(def);\n        inferred_locals.insert(local_id, arret_type);\n        exports.insert(fun_name_data_str, hir::scope::Binding::Var(None, local_id));\n    }\n\n    Module {\n        module_id: ModuleId::alloc(),\n\n        imports: HashMap::new(),\n        defs,\n        inferred_locals: Arc::new(inferred_locals),\n        exports,\n\n        main_local_id: None,\n        linked_library: Some(Arc::new(LinkedLibrary {\n            _loaded: loaded,\n            target_path,\n        })),\n    }\n}\n\n/// Shared context for compilation\n///\n/// This isn't specific to a given program or REPL session. It acts as a global cache of compiled\n/// source files and Rust libraries; it should be reused whenever possible.\npub struct CompileCtx {\n    package_paths: PackagePaths,\n    enable_optimisations: bool,\n\n    source_loader: SourceLoader,\n    rfi_loader: rfi::Loader,\n\n    modules_by_name: PromiseMap<ModuleName, CachedModule>,\n}\n\nimpl CompileCtx {\n    pub fn new(package_paths: PackagePaths, enable_optimisations: bool) -> Self {\n        use crate::hir::exports;\n        use std::iter;\n\n        // These modules are always loaded\n        let initial_modules = iter::once((\"primitives\", exports::prims_exports()))\n            .chain(iter::once((\"types\", exports::tys_exports())))\n            .map(|(terminal_name, exports)| {\n                let prims_module = prims_to_module(exports);\n\n                (\n                    ModuleName::new(\n                        \"arret\".into(),\n                        vec![\"internal\".into()],\n                        (*terminal_name).into(),\n                    ),\n                    Ok(Arc::new(prims_module)),\n                )\n            });\n\n        Self {\n            package_paths,\n            enable_optimisations,\n\n            source_loader: SourceLoader::new(),\n            rfi_loader: rfi::Loader::new(),\n            modules_by_name: PromiseMap::new(initial_modules),\n        }\n    }\n\n    pub fn package_paths(&self) -> &PackagePaths {\n        &self.package_paths\n    }\n\n    pub fn enable_optimisations(&self) -> bool {\n        self.enable_optimisations\n    }\n\n    pub fn source_loader(&self) -> &SourceLoader {\n        &self.source_loader\n    }\n\n    pub(crate) fn rfi_loader(&self) -> &rfi::Loader {\n        &self.rfi_loader\n    }\n\n    /// Returns a module for the given module name\n    ///\n    /// This returns a cached module; the module will only be compiled once per `CompileCtx`\n    /// instance. If the module is being compiled on another thread this will block until the\n    /// compilation is finished.\n    fn get_module_by_name(&self, span: Span, module_name: ModuleName) -> CachedModule {\n        self.modules_by_name\n            .get_or_insert_with(\n                module_name.clone(),\n                move || match hir::loader::load_module_by_name(self, span, &module_name) {\n                    Ok(LoadedModule::Source(source_file)) => {\n                        self.source_file_to_module(&source_file).map(Arc::new)\n                    }\n                    Ok(LoadedModule::Rust(rfi_library)) => {\n                        Ok(Arc::new(rfi_library_to_module(span, rfi_library)))\n                    }\n                    Err(err) => Err(vec![err.into()]),\n                },\n            )\n    }\n\n    /// Returns an uncached module for a source file\n    pub(crate) fn source_file_to_module(&self, source_file: &SourceFile) -> UncachedModule {\n        let data = source_file\n            .parsed()\n            .map_err(|err| vec![diagnostic_for_syntax_error(&err)])?;\n\n        self.data_to_module(data)\n    }\n\n    /// Collects all imports for a module's syntax data\n    ///\n    /// This attempts to import modules concurrently where possible\n    pub(crate) fn imports_for_data<'a>(\n        &self,\n        data: impl Iterator<Item = &'a Datum>,\n    ) -> Result<ModuleImports, Vec<Diagnostic<FileId>>> {\n        let imported_module_names =\n            import::collect_imported_module_names(data).map_err(errors_to_diagnostics)?;\n        let import_count = imported_module_names.len();\n\n        let loaded_module_results: Vec<(ModuleName, CachedModule)> = imported_module_names\n            .into_iter()\n            .map(|(module_name, span)| {\n                let module = self.get_module_by_name(span, module_name.clone());\n                (module_name, module)\n            })\n            .collect();\n\n        let mut diagnostics = Vec::<Diagnostic<FileId>>::new();\n\n        let mut imports = HashMap::<ModuleName, Arc<Module>>::with_capacity(import_count);\n\n        for (module_name, loaded_module_result) in loaded_module_results {\n            match loaded_module_result {\n                Ok(module) => {\n                    imports.insert(module_name, module);\n                }\n                Err(mut new_diagnostics) => diagnostics.append(&mut new_diagnostics),\n            }\n        }\n\n        if !diagnostics.is_empty() {\n            return Err(diagnostics);\n        }\n\n        Ok(imports)\n    }\n\n    /// Returns an uncached module for syntax data\n    fn data_to_module(&self, data: &[Datum]) -> UncachedModule {\n        let imports = self.imports_for_data(data.iter())?;\n        let lowered_module =\n            hir::lowering::lower_data(&imports, data).map_err(errors_to_diagnostics)?;\n\n        let LoweredModule {\n            defs: lowered_defs,\n            exports,\n            main_local_id,\n        } = lowered_module;\n\n        let imported_inferred_vars = transitive_deps(&imports)\n            .into_iter()\n            .map(|module| (module.module_id, module.inferred_locals.clone()))\n            .collect();\n\n        let inferred_module = infer::infer_module(&imported_inferred_vars, lowered_defs)\n            .map_err(errors_to_diagnostics)?;\n\n        let infer::InferredModule {\n            defs: inferred_defs,\n            inferred_locals,\n        } = inferred_module;\n\n        Ok(Module {\n            module_id: ModuleId::alloc(),\n\n            imports,\n            defs: inferred_defs,\n            inferred_locals: Arc::new(inferred_locals),\n            exports,\n            main_local_id,\n\n            linked_library: None,\n        })\n    }\n}\n"
  },
  {
    "path": "compiler/hir/destruc.rs",
    "content": "use arret_syntax::datum::DataStr;\nuse arret_syntax::span::Span;\n\nuse crate::hir;\nuse crate::ty;\nuse crate::ty::Ty;\n\n#[derive(Debug, PartialEq, Clone)]\npub enum Destruc<P: hir::Phase> {\n    Scalar(Span, Scalar<P>),\n    List(Span, List<P>),\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct List<P: hir::Phase> {\n    fixed: Vec<Destruc<P>>,\n    rest: Option<Box<Scalar<P>>>,\n}\n\nimpl<P: hir::Phase> List<P> {\n    pub fn new(fixed: Vec<Destruc<P>>, rest: Option<Box<Scalar<P>>>) -> List<P> {\n        List { fixed, rest }\n    }\n\n    pub fn fixed(&self) -> &Vec<Destruc<P>> {\n        &self.fixed\n    }\n\n    pub fn rest(&self) -> &Option<Box<Scalar<P>>> {\n        &self.rest\n    }\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct Scalar<P: hir::Phase> {\n    /// ID of the local. If this is None it's treated as a wildcard.\n    local_id: Option<hir::LocalId>,\n    source_name: DataStr,\n    ty: P::DeclType,\n}\n\nimpl<P: hir::Phase> Scalar<P> {\n    pub fn new(local_id: Option<hir::LocalId>, source_name: DataStr, ty: P::DeclType) -> Scalar<P> {\n        Scalar {\n            local_id,\n            source_name,\n            ty,\n        }\n    }\n\n    pub fn local_id(&self) -> &Option<hir::LocalId> {\n        &self.local_id\n    }\n\n    pub fn source_name(&self) -> &DataStr {\n        &self.source_name\n    }\n\n    pub fn ty(&self) -> &P::DeclType {\n        &self.ty\n    }\n}\n\npub fn subst_list_destruc(\n    free_ty_polys: &mut impl Iterator<Item = ty::Ref<ty::Poly>>,\n    list: List<hir::Lowered>,\n) -> List<hir::Inferred> {\n    let fixed = list\n        .fixed\n        .into_iter()\n        .map(|fixed_destruc| subst_destruc(free_ty_polys, fixed_destruc))\n        .collect();\n\n    let rest = list\n        .rest\n        .map(|rest_destruc| Box::new(subst_scalar_destruc(free_ty_polys, *rest_destruc)));\n\n    List::new(fixed, rest)\n}\n\npub fn subst_scalar_destruc(\n    free_ty_polys: &mut impl Iterator<Item = ty::Ref<ty::Poly>>,\n    scalar: Scalar<hir::Lowered>,\n) -> Scalar<hir::Inferred> {\n    let Scalar {\n        local_id,\n        ty,\n        source_name,\n    } = scalar;\n\n    let poly_type = match ty {\n        hir::DeclTy::Known(poly) => poly,\n        hir::DeclTy::Free => free_ty_polys.next().unwrap(),\n    };\n\n    Scalar::new(local_id, source_name, poly_type)\n}\n\n/// Substitutes free types with their inferred types\n///\n/// `free_ty_polys` must be ordered in the same way the types appear in the destruc type in\n/// depth-first order\npub fn subst_destruc(\n    free_ty_polys: &mut impl Iterator<Item = ty::Ref<ty::Poly>>,\n    destruc: Destruc<hir::Lowered>,\n) -> Destruc<hir::Inferred> {\n    match destruc {\n        Destruc::Scalar(span, scalar) => {\n            Destruc::Scalar(span, subst_scalar_destruc(free_ty_polys, scalar))\n        }\n        Destruc::List(span, list) => Destruc::List(span, subst_list_destruc(free_ty_polys, list)),\n    }\n}\n\npub fn poly_for_list_destruc(list: &List<hir::Inferred>) -> ty::List<ty::Poly> {\n    let fixed_polys = list.fixed().iter().map(poly_for_destruc).collect();\n\n    let rest_poly = match list.rest() {\n        Some(rest) => rest.ty().clone(),\n        None => Ty::never().into(),\n    };\n\n    ty::List::new(fixed_polys, rest_poly)\n}\n\npub fn poly_for_destruc(destruc: &Destruc<hir::Inferred>) -> ty::Ref<ty::Poly> {\n    match destruc {\n        Destruc::Scalar(_, scalar) => scalar.ty().clone(),\n        Destruc::List(_, list) => poly_for_list_destruc(list).into(),\n    }\n}\n"
  },
  {
    "path": "compiler/hir/error.rs",
    "content": "use std::{error, fmt, io, iter, path, result};\n\nuse codespan_reporting::diagnostic::Diagnostic;\n\nuse arret_syntax::datum::DataStr;\nuse arret_syntax::error::Error as SyntaxError;\nuse arret_syntax::span::{FileId, Span};\n\nuse crate::hir::types::{str_for_purity, str_for_ty_ref};\nuse crate::reporting::{\n    diagnostic_for_syntax_error, new_primary_label, new_secondary_label, LocTrace,\n};\nuse crate::ty;\nuse crate::ty::purity;\n\n#[derive(Debug, PartialEq, Clone)]\npub struct ExpectedSym {\n    pub found: &'static str,\n    pub usage: &'static str,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct PolyArgIsNotTy {\n    pub arg_type: ty::Ref<ty::Poly>,\n    pub param_bound: ty::Ref<ty::Poly>,\n    pub param_span: Span,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct PolyArgIsNotPure {\n    pub arg_purity: purity::Ref,\n    pub param_span: Span,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct ExpectedPolyPurityArg {\n    pub found: &'static str,\n    pub param_span: Span,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub enum ErrorKind {\n    ExpectedValue(&'static str),\n    ExpectedTy(&'static str),\n    ExpectedTyCons(&'static str),\n    ExpectedParamList(&'static str),\n    ExpectedPolyVarsDecl(&'static str),\n    ExpectedMacroSpecList(&'static str),\n    ExpectedMacroRuleVec(&'static str),\n    ExpectedMacroRulePatternList(&'static str),\n    ExpectedMacroEllipsisEscape(&'static str),\n    ExpectedCompileErrorString(&'static str),\n    ExpectedImportFilterKeyword(&'static str),\n    ExpectedImportRenameMap(&'static str),\n    ExpectedRecordTyConsDecl(&'static str),\n    ExpectedRecordValueConsDecl(&'static str),\n    ExpectedRecordFieldDecl(&'static str),\n    ExpectedSym(Box<ExpectedSym>),\n    UnboundIdent(DataStr),\n    WrongArgCount(usize),\n    WrongCondArgCount,\n    WrongDefLikeArgCount(&'static str),\n    WrongDefRecordArgCount,\n    DefOutsideBody,\n    ExportOutsideModule,\n    NonDefInsideModule,\n    ExportInsideRepl,\n    PackageNotFound,\n    ModuleNotFound(Box<path::Path>),\n    NoMacroRule(Box<[Span]>),\n    DuplicateDef(Option<Span>, DataStr),\n    MultipleZeroOrMoreMatch(Span),\n    NoVecDestruc,\n    UserError(DataStr),\n    ReadError(Box<path::Path>),\n    SyntaxError(SyntaxError),\n    RustFunError(Box<str>),\n    BadListDestruc,\n    BadRestDestruc,\n    NoBindingVec,\n    BindingsNotVec(&'static str),\n    UnevenBindingVec,\n    BadPolyVarDecl,\n    UnsupportedLiteralType,\n    VarPurityBound,\n    NoParamDecl,\n    NoPolyVarsDecl,\n    UnsupportedImportFilter,\n    MacroMultiPatternRef(Box<[Span]>),\n    MacroNoPatternRef,\n    MacroNoTemplateVars,\n    MacroBadEllipsis,\n    MacroBadSetPattern,\n    WrongMacroRuleVecCount(usize),\n    NoMacroType,\n    BadMacroType,\n    BadImportSet,\n    NonFunPolyTy,\n    ShortModuleName,\n    AnonymousPolymorphicParam,\n    PolyArgIsNotTy(Box<PolyArgIsNotTy>),\n    PolyArgIsNotPure(Box<PolyArgIsNotPure>),\n    ExpectedPolyPurityArg(Box<ExpectedPolyPurityArg>),\n    UnusedPolyPurityParam(purity::PVarId),\n    UnusedPolyTyParam(ty::TVarId),\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct Error {\n    loc_trace: LocTrace,\n    kind: ErrorKind,\n}\n\npub type Result<T, E = Error> = result::Result<T, E>;\n\nimpl Error {\n    pub fn new(span: Span, kind: ErrorKind) -> Error {\n        Error {\n            loc_trace: span.into(),\n            kind,\n        }\n    }\n\n    pub fn kind(&self) -> &ErrorKind {\n        &self.kind\n    }\n\n    pub fn from_module_io(span: Span, path: &path::Path, error: &io::Error) -> Error {\n        match error.kind() {\n            io::ErrorKind::NotFound => Error::new(span, ErrorKind::ModuleNotFound(path.into())),\n            _ => Error::new(span, ErrorKind::ReadError(path.into())),\n        }\n    }\n\n    pub fn with_macro_invocation_span(self, span: Span) -> Error {\n        Error {\n            loc_trace: self.loc_trace.with_macro_invocation(span),\n            ..self\n        }\n    }\n}\n\nimpl From<Error> for Diagnostic<FileId> {\n    fn from(error: Error) -> Diagnostic<FileId> {\n        let Error { loc_trace, kind } = error;\n        let origin = loc_trace.origin();\n\n        let diagnostic = match kind {\n            ErrorKind::ExpectedValue(found) => Diagnostic::error()\n                .with_message(format!(\"cannot take the value of a {}\", found))\n                .with_labels(vec![new_primary_label(origin, \"expected value\")]),\n\n            ErrorKind::ExpectedTy(found) => Diagnostic::error()\n                .with_message(format!(\"{} cannot be used as a type\", found))\n                .with_labels(vec![new_primary_label(origin, \"expected type\")]),\n\n            ErrorKind::ExpectedTyCons(found) => Diagnostic::error()\n                .with_message(format!(\"{} cannot be used as a type constructor\", found))\n                .with_labels(vec![new_primary_label(origin, \"expected type constructor\")]),\n\n            ErrorKind::ExpectedSym(details) => {\n                let ExpectedSym { found, usage } = *details;\n\n                Diagnostic::error()\n                    .with_message(format!(\"expected symbol, found {}\", found))\n                    .with_labels(vec![new_primary_label(\n                        origin,\n                        format!(\"expected {}\", usage),\n                    )])\n            }\n\n            ErrorKind::ExpectedParamList(found) => Diagnostic::error()\n                .with_message(format!(\n                    \"expected parameter declaration list, found {}\",\n                    found\n                ))\n                .with_labels(vec![new_primary_label(origin, \"expected parameter list\")]),\n\n            ErrorKind::ExpectedPolyVarsDecl(found) => Diagnostic::error()\n                .with_message(format!(\n                    \"expected polymorphic variable set, found {}\",\n                    found\n                ))\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected polymorphic variable set\",\n                )]),\n\n            ErrorKind::ExpectedMacroSpecList(found) => Diagnostic::error()\n                .with_message(format!(\n                    \"expected macro specification list, found {}\",\n                    found\n                ))\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected `(macro-rules ...)`\",\n                )]),\n\n            ErrorKind::ExpectedMacroRuleVec(found) => Diagnostic::error()\n                .with_message(format!(\"expected macro rule vector, found {}\", found))\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected `[pattern template]`\",\n                )]),\n\n            ErrorKind::ExpectedMacroRulePatternList(found) => Diagnostic::error()\n                .with_message(format!(\"expected macro rule pattern list, found {}\", found))\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected macro rule pattern list\",\n                )]),\n\n            ErrorKind::ExpectedMacroEllipsisEscape(found) => Diagnostic::error()\n                .with_message(format!(\"expected macro symbol to escape, found {}\", found))\n                .with_labels(vec![new_primary_label(origin, \"expected symbol\")]),\n\n            ErrorKind::ExpectedCompileErrorString(found) => Diagnostic::error()\n                .with_message(format!(\"expected error message string, found {}\", found))\n                .with_labels(vec![new_primary_label(origin, \"expected string\")]),\n\n            ErrorKind::ExpectedImportFilterKeyword(found) => Diagnostic::error()\n                .with_message(format!(\"expected import filter keyword, found {}\", found))\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected `:only`, `:exclude`, `:rename`, `:prefix` or `:prefixed`\",\n                )]),\n\n            ErrorKind::ExpectedImportRenameMap(found) => Diagnostic::error()\n                .with_message(format!(\"expected identifier rename map, found {}\", found))\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected identifier rename map\",\n                )]),\n\n            ErrorKind::ExpectedRecordTyConsDecl(found) => Diagnostic::error()\n                .with_message(format!(\n                    \"expected record type constuctor declaration, found {}\",\n                    found\n                ))\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected symbol or polymorphic constructor list\",\n                )]),\n\n            ErrorKind::ExpectedRecordValueConsDecl(found) => Diagnostic::error()\n                .with_message(format!(\n                    \"expected record value constructor declaration, found {}\",\n                    found\n                ))\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected record field list\",\n                )]),\n\n            ErrorKind::ExpectedRecordFieldDecl(found) => Diagnostic::error()\n                .with_message(format!(\n                    \"expected record field declaration, found {}\",\n                    found\n                ))\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected record field declaration\",\n                )]),\n\n            ErrorKind::UnboundIdent(ref ident) => {\n                let diagnostic = Diagnostic::error()\n                    .with_message(format!(\"unable to resolve `{}`\", ident))\n                    .with_labels(vec![new_primary_label(origin, \"not found in this scope\")]);\n\n                if ident.as_ref() == \"nil\" {\n                    diagnostic.with_notes(vec![\n                        \"Arret does not have a distinct `nil` value; consider using `()` instead\"\n                            .to_owned(),\n                    ])\n                } else {\n                    diagnostic\n                }\n            }\n\n            ErrorKind::WrongArgCount(expected) => {\n                let label_message = if expected == 1 {\n                    \"expected 1 argument\".to_owned()\n                } else {\n                    format!(\"expected {} arguments\", expected)\n                };\n\n                Diagnostic::error()\n                    .with_message(format!(\"wrong argument count; expected {}\", expected))\n                    .with_labels(vec![new_primary_label(origin, label_message)])\n            }\n\n            ErrorKind::WrongCondArgCount => Diagnostic::error()\n                .with_message(\"wrong argument count; expected 3\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected `(if test-expr true-expr false-expr)`\",\n                )]),\n\n            ErrorKind::WrongDefLikeArgCount(name) => Diagnostic::error()\n                .with_message(\"wrong argument count; expected 2\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    format!(\"expected `({} name definition)`\", name),\n                )]),\n\n            ErrorKind::WrongDefRecordArgCount => Diagnostic::error()\n                .with_message(\"wrong argument count; expected 2\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected `(defrecord ty-cons-decl value-cons-decl)`\",\n                )]),\n\n            ErrorKind::WrongMacroRuleVecCount(found) => Diagnostic::error()\n                .with_message(format!(\n                    \"expected macro rule vector with 2 elements, found {}\",\n                    found\n                ))\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected `[pattern template]`\",\n                )]),\n\n            ErrorKind::DefOutsideBody => Diagnostic::error()\n                .with_message(\"definition outside module body\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"not at top-level of module\",\n                )]),\n\n            ErrorKind::DuplicateDef(first_def_span, ref ident) => {\n                let diagnostic = Diagnostic::error()\n                    .with_message(format!(\"duplicate definition of `{}`\", ident));\n\n                let primary_label = new_primary_label(origin, \"second definition here\");\n\n                if let Some(first_def_span) = first_def_span {\n                    let secondary_label =\n                        new_secondary_label(first_def_span, \"first definition here\");\n\n                    diagnostic.with_labels(vec![primary_label, secondary_label])\n                } else {\n                    diagnostic.with_labels(vec![primary_label])\n                }\n            }\n\n            ErrorKind::ExportOutsideModule => Diagnostic::error()\n                .with_message(\"(export) outside of module body\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"not at top-level of module\",\n                )]),\n\n            ErrorKind::NonDefInsideModule => Diagnostic::error()\n                .with_message(\"value at top-level of module body\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"(import), (export) or definition expected\",\n                )]),\n\n            ErrorKind::ExportInsideRepl => Diagnostic::error()\n                .with_message(\"export not supported within REPL\")\n                .with_labels(vec![new_primary_label(origin, \"export not supported\")]),\n\n            ErrorKind::PackageNotFound => Diagnostic::error()\n                .with_message(\"package not found\")\n                .with_labels(vec![new_primary_label(origin, \"at this import\")]),\n\n            ErrorKind::ModuleNotFound(ref filename) => Diagnostic::error()\n                .with_message(format!(\n                    \"module not found at `{}`\",\n                    filename.to_string_lossy()\n                ))\n                .with_labels(vec![new_primary_label(origin, \"at this import\")]),\n\n            ErrorKind::NoMacroRule(pattern_spans) => Diagnostic::error()\n                .with_message(\"no matching macro rule\")\n                .with_labels(\n                    iter::once(new_primary_label(origin, \"at this macro invocation\"))\n                        .chain(pattern_spans.iter().map(|pattern_span| {\n                            new_secondary_label(*pattern_span, \"unmatched macro rule\")\n                        }))\n                        .collect(),\n                ),\n\n            ErrorKind::MultipleZeroOrMoreMatch(first_zero_or_more_span) => Diagnostic::error()\n                .with_message(\"multiple zero or more matches in the same sequence\")\n                .with_labels(vec![\n                    new_primary_label(origin, \"second zero or more match\"),\n                    new_secondary_label(first_zero_or_more_span, \"first zero or more match\"),\n                ]),\n\n            ErrorKind::NoVecDestruc => Diagnostic::error()\n                .with_message(\"vectors can only be used in a destructure in the form `[name Type]`\")\n                .with_labels(vec![new_primary_label(origin, \"unexpected vector\")]),\n\n            ErrorKind::UserError(ref message) => Diagnostic::error()\n                .with_message(message.as_ref())\n                .with_labels(vec![new_primary_label(origin, \"user error raised here\")]),\n\n            ErrorKind::ReadError(ref filename) => Diagnostic::error()\n                .with_message(format!(\"error reading `{}`\", filename.to_string_lossy()))\n                .with_labels(vec![new_primary_label(origin, \"at this import\")]),\n\n            ErrorKind::SyntaxError(ref err) => {\n                // Just proxy this\n                return diagnostic_for_syntax_error(err);\n            }\n\n            ErrorKind::RustFunError(ref message) => Diagnostic::error()\n                .with_message(\"error loading RFI module\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    message.clone().into_string(),\n                )]),\n\n            ErrorKind::BadListDestruc => Diagnostic::error()\n                .with_message(\"unsupported destructuring binding\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected variable name, list or `[name Type]`\",\n                )]),\n\n            ErrorKind::BadRestDestruc => Diagnostic::error()\n                .with_message(\"unsupported rest destructuring\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected variable name or `[name Type]`\",\n                )]),\n\n            ErrorKind::NoBindingVec => Diagnostic::error()\n                .with_message(\"binding vector expected\")\n                .with_labels(vec![new_primary_label(origin, \"expected vector argument\")]),\n\n            ErrorKind::BindingsNotVec(found) => Diagnostic::error()\n                .with_message(format!(\"binding vector expected, found {}\", found))\n                .with_labels(vec![new_primary_label(origin, \"vector expected\")]),\n\n            ErrorKind::UnevenBindingVec => Diagnostic::error()\n                .with_message(\"binding vector must have an even number of forms\")\n                .with_labels(vec![new_primary_label(origin, \"extra binding form\")]),\n\n            ErrorKind::BadPolyVarDecl => Diagnostic::error()\n                .with_message(\"bad polymorphic variable declaration\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected polymorphic variable name or `[name Bound]`\",\n                )]),\n\n            ErrorKind::UnsupportedLiteralType => Diagnostic::error()\n                .with_message(\"unsupported literal type\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected boolean, symbol, keyword, list or vector\",\n                )]),\n\n            ErrorKind::VarPurityBound => Diagnostic::error()\n                .with_message(\"purity variables cannot be bound by other variables\")\n                .with_labels(vec![new_primary_label(origin, \"expected `->` or `->!`\")]),\n\n            ErrorKind::NoParamDecl => Diagnostic::error()\n                .with_message(\"parameter declaration missing\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected parameter list argument\",\n                )]),\n\n            ErrorKind::NoPolyVarsDecl => Diagnostic::error()\n                .with_message(\"polymorphic variable declaration missing\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected polymorphic variable set argument\",\n                )]),\n\n            ErrorKind::UnsupportedImportFilter => Diagnostic::error()\n                .with_message(\"unsupported import filter\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected `:only`, `:exclude`, `:rename`, `:prefix` or `:prefixed`\",\n                )]),\n\n            ErrorKind::MacroMultiPatternRef(sub_var_spans) => Diagnostic::error()\n                .with_message(\"subtemplate references macro variables from multiple subpatterns\")\n                .with_labels(\n                    iter::once(new_primary_label(\n                        origin,\n                        \"subtemplate references multiple subpatterns\",\n                    ))\n                    .chain(sub_var_spans.iter().map(|sub_var_span| {\n                        new_secondary_label(*sub_var_span, \"referenced macro variable\")\n                    }))\n                    .collect(),\n                ),\n\n            ErrorKind::MacroNoTemplateVars => Diagnostic::error()\n                .with_message(\"subtemplate does not include any macro variables\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"subtemplate includes no variables\",\n                )]),\n\n            ErrorKind::MacroNoPatternRef => Diagnostic::error()\n                .with_message(\"subtemplate does not reference macro variables from any subpattern\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"subtemplate does not reference subpatterns\",\n                )]),\n\n            ErrorKind::MacroBadEllipsis => Diagnostic::error()\n                .with_message(\"unexpected ellipsis in macro rule\")\n                .with_labels(vec![new_primary_label(origin, \"expected `var ...`\")]),\n\n            ErrorKind::MacroBadSetPattern => Diagnostic::error()\n                .with_message(\"set patterns must either be empty or a zero or more match\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected `#{}` or `#{var ...}`\",\n                )]),\n\n            ErrorKind::NoMacroType => Diagnostic::error()\n                .with_message(\"missing macro type\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected `(macro-rules ...)`\",\n                )]),\n\n            ErrorKind::BadMacroType => Diagnostic::error()\n                .with_message(\"unsupported macro type\")\n                .with_labels(vec![new_primary_label(origin, \"expected `macro-rules`\")]),\n\n            ErrorKind::BadImportSet => Diagnostic::error()\n                .with_message(\"bad import set\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected module name vector or applied filter\",\n                )]),\n\n            ErrorKind::NonFunPolyTy => Diagnostic::error()\n                .with_message(\"polymorphism on non-function type\")\n                .with_labels(vec![new_primary_label(origin, \"expected function type\")]),\n\n            ErrorKind::ShortModuleName => Diagnostic::error()\n                .with_message(\"module name requires a least two components\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected vector of 2 or more symbols\",\n                )]),\n\n            ErrorKind::AnonymousPolymorphicParam => Diagnostic::error()\n                .with_message(\"polymorphic parameters must have a name\")\n                .with_labels(vec![new_primary_label(\n                    origin,\n                    \"expected polymorphic parameter name\",\n                )]),\n\n            ErrorKind::PolyArgIsNotTy(boxed_details) => {\n                let PolyArgIsNotTy {\n                    arg_type,\n                    param_bound,\n                    param_span,\n                } = *boxed_details;\n\n                Diagnostic::error()\n                    .with_message(\"mismatched types\")\n                    .with_labels(vec![\n                        new_primary_label(\n                            origin,\n                            format!(\n                                \"`{}` does not satisfy the lower bound of `{}`\",\n                                str_for_ty_ref(&arg_type),\n                                str_for_ty_ref(&param_bound)\n                            ),\n                        ),\n                        new_secondary_label(param_span, \"type parameter declared here\"),\n                    ])\n            }\n\n            ErrorKind::PolyArgIsNotPure(boxed_details) => {\n                let PolyArgIsNotPure {\n                    arg_purity,\n                    param_span,\n                } = *boxed_details;\n                Diagnostic::error()\n                    .with_message(\"mismatched purities\")\n                    .with_labels(vec![\n                        new_primary_label(\n                            origin,\n                            format!(\"`{}` is not pure\", str_for_purity(&arg_purity)),\n                        ),\n                        new_secondary_label(param_span, \"purity parameter declared here\"),\n                    ])\n            }\n\n            ErrorKind::ExpectedPolyPurityArg(boxed_details) => {\n                let ExpectedPolyPurityArg { found, param_span } = *boxed_details;\n\n                Diagnostic::error()\n                    .with_message(format!(\"{} cannot be used as a purity\", found))\n                    .with_labels(vec![\n                        new_primary_label(origin, \"expected purity\"),\n                        new_secondary_label(param_span, \"purity parameter declared here\"),\n                    ])\n            }\n\n            ErrorKind::UnusedPolyPurityParam(pvar) => Diagnostic::error()\n                .with_message(format!(\n                    \"unused polymorphic purity parameter `{}`\",\n                    pvar.source_name()\n                ))\n                .with_labels(vec![new_primary_label(\n                    pvar.span(),\n                    \"purity parameter declared here\",\n                )]),\n\n            ErrorKind::UnusedPolyTyParam(tvar) => Diagnostic::error()\n                .with_message(format!(\n                    \"unused polymorphic type parameter `{}`\",\n                    tvar.source_name()\n                ))\n                .with_labels(vec![new_primary_label(\n                    tvar.span(),\n                    \"type parameter declared here\",\n                )]),\n        };\n\n        loc_trace.label_macro_invocation(diagnostic)\n    }\n}\n\nimpl error::Error for Error {}\n\nimpl fmt::Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let diagnostic: Diagnostic<FileId> = self.clone().into();\n        f.write_str(&diagnostic.message)\n    }\n}\n\nimpl From<SyntaxError> for Error {\n    fn from(err: SyntaxError) -> Error {\n        Error::new(err.span(), ErrorKind::SyntaxError(err))\n    }\n}\n\nimpl From<Error> for Vec<Error> {\n    fn from(error: Error) -> Vec<Error> {\n        vec![error]\n    }\n}\n"
  },
  {
    "path": "compiler/hir/exports.rs",
    "content": "use std::collections::HashMap;\n\nuse arret_syntax::datum::DataStr;\n\nuse crate::hir::prim::PRIM_EXPORTS;\nuse crate::hir::scope::Binding;\nuse crate::hir::types::TY_EXPORTS;\n\npub type Exports = HashMap<DataStr, Binding>;\n\npub fn prims_exports() -> Exports {\n    PRIM_EXPORTS\n        .iter()\n        .map(|(name, binding)| ((*name).into(), binding.clone()))\n        .collect()\n}\n\npub fn tys_exports() -> Exports {\n    TY_EXPORTS\n        .iter()\n        .map(|(name, binding)| ((*name).into(), binding.clone()))\n        .collect()\n}\n"
  },
  {
    "path": "compiler/hir/import/filter.rs",
    "content": "use std::result;\n\nuse crate::hir::error::{Error, ErrorKind};\nuse crate::hir::exports::Exports;\nuse crate::hir::import::parse::{ParsedFilter, ParsedImportSet};\n\ntype Result<T> = result::Result<T, Vec<Error>>;\n\nfn apply_filter(filter: ParsedFilter, exports: &Exports) -> Result<Exports> {\n    match filter {\n        ParsedFilter::Only(only_spanned_names) => Ok(only_spanned_names\n            .into_vec()\n            .into_iter()\n            .map(|(span, name)| {\n                if let Some(binding) = exports.get(&name) {\n                    Ok((name, binding.clone()))\n                } else {\n                    Err(Error::new(span, ErrorKind::UnboundIdent(name)))\n                }\n            })\n            .collect::<result::Result<Exports, Error>>()?),\n\n        ParsedFilter::Exclude(exclude_spanned_names) => {\n            let mut exports = exports.clone();\n            let mut errors = vec![];\n\n            for (span, name) in exclude_spanned_names.into_vec().into_iter() {\n                if exports.remove(&name).is_none() {\n                    errors.push(Error::new(span, ErrorKind::UnboundIdent(name)));\n                }\n            }\n\n            if errors.is_empty() {\n                Ok(exports)\n            } else {\n                Err(errors)\n            }\n        }\n\n        ParsedFilter::Rename(rename_spanned_names) => {\n            let mut exports = exports.clone();\n            let mut errors = vec![];\n\n            for ((from_span, from_name), to_name) in rename_spanned_names.into_vec().into_iter() {\n                match exports.remove(&from_name) {\n                    Some(binding) => {\n                        exports.insert(to_name, binding);\n                    }\n                    None => {\n                        errors.push(Error::new(from_span, ErrorKind::UnboundIdent(from_name)));\n                    }\n                }\n            }\n\n            if errors.is_empty() {\n                Ok(exports)\n            } else {\n                Err(errors)\n            }\n        }\n\n        ParsedFilter::Prefix(prefix_name) => Ok(exports\n            .iter()\n            .map(|(name, binding)| (format!(\"{}{}\", prefix_name, name).into(), binding.clone()))\n            .collect()),\n    }\n}\n\n/// Applies the parsed import to the passed exports\n///\n/// If there are no filters to apply then `exports` will be directly returned.\npub fn filter_imported_exports(\n    parsed_import_set: ParsedImportSet,\n    exports: &Exports,\n) -> Result<Exports> {\n    match parsed_import_set {\n        ParsedImportSet::Module(_, _) => Ok(exports.clone()),\n        ParsedImportSet::Filter(filter, inner_parsed_import) => {\n            let inner_exports = filter_imported_exports(*inner_parsed_import, exports)?;\n            Ok(apply_filter(filter, &inner_exports)?)\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/hir/import/mod.rs",
    "content": "mod filter;\nmod parse;\n\nuse std::collections::HashMap;\n\nuse arret_syntax::datum::Datum;\nuse arret_syntax::span::Span;\n\nuse crate::hir::error::Error;\nuse crate::hir::loader::ModuleName;\n\npub use filter::filter_imported_exports;\npub use parse::{parse_import_set, ParsedFilter, ParsedImportSet};\n\npub fn try_extract_import_set(datum: &Datum) -> Option<&[Datum]> {\n    if let Datum::List(_, vs) = datum {\n        match vs.as_ref() {\n            [Datum::Sym(_, name), import_set @ ..] if name.as_ref() == \"import\" => Some(import_set),\n            _ => None,\n        }\n    } else {\n        None\n    }\n}\n\n/// Returns all unique imported module names for the passed module data\n///\n/// The value of the `HashMap` will be the first span where that module name occurs. This is\n/// intended to provide a stable location for error reporting.\npub fn collect_imported_module_names<'a>(\n    data: impl Iterator<Item = &'a Datum>,\n) -> Result<HashMap<ModuleName, Span>, Vec<Error>> {\n    let mut imported_module_names = HashMap::new();\n    let mut errors = vec![];\n\n    for datum in data {\n        if let Some(arg_data) = try_extract_import_set(datum) {\n            for arg_datum in arg_data {\n                match parse_import_set(arg_datum) {\n                    Ok(parsed_import) => {\n                        let (span, module_name) = parsed_import.into_spanned_module_name();\n                        imported_module_names.entry(module_name).or_insert(span);\n                    }\n                    Err(error) => {\n                        errors.push(error);\n                    }\n                }\n            }\n        }\n    }\n\n    if !errors.is_empty() {\n        return Err(errors);\n    }\n\n    Ok(imported_module_names)\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use std::collections::HashMap;\n    use std::result;\n\n    use arret_syntax::span::t2s;\n\n    use crate::hir::error::{Error, ErrorKind};\n    use crate::hir::exports::Exports;\n    use crate::hir::loader::ModuleName;\n    use crate::hir::prim::Prim;\n    use crate::hir::scope::Binding;\n\n    type Result<T> = result::Result<T, Vec<Error>>;\n\n    fn exports_for_import_set(datum: &str) -> Result<Exports> {\n        use arret_syntax::parser::datum_from_str;\n\n        let parsed_import = parse::parse_import_set(&datum_from_str(None, datum).unwrap())?;\n        let (span, module_name) = parsed_import.spanned_module_name();\n\n        if module_name == &ModuleName::new(\"lib\".into(), vec![], \"test\".into()) {\n            let mut exports = HashMap::new();\n            exports.insert(\"quote\".into(), Binding::Prim(Prim::Quote));\n            exports.insert(\"if\".into(), Binding::Prim(Prim::If));\n\n            Ok(filter::filter_imported_exports(parsed_import, &exports)?)\n        } else {\n            Err(vec![Error::new(span, ErrorKind::PackageNotFound)])\n        }\n    }\n\n    fn assert_exports_prim(exports: &Exports, name: &'static str, expected_prim: Prim) {\n        match exports.get(name) {\n            Some(Binding::Prim(actual_prim)) => {\n                assert_eq!(actual_prim, &expected_prim);\n            }\n            Some(other) => {\n                panic!(\"Non-prim binding {:?} for {}\", other, name);\n            }\n            None => {\n                panic!(\"Missing binding for {}\", name);\n            }\n        }\n    }\n\n    #[test]\n    fn basic_import() {\n        let j = \"[lib test]\";\n        let exports = exports_for_import_set(j).unwrap();\n\n        assert_exports_prim(&exports, \"quote\", Prim::Quote);\n        assert_exports_prim(&exports, \"if\", Prim::If);\n    }\n\n    #[test]\n    fn package_not_found() {\n        let j = \"[not found]\";\n        let t = \"^^^^^^^^^^^\";\n\n        let err = vec![Error::new(t2s(t), ErrorKind::PackageNotFound)];\n\n        assert_eq!(err, exports_for_import_set(j).unwrap_err());\n    }\n\n    #[test]\n    fn only_filter() {\n        let j = \"(:only [lib test] quote)\";\n        let exports = exports_for_import_set(j).unwrap();\n\n        assert_exports_prim(&exports, \"quote\", Prim::Quote);\n        assert!(!exports.contains_key(\"if\"));\n\n        let j = \"(:only [lib test] quote ifz)\";\n        let t = \"                        ^^^ \";\n        let err = vec![Error::new(t2s(t), ErrorKind::UnboundIdent(\"ifz\".into()))];\n\n        assert_eq!(err, exports_for_import_set(j).unwrap_err());\n    }\n\n    #[test]\n    fn exclude_filter() {\n        let j = \"(:exclude [lib test] if)\";\n        let exports = exports_for_import_set(j).unwrap();\n\n        assert_exports_prim(&exports, \"quote\", Prim::Quote);\n        assert!(!exports.contains_key(\"if\"));\n\n        let j = \"(:exclude [lib test] ifz)\";\n        let t = \"                     ^^^ \";\n        let err = vec![Error::new(t2s(t), ErrorKind::UnboundIdent(\"ifz\".into()))];\n\n        assert_eq!(err, exports_for_import_set(j).unwrap_err());\n    }\n\n    #[test]\n    fn rename_filter() {\n        let j = \"(:rename [lib test] {quote new-quote, if new-if})\";\n        let exports = exports_for_import_set(j).unwrap();\n\n        assert_exports_prim(&exports, \"new-quote\", Prim::Quote);\n        assert_exports_prim(&exports, \"new-if\", Prim::If);\n\n        let j = \"(:rename [lib test] {ifz new-ifz})\";\n        let t = \"                     ^^^          \";\n        let err = vec![Error::new(t2s(t), ErrorKind::UnboundIdent(\"ifz\".into()))];\n\n        assert_eq!(err, exports_for_import_set(j).unwrap_err());\n    }\n\n    #[test]\n    fn prefix_filter() {\n        let j = \"(:prefix [lib test] new-)\";\n        let exports = exports_for_import_set(j).unwrap();\n\n        assert_exports_prim(&exports, \"new-quote\", Prim::Quote);\n        assert_exports_prim(&exports, \"new-if\", Prim::If);\n    }\n\n    #[test]\n    fn prefixed_filter() {\n        let j = \"(:prefixed [lib test])\";\n        let exports = exports_for_import_set(j).unwrap();\n\n        assert_exports_prim(&exports, \"test/quote\", Prim::Quote);\n        assert_exports_prim(&exports, \"test/if\", Prim::If);\n    }\n}\n"
  },
  {
    "path": "compiler/hir/import/parse.rs",
    "content": "use std::result;\n\nuse arret_syntax::datum::{DataStr, Datum};\nuse arret_syntax::span::Span;\n\nuse crate::hir::error::{Error, ErrorKind};\nuse crate::hir::loader::ModuleName;\nuse crate::hir::util::{expect_ident, expect_spanned_ident};\n\ntype Result<T> = result::Result<T, Error>;\n\npub enum ParsedImportSet {\n    Module(Span, ModuleName),\n    Filter(ParsedFilter, Box<ParsedImportSet>),\n}\n\nimpl ParsedImportSet {\n    pub fn into_spanned_module_name(self) -> (Span, ModuleName) {\n        match self {\n            ParsedImportSet::Module(span, module_name) => (span, module_name),\n            ParsedImportSet::Filter(_, inner_import) => inner_import.into_spanned_module_name(),\n        }\n    }\n\n    pub fn spanned_module_name(&self) -> (Span, &ModuleName) {\n        match self {\n            ParsedImportSet::Module(span, module_name) => (*span, module_name),\n            ParsedImportSet::Filter(_, inner_import) => inner_import.spanned_module_name(),\n        }\n    }\n\n    pub fn module_name(&self) -> &ModuleName {\n        self.spanned_module_name().1\n    }\n}\n\npub enum ParsedFilter {\n    Only(Box<[(Span, DataStr)]>),\n    Exclude(Box<[(Span, DataStr)]>),\n    Rename(Box<[((Span, DataStr), DataStr)]>),\n    Prefix(DataStr),\n}\n\nfn parse_module_name(span: Span, name: &[Datum]) -> Result<ModuleName> {\n    if name.len() < 2 {\n        return Err(Error::new(span, ErrorKind::ShortModuleName));\n    }\n\n    let mut name_idents = name\n        .iter()\n        .map(|datum| Ok(expect_ident(datum, \"module name component\")?.clone()));\n\n    let package_name = name_idents.next().unwrap()?;\n    let terminal_name = name_idents.next_back().unwrap()?;\n    let name_components = name_idents.collect::<result::Result<Vec<_>, Error>>()?;\n\n    Ok(ModuleName::new(\n        package_name,\n        name_components,\n        terminal_name,\n    ))\n}\n\nfn parse_filter(\n    apply_span: Span,\n    filter_datum: &Datum,\n    filter_input: &ParsedImportSet,\n    arg_data: &[Datum],\n) -> Result<ParsedFilter> {\n    let (filter_span, filter_name) = match filter_datum {\n        Datum::Sym(span, name) if name.starts_with(':') => (span, name),\n        _ => {\n            return Err(Error::new(\n                filter_datum.span(),\n                ErrorKind::ExpectedImportFilterKeyword(filter_datum.description()),\n            ));\n        }\n    };\n\n    match filter_name.as_ref() {\n        \":only\" => {\n            let only_spanned_names = arg_data\n                .iter()\n                .map(|arg_datum| {\n                    expect_spanned_ident(arg_datum, \"identifier to include\")\n                        .map(|(span, ident)| (span, ident.clone()))\n                })\n                .collect::<Result<Box<[_]>>>()?;\n\n            Ok(ParsedFilter::Only(only_spanned_names))\n        }\n        \":exclude\" => {\n            let exclude_spanned_names = arg_data\n                .iter()\n                .map(|arg_datum| {\n                    expect_spanned_ident(arg_datum, \"identifier to exclude\")\n                        .map(|(span, ident)| (span, ident.clone()))\n                })\n                .collect::<Result<Box<[_]>>>()?;\n\n            Ok(ParsedFilter::Exclude(exclude_spanned_names))\n        }\n        \":rename\" => match arg_data {\n            [Datum::Map(_, vs)] => {\n                let rename_spanned_names = vs\n                    .iter()\n                    .map(|(from_datum, to_datum)| {\n                        let (from_span, from_ident) =\n                            expect_spanned_ident(from_datum, \"identifier to rename from\")?;\n                        let to_ident = expect_ident(to_datum, \"identifier to rename to\")?;\n\n                        Ok(((from_span, from_ident.clone()), to_ident.clone()))\n                    })\n                    .collect::<Result<Box<[_]>>>()?;\n\n                Ok(ParsedFilter::Rename(rename_spanned_names))\n            }\n            [other] => Err(Error::new(\n                other.span(),\n                ErrorKind::ExpectedImportRenameMap(other.description()),\n            )),\n            _ => Err(Error::new(apply_span, ErrorKind::WrongArgCount(2))),\n        },\n        \":prefix\" => match arg_data {\n            [prefix_datum] => {\n                let prefix_ident = expect_ident(prefix_datum, \"identifier prefix\")?;\n                Ok(ParsedFilter::Prefix(prefix_ident.clone()))\n            }\n            _ => Err(Error::new(apply_span, ErrorKind::WrongArgCount(2))),\n        },\n        \":prefixed\" => {\n            if !arg_data.is_empty() {\n                return Err(Error::new(apply_span, ErrorKind::WrongArgCount(1)));\n            }\n\n            Ok(ParsedFilter::Prefix(\n                format!(\"{}/\", filter_input.module_name().terminal_name()).into(),\n            ))\n        }\n        _ => Err(Error::new(*filter_span, ErrorKind::UnsupportedImportFilter)),\n    }\n}\n\n/// Parses the passed import set datum\n///\n/// This produces an AST without performing the import itself.\npub fn parse_import_set(import_set_datum: &Datum) -> Result<ParsedImportSet> {\n    let span = import_set_datum.span();\n\n    match import_set_datum {\n        Datum::Vector(_, vs) => {\n            let module_name = parse_module_name(span, vs.as_ref())?;\n            Ok(ParsedImportSet::Module(span, module_name))\n        }\n        Datum::List(_, vs) if vs.len() >= 2 => {\n            let filter_datum = &vs[0];\n\n            let inner_import_datum = &vs[1];\n            let filter_input = parse_import_set(inner_import_datum)?;\n\n            let filter = parse_filter(span, filter_datum, &filter_input, &vs[2..])?;\n            Ok(ParsedImportSet::Filter(filter, Box::new(filter_input)))\n        }\n        _ => Err(Error::new(span, ErrorKind::BadImportSet)),\n    }\n}\n"
  },
  {
    "path": "compiler/hir/loader.rs",
    "content": "use std::collections::HashMap;\nuse std::path;\n\nuse arret_syntax::datum::DataStr;\nuse arret_syntax::span::Span;\n\nuse crate::hir::error::{Error, ErrorKind, Result};\nuse crate::rfi;\nuse crate::source::SourceFile;\nuse crate::CompileCtx;\n\npub struct PackagePath {\n    arret_base: Box<path::Path>,\n    native_rust_base: Box<path::Path>,\n    target_rust_base: Box<path::Path>,\n}\n\npub struct PackagePaths {\n    paths: HashMap<Box<str>, PackagePath>,\n}\n\nimpl PackagePaths {\n    pub fn empty() -> PackagePaths {\n        PackagePaths {\n            paths: HashMap::new(),\n        }\n    }\n\n    /// Creates an instance including the `stdlib` package\n    pub fn with_stdlib(arret_root_dir: &path::Path, target_triple: Option<&str>) -> PackagePaths {\n        let mut pp = PackagePaths::empty();\n\n        let native_rust_base = arret_root_dir.join(\"target\");\n        let target_rust_base = if let Some(target_triple) = target_triple {\n            native_rust_base.join(target_triple)\n        } else {\n            native_rust_base.clone()\n        };\n\n        let stdlib_path = PackagePath {\n            arret_base: arret_root_dir.join(\"stdlib/arret\").into(),\n            native_rust_base: native_rust_base.into(),\n            target_rust_base: target_rust_base.into(),\n        };\n\n        pp.add_package(\"stdlib\", stdlib_path);\n        pp\n    }\n\n    /// Creates an instance for use in our internal unit and integration tests\n    pub fn test_paths(target_triple: Option<&str>) -> PackagePaths {\n        let parent_path = path::Path::new(\"..\");\n        Self::with_stdlib(parent_path, target_triple)\n    }\n\n    pub fn add_package(&mut self, package_name: &str, path: PackagePath) {\n        self.paths.insert(package_name.into(), path);\n    }\n}\n\n#[derive(PartialEq, Eq, Hash, Clone)]\npub struct ModuleName {\n    package_name: DataStr,\n    path: Vec<DataStr>,\n    terminal_name: DataStr,\n}\n\n#[derive(Debug)]\npub enum LoadedModule {\n    Source(SourceFile),\n    Rust(rfi::Library),\n}\n\nimpl ModuleName {\n    pub fn new(package_name: DataStr, path: Vec<DataStr>, terminal_name: DataStr) -> ModuleName {\n        ModuleName {\n            package_name,\n            path,\n            terminal_name,\n        }\n    }\n\n    pub fn is_rfi(&self) -> bool {\n        self.path.is_empty() && self.terminal_name.as_ref() == \"rust\"\n    }\n\n    pub fn terminal_name(&self) -> &DataStr {\n        &self.terminal_name\n    }\n}\n\npub fn load_module_by_name(\n    ccx: &CompileCtx,\n    span: Span,\n    module_name: &ModuleName,\n) -> Result<LoadedModule> {\n    let package_path = if let Some(package_path) = ccx\n        .package_paths()\n        .paths\n        .get(module_name.package_name.as_ref())\n    {\n        package_path\n    } else {\n        return Err(Error::new(span, ErrorKind::PackageNotFound));\n    };\n\n    if module_name.is_rfi() {\n        ccx.rfi_loader()\n            .load(\n                span,\n                ccx.source_loader(),\n                &package_path.native_rust_base,\n                &package_path.target_rust_base,\n                &module_name.package_name,\n            )\n            .map(LoadedModule::Rust)\n    } else {\n        // Look for files starting in the package path\n        let mut path_buf = path::PathBuf::new();\n        path_buf.push(&package_path.arret_base);\n\n        for path_component in &module_name.path {\n            path_buf.push(path_component.as_ref());\n        }\n\n        path_buf.push(format!(\"{}.arret\", module_name.terminal_name));\n        let path = path_buf.as_path();\n\n        let source_file = ccx\n            .source_loader()\n            .load_path(path)\n            .map_err(|err| Error::from_module_io(span, path, &err))?;\n\n        Ok(LoadedModule::Source(source_file))\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::source::EMPTY_SPAN;\n\n    fn load_stdlib_module(name: &'static str) -> Result<LoadedModule> {\n        let ccx = CompileCtx::new(PackagePaths::test_paths(None), true);\n        let module_name = ModuleName::new(\"stdlib\".into(), vec![], name.into());\n\n        load_module_by_name(&ccx, EMPTY_SPAN, &module_name)\n    }\n\n    #[test]\n    fn load_stdlib_base() {\n        let loaded_module = load_stdlib_module(\"base\").unwrap();\n\n        if let LoadedModule::Source(data) = loaded_module {\n            assert!(!data.parsed().unwrap().is_empty());\n        } else {\n            panic!(\"Did not get source module; got {:?}\", loaded_module);\n        }\n    }\n\n    #[test]\n    fn load_stdlib_rust() {\n        // Ensure we can locate and load the module. The RFI itself is tested separately.\n        let loaded_module = load_stdlib_module(\"rust\").expect(\n            \"unable to load stdlib library; you may need to `cargo build` before running tests\",\n        );\n\n        if let LoadedModule::Rust(rfi_module) = loaded_module {\n            assert!(!rfi_module.exported_funs.is_empty());\n        } else {\n            panic!(\"Did not get Rust module; got {:?}\", loaded_module);\n        }\n    }\n\n    #[test]\n    fn load_stdlib_missing() {\n        let err = load_stdlib_module(\"notamodule\").unwrap_err();\n\n        if let ErrorKind::ModuleNotFound(_) = err.kind() {\n        } else {\n            panic!(\"Unexpected error kind: {:?}\", err.kind())\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/hir/lowering.rs",
    "content": "use std::collections::HashMap;\n\nuse codespan_reporting::diagnostic::Diagnostic;\n\nuse arret_syntax::datum::Datum;\nuse arret_syntax::span::{FileId, Span};\n\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::CompileCtx;\n\nuse crate::context::ModuleImports;\nuse crate::hir::destruc;\nuse crate::hir::error::{Error, ErrorKind, ExpectedSym, Result};\nuse crate::hir::exports::Exports;\nuse crate::hir::import;\nuse crate::hir::macros::{expand_macro, lower_macro_rules};\nuse crate::hir::ns::{Ident, NsDataIter, NsDatum};\nuse crate::hir::prim::Prim;\nuse crate::hir::records::lower_record;\nuse crate::hir::scope::{Binding, Scope};\nuse crate::hir::types::{lower_poly, lower_polymorphic_var_set, try_lower_purity};\nuse crate::hir::util::{expect_one_arg, expect_spanned_ns_ident, try_take_rest_arg};\nuse crate::hir::var_id::{ExportId, LocalIdAlloc};\nuse crate::hir::Lowered;\nuse crate::hir::{\n    App, Cond, DeclPurity, DeclTy, Def, Expr, ExprKind, FieldAccessor, Fun, Let, LocalId, Recur,\n};\n\n#[cfg(test)]\nuse crate::source::EMPTY_SPAN;\n\n/// Module lowered to HIR\npub struct LoweredModule {\n    /// Defs in the order the were lowered\n    pub defs: Vec<Def<Lowered>>,\n\n    pub exports: Exports,\n    pub main_local_id: Option<LocalId>,\n}\n\nstruct DeferredDef {\n    span: Span,\n    macro_invocation_span: Option<Span>,\n    destruc: destruc::Destruc<Lowered>,\n    value_datum: NsDatum,\n}\n\nstruct DeferredExport {\n    span: Span,\n    ident: Ident,\n}\n\nenum DeferredModulePrim {\n    Def(DeferredDef),\n    Exports(Vec<DeferredExport>),\n}\n\nimpl DeferredModulePrim {\n    fn with_macro_invocation_span(self, span: Span) -> DeferredModulePrim {\n        match self {\n            DeferredModulePrim::Def(deferred_def) => DeferredModulePrim::Def(DeferredDef {\n                macro_invocation_span: Some(span),\n                ..deferred_def\n            }),\n            other => other,\n        }\n    }\n}\n\npub(crate) enum LoweredReplDatum {\n    /// One or more modules were imported\n    Import(ModuleImports),\n    /// An evaluable definition\n    EvaluableDef(Def<Lowered>),\n    /// A non-evalable definition handled by HIR lowering\n    NonEvaluableDef,\n    /// An expression\n    Expr(Expr<Lowered>),\n}\n\n// This would be less ugly as Result<!> once it's stabilised\nfn lower_user_compile_error(span: Span, arg_iter: NsDataIter) -> Error {\n    match expect_one_arg(span, arg_iter) {\n        Ok(NsDatum::Str(_, user_message)) => Error::new(span, ErrorKind::UserError(user_message)),\n        Ok(other) => Error::new(\n            other.span(),\n            ErrorKind::ExpectedCompileErrorString(other.description()),\n        ),\n        Err(error) => error,\n    }\n}\n\nfn lower_macro(\n    scope: &mut Scope<'_>,\n    self_datum: NsDatum,\n    transformer_spec: NsDatum,\n) -> Result<()> {\n    let (self_span, self_ident) = expect_spanned_ns_ident(self_datum, \"new macro name\")?;\n\n    let macro_rules_data = if let NsDatum::List(span, vs) = transformer_spec {\n        let mut transformer_data = vs.into_vec();\n\n        let macro_type_datum = if let Some(macro_type_datum) = transformer_data.first() {\n            macro_type_datum\n        } else {\n            return Err(Error::new(span, ErrorKind::NoMacroType));\n        };\n\n        if let Some(Binding::Prim(Prim::MacroRules)) = scope.get_datum(macro_type_datum) {\n        } else {\n            return Err(Error::new(macro_type_datum.span(), ErrorKind::BadMacroType));\n        }\n\n        transformer_data.remove(0);\n        transformer_data\n    } else {\n        return Err(Error::new(\n            transformer_spec.span(),\n            ErrorKind::ExpectedMacroSpecList(transformer_spec.description()),\n        ));\n    };\n\n    let mac = lower_macro_rules(scope, &self_ident, macro_rules_data)?;\n    scope.insert_binding(self_span, self_ident, Binding::Macro(None, mac))?;\n\n    Ok(())\n}\n\nfn lower_defmacro(scope: &mut Scope<'_>, span: Span, mut arg_iter: NsDataIter) -> Result<()> {\n    if arg_iter.len() != 2 {\n        return Err(Error::new(\n            span,\n            ErrorKind::WrongDefLikeArgCount(\"defmacro\"),\n        ));\n    }\n\n    let self_datum = arg_iter.next().unwrap();\n    let transformer_spec = arg_iter.next().unwrap();\n\n    lower_macro(scope, self_datum, transformer_spec)\n}\n\nfn lower_letmacro(\n    lia: &LocalIdAlloc,\n    scope: &Scope<'_>,\n    span: Span,\n    arg_iter: NsDataIter,\n) -> Result<Expr<Lowered>> {\n    lower_let_like(lia, scope, span, arg_iter, lower_macro, |expr, _| expr)\n}\n\nfn lower_type(scope: &mut Scope<'_>, self_datum: NsDatum, ty_datum: NsDatum) -> Result<()> {\n    let (span, ident) = expect_spanned_ns_ident(self_datum, \"new type name\")?;\n    let ty = lower_poly(scope, ty_datum)?;\n\n    scope.insert_binding(span, ident, Binding::Ty(ty))?;\n    Ok(())\n}\n\nfn lower_deftype(scope: &mut Scope<'_>, span: Span, mut arg_iter: NsDataIter) -> Result<()> {\n    if arg_iter.len() != 2 {\n        return Err(Error::new(span, ErrorKind::WrongDefLikeArgCount(\"deftype\")));\n    }\n\n    let self_datum = arg_iter.next().unwrap();\n    let ty_datum = arg_iter.next().unwrap();\n\n    lower_type(scope, self_datum, ty_datum)\n}\n\nfn lower_lettype(\n    lia: &LocalIdAlloc,\n    scope: &Scope<'_>,\n    span: Span,\n    arg_iter: NsDataIter,\n) -> Result<Expr<Lowered>> {\n    lower_let_like(lia, scope, span, arg_iter, lower_type, |expr, _| expr)\n}\n\nfn lower_defrecord(scope: &mut Scope<'_>, span: Span, mut arg_iter: NsDataIter) -> Result<()> {\n    if arg_iter.len() != 2 {\n        return Err(Error::new(span, ErrorKind::WrongDefRecordArgCount));\n    }\n\n    let ty_cons_datum = arg_iter.next().unwrap();\n    let value_cons_datum = arg_iter.next().unwrap();\n\n    lower_record(scope, ty_cons_datum, value_cons_datum)\n}\n\nfn lower_letrecord(\n    lia: &LocalIdAlloc,\n    scope: &Scope<'_>,\n    span: Span,\n    arg_iter: NsDataIter,\n) -> Result<Expr<Lowered>> {\n    lower_let_like(lia, scope, span, arg_iter, lower_record, |expr, _| expr)\n}\n\n/// Lowers an identifier in to a scalar destruc with the passed type\nfn lower_ident_destruc(\n    lia: &LocalIdAlloc,\n    scope: &mut Scope<'_>,\n    span: Span,\n    ident: Ident,\n    decl_ty: DeclTy,\n) -> Result<destruc::Scalar<Lowered>> {\n    if ident.is_underscore() {\n        Ok(destruc::Scalar::new(None, ident.into_name(), decl_ty))\n    } else {\n        let local_id = lia.alloc();\n        let source_name = ident.name().clone();\n\n        scope.insert_local(span, ident, local_id)?;\n\n        Ok(destruc::Scalar::new(Some(local_id), source_name, decl_ty))\n    }\n}\n\nfn lower_scalar_destruc(\n    lia: &LocalIdAlloc,\n    scope: &mut Scope<'_>,\n    destruc_datum: NsDatum,\n) -> Result<destruc::Scalar<Lowered>> {\n    match destruc_datum {\n        NsDatum::Ident(span, ident) => lower_ident_destruc(lia, scope, span, ident, DeclTy::Free),\n        NsDatum::Vector(span, vs) => {\n            let mut data = vs.into_vec();\n\n            if data.len() != 2 {\n                return Err(Error::new(span, ErrorKind::NoVecDestruc));\n            }\n\n            let ty = lower_poly(scope, data.pop().unwrap())?;\n\n            let (span, ident) = expect_spanned_ns_ident(data.pop().unwrap(), \"new variable name\")?;\n            lower_ident_destruc(lia, scope, span, ident, ty.into())\n        }\n        _ => Err(Error::new(destruc_datum.span(), ErrorKind::BadRestDestruc)),\n    }\n}\n\nfn lower_list_destruc(\n    lia: &LocalIdAlloc,\n    scope: &mut Scope<'_>,\n    mut data_iter: NsDataIter,\n) -> Result<destruc::List<Lowered>> {\n    let rest = try_take_rest_arg(&mut data_iter);\n\n    let fixed_destrucs = data_iter\n        .map(|v| lower_destruc(lia, scope, v))\n        .collect::<Result<Vec<destruc::Destruc<Lowered>>>>()?;\n\n    let rest_destruc = match rest {\n        Some(rest) => Some(Box::new(lower_scalar_destruc(lia, scope, rest)?)),\n        None => None,\n    };\n\n    Ok(destruc::List::new(fixed_destrucs, rest_destruc))\n}\n\nfn lower_destruc(\n    lia: &LocalIdAlloc,\n    scope: &mut Scope<'_>,\n    destruc_datum: NsDatum,\n) -> Result<destruc::Destruc<Lowered>> {\n    match destruc_datum {\n        NsDatum::Ident(span, _) | NsDatum::Vector(span, _) => {\n            lower_scalar_destruc(lia, scope, destruc_datum)\n                .map(|scalar| destruc::Destruc::Scalar(span, scalar))\n        }\n\n        NsDatum::List(span, vs) => lower_list_destruc(lia, scope, vs.into_vec().into_iter())\n            .map(|list_destruc| destruc::Destruc::List(span, list_destruc)),\n\n        NsDatum::Keyword(span, _) => Err(Error::new(\n            span,\n            ErrorKind::ExpectedSym(\n                ExpectedSym {\n                    found: \"keyword\",\n                    usage: \"new variable name\",\n                }\n                .into(),\n            ),\n        )),\n        _ => Err(Error::new(destruc_datum.span(), ErrorKind::BadListDestruc)),\n    }\n}\n\nfn lower_let_like<B, C, O>(\n    lia: &LocalIdAlloc,\n    outer_scope: &Scope<'_>,\n    span: Span,\n    mut arg_iter: NsDataIter,\n    binder: B,\n    fold_output: C,\n) -> Result<Expr<Lowered>>\nwhere\n    B: Fn(&mut Scope<'_>, NsDatum, NsDatum) -> Result<O>,\n    C: Fn(Expr<Lowered>, O) -> Expr<Lowered>,\n{\n    let bindings_datum = arg_iter\n        .next()\n        .ok_or_else(|| Error::new(span, ErrorKind::NoBindingVec))?;\n\n    let bindings_data = if let NsDatum::Vector(_, vs) = bindings_datum {\n        vs.into_vec()\n    } else {\n        return Err(Error::new(\n            bindings_datum.span(),\n            ErrorKind::BindingsNotVec(bindings_datum.description()),\n        ));\n    };\n\n    let mut scope = outer_scope.child();\n    let mut outputs = Vec::<O>::with_capacity(bindings_data.len() / 2);\n\n    let mut bindings_iter = bindings_data.into_iter();\n    while let Some(target_datum) = bindings_iter.next() {\n        let value_datum = bindings_iter\n            .next()\n            .ok_or_else(|| Error::new(target_datum.span(), ErrorKind::UnevenBindingVec))?;\n\n        outputs.push(binder(&mut scope, target_datum, value_datum)?);\n    }\n\n    let body_expr = lower_body(lia, &scope, arg_iter)?;\n\n    // This is to build nested `Let` expressions. Types/macros don't need this\n    Ok(outputs.into_iter().rfold(body_expr, fold_output))\n}\n\nfn lower_body(\n    lia: &LocalIdAlloc,\n    scope: &Scope<'_>,\n    body_data: NsDataIter,\n) -> Result<Expr<Lowered>> {\n    let mut flattened_exprs = vec![];\n\n    for body_datum in body_data {\n        match lower_expr(lia, scope, body_datum)? {\n            Expr {\n                kind: ExprKind::Do(mut exprs),\n                ..\n            } => {\n                flattened_exprs.append(&mut exprs);\n            }\n            other => {\n                flattened_exprs.push(other);\n            }\n        }\n    }\n\n    if flattened_exprs.len() == 1 {\n        Ok(flattened_exprs.pop().unwrap())\n    } else {\n        Ok(ExprKind::Do(flattened_exprs).into())\n    }\n}\n\nfn lower_let(\n    lia: &LocalIdAlloc,\n    scope: &Scope<'_>,\n    span: Span,\n    arg_iter: NsDataIter,\n) -> Result<Expr<Lowered>> {\n    lower_let_like(\n        lia,\n        scope,\n        span,\n        arg_iter,\n        |scope, target_datum, value_datum| {\n            let value_expr = lower_expr(lia, scope, value_datum)?;\n            let destruc = lower_destruc(lia, scope, target_datum)?;\n            Ok((destruc, value_expr))\n        },\n        |body_expr, (destruc, value_expr)| {\n            ExprKind::Let(Box::new(Let {\n                span,\n                destruc,\n                value_expr,\n                body_expr,\n            }))\n            .into()\n        },\n    )\n}\n\nfn lower_fun(\n    lia: &LocalIdAlloc,\n    outer_scope: &Scope<'_>,\n    span: Span,\n    mut arg_iter: NsDataIter,\n) -> Result<Expr<Lowered>> {\n    let mut fun_scope = outer_scope.child();\n\n    let mut next_datum = arg_iter\n        .next()\n        .ok_or_else(|| Error::new(span, ErrorKind::NoParamDecl))?;\n\n    // We can either begin with a set of type variables or a list of parameters\n    let (pvars, tvars) = if let NsDatum::Set(_, vs) = next_datum {\n        next_datum = arg_iter\n            .next()\n            .ok_or_else(|| Error::new(span, ErrorKind::NoParamDecl))?;\n\n        lower_polymorphic_var_set(outer_scope, &mut fun_scope, vs.into_vec().into_iter())?\n    } else {\n        (purity::PVars::new(), ty::TVars::new())\n    };\n\n    // Pull out our params\n    let params = match next_datum {\n        NsDatum::List(_, vs) => lower_list_destruc(lia, &mut fun_scope, vs.into_vec().into_iter())?,\n        other => {\n            return Err(Error::new(\n                other.span(),\n                ErrorKind::ExpectedParamList(other.description()),\n            ));\n        }\n    };\n\n    // Determine if we have a purity and return type after the parameters, eg (param) -> RetTy\n    let mut purity = DeclPurity::Free;\n    let mut ret_ty = DeclTy::Free;\n    let mut ret_ty_span = None;\n\n    if arg_iter.len() >= 2 {\n        if let Some(poly_purity) = try_lower_purity(&fun_scope, &arg_iter.as_slice()[0]) {\n            arg_iter.next();\n            purity = poly_purity.into();\n\n            match arg_iter.next().unwrap() {\n                NsDatum::Ident(_, ref ident) if ident.is_underscore() => {}\n                ret_datum => {\n                    ret_ty_span = Some(ret_datum.span());\n                    ret_ty = lower_poly(&fun_scope, ret_datum)?.into();\n                }\n            }\n        }\n    }\n\n    // Extract the body\n    let body_expr = lower_body(lia, &fun_scope, arg_iter)?;\n\n    Ok(ExprKind::Fun(Box::new(Fun {\n        span,\n        pvars,\n        tvars,\n        purity,\n        params,\n        ret_ty,\n        ret_ty_span,\n        body_expr,\n    }))\n    .into())\n}\n\nfn lower_expr_prim_apply(\n    lia: &LocalIdAlloc,\n    scope: &Scope<'_>,\n    span: Span,\n    prim: Prim,\n    mut arg_iter: NsDataIter,\n) -> Result<Expr<Lowered>> {\n    match prim {\n        Prim::Def | Prim::DefMacro | Prim::DefType | Prim::ImportPlaceholder | Prim::DefRecord => {\n            Err(Error::new(span, ErrorKind::DefOutsideBody))\n        }\n        Prim::Let => lower_let(lia, scope, span, arg_iter),\n        Prim::LetMacro => lower_letmacro(lia, scope, span, arg_iter),\n        Prim::LetType => lower_lettype(lia, scope, span, arg_iter),\n        Prim::LetRecord => lower_letrecord(lia, scope, span, arg_iter),\n        Prim::Export => Err(Error::new(span, ErrorKind::ExportOutsideModule)),\n        Prim::Quote => {\n            let literal_datum = expect_one_arg(span, arg_iter)?;\n            Ok(literal_datum.into_syntax_datum().into())\n        }\n        Prim::Fun => lower_fun(lia, scope, span, arg_iter),\n        Prim::If => {\n            if arg_iter.len() != 3 {\n                return Err(Error::new(span, ErrorKind::WrongCondArgCount));\n            }\n\n            Ok(ExprKind::Cond(Box::new(Cond {\n                span,\n                test_expr: lower_expr(lia, scope, arg_iter.next().unwrap())?,\n                true_expr: lower_expr(lia, scope, arg_iter.next().unwrap())?,\n                false_expr: lower_expr(lia, scope, arg_iter.next().unwrap())?,\n            }))\n            .into())\n        }\n        Prim::Do => lower_body(lia, scope, arg_iter),\n        Prim::Recur => lower_recur(lia, scope, span, arg_iter),\n        Prim::CompileError => Err(lower_user_compile_error(span, arg_iter)),\n        Prim::MacroRules | Prim::All => {\n            Err(Error::new(span, ErrorKind::ExpectedValue(\"primitive\")))\n        }\n    }\n}\n\nfn lower_expr_apply(\n    lia: &LocalIdAlloc,\n    scope: &Scope<'_>,\n    span: Span,\n    fun_expr: Expr<Lowered>,\n    mut arg_iter: NsDataIter,\n) -> Result<Expr<Lowered>> {\n    let rest_arg_datum = try_take_rest_arg(&mut arg_iter);\n\n    let fixed_arg_exprs = arg_iter\n        .map(|arg_datum| lower_expr(lia, scope, arg_datum))\n        .collect::<Result<Vec<Expr<Lowered>>>>()?;\n\n    let rest_arg_expr = match rest_arg_datum {\n        Some(rest_arg_datum) => Some(lower_expr(lia, scope, rest_arg_datum)?),\n        None => None,\n    };\n\n    Ok(ExprKind::App(Box::new(App {\n        span,\n        fun_expr,\n        ty_args: (),\n        fixed_arg_exprs,\n        rest_arg_expr,\n    }))\n    .into())\n}\n\nfn lower_recur(\n    lia: &LocalIdAlloc,\n    scope: &Scope<'_>,\n    span: Span,\n    mut arg_iter: NsDataIter,\n) -> Result<Expr<Lowered>> {\n    let rest_arg_datum = try_take_rest_arg(&mut arg_iter);\n\n    let fixed_arg_exprs = arg_iter\n        .map(|arg_datum| lower_expr(lia, scope, arg_datum))\n        .collect::<Result<Vec<Expr<Lowered>>>>()?;\n\n    let rest_arg_expr = match rest_arg_datum {\n        Some(rest_arg_datum) => Some(lower_expr(lia, scope, rest_arg_datum)?),\n        None => None,\n    };\n\n    Ok(ExprKind::Recur(Box::new(Recur {\n        span,\n        fixed_arg_exprs,\n        rest_arg_expr,\n    }))\n    .into())\n}\n\nfn lower_expr(lia: &LocalIdAlloc, scope: &Scope<'_>, datum: NsDatum) -> Result<Expr<Lowered>> {\n    match datum {\n        NsDatum::Ident(span, ident) => match scope.get_or_err(span, &ident)? {\n            Binding::Var(Some(module_id), local_id) => {\n                Ok(ExprKind::ExportRef(span, ExportId::new(*module_id, *local_id)).into())\n            }\n            Binding::Var(None, local_id) => Ok(ExprKind::LocalRef(span, *local_id).into()),\n            Binding::TyPred(test_ty) => Ok(ExprKind::TyPred(span, test_ty.clone()).into()),\n            Binding::EqPred => Ok(ExprKind::EqPred(span).into()),\n            Binding::RecordValueCons(record_cons) => {\n                Ok(ExprKind::RecordCons(span, record_cons.clone()).into())\n            }\n            Binding::FieldAccessor(record_cons, field_index) => {\n                Ok(ExprKind::FieldAccessor(Box::new(FieldAccessor {\n                    span,\n                    record_cons: record_cons.clone(),\n                    field_index: *field_index,\n                }))\n                .into())\n            }\n            other => Err(Error::new(\n                span,\n                ErrorKind::ExpectedValue(other.description()),\n            )),\n        },\n        NsDatum::List(span, vs) => {\n            let mut data_iter = vs.into_vec().into_iter();\n\n            let fn_datum = if let Some(fn_datum) = data_iter.next() {\n                fn_datum\n            } else {\n                return Ok(Datum::List(span, Box::new([])).into());\n            };\n\n            if let NsDatum::Ident(fn_span, ref ident) = fn_datum {\n                match scope.get_or_err(fn_span, ident)? {\n                    Binding::Prim(prim) => {\n                        return lower_expr_prim_apply(lia, scope, span, *prim, data_iter);\n                    }\n                    Binding::Macro(module_id, mac) => {\n                        let mut macro_scope = scope.child();\n\n                        let expanded_datum = expand_macro(\n                            &mut macro_scope,\n                            span,\n                            *module_id,\n                            mac,\n                            data_iter.as_slice(),\n                        )?;\n\n                        return lower_expr(lia, &macro_scope, expanded_datum)\n                            .map(|expr| ExprKind::MacroExpand(span, Box::new(expr)).into())\n                            .map_err(|e| e.with_macro_invocation_span(span));\n                    }\n                    _ => {}\n                }\n            }\n\n            let fn_expr = lower_expr(lia, scope, fn_datum)?;\n            lower_expr_apply(lia, scope, span, fn_expr, data_iter)\n        }\n        other => Ok(other.into_syntax_datum().into()),\n    }\n}\n\nfn lower_module_prim_apply(\n    lia: &LocalIdAlloc,\n    scope: &mut Scope<'_>,\n    span: Span,\n    prim: Prim,\n    mut arg_iter: NsDataIter,\n) -> Result<Option<DeferredModulePrim>, Vec<Error>> {\n    match prim {\n        Prim::Export => {\n            let deferred_exports = arg_iter\n                .map(|datum| {\n                    let (span, ident) = expect_spanned_ns_ident(datum, \"identifier to export\")?;\n                    Ok(DeferredExport { span, ident })\n                })\n                .collect::<Result<Vec<DeferredExport>>>()?;\n\n            Ok(Some(DeferredModulePrim::Exports(deferred_exports)))\n        }\n        Prim::Def => {\n            if arg_iter.len() != 2 {\n                return Err(vec![Error::new(\n                    span,\n                    ErrorKind::WrongDefLikeArgCount(\"def\"),\n                )]);\n            }\n\n            let destruc_datum = arg_iter.next().unwrap();\n            let destruc = lower_destruc(lia, scope, destruc_datum)?;\n\n            let value_datum = arg_iter.next().unwrap();\n\n            let deferred_def = DeferredDef {\n                span,\n                macro_invocation_span: None,\n                destruc,\n                value_datum,\n            };\n\n            Ok(Some(DeferredModulePrim::Def(deferred_def)))\n        }\n        Prim::DefMacro => Ok(lower_defmacro(scope, span, arg_iter).map(|_| None)?),\n        Prim::DefType => Ok(lower_deftype(scope, span, arg_iter).map(|_| None)?),\n        Prim::DefRecord => Ok(lower_defrecord(scope, span, arg_iter).map(|_| None)?),\n        Prim::CompileError => Err(vec![lower_user_compile_error(span, arg_iter)]),\n        _ => Err(vec![Error::new(span, ErrorKind::NonDefInsideModule)]),\n    }\n}\n\nfn lower_module_def(\n    lia: &LocalIdAlloc,\n    scope: &mut Scope<'_>,\n    datum: NsDatum,\n) -> Result<Option<DeferredModulePrim>, Vec<Error>> {\n    let span = datum.span();\n\n    if let NsDatum::List(span, vs) = datum {\n        let mut data_iter = vs.into_vec().into_iter();\n\n        if let Some(NsDatum::Ident(fn_span, ref ident)) = data_iter.next() {\n            match scope.get_or_err(fn_span, ident)? {\n                Binding::Prim(prim) => {\n                    let prim = *prim;\n                    return lower_module_prim_apply(lia, scope, span, prim, data_iter);\n                }\n                Binding::Macro(module_id, mac) => {\n                    let module_id = *module_id;\n                    let mac = &mac.clone();\n\n                    let expanded_datum =\n                        expand_macro(scope, span, module_id, mac, data_iter.as_slice())?;\n\n                    return lower_module_def(lia, scope, expanded_datum)\n                        .map(|def| def.map(|def| def.with_macro_invocation_span(span)))\n                        .map_err(|errs| {\n                            errs.into_iter()\n                                .map(|e| e.with_macro_invocation_span(span))\n                                .collect()\n                        });\n                }\n                _ => {\n                    // Non-def\n                }\n            }\n        }\n    }\n\n    Err(vec![Error::new(span, ErrorKind::NonDefInsideModule)])\n}\n\nfn insert_import_bindings(\n    imports: &ModuleImports,\n    scope: &mut Scope<'_>,\n    arg_data: &[Datum],\n) -> Result<(), Vec<Error>> {\n    for arg_datum in arg_data {\n        let span = arg_datum.span();\n\n        let parsed_import = import::parse_import_set(arg_datum)?;\n        let import_module = &imports[parsed_import.module_name()];\n\n        let exports = import::filter_imported_exports(parsed_import, &import_module.exports)?;\n\n        scope.insert_bindings(\n            span,\n            exports.into_iter().map(|(name, binding)| {\n                (\n                    Ident::new(Scope::root_ns_id(), name),\n                    binding.import_from(import_module.module_id),\n                )\n            }),\n        )?;\n    }\n\n    Ok(())\n}\n\npub(crate) fn lower_data(\n    imports: &ModuleImports,\n    data: &[Datum],\n) -> Result<LoweredModule, Vec<Error>> {\n    let lia = LocalIdAlloc::new();\n    let mut scope = Scope::root();\n\n    // Build up a list of errors to return at once\n    let mut errors: Vec<Error> = vec![];\n\n    // Extract all of our definitions.\n    //\n    // This occurs in two passes:\n    // - Imports, types and macros are resolved immediately and cannot refer to bindings later\n    //   in the body\n    // - Definitions are resolved after the module has been loaded\n    let mut deferred_exports = Vec::<DeferredExport>::new();\n    let mut deferred_defs = Vec::<DeferredDef>::new();\n\n    for input_datum in data {\n        if let Some(arg_data) = import::try_extract_import_set(input_datum) {\n            if let Err(mut new_errors) = insert_import_bindings(imports, &mut scope, arg_data) {\n                errors.append(&mut new_errors);\n            }\n\n            continue;\n        }\n\n        let ns_datum = NsDatum::from_syntax_datum(input_datum);\n        match lower_module_def(&lia, &mut scope, ns_datum) {\n            Ok(Some(DeferredModulePrim::Exports(mut exports))) => {\n                deferred_exports.append(&mut exports);\n            }\n            Ok(Some(DeferredModulePrim::Def(deferred_def))) => {\n                deferred_defs.push(deferred_def);\n            }\n            Ok(None) => {}\n            Err(mut new_errors) => {\n                errors.append(&mut new_errors);\n            }\n        };\n    }\n\n    // Process any exports\n    let mut exports = HashMap::with_capacity(deferred_exports.len());\n    for deferred_export in deferred_exports {\n        let DeferredExport { span, ident } = deferred_export;\n        match scope.get_or_err(span, &ident) {\n            Ok(binding) => {\n                exports.insert(ident.into_name(), binding.clone());\n            }\n            Err(err) => {\n                errors.push(err);\n            }\n        };\n    }\n\n    // And now process any deferred defs\n    let mut defs = Vec::with_capacity(deferred_defs.len());\n    for deferred_def in deferred_defs {\n        match resolve_deferred_def(&lia, &scope, deferred_def) {\n            Ok(def) => {\n                defs.push(def);\n            }\n            Err(error) => {\n                errors.push(error);\n            }\n        }\n    }\n\n    // Try to find `main!`. If we're not the entry module this will be ignored.\n    let main_ident = Ident::new(Scope::root_ns_id(), \"main!\".into());\n    let main_local_id = if let Some(Binding::Var(None, local_id)) = scope.get(&main_ident) {\n        Some(*local_id)\n    } else {\n        None\n    };\n\n    if errors.is_empty() {\n        Ok(LoweredModule {\n            defs,\n            exports,\n            main_local_id,\n        })\n    } else {\n        Err(errors)\n    }\n}\n\nfn resolve_deferred_def(\n    lia: &LocalIdAlloc,\n    scope: &Scope<'_>,\n    deferred_def: DeferredDef,\n) -> Result<Def<Lowered>> {\n    let DeferredDef {\n        span,\n        macro_invocation_span,\n        destruc,\n        value_datum,\n    } = deferred_def;\n\n    lower_expr(lia, scope, value_datum).map(|value_expr| Def {\n        span,\n        macro_invocation_span,\n        destruc,\n        value_expr,\n    })\n}\n\n// REPL interface\npub(crate) fn lower_repl_datum(\n    ccx: &CompileCtx,\n    scope: &mut Scope<'_>,\n    datum: &Datum,\n) -> Result<LoweredReplDatum, Vec<Diagnostic<FileId>>> {\n    use crate::reporting::errors_to_diagnostics;\n\n    // For the purposes of type inference every datum is a new module\n    let lia = LocalIdAlloc::new();\n\n    if let Some(arg_data) = import::try_extract_import_set(datum) {\n        let imports = ccx.imports_for_data(std::iter::once(datum))?;\n\n        insert_import_bindings(&imports, scope, arg_data).map_err(errors_to_diagnostics)?;\n\n        return Ok(LoweredReplDatum::Import(imports));\n    }\n\n    // Try interpreting this as a module def\n    let ns_datum = NsDatum::from_syntax_datum(datum);\n    match lower_module_def(&lia, scope, ns_datum.clone()) {\n        Ok(Some(DeferredModulePrim::Def(deferred_def))) => {\n            let def =\n                resolve_deferred_def(&lia, scope, deferred_def).map_err(|err| vec![err.into()])?;\n\n            Ok(LoweredReplDatum::EvaluableDef(def))\n        }\n        Ok(Some(DeferredModulePrim::Exports(_))) => {\n            Err(vec![\n                Error::new(datum.span(), ErrorKind::ExportInsideRepl).into()\n            ])\n        }\n        Ok(None) => Ok(LoweredReplDatum::NonEvaluableDef),\n        Err(mut errs) => {\n            // `NonDefInsideModule` doesn't apply because we allow non-defs in the REPL\n            errs.retain(|err| err.kind() != &ErrorKind::NonDefInsideModule);\n\n            if errs.is_empty() {\n                // Re-interpret as an expression\n                let expr = lower_expr(&lia, scope, ns_datum).map_err(|err| vec![err.into()])?;\n\n                Ok(LoweredReplDatum::Expr(expr))\n            } else {\n                Err(errors_to_diagnostics(errs))\n            }\n        }\n    }\n}\n\n////\n#[cfg(test)]\nfn import_statement_for_module(names: &[&'static str]) -> Datum {\n    Datum::List(\n        EMPTY_SPAN,\n        Box::new([\n            Datum::Sym(EMPTY_SPAN, \"import\".into()),\n            Datum::Vector(\n                EMPTY_SPAN,\n                names\n                    .iter()\n                    .map(|&n| Datum::Sym(EMPTY_SPAN, n.into()))\n                    .collect(),\n            ),\n        ]),\n    )\n}\n\n#[cfg(test)]\nfn module_for_str(data_str: &str) -> Result<LoweredModule> {\n    use std::iter;\n    use std::sync::Arc;\n\n    use arret_syntax::parser::data_from_str;\n\n    use crate::context;\n    use crate::hir::exports;\n    use crate::hir::loader::ModuleName;\n\n    let mut program_data = vec![];\n    let mut imports: ModuleImports = HashMap::new();\n\n    for (terminal_name, exports) in iter::once((\"primitives\", exports::prims_exports()))\n        .chain(iter::once((\"types\", exports::tys_exports())))\n    {\n        program_data.push(import_statement_for_module(&[\n            \"arret\",\n            \"internal\",\n            terminal_name,\n        ]));\n\n        imports.insert(\n            ModuleName::new(\n                \"arret\".into(),\n                vec![\"internal\".into()],\n                terminal_name.into(),\n            ),\n            Arc::new(context::prims_to_module(exports)),\n        );\n    }\n\n    let mut test_data = data_from_str(None, data_str).unwrap();\n    program_data.append(&mut test_data);\n\n    imports.insert(\n        ModuleName::new(\"arret\".into(), vec![\"internal\".into()], \"types\".into()),\n        Arc::new(context::prims_to_module(exports::tys_exports())),\n    );\n\n    lower_data(&imports, &program_data).map_err(|mut errors| errors.remove(0))\n}\n\n#[cfg(test)]\npub fn expr_for_str(data_str: &str) -> Expr<Lowered> {\n    use arret_syntax::parser::datum_from_str;\n\n    let lia = LocalIdAlloc::new();\n    let scope = Scope::new_with_primitives();\n\n    let test_datum = datum_from_str(None, data_str).unwrap();\n    let test_nsdatum = NsDatum::from_syntax_datum(&test_datum);\n\n    lower_expr(&lia, &scope, test_nsdatum).unwrap()\n}\n\n#[allow(clippy::many_single_char_names)]\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use arret_syntax::span::t2s;\n\n    use crate::ty::purity::Purity;\n    use crate::ty::Ty;\n\n    #[test]\n    fn self_quoting_bool() {\n        let j = \"false\";\n        let t = \"^^^^^\";\n\n        let expected: Expr<_> = Datum::Bool(t2s(t), false).into();\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn self_quoting_empty_list() {\n        let j = \"()\";\n        let t = \"^^\";\n\n        let expected: Expr<_> = Datum::List(t2s(t), Box::new([])).into();\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn quoted_datum_shorthand() {\n        let j = \"'foo\";\n        let t = \" ^^^\";\n\n        let expected: Expr<_> = Datum::Sym(t2s(t), \"foo\".into()).into();\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn quoted_datum_explicit() {\n        let j = \"(quote foo)\";\n        let t = \"       ^^^ \";\n\n        let expected: Expr<_> = Datum::Sym(t2s(t), \"foo\".into()).into();\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn self_evaluating_keyword() {\n        let j = \":foo\";\n        let t = \"^^^^\";\n\n        let expected: Expr<_> = Datum::Sym(t2s(t), \":foo\".into()).into();\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn wildcard_let() {\n        let j = \"(let [_ 1])\";\n        let t = \"      ^    \";\n        let u = \"^^^^^^^^^^^\";\n        let v = \"        ^  \";\n\n        let destruc =\n            destruc::Destruc::Scalar(t2s(t), destruc::Scalar::new(None, \"_\".into(), DeclTy::Free));\n\n        let expected: Expr<_> = ExprKind::Let(Box::new(Let {\n            span: t2s(u),\n            destruc,\n            value_expr: Datum::Int(t2s(v), 1).into(),\n            body_expr: ExprKind::Do(vec![]).into(),\n        }))\n        .into();\n\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn empty_fn() {\n        let j = \"(fn ())\";\n        let t = \"^^^^^^^\";\n\n        let expected: Expr<_> = ExprKind::Fun(Box::new(Fun {\n            span: t2s(t),\n            pvars: purity::PVars::new(),\n            tvars: ty::TVars::new(),\n            purity: DeclPurity::Free,\n            params: destruc::List::new(vec![], None),\n            ret_ty: DeclTy::Free,\n            ret_ty_span: None,\n            body_expr: ExprKind::Do(vec![]).into(),\n        }))\n        .into();\n\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn empty_fn_with_purity() {\n        let j = \"(fn () -> _ 1)\";\n        let t = \"^^^^^^^^^^^^^^\";\n        let u = \"            ^ \";\n\n        let expected: Expr<_> = ExprKind::Fun(Box::new(Fun {\n            span: t2s(t),\n            pvars: purity::PVars::new(),\n            tvars: ty::TVars::new(),\n            purity: Purity::Pure.into(),\n            params: destruc::List::new(vec![], None),\n            ret_ty: DeclTy::Free,\n            ret_ty_span: None,\n            body_expr: Datum::Int(t2s(u), 1).into(),\n        }))\n        .into();\n\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn empty_fn_with_ret_ty() {\n        let j = \"(fn () -> Int 1)\";\n        let t = \"^^^^^^^^^^^^^^^^\";\n        let u = \"          ^^^   \";\n        let v = \"              ^ \";\n\n        let expected: Expr<_> = ExprKind::Fun(Box::new(Fun {\n            span: t2s(t),\n            pvars: purity::PVars::new(),\n            tvars: ty::TVars::new(),\n            purity: Purity::Pure.into(),\n            params: destruc::List::new(vec![], None),\n            ret_ty: Ty::Int.into(),\n            ret_ty_span: Some(t2s(u)),\n            body_expr: Datum::Int(t2s(v), 1).into(),\n        }))\n        .into();\n\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn fixed_expr_apply() {\n        let j = \"(1 2 3)\";\n        let t = \"^^^^^^^\";\n        let u = \" ^     \";\n        let v = \"   ^   \";\n        let w = \"     ^ \";\n\n        let expected: Expr<_> = ExprKind::App(Box::new(App {\n            span: t2s(t),\n            fun_expr: Datum::Int(t2s(u), 1).into(),\n            ty_args: (),\n            fixed_arg_exprs: vec![Datum::Int(t2s(v), 2).into(), Datum::Int(t2s(w), 3).into()],\n            rest_arg_expr: None,\n        }))\n        .into();\n\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn rest_expr_apply() {\n        let j = \"(1 2 & 3)\";\n        let t = \"^^^^^^^^^\";\n        let u = \" ^       \";\n        let v = \"   ^     \";\n        let w = \"       ^ \";\n\n        let expected: Expr<_> = ExprKind::App(Box::new(App {\n            span: t2s(t),\n            fun_expr: Datum::Int(t2s(u), 1).into(),\n            ty_args: (),\n            fixed_arg_exprs: vec![Datum::Int(t2s(v), 2).into()],\n            rest_arg_expr: Some(Datum::Int(t2s(w), 3).into()),\n        }))\n        .into();\n\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn recur_expr() {\n        let j = \"(recur 1 2 3)\";\n        let t = \"^^^^^^^^^^^^^\";\n        let u = \"       ^     \";\n        let v = \"         ^   \";\n        let w = \"           ^ \";\n\n        let expected: Expr<_> = ExprKind::Recur(Box::new(Recur {\n            span: t2s(t),\n            fixed_arg_exprs: vec![\n                Datum::Int(t2s(u), 1).into(),\n                Datum::Int(t2s(v), 2).into(),\n                Datum::Int(t2s(w), 3).into(),\n            ],\n            rest_arg_expr: None,\n        }))\n        .into();\n\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn if_expr() {\n        let j = \"(if true 1 2)\";\n        let t = \"^^^^^^^^^^^^^\";\n        let u = \"    ^^^^     \";\n        let v = \"         ^   \";\n        let w = \"           ^ \";\n\n        let expected: Expr<_> = ExprKind::Cond(Box::new(Cond {\n            span: t2s(t),\n            test_expr: ExprKind::Lit(Datum::Bool(t2s(u), true)).into(),\n            true_expr: ExprKind::Lit(Datum::Int(t2s(v), 1)).into(),\n            false_expr: ExprKind::Lit(Datum::Int(t2s(w), 2)).into(),\n        }))\n        .into();\n\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn expand_trivial_macro() {\n        let j = \"(letmacro [one (macro-rules [() 1])] (one))\";\n        let t = \"                                     ^^^^^ \";\n        let u = \"                                ^          \";\n\n        let expected: Expr<_> =\n            ExprKind::MacroExpand(t2s(t), Box::new(Datum::Int(t2s(u), 1).into())).into();\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn mutual_module_def() {\n        let j1 = \"(export x y)\";\n        let j2 = \"(def x y)\";\n        let j3 = \"(def y x)\";\n\n        let j = &[j1, j2, j3].join(\"\");\n\n        let module = module_for_str(j).unwrap();\n        assert_eq!(2, module.exports.len());\n    }\n\n    #[test]\n    fn type_predicate() {\n        let j = \"bool?\";\n        let t = \"^^^^^\";\n\n        let expected: Expr<_> = ExprKind::TyPred(t2s(t), ty::pred::TestTy::Bool).into();\n        assert_eq!(expected, expr_for_str(j));\n    }\n\n    #[test]\n    fn equality_predicate() {\n        let j = \"=\";\n        let t = \"^\";\n\n        let expected: Expr<_> = ExprKind::EqPred(t2s(t)).into();\n        assert_eq!(expected, expr_for_str(j));\n    }\n}\n"
  },
  {
    "path": "compiler/hir/macros/expander.rs",
    "content": "use std::collections::HashMap;\nuse std::sync::Arc;\n\nuse arret_syntax::span::Span;\n\nuse crate::context::ModuleId;\nuse crate::hir::macros::linker::{TemplateIdent, VarLinks};\nuse crate::hir::macros::matcher::MatchData;\nuse crate::hir::macros::{get_escaped_ident, starts_with_zero_or_more, Macro};\nuse crate::hir::ns::{Ident, NsDatum, NsId};\nuse crate::hir::scope::{Binding, Scope};\n\nstruct ExpandCursor<'data, 'links> {\n    match_data: &'data MatchData<'data>,\n    var_links: &'links VarLinks,\n\n    ident_index: usize,\n    subtemplate_index: usize,\n}\n\nstruct ExpandCtx<'scope, 'parent> {\n    scope: &'scope mut Scope<'parent>,\n    module_id: Option<ModuleId>,\n    ns_mapping: HashMap<NsId, NsId>,\n}\n\nimpl<'scope, 'parent> ExpandCtx<'scope, 'parent> {\n    fn new(scope: &'scope mut Scope<'parent>, module_id: Option<ModuleId>) -> Self {\n        ExpandCtx {\n            scope,\n            module_id,\n            ns_mapping: HashMap::new(),\n        }\n    }\n\n    fn expand_ident(\n        &mut self,\n        self_mac: &Arc<Macro>,\n        cursor: &mut ExpandCursor<'_, '_>,\n        span: Span,\n        ident: &Ident,\n    ) -> NsDatum {\n        let binding = if !ident.is_underscore() {\n            let template_ident = cursor.var_links.template_ident(cursor.ident_index);\n            cursor.ident_index += 1;\n\n            match template_ident {\n                TemplateIdent::SubpatternVar(var_index) => {\n                    return cursor.match_data.var(*var_index).clone();\n                }\n                TemplateIdent::SelfIdent => Some(Binding::Macro(self.module_id, self_mac.clone())),\n                TemplateIdent::Bound(binding) => Some(if let Some(module_id) = self.module_id {\n                    binding.import_from(module_id)\n                } else {\n                    binding.clone()\n                }),\n                TemplateIdent::Unbound => None,\n            }\n        } else {\n            None\n        };\n\n        // Re-scope this ident\n        let old_ns_id = ident.ns_id();\n        let scope = &mut self.scope;\n        let new_ns_id = self\n            .ns_mapping\n            .entry(old_ns_id)\n            .or_insert_with(|| scope.alloc_ns_id());\n\n        let new_ident = ident.with_ns_id(*new_ns_id);\n\n        if let Some(binding) = binding {\n            scope.replace_binding(span, new_ident.clone(), binding);\n        };\n\n        NsDatum::Ident(span, new_ident)\n    }\n\n    fn expand_zero_or_more(\n        &mut self,\n        self_mac: &Arc<Macro>,\n        cursor: &mut ExpandCursor<'_, '_>,\n        template: &NsDatum,\n    ) -> Vec<NsDatum> {\n        // Find our subpattern index from our subtemplate index\n        let subtemplate_index = cursor.subtemplate_index;\n        let subvar_links = &cursor.var_links.subtemplates()[subtemplate_index];\n        let subpattern_index = subvar_links.subpattern_index();\n        let submatches = &cursor.match_data.subpattern(subpattern_index);\n\n        cursor.subtemplate_index += 1;\n\n        submatches\n            .iter()\n            .map(|m| {\n                // Build a new cursor pointing to our subpattern\n                let mut subcursor = ExpandCursor {\n                    match_data: m,\n                    var_links: subvar_links,\n\n                    ident_index: 0,\n                    subtemplate_index: 0,\n                };\n\n                self.expand_datum(self_mac, &mut subcursor, template)\n            })\n            .collect()\n    }\n\n    fn expand_slice(\n        &mut self,\n        self_mac: &Arc<Macro>,\n        cursor: &mut ExpandCursor<'_, '_>,\n        mut templates: &[NsDatum],\n    ) -> Box<[NsDatum]> {\n        let mut result: Vec<NsDatum> = vec![];\n\n        while !templates.is_empty() {\n            if starts_with_zero_or_more(templates) {\n                let mut expanded = self.expand_zero_or_more(self_mac, cursor, &templates[0]);\n                result.append(&mut expanded);\n\n                // Skip the ellipsis as well\n                templates = &templates[2..];\n            } else {\n                let expanded = self.expand_datum(self_mac, cursor, &templates[0]);\n                result.push(expanded);\n\n                templates = &templates[1..];\n            }\n        }\n\n        result.into_boxed_slice()\n    }\n\n    fn expand_list(\n        &mut self,\n        self_mac: &Arc<Macro>,\n        cursor: &mut ExpandCursor<'_, '_>,\n        span: Span,\n        templates: &[NsDatum],\n    ) -> NsDatum {\n        if let Some(ident) = get_escaped_ident(templates) {\n            NsDatum::Ident(span, ident.clone())\n        } else {\n            NsDatum::List(span, self.expand_slice(self_mac, cursor, templates))\n        }\n    }\n\n    fn expand_datum(\n        &mut self,\n        self_mac: &Arc<Macro>,\n        cursor: &mut ExpandCursor<'_, '_>,\n        template: &NsDatum,\n    ) -> NsDatum {\n        match template {\n            NsDatum::Ident(span, ident) => self.expand_ident(self_mac, cursor, *span, ident),\n            NsDatum::List(span, vs) => self.expand_list(self_mac, cursor, *span, vs),\n            NsDatum::Vector(span, vs) => {\n                NsDatum::Vector(*span, self.expand_slice(self_mac, cursor, vs))\n            }\n            NsDatum::Set(span, vs) => NsDatum::Set(*span, self.expand_slice(self_mac, cursor, vs)),\n            other => other.clone(),\n        }\n    }\n}\n\npub fn expand_rule<'scope, 'parent, 'data>(\n    scope: &'scope mut Scope<'parent>,\n    module_id: Option<ModuleId>,\n    self_mac: &Arc<Macro>,\n    match_data: &'data MatchData<'data>,\n    var_links: &VarLinks,\n    template: &NsDatum,\n) -> NsDatum {\n    let mut mcx = ExpandCtx::new(scope, module_id);\n\n    let mut cursor = ExpandCursor {\n        match_data,\n        var_links,\n\n        ident_index: 0,\n        subtemplate_index: 0,\n    };\n\n    mcx.expand_datum(self_mac, &mut cursor, template)\n}\n"
  },
  {
    "path": "compiler/hir/macros/linker.rs",
    "content": "use std::collections::HashMap;\nuse std::result;\n\nuse arret_syntax::span::Span;\n\nuse crate::hir::error::{Error, ErrorKind, Result};\nuse crate::hir::macros::starts_with_zero_or_more;\nuse crate::hir::ns::{Ident, NsDatum};\nuse crate::hir::scope::{Binding, Scope};\n\n/// Indicates the meaning of a given ident in the template\n#[derive(Debug)]\npub enum TemplateIdent {\n    /// Refers to a template variable with a given index\n    SubpatternVar(usize),\n    /// Ident of the macro being expanded\n    SelfIdent,\n    /// Ident bound to the given binding\n    Bound(Binding),\n    /// Unbound ident\n    Unbound,\n}\n\n/// Precomputed links from variables in the template to the pattern\n#[derive(Debug)]\npub struct VarLinks {\n    subpattern_index: usize,\n    template_idents: Box<[TemplateIdent]>,\n    subtemplates: Box<[VarLinks]>,\n}\n\nimpl VarLinks {\n    /// Index of the subpattern for this subtemplate\n    pub fn subpattern_index(&self) -> usize {\n        self.subpattern_index\n    }\n\n    /// Returns the template ident for the given index\n    pub fn template_ident(&self, i: usize) -> &TemplateIdent {\n        &self.template_idents[i]\n    }\n\n    /// Links for our subtemplates in visit order\n    pub fn subtemplates(&self) -> &[VarLinks] {\n        &self.subtemplates\n    }\n}\n\n#[derive(Debug)]\nstruct FoundVars<'data> {\n    span: Span,\n    idents: Vec<&'data Ident>,\n    subs: Vec<FoundVars<'data>>,\n}\n\nimpl<'data> FoundVars<'data> {\n    fn new(span: Span) -> Self {\n        FoundVars {\n            span,\n            idents: vec![],\n            subs: vec![],\n        }\n    }\n}\n\n/// Tracks which type of input is being provided to `FindVarsCtx`\n#[derive(Clone, Copy, PartialEq)]\nenum FindVarsInputType {\n    Pattern,\n    Template,\n}\n\nstruct FindVarsCtx<'data> {\n    input_type: FindVarsInputType,\n    var_spans: Option<HashMap<&'data Ident, Span>>,\n}\n\ntype FindVarsResult = result::Result<(), Error>;\n\nimpl<'data> FindVarsCtx<'data> {\n    fn new(input_type: FindVarsInputType) -> Self {\n        let var_spans = if input_type == FindVarsInputType::Template {\n            // Duplicate vars are allowed in the template as they must all resolve to the same\n            // value.\n            None\n        } else {\n            // This tracks the name of variables and where they were first used (for error\n            // reporting)\n            Some(HashMap::<&'data Ident, Span>::new())\n        };\n\n        FindVarsCtx {\n            input_type,\n            var_spans,\n        }\n    }\n\n    fn visit_ident(\n        &mut self,\n        pattern_vars: &mut FoundVars<'data>,\n        span: Span,\n        ident: &'data Ident,\n    ) -> FindVarsResult {\n        if ident.is_underscore() {\n            // This is a wildcard\n            return Ok(());\n        }\n\n        if ident.is_ellipsis() {\n            return Err(Error::new(span, ErrorKind::MacroBadEllipsis));\n        }\n\n        if let Some(ref mut var_spans) = self.var_spans {\n            if let Some(old_span) = var_spans.insert(ident, span) {\n                return Err(Error::new(\n                    span,\n                    ErrorKind::DuplicateDef(Some(old_span), ident.name().clone()),\n                ));\n            }\n        }\n\n        pattern_vars.idents.push(ident);\n        Ok(())\n    }\n\n    fn visit_zero_or_more(\n        &mut self,\n        pattern_vars: &mut FoundVars<'data>,\n        pattern: &'data NsDatum,\n    ) -> FindVarsResult {\n        let mut sub_vars = FoundVars::new(pattern.span());\n        self.visit_datum(&mut sub_vars, pattern)?;\n\n        pattern_vars.subs.push(sub_vars);\n        Ok(())\n    }\n\n    fn visit_datum(\n        &mut self,\n        pattern_vars: &mut FoundVars<'data>,\n        pattern: &'data NsDatum,\n    ) -> FindVarsResult {\n        match pattern {\n            NsDatum::Ident(span, ident) => self.visit_ident(pattern_vars, *span, ident),\n            NsDatum::List(_, vs) => self.visit_list(pattern_vars, vs),\n            NsDatum::Vector(_, vs) => self.visit_seq(pattern_vars, vs),\n            NsDatum::Set(span, vs) => self.visit_set(pattern_vars, *span, vs),\n            _ => {\n                // Can't contain a pattern var\n                Ok(())\n            }\n        }\n    }\n\n    fn visit_seq(\n        &mut self,\n        pattern_vars: &mut FoundVars<'data>,\n        mut patterns: &'data [NsDatum],\n    ) -> FindVarsResult {\n        let mut zero_or_more_span: Option<Span> = None;\n\n        while !patterns.is_empty() {\n            if starts_with_zero_or_more(patterns) {\n                let pattern = &patterns[0];\n\n                // Make sure we don't have multiple zero or more matches in the same slice\n                if self.input_type == FindVarsInputType::Pattern {\n                    if let Some(old_span) = zero_or_more_span.replace(pattern.span()) {\n                        // We've already had a zero-or-more match\n                        return Err(Error::new(\n                            pattern.span(),\n                            ErrorKind::MultipleZeroOrMoreMatch(old_span),\n                        ));\n                    }\n                }\n\n                self.visit_zero_or_more(pattern_vars, pattern)?;\n                patterns = &patterns[2..];\n            } else {\n                self.visit_datum(pattern_vars, &patterns[0])?;\n                patterns = &patterns[1..];\n            }\n        }\n\n        Ok(())\n    }\n\n    fn visit_list(\n        &mut self,\n        pattern_vars: &mut FoundVars<'data>,\n        patterns: &'data [NsDatum],\n    ) -> FindVarsResult {\n        match patterns {\n            [NsDatum::Ident(_, ellipsis_ident), escaped_datum] if ellipsis_ident.is_ellipsis() => {\n                if let NsDatum::Ident(_, _) = escaped_datum {\n                    // This isn't actually a list\n                    Ok(())\n                } else {\n                    Err(Error::new(\n                        escaped_datum.span(),\n                        ErrorKind::ExpectedMacroEllipsisEscape(escaped_datum.description()),\n                    ))\n                }\n            }\n            _ => self.visit_seq(pattern_vars, patterns),\n        }\n    }\n\n    fn visit_set(\n        &mut self,\n        pattern_vars: &mut FoundVars<'data>,\n        span: Span,\n        patterns: &'data [NsDatum],\n    ) -> FindVarsResult {\n        if self.input_type == FindVarsInputType::Template {\n            // Sets are expanded the same way as any other sequence\n            return self.visit_seq(pattern_vars, patterns);\n        }\n\n        match patterns.len() {\n            0 => Ok(()),\n            2 if starts_with_zero_or_more(patterns) => {\n                self.visit_zero_or_more(pattern_vars, &patterns[0])\n            }\n            _ => Err(Error::new(span, ErrorKind::MacroBadSetPattern)),\n        }\n    }\n}\n\nfn link_template_ident(\n    scope: &Scope<'_>,\n    self_ident: &Ident,\n    template_ident: &Ident,\n    pattern_idents: &[&Ident],\n) -> TemplateIdent {\n    // First, see if this corresponds to a var in the pattern\n    if let Some(subpattern_index) = pattern_idents\n        .iter()\n        .position(|pattern_ident| *pattern_ident == template_ident)\n    {\n        TemplateIdent::SubpatternVar(subpattern_index)\n    } else if template_ident == self_ident {\n        TemplateIdent::SelfIdent\n    } else if let Some(binding) = scope.get(template_ident) {\n        TemplateIdent::Bound(binding.clone())\n    } else {\n        TemplateIdent::Unbound\n    }\n}\n\nfn link_found_vars(\n    scope: &Scope<'_>,\n    self_ident: &Ident,\n    subpattern_index: usize,\n    pattern_vars: &FoundVars<'_>,\n    template_vars: &FoundVars<'_>,\n) -> Result<VarLinks> {\n    let template_idents = template_vars\n        .idents\n        .iter()\n        .map(|template_ident| {\n            link_template_ident(scope, self_ident, template_ident, &pattern_vars.idents)\n        })\n        .collect();\n\n    let subtemplates = template_vars\n        .subs\n        .iter()\n        .map(|subtemplate_vars| {\n            if subtemplate_vars.idents.is_empty() {\n                return Err(Error::new(\n                    template_vars.span,\n                    ErrorKind::MacroNoTemplateVars,\n                ));\n            }\n\n            // Find possible indices for subpatterns in our pattern\n            let possible_indices = pattern_vars\n                .subs\n                .iter()\n                .enumerate()\n                .filter(|(_, subpattern_vars)| {\n                    subpattern_vars\n                        .idents\n                        .iter()\n                        .any(|subpattern_var| subtemplate_vars.idents.contains(subpattern_var))\n                })\n                .collect::<Vec<(usize, &FoundVars<'_>)>>();\n\n            if possible_indices.is_empty() {\n                return Err(Error::new(template_vars.span, ErrorKind::MacroNoPatternRef));\n            } else if possible_indices.len() > 1 {\n                let sub_var_spans = possible_indices\n                    .iter()\n                    .map(|(_, subpattern_vars)| subpattern_vars.span)\n                    .collect();\n\n                return Err(Error::new(\n                    template_vars.span,\n                    ErrorKind::MacroMultiPatternRef(sub_var_spans),\n                ));\n            }\n\n            // Iterate over our subpatterns\n            let (pattern_index, subpattern_vars) = possible_indices[0];\n            link_found_vars(\n                scope,\n                self_ident,\n                pattern_index,\n                subpattern_vars,\n                subtemplate_vars,\n            )\n        })\n        .collect::<Result<Box<[VarLinks]>>>()?;\n\n    Ok(VarLinks {\n        subpattern_index,\n        template_idents,\n        subtemplates,\n    })\n}\n\npub fn link_rule_vars(\n    scope: &Scope<'_>,\n    self_ident: &Ident,\n    pattern_span: Span,\n    patterns: &[NsDatum],\n    template: &NsDatum,\n) -> Result<VarLinks> {\n    let mut fpvcx = FindVarsCtx::new(FindVarsInputType::Pattern);\n\n    let mut pattern_vars = FoundVars::new(pattern_span);\n    fpvcx.visit_seq(&mut pattern_vars, patterns)?;\n\n    let mut ftvcx = FindVarsCtx::new(FindVarsInputType::Template);\n    let mut template_vars = FoundVars::new(template.span());\n    ftvcx.visit_datum(&mut template_vars, template)?;\n\n    link_found_vars(scope, self_ident, 0, &pattern_vars, &template_vars)\n}\n"
  },
  {
    "path": "compiler/hir/macros/matcher.rs",
    "content": "use std::result;\n\nuse crate::hir::macros::{get_escaped_ident, starts_with_zero_or_more, Rule};\nuse crate::hir::ns::{Ident, NsDatum};\n\n#[derive(Debug)]\npub struct MatchData<'data> {\n    vars: Vec<&'data NsDatum>,\n    // The outside vector is the subpatterns; the inside slice contains the zero or more matches\n    subpatterns: Vec<Box<[MatchData<'data>]>>,\n}\n\nimpl<'data> MatchData<'data> {\n    fn new() -> MatchData<'data> {\n        MatchData {\n            vars: vec![],\n            subpatterns: vec![],\n        }\n    }\n\n    pub fn var(&self, i: usize) -> &'data NsDatum {\n        self.vars[i]\n    }\n\n    pub fn subpattern(&self, i: usize) -> &[MatchData<'data>] {\n        &self.subpatterns[i]\n    }\n}\n\nstruct MatchCtx<'data> {\n    match_data: MatchData<'data>,\n}\n\ntype Result<T> = result::Result<T, ()>;\n\nimpl<'data> MatchCtx<'data> {\n    fn new() -> Self {\n        MatchCtx {\n            match_data: MatchData::new(),\n        }\n    }\n\n    fn match_ident(&mut self, pattern_ident: &'data Ident, arg: &'data NsDatum) -> bool {\n        if pattern_ident.is_underscore() {\n            // This is a wildcard; just discard\n        } else {\n            self.match_data.vars.push(arg);\n        }\n\n        true\n    }\n\n    // TODO: Maps\n    #[allow(clippy::float_cmp)]\n    fn match_datum(&mut self, pattern: &'data NsDatum, arg: &'data NsDatum) -> bool {\n        match (pattern, arg) {\n            (NsDatum::Ident(_, pattern_ident), arg) => self.match_ident(pattern_ident, arg),\n            (NsDatum::Keyword(_, pv), NsDatum::Keyword(_, av)) => pv == av,\n            (NsDatum::List(_, pvs), NsDatum::List(_, avs)) => self.match_slice(pvs, avs),\n            (NsDatum::Vector(_, pvs), NsDatum::Vector(_, avs)) => self.match_slice(pvs, avs),\n            (NsDatum::Set(_, pvs), NsDatum::Set(_, avs)) => self.match_slice(pvs, avs),\n            (NsDatum::Bool(_, pv), NsDatum::Bool(_, av)) => pv == av,\n            (NsDatum::Int(_, pv), NsDatum::Int(_, av)) => pv == av,\n            // Don't match NaNs against other NaNs. This is consistent with `=`.\n            (NsDatum::Float(_, pv), NsDatum::Float(_, av)) => pv == av,\n            (NsDatum::Char(_, pv), NsDatum::Char(_, av)) => pv == av,\n            (NsDatum::Str(_, pv), NsDatum::Str(_, av)) => pv == av,\n            (NsDatum::List(_, pv), NsDatum::Ident(_, arg)) => {\n                if let Some(escaped_ident) = get_escaped_ident(pv) {\n                    escaped_ident.name() == arg.name()\n                } else {\n                    false\n                }\n            }\n            _ => false,\n        }\n    }\n\n    fn match_zero_or_more(&mut self, pattern: &'data NsDatum, args: &'data [NsDatum]) -> bool {\n        let submatch_result = args\n            .iter()\n            .map(|arg| {\n                let mut subcontext = MatchCtx {\n                    match_data: MatchData::new(),\n                };\n\n                if !subcontext.match_datum(pattern, arg) {\n                    Err(())\n                } else {\n                    Ok(subcontext.match_data)\n                }\n            })\n            .collect::<result::Result<Box<[MatchData<'data>]>, ()>>();\n\n        match submatch_result {\n            Ok(submatch_data) => {\n                self.match_data.subpatterns.push(submatch_data);\n                true\n            }\n            Err(()) => false,\n        }\n    }\n\n    fn match_slice(&mut self, mut patterns: &'data [NsDatum], mut args: &'data [NsDatum]) -> bool {\n        loop {\n            if starts_with_zero_or_more(patterns) {\n                let rest_patterns_len = patterns.len() - 2;\n\n                if args.len() < rest_patterns_len {\n                    // Cannot match\n                    break false;\n                }\n\n                let (zero_or_more_args, rest_args) = args.split_at(args.len() - rest_patterns_len);\n                if !self.match_zero_or_more(&patterns[0], zero_or_more_args) {\n                    break false;\n                }\n\n                patterns = &patterns[2..];\n                args = rest_args;\n            } else {\n                let (pattern, arg) = match (patterns.first(), args.first()) {\n                    (Some(pattern), Some(arg)) => (pattern, arg),\n                    (None, None) => {\n                        // Patterns and args ran out at the same time\n                        break true;\n                    }\n                    _ => {\n                        // Mismatched lengths\n                        break false;\n                    }\n                };\n\n                if !self.match_datum(pattern, arg) {\n                    break false;\n                }\n\n                patterns = &patterns[1..];\n                args = &args[1..];\n            }\n        }\n    }\n\n    fn visit_rule(\n        mut self,\n        rule: &'data Rule,\n        arg_data: &'data [NsDatum],\n    ) -> Result<MatchData<'data>> {\n        if self.match_slice(&rule.pattern, arg_data) {\n            Ok(self.match_data)\n        } else {\n            Err(())\n        }\n    }\n}\n\npub fn match_rule<'data>(\n    rule: &'data Rule,\n    arg_data: &'data [NsDatum],\n) -> Result<MatchData<'data>> {\n    let mcx = MatchCtx::new();\n    mcx.visit_rule(rule, arg_data)\n}\n"
  },
  {
    "path": "compiler/hir/macros/mod.rs",
    "content": "mod expander;\nmod linker;\nmod matcher;\n\nuse std::sync::Arc;\n\nuse arret_syntax::span::Span;\n\nuse crate::context::ModuleId;\nuse crate::hir::error::{Error, ErrorKind, Result};\nuse crate::hir::macros::expander::expand_rule;\nuse crate::hir::macros::linker::{link_rule_vars, VarLinks};\nuse crate::hir::macros::matcher::match_rule;\nuse crate::hir::ns::{Ident, NsDatum};\nuse crate::hir::scope::Scope;\n\n#[derive(Debug)]\npub struct Rule {\n    pattern_span: Span,\n    pattern: Box<[NsDatum]>,\n    template: NsDatum,\n    var_links: VarLinks,\n}\n\n#[derive(Debug)]\npub struct Macro {\n    rules: Box<[Rule]>,\n}\n\nimpl Macro {\n    pub fn new(rules: Box<[Rule]>) -> Arc<Self> {\n        Arc::new(Self { rules })\n    }\n}\n\nfn starts_with_zero_or_more(data: &[NsDatum]) -> bool {\n    match data {\n        [_, NsDatum::Ident(_, ident), ..] => ident.is_ellipsis(),\n        _ => false,\n    }\n}\n\nfn get_escaped_ident(data: &[NsDatum]) -> Option<&Ident> {\n    match data {\n        [NsDatum::Ident(_, ellipsis_ident), NsDatum::Ident(_, escaped_ident)]\n            if ellipsis_ident.is_ellipsis() =>\n        {\n            Some(escaped_ident)\n        }\n        _ => None,\n    }\n}\n\nfn lower_macro_rule_datum(\n    scope: &Scope<'_>,\n    self_ident: &Ident,\n    rule_datum: NsDatum,\n) -> Result<Rule> {\n    let (span, mut rule_values) = if let NsDatum::Vector(span, vs) = rule_datum {\n        (span, vs.into_vec())\n    } else {\n        return Err(Error::new(\n            rule_datum.span(),\n            ErrorKind::ExpectedMacroRuleVec(rule_datum.description()),\n        ));\n    };\n\n    if rule_values.len() != 2 {\n        return Err(Error::new(\n            span,\n            ErrorKind::WrongMacroRuleVecCount(rule_values.len()),\n        ));\n    }\n\n    let template = rule_values.pop().unwrap();\n    let pattern_datum = rule_values.pop().unwrap();\n\n    let (pattern_span, pattern) = if let NsDatum::List(span, vs) = pattern_datum {\n        (span, vs)\n    } else {\n        return Err(Error::new(\n            pattern_datum.span(),\n            ErrorKind::ExpectedMacroRulePatternList(pattern_datum.description()),\n        ));\n    };\n\n    let var_links = link_rule_vars(scope, self_ident, pattern_span, &pattern, &template)?;\n\n    Ok(Rule {\n        pattern_span,\n        pattern,\n        template,\n        var_links,\n    })\n}\n\npub fn lower_macro_rules(\n    scope: &Scope<'_>,\n    self_ident: &Ident,\n    macro_rules_data: Vec<NsDatum>,\n) -> Result<Arc<Macro>> {\n    let rules = macro_rules_data\n        .into_iter()\n        .map(|rule_datum| lower_macro_rule_datum(scope, self_ident, rule_datum))\n        .collect::<Result<Box<[Rule]>>>()?;\n\n    Ok(Macro::new(rules))\n}\n\npub fn expand_macro<'s, 'p>(\n    scope: &'s mut Scope<'p>,\n    invocation_span: Span,\n    module_id: Option<ModuleId>,\n    mac: &Arc<Macro>,\n    arg_data: &[NsDatum],\n) -> Result<NsDatum> {\n    for rule in mac.rules.iter() {\n        let match_result = match_rule(rule, arg_data);\n\n        if let Ok(match_data) = match_result {\n            return Ok(expand_rule(\n                scope,\n                module_id,\n                mac,\n                &match_data,\n                &rule.var_links,\n                &rule.template,\n            ));\n        }\n    }\n\n    Err(Error::new(\n        invocation_span,\n        ErrorKind::NoMacroRule(mac.rules.iter().map(|rule| rule.pattern_span).collect()),\n    ))\n}\n"
  },
  {
    "path": "compiler/hir/mod.rs",
    "content": "pub(crate) mod destruc;\npub(crate) mod error;\npub(crate) mod exports;\npub(crate) mod import;\npub(crate) mod loader;\npub(crate) mod lowering;\nmod macros;\npub(crate) mod ns;\nmod prim;\nmod records;\npub(crate) mod scope;\nmod types;\nmod util;\npub(crate) mod var_id;\npub(crate) mod visitor;\n\nuse std::sync::Arc;\n\nuse arret_syntax::datum::Datum;\nuse arret_syntax::span::Span;\n\nuse crate::rfi;\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::ty::record;\nuse crate::ty::Ty;\n\npub use crate::hir::var_id::{ExportId, LocalId};\n\n/// DeclTy is a type declared by a user\n///\n/// The `Known` variant indicates the type is specified while `Free` indicates it must be inferred.\n#[derive(PartialEq, Debug, Clone)]\npub enum DeclTy {\n    Known(ty::Ref<ty::Poly>),\n    Free,\n}\n\nimpl From<Ty<ty::Poly>> for DeclTy {\n    fn from(ty: Ty<ty::Poly>) -> Self {\n        DeclTy::Known(ty::Ref::Fixed(ty))\n    }\n}\n\nimpl From<ty::Ref<ty::Poly>> for DeclTy {\n    fn from(poly: ty::Ref<ty::Poly>) -> Self {\n        DeclTy::Known(poly)\n    }\n}\n\n/// Decl is a purity declared by a user\n///\n/// The `Known` variant indicates the purity is specified while `Free` indicates it must be\n/// inferred.\n#[derive(PartialEq, Eq, Debug, Clone)]\npub enum DeclPurity {\n    Known(purity::Ref),\n    Free,\n}\n\nimpl From<purity::Ref> for DeclPurity {\n    fn from(poly: purity::Ref) -> Self {\n        DeclPurity::Known(poly)\n    }\n}\n\n#[cfg(test)]\nimpl From<purity::Purity> for DeclPurity {\n    fn from(purity: purity::Purity) -> Self {\n        DeclPurity::Known(purity::Ref::Fixed(purity))\n    }\n}\n\npub trait Phase: Clone + std::cmp::PartialEq + std::fmt::Debug {\n    type Purity: Clone + std::cmp::PartialEq + std::fmt::Debug;\n    type DeclType: Clone + std::cmp::PartialEq + std::fmt::Debug;\n    type ResultType: Clone + std::cmp::PartialEq + std::fmt::Debug;\n    type TyArgs: Clone + std::cmp::PartialEq + std::fmt::Debug;\n}\n\n#[derive(Clone, PartialEq, Debug)]\npub struct Inferred {}\nimpl Phase for Inferred {\n    type Purity = purity::Ref;\n    type DeclType = ty::Ref<ty::Poly>;\n    type ResultType = ty::Ref<ty::Poly>;\n    type TyArgs = ty::ty_args::TyArgs<ty::Poly>;\n}\n\n#[derive(Clone, PartialEq, Debug)]\npub struct Lowered {}\nimpl Phase for Lowered {\n    type Purity = DeclPurity;\n    type DeclType = DeclTy;\n    type ResultType = ();\n    type TyArgs = ();\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct Fun<P: Phase> {\n    pub span: Span,\n\n    pub pvars: purity::PVars,\n    pub tvars: ty::TVars,\n\n    pub purity: P::Purity,\n    pub params: destruc::List<P>,\n\n    pub ret_ty: P::DeclType,\n    pub ret_ty_span: Option<Span>,\n\n    pub body_expr: Expr<P>,\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct Cond<P: Phase> {\n    pub span: Span,\n    pub test_expr: Expr<P>,\n    pub true_expr: Expr<P>,\n    pub false_expr: Expr<P>,\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct Let<P: Phase> {\n    pub span: Span,\n    pub destruc: destruc::Destruc<P>,\n    pub value_expr: Expr<P>,\n    pub body_expr: Expr<P>,\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct App<P: Phase> {\n    pub span: Span,\n    pub fun_expr: Expr<P>,\n    pub ty_args: P::TyArgs,\n    pub fixed_arg_exprs: Vec<Expr<P>>,\n    pub rest_arg_expr: Option<Expr<P>>,\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct Recur<P: Phase> {\n    pub span: Span,\n    pub fixed_arg_exprs: Vec<Expr<P>>,\n    pub rest_arg_expr: Option<Expr<P>>,\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct FieldAccessor {\n    pub span: Span,\n    pub record_cons: record::ConsId,\n    pub field_index: usize,\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct Expr<P: Phase> {\n    pub result_ty: P::ResultType,\n    pub kind: ExprKind<P>,\n}\n\nimpl From<Datum> for Expr<Lowered> {\n    fn from(datum: Datum) -> Expr<Lowered> {\n        ExprKind::Lit(datum).into()\n    }\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub enum ExprKind<P: Phase> {\n    Lit(Datum),\n    App(Box<App<P>>),\n    Recur(Box<Recur<P>>),\n    Fun(Box<Fun<P>>),\n    RustFun(Arc<rfi::Fun>),\n    Let(Box<Let<P>>),\n    Cond(Box<Cond<P>>),\n    ExportRef(Span, ExportId),\n    LocalRef(Span, LocalId),\n\n    TyPred(Span, ty::pred::TestTy),\n    EqPred(Span),\n    RecordCons(Span, record::ConsId),\n    FieldAccessor(Box<FieldAccessor>),\n\n    Do(Vec<Expr<P>>),\n\n    /// Used for tracing macro expansion for error report and debug information\n    ///\n    /// Other than the above this should be treated identically to the inner expression.\n    MacroExpand(Span, Box<Expr<P>>),\n}\n\nimpl From<ExprKind<Lowered>> for Expr<Lowered> {\n    fn from(kind: ExprKind<Lowered>) -> Expr<Lowered> {\n        Expr {\n            result_ty: (),\n            kind,\n        }\n    }\n}\n\n#[derive(PartialEq, Debug)]\npub struct Def<P: Phase> {\n    pub span: Span,\n    pub macro_invocation_span: Option<Span>,\n    pub destruc: destruc::Destruc<P>,\n    pub value_expr: Expr<P>,\n}\n\npub use self::loader::PackagePaths;\npub use self::types::lower_poly;\npub use self::types::str_for_purity;\npub use self::types::str_for_ty_ref;\n\n#[cfg(test)]\npub use self::types::{lower_polymorphic_var_set, poly_for_str, try_lower_purity, tvar_bounded_by};\n\n#[cfg(test)]\npub use self::lowering::expr_for_str;\n"
  },
  {
    "path": "compiler/hir/ns.rs",
    "content": "use std::vec;\n\nuse arret_syntax::datum::{DataStr, Datum};\nuse arret_syntax::span::Span;\n\nuse crate::hir::scope::Scope;\n\nnew_counting_id_type!(NsIdCounter, NsId);\n\n#[derive(Clone, PartialEq, Eq, Hash, Debug)]\npub struct Ident {\n    ns_id: NsId,\n    data_name: DataStr,\n}\n\nimpl Ident {\n    pub fn new(ns_id: NsId, data_name: DataStr) -> Ident {\n        Ident { ns_id, data_name }\n    }\n\n    pub fn ns_id(&self) -> NsId {\n        self.ns_id\n    }\n\n    pub fn name(&self) -> &DataStr {\n        &self.data_name\n    }\n\n    pub fn into_name(self) -> DataStr {\n        self.data_name\n    }\n\n    pub fn is_underscore(&self) -> bool {\n        self.data_name.as_ref() == \"_\"\n    }\n\n    pub fn is_ellipsis(&self) -> bool {\n        self.data_name.as_ref() == \"...\"\n    }\n\n    pub fn is_ampersand(&self) -> bool {\n        self.data_name.as_ref() == \"&\"\n    }\n\n    pub fn with_ns_id(&self, new_ns_id: NsId) -> Ident {\n        Ident {\n            ns_id: new_ns_id,\n            data_name: self.data_name.clone(),\n        }\n    }\n}\n\n#[derive(Clone, PartialEq, Debug)]\npub enum NsDatum {\n    Bool(Span, bool),\n    Char(Span, char),\n    Int(Span, i64),\n    Float(Span, f64),\n    List(Span, Box<[NsDatum]>),\n    Str(Span, DataStr),\n    Keyword(Span, DataStr),\n    Ident(Span, Ident),\n    Vector(Span, Box<[NsDatum]>),\n    Map(Span, Box<[(NsDatum, NsDatum)]>),\n    Set(Span, Box<[NsDatum]>),\n}\n\nimpl NsDatum {\n    fn map_syntax_data(vs: &[Datum]) -> Box<[NsDatum]> {\n        vs.iter().map(Self::from_syntax_datum).collect()\n    }\n\n    pub fn from_syntax_datum(value: &Datum) -> NsDatum {\n        match value {\n            Datum::Bool(span, v) => NsDatum::Bool(*span, *v),\n            Datum::Char(span, v) => NsDatum::Char(*span, *v),\n            Datum::Int(span, v) => NsDatum::Int(*span, *v),\n            Datum::Float(span, v) => NsDatum::Float(*span, *v),\n            Datum::Str(span, v) => NsDatum::Str(*span, v.clone()),\n            Datum::Sym(span, v) => {\n                if v.starts_with(':') {\n                    NsDatum::Keyword(*span, v.clone())\n                } else {\n                    NsDatum::Ident(*span, Ident::new(Scope::root_ns_id(), v.clone()))\n                }\n            }\n            Datum::List(span, vs) => NsDatum::List(*span, Self::map_syntax_data(vs)),\n            Datum::Vector(span, vs) => NsDatum::Vector(*span, Self::map_syntax_data(vs)),\n            Datum::Set(span, vs) => NsDatum::Set(*span, Self::map_syntax_data(vs)),\n            Datum::Map(span, vs) => NsDatum::Map(\n                *span,\n                vs.iter()\n                    .map(|(k, v)| (NsDatum::from_syntax_datum(k), NsDatum::from_syntax_datum(v)))\n                    .collect(),\n            ),\n        }\n    }\n\n    fn map_nsdata(vs: Box<[NsDatum]>) -> Box<[Datum]> {\n        vs.into_vec()\n            .into_iter()\n            .map(NsDatum::into_syntax_datum)\n            .collect()\n    }\n\n    pub fn into_syntax_datum(self) -> Datum {\n        match self {\n            NsDatum::Bool(span, v) => Datum::Bool(span, v),\n            NsDatum::Char(span, v) => Datum::Char(span, v),\n            NsDatum::Int(span, v) => Datum::Int(span, v),\n            NsDatum::Float(span, v) => Datum::Float(span, v),\n            NsDatum::Str(span, v) => Datum::Str(span, v),\n            NsDatum::Keyword(span, v) => Datum::Sym(span, v),\n            NsDatum::Ident(span, v) => Datum::Sym(span, v.into_name()),\n            NsDatum::List(span, vs) => Datum::List(span, Self::map_nsdata(vs)),\n            NsDatum::Vector(span, vs) => Datum::Vector(span, Self::map_nsdata(vs)),\n            NsDatum::Set(span, vs) => Datum::Set(span, Self::map_nsdata(vs)),\n            NsDatum::Map(span, vs) => Datum::Map(\n                span,\n                vs.into_vec()\n                    .into_iter()\n                    .map(|(k, v)| (k.into_syntax_datum(), v.into_syntax_datum()))\n                    .collect(),\n            ),\n        }\n    }\n\n    pub fn span(&self) -> Span {\n        match self {\n            NsDatum::Bool(span, _)\n            | NsDatum::Char(span, _)\n            | NsDatum::Int(span, _)\n            | NsDatum::Float(span, _)\n            | NsDatum::Str(span, _)\n            | NsDatum::Keyword(span, _)\n            | NsDatum::Ident(span, _)\n            | NsDatum::List(span, _)\n            | NsDatum::Vector(span, _)\n            | NsDatum::Set(span, _)\n            | NsDatum::Map(span, _) => *span,\n        }\n    }\n\n    pub fn description(&self) -> &'static str {\n        match self {\n            NsDatum::Bool(_, true) => \"boolean true\",\n            NsDatum::Bool(_, false) => \"boolean false\",\n            NsDatum::Char(_, _) => \"character\",\n            NsDatum::Int(_, _) => \"integer\",\n            NsDatum::Float(_, _) => \"floating point number\",\n            NsDatum::Str(_, _) => \"string\",\n            NsDatum::Keyword(_, _) => \"keyword\",\n            NsDatum::Ident(_, _) => \"symbol\",\n\n            NsDatum::List(_, vs) if vs.is_empty() => \"empty list\",\n            NsDatum::List(_, _) => \"list\",\n\n            NsDatum::Vector(_, vs) if vs.is_empty() => \"empty vector\",\n            NsDatum::Vector(_, _) => \"vector\",\n\n            NsDatum::Set(_, vs) if vs.is_empty() => \"empty set\",\n            NsDatum::Set(_, _) => \"set\",\n\n            NsDatum::Map(_, vs) if vs.is_empty() => \"empty map\",\n            NsDatum::Map(_, _) => \"map\",\n        }\n    }\n}\n\n/// Iterator for NsDatum used inside the HIR\n///\n/// This is a specific type as we use .as_slice() to peek at data in certain places for\n/// context-sensitive parsing.\npub type NsDataIter = vec::IntoIter<NsDatum>;\n"
  },
  {
    "path": "compiler/hir/prim.rs",
    "content": "use crate::hir::scope::Binding;\n\nmacro_rules! export_prims {\n    ( $( ($n:expr, $i:ident) ),* ) => {\n        #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]\n        pub enum Prim {\n            // `(import)` is magically added to every scope. If we add it again in `PRIM_EXPORTS`\n            // we will trigger duplicate definition errors if someone imports the prim modules.\n            // Note that `(import)` is actually parsed specially; this is here to catch duplicate\n            // definitions.\n            ImportPlaceholder,\n            $($i,)*\n        }\n\n        pub const PRIM_EXPORTS: &[(&str, Binding)] = &[\n            // This is a pseudo-primitive. Unlike normal primitives we can take the value of `=`.\n            (\"=\", Binding::EqPred),\n            $(\n                ($n, Binding::Prim(Prim::$i))\n            ),*\n        ];\n    }\n}\n\nexport_prims!(\n    (\"def\", Def),\n    (\"let\", Let),\n    (\"fn\", Fun),\n    (\"if\", If),\n    (\"do\", Do),\n    (\"recur\", Recur),\n    (\"quote\", Quote),\n    (\"export\", Export),\n    (\"defmacro\", DefMacro),\n    (\"letmacro\", LetMacro),\n    (\"macro-rules\", MacroRules),\n    (\"deftype\", DefType),\n    (\"lettype\", LetType),\n    (\"defrecord\", DefRecord),\n    (\"letrecord\", LetRecord),\n    (\"compile-error\", CompileError),\n    (\"All\", All)\n);\n"
  },
  {
    "path": "compiler/hir/records.rs",
    "content": "use arret_syntax::datum::DataStr;\nuse arret_syntax::span::Span;\n\nuse crate::hir::error::{Error, ErrorKind, Result};\nuse crate::hir::ns::Ident;\nuse crate::hir::ns::{NsDataIter, NsDatum};\nuse crate::hir::scope::{Binding, Scope};\nuse crate::hir::types::lower_poly;\nuse crate::hir::types::lower_polymorphic_var_list;\nuse crate::hir::util::{expect_ns_ident, expect_spanned_ns_ident};\nuse crate::ty;\nuse crate::ty::record;\nuse crate::ty::Ty;\n\nenum LoweredRecordCons {\n    Parameterised(Span, Ident, NsDataIter),\n    Singleton(Span, Ident),\n}\n\nfn lower_record_field_decl(scope: &Scope<'_>, field_datum: NsDatum) -> Result<record::Field> {\n    let datum_span = field_datum.span();\n    let datum_description = field_datum.description();\n\n    let (ident, poly) = match field_datum {\n        NsDatum::Ident(_, ident) => (ident, Ty::Any.into()),\n        NsDatum::Vector(span, vs) => {\n            let mut data = vs.into_vec();\n\n            if data.len() != 2 {\n                return Err(Error::new(\n                    span,\n                    ErrorKind::ExpectedRecordFieldDecl(datum_description),\n                ));\n            }\n\n            let poly = lower_poly(scope, data.pop().unwrap())?;\n            let ident = expect_ns_ident(data.pop().unwrap(), \"new record field name\")?;\n\n            (ident, poly)\n        }\n        other => {\n            return Err(Error::new(\n                other.span(),\n                ErrorKind::ExpectedRecordFieldDecl(datum_description),\n            ));\n        }\n    };\n\n    Ok(record::Field::new(datum_span, ident.into_name(), poly))\n}\n\n/// Lowers either the type or value constructor for a `(defrecord)`\nfn lower_record_cons_decl<F>(cons_datum: NsDatum, error_kind_cons: F) -> Result<LoweredRecordCons>\nwhere\n    F: Fn(&'static str) -> ErrorKind,\n{\n    let datum_description = cons_datum.description();\n\n    match cons_datum {\n        NsDatum::Ident(span, ident) => Ok(LoweredRecordCons::Singleton(span, ident)),\n        NsDatum::List(span, vs) => {\n            let mut param_data_iter = vs.into_vec().into_iter();\n\n            if let Some(name_datum) = param_data_iter.next() {\n                let (ident_span, ident) =\n                    expect_spanned_ns_ident(name_datum, \"new record constructor name\")?;\n\n                Ok(LoweredRecordCons::Parameterised(\n                    ident_span,\n                    ident,\n                    param_data_iter,\n                ))\n            } else {\n                Err(Error::new(span, error_kind_cons(datum_description)))\n            }\n        }\n        other => Err(Error::new(other.span(), error_kind_cons(datum_description))),\n    }\n}\n\npub fn lower_record(\n    outer_scope: &mut Scope<'_>,\n    ty_cons_datum: NsDatum,\n    value_cons_datum: NsDatum,\n) -> Result<()> {\n    use crate::hir::types::PolymorphicVar;\n    use crate::ty::ty_args::TyArgs;\n\n    let mut inner_scope = outer_scope.child();\n\n    // Lower our type constructor\n    let ty_cons_span = ty_cons_datum.span();\n    let ty_cons_decl = lower_record_cons_decl(ty_cons_datum, ErrorKind::ExpectedRecordTyConsDecl)?;\n\n    let (ty_ident_span, ty_ident, poly_vars) = match ty_cons_decl {\n        LoweredRecordCons::Singleton(span, ident) => (span, ident, None),\n        LoweredRecordCons::Parameterised(span, ident, param_data_iter) => {\n            let poly_params =\n                lower_polymorphic_var_list(outer_scope, &mut inner_scope, param_data_iter)?;\n\n            (span, ident, Some(poly_params))\n        }\n    };\n\n    // Lower our value destructor\n    let value_cons_decl =\n        lower_record_cons_decl(value_cons_datum, ErrorKind::ExpectedRecordValueConsDecl)?;\n\n    let fields: Box<[record::Field]>;\n    let (value_cons_ident_span, value_cons_ident) = match value_cons_decl {\n        LoweredRecordCons::Singleton(_, _) => {\n            todo!(\"singleton record values\");\n        }\n        LoweredRecordCons::Parameterised(span, ident, param_data_iter) => {\n            fields = param_data_iter\n                .map(|field_datum| lower_record_field_decl(&inner_scope, field_datum))\n                .collect::<Result<Box<_>>>()?;\n\n            (span, ident)\n        }\n    };\n\n    // Convert our lowered polymorphic vars to polymorphic parameters\n    let poly_params_list = match poly_vars {\n        Some(poly_vars) => {\n            use crate::ty::var_usage::VarUsages;\n\n            let mut var_usages = VarUsages::new();\n            for field in fields.iter() {\n                var_usages.add_poly_usages(field.ty_ref());\n            }\n\n            let poly_params_list = poly_vars\n                .into_vec()\n                .into_iter()\n                .map(|poly_var| {\n                    match poly_var {\n                        PolymorphicVar::PVar(pvar) => {\n                            if let Some(variance) = var_usages.pvar_variance(&pvar) {\n                                Ok(record::PolyParam::PVar(variance, pvar))\n                            } else {\n                                Err(Error::new(\n                                    pvar.span(),\n                                    ErrorKind::UnusedPolyPurityParam(pvar),\n                                ))\n                            }\n                        }\n                        // It'd be nice to check if the param was used but it's been erased to\n                        // `Pure` by this point\n                        PolymorphicVar::Pure(span) => Ok(record::PolyParam::Pure(span)),\n                        PolymorphicVar::TVar(tvar) => {\n                            if let Some(variance) = var_usages.tvar_variance(&tvar) {\n                                Ok(record::PolyParam::TVar(variance, tvar))\n                            } else {\n                                Err(Error::new(tvar.span(), ErrorKind::UnusedPolyTyParam(tvar)))\n                            }\n                        }\n                        PolymorphicVar::TFixed(span, fixed_poly) => {\n                            Ok(record::PolyParam::TFixed(span, fixed_poly))\n                        }\n                    }\n                })\n                .collect::<Result<Box<[record::PolyParam]>>>()?;\n\n            Some(poly_params_list)\n        }\n        None => None,\n    };\n\n    let predicate_name: DataStr = format!(\"{}?\", value_cons_ident.name()).into();\n    let predicate_ident = Ident::new(value_cons_ident.ns_id(), predicate_name);\n\n    let record_ty_cons = record::Cons::new(\n        ty_cons_span,\n        ty_ident.name().clone(),\n        value_cons_ident.name().clone(),\n        poly_params_list,\n        fields,\n    );\n\n    for (idx, field) in record_ty_cons.fields().iter().enumerate() {\n        if field.name().as_ref() != \"_\" {\n            let accessor_name = format!(\"{}-{}\", value_cons_ident.name(), field.name());\n            let accessor_ident = Ident::new(value_cons_ident.ns_id(), accessor_name.into());\n\n            outer_scope.insert_binding(\n                field.span(),\n                accessor_ident,\n                Binding::FieldAccessor(record_ty_cons.clone(), idx),\n            )?;\n        }\n    }\n\n    outer_scope.insert_binding(\n        value_cons_ident_span,\n        predicate_ident,\n        Binding::TyPred(ty::pred::TestTy::RecordClass(record_ty_cons.clone())),\n    )?;\n\n    outer_scope.insert_binding(\n        value_cons_ident_span,\n        value_cons_ident,\n        Binding::RecordValueCons(record_ty_cons.clone()),\n    )?;\n\n    if record_ty_cons.is_singleton() {\n        // We were used as a singleton; bind a type\n        let record_instance = record::Instance::new(record_ty_cons, TyArgs::empty());\n        outer_scope.insert_binding(ty_ident_span, ty_ident, Binding::Ty(record_instance.into()))?;\n    } else {\n        // We were used as a type constructor; bind a type constructor\n        outer_scope.insert_binding(\n            ty_ident_span,\n            ty_ident,\n            Binding::RecordTyCons(record_ty_cons),\n        )?;\n    };\n\n    Ok(())\n}\n"
  },
  {
    "path": "compiler/hir/scope.rs",
    "content": "use std::collections::HashMap;\nuse std::sync::Arc;\n\nuse arret_syntax::span::Span;\n\nuse crate::context::ModuleId;\nuse crate::hir::error::{Error, ErrorKind};\nuse crate::hir::macros::Macro;\nuse crate::hir::ns::{Ident, NsDatum, NsId, NsIdCounter};\nuse crate::hir::prim::Prim;\nuse crate::hir::{types, LocalId};\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::ty::record;\n\n#[derive(Clone, Debug)]\npub enum Binding {\n    Var(Option<ModuleId>, LocalId),\n    Prim(Prim),\n    Macro(Option<ModuleId>, Arc<Macro>),\n    Ty(ty::Ref<ty::Poly>),\n    TyCons(types::TyCons),\n    TyPred(ty::pred::TestTy),\n    EqPred,\n    RecordValueCons(record::ConsId),\n    RecordTyCons(record::ConsId),\n    FieldAccessor(record::ConsId, usize),\n    Purity(purity::Ref),\n}\n\nimpl Binding {\n    pub fn description(&self) -> &'static str {\n        match self {\n            Binding::Var(_, _) | Binding::TyPred(_) | Binding::EqPred => \"value\",\n            Binding::Prim(_) => \"primitive\",\n            Binding::Macro(_, _) => \"macro\",\n            Binding::Ty(_) => \"type\",\n            Binding::TyCons(_) => \"type constructor\",\n            Binding::RecordValueCons(_) => \"record value constructor\",\n            Binding::RecordTyCons(_) => \"record type constructor\",\n            Binding::FieldAccessor(_, _) => \"record field accessor\",\n            Binding::Purity(_) => \"purity\",\n        }\n    }\n\n    pub fn import_from(&self, module_id: ModuleId) -> Binding {\n        match self {\n            Binding::Var(None, local_id) => Binding::Var(Some(module_id), *local_id),\n            Binding::Macro(None, macro_id) => Binding::Macro(Some(module_id), macro_id.clone()),\n            other => other.clone(),\n        }\n    }\n}\n\npub struct SpannedBinding {\n    span: Option<Span>,\n    binding: Binding,\n}\n\npub struct Scope<'parent> {\n    ns_id_counter: NsIdCounter,\n\n    entries: HashMap<Ident, SpannedBinding>,\n    parent: Option<&'parent Scope<'parent>>,\n}\n\nimpl<'parent> Scope<'parent> {\n    pub fn root_ns_id() -> NsId {\n        NsId::new(0)\n    }\n\n    /// Creates a new root scope with entries\n    pub fn new_with_entries<I>(entries: I) -> Scope<'static>\n    where\n        I: Iterator<Item = (&'static str, Binding)>,\n    {\n        let entries = entries\n            .map(|(name, binding)| {\n                (\n                    Ident::new(Self::root_ns_id(), (*name).into()),\n                    SpannedBinding {\n                        span: None,\n                        binding,\n                    },\n                )\n            })\n            .collect::<HashMap<Ident, SpannedBinding>>();\n\n        Scope {\n            ns_id_counter: NsIdCounter::new(),\n            entries,\n            parent: None,\n        }\n    }\n\n    /// Creates a root scope containing `import`\n    pub fn root() -> Scope<'static> {\n        // The default root scope only consists of a placeholder for (import)\n        let entries = std::iter::once((\"import\", Binding::Prim(Prim::ImportPlaceholder)));\n        Self::new_with_entries(entries)\n    }\n\n    /// Creates a new root scope containing all primitives and types\n    pub fn new_with_primitives() -> Scope<'static> {\n        use crate::hir::prim::PRIM_EXPORTS;\n        use crate::hir::types::TY_EXPORTS;\n\n        let entries = PRIM_EXPORTS\n            .iter()\n            .chain(TY_EXPORTS.iter())\n            .map(|(name, binding)| (*name, binding.clone()));\n\n        Self::new_with_entries(entries)\n    }\n\n    pub fn child(&'parent self) -> Scope<'parent> {\n        Scope {\n            ns_id_counter: self.ns_id_counter.clone(),\n            entries: HashMap::new(),\n            parent: Some(self),\n        }\n    }\n\n    /// Returns the binding for a given datum if it exists\n    ///\n    /// Only idents can have bindings; other data will return None.\n    pub fn get_datum<'a>(&'a self, datum: &NsDatum) -> Option<&'a Binding> {\n        if let NsDatum::Ident(_, ident) = datum {\n            self.get(ident)\n        } else {\n            None\n        }\n    }\n\n    /// Returns the binding for a given ident if it exists\n    pub fn get<'a>(&'a self, ident: &Ident) -> Option<&'a Binding> {\n        self.entries.get(ident).map(|e| &e.binding).or_else(|| {\n            if let Some(parent) = self.parent {\n                parent.get(ident)\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Returns the binding for a given ident if it exists, otherwise returns an error\n    pub fn get_or_err<'a>(&'a self, span: Span, ident: &Ident) -> Result<&'a Binding, Error> {\n        self.get(ident)\n            .ok_or_else(|| Error::new(span, ErrorKind::UnboundIdent(ident.name().clone())))\n    }\n\n    /// Inserts a new binding if it doesn't exist or redefinition is allowed\n    pub fn insert_binding(\n        &mut self,\n        span: Span,\n        ident: Ident,\n        binding: Binding,\n    ) -> Result<(), Error> {\n        use std::iter;\n        self.insert_bindings(span, iter::once((ident, binding)))\n    }\n\n    pub fn insert_bindings<I>(&mut self, span: Span, new_bindings: I) -> Result<(), Error>\n    where\n        I: Iterator<Item = (Ident, Binding)>,\n    {\n        use std::collections::hash_map::Entry;\n\n        self.entries.reserve(new_bindings.size_hint().0);\n\n        for (ident, binding) in new_bindings.filter(|b| !b.0.is_underscore()) {\n            let entry = SpannedBinding {\n                span: Some(span),\n                binding,\n            };\n\n            match self.entries.entry(ident) {\n                Entry::Occupied(occupied) => {\n                    return Err(Error::new(\n                        span,\n                        ErrorKind::DuplicateDef(occupied.get().span, occupied.key().name().clone()),\n                    ));\n                }\n                Entry::Vacant(vacant) => {\n                    vacant.insert(entry);\n                }\n            }\n        }\n        Ok(())\n    }\n\n    /// Unconditionally replaces a binding\n    pub fn replace_binding(&mut self, span: Span, ident: Ident, binding: Binding) {\n        self.entries.insert(\n            ident,\n            SpannedBinding {\n                span: Some(span),\n                binding,\n            },\n        );\n    }\n\n    pub fn insert_local(\n        &mut self,\n        span: Span,\n        ident: Ident,\n        local_id: LocalId,\n    ) -> Result<(), Error> {\n        self.insert_binding(span, ident, Binding::Var(None, local_id))\n    }\n\n    /// Returns all bound idents\n    pub fn bound_idents(&self) -> impl Iterator<Item = &Ident> {\n        self.entries.iter().map(|(ident, _)| ident)\n    }\n\n    /// Allocates a new NsId\n    ///\n    /// This is not globally unique; it will only be unique in the current scope chain\n    pub fn alloc_ns_id(&mut self) -> NsId {\n        self.ns_id_counter.alloc()\n    }\n\n    /// Exports our entire contents as bindings suitable to be imported in to another scope\n    ///\n    /// This is used to fold child REPL scopes back in to their parent\n    pub fn into_exported_bindings(self) -> HashMap<Ident, SpannedBinding> {\n        self.entries\n    }\n\n    pub fn import_bindings(\n        &mut self,\n        exported_bindings: impl IntoIterator<Item = (Ident, SpannedBinding)>,\n        module_id: ModuleId,\n    ) {\n        self.entries.extend(exported_bindings.into_iter().map(\n            |(ident, SpannedBinding { span, binding })| {\n                (\n                    ident,\n                    SpannedBinding {\n                        span,\n                        binding: binding.import_from(module_id),\n                    },\n                )\n            },\n        ));\n    }\n}\n"
  },
  {
    "path": "compiler/hir/types.rs",
    "content": "use arret_syntax::span::Span;\n\nuse crate::hir::error::{\n    Error, ErrorKind, ExpectedPolyPurityArg, PolyArgIsNotPure, PolyArgIsNotTy, Result,\n};\nuse crate::hir::ns::{Ident, NsDataIter, NsDatum};\nuse crate::hir::prim::Prim;\nuse crate::hir::scope::{Binding, Scope};\nuse crate::hir::util::{\n    expect_arg_count, expect_one_arg, expect_spanned_ns_ident, try_take_rest_arg,\n};\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\nuse crate::ty::record;\nuse crate::ty::ty_args::TyArgs;\nuse crate::ty::Ty;\n\n#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]\npub enum TyCons {\n    List,\n    Vector,\n    Vectorof,\n    Set,\n    Map,\n    Union,\n    #[cfg(test)]\n    RawU,\n}\n\n#[derive(Clone)]\npub enum PolymorphicVar {\n    TVar(ty::TVarId),\n    PVar(purity::PVarId),\n    TFixed(Span, ty::Ref<ty::Poly>),\n    Pure(Span),\n}\n\nstruct LoweredPolymorphicVar {\n    ident: Ident,\n    polymorphic_var: PolymorphicVar,\n}\n\nfn lower_polymorphic_var(scope: &Scope<'_>, tvar_datum: NsDatum) -> Result<LoweredPolymorphicVar> {\n    let span = tvar_datum.span();\n\n    match tvar_datum {\n        NsDatum::Ident(span, ident) => {\n            if ident.is_underscore() {\n                return Err(Error::new(span, ErrorKind::AnonymousPolymorphicParam));\n            }\n\n            let source_name = ident.name().clone();\n            return Ok(LoweredPolymorphicVar {\n                ident,\n                polymorphic_var: PolymorphicVar::TVar(ty::TVar::new(\n                    span,\n                    source_name,\n                    Ty::Any.into(),\n                )),\n            });\n        }\n        NsDatum::Vector(vector_span, vs) => {\n            let mut arg_data = vs.into_vec();\n\n            if arg_data.len() == 2 {\n                let bound_datum = arg_data.pop().unwrap();\n                let (ident_span, ident) = expect_spanned_ns_ident(\n                    arg_data.pop().unwrap(),\n                    \"new polymorphic parameter name\",\n                )?;\n\n                if ident.is_underscore() {\n                    return Err(Error::new(ident_span, ErrorKind::AnonymousPolymorphicParam));\n                }\n\n                let source_name = ident.name().clone();\n                match try_lower_purity(scope, &bound_datum) {\n                    Some(purity::Ref::Fixed(Purity::Impure)) => {\n                        return Ok(LoweredPolymorphicVar {\n                            ident,\n                            polymorphic_var: PolymorphicVar::PVar(purity::PVar::new(\n                                vector_span,\n                                source_name,\n                            )),\n                        });\n                    }\n                    Some(purity::Ref::Fixed(Purity::Pure)) => {\n                        // Emulate bounding to pure in case the purity comes from e.g. a macro\n                        // expansion\n                        return Ok(LoweredPolymorphicVar {\n                            ident,\n                            polymorphic_var: PolymorphicVar::Pure(vector_span),\n                        });\n                    }\n                    Some(_) => {\n                        return Err(Error::new(bound_datum.span(), ErrorKind::VarPurityBound));\n                    }\n                    None => {\n                        let bound_ty = lower_poly(scope, bound_datum)?;\n\n                        let polymorphic_var = if ty::props::has_subtypes(&bound_ty) {\n                            PolymorphicVar::TVar(ty::TVar::new(vector_span, source_name, bound_ty))\n                        } else {\n                            PolymorphicVar::TFixed(vector_span, bound_ty)\n                        };\n\n                        return Ok(LoweredPolymorphicVar {\n                            ident,\n                            polymorphic_var,\n                        });\n                    }\n                }\n            }\n        }\n        _ => {}\n    }\n\n    Err(Error::new(span, ErrorKind::BadPolyVarDecl))\n}\n\nfn lower_list_cons(scope: &Scope<'_>, mut arg_iter: NsDataIter) -> Result<ty::List<ty::Poly>> {\n    let rest = try_take_rest_arg(&mut arg_iter);\n\n    let fixed_polys = arg_iter\n        .map(|fixed_datum| lower_poly(scope, fixed_datum))\n        .collect::<Result<Box<[ty::Ref<ty::Poly>]>>>()?;\n\n    let rest_poly = match rest {\n        Some(rest_datum) => lower_poly(scope, rest_datum)?,\n        None => Ty::never().into(),\n    };\n\n    Ok(ty::List::new(fixed_polys, rest_poly))\n}\n\nfn lower_fun_cons(\n    scope: &Scope<'_>,\n    purity: purity::Ref,\n    mut arg_iter: NsDataIter,\n) -> Result<ty::Ref<ty::Poly>> {\n    let ret_ty = lower_poly(scope, arg_iter.next_back().unwrap())?;\n\n    // Discard the purity\n    arg_iter.next_back();\n\n    let top_fun = ty::TopFun::new(purity, ret_ty);\n\n    if arg_iter.len() == 1 {\n        if let NsDatum::Ident(_, ident) = &arg_iter.as_slice()[0] {\n            if ident.is_ellipsis() {\n                // Top function type in the form `(... -> ReturnType)`\n                return Ok(top_fun.into());\n            }\n        }\n    }\n\n    let params = lower_list_cons(scope, arg_iter)?;\n    Ok(ty::Fun::new(purity::PVars::new(), ty::TVars::new(), top_fun, params).into())\n}\n\nfn lower_ty_cons_apply(\n    scope: &Scope<'_>,\n    span: Span,\n    ty_cons: TyCons,\n    mut arg_iter: NsDataIter,\n) -> Result<ty::Ref<ty::Poly>> {\n    Ok(match ty_cons {\n        TyCons::List => lower_list_cons(scope, arg_iter)?.into(),\n        TyCons::Vector => {\n            let member_tys = arg_iter\n                .map(|arg_datum| lower_poly(scope, arg_datum))\n                .collect::<Result<Box<[ty::Ref<ty::Poly>]>>>()?;\n\n            Ty::Vector(member_tys).into()\n        }\n        TyCons::Vectorof => {\n            let start_datum = expect_one_arg(span, arg_iter)?;\n            let start_ty = lower_poly(scope, start_datum)?;\n            Ty::Vectorof(Box::new(start_ty)).into()\n        }\n        TyCons::Set => {\n            let member_datum = expect_one_arg(span, arg_iter)?;\n            let member_ty = lower_poly(scope, member_datum)?;\n            Ty::Set(Box::new(member_ty)).into()\n        }\n        TyCons::Map => {\n            expect_arg_count(span, 2, arg_iter.len())?;\n            let key_ty = lower_poly(scope, arg_iter.next().unwrap())?;\n            let value_ty = lower_poly(scope, arg_iter.next().unwrap())?;\n            ty::Map::new(key_ty, value_ty).into()\n        }\n        TyCons::Union => {\n            let member_tys = arg_iter\n                .map(|arg_datum| lower_poly(scope, arg_datum))\n                .collect::<Result<Vec<ty::Ref<ty::Poly>>>>()?;\n\n            ty::unify::unify_ty_ref_iter(member_tys.into_iter())\n        }\n        #[cfg(test)]\n        TyCons::RawU => {\n            // This performs a union *without* unifying the types. This is used when testing the\n            // union code itself\n            let member_tys = arg_iter\n                .map(|arg_datum| lower_poly(scope, arg_datum))\n                .collect::<Result<Box<[ty::Ref<ty::Poly>]>>>()?;\n\n            Ty::Union(member_tys).into()\n        }\n    })\n}\n\nfn lower_record_ty_cons_purity_arg(\n    scope: &Scope<'_>,\n    param_span: Span,\n    arg_datum: &NsDatum,\n) -> Result<purity::Ref> {\n    (match arg_datum {\n        NsDatum::Ident(span, ident) => match scope.get_or_err(*span, ident)? {\n            Binding::Purity(purity) => Ok(purity.clone()),\n            other => Err(other.description()),\n        },\n        other => Err(other.description()),\n    })\n    .map_err(|found| {\n        let details = Box::new(ExpectedPolyPurityArg { found, param_span });\n        Error::new(arg_datum.span(), ErrorKind::ExpectedPolyPurityArg(details))\n    })\n}\n\nfn lower_record_ty_cons_apply(\n    scope: &Scope<'_>,\n    span: Span,\n    record_cons: &record::ConsId,\n    arg_iter: NsDataIter,\n) -> Result<ty::Ref<ty::Poly>> {\n    use crate::ty::is_a::{ty_ref_is_a, ty_refs_equivalent};\n    use std::collections::HashMap;\n\n    expect_arg_count(span, record_cons.poly_params().len(), arg_iter.len())?;\n\n    let mut pvar_purities = HashMap::new();\n    let mut tvar_types = HashMap::new();\n\n    for (poly_param, arg_datum) in record_cons.poly_params().iter().zip(arg_iter) {\n        let arg_span = arg_datum.span();\n\n        match poly_param {\n            record::PolyParam::PVar(_, pvar) => {\n                let purity_ref = lower_record_ty_cons_purity_arg(scope, pvar.span(), &arg_datum)?;\n                pvar_purities.insert(pvar.clone(), purity_ref);\n            }\n            record::PolyParam::Pure(span) => {\n                let purity_ref = lower_record_ty_cons_purity_arg(scope, *span, &arg_datum)?;\n\n                if purity_ref != Purity::Pure.into() {\n                    let details = Box::new(PolyArgIsNotPure {\n                        arg_purity: purity_ref,\n                        param_span: *span,\n                    });\n\n                    return Err(Error::new(arg_span, ErrorKind::PolyArgIsNotPure(details)));\n                }\n            }\n            record::PolyParam::TVar(_, tvar) => {\n                let arg_type = lower_poly(scope, arg_datum)?;\n                if !ty_ref_is_a(&arg_type, tvar.bound()) {\n                    let details = Box::new(PolyArgIsNotTy {\n                        arg_type,\n                        param_bound: tvar.bound().clone(),\n                        param_span: tvar.span(),\n                    });\n\n                    return Err(Error::new(arg_span, ErrorKind::PolyArgIsNotTy(details)));\n                }\n\n                tvar_types.insert(tvar.clone(), arg_type);\n            }\n            record::PolyParam::TFixed(span, fixed_poly) => {\n                let arg_type = lower_poly(scope, arg_datum)?;\n                if !ty_refs_equivalent(&arg_type, fixed_poly) {\n                    let details = Box::new(PolyArgIsNotTy {\n                        arg_type,\n                        param_bound: fixed_poly.clone(),\n                        param_span: *span,\n                    });\n\n                    return Err(Error::new(arg_span, ErrorKind::PolyArgIsNotTy(details)));\n                }\n            }\n        }\n    }\n\n    Ok(record::Instance::new(record_cons.clone(), TyArgs::new(pvar_purities, tvar_types)).into())\n}\n\nfn lower_literal_vec(literal_data: Vec<NsDatum>) -> Result<Vec<ty::Ref<ty::Poly>>> {\n    literal_data.into_iter().map(lower_literal).collect()\n}\n\nfn lower_literal(datum: NsDatum) -> Result<ty::Ref<ty::Poly>> {\n    match datum {\n        NsDatum::Bool(_, v) => Ok(Ty::LitBool(v).into()),\n        NsDatum::Keyword(_, name) => Ok(Ty::LitSym(name).into()),\n        NsDatum::Ident(_, ident) => Ok(Ty::LitSym(ident.into_name()).into()),\n        NsDatum::List(_, vs) => {\n            let fixed_literals = lower_literal_vec(vs.into_vec())?;\n            Ok(ty::List::new_tuple(fixed_literals.into_boxed_slice()).into())\n        }\n        NsDatum::Vector(_, vs) => {\n            let fixed_literals = lower_literal_vec(vs.into_vec())?;\n            Ok(Ty::Vector(fixed_literals.into_boxed_slice()).into())\n        }\n        _ => Err(Error::new(datum.span(), ErrorKind::UnsupportedLiteralType)),\n    }\n}\n\nfn lower_ident(scope: &Scope<'_>, span: Span, ident: &Ident) -> Result<ty::Ref<ty::Poly>> {\n    match scope.get_or_err(span, ident)? {\n        Binding::Ty(ty) => Ok(ty.clone()),\n        Binding::TyPred(test_ty) => Ok(Ty::TyPred(test_ty.clone()).into()),\n        Binding::EqPred => Ok(Ty::EqPred.into()),\n        other => Err(Error::new(span, ErrorKind::ExpectedTy(other.description()))),\n    }\n}\n\nfn lower_polymorphic_poly(\n    scope: &Scope<'_>,\n    span: Span,\n    mut data_iter: NsDataIter,\n) -> Result<ty::Ref<ty::Poly>> {\n    let polymorphic_vars_datum = if let Some(datum) = data_iter.next() {\n        datum\n    } else {\n        return Err(Error::new(span, ErrorKind::NoPolyVarsDecl));\n    };\n\n    let polymorphic_var_data = if let NsDatum::Set(_, vs) = polymorphic_vars_datum {\n        vs\n    } else {\n        return Err(Error::new(\n            polymorphic_vars_datum.span(),\n            ErrorKind::ExpectedPolyVarsDecl(polymorphic_vars_datum.description()),\n        ));\n    };\n\n    let mut inner_scope = scope.child();\n    let (pvars, tvars) = lower_polymorphic_var_set(\n        scope,\n        &mut inner_scope,\n        polymorphic_var_data.into_vec().into_iter(),\n    )?;\n\n    let inner_poly = lower_poly_data_iter(&inner_scope, span, data_iter)?;\n\n    if let ty::Ref::Fixed(Ty::Fun(fun)) = inner_poly {\n        Ok(Ty::Fun(Box::new(fun.with_polymorphic_vars(pvars, tvars))).into())\n    } else {\n        Err(Error::new(span, ErrorKind::NonFunPolyTy))\n    }\n}\n\nfn lower_poly_data_iter(\n    scope: &Scope<'_>,\n    span: Span,\n    mut data_iter: NsDataIter,\n) -> Result<ty::Ref<ty::Poly>> {\n    let data_len = data_iter.len();\n\n    if data_len == 0 {\n        // This is by analogy with () being self-evaluating in expressions\n        return Ok(ty::List::empty().into());\n    }\n\n    if let Some(Binding::Prim(Prim::All)) = scope.get_datum(&data_iter.as_slice()[0]) {\n        // Discard the `All`\n        data_iter.next();\n\n        return lower_polymorphic_poly(scope, span, data_iter);\n    }\n\n    if data_len >= 2 {\n        if let Some(purity) = try_lower_purity(scope, &data_iter.as_slice()[data_len - 2]) {\n            // This is a function type\n            return lower_fun_cons(scope, purity, data_iter);\n        };\n    }\n\n    let fn_datum = data_iter.next().unwrap();\n    let (ident_span, ident) = expect_spanned_ns_ident(fn_datum, \"type constructor name\")?;\n\n    match scope.get_or_err(ident_span, &ident)? {\n        Binding::Prim(Prim::Quote) => {\n            let literal_datum = expect_one_arg(span, data_iter)?;\n            lower_literal(literal_datum)\n        }\n        Binding::TyCons(ty_cons) => lower_ty_cons_apply(scope, span, *ty_cons, data_iter),\n        Binding::RecordTyCons(record_cons) => {\n            lower_record_ty_cons_apply(scope, span, record_cons, data_iter)\n        }\n        other => Err(Error::new(\n            ident_span,\n            ErrorKind::ExpectedTyCons(other.description()),\n        )),\n    }\n}\n\npub fn lower_poly(scope: &Scope<'_>, datum: NsDatum) -> Result<ty::Ref<ty::Poly>> {\n    match datum {\n        NsDatum::List(span, vs) => lower_poly_data_iter(scope, span, vs.into_vec().into_iter()),\n        NsDatum::Ident(span, ident) => lower_ident(scope, span, &ident),\n        _ => lower_literal(datum),\n    }\n}\n\nfn bind_polymorphic_vars(\n    scope: &mut Scope<'_>,\n    lowered_poly_vars: Vec<LoweredPolymorphicVar>,\n) -> Result<()> {\n    for LoweredPolymorphicVar {\n        ident,\n        polymorphic_var,\n    } in lowered_poly_vars\n    {\n        let (span, binding) = match polymorphic_var {\n            PolymorphicVar::PVar(pvar) => (pvar.span(), Binding::Purity(pvar.into())),\n            PolymorphicVar::TVar(tvar) => (tvar.span(), Binding::Ty(tvar.into())),\n            PolymorphicVar::TFixed(fixed_span, poly) => (fixed_span, Binding::Ty(poly)),\n            PolymorphicVar::Pure(pure_span) => (pure_span, Binding::Purity(Purity::Pure.into())),\n        };\n\n        scope.insert_binding(span, ident, binding)?;\n    }\n\n    Ok(())\n}\n\n/// Lowers a set of polymorphic variables defined in `outer_scope` and places them in `inner_scope`\n///\n/// This is used for functions and function types\npub fn lower_polymorphic_var_set(\n    outer_scope: &Scope<'_>,\n    inner_scope: &mut Scope<'_>,\n    polymorphic_var_data: NsDataIter,\n) -> Result<(purity::PVars, ty::TVars)> {\n    let mut pvars = purity::PVars::new();\n    let mut tvars = ty::TVars::new();\n\n    let lowered_poly_vars = polymorphic_var_data\n        .map(|var_datum| lower_polymorphic_var(outer_scope, var_datum))\n        .collect::<Result<Vec<LoweredPolymorphicVar>>>()?;\n\n    for lowered_poly_var in lowered_poly_vars.iter() {\n        match &lowered_poly_var.polymorphic_var {\n            PolymorphicVar::PVar(pvar) => {\n                pvars.push(pvar.clone());\n            }\n            PolymorphicVar::TVar(tvar) => {\n                tvars.push(tvar.clone());\n            }\n            PolymorphicVar::Pure(_) | PolymorphicVar::TFixed(_, _) => {}\n        }\n    }\n\n    bind_polymorphic_vars(inner_scope, lowered_poly_vars)?;\n    Ok((pvars, tvars))\n}\n\n/// Lowers a list of polymorphic variables defined in `outer_scope` and places them in `inner_scope`\n///\n/// This is used for record types\npub fn lower_polymorphic_var_list(\n    outer_scope: &Scope<'_>,\n    inner_scope: &mut Scope<'_>,\n    param_data: NsDataIter,\n) -> Result<Box<[PolymorphicVar]>> {\n    let lowered_poly_vars = param_data\n        .map(|var_datum| lower_polymorphic_var(outer_scope, var_datum))\n        .collect::<Result<Vec<LoweredPolymorphicVar>>>()?;\n\n    let poly_vars = lowered_poly_vars\n        .iter()\n        .map(|lpv| lpv.polymorphic_var.clone())\n        .collect();\n\n    bind_polymorphic_vars(inner_scope, lowered_poly_vars)?;\n    Ok(poly_vars)\n}\n\npub fn try_lower_purity(scope: &Scope<'_>, datum: &NsDatum) -> Option<purity::Ref> {\n    scope.get_datum(datum).and_then(|binding| match binding {\n        Binding::Purity(purity) => Some(purity.clone()),\n        _ => None,\n    })\n}\n\nmacro_rules! export_ty {\n    ($name:expr, $type:expr) => {\n        ($name, Binding::Ty(ty::Ref::Fixed($type)))\n    };\n}\n\nmacro_rules! export_ty_cons {\n    ($name:expr, $ty_cons:expr) => {\n        ($name, Binding::TyCons($ty_cons))\n    };\n}\n\nmacro_rules! export_purity {\n    ($name:expr, $purity:expr) => {\n        ($name, Binding::Purity(purity::Ref::Fixed($purity)))\n    };\n}\n\nmacro_rules! export_ty_pred {\n    ($name:expr, $test_ty:expr) => {\n        ($name, Binding::TyPred($test_ty))\n    };\n}\n\npub const TY_EXPORTS: &[(&str, Binding)] = &[\n    export_ty!(\"Any\", Ty::Any),\n    export_ty!(\"Bool\", Ty::Bool),\n    export_ty!(\"Sym\", Ty::Sym),\n    export_ty!(\"Str\", Ty::Str),\n    export_ty!(\"Int\", Ty::Int),\n    export_ty!(\"Float\", Ty::Float),\n    export_ty!(\"Num\", Ty::Num),\n    export_ty!(\"Char\", Ty::Char),\n    export_ty!(\"Record\", Ty::TopRecord),\n    export_ty_cons!(\"List\", TyCons::List),\n    export_ty_cons!(\"Vector\", TyCons::Vector),\n    export_ty_cons!(\"Vectorof\", TyCons::Vectorof),\n    export_ty_cons!(\"Setof\", TyCons::Set),\n    export_ty_cons!(\"Map\", TyCons::Map),\n    export_ty_cons!(\"U\", TyCons::Union),\n    export_purity!(\"->\", Purity::Pure),\n    export_purity!(\"->!\", Purity::Impure),\n    export_ty_pred!(\"str?\", ty::pred::TestTy::Str),\n    export_ty_pred!(\"sym?\", ty::pred::TestTy::Sym),\n    export_ty_pred!(\"bool?\", ty::pred::TestTy::Bool),\n    export_ty_pred!(\"num?\", ty::pred::TestTy::Num),\n    export_ty_pred!(\"int?\", ty::pred::TestTy::Int),\n    export_ty_pred!(\"float?\", ty::pred::TestTy::Float),\n    export_ty_pred!(\"char?\", ty::pred::TestTy::Char),\n    export_ty_pred!(\"list?\", ty::pred::TestTy::List),\n    export_ty_pred!(\"vector?\", ty::pred::TestTy::Vector),\n    export_ty_pred!(\"set?\", ty::pred::TestTy::Set),\n    export_ty_pred!(\"map?\", ty::pred::TestTy::Map),\n    export_ty_pred!(\"fn?\", ty::pred::TestTy::Fun),\n    export_ty_pred!(\"nil?\", ty::pred::TestTy::Nil),\n    export_ty_pred!(\"record?\", ty::pred::TestTy::TopRecord),\n    #[cfg(test)]\n    export_ty_cons!(\"RawU\", TyCons::RawU),\n];\n\n/// Pushes the arguments for a list constructor on to the passed `Vec`\n///\n/// This is used to share code between list and function types\nfn push_list_parts<M: ty::Pm>(list_parts: &mut Vec<String>, list_ref: &ty::List<M>) {\n    for fixed in list_ref.fixed() {\n        list_parts.push(str_for_ty_ref(fixed));\n    }\n\n    let rest = list_ref.rest();\n    if !rest.is_never() {\n        list_parts.push(\"&\".to_owned());\n        list_parts.push(str_for_ty_ref(rest));\n    }\n}\n\nfn str_for_bounds(bound_pvars: &[purity::PVarId], bound_tvars: &[ty::TVarId]) -> String {\n    let pvar_parts = bound_pvars\n        .iter()\n        .map(|pvar| format!(\"[{} ->!]\", pvar.source_name()));\n\n    let tvar_parts = bound_tvars.iter().map(|tvar| {\n        if tvar.bound() == &Ty::Any.into() {\n            return tvar.source_name().into();\n        }\n\n        format!(\"[{} {}]\", tvar.source_name(), str_for_ty_ref(tvar.bound()))\n    });\n\n    let all_parts = pvar_parts.chain(tvar_parts).collect::<Vec<String>>();\n    format!(\"#{{{}}}\", all_parts.join(\" \"))\n}\n\nfn str_for_record_poly_arg<M: ty::Pm>(\n    instance: &record::Instance<M>,\n    poly_param: &record::PolyParam,\n) -> String {\n    let ty_args = instance.ty_args();\n\n    match poly_param {\n        record::PolyParam::PVar(_, pvar) => str_for_purity(&ty_args.pvar_purities()[pvar]),\n        record::PolyParam::Pure(_) => str_for_purity(&Purity::Pure.into()),\n        record::PolyParam::TVar(_, tvar) => str_for_ty_ref(&ty_args.tvar_types()[tvar]),\n        record::PolyParam::TFixed(_, fixed_poly) => str_for_ty_ref(fixed_poly),\n    }\n}\n\nfn str_for_ty<M: ty::Pm>(ty: &Ty<M>) -> String {\n    match ty {\n        Ty::Any => \"Any\".to_owned(),\n        Ty::Bool => \"Bool\".to_owned(),\n        Ty::Char => \"Char\".to_owned(),\n        Ty::Int => \"Int\".to_owned(),\n        Ty::Sym => \"Sym\".to_owned(),\n        Ty::Str => \"Str\".to_owned(),\n        Ty::Float => \"Float\".to_owned(),\n        Ty::Num => \"Num\".to_owned(),\n        Ty::LitBool(false) => \"false\".to_owned(),\n        Ty::LitBool(true) => \"true\".to_owned(),\n        Ty::LitSym(name) => {\n            if name.starts_with(':') {\n                name.to_string()\n            } else {\n                format!(\"'{}\", name)\n            }\n        }\n        Ty::Map(map) => format!(\n            \"(Map {} {})\",\n            str_for_ty_ref(map.key()),\n            str_for_ty_ref(map.value())\n        ),\n        Ty::Set(member) => format!(\"(Setof {})\", str_for_ty_ref(member)),\n        Ty::Vector(members) => {\n            let result_parts: Vec<String> = members\n                .iter()\n                .map(|member| format!(\" {}\", str_for_ty_ref(member)))\n                .collect();\n\n            format!(\"(Vector{})\", result_parts.join(\"\"))\n        }\n        Ty::Vectorof(member) => format!(\"(Vectorof {})\", str_for_ty_ref(member)),\n        Ty::TopFun(top_fun) => format!(\n            \"(... {} {})\",\n            str_for_purity(top_fun.purity()),\n            str_for_ty_ref(top_fun.ret())\n        ),\n        Ty::Fun(fun) => {\n            let mut fun_parts = Vec::with_capacity(2);\n\n            push_list_parts(&mut fun_parts, fun.params());\n            fun_parts.push(str_for_purity(fun.purity()));\n            fun_parts.push(str_for_ty_ref(fun.ret()));\n\n            if fun.has_polymorphic_vars() {\n                format!(\n                    \"(All {} {})\",\n                    str_for_bounds(fun.pvars(), fun.tvars()),\n                    fun_parts.join(\" \")\n                )\n            } else {\n                format!(\"({})\", fun_parts.join(\" \"))\n            }\n        }\n        Ty::TyPred(test_ty) => test_ty.to_string(),\n        Ty::EqPred => \"=\".to_owned(),\n        Ty::Union(members) => {\n            let member_strs: Vec<String> = members\n                .iter()\n                .map(|m| format!(\" {}\", str_for_ty_ref(m)))\n                .collect();\n\n            format!(\"(U{})\", member_strs.join(\"\"))\n        }\n        Ty::Intersect(members) => {\n            let member_strs: Vec<String> = members\n                .iter()\n                .map(|m| format!(\" {}\", str_for_ty_ref(m)))\n                .collect();\n\n            format!(\"(∩{})\", member_strs.join(\"\"))\n        }\n        Ty::List(list) => {\n            // While all list types can be expressed using `(List)` we try to find the shortest\n            // representation\n            if list.is_empty() {\n                \"()\".to_owned()\n            } else {\n                let mut list_parts = Vec::with_capacity(2);\n\n                list_parts.push(\"List\".to_owned());\n                push_list_parts(&mut list_parts, list);\n\n                format!(\"({})\", list_parts.join(\" \"))\n            }\n        }\n\n        Ty::TopRecord => \"Record\".to_owned(),\n        Ty::RecordClass(record_cons) => format!(\"({} ...)\", record_cons.ty_cons_name()),\n        Ty::Record(instance) => {\n            let record_cons = instance.cons();\n\n            if record_cons.is_singleton() {\n                // This is bound as its name\n                return record_cons.ty_cons_name().to_string();\n            }\n\n            let record_parts: Vec<String> = std::iter::once(record_cons.ty_cons_name().to_string())\n                .chain(\n                    record_cons\n                        .poly_params()\n                        .iter()\n                        .map(|poly_param| str_for_record_poly_arg(instance, poly_param)),\n                )\n                .collect();\n\n            format!(\"({})\", record_parts.join(\" \"))\n        }\n    }\n}\n\npub fn str_for_ty_ref<M: ty::Pm>(ty_ref: &ty::Ref<M>) -> String {\n    match ty_ref {\n        ty::Ref::Var(tvar, _) => tvar.source_name().to_owned(),\n        ty::Ref::Fixed(ty) => str_for_ty(ty),\n    }\n}\n\npub fn str_for_purity(purity: &purity::Ref) -> String {\n    match purity {\n        purity::Ref::Fixed(Purity::Pure) => \"->\".to_owned(),\n        purity::Ref::Fixed(Purity::Impure) => \"->!\".to_owned(),\n        purity::Ref::Var(pvar) => pvar.source_name().into(),\n    }\n}\n\n#[cfg(test)]\npub fn poly_for_str(datum_str: &str) -> ty::Ref<ty::Poly> {\n    use crate::hir::prim::PRIM_EXPORTS;\n    use arret_syntax::parser::datum_from_str;\n\n    let prim_entries = PRIM_EXPORTS\n        .iter()\n        .chain(TY_EXPORTS.iter())\n        .map(|(name, binding)| {\n            if *name == \"U\" {\n                // Using `U` in tests is very dubious as it invokes a lot of type system logic. It's\n                // easy to write tautological tests due to `U` creating a simplified type. Rename to\n                // `UnifyingU` so it's clear what's happening.\n                (\"UnifyingU\", binding.clone())\n            } else {\n                (*name, binding.clone())\n            }\n        });\n\n    let scope = Scope::new_with_entries(prim_entries);\n\n    let test_datum = datum_from_str(None, datum_str).unwrap();\n    lower_poly(&scope, NsDatum::from_syntax_datum(&test_datum)).unwrap()\n}\n\n#[cfg(test)]\npub fn tvar_bounded_by(bound: ty::Ref<ty::Poly>) -> ty::Ref<ty::Poly> {\n    ty::TVar::new(crate::source::EMPTY_SPAN, \"TVar\".into(), bound).into()\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use std::collections::HashMap;\n\n    use crate::source::EMPTY_SPAN;\n    use crate::ty::var_usage::Variance;\n\n    fn assert_ty_for_str(expected: Ty<ty::Poly>, datum_str: &str) {\n        let expected_poly = expected.into();\n        assert_eq!(expected_poly, poly_for_str(datum_str));\n\n        // Try to round trip this to make sure str_for_ty_ref works\n        let recovered_str = str_for_ty_ref(&expected_poly);\n        assert_eq!(expected_poly, poly_for_str(&recovered_str));\n    }\n\n    /// This asserts that a type uses an exact string in str_for_ty_ref\n    fn assert_exact_str_repr(datum_str: &str) {\n        assert_eq!(datum_str, str_for_ty_ref(&poly_for_str(datum_str)));\n    }\n\n    #[test]\n    fn true_literal() {\n        let j = \"true\";\n\n        let expected = Ty::LitBool(true);\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn false_literal() {\n        let j = \"false\";\n\n        let expected = Ty::LitBool(false);\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn sym_literal() {\n        let j = \"'foo\";\n\n        let expected = Ty::LitSym(\"foo\".into());\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn keyword_literal() {\n        let j = \":foo\";\n\n        let expected = Ty::LitSym(\":foo\".into());\n        assert_ty_for_str(expected, j);\n\n        // Make sure we don't quote this needlessly\n        assert_exact_str_repr(j);\n    }\n\n    #[test]\n    fn empty_list_literal() {\n        let j = \"()\";\n\n        let expected = ty::List::empty().into();\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn quoted_list_literal() {\n        let j = \"'(true false)\";\n\n        let expected = ty::List::new_tuple(Box::new([\n            Ty::LitBool(true).into(),\n            Ty::LitBool(false).into(),\n        ]))\n        .into();\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn empty_vector_literal() {\n        let j = \"[]\";\n\n        let expected = Ty::Vector(Box::new([]));\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn vector_literal() {\n        let j = \"[true false]\";\n\n        let expected = Ty::Vector(Box::new([\n            Ty::LitBool(true).into(),\n            Ty::LitBool(false).into(),\n        ]));\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn ty_ref() {\n        let j = \"Sym\";\n\n        let expected = Ty::Sym;\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn fixed_list_cons() {\n        let j = \"(List true false)\";\n\n        let expected = ty::List::new_tuple(Box::new([\n            Ty::LitBool(true).into(),\n            Ty::LitBool(false).into(),\n        ]))\n        .into();\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn rest_list_cons() {\n        let j = \"(List true & false)\";\n\n        let expected = ty::List::new(\n            Box::new([Ty::LitBool(true).into()]),\n            Ty::LitBool(false).into(),\n        )\n        .into();\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn vectorof_cons() {\n        let j = \"(Vectorof true)\";\n\n        let inner_poly = Ty::LitBool(true).into();\n        let expected = Ty::Vectorof(Box::new(inner_poly));\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn vector_cons() {\n        let j = \"(Vector true false)\";\n\n        let expected = Ty::Vector(Box::new([\n            Ty::LitBool(true).into(),\n            Ty::LitBool(false).into(),\n        ]));\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn pure_fun() {\n        let j = \"(-> true)\";\n\n        let expected = ty::Fun::new_mono(\n            ty::List::empty(),\n            Purity::Pure.into(),\n            Ty::LitBool(true).into(),\n        )\n        .into();\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn impure_fun() {\n        let j = \"(->! true)\";\n\n        let expected = ty::Fun::new_mono(\n            ty::List::empty(),\n            Purity::Impure.into(),\n            Ty::LitBool(true).into(),\n        )\n        .into();\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn fixed_fun() {\n        let j = \"(false -> true)\";\n\n        let expected = ty::Fun::new_mono(\n            ty::List::new_tuple(Box::new([Ty::LitBool(false).into()])),\n            Purity::Pure.into(),\n            Ty::LitBool(true).into(),\n        )\n        .into();\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn rest_impure_fun() {\n        let j = \"(Str & Sym ->! true)\";\n\n        let expected = ty::Fun::new_mono(\n            ty::List::new(Box::new([Ty::Str.into()]), Ty::Sym.into()),\n            Purity::Impure.into(),\n            Ty::LitBool(true).into(),\n        )\n        .into();\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn top_impure_fun() {\n        let j = \"(... ->! true)\";\n\n        let expected = ty::TopFun::new(Purity::Impure.into(), Ty::LitBool(true).into()).into();\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn type_predicate() {\n        let j = \"str?\";\n\n        let expected = Ty::TyPred(ty::pred::TestTy::Str);\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn equality_predicate() {\n        let j = \"=\";\n\n        let expected = Ty::EqPred;\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn set_cons() {\n        let j = \"(Setof true)\";\n\n        let inner_poly = Ty::LitBool(true).into();\n        let expected = Ty::Set(Box::new(inner_poly));\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn map_cons() {\n        let j = \"(Map true false)\";\n\n        let key_ty = Ty::LitBool(true);\n        let value_ty = Ty::LitBool(false);\n        let expected = ty::Map::new(key_ty.into(), value_ty.into()).into();\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn merged_union_cons() {\n        let j = \"(UnifyingU true false)\";\n        let expected = Ty::Bool;\n\n        assert_ty_for_str(expected, j);\n    }\n\n    #[test]\n    fn simpifying_str_for_ty_ref() {\n        assert_exact_str_repr(\"(List Int Float)\");\n        assert_exact_str_repr(\"(List Int & Float)\");\n        assert_exact_str_repr(\"(List & Float)\");\n        assert_exact_str_repr(\"(Float & Int -> Sym)\");\n    }\n\n    #[test]\n    fn polymorphic_fun_str() {\n        assert_exact_str_repr(\"(All #{[->? ->!] A [B Bool] C} B C ->? A)\");\n    }\n\n    #[test]\n    fn singleton_record_type() {\n        let mono_record_cons = record::Cons::new(\n            EMPTY_SPAN,\n            \"MonoCons\".into(),\n            \"mono-cons?\".into(),\n            None,\n            Box::new([record::Field::new(EMPTY_SPAN, \"num\".into(), Ty::Num.into())]),\n        );\n\n        let record_class_ref: ty::Ref<ty::Poly> = mono_record_cons.clone().into();\n        assert_eq!(\"(MonoCons ...)\", str_for_ty_ref(&record_class_ref));\n\n        let int_record_instance_ref: ty::Ref<ty::Poly> =\n            record::Instance::new(mono_record_cons, TyArgs::empty()).into();\n        assert_eq!(\"MonoCons\", str_for_ty_ref(&int_record_instance_ref));\n    }\n\n    #[test]\n    fn poly_record_type() {\n        let tvar = ty::TVar::new(EMPTY_SPAN, \"tvar\".into(), Ty::Any.into());\n\n        let poly_record_cons = record::Cons::new(\n            EMPTY_SPAN,\n            \"PolyCons\".into(),\n            \"poly-cons?\".into(),\n            Some(Box::new([\n                record::PolyParam::Pure(EMPTY_SPAN),\n                record::PolyParam::TVar(Variance::Covariant, tvar.clone()),\n            ])),\n            Box::new([record::Field::new(\n                EMPTY_SPAN,\n                \"num\".into(),\n                tvar.clone().into(),\n            )]),\n        );\n\n        // Record class type\n        let record_class_ref: ty::Ref<ty::Poly> = poly_record_cons.clone().into();\n        assert_eq!(\"(PolyCons ...)\", str_for_ty_ref(&record_class_ref));\n\n        // Instance parameterised with an `Int`\n        let mut int_tvars = HashMap::new();\n        int_tvars.insert(tvar, Ty::Int.into());\n        let int_ty_args = TyArgs::new(HashMap::new(), int_tvars);\n\n        let poly_record_instance_ref: ty::Ref<ty::Poly> =\n            record::Instance::new(poly_record_cons, int_ty_args).into();\n\n        assert_eq!(\n            \"(PolyCons -> Int)\",\n            str_for_ty_ref(&poly_record_instance_ref)\n        );\n    }\n}\n"
  },
  {
    "path": "compiler/hir/util.rs",
    "content": "use arret_syntax::datum::{DataStr, Datum};\nuse arret_syntax::span::Span;\n\nuse crate::hir::error::{Error, ErrorKind, ExpectedSym, Result};\nuse crate::hir::ns::{Ident, NsDataIter, NsDatum};\n\n/// Removes the rest argument from the passed iterator and returns it\n///\n/// The rest argument is denoted by using `&` before a final datum\npub fn try_take_rest_arg(data_iter: &mut NsDataIter) -> Option<NsDatum> {\n    let data_len = data_iter.len();\n    if data_len < 2 {\n        return None;\n    }\n\n    // This is gross because we need to \"peek\" at the end of the iterator\n    if let NsDatum::Ident(_, ident) = &data_iter.as_slice()[data_len - 2] {\n        if ident.is_ampersand() {\n            let rest = data_iter.next_back();\n            // Remove the & completely\n            data_iter.next_back();\n            return rest;\n        }\n    }\n\n    None\n}\n\npub fn expect_arg_count(\n    span: Span,\n    expected_arg_count: usize,\n    actual_arg_count: usize,\n) -> Result<()> {\n    if actual_arg_count != expected_arg_count {\n        Err(Error::new(\n            span,\n            ErrorKind::WrongArgCount(expected_arg_count),\n        ))\n    } else {\n        Ok(())\n    }\n}\n\npub fn expect_one_arg(span: Span, mut iter: NsDataIter) -> Result<NsDatum> {\n    expect_arg_count(span, 1, iter.len())?;\n    Ok(iter.next().unwrap())\n}\n\npub fn expect_spanned_ns_ident(datum: NsDatum, usage: &'static str) -> Result<(Span, Ident)> {\n    if let NsDatum::Ident(span, ident) = datum {\n        Ok((span, ident))\n    } else {\n        Err(Error::new(\n            datum.span(),\n            ErrorKind::ExpectedSym(\n                ExpectedSym {\n                    found: datum.description(),\n                    usage,\n                }\n                .into(),\n            ),\n        ))\n    }\n}\n\npub fn expect_ns_ident(datum: NsDatum, usage: &'static str) -> Result<Ident> {\n    expect_spanned_ns_ident(datum, usage).map(|(_, ident)| ident)\n}\n\npub fn expect_spanned_ident<'a>(\n    datum: &'a Datum,\n    usage: &'static str,\n) -> Result<(Span, &'a DataStr)> {\n    if let Datum::Sym(span, name) = datum {\n        if !name.starts_with(':') {\n            return Ok((*span, name));\n        }\n    }\n\n    Err(Error::new(\n        datum.span(),\n        ErrorKind::ExpectedSym(\n            ExpectedSym {\n                found: datum.description(),\n                usage,\n            }\n            .into(),\n        ),\n    ))\n}\n\npub fn expect_ident<'a>(datum: &'a Datum, usage: &'static str) -> Result<&'a DataStr> {\n    expect_spanned_ident(datum, usage).map(|(_, ident)| ident)\n}\n"
  },
  {
    "path": "compiler/hir/var_id.rs",
    "content": "use std::num::NonZeroU32;\nuse std::sync::atomic::{AtomicU32, Ordering};\n\nuse crate::context::ModuleId;\n\n/// Identifier for a local variable within a module\n///\n/// This is not globally unique; it must be combined with a `ModuleId`\n#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]\npub struct LocalId(NonZeroU32);\n\n/// Identifier for a variable exported from another module\n#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]\npub struct ExportId(ModuleId, LocalId);\n\nimpl ExportId {\n    pub fn new(module_id: ModuleId, local_id: LocalId) -> Self {\n        Self(module_id, local_id)\n    }\n\n    pub fn module_id(self) -> ModuleId {\n        self.0\n    }\n\n    pub fn local_id(self) -> LocalId {\n        self.1\n    }\n}\n\npub struct LocalIdAlloc {\n    local_id_counter: AtomicU32,\n}\n\nimpl LocalIdAlloc {\n    pub fn new() -> Self {\n        Self {\n            local_id_counter: AtomicU32::new(1),\n        }\n    }\n\n    /// Allocates a `LocalId` using atomic operations on a shared instance\n    pub fn alloc(&self) -> LocalId {\n        LocalId(NonZeroU32::new(self.local_id_counter.fetch_add(1, Ordering::Relaxed)).unwrap())\n    }\n\n    /// Allocates a `LocalId` using non-atomic operations on an exclusive instance\n    pub fn alloc_mut(&mut self) -> LocalId {\n        let local_id_counter = self.local_id_counter.get_mut();\n\n        let raw_id = *local_id_counter;\n        *local_id_counter += 1;\n\n        LocalId(NonZeroU32::new(raw_id).unwrap())\n    }\n}\n"
  },
  {
    "path": "compiler/hir/visitor.rs",
    "content": "use crate::hir;\n\n/// Visits an expression and all of its subexpressions\npub fn visit_exprs<'a, P: hir::Phase, F>(expr: &'a hir::Expr<P>, visitor: &mut F)\nwhere\n    F: FnMut(&'a hir::Expr<P>),\n{\n    visitor(expr);\n\n    use crate::hir::ExprKind;\n    match &expr.kind {\n        ExprKind::Cond(cond) => {\n            visit_exprs(&cond.test_expr, visitor);\n            visit_exprs(&cond.true_expr, visitor);\n            visit_exprs(&cond.false_expr, visitor);\n        }\n        ExprKind::Fun(fun) => {\n            visit_exprs(&fun.body_expr, visitor);\n        }\n        ExprKind::App(app) => {\n            visit_exprs(&app.fun_expr, visitor);\n            for fixed_arg_expr in &app.fixed_arg_exprs {\n                visit_exprs(fixed_arg_expr, visitor);\n            }\n            for rest_arg_expr in &app.rest_arg_expr {\n                visit_exprs(rest_arg_expr, visitor);\n            }\n        }\n        ExprKind::Recur(recur) => {\n            for fixed_arg_expr in &recur.fixed_arg_exprs {\n                visit_exprs(fixed_arg_expr, visitor);\n            }\n            for rest_arg_expr in &recur.rest_arg_expr {\n                visit_exprs(rest_arg_expr, visitor);\n            }\n        }\n        ExprKind::Let(hir_let) => {\n            visit_exprs(&hir_let.value_expr, visitor);\n            visit_exprs(&hir_let.body_expr, visitor);\n        }\n        ExprKind::Do(exprs) => {\n            for expr in exprs {\n                visit_exprs(expr, visitor);\n            }\n        }\n        ExprKind::MacroExpand(_, expr) => {\n            visit_exprs(expr, visitor);\n        }\n        ExprKind::ExportRef(_, _)\n        | ExprKind::LocalRef(_, _)\n        | ExprKind::Lit(_)\n        | ExprKind::RustFun(_)\n        | ExprKind::TyPred(_, _)\n        | ExprKind::EqPred(_)\n        | ExprKind::RecordCons(_, _)\n        | ExprKind::FieldAccessor(_) => {\n            // Terminal expression\n        }\n    };\n}\n"
  },
  {
    "path": "compiler/id_type.rs",
    "content": "use std::sync::Arc;\nuse std::{fmt, hash, ops};\n\n/// Builds a new ID type based off indexing in to a `Vec` lookup table\n///\n/// This stores the value internally at $ty (typically `u32`) while the interface uses `usize` to\n/// support easy indexing.\n#[macro_export]\nmacro_rules! new_indexing_id_type {\n    ($name:ident, $ty:ty) => {\n        #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd)]\n        pub struct $name($ty);\n\n        impl $name {\n            pub fn new(value: usize) -> $name {\n                $name(value as $ty)\n            }\n\n            #[allow(unused)]\n            pub fn new_entry_id<T>(lookup_vec: &mut Vec<T>, entry: T) -> $name {\n                let id = Self::new(lookup_vec.len());\n                lookup_vec.push(entry);\n                id\n            }\n\n            #[allow(unused)]\n            pub fn to_usize(self) -> usize {\n                self.0 as usize\n            }\n        }\n    };\n}\n\n/// Builds a new ID type using a global counter\n///\n/// This allows allocating IDs without threading a mutable counter through multiple layers of\n/// code.\n#[macro_export]\nmacro_rules! new_global_id_type {\n    ($id_name:ident) => {\n        new_global_id_type!(\n            $id_name,\n            usize,\n            std::sync::atomic::AtomicUsize,\n            std::num::NonZeroUsize\n        );\n    };\n    ($id_name:ident, $native_type:ty, $atomic_type:ty, $non_zero_type:ty) => {\n        // These counters are very hot and shared between threads\n        // They're not strongly correlated with each other so put them on different cachelines to\n        // avoid bouncing them between CPUs. The value of 64 is just a guess; it's a typical value\n        // and isn't needed for correctness.\n        #[repr(align(64))]\n        struct AlignedAtomic($atomic_type);\n\n        static NEXT_VALUE: AlignedAtomic = AlignedAtomic(<$atomic_type>::new(1));\n\n        #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]\n        pub struct $id_name($non_zero_type);\n\n        impl $id_name {\n            /// Allocates a ID unique for the duration of compiler's execution\n            pub fn alloc() -> Self {\n                use std::sync::atomic::Ordering;\n\n                // We used relaxed ordering because the order doesn't actually matter; these are\n                // used only for uniqueness\n                let raw_id = NEXT_VALUE.0.fetch_add(1, Ordering::Relaxed);\n                Self::new(raw_id)\n            }\n\n            #[allow(unused)]\n            pub fn get(&self) -> $native_type {\n                self.0.into()\n            }\n\n            fn new(raw_id: $native_type) -> Self {\n                $id_name(<$non_zero_type>::new(raw_id).unwrap())\n            }\n        }\n    };\n}\n\n/// Builds a new ID type based off an arbitrary counter\n#[macro_export]\nmacro_rules! new_counting_id_type {\n    ($counter_name:ident, $id_name:ident) => {\n        #[derive(Clone)]\n        pub struct $counter_name(u32);\n\n        impl $counter_name {\n            pub fn new() -> $counter_name {\n                $counter_name(1)\n            }\n\n            pub fn alloc(&mut self) -> $id_name {\n                let id = $id_name(self.0);\n                self.0 += 1;\n                id\n            }\n        }\n\n        #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd)]\n        pub struct $id_name(u32);\n\n        impl $id_name {\n            #[allow(unused)]\n            pub fn new(value: u32) -> $id_name {\n                $id_name(value)\n            }\n\n            #[allow(unused)]\n            pub fn to_u32(self) -> u32 {\n                self.0\n            }\n        }\n    };\n}\n\n/// Reference-counted pointer that uses pointer identity\n///\n/// Traits such as `Hash`, `Eq`, `Ord` etc. are implemented in terms of the value's memory location.\n/// This means that the value returned by `ArcId::new()` is considered equal to itself and its\n/// clones regardless of the value it points to.\npub struct ArcId<T> {\n    inner: Arc<T>,\n}\n\nimpl<T> ArcId<T> {\n    pub fn new(value: T) -> Self {\n        ArcId {\n            inner: Arc::new(value),\n        }\n    }\n}\n\nimpl<T> Clone for ArcId<T> {\n    fn clone(&self) -> Self {\n        ArcId {\n            inner: self.inner.clone(),\n        }\n    }\n}\n\nimpl<T> ops::Deref for ArcId<T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        self.inner.deref()\n    }\n}\n\nimpl<T> PartialEq for ArcId<T> {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.inner, &other.inner)\n    }\n}\n\nimpl<T> Eq for ArcId<T> {}\n\nimpl<T> hash::Hash for ArcId<T> {\n    fn hash<H: hash::Hasher>(&self, state: &mut H) {\n        state.write_usize(self.inner.as_ref() as *const T as usize)\n    }\n}\n\nimpl<T> PartialOrd for ArcId<T> {\n    fn partial_cmp(&self, other: &ArcId<T>) -> Option<std::cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl<T> Ord for ArcId<T> {\n    fn cmp(&self, other: &ArcId<T>) -> std::cmp::Ordering {\n        (self.inner.as_ref() as *const T as usize).cmp(&(other.inner.as_ref() as *const T as usize))\n    }\n}\n\nimpl<T: fmt::Debug> fmt::Debug for ArcId<T> {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.inner.fmt(formatter)\n    }\n}\n"
  },
  {
    "path": "compiler/lib.rs",
    "content": "#![warn(clippy::all)]\n#![warn(rust_2018_idioms)]\n\n#[macro_use]\nmod id_type;\n\nmod arret_root;\nmod codegen;\nmod context;\nmod hir;\nmod mir;\nmod promise;\npub mod repl;\nmod reporting;\nmod rfi;\nmod source;\nmod ty;\nmod typeck;\n\nuse std::collections::HashSet;\nuse std::sync::Arc;\n\nuse codespan_reporting::diagnostic::Diagnostic;\n\nuse arret_syntax::span::FileId;\n\npub use crate::arret_root::{find_arret_root, FindArretRootError};\npub use crate::codegen::initialise_llvm;\npub use crate::codegen::program::{gen_program, Options as GenProgramOptions, OutputType};\npub use crate::context::{CompileCtx, LinkedLibrary};\npub use crate::hir::PackagePaths;\npub use crate::id_type::ArcId;\npub use crate::mir::eval_hir::{BuiltProgram, EvalHirCtx};\npub use crate::mir::print_program as print_program_mir;\npub use crate::reporting::emit_diagnostics_to_stderr;\npub use crate::source::{SourceFile, SourceLoader, SourceText};\n\npub struct EvaluableProgram {\n    pub ehx: EvalHirCtx,\n    pub main_export_id: hir::ExportId,\n    pub linked_libraries: Vec<Arc<LinkedLibrary>>,\n}\n\n/// Visits a subtree of modules, evaluates their definitions and collects their RFI libraries\nfn include_imports(\n    ehx: &mut EvalHirCtx,\n    visited_modules: &mut HashSet<context::ModuleId>,\n    linked_libraries: &mut Vec<Arc<LinkedLibrary>>,\n    root_module: &context::Module,\n) -> Result<(), Vec<Diagnostic<FileId>>> {\n    if visited_modules.contains(&root_module.module_id) {\n        return Ok(());\n    }\n\n    visited_modules.insert(root_module.module_id);\n\n    if let Some(ref linked_library) = root_module.linked_library {\n        linked_libraries.push(linked_library.clone());\n    }\n\n    // Make sure our imports are first\n    for import in root_module.imports.values() {\n        include_imports(ehx, visited_modules, linked_libraries, import)?;\n    }\n\n    ehx.visit_module_defs(root_module.module_id, &root_module.defs)?;\n\n    Ok(())\n}\n\npub fn program_to_evaluable(\n    ccx: &CompileCtx,\n    source_file: &SourceFile,\n) -> Result<EvaluableProgram, Vec<Diagnostic<FileId>>> {\n    use arret_syntax::span::Span;\n\n    use crate::typeck::infer;\n\n    let entry_module = ccx.source_file_to_module(source_file)?;\n\n    let main_local_id = if let Some(local_id) = entry_module.main_local_id {\n        local_id\n    } else {\n        use codespan_reporting::diagnostic::Label;\n\n        return Err(vec![Diagnostic::error()\n            .with_message(\"no main! function defined in entry module\")\n            .with_labels(vec![Label::primary(source_file.file_id(), 0..1)\n                .with_message(\"main! function expected in this file\")])]);\n    };\n\n    let inferred_main_type = &entry_module.inferred_locals[&main_local_id];\n\n    infer::ensure_main_type(\n        Span::new(Some(source_file.file_id()), 0, 0),\n        &entry_module.defs,\n        main_local_id,\n        inferred_main_type,\n    )\n    .map_err(|err| vec![err.into()])?;\n\n    let mut ehx = EvalHirCtx::new(ccx.enable_optimisations());\n    let mut linked_libraries = vec![];\n    let mut visited_modules = HashSet::new();\n\n    for import in entry_module.imports.values() {\n        include_imports(\n            &mut ehx,\n            &mut visited_modules,\n            &mut linked_libraries,\n            import,\n        )?;\n    }\n\n    // We can consume here because we own the entry module\n    ehx.consume_module_defs(entry_module.module_id, entry_module.defs)?;\n\n    if ehx.should_collect() {\n        ehx.collect_garbage();\n    }\n\n    Ok(EvaluableProgram {\n        ehx,\n        main_export_id: hir::ExportId::new(entry_module.module_id, main_local_id),\n        linked_libraries,\n    })\n}\n"
  },
  {
    "path": "compiler/mir/app_purity.rs",
    "content": "use std::collections::HashMap;\n\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\n\nfn resolve_ref_to_purity(\n    pvar_purities: &HashMap<purity::PVarId, purity::Ref>,\n    poly: &purity::Ref,\n) -> Purity {\n    match poly {\n        purity::Ref::Fixed(purity) => *purity,\n        purity::Ref::Var(pvar) => {\n            let inner_ref = pvar_purities\n                .get(pvar)\n                .expect(\"Unable to find PVar determining fun apply purity\");\n\n            resolve_ref_to_purity(pvar_purities, inner_ref)\n        }\n    }\n}\n\n/// Returns the purity for a fun application\npub fn fun_app_purity(\n    pvar_purities: &HashMap<purity::PVarId, purity::Ref>,\n    fun_purity: &purity::Ref,\n    fun_ret_ty: &ty::Ref<ty::Poly>,\n) -> Purity {\n    if fun_ret_ty.is_never() {\n        // This is a hack for things like `panic`. Pure funs are allowed to panic but if they\n        // return `(U)` they're likely only called to terminate the program. Without this `panic`\n        // would be optimised away.\n        return Purity::Impure;\n    }\n\n    resolve_ref_to_purity(pvar_purities, fun_purity)\n}\n"
  },
  {
    "path": "compiler/mir/arg_list.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::abitype;\n\nuse crate::mir::builder::{Builder, BuiltReg};\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::ops;\nuse crate::mir::polymorph::PolymorphAbi;\nuse crate::mir::value::Value;\nuse crate::ty;\n\npub struct LoadedArgList {\n    /// Reg holding the captures parameter\n    pub captures_reg: Option<BuiltReg>,\n\n    /// All regs the function takes including the captures\n    pub param_regs: Box<[ops::RegId]>,\n\n    /// Built list value of the arguments\n    pub arg_list_value: Value,\n}\n\n/// Builds the regs and ops for loading the argument list of a function\n///\n/// This results in an argument list value which contains all arguments passed to the function.\npub fn build_load_arg_list_value(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    polymorph_abi: &PolymorphAbi,\n    param_list_poly: &ty::List<ty::Poly>,\n) -> LoadedArgList {\n    use crate::mir::value::from_reg::reg_to_value;\n    use crate::ty::list_iter::ListIterator;\n\n    let captures_reg: Option<BuiltReg> = if polymorph_abi.has_captures {\n        Some(b.alloc_local())\n    } else {\n        None\n    };\n\n    let mut param_list_poly_iter = ListIterator::new(param_list_poly);\n\n    let fixed_reg_values: Vec<(ops::RegId, Value)> = polymorph_abi\n        .fixed_params\n        .iter()\n        .map(|abi_type| {\n            let reg = b.alloc_local();\n            let param_type = param_list_poly_iter.next().unwrap();\n\n            (reg.into(), reg_to_value(ehx, reg, abi_type, param_type))\n        })\n        .collect();\n\n    let rest_reg_value: Option<(ops::RegId, Value)> =\n        polymorph_abi.rest_param.as_ref().map(|abi_type| {\n            let reg = b.alloc_local();\n            let tail_type = param_list_poly_iter.tail_type();\n\n            (\n                reg.into(),\n                reg_to_value(ehx, reg, abi_type, &tail_type.into()),\n            )\n        });\n\n    let param_regs = captures_reg\n        .into_iter()\n        .map(Into::into)\n        .chain(fixed_reg_values.iter().map(|(reg, _)| *reg))\n        .chain(rest_reg_value.iter().map(|(reg, _)| *reg))\n        .collect();\n\n    let arg_list_value = Value::List(\n        fixed_reg_values\n            .into_iter()\n            .map(|(_, value)| value)\n            .collect(),\n        rest_reg_value.map(|(_, value)| Box::new(value)),\n    );\n\n    LoadedArgList {\n        captures_reg,\n        param_regs,\n        arg_list_value,\n    }\n}\n\npub fn build_save_arg_list_to_regs<'a>(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: Value,\n    fixed_abi_types: impl ExactSizeIterator<Item = &'a abitype::AbiType>,\n    rest_abi_type: Option<&'a abitype::AbiType>,\n) -> Vec<ops::RegId> {\n    use crate::mir::value::build_reg::value_to_reg;\n\n    let mut list_iter = arg_list_value.into_unsized_list_iter();\n\n    let mut arg_regs = vec![];\n    for abi_type in fixed_abi_types {\n        let fixed_value = list_iter.next_unchecked(b, span);\n        let reg_id = value_to_reg(ehx, b, span, &fixed_value, abi_type);\n        arg_regs.push(reg_id.into());\n    }\n\n    if let Some(rest_abi_type) = rest_abi_type {\n        let reg_id = value_to_reg(ehx, b, span, &list_iter.into_rest(), rest_abi_type);\n        arg_regs.push(reg_id.into());\n    };\n\n    arg_regs\n}\n"
  },
  {
    "path": "compiler/mir/builder.rs",
    "content": "use std::fmt;\n\nuse crate::mir::ops::{CastBoxedOp, CondOp, Op, OpKind, RegId, RegPhi};\nuse arret_runtime::abitype;\nuse arret_syntax::span::Span;\n\npub struct Builder {\n    ops: Vec<Op>,\n}\n\n#[derive(Clone, Copy)]\npub enum BuiltReg {\n    Const(RegId),\n    Local(RegId),\n}\n\nimpl BuiltReg {\n    pub fn into_reg_id(self) -> RegId {\n        match self {\n            BuiltReg::Const(reg_id) | BuiltReg::Local(reg_id) => reg_id,\n        }\n    }\n\n    pub fn is_const(self) -> bool {\n        match self {\n            BuiltReg::Const(_) => true,\n            BuiltReg::Local(_) => false,\n        }\n    }\n}\n\nimpl From<BuiltReg> for RegId {\n    fn from(built_reg: BuiltReg) -> RegId {\n        built_reg.into_reg_id()\n    }\n}\n\nimpl fmt::Debug for BuiltReg {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        // This is by analogy with LLVM\n        if self.is_const() {\n            write!(f, \"@{}\", self.into_reg_id().get())\n        } else {\n            write!(f, \"%{}\", self.into_reg_id().get())\n        }\n    }\n}\n\nimpl Builder {\n    pub fn new() -> Builder {\n        Builder { ops: vec![] }\n    }\n\n    pub fn alloc_local(&mut self) -> BuiltReg {\n        BuiltReg::Local(RegId::alloc())\n    }\n\n    pub fn push_reg<F, P>(&mut self, span: Span, kind_cons: F, kind_param: P) -> BuiltReg\n    where\n        F: FnOnce(RegId, P) -> OpKind,\n    {\n        let reg_id = RegId::alloc();\n        let kind = kind_cons(reg_id, kind_param);\n        let output_reg_is_const = kind.const_output();\n\n        self.push(span, kind);\n\n        if output_reg_is_const {\n            BuiltReg::Const(reg_id)\n        } else {\n            BuiltReg::Local(reg_id)\n        }\n    }\n\n    pub fn push(&mut self, span: Span, kind: OpKind) {\n        self.ops.push(Op::new(span, kind));\n    }\n\n    pub fn append(&mut self, ops: impl IntoIterator<Item = Op>) {\n        self.ops.extend(ops);\n    }\n\n    pub fn into_ops(self) -> Box<[Op]> {\n        self.ops.into_boxed_slice()\n    }\n\n    pub fn push_cond<T, F>(\n        &mut self,\n        span: Span,\n        test_reg: RegId,\n        true_cons: T,\n        false_cons: F,\n    ) -> BuiltReg\n    where\n        T: FnOnce(&mut Builder) -> RegId,\n        F: FnOnce(&mut Builder) -> RegId,\n    {\n        let mut true_builder = Builder::new();\n        let true_result_reg = true_cons(&mut true_builder);\n\n        let mut false_builder = Builder::new();\n        let false_result_reg = false_cons(&mut false_builder);\n\n        let output_reg = RegId::alloc();\n        self.push(\n            span,\n            OpKind::Cond(CondOp {\n                reg_phi: Some(RegPhi {\n                    output_reg,\n                    true_result_reg,\n                    false_result_reg,\n                }),\n                test_reg,\n                true_ops: true_builder.into_ops(),\n                false_ops: false_builder.into_ops(),\n            }),\n        );\n\n        BuiltReg::Local(output_reg)\n    }\n\n    pub fn cast_boxed(\n        &mut self,\n        span: Span,\n        from_reg: BuiltReg,\n        to_type: abitype::BoxedAbiType,\n    ) -> BuiltReg {\n        let kind_cons = if from_reg.is_const() {\n            OpKind::ConstCastBoxed\n        } else {\n            OpKind::CastBoxed\n        };\n\n        let cast_boxed_op = CastBoxedOp {\n            from_reg: from_reg.into(),\n            to_type,\n        };\n\n        self.push_reg(span, kind_cons, cast_boxed_op)\n    }\n\n    pub fn cast_boxed_cond(\n        &mut self,\n        span: Span,\n        from_type: &abitype::BoxedAbiType,\n        from_reg: BuiltReg,\n        to_type: abitype::BoxedAbiType,\n    ) -> BuiltReg {\n        if from_type == &to_type {\n            from_reg\n        } else {\n            self.cast_boxed(span, from_reg, to_type)\n        }\n    }\n}\n\nimpl Default for Builder {\n    fn default() -> Builder {\n        Builder::new()\n    }\n}\n\npub trait TryToBuilder {\n    fn try_to_builder(&mut self) -> Option<&mut Builder>;\n}\n\nimpl TryToBuilder for Option<Builder> {\n    fn try_to_builder(&mut self) -> Option<&mut Builder> {\n        self.as_mut()\n    }\n}\n\nimpl TryToBuilder for Builder {\n    fn try_to_builder(&mut self) -> Option<&mut Builder> {\n        Some(self)\n    }\n}\n"
  },
  {
    "path": "compiler/mir/costing.rs",
    "content": "use crate::mir::ops;\n\n/// Abstract unit for measuring the runtime cost of ops\npub type OpCost = u32;\n\n/// Abstract unit for a multiplier of an `OpCost`\npub type OpCostFactor = f32;\n\n/// Returns the approximate runtime cost of an operation category\n///\n/// This isn't adjusted for any specifics of a given op. `cost_for_ops` should be used when costing\n/// a known sequence of ops.\npub fn cost_for_op_category(category: ops::OpCategory) -> OpCost {\n    use crate::mir::ops::OpCategory;\n\n    match category {\n        OpCategory::Unreachable => 0,\n        OpCategory::ConstCastBoxed | OpCategory::CastBoxed => 0,\n        OpCategory::ConstReg => 1,\n        OpCategory::RegOp => 2,\n        OpCategory::ConstBox => 4,\n        OpCategory::Cond => 5, // Adjusted below to include branches\n        OpCategory::MakeCallback => 5,\n        OpCategory::MemLoad => 5,\n        OpCategory::Ret => 5,\n        OpCategory::Call => 9, // Adjusted below based on the call purity\n        // This is tricky. This could either do a stack allocation (which is cheap) or a heap\n        // allocation (which is very expensive). This depends on the type and escape analysis\n        // in codegen. We need to make use compromise between those two costs here.\n        OpCategory::AllocBoxed => 15,\n    }\n}\n\n/// Returns the approximate runtime cost of an operation in an abstract unit\nfn cost_for_op(op: &ops::Op) -> OpCost {\n    let category_cost = cost_for_op_category(op.kind().category());\n\n    let op_adjustment = match op.kind() {\n        ops::OpKind::Cond(cond_op) => {\n            // Only one branch can be taken so the runtime cost is the average of the branches. On\n            // the other hand, the code size (and thus icache footprint) is the sum of the\n            // branches. Compromise by using the most expensive branch.\n            std::cmp::max(\n                cost_for_ops(cond_op.true_ops.iter()),\n                cost_for_ops(cond_op.false_ops.iter()),\n            )\n        }\n        ops::OpKind::Call(_, call_op) => {\n            // Impure calls are harder to optimise. Penalise them.\n            let impure_penalty = if call_op.impure { 2 } else { 0 };\n\n            let callee_penalty = match call_op.callee {\n                // These cannot be inlined and need to use the standard calling convention\n                ops::Callee::BoxedFunThunk(_) | ops::Callee::StaticSymbol(_) => 2,\n                _ => 0,\n            };\n\n            impure_penalty + callee_penalty\n        }\n        _ => 0,\n    };\n\n    category_cost + op_adjustment\n}\n\n/// Returns the cost for a sequence of ops\npub fn cost_for_ops<'o>(ops: impl Iterator<Item = &'o ops::Op>) -> OpCost {\n    ops.map(cost_for_op).sum()\n}\n"
  },
  {
    "path": "compiler/mir/env_values.rs",
    "content": "use std::collections::HashMap;\n\nuse arret_syntax::datum::DataStr;\nuse arret_syntax::span::Span;\n\nuse crate::hir;\nuse crate::mir::builder::{Builder, BuiltReg};\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::ops::{RecordStruct, RecordStructId};\nuse crate::mir::specific_abi_type::specific_abi_type_for_value;\nuse crate::mir::value::Value;\n\ntype Values = Box<[(hir::LocalId, Value)]>;\n\n/// Indicates the layout of captured values\n#[derive(Clone, Debug)]\nenum CapturesRepr {\n    Empty,\n    SingleBox,\n    RecordStruct(RecordStructId),\n}\n\n/// Tracks the constant and free values captured by an expression\n#[derive(Clone, Debug)]\npub struct EnvValues {\n    pub const_values: Values,\n    pub free_values: Values,\n    captures_repr: CapturesRepr,\n}\n\nimpl EnvValues {\n    pub fn empty() -> EnvValues {\n        EnvValues {\n            const_values: Box::new([]),\n            free_values: Box::new([]),\n            captures_repr: CapturesRepr::Empty,\n        }\n    }\n}\n\nfn can_reference_local_regs(value: &Value) -> bool {\n    match value {\n        Value::Const(_)\n        | Value::EqPred\n        | Value::TyPred(_)\n        | Value::RecordCons(_)\n        | Value::FieldAccessor(_, _)\n        | Value::RustFun(_) => false,\n        Value::Reg(_) => true,\n        Value::ArretFun(arret_fun) => !arret_fun.env_values().free_values.is_empty(),\n        Value::List(fixed, rest) => fixed\n            .iter()\n            .chain(rest.iter().map(AsRef::as_ref))\n            .any(can_reference_local_regs),\n        Value::Record(_, fields) => fields.iter().any(can_reference_local_regs),\n    }\n}\n\n/// Calculates the values captured from the environment by the passed expression\npub fn calculate_env_values(\n    local_values: &HashMap<hir::LocalId, Value>,\n    capturing_expr: &hir::Expr<hir::Inferred>,\n    source_name: Option<&DataStr>,\n) -> EnvValues {\n    let mut captured_values = HashMap::new();\n\n    // Only process captures if there are local values. This is to avoid visiting the expression\n    // when capturing isn't possible\n    if !local_values.is_empty() {\n        // Look for references to variables inside the function\n        hir::visitor::visit_exprs(capturing_expr, &mut |expr| {\n            if let hir::ExprKind::LocalRef(_, local_id) = &expr.kind {\n                if !captured_values.contains_key(local_id) {\n                    if let Some(value) = local_values.get(local_id) {\n                        captured_values.insert(*local_id, value.clone());\n                    }\n                }\n            }\n        });\n    }\n\n    // Determine which captures are constants\n    let (free_values, const_values): (Vec<_>, Vec<_>) = captured_values\n        .into_iter()\n        .partition(|(_, value)| can_reference_local_regs(value));\n\n    let captures_repr = match free_values.len() {\n        0 => CapturesRepr::Empty,\n        1 => {\n            // Single field records can never box as efficiently as our native box representation\n            CapturesRepr::SingleBox\n        }\n        _ => {\n            let captures_source_name = source_name\n                .map(|source_name| format!(\"{}_captures\", source_name).into())\n                .unwrap_or_else(|| \"anon_captures\".into());\n\n            let field_abi_types = free_values\n                .iter()\n                .map(|(_, value)| specific_abi_type_for_value(value))\n                .collect();\n\n            let record_struct_id = RecordStruct::new(captures_source_name, field_abi_types);\n            CapturesRepr::RecordStruct(record_struct_id)\n        }\n    };\n\n    EnvValues {\n        const_values: const_values.into_boxed_slice(),\n        free_values: free_values.into_boxed_slice(),\n        captures_repr,\n    }\n}\n\n/// Builds code to save local values into a captures reg\npub fn save_to_captures_reg(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    env_values: &EnvValues,\n) -> Option<BuiltReg> {\n    use crate::mir::value::build_reg::value_to_reg;\n    use arret_runtime::abitype;\n\n    match &env_values.captures_repr {\n        CapturesRepr::Empty => None,\n        CapturesRepr::SingleBox => {\n            let value = &env_values.free_values[0].1;\n\n            Some(value_to_reg(\n                ehx,\n                b,\n                span,\n                value,\n                &abitype::BoxedAbiType::Any.into(),\n            ))\n        }\n        CapturesRepr::RecordStruct(record_struct) => {\n            use crate::mir::ops::*;\n\n            let field_regs = env_values\n                .free_values\n                .iter()\n                .zip(record_struct.field_abi_types.iter())\n                .map(|((_, value), abi_type)| value_to_reg(ehx, b, span, value, abi_type).into())\n                .collect();\n\n            let record_reg = b.push_reg(\n                span,\n                OpKind::AllocBoxedRecord,\n                BoxRecordOp {\n                    record_struct: record_struct.clone(),\n                    field_regs,\n                },\n            );\n\n            Some(b.cast_boxed(span, record_reg, abitype::BoxedAbiType::Any))\n        }\n    }\n}\n\n/// Loads env values assuming all captured variables are still inside the local function\npub fn load_from_current_fun(\n    local_values: &mut HashMap<hir::LocalId, Value>,\n    env_values: &EnvValues,\n) {\n    local_values.extend(\n        env_values\n            .const_values\n            .iter()\n            .chain(env_values.free_values.iter())\n            .map(|(local_id, value)| (*local_id, value.clone())),\n    );\n}\n\n/// Loads environment values from an env parameter\npub fn load_from_env_param(\n    b: &mut Builder,\n    span: Span,\n    local_values: &mut HashMap<hir::LocalId, Value>,\n    env_values: &mut EnvValues,\n    captures_reg: Option<BuiltReg>,\n) {\n    use crate::mir::value;\n    use arret_runtime::abitype;\n\n    // Include the const values directly\n    local_values.extend(\n        env_values\n            .const_values\n            .iter()\n            .map(|(local_id, value)| (*local_id, value.clone())),\n    );\n\n    match &env_values.captures_repr {\n        CapturesRepr::Empty => {}\n        CapturesRepr::SingleBox => {\n            let var_id = &env_values.free_values[0].0;\n            let captures_reg = captures_reg.unwrap();\n            let new_value: Value =\n                value::RegValue::new(captures_reg, abitype::BoxedAbiType::Any.into()).into();\n\n            local_values.insert(*var_id, new_value.clone());\n            env_values.free_values[0].1 = new_value;\n        }\n        CapturesRepr::RecordStruct(record_struct) => {\n            use crate::mir::ops::*;\n\n            let record_reg: RegId = captures_reg.unwrap().into();\n\n            for (field_index, (local_id, free_value)) in\n                env_values.free_values.iter_mut().enumerate()\n            {\n                let field_reg = b.push_reg(\n                    span,\n                    OpKind::LoadBoxedRecordField,\n                    LoadBoxedRecordFieldOp {\n                        record_reg,\n                        record_struct: record_struct.clone(),\n                        field_index,\n                    },\n                );\n\n                let field_abi_type = record_struct.field_abi_types[field_index].clone();\n\n                let new_value: Value = value::RegValue::new(field_reg, field_abi_type).into();\n\n                local_values.insert(*local_id, new_value.clone());\n                *free_value = new_value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/mir/equality.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::abitype;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\n\nuse crate::codegen::GenAbi;\nuse crate::mir::builder::{Builder, BuiltReg, TryToBuilder};\nuse crate::mir::costing::{cost_for_op_category, cost_for_ops};\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::ops::*;\nuse crate::mir::tagset::TypeTagSet;\nuse crate::mir::value;\nuse crate::mir::value::build_reg::value_to_reg;\nuse crate::mir::value::to_const::value_to_const;\nuse crate::mir::value::Value;\nuse crate::ty::record;\n\npub enum EqualityResult {\n    Static(bool),\n    Dynamic(Value),\n}\n\nimpl EqualityResult {\n    fn from_bool_reg(reg: BuiltReg) -> EqualityResult {\n        EqualityResult::Dynamic(value::RegValue::new(reg, abitype::AbiType::Bool).into())\n    }\n}\n\nimpl From<EqualityResult> for Value {\n    fn from(er: EqualityResult) -> Value {\n        match er {\n            EqualityResult::Static(true) => boxed::TRUE_INSTANCE.as_any_ref().into(),\n            EqualityResult::Static(false) => boxed::FALSE_INSTANCE.as_any_ref().into(),\n            EqualityResult::Dynamic(value) => value,\n        }\n    }\n}\n\nfn runtime_compare(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    left_value: &Value,\n    right_value: &Value,\n) -> BuiltReg {\n    let left_reg = value_to_reg(ehx, b, span, left_value, &abitype::BoxedAbiType::Any.into());\n\n    let right_reg = value_to_reg(\n        ehx,\n        b,\n        span,\n        right_value,\n        &abitype::BoxedAbiType::Any.into(),\n    );\n\n    let abi = GenAbi {\n        takes_task: true,\n        params: Box::new([\n            abitype::BoxedAbiType::Any.into(),\n            abitype::BoxedAbiType::Any.into(),\n        ]),\n        ret: abitype::AbiType::Bool.into(),\n    };\n\n    let callee = Callee::StaticSymbol(StaticSymbol {\n        symbol: \"arret_runtime_equals\",\n        impure: false,\n        abi,\n    });\n\n    b.push_reg(\n        span,\n        OpKind::Call,\n        CallOp {\n            callee,\n            impure: false,\n            args: Box::new([left_reg.into(), right_reg.into()]),\n        },\n    )\n}\n\nfn build_native_compare<F>(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    left_value: &Value,\n    right_value: &Value,\n    abi_type: &abitype::AbiType,\n    op_kind: F,\n) -> BuiltReg\nwhere\n    F: FnOnce(RegId, BinaryOp) -> OpKind,\n{\n    let left_reg = value_to_reg(ehx, b, span, left_value, abi_type);\n    let right_reg = value_to_reg(ehx, b, span, right_value, abi_type);\n\n    b.push_reg(\n        span,\n        op_kind,\n        BinaryOp {\n            lhs_reg: left_reg.into(),\n            rhs_reg: right_reg.into(),\n        },\n    )\n}\n\nfn build_record_equality(\n    ehx: &mut EvalHirCtx,\n    parent_b: &mut Builder,\n    span: Span,\n    record_cons: &record::ConsId,\n    left_value: &Value,\n    right_value: &Value,\n) -> EqualityResult {\n    use crate::mir::record_field::load_record_field;\n\n    // Try a fieldwise comparison\n    let field_count = record_cons.fields().len();\n    let mut fieldwise_b = Builder::new();\n    let mut fieldwise_regs = Vec::<BuiltReg>::with_capacity(field_count);\n\n    for field_index in 0..field_count {\n        let left_field = load_record_field(\n            ehx,\n            &mut fieldwise_b,\n            span,\n            record_cons,\n            left_value,\n            field_index,\n        );\n\n        let right_field = load_record_field(\n            ehx,\n            &mut fieldwise_b,\n            span,\n            record_cons,\n            right_value,\n            field_index,\n        );\n\n        match eval_equality(ehx, &mut fieldwise_b, span, &left_field, &right_field) {\n            EqualityResult::Static(false) => {\n                // The whole comparison is false; we don't need to build anything\n                return EqualityResult::Static(false);\n            }\n            EqualityResult::Static(true) => {\n                // We can ignore this comparison\n            }\n            EqualityResult::Dynamic(value) => {\n                let fieldwise_reg =\n                    value_to_reg(ehx, &mut fieldwise_b, span, &value, &abitype::AbiType::Bool);\n                fieldwise_regs.push(fieldwise_reg);\n            }\n        }\n    }\n\n    let mut fieldwise_reg_iter = fieldwise_regs.into_iter();\n    let first_fieldwise_reg = if let Some(fieldwise_reg) = fieldwise_reg_iter.next() {\n        fieldwise_reg\n    } else {\n        // This is statically true\n        return EqualityResult::Static(true);\n    };\n\n    let combined_fieldwise_reg =\n        fieldwise_reg_iter.fold(first_fieldwise_reg, |acc_reg, fieldwise_reg| {\n            let phi_result_reg = fieldwise_b.alloc_local();\n            fieldwise_b.push(\n                span,\n                OpKind::Cond(CondOp {\n                    reg_phi: Some(RegPhi {\n                        output_reg: phi_result_reg.into(),\n                        true_result_reg: acc_reg.into(),\n                        false_result_reg: fieldwise_reg.into(),\n                    }),\n                    test_reg: fieldwise_reg.into(),\n                    true_ops: Box::new([]),\n                    false_ops: Box::new([]),\n                }),\n            );\n\n            phi_result_reg\n        });\n\n    // Try a runtime compare\n    let mut runtime_b = Builder::new();\n    let runtime_reg = runtime_compare(ehx, &mut runtime_b, span, left_value, right_value);\n\n    // Build ops for both options and cost them\n    let fieldwise_ops = fieldwise_b.into_ops();\n    let fieldwise_cost = cost_for_ops(fieldwise_ops.iter());\n\n    let runtime_ops = runtime_b.into_ops();\n    // Favour fieldwise comparisons. Runtime comparisons of records are more expensive than other\n    // types but this wouldn't be captured by `cost_for_ops`. Account for at least the cost of\n    // loading the class map.\n    let runtime_cost = cost_for_ops(runtime_ops.iter()) + cost_for_op_category(OpCategory::MemLoad);\n\n    if runtime_cost < fieldwise_cost {\n        parent_b.append(runtime_ops.into_vec().into_iter());\n        EqualityResult::from_bool_reg(runtime_reg)\n    } else {\n        parent_b.append(fieldwise_ops.into_vec().into_iter());\n        EqualityResult::from_bool_reg(combined_fieldwise_reg)\n    }\n}\n\n/// Builds a comparison between two values known to be boolean\nfn build_bool_equality(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    left_value: &Value,\n    right_value: &Value,\n) -> EqualityResult {\n    enum ValueClass {\n        ConstTrue,\n        Boxed,\n        Other,\n    }\n\n    fn classify_value(value: &Value) -> ValueClass {\n        match value {\n            Value::Const(any_ref) if any_ref.header().type_tag() == boxed::TypeTag::True => {\n                ValueClass::ConstTrue\n            }\n            Value::Reg(reg_value) => {\n                if let abitype::AbiType::Boxed(_) = &reg_value.abi_type {\n                    ValueClass::Boxed\n                } else {\n                    ValueClass::Other\n                }\n            }\n            _ => ValueClass::Other,\n        }\n    }\n\n    let left_class = classify_value(left_value);\n    let right_class = classify_value(right_value);\n\n    let result_reg = match (left_class, right_class) {\n        // Comparing a boolean to constant true can be simplified to a no-op\n        (ValueClass::ConstTrue, _) => {\n            return EqualityResult::Dynamic(right_value.clone());\n        }\n        (_, ValueClass::ConstTrue) => {\n            return EqualityResult::Dynamic(left_value.clone());\n        }\n        (ValueClass::Boxed, ValueClass::Boxed) => {\n            // If both values are boxed we can just compare the pointers\n            build_native_compare(\n                ehx,\n                b,\n                span,\n                left_value,\n                right_value,\n                &abitype::BoxedAbiType::Any.into(),\n                OpKind::BoxIdentical,\n            )\n        }\n        _ => {\n            // Fall back to a native comparison of the unboxed values\n            build_native_compare(\n                ehx,\n                b,\n                span,\n                left_value,\n                right_value,\n                &abitype::AbiType::Bool,\n                OpKind::BoolEqual,\n            )\n        }\n    };\n\n    EqualityResult::from_bool_reg(result_reg)\n}\n\n/// Determines if two values are statically equal\npub fn values_statically_equal(\n    ehx: &mut EvalHirCtx,\n    left_value: &Value,\n    right_value: &Value,\n) -> Option<bool> {\n    match (left_value, right_value) {\n        (Value::Reg(left_reg), Value::Reg(right_reg)) => {\n            if [left_reg, right_reg]\n                .iter()\n                .any(|reg| reg.possible_type_tags == boxed::TypeTag::FunThunk.into())\n            {\n                // Functions are equal to nothing, including themselves\n                return Some(false);\n            }\n\n            if left_reg.reg.into_reg_id() != right_reg.reg.into_reg_id() {\n                // We can't determine if these are statically equal\n                return None;\n            }\n\n            for partial_equal_type_tag in TypeTagSet::all().into_iter().filter(|type_tag| {\n                match type_tag {\n                    // Functions never compare equal\n                    boxed::TypeTag::FunThunk => true,\n                    // NaN != NaN\n                    boxed::TypeTag::Float => true,\n                    // Can contain partial equal values\n                    boxed::TypeTag::Pair\n                    | boxed::TypeTag::Record\n                    | boxed::TypeTag::Set\n                    | boxed::TypeTag::Map\n                    | boxed::TypeTag::Vector => true,\n                    // The rest can be compared. Add them explicitly so we will be forced to\n                    // classify new types\n                    boxed::TypeTag::Int\n                    | boxed::TypeTag::Char\n                    | boxed::TypeTag::Str\n                    | boxed::TypeTag::Sym\n                    | boxed::TypeTag::True\n                    | boxed::TypeTag::False\n                    | boxed::TypeTag::Nil => false,\n                }\n            }) {\n                if [left_reg, right_reg]\n                    .iter()\n                    .all(|reg| reg.possible_type_tags.contains(partial_equal_type_tag))\n                {\n                    return None;\n                }\n            }\n\n            Some(true)\n        }\n        // Functions never compare equal\n        (Value::ArretFun(_) | Value::RustFun(_) | Value::TyPred(_) | Value::EqPred, _)\n        | (_, Value::ArretFun(_) | Value::RustFun(_) | Value::TyPred(_) | Value::EqPred) => {\n            Some(false)\n        }\n        _ => {\n            if let Some(const_left) = value_to_const(ehx, left_value) {\n                if let Some(const_right) = value_to_const(ehx, right_value) {\n                    return Some(const_left.eq_in_heap(ehx.as_heap(), &const_right));\n                }\n            }\n\n            None\n        }\n    }\n}\n\n/// Evaluates if two values are equal\n///\n/// This attempts `values_statically_equal` before building a runtime comparison.\npub fn eval_equality(\n    ehx: &mut EvalHirCtx,\n    b: &mut impl TryToBuilder,\n    span: Span,\n    left_value: &Value,\n    right_value: &Value,\n) -> EqualityResult {\n    use crate::mir::value::types::{known_record_cons_for_value, possible_type_tags_for_value};\n\n    if let Some(static_result) = values_statically_equal(ehx, left_value, right_value) {\n        return EqualityResult::Static(static_result);\n    }\n\n    let b = if let Some(some_b) = b.try_to_builder() {\n        some_b\n    } else {\n        panic!(\"runtime equality without builder\")\n    };\n\n    let left_type_tags = possible_type_tags_for_value(left_value);\n    let right_type_tags = possible_type_tags_for_value(right_value);\n    let all_type_tags = left_type_tags | right_type_tags;\n    let common_type_tags = left_type_tags & right_type_tags;\n\n    if common_type_tags.is_empty() {\n        // No types in common\n        return EqualityResult::Static(false);\n    }\n\n    if [left_type_tags, right_type_tags].contains(&boxed::TypeTag::FunThunk.into()) {\n        // Functions always compare false\n        return EqualityResult::Static(false);\n    }\n\n    if all_type_tags == abitype::AbiType::Bool.into() {\n        // Build a specialised comparison for `Bool`\n        return build_bool_equality(ehx, b, span, left_value, right_value);\n    }\n\n    let boxed_singleton_type_tags: TypeTagSet = [\n        boxed::TypeTag::True,\n        boxed::TypeTag::False,\n        boxed::TypeTag::Nil,\n    ]\n    .iter()\n    .collect();\n\n    let result_reg = if common_type_tags.is_subset(boxed_singleton_type_tags) {\n        // We an do a direct pointer comparison\n        build_native_compare(\n            ehx,\n            b,\n            span,\n            left_value,\n            right_value,\n            &abitype::BoxedAbiType::Any.into(),\n            OpKind::BoxIdentical,\n        )\n    } else if all_type_tags == boxed::TypeTag::Int.into() {\n        build_native_compare(\n            ehx,\n            b,\n            span,\n            left_value,\n            right_value,\n            &abitype::AbiType::Int,\n            |reg_id, BinaryOp { lhs_reg, rhs_reg }| {\n                OpKind::IntCompare(\n                    reg_id,\n                    CompareOp {\n                        comparison: Comparison::Eq,\n                        lhs_reg,\n                        rhs_reg,\n                    },\n                )\n            },\n        )\n    } else if all_type_tags == boxed::TypeTag::Char.into() {\n        build_native_compare(\n            ehx,\n            b,\n            span,\n            left_value,\n            right_value,\n            &abitype::AbiType::Char,\n            OpKind::CharEqual,\n        )\n    } else if all_type_tags == boxed::TypeTag::Sym.into() {\n        build_native_compare(\n            ehx,\n            b,\n            span,\n            left_value,\n            right_value,\n            &abitype::AbiType::InternedSym,\n            OpKind::InternedSymEqual,\n        )\n    } else if all_type_tags == boxed::TypeTag::Float.into() {\n        build_native_compare(\n            ehx,\n            b,\n            span,\n            left_value,\n            right_value,\n            &abitype::AbiType::Float,\n            |reg_id, BinaryOp { lhs_reg, rhs_reg }| {\n                OpKind::FloatCompare(\n                    reg_id,\n                    CompareOp {\n                        comparison: Comparison::Eq,\n                        lhs_reg,\n                        rhs_reg,\n                    },\n                )\n            },\n        )\n    } else if all_type_tags == boxed::TypeTag::Record.into() {\n        let known_left_cons = known_record_cons_for_value(ehx, left_value);\n        let known_right_cons = known_record_cons_for_value(ehx, right_value);\n\n        match (known_left_cons, known_right_cons) {\n            (Some(left_cons), Some(right_cons)) => {\n                if left_cons == right_cons {\n                    let common_cons = left_cons.clone();\n\n                    return build_record_equality(\n                        ehx,\n                        b,\n                        span,\n                        &common_cons,\n                        left_value,\n                        right_value,\n                    );\n                } else {\n                    return EqualityResult::Static(false);\n                }\n            }\n            _ => runtime_compare(ehx, b, span, left_value, right_value),\n        }\n    } else {\n        runtime_compare(ehx, b, span, left_value, right_value)\n    };\n\n    EqualityResult::from_bool_reg(result_reg)\n}\n"
  },
  {
    "path": "compiler/mir/error.rs",
    "content": "use std::{error, fmt, result};\n\nuse codespan_reporting::diagnostic::Diagnostic;\n\nuse arret_syntax::span::{FileId, Span};\n\nuse crate::mir::inliner::ApplyCookie;\nuse crate::reporting::{new_primary_label, LocTrace};\n\n#[derive(Debug, PartialEq)]\npub struct Panic {\n    loc_trace: LocTrace,\n    message: String,\n}\n\nimpl Panic {\n    pub fn new(span: Span, message: String) -> Panic {\n        Panic {\n            loc_trace: span.into(),\n            message,\n        }\n    }\n}\n\n#[derive(Debug, PartialEq)]\npub enum Error {\n    Panic(Panic),\n    /// Internal error used to abort a recursive function application when a loop is detected\n    AbortRecursion(ApplyCookie),\n    /// Internal error indicating that a divergent value was encountered\n    Diverged,\n}\n\npub type Result<T> = result::Result<T, Error>;\n\nimpl Error {\n    pub fn with_macro_invocation_span(self, span: Span) -> Error {\n        match self {\n            Error::Panic(Panic { loc_trace, message }) => Error::Panic(Panic {\n                loc_trace: loc_trace.with_macro_invocation(span),\n                message,\n            }),\n            other => other,\n        }\n    }\n}\n\nimpl From<Error> for Diagnostic<FileId> {\n    fn from(error: Error) -> Self {\n        if let Error::Panic(panic) = error {\n            let diagnostic = Diagnostic::error()\n                .with_message(panic.message)\n                .with_labels(vec![new_primary_label(\n                    panic.loc_trace.origin(),\n                    \"panicked here\",\n                )]);\n\n            return panic.loc_trace.label_macro_invocation(diagnostic);\n        }\n\n        panic!(\n            \"attempted to convert an internal {:?} flow control error to a diagnostic\",\n            error\n        );\n    }\n}\n\nimpl From<Error> for Vec<Diagnostic<FileId>> {\n    fn from(error: Error) -> Self {\n        vec![error.into()]\n    }\n}\n\nimpl error::Error for Panic {}\n\nimpl fmt::Display for Panic {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(&self.message)\n    }\n}\n"
  },
  {
    "path": "compiler/mir/eval_hir.rs",
    "content": "use std::collections::HashMap;\nuse std::sync::Arc;\nuse std::{alloc, ffi, panic};\n\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::callback::EntryPointAbiType as CallbackEntryPointAbiType;\nuse arret_runtime::intern::{AsInterner, Interner};\n\nuse arret_runtime::abitype;\nuse arret_runtime_syntax::reader;\nuse arret_syntax::datum::{DataStr, Datum};\nuse arret_syntax::span::Span;\n\nuse crate::codegen;\nuse crate::context::ModuleId;\nuse crate::hir;\nuse crate::mir::builder::{Builder, BuiltReg, TryToBuilder};\nuse crate::mir::error::{Error, Result};\nuse crate::mir::inliner;\nuse crate::mir::ops;\nuse crate::mir::polymorph::PolymorphAbi;\nuse crate::mir::value;\nuse crate::mir::value::synthetic_fun::SyntheticFuns;\nuse crate::mir::value::types::TypeHint;\nuse crate::mir::{Expr, Value};\nuse crate::rfi;\nuse crate::source::EMPTY_SPAN;\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\nuse crate::ty::record;\nuse crate::ty::ty_args::TyArgs;\nuse crate::ty::Ty;\n\n#[derive(PartialEq, Eq, Hash)]\nstruct RustFunKey {\n    symbol: &'static str,\n    polymorph_abi: PolymorphAbi,\n}\n\n#[derive(PartialEq, Eq, Hash)]\nstruct ArretFunKey {\n    arret_fun_id: value::ArretFunId,\n    polymorph_abi: PolymorphAbi,\n}\n\n#[derive(PartialEq, Eq)]\npub struct EvaledRecordClass {\n    pub jit_record_class_id: boxed::RecordClassId,\n    pub jit_data_layout: Option<alloc::Layout>,\n    pub record_struct: ops::RecordStructId,\n}\n\npub struct EvalHirCtx {\n    runtime_task: arret_runtime::task::Task,\n    global_values: HashMap<hir::ExportId, Value>,\n\n    private_fun_id_counter: ops::PrivateFunIdCounter,\n    private_funs: HashMap<ops::PrivateFunId, ops::Fun>,\n    rust_funs: HashMap<RustFunKey, ops::PrivateFunId>,\n    arret_funs: HashMap<ArretFunKey, ops::PrivateFunId>,\n    synthetic_funs: SyntheticFuns,\n\n    rust_fun_thunks: HashMap<usize, boxed::ThunkEntry>,\n    arret_fun_thunks: HashMap<value::ArretFunId, boxed::ThunkEntry>,\n\n    // This uses pointers because `FunThunk` is always inequal to itself\n    thunk_fun_values: HashMap<*const boxed::FunThunk, Value>,\n    thunk_jit: codegen::jit::JitCtx,\n\n    pub(super) record_class_for_cons: HashMap<record::ConsId, EvaledRecordClass>,\n    cons_for_jit_record_class_id: HashMap<boxed::RecordClassId, record::ConsId>,\n}\n\n/// Context for performing a tail call in `(recur)`\nstruct TailCallCtx {\n    self_abi: PolymorphAbi,\n    captures_reg: Option<BuiltReg>,\n}\n\nstruct RecurSelf<'af> {\n    arret_fun: &'af value::ArretFun,\n\n    /// Return ABI type of expected by tail calls, if they're allowed\n    tail_call_ctx: Option<TailCallCtx>,\n}\n\npub struct FunCtx<'rs> {\n    /// Optional module to find local variables in\n    ///\n    /// If this isn't specified the function cannot refer to other top-level definitions in the\n    /// same module.\n    module_id: Option<ModuleId>,\n\n    mono_ty_args: TyArgs<ty::Mono>,\n    local_values: HashMap<hir::LocalId, Value>,\n    recur_self: Option<Box<RecurSelf<'rs>>>,\n\n    pub(super) inliner_stack: inliner::ApplyStack,\n}\n\nimpl<'sv> FunCtx<'sv> {\n    pub fn new(module_id: Option<ModuleId>) -> FunCtx<'static> {\n        FunCtx {\n            module_id,\n\n            mono_ty_args: TyArgs::empty(),\n            local_values: HashMap::new(),\n            recur_self: None,\n\n            inliner_stack: inliner::ApplyStack::new(),\n        }\n    }\n\n    pub fn monomorphise(&self, poly: &ty::Ref<ty::Poly>) -> ty::Ref<ty::Mono> {\n        ty::subst::monomorphise(&self.mono_ty_args, poly)\n    }\n}\n\nstruct BuiltCondBranch {\n    b: Builder,\n    result: Result<Value>,\n}\n\npub struct BuiltProgram {\n    pub main: ops::Fun,\n    pub private_funs: HashMap<ops::PrivateFunId, ops::Fun>,\n}\n\nimpl BuiltProgram {\n    /// Returns true if the program always executes successfully with no output or side effects\n    pub fn is_empty(&self) -> bool {\n        matches!(\n            self.main.ops.as_ref(),\n            [ops::Op {\n                kind: ops::OpKind::RetVoid,\n                ..\n            }]\n        )\n    }\n}\n\n#[derive(Clone)]\npub(super) struct ApplyArgs<'tyargs> {\n    ty_args: &'tyargs TyArgs<ty::Poly>,\n    pub(super) list_value: Value,\n}\n\nfn merge_apply_purity_into_scope(\n    scope: &HashMap<purity::PVarId, purity::Ref>,\n    apply_purities: &HashMap<purity::PVarId, purity::Ref>,\n    subst_with: &TyArgs<ty::Mono>,\n) -> HashMap<purity::PVarId, purity::Ref> {\n    use crate::ty::subst;\n\n    scope\n        .iter()\n        .map(|(pvar, v)| (pvar.clone(), v.clone()))\n        .chain(apply_purities.iter().map(|(pvar, poly_purity)| {\n            let subst_purity = subst::monomorphise_purity(subst_with, poly_purity);\n            (pvar.clone(), subst_purity)\n        }))\n        .collect()\n}\n\n/// Merge poly type args in to existing mono type args\n///\n/// This is used when applying a polymorphic function. The `subst_with` are used to monomorphise\n/// the `apply_ty_args` which are then added to the existing `scope` and returned.\nfn merge_apply_ty_args_into_scope(\n    scope: &TyArgs<ty::Mono>,\n    apply_ty_args: &TyArgs<ty::Poly>,\n    subst_with: &TyArgs<ty::Mono>,\n) -> TyArgs<ty::Mono> {\n    use crate::ty::subst;\n\n    let pvar_purities = merge_apply_purity_into_scope(\n        scope.pvar_purities(),\n        apply_ty_args.pvar_purities(),\n        subst_with,\n    );\n\n    let tvar_types = scope\n        .tvar_types()\n        .iter()\n        .map(|(tvar, mono)| (tvar.clone(), mono.clone()))\n        .chain(apply_ty_args.tvar_types().iter().map(|(tvar, poly_type)| {\n            let mono_ty = subst::monomorphise(subst_with, poly_type);\n            (tvar.clone(), mono_ty)\n        }))\n        .collect();\n\n    TyArgs::new(pvar_purities, tvar_types)\n}\n\nimpl EvalHirCtx {\n    pub fn new(optimising: bool) -> EvalHirCtx {\n        let thunk_jit = codegen::jit::JitCtx::new(optimising);\n\n        EvalHirCtx {\n            runtime_task: arret_runtime::task::Task::new(),\n            global_values: HashMap::new(),\n\n            private_fun_id_counter: ops::PrivateFunIdCounter::new(),\n            private_funs: HashMap::new(),\n            rust_funs: HashMap::new(),\n            arret_funs: HashMap::new(),\n            synthetic_funs: SyntheticFuns::new(),\n\n            rust_fun_thunks: HashMap::new(),\n            arret_fun_thunks: HashMap::new(),\n\n            thunk_fun_values: HashMap::new(),\n            thunk_jit,\n\n            record_class_for_cons: HashMap::new(),\n            cons_for_jit_record_class_id: HashMap::new(),\n        }\n    }\n\n    fn destruc_scalar<F>(\n        scalar: &hir::destruc::Scalar<hir::Inferred>,\n        value: Value,\n        insert_local: &mut F,\n    ) where\n        F: FnMut(hir::LocalId, Value),\n    {\n        if let Some(local_id) = scalar.local_id() {\n            insert_local(*local_id, value);\n        }\n    }\n\n    fn destruc_list<F>(\n        b: &mut Option<Builder>,\n        span: Span,\n        list: &hir::destruc::List<hir::Inferred>,\n        value: Value,\n        insert_local: &mut F,\n    ) where\n        F: FnMut(hir::LocalId, Value),\n    {\n        let mut iter = value.into_unsized_list_iter();\n\n        for fixed_destruc in list.fixed() {\n            let value = iter.next_unchecked(b, span);\n            Self::destruc_value(b, fixed_destruc, value, insert_local);\n        }\n\n        if let Some(rest_destruc) = list.rest() {\n            Self::destruc_scalar(rest_destruc, iter.into_rest(), insert_local)\n        }\n    }\n\n    fn destruc_value<F>(\n        b: &mut Option<Builder>,\n        destruc: &hir::destruc::Destruc<hir::Inferred>,\n        value: Value,\n        insert_local: &mut F,\n    ) where\n        F: FnMut(hir::LocalId, Value),\n    {\n        use crate::hir::destruc::Destruc;\n\n        match destruc {\n            Destruc::Scalar(_, scalar) => Self::destruc_scalar(scalar, value, insert_local),\n            Destruc::List(span, list) => Self::destruc_list(b, *span, list, value, insert_local),\n        }\n    }\n\n    fn destruc_source_name(destruc: &hir::destruc::Destruc<hir::Inferred>) -> Option<&DataStr> {\n        use crate::hir::destruc::Destruc;\n\n        match destruc {\n            Destruc::Scalar(_, scalar) => Some(scalar.source_name()),\n            Destruc::List(_, _) => None,\n        }\n    }\n\n    fn eval_local_ref(&self, fcx: &FunCtx<'_>, local_id: hir::LocalId) -> Value {\n        // Try local values\n        if let Some(local_value) = fcx.local_values.get(&local_id) {\n            return local_value.clone();\n        }\n\n        let module_id = fcx\n            .module_id\n            .expect(\"could not fall back to global for missing local\");\n\n        // If this is a top-level def from the same module it will be a global\n        fcx.local_values\n            .get(&local_id)\n            .unwrap_or_else(|| &self.global_values[&hir::ExportId::new(module_id, local_id)])\n            .clone()\n    }\n\n    fn eval_do(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        exprs: &[Expr],\n    ) -> Result<Value> {\n        let initial_value = Value::List(Box::new([]), None);\n\n        exprs\n            .iter()\n            .try_fold(initial_value, |_, expr| self.eval_expr(fcx, b, expr))\n    }\n\n    fn eval_let(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        hir_let: &hir::Let<hir::Inferred>,\n    ) -> Result<Value> {\n        let source_name = Self::destruc_source_name(&hir_let.destruc);\n        let value = self.eval_expr_with_source_name(fcx, b, &hir_let.value_expr, source_name)?;\n\n        Self::destruc_value(b, &hir_let.destruc, value, &mut |local_id, value| {\n            fcx.local_values.insert(local_id, value);\n        });\n\n        self.eval_expr(fcx, b, &hir_let.body_expr)\n    }\n\n    fn eval_lit(&mut self, literal: &Datum) -> Value {\n        reader::box_syntax_datum(self, literal).into()\n    }\n\n    pub(super) fn synthetic_funs(&mut self) -> &mut SyntheticFuns {\n        &mut self.synthetic_funs\n    }\n\n    pub(super) fn build_arret_fun_app(\n        &mut self,\n        fcx: &FunCtx<'_>,\n        b: &mut Builder,\n        span: Span,\n        ret_ty: &ty::Ref<ty::Mono>,\n        arret_fun: &value::ArretFun,\n        apply_args: &ApplyArgs<'_>,\n    ) -> Result<Value> {\n        use crate::hir::destruc::poly_for_list_destruc;\n        use crate::mir::app_purity::fun_app_purity;\n        use crate::mir::arg_list::build_save_arg_list_to_regs;\n        use crate::mir::env_values;\n        use crate::mir::ops::*;\n        use crate::mir::polymorph::polymorph_abi_for_list_ty;\n        use crate::mir::ret_value::ret_reg_to_value;\n        use crate::ty::subst;\n\n        let ApplyArgs {\n            list_value: arg_list_value,\n            ty_args: apply_ty_args,\n        } = apply_args;\n\n        let mono_ty_args = merge_apply_ty_args_into_scope(\n            arret_fun.env_ty_args(),\n            apply_ty_args,\n            &fcx.mono_ty_args,\n        );\n\n        let captures_reg = env_values::save_to_captures_reg(self, b, span, arret_fun.env_values());\n\n        let param_list_poly = poly_for_list_destruc(&arret_fun.fun_expr().params);\n        let param_list_mono = subst::monomorphise_list(&mono_ty_args, &param_list_poly);\n\n        let wanted_abi =\n            polymorph_abi_for_list_ty(captures_reg.is_some(), &param_list_mono, ret_ty);\n        let ret_abi = wanted_abi.ret.clone();\n\n        let mut arg_regs: Vec<RegId> = vec![];\n        if let Some(captures_reg) = captures_reg {\n            arg_regs.push(captures_reg.into());\n        }\n\n        arg_regs.extend(build_save_arg_list_to_regs(\n            self,\n            b,\n            span,\n            arg_list_value.clone(),\n            wanted_abi.fixed_params.iter(),\n            wanted_abi.rest_param.as_ref(),\n        ));\n\n        let private_fun_id = self.id_for_arret_fun(arret_fun, wanted_abi);\n        let fun_expr = arret_fun.fun_expr();\n\n        let app_purity = fun_app_purity(\n            mono_ty_args.pvar_purities(),\n            &fun_expr.purity,\n            &fun_expr.ret_ty,\n        );\n\n        let ret_reg = b.push_reg(\n            span,\n            OpKind::Call,\n            CallOp {\n                callee: Callee::PrivateFun(private_fun_id),\n                impure: app_purity == Purity::Impure,\n                args: arg_regs.into_boxed_slice(),\n            },\n        );\n\n        ret_reg_to_value(ret_reg, ret_abi)\n    }\n\n    pub(super) fn inline_arret_fun_app(\n        &mut self,\n        outer_fcx: &FunCtx<'_>,\n        b: &mut Option<Builder>,\n        span: Span,\n        arret_fun: &value::ArretFun,\n        apply_args: ApplyArgs<'_>,\n        inliner_stack: inliner::ApplyStack,\n    ) -> Result<Value> {\n        let fun_expr = arret_fun.fun_expr();\n\n        let mut inner_fcx = FunCtx {\n            module_id: arret_fun.module_id(),\n            mono_ty_args: merge_apply_ty_args_into_scope(\n                arret_fun.env_ty_args(),\n                apply_args.ty_args,\n                &outer_fcx.mono_ty_args,\n            ),\n            local_values: outer_fcx.local_values.clone(),\n            recur_self: Some(Box::new(RecurSelf {\n                arret_fun,\n                tail_call_ctx: None,\n            })),\n\n            inliner_stack,\n        };\n\n        Self::destruc_list(\n            b,\n            span,\n            &fun_expr.params,\n            apply_args.list_value,\n            &mut |local_id, value| {\n                inner_fcx.local_values.insert(local_id, value);\n            },\n        );\n\n        self.eval_expr(&mut inner_fcx, b, &fun_expr.body_expr)\n    }\n\n    fn eval_arret_fun_app(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        span: Span,\n        ret_ty: &ty::Ref<ty::Mono>,\n        arret_fun: &value::ArretFun,\n        apply_args: ApplyArgs<'_>,\n    ) -> Result<Value> {\n        if arret_fun.has_multiple_usages() {\n            if let Some(outer_b) = b {\n                return inliner::cond_inline(\n                    self, fcx, outer_b, span, ret_ty, arret_fun, apply_args,\n                );\n            }\n        }\n\n        // Always inline\n        self.inline_arret_fun_app(\n            fcx,\n            b,\n            span,\n            arret_fun,\n            apply_args,\n            inliner::ApplyStack::new(),\n        )\n    }\n\n    fn eval_ty_pred_app(\n        &mut self,\n        b: &mut Option<Builder>,\n        span: Span,\n        arg_list_value: &Value,\n        test_ty: &ty::pred::TestTy,\n    ) -> Value {\n        use crate::mir::typred::eval_ty_pred;\n\n        let subject_value = arg_list_value.unsized_list_iter().next_unchecked(b, span);\n        eval_ty_pred(self, b, span, &subject_value, test_ty)\n    }\n\n    fn eval_eq_pred_app(\n        &mut self,\n        b: &mut impl TryToBuilder,\n        span: Span,\n        arg_list_value: &Value,\n    ) -> Value {\n        use crate::mir::equality::eval_equality;\n\n        let mut iter = arg_list_value.unsized_list_iter();\n\n        let left_value = iter.next_unchecked(b, span);\n        let right_value = iter.next_unchecked(b, span);\n\n        eval_equality(self, b, span, &left_value, &right_value).into()\n    }\n\n    fn eval_record_cons_app(\n        &mut self,\n        b: &mut Option<Builder>,\n        span: Span,\n        record_cons: &record::ConsId,\n        arg_list_value: &Value,\n    ) -> Value {\n        let mut iter = arg_list_value.unsized_list_iter();\n\n        let field_values = record_cons\n            .fields()\n            .iter()\n            .map(|_| iter.next_unchecked(b, span))\n            .collect();\n\n        Value::Record(record_cons.clone(), field_values)\n    }\n\n    fn eval_field_accessor_app(\n        &mut self,\n        b: &mut Option<Builder>,\n        span: Span,\n        record_cons: &record::ConsId,\n        field_index: usize,\n        arg_list_value: &Value,\n    ) -> Value {\n        use crate::mir::record_field::load_record_field;\n\n        let mut iter = arg_list_value.unsized_list_iter();\n        let record_value = iter.next_unchecked(b, span);\n\n        load_record_field(self, b, span, record_cons, &record_value, field_index)\n    }\n\n    pub fn rust_fun_to_jit_boxed(&mut self, rust_fun: Arc<rfi::Fun>) -> Gc<boxed::FunThunk> {\n        let captures = boxed::NIL_INSTANCE.as_any_ref();\n        let entry = self.jit_thunk_for_rust_fun(&rust_fun);\n        let new_boxed = boxed::FunThunk::new(self, captures, entry);\n\n        self.thunk_fun_values\n            .insert(new_boxed.as_ptr(), Value::RustFun(rust_fun));\n\n        new_boxed\n    }\n\n    fn jit_thunk_for_rust_fun(&mut self, rust_fun: &rfi::Fun) -> boxed::ThunkEntry {\n        // Create a dynamic thunk to this Rust function if it doesn't exist\n        if let Some(thunk) = self.rust_fun_thunks.get(&rust_fun.entry_point()) {\n            return *thunk;\n        }\n\n        let thunk = unsafe {\n            use crate::mir::rust_fun::ops_for_rust_fun;\n            use std::mem;\n\n            let wanted_abi = PolymorphAbi::thunk_abi();\n            let ops_fun = ops_for_rust_fun(self, rust_fun, wanted_abi);\n            let address = self.thunk_jit.compile_fun(\n                &self.private_funs,\n                self.runtime_task.heap_mut().type_info_mut().interner_mut(),\n                &ops_fun,\n            );\n\n            mem::transmute(address as usize)\n        };\n\n        self.rust_fun_thunks.insert(rust_fun.entry_point(), thunk);\n        thunk\n    }\n\n    /// Ensures the passed `RustFun` is known by the JIT\n    ///\n    /// This can be called multiple times with the same Rust fun. Calling it with a fun that's\n    /// never used by the JIT is harmless\n    pub fn register_rust_fun_with_jit(&mut self, rust_fun: &rfi::Fun) {\n        let symbol_cstring = ffi::CString::new(rust_fun.symbol()).unwrap();\n\n        // Add the inner symbol\n        self.thunk_jit.add_symbol(\n            symbol_cstring.as_bytes_with_nul(),\n            rust_fun.entry_point() as u64,\n        );\n    }\n\n    /// Returns a private fun ID for the wanted Rust fun and ABI\n    ///\n    /// This will return a cached ID if available\n    fn id_for_rust_fun(\n        &mut self,\n        rust_fun: &rfi::Fun,\n        wanted_abi: PolymorphAbi,\n    ) -> ops::PrivateFunId {\n        use crate::mir::rust_fun::ops_for_rust_fun;\n\n        let rust_fun_key = RustFunKey {\n            symbol: rust_fun.symbol(),\n            polymorph_abi: wanted_abi.clone(),\n        };\n\n        if let Some(private_fun_id) = self.rust_funs.get(&rust_fun_key) {\n            return *private_fun_id;\n        }\n\n        let private_fun_id = self.private_fun_id_counter.alloc();\n        self.rust_funs.insert(rust_fun_key, private_fun_id);\n\n        let ops_fun = ops_for_rust_fun(self, rust_fun, wanted_abi);\n        self.private_funs.insert(private_fun_id, ops_fun);\n\n        private_fun_id\n    }\n\n    pub fn rust_fun_to_thunk_reg(\n        &mut self,\n        b: &mut Builder,\n        span: Span,\n        rust_fun: &rfi::Fun,\n    ) -> BuiltReg {\n        use crate::mir::ops::*;\n\n        let wanted_abi = PolymorphAbi::thunk_abi();\n        let private_fun_id = self.id_for_rust_fun(rust_fun, wanted_abi);\n\n        let nil_reg = b.push_reg(span, OpKind::ConstBoxedNil, ());\n        let captures_reg = b.cast_boxed(span, nil_reg, abitype::BoxedAbiType::Any);\n\n        b.push_reg(\n            span,\n            OpKind::ConstBoxedFunThunk,\n            BoxFunThunkOp {\n                captures_reg: captures_reg.into(),\n                callee: ops::Callee::PrivateFun(private_fun_id),\n            },\n        )\n    }\n\n    pub fn rust_fun_to_callback_reg(\n        &mut self,\n        b: &mut Builder,\n        span: Span,\n        rust_fun: &rfi::Fun,\n        entry_point_abi: &CallbackEntryPointAbiType,\n    ) -> BuiltReg {\n        use crate::mir::ops::*;\n\n        let wanted_abi = entry_point_abi.clone().into();\n        let private_fun_id = self.id_for_rust_fun(rust_fun, wanted_abi);\n\n        let nil_reg = b.push_reg(span, OpKind::ConstBoxedNil, ());\n        let captures_reg = b.cast_boxed(span, nil_reg, abitype::BoxedAbiType::Any);\n\n        b.push_reg(\n            span,\n            OpKind::MakeCallback,\n            MakeCallbackOp {\n                captures_reg: captures_reg.into(),\n                callee: ops::Callee::PrivateFun(private_fun_id),\n            },\n        )\n    }\n\n    fn call_native_fun<F>(span: Span, block: F) -> Result<Value>\n    where\n        F: FnOnce() -> Gc<boxed::Any>,\n    {\n        // By convention convert string panics in to our `ErrorKind::Panic`\n        panic::catch_unwind(panic::AssertUnwindSafe(block))\n            .map(Value::Const)\n            .map_err(|err| {\n                use crate::mir::error;\n\n                let message = if let Some(message) = err.downcast_ref::<String>() {\n                    message.clone()\n                } else {\n                    \"Unexpected panic type\".to_owned()\n                };\n\n                Error::Panic(error::Panic::new(span, message))\n            })\n    }\n\n    fn eval_rust_fun_app(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        span: Span,\n        ret_ty: &ty::Ref<ty::Mono>,\n        rust_fun: &rfi::Fun,\n        apply_args: ApplyArgs<'_>,\n    ) -> Result<Value> {\n        use crate::mir::app_purity::fun_app_purity;\n        use crate::mir::intrinsic;\n        use crate::mir::rust_fun::build_rust_fun_app;\n        use crate::mir::value::to_const::value_to_const;\n\n        let ApplyArgs {\n            list_value: arg_list_value,\n            ty_args: apply_ty_args,\n        } = apply_args;\n\n        let arret_fun_type = rust_fun.arret_fun_type();\n\n        let mono_purities = merge_apply_purity_into_scope(\n            &HashMap::new(),\n            apply_ty_args.pvar_purities(),\n            &fcx.mono_ty_args,\n        );\n\n        let call_purity = fun_app_purity(\n            &mono_purities,\n            arret_fun_type.purity(),\n            arret_fun_type.ret(),\n        );\n\n        let can_const_eval = b.is_none() || (call_purity == Purity::Pure);\n\n        if let Some(intrinsic_name) = rust_fun.intrinsic_name() {\n            // Attempt specialised evaluation\n            if let Some(value) =\n                intrinsic::try_eval(self, b, span, intrinsic_name, &arg_list_value)?\n            {\n                return Ok(value);\n            }\n        }\n\n        if can_const_eval {\n            let boxed_arg_list = value_to_const(self, &arg_list_value);\n\n            if let Some(boxed_arg_list) = boxed_arg_list {\n                let thunk = self.jit_thunk_for_rust_fun(rust_fun);\n\n                let runtime_task = &mut self.runtime_task;\n\n                let native_result = Self::call_native_fun(span, || {\n                    let captures = boxed::NIL_INSTANCE.as_any_ref();\n                    thunk(runtime_task, captures, boxed_arg_list)\n                });\n\n                // If we receive a panic while building we want to still build the function call.\n                // This `panic` could be in conditional code and we want to build all the\n                // expressions before this panic for their side effects.\n                if native_result.is_ok() || !b.is_some() {\n                    return native_result;\n                }\n            }\n        }\n\n        if let Some(b) = b {\n            let arg_list_value = if let Some(intrinsic_name) = rust_fun.intrinsic_name() {\n                match intrinsic::try_build(self, b, span, intrinsic_name, &arg_list_value)? {\n                    intrinsic::BuildOutcome::None => arg_list_value,\n                    intrinsic::BuildOutcome::SimplifiedArgs(simplified_arg_list_value) => {\n                        simplified_arg_list_value\n                    }\n                    intrinsic::BuildOutcome::ReturnValue(return_value) => {\n                        return Ok(return_value);\n                    }\n                }\n            } else {\n                arg_list_value\n            };\n\n            build_rust_fun_app(self, b, span, ret_ty, rust_fun, call_purity, arg_list_value)\n        } else {\n            panic!(\"Need builder for non-const function application\");\n        }\n    }\n\n    fn eval_const_fun_thunk_app(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        span: Span,\n        ret_ty: &ty::Ref<ty::Mono>,\n        fun_thunk: Gc<boxed::FunThunk>,\n        apply_args: ApplyArgs<'_>,\n    ) -> Result<Value> {\n        use crate::mir::value::to_const::value_to_const;\n\n        if let Some(actual_value) = self.thunk_fun_values.get(&fun_thunk.as_ptr()) {\n            let actual_value = actual_value.clone();\n            return self.eval_value_app(fcx, b, span, ret_ty, &actual_value, apply_args);\n        }\n\n        if b.is_some() {\n            panic!(\"attempt to apply unknown fun thunk during compile phase\");\n        }\n\n        let const_arg_list =\n            value_to_const(self, &apply_args.list_value).expect(\"non-constant value during apply\");\n\n        Self::call_native_fun(span, || {\n            fun_thunk.apply(&mut self.runtime_task, const_arg_list)\n        })\n    }\n\n    fn build_reg_fun_thunk_app(\n        &mut self,\n        b: &mut Builder,\n        span: Span,\n        fun_reg_value: &value::RegValue,\n        arg_list_value: &Value,\n    ) -> Value {\n        use crate::mir::ops::*;\n        use crate::mir::value::build_reg::value_to_reg;\n\n        let fun_boxed_abi_type =\n            if let abitype::AbiType::Boxed(ref fun_boxed_abi_type) = fun_reg_value.abi_type {\n                fun_boxed_abi_type\n            } else {\n                panic!(\n                    \"Attempted to apply reg value with unboxed ABI type of {:?}\",\n                    fun_reg_value.abi_type\n                )\n            };\n\n        let fun_thunk_reg = b.cast_boxed_cond(\n            span,\n            fun_boxed_abi_type,\n            fun_reg_value.reg,\n            boxed::TypeTag::FunThunk.into(),\n        );\n\n        let captures_reg = b.push_reg(\n            span,\n            OpKind::LoadBoxedFunThunkCaptures,\n            fun_thunk_reg.into(),\n        );\n        let arg_list_reg = value_to_reg(\n            self,\n            b,\n            span,\n            arg_list_value,\n            &abitype::TOP_LIST_BOXED_ABI_TYPE.into(),\n        );\n\n        let ret_reg = b.push_reg(\n            span,\n            OpKind::Call,\n            CallOp {\n                callee: Callee::BoxedFunThunk(fun_thunk_reg.into()),\n                impure: true,\n                args: Box::new([captures_reg.into(), arg_list_reg.into()]),\n            },\n        );\n\n        value::RegValue::new(ret_reg, abitype::BoxedAbiType::Any.into()).into()\n    }\n\n    fn eval_value_app(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        span: Span,\n        ret_ty: &ty::Ref<ty::Mono>,\n        fun_value: &Value,\n        apply_args: ApplyArgs<'_>,\n    ) -> Result<Value> {\n        match fun_value {\n            Value::ArretFun(arret_fun) => {\n                use crate::mir::env_values;\n\n                env_values::load_from_current_fun(&mut fcx.local_values, arret_fun.env_values());\n                self.eval_arret_fun_app(fcx, b, span, ret_ty, arret_fun, apply_args)\n            }\n            Value::RustFun(rust_fun) => {\n                self.eval_rust_fun_app(fcx, b, span, ret_ty, rust_fun, apply_args)\n            }\n            Value::TyPred(test_ty) => {\n                Ok(self.eval_ty_pred_app(b, span, &apply_args.list_value, test_ty))\n            }\n            Value::EqPred => Ok(self.eval_eq_pred_app(b, span, &apply_args.list_value)),\n            Value::RecordCons(record_cons) => {\n                Ok(self.eval_record_cons_app(b, span, record_cons, &apply_args.list_value))\n            }\n            Value::FieldAccessor(record_cons, field_index) => Ok(self.eval_field_accessor_app(\n                b,\n                span,\n                record_cons,\n                *field_index,\n                &apply_args.list_value,\n            )),\n            Value::Const(boxed_fun) => {\n                let fun_thunk = boxed_fun\n                    .downcast_ref::<boxed::FunThunk>()\n                    .expect(\"applying non-function box\");\n\n                self.eval_const_fun_thunk_app(fcx, b, span, ret_ty, fun_thunk, apply_args)\n            }\n            Value::Reg(reg_value) => {\n                if let Some(b) = b {\n                    Ok(self.build_reg_fun_thunk_app(b, span, reg_value, &apply_args.list_value))\n                } else {\n                    panic!(\"Need builder for reg function application\");\n                }\n            }\n            other => {\n                unimplemented!(\"applying function value type: {:?}\", other);\n            }\n        }\n    }\n\n    fn eval_app(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        result_ty: &ty::Ref<ty::Poly>,\n        app: &hir::App<hir::Inferred>,\n    ) -> Result<Value> {\n        let fun_value = self.eval_expr(fcx, b, &app.fun_expr)?;\n\n        let fixed_values = app\n            .fixed_arg_exprs\n            .iter()\n            .map(|arg| self.eval_expr(fcx, b, arg))\n            .collect::<Result<Box<[Value]>>>()?;\n\n        let rest_value = match &app.rest_arg_expr {\n            Some(rest_arg) => Some(Box::new(self.eval_expr(fcx, b, rest_arg)?)),\n            None => None,\n        };\n\n        let ret_ty = fcx.monomorphise(result_ty);\n        let arg_list_value = Value::List(fixed_values, rest_value);\n        self.eval_value_app(\n            fcx,\n            b,\n            app.span,\n            &ret_ty,\n            &fun_value,\n            ApplyArgs {\n                ty_args: &app.ty_args,\n                list_value: arg_list_value,\n            },\n        )\n    }\n\n    /// Evaluates a `(recur)` within a fun body\n    ///\n    /// While `(recur)` is semantically equivalent to calling the fun by name it has a different\n    /// implication about programmer intent. `(recur)` is used in positions where unbounded tail\n    /// recursion can occur, typically to iteratively process a data structure. This is used where\n    /// a loop would be used in other languages.\n    ///\n    /// For this reason they are evaluated quite differently from normal applies. Four things are\n    /// tried in order of preference:\n    ///\n    /// 1. If we don't have a builder we will JIT a thunk and call into it. This is important\n    ///    because our MIR evaluation is not tail recursive; we could exhaust our Rust stack if\n    ///    we attempted to MIR evaluate until the end of recursion.\n    ///\n    /// 2. If the arg list is constant and the apply is pure then we will also evaluate through a\n    ///    thunk. This is the same way Rust funs are treated.\n    ///\n    /// 3. If we have a `tail_call_ctx` we will built a special `TailCall` op and immediately\n    ///    return its value. This maximises the chance that codegen and LLVM will be able to\n    ///    optimise the tail call in to a loop. We're also able to reuse our existing captures arg\n    ///    directly.\n    ///\n    /// 4. If we don't have a `tail_call_ctx` we will treat this as if it was an Arret fun apply.\n    ///\n    ///    This can happen if we're being inlined or we're inside a thunk or callback. This will\n    ///    only happen for one recursion; the next one will have a `tail_call_ctx` and use one of\n    ///    the above cases.\n    ///\n    ///    We directly build a fun app instead of attempting inlining. Inlining a recursion with a\n    ///    non-constant arg list will nearly always hit the maximum inline depth and abort. This is\n    ///    wasteful of compiler time.\n    fn eval_recur(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        result_ty: &ty::Ref<ty::Poly>,\n        recur: &hir::Recur<hir::Inferred>,\n    ) -> Result<Value> {\n        use crate::mir::app_purity::fun_app_purity;\n\n        let span = recur.span;\n\n        let fixed_values = recur\n            .fixed_arg_exprs\n            .iter()\n            .map(|arg| self.eval_expr(fcx, b, arg))\n            .collect::<Result<Box<[Value]>>>()?;\n\n        let rest_value = match &recur.rest_arg_expr {\n            Some(rest_arg) => Some(Box::new(self.eval_expr(fcx, b, rest_arg)?)),\n            None => None,\n        };\n\n        let (arret_fun, tail_call_ctx) = if let Some(recur_self) = &fcx.recur_self {\n            (&recur_self.arret_fun, &recur_self.tail_call_ctx)\n        } else {\n            panic!(\"`(recur)` outside function\");\n        };\n\n        let ret_ty = fcx.monomorphise(result_ty);\n        let arg_list_value = Value::List(fixed_values, rest_value);\n\n        // Determine our purity to see if we can const eval\n        let recur_purity = fun_app_purity(\n            fcx.mono_ty_args.pvar_purities(),\n            &arret_fun.fun_expr().purity,\n            &arret_fun.fun_expr().ret_ty,\n        );\n\n        // If we're impure or we have dynamic environment values we need to be evaluated at runtime\n        let can_const_eval = b.is_none()\n            || (recur_purity == Purity::Pure && arret_fun.env_values().free_values.is_empty());\n\n        if can_const_eval {\n            use crate::mir::value::to_const::value_to_const;\n\n            if let Some(boxed_arg_list) = value_to_const(self, &arg_list_value) {\n                let thunk = self.jit_thunk_for_arret_fun(arret_fun);\n                return Self::call_native_fun(span, || {\n                    let captures = boxed::NIL_INSTANCE.as_any_ref();\n                    thunk(&mut self.runtime_task, captures, boxed_arg_list)\n                });\n            }\n        }\n\n        let some_b = if let Some(some_b) = b {\n            some_b\n        } else {\n            panic!(\"failed to const eval (recur) during eval\");\n        };\n\n        if let Some(tail_call_ctx) = tail_call_ctx {\n            use crate::mir::arg_list::build_save_arg_list_to_regs;\n            use crate::mir::ops::*;\n\n            let self_abi = &tail_call_ctx.self_abi;\n\n            let mut arg_regs: Vec<RegId> = vec![];\n            if let Some(captures_reg) = tail_call_ctx.captures_reg {\n                arg_regs.push(captures_reg.into());\n            }\n\n            arg_regs.extend(build_save_arg_list_to_regs(\n                self,\n                some_b,\n                span,\n                arg_list_value,\n                self_abi.fixed_params.iter(),\n                self_abi.rest_param.as_ref(),\n            ));\n\n            // All of the context for `TailCall` is implicit except the arg regs\n            let ret_reg = some_b.push_reg(\n                span,\n                OpKind::TailCall,\n                TailCallOp {\n                    impure: recur_purity == Purity::Impure,\n                    args: arg_regs.into_boxed_slice(),\n                },\n            );\n\n            match &self_abi.ret {\n                abitype::RetAbiType::Inhabited(_) => {\n                    some_b.push(span, OpKind::Ret(ret_reg.into()));\n                }\n                abitype::RetAbiType::Never => {\n                    some_b.push(span, OpKind::Unreachable);\n                }\n                abitype::RetAbiType::Void => {\n                    some_b.push(span, OpKind::RetVoid);\n                }\n            }\n\n            Err(Error::Diverged)\n        } else {\n            // By definition our ty args are the fun's type args pointed to themselves\n            let pvar_purities = arret_fun\n                .fun_expr()\n                .pvars\n                .iter()\n                .map(|pvar| (pvar.clone(), pvar.clone().into()))\n                .collect();\n\n            let tvar_types = arret_fun\n                .fun_expr()\n                .tvars\n                .iter()\n                .map(|tvar| (tvar.clone(), tvar.clone().into()))\n                .collect();\n\n            let ty_args = TyArgs::new(pvar_purities, tvar_types);\n\n            // We can't do a native tail call. This can happen if we're inside a thunk or callback\n            // since they don't have the `FastCC` calling convention.\n            self.build_arret_fun_app(\n                fcx,\n                some_b,\n                recur.span,\n                &ret_ty,\n                arret_fun,\n                &ApplyArgs {\n                    ty_args: &ty_args,\n                    list_value: arg_list_value,\n                },\n            )\n        }\n    }\n\n    fn eval_cond(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        cond: &hir::Cond<hir::Inferred>,\n    ) -> Result<Value> {\n        let test_value = self.eval_expr(fcx, b, &cond.test_expr)?;\n\n        match test_value {\n            Value::Const(any_ref) => {\n                let bool_ref = any_ref.downcast_ref::<boxed::Bool>().unwrap();\n\n                if bool_ref.as_bool() {\n                    self.eval_expr(fcx, b, &cond.true_expr)\n                } else {\n                    self.eval_expr(fcx, b, &cond.false_expr)\n                }\n            }\n            dynamic_value => {\n                if let Some(b) = b {\n                    self.build_cond(fcx, b, &dynamic_value, cond)\n                } else {\n                    panic!(\"need builder for dynamic cond\");\n                }\n            }\n        }\n    }\n\n    fn build_cond_branch(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        branch_expr: &hir::Expr<hir::Inferred>,\n    ) -> BuiltCondBranch {\n        let b = Builder::new();\n\n        let mut some_b = Some(b);\n        let result = self.eval_expr(fcx, &mut some_b, branch_expr);\n        let b = some_b.unwrap();\n\n        BuiltCondBranch { b, result }\n    }\n\n    fn build_cond(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Builder,\n        test_value: &Value,\n        cond: &hir::Cond<hir::Inferred>,\n    ) -> Result<Value> {\n        use arret_runtime::boxed::TypeTag;\n\n        use crate::mir::equality::values_statically_equal;\n        use crate::mir::ops::{BinaryOp, OpKind};\n        use crate::mir::value::build_reg::value_to_reg;\n        use crate::mir::value::plan_phi::*;\n        use crate::mir::value::types::{possible_type_tags_for_value, type_hint_for_value};\n\n        let span = cond.span;\n        let test_reg = value_to_reg(self, b, span, test_value, &abitype::AbiType::Bool);\n\n        let mut built_true = self.build_cond_branch(fcx, &cond.true_expr);\n        let mut built_false = self.build_cond_branch(fcx, &cond.false_expr);\n\n        let output_value;\n        let reg_phi;\n\n        match (built_true.result, built_false.result) {\n            (Ok(true_value), Ok(false_value)) => {\n                let possible_true_type_tags = possible_type_tags_for_value(&true_value);\n                let possible_false_type_tags = possible_type_tags_for_value(&false_value);\n\n                if values_statically_equal(self, &true_value, &false_value) == Some(true) {\n                    output_value = true_value;\n                    reg_phi = None;\n                } else if possible_true_type_tags == TypeTag::True.into()\n                    && possible_false_type_tags == TypeTag::False.into()\n                {\n                    // Our output value is our test\n                    // Use the unboxed value because LLVM has trouble reasoning about our boxed bools\n                    let reg_value = value::RegValue::new(test_reg, abitype::AbiType::Bool);\n                    output_value = reg_value.into();\n                    reg_phi = None;\n                } else if possible_true_type_tags == TypeTag::False.into()\n                    && possible_false_type_tags == TypeTag::True.into()\n                {\n                    // Our output value is the negation of our test\n                    let const_false_reg = b.push_reg(span, OpKind::ConstBool, false);\n                    let negated_test_reg = b.push_reg(\n                        span,\n                        OpKind::BoolEqual,\n                        BinaryOp {\n                            lhs_reg: test_reg.into(),\n                            rhs_reg: const_false_reg.into(),\n                        },\n                    );\n\n                    let reg_value = value::RegValue::new(negated_test_reg, abitype::AbiType::Bool);\n                    output_value = reg_value.into();\n                    reg_phi = None;\n                } else {\n                    let phi_abi_type = plan_phi_abi_type(&true_value, &false_value);\n\n                    let true_result_reg =\n                        value_to_reg(self, &mut built_true.b, span, &true_value, &phi_abi_type);\n\n                    let false_result_reg =\n                        value_to_reg(self, &mut built_false.b, span, &false_value, &phi_abi_type);\n\n                    let output_reg = b.alloc_local();\n\n                    let possible_type_tags = possible_true_type_tags | possible_false_type_tags;\n\n                    let true_type_hint = type_hint_for_value(self, &true_value);\n                    let false_type_hint = type_hint_for_value(self, &false_value);\n                    let common_type_hint = if true_type_hint == false_type_hint {\n                        true_type_hint\n                    } else {\n                        TypeHint::None\n                    };\n\n                    let reg_value = value::RegValue {\n                        reg: output_reg,\n                        abi_type: phi_abi_type,\n                        possible_type_tags,\n                        type_hint: common_type_hint,\n                    };\n\n                    output_value = reg_value.into();\n\n                    reg_phi = Some(ops::RegPhi {\n                        output_reg: output_reg.into(),\n                        true_result_reg: true_result_reg.into(),\n                        false_result_reg: false_result_reg.into(),\n                    });\n                }\n            }\n            (Ok(true_value), Err(Error::Diverged)) => {\n                output_value = true_value;\n                reg_phi = None;\n            }\n            (Err(Error::Diverged), Ok(false_value)) => {\n                output_value = false_value;\n                reg_phi = None;\n            }\n            (Err(true_error), _) => {\n                return Err(true_error);\n            }\n            (_, Err(false_error)) => {\n                return Err(false_error);\n            }\n        };\n\n        let true_ops = built_true.b.into_ops();\n        let false_ops = built_false.b.into_ops();\n\n        // Avoid adding an empty `Cond` we'd have to optimise away later\n        if reg_phi.is_some() || !true_ops.is_empty() || !false_ops.is_empty() {\n            b.push(\n                span,\n                ops::OpKind::Cond(ops::CondOp {\n                    reg_phi,\n                    test_reg: test_reg.into(),\n                    true_ops,\n                    false_ops,\n                }),\n            );\n        }\n\n        Ok(output_value)\n    }\n\n    fn eval_arret_fun(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        fun_expr: hir::Fun<hir::Inferred>,\n        source_name: Option<&DataStr>,\n    ) -> Value {\n        use crate::mir::env_values;\n\n        let env_values =\n            env_values::calculate_env_values(&fcx.local_values, &fun_expr.body_expr, source_name);\n\n        Value::ArretFun(value::ArretFun::new(\n            fcx.module_id,\n            source_name.cloned(),\n            fcx.mono_ty_args.clone(),\n            env_values,\n            fun_expr,\n        ))\n    }\n\n    pub fn arret_fun_to_jit_boxed(\n        &mut self,\n        arret_fun: &value::ArretFun,\n    ) -> Option<Gc<boxed::FunThunk>> {\n        // If we have non-const (i.e. \"free\") values in our environment we can't be const\n        if !arret_fun.env_values().free_values.is_empty() {\n            return None;\n        }\n\n        let entry = self.jit_thunk_for_arret_fun(arret_fun);\n\n        let captures = boxed::NIL_INSTANCE.as_any_ref();\n        let new_boxed = boxed::FunThunk::new(self, captures, entry);\n\n        let arret_fun_value = Value::ArretFun(arret_fun.clone());\n        self.thunk_fun_values\n            .insert(new_boxed.as_ptr(), arret_fun_value);\n        Some(new_boxed)\n    }\n\n    fn jit_thunk_for_arret_fun(&mut self, arret_fun: &value::ArretFun) -> boxed::ThunkEntry {\n        // Create a dynamic thunk to this Arret function if it doesn't exist\n        if let Some(thunk) = self.arret_fun_thunks.get(&arret_fun.id()) {\n            return *thunk;\n        }\n\n        let thunk = unsafe {\n            use std::mem;\n\n            let wanted_abi = PolymorphAbi::thunk_abi();\n            let ops_fun = self.ops_for_arret_fun(arret_fun, wanted_abi);\n\n            let address = self.thunk_jit.compile_fun(\n                &self.private_funs,\n                self.runtime_task.heap_mut().type_info_mut().interner_mut(),\n                &ops_fun,\n            );\n\n            mem::transmute(address as usize)\n        };\n\n        self.arret_fun_thunks.insert(arret_fun.id(), thunk);\n        thunk\n    }\n\n    /// Returns a private fun ID for the wanted Arret fun and ABI\n    ///\n    /// This will return a cached ID if available\n    fn id_for_arret_fun(\n        &mut self,\n        arret_fun: &value::ArretFun,\n        wanted_abi: PolymorphAbi,\n    ) -> ops::PrivateFunId {\n        let arret_fun_key = ArretFunKey {\n            arret_fun_id: arret_fun.id(),\n            polymorph_abi: wanted_abi.clone(),\n        };\n\n        if let Some(private_fun_id) = self.arret_funs.get(&arret_fun_key) {\n            return *private_fun_id;\n        }\n\n        // Allocate and track our private fun ID before we actually build the fun.\n        // This is to prevent a compile-time loop if this fun ends up recursing.\n        let private_fun_id = self.private_fun_id_counter.alloc();\n        self.arret_funs.insert(arret_fun_key, private_fun_id);\n\n        let ops_fun = self.ops_for_arret_fun(arret_fun, wanted_abi);\n        self.private_funs.insert(private_fun_id, ops_fun);\n\n        private_fun_id\n    }\n\n    pub fn evaled_record_class_for_cons(\n        &mut self,\n        record_cons: &record::ConsId,\n    ) -> &EvaledRecordClass {\n        use crate::mir::specific_abi_type::specific_abi_type_for_ty_ref;\n\n        if self.record_class_for_cons.contains_key(record_cons) {\n            return &self.record_class_for_cons[record_cons];\n        }\n\n        let field_abi_types = record_cons\n            .fields()\n            .iter()\n            .map(|field| specific_abi_type_for_ty_ref(field.ty_ref()))\n            .collect();\n\n        let record_struct =\n            ops::RecordStruct::new(record_cons.ty_cons_name().clone(), field_abi_types);\n        let registered_record_struct = self.thunk_jit.register_record_struct(\n            &record_struct,\n            self.runtime_task.heap_mut().type_info_mut().class_map_mut(),\n        );\n\n        let evaled_record_class = EvaledRecordClass {\n            jit_record_class_id: registered_record_struct.record_class_id,\n            jit_data_layout: registered_record_struct.data_layout,\n            record_struct,\n        };\n\n        self.cons_for_jit_record_class_id.insert(\n            registered_record_struct.record_class_id,\n            record_cons.clone(),\n        );\n\n        self.record_class_for_cons\n            .entry(record_cons.clone())\n            .or_insert(evaled_record_class)\n    }\n\n    pub fn cons_for_jit_record_class_id(\n        &self,\n        record_class_id: boxed::RecordClassId,\n    ) -> Option<&record::ConsId> {\n        self.cons_for_jit_record_class_id.get(&record_class_id)\n    }\n\n    pub fn arret_fun_to_thunk_reg(\n        &mut self,\n        b: &mut Builder,\n        span: Span,\n        arret_fun: &value::ArretFun,\n    ) -> BuiltReg {\n        use crate::mir::env_values;\n        use crate::mir::ops::*;\n\n        let wanted_abi = PolymorphAbi::thunk_abi();\n        let private_fun_id = self.id_for_arret_fun(arret_fun, wanted_abi);\n\n        let captures_reg = env_values::save_to_captures_reg(self, b, span, arret_fun.env_values());\n\n        if let Some(captures_reg) = captures_reg {\n            b.push_reg(\n                span,\n                OpKind::AllocBoxedFunThunk,\n                BoxFunThunkOp {\n                    captures_reg: captures_reg.into(),\n                    callee: ops::Callee::PrivateFun(private_fun_id),\n                },\n            )\n        } else {\n            let nil_reg = b.push_reg(span, OpKind::ConstBoxedNil, ());\n            let outer_captures_reg = b.cast_boxed(span, nil_reg, abitype::BoxedAbiType::Any);\n\n            b.push_reg(\n                span,\n                OpKind::ConstBoxedFunThunk,\n                BoxFunThunkOp {\n                    captures_reg: outer_captures_reg.into(),\n                    callee: ops::Callee::PrivateFun(private_fun_id),\n                },\n            )\n        }\n    }\n\n    pub fn arret_fun_to_callback_reg(\n        &mut self,\n        b: &mut Builder,\n        span: Span,\n        arret_fun: &value::ArretFun,\n        entry_point_abi: &CallbackEntryPointAbiType,\n    ) -> BuiltReg {\n        use crate::mir::env_values;\n        use crate::mir::ops::*;\n\n        let wanted_abi = entry_point_abi.clone().into();\n        let private_fun_id = self.id_for_arret_fun(arret_fun, wanted_abi);\n\n        let captures_reg = env_values::save_to_captures_reg(self, b, span, arret_fun.env_values())\n            .unwrap_or_else(|| {\n                let nil_reg = b.push_reg(span, OpKind::ConstBoxedNil, ());\n                b.cast_boxed(span, nil_reg, abitype::BoxedAbiType::Any)\n            });\n\n        b.push_reg(\n            span,\n            OpKind::MakeCallback,\n            MakeCallbackOp {\n                captures_reg: captures_reg.into(),\n                callee: ops::Callee::PrivateFun(private_fun_id),\n            },\n        )\n    }\n\n    pub(super) fn ops_for_arret_fun(\n        &mut self,\n        arret_fun: &value::ArretFun,\n        wanted_abi: PolymorphAbi,\n    ) -> ops::Fun {\n        use crate::hir::destruc::poly_for_list_destruc;\n        use crate::mir::arg_list::{build_load_arg_list_value, LoadedArgList};\n        use crate::mir::env_values;\n        use crate::mir::optimise::optimise_fun;\n        use crate::mir::ret_value::build_value_ret;\n\n        let mut b = Builder::new();\n        let fun_expr = arret_fun.fun_expr();\n        let span = fun_expr.span;\n\n        let param_list_poly = poly_for_list_destruc(&arret_fun.fun_expr().params);\n        let LoadedArgList {\n            captures_reg,\n            param_regs,\n            arg_list_value,\n        } = build_load_arg_list_value(self, &mut b, &wanted_abi, &param_list_poly);\n\n        // Start by loading the captures\n        let mut local_values: HashMap<hir::LocalId, Value> = HashMap::new();\n        let mut recur_env_values = arret_fun.env_values().clone();\n\n        env_values::load_from_env_param(\n            &mut b,\n            span,\n            &mut local_values,\n            &mut recur_env_values,\n            captures_reg,\n        );\n\n        // Our env values have been updated with its new reg IDs\n        let recur_arret_fun = arret_fun.with_env_values(recur_env_values);\n\n        // Try to refine our polymorphic type variables based on our requested op ABI\n        let mut stx = ty::select::SelectCtx::new(&fun_expr.pvars, &fun_expr.tvars);\n\n        let fun_param_poly = hir::destruc::poly_for_list_destruc(&fun_expr.params);\n        let wanted_abi_poly = wanted_abi.param_ty_ref();\n        stx.add_evidence(&fun_param_poly.into(), &wanted_abi_poly.into());\n\n        let ty_args = stx.into_poly_ty_args();\n\n        let tail_call_ctx = if wanted_abi.call_conv == ops::CallConv::FastCc {\n            Some(TailCallCtx {\n                self_abi: wanted_abi.clone(),\n                captures_reg,\n            })\n        } else {\n            None\n        };\n\n        // Now build a function context\n        let mut fcx = FunCtx {\n            module_id: arret_fun.module_id(),\n            mono_ty_args: merge_apply_ty_args_into_scope(\n                arret_fun.env_ty_args(),\n                &ty_args,\n                &TyArgs::empty(),\n            ),\n            local_values,\n            recur_self: Some(Box::new(RecurSelf {\n                arret_fun: &recur_arret_fun,\n                tail_call_ctx,\n            })),\n\n            inliner_stack: inliner::ApplyStack::new(),\n        };\n\n        let mut some_b = Some(b);\n        Self::destruc_list(\n            &mut some_b,\n            span,\n            &fun_expr.params,\n            arg_list_value,\n            &mut |local_id, value| {\n                fcx.local_values.insert(local_id, value);\n            },\n        );\n\n        let app_result = self.eval_expr(&mut fcx, &mut some_b, &fun_expr.body_expr);\n\n        let mut b = some_b.unwrap();\n        build_value_ret(self, &mut b, span, app_result, &wanted_abi.ret);\n\n        optimise_fun(ops::Fun {\n            span: arret_fun.fun_expr().span,\n            source_name: arret_fun.source_name().clone(),\n\n            abi: wanted_abi.into(),\n            param_regs,\n            ops: b.into_ops(),\n        })\n    }\n\n    /// Builds a function with a callback ABI that calls a thunk passed as its captures\n    fn ops_for_callback_to_thunk_adapter(\n        &mut self,\n        entry_point_abi: CallbackEntryPointAbiType,\n    ) -> ops::Fun {\n        use crate::mir::arg_list::{build_load_arg_list_value, LoadedArgList};\n        use crate::mir::optimise::optimise_fun;\n        use crate::mir::ret_value::build_value_ret;\n\n        let span = EMPTY_SPAN;\n        let wanted_abi = entry_point_abi.into();\n\n        let mut b = Builder::new();\n\n        let LoadedArgList {\n            captures_reg,\n            param_regs,\n            arg_list_value,\n        } = build_load_arg_list_value(\n            self,\n            &mut b,\n            &wanted_abi,\n            &ty::List::new_uniform(Ty::Any.into()),\n        );\n\n        let fun_reg_value =\n            value::RegValue::new(captures_reg.unwrap(), abitype::BoxedAbiType::Any.into());\n\n        let result_value =\n            self.build_reg_fun_thunk_app(&mut b, span, &fun_reg_value, &arg_list_value);\n\n        build_value_ret(self, &mut b, span, Ok(result_value), &wanted_abi.ret);\n\n        optimise_fun(ops::Fun {\n            span,\n            source_name: Some(\"callback_to_thunk_adapter\".into()),\n\n            abi: wanted_abi.into(),\n            param_regs,\n            ops: b.into_ops(),\n        })\n    }\n\n    pub fn thunk_reg_to_callback_reg(\n        &mut self,\n        b: &mut Builder,\n        span: Span,\n        thunk_reg_abi_type: &arret_runtime::abitype::BoxedAbiType,\n        thunk_reg: BuiltReg,\n        entry_point_abi: &CallbackEntryPointAbiType,\n    ) -> BuiltReg {\n        use crate::mir::ops::*;\n\n        // Captures are of type `Any`\n        let captures_reg = b.cast_boxed_cond(\n            span,\n            thunk_reg_abi_type,\n            thunk_reg,\n            abitype::BoxedAbiType::Any,\n        );\n\n        let private_fun_id = self.private_fun_id_counter.alloc();\n        let ops_fun = self.ops_for_callback_to_thunk_adapter(entry_point_abi.clone());\n        self.private_funs.insert(private_fun_id, ops_fun);\n\n        b.push_reg(\n            span,\n            OpKind::MakeCallback,\n            MakeCallbackOp {\n                captures_reg: captures_reg.into(),\n                callee: ops::Callee::PrivateFun(private_fun_id),\n            },\n        )\n    }\n\n    pub fn jit_boxed_to_fun_value(&self, boxed_thunk: Gc<boxed::FunThunk>) -> Option<&Value> {\n        self.thunk_fun_values.get(&boxed_thunk.as_ptr())\n    }\n\n    pub fn visit_module_defs<'a>(\n        &mut self,\n        module_id: ModuleId,\n        defs: impl IntoIterator<Item = &'a hir::Def<hir::Inferred>>,\n    ) -> Result<()> {\n        for def in defs {\n            let hir::Def {\n                destruc,\n                value_expr,\n                ..\n            } = def;\n\n            let mut fcx = FunCtx::new(Some(module_id));\n\n            // Don't pass a builder; we should never generate ops based on a def\n            let source_name = Self::destruc_source_name(destruc);\n            let value =\n                self.eval_expr_with_source_name(&mut fcx, &mut None, value_expr, source_name)?;\n\n            Self::destruc_value(&mut None, destruc, value, &mut |local_id, value| {\n                self.global_values\n                    .insert(hir::ExportId::new(module_id, local_id), value);\n            });\n        }\n\n        Ok(())\n    }\n\n    pub fn consume_module_defs(\n        &mut self,\n        module_id: ModuleId,\n        defs: impl IntoIterator<Item = hir::Def<hir::Inferred>>,\n    ) -> Result<()> {\n        for def in defs {\n            let hir::Def {\n                destruc,\n                value_expr,\n                ..\n            } = def;\n\n            let mut fcx = FunCtx::new(Some(module_id));\n\n            // Don't pass a builder; we should never generate ops based on a def\n            let source_name = Self::destruc_source_name(&destruc);\n            let value =\n                self.consume_expr_with_source_name(&mut fcx, &mut None, value_expr, source_name)?;\n\n            Self::destruc_value(&mut None, &destruc, value, &mut |local_id, value| {\n                self.global_values\n                    .insert(hir::ExportId::new(module_id, local_id), value);\n            });\n        }\n\n        Ok(())\n    }\n\n    pub fn should_collect(&self) -> bool {\n        self.runtime_task.heap().should_collect()\n    }\n\n    /// Collect any boxed values that are no longer reachable\n    pub fn collect_garbage(&mut self) {\n        use arret_runtime::boxed::collect;\n        use std::mem;\n\n        let old_heap = mem::take(self.runtime_task.heap_mut());\n        let mut strong_pass = collect::StrongPass::new(old_heap);\n\n        // Move all of our global values to the new heap\n        for value_ref in self.global_values.values_mut() {\n            value::visit_value_root(&mut strong_pass, value_ref);\n        }\n\n        for value_ref in self.thunk_fun_values.values_mut() {\n            // TODO: This can cause a circular reference with the weak pass below\n            value::visit_value_root(&mut strong_pass, value_ref);\n        }\n\n        // Any function values that are still live need to be updated\n        let weak_pass = strong_pass.into_weak_pass();\n\n        let old_thunk_fun_values = mem::take(&mut self.thunk_fun_values);\n        self.thunk_fun_values = old_thunk_fun_values\n            .into_iter()\n            .filter_map(|(fun_thunk, value)| unsafe {\n                weak_pass\n                    .new_heap_ref_for(Gc::new(fun_thunk))\n                    .map(|new_fun_thunk| (new_fun_thunk.as_ptr(), value))\n            })\n            .collect();\n\n        *self.runtime_task.heap_mut() = weak_pass.into_new_heap();\n    }\n\n    fn eval_expr_with_source_name(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        expr: &Expr,\n        source_name: Option<&DataStr>,\n    ) -> Result<Value> {\n        use crate::mir::value::types::value_with_arret_ty;\n\n        use crate::hir::ExprKind;\n        let value = match &expr.kind {\n            ExprKind::Lit(literal) => Ok(self.eval_lit(literal)),\n            ExprKind::Do(exprs) => self.eval_do(fcx, b, exprs),\n            ExprKind::Fun(fun_expr) => {\n                Ok(self.eval_arret_fun(fcx, fun_expr.as_ref().clone(), source_name))\n            }\n            ExprKind::RustFun(rust_fun) => Ok(Value::RustFun(rust_fun.clone())),\n            ExprKind::TyPred(_, test_ty) => Ok(Value::TyPred(test_ty.clone())),\n            ExprKind::EqPred(_) => Ok(Value::EqPred),\n            ExprKind::RecordCons(_, record_cons) => Ok(Value::RecordCons(record_cons.clone())),\n            ExprKind::FieldAccessor(field_accessor) => Ok(Value::FieldAccessor(\n                field_accessor.record_cons.clone(),\n                field_accessor.field_index,\n            )),\n            ExprKind::LocalRef(_, local_id) => Ok(self.eval_local_ref(fcx, *local_id)),\n            ExprKind::ExportRef(_, export_id) => Ok(self.global_values[export_id].clone()),\n            ExprKind::Let(hir_let) => self.eval_let(fcx, b, hir_let),\n            ExprKind::App(app) => self.eval_app(fcx, b, &expr.result_ty, app),\n            ExprKind::Recur(recur) => self.eval_recur(fcx, b, &expr.result_ty, recur),\n            ExprKind::MacroExpand(span, expr) => self\n                .eval_expr(fcx, b, expr)\n                .map_err(|err| err.with_macro_invocation_span(*span)),\n            ExprKind::Cond(cond) => self.eval_cond(fcx, b, cond),\n        }?;\n\n        // Annotate this value with the expression's result type as it passes through\n        Ok(value_with_arret_ty(self, value, || {\n            fcx.monomorphise(&expr.result_ty)\n        }))\n    }\n\n    pub fn eval_expr(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        expr: &Expr,\n    ) -> Result<Value> {\n        self.eval_expr_with_source_name(fcx, b, expr, None)\n    }\n\n    fn consume_expr_with_source_name(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        expr: Expr,\n        source_name: Option<&DataStr>,\n    ) -> Result<Value> {\n        use crate::hir::ExprKind;\n        match expr.kind {\n            ExprKind::Fun(fun_expr) => Ok(self.eval_arret_fun(fcx, *fun_expr, source_name)),\n            ExprKind::RustFun(rust_fun) => Ok(Value::RustFun(rust_fun)),\n            _ => self.eval_expr_with_source_name(fcx, b, &expr, source_name),\n        }\n    }\n\n    pub fn consume_expr(\n        &mut self,\n        fcx: &mut FunCtx<'_>,\n        b: &mut Option<Builder>,\n        expr: Expr,\n    ) -> Result<Value> {\n        self.consume_expr_with_source_name(fcx, b, expr, None)\n    }\n\n    /// Evaluates the main function of a program\n    pub fn eval_main_fun(&mut self, main_export_id: hir::ExportId) -> Result<()> {\n        let mut fcx = FunCtx::new(Some(main_export_id.module_id()));\n        let main_value = self.eval_local_ref(&fcx, main_export_id.local_id());\n\n        let empty_list_value = Value::List(Box::new([]), None);\n\n        self.eval_value_app(\n            &mut fcx,\n            &mut None,\n            EMPTY_SPAN,\n            &Ty::unit().into(),\n            &main_value,\n            ApplyArgs {\n                ty_args: &TyArgs::empty(),\n                list_value: empty_list_value,\n            },\n        )?;\n\n        Ok(())\n    }\n\n    /// Builds the main function of the program\n    pub fn into_built_program(mut self, main_export_id: hir::ExportId) -> Result<BuiltProgram> {\n        let fcx = FunCtx::new(Some(main_export_id.module_id()));\n        let main_value = self.eval_local_ref(&fcx, main_export_id.local_id());\n\n        let main_arret_fun = if let Value::ArretFun(main_arret_fun) = main_value {\n            main_arret_fun\n        } else {\n            unimplemented!(\"Non-Arret main!\");\n        };\n\n        let main_abi = PolymorphAbi {\n            call_conv: ops::CallConv::Ccc,\n\n            // Main is a top-level function; it can't capture\n            has_captures: false,\n            fixed_params: Box::new([]),\n            rest_param: None,\n\n            ret: abitype::RetAbiType::Void,\n        };\n\n        let main = self.ops_for_arret_fun(&main_arret_fun, main_abi);\n\n        Ok(BuiltProgram {\n            main,\n            private_funs: self.private_funs,\n        })\n    }\n\n    pub fn value_to_const(&mut self, value: &Value) -> Option<Gc<boxed::Any>> {\n        use crate::mir::value::to_const::value_to_const;\n        value_to_const(self, value)\n    }\n}\n\nimpl AsHeap for EvalHirCtx {\n    fn as_heap(&self) -> &boxed::Heap {\n        self.runtime_task.heap()\n    }\n\n    fn as_heap_mut(&mut self) -> &mut boxed::Heap {\n        self.runtime_task.heap_mut()\n    }\n}\n\nimpl AsInterner for EvalHirCtx {\n    fn as_interner(&self) -> &Interner {\n        self.runtime_task.heap().type_info().interner()\n    }\n}\n"
  },
  {
    "path": "compiler/mir/inliner.rs",
    "content": "use std::hash::{Hash, Hasher};\n\nuse arret_syntax::span::Span;\n\nuse arret_runtime::boxed::prelude::*;\nuse arret_runtime::boxed::Heap;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::costing::{cost_for_ops, OpCost, OpCostFactor};\nuse crate::mir::env_values::EnvValues;\nuse crate::mir::error::{Error, Result};\nuse crate::mir::eval_hir::ApplyArgs;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::eval_hir::FunCtx;\nuse crate::mir::optimise::optimise_inlined_fun;\nuse crate::mir::value;\nuse crate::mir::value::Value;\nuse crate::ty;\n\n/// Maximum number of consecutive inlinings in a call stack\nconst MAX_INLINE_DEPTH: usize = 16;\n\n/// Opaque hash of an Arret fun application\n///\n/// This is used to heuristically detect recursion loops\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub struct ApplyCookie {\n    arret_fun_id: value::ArretFunId,\n    arg_hash: u64,\n}\n\nimpl ApplyCookie {\n    pub fn new(heap: &Heap, arret_fun: &value::ArretFun, arg_list_value: &Value) -> Self {\n        ApplyCookie {\n            arret_fun_id: arret_fun.id(),\n            arg_hash: hash_for_arg_list_value(heap, arg_list_value),\n        }\n    }\n}\n\n/// Tracks a stack of inline Arret fun applications\n///\n/// This is used to make inlining decisions\npub struct ApplyStack {\n    entries: Vec<ApplyCookie>,\n}\n\nimpl ApplyStack {\n    pub fn new() -> ApplyStack {\n        ApplyStack { entries: vec![] }\n    }\n\n    fn with_apply_cookie(&self, apply_cookie: ApplyCookie) -> ApplyStack {\n        use std::iter;\n\n        ApplyStack {\n            entries: self\n                .entries\n                .iter()\n                .cloned()\n                .chain(iter::once(apply_cookie))\n                .collect(),\n        }\n    }\n}\n\n/// Returns the scaling factor we should use to prefer inlining for a given value\n///\n/// This is used to identify values that lose significant information when converted to a reg and\n/// back as part of a call. This is intended as a proxy for the \"hidden\" cost of calling to an\n/// out-of-line function that has lost optimisation-related information.\n///\n/// These factors seem small but the we take the product of all args and captured free values.\n/// This can cause the final factor to be quite large.\nfn inline_preference_factor_for_value(arg_value: &Value) -> OpCostFactor {\n    match arg_value {\n        Value::Reg(_) => 1.0,\n        // Consts allow for const evaling, const propagation, dead code elimination, etc\n        Value::Const(_) => 1.1,\n        // Lists and records can partially evaluate their type-specific operations\n        Value::List(_, _) | Value::Record(_, _) => 1.2,\n        // RustFuns can be const eval'ed\n        Value::RustFun(_) => 1.5,\n        // These can be const eval'ed or completely inlined\n        Value::ArretFun(_)\n        | Value::TyPred(_)\n        | Value::EqPred\n        | Value::RecordCons(_)\n        | Value::FieldAccessor(_, _) => 2.0,\n    }\n}\n\n/// Returns the product of the inline scaling factor for each of our arguments\nfn inline_preference_factor_for_arg_list_value(arg_list_value: &Value) -> OpCostFactor {\n    match arg_list_value {\n        Value::List(fixed, rest) => fixed\n            .iter()\n            .chain(rest.iter().map(AsRef::as_ref))\n            .map(inline_preference_factor_for_value)\n            .product(),\n        Value::Const(_) => 1.5,\n        _ => 1.0,\n    }\n}\n\n/// Returns the product of the inline scaling factor for each captured value\nfn inline_preference_factor_for_env_values(env_values: &EnvValues) -> OpCostFactor {\n    env_values\n        .free_values\n        .iter()\n        .map(|(_, value)| inline_preference_factor_for_value(value))\n        .product()\n}\n\n/// Returns the scaling factor we should use to prefer inlining for the given application\nfn calc_inline_preference_factor(\n    arret_fun: &value::ArretFun,\n    arg_list_value: &Value,\n) -> OpCostFactor {\n    inline_preference_factor_for_arg_list_value(arg_list_value)\n        * inline_preference_factor_for_env_values(arret_fun.env_values())\n}\n\n/// Hashes the passed value, poorly\n///\n/// This can only distinguish constants; regs hash to the same value. It's possible for constants\n/// would compare as equal to receive different hashes depending on their representation.\nfn hash_value<H: Hasher>(heap: &Heap, value: &Value, state: &mut H) {\n    match value {\n        Value::List(fixed, rest) => {\n            state.write_u8(0);\n\n            state.write_usize(fixed.len());\n            for member in fixed.iter() {\n                hash_value(heap, member, state);\n            }\n\n            state.write_u8(rest.is_some() as u8);\n            if let Some(rest_value) = rest {\n                hash_value(heap, rest_value, state);\n            }\n        }\n        Value::Record(record_cons, fields) => {\n            state.write_u8(1);\n            record_cons.hash(state);\n            for field in fields.iter() {\n                hash_value(heap, field, state);\n            }\n        }\n        Value::Const(any_ref) => {\n            state.write_u8(2);\n            any_ref.hash_in_heap(heap, state);\n        }\n        Value::EqPred => {\n            state.write_u8(3);\n        }\n        Value::TyPred(test_ty) => {\n            state.write_u8(4);\n            test_ty.hash(state);\n        }\n        Value::RecordCons(record_cons) => {\n            state.write_u8(5);\n            record_cons.hash(state);\n        }\n        Value::FieldAccessor(record_cons, field_index) => {\n            state.write_u8(6);\n            record_cons.hash(state);\n            field_index.hash(state);\n        }\n        Value::RustFun(rust_fun) => {\n            state.write_u8(7);\n            rust_fun.symbol().hash(state);\n        }\n        Value::ArretFun(arret_fun) => {\n            state.write_u8(8);\n\n            state.write_usize(arret_fun.env_values().const_values.len());\n            for (_, const_value) in arret_fun.env_values().const_values.iter() {\n                hash_value(heap, const_value, state);\n            }\n        }\n        Value::Reg(_) => {\n            state.write_u8(9);\n        }\n    };\n}\n\n/// Hashes the arg list, poorly\n///\n/// This is used to detect if a recursive loop is making forward progress. Collisions will cause us\n/// to abort recursive inlining.\nfn hash_for_arg_list_value(heap: &Heap, arg_list_value: &Value) -> u64 {\n    use std::collections::hash_map::DefaultHasher;\n    let mut state = DefaultHasher::new();\n\n    hash_value(heap, arg_list_value, &mut state);\n    state.finish()\n}\n\n/// Conditionally inlines an Arret fun\n///\n/// This makes an inlining decision based on four criteria:\n///\n/// 1. The approximate cost of performing a call versus inlining. This is calculated by attempting\n///    both options and measuring the cost of the ops they build.\n///\n/// 2. The amount of knowledge lost by calling through a fun and arg regs. This is referred to as\n///    the inlining preference factor. This is multiplied against the call cost.\n///\n/// 3. If the inlining limit has been reached. This is a basic fixed threshold.\n///\n/// 4. If we've seen this exact call before in our inlining call stack. In that case we use\n///    `Err::AbortRecursion` to \"unwind\" back to the original inlining and replace it with a call.\n///\n/// Due to the amount of partial evaluation we support this is very eager to inline. Out-of-line\n/// calls should only be used in extreme cases.\npub(super) fn cond_inline<'a>(\n    ehx: &mut EvalHirCtx,\n    fcx: &mut FunCtx<'_>,\n    outer_b: &mut Builder,\n    span: Span,\n    ret_ty: &ty::Ref<ty::Mono>,\n    arret_fun: &value::ArretFun,\n    apply_args: ApplyArgs<'a>,\n) -> Result<value::Value> {\n    // We need to build an out-of-line call in every case\n    let mut call_b = Builder::new();\n    let call_result =\n        ehx.build_arret_fun_app(fcx, &mut call_b, span, ret_ty, arret_fun, &apply_args);\n    let call_ops = call_b.into_ops();\n\n    let apply_stack = &fcx.inliner_stack;\n    let apply_cookie = ApplyCookie::new(ehx.as_heap(), arret_fun, &apply_args.list_value);\n    if apply_stack.entries.len() >= MAX_INLINE_DEPTH || apply_stack.entries.contains(&apply_cookie)\n    {\n        // Abort recursion all the way back to the original call of this function\n\n        // This prevents us from doing a \"partial unroll\" where we recurse in to one iteration\n        // of the fun application and then bail out to a call. This is a bit gnarly as we're\n        // using errors for flow control but it's isolated to this function.\n        let abort_to = apply_stack\n            .entries\n            .iter()\n            .find(|apply_cookie| apply_cookie.arret_fun_id == arret_fun.id());\n\n        if let Some(abort_to) = abort_to {\n            return Err(Error::AbortRecursion(*abort_to));\n        } else {\n            // Inline limit reached; don't attempt another inline\n            outer_b.append(call_ops.into_vec().into_iter());\n            return call_result;\n        }\n    }\n\n    let mut inline_b = Some(Builder::new());\n    let apply_stack = apply_stack.with_apply_cookie(apply_cookie);\n\n    // Figure out how much we should prefer an inline version\n    let inline_preference_factor = calc_inline_preference_factor(arret_fun, &apply_args.list_value);\n\n    // Build an inline version\n    let inline_result =\n        ehx.inline_arret_fun_app(fcx, &mut inline_b, span, arret_fun, apply_args, apply_stack);\n\n    let inline_ops = inline_b.unwrap().into_ops();\n    let inline_ops = if let Ok(ref return_value) = inline_result {\n        // In order to cost the inline function accurately we need to optimise it first\n        optimise_inlined_fun(inline_ops, return_value)\n    } else {\n        inline_ops\n    };\n\n    // Determine if calling is cheaper\n    let call_cost = cost_for_ops(call_ops.iter());\n    let inline_cost = cost_for_ops(inline_ops.iter());\n\n    if ((call_cost as OpCostFactor * inline_preference_factor) as OpCost) < inline_cost {\n        // Calling is cheaper than inlining\n        outer_b.append(call_ops.into_vec().into_iter());\n        return call_result;\n    }\n\n    match inline_result {\n        Ok(_) | Err(Error::Diverged) => {\n            // We either succeeded or hit a divergence - use the steps\n            outer_b.append(inline_ops.into_vec().into_iter());\n            inline_result\n        }\n        Err(Error::AbortRecursion(abort_to_cookie)) if abort_to_cookie == apply_cookie => {\n            // We detected another application of this fun and requested recursion is aborted back\n            // to this point\n            outer_b.append(call_ops.into_vec().into_iter());\n            call_result\n        }\n        Err(other) => Err(other),\n    }\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/bitwise.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::abitype;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::error::Result;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::intrinsic::num_utils::try_value_to_i64;\nuse crate::mir::intrinsic::BuildOutcome;\nuse crate::mir::ops::{BinaryOp, OpKind, RegId, ShiftOp};\nuse crate::mir::value::build_reg::value_to_reg;\n\nuse crate::mir::value;\nuse crate::mir::value::Value;\n\nfn fold_bitwise_operands<O>(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n    bitwise_op: O,\n) -> BuildOutcome\nwhere\n    O: Fn(RegId, BinaryOp) -> OpKind + Copy,\n{\n    let mut list_iter = if let Some(list_iter) = arg_list_value.try_sized_list_iter() {\n        list_iter\n    } else {\n        return BuildOutcome::None;\n    };\n\n    let initial_value = list_iter.next(b, span).unwrap();\n    let mut acc_reg = value_to_reg(ehx, b, span, &initial_value, &abitype::AbiType::Int);\n\n    while let Some(next_value) = list_iter.next(b, span) {\n        let next_reg = value_to_reg(ehx, b, span, &next_value, &abitype::AbiType::Int);\n\n        acc_reg = b.push_reg(\n            span,\n            bitwise_op,\n            BinaryOp {\n                lhs_reg: acc_reg.into(),\n                rhs_reg: next_reg.into(),\n            },\n        );\n    }\n\n    BuildOutcome::ReturnValue(value::RegValue::new(acc_reg, abitype::AbiType::Int).into())\n}\n\nfn bit_shift_op<O>(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n    shift_op: O,\n) -> BuildOutcome\nwhere\n    O: Fn(RegId, ShiftOp) -> OpKind + Copy,\n{\n    let mut iter = arg_list_value.unsized_list_iter();\n\n    let int_value = iter.next_unchecked(b, span);\n    let int_reg = value_to_reg(ehx, b, span, &int_value, &abitype::AbiType::Int);\n\n    let bit_count_value = iter.next_unchecked(b, span);\n\n    let bit_count = if let Some(bit_count) = try_value_to_i64(bit_count_value) {\n        bit_count\n    } else {\n        return BuildOutcome::None;\n    };\n\n    if !(0..=64).contains(&bit_count) {\n        return BuildOutcome::None;\n    }\n\n    let result_reg = b.push_reg(\n        span,\n        shift_op,\n        ShiftOp {\n            int_reg: int_reg.into(),\n            bit_count: bit_count as u32,\n        },\n    );\n\n    BuildOutcome::ReturnValue(value::RegValue::new(result_reg, abitype::AbiType::Int).into())\n}\n\npub fn bit_and(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::ops::*;\n\n    Ok(fold_bitwise_operands(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        OpKind::Int64BitwiseAnd,\n    ))\n}\n\npub fn bit_or(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::ops::*;\n\n    Ok(fold_bitwise_operands(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        OpKind::Int64BitwiseOr,\n    ))\n}\n\npub fn bit_xor(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::ops::*;\n\n    Ok(fold_bitwise_operands(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        OpKind::Int64BitwiseXor,\n    ))\n}\n\npub fn bit_not(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::ops::*;\n\n    let mut iter = arg_list_value.unsized_list_iter();\n\n    let int_value = iter.next_unchecked(b, span);\n    let int_reg = value_to_reg(ehx, b, span, &int_value, &abitype::AbiType::Int);\n\n    let result_reg = b.push_reg(span, OpKind::Int64BitwiseNot, int_reg.into());\n\n    Ok(BuildOutcome::ReturnValue(\n        value::RegValue::new(result_reg, abitype::AbiType::Int).into(),\n    ))\n}\n\npub fn bit_shift_left(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    Ok(bit_shift_op(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        OpKind::Int64ShiftLeft,\n    ))\n}\n\npub fn bit_shift_right(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    Ok(bit_shift_op(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        OpKind::Int64ArithmeticShiftRight,\n    ))\n}\n\npub fn unsigned_bit_shift_right(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    Ok(bit_shift_op(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        OpKind::Int64LogicalShiftRight,\n    ))\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/list.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::boxed;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::error::Result;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::value::list::{list_value_len, ListValueLen};\nuse crate::mir::Value;\n\npub fn length(\n    ehx: &mut EvalHirCtx,\n    b: &mut Option<Builder>,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<Option<Value>> {\n    let mut iter = arg_list_value.unsized_list_iter();\n    let single_arg = iter.next_unchecked(b, span);\n\n    let list_len = list_value_len(&single_arg);\n\n    if let ListValueLen::Exact(known_len) = list_len {\n        return Ok(Some(boxed::Int::new(ehx, known_len as i64).into()));\n    }\n\n    if let Some(b) = b {\n        use crate::mir::ops::*;\n        use crate::mir::value;\n        use crate::mir::value::build_reg::value_to_reg;\n        use arret_runtime::abitype;\n\n        let list_reg = value_to_reg(\n            ehx,\n            b,\n            span,\n            &single_arg,\n            &abitype::TOP_LIST_BOXED_ABI_TYPE.into(),\n        );\n\n        let list_len_reg = b.push_reg(\n            span,\n            OpKind::LoadBoxedListLen,\n            LoadBoxedListLenOp {\n                list_reg: list_reg.into(),\n                min_list_len: list_len.lower_bound(),\n            },\n        );\n\n        return Ok(Some(\n            value::RegValue::new(list_len_reg, abitype::AbiType::Int).into(),\n        ));\n    }\n\n    Ok(None)\n}\n\npub fn cons(\n    _ehx: &mut EvalHirCtx,\n    b: &mut Option<Builder>,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<Option<Value>> {\n    let mut iter = arg_list_value.unsized_list_iter();\n\n    let head = iter.next_unchecked(b, span);\n    let rest = iter.next_unchecked(b, span);\n\n    Ok(Some(Value::List(Box::new([head]), Some(Box::new(rest)))))\n}\n\npub fn repeat(\n    _ehx: &mut EvalHirCtx,\n    b: &mut Option<Builder>,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<Option<Value>> {\n    // Avoid creating giant constants at compile time\n    const MAX_REPEAT_EVAL_LEN: i64 = 64;\n\n    use crate::mir::intrinsic::num_utils::try_value_to_i64;\n\n    let mut iter = arg_list_value.unsized_list_iter();\n\n    let count_value = iter.next_unchecked(b, span);\n    let count = if let Some(count) = try_value_to_i64(count_value) {\n        count\n    } else {\n        return Ok(None);\n    };\n\n    let value = iter.next_unchecked(b, span);\n\n    if count <= 0 {\n        return Ok(Some(Value::List(Box::new([]), None)));\n    } else if count > MAX_REPEAT_EVAL_LEN {\n        return Ok(None);\n    }\n\n    // This lets us build a list of a known length and MIR values\n    Ok(Some(Value::List(\n        std::iter::repeat(value).take(count as usize).collect(),\n        None,\n    )))\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/math.rs",
    "content": "//! Intrinsics for math operations on numbers\n//!\n//! This strives to match the behaviour of the stdlib in term of operation and conversion order.\n//! For multi-operand math operations we behave as if we reduce our input pairwise from left to\n//! right. If either pairwise operand is a `Float` then both operands are converted to `Float` and\n//! the result is a `Float`. If both operands are `Int`s then we perform checked math to ensure\n//! the value doesn't overflow its `Int` result.\n//!\n//! The input-dependent result type makes it difficult for us to build operations on values that\n//! aren't definite `Int` or `Float` (i.e. `Num`). Once we encounter a known `Float` we can safely\n//! convert every remaining operand to `Float` with at most a single branch per operand. However, if\n//! we encounter an unknown `Num` along with another `Num` or `Int` we don't know the result type\n//! of the pairwise operation. This can produce a combinatorial explosion of branches.\n//!\n//! For this reason this code will return `None` if it encounters a `Num` before a `Float`. This\n//! will cause us to fallback to the stdlib at runtime.\n//!\n//! This also makes no attempt at simplification or strength reduction. The presumption is LLVM is\n//! much better at this than we are.\n\nuse arret_syntax::span::Span;\n\nuse arret_runtime::abitype;\n\nuse crate::mir::builder::{Builder, BuiltReg};\nuse crate::mir::error::Result;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::intrinsic::num_utils::{num_value_to_float_reg, try_value_to_i64, NumOperand};\nuse crate::mir::intrinsic::BuildOutcome;\nuse crate::mir::ops::{BinaryOp, OpKind, RegId};\n\nuse crate::mir::value;\nuse crate::mir::value::build_reg::value_to_reg;\nuse crate::mir::value::list::SizedListIterator;\nuse crate::mir::value::Value;\n\n/// Folds a series of numerical operands as `Float`s\n///\n/// This is used once we know our result will be a `Float`\nfn fold_float_operands<F>(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    mut acc_float_reg: BuiltReg,\n    mut list_iter: SizedListIterator,\n    float_op: F,\n) -> Value\nwhere\n    F: Fn(RegId, BinaryOp) -> OpKind + Copy,\n{\n    while let Some(value) = list_iter.next(b, span) {\n        let operand_reg = num_value_to_float_reg(ehx, b, span, &value);\n\n        acc_float_reg = b.push_reg(\n            span,\n            float_op,\n            BinaryOp {\n                lhs_reg: acc_float_reg.into(),\n                rhs_reg: operand_reg.into(),\n            },\n        );\n    }\n\n    value::RegValue::new(acc_float_reg, abitype::AbiType::Float).into()\n}\n\n/// Folds a series of numerical operands with the given reducers for `Int` and `Float`s\n///\n/// This is used when the precise type of the result is still unknown\nfn fold_num_operands<I, F>(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    mut acc_int_reg: BuiltReg,\n    mut list_iter: SizedListIterator,\n    int64_op: I,\n    float_op: F,\n) -> BuildOutcome\nwhere\n    I: Fn(RegId, BinaryOp) -> OpKind + Copy,\n    F: Fn(RegId, BinaryOp) -> OpKind + Copy,\n{\n    while let Some(value) = list_iter.next(b, span) {\n        let operand = if let Some(operand) = NumOperand::try_from_value(ehx, b, span, &value) {\n            operand\n        } else {\n            // Can't continue. Use the work we've done so far to simplify the\n            // stdlib call.\n            return BuildOutcome::SimplifiedArgs(Value::List(\n                Box::new([\n                    value::RegValue::new(acc_int_reg, abitype::AbiType::Int).into(),\n                    value,\n                ]),\n                Some(Box::new(list_iter.into_rest())),\n            ));\n        };\n\n        acc_int_reg = match operand {\n            NumOperand::Int(operand_int_reg) => b.push_reg(\n                span,\n                int64_op,\n                BinaryOp {\n                    lhs_reg: acc_int_reg.into(),\n                    rhs_reg: operand_int_reg.into(),\n                },\n            ),\n            NumOperand::Float(operand_float_reg) => {\n                let int_as_float_reg = b.push_reg(span, OpKind::Int64ToFloat, acc_int_reg.into());\n\n                let result_reg = b.push_reg(\n                    span,\n                    float_op,\n                    BinaryOp {\n                        lhs_reg: int_as_float_reg.into(),\n                        rhs_reg: operand_float_reg.into(),\n                    },\n                );\n\n                return BuildOutcome::ReturnValue(fold_float_operands(\n                    ehx, b, span, result_reg, list_iter, float_op,\n                ));\n            }\n        }\n    }\n\n    BuildOutcome::ReturnValue(value::RegValue::new(acc_int_reg, abitype::AbiType::Int).into())\n}\n\n/// Reduces a series of numerical operands with the given reducer ops for `Int` and `Float`s\n///\n/// This does not assume the reducers are associative\nfn reduce_operands<I, F>(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    mut list_iter: SizedListIterator,\n    int64_op: I,\n    float_op: F,\n) -> BuildOutcome\nwhere\n    I: Fn(RegId, BinaryOp) -> OpKind + Copy,\n    F: Fn(RegId, BinaryOp) -> OpKind + Copy,\n{\n    let initial_value = list_iter.next(b, span).unwrap();\n    let initial_operand =\n        if let Some(initial_operand) = NumOperand::try_from_value(ehx, b, span, &initial_value) {\n            initial_operand\n        } else {\n            return BuildOutcome::None;\n        };\n\n    match initial_operand {\n        NumOperand::Int(int_reg) => {\n            fold_num_operands(ehx, b, span, int_reg, list_iter, int64_op, float_op)\n        }\n        NumOperand::Float(float_reg) => BuildOutcome::ReturnValue(fold_float_operands(\n            ehx, b, span, float_reg, list_iter, float_op,\n        )),\n    }\n}\n\n/// Reduces a series of numerical operands with the given reducer ops for `Int` and `Float`s\n///\n/// This assumes the reducers are associative\nfn reduce_assoc_operands<I, F>(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n    int64_op: I,\n    float_op: F,\n) -> BuildOutcome\nwhere\n    I: Fn(RegId, BinaryOp) -> OpKind + Copy,\n    F: Fn(RegId, BinaryOp) -> OpKind + Copy,\n{\n    let mut list_iter = if let Some(list_iter) = arg_list_value.try_sized_list_iter() {\n        list_iter\n    } else {\n        return BuildOutcome::None;\n    };\n\n    if list_iter.len() == 1 {\n        // The associative math functions (`+` and `*`) act as the identity function with 1 arg.\n        // We check here so even if the value doesn't have a definite type it's still returned.\n        list_iter\n            .next(b, span)\n            .map_or(BuildOutcome::None, BuildOutcome::ReturnValue)\n    } else {\n        reduce_operands(ehx, b, span, list_iter, int64_op, float_op)\n    }\n}\n\npub fn add(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::ops::*;\n\n    Ok(reduce_assoc_operands(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        OpKind::Int64CheckedAdd,\n        OpKind::FloatAdd,\n    ))\n}\n\npub fn mul(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::ops::*;\n\n    Ok(reduce_assoc_operands(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        OpKind::Int64CheckedMul,\n        OpKind::FloatMul,\n    ))\n}\n\npub fn sub(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::ops::*;\n\n    let list_iter = if let Some(list_iter) = arg_list_value.try_sized_list_iter() {\n        list_iter\n    } else {\n        return Ok(BuildOutcome::None);\n    };\n\n    if list_iter.len() == 1 {\n        // Rewrite `(- x)` to `(- 0 x)`\n        let int_zero_reg = b.push_reg(span, OpKind::ConstInt64, 0);\n\n        Ok(fold_num_operands(\n            ehx,\n            b,\n            span,\n            int_zero_reg,\n            list_iter,\n            OpKind::Int64CheckedSub,\n            OpKind::FloatSub,\n        ))\n    } else {\n        Ok(reduce_operands(\n            ehx,\n            b,\n            span,\n            list_iter,\n            OpKind::Int64CheckedSub,\n            OpKind::FloatSub,\n        ))\n    }\n}\n\npub fn div(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::ops::*;\n\n    let mut list_iter = if let Some(list_iter) = arg_list_value.try_sized_list_iter() {\n        list_iter\n    } else {\n        return Ok(BuildOutcome::None);\n    };\n\n    let initial_value = list_iter.next(b, span).unwrap();\n    let initial_reg = value_to_reg(ehx, b, span, &initial_value, &abitype::AbiType::Float);\n\n    let result_reg = if list_iter.is_empty() {\n        // Rewrite `(/ x)` to `(/ 1.0 x)`\n        let const_one_reg = b.push_reg(span, OpKind::ConstFloat, 1.0f64);\n\n        b.push_reg(\n            span,\n            OpKind::FloatDiv,\n            BinaryOp {\n                lhs_reg: const_one_reg.into(),\n                rhs_reg: initial_reg.into(),\n            },\n        )\n    } else {\n        let mut acc = initial_reg;\n        while let Some(value) = list_iter.next(b, span) {\n            let value_reg = value_to_reg(ehx, b, span, &value, &abitype::AbiType::Float);\n\n            acc = b.push_reg(\n                span,\n                OpKind::FloatDiv,\n                BinaryOp {\n                    lhs_reg: acc.into(),\n                    rhs_reg: value_reg.into(),\n                },\n            );\n        }\n\n        acc\n    };\n\n    Ok(BuildOutcome::ReturnValue(\n        value::RegValue::new(result_reg, abitype::AbiType::Float).into(),\n    ))\n}\n\nfn int_division_op<CI, UI>(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n    checked_op_kind: CI,\n    unchecked_op_kind: UI,\n) -> BuildOutcome\nwhere\n    CI: FnOnce(RegId, BinaryOp) -> OpKind,\n    UI: FnOnce(RegId, BinaryOp) -> OpKind,\n{\n    let mut iter = arg_list_value.unsized_list_iter();\n\n    let numer_value = iter.next_unchecked(b, span);\n    let numer_reg = value_to_reg(ehx, b, span, &numer_value, &abitype::AbiType::Int);\n\n    let denom_value = iter.next_unchecked(b, span);\n    let denom_reg = value_to_reg(ehx, b, span, &denom_value, &abitype::AbiType::Int);\n\n    let needs_checked = match try_value_to_i64(denom_value) {\n        None => {\n            // Completely unknown, we need a check\n            true\n        }\n        Some(0) | Some(-1) => {\n            // Definite divide-by-zero or possible overflow\n            true\n        }\n        Some(_) => {\n            // Don't need a check\n            false\n        }\n    };\n\n    let div_binary_op = BinaryOp {\n        lhs_reg: numer_reg.into(),\n        rhs_reg: denom_reg.into(),\n    };\n\n    let result_reg = if needs_checked {\n        b.push_reg(span, checked_op_kind, div_binary_op)\n    } else {\n        b.push_reg(span, unchecked_op_kind, div_binary_op)\n    };\n\n    BuildOutcome::ReturnValue(value::RegValue::new(result_reg, abitype::AbiType::Int).into())\n}\n\npub fn quot(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    Ok(int_division_op(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        OpKind::Int64CheckedDiv,\n        OpKind::Int64Div,\n    ))\n}\n\npub fn rem(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    Ok(int_division_op(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        OpKind::Int64CheckedRem,\n        OpKind::Int64Rem,\n    ))\n}\n\npub fn sqrt(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    let radicand_value = arg_list_value.unsized_list_iter().next_unchecked(b, span);\n\n    let radicand_reg = value_to_reg(ehx, b, span, &radicand_value, &abitype::AbiType::Float);\n    let result_reg = b.push_reg(span, OpKind::FloatSqrt, radicand_reg.into());\n\n    Ok(BuildOutcome::ReturnValue(\n        value::RegValue::new(result_reg, abitype::AbiType::Float).into(),\n    ))\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/mod.rs",
    "content": "mod bitwise;\nmod list;\nmod math;\nmod num_utils;\nmod number;\nmod panics;\nmod partial_print;\nmod print;\nmod testing;\nmod vector;\n\nuse arret_syntax::span::Span;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::error::Result;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::Value;\n\nmacro_rules! define_eval_intrinsics {\n    ( $($name:expr => $handler:path),* ) => {\n        pub fn try_eval(\n            ehx: &mut EvalHirCtx,\n            b: &mut Option<Builder>,\n            span: Span,\n            intrinsic_name: &'static str,\n            arg_list_value: &Value,\n        ) -> Result<Option<Value>> {\n            match intrinsic_name {\n                $(\n                    $name => {\n                        $handler(ehx, b, span, arg_list_value)\n                    }\n                ),*\n                _ => Ok(None),\n            }\n        }\n    };\n}\n\npub enum BuildOutcome {\n    None,\n    ReturnValue(Value),\n    SimplifiedArgs(Value),\n}\n\nmacro_rules! define_build_intrinsics {\n    ( $($name:expr => $handler:path),* ) => {\n        pub fn try_build(\n            ehx: &mut EvalHirCtx,\n            b: &mut Builder,\n            span: Span,\n            intrinsic_name: &'static str,\n            arg_list_value: &Value,\n        ) -> Result<BuildOutcome> {\n            match intrinsic_name {\n                $(\n                    $name => {\n                        $handler(ehx, b, span, arg_list_value)\n                    }\n                ),*\n                _ => Ok(BuildOutcome::None),\n            }\n        }\n    };\n}\n\ndefine_eval_intrinsics! {\n    \"length\" => list::length,\n    \"cons\" => list::cons,\n    \"repeat\" => list::repeat,\n    \"fn-op-categories\" => testing::fn_op_categories\n}\n\ndefine_build_intrinsics! {\n    \"+\" => math::add,\n    \"*\" => math::mul,\n    \"-\" => math::sub,\n    \"/\" => math::div,\n    \"quot\" => math::quot,\n    \"rem\" => math::rem,\n    \"sqrt\" => math::sqrt,\n\n    \"int\" => number::int,\n    \"float\" => number::float,\n    \"<\" => number::num_lt,\n    \"<=\" => number::num_le,\n    \"==\" => number::num_eq,\n    \">\" => number::num_gt,\n    \">=\" => number::num_ge,\n\n    // Purity doesn't matter at the MIR level; these are both treated as impure so they're not\n    // optimised away.\n    \"panic\" => panics::panics,\n    \"panic!\" => panics::panics,\n\n    \"print!\" => print::print,\n    \"println!\" => print::print,\n    \"print-str\" => print::print_str,\n\n    \"vector-length\" => vector::vector_length,\n    \"vector-ref\" => vector::vector_ref,\n\n    \"bit-and\" => bitwise::bit_and,\n    \"bit-or\" => bitwise::bit_or,\n    \"bit-xor\" => bitwise::bit_xor,\n    \"bit-not\" => bitwise::bit_not,\n    \"bit-shift-left\" => bitwise::bit_shift_left,\n    \"bit-shift-right\" => bitwise::bit_shift_right,\n    \"unsigned-bit-shift-right\" => bitwise::unsigned_bit_shift_right\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/num_utils.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::abitype;\nuse arret_runtime::boxed;\n\nuse crate::mir::builder::{Builder, BuiltReg};\nuse crate::mir::eval_hir::EvalHirCtx;\n\nuse crate::mir::value::build_reg::value_to_reg;\nuse crate::mir::value::types::possible_type_tags_for_value;\nuse crate::mir::value::Value;\n\n/// Represents a numerical operand of a known type\npub enum NumOperand {\n    Int(BuiltReg),\n    Float(BuiltReg),\n}\n\nimpl NumOperand {\n    /// Attempts to build numerical operand from a value\n    ///\n    /// If the value isn't a definite `Int` or `Float` this will return `None`\n    pub fn try_from_value(\n        ehx: &mut EvalHirCtx,\n        b: &mut Builder,\n        span: Span,\n        value: &Value,\n    ) -> Option<NumOperand> {\n        let possible_type_tags = possible_type_tags_for_value(value);\n\n        if !possible_type_tags.contains(boxed::TypeTag::Float) {\n            let int64_reg = value_to_reg(ehx, b, span, value, &abitype::AbiType::Int);\n            Some(NumOperand::Int(int64_reg))\n        } else if !possible_type_tags.contains(boxed::TypeTag::Int) {\n            let float_reg = value_to_reg(ehx, b, span, value, &abitype::AbiType::Float);\n            Some(NumOperand::Float(float_reg))\n        } else {\n            None\n        }\n    }\n}\n\n/// Converts a value of type `Num` to a float reg\npub fn num_value_to_float_reg(\n    ehx: &mut EvalHirCtx,\n    outer_b: &mut Builder,\n    span: Span,\n    value: &Value,\n) -> BuiltReg {\n    use crate::mir::ops::*;\n\n    let num_type_tags = [boxed::TypeTag::Int, boxed::TypeTag::Float]\n        .iter()\n        .collect();\n\n    let possible_type_tags = possible_type_tags_for_value(value) & num_type_tags;\n\n    if possible_type_tags == boxed::TypeTag::Float.into() {\n        value_to_reg(ehx, outer_b, span, value, &abitype::AbiType::Float)\n    } else if possible_type_tags == boxed::TypeTag::Int.into() {\n        let int64_reg = value_to_reg(ehx, outer_b, span, value, &abitype::AbiType::Int);\n        outer_b.push_reg(span, OpKind::Int64ToFloat, int64_reg.into())\n    } else {\n        let boxed_any_reg = value_to_reg(\n            ehx,\n            outer_b,\n            span,\n            value,\n            &abitype::BoxedAbiType::Any.into(),\n        )\n        .into();\n\n        let value_type_tag_reg = outer_b.push_reg(\n            span,\n            OpKind::LoadBoxedTypeTag,\n            LoadBoxedTypeTagOp {\n                subject_reg: boxed_any_reg,\n                possible_type_tags: num_type_tags,\n            },\n        );\n\n        let float_tag_reg = outer_b.push_reg(span, OpKind::ConstTypeTag, boxed::TypeTag::Float);\n\n        let is_float_reg = outer_b.push_reg(\n            span,\n            OpKind::TypeTagEqual,\n            BinaryOp {\n                lhs_reg: value_type_tag_reg.into(),\n                rhs_reg: float_tag_reg.into(),\n            },\n        );\n\n        let mut is_float_b = Builder::new();\n        let is_float_result_reg =\n            value_to_reg(ehx, &mut is_float_b, span, value, &abitype::AbiType::Float);\n\n        let mut is_int_b = Builder::new();\n        let int64_reg = value_to_reg(ehx, &mut is_int_b, span, value, &abitype::AbiType::Int);\n        let is_int_result_reg = is_int_b.push_reg(span, OpKind::Int64ToFloat, int64_reg.into());\n\n        let output_reg = RegId::alloc();\n        outer_b.push(\n            span,\n            OpKind::Cond(CondOp {\n                reg_phi: Some(RegPhi {\n                    output_reg,\n                    true_result_reg: is_float_result_reg.into(),\n                    false_result_reg: is_int_result_reg.into(),\n                }),\n                test_reg: is_float_reg.into(),\n                true_ops: is_float_b.into_ops(),\n                false_ops: is_int_b.into_ops(),\n            }),\n        );\n\n        BuiltReg::Local(output_reg)\n    }\n}\n\n/// Tries to convert a `Value` to a constant `i64`\npub fn try_value_to_i64(value: Value) -> Option<i64> {\n    match value {\n        Value::Const(any_ref) => any_ref\n            .downcast_ref::<boxed::Int>()\n            .map(|int_ref| int_ref.value()),\n        _ => None,\n    }\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/number.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::abitype;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\n\nuse crate::mir::builder::{Builder, BuiltReg};\nuse crate::mir::error::Result;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::intrinsic::num_utils::{num_value_to_float_reg, NumOperand};\nuse crate::mir::intrinsic::BuildOutcome;\nuse crate::mir::ops::Comparison;\n\nuse crate::mir::value;\nuse crate::mir::value::list::SizedListIterator;\nuse crate::mir::value::types::possible_type_tags_for_value;\nuse crate::mir::value::Value;\n\nfn build_operand_pair_compare(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    left_value: &Value,\n    right_value: &Value,\n    comparison: Comparison,\n) -> Option<BuiltReg> {\n    use crate::mir::ops::*;\n\n    let left_num_operand = NumOperand::try_from_value(ehx, b, span, left_value)?;\n    let right_num_operand = NumOperand::try_from_value(ehx, b, span, right_value)?;\n\n    match (left_num_operand, right_num_operand) {\n        (NumOperand::Int(left_int_reg), NumOperand::Int(right_int_reg)) => Some(b.push_reg(\n            span,\n            OpKind::IntCompare,\n            CompareOp {\n                comparison,\n                lhs_reg: left_int_reg.into(),\n                rhs_reg: right_int_reg.into(),\n            },\n        )),\n        (NumOperand::Float(left_float_reg), NumOperand::Int(right_int_reg)) => {\n            let right_float_reg = b.push_reg(span, OpKind::Int64ToFloat, right_int_reg.into());\n            Some(b.push_reg(\n                span,\n                OpKind::FloatCompare,\n                CompareOp {\n                    comparison,\n                    lhs_reg: left_float_reg.into(),\n                    rhs_reg: right_float_reg.into(),\n                },\n            ))\n        }\n        (NumOperand::Int(left_int_reg), NumOperand::Float(right_float_reg)) => {\n            let left_float_reg = b.push_reg(span, OpKind::Int64ToFloat, left_int_reg.into());\n            Some(b.push_reg(\n                span,\n                OpKind::FloatCompare,\n                CompareOp {\n                    comparison,\n                    lhs_reg: left_float_reg.into(),\n                    rhs_reg: right_float_reg.into(),\n                },\n            ))\n        }\n        (NumOperand::Float(left_float_reg), NumOperand::Float(right_float_reg)) => {\n            Some(b.push_reg(\n                span,\n                OpKind::FloatCompare,\n                CompareOp {\n                    comparison,\n                    lhs_reg: left_float_reg.into(),\n                    rhs_reg: right_float_reg.into(),\n                },\n            ))\n        }\n    }\n}\n\nfn build_operand_iter_compare(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    left_value: &Value,\n    rest_iter: &mut SizedListIterator,\n    comparison: Comparison,\n) -> Option<BuiltReg> {\n    use crate::mir::ops::*;\n\n    let right_value = rest_iter.next(b, span).unwrap();\n    let compare_result_reg =\n        build_operand_pair_compare(ehx, b, span, left_value, &right_value, comparison)?;\n\n    let combined_result_reg;\n    if rest_iter.is_empty() {\n        // We're terminal, this is simple\n        combined_result_reg = compare_result_reg;\n    } else {\n        let mut rest_b = Builder::new();\n        let rest_result_reg = build_operand_iter_compare(\n            ehx,\n            &mut rest_b,\n            span,\n            &right_value,\n            rest_iter,\n            comparison,\n        )?;\n\n        combined_result_reg = b.alloc_local();\n        b.push(\n            span,\n            OpKind::Cond(CondOp {\n                reg_phi: Some(RegPhi {\n                    output_reg: combined_result_reg.into(),\n                    true_result_reg: rest_result_reg.into(),\n                    // This is known false\n                    false_result_reg: compare_result_reg.into(),\n                }),\n                test_reg: compare_result_reg.into(),\n                true_ops: rest_b.into_ops(),\n                false_ops: Box::new([]),\n            }),\n        );\n    };\n\n    Some(combined_result_reg)\n}\n\nfn compare_operand_list(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n    comparison: Comparison,\n) -> BuildOutcome {\n    let mut list_iter = if let Some(list_iter) = arg_list_value.try_sized_list_iter() {\n        list_iter\n    } else {\n        return BuildOutcome::None;\n    };\n\n    if list_iter.len() == 1 {\n        return BuildOutcome::ReturnValue(boxed::TRUE_INSTANCE.as_any_ref().into());\n    }\n\n    let left_value = list_iter.next(b, span).unwrap();\n    match build_operand_iter_compare(ehx, b, span, &left_value, &mut list_iter, comparison) {\n        Some(result_reg) => BuildOutcome::ReturnValue(\n            value::RegValue::new(result_reg, abitype::AbiType::Bool).into(),\n        ),\n        None => BuildOutcome::None,\n    }\n}\n\npub fn int(\n    _ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    let value = arg_list_value.unsized_list_iter().next_unchecked(b, span);\n\n    Ok(\n        if possible_type_tags_for_value(&value) == boxed::TypeTag::Int.into() {\n            BuildOutcome::ReturnValue(value)\n        } else {\n            BuildOutcome::None\n        },\n    )\n}\n\npub fn float(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    let value = arg_list_value.unsized_list_iter().next_unchecked(b, span);\n\n    Ok(BuildOutcome::ReturnValue(\n        value::RegValue::new(\n            num_value_to_float_reg(ehx, b, span, &value),\n            abitype::AbiType::Float,\n        )\n        .into(),\n    ))\n}\n\npub fn num_lt(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    Ok(compare_operand_list(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        Comparison::Lt,\n    ))\n}\n\npub fn num_le(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    Ok(compare_operand_list(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        Comparison::Le,\n    ))\n}\n\npub fn num_eq(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    Ok(compare_operand_list(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        Comparison::Eq,\n    ))\n}\n\npub fn num_gt(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    Ok(compare_operand_list(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        Comparison::Gt,\n    ))\n}\n\npub fn num_ge(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    Ok(compare_operand_list(\n        ehx,\n        b,\n        span,\n        arg_list_value,\n        Comparison::Ge,\n    ))\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/panics.rs",
    "content": "use arret_syntax::span::Span;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::error::{Error, Result};\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::intrinsic::BuildOutcome;\nuse crate::mir::value::Value;\n\nuse crate::mir::intrinsic::partial_print::{partial_pretty_print, PartialPrint};\n\npub fn panics(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::ops::*;\n\n    match partial_pretty_print(ehx, b, span, arg_list_value) {\n        // Can simplify this to a single MIR opt\n        Some(PartialPrint::Constant(message)) => {\n            b.push(span, OpKind::Panic(message));\n            Err(Error::Diverged)\n        }\n        // Still contains variables. Simplify the arguments.\n        Some(partial_print) => Ok(BuildOutcome::SimplifiedArgs(\n            partial_print.into_arg_list_value(ehx),\n        )),\n        // Declined to partial print\n        None => Ok(BuildOutcome::None),\n    }\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/partial_print.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::boxed;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::value::to_const::value_to_const;\nuse crate::mir::value::types::possible_type_tags_for_value;\nuse crate::mir::value::Value;\n\npub enum PartialPrint {\n    Constant(String),\n    SimplifiedArgs(Box<[Value]>),\n}\n\nimpl PartialPrint {\n    /// Converts the partial print to an arg list suitable for passing to a stdlib print function\n    pub fn into_arg_list_value(self, ehx: &mut EvalHirCtx) -> Value {\n        match self {\n            PartialPrint::Constant(string) => {\n                Value::List(Box::new([boxed::Str::new(ehx, &string).into()]), None)\n            }\n            PartialPrint::SimplifiedArgs(args) => Value::List(args, None),\n        }\n    }\n}\n\n/// Partially pretty prints an arg list value\n///\n/// If partial printing isn't possible or isn't an improvement over the original arg list, `None`\n/// will be returned.\npub fn partial_pretty_print(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Option<PartialPrint> {\n    let mut list_iter = arg_list_value.try_sized_list_iter()?;\n    let original_arg_count = list_iter.len();\n\n    if original_arg_count < 2 {\n        // Nothing we can do to simplify this further\n        return None;\n    }\n\n    // Accumulates our string literal\n    let mut literal_acc = String::new();\n    let mut simplified_args: Vec<Value> = vec![];\n\n    while let Some(value) = list_iter.next(b, span) {\n        // Check early for function values\n        // This is to avoid doing a full JIT just to print `#fn` and to allow\n        // printing non-constant function values.\n        if possible_type_tags_for_value(&value) == boxed::TypeTag::FunThunk.into() {\n            literal_acc.push_str(\"#fn\");\n            continue;\n        }\n\n        match value_to_const(ehx, &value) {\n            Some(boxed) => {\n                let mut output: Vec<u8> = vec![];\n                arret_runtime_syntax::writer::pretty_print_boxed(&mut output, ehx, boxed);\n\n                literal_acc.push_str(\n                    std::str::from_utf8(&output)\n                        .expect(\"pretty printed invalid UTF-8 during partial print\"),\n                );\n            }\n            None => {\n                if !literal_acc.is_empty() {\n                    simplified_args.push(boxed::Str::new(ehx, &literal_acc).into());\n                    literal_acc.clear();\n                }\n\n                simplified_args.push(value);\n            }\n        };\n    }\n\n    if simplified_args.is_empty() {\n        Some(PartialPrint::Constant(literal_acc))\n    } else {\n        // Push on the end of the accumulator\n        if !literal_acc.is_empty() {\n            simplified_args.push(boxed::Str::new(ehx, &literal_acc).into());\n        }\n\n        if simplified_args.len() < original_arg_count {\n            Some(PartialPrint::SimplifiedArgs(\n                simplified_args.into_boxed_slice(),\n            ))\n        } else {\n            // Didn't improve anything\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/print.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::boxed;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::error::Result;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::intrinsic::BuildOutcome;\nuse crate::mir::value::Value;\n\nuse crate::mir::intrinsic::partial_print::{partial_pretty_print, PartialPrint};\n\n// `print!` & `println!`\npub fn print(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    match partial_pretty_print(ehx, b, span, arg_list_value) {\n        Some(partial_print) => Ok(BuildOutcome::SimplifiedArgs(\n            partial_print.into_arg_list_value(ehx),\n        )),\n        None => Ok(BuildOutcome::None),\n    }\n}\n\n// `print-str`\npub fn print_str(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    match partial_pretty_print(ehx, b, span, arg_list_value) {\n        Some(PartialPrint::Constant(literal_str)) => Ok(BuildOutcome::ReturnValue(\n            boxed::Str::new(ehx, &literal_str).into(),\n        )),\n        Some(partial_print @ PartialPrint::SimplifiedArgs(_)) => Ok(BuildOutcome::SimplifiedArgs(\n            partial_print.into_arg_list_value(ehx),\n        )),\n        None => Ok(BuildOutcome::None),\n    }\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/testing.rs",
    "content": "use std::collections::BTreeSet;\n\nuse arret_syntax::span::Span;\n\nuse arret_runtime::boxed;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::error;\nuse crate::mir::error::{Error, Result};\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::ops;\nuse crate::mir::polymorph::PolymorphAbi;\nuse crate::mir::value;\nuse crate::mir::value::Value;\n\nfn ideal_polymorph_abi_for_arret_fun(arret_fun: &value::ArretFun) -> PolymorphAbi {\n    use crate::hir::destruc::poly_for_list_destruc;\n    use crate::mir::polymorph::polymorph_abi_for_list_ty;\n\n    let has_env = !arret_fun.env_values().free_values.is_empty();\n    let fun_expr = arret_fun.fun_expr();\n    let param_list_type = poly_for_list_destruc(&fun_expr.params);\n\n    polymorph_abi_for_list_ty(has_env, &param_list_type, &fun_expr.ret_ty)\n}\n\nfn add_ops_categories<'a>(\n    categories: &mut BTreeSet<ops::OpCategory>,\n    ops: impl IntoIterator<Item = &'a ops::Op>,\n) {\n    for op in ops {\n        categories.insert(op.kind().category());\n\n        if let ops::OpKind::Cond(cond_op) = op.kind() {\n            add_ops_categories(categories, cond_op.true_ops.iter());\n            add_ops_categories(categories, cond_op.false_ops.iter());\n        }\n    }\n}\n\nfn op_category_to_string(category: ops::OpCategory) -> &'static str {\n    use crate::mir::ops::OpCategory;\n\n    match category {\n        OpCategory::AllocBoxed => \":alloc-boxed\",\n        OpCategory::Call => \":call\",\n        OpCategory::ConstBox => \":const-box\",\n        OpCategory::ConstCastBoxed => \":const-cast-box\",\n        OpCategory::ConstReg => \":const-reg\",\n        OpCategory::Cond => \":cond\",\n        OpCategory::MakeCallback => \":make-callback\",\n        OpCategory::MemLoad => \":mem-load\",\n        OpCategory::CastBoxed => \":cast-boxed\",\n        OpCategory::RegOp => \":reg-op\",\n        OpCategory::Ret => \":ret\",\n        OpCategory::Unreachable => \":unreachable\",\n    }\n}\n\npub fn fn_op_categories(\n    ehx: &mut EvalHirCtx,\n    b: &mut Option<Builder>,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<Option<Value>> {\n    let mut iter = arg_list_value.unsized_list_iter();\n    let single_arg = iter.next_unchecked(b, span);\n\n    let arret_fun = if let Value::ArretFun(arret_fun) = single_arg {\n        arret_fun\n    } else {\n        return Err(Error::Panic(error::Panic::new(\n            span,\n            \"argument must be an Arret function\".to_owned(),\n        )));\n    };\n\n    let ideal_polymorph_abi = ideal_polymorph_abi_for_arret_fun(&arret_fun);\n    let ops_fun = ehx.ops_for_arret_fun(&arret_fun, ideal_polymorph_abi);\n\n    let mut categories = BTreeSet::<ops::OpCategory>::new();\n    add_ops_categories(&mut categories, ops_fun.ops.iter());\n\n    let category_list = boxed::List::from_values(\n        ehx,\n        categories.into_iter().map(op_category_to_string),\n        boxed::Sym::new,\n    );\n\n    Ok(Some(category_list.into()))\n}\n"
  },
  {
    "path": "compiler/mir/intrinsic/vector.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::boxed;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::error::Result;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::intrinsic::num_utils::try_value_to_i64;\nuse crate::mir::intrinsic::BuildOutcome;\nuse crate::mir::value::types::known_vector_len_for_value;\nuse crate::mir::Value;\n\npub fn vector_length(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::ops::*;\n    use crate::mir::value;\n    use crate::mir::value::build_reg::value_to_reg;\n    use arret_runtime::abitype;\n\n    let mut iter = arg_list_value.unsized_list_iter();\n    let vector_value = iter.next_unchecked(b, span);\n\n    if let Some(known_len) = known_vector_len_for_value(&vector_value) {\n        return Ok(BuildOutcome::ReturnValue(\n            boxed::Int::new(ehx, known_len as i64).into(),\n        ));\n    }\n\n    let vector_reg = value_to_reg(\n        ehx,\n        b,\n        span,\n        &vector_value,\n        &abitype::BoxedAbiType::Vector(&abitype::BoxedAbiType::Any).into(),\n    )\n    .into();\n\n    let vector_len_reg = b.push_reg(span, OpKind::LoadBoxedVectorLen, vector_reg);\n    Ok(BuildOutcome::ReturnValue(\n        value::RegValue::new(vector_len_reg, abitype::AbiType::Int).into(),\n    ))\n}\n\npub fn vector_ref(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arg_list_value: &Value,\n) -> Result<BuildOutcome> {\n    use crate::mir::vector_member;\n\n    let mut iter = arg_list_value.unsized_list_iter();\n    let vector_value = iter.next_unchecked(b, span);\n    let index_value = iter.next_unchecked(b, span);\n\n    let known_len = if let Some(known_len) = known_vector_len_for_value(&vector_value) {\n        known_len\n    } else {\n        return Ok(BuildOutcome::None);\n    };\n\n    let index = if let Some(index) = try_value_to_i64(index_value) {\n        index as usize\n    } else {\n        return Ok(BuildOutcome::None);\n    };\n\n    Ok(BuildOutcome::ReturnValue(\n        vector_member::load_vector_member(ehx, b, span, known_len, &vector_value, index),\n    ))\n}\n"
  },
  {
    "path": "compiler/mir/mod.rs",
    "content": "mod app_purity;\nmod arg_list;\nmod builder;\nmod costing;\nmod env_values;\nmod equality;\npub mod error;\npub mod eval_hir;\nmod inliner;\nmod intrinsic;\npub mod ops;\nmod optimise;\nmod polymorph;\npub mod printer;\nmod record_field;\nmod ret_value;\nmod rust_fun;\nmod specific_abi_type;\nmod tagset;\nmod typred;\nmod value;\nmod vector_member;\n\npub use eval_hir::BuiltProgram;\npub use printer::print_program;\npub use value::Value;\n\nuse crate::hir;\ntype Expr = hir::Expr<hir::Inferred>;\n"
  },
  {
    "path": "compiler/mir/ops.rs",
    "content": "use std::rc::Rc;\n\nuse arret_runtime::abitype;\nuse arret_runtime::boxed;\n\nuse arret_syntax::datum::DataStr;\nuse arret_syntax::span::Span;\n\nuse crate::codegen::GenAbi;\n\nuse crate::id_type::ArcId;\nuse crate::mir::tagset::TypeTagSet;\n\nnew_counting_id_type!(PrivateFunIdCounter, PrivateFunId);\nnew_global_id_type!(RegId);\n\n#[derive(Debug, PartialEq, Eq, Hash, Clone)]\npub enum CallConv {\n    /// C calling convention\n    ///\n    /// This is required for thunks and callbacks because they can be called from Rust.\n    Ccc,\n\n    /// Fast calling convention\n    ///\n    /// This supports tail recursion.\n    FastCc,\n}\n\n#[derive(Debug, PartialEq, Eq, Hash, Clone)]\npub struct OpsAbi {\n    pub call_conv: CallConv,\n    pub params: Box<[abitype::AbiType]>,\n    pub ret: abitype::RetAbiType,\n}\n\nimpl OpsAbi {\n    pub fn thunk_abi() -> OpsAbi {\n        OpsAbi {\n            call_conv: CallConv::Ccc,\n            params: Box::new([\n                // Captures\n                abitype::BoxedAbiType::Any.into(),\n                // Rest argument\n                abitype::TOP_LIST_BOXED_ABI_TYPE.into(),\n            ]),\n            ret: abitype::BoxedAbiType::Any.into(),\n        }\n    }\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct StaticSymbol {\n    pub symbol: &'static str,\n    pub impure: bool,\n    pub abi: GenAbi,\n}\n\n/// Represents a callable function\n///\n/// This is used instead of a `RegId` to make the ABI of the called function obvious for codegen's\n/// analysis passes.\n#[derive(Debug, PartialEq, Clone)]\npub enum Callee {\n    PrivateFun(PrivateFunId),\n    BoxedFunThunk(RegId),\n    StaticSymbol(StaticSymbol),\n}\n\n/// Represents a structure tagged with a class ID\n///\n/// This is the MIR analog of [`record::Cons`](crate::ty::record::Cons)\n#[derive(Debug, PartialEq, Clone)]\npub struct RecordStruct {\n    pub source_name: DataStr,\n    pub field_abi_types: Box<[abitype::AbiType]>,\n}\n\npub type RecordStructId = ArcId<RecordStruct>;\n\nimpl RecordStruct {\n    pub fn new(source_name: DataStr, field_abi_types: Box<[abitype::AbiType]>) -> RecordStructId {\n        ArcId::new(RecordStruct {\n            source_name,\n            field_abi_types,\n        })\n    }\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct CallOp {\n    pub callee: Callee,\n    pub impure: bool,\n    pub args: Box<[RegId]>,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct TailCallOp {\n    pub impure: bool,\n    pub args: Box<[RegId]>,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct BoxPairOp {\n    pub head_reg: RegId,\n    pub rest_reg: RegId,\n    pub list_len_reg: RegId,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct BoxFunThunkOp {\n    pub captures_reg: RegId,\n    pub callee: Callee,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct BoxRecordOp {\n    pub record_struct: RecordStructId,\n    pub field_regs: Box<[RegId]>,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct LoadBoxedRecordFieldOp {\n    pub record_reg: RegId,\n    pub record_struct: RecordStructId,\n    pub field_index: usize,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct LoadBoxedVectorMemberOp {\n    pub vector_reg: RegId,\n    /// Exact known length of the vector we're loading from\n    pub known_vector_len: usize,\n    pub member_index: usize,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct CastBoxedOp {\n    pub from_reg: RegId,\n    pub to_type: abitype::BoxedAbiType,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct RegPhi {\n    pub output_reg: RegId,\n    pub true_result_reg: RegId,\n    pub false_result_reg: RegId,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct CondOp {\n    pub reg_phi: Option<RegPhi>,\n    pub test_reg: RegId,\n    pub true_ops: Box<[Op]>,\n    pub false_ops: Box<[Op]>,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct BinaryOp {\n    pub lhs_reg: RegId,\n    pub rhs_reg: RegId,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct CompareOp {\n    pub comparison: Comparison,\n    pub lhs_reg: RegId,\n    pub rhs_reg: RegId,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct LoadBoxedTypeTagOp {\n    pub subject_reg: RegId,\n    pub possible_type_tags: TypeTagSet,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct LoadBoxedListLenOp {\n    pub list_reg: RegId,\n    pub min_list_len: usize,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct MakeCallbackOp {\n    pub captures_reg: RegId,\n    pub callee: Callee,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct ShiftOp {\n    pub int_reg: RegId,\n    pub bit_count: u32,\n}\n\n#[derive(Debug, PartialEq, Clone, Copy)]\npub enum Comparison {\n    Lt,\n    Le,\n    Eq,\n    Gt,\n    Ge,\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub enum OpKind {\n    ConstInt64(RegId, i64),\n    ConstFloat(RegId, f64),\n    ConstChar(RegId, char),\n    ConstBool(RegId, bool),\n    ConstInternedSym(RegId, Rc<str>),\n    ConstTypeTag(RegId, boxed::TypeTag),\n    ConstRecordClassId(RegId, RecordStructId),\n\n    ConstBoxedNil(RegId, ()),\n    ConstBoxedTrue(RegId, ()),\n    ConstBoxedFalse(RegId, ()),\n    ConstBoxedInt(RegId, i64),\n    ConstBoxedFloat(RegId, f64),\n    ConstBoxedChar(RegId, char),\n    ConstBoxedStr(RegId, Box<str>),\n    ConstBoxedSym(RegId, Rc<str>),\n    ConstBoxedPair(RegId, BoxPairOp),\n    ConstBoxedFunThunk(RegId, BoxFunThunkOp),\n    ConstBoxedVector(RegId, Box<[RegId]>),\n    ConstBoxedSet(RegId, Box<[RegId]>),\n    ConstBoxedMap(RegId, Box<[(RegId, RegId)]>),\n\n    AllocBoxedInt(RegId, RegId),\n    AllocBoxedFloat(RegId, RegId),\n    AllocBoxedChar(RegId, RegId),\n    AllocBoxedSym(RegId, RegId),\n    AllocBoxedPair(RegId, BoxPairOp),\n    AllocBoxedFunThunk(RegId, BoxFunThunkOp),\n\n    ConstCastBoxed(RegId, CastBoxedOp),\n    CastBoxed(RegId, CastBoxedOp),\n    Alias(RegId, RegId), // TODO: This is a hack for `duplicate_alloc_ops`\n\n    Call(RegId, CallOp),\n    TailCall(RegId, TailCallOp),\n\n    LoadBoxedTypeTag(RegId, LoadBoxedTypeTagOp),\n    LoadBoxedListLen(RegId, LoadBoxedListLenOp),\n    LoadBoxedPairHead(RegId, RegId),\n    LoadBoxedPairRest(RegId, RegId),\n    LoadBoxedIntValue(RegId, RegId),\n    LoadBoxedFloatValue(RegId, RegId),\n    LoadBoxedCharValue(RegId, RegId),\n    LoadBoxedSymInterned(RegId, RegId),\n    LoadBoxedFunThunkCaptures(RegId, RegId),\n    LoadBoxedRecordClassId(RegId, RegId),\n    LoadBoxedVectorLen(RegId, RegId),\n    LoadBoxedVectorMember(RegId, LoadBoxedVectorMemberOp),\n\n    Cond(CondOp),\n\n    MakeCallback(RegId, MakeCallbackOp),\n\n    BoolEqual(RegId, BinaryOp),\n    CharEqual(RegId, BinaryOp),\n    InternedSymEqual(RegId, BinaryOp),\n    TypeTagEqual(RegId, BinaryOp),\n    RecordClassIdEqual(RegId, BinaryOp),\n    BoxIdentical(RegId, BinaryOp),\n    Int64ToFloat(RegId, RegId),\n\n    IntCompare(RegId, CompareOp),\n    FloatCompare(RegId, CompareOp),\n\n    FloatAdd(RegId, BinaryOp),\n    Int64Add(RegId, BinaryOp),\n    Int64CheckedAdd(RegId, BinaryOp),\n    FloatMul(RegId, BinaryOp),\n    Int64CheckedMul(RegId, BinaryOp),\n    FloatSub(RegId, BinaryOp),\n    Int64CheckedSub(RegId, BinaryOp),\n    FloatDiv(RegId, BinaryOp),\n    Int64Div(RegId, BinaryOp),\n    Int64CheckedDiv(RegId, BinaryOp),\n    Int64Rem(RegId, BinaryOp),\n    Int64CheckedRem(RegId, BinaryOp),\n    FloatSqrt(RegId, RegId),\n\n    Int64BitwiseAnd(RegId, BinaryOp),\n    Int64BitwiseOr(RegId, BinaryOp),\n    Int64BitwiseXor(RegId, BinaryOp),\n    Int64BitwiseNot(RegId, RegId),\n    Int64ShiftLeft(RegId, ShiftOp),\n    Int64LogicalShiftRight(RegId, ShiftOp),\n    Int64ArithmeticShiftRight(RegId, ShiftOp),\n\n    ConstBoxedRecord(RegId, BoxRecordOp),\n    AllocBoxedRecord(RegId, BoxRecordOp),\n    LoadBoxedRecordField(RegId, LoadBoxedRecordFieldOp),\n\n    Ret(RegId),\n    RetVoid,\n    Unreachable,\n    Panic(String),\n}\n\n/// Indicates the high-level category of an op\n#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]\npub enum OpCategory {\n    ConstReg,\n    ConstBox,\n    ConstCastBoxed,\n    AllocBoxed,\n    Call,\n    Cond,\n    MemLoad,\n    CastBoxed,\n    RegOp,\n    MakeCallback,\n    Ret,\n    Unreachable,\n}\n\nimpl OpKind {\n    pub fn output_reg(&self) -> Option<RegId> {\n        use crate::mir::ops::OpKind::*;\n\n        match self {\n            ConstBoxedNil(reg_id, _)\n            | ConstBoxedTrue(reg_id, _)\n            | ConstBoxedFalse(reg_id, _)\n            | ConstInt64(reg_id, _)\n            | ConstFloat(reg_id, _)\n            | ConstChar(reg_id, _)\n            | ConstBool(reg_id, _)\n            | ConstInternedSym(reg_id, _)\n            | ConstTypeTag(reg_id, _)\n            | ConstBoxedInt(reg_id, _)\n            | ConstBoxedFloat(reg_id, _)\n            | ConstBoxedChar(reg_id, _)\n            | ConstBoxedStr(reg_id, _)\n            | ConstBoxedSym(reg_id, _)\n            | ConstBoxedPair(reg_id, _)\n            | ConstBoxedFunThunk(reg_id, _)\n            | ConstBoxedVector(reg_id, _)\n            | ConstBoxedSet(reg_id, _)\n            | ConstBoxedMap(reg_id, _)\n            | ConstRecordClassId(reg_id, _)\n            | AllocBoxedInt(reg_id, _)\n            | AllocBoxedFloat(reg_id, _)\n            | AllocBoxedChar(reg_id, _)\n            | AllocBoxedSym(reg_id, _)\n            | AllocBoxedPair(reg_id, _)\n            | AllocBoxedFunThunk(reg_id, _)\n            | ConstCastBoxed(reg_id, _)\n            | CastBoxed(reg_id, _)\n            | Alias(reg_id, _)\n            | Call(reg_id, _)\n            | TailCall(reg_id, _)\n            | LoadBoxedTypeTag(reg_id, _)\n            | LoadBoxedListLen(reg_id, _)\n            | LoadBoxedPairHead(reg_id, _)\n            | LoadBoxedPairRest(reg_id, _)\n            | LoadBoxedIntValue(reg_id, _)\n            | LoadBoxedSymInterned(reg_id, _)\n            | LoadBoxedFloatValue(reg_id, _)\n            | LoadBoxedCharValue(reg_id, _)\n            | LoadBoxedFunThunkCaptures(reg_id, _)\n            | LoadBoxedRecordClassId(reg_id, _)\n            | LoadBoxedRecordField(reg_id, _)\n            | LoadBoxedVectorLen(reg_id, _)\n            | LoadBoxedVectorMember(reg_id, _)\n            | FloatAdd(reg_id, _)\n            | Int64Add(reg_id, _)\n            | Int64CheckedAdd(reg_id, _)\n            | FloatMul(reg_id, _)\n            | Int64CheckedMul(reg_id, _)\n            | FloatSub(reg_id, _)\n            | Int64CheckedSub(reg_id, _)\n            | FloatDiv(reg_id, _)\n            | Int64Div(reg_id, _)\n            | Int64CheckedDiv(reg_id, _)\n            | Int64Rem(reg_id, _)\n            | Int64CheckedRem(reg_id, _)\n            | FloatSqrt(reg_id, _)\n            | Int64BitwiseAnd(reg_id, _)\n            | Int64BitwiseOr(reg_id, _)\n            | Int64BitwiseXor(reg_id, _)\n            | Int64BitwiseNot(reg_id, _)\n            | Int64ShiftLeft(reg_id, _)\n            | Int64ArithmeticShiftRight(reg_id, _)\n            | Int64LogicalShiftRight(reg_id, _)\n            | IntCompare(reg_id, _)\n            | BoolEqual(reg_id, _)\n            | CharEqual(reg_id, _)\n            | InternedSymEqual(reg_id, _)\n            | TypeTagEqual(reg_id, _)\n            | RecordClassIdEqual(reg_id, _)\n            | FloatCompare(reg_id, _)\n            | BoxIdentical(reg_id, _)\n            | Int64ToFloat(reg_id, _)\n            | MakeCallback(reg_id, _)\n            | ConstBoxedRecord(reg_id, _)\n            | AllocBoxedRecord(reg_id, _) => Some(*reg_id),\n            Cond(cond_op) => cond_op.reg_phi.clone().map(|reg_phi| reg_phi.output_reg),\n            Ret(_) | RetVoid | Unreachable | Panic(_) => None,\n        }\n    }\n\n    pub fn add_input_regs(&self, coll: &mut impl Extend<RegId>) {\n        use crate::mir::ops::OpKind::*;\n        use std::iter;\n\n        match self {\n            ConstBoxedNil(_, _)\n            | ConstBoxedTrue(_, _)\n            | ConstBoxedFalse(_, _)\n            | ConstInt64(_, _)\n            | ConstFloat(_, _)\n            | ConstChar(_, _)\n            | ConstBool(_, _)\n            | ConstInternedSym(_, _)\n            | ConstTypeTag(_, _)\n            | ConstRecordClassId(_, _)\n            | ConstBoxedInt(_, _)\n            | ConstBoxedFloat(_, _)\n            | ConstBoxedChar(_, _)\n            | ConstBoxedStr(_, _)\n            | ConstBoxedSym(_, _)\n            | RetVoid\n            | Unreachable\n            | Panic(_) => {}\n            ConstBoxedPair(_, box_pair_op) | AllocBoxedPair(_, box_pair_op) => {\n                coll.extend(\n                    [\n                        box_pair_op.list_len_reg,\n                        box_pair_op.head_reg,\n                        box_pair_op.rest_reg,\n                    ]\n                    .iter()\n                    .cloned(),\n                );\n            }\n            ConstBoxedFunThunk(_, box_fun_thunk_op) | AllocBoxedFunThunk(_, box_fun_thunk_op) => {\n                coll.extend(iter::once(box_fun_thunk_op.captures_reg));\n            }\n            ConstBoxedVector(_, element_regs) | ConstBoxedSet(_, element_regs) => {\n                coll.extend(element_regs.iter().copied())\n            }\n            ConstBoxedMap(_, entry_regs) => {\n                coll.extend(\n                    entry_regs\n                        .iter()\n                        .map(|(key, _)| *key)\n                        .chain(entry_regs.iter().map(|(_, value)| *value)),\n                );\n            }\n            AllocBoxedInt(_, reg_id)\n            | AllocBoxedFloat(_, reg_id)\n            | AllocBoxedChar(_, reg_id)\n            | AllocBoxedSym(_, reg_id)\n            | ConstCastBoxed(\n                _,\n                CastBoxedOp {\n                    from_reg: reg_id, ..\n                },\n            )\n            | CastBoxed(\n                _,\n                CastBoxedOp {\n                    from_reg: reg_id, ..\n                },\n            )\n            | Alias(_, reg_id)\n            | Ret(reg_id)\n            | LoadBoxedTypeTag(\n                _,\n                LoadBoxedTypeTagOp {\n                    subject_reg: reg_id,\n                    ..\n                },\n            )\n            | LoadBoxedListLen(\n                _,\n                LoadBoxedListLenOp {\n                    list_reg: reg_id, ..\n                },\n            )\n            | LoadBoxedPairHead(_, reg_id)\n            | LoadBoxedPairRest(_, reg_id)\n            | LoadBoxedIntValue(_, reg_id)\n            | LoadBoxedFloatValue(_, reg_id)\n            | LoadBoxedCharValue(_, reg_id)\n            | LoadBoxedSymInterned(_, reg_id)\n            | LoadBoxedFunThunkCaptures(_, reg_id)\n            | LoadBoxedRecordClassId(_, reg_id)\n            | LoadBoxedRecordField(\n                _,\n                LoadBoxedRecordFieldOp {\n                    record_reg: reg_id, ..\n                },\n            )\n            | LoadBoxedVectorLen(_, reg_id)\n            | LoadBoxedVectorMember(\n                _,\n                LoadBoxedVectorMemberOp {\n                    vector_reg: reg_id, ..\n                },\n            )\n            | Int64ToFloat(_, reg_id)\n            | FloatSqrt(_, reg_id)\n            | Int64BitwiseNot(_, reg_id)\n            | Int64ShiftLeft(\n                _,\n                ShiftOp {\n                    int_reg: reg_id, ..\n                },\n            )\n            | Int64ArithmeticShiftRight(\n                _,\n                ShiftOp {\n                    int_reg: reg_id, ..\n                },\n            )\n            | Int64LogicalShiftRight(\n                _,\n                ShiftOp {\n                    int_reg: reg_id, ..\n                },\n            )\n            | MakeCallback(\n                _,\n                MakeCallbackOp {\n                    captures_reg: reg_id,\n                    ..\n                },\n            ) => {\n                coll.extend(iter::once(*reg_id));\n            }\n            Call(_, CallOp { args, .. }) | TailCall(_, TailCallOp { args, .. }) => {\n                coll.extend(args.iter().cloned());\n            }\n            Cond(cond_op) => {\n                coll.extend(iter::once(cond_op.test_reg));\n\n                if let Some(reg_phi) = &cond_op.reg_phi {\n                    coll.extend(\n                        [reg_phi.true_result_reg, reg_phi.false_result_reg]\n                            .iter()\n                            .cloned(),\n                    );\n                }\n\n                for op in cond_op.true_ops.iter().chain(cond_op.false_ops.iter()) {\n                    op.kind().add_input_regs(coll);\n                }\n            }\n            FloatAdd(_, binary_op)\n            | Int64Add(_, binary_op)\n            | Int64CheckedAdd(_, binary_op)\n            | FloatMul(_, binary_op)\n            | Int64CheckedMul(_, binary_op)\n            | FloatSub(_, binary_op)\n            | Int64CheckedSub(_, binary_op)\n            | FloatDiv(_, binary_op)\n            | Int64Div(_, binary_op)\n            | Int64CheckedDiv(_, binary_op)\n            | Int64Rem(_, binary_op)\n            | Int64CheckedRem(_, binary_op)\n            | Int64BitwiseAnd(_, binary_op)\n            | Int64BitwiseOr(_, binary_op)\n            | Int64BitwiseXor(_, binary_op)\n            | BoolEqual(_, binary_op)\n            | CharEqual(_, binary_op)\n            | InternedSymEqual(_, binary_op)\n            | TypeTagEqual(_, binary_op)\n            | RecordClassIdEqual(_, binary_op)\n            | BoxIdentical(_, binary_op) => {\n                coll.extend([binary_op.lhs_reg, binary_op.rhs_reg].iter().cloned());\n            }\n            IntCompare(_, compare_op) | FloatCompare(_, compare_op) => {\n                coll.extend([compare_op.lhs_reg, compare_op.rhs_reg].iter().cloned());\n            }\n            ConstBoxedRecord(_, box_record_op) | AllocBoxedRecord(_, box_record_op) => {\n                coll.extend(box_record_op.field_regs.iter().cloned());\n            }\n        }\n    }\n\n    /// Indicates if the output of this op is a constant\n    pub fn const_output(&self) -> bool {\n        [\n            OpCategory::ConstBox,\n            OpCategory::ConstReg,\n            OpCategory::ConstCastBoxed,\n        ]\n        .contains(&self.category())\n    }\n\n    pub fn has_side_effects(&self) -> bool {\n        use crate::mir::ops::OpKind::*;\n\n        match self {\n            Ret(_) | RetVoid | Unreachable | Panic(_) => true,\n            Call(_, CallOp { impure, .. }) | TailCall(_, TailCallOp { impure, .. }) => *impure,\n            Cond(cond_op) => cond_op\n                .true_ops\n                .iter()\n                .chain(cond_op.false_ops.iter())\n                .any(|op| op.kind().has_side_effects()),\n            _ => false,\n        }\n    }\n\n    pub fn is_terminator(&self) -> bool {\n        let category = self.category();\n        category == OpCategory::Ret || category == OpCategory::Unreachable\n    }\n\n    pub fn category(&self) -> OpCategory {\n        use crate::mir::ops::OpKind::*;\n\n        match self {\n            ConstInt64(_, _)\n            | ConstFloat(_, _)\n            | ConstChar(_, _)\n            | ConstBool(_, _)\n            | ConstInternedSym(_, _)\n            | ConstTypeTag(_, _)\n            | ConstRecordClassId(_, _) => OpCategory::ConstReg,\n\n            ConstBoxedNil(_, _)\n            | ConstBoxedTrue(_, _)\n            | ConstBoxedFalse(_, _)\n            | ConstBoxedInt(_, _)\n            | ConstBoxedFloat(_, _)\n            | ConstBoxedChar(_, _)\n            | ConstBoxedStr(_, _)\n            | ConstBoxedSym(_, _)\n            | ConstBoxedPair(_, _)\n            | ConstBoxedFunThunk(_, _)\n            | ConstBoxedRecord(_, _)\n            | ConstBoxedVector(_, _)\n            | ConstBoxedSet(_, _)\n            | ConstBoxedMap(_, _) => OpCategory::ConstBox,\n\n            AllocBoxedInt(_, _)\n            | AllocBoxedFloat(_, _)\n            | AllocBoxedChar(_, _)\n            | AllocBoxedSym(_, _)\n            | AllocBoxedPair(_, _)\n            | AllocBoxedFunThunk(_, _)\n            | AllocBoxedRecord(_, _) => OpCategory::AllocBoxed,\n\n            LoadBoxedTypeTag(_, _)\n            | LoadBoxedListLen(_, _)\n            | LoadBoxedPairHead(_, _)\n            | LoadBoxedPairRest(_, _)\n            | LoadBoxedIntValue(_, _)\n            | LoadBoxedFloatValue(_, _)\n            | LoadBoxedCharValue(_, _)\n            | LoadBoxedSymInterned(_, _)\n            | LoadBoxedFunThunkCaptures(_, _)\n            | LoadBoxedRecordClassId(_, _)\n            | LoadBoxedRecordField(_, _)\n            | LoadBoxedVectorLen(_, _)\n            | LoadBoxedVectorMember(_, _) => OpCategory::MemLoad,\n\n            FloatAdd(_, _)\n            | Int64Add(_, _)\n            | Int64CheckedAdd(_, _)\n            | FloatSub(_, _)\n            | Int64CheckedSub(_, _)\n            | FloatMul(_, _)\n            | Int64CheckedMul(_, _)\n            | FloatDiv(_, _)\n            | Int64Div(_, _)\n            | Int64CheckedDiv(_, _)\n            | Int64Rem(_, _)\n            | Int64CheckedRem(_, _)\n            | FloatSqrt(_, _)\n            | IntCompare(_, _)\n            | BoolEqual(_, _)\n            | CharEqual(_, _)\n            | InternedSymEqual(_, _)\n            | TypeTagEqual(_, _)\n            | RecordClassIdEqual(_, _)\n            | FloatCompare(_, _)\n            | BoxIdentical(_, _)\n            | Int64ToFloat(_, _)\n            | Int64BitwiseAnd(_, _)\n            | Int64BitwiseOr(_, _)\n            | Int64BitwiseXor(_, _)\n            | Int64BitwiseNot(_, _)\n            | Int64ShiftLeft(_, _)\n            | Int64ArithmeticShiftRight(_, _)\n            | Int64LogicalShiftRight(_, _) => OpCategory::RegOp,\n\n            Ret(_) | RetVoid => OpCategory::Ret,\n\n            Cond(_) => OpCategory::Cond,\n            MakeCallback(_, _) => OpCategory::MakeCallback,\n            ConstCastBoxed(_, _) => OpCategory::ConstCastBoxed,\n            CastBoxed(_, _) | Alias(_, _) => OpCategory::CastBoxed,\n\n            Call(_, _) | TailCall(_, _) => OpCategory::Call,\n\n            Unreachable | Panic(_) => OpCategory::Unreachable,\n        }\n    }\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct Op {\n    pub span: Span,\n    pub kind: OpKind,\n}\n\nimpl Op {\n    pub fn new(span: Span, kind: OpKind) -> Op {\n        Op { span, kind }\n    }\n\n    pub fn span(&self) -> Span {\n        self.span\n    }\n\n    pub fn kind(&self) -> &OpKind {\n        &self.kind\n    }\n}\n\npub struct Fun {\n    pub span: Span,\n    pub source_name: Option<DataStr>,\n\n    pub abi: OpsAbi,\n    pub param_regs: Box<[RegId]>,\n    pub ops: Box<[Op]>,\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use std::collections::HashSet;\n\n    impl From<OpKind> for Op {\n        fn from(op_kind: OpKind) -> Self {\n            Op::new(crate::source::EMPTY_SPAN, op_kind)\n        }\n    }\n\n    #[test]\n    fn output_reg() {\n        let reg1 = RegId::alloc();\n\n        assert_eq!(None, OpKind::RetVoid.output_reg());\n        assert_eq!(None, OpKind::Ret(reg1).output_reg());\n        assert_eq!(Some(reg1), OpKind::ConstInt64(reg1, 14).output_reg());\n    }\n\n    #[test]\n    fn has_side_effects() {\n        let reg1 = RegId::alloc();\n\n        assert!(OpKind::RetVoid.has_side_effects());\n        assert!(!OpKind::ConstInt64(reg1, 14).has_side_effects());\n\n        let cond_op_with_no_side_effects = CondOp {\n            reg_phi: None,\n            test_reg: reg1,\n            true_ops: Box::new([]),\n            false_ops: Box::new([]),\n        };\n\n        assert!(!OpKind::Cond(cond_op_with_no_side_effects).has_side_effects());\n\n        let cond_op_with_true_side_effects = CondOp {\n            reg_phi: None,\n            test_reg: reg1,\n            true_ops: Box::new([OpKind::RetVoid.into()]),\n            false_ops: Box::new([]),\n        };\n\n        assert!(OpKind::Cond(cond_op_with_true_side_effects).has_side_effects());\n\n        let cond_op_with_false_side_effects = CondOp {\n            reg_phi: None,\n            test_reg: reg1,\n            true_ops: Box::new([]),\n            false_ops: Box::new([OpKind::RetVoid.into()]),\n        };\n\n        assert!(OpKind::Cond(cond_op_with_false_side_effects).has_side_effects());\n    }\n\n    #[test]\n    fn const_output() {\n        assert!(OpKind::ConstBool(RegId::alloc(), true).const_output());\n        assert!(OpKind::ConstBoxedFalse(RegId::alloc(), ()).const_output());\n        assert!(OpKind::ConstCastBoxed(\n            RegId::alloc(),\n            CastBoxedOp {\n                from_reg: RegId::alloc(),\n                to_type: abitype::BoxedAbiType::Any\n            }\n        )\n        .const_output());\n\n        assert!(!OpKind::AllocBoxedInt(RegId::alloc(), RegId::alloc()).const_output());\n        assert!(!OpKind::LoadBoxedListLen(\n            RegId::alloc(),\n            LoadBoxedListLenOp {\n                list_reg: RegId::alloc(),\n                min_list_len: 0\n            }\n        )\n        .const_output());\n    }\n\n    #[test]\n    fn ret_input_regs() {\n        let mut used_regs = HashSet::<RegId>::new();\n\n        let ret_reg = RegId::alloc();\n        OpKind::Ret(ret_reg).add_input_regs(&mut used_regs);\n\n        assert_eq!(1, used_regs.len());\n        assert!(used_regs.contains(&ret_reg));\n    }\n\n    #[test]\n    fn cond_input_regs() {\n        let mut used_regs = HashSet::<RegId>::new();\n\n        let output_reg = RegId::alloc();\n        let test_reg = RegId::alloc();\n\n        let true_input_reg = RegId::alloc();\n        let true_result_reg = RegId::alloc();\n\n        let false_input_reg = RegId::alloc();\n        let false_result_reg = RegId::alloc();\n\n        let cond_op = CondOp {\n            reg_phi: Some(RegPhi {\n                output_reg,\n                true_result_reg,\n                false_result_reg,\n            }),\n            test_reg,\n            true_ops: Box::new([OpKind::Ret(true_input_reg).into()]),\n            false_ops: Box::new([OpKind::Ret(false_input_reg).into()]),\n        };\n\n        OpKind::Cond(cond_op).add_input_regs(&mut used_regs);\n\n        for used_reg in &[\n            test_reg,\n            true_input_reg,\n            true_result_reg,\n            false_input_reg,\n            false_result_reg,\n        ] {\n            assert!(used_regs.contains(used_reg));\n        }\n\n        assert!(!used_regs.contains(&output_reg));\n    }\n}\n"
  },
  {
    "path": "compiler/mir/optimise/duplicate_alloc_ops.rs",
    "content": "use std::collections::HashMap;\n\nuse crate::mir::ops;\n\nfn visit_simple_alloc_op_kind(\n    op_kind: &mut ops::OpKind,\n    boxed_reg: ops::RegId,\n    native_reg: ops::RegId,\n    native_to_boxed: &mut HashMap<ops::RegId, ops::RegId>,\n) {\n    use std::collections::hash_map::Entry;\n\n    match native_to_boxed.entry(native_reg) {\n        Entry::Occupied(existing_output) => {\n            *op_kind = ops::OpKind::Alias(boxed_reg, *existing_output.get())\n        }\n        Entry::Vacant(vacant_entry) => {\n            vacant_entry.insert(boxed_reg);\n        }\n    }\n}\n\nfn remove_branch_redundant_alloc_ops(\n    ops: &mut [ops::Op],\n    // We can use a single `HashMap` because simple alloc ops all take distinct native reg types\n    native_to_boxed: &mut HashMap<ops::RegId, ops::RegId>,\n) {\n    for op in ops.iter_mut() {\n        match op.kind {\n            ops::OpKind::AllocBoxedInt(boxed_reg, native_reg)\n            | ops::OpKind::AllocBoxedFloat(boxed_reg, native_reg)\n            | ops::OpKind::AllocBoxedChar(boxed_reg, native_reg)\n            | ops::OpKind::AllocBoxedSym(boxed_reg, native_reg) => {\n                visit_simple_alloc_op_kind(&mut op.kind, boxed_reg, native_reg, native_to_boxed);\n            }\n            ops::OpKind::LoadBoxedIntValue(native_reg, boxed_reg)\n            | ops::OpKind::LoadBoxedFloatValue(native_reg, boxed_reg)\n            | ops::OpKind::LoadBoxedCharValue(native_reg, boxed_reg)\n            | ops::OpKind::LoadBoxedSymInterned(native_reg, boxed_reg) => {\n                native_to_boxed.insert(native_reg, boxed_reg);\n            }\n            ops::OpKind::Cond(ref mut cond_op) => {\n                remove_branch_redundant_alloc_ops(\n                    &mut cond_op.true_ops,\n                    &mut native_to_boxed.clone(),\n                );\n                remove_branch_redundant_alloc_ops(\n                    &mut cond_op.false_ops,\n                    &mut native_to_boxed.clone(),\n                );\n            }\n            _ => {}\n        }\n    }\n}\n\n/// Updates `ops` in-place to replace allocs of the same native value with `OpKind::Alias`\npub fn remove_redundant_alloc_ops(ops: &mut [ops::Op]) {\n    let mut native_to_boxed = HashMap::new();\n    remove_branch_redundant_alloc_ops(ops, &mut native_to_boxed)\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::source::EMPTY_SPAN;\n\n    #[test]\n    fn test_box_different_native_regs() {\n        let native_reg1 = ops::RegId::alloc();\n        let boxed_reg1 = ops::RegId::alloc();\n\n        let native_reg2 = ops::RegId::alloc();\n        let boxed_reg2 = ops::RegId::alloc();\n\n        let ops = &mut [\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::AllocBoxedInt(boxed_reg1, native_reg1),\n            ),\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::AllocBoxedInt(boxed_reg2, native_reg2),\n            ),\n        ];\n\n        // Should be identical because we're boxing different native values\n        let expected_ops = ops.clone();\n\n        remove_redundant_alloc_ops(ops);\n        assert_eq!(&expected_ops, ops);\n    }\n\n    #[test]\n    fn test_box_same_native_regs() {\n        let native_reg1 = ops::RegId::alloc();\n        let boxed_reg1 = ops::RegId::alloc();\n\n        let boxed_reg2 = ops::RegId::alloc();\n\n        let ops = &mut [\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::AllocBoxedFloat(boxed_reg1, native_reg1),\n            ),\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::AllocBoxedFloat(boxed_reg2, native_reg1),\n            ),\n        ];\n\n        let expected_ops = &[\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::AllocBoxedFloat(boxed_reg1, native_reg1),\n            ),\n            // Should remove the redundant alloc of the same native value\n            ops::Op::new(EMPTY_SPAN, ops::OpKind::Alias(boxed_reg2, boxed_reg1)),\n        ];\n\n        remove_redundant_alloc_ops(ops);\n        assert_eq!(expected_ops, ops);\n    }\n\n    #[test]\n    fn test_reboxing() {\n        let native_reg1 = ops::RegId::alloc();\n        let boxed_reg1 = ops::RegId::alloc();\n        let boxed_reg2 = ops::RegId::alloc();\n\n        let ops = &mut [\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::LoadBoxedSymInterned(native_reg1, boxed_reg1),\n            ),\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::AllocBoxedSym(boxed_reg2, native_reg1),\n            ),\n        ];\n\n        let expected_ops = &[\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::LoadBoxedSymInterned(native_reg1, boxed_reg1),\n            ),\n            // Should re-use the original box we got the native value from\n            ops::Op::new(EMPTY_SPAN, ops::OpKind::Alias(boxed_reg2, boxed_reg1)),\n        ];\n\n        remove_redundant_alloc_ops(ops);\n        assert_eq!(expected_ops, ops);\n    }\n\n    #[test]\n    fn test_cond_branch() {\n        let outer_native_reg1 = ops::RegId::alloc();\n        let outer_boxed_reg1 = ops::RegId::alloc();\n\n        let test_reg = ops::RegId::alloc();\n\n        let branch_boxed_reg1 = ops::RegId::alloc();\n        let branch_native_reg2 = ops::RegId::alloc();\n        let branch_boxed_reg2 = ops::RegId::alloc();\n\n        let outer_boxed_reg2 = ops::RegId::alloc();\n\n        let ops = &mut [\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::AllocBoxedChar(outer_boxed_reg1, outer_native_reg1),\n            ),\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::Cond(ops::CondOp {\n                    reg_phi: None,\n                    test_reg,\n                    true_ops: Box::new([\n                        ops::Op::new(\n                            EMPTY_SPAN,\n                            ops::OpKind::AllocBoxedChar(branch_boxed_reg1, outer_native_reg1),\n                        ),\n                        ops::Op::new(\n                            EMPTY_SPAN,\n                            ops::OpKind::AllocBoxedChar(branch_boxed_reg2, branch_native_reg2),\n                        ),\n                    ]),\n                    false_ops: Box::new([]),\n                }),\n            ),\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::AllocBoxedChar(outer_boxed_reg2, branch_native_reg2),\n            ),\n        ];\n\n        let expected_ops = &[\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::AllocBoxedChar(outer_boxed_reg1, outer_native_reg1),\n            ),\n            ops::Op::new(\n                EMPTY_SPAN,\n                ops::OpKind::Cond(ops::CondOp {\n                    reg_phi: None,\n                    test_reg,\n                    true_ops: Box::new([\n                        ops::Op::new(\n                            EMPTY_SPAN,\n                            // We can use the alloc from outside this branch\n                            ops::OpKind::Alias(branch_boxed_reg1, outer_boxed_reg1),\n                        ),\n                        ops::Op::new(\n                            EMPTY_SPAN,\n                            ops::OpKind::AllocBoxedChar(branch_boxed_reg2, branch_native_reg2),\n                        ),\n                    ]),\n                    false_ops: Box::new([]),\n                }),\n            ),\n            ops::Op::new(\n                EMPTY_SPAN,\n                // We can't use an alloc from within the branch\n                ops::OpKind::AllocBoxedChar(outer_boxed_reg2, branch_native_reg2),\n            ),\n        ];\n\n        remove_redundant_alloc_ops(ops);\n        assert_eq!(expected_ops, ops);\n    }\n}\n"
  },
  {
    "path": "compiler/mir/optimise/mod.rs",
    "content": "use crate::mir::ops;\nuse crate::mir::value::Value;\n\nmod duplicate_alloc_ops;\nmod unused_ops;\n\npub fn optimise_fun(fun: ops::Fun) -> ops::Fun {\n    let mut used_ops = unused_ops::remove_unused_fun_ops(fun.ops);\n    duplicate_alloc_ops::remove_redundant_alloc_ops(&mut used_ops);\n\n    ops::Fun {\n        ops: unused_ops::remove_unused_fun_ops(used_ops),\n        ..fun\n    }\n}\n\n/// Optimise a function that has been inlined and returned the provided value\npub fn optimise_inlined_fun(ops: Box<[ops::Op]>, return_value: &Value) -> Box<[ops::Op]> {\n    let mut used_ops = unused_ops::remove_unused_value_ops(ops, return_value);\n    duplicate_alloc_ops::remove_redundant_alloc_ops(&mut used_ops);\n\n    used_ops\n}\n"
  },
  {
    "path": "compiler/mir/optimise/unused_ops.rs",
    "content": "use std::collections::HashSet;\n\nuse crate::mir::ops;\nuse crate::mir::value::Value;\n\nfn remove_unused_cond_ops(\n    cond_op: ops::CondOp,\n    used_regs: &mut HashSet<ops::RegId>,\n) -> Option<ops::CondOp> {\n    let ops::CondOp {\n        reg_phi,\n        test_reg,\n        true_ops,\n        false_ops,\n    } = cond_op;\n\n    // Determine if our output is used\n    let used_reg_phi = reg_phi.filter(|reg_phi| used_regs.contains(&reg_phi.output_reg));\n\n    // Instead of cloning `used_regs` just rollback any changes we make do it\n    let (rollback_true_reg, rollback_false_reg) = match used_reg_phi {\n        Some(ops::RegPhi {\n            true_result_reg,\n            false_result_reg,\n            ..\n        }) => (\n            Some(true_result_reg).filter(|_| !used_regs.insert(true_result_reg)),\n            Some(false_result_reg).filter(|_| !used_regs.insert(false_result_reg)),\n        ),\n        _ => (None, None),\n    };\n\n    let used_true_ops = remove_unused_branch_ops(true_ops, used_regs);\n    let used_false_ops = remove_unused_branch_ops(false_ops, used_regs);\n\n    if used_true_ops.is_empty() && used_false_ops.is_empty() && used_reg_phi.is_none() {\n        // We can disappear!\n        if let Some(rollback_true_reg) = rollback_true_reg {\n            used_regs.remove(&rollback_true_reg);\n        }\n        if let Some(rollback_false_reg) = rollback_false_reg {\n            used_regs.remove(&rollback_false_reg);\n        }\n\n        None\n    } else {\n        used_regs.insert(test_reg);\n        Some(ops::CondOp {\n            reg_phi: used_reg_phi,\n            test_reg,\n            true_ops: used_true_ops,\n            false_ops: used_false_ops,\n        })\n    }\n}\n\nfn remove_unused_branch_ops(\n    ops: Box<[ops::Op]>,\n    used_regs: &mut HashSet<ops::RegId>,\n) -> Box<[ops::Op]> {\n    let mut reverse_ops = ops\n        .into_vec()\n        .into_iter()\n        .rev()\n        .filter_map(|op| {\n            let ops::Op { span, kind } = op;\n\n            match kind {\n                ops::OpKind::Cond(cond_op) => remove_unused_cond_ops(cond_op, used_regs)\n                    .map(|cond_op| ops::Op::new(span, ops::OpKind::Cond(cond_op))),\n                _ => {\n                    // Does this have no side effects and its output is unused?\n                    if !kind.has_side_effects()\n                        && kind\n                            .output_reg()\n                            .map(|output_reg| !used_regs.contains(&output_reg))\n                            .unwrap_or(true)\n                    {\n                        None\n                    } else {\n                        kind.add_input_regs(used_regs);\n                        Some(ops::Op { span, kind })\n                    }\n                }\n            }\n        })\n        .collect::<Vec<ops::Op>>();\n\n    // If we do .rev() on the iterator before we collect it will change the order we iterate in\n    reverse_ops.reverse();\n    reverse_ops.into_boxed_slice()\n}\n\npub fn remove_unused_fun_ops(ops: Box<[ops::Op]>) -> Box<[ops::Op]> {\n    // Nothing is used at the beginning of a function\n    let mut used_regs = HashSet::new();\n    remove_unused_branch_ops(ops, &mut used_regs)\n}\n\n/// Adds regs referenced by the passed value\nfn add_value_used_regs(value: &Value, used_regs: &mut HashSet<ops::RegId>) {\n    match value {\n        Value::Const(_)\n        | Value::RustFun(_)\n        | Value::TyPred(_)\n        | Value::EqPred\n        | Value::RecordCons(_)\n        | Value::FieldAccessor(_, _) => {}\n        Value::ArretFun(arret_fun) => {\n            for (_, free_value) in arret_fun.env_values().free_values.iter() {\n                add_value_used_regs(free_value, used_regs);\n            }\n        }\n        Value::Reg(reg_value) => {\n            used_regs.insert(reg_value.reg.into());\n        }\n        Value::Record(_, field_values) => {\n            for field_value in field_values.iter() {\n                add_value_used_regs(field_value, used_regs);\n            }\n        }\n        Value::List(fixed_values, rest_value) => {\n            for fixed_value in fixed_values.iter() {\n                add_value_used_regs(fixed_value, used_regs);\n            }\n\n            if let Some(rest_value) = rest_value {\n                add_value_used_regs(rest_value, used_regs);\n            }\n        }\n    }\n}\n\npub fn remove_unused_value_ops(ops: Box<[ops::Op]>, value: &Value) -> Box<[ops::Op]> {\n    let mut used_regs = HashSet::new();\n    add_value_used_regs(value, &mut used_regs);\n\n    remove_unused_branch_ops(ops, &mut used_regs)\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn empty_ops() {\n        let ops = remove_unused_fun_ops(Box::new([]));\n        assert!(ops.is_empty());\n    }\n\n    #[test]\n    fn simple_unused() {\n        let reg1 = ops::RegId::alloc();\n        let reg2 = ops::RegId::alloc();\n        let reg3 = ops::RegId::alloc();\n\n        let input_ops = Box::new([\n            ops::OpKind::ConstBoxedNil(reg1, ()).into(),\n            ops::OpKind::ConstBoxedNil(reg2, ()).into(),\n            ops::OpKind::ConstBoxedNil(reg3, ()).into(),\n            ops::OpKind::Ret(reg2).into(),\n        ]);\n\n        let output_ops = remove_unused_fun_ops(input_ops);\n\n        let expected_ops: Box<[ops::Op]> = Box::new([\n            ops::OpKind::ConstBoxedNil(reg2, ()).into(),\n            ops::OpKind::Ret(reg2).into(),\n        ]);\n\n        assert_eq!(expected_ops, output_ops);\n    }\n\n    #[test]\n    fn fully_used_cond() {\n        let output_reg = ops::RegId::alloc();\n        let test_reg = ops::RegId::alloc();\n        let true_result_reg = ops::RegId::alloc();\n        let false_result_reg = ops::RegId::alloc();\n\n        let true_ops = Box::new([ops::OpKind::ConstBoxedNil(true_result_reg, ()).into()]);\n\n        let false_ops = Box::new([ops::OpKind::ConstBoxedNil(false_result_reg, ()).into()]);\n\n        let input_ops: Box<[ops::Op]> = Box::new([\n            ops::OpKind::ConstBoxedNil(test_reg, ()).into(),\n            ops::OpKind::Cond(ops::CondOp {\n                reg_phi: Some(ops::RegPhi {\n                    output_reg,\n                    true_result_reg,\n                    false_result_reg,\n                }),\n                test_reg,\n                true_ops,\n                false_ops,\n            })\n            .into(),\n            ops::OpKind::Ret(output_reg).into(),\n        ]);\n\n        let expected_ops = input_ops.clone();\n        let output_ops = remove_unused_fun_ops(input_ops);\n\n        assert_eq!(expected_ops, output_ops);\n    }\n\n    #[test]\n    fn partially_used_cond() {\n        let output_reg = ops::RegId::alloc();\n        let test_reg = ops::RegId::alloc();\n        let true_result_reg = ops::RegId::alloc();\n        let false_result_reg = ops::RegId::alloc();\n\n        let true_ops = Box::new([ops::OpKind::ConstBoxedNil(true_result_reg, ()).into()]);\n\n        let false_ops = Box::new([ops::OpKind::ConstBoxedNil(false_result_reg, ()).into()]);\n\n        let input_ops = Box::new([\n            ops::OpKind::ConstBoxedNil(test_reg, ()).into(),\n            ops::OpKind::Cond(ops::CondOp {\n                reg_phi: Some(ops::RegPhi {\n                    output_reg,\n                    true_result_reg: test_reg, // This makes the true branch unused\n                    false_result_reg,\n                }),\n                test_reg,\n                true_ops,\n                false_ops: false_ops.clone(),\n            })\n            .into(),\n            ops::OpKind::Ret(output_reg).into(),\n        ]);\n\n        let expected_ops: Box<[ops::Op]> = Box::new([\n            ops::OpKind::ConstBoxedNil(test_reg, ()).into(),\n            ops::OpKind::Cond(ops::CondOp {\n                reg_phi: Some(ops::RegPhi {\n                    output_reg,\n                    true_result_reg: test_reg,\n                    false_result_reg,\n                }),\n                test_reg,\n                true_ops: Box::new([]),\n                false_ops,\n            })\n            .into(),\n            ops::OpKind::Ret(output_reg).into(),\n        ]);\n\n        let output_ops = remove_unused_fun_ops(input_ops);\n\n        assert_eq!(expected_ops, output_ops);\n    }\n\n    #[test]\n    fn output_only_cond() {\n        let output_reg = ops::RegId::alloc();\n        let test_reg = ops::RegId::alloc();\n        let true_result_reg = ops::RegId::alloc();\n        let false_result_reg = ops::RegId::alloc();\n\n        let true_ops = Box::new([ops::OpKind::ConstBoxedNil(true_result_reg, ()).into()]);\n\n        let false_ops = Box::new([ops::OpKind::ConstBoxedNil(false_result_reg, ()).into()]);\n\n        let input_ops = Box::new([\n            ops::OpKind::ConstBoxedNil(test_reg, ()).into(),\n            ops::OpKind::Cond(ops::CondOp {\n                reg_phi: Some(ops::RegPhi {\n                    output_reg,\n                    true_result_reg: test_reg, // This makes the true branch unused\n                    false_result_reg: test_reg, // This makes the false branch unused\n                }),\n                test_reg,\n                true_ops,\n                false_ops,\n            })\n            .into(),\n            // However, the output of the `Cond` is still used\n            ops::OpKind::Ret(output_reg).into(),\n        ]);\n\n        let expected_ops: Box<[ops::Op]> = Box::new([\n            ops::OpKind::ConstBoxedNil(test_reg, ()).into(),\n            ops::OpKind::Cond(ops::CondOp {\n                reg_phi: Some(ops::RegPhi {\n                    output_reg,\n                    true_result_reg: test_reg,\n                    false_result_reg: test_reg,\n                }),\n                test_reg,\n                true_ops: Box::new([]),\n                false_ops: Box::new([]),\n            })\n            .into(),\n            ops::OpKind::Ret(output_reg).into(),\n        ]);\n\n        let output_ops = remove_unused_fun_ops(input_ops);\n\n        assert_eq!(expected_ops, output_ops);\n    }\n\n    #[test]\n    fn output_unused_cond() {\n        let output_reg = ops::RegId::alloc();\n        let test_reg = ops::RegId::alloc();\n\n        let true_ops = Box::new([ops::OpKind::RetVoid.into()]);\n        let false_ops = Box::new([ops::OpKind::RetVoid.into()]);\n\n        let input_ops = Box::new([\n            ops::OpKind::ConstBoxedNil(test_reg, ()).into(),\n            ops::OpKind::Cond(ops::CondOp {\n                reg_phi: Some(ops::RegPhi {\n                    output_reg,\n                    true_result_reg: test_reg, // This makes the true result unused\n                    false_result_reg: test_reg, // This makes the false result unused\n                }),\n                test_reg,\n                true_ops: true_ops.clone(),\n                false_ops: false_ops.clone(),\n            })\n            .into(),\n        ]);\n\n        let expected_ops: Box<[ops::Op]> = Box::new([\n            ops::OpKind::ConstBoxedNil(test_reg, ()).into(),\n            ops::OpKind::Cond(ops::CondOp {\n                reg_phi: None,\n                test_reg,\n                true_ops,\n                false_ops,\n            })\n            .into(),\n        ]);\n\n        let output_ops = remove_unused_fun_ops(input_ops);\n\n        assert_eq!(expected_ops, output_ops);\n    }\n\n    #[test]\n    fn fully_unused_cond() {\n        let output_reg = ops::RegId::alloc();\n        let test_reg = ops::RegId::alloc();\n        let true_result_reg = ops::RegId::alloc();\n        let false_result_reg = ops::RegId::alloc();\n\n        let true_ops = Box::new([ops::OpKind::ConstBoxedNil(true_result_reg, ()).into()]);\n\n        let false_ops = Box::new([ops::OpKind::ConstBoxedNil(false_result_reg, ()).into()]);\n\n        let input_ops = Box::new([\n            ops::OpKind::ConstBoxedNil(test_reg, ()).into(),\n            ops::OpKind::Cond(ops::CondOp {\n                reg_phi: Some(ops::RegPhi {\n                    output_reg,\n                    true_result_reg: test_reg, // This makes the true branch unused\n                    false_result_reg: test_reg, // This makes the false branch unused\n                }),\n                test_reg,\n                true_ops,\n                false_ops,\n            })\n            .into(),\n        ]);\n\n        let output_ops = remove_unused_fun_ops(input_ops);\n        assert!(output_ops.is_empty());\n    }\n}\n"
  },
  {
    "path": "compiler/mir/polymorph.rs",
    "content": "use arret_runtime::abitype;\nuse arret_runtime::callback;\n\nuse crate::mir::ops;\nuse crate::ty;\nuse crate::ty::Ty;\n\n/// PolymorphAbi annotates OpsAbi with information about if a function expects a captures or rest\n///\n/// This is information that's useful while generating MIR but can be discarded when building Ops.\n#[derive(PartialEq, Eq, Hash, Clone)]\npub struct PolymorphAbi {\n    pub call_conv: ops::CallConv,\n\n    pub has_captures: bool,\n    pub fixed_params: Box<[abitype::AbiType]>,\n    pub rest_param: Option<abitype::AbiType>,\n\n    pub ret: abitype::RetAbiType,\n}\n\nimpl PolymorphAbi {\n    pub fn thunk_abi() -> PolymorphAbi {\n        PolymorphAbi {\n            call_conv: ops::CallConv::Ccc,\n\n            has_captures: true,\n            fixed_params: Box::new([]),\n            rest_param: Some(abitype::TOP_LIST_BOXED_ABI_TYPE.into()),\n\n            ret: abitype::BoxedAbiType::Any.into(),\n        }\n    }\n\n    /// Returns the Arret type for our parameter list\n    pub fn param_ty_ref<M: ty::Pm>(&self) -> ty::List<M> {\n        use crate::ty::conv_abi::ConvertableAbiType;\n\n        let fixed_refs = self\n            .fixed_params\n            .iter()\n            .map(ConvertableAbiType::to_ty_ref)\n            .collect();\n\n        let rest_ref = match &self.rest_param {\n            Some(abitype::AbiType::Boxed(abitype::BoxedAbiType::List(member_abi_type))) => {\n                member_abi_type.to_ty_ref()\n            }\n            Some(other) => {\n                panic!(\"cannot determine member type for ABI rest list {:?}\", other);\n            }\n            None => Ty::never().into(),\n        };\n\n        // If our rest type uses a Pair this can have fixed members\n        ty::List::new(fixed_refs, rest_ref)\n    }\n}\n\nimpl From<callback::EntryPointAbiType> for PolymorphAbi {\n    fn from(abi_type: callback::EntryPointAbiType) -> Self {\n        PolymorphAbi {\n            call_conv: ops::CallConv::Ccc,\n\n            has_captures: true,\n            fixed_params: abi_type.params.iter().cloned().collect(),\n            rest_param: None,\n\n            ret: abi_type.ret,\n        }\n    }\n}\n\nimpl From<PolymorphAbi> for ops::OpsAbi {\n    fn from(polymorph_abi: PolymorphAbi) -> Self {\n        ops::OpsAbi {\n            params: Some(abitype::BoxedAbiType::Any.into())\n                .filter(|_| polymorph_abi.has_captures)\n                .into_iter()\n                .chain(polymorph_abi.fixed_params.iter().cloned())\n                .chain(polymorph_abi.rest_param.iter().cloned())\n                .collect(),\n\n            call_conv: polymorph_abi.call_conv,\n            ret: polymorph_abi.ret,\n        }\n    }\n}\n\n/// Recommends a polymorph ABI for a given list and ret type\npub fn polymorph_abi_for_list_ty<M: ty::Pm>(\n    has_captures: bool,\n    list_ty: &ty::List<M>,\n    ret_ty: &ty::Ref<M>,\n) -> PolymorphAbi {\n    use crate::mir::specific_abi_type::*;\n\n    PolymorphAbi {\n        call_conv: ops::CallConv::FastCc,\n\n        has_captures,\n\n        fixed_params: list_ty\n            .fixed()\n            .iter()\n            .map(specific_abi_type_for_ty_ref)\n            .collect(),\n\n        rest_param: Some(\n            abitype::BoxedAbiType::List(specific_boxed_abi_type_for_ty_ref(list_ty.rest())).into(),\n        )\n        .filter(|_| list_ty.has_rest()),\n\n        ret: specific_ret_abi_type_for_ty_ref(ret_ty),\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::hir;\n\n    #[test]\n    fn polymorph_abi_param_ty_ref() {\n        use arret_runtime::abitype::EncodeBoxedAbiType;\n        use arret_runtime::boxed;\n\n        let thunk_param_poly = PolymorphAbi::thunk_abi().param_ty_ref();\n        let expected_poly = hir::poly_for_str(\"(List & Any)\");\n        assert_eq!(expected_poly, thunk_param_poly.into());\n\n        let mul_param_poly = PolymorphAbi {\n            call_conv: ops::CallConv::FastCc,\n\n            has_captures: false,\n            fixed_params: Box::new([boxed::Num::BOXED_ABI_TYPE.into()]),\n            rest_param: Some(abitype::BoxedAbiType::List(&boxed::Num::BOXED_ABI_TYPE).into()),\n\n            ret: boxed::Num::BOXED_ABI_TYPE.into(),\n        }\n        .param_ty_ref();\n\n        let expected_poly = hir::poly_for_str(\"(List Num & Num)\");\n        assert_eq!(expected_poly, mul_param_poly.into());\n    }\n}\n"
  },
  {
    "path": "compiler/mir/printer.rs",
    "content": "use std::collections::HashMap;\nuse std::io::{Result, Write};\nuse std::iter;\n\nuse codespan_reporting::files::Files as _;\n\nuse arret_syntax::span::Span;\n\nuse crate::codegen::GenAbi;\nuse crate::mir::ops;\nuse crate::mir::BuiltProgram;\nuse crate::source::SourceLoader;\nuse crate::ty::conv_abi::ConvertableAbiType;\n\nfn span_to_human_location(source_loader: Option<&SourceLoader>, span: Span) -> Option<String> {\n    let source_loader = source_loader?;\n    let file_id = span.file_id()?;\n\n    let files = source_loader.files();\n    let location = files.location(file_id, span.start() as usize).ok()?;\n\n    Some(format!(\n        \"{}:{}:{}\",\n        files.name(file_id).ok()?,\n        location.line_number,\n        location.column_number\n    ))\n}\n\nfn private_fun_to_string(\n    private_funs: &HashMap<ops::PrivateFunId, ops::Fun>,\n    private_fun_id: ops::PrivateFunId,\n) -> String {\n    private_funs[&private_fun_id]\n        .source_name\n        .clone()\n        .map(|s| format!(\"%{}\", s))\n        .unwrap_or_else(|| format!(\"[private-{}]\", private_fun_id.to_u32()))\n}\n\nfn callee_to_string(\n    private_funs: &HashMap<ops::PrivateFunId, ops::Fun>,\n    callee: &ops::Callee,\n) -> String {\n    match callee {\n        ops::Callee::StaticSymbol(static_symbol) => format!(\"@{}\", static_symbol.symbol),\n        ops::Callee::PrivateFun(private_fun_id) => {\n            private_fun_to_string(private_funs, *private_fun_id)\n        }\n        ops::Callee::BoxedFunThunk(thunk_reg) => {\n            format!(\"<%{} as boxed::FunThunk>.entry\", thunk_reg.get())\n        }\n    }\n}\nfn callee_to_gen_abi(\n    private_funs: &HashMap<ops::PrivateFunId, ops::Fun>,\n    callee: &ops::Callee,\n) -> GenAbi {\n    match callee {\n        ops::Callee::StaticSymbol(static_symbol) => static_symbol.abi.clone(),\n        ops::Callee::PrivateFun(private_fun_id) => (&private_funs[private_fun_id].abi).into(),\n        ops::Callee::BoxedFunThunk(_) => GenAbi::thunk_abi(),\n    }\n}\n\nfn box_pair_op_to_string(\n    ops::BoxPairOp {\n        head_reg,\n        rest_reg,\n        list_len_reg,\n    }: &ops::BoxPairOp,\n) -> String {\n    format!(\n        \"boxed::Pair {{ head: %{}, rest: %{}, list_len: %{} }}\",\n        head_reg.get(),\n        rest_reg.get(),\n        list_len_reg.get()\n    )\n}\n\nfn box_fun_thunk_op_to_string(\n    private_funs: &HashMap<ops::PrivateFunId, ops::Fun>,\n    ops::BoxFunThunkOp {\n        captures_reg,\n        callee,\n    }: &ops::BoxFunThunkOp,\n) -> String {\n    format!(\n        \"boxed::FunThunk {{ captures: %{captures_reg}, entry: {entry} }}\",\n        captures_reg = captures_reg.get(),\n        entry = callee_to_string(private_funs, callee)\n    )\n}\n\nfn box_record_op_to_string(\n    ops::BoxRecordOp {\n        record_struct,\n        field_regs,\n    }: &ops::BoxRecordOp,\n) -> String {\n    let field_strings = field_regs\n        .iter()\n        .zip(record_struct.field_abi_types.iter())\n        .map(|(field_reg, field_abi_type)| {\n            format!(\"%{}: {}\", field_reg.get(), field_abi_type.to_rust_str())\n        })\n        .collect::<Vec<String>>()\n        .join(\", \");\n\n    format!(\n        \"record::{} {{ {} }}\",\n        record_struct.source_name, field_strings\n    )\n}\n\nfn comparison_to_str(comparison: ops::Comparison) -> &'static str {\n    match comparison {\n        ops::Comparison::Lt => \"<\",\n        ops::Comparison::Le => \"<=\",\n        ops::Comparison::Eq => \"==\",\n        ops::Comparison::Gt => \">\",\n        ops::Comparison::Ge => \">=\",\n    }\n}\n\nfn print_cond_branch(\n    w: &mut dyn Write,\n    private_funs: &HashMap<ops::PrivateFunId, ops::Fun>,\n    ident_level: usize,\n    ops: &[ops::Op],\n    result_reg: Option<ops::RegId>,\n) -> Result<()> {\n    let ident_level = ident_level + 1;\n    print_branch(w, private_funs, ident_level, ops)?;\n\n    if let Some(result_reg) = result_reg {\n        for _ in 0..ident_level {\n            write!(w, \"  \")?;\n        }\n        writeln!(w, \"%{}\", result_reg.get())?;\n    }\n\n    Ok(())\n}\n\nfn print_branch(\n    w: &mut dyn Write,\n    private_funs: &HashMap<ops::PrivateFunId, ops::Fun>,\n    ident_level: usize,\n    ops: &[ops::Op],\n) -> Result<()> {\n    for op in ops.iter() {\n        for _ in 0..ident_level {\n            write!(w, \"  \")?;\n        }\n\n        match &op.kind {\n            ops::OpKind::ConstBoxedNil(reg, _) => {\n                writeln!(w, \"%{} = const boxed::NIL_INSTANCE;\", reg.get())?;\n            }\n            ops::OpKind::ConstInt64(reg, value) => {\n                writeln!(w, \"%{} = const {}i64;\", reg.get(), value)?\n            }\n            ops::OpKind::ConstChar(reg, value) => {\n                writeln!(w, \"%{} = const {:?}\", reg.get(), value)?\n            }\n            ops::OpKind::ConstFloat(reg, value) => {\n                writeln!(w, \"%{} = const {}f64;\", reg.get(), value)?\n            }\n            ops::OpKind::ConstInternedSym(reg, name) => {\n                writeln!(\n                    w,\n                    \"%{} = const interned::InternedSym {{ name: {:?} }};\",\n                    reg.get(),\n                    name\n                )?;\n            }\n            ops::OpKind::ConstTypeTag(reg, type_tag) => {\n                writeln!(w, \"%{} = const TypeTag::{:?};\", reg.get(), type_tag)?\n            }\n            ops::OpKind::ConstBool(reg, value) => writeln!(w, \"%{} = const {};\", reg.get(), value)?,\n            ops::OpKind::ConstBoxedTrue(reg, ()) => {\n                writeln!(w, \"%{} = const boxed::TRUE_INSTANCE;\", reg.get())?\n            }\n            ops::OpKind::ConstBoxedFalse(reg, ()) => {\n                writeln!(w, \"%{} = const boxed::FALSE_INSTANCE;\", reg.get())?\n            }\n            ops::OpKind::ConstBoxedVector(reg, element_regs) => writeln!(\n                w,\n                \"%{} = const boxed::Vector {{ elements: [{}] }};\",\n                reg.get(),\n                element_regs\n                    .iter()\n                    .map(|element_reg| format!(\"%{}\", element_reg.get()))\n                    .collect::<Vec<String>>()\n                    .join(\", \")\n            )?,\n            ops::OpKind::ConstBoxedSet(reg, element_regs) => writeln!(\n                w,\n                \"%{} = const boxed::Set {{ elements: [{}] }};\",\n                reg.get(),\n                element_regs\n                    .iter()\n                    .map(|element_reg| format!(\"%{}\", element_reg.get()))\n                    .collect::<Vec<String>>()\n                    .join(\", \")\n            )?,\n            ops::OpKind::ConstBoxedMap(reg, entry_regs) => writeln!(\n                w,\n                \"%{} = const boxed::Map {{ elements: [{}] }};\",\n                reg.get(),\n                entry_regs\n                    .iter()\n                    .map(|(key_reg, value_reg)| format!(\n                        \"(%{}, %{})\",\n                        key_reg.get(),\n                        value_reg.get()\n                    ))\n                    .collect::<Vec<String>>()\n                    .join(\", \")\n            )?,\n            ops::OpKind::ConstRecordClassId(reg, record_class_id) => writeln!(\n                w,\n                \"%{} = const record::{}::CLASS_ID;\",\n                reg.get(),\n                record_class_id.source_name\n            )?,\n            ops::OpKind::CastBoxed(reg, ops::CastBoxedOp { from_reg, to_type }) => writeln!(\n                w,\n                \"%{} = %{} as {};\",\n                reg.get(),\n                from_reg.get(),\n                to_type.to_rust_str()\n            )?,\n            ops::OpKind::ConstCastBoxed(reg, ops::CastBoxedOp { from_reg, to_type }) => writeln!(\n                w,\n                \"%{} = const %{} as {};\",\n                reg.get(),\n                from_reg.get(),\n                to_type.to_rust_str()\n            )?,\n            ops::OpKind::Alias(reg, from_reg) => {\n                writeln!(w, \"%{} = %{};\", reg.get(), from_reg.get(),)?\n            }\n            ops::OpKind::Int64ToFloat(reg, from_reg) => {\n                writeln!(w, \"%{} = (%{}: i64) as f64;\", reg.get(), from_reg.get(),)?\n            }\n            ops::OpKind::ConstBoxedPair(reg, box_pair_op) => {\n                writeln!(\n                    w,\n                    \"%{} = const {};\",\n                    reg.get(),\n                    box_pair_op_to_string(box_pair_op)\n                )?;\n            }\n            ops::OpKind::AllocBoxedPair(reg, box_pair_op) => {\n                writeln!(\n                    w,\n                    \"%{} = alloc {};\",\n                    reg.get(),\n                    box_pair_op_to_string(box_pair_op)\n                )?;\n            }\n            ops::OpKind::ConstBoxedInt(reg, value) => {\n                writeln!(\n                    w,\n                    \"%{} = const boxed::Int {{ value: {}i64 }};\",\n                    reg.get(),\n                    value\n                )?;\n            }\n            ops::OpKind::AllocBoxedInt(reg, value_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = alloc boxed::Int {{ value: %{} }};\",\n                    reg.get(),\n                    value_reg.get()\n                )?;\n            }\n            ops::OpKind::ConstBoxedChar(reg, value) => {\n                writeln!(\n                    w,\n                    \"%{} = const boxed::Char {{ value: {:?} }};\",\n                    reg.get(),\n                    value\n                )?;\n            }\n            ops::OpKind::AllocBoxedChar(reg, value_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = alloc boxed::Char {{ value: %{} }};\",\n                    reg.get(),\n                    value_reg.get()\n                )?;\n            }\n            ops::OpKind::ConstBoxedFloat(reg, value) => {\n                writeln!(\n                    w,\n                    \"%{} = const boxed::Float {{ value: {}f64 }};\",\n                    reg.get(),\n                    value\n                )?;\n            }\n            ops::OpKind::AllocBoxedFloat(reg, value_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = alloc boxed::Float {{ value: %{} }};\",\n                    reg.get(),\n                    value_reg.get()\n                )?;\n            }\n            ops::OpKind::ConstBoxedFunThunk(reg, box_fun_thunk_op) => {\n                writeln!(\n                    w,\n                    \"%{} = const {};\",\n                    reg.get(),\n                    box_fun_thunk_op_to_string(private_funs, box_fun_thunk_op)\n                )?;\n            }\n            ops::OpKind::AllocBoxedFunThunk(reg, box_fun_thunk_op) => {\n                writeln!(\n                    w,\n                    \"%{} = alloc {};\",\n                    reg.get(),\n                    box_fun_thunk_op_to_string(private_funs, box_fun_thunk_op)\n                )?;\n            }\n            ops::OpKind::MakeCallback(\n                reg,\n                ops::MakeCallbackOp {\n                    captures_reg,\n                    callee,\n                },\n            ) => {\n                writeln!(\n                    w,\n                    \"%{} = callback::Callback {{ captures: %{captures_reg}, entry_point: {entry_point} }};\",\n                    reg.get(),\n                    captures_reg = captures_reg.get(),\n                    entry_point = callee_to_string(private_funs, callee)\n                )?;\n            }\n            ops::OpKind::ConstBoxedRecord(reg, box_record_op) => {\n                writeln!(\n                    w,\n                    \"%{} = const {};\",\n                    reg.get(),\n                    box_record_op_to_string(box_record_op)\n                )?;\n            }\n            ops::OpKind::AllocBoxedRecord(reg, box_record_op) => {\n                writeln!(\n                    w,\n                    \"%{} = alloc {};\",\n                    reg.get(),\n                    box_record_op_to_string(box_record_op)\n                )?;\n            }\n            ops::OpKind::ConstBoxedSym(reg, name) => {\n                writeln!(\n                    w,\n                    \"%{} = const boxed::Sym {{ name: {:?} }};\",\n                    reg.get(),\n                    name\n                )?;\n            }\n            ops::OpKind::AllocBoxedSym(reg, interned_sym_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = alloc boxed::Sym {{ interned: %{} }};\",\n                    reg.get(),\n                    interned_sym_reg.get()\n                )?;\n            }\n            ops::OpKind::ConstBoxedStr(reg, name) => {\n                writeln!(\n                    w,\n                    \"%{} = const boxed::Str {{ value: {:?} }};\",\n                    reg.get(),\n                    name\n                )?;\n            }\n            ops::OpKind::LoadBoxedRecordField(\n                reg,\n                ops::LoadBoxedRecordFieldOp {\n                    record_reg,\n                    record_struct,\n                    field_index,\n                },\n            ) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as record::{}>.{}: {};\",\n                    reg.get(),\n                    record_reg.get(),\n                    record_struct.source_name,\n                    field_index,\n                    record_struct.field_abi_types[*field_index].to_rust_str()\n                )?;\n            }\n            ops::OpKind::LoadBoxedTypeTag(\n                reg,\n                ops::LoadBoxedTypeTagOp {\n                    subject_reg,\n                    possible_type_tags,\n                },\n            ) => {\n                let type_tags_string = possible_type_tags\n                    .into_iter()\n                    .map(|type_tag| format!(\"{:?}\", type_tag))\n                    .collect::<Vec<String>>()\n                    .join(\", \");\n\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::Any>.type_tag in [{}];\",\n                    reg.get(),\n                    subject_reg.get(),\n                    type_tags_string\n                )?;\n            }\n            ops::OpKind::LoadBoxedPairHead(reg, pair_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::Pair>.head;\",\n                    reg.get(),\n                    pair_reg.get()\n                )?;\n            }\n            ops::OpKind::LoadBoxedPairRest(reg, pair_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::Pair>.rest;\",\n                    reg.get(),\n                    pair_reg.get()\n                )?;\n            }\n            ops::OpKind::LoadBoxedVectorMember(\n                reg,\n                ops::LoadBoxedVectorMemberOp {\n                    vector_reg,\n                    known_vector_len,\n                    member_index,\n                },\n            ) => {\n                writeln!(\n                    w,\n                    \"%{reg} = <%{vector_reg} as boxed::Vector>[{member_index}] where <${vector_reg} as boxed::Vector>.len == {known_vector_len};\",\n                    reg = reg.get(),\n                    vector_reg = vector_reg.get(),\n                    known_vector_len = known_vector_len,\n                    member_index = member_index\n                )?;\n            }\n            ops::OpKind::LoadBoxedListLen(\n                reg,\n                ops::LoadBoxedListLenOp {\n                    list_reg,\n                    min_list_len,\n                },\n            ) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::List>.list_len where > {};\",\n                    reg.get(),\n                    list_reg.get(),\n                    min_list_len\n                )?;\n            }\n            ops::OpKind::LoadBoxedVectorLen(reg, list_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::Vector>.len;\",\n                    reg.get(),\n                    list_reg.get(),\n                )?;\n            }\n            ops::OpKind::LoadBoxedSymInterned(reg, sym_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::Sym>.interned;\",\n                    reg.get(),\n                    sym_reg.get()\n                )?;\n            }\n            ops::OpKind::LoadBoxedIntValue(reg, int_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::Int>.value;\",\n                    reg.get(),\n                    int_reg.get()\n                )?;\n            }\n            ops::OpKind::LoadBoxedFloatValue(reg, float_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::Float>.value;\",\n                    reg.get(),\n                    float_reg.get()\n                )?;\n            }\n            ops::OpKind::LoadBoxedCharValue(reg, float_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::Char>.value;\",\n                    reg.get(),\n                    float_reg.get()\n                )?;\n            }\n            ops::OpKind::LoadBoxedFunThunkCaptures(reg, fun_thunk_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::FunThunk>.env;\",\n                    reg.get(),\n                    fun_thunk_reg.get()\n                )?;\n            }\n            ops::OpKind::LoadBoxedRecordClassId(reg, record_reg) => {\n                writeln!(\n                    w,\n                    \"%{} = <%{} as boxed::Record>.class_id;\",\n                    reg.get(),\n                    record_reg.get()\n                )?;\n            }\n            ops::OpKind::IntCompare(\n                reg,\n                ops::CompareOp {\n                    lhs_reg,\n                    rhs_reg,\n                    comparison,\n                },\n            ) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: i64) {} (%{}: i64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    comparison_to_str(*comparison),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::FloatCompare(\n                reg,\n                ops::CompareOp {\n                    lhs_reg,\n                    rhs_reg,\n                    comparison,\n                },\n            ) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: f64) {} (%{}: f64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    comparison_to_str(*comparison),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::FloatAdd(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: f64) + (%{}: f64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::FloatSub(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: f64) - (%{}: f64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::FloatMul(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: f64) * (%{}: f64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::FloatDiv(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: f64) / (%{}: f64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64Add(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = unchecked (%{}: i64) + (%{}: i64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64CheckedAdd(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = checked (%{}: i64) + (%{}: i64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64CheckedSub(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = checked (%{}: i64) - (%{}: i64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64CheckedMul(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = checked (%{}: i64) * (%{}: i64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64Div(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = unchecked (%{}: i64) / (%{}: i64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64CheckedDiv(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = checked (%{}: i64) / (%{}: i64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64Rem(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = unchecked (%{}: i64) % (%{}: i64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64CheckedRem(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = checked (%{}: i64) % (%{}: i64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64BitwiseAnd(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: u64) & (%{}: u64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64BitwiseOr(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: u64) | (%{}: u64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64BitwiseXor(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: u64) ^ (%{}: u64);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Int64BitwiseNot(reg, int_reg) => {\n                writeln!(w, \"%{} = ~(%{}: u64);\", reg.get(), int_reg.get())?;\n            }\n            ops::OpKind::Int64ShiftLeft(reg, ops::ShiftOp { int_reg, bit_count }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: u64) << {};\",\n                    reg.get(),\n                    int_reg.get(),\n                    bit_count\n                )?;\n            }\n            ops::OpKind::Int64ArithmeticShiftRight(reg, ops::ShiftOp { int_reg, bit_count }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: i64) >> {};\",\n                    reg.get(),\n                    int_reg.get(),\n                    bit_count\n                )?;\n            }\n            ops::OpKind::Int64LogicalShiftRight(reg, ops::ShiftOp { int_reg, bit_count }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: u64) >> {};\",\n                    reg.get(),\n                    int_reg.get(),\n                    bit_count\n                )?;\n            }\n            ops::OpKind::FloatSqrt(reg, radicand_reg) => {\n                writeln!(w, \"%{} = sqrt(%{}: f64);\", reg.get(), radicand_reg.get(),)?;\n            }\n            ops::OpKind::TypeTagEqual(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: TypeTag) == (%{}: TypeTag);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::BoxIdentical(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = &(%{}: boxed::Any) == &(%{}: boxed::Any);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::BoolEqual(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: bool) == (%{}: bool);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::CharEqual(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: char) == (%{}: char);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::InternedSymEqual(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: interned::InternedSym) == (%{}: interned::InternedSym);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::RecordClassIdEqual(reg, ops::BinaryOp { lhs_reg, rhs_reg }) => {\n                writeln!(\n                    w,\n                    \"%{} = (%{}: boxed::RecordClassId) == (%{}: boxed::RecordClassId);\",\n                    reg.get(),\n                    lhs_reg.get(),\n                    rhs_reg.get(),\n                )?;\n            }\n            ops::OpKind::Call(\n                reg,\n                ops::CallOp {\n                    callee,\n                    impure,\n                    args,\n                },\n            ) => {\n                let purity = if *impure { \"impure\" } else { \"pure\" };\n\n                let callee_abi = callee_to_gen_abi(private_funs, callee);\n\n                let args = Some(\"%task\".to_owned())\n                    .into_iter()\n                    .filter(|_| callee_abi.takes_task)\n                    .chain(callee_abi.params.iter().zip(args.iter()).map(\n                        |(param_abi_type, arg_reg)| {\n                            format!(\"%{}: {}\", arg_reg.get(), param_abi_type.to_rust_str())\n                        },\n                    ))\n                    .collect::<Vec<String>>()\n                    .join(\", \");\n\n                writeln!(\n                    w,\n                    \"%{} = {} {}({}): {};\",\n                    reg.get(),\n                    purity,\n                    callee_to_string(private_funs, callee),\n                    args,\n                    callee_abi.ret.to_rust_str()\n                )?;\n            }\n            ops::OpKind::TailCall(reg, ops::TailCallOp { impure, args }) => {\n                let purity = if *impure { \"impure\" } else { \"pure\" };\n\n                let args = iter::once(\"%task\".to_owned())\n                    .chain(args.iter().map(|arg_reg| format!(\", %{}\", arg_reg.get())))\n                    .collect::<String>();\n\n                writeln!(w, \"%{} = {} recur({});\", reg.get(), purity, args,)?;\n            }\n            ops::OpKind::Cond(cond_op) => {\n                if let Some(reg_phi) = &cond_op.reg_phi {\n                    write!(w, \"%{} = \", reg_phi.output_reg.get())?;\n                }\n                writeln!(w, \"if %{} {{\", cond_op.test_reg.get())?;\n\n                print_cond_branch(\n                    w,\n                    private_funs,\n                    ident_level,\n                    &cond_op.true_ops,\n                    cond_op.reg_phi.as_ref().map(|rp| rp.true_result_reg),\n                )?;\n\n                if !(cond_op.false_ops.is_empty() && cond_op.reg_phi.is_none()) {\n                    for _ in 0..ident_level {\n                        write!(w, \"  \")?;\n                    }\n                    writeln!(w, \"}} else {{\")?;\n\n                    print_cond_branch(\n                        w,\n                        private_funs,\n                        ident_level,\n                        &cond_op.false_ops,\n                        cond_op.reg_phi.as_ref().map(|rp| rp.false_result_reg),\n                    )?;\n                }\n\n                for _ in 0..ident_level {\n                    write!(w, \"  \")?;\n                }\n                if cond_op.reg_phi.is_some() {\n                    writeln!(w, \"}};\")?;\n                } else {\n                    writeln!(w, \"}}\")?;\n                }\n            }\n            ops::OpKind::Ret(reg) => {\n                writeln!(w, \"return %{};\", reg.get())?;\n            }\n            ops::OpKind::RetVoid => {\n                writeln!(w, \"return;\")?;\n            }\n            ops::OpKind::Unreachable => {\n                writeln!(w, \"unreachable;\")?;\n            }\n            ops::OpKind::Panic(message) => {\n                writeln!(w, \"panic({:?});\", message)?;\n            }\n        }\n    }\n\n    Ok(())\n}\n\n/// Prints a textual representation of a function's MIR to to `w`\npub fn print_fun(\n    w: &mut dyn Write,\n    private_funs: &HashMap<ops::PrivateFunId, ops::Fun>,\n    ops_fun: &ops::Fun,\n    private_fun_id: Option<ops::PrivateFunId>,\n) -> Result<()> {\n    let fun_name = ops_fun\n        .source_name\n        .clone()\n        .map(|s| s.to_string())\n        .or_else(|| private_fun_id.map(|pfi| private_fun_to_string(private_funs, pfi)))\n        .unwrap_or_else(|| \"[anonymous]\".into());\n\n    let call_conv_name = match ops_fun.abi.call_conv {\n        ops::CallConv::Ccc => \"extern \\\"C\\\" \",\n        ops::CallConv::FastCc => \"\",\n    };\n\n    let params = ops_fun\n        .abi\n        .params\n        .iter()\n        .zip(ops_fun.param_regs.iter())\n        .map(|(abi_type, param_reg)| format!(\", %{}: {}\", param_reg.get(), abi_type.to_rust_str()))\n        .collect::<String>();\n\n    writeln!(\n        w,\n        \"{}fn {}(%task{}) -> {} {{\",\n        call_conv_name,\n        fun_name,\n        params,\n        ops_fun.abi.ret.to_rust_str()\n    )?;\n\n    print_branch(w, private_funs, 1, &ops_fun.ops)?;\n    writeln!(w, \"}}\")?;\n\n    Ok(())\n}\n\n/// Prints a textual representation of a program's MIR to to `w`\n///\n/// This is an internal, undocumented and unstable format that has no equivalent parser. It's\n/// intended to aid human debugging an optimisation.\npub fn print_program(\n    w: &mut dyn Write,\n    program: &BuiltProgram,\n    source_loader: Option<&SourceLoader>,\n) -> Result<()> {\n    for (private_fun_id, private_fun) in &program.private_funs {\n        if private_fun.source_name.is_none() {\n            if let Some(human_location) = span_to_human_location(source_loader, private_fun.span) {\n                writeln!(w, \"// Anonymous function defined at {}\", human_location)?;\n            }\n        }\n\n        print_fun(w, &program.private_funs, private_fun, Some(*private_fun_id))?;\n        writeln!(w)?;\n    }\n\n    print_fun(w, &program.private_funs, &program.main, None)\n}\n"
  },
  {
    "path": "compiler/mir/record_field.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\n\nuse crate::mir::builder::TryToBuilder;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::value;\nuse crate::mir::value::Value;\nuse crate::ty::record;\n\npub fn load_record_field(\n    ehx: &mut EvalHirCtx,\n    b: &mut impl TryToBuilder,\n    span: Span,\n    record_cons: &record::ConsId,\n    record_value: &Value,\n    field_index: usize,\n) -> Value {\n    match record_value {\n        Value::Record(_, fields) => fields[field_index].clone(),\n        Value::Const(boxed_any) => {\n            use boxed::FieldValue;\n\n            let boxed_record = boxed_any\n                .downcast_ref::<boxed::Record>()\n                .expect(\"unexpected type when accessing record field\");\n\n            match boxed_record\n                .field_values(ehx.as_heap())\n                .nth(field_index)\n                .unwrap()\n            {\n                FieldValue::Bool(bool_value) => boxed::Bool::singleton_ref(bool_value).into(),\n                FieldValue::Int(int_value) => boxed::Int::new(ehx, int_value).into(),\n                FieldValue::Float(float_value) => boxed::Float::new(ehx, float_value).into(),\n                FieldValue::Char(char_value) => boxed::Char::new(ehx, char_value).into(),\n                FieldValue::Boxed(boxed_any) => boxed_any.into(),\n                FieldValue::InternedSym(interned) => {\n                    boxed::Sym::from_interned_sym(ehx, interned).into()\n                }\n            }\n        }\n        other_value => {\n            use crate::mir::ops::*;\n            use crate::mir::value::build_reg::value_to_reg;\n\n            let record_struct = ehx\n                .evaled_record_class_for_cons(record_cons)\n                .record_struct\n                .clone();\n\n            let b = if let Some(b) = b.try_to_builder() {\n                b\n            } else {\n                panic!(\"need builder to access field of boxed record reg\");\n            };\n\n            let record_reg =\n                value_to_reg(ehx, b, span, other_value, &boxed::TypeTag::Record.into());\n\n            let field_reg = b.push_reg(\n                span,\n                OpKind::LoadBoxedRecordField,\n                LoadBoxedRecordFieldOp {\n                    field_index,\n                    record_reg: record_reg.into(),\n                    record_struct: record_struct.clone(),\n                },\n            );\n\n            let field_abi_type = record_struct.field_abi_types[field_index].clone();\n            value::RegValue::new(field_reg, field_abi_type).into()\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/mir/ret_value.rs",
    "content": "use arret_runtime::abitype;\nuse arret_syntax::span::Span;\n\nuse crate::mir::builder::{Builder, BuiltReg};\nuse crate::mir::error::{Error, Result};\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::ops;\nuse crate::mir::value;\nuse crate::mir::value::Value;\n\n/// Builds the ops to return a value from a function\n///\n/// This deals with uninhabited and void return values which require special handling.\npub fn build_value_ret(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    result: Result<Value>,\n    ret_abi: &abitype::RetAbiType,\n) {\n    use crate::mir::value::build_reg::value_to_reg;\n\n    match result {\n        Ok(result_value) => match ret_abi {\n            abitype::RetAbiType::Inhabited(abi_type) => {\n                let ret_reg = value_to_reg(ehx, b, span, &result_value, abi_type);\n                b.push(span, ops::OpKind::Ret(ret_reg.into()));\n            }\n            abitype::RetAbiType::Never => {\n                b.push(span, ops::OpKind::Unreachable);\n            }\n            abitype::RetAbiType::Void => {\n                b.push(span, ops::OpKind::RetVoid);\n            }\n        },\n        Err(Error::Diverged) => {}\n        Err(other) => {\n            panic!(\"unexpected error when returning value: {:?}\", other);\n        }\n    }\n}\n\npub fn ret_reg_to_value(ret_reg: BuiltReg, ret_abi: abitype::RetAbiType) -> Result<Value> {\n    match ret_abi {\n        abitype::RetAbiType::Inhabited(abi_type) => {\n            Ok(value::RegValue::new(ret_reg, abi_type).into())\n        }\n        abitype::RetAbiType::Never => Err(Error::Diverged),\n        abitype::RetAbiType::Void => Ok(Value::List(Box::new([]), None)),\n    }\n}\n"
  },
  {
    "path": "compiler/mir/rust_fun.rs",
    "content": "use arret_syntax::span::Span;\n\nuse crate::codegen::GenAbi;\nuse crate::mir::builder::Builder;\nuse crate::mir::error::{Error, Result};\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::ops;\nuse crate::mir::polymorph::PolymorphAbi;\nuse crate::mir::value::Value;\nuse crate::rfi;\nuse crate::ty;\nuse crate::ty::purity::Purity;\nuse crate::ty::Ty;\n\n/// Returns the upper bound on the purity for a Rust fun\npub fn rust_fun_purity_upper_bound(rust_fun: &rfi::Fun) -> Purity {\n    let arret_fun_type = rust_fun.arret_fun_type();\n\n    if arret_fun_type.ret().is_never() {\n        Purity::Impure\n    } else if arret_fun_type.purity() == &Purity::Pure.into() {\n        Purity::Pure\n    } else {\n        Purity::Impure\n    }\n}\n\npub fn build_rust_fun_app(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    ret_ty: &ty::Ref<ty::Mono>,\n    rust_fun: &rfi::Fun,\n    call_purity: Purity,\n    arg_list_value: Value,\n) -> Result<Value> {\n    use crate::mir::arg_list::build_save_arg_list_to_regs;\n    use crate::mir::ops::*;\n    use crate::mir::value::from_reg::reg_to_value;\n    use arret_runtime::abitype::RetAbiType;\n\n    let mut arg_abi_types = rust_fun\n        .params()\n        .iter()\n        .map(|param_abi_type| &param_abi_type.abi_type);\n\n    let rest_abi_type = if rust_fun.has_rest() {\n        arg_abi_types.next_back()\n    } else {\n        None\n    };\n\n    let arg_regs =\n        build_save_arg_list_to_regs(ehx, b, span, arg_list_value, arg_abi_types, rest_abi_type);\n\n    let purity_upper_bound = rust_fun_purity_upper_bound(rust_fun);\n\n    let abi = GenAbi {\n        takes_task: rust_fun.takes_task(),\n        params: rust_fun.params().to_owned().into(),\n        ret: rust_fun.ret().clone(),\n    };\n\n    ehx.register_rust_fun_with_jit(rust_fun);\n    let callee = ops::Callee::StaticSymbol(ops::StaticSymbol {\n        symbol: rust_fun.symbol(),\n        impure: purity_upper_bound == Purity::Impure,\n        abi,\n    });\n\n    let ret_reg = b.push_reg(\n        span,\n        OpKind::Call,\n        CallOp {\n            callee,\n            impure: call_purity == Purity::Impure,\n            args: arg_regs.into_boxed_slice(),\n        },\n    );\n\n    match rust_fun.ret() {\n        RetAbiType::Void => Ok(Value::List(Box::new([]), None)),\n        RetAbiType::Never => {\n            b.push(span, OpKind::Unreachable);\n            Err(Error::Diverged)\n        }\n        RetAbiType::Inhabited(abi_type) => Ok(reg_to_value(ehx, ret_reg, abi_type, ret_ty)),\n    }\n}\n\npub fn ops_for_rust_fun(\n    ehx: &mut EvalHirCtx,\n    rust_fun: &rfi::Fun,\n    wanted_abi: PolymorphAbi,\n) -> ops::Fun {\n    use crate::mir::arg_list::{build_load_arg_list_value, LoadedArgList};\n    use crate::mir::optimise::optimise_fun;\n    use crate::mir::ret_value::build_value_ret;\n\n    let mut b = Builder::new();\n    let span = rust_fun.span();\n\n    let fun_symbol = format!(\"{}_adapter\", rust_fun.symbol());\n\n    let LoadedArgList {\n        param_regs,\n        arg_list_value,\n        ..\n    } = build_load_arg_list_value(ehx, &mut b, &wanted_abi, rust_fun.arret_fun_type().params());\n\n    let purity_upper_bound = rust_fun_purity_upper_bound(rust_fun);\n    let ret_ty = Ty::Any.into();\n    let app_result = build_rust_fun_app(\n        ehx,\n        &mut b,\n        span,\n        &ret_ty,\n        rust_fun,\n        purity_upper_bound,\n        arg_list_value,\n    );\n\n    build_value_ret(ehx, &mut b, span, app_result, &wanted_abi.ret);\n\n    optimise_fun(ops::Fun {\n        span,\n        source_name: Some(fun_symbol.into()),\n\n        abi: wanted_abi.into(),\n        param_regs,\n        ops: b.into_ops(),\n    })\n}\n"
  },
  {
    "path": "compiler/mir/specific_abi_type.rs",
    "content": "use arret_runtime::abitype;\nuse arret_runtime::boxed::TypeTag;\n\nuse crate::mir::tagset::TypeTagSet;\nuse crate::mir::value::Value;\nuse crate::ty;\nuse crate::ty::Ty;\n\nconst ANY_BOXED_ABI_TYPE: abitype::BoxedAbiType = abitype::BoxedAbiType::Any;\n\nconst TOP_RECORD_BOXED_ABI_TYPE: abitype::BoxedAbiType =\n    abitype::BoxedAbiType::UniqueTagged(TypeTag::Record);\n\nfn specific_boxed_abi_type_for_type_tag(type_tag: TypeTag) -> &'static abitype::BoxedAbiType {\n    use arret_runtime::abitype::EncodeBoxedAbiType;\n    use arret_runtime::boxed;\n\n    match type_tag {\n        TypeTag::Pair => &boxed::Pair::<boxed::Any>::BOXED_ABI_TYPE,\n        TypeTag::Vector => &boxed::Vector::<boxed::Any>::BOXED_ABI_TYPE,\n        TypeTag::Char => &boxed::Char::BOXED_ABI_TYPE,\n        TypeTag::Int => &boxed::Int::BOXED_ABI_TYPE,\n        TypeTag::Float => &boxed::Float::BOXED_ABI_TYPE,\n        TypeTag::Str => &boxed::Str::BOXED_ABI_TYPE,\n        TypeTag::Sym => &boxed::Sym::BOXED_ABI_TYPE,\n        TypeTag::True => &boxed::True::BOXED_ABI_TYPE,\n        TypeTag::False => &boxed::False::BOXED_ABI_TYPE,\n        TypeTag::Nil => &boxed::Nil::BOXED_ABI_TYPE,\n        TypeTag::FunThunk => &boxed::FunThunk::BOXED_ABI_TYPE,\n        TypeTag::Record => &TOP_RECORD_BOXED_ABI_TYPE,\n        TypeTag::Set => &boxed::Set::<boxed::Any>::BOXED_ABI_TYPE,\n        TypeTag::Map => &boxed::Map::<boxed::Any, boxed::Any>::BOXED_ABI_TYPE,\n    }\n}\n\nfn specific_abi_type_for_type_tag(type_tag: TypeTag) -> abitype::AbiType {\n    match type_tag {\n        TypeTag::Int => abitype::AbiType::Int,\n        TypeTag::Float => abitype::AbiType::Float,\n        TypeTag::Char => abitype::AbiType::Char,\n        TypeTag::Sym => abitype::AbiType::InternedSym,\n        other_tag => specific_boxed_abi_type_for_type_tag(other_tag)\n            .clone()\n            .into(),\n    }\n}\n\nfn specific_boxed_abi_type_for_type_tags(\n    possible_type_tags: TypeTagSet,\n) -> &'static abitype::BoxedAbiType {\n    use arret_runtime::abitype::EncodeBoxedAbiType;\n    use arret_runtime::boxed;\n\n    if possible_type_tags.len() == 1 {\n        let single_type_tag = possible_type_tags.into_iter().next().unwrap();\n        specific_boxed_abi_type_for_type_tag(single_type_tag)\n    } else if possible_type_tags == [TypeTag::Pair, TypeTag::Nil].iter().collect() {\n        &boxed::List::<boxed::Any>::BOXED_ABI_TYPE\n    } else if possible_type_tags == [TypeTag::Float, TypeTag::Int].iter().collect() {\n        &boxed::Num::BOXED_ABI_TYPE\n    } else if possible_type_tags == [TypeTag::True, TypeTag::False].iter().collect() {\n        &boxed::Bool::BOXED_ABI_TYPE\n    } else {\n        &ANY_BOXED_ABI_TYPE\n    }\n}\n\npub fn specific_boxed_abi_type_for_ty_ref<M: ty::Pm>(\n    ty_ref: &ty::Ref<M>,\n) -> &'static abitype::BoxedAbiType {\n    specific_boxed_abi_type_for_type_tags(ty_ref.into())\n}\n\nfn specific_abi_type_for_type_tags(possible_type_tags: TypeTagSet) -> abitype::AbiType {\n    if possible_type_tags.is_subset([TypeTag::True, TypeTag::False].iter().collect()) {\n        abitype::AbiType::Bool\n    } else if possible_type_tags.len() == 1 {\n        let single_type_tag = possible_type_tags.into_iter().next().unwrap();\n        specific_abi_type_for_type_tag(single_type_tag)\n    } else {\n        specific_boxed_abi_type_for_type_tags(possible_type_tags)\n            .clone()\n            .into()\n    }\n}\n\n/// Returns a specific ABI type to encode the given ty_ref\npub fn specific_abi_type_for_ty_ref<M: ty::Pm>(ty_ref: &ty::Ref<M>) -> abitype::AbiType {\n    use crate::ty::list_iter::ListIterator;\n\n    match ty_ref.resolve_to_ty() {\n        Ty::List(list_ty) if !list_ty.is_empty() => {\n            let member_ty_ref = ListIterator::new(list_ty).collect_rest();\n            let member_boxed_abi_type = specific_boxed_abi_type_for_ty_ref(&member_ty_ref);\n\n            if list_ty.fixed().is_empty() {\n                abitype::BoxedAbiType::List(member_boxed_abi_type).into()\n            } else {\n                abitype::BoxedAbiType::Pair(member_boxed_abi_type).into()\n            }\n        }\n        Ty::Vectorof(member_ty) => {\n            let member_boxed_abi_type = specific_boxed_abi_type_for_ty_ref(member_ty.as_ref());\n            abitype::BoxedAbiType::Vector(member_boxed_abi_type).into()\n        }\n        Ty::Vector(member_tys) => {\n            let member_ty_ref = ty::unify::unify_ty_ref_iter(member_tys.iter().cloned());\n            let member_boxed_abi_type = specific_boxed_abi_type_for_ty_ref(&member_ty_ref);\n\n            abitype::BoxedAbiType::Vector(member_boxed_abi_type).into()\n        }\n        _ => specific_abi_type_for_type_tags(ty_ref.into()),\n    }\n}\n\npub fn specific_ret_abi_type_for_ty_ref<M: ty::Pm>(ty_ref: &ty::Ref<M>) -> abitype::RetAbiType {\n    if ty_ref == &ty::List::empty().into() {\n        abitype::RetAbiType::Void\n    } else {\n        specific_abi_type_for_type_tags(ty_ref.into()).into()\n    }\n}\n\nfn specific_type_for_values<'v, F, T>(\n    possible_values: impl Iterator<Item = &'v Value>,\n    tagset_to_type: F,\n) -> T\nwhere\n    F: FnOnce(TypeTagSet) -> T,\n{\n    use crate::mir::value::types::possible_type_tags_for_value;\n\n    let possible_type_tags = possible_values\n        .map(possible_type_tags_for_value)\n        .fold(TypeTagSet::new(), |acc, type_tags| acc | type_tags);\n\n    tagset_to_type(possible_type_tags)\n}\n\n/// Returns a specific boxed ABI type to encode the given set of possible values\npub fn specific_boxed_abi_type_for_values<'v>(\n    possible_values: impl Iterator<Item = &'v Value>,\n) -> abitype::BoxedAbiType {\n    specific_type_for_values(possible_values, specific_boxed_abi_type_for_type_tags).clone()\n}\n\n/// Returns a specific ABI type to compactly encode the given set of possible values\npub fn specific_abi_type_for_values<'v>(\n    possible_values: impl Iterator<Item = &'v Value>,\n) -> abitype::AbiType {\n    specific_type_for_values(possible_values, specific_abi_type_for_type_tags)\n}\n\n/// Return a specific ABI type to compactly encode the given value\npub fn specific_abi_type_for_value(value: &Value) -> abitype::AbiType {\n    specific_abi_type_for_values(std::iter::once(value))\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::hir::poly_for_str;\n    use arret_runtime::abitype::EncodeBoxedAbiType;\n    use arret_runtime::boxed;\n\n    fn assert_abi_type_for_str(abi_type: abitype::AbiType, ty_str: &'static str) {\n        let poly = poly_for_str(ty_str);\n        assert_eq!(abi_type, specific_abi_type_for_ty_ref(&poly));\n    }\n\n    #[test]\n    fn test_specific_abi_type_for_ty_ref() {\n        assert_abi_type_for_str(abitype::AbiType::Bool, \"true\");\n        assert_abi_type_for_str(abitype::AbiType::Bool, \"false\");\n        assert_abi_type_for_str(abitype::AbiType::Bool, \"Bool\");\n\n        assert_abi_type_for_str(abitype::AbiType::Float, \"Float\");\n        assert_abi_type_for_str(abitype::AbiType::Int, \"Int\");\n        assert_abi_type_for_str(boxed::Num::BOXED_ABI_TYPE.into(), \"Num\");\n        assert_abi_type_for_str(abitype::AbiType::Char, \"Char\");\n        assert_abi_type_for_str(abitype::AbiType::InternedSym, \"Sym\");\n        assert_abi_type_for_str(abitype::BoxedAbiType::Any.into(), \"(RawU Num Bool)\");\n\n        assert_abi_type_for_str(boxed::Nil::BOXED_ABI_TYPE.into(), \"(List)\");\n\n        assert_abi_type_for_str(\n            abitype::BoxedAbiType::List(&boxed::Bool::BOXED_ABI_TYPE).into(),\n            \"(List & Bool)\",\n        );\n\n        assert_abi_type_for_str(\n            abitype::BoxedAbiType::Pair(&boxed::Num::BOXED_ABI_TYPE).into(),\n            \"(List Float & Int)\",\n        );\n\n        assert_abi_type_for_str(\n            abitype::BoxedAbiType::Vector(&boxed::Str::BOXED_ABI_TYPE).into(),\n            \"(Vectorof Str)\",\n        );\n\n        assert_abi_type_for_str(\n            abitype::BoxedAbiType::Vector(&boxed::Sym::BOXED_ABI_TYPE).into(),\n            \"(Vector 'foo 'bar)\",\n        );\n    }\n}\n"
  },
  {
    "path": "compiler/mir/tagset.rs",
    "content": "use std::{fmt, iter, ops};\n\nuse crate::ty;\nuse crate::ty::Ty;\nuse arret_runtime::abitype;\nuse arret_runtime::boxed::{TypeTag, ALL_TYPE_TAGS};\n\nconst INNER_BITS: u8 = 32;\ntype Inner = u32;\n\n#[derive(Clone, Copy, PartialEq, Eq, Default)]\npub struct TypeTagSet(Inner);\n\n/// Efficient representation of a set of TypeTag\nimpl TypeTagSet {\n    pub fn new() -> TypeTagSet {\n        TypeTagSet(0)\n    }\n\n    pub fn all() -> TypeTagSet {\n        ALL_TYPE_TAGS.iter().collect()\n    }\n\n    pub fn is_empty(self) -> bool {\n        self.0 == 0\n    }\n\n    pub fn len(self) -> usize {\n        self.0.count_ones() as usize\n    }\n\n    pub fn insert(&mut self, type_tag: TypeTag) {\n        // The compiler is smart enough to eliminate this\n        assert!((type_tag as u8) < INNER_BITS);\n        self.0 |= 1 << type_tag as u8;\n    }\n\n    pub fn is_subset(self, superset: Self) -> bool {\n        (self.0 & superset.0) == self.0\n    }\n\n    pub fn is_disjoint(self, other: Self) -> bool {\n        self.intersection(other).is_empty()\n    }\n\n    pub fn intersection(self, other: Self) -> TypeTagSet {\n        TypeTagSet(self.0 & other.0)\n    }\n\n    pub fn union(self, other: Self) -> TypeTagSet {\n        TypeTagSet(self.0 | other.0)\n    }\n\n    pub fn contains(self, type_tag: TypeTag) -> bool {\n        let type_tag_set: TypeTagSet = type_tag.into();\n        type_tag_set.is_subset(self)\n    }\n\n    /// Returns an iterator over all type tags in the set\n    ///\n    /// These are returned in sorted order.\n    pub fn into_iter(self) -> impl Iterator<Item = TypeTag> {\n        ALL_TYPE_TAGS\n            .iter()\n            .cloned()\n            .filter(move |type_tag| self.contains(*type_tag))\n    }\n}\n\nimpl fmt::Debug for TypeTagSet {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        formatter.write_str(\"TypeTagSet(\")?;\n        formatter.debug_list().entries(self.into_iter()).finish()?;\n        formatter.write_str(\")\")\n    }\n}\n\nimpl From<TypeTag> for TypeTagSet {\n    fn from(type_tag: TypeTag) -> TypeTagSet {\n        let mut type_tag_set = TypeTagSet::new();\n        type_tag_set.insert(type_tag);\n        type_tag_set\n    }\n}\n\nimpl<'a, M> From<&'a ty::Ref<M>> for TypeTagSet\nwhere\n    M: ty::Pm,\n{\n    fn from(ty_ref: &'a ty::Ref<M>) -> TypeTagSet {\n        match ty_ref.resolve_to_ty() {\n            Ty::Any => TypeTagSet::all(),\n            Ty::Int => TypeTag::Int.into(),\n            Ty::Float => TypeTag::Float.into(),\n            Ty::Char => TypeTag::Char.into(),\n            Ty::Bool => [TypeTag::True, TypeTag::False].iter().collect(),\n            Ty::Num => [TypeTag::Int, TypeTag::Float].iter().collect(),\n            Ty::LitBool(true) => TypeTag::True.into(),\n            Ty::LitBool(false) => TypeTag::False.into(),\n            Ty::Sym | Ty::LitSym(_) => TypeTag::Sym.into(),\n            Ty::Str => TypeTag::Str.into(),\n            Ty::Fun(_) | Ty::TopFun(_) | Ty::TyPred(_) | Ty::EqPred => TypeTag::FunThunk.into(),\n            Ty::Vector(_) | Ty::Vectorof(_) => TypeTag::Vector.into(),\n            Ty::Set(_) => TypeTag::Set.into(),\n            Ty::Map(_) => TypeTag::Map.into(),\n            Ty::TopRecord | Ty::RecordClass(_) | Ty::Record(_) => TypeTag::Record.into(),\n            Ty::List(list) => {\n                if list.is_empty() {\n                    TypeTag::Nil.into()\n                } else if !list.fixed().is_empty() {\n                    TypeTag::Pair.into()\n                } else {\n                    [TypeTag::Nil, TypeTag::Pair].iter().collect()\n                }\n            }\n            Ty::Union(members) => members\n                .iter()\n                .map(TypeTagSet::from)\n                .fold(TypeTagSet::new(), |a, b| a | b),\n            Ty::Intersect(members) => members\n                .iter()\n                .map(TypeTagSet::from)\n                .fold(TypeTagSet::all(), |a, b| a & b),\n        }\n    }\n}\n\nimpl<'a> From<&'a abitype::BoxedAbiType> for TypeTagSet {\n    fn from(boxed_abi_type: &'a abitype::BoxedAbiType) -> TypeTagSet {\n        use arret_runtime::abitype::BoxedAbiType;\n\n        match boxed_abi_type {\n            BoxedAbiType::Any => TypeTagSet::all(),\n            BoxedAbiType::UniqueTagged(type_tag) => (*type_tag).into(),\n            BoxedAbiType::List(_) => [TypeTag::Pair, TypeTag::Nil].iter().collect(),\n            BoxedAbiType::Pair(_) => TypeTag::Pair.into(),\n            BoxedAbiType::Vector(_) => TypeTag::Vector.into(),\n            BoxedAbiType::Set(_) => TypeTag::Set.into(),\n            BoxedAbiType::Map(_, _) => TypeTag::Map.into(),\n            BoxedAbiType::Union(_, type_tags) => type_tags.iter().collect(),\n        }\n    }\n}\n\nimpl<'a> From<&'a abitype::AbiType> for TypeTagSet {\n    fn from(abi_type: &'a abitype::AbiType) -> TypeTagSet {\n        use arret_runtime::abitype::AbiType;\n\n        match abi_type {\n            AbiType::Int => TypeTag::Int.into(),\n            AbiType::Float => TypeTag::Float.into(),\n            AbiType::Char => TypeTag::Char.into(),\n            AbiType::Bool => [TypeTag::True, TypeTag::False].iter().collect(),\n            AbiType::InternedSym => TypeTag::Sym.into(),\n            AbiType::Boxed(boxed_abi_type) => boxed_abi_type.into(),\n            AbiType::Callback(_) => TypeTag::FunThunk.into(),\n        }\n    }\n}\n\nimpl From<abitype::AbiType> for TypeTagSet {\n    fn from(abi_type: abitype::AbiType) -> TypeTagSet {\n        (&abi_type).into()\n    }\n}\n\nimpl iter::FromIterator<TypeTag> for TypeTagSet {\n    fn from_iter<I: IntoIterator<Item = TypeTag>>(iter: I) -> TypeTagSet {\n        let mut type_tag_set = TypeTagSet::new();\n        for type_tag in iter {\n            type_tag_set.insert(type_tag);\n        }\n        type_tag_set\n    }\n}\n\nimpl<'a> iter::FromIterator<&'a TypeTag> for TypeTagSet {\n    fn from_iter<I: IntoIterator<Item = &'a TypeTag>>(iter: I) -> TypeTagSet {\n        iter.into_iter().cloned().collect()\n    }\n}\n\nimpl ops::BitOr for TypeTagSet {\n    type Output = Self;\n\n    fn bitor(self, rhs: Self) -> Self {\n        self.union(rhs)\n    }\n}\n\nimpl ops::BitAnd for TypeTagSet {\n    type Output = Self;\n\n    fn bitand(self, rhs: Self) -> Self {\n        self.intersection(rhs)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::source::EMPTY_SPAN;\n\n    #[test]\n    fn basic_operations() {\n        let empty_set = TypeTagSet::new();\n        let list_set: TypeTagSet = [TypeTag::Nil, TypeTag::Pair].iter().cloned().collect();\n        let nil_sym_set: TypeTagSet = [TypeTag::Nil, TypeTag::Sym].iter().cloned().collect();\n        let pair_set: TypeTagSet = TypeTag::Pair.into();\n        let nil_set: TypeTagSet = TypeTag::Nil.into();\n        let full_set = TypeTagSet::all();\n\n        assert!(empty_set.is_empty());\n        assert!(!full_set.is_empty());\n        assert!(!nil_sym_set.is_empty());\n        assert!(!pair_set.is_empty());\n        assert!(!full_set.is_empty());\n\n        assert!(empty_set.is_subset(full_set));\n        assert!(empty_set.is_subset(nil_sym_set));\n        assert!(empty_set.is_subset(empty_set));\n\n        assert!(list_set.is_subset(full_set));\n        assert!(list_set.is_subset(list_set));\n        assert!(!list_set.is_subset(pair_set));\n        assert!(!list_set.is_subset(empty_set));\n\n        assert!(empty_set.is_disjoint(full_set));\n        assert!(nil_sym_set.is_disjoint(pair_set));\n        assert!(!nil_sym_set.is_disjoint(list_set));\n\n        assert_eq!(nil_set, list_set & nil_sym_set);\n        assert_eq!(list_set, pair_set | nil_set);\n    }\n\n    #[test]\n    fn set_into_iter() {\n        use std::collections::HashSet;\n\n        let empty_set = TypeTagSet::new();\n        let list_set: TypeTagSet = [TypeTag::Nil, TypeTag::Pair].iter().collect();\n        let nil_set: TypeTagSet = TypeTag::Nil.into();\n        let full_set = TypeTagSet::all();\n\n        assert_eq!(None, empty_set.into_iter().next());\n\n        let mut nil_set_iter = nil_set.into_iter();\n        assert_eq!(Some(TypeTag::Nil), nil_set_iter.next());\n        assert_eq!(None, nil_set_iter.next());\n\n        let list_hash_set: HashSet<TypeTag> = list_set.into_iter().collect();\n        assert_eq!(2, list_hash_set.len());\n        assert!(list_hash_set.contains(&TypeTag::Pair));\n        assert!(list_hash_set.contains(&TypeTag::Nil));\n\n        assert_eq!(ALL_TYPE_TAGS.len(), full_set.into_iter().count());\n    }\n\n    #[test]\n    fn from_ty_ref() {\n        let int_ty_ref: ty::Ref<ty::Poly> = Ty::Int.into();\n        assert_eq!(\n            TypeTagSet::from(TypeTag::Int),\n            TypeTagSet::from(&int_ty_ref)\n        );\n\n        let poly_sym_ref: ty::Ref<ty::Poly> =\n            ty::TVar::new(EMPTY_SPAN, \"tvar1\".into(), Ty::Sym.into()).into();\n        assert_eq!(\n            TypeTagSet::from(TypeTag::Sym),\n            TypeTagSet::from(&poly_sym_ref)\n        );\n\n        let num_float_intersect: ty::Ref<ty::Poly> =\n            Ty::Intersect(Box::new([Ty::Num.into(), Ty::Float.into()])).into();\n        assert_eq!(\n            TypeTagSet::from(TypeTag::Float),\n            TypeTagSet::from(&num_float_intersect)\n        );\n    }\n}\n"
  },
  {
    "path": "compiler/mir/typred.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::abitype;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\n\nuse crate::mir::builder::{Builder, BuiltReg};\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::ops::*;\nuse crate::mir::tagset::TypeTagSet;\nuse crate::mir::value::build_reg::value_to_reg;\nuse crate::mir::value::from_reg::reg_to_value;\nuse crate::mir::value::types::TypeHint;\nuse crate::mir::value::Value;\nuse crate::ty;\nuse crate::ty::record;\nuse crate::ty::Ty;\n\n/// Returns a set of type tags that would satisfy the type predicate\nfn type_tags_for_test_ty(test_ty: &ty::pred::TestTy) -> TypeTagSet {\n    use crate::ty::pred::TestTy;\n\n    match test_ty {\n        TestTy::Str => boxed::TypeTag::Str.into(),\n        TestTy::Sym => boxed::TypeTag::Sym.into(),\n        TestTy::Int => boxed::TypeTag::Int.into(),\n        TestTy::Float => boxed::TypeTag::Float.into(),\n        TestTy::Char => boxed::TypeTag::Char.into(),\n        TestTy::Nil => boxed::TypeTag::Nil.into(),\n        TestTy::Fun => boxed::TypeTag::FunThunk.into(),\n        TestTy::Bool => [boxed::TypeTag::True, boxed::TypeTag::False]\n            .iter()\n            .collect(),\n        TestTy::Num => [boxed::TypeTag::Int, boxed::TypeTag::Float]\n            .iter()\n            .collect(),\n        TestTy::List => [boxed::TypeTag::Pair, boxed::TypeTag::Nil].iter().collect(),\n        TestTy::Vector => boxed::TypeTag::Vector.into(),\n        TestTy::Set => boxed::TypeTag::Set.into(),\n        TestTy::Map => boxed::TypeTag::Map.into(),\n        TestTy::TopRecord => boxed::TypeTag::Record.into(),\n        TestTy::RecordClass(_) => {\n            todo!(\"record classes\");\n        }\n    }\n}\n\nfn build_load_type_tag(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    value: &Value,\n    possible_type_tags: TypeTagSet,\n) -> BuiltReg {\n    let subject_reg = value_to_reg(ehx, b, span, value, &abitype::BoxedAbiType::Any.into()).into();\n\n    b.push_reg(\n        span,\n        OpKind::LoadBoxedTypeTag,\n        LoadBoxedTypeTagOp {\n            subject_reg,\n            possible_type_tags,\n        },\n    )\n}\n\nfn build_is_type_tag(\n    b: &mut Builder,\n    span: Span,\n    subject_type_tag_reg: BuiltReg,\n    test_tag: boxed::TypeTag,\n) -> BuiltReg {\n    let test_tag_reg = b.push_reg(span, OpKind::ConstTypeTag, test_tag);\n\n    b.push_reg(\n        span,\n        OpKind::TypeTagEqual,\n        BinaryOp {\n            lhs_reg: subject_type_tag_reg.into(),\n            rhs_reg: test_tag_reg.into(),\n        },\n    )\n}\n\nfn eval_tagged_ty_pred(\n    ehx: &mut EvalHirCtx,\n    b: &mut Option<Builder>,\n    span: Span,\n    subject_value: &Value,\n    qualifying_type_tags: TypeTagSet,\n) -> Value {\n    use crate::mir::value::types::possible_type_tags_for_value;\n    let possible_type_tags = possible_type_tags_for_value(subject_value);\n\n    if possible_type_tags.is_subset(qualifying_type_tags) {\n        // Statically true\n        return boxed::TRUE_INSTANCE.as_any_ref().into();\n    } else if qualifying_type_tags.is_disjoint(possible_type_tags) {\n        // Statically false\n        return boxed::FALSE_INSTANCE.as_any_ref().into();\n    }\n\n    let b = if let Some(some_b) = b {\n        some_b\n    } else {\n        panic!(\n            \"runtime tagged type predicate without builder: {:?} is in type tag set {:?}\",\n            subject_value, qualifying_type_tags\n        );\n    };\n\n    let subject_type_tag_reg = build_load_type_tag(ehx, b, span, subject_value, possible_type_tags);\n    let result_reg = (qualifying_type_tags & possible_type_tags)\n        .into_iter()\n        .fold(None, |tail_result_reg: Option<BuiltReg>, test_tag| {\n            let is_test_tag = build_is_type_tag(b, span, subject_type_tag_reg, test_tag);\n\n            if let Some(tail_result_reg) = tail_result_reg {\n                // Logically or this with our tail result\n                let or_result_reg = b.alloc_local();\n\n                let cond_op_kind = OpKind::Cond(CondOp {\n                    reg_phi: Some(RegPhi {\n                        output_reg: or_result_reg.into(),\n                        true_result_reg: is_test_tag.into(),\n                        false_result_reg: tail_result_reg.into(),\n                    }),\n                    test_reg: is_test_tag.into(),\n                    true_ops: Box::new([]),\n                    false_ops: Box::new([]),\n                });\n                b.push(span, cond_op_kind);\n\n                Some(or_result_reg)\n            } else {\n                // We are the first result\n                Some(is_test_tag)\n            }\n        })\n        .unwrap();\n\n    reg_to_value(\n        ehx,\n        result_reg,\n        &abitype::AbiType::Bool,\n        &Ty::<ty::Mono>::Bool.into(),\n    )\n}\n\nfn eval_record_ty_pred(\n    ehx: &mut EvalHirCtx,\n    b: &mut Option<Builder>,\n    span: Span,\n    subject_value: &Value,\n    test_cons: &record::ConsId,\n) -> Value {\n    use crate::mir::value::types::{possible_type_tags_for_value, type_hint_for_value};\n\n    let possible_type_tags = possible_type_tags_for_value(subject_value);\n\n    if !possible_type_tags.contains(boxed::TypeTag::Record) {\n        // Cannot be a record\n        return boxed::FALSE_INSTANCE.as_any_ref().into();\n    }\n\n    let is_definite_record = possible_type_tags == boxed::TypeTag::Record.into();\n    let definite_matching_cons =\n        if let TypeHint::KnownRecordCons(subject_cons) = type_hint_for_value(ehx, subject_value) {\n            let known_matching_cons = test_cons == &subject_cons;\n            if !known_matching_cons {\n                // This cannot possibly match regardless of if it's record\n                return boxed::FALSE_INSTANCE.as_any_ref().into();\n            }\n\n            known_matching_cons\n        } else {\n            false\n        };\n\n    if is_definite_record && definite_matching_cons {\n        // This is a record with a matching cons\n        return boxed::TRUE_INSTANCE.as_any_ref().into();\n    }\n\n    let b = if let Some(some_b) = b {\n        some_b\n    } else {\n        panic!(\"runtime record type predicate without builder\");\n    };\n\n    let is_record_reg = if is_definite_record {\n        None\n    } else {\n        // If this isn't guaranteed to be a record we need to test its type tag first\n        let subject_type_tag_reg =\n            build_load_type_tag(ehx, b, span, subject_value, possible_type_tags);\n\n        Some(build_is_type_tag(\n            b,\n            span,\n            subject_type_tag_reg,\n            boxed::TypeTag::Record,\n        ))\n    };\n\n    let is_record_class_b_and_reg = if definite_matching_cons {\n        None\n    } else {\n        // Create a builder for testing the record class\n        let mut is_record_class_b = Builder::new();\n\n        let record_reg = value_to_reg(\n            ehx,\n            &mut is_record_class_b,\n            span,\n            subject_value,\n            &abitype::BoxedAbiType::UniqueTagged(boxed::TypeTag::Record).into(),\n        )\n        .into();\n\n        // Load our subject record class ID\n        let subject_record_class_id_reg =\n            is_record_class_b.push_reg(span, OpKind::LoadBoxedRecordClassId, record_reg);\n\n        // Create our test record class ID\n        let test_record_class_id_reg = is_record_class_b.push_reg(\n            span,\n            OpKind::ConstRecordClassId,\n            ehx.evaled_record_class_for_cons(test_cons)\n                .record_struct\n                .clone(),\n        );\n\n        // Compare them for equality\n        let is_record_class_reg = is_record_class_b.push_reg(\n            span,\n            OpKind::RecordClassIdEqual,\n            BinaryOp {\n                lhs_reg: subject_record_class_id_reg.into(),\n                rhs_reg: test_record_class_id_reg.into(),\n            },\n        );\n\n        Some((is_record_class_b, is_record_class_reg))\n    };\n\n    let result_reg = match (is_record_reg, is_record_class_b_and_reg) {\n        (Some(is_record_reg), Some((is_record_class_b, is_record_class_reg))) => {\n            // Need to merge the type tag test with the record class test\n            let and_result_reg = b.alloc_local();\n\n            let cond_op_kind = OpKind::Cond(CondOp {\n                reg_phi: Some(RegPhi {\n                    output_reg: and_result_reg.into(),\n                    true_result_reg: is_record_class_reg.into(),\n                    false_result_reg: is_record_reg.into(),\n                }),\n                test_reg: is_record_reg.into(),\n                true_ops: is_record_class_b.into_ops(),\n                false_ops: Box::new([]),\n            });\n\n            b.push(span, cond_op_kind);\n            and_result_reg\n        }\n        (Some(is_record_reg), None) => is_record_reg,\n        (None, Some((is_record_class_b, is_record_class_reg))) => {\n            b.append(is_record_class_b.into_ops().into_vec());\n            is_record_class_reg\n        }\n        (None, None) => {\n            // This is unreachable but has a sane answer anyway\n            return boxed::TRUE_INSTANCE.as_any_ref().into();\n        }\n    };\n\n    reg_to_value(\n        ehx,\n        result_reg,\n        &abitype::AbiType::Bool,\n        &Ty::<ty::Mono>::Bool.into(),\n    )\n}\n\npub fn eval_ty_pred(\n    ehx: &mut EvalHirCtx,\n    b: &mut Option<Builder>,\n    span: Span,\n    subject_value: &Value,\n    test_ty: &ty::pred::TestTy,\n) -> Value {\n    match test_ty {\n        ty::pred::TestTy::RecordClass(record_cons) => {\n            eval_record_ty_pred(ehx, b, span, subject_value, record_cons)\n        }\n        tagged_ty => {\n            let qualifying_type_tags = type_tags_for_test_ty(tagged_ty);\n            eval_tagged_ty_pred(ehx, b, span, subject_value, qualifying_type_tags)\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/mir/value/arret_fun.rs",
    "content": "use std::rc::Rc;\n\nuse arret_syntax::datum::DataStr;\n\nuse crate::context::ModuleId;\nuse crate::hir;\nuse crate::mir::env_values::EnvValues;\nuse crate::ty;\nuse crate::ty::ty_args::TyArgs;\n\nnew_global_id_type!(ArretFunId);\n\n#[derive(Clone, Debug)]\nstruct ArretFunConsts {\n    id: ArretFunId,\n    module_id: Option<ModuleId>,\n    source_name: Option<DataStr>,\n    env_ty_args: TyArgs<ty::Mono>,\n    fun_expr: hir::Fun<hir::Inferred>,\n}\n\n#[derive(Clone, Debug)]\npub struct ArretFun {\n    consts: Rc<ArretFunConsts>,\n    env_values: EnvValues,\n}\n\nimpl ArretFun {\n    pub fn new(\n        module_id: Option<ModuleId>,\n        source_name: Option<DataStr>,\n        env_ty_args: TyArgs<ty::Mono>,\n        env_values: EnvValues,\n        fun_expr: hir::Fun<hir::Inferred>,\n    ) -> Self {\n        Self {\n            consts: Rc::new(ArretFunConsts {\n                id: ArretFunId::alloc(),\n                module_id,\n                source_name,\n                env_ty_args,\n                fun_expr,\n            }),\n            env_values,\n        }\n    }\n\n    pub fn id(&self) -> ArretFunId {\n        self.consts.id\n    }\n\n    /// Returns the optional module ID this function occurs in\n    ///\n    /// This is used to look up local variables from other definitions in the same module.\n    pub fn module_id(&self) -> Option<ModuleId> {\n        self.consts.module_id\n    }\n\n    pub fn source_name(&self) -> &Option<DataStr> {\n        &self.consts.source_name\n    }\n\n    pub fn env_ty_args(&self) -> &TyArgs<ty::Mono> {\n        &self.consts.env_ty_args\n    }\n\n    pub fn env_values(&self) -> &EnvValues {\n        &self.env_values\n    }\n\n    pub fn env_values_mut(&mut self) -> &mut EnvValues {\n        &mut self.env_values\n    }\n\n    pub fn fun_expr(&self) -> &hir::Fun<hir::Inferred> {\n        &self.consts.fun_expr\n    }\n\n    pub fn with_env_values(&self, env_values: EnvValues) -> ArretFun {\n        ArretFun {\n            consts: self.consts.clone(),\n            env_values,\n        }\n    }\n\n    /// Indicates if this `ArretFun` is used in multiple places\n    ///\n    /// This is a heuristic; if a `Fun` is bound to a variable this will return true regardless\n    /// of the number of usages.\n    pub fn has_multiple_usages(&self) -> bool {\n        // This is a hack but has the benefit of not requiring a separate analysis pass\n        Rc::strong_count(&self.consts) > 1\n    }\n}\n"
  },
  {
    "path": "compiler/mir/value/build_reg.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::abitype;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::builder::BuiltReg;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::value;\nuse crate::mir::value::Value;\nuse crate::rfi;\nuse crate::ty::record;\n\nenum RestLen {\n    Known(usize),\n    Loaded(BuiltReg),\n}\n\nfn const_to_reg(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    any_ref: Gc<boxed::Any>,\n    abi_type: &abitype::AbiType,\n) -> BuiltReg {\n    use crate::mir::ops::*;\n    use arret_runtime::boxed::prelude::*;\n\n    let subtype = any_ref.as_subtype();\n\n    match (subtype, abi_type) {\n        (boxed::AnySubtype::Int(int_ref), abitype::AbiType::Int) => {\n            b.push_reg(span, OpKind::ConstInt64, int_ref.value())\n        }\n        (boxed::AnySubtype::Float(float_ref), abitype::AbiType::Float) => {\n            b.push_reg(span, OpKind::ConstFloat, float_ref.value())\n        }\n        (boxed::AnySubtype::Char(char_ref), abitype::AbiType::Char) => {\n            b.push_reg(span, OpKind::ConstChar, char_ref.value())\n        }\n        (boxed::AnySubtype::True(_), abitype::AbiType::Bool) => {\n            b.push_reg(span, OpKind::ConstBool, true)\n        }\n        (boxed::AnySubtype::False(_), abitype::AbiType::Bool) => {\n            b.push_reg(span, OpKind::ConstBool, false)\n        }\n        (boxed::AnySubtype::Sym(sym_ref), abitype::AbiType::InternedSym) => {\n            b.push_reg(span, OpKind::ConstInternedSym, sym_ref.name(ehx).into())\n        }\n        (boxed::AnySubtype::Int(int_ref), abitype::AbiType::Boxed(to_abi_type)) => {\n            let from_abi_type = boxed::TypeTag::Int.into();\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedInt, int_ref.value());\n\n            b.cast_boxed_cond(span, &from_abi_type, from_reg, to_abi_type.clone())\n        }\n        (boxed::AnySubtype::Float(float_ref), abitype::AbiType::Boxed(to_abi_type)) => {\n            let from_abi_type = boxed::TypeTag::Float.into();\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedFloat, float_ref.value());\n\n            b.cast_boxed_cond(span, &from_abi_type, from_reg, to_abi_type.clone())\n        }\n        (boxed::AnySubtype::Char(char_ref), abitype::AbiType::Boxed(to_abi_type)) => {\n            let from_abi_type = boxed::TypeTag::Char.into();\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedChar, char_ref.value());\n\n            b.cast_boxed_cond(span, &from_abi_type, from_reg, to_abi_type.clone())\n        }\n        (boxed::AnySubtype::Str(str_ref), abitype::AbiType::Boxed(to_abi_type)) => {\n            let from_abi_type = boxed::TypeTag::Str.into();\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedStr, str_ref.as_str().into());\n\n            b.cast_boxed_cond(span, &from_abi_type, from_reg, to_abi_type.clone())\n        }\n        (boxed::AnySubtype::Sym(sym_ref), abitype::AbiType::Boxed(to_abi_type)) => {\n            let from_abi_type = boxed::TypeTag::Sym.into();\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedSym, sym_ref.name(ehx).into());\n\n            b.cast_boxed_cond(span, &from_abi_type, from_reg, to_abi_type.clone())\n        }\n        (boxed::AnySubtype::False(_), abitype::AbiType::Boxed(to_abi_type)) => {\n            let from_abi_type = boxed::TypeTag::False.into();\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedFalse, ());\n\n            b.cast_boxed_cond(span, &from_abi_type, from_reg, to_abi_type.clone())\n        }\n        (boxed::AnySubtype::True(_), abitype::AbiType::Boxed(to_abi_type)) => {\n            let from_abi_type = boxed::TypeTag::True.into();\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedTrue, ());\n\n            b.cast_boxed_cond(span, &from_abi_type, from_reg, to_abi_type.clone())\n        }\n        (boxed::AnySubtype::Nil(_), abitype::AbiType::Boxed(to_abi_type)) => {\n            let from_abi_type = boxed::TypeTag::Nil.into();\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedNil, ());\n\n            b.cast_boxed_cond(span, &from_abi_type, from_reg, to_abi_type.clone())\n        }\n        (boxed::AnySubtype::Pair(pair_ref), abitype::AbiType::Boxed(to_abi_type)) => {\n            let head_reg = const_to_reg(\n                ehx,\n                b,\n                span,\n                pair_ref.head(),\n                &abitype::BoxedAbiType::Any.into(),\n            );\n            let rest_reg = const_to_reg(\n                ehx,\n                b,\n                span,\n                pair_ref.rest().as_any_ref(),\n                &abitype::TOP_LIST_BOXED_ABI_TYPE.into(),\n            );\n            let list_len_reg = b.push_reg(span, OpKind::ConstInt64, pair_ref.len() as i64);\n\n            let from_reg = b.push_reg(\n                span,\n                OpKind::ConstBoxedPair,\n                BoxPairOp {\n                    head_reg: head_reg.into(),\n                    rest_reg: rest_reg.into(),\n                    list_len_reg: list_len_reg.into(),\n                },\n            );\n\n            b.cast_boxed_cond(\n                span,\n                &boxed::TypeTag::Pair.into(),\n                from_reg,\n                to_abi_type.clone(),\n            )\n        }\n        (boxed::AnySubtype::Record(record_ref), abitype::AbiType::Boxed(to_abi_type)) => {\n            let record_cons = ehx\n                .cons_for_jit_record_class_id(record_ref.class_id())\n                .expect(\"unable to lookup record cons for JIT record class ID\");\n\n            let record_struct = ehx\n                .record_class_for_cons\n                .get(record_cons)\n                .expect(\"unable to lookup record class for cons\")\n                .record_struct\n                .clone();\n\n            let field_values: Vec<_> = record_ref.field_values(ehx.as_heap()).collect();\n\n            let field_regs = field_values\n                .into_iter()\n                .zip(record_struct.field_abi_types.iter())\n                .map(|(field_value, abi_type)| {\n                    let built_reg =\n                        record_field_value_to_const_reg(ehx, b, span, &field_value, abi_type);\n\n                    built_reg.into()\n                })\n                .collect();\n\n            let box_record_op = BoxRecordOp {\n                record_struct,\n                field_regs,\n            };\n\n            let from_abi_type = boxed::TypeTag::Record.into();\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedRecord, box_record_op);\n\n            b.cast_boxed_cond(span, &from_abi_type, from_reg, to_abi_type.clone())\n        }\n        (boxed::AnySubtype::Vector(vector_ref), abitype::AbiType::Boxed(to_abi_type)) => {\n            let element_regs = vector_ref\n                .iter()\n                .map(|element_ref| {\n                    const_to_reg(\n                        ehx,\n                        b,\n                        span,\n                        element_ref,\n                        &abitype::BoxedAbiType::Any.into(),\n                    )\n                    .into()\n                })\n                .collect();\n\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedVector, element_regs);\n\n            b.cast_boxed_cond(\n                span,\n                &boxed::TypeTag::Vector.into(),\n                from_reg,\n                to_abi_type.clone(),\n            )\n        }\n        (boxed::AnySubtype::Set(set_ref), abitype::AbiType::Boxed(to_abi_type)) => {\n            let element_regs = set_ref\n                .iter()\n                .map(|element_ref| {\n                    const_to_reg(\n                        ehx,\n                        b,\n                        span,\n                        element_ref,\n                        &abitype::BoxedAbiType::Any.into(),\n                    )\n                    .into()\n                })\n                .collect();\n\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedSet, element_regs);\n\n            b.cast_boxed_cond(\n                span,\n                &boxed::TypeTag::Set.into(),\n                from_reg,\n                to_abi_type.clone(),\n            )\n        }\n        (boxed::AnySubtype::Map(map_ref), abitype::AbiType::Boxed(to_abi_type)) => {\n            let entry_regs = map_ref\n                .iter()\n                .map(|(key_ref, value_ref)| {\n                    let key_reg =\n                        const_to_reg(ehx, b, span, key_ref, &abitype::BoxedAbiType::Any.into())\n                            .into();\n\n                    let value_reg =\n                        const_to_reg(ehx, b, span, value_ref, &abitype::BoxedAbiType::Any.into())\n                            .into();\n\n                    (key_reg, value_reg)\n                })\n                .collect();\n\n            let from_reg = b.push_reg(span, OpKind::ConstBoxedMap, entry_regs);\n\n            b.cast_boxed_cond(\n                span,\n                &boxed::TypeTag::Map.into(),\n                from_reg,\n                to_abi_type.clone(),\n            )\n        }\n        (boxed::AnySubtype::FunThunk(fun_thunk_ref), abi_type) => {\n            let fun_value = ehx\n                .jit_boxed_to_fun_value(unsafe { Gc::new(fun_thunk_ref as *const _) })\n                .expect(\"attempt to convert unknown fun thunk to reg\")\n                .clone();\n\n            value_to_reg(ehx, b, span, &fun_value, abi_type)\n        }\n        (subtype, abi_type) => unimplemented!(\n            \"Unimplemented const {:?} to reg {:?} conversion\",\n            subtype,\n            abi_type\n        ),\n    }\n}\n\nfn list_to_reg(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    fixed: &[Value],\n    rest: Option<&Value>,\n    boxed_abi_type: &abitype::BoxedAbiType,\n) -> BuiltReg {\n    use crate::mir::ops::*;\n    use crate::mir::value::list::{list_value_len, ListValueLen};\n    use arret_runtime::abitype::TOP_LIST_BOXED_ABI_TYPE;\n\n    let tail_reg = if let Some(rest) = rest {\n        value_to_reg(\n            ehx,\n            b,\n            span,\n            rest,\n            &abitype::AbiType::Boxed(TOP_LIST_BOXED_ABI_TYPE),\n        )\n    } else {\n        let nil_reg = b.push_reg(span, OpKind::ConstBoxedNil, ());\n        b.cast_boxed(span, nil_reg, TOP_LIST_BOXED_ABI_TYPE)\n    };\n\n    let list_reg = if fixed.is_empty() {\n        tail_reg\n    } else {\n        let rest_len = match rest {\n            Some(rest) => match list_value_len(rest) {\n                ListValueLen::Exact(known) => RestLen::Known(known),\n                ListValueLen::Min(min_list_len) => {\n                    let len_reg = b.push_reg(\n                        span,\n                        OpKind::LoadBoxedListLen,\n                        LoadBoxedListLenOp {\n                            list_reg: tail_reg.into(),\n                            min_list_len,\n                        },\n                    );\n                    RestLen::Loaded(len_reg)\n                }\n            },\n            None => RestLen::Known(0),\n        };\n\n        fixed\n            .iter()\n            .rev()\n            .enumerate()\n            .fold(tail_reg, |tail_reg, (i, fixed)| {\n                let list_len_reg = match rest_len {\n                    RestLen::Known(known) => {\n                        b.push_reg(span, OpKind::ConstInt64, (known + i + 1) as i64)\n                    }\n                    RestLen::Loaded(rest_len_reg) => {\n                        let index_reg = b.push_reg(span, OpKind::ConstInt64, (i + 1) as i64);\n                        b.push_reg(\n                            span,\n                            OpKind::Int64Add,\n                            BinaryOp {\n                                lhs_reg: rest_len_reg.into(),\n                                rhs_reg: index_reg.into(),\n                            },\n                        )\n                    }\n                };\n\n                let fixed_reg =\n                    value_to_reg(ehx, b, span, fixed, &abitype::BoxedAbiType::Any.into());\n\n                let box_pair_op = BoxPairOp {\n                    head_reg: fixed_reg.into(),\n                    rest_reg: tail_reg.into(),\n                    list_len_reg: list_len_reg.into(),\n                };\n\n                let pair_head_reg = if fixed_reg.is_const() && tail_reg.is_const() {\n                    b.push_reg(span, OpKind::ConstBoxedPair, box_pair_op)\n                } else {\n                    b.push_reg(span, OpKind::AllocBoxedPair, box_pair_op)\n                };\n\n                b.cast_boxed(span, pair_head_reg, TOP_LIST_BOXED_ABI_TYPE.clone())\n            })\n    };\n\n    b.cast_boxed_cond(\n        span,\n        &TOP_LIST_BOXED_ABI_TYPE,\n        list_reg,\n        boxed_abi_type.clone(),\n    )\n}\n\nfn record_to_reg(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    record_cons: &record::ConsId,\n    fields: &[Value],\n    boxed_abi_type: &abitype::BoxedAbiType,\n) -> BuiltReg {\n    use crate::mir::ops::*;\n\n    let record_struct = ehx\n        .evaled_record_class_for_cons(record_cons)\n        .record_struct\n        .clone();\n\n    let mut has_non_const_fields = false;\n    let field_regs = fields\n        .iter()\n        .zip(record_struct.field_abi_types.iter())\n        .map(|(field, abi_type)| {\n            let built_reg = value_to_reg(ehx, b, span, field, abi_type);\n            has_non_const_fields = has_non_const_fields || !built_reg.is_const();\n\n            built_reg.into()\n        })\n        .collect();\n\n    let box_record_op = BoxRecordOp {\n        record_struct,\n        field_regs,\n    };\n\n    let record_reg = if has_non_const_fields {\n        b.push_reg(span, OpKind::AllocBoxedRecord, box_record_op)\n    } else {\n        b.push_reg(span, OpKind::ConstBoxedRecord, box_record_op)\n    };\n\n    b.cast_boxed(span, record_reg, boxed_abi_type.clone())\n}\n\nfn record_field_value_to_const_reg(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    field_value: &boxed::FieldValue,\n    abi_type: &abitype::AbiType,\n) -> BuiltReg {\n    use crate::mir::ops::*;\n    use arret_runtime::boxed::prelude::*;\n    use boxed::FieldValue;\n\n    // This depends on the fact we're encoding the exact record layout we're reading from. We only\n    // need `abi_type` to find the specific pointer type for boxed values.\n    match field_value {\n        FieldValue::Int(v) => b.push_reg(span, OpKind::ConstInt64, *v),\n        FieldValue::Float(v) => b.push_reg(span, OpKind::ConstFloat, *v),\n        FieldValue::Bool(v) => b.push_reg(span, OpKind::ConstBool, *v),\n        FieldValue::Char(v) => b.push_reg(span, OpKind::ConstChar, *v),\n        FieldValue::InternedSym(interned) => {\n            let name = ehx.as_heap().type_info().interner().unintern(interned);\n            b.push_reg(span, OpKind::ConstInternedSym, name.into())\n        }\n        FieldValue::Boxed(any_ref) => const_to_reg(ehx, b, span, *any_ref, abi_type),\n    }\n}\n\npub fn reg_to_boxed_reg(\n    b: &mut Builder,\n    span: Span,\n    reg_value: &value::RegValue,\n    to_boxed: &abitype::BoxedAbiType,\n) -> BuiltReg {\n    use crate::mir::ops::*;\n    use arret_runtime::boxed::TypeTag;\n\n    match &reg_value.abi_type {\n        abitype::AbiType::Boxed(from_boxed) => {\n            b.cast_boxed_cond(span, from_boxed, reg_value.reg, to_boxed.clone())\n        }\n        abitype::AbiType::Int => {\n            let boxed_int_reg = b.push_reg(span, OpKind::AllocBoxedInt, reg_value.reg.into());\n            b.cast_boxed_cond(span, &TypeTag::Int.into(), boxed_int_reg, to_boxed.clone())\n        }\n        abitype::AbiType::Char => {\n            let boxed_char_reg = b.push_reg(span, OpKind::AllocBoxedChar, reg_value.reg.into());\n            b.cast_boxed_cond(\n                span,\n                &TypeTag::Char.into(),\n                boxed_char_reg,\n                to_boxed.clone(),\n            )\n        }\n        abitype::AbiType::InternedSym => {\n            let boxed_sym_reg = b.push_reg(span, OpKind::AllocBoxedSym, reg_value.reg.into());\n            b.cast_boxed_cond(span, &TypeTag::Sym.into(), boxed_sym_reg, to_boxed.clone())\n        }\n        abitype::AbiType::Float => {\n            let boxed_float_reg = b.push_reg(span, OpKind::AllocBoxedFloat, reg_value.reg.into());\n            b.cast_boxed_cond(\n                span,\n                &TypeTag::Float.into(),\n                boxed_float_reg,\n                to_boxed.clone(),\n            )\n        }\n        abitype::AbiType::Bool => b.push_cond(\n            span,\n            reg_value.reg.into(),\n            |b| {\n                let const_true_reg = b.push_reg(span, OpKind::ConstBoxedTrue, ());\n                b.cast_boxed_cond(\n                    span,\n                    &TypeTag::True.into(),\n                    const_true_reg,\n                    to_boxed.clone(),\n                )\n                .into()\n            },\n            |b| {\n                let const_false_reg = b.push_reg(span, OpKind::ConstBoxedFalse, ());\n                b.cast_boxed_cond(\n                    span,\n                    &TypeTag::False.into(),\n                    const_false_reg,\n                    to_boxed.clone(),\n                )\n                .into()\n            },\n        ),\n        // Callbacks are ephemeral unboxed types. They cannot be returned from functions and should\n        // never need to be boxed.\n        abitype::AbiType::Callback(_) => {\n            unimplemented!(\"callback to boxed reg {:?} conversion\", to_boxed)\n        }\n    }\n}\n\nfn boxed_to_bool(\n    b: &mut Builder,\n    span: Span,\n    from_boxed: &abitype::BoxedAbiType,\n    reg_value: &value::RegValue,\n) -> BuiltReg {\n    use crate::mir::ops::*;\n    use arret_runtime::boxed::TypeTag;\n\n    let possible_type_tags =\n        reg_value.possible_type_tags & [TypeTag::True, TypeTag::False].iter().collect();\n\n    if possible_type_tags == TypeTag::True.into() {\n        b.push_reg(span, OpKind::ConstBool, true)\n    } else if possible_type_tags == TypeTag::False.into() {\n        b.push_reg(span, OpKind::ConstBool, false)\n    } else {\n        let boxed_any_reg =\n            b.cast_boxed_cond(span, from_boxed, reg_value.reg, abitype::BoxedAbiType::Any);\n\n        let boxed_type_tag_reg = b.push_reg(\n            span,\n            OpKind::LoadBoxedTypeTag,\n            LoadBoxedTypeTagOp {\n                subject_reg: boxed_any_reg.into(),\n                possible_type_tags,\n            },\n        );\n\n        let true_type_tag_reg = b.push_reg(span, OpKind::ConstTypeTag, TypeTag::True);\n\n        b.push_reg(\n            span,\n            OpKind::TypeTagEqual,\n            BinaryOp {\n                lhs_reg: boxed_type_tag_reg.into(),\n                rhs_reg: true_type_tag_reg.into(),\n            },\n        )\n    }\n}\n\nfn reg_to_reg(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    reg_value: &value::RegValue,\n    abi_type: &abitype::AbiType,\n) -> BuiltReg {\n    use crate::mir::ops::*;\n    use arret_runtime::boxed::TypeTag;\n\n    match (&reg_value.abi_type, abi_type) {\n        (from, to) if from == to => reg_value.reg,\n        (_, abitype::AbiType::Boxed(to_boxed)) => reg_to_boxed_reg(b, span, reg_value, to_boxed),\n        (abitype::AbiType::Boxed(from_boxed), abitype::AbiType::Int) => {\n            let boxed_int_reg =\n                b.cast_boxed_cond(span, from_boxed, reg_value.reg, TypeTag::Int.into());\n            b.push_reg(span, OpKind::LoadBoxedIntValue, boxed_int_reg.into())\n        }\n        (abitype::AbiType::Boxed(from_boxed), abitype::AbiType::Float) => {\n            let boxed_float_reg =\n                b.cast_boxed_cond(span, from_boxed, reg_value.reg, TypeTag::Float.into());\n            b.push_reg(span, OpKind::LoadBoxedFloatValue, boxed_float_reg.into())\n        }\n        (abitype::AbiType::Boxed(from_boxed), abitype::AbiType::Char) => {\n            let boxed_char_reg =\n                b.cast_boxed_cond(span, from_boxed, reg_value.reg, TypeTag::Char.into());\n            b.push_reg(span, OpKind::LoadBoxedCharValue, boxed_char_reg.into())\n        }\n        (abitype::AbiType::Boxed(from_boxed), abitype::AbiType::Bool) => {\n            boxed_to_bool(b, span, from_boxed, reg_value)\n        }\n        (abitype::AbiType::Boxed(from_boxed), abitype::AbiType::InternedSym) => {\n            let boxed_sym_reg =\n                b.cast_boxed_cond(span, from_boxed, reg_value.reg, TypeTag::Sym.into());\n            b.push_reg(span, OpKind::LoadBoxedSymInterned, boxed_sym_reg.into())\n        }\n        (abitype::AbiType::Boxed(from_boxed), abitype::AbiType::Callback(entry_point_abi)) => {\n            ehx.thunk_reg_to_callback_reg(b, span, from_boxed, reg_value.reg, entry_point_abi)\n        }\n        (from, to) => unimplemented!(\"reg {:?} to reg {:?} conversion\", from, to),\n    }\n}\n\nfn thunk_reg_to_reg(\n    b: &mut Builder,\n    span: Span,\n    boxed_thunk_reg: BuiltReg,\n    boxed_abi_type: &abitype::BoxedAbiType,\n) -> BuiltReg {\n    use arret_runtime::boxed::TypeTag;\n\n    b.cast_boxed_cond(\n        span,\n        &TypeTag::FunThunk.into(),\n        boxed_thunk_reg,\n        boxed_abi_type.clone(),\n    )\n}\n\nfn arret_fun_to_reg(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    arret_fun: &value::ArretFun,\n    abi_type: &abitype::AbiType,\n) -> BuiltReg {\n    match abi_type {\n        abitype::AbiType::Boxed(boxed_abi_type) => {\n            let thunk_reg = ehx.arret_fun_to_thunk_reg(b, span, arret_fun);\n            thunk_reg_to_reg(b, span, thunk_reg, boxed_abi_type)\n        }\n        abitype::AbiType::Callback(entry_point_abi) => {\n            ehx.arret_fun_to_callback_reg(b, span, arret_fun, entry_point_abi)\n        }\n        other => {\n            panic!(\"Attempt to convert Arret fun to {:?}\", other);\n        }\n    }\n}\n\nfn rust_fun_to_reg(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    rust_fun: &rfi::Fun,\n    abi_type: &abitype::AbiType,\n) -> BuiltReg {\n    match abi_type {\n        abitype::AbiType::Boxed(boxed_abi_type) => {\n            let thunk_reg = ehx.rust_fun_to_thunk_reg(b, span, rust_fun);\n            thunk_reg_to_reg(b, span, thunk_reg, boxed_abi_type)\n        }\n        abitype::AbiType::Callback(entry_point_abi) => {\n            ehx.rust_fun_to_callback_reg(b, span, rust_fun, entry_point_abi)\n        }\n        other => {\n            panic!(\"Attempt to convert Rust fun to {:?}\", other);\n        }\n    }\n}\n\npub fn value_to_reg(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    value: &Value,\n    abi_type: &abitype::AbiType,\n) -> BuiltReg {\n    match value {\n        Value::Reg(reg_value) => reg_to_reg(ehx, b, span, reg_value, abi_type),\n        Value::Const(any_ref) => const_to_reg(ehx, b, span, *any_ref, abi_type),\n        Value::List(fixed, rest) => {\n            if let abitype::AbiType::Boxed(boxed_abi_type) = abi_type {\n                list_to_reg(\n                    ehx,\n                    b,\n                    span,\n                    fixed,\n                    rest.as_ref().map(AsRef::as_ref),\n                    boxed_abi_type,\n                )\n            } else {\n                panic!(\"Attempt to construct non-boxed list\");\n            }\n        }\n        Value::Record(record_cons, fields) => {\n            if let abitype::AbiType::Boxed(boxed_abi_type) = abi_type {\n                record_to_reg(ehx, b, span, record_cons, fields, boxed_abi_type)\n            } else {\n                panic!(\"Attempt to construct non-boxed record\");\n            }\n        }\n        Value::ArretFun(ref arret_fun) => arret_fun_to_reg(ehx, b, span, arret_fun, abi_type),\n        Value::TyPred(test_ty) => {\n            let ty_pred_arret_fun = ehx\n                .synthetic_funs()\n                .ty_pred_arret_fun(test_ty.clone())\n                .clone();\n\n            arret_fun_to_reg(ehx, b, span, &ty_pred_arret_fun, abi_type)\n        }\n        Value::EqPred => {\n            let eq_pred_arret_fun = ehx.synthetic_funs().eq_pred_arret_fun().clone();\n            arret_fun_to_reg(ehx, b, span, &eq_pred_arret_fun, abi_type)\n        }\n        Value::RecordCons(cons) => {\n            let record_cons_arret_fun = ehx.synthetic_funs().record_cons_arret_fun(cons).clone();\n            arret_fun_to_reg(ehx, b, span, &record_cons_arret_fun, abi_type)\n        }\n        Value::FieldAccessor(cons, field_index) => {\n            let field_accessor_arret_fun = ehx\n                .synthetic_funs()\n                .field_accessor_arret_fun(cons, *field_index)\n                .clone();\n            arret_fun_to_reg(ehx, b, span, &field_accessor_arret_fun, abi_type)\n        }\n        Value::RustFun(ref rust_fun) => rust_fun_to_reg(ehx, b, span, rust_fun, abi_type),\n    }\n}\n"
  },
  {
    "path": "compiler/mir/value/from_reg.rs",
    "content": "use arret_runtime::abitype;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\n\nuse crate::mir::builder::BuiltReg;\nuse crate::mir::tagset::TypeTagSet;\nuse crate::mir::value;\nuse crate::mir::value::types::TypeHint;\nuse crate::mir::value::Value;\nuse crate::ty;\nuse crate::ty::Ty;\n\nfn reg_to_value_with_constraints<M>(\n    heap: &mut impl boxed::AsHeap,\n    reg: BuiltReg,\n    abi_type: &abitype::AbiType,\n    arret_ty: &ty::Ref<M>,\n    constrain_possible_type_tags: TypeTagSet,\n    fallback_type_hint: &TypeHint,\n) -> Value\nwhere\n    M: ty::Pm,\n{\n    use crate::mir::value::types::type_hint_for_ty_ref;\n\n    let type_hint_from_ty_ref = type_hint_for_ty_ref(arret_ty);\n\n    let type_hint = if type_hint_from_ty_ref == TypeHint::None {\n        // Hopefully our fallback type hint has more information\n        fallback_type_hint.clone()\n    } else {\n        type_hint_from_ty_ref\n    };\n\n    if let Ty::LitSym(value) = arret_ty.resolve_to_ty() {\n        // Unlike other literal types we can't encode a literal sym in a `RegValue` without losing\n        // information. This means we will need to rebuild the sym every time it's referenced but\n        // this should be a net win.\n        boxed::Sym::new(heap, value.as_ref()).as_any_ref().into()\n    } else {\n        value::RegValue {\n            reg,\n            abi_type: abi_type.clone(),\n            possible_type_tags: TypeTagSet::from(abi_type)\n                & TypeTagSet::from(arret_ty)\n                & constrain_possible_type_tags,\n            type_hint,\n        }\n        .into()\n    }\n}\n\n/// Creates a Value from a register of the given ABI and Arret type\n///\n/// Supported literal types will be converted to `Value::Const`. Everything else will become a\n/// `Value::Reg`.\npub fn reg_to_value<M>(\n    heap: &mut impl boxed::AsHeap,\n    reg: BuiltReg,\n    abi_type: &abitype::AbiType,\n    arret_ty: &ty::Ref<M>,\n) -> Value\nwhere\n    M: ty::Pm,\n{\n    reg_to_value_with_constraints(\n        heap,\n        reg,\n        abi_type,\n        arret_ty,\n        TypeTagSet::all(),\n        &TypeHint::None,\n    )\n}\n\npub fn refine_reg_value_with_arret_ty<M>(\n    heap: &mut impl boxed::AsHeap,\n    reg_value: &value::RegValue,\n    arret_ty: &ty::Ref<M>,\n) -> Value\nwhere\n    M: ty::Pm,\n{\n    reg_to_value_with_constraints(\n        heap,\n        reg_value.reg,\n        &reg_value.abi_type,\n        arret_ty,\n        reg_value.possible_type_tags,\n        &reg_value.type_hint,\n    )\n}\n"
  },
  {
    "path": "compiler/mir/value/list.rs",
    "content": "use std::vec;\n\nuse arret_syntax::span::Span;\n\nuse crate::mir::builder::{Builder, TryToBuilder};\nuse crate::mir::value;\nuse crate::mir::value::types::TypeHint;\nuse crate::mir::value::Value;\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum ListValueLen {\n    Exact(usize),\n    Min(usize),\n}\n\nimpl ListValueLen {\n    pub fn lower_bound(&self) -> usize {\n        match self {\n            ListValueLen::Exact(len) => *len,\n            ListValueLen::Min(len) => *len,\n        }\n    }\n}\n\nimpl std::ops::Add for ListValueLen {\n    type Output = ListValueLen;\n\n    fn add(self, other: ListValueLen) -> ListValueLen {\n        match (self, other) {\n            (ListValueLen::Exact(self_len), ListValueLen::Exact(other_len)) => {\n                ListValueLen::Exact(self_len + other_len)\n            }\n\n            _ => ListValueLen::Min(self.lower_bound() + other.lower_bound()),\n        }\n    }\n}\n\npub fn list_value_len(value: &Value) -> ListValueLen {\n    use arret_runtime::boxed;\n\n    match value {\n        Value::List(fixed, rest) => {\n            let fixed_len = ListValueLen::Exact(fixed.len());\n\n            match rest {\n                Some(rest) => fixed_len + list_value_len(rest),\n                None => fixed_len,\n            }\n        }\n\n        Value::Const(any_ref) => match any_ref.downcast_ref::<boxed::List<boxed::Any>>() {\n            Some(list_ref) => ListValueLen::Exact(list_ref.len()),\n            None => ListValueLen::Min(0),\n        },\n\n        Value::Reg(reg_value) => {\n            if !reg_value.possible_type_tags.contains(boxed::TypeTag::Pair) {\n                // Must be empty\n                ListValueLen::Exact(0)\n            } else if !reg_value.possible_type_tags.contains(boxed::TypeTag::Nil) {\n                if let TypeHint::KnownListLen(len) = reg_value.type_hint {\n                    ListValueLen::Exact(len)\n                } else {\n                    // Cannot be empty\n                    ListValueLen::Min(1)\n                }\n            } else {\n                ListValueLen::Min(0)\n            }\n        }\n\n        _ => ListValueLen::Min(0),\n    }\n}\n\npub struct UnsizedListIterator {\n    fixed: vec::IntoIter<Value>,\n    rest: Option<Value>,\n}\n\nimpl UnsizedListIterator {\n    pub fn new(value: Value) -> Self {\n        Self {\n            fixed: Vec::new().into_iter(),\n            rest: Some(value),\n        }\n    }\n\n    /// Returns the next element in the list\n    ///\n    /// It is undefined if the list has no more elements. This function may panic, generate\n    /// nonsense code, generate code that crashes at runtime, etc.\n    #[must_use]\n    pub fn next_unchecked(&mut self, b: &mut impl TryToBuilder, span: Span) -> Value {\n        if let Some(next) = self.fixed.next() {\n            return next;\n        }\n\n        let rest_value = self\n            .rest\n            .take()\n            .expect(\"ran off the end of list with no rest argument\");\n\n        match rest_value {\n            Value::List(fixed, rest) => {\n                // Become our tail\n                self.fixed = fixed.into_vec().into_iter();\n                self.rest = rest.map(|rest| *rest);\n\n                self.next_unchecked(b, span)\n            }\n            Value::Const(any_ref) => {\n                use arret_runtime::boxed;\n\n                let const_pair = any_ref\n                    .downcast_ref::<boxed::Pair<boxed::Any>>()\n                    .expect(\"tried to pop off non-pair constant\");\n\n                let tail = const_pair.rest();\n                self.rest = if tail.is_empty() {\n                    None\n                } else {\n                    Some(tail.into())\n                };\n\n                const_pair.head().into()\n            }\n            Value::Reg(reg_value) => {\n                let b = b\n                    .try_to_builder()\n                    .expect(\"popping rest argument without builder\");\n\n                self.build_rest_next(b, span, &reg_value)\n            }\n            other => unimplemented!(\"popping rest argument off value {:?}\", other),\n        }\n    }\n\n    /// Returns a Value containing the rest of the iterator\n    #[must_use]\n    pub fn into_rest(self) -> Value {\n        Value::List(self.fixed.collect(), self.rest.map(Box::new))\n    }\n\n    fn build_rest_next(\n        &mut self,\n        b: &mut Builder,\n        span: Span,\n        current_rest_value: &value::RegValue,\n    ) -> Value {\n        use crate::mir::ops::*;\n        use crate::mir::value::build_reg::reg_to_boxed_reg;\n        use arret_runtime::abitype;\n\n        let needed_pair_type = abitype::BoxedAbiType::Pair(&abitype::BoxedAbiType::Any);\n        let current_rest_reg = reg_to_boxed_reg(b, span, current_rest_value, &needed_pair_type);\n\n        let head_reg = b.push_reg(span, OpKind::LoadBoxedPairHead, current_rest_reg.into());\n        let rest_reg = b.push_reg(span, OpKind::LoadBoxedPairRest, current_rest_reg.into());\n\n        self.rest =\n            Some(value::RegValue::new(rest_reg, abitype::TOP_LIST_BOXED_ABI_TYPE.into()).into());\n\n        value::RegValue::new(head_reg, abitype::BoxedAbiType::Any.into()).into()\n    }\n}\n\npub struct SizedListIterator {\n    size: usize,\n    unsized_list_iterator: UnsizedListIterator,\n}\n\nimpl SizedListIterator {\n    pub fn try_new(value: &Value) -> Option<Self> {\n        match list_value_len(value) {\n            ListValueLen::Exact(size) => Some(Self {\n                size,\n                unsized_list_iterator: UnsizedListIterator::new(value.clone()),\n            }),\n            _ => None,\n        }\n    }\n}\n\nimpl SizedListIterator {\n    pub fn next(&mut self, b: &mut impl TryToBuilder, span: Span) -> Option<Value> {\n        if self.size == 0 {\n            return None;\n        }\n\n        self.size -= 1;\n        Some(self.unsized_list_iterator.next_unchecked(b, span))\n    }\n\n    #[must_use]\n    pub fn into_rest(self) -> Value {\n        self.unsized_list_iterator.into_rest()\n    }\n\n    pub fn len(&self) -> usize {\n        self.size\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.size == 0\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use arret_runtime::boxed;\n    use arret_runtime::boxed::prelude::*;\n\n    use crate::source::EMPTY_SPAN;\n\n    #[test]\n    fn list_len() {\n        use crate::mir::builder::BuiltReg;\n        use crate::mir::ops::RegId;\n        use arret_runtime::abitype;\n\n        let mut heap = boxed::Heap::empty();\n        let elements = &[1, 2, 3];\n\n        // Start with three fixed values\n        let fixed_values: Box<[Value]> = elements\n            .iter()\n            .map(|element| boxed::Int::new(&mut heap, *element).into())\n            .collect();\n\n        // Have a constant list tail\n        let boxed_list_tail =\n            boxed::List::from_values(&mut heap, elements.iter().cloned(), boxed::Int::new);\n\n        let const_list_tail = Value::List(Box::new([]), Some(Box::new(boxed_list_tail.into())));\n\n        // Add the fixed values (3 elements) to the constant tail (3 elements)\n        let list_value = Value::List(fixed_values.clone(), Some(Box::new(const_list_tail)));\n\n        // The length should be 6\n        assert_eq!(ListValueLen::Exact(6), list_value_len(&list_value));\n\n        // Try 3 fixed values with a completely unknown tail\n        let list_with_unknown_tail = Value::List(\n            fixed_values.clone(),\n            Some(Box::new(\n                value::RegValue::new(\n                    BuiltReg::Local(RegId::alloc()),\n                    abitype::BoxedAbiType::Any.into(),\n                )\n                .into(),\n            )),\n        );\n\n        // Length should be at least 3\n        assert_eq!(\n            ListValueLen::Min(3),\n            list_value_len(&list_with_unknown_tail)\n        );\n\n        // Try 3 fixed values with a pair tail\n        let list_with_pair_tail = Value::List(\n            fixed_values.clone(),\n            Some(Box::new(\n                value::RegValue::new(BuiltReg::Local(RegId::alloc()), boxed::TypeTag::Pair.into())\n                    .into(),\n            )),\n        );\n\n        // Length should be at least 4\n        assert_eq!(ListValueLen::Min(4), list_value_len(&list_with_pair_tail));\n\n        // Try 3 fixed values with a nil tail\n        let list_with_nil_tail = Value::List(\n            fixed_values,\n            Some(Box::new(\n                value::RegValue::new(BuiltReg::Local(RegId::alloc()), boxed::TypeTag::Nil.into())\n                    .into(),\n            )),\n        );\n\n        // Length should be at exactly 3\n        assert_eq!(ListValueLen::Exact(3), list_value_len(&list_with_nil_tail));\n    }\n\n    #[test]\n    fn const_unsized_list_iter() {\n        let mut heap = boxed::Heap::empty();\n\n        let elements = &[1, 2, 3];\n\n        let boxed_list =\n            boxed::List::from_values(&mut heap, elements.iter().cloned(), boxed::Int::new);\n\n        let mut iter = UnsizedListIterator {\n            fixed: Vec::new().into_iter(),\n            rest: Some(boxed_list.into()),\n        };\n\n        for expected in elements {\n            let next_value = iter.next_unchecked(&mut None, EMPTY_SPAN);\n\n            if let Value::Const(next_ref) = next_value {\n                let expected_ref = boxed::Int::new(&mut heap, *expected).as_any_ref();\n                assert!(expected_ref.eq_in_heap(&heap, &next_ref));\n            } else {\n                panic!(\"expected const value, got {:?}\", next_value);\n            }\n        }\n    }\n\n    #[test]\n    fn fixed_list_value_unsized_iter() {\n        let mut heap = boxed::Heap::empty();\n\n        let elements = &[1, 2, 3];\n\n        let element_values: Vec<Value> = elements\n            .iter()\n            .map(|element| boxed::Int::new(&mut heap, *element).into())\n            .collect();\n\n        let mut iter = UnsizedListIterator {\n            fixed: element_values.into_iter(),\n            rest: None,\n        };\n\n        for expected in elements {\n            let next_value = iter.next_unchecked(&mut None, EMPTY_SPAN);\n\n            if let Value::Const(next_ref) = next_value {\n                let expected_ref = boxed::Int::new(&mut heap, *expected).as_any_ref();\n                assert!(expected_ref.eq_in_heap(&heap, &next_ref));\n            } else {\n                panic!(\"expected const value, got {:?}\", next_value);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/mir/value/mod.rs",
    "content": "mod arret_fun;\npub mod build_reg;\npub mod from_reg;\npub mod list;\npub mod plan_phi;\npub mod synthetic_fun;\npub mod to_const;\npub mod types;\n\nuse std::rc::Rc;\nuse std::sync::Arc;\n\nuse arret_runtime::abitype;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\n\nuse crate::mir::builder::BuiltReg;\nuse crate::mir::tagset::TypeTagSet;\nuse crate::rfi;\nuse crate::ty;\nuse crate::ty::record;\n\npub use arret_fun::{ArretFun, ArretFunId};\n\n#[derive(Clone, Debug)]\npub struct RegValue {\n    pub reg: BuiltReg,\n    pub abi_type: abitype::AbiType,\n    pub possible_type_tags: TypeTagSet,\n    pub type_hint: types::TypeHint,\n}\n\nimpl RegValue {\n    pub fn new(reg: BuiltReg, abi_type: abitype::AbiType) -> RegValue {\n        RegValue {\n            reg,\n            possible_type_tags: (&abi_type).into(),\n            abi_type,\n            type_hint: types::TypeHint::None,\n        }\n    }\n}\n\n#[derive(Clone, Debug)]\npub enum Value {\n    Const(Gc<boxed::Any>),\n    // This uses `Box<[]>` because we can't convert from a `Vec<>` to `Rc<[]>` without reallocating\n    List(Box<[Value]>, Option<Box<Value>>),\n    Record(record::ConsId, Box<[Value]>),\n    ArretFun(ArretFun),\n    RustFun(Arc<rfi::Fun>),\n    Reg(Rc<RegValue>),\n\n    TyPred(ty::pred::TestTy),\n    EqPred,\n    RecordCons(record::ConsId),\n    FieldAccessor(record::ConsId, usize),\n}\n\nimpl Value {\n    pub fn unsized_list_iter(&self) -> list::UnsizedListIterator {\n        self.clone().into_unsized_list_iter()\n    }\n\n    pub fn into_unsized_list_iter(self) -> list::UnsizedListIterator {\n        list::UnsizedListIterator::new(self)\n    }\n\n    pub fn try_sized_list_iter(&self) -> Option<list::SizedListIterator> {\n        list::SizedListIterator::try_new(self)\n    }\n}\n\nimpl<T: boxed::Boxed> From<Gc<T>> for Value {\n    fn from(boxed_ref: Gc<T>) -> Self {\n        Value::Const(boxed_ref.as_any_ref())\n    }\n}\n\nimpl From<RegValue> for Value {\n    fn from(reg_value: RegValue) -> Self {\n        Value::Reg(Rc::new(reg_value))\n    }\n}\n\npub fn visit_value_root(strong_pass: &mut boxed::collect::StrongPass, value: &mut Value) {\n    match value {\n        Value::Const(ref mut any_ref) => strong_pass.visit_box(any_ref),\n        Value::List(ref mut fixed, ref mut rest) => {\n            for any_ref in fixed.iter_mut() {\n                visit_value_root(strong_pass, any_ref);\n            }\n            for any_ref in rest {\n                visit_value_root(strong_pass, any_ref);\n            }\n        }\n        Value::ArretFun(ref mut arret_fun) => {\n            for (_, value) in arret_fun.env_values_mut().const_values.iter_mut() {\n                visit_value_root(strong_pass, value);\n            }\n            for (_, value) in arret_fun.env_values_mut().free_values.iter_mut() {\n                visit_value_root(strong_pass, value);\n            }\n        }\n        _ => {}\n    }\n}\n"
  },
  {
    "path": "compiler/mir/value/plan_phi.rs",
    "content": "use arret_runtime::abitype;\nuse arret_runtime::boxed;\n\nuse crate::mir::value::Value;\n\npub fn plan_phi_abi_type(lhs: &Value, rhs: &Value) -> abitype::AbiType {\n    use crate::mir::specific_abi_type::*;\n\n    match (lhs, rhs) {\n        (Value::Reg(lhs_reg_value), Value::Reg(rhs_reg_value))\n            if lhs_reg_value.abi_type == rhs_reg_value.abi_type =>\n        {\n            // We have identical ABI types; this is easy\n            rhs_reg_value.abi_type.clone()\n        }\n        (lhs, rhs) => {\n            use std::iter;\n\n            // We prefer working with unboxed values whenever possible. However, having to box a\n            // value is much worse than working with a boxed temporary. Boxing requires calling in\n            // to the allocator which is one of the most expensive things we can do.\n            //\n            // If both values are boxed then create an boxed phi. This prevents us from \"wasting\"\n            // a box from prematurely unboxing it and then having to allocate to re-box it later.\n            let both_boxed_non_bools = [lhs, rhs].iter().all(|value| {\n                match value {\n                    Value::Const(any_ref) => {\n                        match any_ref.as_subtype() {\n                            boxed::AnySubtype::True(_) | boxed::AnySubtype::False(_) => {\n                                // LLVM has trouble following bool values through boxing and\n                                // unboxing. Also, boxing bools is relatively cheap because we just\n                                // need to return a pointer to the correct singleton value.\n                                false\n                            }\n                            _ => {\n                                // `Const`s can be either boxed or unboxed\n                                // This effectively means \"whatever the other value wants\"\n                                true\n                            }\n                        }\n                    }\n                    Value::Reg(reg_value) => {\n                        matches!(reg_value.abi_type, abitype::AbiType::Boxed(_))\n                    }\n                    _ => true,\n                }\n            });\n\n            let values_iter = iter::once(lhs).chain(iter::once(rhs));\n\n            if both_boxed_non_bools {\n                specific_boxed_abi_type_for_values(values_iter).into()\n            } else {\n                specific_abi_type_for_values(values_iter)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/mir/value/synthetic_fun.rs",
    "content": "use std::collections::HashMap;\n\nuse arret_syntax::datum::DataStr;\nuse arret_syntax::span::Span;\n\nuse crate::hir;\nuse crate::hir::var_id::LocalIdAlloc;\nuse crate::mir::env_values::EnvValues;\nuse crate::mir::value;\nuse crate::source::EMPTY_SPAN;\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\nuse crate::ty::record;\nuse crate::ty::ty_args::TyArgs;\nuse crate::ty::Ty;\n\nstruct ExprParam {\n    source_name: DataStr,\n    poly_type: ty::Ref<ty::Poly>,\n}\n\nfn wrap_poly_expr_in_arret_fun(\n    span: Span,\n    source_name: DataStr,\n    ty_args: TyArgs<ty::Poly>,\n    expr_params: &[ExprParam],\n    ret_ty: ty::Ref<ty::Poly>,\n    wrapped_expr: hir::Expr<hir::Inferred>,\n) -> value::ArretFun {\n    let mut lia = LocalIdAlloc::new();\n\n    let pvars: purity::PVars = ty_args.pvar_purities().keys().cloned().collect();\n    let tvars: ty::TVars = ty_args.tvar_types().keys().cloned().collect();\n\n    let expr_params_with_local_id: Vec<(&ExprParam, hir::LocalId)> = expr_params\n        .iter()\n        .map(|expr_param| (expr_param, lia.alloc_mut()))\n        .collect();\n\n    let params = hir::destruc::List::new(\n        expr_params_with_local_id\n            .iter()\n            .map(|(expr_param, param_local_id)| {\n                hir::destruc::Destruc::Scalar(\n                    span,\n                    hir::destruc::Scalar::new(\n                        Some(*param_local_id),\n                        expr_param.source_name.clone(),\n                        expr_param.poly_type.clone(),\n                    ),\n                )\n            })\n            .collect(),\n        None,\n    );\n\n    let fixed_arg_exprs = expr_params_with_local_id\n        .iter()\n        .map(|(expr_param, param_local_id)| hir::Expr {\n            result_ty: expr_param.poly_type.clone(),\n            kind: hir::ExprKind::LocalRef(span, *param_local_id),\n        })\n        .collect();\n\n    value::ArretFun::new(\n        None,\n        Some(source_name),\n        // These are the environment type args, not our own\n        TyArgs::empty(),\n        EnvValues::empty(),\n        hir::Fun {\n            span,\n\n            pvars,\n            tvars,\n\n            purity: Purity::Pure.into(),\n            params,\n            ret_ty: ret_ty.clone(),\n            ret_ty_span: None,\n\n            body_expr: hir::Expr {\n                result_ty: ret_ty,\n                kind: hir::ExprKind::App(Box::new(hir::App {\n                    span,\n                    fun_expr: wrapped_expr,\n                    ty_args,\n                    fixed_arg_exprs,\n                    rest_arg_expr: None,\n                })),\n            },\n        },\n    )\n}\n\nfn wrap_mono_expr_in_arret_fun(\n    span: Span,\n    source_name: DataStr,\n    expr_params: &[ExprParam],\n    ret_ty: ty::Ref<ty::Poly>,\n    wrapped_expr: hir::Expr<hir::Inferred>,\n) -> value::ArretFun {\n    wrap_poly_expr_in_arret_fun(\n        span,\n        source_name,\n        TyArgs::empty(),\n        expr_params,\n        ret_ty,\n        wrapped_expr,\n    )\n}\n\nfn new_eq_pred_arret_fun(span: Span) -> value::ArretFun {\n    let expr_params = [\n        ExprParam {\n            source_name: \"left\".into(),\n            poly_type: Ty::Any.into(),\n        },\n        ExprParam {\n            source_name: \"right\".into(),\n            poly_type: Ty::Any.into(),\n        },\n    ];\n\n    let wrapped_expr = hir::Expr {\n        result_ty: Ty::EqPred.into(),\n        kind: hir::ExprKind::EqPred(span),\n    };\n\n    wrap_mono_expr_in_arret_fun(\n        span,\n        \"=\".into(),\n        &expr_params,\n        Ty::Bool.into(),\n        wrapped_expr,\n    )\n}\n\nfn new_ty_pred_arret_fun(span: Span, test_ty: ty::pred::TestTy) -> value::ArretFun {\n    let expr_params = [ExprParam {\n        source_name: \"subject\".into(),\n        poly_type: Ty::Any.into(),\n    }];\n\n    let wrapped_expr = hir::Expr {\n        result_ty: Ty::TyPred(test_ty.clone()).into(),\n        kind: hir::ExprKind::TyPred(span, test_ty.clone()),\n    };\n\n    wrap_mono_expr_in_arret_fun(\n        span,\n        test_ty.to_string().into(),\n        &expr_params,\n        Ty::Bool.into(),\n        wrapped_expr,\n    )\n}\n\nfn new_record_cons_arret_fun(span: Span, cons: &record::ConsId) -> value::ArretFun {\n    let ty_args = cons.identity_ty_args();\n\n    let cons_fun_ty = record::Cons::value_cons_fun_type(cons);\n    let record_instance_ty = Ty::Record(Box::new(record::Instance::new(\n        cons.clone(),\n        ty_args.clone(),\n    )));\n\n    let expr_params: Vec<ExprParam> = cons\n        .fields()\n        .iter()\n        .map(|field| ExprParam {\n            source_name: field.name().clone(),\n            poly_type: field.ty_ref().clone(),\n        })\n        .collect();\n\n    let wrapped_expr = hir::Expr {\n        result_ty: cons_fun_ty.into(),\n        kind: hir::ExprKind::RecordCons(span, cons.clone()),\n    };\n\n    wrap_poly_expr_in_arret_fun(\n        span,\n        cons.value_cons_name().clone(),\n        ty_args,\n        &expr_params,\n        record_instance_ty.into(),\n        wrapped_expr,\n    )\n}\n\nfn new_field_accessor_arret_fun(\n    span: Span,\n    cons: &record::ConsId,\n    field_index: usize,\n) -> value::ArretFun {\n    let ty_args = cons.identity_ty_args();\n\n    let field = &cons.fields()[field_index];\n    let accessor_fun_ty = field.accessor_fun_type(cons);\n    let record_instance_ty = Ty::Record(Box::new(record::Instance::new(\n        cons.clone(),\n        ty_args.clone(),\n    )));\n\n    let expr_params = &[ExprParam {\n        source_name: cons.value_cons_name().clone(),\n        poly_type: record_instance_ty.clone().into(),\n    }];\n\n    let wrapped_expr = hir::Expr {\n        result_ty: accessor_fun_ty.into(),\n        kind: hir::ExprKind::FieldAccessor(Box::new(hir::FieldAccessor {\n            span,\n            record_cons: cons.clone(),\n            field_index,\n        })),\n    };\n\n    wrap_poly_expr_in_arret_fun(\n        span,\n        format!(\"{}-{}\", cons.value_cons_name(), field.name()).into(),\n        ty_args,\n        expr_params,\n        record_instance_ty.into(),\n        wrapped_expr,\n    )\n}\n\npub struct SyntheticFuns {\n    eq_pred_arret_fun: Option<value::ArretFun>,\n    ty_pred_arret_fun: HashMap<ty::pred::TestTy, value::ArretFun>,\n    record_cons_arret_fun: HashMap<record::ConsId, value::ArretFun>,\n    field_accessor_arret_fun: HashMap<(record::ConsId, usize), value::ArretFun>,\n}\n\nimpl SyntheticFuns {\n    pub fn new() -> Self {\n        Self {\n            eq_pred_arret_fun: None,\n            ty_pred_arret_fun: HashMap::new(),\n            record_cons_arret_fun: HashMap::new(),\n            field_accessor_arret_fun: HashMap::new(),\n        }\n    }\n\n    pub fn eq_pred_arret_fun(&mut self) -> &value::ArretFun {\n        self.eq_pred_arret_fun\n            .get_or_insert_with(|| new_eq_pred_arret_fun(EMPTY_SPAN))\n    }\n\n    pub fn ty_pred_arret_fun(&mut self, test_ty: ty::pred::TestTy) -> &value::ArretFun {\n        self.ty_pred_arret_fun\n            .entry(test_ty.clone())\n            .or_insert_with(|| new_ty_pred_arret_fun(EMPTY_SPAN, test_ty))\n    }\n\n    pub fn record_cons_arret_fun(&mut self, cons: &record::ConsId) -> &value::ArretFun {\n        self.record_cons_arret_fun\n            .entry(cons.clone())\n            .or_insert_with(|| new_record_cons_arret_fun(EMPTY_SPAN, cons))\n    }\n\n    pub fn field_accessor_arret_fun(\n        &mut self,\n        cons: &record::ConsId,\n        field_index: usize,\n    ) -> &value::ArretFun {\n        let lookup_key = (cons.clone(), field_index);\n\n        self.field_accessor_arret_fun\n            .entry(lookup_key)\n            .or_insert_with(|| new_field_accessor_arret_fun(EMPTY_SPAN, cons, field_index))\n    }\n}\n"
  },
  {
    "path": "compiler/mir/value/to_const.rs",
    "content": "use arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::boxed::TypeTag;\nuse arret_runtime::class_map;\nuse arret_runtime::intern::InternedSym;\n\nuse crate::mir::eval_hir::{EvalHirCtx, EvaledRecordClass};\nuse crate::mir::value::Value;\nuse crate::ty::record;\n\nfn record_to_const(\n    ehx: &mut EvalHirCtx,\n    record_cons: &record::ConsId,\n    field_values: &[Value],\n) -> Option<Gc<boxed::Any>> {\n    let EvaledRecordClass {\n        jit_record_class_id,\n        jit_data_layout,\n        ..\n    } = *ehx.evaled_record_class_for_cons(record_cons);\n\n    let classmap_class = ehx\n        .as_heap()\n        .type_info()\n        .class_map()\n        .class_for_record_class_id(jit_record_class_id);\n\n    let data = boxed::RecordData::alloc(jit_data_layout);\n\n    let classmap_fields: Vec<class_map::Field> = classmap_class.field_iter().collect();\n    for (classmap_field, field_value) in classmap_fields.iter().zip(field_values.iter()) {\n        unsafe {\n            use class_map::FieldType;\n            let field_ptr = data.as_ptr().add(classmap_field.offset());\n\n            match classmap_field.field_type() {\n                FieldType::Bool => {\n                    let bool_ref = &mut *(field_ptr as *mut bool);\n                    let boxed_field = value_to_const(ehx, field_value)?;\n\n                    *bool_ref = boxed_field\n                        .downcast_ref::<boxed::Bool>()\n                        .expect(\"unexpected value field type while boxing constant bool\")\n                        .as_bool();\n                }\n                FieldType::Int => {\n                    let int_ref = &mut *(field_ptr as *mut i64);\n                    let boxed_field = value_to_const(ehx, field_value)?;\n\n                    *int_ref = boxed_field\n                        .downcast_ref::<boxed::Int>()\n                        .expect(\"unexpected value field type while boxing constant int\")\n                        .value();\n                }\n                FieldType::Float => {\n                    let float_ref = &mut *(field_ptr as *mut f64);\n                    let boxed_field = value_to_const(ehx, field_value)?;\n\n                    *float_ref = boxed_field\n                        .downcast_ref::<boxed::Float>()\n                        .expect(\"unexpected value field type while boxing constant float\")\n                        .value();\n                }\n                FieldType::Char => {\n                    let char_ref = &mut *(field_ptr as *mut char);\n                    let boxed_field = value_to_const(ehx, field_value)?;\n\n                    *char_ref = boxed_field\n                        .downcast_ref::<boxed::Char>()\n                        .expect(\"unexpected value field type while boxing constant char\")\n                        .value();\n                }\n                FieldType::InternedSym => {\n                    let interned_sym_ref = &mut *(field_ptr as *mut InternedSym);\n                    let boxed_field = value_to_const(ehx, field_value)?;\n\n                    *interned_sym_ref = boxed_field\n                        .downcast_ref::<boxed::Sym>()\n                        .expect(\"unexpected value field type while boxing constant interned sym\")\n                        .interned();\n                }\n                FieldType::Boxed => {\n                    let boxed_ref = &mut *(field_ptr as *mut Gc<boxed::Any>);\n                    let boxed_field = value_to_const(ehx, field_value)?;\n\n                    *boxed_ref = boxed_field;\n                }\n            }\n        }\n    }\n\n    Some(boxed::Record::new(ehx, jit_record_class_id, data).as_any_ref())\n}\n\npub fn list_to_const(\n    ehx: &mut EvalHirCtx,\n    fixed: &[Value],\n    rest: Option<&Value>,\n) -> Option<Gc<boxed::Any>> {\n    let fixed_boxes = fixed\n        .iter()\n        .map(|value| value_to_const(ehx, value))\n        .collect::<Option<Vec<Gc<boxed::Any>>>>()?;\n\n    let rest_box = match rest {\n        Some(rest) => {\n            let rest_boxed = value_to_const(ehx, rest)?;\n            if let Some(list_ref) = rest_boxed.downcast_ref::<boxed::List<boxed::Any>>() {\n                list_ref\n            } else {\n                panic!(\"Attempted to build list with non-list tail\");\n            }\n        }\n        None => boxed::List::<boxed::Any>::empty(),\n    };\n\n    let list = boxed::List::<boxed::Any>::new_with_tail(ehx, fixed_boxes.into_iter(), rest_box);\n\n    Some(list.as_any_ref())\n}\n\n/// Attempts to convert a MIR value to a constant boxed values\n///\n/// Non-singleton regs do not have a constant value at compile time; they will return None\npub fn value_to_const(ehx: &mut EvalHirCtx, value: &Value) -> Option<Gc<boxed::Any>> {\n    match value {\n        Value::Const(boxed) => Some(*boxed),\n        Value::List(fixed, Some(rest)) => list_to_const(ehx, fixed, Some(&*rest)),\n        Value::List(fixed, None) => list_to_const(ehx, fixed, None),\n        Value::Record(record_cons, field_values) => record_to_const(ehx, record_cons, field_values),\n        Value::TyPred(test_ty) => {\n            let ty_pred_arret_fun = ehx\n                .synthetic_funs()\n                .ty_pred_arret_fun(test_ty.clone())\n                .clone();\n\n            ehx.arret_fun_to_jit_boxed(&ty_pred_arret_fun)\n                .map(|f| f.as_any_ref())\n        }\n        Value::EqPred => {\n            let eq_pred_arret_fun = ehx.synthetic_funs().eq_pred_arret_fun().clone();\n            ehx.arret_fun_to_jit_boxed(&eq_pred_arret_fun)\n                .map(|f| f.as_any_ref())\n        }\n        Value::ArretFun(ref arret_fun) => ehx\n            .arret_fun_to_jit_boxed(arret_fun)\n            .map(|f| f.as_any_ref()),\n        Value::RecordCons(cons) => {\n            let record_cons_arret_fun = ehx.synthetic_funs().record_cons_arret_fun(cons).clone();\n\n            ehx.arret_fun_to_jit_boxed(&record_cons_arret_fun)\n                .map(|f| f.as_any_ref())\n        }\n        Value::FieldAccessor(cons, field_index) => {\n            let field_accessor_arret_fun = ehx\n                .synthetic_funs()\n                .field_accessor_arret_fun(cons, *field_index)\n                .clone();\n\n            ehx.arret_fun_to_jit_boxed(&field_accessor_arret_fun)\n                .map(|f| f.as_any_ref())\n        }\n        Value::RustFun(ref rust_fun) => {\n            Some(ehx.rust_fun_to_jit_boxed(rust_fun.clone()).as_any_ref())\n        }\n        Value::Reg(ref reg_value) => {\n            if reg_value.possible_type_tags == TypeTag::Nil.into() {\n                Some(boxed::NIL_INSTANCE.as_any_ref())\n            } else if reg_value.possible_type_tags == TypeTag::True.into() {\n                Some(boxed::TRUE_INSTANCE.as_any_ref())\n            } else if reg_value.possible_type_tags == TypeTag::False.into() {\n                Some(boxed::FALSE_INSTANCE.as_any_ref())\n            } else {\n                None\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/mir/value/types.rs",
    "content": "use arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\n\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::tagset::TypeTagSet;\nuse crate::mir::value::Value;\nuse crate::ty;\nuse crate::ty::record;\nuse crate::ty::Ty;\n\n/// Compact hint for `RegValue`'s type that can't be captured in its type tags\n///\n/// To allow type hints to apply to unions, each hint is predicated on the value having the\n/// appropriate type. For example, `KnownRecordCons` does not imply that the value is a record, its\n/// type tag must be checked first.\n///\n/// It's possible for multiple `TypeHint`s to be applicable to the same type. However, this is\n/// unlikely so only a single type hint will be stored. The choice of type hint in these cases is\n/// arbitrary.\n#[derive(Debug, Clone, PartialEq)]\npub enum TypeHint {\n    /// Record of a known class\n    KnownRecordCons(record::ConsId),\n\n    /// List of a known length\n    KnownListLen(usize),\n\n    /// Vector of a known length\n    KnownVectorLen(usize),\n\n    /// No type hint\n    None,\n}\n\n#[derive(PartialEq, Debug)]\nenum FoundRecordConses<'a> {\n    Multi,\n    Single(&'a record::ConsId),\n    None,\n}\n\n/// Looks for the possible record conses of a type reference\nfn find_record_conses_for_ty_ref<M>(ty_ref: &ty::Ref<M>) -> FoundRecordConses<'_>\nwhere\n    M: ty::Pm,\n{\n    match ty_ref.try_to_fixed() {\n        Some(Ty::Union(members)) => members\n            .iter()\n            .map(|member| find_record_conses_for_ty_ref(member))\n            .fold(FoundRecordConses::None, |member1, member2| {\n                match (member1, member2) {\n                    (FoundRecordConses::Multi, _) | (_, FoundRecordConses::Multi) => {\n                        FoundRecordConses::Multi\n                    }\n                    (FoundRecordConses::None, FoundRecordConses::Single(single))\n                    | (FoundRecordConses::Single(single), FoundRecordConses::None) => {\n                        FoundRecordConses::Single(single)\n                    }\n                    (FoundRecordConses::Single(single1), FoundRecordConses::Single(single2)) => {\n                        if single1 == single2 {\n                            FoundRecordConses::Single(single1)\n                        } else {\n                            FoundRecordConses::Multi\n                        }\n                    }\n                    (FoundRecordConses::None, FoundRecordConses::None) => FoundRecordConses::None,\n                }\n            }),\n\n        Some(Ty::Record(instance)) => FoundRecordConses::Single(instance.cons()),\n        Some(Ty::RecordClass(cons)) => FoundRecordConses::Single(cons),\n        // These could be anything\n        None | Some(Ty::Any) | Some(Ty::TopRecord) => FoundRecordConses::Multi,\n        Some(_) => FoundRecordConses::None,\n    }\n}\n\npub fn type_hint_for_ty_ref<M>(ty_ref: &ty::Ref<M>) -> TypeHint\nwhere\n    M: ty::Pm,\n{\n    if let FoundRecordConses::Single(known_record_cons) = find_record_conses_for_ty_ref(ty_ref) {\n        return TypeHint::KnownRecordCons(known_record_cons.clone());\n    }\n\n    if let Some(Ty::List(list)) = ty_ref.try_to_fixed() {\n        let std::ops::Range { start, end } = list.size_range();\n\n        if start == end {\n            return TypeHint::KnownListLen(start);\n        }\n    }\n\n    if let Some(Ty::Vector(members)) = ty_ref.try_to_fixed() {\n        return TypeHint::KnownVectorLen(members.len());\n    }\n\n    TypeHint::None\n}\n\npub fn known_record_cons_for_value<'a>(\n    ehx: &'a EvalHirCtx,\n    value: &'a Value,\n) -> Option<&'a record::ConsId> {\n    match value {\n        Value::Const(any_ref) => any_ref.downcast_ref::<boxed::Record>().map(|record_ref| {\n            ehx.cons_for_jit_record_class_id(record_ref.class_id())\n                .expect(\"unable to lookup record cons for JIT record class ID\")\n        }),\n        Value::Record(cons, _) => Some(cons),\n        Value::Reg(reg_value) => {\n            if let TypeHint::KnownRecordCons(ref cons) = reg_value.type_hint {\n                Some(cons)\n            } else {\n                None\n            }\n        }\n        _ => None,\n    }\n}\n\npub fn known_vector_len_for_value(value: &Value) -> Option<usize> {\n    match value {\n        Value::Const(any_ref) => any_ref\n            .downcast_ref::<boxed::Vector>()\n            .map(|vector_ref| vector_ref.len()),\n        Value::Reg(reg_value) => {\n            if let TypeHint::KnownVectorLen(known_len) = reg_value.type_hint {\n                Some(known_len)\n            } else {\n                None\n            }\n        }\n        _ => None,\n    }\n}\n\npub fn type_hint_for_value(ehx: &EvalHirCtx, value: &Value) -> TypeHint {\n    if let Some(cons) = known_record_cons_for_value(ehx, value) {\n        return TypeHint::KnownRecordCons(cons.clone());\n    }\n\n    match value {\n        Value::Const(any_ref) => any_ref\n            .downcast_ref::<boxed::Vector>()\n            .map(|vector_ref| TypeHint::KnownVectorLen(vector_ref.len()))\n            .unwrap_or(TypeHint::None),\n        Value::Reg(reg_value) => reg_value.type_hint.clone(),\n        _ => TypeHint::None,\n    }\n}\n\n/// Returns a TypeTagSet containing the possible type tags for a given value\npub fn possible_type_tags_for_value(value: &Value) -> TypeTagSet {\n    match value {\n        Value::Const(any_ref) => any_ref.header().type_tag().into(),\n        Value::ArretFun(_)\n        | Value::RustFun(_)\n        | Value::TyPred(_)\n        | Value::EqPred\n        | Value::RecordCons(_)\n        | Value::FieldAccessor(_, _) => boxed::TypeTag::FunThunk.into(),\n        Value::List(fixed, rest) => {\n            if !fixed.is_empty() {\n                // Non-empty list\n                boxed::TypeTag::Pair.into()\n            } else if let Some(tail) = rest {\n                possible_type_tags_for_value(tail)\n            } else {\n                // Empty list\n                boxed::TypeTag::Nil.into()\n            }\n        }\n        Value::Record(_, _) => boxed::TypeTag::Record.into(),\n        Value::Reg(reg_value) => reg_value.possible_type_tags,\n    }\n}\n\n/// Annotates an existing value with Arret type information\n///\n/// For the majority of values this is a no-op. For this reason this function takes a builder for\n/// the Arret type that is only invoked if the type information can be used.\npub fn value_with_arret_ty<F>(\n    heap: &mut impl boxed::AsHeap,\n    value: Value,\n    build_arret_ty: F,\n) -> Value\nwhere\n    F: FnOnce() -> ty::Ref<ty::Mono>,\n{\n    if let Value::Reg(reg_value) = value {\n        use crate::mir::value::from_reg::refine_reg_value_with_arret_ty;\n\n        // This could be useful; request the type\n        let arret_ty = build_arret_ty();\n        refine_reg_value_with_arret_ty(heap, &reg_value, &arret_ty)\n    } else {\n        value\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::hir::tvar_bounded_by;\n    use crate::source::EMPTY_SPAN;\n    use crate::ty::ty_args::TyArgs;\n\n    #[test]\n    fn test_find_record_conses_for_ty_ref() {\n        let cons1 = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons1\".into(),\n            \"cons1?\".into(),\n            None,\n            Box::new([]),\n        );\n\n        let cons2 = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons2\".into(),\n            \"cons2?\".into(),\n            None,\n            Box::new([]),\n        );\n\n        let class1_poly: ty::Ref<ty::Poly> = cons1.clone().into();\n        let class2_poly: ty::Ref<ty::Poly> = cons2.clone().into();\n\n        let instance1_poly: ty::Ref<ty::Poly> =\n            record::Instance::new(cons1.clone(), TyArgs::empty()).into();\n        let instance2_poly: ty::Ref<ty::Poly> =\n            record::Instance::new(cons2.clone(), TyArgs::empty()).into();\n\n        // Unit type can't contain a record type\n        assert_eq!(\n            FoundRecordConses::None,\n            find_record_conses_for_ty_ref::<ty::Poly>(&Ty::unit().into())\n        );\n\n        // `Any` could contain any record cons\n        assert_eq!(\n            FoundRecordConses::Multi,\n            find_record_conses_for_ty_ref::<ty::Poly>(&Ty::Any.into())\n        );\n\n        // `TopRecord` could contain any record cons\n        assert_eq!(\n            FoundRecordConses::Multi,\n            find_record_conses_for_ty_ref::<ty::Poly>(&Ty::TopRecord.into())\n        );\n\n        // TVar could contain any record cons\n        assert_eq!(\n            FoundRecordConses::Multi,\n            find_record_conses_for_ty_ref(&tvar_bounded_by(Ty::Any.into()))\n        );\n\n        // Class type can have the record cons\n        assert_eq!(\n            FoundRecordConses::Single(&cons1),\n            find_record_conses_for_ty_ref(&class1_poly)\n        );\n\n        // Instance type can have the record cons\n        assert_eq!(\n            FoundRecordConses::Single(&cons2),\n            find_record_conses_for_ty_ref(&instance2_poly)\n        );\n\n        // Union of class and instance of the same class has the record cons\n        assert_eq!(\n            FoundRecordConses::Single(&cons1),\n            find_record_conses_for_ty_ref(\n                &Ty::Union(Box::new([class1_poly, instance1_poly.clone()])).into()\n            )\n        );\n\n        // Bool + record could only have the record cons\n        assert_eq!(\n            FoundRecordConses::Single(&cons2),\n            find_record_conses_for_ty_ref(\n                &Ty::Union(Box::new([Ty::Bool.into(), instance2_poly.clone()])).into()\n            )\n        );\n\n        // Multiple record types\n        assert_eq!(\n            FoundRecordConses::Multi,\n            find_record_conses_for_ty_ref(\n                &Ty::Union(Box::new([class2_poly, instance1_poly])).into()\n            )\n        );\n\n        // TVar inside a union could be any record type\n        assert_eq!(\n            FoundRecordConses::Multi,\n            find_record_conses_for_ty_ref(\n                &Ty::Union(Box::new([tvar_bounded_by(Ty::Any.into()), instance2_poly])).into()\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "compiler/mir/vector_member.rs",
    "content": "use arret_syntax::span::Span;\n\nuse arret_runtime::abitype;\n\nuse crate::mir::builder::Builder;\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::value::Value;\n\nfn vector_member_type(vector_value: &Value) -> &abitype::BoxedAbiType {\n    if let Value::Reg(reg_value) = vector_value {\n        if let abitype::AbiType::Boxed(abitype::BoxedAbiType::Vector(member_boxed_abi_type)) =\n            &reg_value.abi_type\n        {\n            return *member_boxed_abi_type;\n        }\n    }\n\n    &abitype::BoxedAbiType::Any\n}\n\n/// Loads a vector member from a vector of known length\n///\n/// [`vector_length`] must be less than [`MAX_DIRECT_ACCESS_LENGTH`]\npub fn load_vector_member(\n    ehx: &mut EvalHirCtx,\n    b: &mut Builder,\n    span: Span,\n    vector_len: usize,\n    vector_value: &Value,\n    member_index: usize,\n) -> Value {\n    use crate::mir::ops::*;\n    use crate::mir::tagset::TypeTagSet;\n    use crate::mir::value::build_reg::value_to_reg;\n    use crate::mir::value::types::TypeHint;\n    use crate::mir::value::RegValue;\n\n    let member_possible_type_tags: TypeTagSet = vector_member_type(vector_value).into();\n\n    let vector_reg = value_to_reg(\n        ehx,\n        b,\n        span,\n        vector_value,\n        &abitype::BoxedAbiType::Vector(&abitype::BoxedAbiType::Any).into(),\n    );\n\n    let member_reg = b.push_reg(\n        span,\n        OpKind::LoadBoxedVectorMember,\n        LoadBoxedVectorMemberOp {\n            vector_reg: vector_reg.into(),\n            known_vector_len: vector_len as usize,\n            member_index: member_index as usize,\n        },\n    );\n\n    (RegValue {\n        reg: member_reg,\n        possible_type_tags: member_possible_type_tags,\n        abi_type: abitype::BoxedAbiType::Any.into(),\n        type_hint: TypeHint::None,\n    })\n    .into()\n}\n"
  },
  {
    "path": "compiler/promise.rs",
    "content": "use std::collections::HashMap;\nuse std::ops::Deref;\nuse std::sync::{Arc, Condvar, Mutex, RwLock};\n\nstruct Inner<T>\nwhere\n    T: Send + Clone,\n{\n    value: Mutex<Option<T>>,\n    waker: Condvar,\n}\n\n/// Handle to a complete an associated promise\nstruct Completer<T>\nwhere\n    T: Send + Clone,\n{\n    inner: Arc<Inner<T>>,\n}\n\nimpl<T> Completer<T>\nwhere\n    T: Send + Clone,\n{\n    /// Sets the associated promise as complete\n    pub fn set(self, new_value: T) {\n        let mut value_lock = self.inner.value.lock().unwrap();\n        value_lock.replace(new_value);\n        drop(value_lock);\n\n        self.inner.waker.notify_all();\n    }\n}\n\n/// Promise of a future computation\n///\n/// The Promise is initially incomplete. Once its been completed it will return clones of the\n/// completed value until it's dropped.\n#[derive(Clone)]\nstruct Promise<T>\nwhere\n    T: Send + Clone,\n{\n    inner: Arc<Inner<T>>,\n}\n\nimpl<T> Promise<T>\nwhere\n    T: Send + Clone,\n{\n    /// Waits until the promise is complete and returns a clone of its value\n    pub fn value(&self) -> T {\n        let mut value_lock = self.inner.value.lock().unwrap();\n\n        loop {\n            match value_lock.deref() {\n                Some(value) => break value.clone(),\n                None => {\n                    value_lock = self.inner.waker.wait(value_lock).unwrap();\n                }\n            }\n        }\n    }\n}\n\n/// Creates new completer and promise\nfn promise<T>() -> (Completer<T>, Promise<T>)\nwhere\n    T: Send + Clone,\n{\n    let inner = Arc::new(Inner {\n        value: Mutex::new(None),\n        waker: Condvar::new(),\n    });\n\n    (\n        Completer {\n            inner: inner.clone(),\n        },\n        Promise { inner },\n    )\n}\n\n/// Create an immediately completed promise\nfn completed<T>(value: T) -> Promise<T>\nwhere\n    T: Send + Clone,\n{\n    Promise {\n        inner: {\n            Arc::new(Inner {\n                value: Mutex::new(Some(value)),\n                waker: Condvar::new(),\n            })\n        },\n    }\n}\n\n/// Concurrent map of keys to values where each key is only calculated once\npub struct PromiseMap<K, V>\nwhere\n    K: std::hash::Hash,\n    V: Send + Clone,\n{\n    promises: RwLock<HashMap<K, Promise<V>>>,\n}\n\nimpl<K, V> PromiseMap<K, V>\nwhere\n    K: std::hash::Hash + Eq,\n    V: Send + Clone,\n{\n    /// Creates a new `PromiseMap` with the passed values\n    pub fn new(values: impl IntoIterator<Item = (K, V)>) -> Self {\n        PromiseMap {\n            promises: RwLock::new(values.into_iter().map(|(k, v)| (k, completed(v))).collect()),\n        }\n    }\n\n    /// Fetches the value from the promise map or inserts it if it does not exist\n    ///\n    /// Each key will only be calculated once. If a calculation is already in progress on another\n    /// thread the current thread will block until the existing calculation completes.\n    pub fn get_or_insert_with<F>(&self, key: K, func: F) -> V\n    where\n        F: FnOnce() -> V,\n    {\n        // Opportunistically try to fetch the promise with a read lock\n        let promises_read = self.promises.read().unwrap();\n        if let Some(promise) = promises_read.get(&key) {\n            let cloned_promise = promise.clone();\n            drop(promises_read);\n\n            return cloned_promise.value();\n        }\n\n        drop(promises_read);\n\n        // Try again with a write lock to ensure another thread didn't already insert\n        let mut promises_write = self.promises.write().unwrap();\n        if let Some(promise) = promises_write.get(&key) {\n            let cloned_promise = promise.clone();\n            drop(promises_write);\n\n            return cloned_promise.value();\n        }\n\n        let (completer, promise) = promise();\n        promises_write.insert(key, promise);\n        drop(promises_write);\n\n        // Build a new value. This is presumably expensive\n        let value = func();\n        completer.set(value.clone());\n        value\n    }\n}\n"
  },
  {
    "path": "compiler/repl.rs",
    "content": "use std::collections::{HashMap, HashSet};\nuse std::sync::Arc;\nuse std::thread;\n\nuse codespan_reporting::diagnostic::Diagnostic;\n\nuse arret_syntax::datum::DataStr;\nuse arret_syntax::span::FileId;\n\nuse crate::context;\nuse crate::context::ModuleId;\nuse crate::hir;\nuse crate::hir::scope::Scope;\nuse crate::reporting::{diagnostic_for_syntax_error, errors_to_diagnostics, new_primary_label};\nuse crate::ty;\nuse crate::CompileCtx;\n\nuse crate::mir::eval_hir::EvalHirCtx;\nuse crate::mir::Value;\nuse crate::typeck::infer::{infer_module, infer_repl_expr};\n\n/// Indicates the kind of evaluation to perform on the input\n///\n/// This applies to expressions; it has no effect on empty input or definitions\n#[derive(Clone, Copy)]\npub enum EvalKind {\n    /// Infers the type of the expression\n    ///\n    /// This only runs as far as type checking\n    Type,\n\n    /// Fully evaluates the expression\n    Value,\n}\n\n#[derive(Debug, PartialEq)]\npub struct EvaledExprValue {\n    /// Rendered type of the expression\n    pub type_str: String,\n\n    /// Rendered value of the expression\n    pub value_str: String,\n\n    /// Indicates if the type is a literal\n    ///\n    /// REPL implementations may want to suppress printing the type of literal values as they\n    /// contain no additional information.\n    pub type_is_literal: bool,\n}\n\n#[derive(Debug, PartialEq)]\npub enum EvaledLine {\n    /// Line was all whitepsace\n    EmptyInput,\n\n    /// Line added new definitions\n    ///\n    /// All bound identifiers in the root scope are returned. This is useful for tab &\n    /// autocompletion.\n    Defs(Vec<DataStr>),\n\n    /// Line was evaluated to a type with the given name\n    ExprType(String),\n\n    /// Line was evaluate to a value\n    ExprValue(EvaledExprValue),\n}\n\nstruct ReplEngine<'ccx> {\n    root_scope: Scope<'static>,\n    ccx: &'ccx CompileCtx,\n\n    inferred_module_vars: HashMap<context::ModuleId, Arc<HashMap<hir::LocalId, ty::Ref<ty::Poly>>>>,\n    seen_modules: HashSet<context::ModuleId>,\n\n    ehx: EvalHirCtx,\n}\n\nimpl<'ccx> ReplEngine<'ccx> {\n    fn new(ccx: &'ccx CompileCtx) -> Self {\n        Self {\n            root_scope: Scope::root(),\n            ccx,\n\n            seen_modules: HashSet::new(),\n            inferred_module_vars: HashMap::new(),\n\n            ehx: EvalHirCtx::new(ccx.enable_optimisations()),\n        }\n    }\n\n    /// Returns all names bound in the root scope and namespace\n    fn bound_names(&self) -> Vec<DataStr> {\n        self.root_scope\n            .bound_idents()\n            .filter_map(move |ident| {\n                if ident.ns_id() == Scope::root_ns_id() {\n                    Some(ident.name().clone())\n                } else {\n                    None\n                }\n            })\n            .collect()\n    }\n\n    /// Visits a subtree of modules and adds any missing defs and inferred module vars\n    fn visit_module_tree(\n        &mut self,\n        root_module: &Arc<context::Module>,\n    ) -> Result<(), Vec<Diagnostic<FileId>>> {\n        if self.seen_modules.contains(&root_module.module_id) {\n            return Ok(());\n        }\n\n        self.seen_modules.insert(root_module.module_id);\n\n        // Make sure our imports are first\n        for import in root_module.imports.values() {\n            self.visit_module_tree(import)?;\n        }\n\n        self.inferred_module_vars\n            .insert(root_module.module_id, root_module.inferred_locals.clone());\n\n        self.ehx\n            .visit_module_defs(root_module.module_id, &root_module.defs)?;\n\n        Ok(())\n    }\n\n    fn eval_line(\n        &mut self,\n        input: String,\n        kind: EvalKind,\n    ) -> Result<EvaledLine, Vec<Diagnostic<FileId>>> {\n        use std::io::Write;\n\n        use crate::hir::lowering::LoweredReplDatum;\n\n        let source_file = self.ccx.source_loader().load_string(\"repl\".into(), input);\n\n        let input_data = source_file\n            .parsed()\n            .map_err(|err| vec![diagnostic_for_syntax_error(&err)])?;\n\n        let input_datum = match input_data {\n            [] => {\n                return Ok(EvaledLine::EmptyInput);\n            }\n            [input_datum] => input_datum,\n            _ => {\n                let extra_span = input_data[1].span();\n\n                return Err(vec![Diagnostic::error()\n                    .with_message(\"unexpected trailing datum\")\n                    .with_labels(vec![new_primary_label(\n                        extra_span,\n                        \"trailing datum\",\n                    )])]);\n            }\n        };\n\n        let module_id = ModuleId::alloc();\n        let mut child_scope = Scope::child(&self.root_scope);\n\n        let lowered_repl_datum =\n            hir::lowering::lower_repl_datum(self.ccx, &mut child_scope, input_datum)\n                .map_err(errors_to_diagnostics)?;\n\n        // Bring all the defs back in to root scope\n        let exported_bindings = child_scope.into_exported_bindings();\n        self.root_scope\n            .import_bindings(exported_bindings, module_id);\n\n        match lowered_repl_datum {\n            LoweredReplDatum::Import(modules) => {\n                for module in modules.values() {\n                    self.visit_module_tree(module)?;\n                }\n\n                Ok(EvaledLine::Defs(self.bound_names()))\n            }\n            LoweredReplDatum::EvaluableDef(def) => {\n                let inferred_module = infer_module(&self.inferred_module_vars, vec![def])\n                    .map_err(errors_to_diagnostics)?;\n\n                self.inferred_module_vars\n                    .insert(module_id, Arc::new(inferred_module.inferred_locals));\n\n                self.ehx\n                    .consume_module_defs(module_id, inferred_module.defs)?;\n\n                Ok(EvaledLine::Defs(self.bound_names()))\n            }\n            LoweredReplDatum::NonEvaluableDef => {\n                // This was handled entirely by HIR lowering\n                Ok(EvaledLine::Defs(self.bound_names()))\n            }\n            LoweredReplDatum::Expr(decl_expr) => {\n                let node = infer_repl_expr(&self.inferred_module_vars, decl_expr)?;\n                let type_str = hir::str_for_ty_ref(node.result_ty());\n\n                match kind {\n                    EvalKind::Type => Ok(EvaledLine::ExprType(type_str)),\n                    EvalKind::Value => {\n                        use crate::mir::eval_hir::FunCtx;\n                        use arret_runtime_syntax::writer;\n                        use std::str;\n\n                        let type_is_literal = ty::props::is_literal(node.result_ty());\n\n                        // Evaluate the expression\n                        let mut fcx = FunCtx::new(None);\n\n                        let value = self\n                            .ehx\n                            .consume_expr(&mut fcx, &mut None, node.into_expr())?;\n\n                        let boxed = self\n                            .ehx\n                            .value_to_const(&value)\n                            .expect(\"Received register from MIR evaluation\");\n\n                        // Write the result to a string\n                        let mut output_buf: Vec<u8> = vec![];\n                        writer::write_boxed(&mut output_buf, &self.ehx, boxed).unwrap();\n\n                        // Just `#fn` isn't very useful, even with a type. Add the source name.\n                        match value {\n                            Value::ArretFun(arret_fun) => {\n                                if let Some(source_name) = arret_fun.source_name() {\n                                    write!(&mut output_buf, \"/{}\", source_name).unwrap();\n                                }\n                            }\n                            Value::RustFun(rust_fun) => {\n                                write!(&mut output_buf, \"/{}\", rust_fun.symbol()).unwrap();\n                            }\n                            _ => {}\n                        }\n\n                        let value_str = str::from_utf8(output_buf.as_slice()).unwrap().to_owned();\n\n                        Ok(EvaledLine::ExprValue(EvaledExprValue {\n                            type_str,\n                            value_str,\n                            type_is_literal,\n                        }))\n                    }\n                }\n            }\n        }\n    }\n}\n\npub struct ReplCtx {\n    send_line: crossbeam_channel::Sender<(String, EvalKind)>,\n    receive_result: crossbeam_channel::Receiver<Result<EvaledLine, Vec<Diagnostic<FileId>>>>,\n}\n\n#[derive(Debug)]\npub struct EngineDisconnected;\n\nimpl ReplCtx {\n    /// Creates a new `ReplCtx`\n    ///\n    /// This will launch a REPL engine thread which can asynchronously evaluate lines.\n    pub fn new(ccx: Arc<CompileCtx>) -> Self {\n        let (send_line, receive_line) = crossbeam_channel::unbounded();\n        let (send_result, receive_result) = crossbeam_channel::unbounded();\n\n        thread::spawn(move || {\n            let mut engine = ReplEngine::new(&ccx);\n\n            for (input, kind) in receive_line.iter() {\n                let result = engine.eval_line(input, kind);\n                send_result.send(result).unwrap();\n\n                if engine.ehx.should_collect() {\n                    engine.ehx.collect_garbage();\n                }\n            }\n        });\n\n        Self {\n            send_line,\n            receive_result,\n        }\n    }\n\n    /// Sends a line to be evaluated by the REPL engine\n    ///\n    /// This is asynchronous and an unlimited number of lines can be sent before reading their\n    /// results. This allows the calling thread to remain responsive to user input while evaluation,\n    /// garbage collection, etc occurs.\n    pub fn send_line(&self, input: String, kind: EvalKind) -> Result<(), EngineDisconnected> {\n        self.send_line\n            .send((input, kind))\n            .map_err(|_| EngineDisconnected)\n    }\n\n    /// Receives the next result from the REPL engine\n    ///\n    /// These will be returned in the order they were submitted with `send_line`.\n    pub fn receive_result(&self) -> Result<EvaledLine, Vec<Diagnostic<FileId>>> {\n        self.receive_result.recv().unwrap()\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    fn eval_line_sync(\n        rcx: &mut ReplCtx,\n        input: String,\n        kind: EvalKind,\n    ) -> Result<EvaledLine, Vec<Diagnostic<FileId>>> {\n        rcx.send_line(input, kind).unwrap();\n        rcx.receive_result()\n    }\n\n    fn assert_defs(rcx: &mut ReplCtx, line: &'static str) {\n        match eval_line_sync(rcx, line.to_owned(), EvalKind::Value).unwrap() {\n            EvaledLine::Defs(_) => {}\n            other => {\n                panic!(\"Expected defs, got {:?}\", other);\n            }\n        }\n    }\n\n    fn assert_empty(rcx: &mut ReplCtx, line: &'static str) {\n        assert_eq!(\n            EvaledLine::EmptyInput,\n            eval_line_sync(rcx, line.to_owned(), EvalKind::Value).unwrap()\n        );\n    }\n\n    fn assert_expr(\n        rcx: &mut ReplCtx,\n        expected_value: &'static str,\n        expected_type: &'static str,\n        line: &'static str,\n    ) {\n        assert_eq!(\n            EvaledLine::ExprType(expected_type.to_owned()),\n            eval_line_sync(rcx, line.to_owned(), EvalKind::Type).unwrap()\n        );\n\n        match eval_line_sync(rcx, line.into(), EvalKind::Value).unwrap() {\n            EvaledLine::ExprValue(EvaledExprValue {\n                value_str,\n                type_str,\n                ..\n            }) => {\n                assert_eq!(value_str, expected_value.to_owned());\n                assert_eq!(type_str, expected_type.to_owned());\n            }\n            other => {\n                panic!(\"unexpected REPL result: {:?}\", other);\n            }\n        }\n    }\n\n    #[test]\n    fn basic_session() {\n        use crate::codegen::test::initialise_test_llvm;\n        use crate::PackagePaths;\n\n        initialise_test_llvm();\n\n        let ccx = Arc::new(CompileCtx::new(PackagePaths::test_paths(None), true));\n        let mut rcx = ReplCtx::new(ccx);\n\n        assert_empty(&mut rcx, \"       \");\n        assert_empty(&mut rcx, \"; COMMENT!\");\n\n        assert_expr(&mut rcx, \"1\", \"Int\", \"1\");\n\n        eval_line_sync(\n            &mut rcx,\n            \"(import [stdlib base])\".to_owned(),\n            EvalKind::Value,\n        )\n        .expect(\n            \"unable to load stdlib library; you may need to `cargo build` before running tests\",\n        );\n\n        // Make sure we can references vars from the imported module\n        assert_expr(&mut rcx, \"true\", \"true\", \"(int? 5)\");\n\n        // Make sure we can redefine\n        assert_defs(&mut rcx, \"(def x 'first)\");\n        assert_defs(&mut rcx, \"(def x 'second)\");\n        assert_expr(&mut rcx, \"second\", \"'second\", \"x\");\n\n        // `(do)` at the expression level\n        assert_expr(&mut rcx, \"baz\", \"'baz\", \"(do 'foo 'bar 'baz)\");\n\n        // Polymorphic capturing closures\n        assert_defs(\n            &mut rcx,\n            \"(def return-constant (fn #{T} ([x T]) (fn () -> T x)))\",\n        );\n\n        assert_defs(&mut rcx, \"(def return-one (return-constant 1))\");\n        assert_defs(&mut rcx, \"(def return-two (return-constant 'two))\");\n\n        assert_expr(&mut rcx, \"1\", \"Int\", \"(return-one)\");\n        assert_expr(&mut rcx, \"two\", \"'two\", \"(return-two)\");\n    }\n}\n"
  },
  {
    "path": "compiler/reporting.rs",
    "content": "use codespan_reporting::diagnostic::{Diagnostic, Label};\n\nuse arret_syntax::span::{FileId, Span};\n\nuse crate::source::SourceLoader;\n\n/// Traces the location of report through macro expansions\n#[derive(Debug, PartialEq, Clone)]\npub struct LocTrace {\n    origin: Span,\n    macro_invocation: Option<Span>,\n}\n\nimpl LocTrace {\n    pub fn new(origin: Span, macro_invocation: Option<Span>) -> LocTrace {\n        LocTrace {\n            origin,\n            macro_invocation,\n        }\n    }\n\n    pub fn with_macro_invocation(self, macro_invocation: Span) -> LocTrace {\n        LocTrace {\n            macro_invocation: Some(macro_invocation),\n            ..self\n        }\n    }\n\n    pub fn origin(&self) -> Span {\n        self.origin\n    }\n\n    pub fn macro_invocation(&self) -> Option<Span> {\n        self.macro_invocation\n    }\n\n    pub fn label_macro_invocation(&self, mut diagnostic: Diagnostic<FileId>) -> Diagnostic<FileId> {\n        match self.macro_invocation {\n            Some(macro_invocation_span) if !macro_invocation_span.contains(self.origin) => {\n                let secondary_label =\n                    new_secondary_label(macro_invocation_span, \"in this macro invocation\");\n\n                diagnostic.labels.push(secondary_label);\n                diagnostic\n            }\n            _ => diagnostic,\n        }\n    }\n}\n\nimpl From<Span> for LocTrace {\n    fn from(span: Span) -> LocTrace {\n        LocTrace::new(span, None)\n    }\n}\n\n/// Helper for converting a series of errors in to diagnostics\n///\n/// This is intended for use with `map_err`\npub fn errors_to_diagnostics<E: Into<Diagnostic<FileId>>>(\n    errors: Vec<E>,\n) -> Vec<Diagnostic<FileId>> {\n    errors.into_iter().map(Into::into).collect()\n}\n\n/// Returns a diagnostic for the passed syntax errror\n///\n/// This is required because `arret-syntax` doesn't depend on `codespan-reporting`. It requires\n/// its consumers to handle reporting themselves.\npub fn diagnostic_for_syntax_error(error: &arret_syntax::error::Error) -> Diagnostic<FileId> {\n    let origin = error.span();\n    let within = error.kind().within_context();\n\n    let primary_label_message = within\n        .and_then(|within| within.expected_next())\n        .map(|en| en.description())\n        .unwrap_or_else(|| \"syntax error\".to_owned());\n\n    let primary_label = new_primary_label(origin, primary_label_message);\n\n    let diagnostic = Diagnostic::error()\n        .with_message(error.kind().message())\n        .with_labels(vec![]);\n\n    if let Some(within) = within {\n        if let Some(open_char_span) = within.open_char_span() {\n            let secondary_label = new_secondary_label(\n                open_char_span,\n                format!(\"{} starts here\", within.description()),\n            );\n\n            return diagnostic.with_labels(vec![primary_label, secondary_label]);\n        }\n    }\n\n    diagnostic.with_labels(vec![primary_label])\n}\n\npub fn new_primary_label(span: Span, message: impl Into<String>) -> Label<FileId> {\n    Label::primary(span.file_id().unwrap(), span.byte_range()).with_message(message)\n}\n\npub fn new_secondary_label(span: Span, message: impl Into<String>) -> Label<FileId> {\n    Label::secondary(span.file_id().unwrap(), span.byte_range()).with_message(message)\n}\n\n/// Emits a series of diagnostics to standard error\n///\n/// This ensures the diagnostics are emitted as a contiguous group even when multiple threads\n/// are emitting concurrently.\npub fn emit_diagnostics_to_stderr(\n    source_loader: &SourceLoader,\n    diagnostics: impl IntoIterator<Item = Diagnostic<FileId>>,\n) {\n    use codespan_reporting::term;\n    use termcolor::{ColorChoice, StandardStream};\n\n    let config = term::Config::default();\n\n    let stderr = StandardStream::stderr(ColorChoice::Auto);\n    let mut stderr_lock = stderr.lock();\n\n    for diagnostic in diagnostics {\n        let _ = codespan_reporting::term::emit(\n            &mut stderr_lock,\n            &config,\n            &source_loader.files(),\n            &diagnostic,\n        );\n    }\n}\n"
  },
  {
    "path": "compiler/rfi/mod.rs",
    "content": "use std::ffi::OsString;\nuse std::sync::Arc;\nuse std::{fmt, path};\n\nuse arret_syntax::datum::Datum;\nuse arret_syntax::span::Span;\n\nuse crate::hir;\nuse crate::hir::error::{Error, ErrorKind};\nuse crate::hir::ns::NsDatum;\nuse crate::hir::scope::Scope;\nuse crate::source::SourceLoader;\nuse crate::ty;\nuse crate::ty::Ty;\n\nuse arret_runtime::{abitype, binding};\n\npub struct Library {\n    pub loaded: libloading::Library,\n    pub target_path: Box<path::Path>,\n    pub exported_funs: Box<[(&'static str, Arc<Fun>)]>,\n}\n\nimpl fmt::Debug for Library {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"rfi::Library({})\", self.target_path.to_string_lossy())\n    }\n}\n\n#[derive(Debug, PartialEq, Clone)]\npub struct Fun {\n    /// Name of this function if it corresponds to an intrinsic\n    ///\n    /// Intrinsics may have optimised partial evaluation in MIR. However, they should be\n    /// semantically equivalent to the non-intrinsic version.\n    intrinsic_name: Option<&'static str>,\n\n    span: Span,\n    arret_fun_type: ty::Fun,\n    takes_task: bool,\n    params: &'static [abitype::ParamAbiType],\n    ret: &'static abitype::RetAbiType,\n    symbol: &'static str,\n    entry_point: usize,\n}\n\nimpl Fun {\n    pub fn intrinsic_name(&self) -> Option<&'static str> {\n        self.intrinsic_name\n    }\n\n    pub fn span(&self) -> Span {\n        self.span\n    }\n\n    pub fn arret_fun_type(&self) -> &ty::Fun {\n        &self.arret_fun_type\n    }\n\n    pub fn symbol(&self) -> &'static str {\n        self.symbol\n    }\n\n    pub fn entry_point(&self) -> usize {\n        self.entry_point\n    }\n\n    pub fn takes_task(&self) -> bool {\n        self.takes_task\n    }\n\n    pub fn params(&self) -> &'static [abitype::ParamAbiType] {\n        self.params\n    }\n\n    pub fn has_rest(&self) -> bool {\n        self.arret_fun_type.params().has_rest()\n    }\n\n    pub fn ret(&self) -> &'static abitype::RetAbiType {\n        self.ret\n    }\n}\n\npub struct Loader {\n    type_scope: Scope<'static>,\n}\n\n/// Ensure that the specified Arret type is compatible with the corresponding Rust type\n///\n/// The Arret types are strictly more expressive than the Rust types. This simply checks that the\n/// Arret type is more specific than the Rust type.\nfn ensure_types_compatible<T>(\n    span: Span,\n    arret_poly: &ty::Ref<ty::Poly>,\n    abi_type: &T,\n) -> Result<(), Error>\nwhere\n    T: ty::conv_abi::ConvertableAbiType,\n{\n    if ty::is_a::ty_ref_is_a(arret_poly, &abi_type.to_ty_ref()) {\n        Ok(())\n    } else {\n        Err(Error::new(\n            span,\n            ErrorKind::RustFunError(\n                format!(\n                    \"Rust type `{}` does not match declared Arret type of `{}`\",\n                    abi_type.to_rust_str(),\n                    hir::str_for_ty_ref(arret_poly),\n                )\n                .into_boxed_str(),\n            ),\n        ))\n    }\n}\n\n#[derive(Clone, Copy)]\nenum LibType {\n    Static,\n    Dynamic,\n}\n\nfn build_rfi_lib_path(base: &path::Path, package_name: &str, lib_type: LibType) -> path::PathBuf {\n    let mut path_buf = path::PathBuf::new();\n    path_buf.push(base);\n\n    #[cfg(debug_assertions)]\n    path_buf.push(\"debug\");\n\n    #[cfg(not(debug_assertions))]\n    path_buf.push(\"release\");\n\n    match lib_type {\n        LibType::Dynamic => {\n            #[cfg(any(target_os = \"macos\", target_os = \"ios\"))]\n            path_buf.push(format!(\"lib{}.dylib\", package_name));\n\n            #[cfg(all(not(target_os = \"macos\"), not(target_os = \"ios\"),))]\n            path_buf.push(format!(\"lib{}.so\", package_name));\n        }\n        LibType::Static => {\n            path_buf.push(format!(\"lib{}.a\", package_name));\n        }\n    }\n\n    path_buf\n}\n\nimpl Loader {\n    pub fn new() -> Loader {\n        Loader {\n            type_scope: Scope::new_with_primitives(),\n        }\n    }\n\n    fn process_rust_fun(\n        &self,\n        arret_type_datum: &Datum,\n        entry_point: usize,\n        rust_fun: &'static binding::RustFun,\n        intrinsic_name: Option<&'static str>,\n    ) -> Result<Fun, Error> {\n        let ns_datum = NsDatum::from_syntax_datum(arret_type_datum);\n        let span = ns_datum.span();\n\n        // Lower the Arret type using a fixed scope\n        let poly_type = hir::lower_poly(&self.type_scope, ns_datum)?;\n\n        // Ensure the type is actually a function type\n        let poly_fun_type = if let ty::Ref::Fixed(Ty::Fun(fun_type)) = poly_type {\n            fun_type\n        } else {\n            return Err(Error::new(\n                span,\n                ErrorKind::RustFunError(\"function type expected\".into()),\n            ));\n        };\n\n        let pvars = poly_fun_type.pvars();\n        let tvars = poly_fun_type.tvars();\n\n        // The Rust function signature should satisfy the upper bound of the Arret type\n        let pta = ty::ty_args::TyArgs::from_upper_bound(pvars, tvars);\n        let upper_fun_type = ty::subst::subst_poly_fun(&pta, &*poly_fun_type);\n\n        // Calculate how many parameters the Rust function should accept\n        let expected_rust_params =\n            upper_fun_type.params().fixed().len() + upper_fun_type.params().has_rest() as usize;\n\n        if expected_rust_params != rust_fun.params.len() {\n            return Err(Error::new(\n                span,\n                ErrorKind::RustFunError(\n                    format!(\n                        \"expected Rust function to have {} parameters; has {}\",\n                        expected_rust_params,\n                        rust_fun.params.len()\n                    )\n                    .into_boxed_str(),\n                ),\n            ));\n        }\n\n        let mut abi_params_iter = rust_fun.params.iter();\n\n        // If there are rest types ensure they're compatible\n        let arret_rest = upper_fun_type.params().rest();\n        if !arret_rest.is_never() {\n            use arret_runtime::abitype::{AbiType, BoxedAbiType};\n\n            let last_rust_param = abi_params_iter.next_back().unwrap();\n\n            if let AbiType::Boxed(BoxedAbiType::List(elem)) = &last_rust_param.abi_type {\n                ensure_types_compatible(span, arret_rest, *elem)?;\n            } else {\n                return Err(Error::new(\n                    span,\n                    ErrorKind::RustFunError(\"expected Rust function to have `boxed::List` as last parameter to receive the rest argument\".into())\n                ));\n            }\n        };\n\n        // Ensure the fixed types are compatible\n        for (arret_fixed_poly, rust_fixed_poly) in\n            upper_fun_type.params().fixed().iter().zip(abi_params_iter)\n        {\n            ensure_types_compatible(span, arret_fixed_poly, &rust_fixed_poly.abi_type)?;\n        }\n\n        // And the return type\n        //\n        // Note that we don't care about contravariance here; simply that the types are compatible\n        ensure_types_compatible(span, upper_fun_type.ret(), &rust_fun.ret)?;\n\n        Ok(Fun {\n            intrinsic_name,\n\n            span: arret_type_datum.span(),\n            arret_fun_type: *poly_fun_type,\n            takes_task: rust_fun.takes_task,\n            params: rust_fun.params,\n            ret: &rust_fun.ret,\n            symbol: rust_fun.symbol,\n            entry_point,\n        })\n    }\n\n    pub fn load(\n        &self,\n        span: Span,\n        source_loader: &SourceLoader,\n        native_base_path: &path::Path,\n        target_base_path: &path::Path,\n        package_name: &str,\n    ) -> Result<Library, Error> {\n        let native_path = build_rfi_lib_path(native_base_path, package_name, LibType::Dynamic);\n        let target_path = build_rfi_lib_path(target_base_path, package_name, LibType::Static);\n\n        let map_loader_err = |err: libloading::Error| match err {\n            libloading::Error::DlOpen { .. } | libloading::Error::DlOpenUnknown => Error::new(\n                span,\n                ErrorKind::ModuleNotFound(native_path.clone().into_boxed_path()),\n            ),\n            _ => Error::new(\n                span,\n                ErrorKind::ReadError(native_path.clone().into_boxed_path()),\n            ),\n        };\n\n        let loaded = unsafe { libloading::Library::new(&native_path).map_err(map_loader_err)? };\n\n        let exports_symbol_name = format!(\"ARRET_{}_RUST_EXPORTS\", package_name.to_uppercase());\n        let exports: binding::RustExports = unsafe {\n            let exports_symbol = loaded\n                .get::<*const binding::RustExports>(exports_symbol_name.as_bytes())\n                .map_err(map_loader_err)?;\n\n            **exports_symbol\n        };\n\n        source_loader.reserve(exports.len());\n\n        let exported_funs = exports\n            .iter()\n            .map(|(fun_name, rust_fun)| {\n                let entry_point_address = unsafe {\n                    *loaded\n                        .get::<usize>(rust_fun.symbol.as_bytes())\n                        .map_err(map_loader_err)?\n                };\n\n                // Parse the declared Arret type string as a datum\n                let mut file_map_name =\n                    OsString::with_capacity(native_path.as_os_str().len() + 1 + fun_name.len());\n\n                file_map_name.push(native_path.as_os_str());\n                file_map_name.push(\":\");\n                file_map_name.push(fun_name);\n\n                let arret_type_source_file =\n                    source_loader.load_string(file_map_name, rust_fun.arret_type);\n\n                let arret_type_datum = match arret_type_source_file.parsed()? {\n                    [arret_type_datum] => arret_type_datum,\n                    _ => {\n                        return Err(Error::new(\n                            Span::from_str(\n                                Some(arret_type_source_file.file_id()),\n                                rust_fun.arret_type,\n                            ),\n                            ErrorKind::RustFunError(\"expected exactly one Arret type datum\".into()),\n                        ));\n                    }\n                };\n\n                // Treat every native function in the stdlib as an intrinsic\n                let intrinsic_name = Some(*fun_name).filter(|_| package_name == \"stdlib\");\n\n                let fun = self.process_rust_fun(\n                    arret_type_datum,\n                    entry_point_address,\n                    rust_fun,\n                    intrinsic_name,\n                )?;\n\n                Ok((*fun_name, Arc::new(fun)))\n            })\n            .collect::<Result<Box<[(&'static str, Arc<Fun>)]>, Error>>()?;\n\n        Ok(Library {\n            loaded,\n            target_path: target_path.into_boxed_path(),\n            exported_funs,\n        })\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use arret_runtime::abitype::{AbiType, BoxedAbiType, ParamAbiType, ParamCapture, RetAbiType};\n    use arret_runtime::boxed::TypeTag;\n    use arret_syntax::parser::datum_from_str;\n\n    fn binding_fun_to_poly_type(rust_fun: &'static binding::RustFun) -> Result<ty::Fun, Error> {\n        let loader = Loader::new();\n        let arret_type_datum = datum_from_str(None, rust_fun.arret_type).unwrap();\n\n        loader\n            .process_rust_fun(&arret_type_datum, 0, rust_fun, None)\n            .map(|rfi_fun| rfi_fun.arret_fun_type)\n    }\n\n    fn assert_valid_binding_fun(rust_fun: &'static binding::RustFun) {\n        binding_fun_to_poly_type(rust_fun).unwrap();\n    }\n\n    fn assert_binding_fun_error(expected_kind: &ErrorKind, rust_fun: &'static binding::RustFun) {\n        assert_eq!(\n            expected_kind,\n            binding_fun_to_poly_type(rust_fun).unwrap_err().kind()\n        );\n    }\n\n    #[test]\n    fn exact_rust_fun() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(Int -> Int)\",\n            takes_task: false,\n            params: &[ParamAbiType {\n                abi_type: AbiType::Int,\n                capture: ParamCapture::Never,\n            }],\n            ret: RetAbiType::Inhabited(AbiType::Boxed(BoxedAbiType::UniqueTagged(TypeTag::Int))),\n            symbol: \"\",\n        };\n\n        assert_valid_binding_fun(&BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn inexact_rust_fun_with_rest() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(& Int -> false)\",\n            takes_task: false,\n            params: &[ParamAbiType {\n                abi_type: AbiType::Boxed(BoxedAbiType::List(&BoxedAbiType::UniqueTagged(\n                    TypeTag::Int,\n                ))),\n                capture: ParamCapture::Auto,\n            }],\n            ret: RetAbiType::Inhabited(AbiType::Bool),\n            symbol: \"\",\n        };\n\n        assert_valid_binding_fun(&BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn void_rust_fun() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(Float -> '())\",\n            takes_task: false,\n            params: &[ParamAbiType {\n                abi_type: AbiType::Float,\n                capture: ParamCapture::Never,\n            }],\n            ret: RetAbiType::Void,\n            symbol: \"\",\n        };\n\n        assert_valid_binding_fun(&BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn diverging_fun() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(-> (U))\",\n            takes_task: false,\n            params: &[],\n            ret: RetAbiType::Never,\n            symbol: \"\",\n        };\n\n        assert_valid_binding_fun(&BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn polymorphic_rust_fun() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(All #{A} (List A & Any) -> A)\",\n            takes_task: false,\n            params: &[ParamAbiType {\n                abi_type: AbiType::Boxed(BoxedAbiType::Pair(&BoxedAbiType::Any)),\n                capture: ParamCapture::Auto,\n            }],\n            ret: RetAbiType::Inhabited(AbiType::Boxed(BoxedAbiType::Any)),\n            symbol: \"\",\n        };\n\n        assert_valid_binding_fun(&BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn incompatible_polymorphic_rust_fun() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(All #{A} (List & Any) -> A)\",\n            takes_task: false,\n            params: &[ParamAbiType {\n                abi_type: AbiType::Boxed(BoxedAbiType::Pair(&BoxedAbiType::Any)),\n                capture: ParamCapture::Auto,\n            }],\n            ret: RetAbiType::Inhabited(AbiType::Boxed(BoxedAbiType::Any)),\n            symbol: \"\",\n        };\n\n        let kind = ErrorKind::RustFunError(\n            \"Rust type `Gc<boxed::Pair<boxed::Any>>` does not match declared Arret type of `(List & Any)`\".into(),\n        );\n        assert_binding_fun_error(&kind, &BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn unbound_arret_type() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(unbound)\",\n            takes_task: false,\n            params: &[ParamAbiType {\n                abi_type: AbiType::Boxed(BoxedAbiType::UniqueTagged(TypeTag::Int)),\n                capture: ParamCapture::Auto,\n            }],\n            ret: RetAbiType::Inhabited(AbiType::Bool),\n            symbol: \"\",\n        };\n\n        let kind = ErrorKind::UnboundIdent(\"unbound\".into());\n        assert_binding_fun_error(&kind, &BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn non_fun_type() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"Str\",\n            takes_task: false,\n            params: &[ParamAbiType {\n                abi_type: AbiType::Boxed(BoxedAbiType::UniqueTagged(TypeTag::Int)),\n                capture: ParamCapture::Auto,\n            }],\n            ret: RetAbiType::Inhabited(AbiType::Bool),\n            symbol: \"\",\n        };\n\n        let kind = ErrorKind::RustFunError(\"function type expected\".into());\n        assert_binding_fun_error(&kind, &BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn non_list_rust_rest_param() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(& Int -> true)\",\n            takes_task: false,\n            params: &[ParamAbiType {\n                abi_type: AbiType::Boxed(BoxedAbiType::UniqueTagged(TypeTag::Int)),\n                capture: ParamCapture::Auto,\n            }],\n            ret: RetAbiType::Inhabited(AbiType::Bool),\n            symbol: \"\",\n        };\n\n        let kind = ErrorKind::RustFunError(\"expected Rust function to have `boxed::List` as last parameter to receive the rest argument\".into());\n        assert_binding_fun_error(&kind, &BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn mismatched_fixed_param_count() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(Int -> Int)\",\n            takes_task: false,\n            params: &[\n                ParamAbiType {\n                    abi_type: AbiType::Int,\n                    capture: ParamCapture::Never,\n                },\n                ParamAbiType {\n                    abi_type: AbiType::Int,\n                    capture: ParamCapture::Never,\n                },\n            ],\n            ret: RetAbiType::Inhabited(AbiType::Boxed(BoxedAbiType::UniqueTagged(TypeTag::Int))),\n            symbol: \"\",\n        };\n\n        let kind =\n            ErrorKind::RustFunError(\"expected Rust function to have 1 parameters; has 2\".into());\n        assert_binding_fun_error(&kind, &BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn incompatible_fixed_param_type() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(Char -> Int)\",\n            takes_task: false,\n            params: &[ParamAbiType {\n                abi_type: AbiType::Int,\n                capture: ParamCapture::Never,\n            }],\n            ret: RetAbiType::Inhabited(AbiType::Boxed(BoxedAbiType::UniqueTagged(TypeTag::Int))),\n            symbol: \"\",\n        };\n\n        let kind = ErrorKind::RustFunError(\n            \"Rust type `i64` does not match declared Arret type of `Char`\".into(),\n        );\n        assert_binding_fun_error(&kind, &BINDING_RUST_FUN);\n    }\n\n    #[test]\n    fn incompatible_ret_type() {\n        const BINDING_RUST_FUN: binding::RustFun = binding::RustFun {\n            arret_type: \"(Int -> Char)\",\n            takes_task: false,\n            params: &[ParamAbiType {\n                abi_type: AbiType::Int,\n                capture: ParamCapture::Never,\n            }],\n            ret: RetAbiType::Inhabited(AbiType::Boxed(BoxedAbiType::UniqueTagged(TypeTag::Int))),\n            symbol: \"\",\n        };\n\n        let kind = ErrorKind::RustFunError(\n            \"Rust type `Gc<boxed::Int>` does not match declared Arret type of `Char`\".into(),\n        );\n        assert_binding_fun_error(&kind, &BINDING_RUST_FUN);\n    }\n}\n"
  },
  {
    "path": "compiler/source.rs",
    "content": "use std::ffi::OsString;\nuse std::ops::Range;\nuse std::sync::{Arc, RwLock, RwLockReadGuard};\nuse std::{fmt, fs, io, path};\n\nuse codespan_reporting::files::Error as CodespanError;\n\nuse arret_syntax::datum::Datum;\nuse arret_syntax::span::{FileId, Span};\n\npub const EMPTY_SPAN: Span = Span::new(None, 0, 0);\n\n#[derive(Clone)]\npub enum SourceText {\n    Static(&'static str),\n    Shared(Arc<str>),\n}\n\nimpl AsRef<str> for SourceText {\n    fn as_ref(&self) -> &str {\n        match self {\n            SourceText::Shared(shared) => shared.as_ref(),\n            SourceText::Static(static_str) => static_str,\n        }\n    }\n}\n\nimpl From<Arc<str>> for SourceText {\n    fn from(s: Arc<str>) -> Self {\n        SourceText::Shared(s)\n    }\n}\n\nimpl From<String> for SourceText {\n    fn from(s: String) -> Self {\n        SourceText::Shared(s.into())\n    }\n}\n\nimpl From<&'static str> for SourceText {\n    fn from(s: &'static str) -> Self {\n        SourceText::Static(s)\n    }\n}\n\npub struct SourceFile {\n    file_id: FileId,\n    source: SourceText,\n    parsed: Result<Vec<Datum>, arret_syntax::error::Error>,\n}\n\nimpl SourceFile {\n    pub fn file_id(&self) -> FileId {\n        self.file_id\n    }\n\n    pub fn source(&self) -> &'_ str {\n        self.source.as_ref()\n    }\n\n    pub fn parsed(&self) -> Result<&[Datum], arret_syntax::error::Error> {\n        match &self.parsed {\n            Ok(data) => Ok(data),\n            Err(err) => Err(err.clone()),\n        }\n    }\n}\n\nimpl fmt::Debug for SourceFile {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.file_id.fmt(formatter)\n    }\n}\n\nstruct ReportableFile {\n    filename: OsString,\n    source: SourceText,\n    line_offsets: Vec<usize>,\n}\n\nimpl ReportableFile {\n    fn name(&self) -> String {\n        self.filename.to_string_lossy().into()\n    }\n\n    fn source(&self) -> &str {\n        self.source.as_ref()\n    }\n\n    fn line_index(&self, offset: usize) -> usize {\n        match self\n            .line_offsets\n            .binary_search_by(|line_start| line_start.cmp(&offset))\n        {\n            Ok(line) => line,\n            Err(line) => line - 1,\n        }\n    }\n\n    fn line_range(&self, line_index: usize) -> Option<Range<usize>> {\n        let start = self.line_offsets.get(line_index)?;\n\n        let end = self\n            .line_offsets\n            .get(line_index + 1)\n            .cloned()\n            .unwrap_or_else(|| self.source.as_ref().len());\n\n        Some(*start..end)\n    }\n}\n\n#[derive(Default)]\npub struct SourceLoader {\n    files: RwLock<Vec<ReportableFile>>,\n}\n\nimpl SourceLoader {\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Synchronously read path into a `SourceFile`\n    pub fn load_path(&self, path: &path::Path) -> Result<SourceFile, io::Error> {\n        let source = fs::read_to_string(path)?;\n\n        Ok(self.load_string(\n            path.as_os_str().to_owned(),\n            SourceText::Shared(source.into()),\n        ))\n    }\n\n    /// Loads a caller-provided string into a `SourceFile`\n    pub fn load_string(&self, filename: OsString, source: impl Into<SourceText>) -> SourceFile {\n        use arret_syntax::parser::data_from_str;\n\n        let source = source.into();\n        let reportable_file = ReportableFile {\n            filename,\n            line_offsets: codespan_reporting::files::line_starts(source.as_ref()).collect(),\n            source: source.clone(),\n        };\n\n        let file_index = {\n            let mut files_write = self.files.write().unwrap();\n\n            files_write.push(reportable_file);\n            files_write.len()\n        };\n\n        let file_id = FileId::new(file_index as u32).unwrap();\n        SourceFile {\n            file_id,\n            parsed: data_from_str(Some(file_id), source.as_ref()),\n            source,\n        }\n    }\n\n    /// Reserves space for `additional` more files\n    ///\n    /// This can be used to avoid allocating memory under our instance's write lock.\n    pub fn reserve(&self, additional: usize) {\n        self.files.write().unwrap().reserve(additional)\n    }\n\n    /// Returns a `ReportableFiles` instance usable with `codespan-reporting`\n    ///\n    /// This will take our instance's read lock.\n    pub fn files(&self) -> ReportableFiles<'_> {\n        ReportableFiles {\n            files: self.files.read().unwrap(),\n        }\n    }\n}\n\npub struct ReportableFiles<'a> {\n    files: RwLockReadGuard<'a, Vec<ReportableFile>>,\n}\n\nimpl<'a> ReportableFiles<'a> {\n    fn get_file(&self, file_id: FileId) -> Option<&ReportableFile> {\n        self.files.get((file_id.get() - 1) as usize)\n    }\n}\n\ntype CodespanResult<T> = Result<T, CodespanError>;\n\nimpl<'a> codespan_reporting::files::Files<'a> for ReportableFiles<'a> {\n    type FileId = FileId;\n    type Source = &'a str;\n    type Name = String;\n\n    fn name(&self, file_id: FileId) -> CodespanResult<String> {\n        self.get_file(file_id)\n            .ok_or(CodespanError::FileMissing)\n            .map(|f| f.name())\n    }\n\n    fn source(&self, file_id: FileId) -> CodespanResult<&str> {\n        self.get_file(file_id)\n            .ok_or(CodespanError::FileMissing)\n            .map(|f| f.source())\n    }\n\n    fn line_index(&self, file_id: FileId, offset: usize) -> CodespanResult<usize> {\n        self.get_file(file_id)\n            .ok_or(CodespanError::FileMissing)\n            .map(|f| f.line_index(offset))\n    }\n\n    fn line_range(&self, file_id: FileId, line_index: usize) -> CodespanResult<Range<usize>> {\n        self.get_file(file_id)\n            .ok_or(CodespanError::FileMissing)\n            .and_then(|f| {\n                f.line_range(line_index).ok_or(CodespanError::LineTooLarge {\n                    given: line_index,\n                    max: f.line_offsets.len(),\n                })\n            })\n    }\n}\n"
  },
  {
    "path": "compiler/tests/compile-error/arity.arret",
    "content": "(import [stdlib base])\n\n(defn not-enough-fixed (_) ())\n(def _ (not-enough-fixed))\n      ;^^^^^^^^^^^^^^^^^^ ERROR incorrect number of arguments: wanted 1, have 0\n\n(defn not-enough-fixed-and-rest (_ & _) ())\n(def _ (not-enough-fixed-and-rest))\n      ;^^^^^^^^^^^^^^^^^^^^^^^^^^^ ERROR incorrect number of arguments: wanted at least 1, have 0\n\n(defn too-many-fixed (_) ())\n(def _ (too-many-fixed 1 2 3))\n      ;^^^^^^^^^^^^^^^^^^^^^^ ERROR incorrect number of arguments: wanted 1, have 3\n\n(def _ (bool?))\n      ;^^^^^^^ ERROR incorrect number of arguments: wanted 1, have 0\n\n(def _ (bool? true true))\n      ;^^^^^^^^^^^^^^^^^ ERROR incorrect number of arguments: wanted 1, have 2\n\n(defn apply-top-fun ([x (... -> Bool)])\n  (x 5))\n ;^^^^^ ERROR cannot determine parameter types for `(... -> Bool)`\n\n (defn apply-with-rest (_ [_ Int] _))\n (def _ (apply-with-rest & '(1 2)))\n                           ;^^^^^ ERROR mismatched types\n (def _ (apply-with-rest & '(1 2 3 4)))\n                           ;^^^^^^^^^ ERROR mismatched types\n\n(defn main! ())"
  },
  {
    "path": "compiler/tests/compile-error/bit-shift-left-negative.arret",
    "content": "(import [stdlib base])\n\n(def _ (bit-shift-left 0 -1))\n      ;^^^^^^^^^^^^^^^^^^^^^ ERROR shift left by negative bit count -1\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/bit-shift-right-overflow.arret",
    "content": "(import [stdlib base])\n\n(def _ (bit-shift-right 0 65))\n      ;^^^^^^^^^^^^^^^^^^^^^^ ERROR shift right by 65 bits exceeds 64 bits\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/destruc-errors.arret",
    "content": "(import [stdlib base])\n\n(def [x y] [1 2])\n       ;^ ERROR unable to resolve `y`\n\n(def [x / y] [1 2 3])\n    ;^^^^^^^ ERROR vectors can only be used in a destructure in the form `[name Type]`\n\n(def [x y z] [1 2 3 4])\n    ;^^^^^^^ ERROR vectors can only be used in a destructure in the form `[name Type]`\n\n(def [1 Int] 1)\n     ;^ ERROR expected symbol, found integer\n\n(def 1 1)\n    ;^ ERROR unsupported destructuring binding\n\n(def :foo 1)\n    ;^^^^ ERROR expected symbol, found keyword\n\n; Double type annotation\n(def [[x Int] Int] 1)\n     ;^^^^^^^ ERROR expected symbol, found vector\n\n(def bad-param-destruc\n  (fn (1)))\n      ;^ ERROR unsupported destructuring binding\n\n(def keyword-param-destruc\n  (fn (:foo)))\n      ;^^^^ ERROR expected symbol, found keyword\n"
  },
  {
    "path": "compiler/tests/compile-error/fn-lowering-errors.arret",
    "content": "(import [stdlib base])\n\n(def _ (fn))\n      ;^^^^ ERROR parameter declaration missing\n\n(def _ (fn #{A}))\n      ;^^^^^^^^^ ERROR parameter declaration missing\n\n(def _ (fn []))\n          ;^^ ERROR expected parameter declaration list, found empty vector\n\n(def _ (fn #{123} ()))\n            ;^^^ ERROR bad polymorphic variable declaration"
  },
  {
    "path": "compiler/tests/compile-error/if-errors.arret",
    "content": "(import [stdlib base])\n\n(def _ (if))            ;~ ERROR wrong argument count; expected 3\n(def _ (if true))       ;~ ERROR wrong argument count; expected 3\n(def _ (if true false)) ;~ ERROR wrong argument count; expected 3"
  },
  {
    "path": "compiler/tests/compile-error/import-parse-errors.arret",
    "content": "(import (unknown [stdlib base]))\n        ;^^^^^^^ ERROR expected import filter keyword, found symbol\n\n(import (:rename [stdlib base] true))\n                              ;^^^^ ERROR expected identifier rename map, found boolean true\n\n(import (:rename [stdlib base] {foo bar} 1))\n       ;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ERROR wrong argument count; expected 2\n\n(import (:prefix [stdlib base] test- 1))\n       ;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ERROR wrong argument count; expected 2\n\n(import (:prefixed [stdlib base] 1))\n       ;^^^^^^^^^^^^^^^^^^^^^^^^^^^ ERROR wrong argument count; expected 1\n\n(import 4)\n       ;^ ERROR bad import set\n\n(import []) ;~ ERROR module name requires a least two components\n(import [just-one]) ;~ ERROR module name requires a least two components"
  },
  {
    "path": "compiler/tests/compile-error/macro-errors.arret",
    "content": "(import [arret internal primitives])\n\n(defmacro 1 (macro-rules))\n         ;^ ERROR expected symbol, found integer\n\n(defmacro a b) ;~ ERROR expected macro specification list, found symbol\n\n(defmacro a ())\n           ;^^ ERROR missing macro type\n\n(defmacro a (macro-fn))\n            ;^^^^^^^^ ERROR unsupported macro type\n\n(defmacro a (macro-rules\n  [(...) false]\n   ;^^^ ERROR unexpected ellipsis in macro rule\n))\n\n(defmacro a (macro-rules\n  [(1) (... 1)]\n           ;^ ERROR expected macro symbol to escape, found integer\n))\n\n(defmacro no-rules (macro-rules))\n(no-rules) ;~ ERROR no matching macro rule\n\n(defmacro _ (macro-rules 1))\n                        ;^ ERROR expected macro rule vector, found integer\n\n(defmacro no-template-datum (macro-rules\n  [()]\n ;^^^^ ERROR expected macro rule vector with 2 elements, found 1\n))\n\n(defmacro more-than-one-template-datum (macro-rules\n  [() 1 2]\n ;^^^^^^^^ ERROR expected macro rule vector with 2 elements, found 3\n))\n\n(defmacro non-list-pattern (macro-rules\n  [self 1]\n  ;^^^^ ERROR expected macro rule pattern list\n))\n\n(defmacro m (macro-rules\n  [((list1 ...) (list2 ...)) ([list1 list2] ...)]\n                            ;^^^^^^^^^^^^^^^^^^^ ERROR subtemplate references macro variables from multiple subpatterns\n))\n\n(defmacro m (macro-rules\n  [(expr ...) (5 ...)]\n             ;^^^^^^^ ERROR subtemplate does not include any macro variables\n))\n\n(defmacro vm (macro-rules [((l ... r ...)) true]))\n                                  ;^ ERROR multiple zero or more matches in the same sequence\n\n(defmacro a (macro-rules [(x x) x]))\n                            ;^ ERROR duplicate definition\n\n(defmacro return-one (macro-rules [() 1]))\n(return-one extra-arg) ;~ ERROR no matching macro rule\n\n; Keyword doesn't match\n(defmacro for (macro-rules [(x :in y) [x y]]))\n(for 1 :for 2) ;~ ERROR no matching macro rule\n\n(defmacro two-set? (macro-rules\n  [(#{_ _}) false]\n   ;^^^^^^ ERROR set patterns must either be empty or a zero or more match\n))\n"
  },
  {
    "path": "compiler/tests/compile-error/misc-body-errors.arret",
    "content": "(import [stdlib base])\n\n(defn too-many-quote-args ()\n  (quote 1 2 3))\n ;^^^^^^^^^^^^^ ERROR wrong argument count; expected 1\n\n(defn def-in-body-context ()\n  (def foo 1))\n ;^^^^^^^^^^^ ERROR definition outside module body\n\n(defn let-without-binding-vector ()\n  (let x 1))\n      ;^ ERROR binding vector expected\n\n(def missing-param-list\n  (fn))\n ;^^^^ ERROR parameter declaration missing\n\n(def duplicate-param\n  (fn (x x)))\n        ;^ ERROR duplicate definition\n\n(def user-compile-error\n  (compile-error \"Some message\")) ;~ ERROR Some message\n\n(def uneven-let (let [x 1 y]))\n                         ;^ ERROR binding vector must have an even number of forms"
  },
  {
    "path": "compiler/tests/compile-error/misc-top-level-errors.arret",
    "content": "(import [stdlib base])\n\n(import (:only [stdlib base] do))\n       ;^^^^^^^^^^^^^^^^^^^^^^^^ ERROR duplicate definition\n\n1                    ;~ ERROR value at top-level of module body\n'foo                 ;~ ERROR value at top-level of module body\n(list)               ;~ ERROR value at top-level of module body\n(if true true false) ;~ ERROR value at top-level of module body\n(fn ())              ;~ ERROR value at top-level of module body\n\n(export unbound)\n       ;^^^^^^^ ERROR unable to resolve `unbound`\n\n(compile-error \"Some message\") ;~ ERROR Some message\n(compile-error 1234)\n              ;^^^^ ERROR expected error message string, found integer\n\n(def x 1)\n(def x 2)\n    ;^ ERROR duplicate definition\n\n(deftype x Int)\n        ;^ ERROR duplicate definition\n\n; This is special because it's part of our prelude\n(def import 5)\n    ;^^^^^^ ERROR duplicate definition\n"
  },
  {
    "path": "compiler/tests/compile-error/missing-module.arret",
    "content": "(import [package does not exist]) ;~ ERROR package not found\n(import [stdlib module does not exist]) ;~ ERROR module not found"
  },
  {
    "path": "compiler/tests/compile-error/no-main.arret",
    "content": " ;~ ERROR no main! function defined in entry module"
  },
  {
    "path": "compiler/tests/compile-error/overflow-add.arret",
    "content": "(import [stdlib base])\n\n(def maximum-int 9223372036854775807)\n(def _ (+ maximum-int 1))\n      ;^^^^^^^^^^^^^^^^^ ERROR attempt to add with overflow\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/overflow-multiply.arret",
    "content": "(import [stdlib base])\n\n(def maximum-int 9223372036854775807)\n(def _ (* maximum-int 2))\n      ;^^^^^^^^^^^^^^^^^ ERROR attempt to multiply with overflow\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/overflow-quot.arret",
    "content": "(import [stdlib base])\n\n(def minimum-int -9223372036854775808)\n\n; This message isn't quite right but it's not worth distinguishing these cases\n(def _ (quot minimum-int -1))\n      ;^^^^^^^^^^^^^^^^^^^^^ ERROR division by zero\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/overflow-subtract.arret",
    "content": "(import [stdlib base])\n\n(def minimum-int -9223372036854775808)\n(def _ (- minimum-int 1))\n      ;^^^^^^^^^^^^^^^^^ ERROR attempt to subtract with overflow\n\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/quot-by-zero.arret",
    "content": "(import [stdlib base])\n\n(def _ (quot 1 0))\n      ;^^^^^^^^^^ ERROR division by zero\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/record-errors.arret",
    "content": "(import [stdlib base])\n\n(defrecord foo) ;~ ERROR wrong argument count; expected 2\n\n(defrecord :keyword ())\n          ;^^^^^^^^ ERROR expected record type constuctor declaration, found keyword\n\n(defrecord _ [])\n            ;^^ ERROR expected record value constructor declaration, found empty vector\n\n(defrecord _ ())\n            ;^^ ERROR expected record value constructor declaration, found empty list\n\n(defrecord _ (1))\n             ;^ ERROR expected symbol, found integer\n\n(defrecord Foo (foo 1))\n                   ;^ ERROR expected record field declaration, found integer\n\n(defrecord Foo (foo []))\n                   ;^^ ERROR expected record field declaration, found empty vector\n\n(defrecord (Foo 1) ())\n               ;^ ERROR bad polymorphic variable declaration\n\n(defrecord (Point [N Num]) (point [x N] [y N]))\n\n(def [_ (Point Int Float)] (point 1 2.0))\n       ;^^^^^^^^^^^^^^^^^ ERROR wrong argument count; expected 1\n\n(def [_ (Point Bool)] (point true))\n              ;^^^^ ERROR mismatched types\n\n; These polymorphic variables are actually fixed to a single type\n(defrecord (FixedPolyRecord [A Int] [->_ ->]) (fixed-poly-record [pred (A ->_ Bool)]))\n\n(def [_ (FixedPolyRecord Float ->)] 1)\n                        ;^^^^^ ERROR mismatched types\n\n(def [_ (FixedPolyRecord Int ->!)] 1)\n                            ;^^^ ERROR mismatched purities\n\n(def [_ (FixedPolyRecord Int Int)] 1)\n                            ;^^^ ERROR type cannot be used as a purity\n\n(def [_ (FixedPolyRecord Int [])] 1)\n                            ;^^ ERROR empty vector cannot be used as a purity\n\n(defrecord (AnonymousPolyParam [_ Int]) (_))\n                               ;^ ERROR polymorphic parameters must have a name\n\n(defrecord (UnusedPolyPurityParam [->_ ->!]) (_))\n                                 ;^^^^^^^^^ ERROR unused polymorphic purity parameter `->_`\n\n(defrecord (UnusedPolyTyParam [A Num]) (_))\n                             ;^^^^^^^ ERROR unused polymorphic type parameter `A`\n\n(defrecord DuplicateFieldName (duplicate-field-name field field))\n                                                         ;^^^^^ ERROR duplicate definition of `duplicate-field-name-field`"
  },
  {
    "path": "compiler/tests/compile-error/recur-errors.arret",
    "content": "(import [stdlib base])\n\n(defn non-tail-reverse #{T} ([lst (List & T)]) -> (List & T)\n  (if (nil? lst)\n    lst\n    (concat (recur (rest lst)) (list (first lst)))))\n           ;^^^^^^^^^^^^^^^^^^ ERROR non-tail `(recur)`\n\n(defn recur-without-fun-ty-decl () (recur))\n                                  ;^^^^^^^ ERROR type annotation needed\n\n(defn recur-with-non-generic-argument #{[T Num]}\n  ([v T]) -> () (recur 5))\n                      ;^ ERROR mismatched types\n\n(defn main! () ->! () ())"
  },
  {
    "path": "compiler/tests/compile-error/reference-errors.arret",
    "content": "(import [stdlib base])\n\n(def _ nopenopenope)\n      ;^^^^^^^^^^^^ ERROR unable to resolve `nopenopenope`\n\n(def _ Str)\n      ;^^^ ERROR cannot take the value of a type\n\n(def _ List)\n      ;^^^^ ERROR cannot take the value of a type constructor\n\n(def _ ->!)\n      ;^^^ ERROR cannot take the value of a purity\n\n(def _ fn)\n      ;^^ ERROR cannot take the value of a primitive\n\n(def _ (let [x x]))\n              ;^ ERROR unable to resolve `x`"
  },
  {
    "path": "compiler/tests/compile-error/rem-by-zero.arret",
    "content": "(import [stdlib base])\n\n(def _ (rem 1 0))\n      ;^^^^^^^^^ ERROR division by zero\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/syntax-error.arret",
    "content": "(this is invalid]\n               ;^ ERROR unexpected `]` while parsing list"
  },
  {
    "path": "compiler/tests/compile-error/type-checking-errors.arret",
    "content": "(import [stdlib base])\n\n(def [wrong-ascription Int] 'foo)\n                            ;^^^ ERROR mismatched types\n\n(def ([one 'one] [two 'two]) '(two one))\n                             ;^^^^^^^^^ ERROR mismatched types\n\n(defn wrong-ret () -> Str\n  'foo)\n  ;^^^ ERROR mismatched types\n\n(def non-bool-if-test (if 'foo true false))\n                          ;^^^ ERROR mismatched types\n\n(defn wrong-if-branch-type ([test Bool]) -> Sym\n  (if test\n    'foo\n    \"foo\"))\n   ;^^^^^ ERROR mismatched types\n\n(def [wrong-do-type Int]\n  (do 1 2 'three))\n          ;^^^^^ ERROR mismatched types\n\n(def wrong-arg-type\n  ((fn ([input Str])) 'foo))\n                      ;^^^ ERROR mismatched types\n\n(defn conflicting-free-type (free-input)\n  (ann free-input Str)\n  (ann free-input Sym))\n      ;^^^^^^^^^^ ERROR type annotation needed\n\n; `input` will gain type information from the function type annotation\n(def [conflicting-closure-type (Sym -> Str)]\n  (fn (input) (ann input Str)))\n                  ;^^^^^ ERROR type annotation needed\n\n(def not-fun-def (\"foo\"))\n                 ;^^^^^ ERROR expected function, found `Str`\n\n(defn impure-fun! () ->! ())\n(def impure-def (impure-fun!))\n                ;^^^^^^^^^^^ ERROR mismatched purities\n\n(defn impure-app () -> ()\n  (impure-fun!))\n  ;^^^^^^^^^^^ ERROR mismatched purities\n\n(defn polymorphic-impure-app #{[->A ->!]} () ->A ()\n  (impure-fun!))\n  ;^^^^^^^^^^^ ERROR mismatched purities\n\n(defn bad-apply-in-return-position () -> ()\n  (length '(1 2 3)))\n  ;^^^^^^ ERROR mismatched types\n\n(defn non-list-rest-type (& x)\n  (ann x (Vector Any)))\n      ;^ ERROR mismatched types\n\n(defn specific-rest-list-type (& x)\n  (ann x (List Int Int Int)))\n      ;^ ERROR mismatched types\n\n; The compiler should suppress this error as it's a cascade error\n(def depends-on-type-error specific-rest-list-type)\n\n; This generic parameter isn't sufficiently bound\n(defn takes-symbol ([x Sym]))\n(defn takes-generic #{T} ([x T])\n  (takes-symbol x))\n               ;^ ERROR mismatched types\n\n; Applying a fun with incorrect polymorphic purity inside a pure context\n(def _ (map println! '(0 1 2 3)))\n           ;^^^^^^^^ ERROR mismatched types\n\n; `input` is used both as a poly `Sym` and a poly `Num`\n(defn conflicting-poly-types #{[A Sym] [B Num]} (input\n                                                 [sym-is-foo? (A -> Bool)]\n                                                 [num-is-zero? (B -> Bool)]) -> ()\n  (sym-is-foo? input)\n  (num-is-zero? input)\n               ;^^^^^ ERROR type annotation needed\n  ())\n\n(defn unselected-purity-variable ()\n  ((fn #{[->_ ->!]} ()))\n ;^^^^^^^^^^^^^^^^^^^^^^ ERROR cannot determine purity of purity variable `->_`\n  ())\n\n(defn unselected-type-variable ()\n  ((fn #{T} ()))\n ;^^^^^^^^^^^^^^ ERROR cannot determine type of type variable `T`\n  ())\n\n; Make sure we still type check the branches\n(defn divergent-cond-test () -> Sym\n  (if (panic \"Divergent\")\n    ; This isn't a `Sym` but we should allow it as it's never returned\n    true\n    ; This isn't well-typed because a test must be a `Bool`\n    (if 'foo 1 2)))\n        ;^^^ ERROR mismatched types\n\n(defrecord RecordOne (record1))\n(defrecord RecordTwo (record2))\n(defrecord RecordThree (record3))\n\n; Make sure we don't unify this to `Record`\n(def [_ (U RecordOne RecordTwo)] (record3))\n                                 ;^^^^^^^ ERROR mismatched types\n\n; `(not)` should only take a `Bool`\n(def _ (not 5))\n           ;^ ERROR mismatched types\n\n(defn main! ())"
  },
  {
    "path": "compiler/tests/compile-error/type-lowering-errors.arret",
    "content": "(import [stdlib base])\n\n(deftype _ unbound)\n          ;^^^^^^^ ERROR unable to resolve `unbound`\n\n(deftype _ (unbound))\n           ;^^^^^^^ ERROR unable to resolve `unbound`\n\n(deftype _ quote)\n          ;^^^^^ ERROR primitive cannot be used as a type\n\n(deftype _ 1)\n          ;^ ERROR unsupported literal type\n\n(deftype _ '(1))\n            ;^ ERROR unsupported literal type\n\n(deftype _ [1])\n           ;^ ERROR unsupported literal type\n\n(deftype _ (->))\n           ;^^ ERROR purity cannot be used as a type constructor\n\n; This isn't public yet but still test it\n(import (:only [arret internal primitives] All))\n\n(deftype _ (All))\n          ;^^^^^ ERROR polymorphic variable declaration missing\n\n(deftype _ (All []))\n               ;^^ ERROR expected polymorphic variable set, found empty vector\n\n(deftype _ (All #{} List Int))\n          ;^^^^^^^^^^^^^^^^^^ ERROR polymorphism on non-function type"
  },
  {
    "path": "compiler/tests/compile-error/vector-assoc-negative.arret",
    "content": "(import [stdlib base])\n\n(def _ (vector-assoc [1 2 3] -5 0))\n      ;^^^^^^^^^^^^^^^^^^^^^^^^^^^ ERROR index -5 is negative\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/vector-assoc-out-of-bounds.arret",
    "content": "(import [stdlib base])\n\n(def _ (vector-assoc [1 2 3] 3 0))\n      ;^^^^^^^^^^^^^^^^^^^^^^^^^^ ERROR index 3 out of bounds for vector of length 3\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/vector-ref-negative.arret",
    "content": "(import [stdlib base])\n\n(def _ (vector-ref [1 2 3] -5))\n      ;^^^^^^^^^^^^^^^^^^^^^^^ ERROR index -5 is negative\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/vector-ref-out-of-bounds.arret",
    "content": "(import [stdlib base])\n\n(def _ (vector-ref [1 2 3] 5))\n      ;^^^^^^^^^^^^^^^^^^^^^^ ERROR index 5 out of bounds for vector of length 3\n\n(defn main! () ->! ())"
  },
  {
    "path": "compiler/tests/compile-error/wrong-main-type.arret",
    "content": "(import [stdlib base])\n\n(defn main! () -> Int 1) ;~ ERROR mismatched types"
  },
  {
    "path": "compiler/tests/integration.rs",
    "content": "#![warn(clippy::all)]\n#![warn(rust_2018_idioms)]\n\nuse std::io::Write;\nuse std::ops::Range;\nuse std::sync::Arc;\nuse std::{fs, io, path, process};\n\nuse codespan_reporting::diagnostic::{Diagnostic, Label, Severity};\nuse codespan_reporting::files::Files as _;\n\nuse tempfile::NamedTempFile;\n\nuse arret_syntax::span::{FileId, Span};\n\nuse arret_compiler::{emit_diagnostics_to_stderr, CompileCtx, SourceText};\n\n#[derive(Clone, PartialEq)]\nstruct RunOutput {\n    stdout: Vec<u8>,\n    stderr: Vec<u8>,\n}\n\n#[derive(Clone, PartialEq)]\nenum RunType {\n    Pass(RunOutput),\n    Error(RunOutput),\n}\n\nimpl RunType {\n    fn expected_output(&self) -> &RunOutput {\n        match self {\n            RunType::Pass(run_output) => run_output,\n            RunType::Error(run_output) => run_output,\n        }\n    }\n}\n\n#[derive(Clone, PartialEq)]\nenum TestType {\n    CompileError,\n    Optimise,\n    Run(RunType),\n}\n\n#[derive(Debug)]\nenum ExpectedSpan {\n    Exact(FileId, Range<usize>),\n    StartRange(FileId, Range<usize>),\n}\n\nimpl ExpectedSpan {\n    fn matches(&self, actual_file_id: FileId, actual_range: Range<usize>) -> bool {\n        match self {\n            ExpectedSpan::Exact(expected_file_id, expected_range) => {\n                actual_file_id == *expected_file_id && actual_range == *expected_range\n            }\n            ExpectedSpan::StartRange(expected_file_id, expected_start_range) => {\n                let actual_range_start: usize = actual_range.start;\n\n                actual_file_id == *expected_file_id\n                    && actual_range_start >= expected_start_range.start\n                    && actual_range_start < expected_start_range.end\n            }\n        }\n    }\n}\n\n#[derive(Debug)]\nstruct ExpectedDiagnostic {\n    expected_severity: Severity,\n    message_prefix: String,\n    span: ExpectedSpan,\n}\n\nimpl ExpectedDiagnostic {\n    fn matches(&self, actual: &Diagnostic<FileId>) -> bool {\n        if self.expected_severity != actual.severity {\n            return false;\n        }\n\n        if !actual.message.starts_with(&self.message_prefix[..]) {\n            return false;\n        }\n\n        actual.labels.iter().any(|candidate_label| {\n            self.span\n                .matches(candidate_label.file_id, candidate_label.range.clone())\n        })\n    }\n\n    /// Returns a diagnostic for reporting missing expectation\n    fn to_error_diagnostic(&self) -> Diagnostic<FileId> {\n        let (file_id, span_range) = match self.span {\n            ExpectedSpan::Exact(ref file_id, ref span_range) => (file_id, span_range),\n            ExpectedSpan::StartRange(ref file_id, ref span_range) => (file_id, span_range),\n        };\n\n        let span = Span::new(\n            Some(*file_id),\n            span_range.start as u32,\n            span_range.end as u32,\n        );\n\n        Diagnostic::error()\n            .with_message(format!(\n                \"expected {}\",\n                severity_name(self.expected_severity)\n            ))\n            .with_labels(vec![Label::primary(\n                span.file_id().unwrap(),\n                span.byte_range(),\n            )\n            .with_message(format!(\"{} ...\", self.message_prefix))])\n    }\n}\n\nfn take_severity(marker_string: &str) -> (Severity, &str) {\n    for (prefix, severity) in &[\n        (\" BUG \", Severity::Bug),\n        (\" ERROR \", Severity::Error),\n        (\" WARNING \", Severity::Warning),\n        (\" HELP \", Severity::Help),\n        (\" NOTE \", Severity::Note),\n    ] {\n        if let Some(message_prefix) = marker_string.strip_prefix(prefix) {\n            return (*severity, message_prefix);\n        }\n    }\n\n    panic!(\"Unknown severity prefix for `{}`\", marker_string)\n}\n\nfn severity_name(severity: Severity) -> &'static str {\n    match severity {\n        Severity::Bug => \"bug\",\n        Severity::Error => \"error\",\n        Severity::Warning => \"warning\",\n        Severity::Help => \"help\",\n        Severity::Note => \"note\",\n    }\n}\n\nfn extract_expected_diagnostics(\n    source_file: &arret_compiler::SourceFile,\n) -> Vec<ExpectedDiagnostic> {\n    let source = source_file.source();\n\n    source\n        .match_indices(\";~\")\n        .map(|(index, _)| {\n            let start_of_line_index = &source[..index].rfind('\\n').map(|i| i + 1).unwrap_or(0);\n\n            let end_of_line_index = &source[index..]\n                .find('\\n')\n                .map(|i| i + index)\n                .unwrap_or_else(|| source.len());\n\n            // Take from after the ;~ to the end of the line\n            let marker_string = &source[index + 2..*end_of_line_index];\n            let (severity, marker_string) = take_severity(marker_string);\n\n            ExpectedDiagnostic {\n                expected_severity: severity,\n                message_prefix: marker_string.into(),\n                span: ExpectedSpan::StartRange(source_file.file_id(), *start_of_line_index..index),\n            }\n        })\n        .chain(source.match_indices(\";^\").map(|(index, _)| {\n            let span_length = source[index..].find(' ').expect(\"Cannot find severity\") - 1;\n\n            let start_of_line_index = &source[..index]\n                .rfind('\\n')\n                .expect(\"Cannot have a spanned error on first line\");\n\n            let start_of_previous_line_index = &source[..*start_of_line_index]\n                .rfind('\\n')\n                .map(|i| i + 1)\n                .unwrap_or(0);\n\n            let end_of_line_index = &source[index..]\n                .find('\\n')\n                .map(|i| i + index)\n                .unwrap_or_else(|| source.len());\n\n            let span_line_offset = index - start_of_line_index;\n\n            let span_start = start_of_previous_line_index + span_line_offset;\n            let span_end = span_start + span_length;\n\n            // Take from after the ;^^ to the end of the line\n            let marker_string = &source[index + span_length + 1..*end_of_line_index];\n            let (severity, message_prefix) = take_severity(marker_string);\n\n            ExpectedDiagnostic {\n                expected_severity: severity,\n                message_prefix: message_prefix.into(),\n                span: ExpectedSpan::Exact(source_file.file_id(), span_start..span_end),\n            }\n        }))\n        .collect()\n}\n\nfn unexpected_diag_to_error_diagnostic(unexpected_diag: Diagnostic<FileId>) -> Diagnostic<FileId> {\n    let unexpected_primary_label = unexpected_diag\n        .labels\n        .iter()\n        .find(|label| label.style == codespan_reporting::diagnostic::LabelStyle::Primary)\n        .cloned();\n\n    Diagnostic::error()\n        .with_message(format!(\n            \"unexpected {}\",\n            severity_name(unexpected_diag.severity)\n        ))\n        .with_labels(\n            unexpected_primary_label\n                .into_iter()\n                .map(|unexpected_primary_label| {\n                    Label::primary(\n                        unexpected_primary_label.file_id,\n                        unexpected_primary_label.range,\n                    )\n                    .with_message(unexpected_diag.message.clone())\n                })\n                .collect(),\n        )\n}\n\nfn exit_with_run_output_difference(\n    source_filename: String,\n    stream_name: &str,\n    expected: &[u8],\n    actual: &[u8],\n) {\n    use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};\n\n    let mut expected_color = ColorSpec::new();\n    expected_color.set_fg(Some(Color::Red));\n\n    let mut actual_color = ColorSpec::new();\n    actual_color.set_fg(Some(Color::Green));\n\n    let stderr = StandardStream::stderr(ColorChoice::Auto);\n    let mut stderr_lock = stderr.lock();\n\n    writeln!(\n        stderr_lock,\n        \"unexpected {} output from integration test {}\\n\",\n        stream_name, source_filename\n    )\n    .unwrap();\n\n    write!(stderr_lock, \"Expected: \\\"\").unwrap();\n\n    let _ = stderr_lock.set_color(&expected_color);\n    stderr_lock.write_all(expected).unwrap();\n    let _ = stderr_lock.reset();\n    writeln!(stderr_lock, \"\\\"\").unwrap();\n\n    write!(stderr_lock, \"Actual:   \\\"\").unwrap();\n\n    let _ = stderr_lock.set_color(&actual_color);\n    stderr_lock.write_all(actual).unwrap();\n    let _ = stderr_lock.reset();\n    writeln!(stderr_lock, \"\\\"\").unwrap();\n\n    std::process::exit(1);\n}\n\nfn result_for_single_test(\n    ccx: &CompileCtx,\n    source_file: &arret_compiler::SourceFile,\n    test_type: TestType,\n) -> Result<(), Vec<Diagnostic<FileId>>> {\n    let (output_path, run_type) = {\n        let arret_compiler::EvaluableProgram {\n            mut ehx,\n            main_export_id,\n            linked_libraries,\n        } = arret_compiler::program_to_evaluable(ccx, source_file)?;\n\n        // Try evaluating if we're not supposed to panic\n        if !matches!(test_type, TestType::Run(RunType::Error(_))) {\n            ehx.eval_main_fun(main_export_id)?;\n        }\n\n        let run_type = if let TestType::Run(run_type) = test_type {\n            run_type\n        } else {\n            return Ok(());\n        };\n\n        // And now compiling and running\n        let mir_program = ehx.into_built_program(main_export_id)?;\n\n        if mir_program.is_empty() {\n            // Don't bother building\n            return Ok(());\n        }\n\n        let gen_program_opts = arret_compiler::GenProgramOptions::new();\n        let output_path = NamedTempFile::new().unwrap().into_temp_path();\n\n        arret_compiler::gen_program(\n            gen_program_opts,\n            &linked_libraries,\n            &mir_program,\n            &output_path,\n            None,\n        );\n\n        (output_path, run_type)\n    };\n\n    let mut process = process::Command::new(output_path.as_os_str());\n\n    let expected_output = run_type.expected_output();\n    let output = process\n        .stdout(std::process::Stdio::piped())\n        .stderr(std::process::Stdio::piped())\n        .output()\n        .unwrap();\n\n    match run_type {\n        RunType::Pass(_) => {\n            if !output.status.success() {\n                // Dump any panic message from the test\n                let _ = io::stderr().write_all(&output.stderr);\n\n                return Err(vec![Diagnostic::error()\n                    .with_message(format!(\n                        \"unexpected status {} returned from integration test\",\n                        output.status,\n                    ))\n                    .with_labels(vec![Label::primary(source_file.file_id(), 0..1)\n                        .with_message(\"integration test file\")])]);\n            }\n        }\n        RunType::Error(_) => {\n            // Code 1 is used by panic. This makes sure we didn't e.g. SIGSEGV.\n            if output.status.code() != Some(1) {\n                return Err(vec![Diagnostic::error()\n                    .with_message(format!(\n                        \"unexpected status {} returned from integration test\",\n                        output.status,\n                    ))\n                    .with_labels(vec![Label::primary(source_file.file_id(), 0..1)\n                        .with_message(\"integration test file\")])]);\n            }\n        }\n    }\n\n    if expected_output.stderr != output.stderr {\n        exit_with_run_output_difference(\n            ccx.source_loader()\n                .files()\n                .name(source_file.file_id())\n                .unwrap(),\n            \"stderr\",\n            &expected_output.stderr,\n            &output.stderr,\n        );\n    }\n\n    if expected_output.stdout != output.stdout {\n        exit_with_run_output_difference(\n            ccx.source_loader()\n                .files()\n                .name(source_file.file_id())\n                .unwrap(),\n            \"stdout\",\n            &expected_output.stdout,\n            &output.stdout,\n        );\n    }\n\n    Ok(())\n}\n\nfn run_single_pass_test(\n    ccx: &CompileCtx,\n    source_file: &arret_compiler::SourceFile,\n    test_type: TestType,\n) -> bool {\n    let result = result_for_single_test(ccx, source_file, test_type);\n\n    if let Err(diagnostics) = result {\n        emit_diagnostics_to_stderr(ccx.source_loader(), diagnostics);\n        false\n    } else {\n        true\n    }\n}\n\nfn run_single_compile_fail_test(\n    ccx: &CompileCtx,\n    source_file: &arret_compiler::SourceFile,\n) -> bool {\n    let result = result_for_single_test(ccx, source_file, TestType::CompileError);\n\n    let mut expected_diags = extract_expected_diagnostics(source_file);\n    let actual_diags = if let Err(diags) = result {\n        diags\n    } else {\n        eprintln!(\n            \"Compilation unexpectedly succeeded for {}\",\n            ccx.source_loader()\n                .files()\n                .name(source_file.file_id())\n                .unwrap()\n        );\n        return false;\n    };\n\n    let mut unexpected_diags = vec![];\n\n    for actual_diag in actual_diags.into_iter() {\n        let expected_report_index = expected_diags\n            .iter()\n            .position(|expected_report| expected_report.matches(&actual_diag));\n\n        match expected_report_index {\n            Some(index) => {\n                expected_diags.swap_remove(index);\n            }\n            None => {\n                unexpected_diags.push(actual_diag);\n            }\n        }\n    }\n\n    if unexpected_diags.is_empty() && expected_diags.is_empty() {\n        return true;\n    }\n\n    let all_diags = unexpected_diags\n        .into_iter()\n        .map(unexpected_diag_to_error_diagnostic)\n        .chain(\n            expected_diags\n                .into_iter()\n                .map(|expected_diag| expected_diag.to_error_diagnostic()),\n        );\n\n    emit_diagnostics_to_stderr(ccx.source_loader(), all_diags);\n    false\n}\n\nfn run_single_test(ccx: &CompileCtx, input_path: &path::Path, test_type: TestType) -> bool {\n    let source = fs::read_to_string(input_path).unwrap();\n\n    let source_file = ccx.source_loader().load_string(\n        input_path.as_os_str().to_owned(),\n        SourceText::Shared(source.into()),\n    );\n\n    if test_type == TestType::CompileError {\n        run_single_compile_fail_test(ccx, &source_file)\n    } else {\n        run_single_pass_test(ccx, &source_file, test_type)\n    }\n}\n\nfn entry_is_arret_source(entry: &fs::DirEntry) -> bool {\n    entry\n        .file_name()\n        .to_str()\n        .map(|file_name| !file_name.starts_with('.') && file_name.ends_with(\".arret\"))\n        .unwrap_or(false)\n}\n\nfn read_or_empty_vec(filename: &path::Path) -> Result<Vec<u8>, io::Error> {\n    match fs::read(filename) {\n        Ok(data) => Ok(data),\n        Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(vec![]),\n        Err(err) => Err(err),\n    }\n}\n\nfn entry_to_compile_test_tuple(\n    entry: io::Result<fs::DirEntry>,\n    test_type: TestType,\n) -> Option<(path::PathBuf, TestType)> {\n    let entry = entry.unwrap();\n\n    if !entry_is_arret_source(&entry) {\n        None\n    } else {\n        Some((entry.path(), test_type))\n    }\n}\n\nfn entry_to_run_test_tuple<RT>(\n    entry: io::Result<fs::DirEntry>,\n    run_type: RT,\n) -> Option<(path::PathBuf, TestType)>\nwhere\n    RT: FnOnce(RunOutput) -> RunType,\n{\n    let entry = entry.unwrap();\n\n    if !entry_is_arret_source(&entry) {\n        return None;\n    }\n\n    let stderr_filename = entry.path().with_extension(\"stderr\");\n    let stdout_filename = entry.path().with_extension(\"stdout\");\n\n    // These files may not exist - we'll treat them as empty\n    let stderr = read_or_empty_vec(&stderr_filename).unwrap();\n    let stdout = read_or_empty_vec(&stdout_filename).unwrap();\n\n    let expected_output = RunOutput { stdout, stderr };\n\n    Some((entry.path(), TestType::Run(run_type(expected_output))))\n}\n\n#[test]\nfn integration() {\n    let package_paths = arret_compiler::PackagePaths::test_paths(None);\n    let ccx = Arc::new(arret_compiler::CompileCtx::new(package_paths, true));\n\n    use arret_compiler::initialise_llvm;\n    initialise_llvm(false);\n\n    let (send_test, recv_test) = crossbeam_channel::unbounded::<(path::PathBuf, TestType)>();\n    let (send_failed_test, recv_failed_test) = crossbeam_channel::unbounded::<String>();\n\n    let worker_threads: Vec<std::thread::JoinHandle<_>> = (0..num_cpus::get())\n        .map(|i| {\n            let ccx = Arc::clone(&ccx);\n            let recv_test = recv_test.clone();\n            let send_failed_test = send_failed_test.clone();\n\n            std::thread::Builder::new()\n                .name(format!(\"integration test worker thread {}\", i))\n                .spawn(move || {\n                    for (input_path, test_type) in recv_test.iter() {\n                        let test_successful =\n                            run_single_test(&ccx, input_path.as_path(), test_type);\n\n                        if !test_successful {\n                            send_failed_test\n                                .send(input_path.to_string_lossy().to_string())\n                                .unwrap();\n                        }\n                    }\n                })\n                .unwrap()\n        })\n        .collect();\n\n    // The main thread doesn't need these\n    drop(send_failed_test);\n    drop(recv_test);\n\n    fs::read_dir(\"./tests/compile-error\")\n        .unwrap()\n        .filter_map(|entry| entry_to_compile_test_tuple(entry, TestType::CompileError))\n        .for_each(|t| send_test.send(t).unwrap());\n\n    fs::read_dir(\"./tests/optimise\")\n        .unwrap()\n        .filter_map(|entry| entry_to_compile_test_tuple(entry, TestType::Optimise))\n        .for_each(|t| send_test.send(t).unwrap());\n\n    fs::read_dir(\"./tests/run-pass\")\n        .unwrap()\n        .filter_map(|entry| entry_to_run_test_tuple(entry, RunType::Pass))\n        .for_each(|t| send_test.send(t).unwrap());\n\n    fs::read_dir(\"./tests/run-error\")\n        .unwrap()\n        .filter_map(|entry| entry_to_run_test_tuple(entry, RunType::Error))\n        .for_each(|t| send_test.send(t).unwrap());\n\n    drop(send_test);\n\n    for thread in worker_threads {\n        thread.join().unwrap();\n    }\n\n    let failed_tests: Vec<String> = recv_failed_test.iter().collect();\n    if !failed_tests.is_empty() {\n        let _ = writeln!(\n            io::stderr(),\n            \"integration tests failed: {}\",\n            failed_tests.join(\", \")\n        );\n\n        std::process::exit(1);\n    }\n}\n"
  },
  {
    "path": "compiler/tests/optimise/application.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  ; Even if the outer function is impure we should optimise inner pure applications\n  (assert-fn-doesnt-contain-op! :call (fn () ->! Bool\n    (every? (fn (_) false) '(1 2 3)))))"
  },
  {
    "path": "compiler/tests/optimise/bitwise.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  (assert-fn-doesnt-contain-op! :call (fn ([lhs Int] [rhs Int])\n    (bit-and lhs rhs)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([lhs Int] [rhs Int])\n    (bit-or lhs rhs)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([lhs Int] [rhs Int])\n    (bit-xor lhs rhs)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([i Int])\n    (bit-not i)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([i Int])\n    (bit-shift-left i 16)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([i Int])\n    (bit-shift-right i 32)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([i Int])\n    (unsigned-bit-shift-right i 32))))"
  },
  {
    "path": "compiler/tests/optimise/const.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  ; Make sure we pass a constant list when calling `(member?)`\n  ; This is stupid but we've broken it in the past\n  (assert-fn-doesnt-contain-op! :alloc-boxed (fn ([needle Any])\n    (member? needle '(\"cat\" \"dog\" \"fish\"))))\n\n  ())"
  },
  {
    "path": "compiler/tests/optimise/equality.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defmacro assert-native-compare-fn! (macro-rules\n  [(f)\n    (do\n      (assert-fn-doesnt-contain-op! :call f)\n      (assert-fn-doesnt-contain-op! :const-box f)\n      (assert-fn-doesnt-contain-op! :alloc-boxed f))]))\n\n(defn main! () ->! ()\n  ; This requires calling in to the runtime\n  (assert-fn-contains-op! :call (fn ([left Any] [right Any]) -> Bool\n    (= left right)))\n\n  ; Ints can be directly compared\n  (assert-native-compare-fn! (fn ([left Int] [right Int]) -> Bool\n    (= left right)))\n\n  ; This should optimise similarly\n  (assert-native-compare-fn! (fn ([left Int] [right Int]) -> Bool\n    (not= left right)))\n\n  ; Floats can be directly compared\n  (assert-native-compare-fn! (fn ([left Float] [right Float]) -> Bool\n    (= left right)))\n\n  ; Bools can be directly compared\n  (assert-native-compare-fn! (fn ([left Bool] [right Bool]) -> Bool\n    (= left right)))\n\n  ; Chars can be directly compared\n  (assert-native-compare-fn! (fn ([left Char] [right Char]) -> Bool\n    (= left right)))\n\n  ; These should be optimised away entirely\n  (assert-fn-doesnt-contain-op! :reg-op (fn ([v Bool]) -> Bool\n    (= true v)))\n\n  (assert-fn-doesnt-contain-op! :reg-op (fn ([v Bool]) -> Bool\n    (= v true)))\n\n  ; Syms can be directly compared\n  (assert-native-compare-fn! (fn ([left Sym] [right Sym]) -> Bool\n    (= left right)))\n\n  ; Fns can be constantly compared because they're always inequal\n  (assert-fn-returns-constant! (fn ([left (... -> Any)] [right (... -> Any)]) -> Bool\n    (= left right)))\n\n  ; Records can be compared fieldwise\n  (letrecord [OneField (one-field [one Int])]\n    (assert-native-compare-fn! (fn ([left OneField] [right OneField]) -> Bool\n      (= left right))))\n\n  ; If one field compares false the entire comparison is false\n  (letrecord [TwoField (two-field [one Int] [two Bool])]\n    (assert-fn-returns-constant! (fn ([left Int] [right Int]) -> Bool\n      (= (two-field left true) (two-field right false))))))"
  },
  {
    "path": "compiler/tests/optimise/inliner.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn recursive-member? ([item Any] [l (List & Any)]) -> Bool\n  (if (nil? l)\n    false\n    (or\n      (= item (first l))\n      (recursive-member? item (rest l)))))\n\n(defn infinite-loop () -> Bool\n  (infinite-loop))\n\n(defn main! () ->! ()\n  ; We should be able to evaluate this at compile time\n  ; This is recursive but does not exceed our inline limit and every iteration makes progress\n  (assert-fn-doesnt-contain-op! :call (fn ()\n    (recursive-member? \"dog\" '(\"cat\" \"dog\" \"fish\"))))\n\n  ; This does not make progress; we should compile this in to a loop\n  (assert-fn-contains-op! :call (fn ()\n    (infinite-loop))))"
  },
  {
    "path": "compiler/tests/optimise/list.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  ; This should just need to load the length from the cell\n  (assert-fn-doesnt-contain-op! :call (fn ([l (List & Any)]) -> Int\n    (length l)))\n\n  ; We should know this is a constant value\n  (assert-fn-doesnt-contain-op! :mem-load (fn ([l (List & Any)]) -> Int\n    (if (nil? l) (length l) 0)))\n\n  ; This should come from the type\n  (assert-fn-doesnt-contain-op! :mem-load (fn ([l (List Any Any Any)]) -> Int\n    (length l))))"
  },
  {
    "path": "compiler/tests/optimise/math.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  ; This should just pass the value through directly\n  (assert-fn-doesnt-contain-op! :call (fn ([n Num])\n    (+ (* n))))\n\n  ;\n  ; These should all be converted to MIR ops\n  ;\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Int] [right Int])\n    (+ left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Int] [right Float])\n    (+ left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Float] [right Float])\n    (+ left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Float] [right Num])\n    (+ left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Int] [right Int])\n    (* left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Int] [right Float])\n    (* left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Float] [right Float])\n    (* left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Float] [right Num])\n    (* left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([value Int])\n    (- value)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([value Float])\n    (- value)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Int] [right Int])\n    (- left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Int] [right Float])\n    (- left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Float] [right Float])\n    (- left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Float] [right Num])\n    (- left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([value Float])\n    (/ value)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Float] [right Float])\n    (/ left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Int] [right Int])\n    (quot left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([left Int] [right Int])\n    (rem left right)))\n\n  (assert-fn-doesnt-contain-op! :call (fn ([radicand Float])\n    (sqrt radicand)))\n\n  ())"
  },
  {
    "path": "compiler/tests/optimise/number.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  ; This should be the identity function for `Int`\n  (assert-fn-doesnt-contain-op! :call (fn ([i Int])\n    (int i)))\n\n  ; This should be the identity function for `Float`\n  (assert-fn-doesnt-contain-op! :call (fn ([f Float])\n    (float f)))\n\n  ; We can build specific MIR ops for these\n  (assert-fn-doesnt-contain-op! :call (fn ([i Int])\n    (float i)))\n  (assert-fn-doesnt-contain-op! :call (fn ([n Num])\n    (float n)))\n\n  ; Single arguments should be optimised to true for comparisons\n  (assert-fn-doesnt-contain-op! :call (fn ([i Int])\n    (< i)))\n  (assert-fn-doesnt-contain-op! :call (fn ([n Num])\n    (<= n)))\n  (assert-fn-doesnt-contain-op! :call (fn ([f Float])\n    (== f)))\n  (assert-fn-doesnt-contain-op! :call (fn ([i Int])\n    (>= i)))\n  (assert-fn-doesnt-contain-op! :call (fn ([f Float])\n    (> f)))\n\n  ; These can generate MIR ops after testing the parameter's type\n  (assert-fn-doesnt-contain-op! :call zero?)\n  (assert-fn-doesnt-contain-op! :call neg?)\n  (assert-fn-doesnt-contain-op! :call pos?)\n\n  ; These can generate comparison MIR ops\n  (assert-fn-doesnt-contain-op! :call (fn ([i1 Int] [i2 Int])\n    (< i1 i2)))\n  (assert-fn-doesnt-contain-op! :call (fn ([f1 Float] [i2 Int])\n    (<= f1 i2)))\n  (assert-fn-doesnt-contain-op! :call (fn ([i1 Int] [f2 Float])\n    (== i1 f2)))\n  (assert-fn-doesnt-contain-op! :call (fn ([f1 Float] [f2 Float])\n    (>= f1 f2)))\n  (assert-fn-doesnt-contain-op! :call (fn ([i1 Int] [f2 Float] [i3 Int])\n    (> i1 f2 i3))))"
  },
  {
    "path": "compiler/tests/optimise/typred.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn test-record-ty-preds! () ->! ()\n  (letrecord [RecordOne (record-one)]\n    ; We should only need to test the type tag here\n    ; `:cond` would indicate we're also attempting to load the record class ID\n    (assert-fn-doesnt-contain-op! :cond (fn ([sub (U RecordOne false)]) -> Bool\n      (record-one? sub)))))\n\n(defn main! () ->! ()\n  (test-record-ty-preds!))"
  },
  {
    "path": "compiler/tests/optimise/vector.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  ; This should just need to load the length from the cell\n  (assert-fn-doesnt-contain-op! :call (fn ([v (Vectorof Any)]) -> Int\n    (vector-length v)))\n\n  ; This should come from the type\n  (assert-fn-doesnt-contain-op! :call (fn ([v (Vector Any Any Any)]) -> Int\n    (vector-length v)))\n    \n  ; For a vector of known length we should be able to read directly\n  (assert-fn-doesnt-contain-op! :call (fn ([v (Vector Int Int Int)]) -> Int\n    (vector-ref v 2))))"
  },
  {
    "path": "compiler/tests/run-error/impure-panic.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  (panic! \"Impure \" \\p \\a (black-box! \\n) (black-box! \\i) \\c (black-box! \\!)))"
  },
  {
    "path": "compiler/tests/run-error/impure-panic.stderr",
    "content": "Impure panic!\n"
  },
  {
    "path": "compiler/tests/run-error/infinite-to-int.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  (black-box! (int (black-box! ##-Inf)))\n  ())"
  },
  {
    "path": "compiler/tests/run-error/infinite-to-int.stderr",
    "content": "Float value `-inf` is infinite; cannot convert to Int\n"
  },
  {
    "path": "compiler/tests/run-error/nan-to-int.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  (black-box! (int (black-box! ##NaN)))\n  ())"
  },
  {
    "path": "compiler/tests/run-error/nan-to-int.stderr",
    "content": "Float value `NaN` is not a number; cannot convert to Int\n"
  },
  {
    "path": "compiler/tests/run-error/overflow-add.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(def maximum-int 9223372036854775807)\n\n(defn main! () ->! ()\n  (black-box! (+ (black-box! maximum-int) 1))\n  ())"
  },
  {
    "path": "compiler/tests/run-error/overflow-add.stderr",
    "content": "attempt to add with overflow\n"
  },
  {
    "path": "compiler/tests/run-error/overflow-multiply.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(def maximum-int 9223372036854775807)\n\n(defn main! () ->! ()\n  (black-box! (* (black-box! maximum-int) 2))\n  ())"
  },
  {
    "path": "compiler/tests/run-error/overflow-multiply.stderr",
    "content": "attempt to multiply with overflow\n"
  },
  {
    "path": "compiler/tests/run-error/overflow-quot.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(def minimum-int -9223372036854775808)\n\n(defn main! () ->! ()\n  (black-box! (quot (black-box! minimum-int) -1))\n  ())"
  },
  {
    "path": "compiler/tests/run-error/overflow-quot.stderr",
    "content": "division by zero\n"
  },
  {
    "path": "compiler/tests/run-error/overflow-subtract.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(def minimum-int -9223372036854775808)\n\n(defn main! () ->! ()\n  (black-box! (- (black-box! minimum-int) 1))\n  ())"
  },
  {
    "path": "compiler/tests/run-error/overflow-subtract.stderr",
    "content": "attempt to subtract with overflow\n"
  },
  {
    "path": "compiler/tests/run-error/pure-panic.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  (panic \"Pure \" \\p \\a (black-box \\n) (black-box \\i) \\c (black-box \\!)))\n"
  },
  {
    "path": "compiler/tests/run-error/pure-panic.stderr",
    "content": "Pure panic!\n"
  },
  {
    "path": "compiler/tests/run-error/quot-by-zero.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  (black-box! (quot 1 (black-box! 0)))\n  ())"
  },
  {
    "path": "compiler/tests/run-error/quot-by-zero.stderr",
    "content": "division by zero\n"
  },
  {
    "path": "compiler/tests/run-error/rem-by-zero.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn main! () ->! ()\n  (black-box! (rem 1 (black-box! 0)))\n  ())"
  },
  {
    "path": "compiler/tests/run-error/rem-by-zero.stderr",
    "content": "division by zero\n"
  },
  {
    "path": "compiler/tests/run-pass/application.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n; Applying a fun with correct polymorphic purity inside a pure context\n(def _ (filter zero? '(0 1 2 3)))\n\n(defn take-exactly-three (_ _ _) false)\n(defn return-rest (& x) x)\n\n(defn invert-pred #{[->_ ->!] T} ([pred (T ->_ Bool)] [input T]) ->_ Bool\n  (false? (pred input)))\n\n(defn wrapped-every? #{[->_ ->!] T} ([pred (T ->_ Bool)] [lst (List & T)]) ->_ Bool\n  ; This is forcing `(every?)` to have the same polymorphic purity as the outer function\n  (every? pred lst))\n\n(defn main! () ->! ()\n  ; Stress test various ways of passing arguments\n  (assert-eq! false (take-exactly-three 1 2 3))\n  (assert-eq! false (take-exactly-three & '(1 2 3)))\n  (assert-eq! false (take-exactly-three 1 2 & '(3)))\n\n  ; Make sure we can figure out this is a `(Listof Int)`\n  (let [l (concat & '((1 2 3) (4 5)))]\n    (assert-eq! '(1 2 3 4 5) l)\n    (ann l (List & Int)))\n\n  ; Make sure we select the return type correctly\n  (let [[ret-str Str] (identity \"Hello polymorphism!\")]\n    (assert-eq! \"Hello polymorphism!\" ret-str))\n\n  ; Make sure we can return our rest argument\n  (let [[rest-list (List & Any)] (return-rest 1 2 3)]\n    (assert-eq! '(1 2 3) rest-list))\n\n  ; Treating functions as first-class values\n  (assert-eq! '(1 2 3) ((black-box return-rest) 1 2 3))\n\n  ; Make sure we can apply functions with polymorphic purity\n  (assert-eq! false (invert-pred int? 5))\n\n  ; Polymorphic purity Rust fun apply inside a polymorphic purity Arret fun\n  (assert-eq! true (wrapped-every? (fn (_) true) '(1 2 3))))"
  },
  {
    "path": "compiler/tests/run-pass/binding.arret",
    "content": "(import [stdlib base])\n\n; Ensure values can be passed through a multi-binding let\n(def [_ :original]\n  (let [x :original\n        y x\n        z y]\n    z))\n\n; We should be able to infer the type for rest bindings\n(def (& x) '(1 2 3))\n(def [_ (List & Int)] x)\n\n(defn main! ())"
  },
  {
    "path": "compiler/tests/run-pass/bitwise.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn test-bit-and! () ->! ()\n  (assert-eq! 8 (bit-and 12 9))\n  (assert-eq! 4 (bit-and (black-box! 12) 21))\n  (assert-eq! 0 ((black-box! bit-and) 9 12 21)))\n\n(defn test-bit-or! () ->! ()\n  (assert-eq! 13 (bit-or 12 9))\n  (assert-eq! 29 (bit-or 12 (black-box! 21)))\n  (assert-eq! 29 (bit-or (black-box! 9) 12 (black-box! 21))))\n\n(defn test-bit-xor! () ->! ()\n  (assert-eq! 5 (bit-xor 12 9))\n  (assert-eq! 25 (bit-xor (black-box! 12) 21))\n  (assert-eq! 16 (bit-xor (black-box! 9) (black-box! 12) (black-box! 21))))\n\n(defn test-bit-not! () ->! ()\n  (assert-eq! -13 (bit-not 12))\n  (assert-eq! 21 (bit-not (black-box! -22))))\n\n(defn test-bit-shift-left! () ->! ()\n  (assert-eq! 24 (bit-shift-left 12 1))\n  (assert-eq! 84 (bit-shift-left 21 (black-box! 2))))\n\n(defn test-bit-shift-right! () ->! ()\n  (assert-eq! 6 (bit-shift-right 12 1))\n  (assert-eq! -6 (bit-shift-right (black-box! -22) 2)))\n\n(defn test-unsigned-bit-shift-right! () ->! ()\n  (assert-eq! 6 (unsigned-bit-shift-right 12 1))\n  (assert-eq! 4611686018427387898 (unsigned-bit-shift-right (black-box! -22) 2)))\n\n(defn main! () ->! ()\n  (test-bit-and!)\n  (test-bit-or!)\n  (test-bit-xor!)\n  (test-bit-not!)\n  (test-bit-shift-left!)\n  (test-bit-shift-right!)\n  (test-unsigned-bit-shift-right!))"
  },
  {
    "path": "compiler/tests/run-pass/closure-typing.arret",
    "content": "(import [stdlib base])\n\n(defn direct-required-type ()\n  (let [[closure (Sym -> Sym)] (fn (x) x)]\n    (ann closure (Sym -> Sym))))\n\n(defn union-required-type ()\n  (let [[closure (U false (Sym -> Sym))] (fn (x) x)]\n    (ann closure (Sym -> Sym))))\n\n(defn main! ())"
  },
  {
    "path": "compiler/tests/run-pass/closure.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n; This is a monomorphic, fixed arity form of `(constantly)`\n(defn return-const (x) (fn () x))\n\n; This would have an inline record struct captures\n(defn return-two-int-values ([one Int] [two Int]) -> (-> (List & Int))\n  (fn () (list one two)))\n\n; This would have a external record struct captures\n(defn return-four-int-values ([one Int] [two Int] [three Int] [four Int]) -> (-> (List & Int))\n  (fn () (list one two three four)))\n\n; This recaptures a closure pointing to a value that's fallen out of scope\n(defn recapture-inner-temporary! () ->! ()\n  (black-box! (let\n    [captures-inner (let\n                     [inner (black-box! 4)]\n                     (fn () inner))]\n    (fn () captures-inner)))\n\n  ())\n\n(defn main! () ->! ()\n  (assert-eq! true (fn? (black-box! return-const)))\n  (assert-eq! 1 ((return-const 1)))\n  (assert-eq! 2 (((black-box! return-const) 2)))\n  (assert-eq! 3 ((return-const (black-box! 3))))\n  (assert-eq! 4 (((black-box! return-const) (black-box! 4))))\n\n  (assert-eq! true (fn? (black-box! constantly)))\n  (assert-eq! 1 ((constantly 1)))\n  (assert-eq! 2 (((black-box! constantly) 2)))\n  (assert-eq! 3 ((constantly (black-box! 3))))\n  (assert-eq! 4 (((black-box! constantly) (black-box! 4))))\n\n  (assert-eq! '(123 456) ((black-box!\n    (return-two-int-values (black-box! 123) (black-box! 456)))))\n\n  (assert-eq! '(1 2 3 4) ((black-box!\n    (return-four-int-values (black-box! 1) (black-box! 2) (black-box! 3) (black-box! 4)))))\n\n  (recapture-inner-temporary!))"
  },
  {
    "path": "compiler/tests/run-pass/comments.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n; This is a line comment\n\n#_\"This is an ignored form\"\n\n(defn main! () ->! ()\n  (assert-eq! '() (comment this is a macro that discards its body)))\n"
  },
  {
    "path": "compiler/tests/run-pass/conditionals.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn pos-neg-or-zero ([n Int]) -> (U 'positive 'negative 'zero)\n  (cond\n    (< n 0) 'negative\n    (> n 0) 'positive\n    :else 'zero))\n\n(defn test-cond! () ->! ()\n  (assert-eq! 'positive (pos-neg-or-zero 5))\n  (assert-eq! 'negative (pos-neg-or-zero -1))\n  (assert-eq! 'zero (pos-neg-or-zero 0))\n\n  (assert-eq! () (cond)))\n\n(defn main! () ->! ()\n  (test-cond!))"
  },
  {
    "path": "compiler/tests/run-pass/divergence.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n; This needs a `(do)` wrapper otherwise reverse type propagation will complain about `expr` not\n; returning `(U)` before fully evaluating it. Real code shouldn't be expecting `(U)` so it would be\n; unaffected.\n(defmacro assert-diverges (macro-rules\n [(expr) (ann (do expr '()) (U))]\n))\n\n; This isn't `(main!)` to make sure it doesn't acually run\n(defn compile-only ([x Bool])\n  ; Direct divergence\n  (assert-diverges (panic \"Hello\"))\n\n  ; Divergence within a `(do)`\n  (assert-diverges (do (panic \"(do)\") 1 2))\n\n  ; Divergence within `(if)` branches\n  (assert-diverges\n    (if x\n      (do (panic \"Left\") 'left)\n      (do (panic \"Right\") 'right)))\n\n  ; Divergence within `(if)` condition\n  (assert-diverges (if (panic \"Test\") 'left 'right))\n  (assert-diverges (if (do (panic \"Test\") true) 'left 'right))\n  (assert-diverges (if (do (panic \"Test\") false) 'left 'right))\n  (ann (if true 'left (panic \"Test\")) 'left)\n  (ann (if false (panic \"Test\") 'right) 'right)\n  (ann (if x 'other (panic \"Test\")) 'other)\n\n  ; Divergence within normal application\n  (assert-diverges (length (panic \"Normal apply\")))\n\n  ; Divergence within type predicate\n  (assert-diverges (str? (panic \"Type predicate apply\")))\n\n  ; Divergence within equality predicate\n  (assert-diverges (= true (panic \"Type predicate apply\")))\n\n  ; Divergence within `(let)`\n  (assert-diverges (let [_ (panic \"(let)\")])))\n\n(defn always-panics () -> (U)\n  (panic \"This panics!\"))\n\n(defn panics-inside-let () -> Int\n  (let [_ (panic \"This panics!\")])\n  5)\n\n(defn panics-inside-cond () -> Int\n  ; This is tricky because our typeck doesn't know this is constant but `eval_hir` does\n  (if (= 0 1)\n    ()\n    (panic \"HERE\"))\n  5)\n\n(defn panics-inside-app () -> (List & Any)\n  (list (panic \"HERE\") 2 3))\n\n(defn main! () ->! ()\n  ; Boxing these should not panic us\n  (black-box! panic)\n  (black-box! always-panics)\n  (black-box! panics-inside-let)\n  (black-box! panics-inside-cond)\n  (black-box! panics-inside-app)\n  \n  ())"
  },
  {
    "path": "compiler/tests/run-pass/empty.arret",
    "content": "(import [stdlib base])\n\n(defn main! ())"
  },
  {
    "path": "compiler/tests/run-pass/equality.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn arg-is-self-equal (arg) -> Bool\n  ; This is very tempting to simpify to `true` but that's not valid for NaN\n  (= arg arg))\n\n(defn num-arg-is-nan? #{[N Num]} ([arg N]) -> Bool\n  (and (float? arg) (not= arg arg)))\n\n(defn test-int-equality! () ->! ()\n  (assert-eq! (black-box! 1) (black-box! 1))\n  (assert-ne! (black-box! 1) (black-box! 2)))\n\n(defn test-bool-equality! () ->! ()\n  (assert-eq! (black-box! true) (black-box! true))\n  (assert-ne! (black-box! true) (black-box! false))\n  (assert-eq! (black-box! false) (black-box! false)))\n\n(defn test-sym-equality! () ->! ()\n  (assert-eq! (black-box-untyped! 'inline1) (black-box-untyped! 'inline1))\n  (assert-ne! (black-box-untyped! 'inline1) (black-box-untyped! 'inline2))\n\n  (assert-eq!\n    (black-box-untyped! 'definitely-out-of-line1)\n    (black-box-untyped! 'definitely-out-of-line1))\n\n  (assert-ne!\n    (black-box-untyped! 'definitely-out-of-line1)\n    (black-box-untyped! 'definitely-out-of-line2)))\n\n(defn test-list-equality! () ->! ()\n  (assert-eq! (black-box! ()) (black-box! ()))\n  (assert-ne! (black-box! ()) (black-box! '(1 2 3)))\n  (assert-eq! true ((black-box! =) (black-box! ()) (black-box! ())))\n\n  ; Same variable list containing NaN\n  (let [nan-list (black-box! '(##NaN))]\n    (assert-ne! nan-list nan-list)))\n\n(defn test-nan-equality! () ->! ()\n  (assert-eq! false (= ##NaN ##NaN))\n  (assert-eq! false ((black-box! =) ##NaN ##NaN))\n  (assert-eq! false (= (black-box! ##NaN) (black-box! ##NaN)))\n  (assert-eq! false ((black-box! =) (black-box! ##NaN) (black-box! ##NaN)))\n\n  (assert-eq! false (arg-is-self-equal (black-box! ##NaN)))\n  (assert-eq! true (num-arg-is-nan? (black-box! ##NaN))))\n\n(defn test-float-zero-equality! () ->! ()\n  (assert-eq! true (= 0.0 -0.0))\n  (assert-eq! true ((black-box! =) -0.0 0.0))\n  (assert-eq! true (= (black-box! 0.0) (black-box! -0.0)))\n  (assert-eq! true ((black-box! =) (black-box! -0.0) (black-box! 0.0))))\n\n(defn test-char-equality! () ->! ()\n  (assert-eq! (black-box! \\space) (black-box! \\space))\n  (assert-eq! \\space (black-box! \\space))\n  (assert-ne! (black-box! \\newline) \\space)\n  (assert-ne! (black-box! \\newline) (black-box! \\space)))\n\n; Make sure functions never compare equal\n(defn test-fn-equality! () ->! ()\n  ; Synthetic fun\n  (assert-ne! = =)\n  ; Rust fun\n  (assert-ne! + +)\n  ; Self fun\n  (assert-ne! test-fn-equality! test-fn-equality!)\n  ; Arret fun\n  (assert-ne! test-sym-equality! test-sym-equality!)\n  ; Same variable fun\n  (let [anon-fun (black-box! (fn ()))]\n    (assert-ne! anon-fun anon-fun))\n\n  (assert-eq! false ((black-box! =) = =))\n  (assert-eq! false ((black-box! =) + +))\n  (assert-eq! false ((black-box! =) test-fn-equality! test-fn-equality!))\n  (assert-eq! false ((black-box! =) test-sym-equality! test-sym-equality!)))\n\n(defn test-empty-record-equality! () ->! ()\n  (letrecord [EmptyOne (empty-one) EmptyTwo (empty-two)]\n    (assert-eq! (empty-one) (empty-one))\n    (assert-ne! (empty-one) (empty-two))\n    (assert-eq! (empty-two) (empty-two))\n    \n    (assert-eq! (empty-one) (black-box! (empty-one)))\n    (assert-ne! (empty-two) (black-box! (empty-one)))))\n\n(defn test-dynamic-record-equality! () ->! ()\n  (letrecord [OneField (one-field [one Int])]\n    (assert-ne! (one-field (black-box! 1)) (one-field (black-box! 2)))\n    (assert-eq! (one-field (black-box! 1)) (one-field (black-box! 1)))))\n\n(defn main! () ->! ()\n  (test-int-equality!)\n  (test-bool-equality!)\n  (test-sym-equality!)\n  (test-list-equality!)\n  (test-nan-equality!)\n  (test-float-zero-equality!)\n  (test-char-equality!)\n  (test-fn-equality!)\n  (test-empty-record-equality!)\n  (test-dynamic-record-equality!))"
  },
  {
    "path": "compiler/tests/run-pass/hash.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn assert-eq-hash! ([left Any] [right Any]) ->! ()\n  (assert-eq! (hash left) (hash right))\n  (assert-eq! (hash (black-box! left)) (hash right))\n  (assert-eq! (hash (black-box! left)) (hash (black-box! right))))\n\n(defn main! () ->! ()\n  ; This would be catastrophic for performance\n  (assert-ne! (hash true) (hash false))\n  (assert-ne! (hash false) (hash ()))\n\n  (assert-eq-hash! true true)\n  (assert-eq-hash! false false)\n\n  (assert-eq-hash! -1 -1)\n  (assert-eq-hash! 0 0)\n  (assert-eq-hash! 8 8)\n\n  (assert-eq-hash! 12.0 12.0)\n  (assert-eq-hash! ##Inf ##Inf)\n  (assert-eq-hash! ##-Inf ##-Inf)\n\n  ; Zero and negative zero are `=`\n  (assert-eq-hash! 0.0 -0.0)\n\n  (assert-eq-hash! 'small 'small)\n  (assert-eq-hash! 'very-long-out-of-line-symbol 'very-long-out-of-line-symbol)\n\n  (assert-eq-hash! \"Small\" \"Small\")\n  (assert-eq-hash! \"Very long out-of-line string\" \"Very long out-of-line string\")\n\n  (assert-eq-hash! () ())\n  (assert-eq-hash! '(1 2 3) '(1 2 3))\n\n  (assert-eq-hash! [] [])\n  (assert-eq-hash! [one two three] [one two three])\n\n  (assert-eq-hash! \\newline \\newline)\n  (assert-eq-hash! \\tab \\tab)\n\n  (letrecord [Record (record [field Int])]\n    (let [twelve-record (record 12)]\n      (assert-eq-hash! twelve-record twelve-record)))\n\n  ; Functions & NaN don't have a defined hash equality - don't test them\n)"
  },
  {
    "path": "compiler/tests/run-pass/list.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn test-length! () ->! ()\n  (assert-eq! 0 (length ()))\n  (assert-eq! 4 (length '(1 2 3 4)))\n  (assert-eq! 4 (length (black-box! '(1 2 3 4))))\n  (assert-eq! 3 (length (list 1 2 3))))\n\n(defn test-first-second-rest! () ->! ()\n  (assert-eq! 'one (ann (first '(one two three)) 'one))\n  (assert-eq! 'two (ann (second '(one two three)) 'two))\n  (assert-eq! '(two three) (ann (rest '(one two three)) (List & Sym)))\n  (assert-eq! () (ann (rest '(one)) '())))\n\n(defn test-cons! () ->! ()\n  (assert-eq! '(1) (ann (cons 1 '()) (List Int)))\n  (assert-eq! '((1 2) 3) (cons '(1 2) '(3))))\n\n(defn test-map! () ->! ()\n  (assert-eq! '() (ann (map (fn (x) x) '()) '()))\n  (assert-eq! '(1 2 3 4 5) (map (fn (x) x) '(1 2 3 4 5)))\n  (assert-eq! '(0 1 2 3) (map #(length %) '(() (1) (1 2) (1 2 3))))\n  (assert-eq! '(4 4 4) (map (constantly 4) '(1 2 3)))\n  (assert-eq! '(() () ()) (map (constantly ()) '(1 2 3)))\n  (assert-eq! '(true false true) (map #(= % \"yes\") '(\"yes\" \"no\" \"yes\")))\n  (assert-eq! '(false true false) (map #(not= % \"yes\") '(\"yes\" \"no\" \"yes\")))\n\n  ; This should be safe because `panic` won't be called\n  (map panic '()))\n\n(defn test-filter! () ->! ()\n  (assert-eq! '() (filter (constantly true) '()))\n  (assert-eq! '() (filter (constantly false) '()))\n\n  (assert-eq! '(1 2 3) (filter (constantly true) '(1 2 3)))\n  (assert-eq! '() (filter (constantly false) '(1 2 3)))\n\n  (assert-eq! '(true true) (filter identity '(true false true false)))\n\n  (assert-eq! '(3) (filter #(= % 3) '(1 2 3 4))))\n\n(defn test-some? () ->! ()\n  (assert-eq! false (some? (constantly true) '()))\n  (assert-eq! false (some? (constantly false) '()))\n\n  (assert-eq! true (some? (constantly true) '(1 2 3)))\n  (assert-eq! false (some? (constantly false) '(1 2 3)))\n\n  (assert-eq! true (some? identity '(true false true false)))\n\n  (assert-eq! true (some? #(= % 3) '(1 2 3 4))))\n\n(defn test-fold! () ->! ()\n  (assert-eq! 7 (fold + 1 '(1 2 3)))\n  (assert-eq! 5.0 (fold + -1.0 '(1.0 2.0 3.0))))\n\n(defn test-every? () ->! ()\n  (assert-eq! true (every? int? '()))\n  (assert-eq! true (every? int? '(1)))\n  (assert-eq! true (every? int? '(1 2)))\n  (assert-eq! false (every? int? '(1.0 2)))\n  (assert-eq! false (every? int? '(1 2.0))))\n\n(defn test-concat! () ->! ()\n  (assert-eq! '() (ann (concat) '()))\n  (assert-eq! '(1 2 3) (concat '(1 2 3)))\n  (assert-eq! '(1 2 3 4 5 6) (concat '(1 2 3) '() '(4 5 6))))\n\n(defn test-member! () ->! ()\n  (assert-eq! false (member? 1 '()))\n  (assert-eq! true (member? 1 '(1 2 3)))\n  (assert-eq! true (member? 1 '(3 2 1)))\n  (assert-eq! false (member? 1 '(4 5 6)))\n  (assert-eq! false (member? ##NaN '(1 2 ##NaN))))\n\n(defn test-threading! () ->! ()\n  (assert-eq! '(3 5 7 9)\n    (->> '(0 1 2 3 4)\n      ; Make sure these are run in the correct order\n      (map #(* % 2))\n      (filter #(not (zero? %)))\n      (map #(+ % 1)))))\n\n(defn test-take! () ->! ()\n  (assert-eq! '() (take -1 '(1 2 3)))\n  (assert-eq! '() (take 0 '(1 2 3)))\n  (assert-eq! '(1) (take 1 '(1 2 3)))\n  (assert-eq! '(1 2) (take 2 '(1 2 3)))\n  (assert-eq! '(1 2 3) (take 3 '(1 2 3)))\n  (assert-eq! '(1 2 3) (take 4 '(1 2 3))))\n\n(defn test-drop! () ->! ()\n  (assert-eq! '(1 2 3) (drop -1 '(1 2 3)))\n  (assert-eq! '(1 2 3) (drop 0 '(1 2 3)))\n  (assert-eq! '(2 3) (drop 1 '(1 2 3)))\n  (assert-eq! '(3) (drop 2 '(1 2 3)))\n  (assert-eq! '() (drop 3 '(1 2 3)))\n  (assert-eq! '() (drop 4 '(1 2 3))))\n\n(defn test-drop-last! () ->! ()\n  (assert-eq! '(1 2 3) (drop-last -1 '(1 2 3)))\n  (assert-eq! '(1 2 3) (drop-last 0 '(1 2 3)))\n  (assert-eq! '(1 2) (drop-last 1 '(1 2 3)))\n  (assert-eq! '(1) (drop-last 2 '(1 2 3)))\n  (assert-eq! '() (drop-last 3 '(1 2 3)))\n  (assert-eq! '() (drop-last 4 '(1 2 3))))\n\n(defn test-reverse! () ->! ()\n  (assert-eq! '() (reverse '()))\n  (assert-eq! '(3 2 1) (reverse '(1 2 3))))\n\n(defn test-nth! () ->! ()\n  (assert-eq! 1 (nth '(1 2 3) 0))\n  (assert-eq! 2 (nth '(1 2 3) 1))\n  (assert-eq! 3 (nth '(1 2 3) 2)))\n\n(defn test-repeat! () ->! ()\n  (assert-eq! '() (repeat -1 false))\n  (assert-eq! '() (repeat 0 true))\n  (assert-eq! '(one one one) (repeat 3 'one)))\n\n(defn main! () ->! ()\n  (test-length!)\n  (test-first-second-rest!)\n  (test-cons!)\n  (test-map!)\n  (test-filter!)\n  (test-some?)\n  (test-fold!)\n  (test-concat!)\n  (test-member!)\n  (test-threading!)\n  (test-take!)\n  (test-drop!)\n  (test-drop-last!)\n  (test-reverse!)\n  (test-nth!)\n  (test-repeat!))"
  },
  {
    "path": "compiler/tests/run-pass/macros.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n; Make sure _ discards the macro\n(defmacro _ (macro-rules))\n(defmacro _ (macro-rules))\n\n(defn main! () ->! ()\n  (letmacro [return-one (macro-rules [() 'one])]\n    (assert-eq! (return-one) 'one))\n\n  (letmacro [identity (macro-rules [(x) x])]\n    (assert-eq! (identity 'one) 'one))\n\n  (letmacro [swap (macro-rules [(x y) '(y x)])]\n    (assert-eq! (swap one two) '(two one)))\n\n  (letmacro [for (macro-rules [(x :in y) [x y]])]\n    (assert-eq! (for two :in one) [two one]))\n\n  (letmacro [return-ellipsis (macro-rules [() '(... ...)])]\n    (assert-eq! (return-ellipsis) '...))\n\n  (letmacro [list-third (macro-rules [(_ _ x) x])]\n    (assert-eq! (list-third 'one 'two 'three) 'three))\n\n  (letmacro [vector-second (macro-rules [([_ x _]) x])]\n    (assert-eq! (vector-second ['one 'two 'three]) 'two))\n\n  (letmacro [recurse (macro-rules [() 'end] [(_) (recurse)])]\n    (assert-eq! (recurse 1) 'end))\n\n  (letmacro [empty-set? (macro-rules [(#{}) true] [(#{_ ...}) false])]\n    (assert-eq! (empty-set? #{}) true)\n    (assert-eq! (empty-set? #{one}) false))\n\n  (letmacro [set->list (macro-rules [(#{v ...}) '(v ...)])]\n    (assert-eq! (set->list #{one two three}) '(one two three)))\n\n  (letmacro [num->name (macro-rules\n                         [(1) 'one-int]\n                         [(2) 'two-int]\n                         [(3) 'three-int]\n                         [(1.0) 'one-float]\n                         [(2.0) 'two-float]\n                         [(3.0) 'three-float]\n                         [(##NaN) 'not-a-number]\n                         [(_) 'no-match])]\n    (assert-eq! (num->name 1) 'one-int)\n    (assert-eq! (num->name 2) 'two-int)\n    (assert-eq! (num->name 3) 'three-int)\n    (assert-eq! (num->name 1.0) 'one-float)\n    (assert-eq! (num->name 2.0) 'two-float)\n    (assert-eq! (num->name 3.0) 'three-float)\n\n    ; NaNs never match\n    (assert-eq! (num->name ##NaN) 'no-match))\n\n  (letmacro [return-all (macro-rules [(values ...) '(values ...)])]\n    (assert-eq! (return-all one two three) '(one two three)))\n\n  (letmacro [mid (macro-rules [([_ vals ... _]) [true vals ... false]])]\n    (assert-eq! (mid [one two three four]) [true two three false]))\n\n  (letmacro [combine-lists (macro-rules [((l ...) (r ...)) [r ... l ...]])]\n    (assert-eq! (combine-lists (one two) (three four)) [three four one two]))\n\n  (letmacro [nested-patterns (macro-rules [((a b rest ...) ...) [(rest ... b a) ...]])]\n    (assert-eq!\n      (nested-patterns (one two three four) (five six))\n      [(three four two one) (six five)]))\n\n  (letmacro [rebind-same-ident (macro-rules [() (quote (quote 2))])]\n    (assert-eq! (rebind-same-ident) ''2))\n\n  ; This ensures that unbound idents (`x`) are hygienic\n  (letmacro [set-x-to-inner (macro-rules [(body) (let [x :inner] body)])]\n    (assert-eq! :outer (let [x :outer] (set-x-to-inner x))))\n\n  ; This ensures the bound idents (`+`) are hygenic\n  (letmacro [set-+-to-* (macro-rules [(body) (let [+ *] body)])]\n    (assert-eq! 2 (set-+-to-* (+ 1 1))))\n\n  ; Use the `(... ident)` literal syntax\n  ; This uses `&` as it's a common candidate for a literal it could be an arbitrary symbol\n  (letmacro [match-literal-& (macro-rules\n                              [((... &)) '(... literal-ampersand)]\n                              [(&) '(... var-ampersand)])]\n    (assert-eq! 'literal-ampersand (match-literal-& &))\n    (assert-eq! 'var-ampersand (match-literal-& 1))))\n"
  },
  {
    "path": "compiler/tests/run-pass/math.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn test-add! () ->! ()\n  (assert-eq! 4 (+ 4))\n\n  (assert-eq! 7 (+ 4 (black-box! 3)))\n  (assert-eq! 7.0 (+ (black-box! 4.0) 3))\n  (assert-eq! 7.0 (+ (black-box! 4) (black-box! 3.0)))\n  (assert-eq! 7.0 (+ 4.0 3.0))\n\n  (assert-eq! true (nan? (+ ##NaN)))\n  (assert-eq! true (nan? (+ 1.0 (black-box! ##NaN)))))\n\n(defn test-mul! () ->! ()\n  (assert-eq! 4 (* (black-box! 4)))\n\n  (assert-eq! 12 (* (black-box! 4) 3))\n  (assert-eq! 12.0 (* (black-box! 4.0) (black-box! 3)))\n  (assert-eq! 12.0 (* 4 3.0))\n  (assert-eq! 12.0 (* 4.0 3.0))\n\n  (assert-eq! true (nan? (* (black-box! ##NaN))))\n  (assert-eq! true (nan? (* 1.0 ##NaN))))\n\n(defn test-sub! () ->! ()\n  (assert-eq! -3.0 (- 3.0))\n  (assert-eq! 3 (- (black-box! -3)))\n\n  (assert-eq! 4 (- (black-box! 7) 3))\n  (assert-eq! 4.0 (- 7 (black-box! 3.0)))\n  (assert-eq! 4.0 (- (black-box! 7.0) (black-box! 3)))\n  (assert-eq! 4.0 (- 7.0 3.0))\n\n  (assert-eq! true (nan? (- (black-box! ##NaN))))\n  (assert-eq! true (nan? (- 1.0 ##NaN))))\n\n(defn test-div! () ->! ()\n  (assert-eq! 1.0 (/ 1.0))\n  (assert-eq! 0.5 (/ (black-box! 2.0)))\n  (assert-eq! 0.25 (/ 1.0 (black-box! 2.0) 2.0))\n\n  (assert-eq! true (nan? (/ ##NaN)))\n  (assert-eq! true (nan? (/ (black-box! 1.0) ##NaN))))\n\n(defn test-inc-dec! () ->! ()\n  (assert-eq! -1 (inc -2))\n  (assert-eq! 0 (inc (black-box! -1)))\n  (assert-eq! 1 (inc 0))\n  (assert-eq! 2 (inc (black-box! 1)))\n\n  (assert-eq! -2 (dec (black-box! -1)))\n  (assert-eq! -1 (dec 0))\n  (assert-eq! 0 (dec (black-box! 1)))\n  (assert-eq! 1 (dec 2)))\n\n(defn test-rem-mod! () ->! ()\n  (assert-eq! 3 (quot 10 (black-box! 3)))\n  (assert-eq! 1 (rem (black-box! 10) 3))\n\n  (assert-eq! 3 (quot (black-box! 11) (black-box! 3)))\n  (assert-eq! 2 (rem 11 3))\n\n  (assert-eq! 3 (quot 11 (black-box! 3)))\n  (assert-eq! 2 (rem (black-box! 11) 3))\n\n  (assert-eq! -3 (quot (black-box! -10) (black-box! 3)))\n  (assert-eq! -1 (rem -10 3)))\n\n(defn test-sqrt! () ->! ()\n  (assert-eq! 0.0 (sqrt 0.0))\n  (assert-eq! 0.0 (sqrt (black-box! 0.0)))\n\n  (assert-eq! 1.0 (sqrt 1.0))\n  (assert-eq! 1.0 (sqrt (black-box! 1.0)))\n\n  (assert-eq! 2.0 (sqrt 4.0))\n  (assert-eq! 4.0 (black-box! (sqrt 16.0)))\n\n  (assert-eq! true (nan? (sqrt ##-Inf)))\n  (assert-eq! true (nan? (sqrt (black-box! ##-Inf))))\n\n  (assert-eq! true (nan? (sqrt -1.0)))\n  (assert-eq! true (nan? (sqrt (black-box! -100.0))))\n\n  (assert-eq! ##Inf (sqrt ##Inf))\n  (assert-eq! ##Inf (sqrt (black-box! ##Inf))))\n\n(defn main! () ->! ()\n  (test-add!)\n  (test-mul!)\n  (test-sub!)\n  (test-div!)\n  (test-inc-dec!)\n  (test-rem-mod!)\n  (test-sqrt!))\n"
  },
  {
    "path": "compiler/tests/run-pass/number.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn test-zero? () ->! ()\n  (assert-eq! true (zero? 0))\n  (assert-eq! true (zero? (black-box! 0.0)))\n  (assert-eq! false (zero? ##NaN))\n\n  (assert-eq! false (zero? 10))\n  (assert-eq! false (zero? (black-box! 10.0)))\n  (assert-eq! false (zero? (black-box! ##Inf)))\n  (assert-eq! false (zero? ##-Inf)))\n\n(defn test-pos? () ->! ()\n  (assert-eq! false (pos? 0))\n  (assert-eq! false (pos? (black-box! 0.0)))\n  (assert-eq! false (pos? ##NaN))\n\n  (assert-eq! true (pos? 10))\n  (assert-eq! false (pos? (black-box! -10.0)))\n  (assert-eq! true (pos? (black-box! ##Inf)))\n  (assert-eq! false (pos? ##-Inf)))\n\n(defn test-neg? () ->! ()\n  (assert-eq! false (neg? 0))\n  (assert-eq! false (neg? (black-box! 0.0)))\n  (assert-eq! false (neg? ##NaN))\n\n  (assert-eq! false (neg? 10))\n  (assert-eq! true (neg? (black-box! -10.0)))\n  (assert-eq! false (neg? (black-box! ##Inf)))\n  (assert-eq! true (neg? ##-Inf)))\n\n(defn test-nan? () ->! ()\n  (assert-eq! false (nan? 10.0))\n  (assert-eq! true (nan? (black-box! ##NaN)))\n  (assert-eq! true (nan? ##NaN))\n  (assert-eq! false (nan? ##Inf))\n  (assert-eq! false (nan? (black-box! ##-Inf))))\n\n(defn test-infinite? () ->! ()\n  (assert-eq! false (infinite? 3.0))\n  (assert-eq! true (infinite? (black-box! ##Inf)))\n  (assert-eq! true (infinite? ##-Inf))\n  (assert-eq! false (infinite? ##NaN)))\n\n(defn test-float! () ->! ()\n  (assert-eq! 1.0 (float 1))\n  (assert-eq! 0.0 (float (black-box! 0)))\n  (assert-eq! -1.0 (float -1))\n\n  (assert-eq! 1.0 (float (black-box! 1.0)))\n  (assert-eq! 0.0 (float 0.0))\n  (assert-eq! -1.0 (float -1.0)))\n\n(defn test-int! () ->! ()\n  (assert-eq! 1 (int (black-box! 1)))\n  (assert-eq! 0 (int 0))\n  (assert-eq! -1 (int -1))\n\n  (assert-eq! 1 (int 1.0))\n  (assert-eq! 0 (int (black-box! 0.0)))\n  (assert-eq! -1 (int -1.0)))\n\n(defn test-comparisons! () ->! ()\n  ; These are all always true\n  (assert-eq! true (< -1))\n  (assert-eq! true (<= (black-box! 0)))\n  (assert-eq! true (== 1.0))\n  (assert-eq! true (> (black-box! -1.0)))\n  (assert-eq! true (>= ##NaN))\n\n  ; NaNs always compare false\n  (assert-eq! false (< ##NaN (black-box! ##NaN)))\n  (assert-eq! false (<= (black-box! ##NaN) ##NaN))\n  (assert-eq! false (== (black-box! ##NaN) (black-box! ##NaN)))\n  (assert-eq! false (> ##NaN ##NaN))\n  (assert-eq! false (>= ##NaN ##NaN))\n\n  (assert-eq! true (< 1 2.0 (black-box! 3)))\n  (assert-eq! true (<= 1 (black-box! 2.0) 3))\n  (assert-eq! false (== 1 (black-box! 2.0) (black-box! 3)))\n  (assert-eq! false (> (black-box! 1) 2.0 3))\n  (assert-eq! false (>= (black-box! 1) 2.0 (black-box! 3)))\n\n  (assert-eq! false (< 1 (black-box! 2) (black-box! 2.0)))\n  (assert-eq! true (<= (black-box! 1) (black-box! 2) (black-box! 2.0)))\n  (assert-eq! false (== 1 2 2.0))\n  (assert-eq! false (> 1 2 (black-box! 2.0)))\n  (assert-eq! false (>= 1 (black-box! 2) 2.0))\n\n  (assert-eq! false (< (black-box! 1) (black-box! 1.0)))\n  (assert-eq! true (<= 1 (black-box! 1.0)))\n  (assert-eq! true (== (black-box! 1) 1.0))\n  (assert-eq! false (> (black-box! 1) (black-box! 1.0)))\n  (assert-eq! true (>= 1 1.0))\n\n  (assert-eq! false (< ##Inf (black-box! ##-Inf)))\n  (assert-eq! false (<= (black-box! ##Inf) ##-Inf))\n  (assert-eq! false (== (black-box! ##Inf) (black-box! ##-Inf)))\n  (assert-eq! true (> ##Inf (black-box! ##-Inf)))\n  (assert-eq! true (>= (black-box! ##Inf) ##-Inf))\n\n  (assert-eq! false (< ##Inf (black-box! ##-Inf) (black-box! ##-Inf)))\n  (assert-eq! false (<= (black-box! ##Inf) ##-Inf ##-Inf))\n  (assert-eq! false (== (black-box! ##Inf) (black-box! ##-Inf) (black-box! ##-Inf)))\n  (assert-eq! false (> ##Inf ##-Inf (black-box! ##-Inf)))\n  (assert-eq! true (>= ##Inf ##-Inf ##-Inf)))\n\n(defn test-even-odd! () ->! ()\n  (assert-eq! false (even? -1))\n  (assert-eq! true (odd? -1))\n\n  (assert-eq! true (even? 0))\n  (assert-eq! false (odd? 0))\n\n  (assert-eq! false (even? 1))\n  (assert-eq! true (odd? 1))\n\n  (assert-eq! true (even? 2))\n  (assert-eq! false (odd? 2)))\n\n(defn test-min-max! () ->! ()\n  (assert-eq! 0 (min 0))\n  (assert-eq! 0 (max 0))\n\n  (assert-eq! true (nan? (min ##NaN)))\n  (assert-eq! true (nan? (max ##NaN)))\n\n  (assert-eq! true (nan? (float (min ##NaN 0))))\n  (assert-eq! true (nan? (float (min 0 ##NaN))))\n  (assert-eq! true (nan? (float (max ##NaN 0))))\n  (assert-eq! true (nan? (float (max 0 ##NaN))))\n\n  (assert-eq! 1 (min 1 2 3))\n  (assert-eq! 3 (max 1 2 3))\n\n  (assert-eq! 1.0 (min 3.0 2 1.0))\n  (assert-eq! 3.0 (max 3.0 2 1.0))\n\n  (assert-eq! ##-Inf (min ##-Inf ##Inf))\n  (assert-eq! ##Inf (max ##-Inf ##Inf)))\n\n(defn main! () ->! ()\n  (test-zero?)\n  (test-pos?)\n  (test-neg?)\n  (test-nan?)\n  (test-infinite?)\n  (test-float!)\n  (test-int!)\n  (test-comparisons!)\n  (test-even-odd!)\n  (test-min-max!))"
  },
  {
    "path": "compiler/tests/run-pass/occurrence-typing.arret",
    "content": "(import [stdlib base])\n\n(defn test-type-in-branches ([test Bool])\n  (if test\n    (ann test true)\n    (ann test false)))\n\n(defn trivial-type-predicate ([input (U Sym Str)])\n  (if (sym? input)\n    (ann input Sym)\n    (ann input Str)))\n\n(defn non-literal-equality-predicate ([input (U Int Float)])\n  (if (= input 123)\n    (ann input Int)\n    (ann input (U Int Float))))\n\n(defn literal-equality-predicate ([input (U 'foo 'bar)])\n  (if (= input 'foo)\n    (ann input 'foo)\n    (ann input 'bar)))\n\n(defn two-var-equality-predicate ([left (U 'foo 'bar)] [right (U 'bar 'baz)])\n  (when (= left right)\n    (ann left 'bar)\n    (ann right 'bar)))\n\n(defn let-preserves-type-information ([input Bool])\n  (if (let [_ ()] 'foo input)\n    (ann input true)\n    (ann input false)))\n\n(defn bool-equality-with-true ([input Num])\n  (if (= true (int? input))\n    (ann input Int)\n    (ann input Float))\n  (if (= (int? input) true)\n    (ann input Int)\n    (ann input Float)))\n\n(defn bool-equality-with-false ([input Num])\n  (if (= false (int? input))\n    (ann input Float)\n    (ann input Int))\n  (if (= (int? input) false)\n    (ann input Float)\n    (ann input Int)))\n\n(defn same-var-and-typing ([lst (U (List & Any) false)]) -> (U Any false)\n  (if (and (list? lst) (not (nil? lst)))\n    (first lst)\n    false))\n\n(defn two-var-and-typing ([left Num] [right Num])\n  (when (and (int? left) (float? right))\n    (ann left Int)\n    (ann right Float)))\n\n(defn cond-typing ([input Num])\n  (cond\n    (int? input) (ann input Int)\n    (float? input) (ann input Float)))\n\n(defn or-typing ([input (U Sym Str Int)])\n  (if (or (sym? input) (str? input))\n    (ann input (U Sym Str))\n    (ann input (U Sym Str Int))))\n\n(defn not-typing ([input (U Sym Str)])\n  (if-not (str? input)\n    (ann input Sym)\n    (ann input Str)))\n\n(defn partial-not-typing ([input (U Sym Str)] [other-bool Bool])\n  ; This doesn't correspond to a logical operation such as `(and)` or `(not)`; it's just tested for\n  ; completeness\n  (when (if (str? input) false other-bool)\n    (ann input Sym)))\n\n(defn unbounded-generic-typing #{T} ([input T]) -> T\n  (if (str? input)\n    (ann input Str)\n    input))\n\n(defn bounded-generic-typing #{[T Num]} ([input T]) -> T\n  (if (float? input)\n    (ann input Float)\n    (ann input Int)))\n\n(defn main! ())"
  },
  {
    "path": "compiler/tests/run-pass/read.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn test-read-str! () ->! ()\n  (assert-eq! 1 (read-str \"1\"))\n\n  (assert-eq! -1.5 (read-str \"-1.5\"))\n  (assert-eq! ##Inf (read-str \"##Inf\"))\n  (assert-eq! ##-Inf (read-str \"##-Inf\"))\n\n  (let [read-nan (read-str \"##NaN\")]\n    (assert-eq! true (and (float? read-nan) (nan? read-nan))))\n\n  (assert-eq! 'tiny (read-str \"tiny\"))\n  (assert-eq! 'large-so-it-wont-intern-inline (read-str \"large-so-it-wont-intern-inline\"))\n\n  ; The `black-box!` forces us to re-intern these at runtime\n  (assert-eq! 'large-so-it-wont-intern-inline (read-str (black-box! \"large-so-it-wont-intern-inline\")))\n  (assert-ne! 'large-so-it-wont-intern-inline (read-str (black-box! \"new-large-symbol-that-didnt-appear-at-compile-time\")))\n\n  (assert-eq! \"tiny\" (read-str \"\\\"tiny\\\"\"))\n\n  (assert-eq! \"Really quite large so it wont fit inside a cell\"\n    (read-str \"\\\"Really quite large so it wont fit inside a cell\\\"\"))\n\n  (assert-eq! \"Really quite large so it wont fit inside a cell\"\n    (read-str (black-box! \"\\\"Really quite large so it wont fit inside a cell\\\"\")))\n\n  (assert-eq! '() (read-str \"()\"))\n  (assert-eq! '(1 2 3) (read-str \"(1 2 3)\"))\n\n  (assert-eq! [] (read-str \"[]\"))\n  (assert-eq! [one two three] (read-str \"[one two three]\"))\n\n  (assert-eq! \\newline (read-str \"\\\\newline\"))\n  (assert-eq! \\λ (read-str \"\\\\λ\")))\n\n(defn main! () ->! ()\n  (test-read-str!))"
  },
  {
    "path": "compiler/tests/run-pass/record.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn test-poly-records! () ->! ()\n  (letrecord [(Record A) (record [poly A] [int Int] any)]\n    (let [test-record (record 'foo 2 'bar)]\n      (assert-eq! 'foo (ann (record-poly test-record) 'foo))\n      (assert-eq! 2 (ann (record-int test-record) Int))\n      (assert-eq! 'bar (ann (record-any test-record) Any))\n      \n      (assert-eq! 'foo (ann ((black-box! record-poly) test-record) 'foo))\n      (assert-eq! 2 (ann ((black-box! record-int) test-record) Int))\n      (assert-eq! 'bar (ann ((black-box! record-any) test-record) Any)))))\n\n(defn test-bool-record-fields! () ->! ()\n  (letrecord [Record (record [field Bool])]\n    (let [true-record (record true)\n          false-record (record false)\n          black-box-true-record ((black-box! record) true)\n          black-box-false-record ((black-box! record) false)]\n\n      (assert-eq! false (record-field false-record))\n      (assert-eq! true (record-field true-record))\n\n      (assert-eq! false (record-field black-box-false-record))\n      (assert-eq! true (record-field black-box-true-record))\n\n      (assert-eq! true-record true-record)\n      (assert-eq! true-record black-box-true-record)\n\n      (assert-ne! true-record false-record)\n      (assert-ne! true-record black-box-false-record)\n      \n      (assert-eq! false ((black-box! record-field) false-record))\n      (assert-eq! true ((black-box! record-field) true-record)))))\n\n(defn test-int-record-fields! () ->! ()\n  (letrecord [Record (record [field Int])]\n    (let [twelve-record (record 12)]\n      (assert-eq! 12 (record-field twelve-record))\n      (assert-eq! 12 (record-field (black-box! twelve-record)))\n      (assert-eq! 12 ((black-box! record-field) twelve-record))\n\n      (assert-eq! twelve-record twelve-record)\n      (assert-eq! (black-box! twelve-record) (black-box! twelve-record))\n\n      ; Using a pure `(black-box)` tests that we can codegen record constants based on boxed values\n      (assert-eq! (black-box! twelve-record) (black-box twelve-record)))))\n\n(defn test-char-record-fields! () ->! ()\n  (letrecord [Record (record [field Char])]\n    (let [newline-record (record \\newline)]\n      (assert-eq! \\newline (record-field newline-record))\n      (assert-eq! \\newline (record-field (black-box! newline-record)))\n      (assert-eq! \\newline ((black-box! record-field) newline-record))\n\n      (assert-eq! newline-record newline-record)\n      (assert-eq! (black-box! newline-record) (black-box! newline-record)))))\n\n(defn test-float-record-fields! () ->! ()\n  (letrecord [Record (record [field Float])]\n    (let [fourteen-record (record 14.0)]\n      (assert-eq! 14.0 (record-field fourteen-record))\n      (assert-eq! 14.0 (record-field (black-box! fourteen-record)))\n      (assert-eq! 14.0 ((black-box! record-field) fourteen-record))\n\n      (assert-eq! fourteen-record fourteen-record)\n      (assert-eq! (black-box! fourteen-record) (black-box! fourteen-record)))))\n\n(defn test-sym-record-fields! () ->! ()\n  (letrecord [Record (record [field Sym])]\n    (let [foo-record (record 'foo)]\n      (assert-eq! 'foo (record-field foo-record))\n      (assert-eq! 'foo (record-field (black-box! foo-record)))\n      (assert-eq! 'foo (black-box! (record-field (black-box! foo-record))))\n      (assert-eq! 'foo ((black-box! record-field) foo-record))\n\n      (assert-eq! foo-record foo-record)\n      (assert-eq! (black-box! foo-record) (black-box! foo-record)))))\n\n(defn test-boxed-record-fields! () ->! ()\n  (letrecord [Record (record [field Any])]\n    (let [list-record (record '(1 2 3))]\n      (assert-eq! '(1 2 3) (record-field list-record))\n      (assert-eq! '(1 2 3) (record-field (black-box! list-record)))\n      (assert-eq! '(1 2 3) ((black-box! record-field) list-record))\n\n      (assert-eq! list-record list-record)\n      (assert-eq! (black-box! list-record) (black-box! list-record)))))\n\n(defn test-external-record! () ->! ()\n  (letrecord [Record (record [first Char] [second Float] [third Int] [fourth Sym] [fifth Bool])]\n    (let [external-record (record \\tab 2.0 3 'four true)]\n      (assert-eq! \\tab (record-first external-record))\n      (assert-eq! \\tab (record-first (black-box! external-record)))\n\n      (assert-eq! 2.0 (record-second external-record))\n      (assert-eq! 2.0 (record-second (black-box! external-record)))\n\n      (assert-eq! 3 (record-third external-record))\n      (assert-eq! 3 (record-third (black-box! external-record)))\n\n      (assert-eq! 'four (record-fourth external-record))\n      (assert-eq! 'four (record-fourth (black-box! external-record)))\n\n      (assert-eq! true (record-fifth external-record))\n      (assert-eq! true (record-fifth (black-box! external-record)))\n\n      (assert-eq! external-record external-record)\n      (assert-eq! (black-box! external-record) (black-box! external-record))\n      (assert-eq! (black-box! external-record) (black-box external-record))\n\n      ; Force this to be allocated on the heap\n      (let [heap-external-record ((black-box! record) \\tab 2.0 (black-box! 3) 'four true)]\n        (assert-eq! external-record heap-external-record)))))\n\n; This makes sure we implement records and first-class functions correctly\n(defn test-record-function-callbacks! () ->! ()\n  (letrecord [Record (record [field Int])]\n    (assert-eq! '(1 2 3)\n      (->>\n        '(1 2 3)\n        (map record)\n        (map record-field)))\n\n    (assert-eq! '(4 5 6)\n      (->>\n        '(4 5 6)\n        (map (black-box! record))\n        (map (black-box! record-field))))\n\n    (assert-eq! '(7 8 9)\n      (->>\n        (black-box! '(7 8 9))\n        (map record)\n        (map record-field)))\n\n    (assert-eq! '(10 11 12)\n      (->>\n        '(10 11 12)\n        ((black-box! map) record)\n        ((black-box! map) record-field)))))\n\n(defn main! () ->! ()\n  (test-poly-records!)\n  (test-bool-record-fields!)\n  (test-int-record-fields!)\n  (test-char-record-fields!)\n  (test-float-record-fields!)\n  (test-sym-record-fields!)\n  (test-boxed-record-fields!)\n  (test-external-record!)\n  (test-record-function-callbacks!))"
  },
  {
    "path": "compiler/tests/run-pass/recursion.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn recursive-reverse #{T} ([lst (List & T)]) -> (List & T)\n  (if (nil? lst)\n    lst\n    (concat (recursive-reverse (rest lst)) (list (first lst)))))\n\n(defn even-length? ([l (List & Any)]) -> Bool\n  (if (nil? l)\n    true\n    (let [tail (rest l)]\n      (if (nil? tail)\n        false\n        (recur (rest tail))))))\n\n; This has a polymorphic purity\n(defn recursive-every? #{T [->_ ->!]} ([pred (T ->_ Bool)] [l (List & T)]) ->_ Bool\n  (if (nil? l)\n    true\n    (if (pred (first l))\n      (recur pred (rest l))\n      false)))\n\n(defn wrapped-recursive-every? #{[->_ ->!] T} ([pred (T ->_ Bool)] [lst (List & T)]) ->_ Bool\n  ; This is forcing `(recursive-every?)` to have the same polymorphic purity as the outer function\n  (recursive-every? pred lst))\n\n; This makes sure `(recur)` works properly capturing an outer variable\n(defn return-infinite-print ([output Str]) -> (->! ())\n  (fn () ->! ()\n    (println! output)\n    (recur)))\n\n(defn main! () ->! ()\n  (assert-eq! '() ((black-box! recursive-reverse) '()))\n  (assert-eq! '(()) ((black-box! recursive-reverse) (black-box! '(()))))\n  (assert-eq! '(\"three\" \"two\" \"one\") (recursive-reverse (black-box! '(\"one\" \"two\" \"three\"))))\n  (assert-eq! '(7 6 5 4 3 2 1) (recursive-reverse '(1 2 3 4 5 6 7)))\n\n  (assert-eq! true (even-length? '()))\n  (assert-eq! false (even-length? (black-box! '(1))))\n  (assert-eq! true ((black-box! even-length?) '(1 2)))\n  (assert-eq! false ((black-box! even-length?) (black-box! '(1 2 3))))\n  (assert-eq! true (even-length? '(1 2 3 4)))\n\n  (assert-eq! true (recursive-every? int? '(1 2 3)))\n  (assert-eq! false (recursive-every? int? '(1 2.0 3)))\n  (assert-eq! true ((black-box! recursive-every?) int? '(1 2 3)))\n  (assert-eq! false ((black-box! recursive-every?) int? '(1 2.0 3)))\n  (assert-eq! true (recursive-every? (black-box! int?) '(1 2 3)))\n  (assert-eq! false (recursive-every? (black-box! int?) '(1 2.0 3)))\n  (assert-eq! true (recursive-every? int? (black-box! '(1 2 3))))\n  (assert-eq! false (recursive-every? int? (black-box! '(1 2.0 3))))\n\n  (assert-eq! true (wrapped-recursive-every? (fn (_) true) '(1 2 3)))\n\n  ; Just building this function would previously crash\n  (assert-eq! true (fn? (black-box! return-infinite-print)))\n\n  ; We should select an ABI for `even-length?` which doesn't require allocaton\n  (let [black-box-list (black-box! '(1 2 3 4 5))\n        (alloc-count is-even) (heap-alloc-count (fn () (even-length? black-box-list)))]\n    (assert-eq! 0 alloc-count)\n    (assert-eq! false is-even))\n\n  ())\n"
  },
  {
    "path": "compiler/tests/run-pass/set.arret",
    "content": "(import [stdlib base])\n(import [stdlib set])\n(import [stdlib test])\n\n(defn test-set! () ->! ()\n  (assert-eq! #{} (set))\n  (assert-eq! #{1} (set 1))\n\n  (assert-eq! #{1 2} ((black-box! set) 1 2))\n  (assert-eq! #{1 2} ((black-box! set) 1 1 2 2))\n\n  (assert-eq! #{1 2 3} (set 1 2 (black-box! 3)))\n  (assert-eq! #{1 2 3} (set (black-box! 3) 2 1)))\n\n(defn test-set-contains! () ->! ()\n  (assert-eq! false (set-contains? #{1 2 3} 0))\n  (assert-eq! true ((black-box! set-contains?) #{1 2 3} 1))\n  (assert-eq! true (set-contains? (black-box! #{1 2 3}) 2))\n\n  ; ##NaN never compares equal\n  (assert-eq! false (set-contains? (set ##NaN ##NaN ##NaN) ##NaN)))\n\n(defn test-set-length! () ->! ()\n  (assert-eq! 0 (set-length #{}))\n  (assert-eq! 1 ((black-box! set-length) #{1}))\n  (assert-eq! 2 (set-length (black-box! #{1 2})))\n  (assert-eq! 3 ((black-box! set-length) (black-box! #{1 2 3})))\n\n  ; Functions never compare equal\n  (assert-eq! 3 (set-length (set zero? zero? zero?))))\n\n(defn test-set->list! () ->! ()\n  (assert-eq! '() (set->list #{}))\n  (assert-eq! '(1) (set->list #{1}))\n  (assert-eq! #{1 2} (set & ((black-box! set->list) #{1 2})))\n  (assert-eq! #{1 2 3} ((black-box! set) & (set->list #{1 2 3}))))\n\n(defn test-subset! () ->! ()\n  (assert-eq! true (subset? #{} #{1 2 3}))\n  (assert-eq! true (subset? #{1 3} #{1 2 3}))\n  (assert-eq! true (subset? #{1 2 3} #{1 2 3}))\n  (assert-eq! false (subset? #{1 2 3 4} #{1 2 3}))\n  (assert-eq! false (subset? #{##NaN} #{##NaN})))\n\n(defn test-superset! () ->! ()\n  (assert-eq! true (superset? #{'one 'two 'three} #{}))\n  (assert-eq! true (superset? #{'one 'two 'three} #{'one 'three}))\n  (assert-eq! true (superset? #{'one 'two 'three} #{'one 'two 'three}))\n  (assert-eq! false (superset? #{'one 'two 'three} #{'one 'two 'three 'four}))\n  (assert-eq! false (superset? #{##NaN} #{##NaN})))\n\n(defn main! () ->! ()\n  (test-set!)\n  (test-set-contains!)\n  (test-set-length!)\n  (test-set->list!)\n  (test-subset!)\n  (test-superset!))"
  },
  {
    "path": "compiler/tests/run-pass/type-definitions.arret",
    "content": "(import [stdlib base])\n\n; Make sure _ eats the type\n(deftype _ Int)\n(deftype _ Float)\n\n(defn takes-int ([x Int])\n  (lettype [Integer Int]\n    (ann x Integer)))\n\n(defn main! ())"
  },
  {
    "path": "compiler/tests/run-pass/typred.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn test-tagged-ty-preds! () ->! ()\n  (assert-eq! true (any? (black-box-untyped! \"foo\")))\n  (assert-eq! true (any? (black-box-untyped! 'foo)))\n  (assert-eq! true (any? (black-box-untyped! :foo)))\n  (assert-eq! true (any? (black-box-untyped! 1)))\n  (assert-eq! true (any? (black-box-untyped! 2.0)))\n\n  (assert-eq! true (str? (black-box-untyped! \"foo\")))\n  (assert-eq! false (str? (black-box-untyped! 123)))\n\n  (assert-eq! false (sym? (black-box-untyped! \"foo\")))\n  (assert-eq! true (sym? (black-box-untyped! 'foo)))\n  (assert-eq! true (sym? (black-box-untyped! :foo)))\n\n  (assert-eq! true (true? (black-box-untyped! true)))\n  (assert-eq! false (true? (black-box-untyped! false)))\n  (assert-eq! false (true? (black-box-untyped! 123)))\n\n  (assert-eq! false (false? (black-box-untyped! true)))\n  (assert-eq! true (false? (black-box-untyped! false)))\n  (assert-eq! false (false? (black-box-untyped! 123)))\n\n  (assert-eq! true (bool? (black-box-untyped! true)))\n  (assert-eq! false (bool? (black-box-untyped! 123)))\n\n  (assert-eq! true (num? (black-box-untyped! 123)))\n  (assert-eq! true (num? (black-box-untyped! -456.7)))\n  (assert-eq! false (num? (black-box-untyped! \"twelve\")))\n\n  (assert-eq! true (int? (black-box-untyped! 123)))\n  (assert-eq! false (int? (black-box-untyped! false)))\n\n  (assert-eq! true (fn? (black-box-untyped! cons)))\n  (assert-eq! false (fn? (black-box-untyped! 123)))\n\n  (assert-eq! true (list? (black-box-untyped! '())))\n  (assert-eq! true (list? (black-box-untyped! '(1 2 3))))\n  (assert-eq! false (list? (black-box-untyped! 123)))\n\n  (assert-eq! true (nil? (black-box-untyped! '())))\n  (assert-eq! false (nil? (black-box-untyped! '(1 2 3))))\n  (assert-eq! false (nil? (black-box-untyped! 123)))\n\n  (assert-eq! true ((black-box! str?) (black-box-untyped! \"foo\")))\n  (assert-eq! false ((black-box! str?) (black-box-untyped! 123)))\n\n  (assert-eq! true (set? (black-box-untyped! #{})))\n  (assert-eq! false (set? (black-box-untyped! {})))\n\n  (assert-eq! true (map? (black-box-untyped! {})))\n  (assert-eq! false (map? (black-box-untyped! #{})))\n\n  ; Make sure type predicates can be treated as first-class functions\n  (assert-eq! '(true false true) (map int? '(1 2.0 3)))\n  (assert-eq! '(true false true) (map (black-box! int?) '(1 2.0 3)))\n  (assert-eq! '(true false true) ((black-box! map) int? '(1 2.0 3))))\n\n(defn test-record-ty-preds! () ->! ()\n  (letrecord [RecordOne (record-one) RecordTwo (record-two)]\n    (let [record-one-instance (record-one)\n          record-two-instance (record-two)\n          record-number (fn ([r (U RecordOne RecordTwo)]) -> Int\n            (if (record-one? r) 1 2))]\n\n      (assert-eq! true (record? record-one-instance))\n      (assert-eq! true (record? (black-box! record-one-instance)))\n\n      (assert-eq! true (record-one? record-one-instance))\n      (assert-eq! false (record-two? record-one-instance))\n      (assert-eq! false (record-one? 123))\n\n      (assert-eq! true (record-one? (black-box-untyped! record-one-instance)))\n      (assert-eq! false (record-two? (black-box-untyped! record-one-instance)))\n      (assert-eq! false (record-two? (black-box-untyped! 123)))\n\n      (assert-eq! 1 (record-number (black-box! record-one-instance)))\n      (assert-eq! 2 (record-number (black-box! record-two-instance))))))\n\n(defn main! () ->! ()\n  (test-tagged-ty-preds!)\n  (test-record-ty-preds!))"
  },
  {
    "path": "compiler/tests/run-pass/vector.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n; This is our node size + 1\n; It's represented as a persistent vector with a tree node and 1 element tail node\n(def thirty-three-element-vector\n  [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33])\n\n(defn test-vector! () ->! ()\n  ; Inline vectors\n  (assert-eq! [] (vector))\n  (assert-eq! [1] (vector 1))\n  (assert-eq! [1 2] ((black-box! vector) 1 2))\n  (assert-eq! [1 2 3] (vector 1 2 (black-box! 3)))\n\n  ; Tail-only vectors\n  (assert-eq! [1 2 3 4] (vector 1 2 3 4))\n  (assert-eq! [1 2 3 4 5] ((black-box! vector) 1 2 3 4 5))\n\n  ; Tail & tree vectors\n  ; This is testing constant vector generation as well as `(vector!)`\n  (assert-eq! thirty-three-element-vector\n    ((black-box! vector) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33)))\n\n(defn test-vector-ref! () ->! ()\n  ; Inline vectors\n  (assert-eq! 1 (vector-ref [1 2 3] 0))\n  (assert-eq! 2 ((black-box! vector-ref) [1 2 3] 1))\n  (assert-eq! 3 (vector-ref (black-box! [1 2 3]) 2))\n\n  ; Tail-only vectors\n  (assert-eq! 4 (vector-ref (black-box! [1 2 3 4]) 3))\n  (assert-eq! 5 ((black-box! vector-ref) [1 2 3 4 5] 4))\n  (assert-eq! 6 ((black-box! vector-ref) (black-box! [1 2 3 4 5 6]) 5))\n\n  ; Tail & tree vector\n  (assert-eq! 1 (vector-ref (black-box! thirty-three-element-vector) 0))\n  (assert-eq! 32 ((black-box! vector-ref) thirty-three-element-vector 31))\n  (assert-eq! 33 (vector-ref (black-box! thirty-three-element-vector) 32)))\n\n(defn test-vector-length! () ->! ()\n  (assert-eq! 0 (vector-length []))\n  (assert-eq! 1 ((black-box! vector-length) [1]))\n  (assert-eq! 2 (vector-length (black-box! [1 2])))\n  (assert-eq! 3 ((black-box! vector-length) (black-box! [1 2 3])))\n  (assert-eq! 4 ((black-box! vector-length) (black-box! [1 2 3 4]))))\n\n(defn test-vector->list! () ->! ()\n  (assert-eq! '(1) (vector->list [1]))\n  (assert-eq! '(1 2) ((black-box! vector->list) [1 2]))\n  (assert-eq! '(1 2 3) (vector->list (black-box! [1 2 3])))\n  (assert-eq! '(1 2 3 4) ((black-box! vector->list) (black-box! [1 2 3 4]))))\n\n(defn test-vector-extend! () ->! ()\n  (assert-eq! [1 2 3 4 5 6]\n    (vector-extend\n      (vector-extend [1 2 3] 4) 5 6)))\n\n(defn test-vector-assoc! () ->! ()\n  ; Inline vector\n  (assert-eq! [one two three]\n    (vector-assoc\n      (vector-assoc [two two two] 0 'one)\n      2 'three))\n\n  ; External vector\n  (assert-eq! [false false false false false]\n    (vector-assoc [false false true false false] 2 false)))\n\n(defn test-vector-append! () ->! ()\n  (assert-eq! [] (vector-append))\n\n  (assert-eq! [1 2 3] (vector-append [] [1 2 3]))\n  (assert-eq! [1 2 3] (vector-append [1] [2 3]))\n  (assert-eq! [1 2 3] (vector-append [1 2] [3]))\n  (assert-eq! [1 2 3 4 5 6] (vector-append [1 2 3] [4 5 6]))\n\n  (assert-eq! [1 2 3 4 5 6 7 8] (vector-append\n    (black-box! [1 2 3])\n    [4 5 6 7]\n    (black-box! [8])))\n\n  (assert-eq! [1 2 3] (vector-append [1] [2] [3])))\n\n(defn test-vector-take! () ->! ()\n  (assert-eq! []\n    (vector-take\n      -1\n      (black-box! [1 2 3 4 5 6])))\n\n  (assert-eq! []\n    (vector-take\n      (black-box! 0)\n      [1 2 3 4 5 6]))\n\n  (assert-eq! [1 2 3]\n    (vector-take\n      (black-box! 3)\n      (black-box! [1 2 3 4 5 6])))\n\n  (assert-eq! [1 2 3 4 5]\n    ((black-box! vector-take)\n    5\n    [1 2 3 4 5 6]))\n\n  (assert-eq! [1 2 3 4 5 6]\n    ((black-box! vector-take)\n    6\n    (black-box! [1 2 3 4 5 6])))\n\n  (assert-eq! [1 2 3 4 5 6]\n    ((black-box! vector-take)\n    (black-box! 100)\n    (black-box! [1 2 3 4 5 6]))))\n\n(defn main! () ->! ()\n  (test-vector!)\n  (test-vector-ref!)\n  (test-vector-length!)\n  (test-vector->list!)\n  (test-vector-assoc!)\n  (test-vector-extend!)\n  (test-vector-append!)\n  (test-vector-take!))"
  },
  {
    "path": "compiler/tests/run-pass/write.arret",
    "content": "(import [stdlib base])\n(import [stdlib test])\n\n(defn test-write-to-stdout! () ->! ()\n  ; This is dumb but it makes us produce a binary\n  (print! \"\")\n\n  ; We should be able to call through a thunk as well\n  ((black-box! print!) \"\")\n\n  ; And through a callback\n  (map print! '(\"\"))\n\n  ; And print an unknown value\n  (print! (black-box! \"\"))\n\n  ; And do both at once\n  ((black-box! print!) (black-box! \"\")))\n\n(defn test-write-to-str! () ->! ()\n  (assert-eq! \"\" (print-str))\n  (assert-eq! \"\" (write-str))\n\n  (assert-eq! \"hello\" (print-str 'hello))\n  (assert-eq! \"hello\" (write-str 'hello))\n\n  (assert-eq! \"Hello, world!\" (print-str \"Hello, world!\"))\n  (assert-eq! \"\\\"Hello, world!\\\"\" (write-str \"Hello, world!\"))\n\n  (assert-eq! \" \" (print-str \\space))\n  (assert-eq! \"\\\\space\" (write-str \\space))\n\n  (assert-eq! \"123456\" (print-str 1 2 (black-box! 3) (black-box! 4) 5 (black-box! 6)))\n  (assert-eq! \"1 2 3 4 5 6\" (write-str 1 2 (black-box! 3) (black-box! 4) 5 (black-box! 6)))\n\n  (assert-eq! \"#fn#fn#fn\" (print-str + (black-box +) (black-box! +)))\n  (assert-eq! \"#fn #fn #fn\" (write-str + (black-box +) (black-box! +))))\n\n(defn main! () ->! ()\n  (test-write-to-stdout!)\n  (test-write-to-str!))"
  },
  {
    "path": "compiler/ty/conv_abi.rs",
    "content": "use arret_runtime::callback;\nuse arret_runtime::{abitype, boxed};\n\nuse crate::ty;\nuse crate::ty::purity::Purity;\nuse crate::ty::Ty;\n\nfn type_tag_to_ty<M: ty::Pm>(type_tag: boxed::TypeTag) -> Ty<M> {\n    use arret_runtime::boxed::TypeTag;\n\n    match type_tag {\n        TypeTag::Float => Ty::Float,\n        TypeTag::Char => Ty::Char,\n        TypeTag::Str => Ty::Str,\n        TypeTag::Sym => Ty::Sym,\n        TypeTag::True => Ty::LitBool(true),\n        TypeTag::False => Ty::LitBool(false),\n        TypeTag::Int => Ty::Int,\n        TypeTag::Vector => Ty::Vectorof(Box::new(Ty::Any.into())),\n        TypeTag::Nil => ty::List::empty().into(),\n        TypeTag::Pair => ty::List::new(Box::new([Ty::Any.into()]), Ty::Any.into()).into(),\n        TypeTag::FunThunk => ty::TopFun::new(Purity::Impure.into(), Ty::Any.into()).into(),\n        TypeTag::Record => Ty::TopRecord,\n        TypeTag::Set => Ty::Set(Box::new(Ty::Any.into())),\n        TypeTag::Map => Ty::Map(Box::new(ty::Map {\n            key: Ty::Any.into(),\n            value: Ty::Any.into(),\n        })),\n    }\n}\n\npub trait ConvertableAbiType {\n    fn to_ty_ref<M: ty::Pm>(&self) -> ty::Ref<M>;\n    fn to_rust_str(&self) -> String;\n}\n\nimpl ConvertableAbiType for abitype::AbiType {\n    fn to_ty_ref<M: ty::Pm>(&self) -> ty::Ref<M> {\n        use arret_runtime::abitype::AbiType;\n\n        match self {\n            AbiType::Bool => Ty::Bool.into(),\n            AbiType::Char => Ty::Char.into(),\n            AbiType::Float => Ty::Float.into(),\n            AbiType::Int => Ty::Int.into(),\n            AbiType::InternedSym => Ty::Sym.into(),\n            AbiType::Boxed(boxed) => boxed.to_ty_ref(),\n            AbiType::Callback(entry_point_abi) => entry_point_abi.to_ty_ref(),\n        }\n    }\n\n    fn to_rust_str(&self) -> String {\n        use arret_runtime::abitype::AbiType;\n\n        match self {\n            AbiType::Bool => \"bool\".to_owned(),\n            AbiType::Char => \"char\".to_owned(),\n            AbiType::Float => \"f64\".to_owned(),\n            AbiType::Int => \"i64\".to_owned(),\n            AbiType::InternedSym => \"InternedSym\".to_owned(),\n            AbiType::Boxed(boxed) => format!(\"Gc<{}>\", boxed.to_rust_str()),\n            AbiType::Callback(entry_point_abi) => entry_point_abi.to_rust_str(),\n        }\n    }\n}\n\nimpl ConvertableAbiType for abitype::BoxedAbiType {\n    fn to_ty_ref<M: ty::Pm>(&self) -> ty::Ref<M> {\n        use arret_runtime::abitype::BoxedAbiType;\n\n        match self {\n            BoxedAbiType::Any => Ty::Any.into(),\n            BoxedAbiType::Vector(member) => Ty::Vectorof(Box::new(member.to_ty_ref())).into(),\n            BoxedAbiType::Set(member) => Ty::Set(Box::new(member.to_ty_ref())).into(),\n            BoxedAbiType::Map(key, value) => Ty::Map(Box::new(ty::Map {\n                key: key.to_ty_ref(),\n                value: value.to_ty_ref(),\n            }))\n            .into(),\n            BoxedAbiType::List(member) => ty::List::new_uniform(member.to_ty_ref()).into(),\n            BoxedAbiType::Pair(member) => {\n                let member_ty_ref: ty::Ref<M> = member.to_ty_ref();\n                ty::List::new(Box::new([member_ty_ref.clone()]), member_ty_ref).into()\n            }\n            BoxedAbiType::UniqueTagged(type_tag) => type_tag_to_ty(*type_tag).into(),\n            BoxedAbiType::Union(_, tags) => {\n                let members = tags.iter().map(|type_tag| type_tag_to_ty(*type_tag).into());\n\n                ty::unify::unify_ty_ref_iter(members)\n            }\n        }\n    }\n\n    fn to_rust_str(&self) -> String {\n        use arret_runtime::abitype::BoxedAbiType;\n\n        match self {\n            BoxedAbiType::Any => \"boxed::Any\".to_owned(),\n            BoxedAbiType::Vector(member) => format!(\"boxed::Vector<{}>\", member.to_rust_str()),\n            BoxedAbiType::List(member) => format!(\"boxed::List<{}>\", member.to_rust_str()),\n            BoxedAbiType::Pair(member) => format!(\"boxed::Pair<{}>\", member.to_rust_str()),\n            BoxedAbiType::Set(member) => format!(\"boxed::Set<{}>\", member.to_rust_str()),\n            BoxedAbiType::UniqueTagged(type_tag) => format!(\"boxed::{}\", type_tag.to_str()),\n            BoxedAbiType::Union(name, _) => format!(\"boxed::{}\", name),\n            BoxedAbiType::Map(key, value) => {\n                format!(\"boxed::Map<{}, {}>\", key.to_rust_str(), value.to_rust_str())\n            }\n        }\n    }\n}\n\nimpl ConvertableAbiType for abitype::RetAbiType {\n    fn to_ty_ref<M: ty::Pm>(&self) -> ty::Ref<M> {\n        use arret_runtime::abitype::RetAbiType;\n\n        match self {\n            RetAbiType::Void => Ty::unit().into(),\n            RetAbiType::Never => Ty::never().into(),\n            RetAbiType::Inhabited(abi_type) => abi_type.to_ty_ref(),\n        }\n    }\n\n    fn to_rust_str(&self) -> String {\n        use arret_runtime::abitype::RetAbiType;\n\n        match self {\n            RetAbiType::Void => \"()\".to_owned(),\n            RetAbiType::Never => \"Never\".to_owned(),\n            RetAbiType::Inhabited(abi_type) => abi_type.to_rust_str(),\n        }\n    }\n}\n\nimpl ConvertableAbiType for abitype::ParamAbiType {\n    fn to_ty_ref<M: ty::Pm>(&self) -> ty::Ref<M> {\n        self.abi_type.to_ty_ref()\n    }\n\n    fn to_rust_str(&self) -> String {\n        use arret_runtime::abitype::AbiType;\n        use arret_runtime::abitype::ParamCapture;\n\n        match &self.abi_type {\n            AbiType::Boxed(boxed) => match self.capture {\n                ParamCapture::Auto => format!(\"Gc<{}>\", boxed.to_rust_str()),\n                ParamCapture::Never => format!(\"NoCapture<{}>\", boxed.to_rust_str()),\n                ParamCapture::Always => format!(\"Capture<{}>\", boxed.to_rust_str()),\n            },\n            other => other.to_rust_str(),\n        }\n    }\n}\n\nimpl ConvertableAbiType for callback::EntryPointAbiType {\n    fn to_ty_ref<M: ty::Pm>(&self) -> ty::Ref<M> {\n        // TODO: How do we deal with rest params?\n        let fixed_param_ty_refs = self\n            .params\n            .iter()\n            .map(ConvertableAbiType::to_ty_ref)\n            .collect();\n\n        ty::Fun::new_mono(\n            ty::List::new_tuple(fixed_param_ty_refs),\n            Purity::Impure.into(),\n            self.ret.to_ty_ref(),\n        )\n        .into()\n    }\n\n    fn to_rust_str(&self) -> String {\n        let params_str = if self.params.is_empty() {\n            \"\".to_owned()\n        } else {\n            self.params\n                .iter()\n                .map(|abi_type| format!(\", {}\", abi_type.to_rust_str()))\n                .collect::<Vec<String>>()\n                .join(\"\")\n        };\n\n        format!(\n            \"extern \\\"C\\\" fn(&mut Task, boxed::Captures{}) -> {}\",\n            params_str,\n            self.ret.to_rust_str()\n        )\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn pair_abi_type() {\n        use arret_runtime::abitype::EncodeBoxedAbiType;\n        use arret_runtime::boxed;\n\n        let boxed_abi_type = <boxed::Pair<boxed::Int> as EncodeBoxedAbiType>::BOXED_ABI_TYPE;\n\n        assert_eq!(\"boxed::Pair<boxed::Int>\", boxed_abi_type.to_rust_str());\n\n        let int_pair_poly: ty::Ref<ty::Poly> =\n            ty::List::new(Box::new([Ty::Int.into()]), Ty::Int.into()).into();\n\n        assert_eq!(int_pair_poly, boxed_abi_type.to_ty_ref());\n    }\n\n    #[test]\n    fn bool_abi_type() {\n        use arret_runtime::abitype::EncodeBoxedAbiType;\n        use arret_runtime::boxed;\n\n        let boxed_abi_type = <boxed::Bool as EncodeBoxedAbiType>::BOXED_ABI_TYPE;\n\n        assert_eq!(\"boxed::Bool\", boxed_abi_type.to_rust_str());\n\n        let bool_poly: ty::Ref<ty::Poly> = Ty::Bool.into();\n        assert_eq!(bool_poly, boxed_abi_type.to_ty_ref());\n    }\n\n    #[test]\n    fn nil_abi_type() {\n        use arret_runtime::abitype::EncodeBoxedAbiType;\n        use arret_runtime::boxed;\n\n        let boxed_abi_type = <boxed::Nil as EncodeBoxedAbiType>::BOXED_ABI_TYPE;\n\n        assert_eq!(\"boxed::Nil\", boxed_abi_type.to_rust_str());\n\n        let nil_poly: ty::Ref<ty::Poly> = ty::List::empty().into();\n        assert_eq!(nil_poly, boxed_abi_type.to_ty_ref());\n    }\n\n    #[test]\n    fn callback_abi_type() {\n        use arret_runtime::task;\n\n        let entry_point_abi_type = <extern \"C\" fn(&mut task::Task, boxed::Captures, i64) -> char as callback::EncodeEntryPointAbiType>::ENTRY_POINT_ABI_TYPE;\n\n        assert_eq!(\n            \"extern \\\"C\\\" fn(&mut Task, boxed::Captures, i64) -> char\",\n            entry_point_abi_type.to_rust_str()\n        );\n\n        let arret_poly: ty::Ref<ty::Poly> = ty::Fun::new_mono(\n            ty::List::new_tuple(Box::new([Ty::Int.into()])),\n            Purity::Impure.into(),\n            Ty::Char.into(),\n        )\n        .into();\n\n        assert_eq!(arret_poly, entry_point_abi_type.to_ty_ref());\n    }\n\n    #[test]\n    fn captured_int_abi_type() {\n        use arret_runtime::abitype::{EncodeBoxedAbiType, ParamAbiType};\n        use arret_runtime::boxed;\n\n        let param_abi_type = ParamAbiType {\n            abi_type: <boxed::Int as EncodeBoxedAbiType>::BOXED_ABI_TYPE.into(),\n            capture: abitype::ParamCapture::Always,\n        };\n\n        assert_eq!(\"Capture<boxed::Int>\", param_abi_type.to_rust_str());\n\n        let int_poly: ty::Ref<ty::Poly> = Ty::Int.into();\n        assert_eq!(int_poly, param_abi_type.to_ty_ref());\n    }\n}\n"
  },
  {
    "path": "compiler/ty/datum.rs",
    "content": "use arret_syntax::datum::Datum;\n\nuse crate::ty;\nuse crate::ty::Ty;\n\npub fn ty_ref_for_datum<M: ty::Pm>(datum: &Datum) -> ty::Ref<M> {\n    (match datum {\n        Datum::Bool(_, val) => Ty::LitBool(*val),\n        Datum::Sym(_, val) => Ty::LitSym(val.clone()),\n        Datum::Char(_, _) => Ty::Char,\n        Datum::Int(_, _) => Ty::Int,\n        Datum::Float(_, _) => Ty::Float,\n        Datum::Str(_, _) => Ty::Str,\n        Datum::List(_, vs) => {\n            ty::List::new_tuple(vs.iter().map(|datum| ty_ref_for_datum(datum)).collect()).into()\n        }\n        Datum::Vector(_, vs) => Ty::Vector(vs.iter().map(|v| ty_ref_for_datum(v)).collect()),\n        Datum::Set(_, vs) => {\n            let unified_type = ty::unify::unify_ty_ref_iter(vs.iter().map(|v| ty_ref_for_datum(v)));\n            Ty::Set(Box::new(unified_type))\n        }\n        Datum::Map(_, vs) => {\n            let unified_key =\n                ty::unify::unify_ty_ref_iter(vs.iter().map(|(k, _)| ty_ref_for_datum(k)));\n\n            let unified_value =\n                ty::unify::unify_ty_ref_iter(vs.iter().map(|(_, v)| ty_ref_for_datum(v)));\n\n            ty::Map::new(unified_key, unified_value).into()\n        }\n    })\n    .into()\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::hir::poly_for_str;\n\n    fn assert_poly_for_str(ty_str: &str, datum_str: &str) {\n        use arret_syntax::parser::datum_from_str;\n        let datum = datum_from_str(None, datum_str).unwrap();\n\n        assert_eq!(poly_for_str(ty_str), ty_ref_for_datum(&datum));\n    }\n\n    #[test]\n    fn trivial_types() {\n        assert_poly_for_str(\"Int\", \"1\");\n        assert_poly_for_str(\"Int\", \"-51\");\n        assert_poly_for_str(\"Char\", \"\\\\newline\");\n        assert_poly_for_str(\"Str\", r#\"\"Test string\"\"#);\n    }\n\n    #[test]\n    fn bool_literal() {\n        assert_poly_for_str(\"true\", \"true\");\n        assert_poly_for_str(\"false\", \"false\");\n    }\n\n    #[test]\n    fn sym_literal() {\n        assert_poly_for_str(\"'foo\", \"foo\");\n    }\n\n    #[test]\n    fn fixed_list() {\n        assert_poly_for_str(\"()\", \"()\");\n        assert_poly_for_str(\"(List Int Int 'foo)\", \"(1 2 foo)\");\n    }\n\n    #[test]\n    fn fixed_vec() {\n        assert_poly_for_str(\"[]\", \"[]\");\n        assert_poly_for_str(\"(Vector false Int 'foo)\", \"[false 2 foo]\");\n    }\n\n    #[test]\n    fn fixed_set() {\n        assert_poly_for_str(\"(Setof (RawU))\", \"#{}\");\n        assert_poly_for_str(\"(Setof Bool)\", \"#{true false}\");\n    }\n\n    #[test]\n    fn fixed_map() {\n        assert_poly_for_str(\"(Map (RawU) (RawU))\", \"{}\");\n        assert_poly_for_str(\"(Map Bool (RawU Int 'foo))\", \"{true 1, false foo}\");\n    }\n}\n"
  },
  {
    "path": "compiler/ty/intersect.rs",
    "content": "use std::cmp;\nuse std::iter;\nuse std::result;\n\nuse crate::ty;\nuse crate::ty::list_iter::ListIterator;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\nuse crate::ty::record;\nuse crate::ty::ty_args::TyArgs;\nuse crate::ty::var_usage::Variance;\nuse crate::ty::Ty;\n\n#[derive(PartialEq, Debug)]\npub enum Error {\n    Disjoint,\n}\n\ntype Result<S> = result::Result<S, Error>;\n\n/// Flattens an intersection between two type references\n///\n/// This has no type logic; it only flattens the structure of the refs.\nfn flatten_ref_intersect<M: ty::Pm>(ref1: &ty::Ref<M>, ref2: &ty::Ref<M>) -> ty::Ref<M> {\n    let mut members: Vec<ty::Ref<M>> = vec![];\n\n    if let Some(Ty::Intersect(members1)) = ref1.try_to_fixed() {\n        members.extend(members1.iter().cloned());\n    } else {\n        members.push(ref1.clone());\n    }\n\n    if let Some(Ty::Intersect(members2)) = ref2.try_to_fixed() {\n        members.extend(members2.iter().cloned());\n    } else {\n        members.push(ref2.clone());\n    }\n\n    match members.len() {\n        0 => Ty::Any.into(),\n        1 => members.pop().unwrap(),\n        _ => Ty::Intersect(members.into_boxed_slice()).into(),\n    }\n}\n\nfn unify_list(\n    list1: &ty::List<ty::Poly>,\n    list2: &ty::List<ty::Poly>,\n) -> Result<ty::List<ty::Poly>> {\n    match ty::unify::unify_list(list1, list2) {\n        ty::unify::UnifiedList::Merged(merged) => Ok(merged),\n        ty::unify::UnifiedList::Discerned => Err(Error::Disjoint),\n    }\n}\n\n/// Intersects a vector of refs with an iterator\n///\n/// `lefts` is a slice as it needs to be iterated over multiple times. `rights` is only visited\n/// once so it can be an arbitrary iterator.\nfn intersect_union_iter<'a, M, I>(lefts: &[ty::Ref<M>], rights: I) -> Result<ty::Ref<M>>\nwhere\n    M: ty::Pm + 'a,\n    I: Iterator<Item = &'a ty::Ref<M>>,\n{\n    let mut intersected_types: Vec<ty::Ref<M>> = vec![];\n\n    for right in rights {\n        for left in lefts {\n            match intersect_ty_refs(left, right) {\n                Err(Error::Disjoint) => {}\n                Ok(intersected) => {\n                    intersected_types.push(intersected);\n                }\n            }\n        }\n    }\n\n    match intersected_types.len() {\n        0 => Err(Error::Disjoint),\n        1 => Ok(intersected_types.pop().unwrap()),\n        _ => Ok(Ty::Union(intersected_types.into_boxed_slice()).into()),\n    }\n}\n\nfn intersect_ty_ref_iter<'a, M, I>(mut ty_refs: I) -> Result<ty::Ref<M>>\nwhere\n    M: ty::Pm + 'a,\n    I: Iterator<Item = &'a ty::Ref<M>>,\n{\n    let mut acc = if let Some(acc) = ty_refs.next() {\n        acc.clone()\n    } else {\n        return Ok(Ty::Any.into());\n    };\n\n    for ty_ref in ty_refs {\n        acc = intersect_ty_refs(&acc, ty_ref)?;\n    }\n    Ok(acc)\n}\n\nfn intersect_record_field_purities<M: ty::Pm>(\n    variance: Variance,\n    pvar: &purity::PVarId,\n    ty_args1: &TyArgs<M>,\n    ty_args2: &TyArgs<M>,\n) -> Result<purity::Ref> {\n    use crate::ty::is_a::purity_refs_equivalent;\n    use crate::ty::unify::unify_purity_refs;\n\n    let purity_ref1 = &ty_args1.pvar_purities()[pvar];\n    let purity_ref2 = &ty_args2.pvar_purities()[pvar];\n\n    match variance {\n        Variance::Covariant => Ok(intersect_purity_refs(purity_ref1, purity_ref2)),\n        Variance::Contravariant => Ok(unify_purity_refs(purity_ref1, purity_ref2)),\n        Variance::Invariant => {\n            if purity_refs_equivalent(purity_ref1, purity_ref2) {\n                Ok(purity_ref1.clone())\n            } else {\n                Err(Error::Disjoint)\n            }\n        }\n    }\n}\n\nfn intersect_record_field_ty_refs<M: ty::Pm>(\n    variance: Variance,\n    tvar: &ty::TVarId,\n    ty_args1: &TyArgs<M>,\n    ty_args2: &TyArgs<M>,\n) -> Result<ty::Ref<M>> {\n    use crate::ty::is_a::ty_refs_equivalent;\n    use crate::ty::unify::unify_to_ty_ref;\n\n    let ty_ref1 = &ty_args1.tvar_types()[tvar];\n    let ty_ref2 = &ty_args2.tvar_types()[tvar];\n\n    match variance {\n        Variance::Covariant => intersect_ty_refs(ty_ref1, ty_ref2),\n        Variance::Contravariant => Ok(unify_to_ty_ref(ty_ref1, ty_ref2)),\n        Variance::Invariant => {\n            if ty_refs_equivalent(ty_ref1, ty_ref2) {\n                Ok(ty_ref1.clone())\n            } else {\n                Err(Error::Disjoint)\n            }\n        }\n    }\n}\n\nfn intersect_record_instance<M: ty::Pm>(\n    instance1: &record::Instance<M>,\n    instance2: &record::Instance<M>,\n) -> Result<record::Instance<M>> {\n    use crate::ty::record::PolyParam;\n    use std::collections::HashMap;\n\n    if instance1.cons() != instance2.cons() {\n        return Err(Error::Disjoint);\n    }\n\n    let mut merged_pvar_purities = HashMap::new();\n    let mut merged_tvar_types = HashMap::new();\n\n    for poly_param in instance1.cons().poly_params() {\n        match poly_param {\n            PolyParam::PVar(variance, pvar) => {\n                merged_pvar_purities.insert(\n                    pvar.clone(),\n                    intersect_record_field_purities(\n                        *variance,\n                        pvar,\n                        instance1.ty_args(),\n                        instance2.ty_args(),\n                    )?,\n                );\n            }\n            PolyParam::TVar(variance, tvar) => {\n                merged_tvar_types.insert(\n                    tvar.clone(),\n                    intersect_record_field_ty_refs(\n                        *variance,\n                        tvar,\n                        instance1.ty_args(),\n                        instance2.ty_args(),\n                    )?,\n                );\n            }\n            PolyParam::Pure(_) | PolyParam::TFixed(_, _) => {}\n        }\n    }\n\n    Ok(record::Instance::new(\n        instance1.cons().clone(),\n        TyArgs::new(merged_pvar_purities, merged_tvar_types),\n    ))\n}\n\n/// Intersects two types under the assumption that they are not subtypes\nfn non_subty_intersect<M: ty::Pm>(\n    ref1: &ty::Ref<M>,\n    ty1: &Ty<M>,\n    ref2: &ty::Ref<M>,\n    ty2: &Ty<M>,\n) -> Result<ty::Ref<M>> {\n    match (ty1, ty2) {\n        // Union types\n        (Ty::Union(refs1), Ty::Union(refs2)) => intersect_union_iter(refs1, refs2.iter()),\n        (Ty::Union(refs1), _) => intersect_union_iter(refs1, iter::once(ref2)),\n        (_, Ty::Union(refs2)) => intersect_union_iter(refs2, iter::once(ref1)),\n\n        // Intersection types\n        (Ty::Intersect(refs1), Ty::Intersect(refs2)) => {\n            intersect_ty_ref_iter(refs1.iter().chain(refs2.iter()))\n        }\n        (Ty::Intersect(refs1), _) => {\n            let mut acc = ref2.clone();\n            for ty_ref in refs1.iter() {\n                acc = intersect_ty_refs(&acc, ty_ref)?;\n            }\n            Ok(acc)\n        }\n        (_, Ty::Intersect(refs2)) => {\n            let mut acc = ref1.clone();\n            for ty_ref in refs2.iter() {\n                acc = intersect_ty_refs(&acc, ty_ref)?;\n            }\n            Ok(acc)\n        }\n\n        // Set type\n        (Ty::Set(member1), Ty::Set(member2)) => Ok(Ty::Set(Box::new(intersect_ty_refs(\n            member1.as_ref(),\n            member2.as_ref(),\n        )?))\n        .into()),\n\n        // Map type\n        (Ty::Map(map1), Ty::Map(map2)) => Ok(ty::Map::new(\n            intersect_ty_refs(map1.key(), map2.key())?,\n            intersect_ty_refs(map1.value(), map2.value())?,\n        )\n        .into()),\n\n        // Vector types\n        (Ty::Vectorof(member1), Ty::Vectorof(member2)) => Ok(Ty::Vectorof(Box::new(\n            intersect_ty_refs(member1.as_ref(), member2.as_ref())?,\n        ))\n        .into()),\n        (Ty::Vector(members1), Ty::Vector(members2)) => {\n            if members1.len() != members2.len() {\n                Err(Error::Disjoint)\n            } else {\n                let intersected_members = members1\n                    .iter()\n                    .zip(members2.iter())\n                    .map(|(member1, member2)| intersect_ty_refs(member1, member2))\n                    .collect::<Result<Box<[ty::Ref<M>]>>>()?;\n\n                Ok(Ty::Vector(intersected_members).into())\n            }\n        }\n        (Ty::Vectorof(member1), Ty::Vector(members2))\n        | (Ty::Vector(members2), Ty::Vectorof(member1)) => {\n            let intersected_members = members2\n                .iter()\n                .map(|member2| intersect_ty_refs(member1.as_ref(), member2))\n                .collect::<Result<Box<[ty::Ref<M>]>>>()?;\n\n            Ok(Ty::Vector(intersected_members).into())\n        }\n\n        // List types\n        (Ty::List(list1), Ty::List(list2)) => Ok(intersect_list(list1, list2)?.into()),\n\n        // Function types\n        (Ty::TopFun(top_fun1), Ty::TopFun(top_fun2)) => {\n            let intersected_purity = intersect_purity_refs(top_fun1.purity(), top_fun2.purity());\n            let intersected_ret = intersect_ty_refs(top_fun1.ret(), top_fun2.ret())?;\n\n            Ok(ty::TopFun::new(intersected_purity, intersected_ret).into())\n        }\n        (Ty::TopFun(top_fun), Ty::Fun(fun)) | (Ty::Fun(fun), Ty::TopFun(top_fun)) => {\n            if fun.has_polymorphic_vars() {\n                // TODO: This might be possible but we would have to recalculate the tvars for\n                // the intersected function\n                return Err(Error::Disjoint);\n            }\n\n            let intersected_purity = intersect_purity_refs(top_fun.purity(), fun.purity());\n            let intersected_params = fun.params().clone();\n            let intersected_ret = intersect_ty_refs(top_fun.ret(), fun.ret())?;\n\n            Ok(ty::Fun::new_mono(intersected_params, intersected_purity, intersected_ret).into())\n        }\n        (Ty::Fun(fun1), Ty::Fun(fun2)) => {\n            if fun1.has_polymorphic_vars() || fun2.has_polymorphic_vars() {\n                // TODO: Same issue as top functions\n                Err(Error::Disjoint)\n            } else {\n                let intersected_purity = intersect_purity_refs(fun1.purity(), fun2.purity());\n                let intersected_params = unify_list(fun1.params(), fun2.params())?;\n                let intersected_ret = intersect_ty_refs(fun1.ret(), fun2.ret())?;\n\n                Ok(\n                    ty::Fun::new_mono(intersected_params, intersected_purity, intersected_ret)\n                        .into(),\n                )\n            }\n        }\n        (Ty::Record(instance1), Ty::Record(instance2)) => {\n            Ok(Ty::Record(Box::new(intersect_record_instance(instance1, instance2)?)).into())\n        }\n        (_, _) => Err(Error::Disjoint),\n    }\n}\n\npub fn intersect_list<M: ty::Pm>(list1: &ty::List<M>, list2: &ty::List<M>) -> Result<ty::List<M>> {\n    if list1.has_disjoint_arity(list2) {\n        return Err(ty::intersect::Error::Disjoint);\n    }\n\n    let mut iter1 = ListIterator::new(list1);\n    let mut iter2 = ListIterator::new(list2);\n\n    let mut merged_fixed: Vec<ty::Ref<M>> =\n        Vec::with_capacity(cmp::max(iter1.fixed_len(), iter2.fixed_len()));\n\n    while iter1.fixed_len() > 0 || iter2.fixed_len() > 0 {\n        let next1 = iter1.next().unwrap();\n        let next2 = iter2.next().unwrap();\n\n        let merged_next = intersect_ty_refs(next1, next2)?;\n        merged_fixed.push(merged_next);\n    }\n\n    let merged_rest = intersect_ty_refs(list1.rest(), list2.rest())?;\n    Ok(ty::List::new(merged_fixed.into_boxed_slice(), merged_rest))\n}\n\npub fn intersect_ty_refs<M: ty::Pm>(\n    ty_ref1: &ty::Ref<M>,\n    ty_ref2: &ty::Ref<M>,\n) -> Result<ty::Ref<M>> {\n    if ty::is_a::ty_ref_is_a(ty_ref1, ty_ref2) {\n        return Ok(ty_ref1.clone());\n    } else if ty::is_a::ty_ref_is_a(ty_ref2, ty_ref1) {\n        return Ok(ty_ref2.clone());\n    }\n\n    match (ty_ref1, ty_ref2) {\n        (ty::Ref::Fixed(ty1), ty::Ref::Fixed(ty2)) => {\n            // We can invoke full intersection logic if we have fixed types\n            non_subty_intersect(ty_ref1, ty1, ty_ref2, ty2)\n        }\n        _ => {\n            let bound1 = ty_ref1.resolve_to_ty();\n            let bound2 = ty_ref2.resolve_to_ty();\n\n            // Make sure the bounds aren't disjoint\n            // We can't simply `non_subty_intersect` because the bounds may be subtypes\n            intersect_ty_refs(&bound1.clone().into(), &bound2.clone().into())?;\n\n            Ok(flatten_ref_intersect(ty_ref1, ty_ref2))\n        }\n    }\n}\n\npub fn intersect_purity_refs(purity1: &purity::Ref, purity2: &purity::Ref) -> purity::Ref {\n    if purity1 == purity2 {\n        purity1.clone()\n    } else {\n        Purity::Pure.into()\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::hir::{poly_for_str, tvar_bounded_by};\n    use crate::source::EMPTY_SPAN;\n\n    fn assert_disjoint_poly(poly1: &ty::Ref<ty::Poly>, poly2: &ty::Ref<ty::Poly>) {\n        assert_eq!(\n            Error::Disjoint,\n            intersect_ty_refs(poly1, poly2).unwrap_err()\n        );\n    }\n\n    fn assert_merged_poly(\n        expected: &ty::Ref<ty::Poly>,\n        poly1: &ty::Ref<ty::Poly>,\n        poly2: &ty::Ref<ty::Poly>,\n    ) {\n        // This is the basic invariant we're testing - each of our merged type satisfies each of\n        // our input types.\n        assert!(\n            ty::is_a::ty_ref_is_a(expected, poly1),\n            \"The expected type does not definitely satisfy the first input type; the test is incorrect\"\n        );\n        assert!(\n            ty::is_a::ty_ref_is_a(expected, poly2),\n            \"The expected type does not definitely satisfy the second input type; the test is incorrect\"\n        );\n\n        assert_eq!(expected, &intersect_ty_refs(poly1, poly2).unwrap());\n    }\n\n    fn assert_disjoint(ty_str1: &str, ty_str2: &str) {\n        let poly1 = poly_for_str(ty_str1);\n        let poly2 = poly_for_str(ty_str2);\n\n        assert_disjoint_poly(&poly1, &poly2)\n    }\n\n    fn assert_merged(expected_str: &str, ty_str1: &str, ty_str2: &str) {\n        let expected = poly_for_str(expected_str);\n        let poly1 = poly_for_str(ty_str1);\n        let poly2 = poly_for_str(ty_str2);\n\n        assert_merged_poly(&expected, &poly1, &poly2);\n    }\n\n    fn assert_disjoint_iter(ty_strs: &[&str]) {\n        let polys: Vec<_> = ty_strs.iter().map(|&s| poly_for_str(s)).collect();\n\n        assert_eq!(\n            Error::Disjoint,\n            intersect_ty_ref_iter(polys.iter()).unwrap_err()\n        );\n    }\n\n    fn assert_merged_iter(expected_str: &str, ty_strs: &[&str]) {\n        let expected = poly_for_str(expected_str);\n        let polys: Vec<_> = ty_strs.iter().map(|&s| poly_for_str(s)).collect();\n\n        assert_eq!(expected, intersect_ty_ref_iter(polys.iter()).unwrap());\n    }\n\n    #[test]\n    fn disjoint_types() {\n        assert_disjoint(\"Sym\", \"Str\");\n    }\n\n    #[test]\n    fn simple_subtypes() {\n        assert_merged(\"true\", \"Bool\", \"true\");\n        assert_merged(\"Float\", \"Num\", \"Float\");\n        assert_merged(\"Bool\", \"Bool\", \"Any\");\n    }\n\n    #[test]\n    fn union_types() {\n        assert_merged(\"'bar\", \"(RawU 'foo 'bar)\", \"(RawU 'bar 'baz)\");\n        assert_merged(\n            \"(RawU 'bar 'baz)\",\n            \"(RawU 'foo 'bar 'baz)\",\n            \"(RawU 'bar 'baz 'foobar)\",\n        );\n        assert_merged(\"true\", \"(RawU true 'foo)\", \"Bool\");\n    }\n\n    #[test]\n    fn intersect_types() {\n        let ptype = tvar_bounded_by(Ty::Any.into());\n\n        let any_int = poly_for_str(\"Int\");\n        let any_float = poly_for_str(\"Float\");\n\n        // These two intersections become disjoint\n        assert_eq!(\n            Error::Disjoint,\n            intersect_ty_refs::<ty::Poly>(\n                &Ty::Intersect(Box::new([ptype.clone(), any_int])).into(),\n                &Ty::Intersect(Box::new([ptype, any_float])).into(),\n            )\n            .unwrap_err()\n        )\n    }\n\n    #[test]\n    fn map_types() {\n        assert_disjoint(\"(Map Int Float)\", \"(Map Float Int)\");\n        assert_merged(\n            \"(Map 'foo Int)\",\n            \"(Map (RawU 'foo 'bar) Int)\",\n            \"(Map (RawU 'foo 'baz) Int)\",\n        );\n    }\n\n    #[test]\n    fn set_types() {\n        assert_disjoint(\"(Setof Sym)\", \"(Setof Str)\");\n        assert_merged(\n            \"(Setof 'foo)\",\n            \"(Setof (RawU 'foo 'bar))\",\n            \"(Setof (RawU 'foo 'baz))\",\n        );\n    }\n\n    #[test]\n    fn list_types() {\n        assert_disjoint(\"(List Sym)\", \"(List Str)\");\n        assert_merged(\"(List Sym Sym)\", \"(List Any Sym)\", \"(List & Sym)\");\n        assert_merged(\n            \"(List false true)\",\n            \"(List Bool true)\",\n            \"(List false Bool & Any)\",\n        );\n\n        assert_disjoint(\"(List Sym Sym)\", \"(List Sym)\");\n    }\n\n    #[test]\n    fn vec_types() {\n        assert_disjoint(\"(Vector Int)\", \"(Vector Float)\");\n        assert_merged(\"(Vector true)\", \"(Vector Bool)\", \"(Vectorof true)\");\n        assert_merged(\"(Vectorof false)\", \"(Vectorof Bool)\", \"(Vectorof false)\");\n    }\n\n    #[test]\n    fn top_fun_types() {\n        assert_disjoint(\"(... -> Float)\", \"(... -> Int)\");\n        assert_merged(\"(... -> true)\", \"(... -> Bool)\", \"(... ->! true)\");\n    }\n\n    #[test]\n    fn fun_types() {\n        assert_merged(\"(Num -> Int)\", \"(Float -> Int)\", \"(Int -> Int)\");\n        assert_disjoint(\"(Str -> Sym)\", \"(Str Str -> Sym)\");\n        assert_merged(\"(-> true)\", \"(-> Bool)\", \"(->! true)\");\n        assert_merged(\"(Bool -> Str)\", \"(true -> Str)\", \"(false ->! Str)\");\n\n        assert_merged(\"(->! true)\", \"(... ->! true)\", \"(->! Any)\");\n    }\n\n    #[test]\n    fn ty_pred_types() {\n        assert_disjoint(\"str?\", \"sym?\");\n        assert_merged(\"str?\", \"str?\", \"str?\");\n        assert_merged(\"str?\", \"str?\", \"(Any -> Bool)\");\n        assert_merged(\"str?\", \"str?\", \"(... -> Bool)\");\n    }\n\n    #[test]\n    fn eq_pred_types() {\n        assert_merged(\"=\", \"=\", \"=\");\n        assert_merged(\"=\", \"=\", \"(Any Any -> Bool)\");\n        assert_merged(\"=\", \"=\", \"(... -> Bool)\");\n    }\n\n    #[test]\n    fn unbounded_poly_var() {\n        let ptype1 = tvar_bounded_by(Ty::Any.into());\n        let ptype2 = tvar_bounded_by(Ty::Any.into());\n\n        let ptype_intersect = Ty::Intersect(Box::new([ptype1.clone(), ptype2.clone()])).into();\n        let any_sym = poly_for_str(\"Sym\");\n\n        // These are equal; it should just return the original type\n        assert_merged_poly(&ptype1, &ptype1, &ptype1);\n\n        // These create an intersect type\n        assert_merged_poly(\n            &Ty::Intersect(Box::new([ptype1.clone(), ptype2.clone()])).into(),\n            &ptype1,\n            &ptype2,\n        );\n\n        assert_merged_poly(\n            &Ty::Intersect(Box::new([any_sym.clone(), ptype2.clone()])).into(),\n            &any_sym,\n            &ptype2,\n        );\n\n        // These extend an existing intersection\n        assert_merged_poly(\n            &Ty::Intersect(Box::new([any_sym.clone(), ptype1, ptype2])).into(),\n            &any_sym,\n            &ptype_intersect,\n        );\n    }\n\n    #[test]\n    fn bounded_poly_vars() {\n        let ptype1_any = tvar_bounded_by(Ty::Any.into());\n        let ptype2_sym = tvar_bounded_by(Ty::Sym.into());\n        let ptype3_str = tvar_bounded_by(Ty::Str.into());\n\n        let any_sym = poly_for_str(\"Sym\");\n\n        assert_merged_poly(\n            &Ty::Intersect(Box::new([ptype1_any.clone(), ptype2_sym.clone()])).into(),\n            &ptype1_any,\n            &ptype2_sym,\n        );\n\n        assert_merged_poly(&ptype2_sym, &any_sym, &ptype2_sym);\n\n        // These have disjoint bounds\n        assert_disjoint_poly(&ptype2_sym, &ptype3_str);\n        assert_disjoint_poly(&ptype3_str, &any_sym);\n    }\n\n    #[test]\n    fn polymorphic_funs() {\n        let pidentity_fun = poly_for_str(\"(All #{A} A -> A)\");\n        let pidentity_impure_bool_fun = poly_for_str(\"(All #{[A Bool]} A ->! A)\");\n        let top_pure_fun = poly_for_str(\"(... -> Any)\");\n\n        // We should intersect polymorphic functions with themselves\n        assert_merged_poly(&pidentity_fun, &pidentity_fun, &pidentity_fun);\n\n        // The intersection of the pure identity function and the top pure function is the identity\n        // function\n        assert_merged_poly(&pidentity_fun, &pidentity_fun, &top_pure_fun);\n\n        // The intersection of the pure identity function and the impure bool identity function is\n        // the identity function\n        // TODO: This seems like it should be `(All #{[A Bool]} A -> A)`\n        assert_merged_poly(&pidentity_fun, &pidentity_fun, &pidentity_impure_bool_fun);\n\n        // These have no subtype relationship\n        // TODO: This also seems like it should be `(All #{[A Bool]} A -> A)`\n        assert_disjoint_poly(&pidentity_impure_bool_fun, &top_pure_fun);\n    }\n\n    #[test]\n    fn intersect_iter() {\n        assert_merged_iter(\"Any\", &[]);\n        assert_merged_iter(\"Sym\", &[\"Sym\"]);\n        assert_merged_iter(\"true\", &[\"true\", \"Bool\"]);\n        assert_disjoint_iter(&[\"true\", \"false\"]);\n    }\n\n    #[test]\n    fn record_instances() {\n        use crate::ty::ty_args::TyArgs;\n        use std::collections::HashMap;\n\n        let tvar1 = ty::TVar::new(EMPTY_SPAN, \"tvar1\".into(), Ty::Any.into());\n        let tvar2 = ty::TVar::new(EMPTY_SPAN, \"tvar2\".into(), Ty::Any.into());\n\n        let cons1 = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons1\".into(),\n            \"cons1?\".into(),\n            Some(Box::new([record::PolyParam::TVar(\n                Variance::Covariant,\n                tvar1.clone(),\n            )])),\n            Box::new([record::Field::new(\n                EMPTY_SPAN,\n                \"cons1-field1\".into(),\n                tvar1.clone().into(),\n            )]),\n        );\n\n        let cons2 = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons2\".into(),\n            \"cons2?\".into(),\n            Some(Box::new([\n                record::PolyParam::TVar(Variance::Covariant, tvar1.clone()),\n                record::PolyParam::TVar(Variance::Contravariant, tvar2.clone()),\n            ])),\n            Box::new([\n                record::Field::new(EMPTY_SPAN, \"cons2-covariant\".into(), tvar1.clone().into()),\n                record::Field::new(\n                    EMPTY_SPAN,\n                    \"cons2-contravariant\".into(),\n                    tvar2.clone().into(),\n                ),\n            ]),\n        );\n\n        let float_instance1_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons1,\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1.clone(), Ty::Float.into())).collect(),\n            ),\n        )\n        .into();\n\n        let float_true_instance2_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons2.clone(),\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1.clone(), Ty::Float.into()))\n                    .chain(std::iter::once((tvar2.clone(), Ty::LitBool(true).into())))\n                    .collect(),\n            ),\n        )\n        .into();\n\n        let int_true_instance2_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons2.clone(),\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1.clone(), Ty::Int.into()))\n                    .chain(std::iter::once((tvar2.clone(), Ty::LitBool(true).into())))\n                    .collect(),\n            ),\n        )\n        .into();\n\n        let int_bool_instance2_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons2.clone(),\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1.clone(), Ty::Int.into()))\n                    .chain(std::iter::once((tvar2.clone(), Ty::Bool.into())))\n                    .collect(),\n            ),\n        )\n        .into();\n\n        let num_bool_instance2_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons2,\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1, Ty::Num.into()))\n                    .chain(std::iter::once((tvar2, Ty::Bool.into())))\n                    .collect(),\n            ),\n        )\n        .into();\n\n        // Different record constructors\n        assert_disjoint_poly(&float_instance1_poly, &float_true_instance2_poly);\n\n        // Disjoint record instances\n        assert_disjoint_poly(&float_true_instance2_poly, &int_bool_instance2_poly);\n\n        // Intersectable record types\n        assert_merged_poly(\n            &int_bool_instance2_poly,\n            &int_true_instance2_poly,\n            &num_bool_instance2_poly,\n        )\n    }\n}\n"
  },
  {
    "path": "compiler/ty/is_a.rs",
    "content": "use crate::ty;\nuse crate::ty::list_iter::ListIterator;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\nuse crate::ty::record;\nuse crate::ty::var_usage::Variance;\nuse crate::ty::Ty;\n\nfn top_fun_is_a(sub_top_fun: &ty::TopFun, par_top_fun: &ty::TopFun) -> bool {\n    purity_ref_is_a(sub_top_fun.purity(), par_top_fun.purity())\n        && ty_ref_is_a(sub_top_fun.ret(), par_top_fun.ret())\n}\n\nfn list_is_a<M: ty::Pm>(sub_list: &ty::List<M>, par_list: &ty::List<M>) -> bool {\n    if (sub_list.fixed().len() > par_list.fixed().len()) && !par_list.has_rest() {\n        // sub is longer than par\n        return false;\n    }\n\n    if sub_list.fixed().len() < par_list.fixed().len() {\n        // sub is less specific due to less fixed types\n        return false;\n    }\n\n    if !ty_ref_is_a(sub_list.rest(), par_list.rest()) {\n        return false;\n    }\n\n    // Compare our fixed types. If the par fixed ends early we'll use the par rest.\n    sub_list\n        .fixed()\n        .iter()\n        .zip(ListIterator::new(par_list))\n        .all(|(sub, par)| ty_ref_is_a(sub, par))\n}\n\nfn record_field_is_a<F, R>(variance: Variance, is_a: &F, sub: &R, par: &R) -> bool\nwhere\n    F: Fn(&R, &R) -> bool,\n{\n    match variance {\n        Variance::Covariant => is_a(sub, par),\n        Variance::Contravariant => is_a(par, sub),\n        Variance::Invariant => is_a(sub, par) && is_a(par, sub),\n    }\n}\n\nfn record_instance_is_a<M: ty::Pm>(\n    sub_instance: &record::Instance<M>,\n    par_instance: &record::Instance<M>,\n) -> bool {\n    // Make sure they came from the same constructor and satisfy their params\n    sub_instance.cons() == par_instance.cons()\n        && sub_instance\n            .cons()\n            .poly_params()\n            .iter()\n            .all(|poly_param| match poly_param {\n                record::PolyParam::PVar(variance, pvar) => record_field_is_a(\n                    *variance,\n                    &purity_ref_is_a,\n                    &sub_instance.ty_args().pvar_purities()[pvar],\n                    &par_instance.ty_args().pvar_purities()[pvar],\n                ),\n                record::PolyParam::TVar(variance, tvar) => record_field_is_a(\n                    *variance,\n                    &ty_ref_is_a,\n                    &sub_instance.ty_args().tvar_types()[tvar],\n                    &par_instance.ty_args().tvar_types()[tvar],\n                ),\n                record::PolyParam::Pure(_) | record::PolyParam::TFixed(_, _) => true,\n            })\n}\n\nfn monomorphic_fun_is_a(sub_fun: &ty::Fun, par_fun: &ty::Fun) -> bool {\n    top_fun_is_a(sub_fun.top_fun(), par_fun.top_fun()) &&\n        // Note that parameters are contravariant\n        list_is_a(par_fun.params(), sub_fun.params())\n}\n\nfn fun_is_a(sub_fun: &ty::Fun, par_fun: &ty::Fun) -> bool {\n    if sub_fun.has_polymorphic_vars() {\n        let sub_mono = inst_polymorphic_fun(sub_fun, par_fun.top_fun());\n        monomorphic_fun_is_a(&sub_mono, par_fun)\n    } else {\n        monomorphic_fun_is_a(sub_fun, par_fun)\n    }\n}\n\nfn ty_is_a<M: ty::Pm>(\n    sub_ref: &ty::Ref<M>,\n    sub_ty: &Ty<M>,\n    parent_ref: &ty::Ref<M>,\n    parent_ty: &Ty<M>,\n) -> bool {\n    if sub_ty == parent_ty {\n        return true;\n    }\n\n    match (sub_ty, parent_ty) {\n        // Union types\n        (Ty::Union(sub_members), _) => sub_members\n            .iter()\n            .all(|sub_member| ty_ref_is_a(sub_member, parent_ref)),\n        (_, Ty::Union(par_members)) => par_members\n            .iter()\n            .any(|par_member| ty_ref_is_a(sub_ref, par_member)),\n\n        // Intersection types\n        (_, Ty::Intersect(par_members)) => par_members\n            .iter()\n            .all(|par_member| ty_ref_is_a(sub_ref, par_member)),\n        (Ty::Intersect(sub_members), _) => sub_members\n            .iter()\n            .any(|sub_member| ty_ref_is_a(sub_member, parent_ref)),\n\n        // Any type\n        (_, Ty::Any) => true,\n\n        // Sym types\n        (Ty::LitSym(_), Ty::Sym) => true,\n\n        // Bool types\n        (Ty::LitBool(_), Ty::Bool) => true,\n\n        // Floats\n        (Ty::Float, Ty::Num) => true,\n\n        // Ints\n        (Ty::Int, Ty::Num) => true,\n\n        // Sets\n        (Ty::Set(sub), Ty::Set(par)) => ty_ref_is_a(sub.as_ref(), par.as_ref()),\n\n        // Maps\n        (Ty::Map(sub_map), Ty::Map(par_map)) => {\n            ty_ref_is_a(sub_map.key(), par_map.key())\n                && ty_ref_is_a(sub_map.value(), par_map.value())\n        }\n\n        // Vector types\n        (Ty::Vector(sub_members), Ty::Vector(par_members)) => {\n            (sub_members.len() == par_members.len())\n                && sub_members\n                    .iter()\n                    .zip(par_members.iter())\n                    .all(|(sub_member, par_member)| ty_ref_is_a(sub_member, par_member))\n        }\n        (Ty::Vectorof(sub_member), Ty::Vectorof(par_member)) => {\n            ty_ref_is_a(sub_member.as_ref(), par_member.as_ref())\n        }\n        (Ty::Vector(sub_members), Ty::Vectorof(par_member)) => sub_members\n            .iter()\n            .all(|sub_member| ty_ref_is_a(sub_member, par_member)),\n\n        // Functions\n        (Ty::TopFun(sub_top_fun), Ty::TopFun(par_top_fun)) => {\n            top_fun_is_a(sub_top_fun, par_top_fun)\n        }\n        (Ty::Fun(sub_fun), Ty::TopFun(par_top_fun)) => {\n            if sub_fun.has_polymorphic_vars() {\n                let sub_mono = inst_polymorphic_fun(sub_fun, par_top_fun);\n                top_fun_is_a(sub_mono.top_fun(), par_top_fun)\n            } else {\n                top_fun_is_a(sub_fun.top_fun(), par_top_fun)\n            }\n        }\n        (Ty::Fun(sub_fun), Ty::Fun(par_fun)) => fun_is_a(sub_fun, par_fun),\n\n        // All predicate types\n        (Ty::TyPred(_), Ty::TopFun(par_top_fun)) | (Ty::EqPred, Ty::TopFun(par_top_fun)) => {\n            top_fun_is_a(&ty::TopFun::new_for_pred(), par_top_fun)\n        }\n\n        // Type predicate types\n        (Ty::TyPred(_), Ty::Fun(par_fun)) => fun_is_a(&ty::Fun::new_for_ty_pred(), par_fun),\n\n        // Equality predicate type\n        (Ty::EqPred, Ty::Fun(par_fun)) => fun_is_a(&ty::Fun::new_for_eq_pred(), par_fun),\n\n        // List types\n        (Ty::List(sub_list), Ty::List(par_list)) => list_is_a(sub_list, par_list),\n\n        // Record types\n        (Ty::RecordClass(_), Ty::TopRecord) => true,\n        (Ty::Record(_), Ty::TopRecord) => true,\n\n        (Ty::Record(sub_instance), Ty::Record(par_instance)) => {\n            record_instance_is_a(sub_instance, par_instance)\n        }\n        (Ty::RecordClass(sub_cons), Ty::RecordClass(par_cons)) => sub_cons == par_cons,\n        (Ty::Record(sub_instance), Ty::RecordClass(par_cons)) => sub_instance.cons() == par_cons,\n        (Ty::RecordClass(sub_cons), Ty::Record(par_instance)) => {\n            // If the record class has no polymorphic params then it only has one instance\n            sub_cons == par_instance.cons() && sub_cons.poly_params().is_empty()\n        }\n\n        _ => false,\n    }\n}\n\nfn tvar_is_bounded_by(sub_tvar: &ty::TVarId, parent_tvar: &ty::TVarId) -> bool {\n    if sub_tvar == parent_tvar {\n        return true;\n    }\n\n    match &sub_tvar.bound {\n        ty::Ref::Fixed(_) => false,\n        ty::Ref::Var(tvar, _) => tvar_is_bounded_by(tvar, parent_tvar),\n    }\n}\n\nfn purity_ref_is_a(sub: &purity::Ref, parent: &purity::Ref) -> bool {\n    sub == &purity::Ref::Fixed(Purity::Pure)\n        || sub == parent\n        || parent == &purity::Ref::Fixed(Purity::Impure)\n}\n\nfn inst_polymorphic_fun(sub_fun: &ty::Fun, par_top_fun: &ty::TopFun) -> ty::Fun {\n    let mut stx = ty::select::SelectCtx::new(sub_fun.pvars(), sub_fun.tvars());\n\n    stx.add_evidence(sub_fun.ret(), par_top_fun.ret());\n    stx.add_evidence_purity(sub_fun.purity(), par_top_fun.purity());\n    let pta = stx.into_poly_ty_args();\n\n    ty::subst::subst_poly_fun(&pta, sub_fun)\n}\n\npub fn ty_ref_is_a<M: ty::Pm>(sub: &ty::Ref<M>, parent: &ty::Ref<M>) -> bool {\n    if let ty::Ref::Var(parent_tvar, _) = parent {\n        // Typically `parent_is_bound` makes the best result for a polymorphic parent `May`.\n        // These are overrides for cases where they can be `Yes`.\n        match sub {\n            ty::Ref::Var(sub_tvar, _) => {\n                // Are we either the same var our bounded by the same var?\n                if tvar_is_bounded_by(sub_tvar, parent_tvar) {\n                    return true;\n                }\n            }\n            ty::Ref::Fixed(Ty::Intersect(sub_members)) => {\n                // Do we satisfy any of the members of the intersection?\n                if sub_members\n                    .iter()\n                    .any(|sub_member| ty_ref_is_a(sub_member, parent))\n                {\n                    return true;\n                }\n            }\n            _ => {}\n        };\n    }\n\n    let sub_ty = sub.resolve_to_ty();\n    if sub_ty.is_never() {\n        // (U) is a definite subtype of every type, regardless if the parent is bound. This is\n        // important as (U) is used as a placeholder for parameters with unknown type. More\n        // generally, it's the contravariant equivalent of Any.\n        return true;\n    }\n\n    if let ty::Ref::Var(_, _) = parent {\n        return false;\n    }\n\n    let parent_ty = parent.resolve_to_ty();\n    ty_is_a(sub, sub_ty, parent, parent_ty)\n}\n\n/// Determines if two type references are equivalent\n///\n/// Our type system has no canonical union order, allows record class types with only a single\n/// possible instance, etc. This makes normal `PartialEq` unreliable for determining if the type\n/// system would treat two types identically. This function is more expensive but can reliably\n/// detect equivalent types with different representations.\npub fn ty_refs_equivalent<M: ty::Pm>(ty_ref1: &ty::Ref<M>, ty_ref2: &ty::Ref<M>) -> bool {\n    ty_ref_is_a(ty_ref1, ty_ref2) && ty_ref_is_a(ty_ref2, ty_ref1)\n}\n\n/// Determines if two purity refs are equivalent\n///\n/// This is for symmetry with [`ty_refs_equivalent`]\npub fn purity_refs_equivalent(purity_ref1: &purity::Ref, purity_ref2: &purity::Ref) -> bool {\n    purity_ref1 == purity_ref2\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::hir::{poly_for_str, tvar_bounded_by};\n    use crate::source::EMPTY_SPAN;\n\n    #[test]\n    fn sym_types() {\n        let foo_sym = poly_for_str(\"'foo\");\n        let bar_sym = poly_for_str(\"'bar\");\n        let any_sym = poly_for_str(\"Sym\");\n        let any_int = poly_for_str(\"Int\");\n\n        assert!(ty_ref_is_a(&foo_sym, &foo_sym));\n        assert!(!ty_ref_is_a(&foo_sym, &bar_sym));\n\n        assert!(ty_ref_is_a(&foo_sym, &any_sym));\n        assert!(!ty_ref_is_a(&any_sym, &foo_sym));\n\n        assert!(!ty_ref_is_a(&any_sym, &any_int));\n        assert!(!ty_ref_is_a(&any_int, &any_sym));\n    }\n\n    #[test]\n    fn set_types() {\n        let foo_set = poly_for_str(\"(Setof 'foo)\");\n        let bar_set = poly_for_str(\"(Setof 'bar)\");\n        let sym_set = poly_for_str(\"(Setof Sym)\");\n\n        assert!(ty_ref_is_a(&foo_set, &foo_set));\n        assert!(!ty_ref_is_a(&foo_set, &bar_set));\n\n        assert!(ty_ref_is_a(&foo_set, &sym_set));\n        assert!(!ty_ref_is_a(&sym_set, &foo_set));\n    }\n\n    #[test]\n    fn map_types() {\n        let foo_sym = poly_for_str(\"'foo\");\n        let any_sym = poly_for_str(\"Sym\");\n        let any_int = poly_for_str(\"Int\");\n\n        let int_to_any_sym = ty::Map::new(any_int.clone(), any_sym.clone()).into();\n\n        let int_to_foo_sym = ty::Map::new(any_int, foo_sym).into();\n\n        let any_sym_to_any_sym = ty::Map::new(any_sym.clone(), any_sym).into();\n\n        assert!(ty_ref_is_a(&int_to_foo_sym, &int_to_any_sym));\n        assert!(!ty_ref_is_a(&int_to_any_sym, &int_to_foo_sym));\n        assert!(!ty_ref_is_a(&int_to_any_sym, &any_sym_to_any_sym));\n    }\n\n    #[test]\n    fn union_types() {\n        let foo_sym = poly_for_str(\"'foo\");\n        let baz_sym = poly_for_str(\"'baz\");\n\n        let foo_bar_union = poly_for_str(\"(RawU 'foo 'bar)\");\n        let bar_baz_union = poly_for_str(\"(RawU 'bar 'baz)\");\n        let foo_bar_baz_union = poly_for_str(\"(RawU 'foo 'bar 'baz)\");\n        let never = poly_for_str(\"(RawU)\");\n\n        assert!(ty_ref_is_a(&foo_sym, &foo_bar_union));\n        assert!(!ty_ref_is_a(&baz_sym, &foo_bar_union));\n        assert!(!ty_ref_is_a(&baz_sym, &never));\n\n        assert!(!ty_ref_is_a(&foo_bar_union, &foo_sym));\n        assert!(!ty_ref_is_a(&foo_bar_union, &baz_sym));\n        assert!(ty_ref_is_a(&never, &foo_sym));\n\n        assert!(!ty_ref_is_a(&foo_bar_union, &bar_baz_union));\n        assert!(ty_ref_is_a(&foo_bar_union, &foo_bar_union));\n        assert!(ty_ref_is_a(&never, &foo_bar_union));\n\n        assert!(ty_ref_is_a(&foo_bar_union, &foo_bar_baz_union));\n    }\n\n    #[test]\n    fn intersect_types() {\n        let ptype1 = tvar_bounded_by(Ty::Any.into());\n        let ptype2 = tvar_bounded_by(Ty::Any.into());\n\n        let any_sym = poly_for_str(\"Sym\");\n        let foo_sym = poly_for_str(\"'foo\");\n\n        let sym_poly1_intersection =\n            Ty::Intersect(Box::new([ptype1.clone(), any_sym.clone()])).into();\n        let sym_poly2_intersection =\n            Ty::Intersect(Box::new([ptype2.clone(), any_sym.clone()])).into();\n        let sym_poly1_poly2_intersection =\n            Ty::Intersect(Box::new([ptype1.clone(), ptype2, any_sym.clone()])).into();\n\n        // `Sym` might not be `Poly`\n        assert!(!ty_ref_is_a(&any_sym, &sym_poly1_intersection));\n\n        // Our intersection must be both `Sym` and `Poly\n        assert!(ty_ref_is_a(&sym_poly1_intersection, &any_sym));\n        assert!(ty_ref_is_a(&sym_poly1_intersection, &ptype1));\n\n        // However, it might not be a 'foo\n        assert!(!ty_ref_is_a(&sym_poly1_intersection, &foo_sym));\n\n        // A more specific intersection must satisfy a less specific one\n        assert!(ty_ref_is_a(\n            &sym_poly1_poly2_intersection,\n            &sym_poly1_intersection\n        ));\n\n        // A less specific intersection may satisfy a more specific one\n        assert!(!ty_ref_is_a(\n            &sym_poly1_intersection,\n            &sym_poly1_poly2_intersection,\n        ));\n\n        // Partially disjoint intersections may satisfy each other\n        assert!(!ty_ref_is_a(\n            &sym_poly1_intersection,\n            &sym_poly2_intersection\n        ));\n    }\n\n    #[test]\n    fn any_and_never_types() {\n        let any = poly_for_str(\"Any\");\n        let never = Ty::never().into();\n        let foo_sym = poly_for_str(\"'foo\");\n\n        assert!(ty_ref_is_a(&foo_sym, &any));\n        assert!(!ty_ref_is_a(&any, &foo_sym));\n        assert!(ty_ref_is_a(&never, &any));\n        assert!(ty_ref_is_a(&never, &never));\n        assert!(!ty_ref_is_a(&any, &never));\n    }\n\n    #[test]\n    fn list_types() {\n        let empty_list = poly_for_str(\"()\");\n        let listof_any = poly_for_str(\"(List & Any)\");\n        let listof_int = poly_for_str(\"(List & Int)\");\n        let two_ints_list = poly_for_str(\"(List Int Int)\");\n        let three_ints_list = poly_for_str(\"(List Int Int Int)\");\n        let at_least_one_int_list = poly_for_str(\"(List Int & Int)\");\n\n        assert!(ty_ref_is_a(&empty_list, &listof_any));\n        assert!(!ty_ref_is_a(&listof_any, &empty_list));\n\n        assert!(ty_ref_is_a(&listof_int, &listof_any));\n        assert!(!ty_ref_is_a(&listof_any, &listof_int));\n\n        assert!(ty_ref_is_a(&two_ints_list, &listof_int));\n        assert!(!ty_ref_is_a(&listof_int, &two_ints_list));\n        assert!(ty_ref_is_a(&two_ints_list, &listof_any));\n\n        assert!(!ty_ref_is_a(&two_ints_list, &three_ints_list));\n        assert!(!ty_ref_is_a(&three_ints_list, &two_ints_list));\n\n        assert!(ty_ref_is_a(&at_least_one_int_list, &listof_int));\n        assert!(!ty_ref_is_a(&listof_int, &at_least_one_int_list));\n    }\n\n    #[test]\n    fn vec_types() {\n        let vecof_any = poly_for_str(\"(Vectorof Any)\");\n        let vecof_int = poly_for_str(\"(Vectorof Int)\");\n        let two_ints_vec = poly_for_str(\"(Vector Int Int)\");\n        let three_ints_vec = poly_for_str(\"(Vector Int Int Int)\");\n\n        assert!(ty_ref_is_a(&vecof_int, &vecof_any));\n        assert!(!ty_ref_is_a(&vecof_any, &vecof_int));\n\n        assert!(ty_ref_is_a(&two_ints_vec, &vecof_int));\n        assert!(!ty_ref_is_a(&vecof_int, &two_ints_vec));\n        assert!(ty_ref_is_a(&two_ints_vec, &vecof_any));\n\n        assert!(!ty_ref_is_a(&two_ints_vec, &three_ints_vec));\n        assert!(!ty_ref_is_a(&three_ints_vec, &two_ints_vec));\n    }\n\n    #[test]\n    fn num_types() {\n        let int = poly_for_str(\"Int\");\n        let float = poly_for_str(\"Float\");\n        let num = poly_for_str(\"Num\");\n\n        assert!(ty_ref_is_a(&int, &num));\n        assert!(ty_ref_is_a(&float, &num));\n        assert!(ty_ref_is_a(&num, &num));\n        assert!(!ty_ref_is_a(&float, &int));\n        assert!(!ty_ref_is_a(&num, &int));\n        assert!(!ty_ref_is_a(&num, &float));\n    }\n\n    #[test]\n    fn fun_types() {\n        let impure_any_to_sym = poly_for_str(\"(Any ->! Sym)\");\n        let impure_sym_to_any = poly_for_str(\"(Sym ->! Any)\");\n        let impure_sym_to_sym = poly_for_str(\"(Sym ->! Sym)\");\n        let pure_sym_to_sym = poly_for_str(\"(Sym -> Sym)\");\n\n        assert!(ty_ref_is_a(&impure_sym_to_sym, &impure_sym_to_any));\n        assert!(ty_ref_is_a(&impure_any_to_sym, &impure_sym_to_sym));\n        assert!(!ty_ref_is_a(&impure_sym_to_any, &impure_sym_to_sym));\n\n        assert!(ty_ref_is_a(&pure_sym_to_sym, &impure_sym_to_sym));\n        assert!(!ty_ref_is_a(&impure_sym_to_sym, &pure_sym_to_sym));\n    }\n\n    #[test]\n    fn ty_pred_types() {\n        let sym_ty_pred = poly_for_str(\"sym?\");\n        let str_ty_pred = poly_for_str(\"str?\");\n        let general_ty_pred = poly_for_str(\"(Any -> Bool)\");\n        let pred_top_fun = poly_for_str(\"(... -> Bool)\");\n\n        // Type predicates always equal themselves\n        assert!(ty_ref_is_a(&sym_ty_pred, &sym_ty_pred));\n\n        // Type predicates never equal other type predicates\n        assert!(!ty_ref_is_a(&sym_ty_pred, &str_ty_pred));\n        assert!(!ty_ref_is_a(&str_ty_pred, &sym_ty_pred));\n\n        // Type predicates are a subtype of (Any -> Bool)\n        assert!(ty_ref_is_a(&sym_ty_pred, &general_ty_pred));\n        assert!(!ty_ref_is_a(&general_ty_pred, &sym_ty_pred));\n\n        // Type predicates are a subtype of (... -> Bool)\n        assert!(ty_ref_is_a(&sym_ty_pred, &pred_top_fun));\n        assert!(!ty_ref_is_a(&pred_top_fun, &sym_ty_pred));\n    }\n\n    #[test]\n    fn eq_pred_type() {\n        let eq_pred = poly_for_str(\"=\");\n        let general_eq_pred = poly_for_str(\"(Any Any -> Bool)\");\n        let pred_top_fun = poly_for_str(\"(... -> Bool)\");\n\n        // Equality predicate equals itself\n        assert!(ty_ref_is_a(&eq_pred, &eq_pred));\n\n        // Equality predicate is a subtype of (Any Any -> Bool)\n        assert!(ty_ref_is_a(&eq_pred, &general_eq_pred));\n        assert!(!ty_ref_is_a(&general_eq_pred, &eq_pred));\n\n        // Equality predicate is a subtype of (... -> Bool)\n        assert!(ty_ref_is_a(&eq_pred, &pred_top_fun));\n        assert!(!ty_ref_is_a(&pred_top_fun, &eq_pred));\n    }\n\n    #[test]\n    fn bool_types() {\n        let true_type = poly_for_str(\"true\");\n        let false_type = poly_for_str(\"false\");\n        let bool_type = poly_for_str(\"Bool\");\n\n        assert!(ty_ref_is_a(&true_type, &bool_type));\n        assert!(!ty_ref_is_a(&bool_type, &true_type));\n        assert!(!ty_ref_is_a(&false_type, &true_type));\n    }\n\n    #[test]\n    fn poly_bool_types() {\n        let true_type = poly_for_str(\"true\");\n        let false_type = poly_for_str(\"false\");\n        let bool_type = poly_for_str(\"Bool\");\n\n        assert!(ty_ref_is_a(&true_type, &bool_type));\n        assert!(!ty_ref_is_a(&bool_type, &true_type));\n        assert!(!ty_ref_is_a(&false_type, &true_type));\n    }\n\n    #[test]\n    fn unbounded_poly_vars() {\n        let ptype1 = tvar_bounded_by(Ty::Any.into());\n        let ptype2 = tvar_bounded_by(Ty::Any.into());\n\n        let poly_bool = poly_for_str(\"Bool\");\n\n        assert!(ty_ref_is_a(&ptype1, &ptype1));\n        assert!(!ty_ref_is_a(&ptype1, &ptype2));\n        assert!(!ty_ref_is_a(&ptype1, &poly_bool));\n    }\n\n    #[test]\n    fn bounded_poly_vars() {\n        let ptype1_sym = tvar_bounded_by(Ty::Sym.into());\n        let ptype2_str = tvar_bounded_by(Ty::Str.into());\n\n        let poly_foo_sym = poly_for_str(\"'foo\");\n\n        // A type var always satisfies itself\n        assert!(ty_ref_is_a(&ptype1_sym, &ptype1_sym));\n\n        // The bounds of these vars are disjoint\n        assert!(!ty_ref_is_a(&ptype1_sym, &ptype2_str));\n\n        // The type var may satisfy a more specific bound\n        assert!(!ty_ref_is_a(&ptype1_sym, &poly_foo_sym));\n\n        // A sub never satisfies a type var with a disjoint bound\n        assert!(!ty_ref_is_a(&poly_foo_sym, &ptype2_str));\n\n        // The sub has a fixed type while the parent has a poly type. We can't ensure that 'foo\n        // satisfies all possible Sym subtypes (such as 'bar)\n        assert!(!ty_ref_is_a(&poly_foo_sym, &ptype1_sym));\n    }\n\n    #[test]\n    fn related_poly_bounds() {\n        let ptype1_unbounded = tvar_bounded_by(Ty::Any.into());\n        let ptype2_bounded_by_1 = tvar_bounded_by(ptype1_unbounded.clone());\n        let ptype3_bounded_by_2 = tvar_bounded_by(ptype2_bounded_by_1.clone());\n\n        // Direct bounding\n        assert!(ty_ref_is_a(&ptype2_bounded_by_1, &ptype1_unbounded));\n        assert!(ty_ref_is_a(&ptype3_bounded_by_2, &ptype2_bounded_by_1));\n\n        // Commutative bounding\n        assert!(ty_ref_is_a(&ptype3_bounded_by_2, &ptype1_unbounded));\n\n        // Inverse bounding relationship may not satisfy - the bounded type can have arbitrary\n        // subtypes\n        assert!(!ty_ref_is_a(&ptype1_unbounded, &ptype2_bounded_by_1));\n    }\n\n    #[test]\n    fn polymorphic_funs() {\n        let pidentity_fun = poly_for_str(\"(All #{A} A -> A)\");\n        let pidentity_sym_fun = poly_for_str(\"(All #{[A Sym]} A -> A)\");\n        let pidentity_impure_string_fun = poly_for_str(\"(All #{[A Str]} A ->! A)\");\n\n        // All functions should have the top function type\n        let top_fun = poly_for_str(\"(... ->! Any)\");\n        assert!(ty_ref_is_a(&pidentity_fun, &top_fun));\n        assert!(ty_ref_is_a(&pidentity_sym_fun, &top_fun));\n        assert!(ty_ref_is_a(&pidentity_impure_string_fun, &top_fun));\n\n        // We should take in to account purity\n        let top_pure_fun = poly_for_str(\"(... -> Any)\");\n        assert!(ty_ref_is_a(&pidentity_fun, &top_pure_fun));\n        assert!(!ty_ref_is_a(&pidentity_impure_string_fun, &top_pure_fun));\n\n        // All functions should have the top one param function type except panys\n        let top_one_param_fun = poly_for_str(\"((RawU) ->! Any)\");\n        assert!(ty_ref_is_a(&pidentity_fun, &top_one_param_fun));\n        assert!(ty_ref_is_a(&pidentity_sym_fun, &top_one_param_fun));\n        assert!(ty_ref_is_a(\n            &pidentity_impure_string_fun,\n            &top_one_param_fun\n        ));\n\n        // The identity function is (Any -> Any)\n        let any_to_any_fun = poly_for_str(\"(Any ->! Any)\");\n        assert!(ty_ref_is_a(&pidentity_fun, &any_to_any_fun));\n        // However, (Any -> Any) is not the identity function because it can take mismatched types\n        // (e.g. Int -> Float)\n        assert!(!ty_ref_is_a(&any_to_any_fun, &pidentity_fun));\n\n        // The identity function is (true -> true)\n        let true_to_true_fun = poly_for_str(\"(true ->! true)\");\n        assert!(ty_ref_is_a(&pidentity_fun, &true_to_true_fun));\n        assert!(!ty_ref_is_a(&true_to_true_fun, &pidentity_fun));\n\n        // The identity function is not (true -> false)\n        let true_to_true_fun = poly_for_str(\"(true ->! false)\");\n        assert!(!ty_ref_is_a(&pidentity_fun, &true_to_true_fun));\n        assert!(!ty_ref_is_a(&true_to_true_fun, &pidentity_fun));\n\n        // The symbol function satisfies ((U) -> Sym) as all of its returns must be bounded by\n        // that\n        let top_to_sym_fun = poly_for_str(\"(... ->! Sym)\");\n        assert!(ty_ref_is_a(&pidentity_fun, &top_to_sym_fun));\n        assert!(ty_ref_is_a(&pidentity_sym_fun, &top_to_sym_fun));\n\n        // The identity string function satisfies (Str -> Str)\n        let str_to_str_fun = poly_for_str(\"(Str ->! Str)\");\n        assert!(ty_ref_is_a(&pidentity_fun, &str_to_str_fun));\n        assert!(!ty_ref_is_a(&pidentity_sym_fun, &str_to_str_fun));\n        assert!(ty_ref_is_a(&pidentity_impure_string_fun, &str_to_str_fun));\n\n        // The polymorphic identity string function satisfies (... ->! Str)\n        let top_impure_str_fun = poly_for_str(\"(... ->! Str)\");\n        assert!(ty_ref_is_a(\n            &pidentity_impure_string_fun,\n            &top_impure_str_fun\n        ));\n\n        // As does the unbounded identity function\n        assert!(ty_ref_is_a(&pidentity_fun, &top_impure_str_fun));\n\n        // But not the polymorphic symbol function\n        assert!(!ty_ref_is_a(&pidentity_sym_fun, &top_impure_str_fun));\n    }\n\n    #[test]\n    fn polymorphic_purity_funs() {\n        let poly_purity_fun = poly_for_str(\"(All #{[->_ ->!]} (->_ Str) ->_ Str)\");\n        // This is the upper bound of `poly_purity_fun\n        let mono_purity_fun = poly_for_str(\"((->! Str) -> Str)\");\n        let top_to_str_fun = poly_for_str(\"(... -> Str)\");\n\n        assert!(ty_ref_is_a(&poly_purity_fun, &top_to_str_fun));\n        assert!(ty_ref_is_a(&mono_purity_fun, &poly_purity_fun));\n        assert!(!ty_ref_is_a(&top_to_str_fun, &poly_purity_fun));\n    }\n\n    #[test]\n    fn distinct_record_cons_instances() {\n        use crate::ty::ty_args::TyArgs;\n\n        let cons1 = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons1\".into(),\n            \"cons1?\".into(),\n            None,\n            Box::new([]),\n        );\n        let cons2 = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons2\".into(),\n            \"cons2?\".into(),\n            None,\n            Box::new([]),\n        );\n\n        let instance1_poly: ty::Ref<ty::Poly> =\n            record::Instance::new(cons1, TyArgs::empty()).into();\n        let instance2_poly: ty::Ref<ty::Poly> =\n            record::Instance::new(cons2, TyArgs::empty()).into();\n\n        // Different record constructors\n        assert!(!ty_ref_is_a(&instance1_poly, &instance2_poly));\n        assert!(!ty_ref_is_a(&instance2_poly, &instance1_poly));\n\n        // They're both top records\n        assert!(ty_ref_is_a(&instance1_poly, &Ty::TopRecord.into()));\n        assert!(ty_ref_is_a(&instance2_poly, &Ty::TopRecord.into()));\n    }\n\n    #[test]\n    fn same_cons_record_instances() {\n        use crate::ty::ty_args::TyArgs;\n        use std::collections::HashMap;\n\n        let tvar1 = ty::TVar::new(EMPTY_SPAN, \"tvar1\".into(), Ty::Any.into());\n        let tvar2 = ty::TVar::new(EMPTY_SPAN, \"tvar2\".into(), Ty::Any.into());\n        let tvar3 = ty::TVar::new(EMPTY_SPAN, \"tvar3\".into(), Ty::Any.into());\n\n        let cons = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons\".into(),\n            \"cons?\".into(),\n            Some(Box::new([\n                record::PolyParam::TVar(Variance::Covariant, tvar1.clone()),\n                record::PolyParam::TVar(Variance::Contravariant, tvar2.clone()),\n                record::PolyParam::TVar(Variance::Invariant, tvar3.clone()),\n            ])),\n            Box::new([\n                record::Field::new(EMPTY_SPAN, \"covariant\".into(), tvar1.clone().into()),\n                record::Field::new(EMPTY_SPAN, \"contravariant\".into(), tvar2.clone().into()),\n                record::Field::new(EMPTY_SPAN, \"invariant\".into(), tvar3.clone().into()),\n            ]),\n        );\n\n        let num_num_num_instance_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons.clone(),\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1.clone(), Ty::Num.into()))\n                    .chain(std::iter::once((tvar2.clone(), Ty::Num.into())))\n                    .chain(std::iter::once((tvar3.clone(), Ty::Num.into())))\n                    .collect(),\n            ),\n        )\n        .into();\n\n        let int_any_num_instance_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons,\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1, Ty::Int.into()))\n                    .chain(std::iter::once((tvar2, Ty::Any.into())))\n                    .chain(std::iter::once((tvar3, Ty::Num.into())))\n                    .collect(),\n            ),\n        )\n        .into();\n\n        assert!(ty_ref_is_a(\n            &int_any_num_instance_poly,\n            &num_num_num_instance_poly\n        ));\n        assert!(!ty_ref_is_a(\n            &num_num_num_instance_poly,\n            &int_any_num_instance_poly\n        ));\n\n        assert!(ty_ref_is_a(\n            &num_num_num_instance_poly,\n            &num_num_num_instance_poly\n        ));\n        assert!(ty_ref_is_a(\n            &int_any_num_instance_poly,\n            &int_any_num_instance_poly\n        ));\n    }\n}\n"
  },
  {
    "path": "compiler/ty/list_iter.rs",
    "content": "use crate::ty;\nuse crate::ty::Ty;\n\n/// Iterates through the member types of a list\n#[derive(Clone)]\npub struct ListIterator<'list, M: ty::Pm> {\n    fixed: &'list [ty::Ref<M>],\n    rest: &'list ty::Ref<M>,\n}\n\nimpl<'list, M: ty::Pm> ListIterator<'list, M> {\n    pub fn new(list: &'list ty::List<M>) -> ListIterator<'list, M> {\n        ListIterator {\n            fixed: list.fixed(),\n            rest: list.rest(),\n        }\n    }\n\n    pub fn try_new_from_ty_ref(ty_ref: &'list ty::Ref<M>) -> Option<ListIterator<'list, M>> {\n        match ty_ref.try_to_fixed() {\n            Some(Ty::List(list)) => Some(Self::new(list)),\n            _ => None,\n        }\n    }\n\n    pub fn fixed_len(&self) -> usize {\n        self.fixed.len()\n    }\n\n    pub fn has_rest(&self) -> bool {\n        !self.rest.is_never()\n    }\n\n    pub fn tail_type(self) -> ty::List<M> {\n        ty::List::new(self.fixed.to_vec().into_boxed_slice(), self.rest.clone())\n    }\n\n    pub fn collect_rest(self) -> ty::Ref<M> {\n        use std::iter;\n\n        if self.fixed.is_empty() {\n            self.rest.clone()\n        } else {\n            ty::unify::unify_ty_ref_iter(self.fixed.iter().chain(iter::once(self.rest)).cloned())\n        }\n    }\n}\n\nimpl<'list, M: ty::Pm> Iterator for ListIterator<'list, M> {\n    type Item = &'list ty::Ref<M>;\n\n    fn next(&mut self) -> Option<&'list ty::Ref<M>> {\n        if self.fixed.is_empty() {\n            if self.rest.is_never() {\n                None\n            } else {\n                Some(self.rest)\n            }\n        } else {\n            let next = self.fixed.first();\n            self.fixed = &self.fixed[1..];\n            next\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/ty/mod.rs",
    "content": "pub mod conv_abi;\npub mod datum;\npub mod intersect;\npub mod is_a;\npub mod list_iter;\npub mod pred;\npub mod props;\npub mod purity;\npub mod record;\npub mod select;\npub mod subst;\npub mod subtract;\npub mod ty_args;\npub mod unify;\npub mod var_usage;\n\nuse std::fmt;\nuse std::ops::Range;\n\nuse arret_syntax::datum::DataStr;\nuse arret_syntax::span::Span;\n\nuse crate::id_type::ArcId;\n\n#[derive(PartialEq, Debug, Clone)]\npub struct TVar {\n    span: Span,\n    source_name: DataStr,\n    bound: Ref<Poly>,\n}\n\npub type TVarId = ArcId<TVar>;\npub type TVars = Vec<TVarId>;\n\nimpl TVar {\n    pub fn new(span: Span, source_name: DataStr, bound: Ref<Poly>) -> TVarId {\n        TVarId::new(TVar {\n            span,\n            source_name,\n            bound,\n        })\n    }\n\n    pub fn span(&self) -> Span {\n        self.span\n    }\n\n    pub fn source_name(&self) -> &str {\n        &self.source_name\n    }\n\n    pub fn bound(&self) -> &Ref<Poly> {\n        &self.bound\n    }\n}\n\n/// Marker that determines if type variables are allowed within a type\npub trait Pm: PartialEq + Clone + Copy + Sized + fmt::Debug {\n    /// Resolves a possibly variable type to its bound\n    fn resolve_ref_to_ty(ty_ref: &Ref<Self>) -> &Ty<Self>;\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum Mono {}\nimpl Pm for Mono {\n    fn resolve_ref_to_ty(ty_ref: &Ref<Mono>) -> &Ty<Mono> {\n        match ty_ref {\n            Ref::Fixed(ty) => ty,\n            Ref::Var(_, _) => unreachable!(),\n        }\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub struct Poly {}\nimpl Pm for Poly {\n    fn resolve_ref_to_ty(ty_ref: &Ref<Poly>) -> &Ty<Poly> {\n        match ty_ref {\n            Ref::Fixed(ty) => ty,\n            Ref::Var(tvar, _) => Self::resolve_ref_to_ty(tvar.bound()),\n        }\n    }\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub enum Ref<M: Pm> {\n    Var(TVarId, M),\n    Fixed(Ty<M>),\n}\n\nimpl<M: Pm> Ref<M> {\n    /// Tries to convert the TyRef to a fixed Ty\n    pub fn try_to_fixed(&self) -> Option<&Ty<M>> {\n        match self {\n            Ref::Var(_, _) => None,\n            Ref::Fixed(ty) => Some(ty),\n        }\n    }\n\n    /// Constructs a fixed TyRef from a union of the passed vector `members`\n    ///\n    /// `members` should already be unified by the type system; this cannot be used to construct\n    /// arbitrary valid unions.\n    pub fn from_vec(mut members: Vec<Self>) -> Self {\n        if members.len() == 1 {\n            members.pop().unwrap()\n        } else {\n            Ty::Union(members.into_boxed_slice()).into()\n        }\n    }\n\n    /// Combination of find + map looking for a particular fixed type\n    ///\n    /// This is identical to `try_to_fixed().and_then(pred)` except it iterates inside unions.\n    pub fn find_member<'a, F, T>(&'a self, f: F) -> Option<T>\n    where\n        F: Fn(&'a Ty<M>) -> Option<T> + Copy,\n        T: 'a,\n    {\n        match self.try_to_fixed() {\n            Some(Ty::Union(members)) => members\n                .iter()\n                .filter_map(|member| member.find_member(f))\n                .next(),\n            Some(other) => f(other),\n            None => None,\n        }\n    }\n\n    pub fn resolve_to_ty(&self) -> &Ty<M> {\n        M::resolve_ref_to_ty(self)\n    }\n\n    pub fn is_never(&self) -> bool {\n        self.try_to_fixed().map_or(false, |fixed| fixed.is_never())\n    }\n}\n\nimpl Ref<Mono> {\n    pub fn as_ty(&self) -> &Ty<Mono> {\n        match self {\n            Ref::Fixed(ty) => ty,\n            Ref::Var(_, _) => {\n                unreachable!();\n            }\n        }\n    }\n\n    pub fn into_ty(self) -> Ty<Mono> {\n        match self {\n            Ref::Fixed(ty) => ty,\n            Ref::Var(_, _) => {\n                unreachable!();\n            }\n        }\n    }\n}\n\nimpl<M: Pm> From<Ty<M>> for Ref<M> {\n    fn from(ty: Ty<M>) -> Self {\n        Ref::Fixed(ty)\n    }\n}\n\nimpl From<TVarId> for Ref<Poly> {\n    fn from(tvar: TVarId) -> Self {\n        Ref::Var(tvar, Poly {})\n    }\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub enum Ty<M: Pm> {\n    Any,\n    Bool,\n    Char,\n    Float,\n    Map(Box<Map<M>>),\n    Int,\n    Num,\n    LitBool(bool),\n    LitSym(DataStr),\n    Set(Box<Ref<M>>),\n    Str,\n    Sym,\n    Union(Box<[Ref<M>]>),\n    Intersect(Box<[Ref<M>]>),\n\n    // Function types\n    TopFun(Box<TopFun>),\n    Fun(Box<Fun>),\n    TyPred(pred::TestTy),\n    EqPred,\n\n    // Vector types\n    Vector(Box<[Ref<M>]>),\n    Vectorof(Box<Ref<M>>),\n\n    // List types\n    List(List<M>),\n\n    // Record types\n    TopRecord,\n    RecordClass(record::ConsId),\n    Record(Box<record::Instance<M>>),\n}\n\nimpl<M: Pm> Ty<M> {\n    /// Returns the canonical unit type\n    pub fn unit() -> Ty<M> {\n        List::empty().into()\n    }\n\n    /// Returns the canonical never type\n    pub fn never() -> Ty<M> {\n        Ty::Union(Box::new([]))\n    }\n\n    /// Returns if this is the never type\n    pub fn is_never(&self) -> bool {\n        self == &Ty::Union(Box::new([]))\n    }\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct Map<M: Pm> {\n    key: Ref<M>,\n    value: Ref<M>,\n}\n\nimpl<M: Pm> Map<M> {\n    pub fn new(key: Ref<M>, value: Ref<M>) -> Map<M> {\n        Map { key, value }\n    }\n\n    pub fn key(&self) -> &Ref<M> {\n        &self.key\n    }\n\n    pub fn value(&self) -> &Ref<M> {\n        &self.value\n    }\n}\n\nimpl<M: Pm> From<Map<M>> for Ty<M> {\n    fn from(map: Map<M>) -> Self {\n        Ty::Map(Box::new(map))\n    }\n}\n\nimpl<M: Pm> From<Map<M>> for Ref<M> {\n    fn from(map: Map<M>) -> Self {\n        Ref::Fixed(Ty::Map(Box::new(map)))\n    }\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct List<M: Pm> {\n    fixed: Box<[Ref<M>]>,\n    rest: Box<Ref<M>>,\n}\n\nimpl<M: Pm> List<M> {\n    /// Creates a list with the given fixed member types and a uniform tail member type\n    pub fn new(fixed: Box<[Ref<M>]>, rest: Ref<M>) -> List<M> {\n        List {\n            fixed,\n            rest: Box::new(rest),\n        }\n    }\n\n    /// Creates a list of zero or more members with a uniform type\n    pub fn new_uniform(rest: Ref<M>) -> List<M> {\n        List {\n            fixed: Box::new([]),\n            rest: Box::new(rest),\n        }\n    }\n\n    /// Creates a fixed sized list with the given member types\n    pub fn new_tuple(fixed: Box<[Ref<M>]>) -> List<M> {\n        List {\n            fixed,\n            rest: Box::new(Ty::never().into()),\n        }\n    }\n\n    /// Creates an empty list\n    pub fn empty() -> List<M> {\n        List::new_tuple(Box::new([]))\n    }\n\n    pub fn fixed(&self) -> &[Ref<M>] {\n        &self.fixed\n    }\n\n    /// Returns the member type of our uniform tail\n    ///\n    /// This will be [`Ty::never()`] if the list has no tail.\n    pub fn rest(&self) -> &Ref<M> {\n        self.rest.as_ref()\n    }\n\n    pub fn size_range(&self) -> Range<usize> {\n        if self.rest.is_never() {\n            self.fixed.len()..self.fixed.len()\n        } else {\n            self.fixed.len()..usize::max_value()\n        }\n    }\n\n    pub fn has_disjoint_arity(&self, other: &Self) -> bool {\n        let range1 = self.size_range();\n        let range2 = other.size_range();\n\n        range2.start > range1.end || range2.end < range1.start\n    }\n\n    /// Return true is the list is empty\n    pub fn is_empty(&self) -> bool {\n        self.fixed.is_empty() && self.rest.is_never()\n    }\n\n    /// Returns true if the list has a uniform tail of zero or more members\n    pub fn has_rest(&self) -> bool {\n        !self.rest.is_never()\n    }\n}\n\nimpl<M: Pm> From<List<M>> for Ty<M> {\n    fn from(list: List<M>) -> Self {\n        Ty::List(list)\n    }\n}\n\nimpl<M: Pm> From<List<M>> for Ref<M> {\n    fn from(list: List<M>) -> Self {\n        Ref::Fixed(Ty::List(list))\n    }\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct TopFun {\n    purity: purity::Ref,\n    ret: Ref<Poly>,\n}\n\nimpl TopFun {\n    /// Returns a top function type\n    pub fn new(purity: purity::Ref, ret: Ref<Poly>) -> TopFun {\n        TopFun { purity, ret }\n    }\n\n    /// Returns the `Fun` top type for all predicate functions\n    pub fn new_for_pred() -> TopFun {\n        Self::new(purity::Purity::Pure.into(), Ty::Bool.into())\n    }\n\n    pub fn purity(&self) -> &purity::Ref {\n        &self.purity\n    }\n\n    pub fn ret(&self) -> &Ref<Poly> {\n        &self.ret\n    }\n}\n\nimpl<M: Pm> From<TopFun> for Ty<M> {\n    fn from(top_fun: TopFun) -> Self {\n        Ty::TopFun(Box::new(top_fun))\n    }\n}\n\nimpl<M: Pm> From<TopFun> for Ref<M> {\n    fn from(top_fun: TopFun) -> Self {\n        Ref::Fixed(Ty::TopFun(Box::new(top_fun)))\n    }\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct Fun {\n    pvars: purity::PVars,\n    tvars: TVars,\n    top_fun: TopFun,\n    params: List<Poly>,\n}\n\nimpl Fun {\n    pub fn new(pvars: purity::PVars, tvars: TVars, top_fun: TopFun, params: List<Poly>) -> Fun {\n        Fun {\n            pvars,\n            tvars,\n            top_fun,\n            params,\n        }\n    }\n\n    /// Creates a new function without polymorphic variables\n    pub fn new_mono(params: List<Poly>, purity: purity::Ref, ret: Ref<Poly>) -> Fun {\n        Fun {\n            pvars: purity::PVars::new(),\n            tvars: TVars::new(),\n            top_fun: TopFun::new(purity, ret),\n            params,\n        }\n    }\n\n    /// Returns the `Fun` type for the `(main!)` function\n    pub fn new_for_main() -> Fun {\n        Self::new_mono(\n            List::empty(),\n            purity::Purity::Impure.into(),\n            Ty::unit().into(),\n        )\n    }\n\n    /// Returns the `Fun` supertype for all type predicate functions\n    ///\n    /// This is the type `(Any -> Bool)`. It captures the signature of the type predicates; however,\n    /// it does not support occurrence typing.\n    pub fn new_for_ty_pred() -> Fun {\n        Self::new(\n            purity::PVars::new(),\n            TVars::new(),\n            TopFun::new_for_pred(),\n            List::new_tuple(Box::new([Ty::Any.into()])),\n        )\n    }\n\n    /// Returns the `Fun` supertype for the equality predicate\n    ///\n    /// This is the type `(Any Any -> Bool)`. It captures the signature of the equality predicate;\n    /// however, it does not support occurrence typing.\n    pub fn new_for_eq_pred() -> Fun {\n        Self::new(\n            purity::PVars::new(),\n            TVars::new(),\n            TopFun::new_for_pred(),\n            List::new_tuple(Box::new([Ty::Any.into(), Ty::Any.into()])),\n        )\n    }\n\n    pub fn pvars(&self) -> &[purity::PVarId] {\n        &self.pvars\n    }\n\n    pub fn tvars(&self) -> &[TVarId] {\n        &self.tvars\n    }\n\n    pub fn top_fun(&self) -> &TopFun {\n        &self.top_fun\n    }\n\n    pub fn purity(&self) -> &purity::Ref {\n        &self.top_fun.purity\n    }\n\n    pub fn params(&self) -> &List<Poly> {\n        &self.params\n    }\n\n    pub fn ret(&self) -> &Ref<Poly> {\n        &self.top_fun.ret\n    }\n\n    pub fn has_polymorphic_vars(&self) -> bool {\n        !self.pvars.is_empty() || !self.tvars.is_empty()\n    }\n\n    pub fn with_polymorphic_vars(self, pvars: purity::PVars, tvars: TVars) -> Fun {\n        Fun {\n            pvars,\n            tvars,\n            ..self\n        }\n    }\n}\n\nimpl<M: Pm> From<Fun> for Ty<M> {\n    fn from(fun: Fun) -> Self {\n        Ty::Fun(Box::new(fun))\n    }\n}\n\nimpl<M: Pm> From<Fun> for Ref<M> {\n    fn from(fun: Fun) -> Self {\n        Ref::Fixed(Ty::Fun(Box::new(fun)))\n    }\n}\n"
  },
  {
    "path": "compiler/ty/pred.rs",
    "content": "use std::fmt;\n\nuse crate::ty;\nuse crate::ty::purity::Purity;\nuse crate::ty::record;\nuse crate::ty::Ty;\n\n#[derive(PartialEq, Eq, Debug, Clone, Hash)]\npub enum TestTy {\n    Sym,\n    Str,\n    Bool,\n    Num,\n    Int,\n    Float,\n    Char,\n    List,\n    Vector,\n    Set,\n    Map,\n    Fun,\n    Nil,\n    TopRecord,\n    RecordClass(record::ConsId),\n}\n\nimpl TestTy {\n    pub fn match_subject_ref<M: ty::Pm>(&self, ty_ref: &ty::Ref<M>) -> Option<bool> {\n        let resolved_ty = ty_ref.resolve_to_ty();\n        match resolved_ty {\n            Ty::Any => None,\n            Ty::Sym | Ty::LitSym(_) => Some(self == &TestTy::Sym),\n            Ty::Bool | Ty::LitBool(_) => Some(self == &TestTy::Bool),\n            Ty::Char => Some(self == &TestTy::Char),\n            Ty::Float => Some(self == &TestTy::Float || self == &TestTy::Num),\n            Ty::Map(_) => Some(self == &TestTy::Map),\n            Ty::Int => Some(self == &TestTy::Int || self == &TestTy::Num),\n            Ty::Num => match self {\n                TestTy::Num => Some(true),\n                TestTy::Int | TestTy::Float => None,\n                _ => Some(false),\n            },\n            Ty::Set(_) => Some(self == &TestTy::Set),\n            Ty::Str => Some(self == &TestTy::Str),\n            Ty::Fun(_) | Ty::TopFun(_) | Ty::TyPred(_) | Ty::EqPred => Some(self == &TestTy::Fun),\n            Ty::List(list) => match self {\n                TestTy::Nil => {\n                    if list.is_empty() {\n                        Some(true)\n                    } else if list.fixed().is_empty() {\n                        None\n                    } else {\n                        Some(false)\n                    }\n                }\n                TestTy::List => Some(true),\n                _ => Some(false),\n            },\n            Ty::Vector(_) | Ty::Vectorof(_) => Some(self == &TestTy::Vector),\n            Ty::TopRecord => match self {\n                TestTy::TopRecord => Some(true),\n                TestTy::RecordClass(_) => None,\n                _ => Some(false),\n            },\n            Ty::RecordClass(subject_cons) => match self {\n                TestTy::TopRecord => Some(true),\n                TestTy::RecordClass(test_cons) => Some(test_cons == subject_cons),\n                _ => Some(false),\n            },\n            Ty::Record(instance) => match self {\n                TestTy::TopRecord => Some(true),\n                TestTy::RecordClass(test_cons) => Some(instance.cons() == test_cons),\n                _ => Some(false),\n            },\n            Ty::Union(members) => {\n                let results: Vec<Option<bool>> = members\n                    .iter()\n                    .map(|member| self.match_subject_ref(member))\n                    .collect();\n\n                if results.contains(&None) {\n                    None\n                } else if !results.contains(&Some(false)) {\n                    Some(true)\n                } else if !results.contains(&Some(true)) {\n                    Some(false)\n                } else {\n                    None\n                }\n            }\n            Ty::Intersect(members) => {\n                let results: Vec<Option<bool>> = members\n                    .iter()\n                    .map(|member| self.match_subject_ref(member))\n                    .collect();\n\n                if results.contains(&Some(true)) {\n                    Some(true)\n                } else if results.contains(&None) {\n                    None\n                } else {\n                    Some(false)\n                }\n            }\n        }\n    }\n\n    pub fn to_ty(&self) -> Ty<ty::Poly> {\n        use crate::ty::ty_args::TyArgs;\n\n        match self {\n            TestTy::Sym => Ty::Sym,\n            TestTy::Str => Ty::Str,\n            TestTy::Bool => Ty::Bool,\n            TestTy::Num => Ty::Num,\n            TestTy::Int => Ty::Int,\n            TestTy::Float => Ty::Float,\n            TestTy::Char => Ty::Char,\n            TestTy::List => ty::List::new_uniform(Ty::Any.into()).into(),\n            TestTy::Vector => Ty::Vectorof(Box::new(Ty::Any.into())),\n            TestTy::Set => Ty::Set(Box::new(Ty::Any.into())),\n            TestTy::Map => ty::Map::new(Ty::Any.into(), Ty::Any.into()).into(),\n            TestTy::Fun => ty::TopFun::new(Purity::Impure.into(), Ty::Any.into()).into(),\n            TestTy::Nil => ty::List::empty().into(),\n            TestTy::TopRecord => Ty::TopRecord,\n            TestTy::RecordClass(cons) => {\n                if cons.poly_params().is_empty() {\n                    // There's a single instance of this record; we can return the instance type.\n                    // Instance types can be used in more situations than top types.\n                    record::Instance::new(cons.clone(), TyArgs::empty()).into()\n                } else {\n                    Ty::RecordClass(cons.clone())\n                }\n            }\n        }\n    }\n}\n\nimpl fmt::Display for TestTy {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        match self {\n            TestTy::Str => write!(formatter, \"str?\"),\n            TestTy::Sym => write!(formatter, \"sym?\"),\n            TestTy::Num => write!(formatter, \"num?\"),\n            TestTy::Int => write!(formatter, \"int?\"),\n            TestTy::Float => write!(formatter, \"float?\"),\n            TestTy::Bool => write!(formatter, \"bool?\"),\n            TestTy::Char => write!(formatter, \"char?\"),\n            TestTy::List => write!(formatter, \"list?\"),\n            TestTy::Vector => write!(formatter, \"vector?\"),\n            TestTy::Set => write!(formatter, \"set?\"),\n            TestTy::Map => write!(formatter, \"map?\"),\n            TestTy::Fun => write!(formatter, \"fn?\"),\n            TestTy::Nil => write!(formatter, \"nil?\"),\n            TestTy::TopRecord => write!(formatter, \"record?\"),\n            TestTy::RecordClass(cons) => write!(formatter, \"{}?\", cons.value_cons_name()),\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::source::EMPTY_SPAN;\n\n    fn assert_test_ty_will_match(test_ty: &TestTy, subject_ref: impl Into<ty::Ref<ty::Poly>>) {\n        let subject_ref = subject_ref.into();\n        assert!(\n            ty::is_a::ty_ref_is_a(&subject_ref, &test_ty.to_ty().into()),\n            \"Subject type is not a definite subtype of the test type\"\n        );\n        assert_eq!(Some(true), test_ty.match_subject_ref(&subject_ref),);\n    }\n\n    fn assert_test_ty_may_match(test_ty: &TestTy, subject_ref: impl Into<ty::Ref<ty::Poly>>) {\n        let subject_ref = subject_ref.into();\n        assert!(\n            !ty::is_a::ty_ref_is_a(&subject_ref, &test_ty.to_ty().into()),\n            \"Subject type is a definite subtype of the test type\"\n        );\n        assert_eq!(None, test_ty.match_subject_ref(&subject_ref),);\n    }\n\n    fn assert_test_ty_wont_match(test_ty: &TestTy, subject_ref: impl Into<ty::Ref<ty::Poly>>) {\n        let subject_ref = subject_ref.into();\n        assert!(\n            !ty::is_a::ty_ref_is_a(&subject_ref, &test_ty.to_ty().into()),\n            \"Subject type is a definite subtype of the test type\"\n        );\n        assert_eq!(Some(false), test_ty.match_subject_ref(&subject_ref),);\n    }\n\n    fn assert_trivial_test_ty(expected_ty: Ty<ty::Poly>, test_ty: TestTy) {\n        let unrelated_ty: Ty<ty::Poly> = if expected_ty == Ty::Char {\n            Ty::Str\n        } else {\n            Ty::Char\n        };\n\n        assert_eq!(expected_ty, test_ty.to_ty());\n\n        assert_test_ty_will_match(&test_ty, expected_ty);\n        assert_test_ty_wont_match(&test_ty, unrelated_ty);\n    }\n\n    #[test]\n    fn sym_test_ty() {\n        let test_ty = TestTy::Sym;\n\n        assert_test_ty_will_match(&test_ty, Ty::Sym);\n        assert_test_ty_will_match(&test_ty, Ty::LitSym(\"foo\".into()));\n        assert_test_ty_wont_match(&test_ty, Ty::Str);\n    }\n\n    #[test]\n    fn str_test_ty() {\n        assert_trivial_test_ty(Ty::Str, TestTy::Str);\n    }\n\n    #[test]\n    fn bool_test_ty() {\n        let test_ty = TestTy::Bool;\n\n        assert_test_ty_will_match(&test_ty, Ty::Bool);\n        assert_test_ty_will_match(&test_ty, Ty::LitBool(false));\n        assert_test_ty_wont_match(&test_ty, Ty::Str);\n    }\n\n    #[test]\n    fn num_test_ty() {\n        let test_ty = TestTy::Num;\n\n        assert_test_ty_will_match(&test_ty, Ty::Num);\n        assert_test_ty_will_match(&test_ty, Ty::Int);\n        assert_test_ty_will_match(&test_ty, Ty::Float);\n        assert_test_ty_wont_match(&test_ty, Ty::Str);\n    }\n\n    #[test]\n    fn int_test_ty() {\n        let test_ty = TestTy::Int;\n\n        assert_test_ty_will_match(&test_ty, Ty::Int);\n        assert_test_ty_may_match(&test_ty, Ty::Num);\n        assert_test_ty_wont_match(&test_ty, Ty::Float);\n    }\n\n    #[test]\n    fn float_test_ty() {\n        let test_ty = TestTy::Float;\n\n        assert_test_ty_will_match(&test_ty, Ty::Float);\n        assert_test_ty_may_match(&test_ty, Ty::Num);\n        assert_test_ty_wont_match(&test_ty, Ty::Int);\n    }\n\n    #[test]\n    fn char_test_ty() {\n        assert_trivial_test_ty(Ty::Char, TestTy::Char);\n    }\n\n    #[test]\n    fn list_test_ty() {\n        assert_trivial_test_ty(ty::List::new_uniform(Ty::Any.into()).into(), TestTy::List);\n    }\n\n    #[test]\n    fn vector_test_ty() {\n        let test_ty = TestTy::Vector;\n\n        assert_test_ty_will_match(&test_ty, Ty::Vector(Box::new([])));\n        assert_test_ty_will_match(&test_ty, Ty::Vectorof(Box::new(Ty::Any.into())));\n        assert_test_ty_wont_match(&test_ty, Ty::Int);\n    }\n\n    #[test]\n    fn set_test_ty() {\n        assert_trivial_test_ty(Ty::Set(Box::new(Ty::Any.into())), TestTy::Set);\n    }\n\n    #[test]\n    fn map_test_ty() {\n        assert_trivial_test_ty(\n            ty::Map::new(Ty::Any.into(), Ty::Any.into()).into(),\n            TestTy::Map,\n        );\n    }\n\n    #[test]\n    fn fun_test_ty() {\n        let test_ty = TestTy::Fun;\n\n        assert_test_ty_will_match(&test_ty, ty::TopFun::new_for_pred());\n        assert_test_ty_will_match(&test_ty, ty::Fun::new_for_main());\n        assert_test_ty_wont_match(&test_ty, Ty::Str);\n    }\n\n    #[test]\n    fn nil_test_ty() {\n        let test_ty = TestTy::Nil;\n\n        assert_test_ty_will_match(&test_ty, ty::List::empty());\n        assert_test_ty_may_match(&test_ty, ty::List::new_uniform(Ty::Any.into()));\n        assert_test_ty_wont_match(&test_ty, ty::List::new_tuple(Box::new([Ty::Any.into()])));\n    }\n\n    #[test]\n    fn top_record_test_ty() {\n        use crate::ty::ty_args::TyArgs;\n\n        let cons = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons\".into(),\n            \"cons?\".into(),\n            None,\n            Box::new([]),\n        );\n\n        let test_ty = TestTy::TopRecord;\n\n        let test_class_poly: ty::Ref<ty::Poly> = cons.clone().into();\n\n        let test_instance_poly: ty::Ref<ty::Poly> =\n            record::Instance::new(cons, TyArgs::empty()).into();\n\n        assert_test_ty_may_match(&test_ty, Ty::Any);\n        assert_test_ty_will_match(&test_ty, Ty::TopRecord);\n        assert_test_ty_will_match(&test_ty, test_class_poly);\n        assert_test_ty_will_match(&test_ty, test_instance_poly);\n    }\n\n    #[test]\n    fn record_class_test_ty() {\n        use crate::ty::ty_args::TyArgs;\n\n        let cons = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons\".into(),\n            \"cons?\".into(),\n            None,\n            Box::new([]),\n        );\n        let other_cons = record::Cons::new(\n            EMPTY_SPAN,\n            \"other_cons\".into(),\n            \"other_cons?\".into(),\n            None,\n            Box::new([]),\n        );\n\n        let test_ty = TestTy::RecordClass(cons.clone());\n\n        let test_class_poly: ty::Ref<ty::Poly> = cons.clone().into();\n        let other_class_poly: ty::Ref<ty::Poly> = other_cons.clone().into();\n\n        let test_instance_poly: ty::Ref<ty::Poly> =\n            record::Instance::new(cons, TyArgs::empty()).into();\n\n        let other_instance_poly: ty::Ref<ty::Poly> =\n            record::Instance::new(other_cons, TyArgs::empty()).into();\n\n        assert_test_ty_may_match(&test_ty, Ty::Any);\n        assert_test_ty_may_match(&test_ty, Ty::TopRecord);\n\n        assert_test_ty_will_match(&test_ty, test_class_poly);\n        assert_test_ty_wont_match(&test_ty, other_class_poly);\n\n        assert_test_ty_will_match(&test_ty, test_instance_poly);\n        assert_test_ty_wont_match(&test_ty, other_instance_poly);\n    }\n\n    #[test]\n    fn union_subject_ref() {\n        let str_sym_union: ty::Ref<ty::Poly> =\n            Ty::Union(Box::new([Ty::Str.into(), Ty::Sym.into()])).into();\n\n        assert_test_ty_may_match(&TestTy::Str, str_sym_union.clone());\n        assert_test_ty_may_match(&TestTy::Sym, str_sym_union.clone());\n        assert_test_ty_wont_match(&TestTy::Int, str_sym_union);\n\n        let list_false_union: ty::Ref<ty::Poly> = Ty::Union(Box::new([\n            ty::List::new_uniform(Ty::Any.into()).into(),\n            Ty::LitBool(false).into(),\n        ]))\n        .into();\n\n        assert_test_ty_may_match(&TestTy::Nil, list_false_union);\n\n        let never: ty::Ref<ty::Poly> = Ty::never().into();\n        assert_test_ty_will_match(&TestTy::Str, never.clone());\n        assert_test_ty_will_match(&TestTy::Sym, never);\n    }\n\n    #[test]\n    fn intersect_subject_ref() {\n        let str_num_intersect: ty::Ref<ty::Poly> =\n            Ty::Intersect(Box::new([Ty::Str.into(), Ty::Num.into()])).into();\n\n        assert_test_ty_will_match(&TestTy::Str, str_num_intersect.clone());\n        assert_test_ty_may_match(&TestTy::Int, str_num_intersect.clone());\n        assert_test_ty_wont_match(&TestTy::Sym, str_num_intersect);\n    }\n}\n"
  },
  {
    "path": "compiler/ty/props.rs",
    "content": "use crate::ty;\nuse crate::ty::purity::Purity;\nuse crate::ty::var_usage::Variance;\nuse crate::ty::Ty;\n\nfn ty_has_subtypes<M: ty::Pm>(ty: &Ty<M>) -> bool {\n    match ty {\n        Ty::Any\n        | Ty::Bool\n        | Ty::Num\n        | Ty::Sym\n        | Ty::TopFun(_)\n        | Ty::TopRecord\n        | Ty::RecordClass(_) => true,\n\n        Ty::Char\n        | Ty::Float\n        | Ty::Int\n        | Ty::LitBool(_)\n        | Ty::LitSym(_)\n        | Ty::Str\n        | Ty::TyPred(_)\n        | Ty::EqPred => false,\n\n        Ty::Fun(fun) => {\n            fun.purity() != &Purity::Pure.into()\n                || !fun.params().fixed().is_empty()\n                || fun.params().rest() != &Ty::Any.into()\n                || has_subtypes(fun.ret())\n        }\n        Ty::Map(map) => has_subtypes(map.key()) || has_subtypes(map.value()),\n        Ty::Set(member) => has_subtypes(member.as_ref()),\n        Ty::Vector(members) => members.iter().any(has_subtypes),\n        Ty::Union(members) => !members.is_empty(),\n        Ty::List(list) => {\n            // Any arbitrary fixed length list is a subtype of a list with rest\n            list.has_rest() || list.fixed().iter().any(has_subtypes)\n        }\n\n        // Any record type supporting variance has subtypes\n        Ty::Record(instance) => instance\n            .cons()\n            .poly_params()\n            .iter()\n            .any(|poly_param| poly_param.variance() != Variance::Invariant),\n\n        Ty::Vectorof(_) => {\n            // Any arbitrary fixed length vector is a subtype of this vector\n            true\n        }\n        Ty::Intersect(_) => {\n            // If we're correctly normalised we should have subtypes\n            true\n        }\n    }\n}\n\npub fn has_subtypes<M: ty::Pm>(ty_ref: &ty::Ref<M>) -> bool {\n    ty_ref\n        .try_to_fixed()\n        .map(|ty| ty_has_subtypes(ty))\n        .unwrap_or(true)\n}\n\nfn ty_is_literal<M: ty::Pm>(ty: &Ty<M>) -> bool {\n    match ty {\n        Ty::LitBool(_) | Ty::LitSym(_) => true,\n        Ty::Vector(members) => members.iter().all(is_literal),\n        Ty::List(list) => !list.has_rest() && list.fixed().iter().all(is_literal),\n        _ => false,\n    }\n}\n\npub fn is_literal<M: ty::Pm>(ty_ref: &ty::Ref<M>) -> bool {\n    ty_ref\n        .try_to_fixed()\n        .map(|ty| ty_is_literal(ty))\n        .unwrap_or(false)\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::hir::poly_for_str;\n    use crate::source::EMPTY_SPAN;\n    use crate::ty::record;\n    use crate::ty::ty_args::TyArgs;\n\n    fn str_has_subtypes(datum_str: &str) -> bool {\n        let poly = poly_for_str(datum_str);\n        has_subtypes(&poly)\n    }\n\n    fn str_is_literal(datum_str: &str) -> bool {\n        let poly = poly_for_str(datum_str);\n        is_literal(&poly)\n    }\n\n    #[test]\n    fn poly_subtypes() {\n        assert!(str_has_subtypes(\"Any\"));\n        assert!(str_has_subtypes(\"Bool\"));\n        assert!(!str_has_subtypes(\"true\"));\n        assert!(!str_has_subtypes(\"Char\"));\n        assert!(!str_has_subtypes(\"Float\"));\n        assert!(!str_has_subtypes(\"Str\"));\n        assert!(str_has_subtypes(\"Sym\"));\n        assert!(str_has_subtypes(\"Num\"));\n\n        assert!(!str_has_subtypes(\"(& Any -> true)\"));\n        assert!(str_has_subtypes(\"(& Any ->! true)\"));\n        assert!(str_has_subtypes(\"(Any -> true)\"));\n        assert!(str_has_subtypes(\"(& Int -> true)\"));\n        assert!(str_has_subtypes(\"(& Any -> Any)\"));\n\n        assert!(str_has_subtypes(\"(Map Sym Int)\"));\n        assert!(!str_has_subtypes(\"(Map Float Int)\"));\n\n        assert!(str_has_subtypes(\"(List Sym Int)\"));\n        assert!(str_has_subtypes(\"(List Str & Int)\"));\n        assert!(!str_has_subtypes(\"(List Str Int)\"));\n\n        assert!(str_has_subtypes(\"(Setof Sym)\"));\n        assert!(!str_has_subtypes(\"(Setof Float)\"));\n\n        assert!(str_has_subtypes(\"(Vectorof false)\"));\n        assert!(!str_has_subtypes(\"(Vector false true)\"));\n\n        assert!(!str_has_subtypes(\"(RawU)\"));\n\n        let tvar = ty::TVar::new(EMPTY_SPAN, \"test\".into(), Ty::Any.into());\n        assert!(has_subtypes(&tvar.into()));\n    }\n\n    #[test]\n    fn poly_literal() {\n        assert!(!str_is_literal(\"Any\"));\n        assert!(!str_is_literal(\"Bool\"));\n        assert!(str_is_literal(\"true\"));\n        assert!(!str_is_literal(\"Char\"));\n        assert!(!str_is_literal(\"Float\"));\n        assert!(!str_is_literal(\"Str\"));\n        assert!(!str_is_literal(\"Sym\"));\n\n        assert!(!str_is_literal(\"(& Any -> true)\"));\n\n        assert!(!str_is_literal(\"(Map Sym Int)\"));\n        assert!(!str_is_literal(\"(Map false true)\"));\n\n        assert!(!str_is_literal(\"(List Sym Int)\"));\n        assert!(!str_is_literal(\"(List Str & Int)\"));\n        assert!(str_is_literal(\"(List true false)\"));\n        assert!(!str_is_literal(\"(List true & false)\"));\n        assert!(str_is_literal(\"()\"));\n\n        assert!(!str_is_literal(\"(Setof ())\"));\n        assert!(!str_is_literal(\"(Setof Float)\"));\n\n        assert!(!str_is_literal(\"(Vectorof false)\"));\n        assert!(str_is_literal(\"(Vector false true)\"));\n\n        let tvar = ty::TVar::new(EMPTY_SPAN, \"test\".into(), Ty::Any.into());\n        assert!(!is_literal(&tvar.into()));\n    }\n\n    #[test]\n    fn mono_record_type() {\n        let mono_record_cons = record::Cons::new(\n            EMPTY_SPAN,\n            \"record_cons\".into(),\n            \"record_cons?\".into(),\n            None,\n            Box::new([record::Field::new(EMPTY_SPAN, \"num\".into(), Ty::Num.into())]),\n        );\n\n        let int_record_instance_ref: ty::Ref<ty::Poly> =\n            record::Instance::new(mono_record_cons, TyArgs::empty()).into();\n\n        assert!(!has_subtypes(&int_record_instance_ref));\n        assert!(!is_literal(&int_record_instance_ref));\n    }\n\n    #[test]\n    fn poly_record_type() {\n        let tvar = ty::TVar::new(EMPTY_SPAN, \"tvar\".into(), Ty::Any.into());\n\n        let poly_record_cons = record::Cons::new(\n            EMPTY_SPAN,\n            \"record_cons\".into(),\n            \"record_cons?\".into(),\n            Some(Box::new([record::PolyParam::TVar(\n                Variance::Covariant,\n                tvar.clone(),\n            )])),\n            Box::new([record::Field::new(EMPTY_SPAN, \"num\".into(), tvar.into())]),\n        );\n\n        let poly_record_instance_ref: ty::Ref<ty::Poly> =\n            record::Instance::new(poly_record_cons, TyArgs::empty()).into();\n\n        assert!(has_subtypes(&poly_record_instance_ref));\n        assert!(!is_literal(&poly_record_instance_ref));\n    }\n}\n"
  },
  {
    "path": "compiler/ty/purity.rs",
    "content": "use arret_syntax::datum::DataStr;\nuse arret_syntax::span::Span;\n\nuse crate::id_type::ArcId;\n\n#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]\npub enum Purity {\n    Pure,\n    Impure,\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct PVar {\n    span: Span,\n    source_name: DataStr,\n}\n\npub type PVarId = ArcId<PVar>;\npub type PVars = Vec<PVarId>;\n\nimpl PVar {\n    pub fn new(span: Span, source_name: DataStr) -> PVarId {\n        PVarId::new(PVar { span, source_name })\n    }\n\n    pub fn span(&self) -> Span {\n        self.span\n    }\n\n    pub fn source_name(&self) -> &str {\n        &self.source_name\n    }\n}\n\n#[derive(Debug, PartialEq, Eq, Clone, Hash)]\npub enum Ref {\n    Fixed(Purity),\n    Var(PVarId),\n}\n\nimpl From<Purity> for Ref {\n    fn from(purity: Purity) -> Self {\n        Ref::Fixed(purity)\n    }\n}\n\nimpl From<PVarId> for Ref {\n    fn from(pvar: PVarId) -> Self {\n        Ref::Var(pvar)\n    }\n}\n"
  },
  {
    "path": "compiler/ty/record.rs",
    "content": "use arret_syntax::datum::DataStr;\nuse arret_syntax::span::Span;\n\nuse crate::id_type::ArcId;\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\nuse crate::ty::ty_args::TyArgs;\nuse crate::ty::var_usage::Variance;\nuse crate::ty::Ty;\n\n/// Record field of a record constructor\n#[derive(PartialEq, Debug, Clone)]\npub struct Field {\n    span: Span,\n    name: DataStr,\n    ty_ref: ty::Ref<ty::Poly>,\n}\n\nimpl Field {\n    pub fn new(span: Span, name: DataStr, ty_ref: ty::Ref<ty::Poly>) -> Self {\n        Self { span, name, ty_ref }\n    }\n\n    /// Returns the span where the constructor was defined\n    pub fn span(&self) -> Span {\n        self.span\n    }\n\n    /// Returns the name of the record field\n    ///\n    /// Unlike other source names that are used for diagnostics, this is semantically meaningful.\n    /// It's used for keyword-based field access syntax.\n    pub fn name(&self) -> &DataStr {\n        &self.name\n    }\n\n    /// Returns the type of the record field\n    pub fn ty_ref(&self) -> &ty::Ref<ty::Poly> {\n        &self.ty_ref\n    }\n\n    /// Returns the type of the field accessor function\n    pub fn accessor_fun_type(&self, cons_id: &ConsId) -> ty::Fun {\n        let ty_args = cons_id.identity_ty_args();\n        let pvars: purity::PVars = ty_args.pvar_purities().keys().cloned().collect();\n        let tvars: ty::TVars = ty_args.tvar_types().keys().cloned().collect();\n\n        let top_fun = ty::TopFun::new(Purity::Pure.into(), self.ty_ref().clone());\n        let params =\n            ty::List::new_tuple(Box::new([Instance::new(cons_id.clone(), ty_args).into()]));\n\n        ty::Fun::new(pvars, tvars, top_fun, params)\n    }\n}\n\n/// Polymorphic parameter to a record constructor\n///\n/// This doesn't use separate [`TVar`](ty::TVar) and [`PVar`](purity::PVar) vectors because they\n/// appear in the same parameter list and their ordering is important.\n#[derive(PartialEq, Debug, Clone)]\npub enum PolyParam {\n    /// Polymorphic purity variable\n    PVar(Variance, purity::PVarId),\n    /// Declared polymorphic purity fixed to `Pure`\n    Pure(Span),\n\n    /// Polymorphic type variable\n    TVar(Variance, ty::TVarId),\n    /// Declared polymorphic type fixed to a known type\n    TFixed(Span, ty::Ref<ty::Poly>),\n}\n\nimpl PolyParam {\n    pub fn variance(&self) -> Variance {\n        match self {\n            PolyParam::PVar(variance, _) => *variance,\n            PolyParam::TVar(variance, _) => *variance,\n            // This is arbitrary as every instance will have the same value. `Invariant` is\n            // probably the least confusing thing to return.\n            PolyParam::Pure(_) | PolyParam::TFixed(_, _) => Variance::Invariant,\n        }\n    }\n}\n\n/// Record type constructor\n///\n/// This is a collection of fields and polymorphic parameters that can be used to construct\n/// [instance types](Instance). This should not be confused with the record constructor function\n/// used to build record values.\n#[derive(PartialEq, Debug, Clone)]\npub struct Cons {\n    span: Span,\n    ty_cons_name: DataStr,\n    value_cons_name: DataStr,\n    poly_params_list: Option<Box<[PolyParam]>>,\n    fields: Box<[Field]>,\n}\n\nimpl Cons {\n    pub fn new(\n        span: Span,\n        ty_cons_name: DataStr,\n        value_cons_name: DataStr,\n        poly_params_list: Option<Box<[PolyParam]>>,\n        fields: Box<[Field]>,\n    ) -> ConsId {\n        ConsId::new(Self {\n            span,\n            ty_cons_name,\n            value_cons_name,\n            poly_params_list,\n            fields,\n        })\n    }\n\n    /// Returns the span where the constructor was defined\n    pub fn span(&self) -> Span {\n        self.span\n    }\n\n    /// Returns the name of the type constructor\n    ///\n    /// Unlike other source names that are used for diagnostics, this is semantically meaningful.\n    /// It's used to define a type constructor.\n    pub fn ty_cons_name(&self) -> &DataStr {\n        &self.ty_cons_name\n    }\n\n    /// Returns the name of the value constructor\n    pub fn value_cons_name(&self) -> &DataStr {\n        &self.value_cons_name\n    }\n\n    /// Returns the polymorphic parameters this constructor accepts\n    pub fn poly_params(&self) -> &[PolyParam] {\n        match self.poly_params_list {\n            Some(ref poly_params) => poly_params.as_ref(),\n            None => &[],\n        }\n    }\n\n    /// Returns true if the constructor was declared as a singleton\n    ///\n    /// This has no effect on the type system; it's only used to accurately print the type.\n    pub fn is_singleton(&self) -> bool {\n        self.poly_params_list.is_none()\n    }\n\n    /// Returns an ordered list of fields of every record type instance\n    pub fn fields(&self) -> &[Field] {\n        self.fields.as_ref()\n    }\n\n    /// Returns an identity map of polymorphic variables associated with the constructor\n    pub fn identity_ty_args(&self) -> TyArgs<ty::Poly> {\n        use std::collections::HashMap;\n\n        let mut pvar_purities = HashMap::new();\n        let mut tvar_types = HashMap::new();\n\n        // Create an identity map of our polymorphic variables. When we substitute in the selected\n        // types the keys will stay the same while the values will be replaced.\n        for poly_param in self.poly_params() {\n            match poly_param {\n                PolyParam::PVar(_, pvar) => {\n                    pvar_purities.insert(pvar.clone(), pvar.clone().into());\n                }\n                PolyParam::TVar(_, tvar) => {\n                    tvar_types.insert(tvar.clone(), tvar.clone().into());\n                }\n                PolyParam::Pure(_) | PolyParam::TFixed(_, _) => {}\n            }\n        }\n\n        TyArgs::new(pvar_purities, tvar_types)\n    }\n\n    /// Returns the type of the value constructor function\n    pub fn value_cons_fun_type(cons_id: &ConsId) -> ty::Fun {\n        let ty_args = cons_id.identity_ty_args();\n        let pvars: purity::PVars = ty_args.pvar_purities().keys().cloned().collect();\n        let tvars: ty::TVars = ty_args.tvar_types().keys().cloned().collect();\n\n        let ret_type = Instance::new(cons_id.clone(), ty_args).into();\n        let top_fun = ty::TopFun::new(Purity::Pure.into(), ret_type);\n\n        let params = ty::List::new_tuple(\n            cons_id\n                .fields\n                .iter()\n                .map(|field| field.ty_ref.clone())\n                .collect(),\n        );\n        ty::Fun::new(pvars, tvars, top_fun, params)\n    }\n}\n\npub type ConsId = ArcId<Cons>;\n\nimpl<M: ty::Pm> From<ConsId> for Ty<M> {\n    fn from(cons_id: ConsId) -> Self {\n        Ty::RecordClass(cons_id)\n    }\n}\n\nimpl<M: ty::Pm> From<ConsId> for ty::Ref<M> {\n    fn from(cons_id: ConsId) -> Self {\n        ty::Ref::Fixed(Ty::RecordClass(cons_id))\n    }\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct Instance<M: ty::Pm> {\n    cons: ConsId,\n    ty_args: TyArgs<M>,\n}\n\nimpl<M: ty::Pm> Instance<M> {\n    pub fn new(cons: ConsId, ty_args: TyArgs<M>) -> Self {\n        Self { cons, ty_args }\n    }\n\n    /// Returns the record constructor this instance was constructed from\n    pub fn cons(&self) -> &ConsId {\n        &self.cons\n    }\n\n    /// Returns the type arguments to the record type constructor\n    ///\n    /// Every [polymorphic parameter](Cons::poly_params) must be specified in the type arguments.\n    pub fn ty_args(&self) -> &TyArgs<M> {\n        &self.ty_args\n    }\n}\n\nimpl<M: ty::Pm> From<Instance<M>> for Ty<M> {\n    fn from(instance: Instance<M>) -> Self {\n        Ty::Record(Box::new(instance))\n    }\n}\n\nimpl<M: ty::Pm> From<Instance<M>> for ty::Ref<M> {\n    fn from(instance: Instance<M>) -> Self {\n        ty::Ref::Fixed(Ty::Record(Box::new(instance)))\n    }\n}\n"
  },
  {
    "path": "compiler/ty/select.rs",
    "content": "use std::collections::HashMap;\n\nuse crate::ty;\nuse crate::ty::list_iter::ListIterator;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\nuse crate::ty::record;\nuse crate::ty::ty_args::TyArgs;\nuse crate::ty::Ty;\n\npub enum Error<'vars> {\n    UnselectedPVar(&'vars purity::PVarId),\n    UnselectedTVar(&'vars ty::TVarId),\n}\n\n/// Selects a set of polymorphic variables for a function application\n///\n/// This context is constructed with a set of purity and type variables the applied function is\n/// polymorphic on. Evidence from the return and argument types can then be incrementally added to\n/// the context. The calculated polymorphic types and purities can be retrieved from the\n/// `pvar_purities` and `tvar_types` methods.\n#[derive(Clone, Debug)]\npub struct SelectCtx<'vars> {\n    selecting_pvars: &'vars [purity::PVarId],\n    selecting_tvars: &'vars [ty::TVarId],\n\n    pvar_purities: HashMap<purity::PVarId, purity::Ref>,\n    tvar_types: HashMap<ty::TVarId, ty::Ref<ty::Poly>>,\n}\n\nimpl<'vars> SelectCtx<'vars> {\n    pub fn new(\n        selecting_pvars: &'vars [purity::PVarId],\n        selecting_tvars: &'vars [ty::TVarId],\n    ) -> SelectCtx<'vars> {\n        SelectCtx {\n            selecting_pvars,\n            selecting_tvars,\n            pvar_purities: HashMap::with_capacity(selecting_pvars.len()),\n            tvar_types: HashMap::with_capacity(selecting_tvars.len()),\n        }\n    }\n\n    fn add_evidence_top_fun(&mut self, target_top_fun: &ty::TopFun, evidence_top_fun: &ty::TopFun) {\n        self.add_evidence_purity(target_top_fun.purity(), evidence_top_fun.purity());\n        self.add_evidence(target_top_fun.ret(), evidence_top_fun.ret());\n    }\n\n    fn add_evidence_fun(&mut self, target_top_fun: &ty::TopFun, evidence_fun: &ty::Fun) {\n        // We have three options for dealing with polymorphic functions:\n        //\n        // 1. Do nothing and treat them normally. This can leak the evidence fun's polymorphic\n        //    return type in to the our selected type which results in an illegal type.\n        // 2. Do a recursive selection where we pass the known types from the target fun in to\n        //    the evidence fun and use that to calculate the return type. This is possible but\n        //    complex.\n        // 3. Do nothing and depend on the fact the target fun is probably already polymorphic\n        //    and expresses the type relationship we care about. This is the option implemented\n        //    below\n        if evidence_fun.pvars().is_empty() {\n            self.add_evidence_purity(target_top_fun.purity(), evidence_fun.purity());\n        }\n        if evidence_fun.tvars().is_empty() {\n            self.add_evidence(target_top_fun.ret(), evidence_fun.ret());\n        }\n    }\n\n    fn add_evidence_record(\n        &mut self,\n        target_instance: &record::Instance<ty::Poly>,\n        evidence_instance: &record::Instance<ty::Poly>,\n    ) {\n        if target_instance.cons() != evidence_instance.cons() {\n            return;\n        }\n\n        for (pvar, target_purity) in target_instance.ty_args().pvar_purities().iter() {\n            let evidence_purity = &evidence_instance.ty_args().pvar_purities()[pvar];\n            self.add_evidence_purity(target_purity, evidence_purity);\n        }\n\n        for (tvar, target_poly) in target_instance.ty_args().tvar_types().iter() {\n            let evidence_poly = &evidence_instance.ty_args().tvar_types()[tvar];\n            self.add_evidence(target_poly, evidence_poly);\n        }\n    }\n\n    fn add_evidence_list(\n        &mut self,\n        target_list: &ty::List<ty::Poly>,\n        evidence_list: &ty::List<ty::Poly>,\n    ) {\n        let mut target_iter = ListIterator::new(target_list);\n        let mut evidence_iter = ListIterator::new(evidence_list);\n\n        while target_iter.fixed_len() > 0 {\n            let target_fixed = target_iter.next().unwrap();\n            let evidence_fixed = if let Some(evidence_fixed) = evidence_iter.next() {\n                evidence_fixed\n            } else {\n                return;\n            };\n\n            self.add_evidence(target_fixed, evidence_fixed);\n        }\n\n        if let Some(target_rest) = target_iter.next() {\n            self.add_evidence(target_rest, &evidence_iter.collect_rest());\n        }\n    }\n\n    /// Adds evidence that the target is a never\n    ///\n    /// The propagates the never in to nested types\n    fn add_evidence_never(&mut self, target_ty: &Ty<ty::Poly>) {\n        match target_ty {\n            Ty::Set(target_member) | Ty::Vectorof(target_member) => {\n                self.add_evidence(target_member, &Ty::never().into());\n            }\n            Ty::Map(target_map) => {\n                self.add_evidence(target_map.key(), &Ty::never().into());\n                self.add_evidence(target_map.value(), &Ty::never().into());\n            }\n            Ty::List(target_list) => {\n                for target_fixed in target_list.fixed() {\n                    self.add_evidence(target_fixed, &Ty::never().into());\n                }\n                self.add_evidence(target_list.rest(), &Ty::never().into());\n            }\n            Ty::Vector(target_members) => {\n                for target_member in target_members.iter() {\n                    self.add_evidence(target_member, &Ty::never().into());\n                }\n            }\n            _ => {}\n        }\n    }\n\n    fn add_evidence_ty(\n        &mut self,\n        target_poly: &ty::Ref<ty::Poly>,\n        target_ty: &Ty<ty::Poly>,\n        evidence_poly: &ty::Ref<ty::Poly>,\n        evidence_ty: &Ty<ty::Poly>,\n    ) {\n        match (target_ty, evidence_ty) {\n            (Ty::Set(target_member), Ty::Set(evidence_member)) => {\n                self.add_evidence(target_member, evidence_member);\n            }\n            (Ty::Map(target_map), Ty::Map(evidence_map)) => {\n                self.add_evidence(target_map.key(), evidence_map.key());\n                self.add_evidence(target_map.value(), evidence_map.value());\n            }\n            (Ty::List(target_list), Ty::List(evidence_list)) => {\n                self.add_evidence_list(target_list, evidence_list);\n            }\n            (Ty::Vector(target_members), Ty::Vector(evidence_members)) => {\n                for (target_member, evidence_member) in\n                    target_members.iter().zip(evidence_members.iter())\n                {\n                    self.add_evidence(target_member, evidence_member);\n                }\n            }\n            (Ty::Vectorof(target_member), Ty::Vectorof(evidence_member)) => {\n                self.add_evidence(target_member, evidence_member);\n            }\n            (Ty::Vectorof(target_member), Ty::Vector(evidence_members)) => {\n                for evidence_member in evidence_members.iter() {\n                    self.add_evidence(target_member, evidence_member);\n                }\n            }\n            (Ty::TopFun(target_top_fun), Ty::TopFun(evidence_top_fun)) => {\n                self.add_evidence_top_fun(target_top_fun, evidence_top_fun);\n            }\n            (Ty::TopFun(target_top_fun), Ty::Fun(evidence_fun)) => {\n                self.add_evidence_fun(target_top_fun, evidence_fun);\n            }\n            (Ty::TopFun(target_top_fun), Ty::TyPred(_) | Ty::EqPred) => {\n                self.add_evidence_top_fun(target_top_fun, &ty::TopFun::new_for_pred());\n            }\n            (Ty::Fun(target_fun), Ty::Fun(evidence_fun)) => {\n                self.add_evidence_fun(target_fun.top_fun(), evidence_fun);\n            }\n            (Ty::Fun(target_fun), Ty::TyPred(_) | Ty::EqPred) => {\n                self.add_evidence_top_fun(target_fun.top_fun(), &ty::TopFun::new_for_pred());\n            }\n            (Ty::Record(target_instance), Ty::Record(evidence_instance)) => {\n                self.add_evidence_record(target_instance, evidence_instance)\n            }\n            (Ty::Union(target_members), _) => {\n                for target_member in target_members.iter() {\n                    self.add_evidence(target_member, evidence_poly);\n                }\n            }\n            (_, Ty::Union(evidence_members)) => {\n                if evidence_members.is_empty() {\n                    self.add_evidence_never(target_ty);\n                } else {\n                    for evidence_member in evidence_members.iter() {\n                        self.add_evidence(target_poly, evidence_member);\n                    }\n                }\n            }\n            _ => {}\n        }\n    }\n\n    fn add_var_evidence(&mut self, tvar: &ty::TVarId, evidence_poly: &ty::Ref<ty::Poly>) {\n        if !self.selecting_tvars.contains(tvar)\n            || !ty::is_a::ty_ref_is_a(evidence_poly, tvar.bound())\n        {\n            return;\n        }\n\n        self.tvar_types\n            .entry(tvar.clone())\n            .and_modify(|existing| {\n                *existing = ty::unify::unify_to_ty_ref(existing, evidence_poly);\n            })\n            .or_insert_with(|| evidence_poly.clone());\n    }\n\n    pub fn add_evidence(\n        &mut self,\n        target_poly: &ty::Ref<ty::Poly>,\n        evidence_poly: &ty::Ref<ty::Poly>,\n    ) {\n        match target_poly {\n            ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),\n            ty::Ref::Fixed(target_ty) => {\n                let evidence_ty = evidence_poly.resolve_to_ty();\n                self.add_evidence_ty(target_poly, target_ty, evidence_poly, evidence_ty)\n            }\n        }\n    }\n\n    pub fn add_evidence_purity(\n        &mut self,\n        target_purity: &purity::Ref,\n        evidence_purity: &purity::Ref,\n    ) {\n        let pvar = if let purity::Ref::Var(pvar) = target_purity {\n            pvar\n        } else {\n            return;\n        };\n\n        if !self.selecting_pvars.contains(pvar) {\n            return;\n        }\n\n        self.pvar_purities\n            .entry(pvar.clone())\n            .and_modify(|existing| {\n                *existing = ty::unify::unify_purity_refs(existing, evidence_purity);\n            })\n            .or_insert_with(|| evidence_purity.clone());\n    }\n\n    /// Creates a `TyArgs` instance with any unselected variables set to their bound\n    pub fn into_poly_ty_args(mut self) -> TyArgs<ty::Poly> {\n        if self.selecting_pvars.len() != self.pvar_purities.len() {\n            for pvar in self.selecting_pvars {\n                if !self.pvar_purities.contains_key(pvar) {\n                    self.pvar_purities\n                        .insert(pvar.clone(), Purity::Impure.into());\n                }\n            }\n        }\n\n        if self.selecting_tvars.len() != self.tvar_types.len() {\n            for tvar in self.selecting_tvars {\n                if !self.tvar_types.contains_key(tvar) {\n                    self.tvar_types.insert(tvar.clone(), tvar.bound().clone());\n                }\n            }\n        }\n\n        TyArgs::new(self.pvar_purities, self.tvar_types)\n    }\n\n    /// Creates a `TyArgs` instance\n    ///\n    /// Any unselected polymorphic variables will return an error unless they have an non-`Any`\n    /// bound to use as a default.\n    pub fn into_complete_poly_ty_args(mut self) -> Result<TyArgs<ty::Poly>, Error<'vars>> {\n        if self.selecting_pvars.len() != self.pvar_purities.len() {\n            for pvar in self.selecting_pvars {\n                if !self.pvar_purities.contains_key(pvar) {\n                    return Err(Error::UnselectedPVar(pvar));\n                }\n            }\n        }\n\n        if self.selecting_tvars.len() != self.tvar_types.len() {\n            for tvar in self.selecting_tvars {\n                if !self.tvar_types.contains_key(tvar) {\n                    if tvar.bound() == &Ty::Any.into() {\n                        return Err(Error::UnselectedTVar(tvar));\n                    }\n\n                    self.tvar_types.insert(tvar.clone(), tvar.bound().clone());\n                }\n            }\n        }\n\n        Ok(TyArgs::new(self.pvar_purities, self.tvar_types))\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::hir::ns::NsDatum;\n    use crate::hir::scope::Scope;\n    use crate::ty::purity::Purity;\n    use arret_syntax::parser::{data_from_str, datum_from_str};\n\n    struct TestScope {\n        scope: Scope<'static>,\n        pvars: purity::PVars,\n        tvars: ty::TVars,\n    }\n\n    impl TestScope {\n        #[allow(clippy::needless_collect)]\n        fn new(polymorphic_str: &str) -> TestScope {\n            use crate::hir::lower_polymorphic_var_set;\n\n            let outer_scope = Scope::new_with_primitives();\n            let mut inner_scope = Scope::new_with_primitives();\n\n            let polymorphic_data = data_from_str(None, polymorphic_str)\n                .unwrap()\n                .iter()\n                .map(NsDatum::from_syntax_datum)\n                .collect::<Vec<NsDatum>>();\n\n            let (pvars, tvars) = lower_polymorphic_var_set(\n                &outer_scope,\n                &mut inner_scope,\n                polymorphic_data.into_iter(),\n            )\n            .unwrap();\n\n            TestScope {\n                scope: inner_scope,\n                pvars,\n                tvars,\n            }\n        }\n\n        fn poly_for_str(&self, poly_str: &str) -> ty::Ref<ty::Poly> {\n            use crate::hir::lower_poly;\n            let test_datum = datum_from_str(None, poly_str).unwrap();\n\n            lower_poly(&self.scope, NsDatum::from_syntax_datum(&test_datum)).unwrap()\n        }\n\n        fn purity_for_str(&self, poly_str: &str) -> purity::Ref {\n            use crate::hir::try_lower_purity;\n            let test_datum = datum_from_str(None, poly_str).unwrap();\n\n            try_lower_purity(&self.scope, &NsDatum::from_syntax_datum(&test_datum)).unwrap()\n        }\n\n        fn select_ctx(&self) -> SelectCtx<'_> {\n            SelectCtx::new(&self.pvars, &self.tvars)\n        }\n    }\n\n    fn assert_unselected_type(ctx: &SelectCtx<'_>, poly_var: &ty::Ref<ty::Poly>) {\n        let tvar = if let ty::Ref::Var(tvar, _) = poly_var {\n            tvar\n        } else {\n            panic!(\"Can't find tvar ID\")\n        };\n\n        assert_eq!(None, ctx.tvar_types.get(tvar));\n    }\n\n    fn assert_selected_type(\n        ctx: &SelectCtx<'_>,\n        poly_var: &ty::Ref<ty::Poly>,\n        selected_poly: &ty::Ref<ty::Poly>,\n    ) {\n        let tvar = if let ty::Ref::Var(tvar, _) = poly_var {\n            tvar\n        } else {\n            panic!(\"Can't find tvar ID\")\n        };\n\n        assert_eq!(Some(selected_poly), ctx.tvar_types.get(tvar));\n    }\n\n    fn assert_unselected_purity(ctx: &SelectCtx<'_>, poly_var: &purity::Ref) {\n        let pvar = if let purity::Ref::Var(pvar) = poly_var {\n            pvar\n        } else {\n            panic!(\"Can't find pvar ID\")\n        };\n\n        assert_eq!(None, ctx.pvar_purities.get(pvar));\n    }\n\n    fn assert_selected_purity(\n        ctx: &SelectCtx<'_>,\n        poly_var: &purity::Ref,\n        selected_purity: Purity,\n    ) {\n        let pvar = if let purity::Ref::Var(pvar) = poly_var {\n            pvar\n        } else {\n            panic!(\"Can't find pvar ID\")\n        };\n\n        assert_eq!(\n            Some(&purity::Ref::Fixed(selected_purity)),\n            ctx.pvar_purities.get(pvar)\n        );\n    }\n\n    #[test]\n    fn trivial_tvar() {\n        let scope = TestScope::new(\"A\");\n\n        let poly_a = scope.poly_for_str(\"A\");\n        let mut stx = scope.select_ctx();\n        assert_unselected_type(&stx, &poly_a);\n\n        stx.add_evidence(&poly_a, &scope.poly_for_str(\"true\"));\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"true\"));\n\n        stx.add_evidence(&poly_a, &scope.poly_for_str(\"false\"));\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"Bool\"));\n    }\n\n    #[test]\n    fn poly_conflicing_tvar() {\n        let scope = TestScope::new(\"[A (... -> Any)] [B (... -> Sym)] [C (... -> 'foo)]\");\n\n        let poly_a = scope.poly_for_str(\"A\");\n        let poly_b = scope.poly_for_str(\"B\");\n        let poly_c = scope.poly_for_str(\"C\");\n\n        let mut stx = scope.select_ctx();\n        assert_unselected_type(&stx, &poly_a);\n\n        // We can handle one tvar as evidence\n        stx.add_evidence(&poly_a, &poly_b);\n        assert_selected_type(&stx, &poly_a, &poly_b);\n\n        stx.add_evidence(&poly_a, &poly_c);\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"(U B C)\"));\n    }\n\n    #[test]\n    fn set_types() {\n        let scope = TestScope::new(\"A\");\n\n        let poly_a = scope.poly_for_str(\"A\");\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(Setof A)\"),\n            &scope.poly_for_str(\"(Setof Bool)\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"Bool\"));\n    }\n\n    #[test]\n    fn map_types() {\n        let scope = TestScope::new(\"A B\");\n        let poly_a = scope.poly_for_str(\"A\");\n        let poly_b = scope.poly_for_str(\"B\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(Map A B)\"),\n            &scope.poly_for_str(\"(Map true false)\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"true\"));\n        assert_selected_type(&stx, &poly_b, &scope.poly_for_str(\"false\"));\n    }\n\n    #[test]\n    fn fixed_list_types() {\n        let scope = TestScope::new(\"A B\");\n        let poly_a = scope.poly_for_str(\"A\");\n        let poly_b = scope.poly_for_str(\"B\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(List A B)\"),\n            &scope.poly_for_str(\"(List true false)\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"true\"));\n        assert_selected_type(&stx, &poly_b, &scope.poly_for_str(\"false\"));\n    }\n\n    #[test]\n    fn listof_types() {\n        let scope = TestScope::new(\"A\");\n        let poly_a = scope.poly_for_str(\"A\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(List & A)\"),\n            &scope.poly_for_str(\"(List & true)\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"true\"));\n    }\n\n    #[test]\n    fn listof_from_fixed_list() {\n        let scope = TestScope::new(\"A\");\n        let poly_a = scope.poly_for_str(\"A\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(List & A)\"),\n            &scope.poly_for_str(\"(List true false)\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"Bool\"));\n    }\n\n    #[test]\n    fn listof_from_list_union() {\n        let scope = TestScope::new(\"A\");\n        let poly_a = scope.poly_for_str(\"A\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(List & A)\"),\n            &scope.poly_for_str(\"(U (List Int Int) (List Int Int Int))\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"Int\"));\n    }\n\n    #[test]\n    fn fixed_vector_types() {\n        let scope = TestScope::new(\"A B\");\n        let poly_a = scope.poly_for_str(\"A\");\n        let poly_b = scope.poly_for_str(\"B\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(Vector A B)\"),\n            &scope.poly_for_str(\"(Vector true false)\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"true\"));\n        assert_selected_type(&stx, &poly_b, &scope.poly_for_str(\"false\"));\n    }\n\n    #[test]\n    fn vectorof_types() {\n        let scope = TestScope::new(\"A\");\n        let poly_a = scope.poly_for_str(\"A\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(Vectorof A)\"),\n            &scope.poly_for_str(\"(Vectorof true)\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"true\"));\n    }\n\n    #[test]\n    fn vectorof_from_fixed_vector() {\n        let scope = TestScope::new(\"A\");\n        let poly_a = scope.poly_for_str(\"A\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(Vectorof A)\"),\n            &scope.poly_for_str(\"(Vector true false)\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"Bool\"));\n    }\n\n    #[test]\n    fn union_types() {\n        let scope = TestScope::new(\"A\");\n        let poly_a = scope.poly_for_str(\"A\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(U A Sym)\"),\n            &scope.poly_for_str(\"true\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"true\"));\n    }\n\n    #[test]\n    fn bounded_union_types() {\n        let scope = TestScope::new(\"[A Sym] [B Bool]\");\n        let poly_a = scope.poly_for_str(\"A\");\n        let poly_b = scope.poly_for_str(\"B\");\n\n        let mut stx = scope.select_ctx();\n\n        // A and B are bounded. We should ensure we only use evidence on the members with satisfied\n        // bounds.\n        stx.add_evidence(&scope.poly_for_str(\"(U A B)\"), &scope.poly_for_str(\"'foo\"));\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"'foo\"));\n        assert_unselected_type(&stx, &poly_b);\n\n        stx.add_evidence(&scope.poly_for_str(\"(U A B)\"), &scope.poly_for_str(\"true\"));\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"'foo\"));\n        assert_selected_type(&stx, &poly_b, &scope.poly_for_str(\"true\"));\n\n        stx.add_evidence(&scope.poly_for_str(\"(U A B)\"), &scope.poly_for_str(\"false\"));\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"'foo\"));\n        assert_selected_type(&stx, &poly_b, &scope.poly_for_str(\"Bool\"));\n    }\n\n    #[test]\n    fn top_fun_types() {\n        let scope = TestScope::new(\"A\");\n        let poly_a = scope.poly_for_str(\"A\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(... -> A)\"),\n            &scope.poly_for_str(\"(... -> true)\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"true\"));\n    }\n\n    #[test]\n    fn top_fun_purities() {\n        let scope = TestScope::new(\"[->A ->!]\");\n        let purity_a = scope.purity_for_str(\"->A\");\n\n        let mut stx = scope.select_ctx();\n        assert_unselected_purity(&stx, &purity_a);\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(... ->A true)\"),\n            &scope.poly_for_str(\"(... -> true)\"),\n        );\n        assert_selected_purity(&stx, &purity_a, Purity::Pure);\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(... ->A true)\"),\n            &scope.poly_for_str(\"(... ->! true)\"),\n        );\n        assert_selected_purity(&stx, &purity_a, Purity::Impure);\n    }\n\n    #[test]\n    fn top_fun_from_fun() {\n        let scope = TestScope::new(\"A\");\n        let poly_a = scope.poly_for_str(\"A\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(... -> A)\"),\n            &scope.poly_for_str(\"(false -> true)\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"true\"));\n    }\n\n    #[test]\n    fn top_fun_from_poly_fun() {\n        let scope = TestScope::new(\"Outer [->_ ->!]\");\n        let poly_outer = scope.poly_for_str(\"Outer\");\n        let poly_purity = scope.purity_for_str(\"->_\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(... ->_ Outer)\"),\n            // This has polymorphic types but monomorphic purity\n            &scope.poly_for_str(\"(All #{[Inner Num]} Inner -> Inner)\"),\n        );\n\n        assert_unselected_type(&stx, &poly_outer);\n        assert_selected_purity(&stx, &poly_purity, Purity::Pure);\n    }\n\n    #[test]\n    fn top_fun_from_ty_pred() {\n        let scope = TestScope::new(\"A\");\n        let poly_a = scope.poly_for_str(\"A\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(... -> A)\"),\n            &scope.poly_for_str(\"sym?\"),\n        );\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"Bool\"));\n    }\n\n    #[test]\n    fn top_fun_from_eq_pred() {\n        let scope = TestScope::new(\"A\");\n        let poly_a = scope.poly_for_str(\"A\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(&scope.poly_for_str(\"(... -> A)\"), &scope.poly_for_str(\"=\"));\n        assert_selected_type(&stx, &poly_a, &scope.poly_for_str(\"Bool\"));\n    }\n\n    #[test]\n    fn fun_types() {\n        let scope = TestScope::new(\"A B\");\n        let poly_a = scope.poly_for_str(\"A\");\n        let poly_b = scope.poly_for_str(\"B\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(A -> B)\"),\n            &scope.poly_for_str(\"(true -> false)\"),\n        );\n        // We intentionally do not use function type params as evidence\n        assert_unselected_type(&stx, &poly_a);\n        assert_selected_type(&stx, &poly_b, &scope.poly_for_str(\"false\"));\n    }\n\n    #[test]\n    fn fun_purities() {\n        let scope = TestScope::new(\"[->A ->!]\");\n        let purity_a = scope.purity_for_str(\"->A\");\n\n        let mut stx = scope.select_ctx();\n        assert_unselected_purity(&stx, &purity_a);\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(->A true)\"),\n            &scope.poly_for_str(\"(->! true)\"),\n        );\n        assert_selected_purity(&stx, &purity_a, Purity::Impure);\n    }\n\n    #[test]\n    fn fun_purity_conflict() {\n        let scope = TestScope::new(\"[->A ->!] [->B ->!] [->C ->!]\");\n        let purity_a = scope.purity_for_str(\"->A\");\n\n        let mut stx = scope.select_ctx();\n        assert_unselected_purity(&stx, &purity_a);\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(->A true)\"),\n            &scope.poly_for_str(\"(->B true)\"),\n        );\n        stx.add_evidence(\n            &scope.poly_for_str(\"(->A true)\"),\n            &scope.poly_for_str(\"(->C true)\"),\n        );\n\n        assert_selected_purity(&stx, &purity_a, Purity::Impure);\n    }\n\n    #[test]\n    fn fun_type_from_ty_pred() {\n        let scope = TestScope::new(\"A B\");\n        let poly_a = scope.poly_for_str(\"A\");\n        let poly_b = scope.poly_for_str(\"B\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(&scope.poly_for_str(\"(A -> B)\"), &scope.poly_for_str(\"sym?\"));\n        assert_unselected_type(&stx, &poly_a);\n        assert_selected_type(&stx, &poly_b, &scope.poly_for_str(\"Bool\"));\n    }\n\n    #[test]\n    fn fun_type_from_eq_pred() {\n        let scope = TestScope::new(\"A B\");\n        let poly_a = scope.poly_for_str(\"A\");\n        let poly_b = scope.poly_for_str(\"B\");\n\n        let mut stx = scope.select_ctx();\n\n        stx.add_evidence(&scope.poly_for_str(\"(A A -> B)\"), &scope.poly_for_str(\"=\"));\n        assert_unselected_type(&stx, &poly_a);\n        assert_selected_type(&stx, &poly_b, &scope.poly_for_str(\"Bool\"));\n    }\n\n    #[test]\n    fn ty_pred_purity() {\n        let scope = TestScope::new(\"[->A ->!]\");\n        let purity_a = scope.purity_for_str(\"->A\");\n\n        let mut stx = scope.select_ctx();\n        assert_unselected_purity(&stx, &purity_a);\n\n        stx.add_evidence(\n            &scope.poly_for_str(\"(->A true)\"),\n            &scope.poly_for_str(\"sym?\"),\n        );\n        assert_selected_purity(&stx, &purity_a, Purity::Pure);\n    }\n}\n"
  },
  {
    "path": "compiler/ty/subst.rs",
    "content": "use crate::ty;\nuse crate::ty::purity;\nuse crate::ty::record;\nuse crate::ty::ty_args::TyArgs;\nuse crate::ty::Ty;\n\nfn subst_ty_ref_slice<S>(stx: &S, inputs: &[ty::Ref<S::InputPM>]) -> Box<[ty::Ref<S::OutputPM>]>\nwhere\n    S: Substitution,\n{\n    inputs.iter().map(|i| stx.subst_ty_ref(i)).collect()\n}\n\nfn subst_list<S>(stx: &S, list: &ty::List<S::InputPM>) -> ty::List<S::OutputPM>\nwhere\n    S: Substitution,\n{\n    ty::List::new(\n        subst_ty_ref_slice(stx, list.fixed()),\n        stx.subst_ty_ref(list.rest()),\n    )\n}\n\nfn subst_record_instance<S>(\n    stx: &S,\n    instance: &record::Instance<S::InputPM>,\n) -> record::Instance<S::OutputPM>\nwhere\n    S: Substitution,\n{\n    let subst_pvar_purities = instance\n        .ty_args()\n        .pvar_purities()\n        .iter()\n        .map(|(pvar, purity_ref)| (pvar.clone(), stx.subst_purity_ref(purity_ref)))\n        .collect();\n\n    let subst_tvar_types = instance\n        .ty_args()\n        .tvar_types()\n        .iter()\n        .map(|(tvar, ty_ref)| (tvar.clone(), stx.subst_ty_ref(ty_ref)))\n        .collect();\n\n    record::Instance::new(\n        instance.cons().clone(),\n        TyArgs::new(subst_pvar_purities, subst_tvar_types),\n    )\n}\n\nfn subst_top_fun<S>(stx: &S, top_fun: &ty::TopFun) -> ty::TopFun\nwhere\n    S: Substitution,\n{\n    let poly_stx = stx.as_poly_subst();\n\n    ty::TopFun::new(\n        poly_stx.subst_purity_ref(top_fun.purity()),\n        poly_stx.subst_ty_ref(top_fun.ret()),\n    )\n}\n\nfn subst_fun<S>(stx: &S, fun: &ty::Fun) -> ty::Fun\nwhere\n    S: Substitution,\n{\n    let poly_stx = stx.as_poly_subst();\n\n    // TODO: This doesn't seem right\n    ty::Fun::new(\n        purity::PVars::new(),\n        ty::TVars::new(),\n        subst_top_fun(stx, fun.top_fun()),\n        subst_list(poly_stx, fun.params()),\n    )\n}\n\nfn subst_ty<S>(stx: &S, ty: &Ty<S::InputPM>) -> Ty<S::OutputPM>\nwhere\n    S: Substitution,\n{\n    match ty {\n        Ty::Any => Ty::Any,\n        Ty::Bool => Ty::Bool,\n        Ty::Char => Ty::Char,\n        Ty::Float => Ty::Float,\n        Ty::Int => Ty::Int,\n        Ty::Num => Ty::Num,\n        Ty::Str => Ty::Str,\n        Ty::Sym => Ty::Sym,\n        Ty::EqPred => Ty::EqPred,\n        Ty::TopRecord => Ty::TopRecord,\n\n        Ty::TyPred(test_ty) => Ty::TyPred(test_ty.clone()),\n        Ty::TopFun(top_fun) => subst_top_fun(stx, top_fun).into(),\n        Ty::Fun(fun) => subst_fun(stx, fun).into(),\n        Ty::Map(map) => {\n            ty::Map::new(stx.subst_ty_ref(map.key()), stx.subst_ty_ref(map.value())).into()\n        }\n        Ty::LitBool(val) => Ty::LitBool(*val),\n        Ty::LitSym(val) => Ty::LitSym(val.clone()),\n        Ty::Set(member) => Ty::Set(Box::new(stx.subst_ty_ref(member))),\n        Ty::Union(members) => Ty::Union(subst_ty_ref_slice(stx, members)),\n        Ty::Intersect(members) => Ty::Intersect(subst_ty_ref_slice(stx, members)),\n        Ty::Vector(members) => Ty::Vector(subst_ty_ref_slice(stx, members)),\n        Ty::Vectorof(member) => Ty::Vectorof(Box::new(stx.subst_ty_ref(member))),\n        Ty::List(list) => subst_list(stx, list).into(),\n        Ty::RecordClass(cons) => Ty::RecordClass(cons.clone()),\n        Ty::Record(instance) => Ty::Record(Box::new(subst_record_instance(stx, instance))),\n    }\n}\n\ntrait Substitution {\n    type InputPM: ty::Pm;\n    type OutputPM: ty::Pm;\n    type AsPolySubst: Substitution<InputPM = ty::Poly, OutputPM = ty::Poly>;\n\n    fn subst_purity_ref(&self, poly: &purity::Ref) -> purity::Ref;\n    fn subst_ty_ref(&self, input: &ty::Ref<Self::InputPM>) -> ty::Ref<Self::OutputPM>;\n    fn as_poly_subst(&self) -> &Self::AsPolySubst;\n}\n\nimpl<'tvars> Substitution for TyArgs<ty::Poly> {\n    type InputPM = ty::Poly;\n    type OutputPM = ty::Poly;\n    type AsPolySubst = Self;\n\n    fn subst_purity_ref(&self, poly: &purity::Ref) -> purity::Ref {\n        match poly {\n            purity::Ref::Fixed(_) => poly.clone(),\n            purity::Ref::Var(pvar) => {\n                if let Some(selected) = self.pvar_purities().get(pvar) {\n                    selected.clone()\n                } else {\n                    poly.clone()\n                }\n            }\n        }\n    }\n\n    fn subst_ty_ref(&self, poly: &ty::Ref<ty::Poly>) -> ty::Ref<ty::Poly> {\n        match poly {\n            ty::Ref::Fixed(fixed) => subst_ty(self, fixed).into(),\n            ty::Ref::Var(tvar, _) => {\n                if let Some(selected) = self.tvar_types().get(tvar) {\n                    selected.clone()\n                } else {\n                    poly.clone()\n                }\n            }\n        }\n    }\n\n    fn as_poly_subst(&self) -> &Self {\n        self\n    }\n}\n\nstruct PolyIdentity {}\n\nimpl Substitution for PolyIdentity {\n    type InputPM = ty::Poly;\n    type OutputPM = ty::Poly;\n    type AsPolySubst = Self;\n\n    fn subst_purity_ref(&self, poly: &purity::Ref) -> purity::Ref {\n        poly.clone()\n    }\n\n    fn subst_ty_ref(&self, poly: &ty::Ref<ty::Poly>) -> ty::Ref<ty::Poly> {\n        poly.clone()\n    }\n\n    fn as_poly_subst(&self) -> &Self {\n        self\n    }\n}\n\nstruct MonoToPoly {\n    poly_identity: PolyIdentity,\n}\n\nimpl MonoToPoly {\n    fn new() -> MonoToPoly {\n        MonoToPoly {\n            poly_identity: PolyIdentity {},\n        }\n    }\n}\n\nimpl Substitution for MonoToPoly {\n    type InputPM = ty::Mono;\n    type OutputPM = ty::Poly;\n    type AsPolySubst = PolyIdentity;\n\n    fn subst_purity_ref(&self, poly: &purity::Ref) -> purity::Ref {\n        poly.clone()\n    }\n\n    fn subst_ty_ref(&self, mono: &ty::Ref<ty::Mono>) -> ty::Ref<ty::Poly> {\n        subst_ty(self, mono.as_ty()).into()\n    }\n\n    fn as_poly_subst(&self) -> &PolyIdentity {\n        &self.poly_identity\n    }\n}\n\nstruct Monomorphise<'tyargs> {\n    mono_ty_args: &'tyargs TyArgs<ty::Mono>,\n    partial: PartialMonomorphise<'tyargs>,\n}\n\nimpl<'tyargs> Monomorphise<'tyargs> {\n    fn new(mta: &'tyargs TyArgs<ty::Mono>) -> Monomorphise<'tyargs> {\n        Monomorphise {\n            mono_ty_args: mta,\n            partial: PartialMonomorphise { mono_ty_args: mta },\n        }\n    }\n}\n\nimpl<'tyargs> Substitution for Monomorphise<'tyargs> {\n    type InputPM = ty::Poly;\n    type OutputPM = ty::Mono;\n    type AsPolySubst = PartialMonomorphise<'tyargs>;\n\n    fn subst_purity_ref(&self, poly: &purity::Ref) -> purity::Ref {\n        match poly {\n            purity::Ref::Fixed(_) => poly.clone(),\n            purity::Ref::Var(pvar) => self\n                .mono_ty_args\n                .pvar_purities()\n                .get(pvar)\n                .unwrap_or_else(|| {\n                    panic!(\n                        \"unable to find purity argument `{}` during monomorphisation\",\n                        pvar.source_name()\n                    )\n                })\n                .clone(),\n        }\n    }\n\n    fn subst_ty_ref(&self, poly: &ty::Ref<ty::Poly>) -> ty::Ref<ty::Mono> {\n        match poly {\n            ty::Ref::Fixed(fixed) => subst_ty(self, fixed).into(),\n            ty::Ref::Var(tvar, _) => self\n                .mono_ty_args\n                .tvar_types()\n                .get(tvar)\n                .unwrap_or_else(|| {\n                    panic!(\n                        \"unable to find type argument `{}` during monomorphisation\",\n                        tvar.source_name()\n                    )\n                })\n                .clone(),\n        }\n    }\n\n    fn as_poly_subst(&self) -> &PartialMonomorphise<'tyargs> {\n        &self.partial\n    }\n}\n\nstruct PartialMonomorphise<'tyargs> {\n    mono_ty_args: &'tyargs TyArgs<ty::Mono>,\n}\n\nimpl<'tyargs> Substitution for PartialMonomorphise<'tyargs> {\n    type InputPM = ty::Poly;\n    type OutputPM = ty::Poly;\n    type AsPolySubst = Self;\n\n    fn subst_purity_ref(&self, poly: &purity::Ref) -> purity::Ref {\n        match poly {\n            purity::Ref::Fixed(_) => poly.clone(),\n            purity::Ref::Var(pvar) => {\n                if let Some(purity) = self.mono_ty_args.pvar_purities().get(pvar) {\n                    purity.clone()\n                } else {\n                    poly.clone()\n                }\n            }\n        }\n    }\n\n    fn subst_ty_ref(&self, poly: &ty::Ref<ty::Poly>) -> ty::Ref<ty::Poly> {\n        match poly {\n            ty::Ref::Fixed(fixed) => subst_ty(self, fixed).into(),\n            ty::Ref::Var(tvar, _) => {\n                if let Some(mono) = self.mono_ty_args.tvar_types().get(tvar) {\n                    subst_ty(&MonoToPoly::new(), mono.as_ty()).into()\n                } else {\n                    poly.clone()\n                }\n            }\n        }\n    }\n\n    fn as_poly_subst(&self) -> &Self {\n        self\n    }\n}\n\npub fn subst_poly(pta: &TyArgs<ty::Poly>, poly: &ty::Ref<ty::Poly>) -> ty::Ref<ty::Poly> {\n    pta.subst_ty_ref(poly)\n}\n\npub fn subst_poly_fun(pta: &TyArgs<ty::Poly>, fun: &ty::Fun) -> ty::Fun {\n    subst_fun(pta, fun)\n}\n\npub fn subst_purity(pta: &TyArgs<ty::Poly>, purity: &purity::Ref) -> purity::Ref {\n    pta.subst_purity_ref(purity)\n}\n\npub fn monomorphise(mta: &TyArgs<ty::Mono>, poly: &ty::Ref<ty::Poly>) -> ty::Ref<ty::Mono> {\n    let stx = Monomorphise::new(mta);\n    stx.subst_ty_ref(poly)\n}\n\npub fn monomorphise_purity(mta: &TyArgs<ty::Mono>, poly: &purity::Ref) -> purity::Ref {\n    let stx = Monomorphise::new(mta);\n    stx.subst_purity_ref(poly)\n}\n\npub fn monomorphise_list(mta: &TyArgs<ty::Mono>, poly: &ty::List<ty::Poly>) -> ty::List<ty::Mono> {\n    let stx = Monomorphise::new(mta);\n    subst_list(&stx, poly)\n}\n"
  },
  {
    "path": "compiler/ty/subtract.rs",
    "content": "use crate::ty;\nuse crate::ty::Ty;\n\nfn subtract_ref_iters<'a, I, M>(minuend_iter: I, subtrahend_ref: &ty::Ref<M>) -> ty::Ref<M>\nwhere\n    I: Iterator<Item = &'a ty::Ref<M>>,\n    M: ty::Pm + 'a,\n{\n    ty::unify::unify_ty_ref_iter(\n        minuend_iter.map(|minuend_ref| subtract_ty_refs(minuend_ref, subtrahend_ref)),\n    )\n}\n\nfn subtract_tys<M: ty::Pm>(\n    minuend_ty: &Ty<M>,\n    subtrahend_ref: &ty::Ref<M>,\n    subtrahend_ty: &Ty<M>,\n) -> ty::Ref<M> {\n    match (minuend_ty, subtrahend_ty) {\n        (Ty::Bool, _) => subtract_ref_iters(\n            [\n                Ty::LitBool(false).into(),\n                Ty::LitBool(true).into(),\n            ]\n                .iter(),\n            subtrahend_ref,\n        ),\n        (Ty::Num, _) => subtract_ref_iters(\n            [\n                Ty::Int.into(),\n                Ty::Float.into(),\n            ]\n                .iter(),\n            subtrahend_ref,\n        ),\n        (Ty::Union(members), _) => subtract_ref_iters(members.iter(), subtrahend_ref),\n        (Ty::List(minuend_list), Ty::List(subtrahend_list))\n            // Make sure this is even useful or else we can recurse splitting list types\n            // indefinitely\n            if !subtrahend_list.has_rest() && minuend_list.fixed().len() == subtrahend_list.fixed().len() =>\n        {\n            // This is required for `(nil?)` to work correctly\n            let minued_rest = minuend_list.rest();\n            if !minued_rest.is_never() {\n                // This is the list type if we have no rest elements\n                let terminated_list =\n                    ty::List::new_tuple(minuend_list.fixed().to_vec().into_boxed_slice());\n\n                // This is the list type if we have at least one rest element\n                let mut continued_fixed = minuend_list.fixed().to_vec();\n                continued_fixed.push(minued_rest.clone());\n                let continued_list =\n                    ty::List::new(continued_fixed.into_boxed_slice(), minued_rest.clone());\n\n                subtract_ref_iters(\n                    [\n                        terminated_list.into(),\n                        continued_list.into(),\n                    ].iter(),\n                    subtrahend_ref,\n                )\n            } else {\n                minuend_ty.clone().into()\n            }\n        },\n        _ => minuend_ty.clone().into(),\n    }\n}\n\npub fn subtract_ty_refs<M: ty::Pm>(\n    minuend_ref: &ty::Ref<M>,\n    subtrahend_ref: &ty::Ref<M>,\n) -> ty::Ref<M> {\n    use crate::ty::intersect;\n\n    if ty::is_a::ty_ref_is_a(minuend_ref, subtrahend_ref) {\n        // No type remains\n        Ty::Union(Box::new([])).into()\n    } else {\n        match (minuend_ref, subtrahend_ref) {\n            (ty::Ref::Fixed(minuend_ty), ty::Ref::Fixed(subtrahend_ty)) => {\n                // We can subtract directly\n                subtract_tys(minuend_ty, subtrahend_ref, subtrahend_ty)\n            }\n            (ty::Ref::Var(_, _), ty::Ref::Fixed(subtrahend_ty)) => {\n                // We can refine the bound using an intersection type\n                let minuend_bound_ty = minuend_ref.resolve_to_ty();\n                let refined_bound_poly =\n                    subtract_tys(minuend_bound_ty, subtrahend_ref, subtrahend_ty);\n\n                intersect::intersect_ty_refs(minuend_ref, &refined_bound_poly)\n                    .unwrap_or_else(|_| minuend_ref.clone())\n            }\n            _ => minuend_ref.clone(),\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use std::collections::HashMap;\n\n    use crate::hir::{poly_for_str, tvar_bounded_by};\n    use crate::source::EMPTY_SPAN;\n    use crate::ty::record;\n    use crate::ty::ty_args::TyArgs;\n    use crate::ty::var_usage::Variance;\n\n    fn assert_subtraction(expected_str: &str, minuend_str: &str, subrahend_str: &str) {\n        let expected_poly = poly_for_str(expected_str);\n        let minuend_poly = poly_for_str(minuend_str);\n        let subtrahend_poly = poly_for_str(subrahend_str);\n\n        let actual_poly = subtract_ty_refs(&minuend_poly, &subtrahend_poly);\n        assert_eq!(expected_poly, actual_poly);\n    }\n\n    #[test]\n    fn trivial_subtraction() {\n        assert_subtraction(\"Int\", \"Int\", \"Float\");\n        assert_subtraction(\"(RawU)\", \"Int\", \"Int\");\n    }\n\n    #[test]\n    fn bool_subtraction() {\n        assert_subtraction(\"true\", \"Bool\", \"false\");\n        assert_subtraction(\"false\", \"Bool\", \"true\");\n    }\n\n    #[test]\n    fn num_subtraction() {\n        assert_subtraction(\"Float\", \"Num\", \"Int\");\n        assert_subtraction(\"Int\", \"Num\", \"Float\");\n    }\n\n    #[test]\n    fn union_subtraction() {\n        assert_subtraction(\"Sym\", \"(RawU Sym Str)\", \"Str\");\n        assert_subtraction(\n            \"(RawU 'foo 'bar)\",\n            \"(RawU 'foo 'bar 'baz 'foobar)\",\n            \"(RawU 'baz 'foobar)\",\n        );\n    }\n\n    #[test]\n    fn list_subtraction() {\n        assert_subtraction(\"(List Int & Int)\", \"(List & Int)\", \"()\");\n    }\n\n    #[test]\n    fn poly_substraction() {\n        let ptype1_unbounded = tvar_bounded_by(Ty::Any.into());\n        let ptype2_sym = tvar_bounded_by(Ty::Sym.into());\n        let ptype3_num = tvar_bounded_by(Ty::Num.into());\n\n        let any_float = poly_for_str(\"Float\");\n        let any_int = poly_for_str(\"Int\");\n        let foo_sym = poly_for_str(\"'foo\");\n\n        // PType1 - 'foo = PType1\n        assert_eq!(\n            ptype1_unbounded,\n            subtract_ty_refs(&ptype1_unbounded, &foo_sym)\n        );\n\n        // [PType2 Sym] - 'foo = PType1\n        assert_eq!(ptype2_sym, subtract_ty_refs(&ptype2_sym, &foo_sym));\n\n        // [PType3 Num] - Float = (∩ PType3 Int)\n        let ptype3_int_intersect: ty::Ref<ty::Poly> =\n            Ty::Intersect(Box::new([ptype3_num.clone(), any_int])).into();\n\n        assert_eq!(\n            ptype3_int_intersect,\n            subtract_ty_refs(&ptype3_num, &any_float)\n        );\n    }\n\n    #[test]\n    fn poly_record_type() {\n        let tvar = ty::TVar::new(EMPTY_SPAN, \"tvar\".into(), Ty::Any.into());\n\n        // Polymorphic record constructor and top type\n        let poly_record_cons = record::Cons::new(\n            EMPTY_SPAN,\n            \"record_cons\".into(),\n            \"record_cons?\".into(),\n            Some(Box::new([record::PolyParam::TVar(\n                Variance::Covariant,\n                tvar.clone(),\n            )])),\n            Box::new([record::Field::new(\n                EMPTY_SPAN,\n                \"num\".into(),\n                tvar.clone().into(),\n            )]),\n        );\n\n        let record_class_ref: ty::Ref<ty::Poly> = poly_record_cons.clone().into();\n\n        // Instance parameterised with an `Int`\n        let mut int_tvars = HashMap::new();\n        int_tvars.insert(tvar, Ty::Int.into());\n        let int_ty_args = TyArgs::new(HashMap::new(), int_tvars);\n\n        let int_instance_ref: ty::Ref<ty::Poly> =\n            record::Instance::new(poly_record_cons, int_ty_args).into();\n\n        // Record class minus an instance is the record class\n        assert_eq!(\n            record_class_ref,\n            subtract_ty_refs(&record_class_ref, &int_instance_ref)\n        );\n\n        // Instance minus the record class is nothing\n        let never_ref: ty::Ref<ty::Poly> = Ty::never().into();\n        assert_eq!(\n            never_ref,\n            subtract_ty_refs(&int_instance_ref, &record_class_ref)\n        );\n    }\n}\n"
  },
  {
    "path": "compiler/ty/ty_args.rs",
    "content": "use std::collections::HashMap;\n\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\n\n/// Type arguments to a polymorphic function or substitution\n#[derive(PartialEq, Clone, Debug)]\npub struct TyArgs<M: ty::Pm> {\n    pvar_purities: HashMap<purity::PVarId, purity::Ref>,\n    tvar_types: HashMap<ty::TVarId, ty::Ref<M>>,\n}\n\nimpl<M: ty::Pm> TyArgs<M> {\n    pub fn new(\n        pvar_purities: HashMap<purity::PVarId, purity::Ref>,\n        tvar_types: HashMap<ty::TVarId, ty::Ref<M>>,\n    ) -> Self {\n        Self {\n            pvar_purities,\n            tvar_types,\n        }\n    }\n\n    pub fn empty() -> Self {\n        Self {\n            pvar_purities: HashMap::new(),\n            tvar_types: HashMap::new(),\n        }\n    }\n\n    pub fn pvar_purities(&self) -> &HashMap<purity::PVarId, purity::Ref> {\n        &self.pvar_purities\n    }\n\n    pub fn tvar_types(&self) -> &HashMap<ty::TVarId, ty::Ref<M>> {\n        &self.tvar_types\n    }\n}\n\nimpl TyArgs<ty::Poly> {\n    /// Returns the args for the passed pvars/tvars where all args are set to their upper bound\n    pub fn from_upper_bound(pvars: &[purity::PVarId], tvars: &[ty::TVarId]) -> Self {\n        let pvar_purities = pvars\n            .iter()\n            .map(|pvar| (pvar.clone(), Purity::Impure.into()))\n            .collect();\n\n        let tvar_types = tvars\n            .iter()\n            .map(|tvar| (tvar.clone(), tvar.bound.clone()))\n            .collect();\n\n        Self {\n            pvar_purities,\n            tvar_types,\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/ty/unify.rs",
    "content": "//! Builds new types by unifying zero or more input types\n//!\n//! Every type can be distinguished from every other type at runtime. This would allow our most\n//! naive implementation to simply detect any duplicate or subtypes and remove them.\n//!\n//! However, while every type can be tested at runtime some type checks are very expensive. A\n//! pathological case would be testing if a long `(List & Any)` is a `(List & Int)`. We need to\n//! allow these checks for completeness but they should be discouraged. To that end, any types\n//! that would be expensive to distinguish at runtime are merged by this code. This ensures in the\n//! general case it should be quick to test for individual members of a union.\nuse std::cmp;\nuse std::iter;\n\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\nuse crate::ty::record;\nuse crate::ty::ty_args::TyArgs;\nuse crate::ty::var_usage::Variance;\nuse crate::ty::Ty;\n\n#[derive(Debug, PartialEq)]\nenum UnifiedTy<M: ty::Pm> {\n    /// The types are distinct and have no clean simplification\n    ///\n    /// An example would be Str and Sym.\n    Discerned,\n\n    /// The types can be simplified in to a single non-union type\n    ///\n    /// A trivial example would be Sym and 'foo because of their subtype relationship. More complex\n    /// per-type logic exists, especially surrounding sequences.\n    Merged(ty::Ref<M>),\n}\n\n#[derive(Debug, PartialEq)]\npub enum UnifiedList<M: ty::Pm> {\n    Discerned,\n    Merged(ty::List<M>),\n}\n\nfn unify_ty_refs<M: ty::Pm>(ref1: &ty::Ref<M>, ref2: &ty::Ref<M>) -> UnifiedTy<M> {\n    if let (ty::Ref::Fixed(ty1), ty::Ref::Fixed(ty2)) = (&ref1, &ref2) {\n        // We can invoke full simplification logic if we have fixed types\n        unify_ty(ref1, ty1, ref2, ty2)\n    } else if ty::is_a::ty_ref_is_a(ref1, ref2) {\n        UnifiedTy::Merged(ref2.clone())\n    } else if ty::is_a::ty_ref_is_a(ref2, ref1) {\n        UnifiedTy::Merged(ref1.clone())\n    } else {\n        // Leave these separate\n        UnifiedTy::Discerned\n    }\n}\n\nfn try_list_to_exact_pair<M: ty::Pm>(list: &ty::List<M>) -> Option<&ty::Ref<M>> {\n    if list.fixed.len() == 1 && &list.fixed[0] == list.rest.as_ref() {\n        Some(list.rest.as_ref())\n    } else {\n        None\n    }\n}\n\n/// Unifies a member in to an existing vector of members\n///\n/// It is assumed `output_members` refers to members of an already unified union.\nfn union_push<M: ty::Pm>(output_members: &mut Vec<ty::Ref<M>>, new_member: ty::Ref<M>) {\n    for i in 0..output_members.len() {\n        match unify_ty_refs(&output_members[i], &new_member) {\n            UnifiedTy::Merged(merged_member) => {\n                // Our merged type may now unify with one of the already processed members of the\n                // union. Remove the member we merged with and recurse using the merged member.\n                output_members.swap_remove(i);\n                return union_push(output_members, merged_member);\n            }\n            UnifiedTy::Discerned => {}\n        }\n    }\n\n    output_members.push(new_member);\n}\n\n/// Extends an existing union with new members\n///\n/// `existing_members` are assumed to be the members of an existing union. If there is no existing\n/// union this must be empty. This is an optimisation to avoid processing the already unified\n/// members.\nfn union_extend<M, I>(existing_members: Vec<ty::Ref<M>>, new_members: I) -> ty::Ref<M>\nwhere\n    M: ty::Pm,\n    I: Iterator<Item = ty::Ref<M>>,\n{\n    let mut output_members = existing_members;\n\n    for new_member in new_members {\n        union_push(&mut output_members, new_member);\n    }\n\n    ty::Ref::from_vec(output_members)\n}\n\nfn unify_top_fun<M: ty::Pm>(top_fun1: &ty::TopFun, top_fun2: &ty::TopFun) -> UnifiedTy<M> {\n    let unified_purity = unify_purity_refs(top_fun1.purity(), top_fun2.purity());\n    let unified_ret = unify_to_ty_ref(top_fun1.ret(), top_fun2.ret());\n\n    UnifiedTy::Merged(ty::TopFun::new(unified_purity, unified_ret).into())\n}\n\nfn unify_fun<M: ty::Pm>(fun1: &ty::Fun, fun2: &ty::Fun) -> UnifiedTy<M> {\n    let unified_purity = unify_purity_refs(fun1.purity(), fun2.purity());\n\n    if fun1.has_polymorphic_vars() || fun2.has_polymorphic_vars() {\n        // TODO: We could do better here by finding our upper bound and unifying them\n        // Preserving the polymorphicness would be very complex\n        UnifiedTy::Merged(ty::TopFun::new(unified_purity, Ty::Any.into()).into())\n    } else {\n        let unified_ret = unify_to_ty_ref(fun1.ret(), fun2.ret());\n\n        match ty::intersect::intersect_list(fun1.params(), fun2.params()) {\n            Ok(unified_params) => UnifiedTy::Merged(\n                ty::Fun::new_mono(unified_params, unified_purity, unified_ret).into(),\n            ),\n            Err(ty::intersect::Error::Disjoint) => {\n                UnifiedTy::Merged(ty::TopFun::new(unified_purity, unified_ret).into())\n            }\n        }\n    }\n}\n\nfn unify_record_field_purities<M: ty::Pm>(\n    variance: Variance,\n    pvar: &purity::PVarId,\n    ty_args1: &TyArgs<M>,\n    ty_args2: &TyArgs<M>,\n) -> purity::Ref {\n    use crate::ty::intersect::intersect_purity_refs;\n    use crate::ty::is_a::purity_refs_equivalent;\n\n    let purity_ref1 = &ty_args1.pvar_purities()[pvar];\n    let purity_ref2 = &ty_args2.pvar_purities()[pvar];\n\n    match variance {\n        Variance::Covariant => unify_purity_refs(purity_ref1, purity_ref2),\n        Variance::Contravariant => intersect_purity_refs(purity_ref1, purity_ref2),\n        Variance::Invariant => {\n            if purity_refs_equivalent(purity_ref1, purity_ref2) {\n                purity_ref1.clone()\n            } else {\n                Purity::Impure.into()\n            }\n        }\n    }\n}\n\nfn unify_record_field_ty_refs<M: ty::Pm>(\n    variance: Variance,\n    tvar: &ty::TVarId,\n    ty_args1: &TyArgs<M>,\n    ty_args2: &TyArgs<M>,\n) -> UnifiedTy<M> {\n    use crate::ty::intersect::intersect_ty_refs;\n    use crate::ty::is_a::ty_refs_equivalent;\n\n    let ty_ref1 = &ty_args1.tvar_types()[tvar];\n    let ty_ref2 = &ty_args2.tvar_types()[tvar];\n\n    match variance {\n        Variance::Covariant => unify_ty_refs(ty_ref1, ty_ref2),\n        Variance::Contravariant => match intersect_ty_refs(ty_ref1, ty_ref2) {\n            Ok(intersected) => UnifiedTy::Merged(intersected),\n            Err(_) => UnifiedTy::Discerned,\n        },\n        Variance::Invariant => {\n            if ty_refs_equivalent(ty_ref1, ty_ref2) {\n                UnifiedTy::Merged(ty_ref1.clone())\n            } else {\n                UnifiedTy::Discerned\n            }\n        }\n    }\n}\n\nfn unify_record_instance<M: ty::Pm>(\n    instance1: &record::Instance<M>,\n    instance2: &record::Instance<M>,\n) -> UnifiedTy<M> {\n    use crate::ty::record::PolyParam;\n    use std::collections::HashMap;\n\n    if instance1.cons() != instance2.cons() {\n        return UnifiedTy::Discerned;\n    }\n\n    let mut merged_pvar_purities = HashMap::new();\n    let mut merged_tvar_types = HashMap::new();\n\n    for poly_param in instance1.cons().poly_params() {\n        match poly_param {\n            PolyParam::PVar(variance, pvar) => {\n                merged_pvar_purities.insert(\n                    pvar.clone(),\n                    unify_record_field_purities(\n                        *variance,\n                        pvar,\n                        instance1.ty_args(),\n                        instance2.ty_args(),\n                    ),\n                );\n            }\n            PolyParam::TVar(variance, tvar) => {\n                let unified_ty = unify_record_field_ty_refs(\n                    *variance,\n                    tvar,\n                    instance1.ty_args(),\n                    instance2.ty_args(),\n                );\n\n                match unified_ty {\n                    UnifiedTy::Merged(merged) => {\n                        merged_tvar_types.insert(tvar.clone(), merged);\n                    }\n                    UnifiedTy::Discerned => return UnifiedTy::Discerned,\n                }\n            }\n            PolyParam::Pure(_) | PolyParam::TFixed(_, _) => {}\n        }\n    }\n\n    UnifiedTy::Merged(\n        record::Instance::new(\n            instance1.cons().clone(),\n            TyArgs::new(merged_pvar_purities, merged_tvar_types),\n        )\n        .into(),\n    )\n}\n\nfn unify_ty<M: ty::Pm>(\n    ref1: &ty::Ref<M>,\n    ty1: &Ty<M>,\n    ref2: &ty::Ref<M>,\n    ty2: &Ty<M>,\n) -> UnifiedTy<M> {\n    if ty1 == ty2 {\n        return UnifiedTy::Merged(ref1.clone());\n    }\n    match (ty1, ty2) {\n        // Handle supertype relationships\n        (_, Ty::Any) | (Ty::Any, _) => UnifiedTy::Merged(Ty::Any.into()),\n        (Ty::LitSym(_), Ty::Sym) | (Ty::Sym, Ty::LitSym(_)) => UnifiedTy::Merged(Ty::Sym.into()),\n        (Ty::LitBool(_), Ty::Bool) | (Ty::Bool, Ty::LitBool(_)) => {\n            UnifiedTy::Merged(Ty::Bool.into())\n        }\n        (Ty::Float, Ty::Num) | (Ty::Num, Ty::Float) => UnifiedTy::Merged(Ty::Num.into()),\n        (Ty::Int, Ty::Num) | (Ty::Num, Ty::Int) => UnifiedTy::Merged(Ty::Num.into()),\n\n        // Simplify (U true false) => Bool\n        (Ty::LitBool(true), Ty::LitBool(false)) | (Ty::LitBool(false), Ty::LitBool(true)) => {\n            UnifiedTy::Merged(Ty::Bool.into())\n        }\n\n        // Simplify (U Float Int) => Num\n        (Ty::Float, Ty::Int) | (Ty::Int, Ty::Float) => UnifiedTy::Merged(Ty::Num.into()),\n\n        // Set type\n        (Ty::Set(ty_ref1), Ty::Set(ty_ref2)) => {\n            let unified_ty_ref = unify_to_ty_ref(ty_ref1.as_ref(), ty_ref2.as_ref());\n            UnifiedTy::Merged(Ty::Set(Box::new(unified_ty_ref)).into())\n        }\n\n        // Map type\n        (Ty::Map(map1), Ty::Map(map2)) => {\n            let unified_key_ref = unify_to_ty_ref(map1.key(), map2.key());\n            let unified_val_ref = unify_to_ty_ref(map1.value(), map2.value());\n\n            UnifiedTy::Merged(ty::Map::new(unified_key_ref, unified_val_ref).into())\n        }\n\n        // Vector types\n        (Ty::Vector(members1), Ty::Vector(members2)) => {\n            if members1.len() != members2.len() {\n                // We can quickly check vector lengths at runtime\n                UnifiedTy::Discerned\n            } else {\n                let unified_members = members1\n                    .iter()\n                    .zip(members2.iter())\n                    .map(|(member1, member2)| unify_to_ty_ref(member1, member2))\n                    .collect();\n\n                UnifiedTy::Merged(Ty::Vector(unified_members).into())\n            }\n        }\n        (Ty::Vectorof(member1), Ty::Vectorof(member2)) => UnifiedTy::Merged(\n            Ty::Vectorof(Box::new(unify_to_ty_ref(\n                member1.as_ref(),\n                member2.as_ref(),\n            )))\n            .into(),\n        ),\n        (Ty::Vector(members1), Ty::Vectorof(member2))\n        | (Ty::Vectorof(member2), Ty::Vector(members1)) => {\n            let unified_member =\n                union_extend(vec![member2.as_ref().clone()], members1.iter().cloned());\n\n            UnifiedTy::Merged(Ty::Vectorof(Box::new(unified_member)).into())\n        }\n\n        // Function types\n        (Ty::TopFun(top_fun1), Ty::TopFun(top_fun2)) => unify_top_fun(top_fun1, top_fun2),\n        (Ty::Fun(fun), Ty::TopFun(top_fun)) | (Ty::TopFun(top_fun), Ty::Fun(fun)) => {\n            unify_top_fun(fun.top_fun(), top_fun)\n        }\n        (Ty::TyPred(_) | Ty::EqPred, Ty::TopFun(top_fun))\n        | (Ty::TopFun(top_fun), Ty::TyPred(_) | Ty::EqPred) => {\n            unify_top_fun(&ty::TopFun::new_for_pred(), top_fun)\n        }\n\n        (Ty::Fun(fun1), Ty::Fun(fun2)) => unify_fun(fun1, fun2),\n        (Ty::TyPred(_), Ty::Fun(fun)) | (Ty::Fun(fun), Ty::TyPred(_)) => {\n            unify_fun(&ty::Fun::new_for_ty_pred(), fun)\n        }\n        (Ty::EqPred, Ty::Fun(fun)) | (Ty::Fun(fun), Ty::EqPred) => {\n            unify_fun(&ty::Fun::new_for_eq_pred(), fun)\n        }\n\n        (Ty::TyPred(_), Ty::TyPred(_)) => {\n            UnifiedTy::Merged(Ty::Fun(Box::new(ty::Fun::new_for_ty_pred())).into())\n        }\n\n        // Union types\n        (Ty::Union(members1), Ty::Union(members2)) => {\n            let new_union = union_extend(members1.to_vec(), members2.iter().cloned());\n            UnifiedTy::Merged(new_union)\n        }\n        (Ty::Union(members1), _) => {\n            let new_union = union_extend(members1.to_vec(), iter::once(ref2).cloned());\n            UnifiedTy::Merged(new_union)\n        }\n        (_, Ty::Union(members2)) => {\n            let new_union = union_extend(members2.to_vec(), iter::once(ref1).cloned());\n            UnifiedTy::Merged(new_union)\n        }\n\n        // List types\n        (Ty::List(list1), Ty::List(list2)) => match unify_list(list1, list2) {\n            UnifiedList::Discerned => UnifiedTy::Discerned,\n            UnifiedList::Merged(merged_list) => UnifiedTy::Merged(merged_list.into()),\n        },\n\n        // Record types\n        (Ty::Record(instance1), Ty::Record(instance2)) => {\n            unify_record_instance(instance1, instance2)\n        }\n\n        _ => UnifiedTy::Discerned,\n    }\n}\n\npub fn unify_purity_refs(purity1: &purity::Ref, purity2: &purity::Ref) -> purity::Ref {\n    if purity1 == purity2 {\n        return purity1.clone();\n    }\n\n    match (purity1, purity2) {\n        // Pure is the \"empty type\" so this is a no-op\n        (purity::Ref::Fixed(Purity::Pure), other) | (other, purity::Ref::Fixed(Purity::Pure)) => {\n            other.clone()\n        }\n        _ => {\n            // Impure is the \"top type\" so this becomes impure\n            Purity::Impure.into()\n        }\n    }\n}\n\npub fn unify_to_ty_ref<M: ty::Pm>(ty_ref1: &ty::Ref<M>, ty_ref2: &ty::Ref<M>) -> ty::Ref<M> {\n    match unify_ty_refs(ty_ref1, ty_ref2) {\n        UnifiedTy::Merged(ty_ref) => ty_ref,\n        UnifiedTy::Discerned => Ty::Union(Box::new([ty_ref1.clone(), ty_ref2.clone()])).into(),\n    }\n}\n\n/// Unifies an iterator of types in to a new type\npub fn unify_ty_ref_iter<M, I>(new_members: I) -> ty::Ref<M>\nwhere\n    M: ty::Pm,\n    I: Iterator<Item = ty::Ref<M>>,\n{\n    union_extend(vec![], new_members)\n}\n\npub fn unify_list<M: ty::Pm>(list1: &ty::List<M>, list2: &ty::List<M>) -> UnifiedList<M> {\n    if list1.is_empty() {\n        if let Some(member) = try_list_to_exact_pair(list2) {\n            return UnifiedList::Merged(ty::List::new_uniform(member.clone()));\n        }\n    } else if list2.is_empty() {\n        if let Some(member) = try_list_to_exact_pair(list1) {\n            return UnifiedList::Merged(ty::List::new_uniform(member.clone()));\n        }\n    }\n\n    if list1.has_disjoint_arity(list2) {\n        return UnifiedList::Discerned;\n    }\n\n    let mut fixed_iter1 = list1.fixed().iter();\n    let mut fixed_iter2 = list2.fixed().iter();\n\n    let mut merged_fixed: Vec<ty::Ref<M>> =\n        Vec::with_capacity(cmp::min(fixed_iter1.len(), fixed_iter2.len()));\n\n    while fixed_iter1.len() > 0 && fixed_iter2.len() > 0 {\n        let fixed1 = fixed_iter1.next().unwrap();\n        let fixed2 = fixed_iter2.next().unwrap();\n\n        merged_fixed.push(unify_to_ty_ref(fixed1, fixed2));\n    }\n\n    // Merge all remaining fixed and rest args together\n    let rest_iter = fixed_iter1\n        .chain(fixed_iter2.chain(iter::once(list1.rest()).chain(iter::once(list2.rest()))));\n\n    let merged_rest = unify_ty_ref_iter(rest_iter.cloned());\n\n    UnifiedList::Merged(ty::List::new(merged_fixed.into_boxed_slice(), merged_rest))\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::hir::{poly_for_str, tvar_bounded_by};\n    use crate::source::EMPTY_SPAN;\n\n    fn assert_discerned(ty_str1: &str, ty_str2: &str) {\n        let poly1 = poly_for_str(ty_str1);\n        let poly2 = poly_for_str(ty_str2);\n\n        assert_eq!(UnifiedTy::Discerned, unify_ty_refs(&poly1, &poly2));\n    }\n\n    fn assert_merged(expected_str: &str, ty_str1: &str, ty_str2: &str) {\n        let expected = poly_for_str(expected_str);\n        let poly1 = poly_for_str(ty_str1);\n        let poly2 = poly_for_str(ty_str2);\n\n        // This is the basic invariant we're testing - each of our input types satisfies the merged\n        // type\n        assert!(ty::is_a::ty_ref_is_a(&poly1, &expected));\n        assert!(ty::is_a::ty_ref_is_a(&poly2, &expected));\n\n        assert_eq!(UnifiedTy::Merged(expected), unify_ty_refs(&poly1, &poly2));\n    }\n\n    fn assert_merged_iter(expected_str: &str, ty_strs: &[&str]) {\n        let expected = poly_for_str(expected_str);\n        let polys = ty_strs.iter().map(|&s| poly_for_str(s));\n\n        assert_eq!(expected, unify_ty_ref_iter(polys));\n    }\n\n    #[test]\n    fn disjoint_types() {\n        assert_discerned(\"Str\", \"Sym\");\n    }\n\n    #[test]\n    fn two_sym_types() {\n        assert_discerned(\"'foo\", \"'bar\");\n    }\n\n    #[test]\n    fn literal_sym_and_any_sym() {\n        assert_merged(\"Sym\", \"Sym\", \"'foo\");\n    }\n\n    #[test]\n    fn two_bool_types() {\n        assert_merged(\"Bool\", \"true\", \"false\");\n    }\n\n    #[test]\n    fn num_types() {\n        assert_merged(\"Int\", \"Int\", \"Int\");\n        assert_merged(\"Num\", \"Int\", \"Float\");\n        assert_merged(\"Num\", \"Float\", \"Int\");\n    }\n\n    #[test]\n    fn top_fun_types() {\n        assert_merged(\"(... ->! Bool)\", \"(... ->! true)\", \"(... -> false)\");\n    }\n\n    #[test]\n    fn fun_types() {\n        // Parameters are contravariant and Float/Int are disjoint\n        assert_merged(\"(... -> Num)\", \"(Float -> Int)\", \"(Int -> Float)\");\n\n        assert_merged(\"(true -> Num)\", \"(Bool -> Int)\", \"(true -> Float)\");\n        assert_merged(\"(->! Int)\", \"(-> Int)\", \"(->! Int)\");\n        assert_merged(\"(->! Bool)\", \"(-> true)\", \"(->! false)\");\n\n        assert_merged(\"(... ->! Bool)\", \"(... -> true)\", \"(->! false)\");\n    }\n\n    #[test]\n    fn ty_pred_types() {\n        assert_merged(\"str?\", \"str?\", \"str?\");\n        assert_merged(\"(Any -> Bool)\", \"str?\", \"sym?\");\n        assert_merged(\"(Int -> Any)\", \"(Int -> Any)\", \"sym?\");\n        assert_merged(\"(... ->! Bool)\", \"(... ->! Bool)\", \"sym?\");\n    }\n\n    #[test]\n    fn eq_pred_type() {\n        assert_merged(\"=\", \"=\", \"=\");\n        assert_merged(\"(Int Int -> Any)\", \"(Int Int -> Any)\", \"=\");\n        assert_merged(\"(... ->! Bool)\", \"(... ->! Bool)\", \"=\");\n    }\n\n    #[test]\n    fn set_types() {\n        assert_merged(\"(Setof Bool)\", \"(Setof true)\", \"(Setof false)\");\n        assert_merged(\"(Setof (RawU Str Sym))\", \"(Setof Str)\", \"(Setof Sym)\");\n    }\n\n    #[test]\n    fn map_types() {\n        assert_merged(\n            \"(Map Bool (RawU 'bar 'foo))\",\n            \"(Map true 'bar)\",\n            \"(Map false 'foo)\",\n        );\n    }\n\n    #[test]\n    fn union_types() {\n        assert_merged(\"(RawU 'foo 'bar 'baz)\", \"(RawU 'foo 'bar)\", \"'baz\");\n        assert_merged(\"(RawU 'foo 'bar 'baz)\", \"'baz\", \"(RawU 'foo 'bar)\");\n        assert_merged(\n            \"(RawU Bool (-> Int))\",\n            \"(RawU true (-> Int))\",\n            \"(RawU false (-> Int))\",\n        );\n        assert_merged(\n            \"(RawU Char Int Str Sym)\",\n            \"(RawU Char Int)\",\n            \"(RawU Str Sym)\",\n        );\n        assert_merged(\n            \"(RawU true (... -> Num))\",\n            \"(RawU true (Int -> Float))\",\n            \"(RawU true (Float -> Int))\",\n        );\n        assert_merged(\"(RawU 'foo 'bar Bool)\", \"(RawU 'foo 'bar)\", \"Bool\");\n        assert_merged(\"Sym\", \"(RawU 'foo 'bar)\", \"Sym\");\n        assert_merged(\"(RawU Int Sym)\", \"(RawU 'foo 'bar Int)\", \"Sym\");\n        assert_merged(\"Sym\", \"(RawU)\", \"Sym\");\n        assert_merged(\"(RawU)\", \"(RawU)\", \"(RawU)\");\n\n        assert_merged(\n            \"(RawU Char Int Str Sym)\",\n            \"(RawU Char Int)\",\n            \"(RawU Str Sym)\",\n        );\n    }\n\n    #[test]\n    fn unify_iter() {\n        assert_merged_iter(\"(RawU)\", &[]);\n        assert_merged_iter(\"Sym\", &[\"Sym\"]);\n        assert_merged_iter(\"Bool\", &[\"true\", \"false\"]);\n        assert_merged_iter(\n            \"(Setof (RawU Str Sym Int))\",\n            &[\"(Setof Str)\", \"(Setof Sym)\", \"(Setof Int)\"],\n        );\n\n        assert_merged_iter(\n            \"(... -> (RawU Sym Str))\",\n            &[\"(Str -> Sym)\", \"(RawU)\", \"(Sym -> Str)\"],\n        );\n    }\n\n    #[test]\n    fn list_types() {\n        assert_merged(\"(List & Any)\", \"(List Any)\", \"(List & Any)\");\n        assert_discerned(\"(List Any)\", \"(List Any Any)\");\n        assert_merged(\"(List (RawU Sym Str))\", \"(List Sym)\", \"(List Str)\");\n        assert_discerned(\"(List Str)\", \"(List Str Str & Str)\");\n        assert_merged(\n            \"(List Int & (RawU Float Sym Str))\",\n            \"(List Int & Sym)\",\n            \"(List Int Float & Str)\",\n        );\n\n        assert_merged(\"(List & Int)\", \"(List Int & Int)\", \"(List)\");\n        assert_merged(\"(List & Sym)\", \"(List)\", \"(List Sym & Sym)\");\n    }\n\n    #[test]\n    fn vec_types() {\n        assert_merged(\"(Vectorof Bool)\", \"(Vector true)\", \"(Vectorof false)\");\n        assert_discerned(\"(Vector Int Sym)\", \"(Vector 'bar Int Str)\");\n    }\n\n    #[test]\n    fn polymorphic_funs() {\n        let pidentity_fun = poly_for_str(\"(All #{A} A -> A)\");\n        let pidentity_impure_string_fun = poly_for_str(\"(All #{[A Str]} A ->! A)\");\n        let top_impure_fun = poly_for_str(\"(... ->! Any)\");\n\n        assert_eq!(\n            UnifiedTy::Merged(pidentity_fun.clone()),\n            unify_ty_refs(&pidentity_fun, &pidentity_fun)\n        );\n\n        assert_eq!(\n            UnifiedTy::Merged(top_impure_fun.clone()),\n            unify_ty_refs(&pidentity_fun, &pidentity_impure_string_fun)\n        );\n\n        assert_eq!(\n            UnifiedTy::Merged(top_impure_fun.clone()),\n            unify_ty_refs(&pidentity_fun, &top_impure_fun)\n        );\n    }\n\n    #[test]\n    fn purity_refs() {\n        let purity_pure = Purity::Pure.into();\n        let purity_impure = Purity::Impure.into();\n\n        let pvar1 = purity::PVar::new(EMPTY_SPAN, \"test\".into());\n        let purity_var1 = purity::Ref::Var(pvar1);\n\n        let pvar2 = purity::PVar::new(EMPTY_SPAN, \"test\".into());\n        let purity_var2 = purity::Ref::Var(pvar2);\n\n        assert_eq!(purity_pure, unify_purity_refs(&purity_pure, &purity_pure));\n\n        assert_eq!(\n            purity_impure,\n            unify_purity_refs(&purity_impure, &purity_impure)\n        );\n\n        assert_eq!(purity_var1, unify_purity_refs(&purity_var1, &purity_var1));\n\n        assert_eq!(\n            purity_impure,\n            unify_purity_refs(&purity_pure, &purity_impure)\n        );\n\n        assert_eq!(purity_var1, unify_purity_refs(&purity_pure, &purity_var1));\n\n        assert_eq!(\n            purity_impure,\n            unify_purity_refs(&purity_impure, &purity_var1)\n        );\n\n        assert_eq!(purity_impure, unify_purity_refs(&purity_var1, &purity_var2));\n    }\n\n    #[test]\n    fn related_poly_bounds() {\n        let ptype1_unbounded = tvar_bounded_by(Ty::Any.into());\n        let ptype2_bounded_by_1 = tvar_bounded_by(ptype1_unbounded.clone());\n\n        assert_eq!(\n            UnifiedTy::Merged(ptype1_unbounded.clone()),\n            unify_ty_refs(&ptype1_unbounded, &ptype1_unbounded)\n        );\n\n        assert_eq!(\n            UnifiedTy::Merged(ptype2_bounded_by_1.clone()),\n            unify_ty_refs(&ptype2_bounded_by_1, &ptype2_bounded_by_1)\n        );\n\n        assert_eq!(\n            UnifiedTy::Merged(ptype1_unbounded.clone()),\n            unify_ty_refs(&ptype2_bounded_by_1, &ptype1_unbounded)\n        );\n\n        assert_eq!(\n            UnifiedTy::Merged(ptype1_unbounded.clone()),\n            unify_ty_refs(&ptype1_unbounded, &ptype2_bounded_by_1,)\n        );\n    }\n\n    #[test]\n    fn record_instances() {\n        use crate::ty::ty_args::TyArgs;\n        use std::collections::HashMap;\n\n        let tvar1 = ty::TVar::new(EMPTY_SPAN, \"tvar1\".into(), Ty::Any.into());\n        let tvar2 = ty::TVar::new(EMPTY_SPAN, \"tvar2\".into(), Ty::Any.into());\n\n        let cons1 = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons1\".into(),\n            \"cons1?\".into(),\n            Some(Box::new([record::PolyParam::TVar(\n                Variance::Covariant,\n                tvar1.clone(),\n            )])),\n            Box::new([record::Field::new(\n                EMPTY_SPAN,\n                \"cons1-field1\".into(),\n                tvar1.clone().into(),\n            )]),\n        );\n\n        let cons2 = record::Cons::new(\n            EMPTY_SPAN,\n            \"cons2\".into(),\n            \"cons2?\".into(),\n            Some(Box::new([\n                record::PolyParam::TVar(Variance::Covariant, tvar1.clone()),\n                record::PolyParam::TVar(Variance::Contravariant, tvar2.clone()),\n            ])),\n            Box::new([\n                record::Field::new(EMPTY_SPAN, \"cons2-covariant\".into(), tvar1.clone().into()),\n                record::Field::new(\n                    EMPTY_SPAN,\n                    \"cons2-contravariant\".into(),\n                    tvar2.clone().into(),\n                ),\n            ]),\n        );\n\n        let float_instance1_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons1,\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1.clone(), Ty::Float.into())).collect(),\n            ),\n        )\n        .into();\n\n        let float_bool_instance2_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons2.clone(),\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1.clone(), Ty::Float.into()))\n                    .chain(std::iter::once((tvar2.clone(), Ty::Bool.into())))\n                    .collect(),\n            ),\n        )\n        .into();\n\n        let int_false_instance2_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons2.clone(),\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1.clone(), Ty::Int.into()))\n                    .chain(std::iter::once((tvar2.clone(), Ty::LitBool(false).into())))\n                    .collect(),\n            ),\n        )\n        .into();\n\n        let num_false_instance2_poly: ty::Ref<ty::Poly> = record::Instance::new(\n            cons2,\n            TyArgs::new(\n                HashMap::new(),\n                std::iter::once((tvar1, Ty::Num.into()))\n                    .chain(std::iter::once((tvar2, Ty::LitBool(false).into())))\n                    .collect(),\n            ),\n        )\n        .into();\n\n        // Different record constructors\n        assert_eq!(\n            UnifiedTy::Discerned,\n            unify_ty_refs(&float_instance1_poly, &float_bool_instance2_poly)\n        );\n\n        // Different instances of same constructor\n        assert_eq!(\n            UnifiedTy::Merged(num_false_instance2_poly),\n            unify_ty_refs(&int_false_instance2_poly, &float_bool_instance2_poly)\n        );\n    }\n}\n"
  },
  {
    "path": "compiler/ty/var_usage.rs",
    "content": "//! Calculates the usage of a polymorphic variable in type\n\nuse std::collections::HashMap;\nuse std::ops;\n\nuse crate::ty;\nuse crate::ty::purity;\nuse crate::ty::record;\nuse crate::ty::Ty;\n\n/// Indicates the variance of a polymorphic parameter\n///\n/// By default variables are covariant. Whenever a variable appears inside a function's parameter\n/// list it becomes contravariant, flipping again for each nested function type. If a variable\n/// appears in both a covariant and contravariant position then it becomes invariant.\n#[derive(PartialEq, Debug, Clone, Copy)]\npub enum Variance {\n    /// Subtypes of the record have subtypes of this variable\n    Covariant,\n    /// Subtypes of the record have supertypes of this variable\n    Contravariant,\n    /// No subtype relationship exists between records with different types of this variable\n    Invariant,\n}\n\nimpl ops::Mul for Variance {\n    type Output = Variance;\n\n    /// Multiplies two variances\n    ///\n    /// This is used to calculate our new polarity when visiting a nested record type's polymorphic\n    /// variables.\n    ///\n    /// When a contravariant relationship appears in a contravariant polarity it's actually\n    /// covariant with respect to the root type. This makes contravariance analogous to a negative\n    /// number for the purposes of multiplication.\n    ///\n    /// An invariant relationship is invariant in any polarity and any relationship is invariant in\n    /// an invariant polarity. This makes invariance analogous to zero.\n    fn mul(self, rhs: Variance) -> Variance {\n        match (self, rhs) {\n            (Variance::Invariant, _) | (_, Variance::Invariant) => Variance::Invariant,\n\n            (Variance::Contravariant, Variance::Covariant)\n            | (Variance::Covariant, Variance::Contravariant) => Variance::Contravariant,\n\n            (Variance::Covariant, Variance::Covariant)\n            | (Variance::Contravariant, Variance::Contravariant) => Variance::Covariant,\n        }\n    }\n}\n\nimpl ops::BitAndAssign for Variance {\n    /// Combines the variance from two different usages\n    fn bitand_assign(&mut self, rhs: Variance) {\n        if *self != rhs {\n            *self = Variance::Invariant;\n        }\n    }\n}\n\n#[derive(Clone, Debug, Default)]\npub struct VarUsages {\n    pvar_variances: HashMap<purity::PVarId, Variance>,\n    tvar_variances: HashMap<ty::TVarId, Variance>,\n}\n\nfn visit_list(var_usages: &mut VarUsages, polarity: Variance, list: &ty::List<ty::Poly>) {\n    for member in list.fixed() {\n        visit_poly(var_usages, polarity, member);\n    }\n\n    visit_poly(var_usages, polarity, list.rest());\n}\n\nfn visit_top_fun(var_usages: &mut VarUsages, polarity: Variance, top_fun: &ty::TopFun) {\n    visit_poly(var_usages, polarity, top_fun.ret());\n}\n\nfn visit_ty(var_usages: &mut VarUsages, polarity: Variance, poly_ty: &Ty<ty::Poly>) {\n    match poly_ty {\n        Ty::Any\n        | Ty::Bool\n        | Ty::Char\n        | Ty::LitBool(_)\n        | Ty::Sym\n        | Ty::LitSym(_)\n        | Ty::Int\n        | Ty::Float\n        | Ty::Num\n        | Ty::Str\n        | Ty::TyPred(_)\n        | Ty::EqPred\n        | Ty::TopRecord\n        | Ty::RecordClass(_) => {\n            // Terminal type\n        }\n\n        Ty::List(list) => {\n            visit_list(var_usages, polarity, list);\n        }\n\n        Ty::Map(map) => {\n            visit_poly(var_usages, polarity, map.key());\n            visit_poly(var_usages, polarity, map.value());\n        }\n\n        Ty::Set(member) | Ty::Vectorof(member) => {\n            visit_poly(var_usages, polarity, member);\n        }\n\n        Ty::Union(members) | Ty::Intersect(members) | Ty::Vector(members) => {\n            for member in members.iter() {\n                visit_poly(var_usages, polarity, member);\n            }\n        }\n\n        Ty::Fun(fun) => {\n            visit_top_fun(var_usages, polarity, fun.top_fun());\n            visit_list(var_usages, polarity * Variance::Contravariant, fun.params());\n        }\n\n        Ty::TopFun(top_fun) => {\n            visit_top_fun(var_usages, polarity, top_fun);\n        }\n\n        Ty::Record(record_instance) => {\n            let record_cons = record_instance.cons();\n\n            for poly_param in record_cons.poly_params() {\n                match poly_param {\n                    record::PolyParam::PVar(variance, pvar) => {\n                        let purity_ref = &record_instance.ty_args().pvar_purities()[pvar];\n                        visit_purity(var_usages, polarity * *variance, purity_ref);\n                    }\n                    record::PolyParam::TVar(variance, tvar) => {\n                        let poly_ref = &record_instance.ty_args().tvar_types()[tvar];\n                        visit_poly(var_usages, polarity * *variance, poly_ref);\n                    }\n                    record::PolyParam::Pure(_) | record::PolyParam::TFixed(_, _) => {}\n                }\n            }\n        }\n    }\n}\n\nfn visit_purity(var_usages: &mut VarUsages, polarity: Variance, purity_ref: &purity::Ref) {\n    match purity_ref {\n        purity::Ref::Fixed(_) => {}\n        purity::Ref::Var(pvar) => {\n            var_usages\n                .pvar_variances\n                .entry(pvar.clone())\n                .and_modify(|existing_usage| *existing_usage &= polarity)\n                .or_insert(polarity);\n        }\n    }\n}\n\nfn visit_poly(var_usages: &mut VarUsages, polarity: Variance, poly_ref: &ty::Ref<ty::Poly>) {\n    match poly_ref {\n        ty::Ref::Fixed(poly_ty) => {\n            visit_ty(var_usages, polarity, poly_ty);\n        }\n        ty::Ref::Var(tvar, _) => {\n            var_usages\n                .tvar_variances\n                .entry(tvar.clone())\n                .and_modify(|existing_usage| *existing_usage &= polarity)\n                .or_insert(polarity);\n        }\n    }\n}\n\nimpl VarUsages {\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    pub fn add_poly_usages(&mut self, poly_ref: &ty::Ref<ty::Poly>) {\n        visit_poly(self, Variance::Covariant, poly_ref);\n    }\n\n    pub fn pvar_variance(&self, pvar: &purity::PVarId) -> Option<Variance> {\n        self.pvar_variances.get(pvar).copied()\n    }\n\n    pub fn tvar_variance(&self, tvar: &ty::TVarId) -> Option<Variance> {\n        self.tvar_variances.get(tvar).copied()\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::source::EMPTY_SPAN;\n    use crate::ty::purity::Purity;\n\n    #[test]\n    fn test_variance_mul() {\n        assert_eq!(\n            Variance::Covariant,\n            Variance::Covariant * Variance::Covariant\n        );\n\n        assert_eq!(\n            Variance::Contravariant,\n            Variance::Covariant * Variance::Contravariant\n        );\n\n        assert_eq!(\n            Variance::Invariant,\n            Variance::Covariant * Variance::Invariant\n        );\n    }\n\n    #[test]\n    fn convariant_usage() {\n        let tvar = ty::TVar::new(EMPTY_SPAN, \"tvar\".into(), Ty::Any.into());\n\n        let mut var_usages = VarUsages::new();\n        var_usages.add_poly_usages(&tvar.clone().into());\n\n        assert_eq!(Some(Variance::Covariant), var_usages.tvar_variance(&tvar));\n    }\n\n    #[test]\n    fn contravariant_usage() {\n        let tvar = ty::TVar::new(EMPTY_SPAN, \"tvar\".into(), Ty::Any.into());\n\n        let mut var_usages = VarUsages::new();\n\n        var_usages.add_poly_usages(\n            &ty::Fun::new_mono(\n                ty::List::new_uniform(tvar.clone().into()),\n                Purity::Pure.into(),\n                Ty::Any.into(),\n            )\n            .into(),\n        );\n\n        assert_eq!(\n            Some(Variance::Contravariant),\n            var_usages.tvar_variance(&tvar)\n        );\n    }\n\n    #[test]\n    fn invariant_usage() {\n        let tvar = ty::TVar::new(EMPTY_SPAN, \"tvar\".into(), Ty::Any.into());\n\n        let mut var_usages = VarUsages::new();\n\n        var_usages.add_poly_usages(\n            &ty::Fun::new_mono(\n                ty::List::new_uniform(tvar.clone().into()),\n                Purity::Pure.into(),\n                tvar.clone().into(),\n            )\n            .into(),\n        );\n\n        assert_eq!(Some(Variance::Invariant), var_usages.tvar_variance(&tvar));\n    }\n}\n"
  },
  {
    "path": "compiler/typeck/dce.rs",
    "content": "use crate::hir;\nuse crate::ty;\nuse crate::ty::purity::Purity;\nuse crate::ty::Ty;\n\n/// Returns if an expression can have a side effect\n///\n/// This is used for very basic dead code elimination during type checking.\npub fn expr_can_side_effect(expr: &hir::Expr<hir::Inferred>) -> bool {\n    use hir::ExprKind;\n    match &expr.kind {\n        ExprKind::LocalRef(_, _)\n        | ExprKind::ExportRef(_, _)\n        | ExprKind::Lit(_)\n        | ExprKind::EqPred(_)\n        | ExprKind::TyPred(_, _)\n        | ExprKind::RecordCons(_, _)\n        | ExprKind::FieldAccessor(_)\n        | ExprKind::Fun(_)\n        | ExprKind::RustFun(_) => false,\n        ExprKind::Do(exprs) => exprs.iter().any(expr_can_side_effect),\n        ExprKind::MacroExpand(_, inner) => expr_can_side_effect(inner),\n        ExprKind::Cond(cond) => {\n            expr_can_side_effect(&cond.test_expr)\n                || expr_can_side_effect(&cond.true_expr)\n                || expr_can_side_effect(&cond.false_expr)\n        }\n        ExprKind::Let(let_expr) => {\n            expr_can_side_effect(&let_expr.value_expr) || expr_can_side_effect(&let_expr.body_expr)\n        }\n        ExprKind::App(app) => {\n            if let ty::Ref::Fixed(Ty::Fun(ref fun_type)) = app.fun_expr.result_ty {\n                fun_type.top_fun().purity() != &Purity::Pure.into()\n                    || fun_type.ret().is_never()\n                    || app.fixed_arg_exprs.iter().any(expr_can_side_effect)\n                    || app.rest_arg_expr.iter().any(expr_can_side_effect)\n            } else {\n                true\n            }\n        }\n        ExprKind::Recur(_) => {\n            // We don't know if a `(recur)` is pure without knowing the function it appears in.\n            // However, by definition `(recur)` always occurs in a position where its value becomes\n            // the return value of a function. This means that in practice it can never be\n            // eliminated anyway.\n            true\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::source::EMPTY_SPAN;\n    use crate::ty::ty_args::TyArgs;\n\n    #[test]\n    fn pure_app_expr() {\n        let app_expr = hir::Expr::<hir::Inferred> {\n            result_ty: ty::List::empty().into(),\n            kind: hir::ExprKind::App(Box::new(hir::App {\n                span: EMPTY_SPAN,\n                fun_expr: hir::Expr {\n                    result_ty: ty::Fun::new_mono(\n                        ty::List::empty(),\n                        Purity::Pure.into(),\n                        ty::List::empty().into(),\n                    )\n                    .into(),\n                    kind: hir::ExprKind::Do(vec![]),\n                },\n                ty_args: TyArgs::empty(),\n                fixed_arg_exprs: vec![],\n                rest_arg_expr: None,\n            })),\n        };\n\n        assert!(!expr_can_side_effect(&app_expr));\n    }\n\n    #[test]\n    fn impure_app_expr() {\n        let app_expr = hir::Expr::<hir::Inferred> {\n            result_ty: ty::List::empty().into(),\n            kind: hir::ExprKind::App(Box::new(hir::App {\n                span: EMPTY_SPAN,\n                fun_expr: hir::Expr {\n                    result_ty: ty::Fun::new_mono(\n                        ty::List::empty(),\n                        Purity::Impure.into(),\n                        ty::List::empty().into(),\n                    )\n                    .into(),\n                    kind: hir::ExprKind::Do(vec![]),\n                },\n                ty_args: TyArgs::empty(),\n                fixed_arg_exprs: vec![],\n                rest_arg_expr: None,\n            })),\n        };\n\n        assert!(expr_can_side_effect(&app_expr));\n    }\n}\n"
  },
  {
    "path": "compiler/typeck/destruc.rs",
    "content": "use crate::hir;\nuse crate::hir::destruc;\nuse crate::ty;\nuse crate::ty::list_iter::ListIterator;\nuse crate::ty::Ty;\n\npub fn type_for_decl_list_destruc(\n    list: &destruc::List<hir::Lowered>,\n    mut guide_type_iter: Option<ListIterator<'_, ty::Poly>>,\n) -> ty::List<ty::Poly> {\n    let fixed_polys = list\n        .fixed()\n        .iter()\n        .map(|fixed_destruc| {\n            let guide_type = if let Some(guide_type_iter) = guide_type_iter.as_mut() {\n                guide_type_iter.next()\n            } else {\n                None\n            };\n\n            type_for_decl_destruc(fixed_destruc, guide_type)\n        })\n        .collect();\n\n    let rest_poly = match list.rest() {\n        Some(rest) => match rest.ty() {\n            hir::DeclTy::Known(poly) => poly.clone(),\n            hir::DeclTy::Free => guide_type_iter\n                .map(ListIterator::collect_rest)\n                .unwrap_or_else(|| Ty::Any.into()),\n        },\n        None => Ty::never().into(),\n    };\n\n    ty::List::new(fixed_polys, rest_poly)\n}\n\n/// Returns the required type for a destruc\npub fn type_for_decl_destruc(\n    destruc: &destruc::Destruc<hir::Lowered>,\n    guide_type: Option<&ty::Ref<ty::Poly>>,\n) -> ty::Ref<ty::Poly> {\n    match destruc {\n        destruc::Destruc::Scalar(_, scalar) => match scalar.ty() {\n            hir::DeclTy::Known(poly) => poly.clone(),\n            hir::DeclTy::Free => guide_type.cloned().unwrap_or_else(|| Ty::Any.into()),\n        },\n\n        destruc::Destruc::List(_, list) => {\n            let guide_type_iter =\n                guide_type.and_then(|guide_type| ListIterator::try_new_from_ty_ref(guide_type));\n\n            type_for_decl_list_destruc(list, guide_type_iter).into()\n        }\n    }\n}\n\nfn visit_scalar_locals<F>(scalar: &destruc::Scalar<hir::Lowered>, visitor: &mut F)\nwhere\n    F: FnMut(hir::LocalId, &hir::DeclTy),\n{\n    if let Some(local_id) = scalar.local_id() {\n        visitor(*local_id, scalar.ty());\n    }\n}\n\n/// Visits the local variables in the passed destruc with the given visitor function\n///\n/// If the root destruc is scalar its VarId will be returned, otherwise None\npub fn visit_locals<F>(\n    destruc: &destruc::Destruc<hir::Lowered>,\n    visitor: &mut F,\n) -> Option<hir::LocalId>\nwhere\n    F: FnMut(hir::LocalId, &hir::DeclTy),\n{\n    match destruc {\n        destruc::Destruc::Scalar(_, ref scalar) => {\n            visit_scalar_locals(scalar, visitor);\n            *scalar.local_id()\n        }\n        destruc::Destruc::List(_, ref list) => {\n            for fixed in list.fixed() {\n                visit_locals(fixed, visitor);\n            }\n\n            if let Some(rest) = list.rest() {\n                visit_scalar_locals(rest, visitor);\n            }\n\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "compiler/typeck/error.rs",
    "content": "use std::fmt::Display;\nuse std::{error, fmt};\n\nuse codespan_reporting::diagnostic::Diagnostic;\n\nuse arret_syntax::span::{FileId, Span};\n\nuse crate::hir;\nuse crate::reporting::{new_primary_label, new_secondary_label, LocTrace};\nuse crate::ty;\nuse crate::ty::purity;\n\n#[derive(PartialEq, Debug, Copy, Clone)]\npub struct WantedArity {\n    fixed_len: usize,\n    has_rest: bool,\n}\n\nimpl WantedArity {\n    pub fn new(fixed_len: usize, has_rest: bool) -> WantedArity {\n        WantedArity {\n            fixed_len,\n            has_rest,\n        }\n    }\n}\n\nimpl fmt::Display for WantedArity {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        if self.has_rest {\n            write!(f, \"at least {}\", self.fixed_len)\n        } else {\n            write!(f, \"{}\", self.fixed_len)\n        }\n    }\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct IsNotRetTy {\n    value_poly: ty::Ref<ty::Poly>,\n    ret_poly: ty::Ref<ty::Poly>,\n    ret_ty_span: Option<Span>,\n}\n\nimpl IsNotRetTy {\n    pub fn new(\n        value_poly: ty::Ref<ty::Poly>,\n        ret_poly: ty::Ref<ty::Poly>,\n        ret_ty_span: Option<Span>,\n    ) -> IsNotRetTy {\n        IsNotRetTy {\n            value_poly,\n            ret_poly,\n            ret_ty_span,\n        }\n    }\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub enum ErrorKind {\n    IsNotTy(ty::Ref<ty::Poly>, ty::Ref<ty::Poly>),\n    IsNotFun(ty::Ref<ty::Poly>),\n    IsNotPurity(ty::Ref<ty::Poly>, purity::Ref),\n    IsNotRetTy(IsNotRetTy),\n    VarHasEmptyType(ty::Ref<ty::Poly>, ty::Ref<ty::Poly>),\n    TopFunApply(ty::Ref<ty::Poly>),\n    RecursiveType,\n    RecurWithoutFunTypeDecl,\n    NonTailRecur,\n    DependsOnError,\n    WrongArity(usize, WantedArity),\n    UnselectedPVar(purity::PVarId),\n    UnselectedTVar(ty::TVarId),\n}\n\n#[derive(PartialEq, Debug, Clone)]\npub struct Error {\n    loc_trace: LocTrace,\n    kind: ErrorKind,\n}\n\nimpl Error {\n    pub fn new(span: Span, kind: ErrorKind) -> Error {\n        Self::new_with_loc_trace(span.into(), kind)\n    }\n\n    pub fn new_with_loc_trace(loc_trace: LocTrace, kind: ErrorKind) -> Error {\n        Error { loc_trace, kind }\n    }\n\n    pub fn kind(&self) -> &ErrorKind {\n        &self.kind\n    }\n\n    pub fn with_macro_invocation_span(self, span: Span) -> Error {\n        Error {\n            loc_trace: self.loc_trace.with_macro_invocation(span),\n            ..self\n        }\n    }\n}\n\nimpl From<Error> for Diagnostic<FileId> {\n    fn from(error: Error) -> Self {\n        let origin = error.loc_trace.origin();\n\n        let diagnostic = match error.kind() {\n            ErrorKind::IsNotFun(ref sub) => Diagnostic::error().with_message(format!(\n                \"expected function, found `{}`\",\n                hir::str_for_ty_ref(sub)\n            )).with_labels(vec![new_primary_label(origin, \"application requires function\")]),\n\n            ErrorKind::IsNotTy(ref sub, ref parent) => Diagnostic::error()\n                .with_message(\"mismatched types\")\n                .with_labels(vec![new_primary_label(origin, format!(\n                    \"`{}` is not a `{}`\",\n                    hir::str_for_ty_ref(sub),\n                    hir::str_for_ty_ref(parent)\n                ))]),\n\n            ErrorKind::IsNotPurity(ref fun, ref purity) => {\n                use crate::ty::purity::Purity;\n\n                let purity_str = if purity == &Purity::Pure.into() {\n                    // `->` might be confusing here\n                    \"pure\".into()\n                } else {\n                    format!(\"`{}`\", hir::str_for_purity(purity))\n                };\n\n                Diagnostic::error()\n                    .with_message(\"mismatched purities\")\n                    .with_labels(vec![new_primary_label(origin, format!(\n                        \"function of type `{}` is not {}\",\n                        hir::str_for_ty_ref(fun),\n                        purity_str\n                    ))]\n                )\n            }\n\n            ErrorKind::IsNotRetTy(IsNotRetTy {\n                value_poly,\n                ret_poly,\n                ret_ty_span,\n            }) => {\n                let ret_poly_str = hir::str_for_ty_ref(ret_poly);\n                let diagnostic = Diagnostic::error()\n                    .with_message(\"mismatched types\");\n\n                let primary_label = new_primary_label(origin, format!(\n                    \"`{}` is not a `{}`\",\n                    hir::str_for_ty_ref(value_poly),\n                    ret_poly_str\n                ));\n\n                if let Some(ret_ty_span) = ret_ty_span {\n                    let secondary_label =\n                        new_secondary_label(*ret_ty_span,format!(\n                            \"expected `{}` due to return type\",\n                            ret_poly_str\n                        ));\n\n                    diagnostic.with_labels(vec![primary_label, secondary_label])\n                } else {\n                    diagnostic.with_labels(vec![primary_label])\n                }\n            }\n\n            ErrorKind::VarHasEmptyType(ref current_type, ref required_type) => {\n                Diagnostic::error()\n                  .with_message(\"type annotation needed\")\n                  .with_labels(vec![\n                    new_primary_label(origin,format!(\n                        \"usage requires `{}` but variable has inferred type of `{}`\",\n                        hir::str_for_ty_ref(required_type),\n                        hir::str_for_ty_ref(current_type)\n                    ))]\n                )\n            }\n\n            ErrorKind::TopFunApply(ref top_fun) => Diagnostic::error()\n                .with_message(format!(\n                    \"cannot determine parameter types for `{}`\",\n                    hir::str_for_ty_ref(top_fun)\n                ))\n                .with_labels(vec![\n                    new_primary_label(origin,\"at this application\")\n                ]),\n\n            ErrorKind::WrongArity(have, ref wanted) => {\n                let label_message = if wanted.fixed_len == 1 {\n                    format!(\"expected {} argument\", wanted)\n                } else {\n                    format!(\"expected {} arguments\", wanted)\n                };\n\n                Diagnostic::error()\n                    .with_message(format!(\n                        \"incorrect number of arguments: wanted {}, have {}\",\n                        wanted, have\n                    ))\n                    .with_labels(vec![new_primary_label(origin, label_message)])\n            }\n\n            ErrorKind::RecursiveType => Diagnostic::error()\n                .with_message(\"type annotation needed\")\n                .with_labels(vec![\n                    new_primary_label(origin, \"recursive usage requires explicit type annotation\")\n                ]),\n\n            ErrorKind::RecurWithoutFunTypeDecl => Diagnostic::error()\n                .with_message(\"type annotation needed\")\n                .with_labels(vec![\n                    new_primary_label(origin,\n                        \"`(recur)` requires the function to have a complete type annotation\"),\n                ]),\n\n            ErrorKind::NonTailRecur => Diagnostic::error()\n                .with_message(\"non-tail `(recur)`\")\n                .with_labels(vec![\n                    new_primary_label(origin,\n                        \"`(recur)` must occur in a position where it immediately becomes the return value of a function\"),\n                ]),\n\n            ErrorKind::DependsOnError =>\n                Diagnostic::error()\n                    .with_message(\"type cannot be determined due to previous error\")\n                    .with_labels(vec![new_primary_label(origin, \"cannot infer type\")]),\n\n            ErrorKind::UnselectedPVar(pvar) => Diagnostic::error()\n                .with_message(format!(\n                    \"cannot determine purity of purity variable `{}`\",\n                    pvar.source_name()\n                ))\n                .with_labels(vec![\n                    new_primary_label(origin,\"at this application\"),\n                    new_secondary_label(pvar.span(),\"purity variable defined here\"),\n                ]),\n\n            ErrorKind::UnselectedTVar(tvar) => Diagnostic::error().with_message(format!(\n                \"cannot determine type of type variable `{}`\",\n                tvar.source_name()\n            )).with_labels(vec![\n                new_primary_label(origin,\"at this application\"),\n                new_secondary_label(tvar.span(), \"type variable defined here\")\n            ])\n        };\n\n        error.loc_trace.label_macro_invocation(diagnostic)\n    }\n}\n\nimpl From<Error> for Vec<Diagnostic<FileId>> {\n    fn from(error: Error) -> Self {\n        vec![error.into()]\n    }\n}\n\nimpl error::Error for Error {}\n\nimpl Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let diagnostic: Diagnostic<FileId> = self.clone().into();\n        f.write_str(&diagnostic.message)\n    }\n}\n"
  },
  {
    "path": "compiler/typeck/infer.rs",
    "content": "use std::collections::HashMap;\nuse std::result;\nuse std::sync::Arc;\n\nuse crate::context::ModuleId;\nuse crate::hir;\nuse crate::hir::destruc;\nuse crate::rfi;\nuse crate::ty;\nuse crate::ty::list_iter::ListIterator;\nuse crate::ty::purity;\nuse crate::ty::purity::Purity;\nuse crate::ty::record;\nuse crate::ty::ty_args::TyArgs;\nuse crate::ty::Ty;\nuse crate::typeck;\nuse crate::typeck::dce::expr_can_side_effect;\nuse crate::typeck::error::{Error, ErrorKind, IsNotRetTy, WantedArity};\n\nuse arret_syntax::datum::Datum;\nuse arret_syntax::span::Span;\n\ntype Result<T> = result::Result<T, Error>;\n\n/// Result of inferring the type for a HIR expression\npub struct InferredNode {\n    /// Expression with all free types replaced with poly types\n    expr: hir::Expr<hir::Inferred>,\n    /// Type conditions depending the poly type of this node\n    type_conds: Vec<VarTypeCond>,\n}\n\nimpl InferredNode {\n    fn is_divergent(&self) -> bool {\n        self.expr.result_ty.is_never()\n    }\n\n    pub fn into_expr(self) -> hir::Expr<hir::Inferred> {\n        self.expr\n    }\n\n    pub fn result_ty(&self) -> &ty::Ref<ty::Poly> {\n        &self.expr.result_ty\n    }\n}\n\n#[derive(Clone, Copy, PartialEq)]\nenum NodeBool {\n    True,\n    False,\n}\n\nstruct VarTypeCond {\n    when: NodeBool,\n    override_local_id: hir::LocalId,\n    override_type: ty::Ref<ty::Poly>,\n}\n\nimpl VarTypeCond {\n    fn with_when(self, when: NodeBool) -> VarTypeCond {\n        VarTypeCond { when, ..self }\n    }\n\n    fn into_inverted(self) -> VarTypeCond {\n        VarTypeCond {\n            when: if self.when == NodeBool::True {\n                NodeBool::False\n            } else {\n                NodeBool::True\n            },\n            ..self\n        }\n    }\n}\n\nnew_indexing_id_type!(FreeTyId, u32);\nnew_indexing_id_type!(InputDefId, u32);\n\n/// Partially inferred function application\n///\n/// The function has been inferred while the arguments have not\nstruct FunApp {\n    fun_expr: hir::Expr<hir::Inferred>,\n    fixed_arg_exprs: Vec<hir::Expr<hir::Lowered>>,\n    rest_arg_expr: Option<hir::Expr<hir::Lowered>>,\n}\n\nenum VarType {\n    // Introduced a definition that has yet to be processed\n    Pending(InputDefId),\n    // (def) currently having its type inferred\n    Recursive,\n\n    /// Type depends on a value that failed to type check\n    Error,\n\n    /// Scalar value being inferred\n    ParamScalar(FreeTyId),\n\n    /// Rest list being inferred\n    ///\n    /// The referenced free type is the member type of the uniform rest list.\n    ParamRest(FreeTyId),\n\n    // Declared or previously inferred type\n    Known(ty::Ref<ty::Poly>),\n}\n\n#[derive(Clone)]\nenum PurityVar {\n    Free(purity::Ref),\n    Known(purity::Ref),\n}\n\n#[derive(Clone)]\nstruct RetExprResultUse<'a> {\n    /// Span of the declared return type if any\n    ret_type_span: Option<Span>,\n    ret_type: &'a ty::Ref<ty::Poly>,\n    known_self_type: Option<&'a ty::Fun>,\n}\n\n/// Describes the use of an expression's result value\n#[derive(Clone)]\nenum ResultUse<'a> {\n    /// Non-return expression with a used value\n    InnerExpr(&'a ty::Ref<ty::Poly>),\n\n    /// Expression used as the return value of a function\n    RetExpr(RetExprResultUse<'a>),\n\n    /// Expression with an unused value\n    ///\n    /// The most common example is a non-terminal expression in a `(do)`.\n    Unused(&'a ty::Ref<ty::Poly>),\n}\n\nimpl<'a> ResultUse<'a> {\n    /// Returns the required type for an expression's result value\n    fn required_type(&self) -> &'a ty::Ref<ty::Poly> {\n        match self {\n            ResultUse::InnerExpr(required_type) => required_type,\n            ResultUse::RetExpr(RetExprResultUse { ret_type, .. }) => ret_type,\n            ResultUse::Unused(required_type) => required_type,\n        }\n    }\n\n    /// Returns `true` if the expression's result value is used\n    fn is_used(&self) -> bool {\n        !matches!(self, ResultUse::Unused(_))\n    }\n}\n\nimpl PurityVar {\n    fn into_poly(self) -> purity::Ref {\n        match self {\n            PurityVar::Free(poly) => poly,\n            PurityVar::Known(poly) => poly,\n        }\n    }\n}\n\nenum InputDef {\n    Pending(hir::Def<hir::Lowered>),\n    Complete,\n}\n\npub type InferredLocals = HashMap<hir::LocalId, ty::Ref<ty::Poly>>;\npub type InferredModuleVars = HashMap<ModuleId, Arc<InferredLocals>>;\n\npub struct InferredModule {\n    pub inferred_locals: InferredLocals,\n    pub defs: Vec<hir::Def<hir::Inferred>>,\n}\n\nstruct RecursiveDefsCtx<'types> {\n    input_defs: Vec<InputDef>,\n    complete_defs: Vec<hir::Def<hir::Inferred>>,\n\n    // The inferred types for free types in the order they're encountered\n    //\n    // Each (def), (let) and (fn) push entries to `free_ty_polys` before they evaluate their body\n    // and then pop them off afterwards.\n    free_ty_polys: Vec<ty::Ref<ty::Poly>>,\n\n    self_locals: HashMap<hir::LocalId, VarType>,\n    imported_vars: &'types InferredModuleVars,\n}\n\n/// Tries to convert a polymorphic type to a literal boolean value\nfn try_to_bool(poly: &ty::Ref<ty::Poly>) -> Option<bool> {\n    match poly {\n        ty::Ref::Fixed(Ty::LitBool(v)) => Some(*v),\n        _ => None,\n    }\n}\n\nfn unify_app_purity(pv: &mut PurityVar, app_purity: &purity::Ref) {\n    if let PurityVar::Free(ref mut free_purity) = pv {\n        *free_purity = ty::unify::unify_purity_refs(free_purity, app_purity)\n    };\n}\n\n/// Inspects the mismatched sub and parent types to attempt to produce an understandable type error\nfn error_kind_for_type_error(\n    value_poly: &ty::Ref<ty::Poly>,\n    value_use: &ResultUse<'_>,\n) -> ErrorKind {\n    if let ty::Ref::Fixed(Ty::TopFun(top_fun)) = value_use.required_type() {\n        let topmost_fun = ty::TopFun::new(Purity::Impure.into(), Ty::Any.into()).into();\n        let impure_top_fun = ty::TopFun::new(Purity::Impure.into(), top_fun.ret().clone()).into();\n\n        if !ty::is_a::ty_ref_is_a(value_poly, &topmost_fun) {\n            // We aren't a function at all\n            return ErrorKind::IsNotFun(value_poly.clone());\n        } else if ty::is_a::ty_ref_is_a(value_poly, &impure_top_fun) {\n            // We have the right return type but the wrong purity\n            return ErrorKind::IsNotPurity(value_poly.clone(), top_fun.purity().clone());\n        }\n    }\n\n    match value_use {\n        ResultUse::Unused(required_type) | ResultUse::InnerExpr(required_type) => {\n            ErrorKind::IsNotTy(value_poly.clone(), (*required_type).clone())\n        }\n        ResultUse::RetExpr(RetExprResultUse {\n            ret_type_span,\n            ret_type,\n            ..\n        }) => ErrorKind::IsNotRetTy(IsNotRetTy::new(\n            value_poly.clone(),\n            (*ret_type).clone(),\n            *ret_type_span,\n        )),\n    }\n}\n\n/// Ensures `value_poly` is appropriate for `value_use`\nfn ensure_is_a(\n    span: Span,\n    value_poly: &ty::Ref<ty::Poly>,\n    value_use: &ResultUse<'_>,\n) -> Result<()> {\n    if !value_use.is_used() {\n        // We don't throw type errors for unused values\n        return Ok(());\n    }\n\n    if ty::is_a::ty_ref_is_a(value_poly, value_use.required_type()) {\n        return Ok(());\n    }\n\n    let error_kind = error_kind_for_type_error(value_poly, value_use);\n    Err(Error::new(span, error_kind))\n}\n\nfn member_type_for_poly_list(\n    span: Span,\n    poly_type: &ty::Ref<ty::Poly>,\n) -> Result<ty::Ref<ty::Poly>> {\n    if poly_type == &Ty::Any.into() {\n        return Ok(Ty::Any.into());\n    }\n\n    let list = poly_type\n        .find_member(|t| {\n            if let Ty::List(list) = t {\n                Some(list)\n            } else {\n                None\n            }\n        })\n        .ok_or_else(|| {\n            Error::new(\n                span,\n                ErrorKind::IsNotTy(\n                    poly_type.clone(),\n                    ty::List::new_uniform(Ty::Any.into()).into(),\n                ),\n            )\n        })?;\n\n    Ok(ListIterator::new(list).collect_rest())\n}\n\n/// Preserves expressions for their side effects\n///\n/// `side_effect_exprs` are discarded if they can't cause side effects. `value_expr` will be used as\n/// the value of the returned expression.\nfn keep_exprs_for_side_effects(\n    side_effect_exprs: impl IntoIterator<Item = hir::Expr<hir::Inferred>>,\n    value_expr: hir::Expr<hir::Inferred>,\n) -> hir::Expr<hir::Inferred> {\n    let mut needed_exprs: Vec<_> = side_effect_exprs\n        .into_iter()\n        .filter(expr_can_side_effect)\n        .collect();\n\n    if needed_exprs.is_empty() {\n        // We don't need any of the `side_effect_exprs`\n        return value_expr;\n    }\n\n    let result_ty = value_expr.result_ty.clone();\n    needed_exprs.push(value_expr);\n\n    hir::Expr {\n        result_ty,\n        kind: hir::ExprKind::Do(needed_exprs),\n    }\n}\n\nimpl<'types> RecursiveDefsCtx<'types> {\n    fn new(\n        imported_vars: &'types InferredModuleVars,\n        defs: Vec<hir::Def<hir::Lowered>>,\n    ) -> RecursiveDefsCtx<'types> {\n        let mut self_locals = HashMap::new();\n\n        // We do this in reverse order because we infer our defs in reverse order. This doesn't\n        // matter for correctness. However, presumably most definitions have more dependencies\n        // before them than after them. Visiting them in forward order should cause less\n        // recursive resolution.\n        let input_defs = defs\n            .into_iter()\n            .rev()\n            .enumerate()\n            .map(|(idx, hir_def)| {\n                let def_id = InputDefId::new(idx);\n\n                typeck::destruc::visit_locals(&hir_def.destruc, &mut |local_id, decl_type| {\n                    let var_type = match decl_type {\n                        hir::DeclTy::Known(poly_type) => VarType::Known(poly_type.clone()),\n                        hir::DeclTy::Free => {\n                            // Record the definition ID so we can deal with forward type references\n                            VarType::Pending(def_id)\n                        }\n                    };\n\n                    self_locals.insert(local_id, var_type);\n                });\n\n                InputDef::Pending(hir_def)\n            })\n            .collect::<Vec<InputDef>>();\n\n        RecursiveDefsCtx {\n            complete_defs: Vec::with_capacity(input_defs.len()),\n            input_defs,\n            free_ty_polys: vec![],\n\n            self_locals,\n            imported_vars,\n        }\n    }\n\n    fn new_local_ref_node(\n        &self,\n        span: Span,\n        local_id: hir::LocalId,\n        poly_type: ty::Ref<ty::Poly>,\n    ) -> InferredNode {\n        // We can't override type conditions across modules\n        let type_conds = if poly_type == Ty::Bool.into() {\n            // This seems useless but it allows occurrence typing to work if this type\n            // flows through another node such as `(do)` or `(let)`\n            vec![\n                VarTypeCond {\n                    when: NodeBool::True,\n                    override_local_id: local_id,\n                    override_type: Ty::LitBool(true).into(),\n                },\n                VarTypeCond {\n                    when: NodeBool::False,\n                    override_local_id: local_id,\n                    override_type: Ty::LitBool(false).into(),\n                },\n            ]\n        } else {\n            vec![]\n        };\n\n        InferredNode {\n            expr: hir::Expr {\n                result_ty: poly_type,\n                kind: hir::ExprKind::LocalRef(span, local_id),\n            },\n            type_conds,\n        }\n    }\n\n    fn insert_free_ty(&mut self, initial_type: ty::Ref<ty::Poly>) -> FreeTyId {\n        FreeTyId::new_entry_id(&mut self.free_ty_polys, initial_type)\n    }\n\n    fn visit_lit(&mut self, result_use: &ResultUse<'_>, datum: Datum) -> Result<InferredNode> {\n        let lit_type = ty::datum::ty_ref_for_datum(&datum);\n        ensure_is_a(datum.span(), &lit_type, result_use)?;\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty: lit_type,\n                kind: hir::ExprKind::Lit(datum),\n            },\n            type_conds: vec![],\n        })\n    }\n\n    /// Calls the passed function with var types overridden by the specified type conds\n    ///\n    /// The var types will be restored after the function returns.\n    #[allow(clippy::needless_collect)]\n    fn with_type_conds_applied<F, R>(\n        &mut self,\n        type_conds: &[VarTypeCond],\n        node_bool: NodeBool,\n        inner: F,\n    ) -> R\n    where\n        F: FnOnce(&mut Self) -> R,\n    {\n        let restore_var_types = type_conds\n            .iter()\n            .filter(|tc| tc.when == node_bool)\n            .map(|type_cond| {\n                let VarTypeCond {\n                    override_local_id,\n                    ref override_type,\n                    ..\n                } = *type_cond;\n\n                (\n                    override_local_id,\n                    self.self_locals\n                        .insert(override_local_id, VarType::Known(override_type.clone()))\n                        .unwrap(),\n                )\n            })\n            .collect::<Vec<(hir::LocalId, VarType)>>();\n\n        let result = inner(self);\n\n        // Restore the original types\n        // We need to use `rev()` to make sure we restore the original type if multiple conds\n        // applied to a single var.\n        for (local_id, original_var_type) in restore_var_types.into_iter().rev() {\n            self.self_locals.insert(local_id, original_var_type);\n        }\n\n        result\n    }\n\n    fn visit_cond(\n        &mut self,\n        pv: &mut PurityVar,\n        result_use: &ResultUse<'_>,\n        cond: hir::Cond<hir::Lowered>,\n    ) -> Result<InferredNode> {\n        use std::iter;\n\n        let hir::Cond {\n            span,\n            test_expr,\n            true_expr,\n            false_expr,\n            ..\n        } = cond;\n\n        let test_node = self.visit_expr(pv, &ResultUse::InnerExpr(&Ty::Bool.into()), test_expr)?;\n        let test_known_bool = try_to_bool(test_node.result_ty());\n\n        // If a branch isn't taken it doesn't need to match the type of the cond expression\n        let unused_use = &ResultUse::Unused(result_use.required_type());\n        let (true_result_use, false_result_use) = if test_node.is_divergent() {\n            (unused_use, unused_use)\n        } else {\n            match test_known_bool {\n                Some(true) => (result_use, unused_use),\n                Some(false) => (unused_use, result_use),\n                None => (result_use, result_use),\n            }\n        };\n\n        let true_node =\n            self.with_type_conds_applied(&test_node.type_conds, NodeBool::True, |s| {\n                s.visit_expr(pv, true_result_use, true_expr)\n            })?;\n\n        let false_node =\n            self.with_type_conds_applied(&test_node.type_conds, NodeBool::False, |s| {\n                s.visit_expr(pv, false_result_use, false_expr)\n            })?;\n\n        if test_node.is_divergent() {\n            // Test diverged; we don't need the branches\n            return Ok(test_node);\n        }\n\n        // If the test is static then we can significantly optimise\n        match test_known_bool {\n            Some(true) => Ok(InferredNode {\n                // Preserve the test expr in case it has side effects but remove the cond\n                expr: keep_exprs_for_side_effects(iter::once(test_node.expr), true_node.expr),\n                ..true_node\n            }),\n            Some(false) => Ok(InferredNode {\n                expr: keep_exprs_for_side_effects(iter::once(test_node.expr), false_node.expr),\n                ..false_node\n            }),\n            None => {\n                let result_ty =\n                    ty::unify::unify_to_ty_ref(true_node.result_ty(), false_node.result_ty());\n\n                let false_node_bool = try_to_bool(false_node.result_ty());\n                let true_node_bool = try_to_bool(true_node.result_ty());\n\n                let mut type_conds: Vec<VarTypeCond> = test_node\n                    .type_conds\n                    .into_iter()\n                    .filter_map(|type_cond| match type_cond.when {\n                        NodeBool::True => {\n                            if false_node_bool == Some(false) {\n                                // If the false node is always false then our result type being\n                                // true implies the test was true\n                                Some(type_cond.with_when(NodeBool::True))\n                            } else if false_node_bool == Some(true) {\n                                // If the false node is always true then our result type being\n                                // false implies the test was true\n                                Some(type_cond.with_when(NodeBool::False))\n                            } else {\n                                None\n                            }\n                        }\n                        NodeBool::False => {\n                            if true_node_bool == Some(true) {\n                                Some(type_cond.with_when(NodeBool::False))\n                            } else if true_node_bool == Some(false) {\n                                Some(type_cond.with_when(NodeBool::True))\n                            } else {\n                                None\n                            }\n                        }\n                    })\n                    .collect();\n\n                // If the false branch is always false we can move the occurrence typing from the\n                // true branch upwards. The same reasoning applies for the true branch. Note that\n                // this may override conds that we brought in from our test node. These should\n                // already have the outer occurrence typing applied so they will be more specific.\n                if false_node_bool == Some(false) {\n                    type_conds.extend(true_node.type_conds);\n                }\n                if true_node_bool == Some(true) {\n                    type_conds.extend(false_node.type_conds);\n                }\n\n                Ok(InferredNode {\n                    expr: hir::Expr {\n                        result_ty,\n                        kind: hir::ExprKind::Cond(Box::new(hir::Cond {\n                            span,\n                            test_expr: test_node.expr,\n                            true_expr: true_node.expr,\n                            false_expr: false_node.expr,\n                        })),\n                    },\n                    type_conds,\n                })\n            }\n        }\n    }\n\n    fn visit_ty_pred(\n        &self,\n        result_use: &ResultUse<'_>,\n        span: Span,\n        test_ty: ty::pred::TestTy,\n    ) -> Result<InferredNode> {\n        let pred_type = Ty::TyPred(test_ty.clone()).into();\n        ensure_is_a(span, &pred_type, result_use)?;\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty: pred_type,\n                kind: hir::ExprKind::TyPred(span, test_ty),\n            },\n            type_conds: vec![],\n        })\n    }\n\n    fn visit_eq_pred(&self, result_use: &ResultUse<'_>, span: Span) -> Result<InferredNode> {\n        let pred_type = Ty::EqPred.into();\n        ensure_is_a(span, &pred_type, result_use)?;\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty: pred_type,\n                kind: hir::ExprKind::EqPred(span),\n            },\n            type_conds: vec![],\n        })\n    }\n\n    fn visit_record_cons(\n        &self,\n        result_use: &ResultUse<'_>,\n        span: Span,\n        record_cons: record::ConsId,\n    ) -> Result<InferredNode> {\n        let value_cons_fun_type = record::Cons::value_cons_fun_type(&record_cons).into();\n        ensure_is_a(span, &value_cons_fun_type, result_use)?;\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty: value_cons_fun_type,\n                kind: hir::ExprKind::RecordCons(span, record_cons),\n            },\n            type_conds: vec![],\n        })\n    }\n\n    fn visit_field_accessor(\n        &self,\n        result_use: &ResultUse<'_>,\n        field_accessor: Box<hir::FieldAccessor>,\n    ) -> Result<InferredNode> {\n        let record_cons = &field_accessor.record_cons;\n        let record_field = &record_cons.fields()[field_accessor.field_index];\n\n        let field_accessor_fun_type = record_field.accessor_fun_type(record_cons).into();\n        ensure_is_a(field_accessor.span, &field_accessor_fun_type, result_use)?;\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty: field_accessor_fun_type,\n                kind: hir::ExprKind::FieldAccessor(field_accessor),\n            },\n            type_conds: vec![],\n        })\n    }\n\n    fn type_for_free_ref(\n        &self,\n        required_type: &ty::Ref<ty::Poly>,\n        span: Span,\n        current_type: &ty::Ref<ty::Poly>,\n    ) -> Result<ty::Ref<ty::Poly>> {\n        // Unlike references to known variables the `current_type` and `required_type` have equal\n        // footing. We intersect here to find the commonality between the two types. This will\n        // become the new type of the variable.\n        ty::intersect::intersect_ty_refs(required_type, current_type).map_err(|_| {\n            Error::new(\n                span,\n                ErrorKind::VarHasEmptyType(current_type.clone(), required_type.clone()),\n            )\n        })\n    }\n\n    fn visit_export_ref(\n        &mut self,\n        result_use: &ResultUse<'_>,\n        span: Span,\n        export_id: hir::ExportId,\n    ) -> Result<InferredNode> {\n        // This comes from an imported module\n        let module_id = export_id.module_id();\n        let local_id = export_id.local_id();\n\n        let known_type = &self.imported_vars[&module_id][&local_id];\n        ensure_is_a(span, known_type, result_use)?;\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty: known_type.clone(),\n                kind: hir::ExprKind::ExportRef(span, export_id),\n            },\n            type_conds: vec![],\n        })\n    }\n\n    fn visit_local_ref(\n        &mut self,\n        result_use: &ResultUse<'_>,\n        span: Span,\n        local_id: hir::LocalId,\n    ) -> Result<InferredNode> {\n        let pending_def_id = match self.self_locals[&local_id] {\n            VarType::Pending(def_id) => def_id,\n            VarType::Recursive => return Err(Error::new(span, ErrorKind::RecursiveType)),\n            VarType::Error => return Err(Error::new(span, ErrorKind::DependsOnError)),\n            VarType::Known(ref known_type) => {\n                ensure_is_a(span, known_type, result_use)?;\n                return Ok(self.new_local_ref_node(span, local_id, known_type.clone()));\n            }\n            VarType::ParamScalar(free_ty_id) => {\n                let current_type = &self.free_ty_polys[free_ty_id.to_usize()];\n                let new_free_type =\n                    self.type_for_free_ref(result_use.required_type(), span, current_type)?;\n\n                self.free_ty_polys[free_ty_id.to_usize()] = new_free_type.clone();\n                return Ok(self.new_local_ref_node(span, local_id, new_free_type));\n            }\n            VarType::ParamRest(free_ty_id) => {\n                let current_member_type = &self.free_ty_polys[free_ty_id.to_usize()];\n                let required_member_type =\n                    member_type_for_poly_list(span, result_use.required_type())?;\n\n                let new_free_type =\n                    self.type_for_free_ref(&required_member_type, span, current_member_type)?;\n\n                self.free_ty_polys[free_ty_id.to_usize()] = new_free_type.clone();\n                let rest_list_type = ty::List::new_uniform(new_free_type).into();\n\n                // Make sure we didn't require a specific list type e.g. `(List Int Int Int)`\n                ensure_is_a(span, &rest_list_type, result_use)?;\n\n                return Ok(self.new_local_ref_node(span, local_id, rest_list_type));\n            }\n        };\n\n        self.recurse_into_def_id(pending_def_id)?;\n        // This assumes `recurse_into_def_id` has populated our variables now\n        self.visit_local_ref(result_use, span, local_id)\n    }\n\n    fn visit_do(\n        &mut self,\n        pv: &mut PurityVar,\n        result_use: &ResultUse<'_>,\n        mut exprs: Vec<hir::Expr<hir::Lowered>>,\n    ) -> Result<InferredNode> {\n        let terminal_expr = if let Some(terminal_expr) = exprs.pop() {\n            terminal_expr\n        } else {\n            return Ok(InferredNode {\n                expr: hir::Expr {\n                    result_ty: Ty::unit().into(),\n                    kind: hir::ExprKind::Do(vec![]),\n                },\n                type_conds: vec![],\n            });\n        };\n\n        let mut is_divergent = false;\n        let mut inferred_exprs = Vec::with_capacity(exprs.len() + 1);\n        for non_terminal_expr in exprs {\n            let was_divergent = is_divergent;\n            // The type of this expression doesn't matter; its value is discarded\n            let node =\n                self.visit_expr(pv, &ResultUse::Unused(&Ty::Any.into()), non_terminal_expr)?;\n\n            is_divergent = was_divergent || node.is_divergent();\n            if !was_divergent && expr_can_side_effect(&node.expr) {\n                inferred_exprs.push(node.expr);\n            }\n        }\n\n        if is_divergent {\n            self.visit_expr(pv, &ResultUse::Unused(&Ty::Any.into()), terminal_expr)?;\n\n            Ok(InferredNode {\n                expr: hir::Expr {\n                    result_ty: Ty::never().into(),\n                    kind: hir::ExprKind::Do(inferred_exprs),\n                },\n                type_conds: vec![],\n            })\n        } else {\n            let terminal_node = self.visit_expr(pv, result_use, terminal_expr)?;\n            let result_ty = terminal_node.result_ty().clone();\n\n            if result_use.is_used() || expr_can_side_effect(&terminal_node.expr) {\n                inferred_exprs.push(terminal_node.expr);\n            }\n\n            Ok(InferredNode {\n                expr: hir::Expr {\n                    result_ty,\n                    kind: hir::ExprKind::Do(inferred_exprs),\n                },\n                type_conds: terminal_node.type_conds,\n            })\n        }\n    }\n\n    /// Visits a function expression\n    ///\n    /// This does a limited amount of backwards type propagation; it will attempt to fill in any\n    /// free param or ret types from `required_type`. All declared types in the function will be\n    /// taken as-is.\n    fn visit_fun(\n        &mut self,\n        result_use: &ResultUse<'_>,\n        decl_fun: hir::Fun<hir::Lowered>,\n        self_local_id: Option<hir::LocalId>,\n    ) -> Result<InferredNode> {\n        let span = decl_fun.span;\n\n        // This is set to false if we encounter any free types in our params or ret\n        let mut decl_tys_are_known = true;\n\n        let required_fun_type = result_use.required_type().find_member(|t| {\n            if let Ty::Fun(fun) = t {\n                Some(fun.as_ref())\n            } else {\n                None\n            }\n        });\n\n        let required_top_fun_type = required_fun_type.map(ty::Fun::top_fun).or_else(|| {\n            result_use.required_type().find_member(|t| {\n                if let Ty::TopFun(top_fun) = t {\n                    Some(top_fun.as_ref())\n                } else {\n                    None\n                }\n            })\n        });\n\n        let initial_param_type: ty::List<ty::Poly> = typeck::destruc::type_for_decl_list_destruc(\n            &decl_fun.params,\n            // Use the required type as a guide for any free types in the parameter list\n            required_fun_type.map(|fun| ListIterator::new(fun.params())),\n        );\n\n        // Bind all of our parameter variables\n        let free_ty_offset = self.destruc_list_value(\n            &decl_fun.params,\n            ListIterator::new(&initial_param_type),\n            // If a parameter has a free decl type then we can refine the type\n            true,\n        );\n\n        if free_ty_offset != self.free_ty_polys.len() {\n            // We have free parameter types\n            decl_tys_are_known = false;\n        }\n\n        // Use the declared return type if possible\n        let wanted_ret_type = match decl_fun.ret_ty {\n            hir::DeclTy::Known(poly) => poly,\n            hir::DeclTy::Free => {\n                decl_tys_are_known = false;\n\n                if let Some(required_top_fun_type) = required_top_fun_type {\n                    // Fall back to the backwards type\n                    required_top_fun_type.ret().clone()\n                } else {\n                    // Use Any as a last resort\n                    Ty::Any.into()\n                }\n            }\n        };\n\n        let mut known_self_type: Option<ty::Fun> = None;\n\n        let mut fun_pv = match decl_fun.purity {\n            hir::DeclPurity::Known(poly_purity) => {\n                if decl_tys_are_known {\n                    let self_type = ty::Fun::new(\n                        decl_fun.pvars.clone(),\n                        decl_fun.tvars.clone(),\n                        ty::TopFun::new(poly_purity.clone(), wanted_ret_type.clone()),\n                        initial_param_type,\n                    );\n\n                    // We have a fully known type; allow recursive calls\n                    if let Some(self_local_id) = self_local_id {\n                        self.self_locals\n                            .insert(self_local_id, VarType::Known(self_type.clone().into()));\n                    }\n                    known_self_type = Some(self_type);\n                }\n\n                PurityVar::Known(poly_purity)\n            }\n            hir::DeclPurity::Free => {\n                // Functions start pure until proven otherwise\n                PurityVar::Free(Purity::Pure.into())\n            }\n        };\n\n        let body_result_use = ResultUse::RetExpr(RetExprResultUse {\n            ret_type_span: decl_fun.ret_ty_span,\n            ret_type: &wanted_ret_type,\n            known_self_type: known_self_type.as_ref(),\n        });\n\n        let body_node = self.visit_expr(&mut fun_pv, &body_result_use, decl_fun.body_expr)?;\n        let revealed_ret_type = body_node.result_ty();\n        let revealed_purity = fun_pv.into_poly();\n\n        let revealed_param_destruc = {\n            let mut inferred_free_types = self.free_ty_polys.drain(free_ty_offset..);\n\n            destruc::subst_list_destruc(&mut inferred_free_types, decl_fun.params)\n        };\n        let revealed_param_type = hir::destruc::poly_for_list_destruc(&revealed_param_destruc);\n\n        let revealed_type = ty::Fun::new(\n            decl_fun.pvars.clone(),\n            decl_fun.tvars.clone(),\n            ty::TopFun::new(revealed_purity.clone(), revealed_ret_type.clone()),\n            revealed_param_type,\n        )\n        .into();\n\n        let revealed_fun = hir::Fun::<hir::Inferred> {\n            span,\n            pvars: decl_fun.pvars,\n            tvars: decl_fun.tvars,\n            purity: revealed_purity,\n            params: revealed_param_destruc,\n            ret_ty: revealed_ret_type.clone(),\n            ret_ty_span: decl_fun.ret_ty_span,\n            body_expr: body_node.expr,\n        };\n\n        ensure_is_a(span, &revealed_type, result_use)?;\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty: revealed_type,\n                kind: hir::ExprKind::Fun(Box::new(revealed_fun)),\n            },\n            type_conds: vec![],\n        })\n    }\n\n    /// Visit a function application\n    ///\n    /// This has a fairly convoluted algorithm for resolving type variables. The essential\n    /// problem is we don't know the type of our parameters until we visit their expressions.\n    /// However, we also need to provide the parameters with backwards type information which may\n    /// come from other parameters. We need to decide in which order to reveal the types which\n    /// maximise the amount of useful information.\n    ///\n    /// Firstly, we visit every non-function fixed parameter and the rest parameter with the\n    /// evidence from the return type. We collect our evidence in to a staged selection context to\n    /// ensure if a type variable appears in multiple parameters they unify instead of supersede\n    /// each other.\n    ///\n    /// In the next phase we visit every function fixed parameter. This is done in a second phase\n    /// as these functions frequently relate to both the type of the parameters and the return\n    /// type (e.g. `map`).\n    ///\n    /// The final phase selects the return type. This uses all the evidence collected above.\n    fn visit_fun_app(\n        &mut self,\n        pv: &mut PurityVar,\n        result_use: &ResultUse<'_>,\n        span: Span,\n        fun_type: &ty::Fun,\n        fun_app: FunApp,\n    ) -> Result<InferredNode> {\n        let FunApp {\n            fun_expr,\n            fixed_arg_exprs,\n            rest_arg_expr,\n        } = fun_app;\n\n        // The context used to select the types for our non-function parameters\n        let mut non_fun_param_stx = ty::select::SelectCtx::new(fun_type.pvars(), fun_type.tvars());\n\n        if let PurityVar::Known(purity_type) = pv {\n            if purity_type != &Purity::Impure.into() {\n                // Add our purity information\n                non_fun_param_stx.add_evidence_purity(fun_type.purity(), purity_type);\n            }\n        }\n\n        // The context used to select the types for our function parameters. This includes the\n        // evidence gathered when visiting non-function parameters.\n        let mut fun_param_stx = non_fun_param_stx.clone();\n        let non_fun_param_pta = non_fun_param_stx.into_poly_ty_args();\n\n        // Iterate over our parameter type to feed type information in to the arguments\n        let mut param_iter = ListIterator::new(fun_type.params());\n\n        let supplied_arg_count = fixed_arg_exprs.len();\n        let wanted_arity = WantedArity::new(param_iter.fixed_len(), param_iter.has_rest());\n\n        let mut is_divergent = false;\n\n        struct PendingFixedArg<'ty> {\n            index: usize,\n            param_type: &'ty ty::Ref<ty::Poly>,\n            expr: hir::Expr<hir::Lowered>,\n        }\n\n        let mut fun_fixed_args: Vec<PendingFixedArg<'_>> = vec![];\n        let mut non_fun_fixed_args: Vec<PendingFixedArg<'_>> = vec![];\n        let mut inferred_fixed_arg_exprs: Vec<(usize, hir::Expr<hir::Inferred>)> =\n            Vec::with_capacity(fixed_arg_exprs.len());\n\n        // Pre-visit our fixed args and categorise them as fun and non-fun\n        for (index, fixed_arg_expr) in fixed_arg_exprs.into_iter().enumerate() {\n            let param_type = param_iter.next().ok_or_else(|| {\n                Error::new(\n                    span,\n                    ErrorKind::WrongArity(supplied_arg_count, wanted_arity),\n                )\n            })?;\n\n            let pending_fixed_arg = PendingFixedArg {\n                index,\n                param_type,\n                expr: fixed_arg_expr,\n            };\n\n            if let ty::Ref::Fixed(Ty::Fun(_)) = param_type {\n                fun_fixed_args.push(pending_fixed_arg);\n            } else {\n                non_fun_fixed_args.push(pending_fixed_arg);\n            }\n        }\n\n        for PendingFixedArg {\n            index,\n            param_type,\n            expr,\n        } in non_fun_fixed_args\n        {\n            let wanted_arg_type = ty::subst::subst_poly(&non_fun_param_pta, param_type);\n            let fixed_arg_node =\n                self.visit_expr(pv, &ResultUse::InnerExpr(&wanted_arg_type), expr)?;\n\n            is_divergent = is_divergent || fixed_arg_node.is_divergent();\n\n            fun_param_stx.add_evidence(param_type, fixed_arg_node.result_ty());\n            inferred_fixed_arg_exprs.push((index, fixed_arg_node.expr));\n        }\n\n        // Visit our rest arg next so it's grouped in the first phase\n        let inferred_rest_arg_expr = if let Some(rest_arg_expr) = rest_arg_expr {\n            let tail_type = param_iter.tail_type().into();\n            let wanted_tail_type = ty::subst::subst_poly(&non_fun_param_pta, &tail_type);\n            let rest_arg_node =\n                self.visit_expr(pv, &ResultUse::InnerExpr(&wanted_tail_type), rest_arg_expr)?;\n\n            is_divergent = is_divergent || rest_arg_node.is_divergent();\n\n            fun_param_stx.add_evidence(&tail_type, rest_arg_node.result_ty());\n            Some(rest_arg_node.expr)\n        } else if param_iter.fixed_len() > 0 {\n            // We wanted more args!\n            return Err(Error::new(\n                span,\n                ErrorKind::WrongArity(supplied_arg_count, wanted_arity),\n            ));\n        } else {\n            // We can use the lack of a rest arg as type evidence\n            fun_param_stx.add_evidence(&param_iter.collect_rest(), &Ty::never().into());\n            None\n        };\n\n        // The context used to select our return type. This includes the evidence gathered when\n        // visiting all parameters.\n        let mut ret_stx = fun_param_stx.clone();\n        let fun_param_pta = fun_param_stx.into_poly_ty_args();\n\n        for PendingFixedArg {\n            index,\n            param_type,\n            expr,\n        } in fun_fixed_args\n        {\n            let wanted_arg_type = ty::subst::subst_poly(&fun_param_pta, param_type);\n            let fixed_arg_node =\n                self.visit_expr(pv, &ResultUse::InnerExpr(&wanted_arg_type), expr)?;\n\n            is_divergent = is_divergent || fixed_arg_node.is_divergent();\n\n            ret_stx.add_evidence(param_type, fixed_arg_node.result_ty());\n            inferred_fixed_arg_exprs.push((index, fixed_arg_node.expr));\n        }\n\n        inferred_fixed_arg_exprs.sort_unstable_by_key(|k| k.0);\n        let inferred_fixed_arg_exprs = inferred_fixed_arg_exprs.into_iter().map(|e| e.1).collect();\n\n        let ret_pta = ret_stx\n            .into_complete_poly_ty_args()\n            .map_err(|error| match error {\n                ty::select::Error::UnselectedPVar(pvar) => {\n                    Error::new(span, ErrorKind::UnselectedPVar(pvar.clone()))\n                }\n                ty::select::Error::UnselectedTVar(tvar) => {\n                    Error::new(span, ErrorKind::UnselectedTVar(tvar.clone()))\n                }\n            })?;\n\n        let ret_type = if is_divergent {\n            Ty::never().into()\n        } else {\n            ty::subst::subst_poly(&ret_pta, fun_type.ret())\n        };\n\n        // Keep track of the purity from the application\n        let app_purity = ty::subst::subst_purity(&ret_pta, fun_type.purity());\n        unify_app_purity(pv, &app_purity);\n\n        ensure_is_a(span, &ret_type, result_use)?;\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty: ret_type,\n                kind: hir::ExprKind::App(Box::new(hir::App {\n                    span,\n                    fun_expr,\n                    ty_args: ret_pta,\n                    fixed_arg_exprs: inferred_fixed_arg_exprs,\n                    rest_arg_expr: inferred_rest_arg_expr,\n                })),\n            },\n            type_conds: vec![],\n        })\n    }\n\n    /// Visit a `(recur)`\n    ///\n    /// This is similar to `visit_fun_app`. However, we require that the `(recur)`'s arguments match\n    /// the generic function type. This allows us to tail recurse when monomorphising polymorphic\n    /// functions because we know we can re-enter the same polymorph the `(recur)` occurs in.\n    ///\n    /// This sounds more complicated than normal function application but it's actual significantly\n    /// easier due to not having to perform type variable selection.\n    fn visit_recur(\n        &mut self,\n        pv: &mut PurityVar,\n        result_use: &ResultUse<'_>,\n        recur: hir::Recur<hir::Lowered>,\n    ) -> Result<InferredNode> {\n        let hir::Recur {\n            span,\n            fixed_arg_exprs,\n            rest_arg_expr,\n            ..\n        } = recur;\n\n        let ret_expr_use = if let ResultUse::RetExpr(ret_expr_use) = result_use {\n            ret_expr_use\n        } else {\n            return Err(Error::new(span, ErrorKind::NonTailRecur));\n        };\n\n        let self_type = if let Some(self_type) = ret_expr_use.known_self_type {\n            self_type\n        } else {\n            return Err(Error::new(span, ErrorKind::RecurWithoutFunTypeDecl));\n        };\n\n        // Iterate over our parameter type to feed type information in to the arguments\n        let mut param_iter = ListIterator::new(self_type.params());\n\n        let supplied_arg_count = fixed_arg_exprs.len();\n        let wanted_arity = WantedArity::new(param_iter.fixed_len(), param_iter.has_rest());\n\n        let mut is_divergent = false;\n\n        let inferred_fixed_arg_exprs = fixed_arg_exprs\n            .into_iter()\n            .map(|fixed_arg_expr| {\n                let param_type = param_iter.next().ok_or_else(|| {\n                    Error::new(\n                        span,\n                        ErrorKind::WrongArity(supplied_arg_count, wanted_arity),\n                    )\n                })?;\n\n                let fixed_arg_node =\n                    self.visit_expr(pv, &ResultUse::InnerExpr(param_type), fixed_arg_expr)?;\n\n                is_divergent = is_divergent || fixed_arg_node.is_divergent();\n                Ok(fixed_arg_node.expr)\n            })\n            .collect::<Result<Vec<_>>>()?;\n\n        let inferred_rest_arg_expr = if let Some(rest_arg_expr) = rest_arg_expr {\n            let tail_type = param_iter.tail_type().into();\n            let rest_arg_node =\n                self.visit_expr(pv, &ResultUse::InnerExpr(&tail_type), rest_arg_expr)?;\n\n            is_divergent = is_divergent || rest_arg_node.is_divergent();\n            Some(rest_arg_node.expr)\n        } else if param_iter.fixed_len() > 0 {\n            // We wanted more args!\n            return Err(Error::new(\n                span,\n                ErrorKind::WrongArity(supplied_arg_count, wanted_arity),\n            ));\n        } else {\n            None\n        };\n\n        let ret_type: ty::Ref<ty::Poly> = if is_divergent {\n            Ty::never().into()\n        } else {\n            self_type.ret().clone()\n        };\n\n        ensure_is_a(span, &ret_type, result_use)?;\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty: ret_type,\n                kind: hir::ExprKind::Recur(Box::new(hir::Recur {\n                    span,\n                    fixed_arg_exprs: inferred_fixed_arg_exprs,\n                    rest_arg_expr: inferred_rest_arg_expr,\n                })),\n            },\n            type_conds: vec![],\n        })\n    }\n\n    /// Visit type predicate application with a single fixed arg\n    ///\n    /// This supports full occurrence typing\n    fn visit_fixed_ty_pred_app(\n        &mut self,\n        pv: &mut PurityVar,\n        span: Span,\n        fun_expr: hir::Expr<hir::Inferred>,\n        test_ty: &ty::pred::TestTy,\n        subject_expr: hir::Expr<hir::Lowered>,\n    ) -> Result<InferredNode> {\n        use std::iter;\n\n        let subject_local_id = if let hir::ExprKind::LocalRef(_, local_id) = &subject_expr.kind {\n            Some(*local_id)\n        } else {\n            None\n        };\n\n        let subject_node =\n            self.visit_expr(pv, &ResultUse::InnerExpr(&Ty::Any.into()), subject_expr)?;\n\n        let subject_poly = subject_node.result_ty();\n        match test_ty.match_subject_ref(subject_poly) {\n            Some(known_result) => {\n                let result_ty = if subject_node.is_divergent() {\n                    Ty::never().into()\n                } else {\n                    Ty::LitBool(known_result).into()\n                };\n\n                // Get rid of the predicate application entirely\n                // Keep the subject expr around for its side effect\n                Ok(InferredNode {\n                    expr: keep_exprs_for_side_effects(\n                        iter::once(subject_node.expr),\n                        hir::Expr {\n                            result_ty,\n                            kind: hir::ExprKind::Lit(Datum::Bool(span, known_result)),\n                        },\n                    ),\n                    type_conds: vec![],\n                })\n            }\n            None => {\n                let poly_type = if subject_node.is_divergent() {\n                    Ty::never().into()\n                } else {\n                    Ty::Bool.into()\n                };\n\n                let type_conds = if let Some(override_local_id) = subject_local_id {\n                    let test_poly = test_ty.to_ty().into();\n                    let type_if_true = ty::intersect::intersect_ty_refs(subject_poly, &test_poly)\n                        .unwrap_or_else(|_| subject_poly.clone());\n\n                    let type_if_false = ty::subtract::subtract_ty_refs(subject_poly, &test_poly);\n\n                    vec![\n                        VarTypeCond {\n                            when: NodeBool::True,\n                            override_local_id,\n                            override_type: type_if_true,\n                        },\n                        VarTypeCond {\n                            when: NodeBool::False,\n                            override_local_id,\n                            override_type: type_if_false,\n                        },\n                    ]\n                } else {\n                    vec![]\n                };\n\n                Ok(InferredNode {\n                    expr: hir::Expr {\n                        result_ty: poly_type,\n                        kind: hir::ExprKind::App(Box::new(hir::App {\n                            span,\n                            fun_expr,\n                            ty_args: TyArgs::empty(),\n                            fixed_arg_exprs: vec![subject_node.expr],\n                            rest_arg_expr: None,\n                        })),\n                    },\n                    type_conds,\n                })\n            }\n        }\n    }\n\n    /// Visit type predicate application with a rest argument\n    ///\n    /// This can do static evaluation but it does not support occurrence typing. This is only\n    /// included to be orthogonal with other function applications; it's not terribly useful\n    /// otherwise.\n    fn visit_rest_ty_pred_app(\n        &mut self,\n        pv: &mut PurityVar,\n        span: Span,\n        fun_expr: hir::Expr<hir::Inferred>,\n        test_ty: &ty::pred::TestTy,\n        subject_list_expr: hir::Expr<hir::Lowered>,\n    ) -> Result<InferredNode> {\n        use std::iter;\n\n        let wanted_subject_list_type = ty::List::new_tuple(Box::new([Ty::Any.into()])).into();\n\n        let subject_list_node = self.visit_expr(\n            pv,\n            &ResultUse::InnerExpr(&wanted_subject_list_type),\n            subject_list_expr,\n        )?;\n\n        let subject_type = ListIterator::try_new_from_ty_ref(subject_list_node.result_ty())\n            .and_then(|mut iter| iter.next())\n            .expect(\"Unable to extract type argument from type predicate rest list\");\n\n        match test_ty.match_subject_ref(subject_type) {\n            Some(known_bool) => {\n                let result_ty = if subject_list_node.is_divergent() {\n                    Ty::never().into()\n                } else {\n                    Ty::LitBool(known_bool).into()\n                };\n\n                Ok(InferredNode {\n                    expr: keep_exprs_for_side_effects(\n                        iter::once(subject_list_node.expr),\n                        hir::Expr {\n                            result_ty,\n                            kind: hir::ExprKind::Lit(Datum::Bool(span, known_bool)),\n                        },\n                    ),\n                    type_conds: vec![],\n                })\n            }\n            None => {\n                let poly_type = if subject_list_node.is_divergent() {\n                    // The subject diverged so we diverged\n                    Ty::never().into()\n                } else {\n                    Ty::Bool.into()\n                };\n\n                Ok(InferredNode {\n                    expr: hir::Expr {\n                        result_ty: poly_type,\n                        kind: hir::ExprKind::App(Box::new(hir::App {\n                            span,\n                            fun_expr,\n                            ty_args: TyArgs::empty(),\n                            fixed_arg_exprs: vec![],\n                            rest_arg_expr: Some(subject_list_node.expr),\n                        })),\n                    },\n                    type_conds: vec![],\n                })\n            }\n        }\n    }\n\n    /// Visit equality predicate application with two fixed args\n    ///\n    /// This supports full occurrence typing\n    fn visit_fixed_eq_pred_app(\n        &mut self,\n        pv: &mut PurityVar,\n        span: Span,\n        fun_expr: hir::Expr<hir::Inferred>,\n        left_expr: hir::Expr<hir::Lowered>,\n        right_expr: hir::Expr<hir::Lowered>,\n    ) -> Result<InferredNode> {\n        use crate::ty::props::is_literal;\n        use std::iter;\n\n        let left_local_id = if let hir::ExprKind::LocalRef(_, local_id) = &left_expr.kind {\n            Some(*local_id)\n        } else {\n            None\n        };\n\n        let right_local_id = if let hir::ExprKind::LocalRef(_, local_id) = &right_expr.kind {\n            Some(*local_id)\n        } else {\n            None\n        };\n\n        let left_node = self.visit_expr(pv, &ResultUse::InnerExpr(&Ty::Any.into()), left_expr)?;\n        let left_ty = left_node.result_ty();\n\n        let right_node = self.visit_expr(pv, &ResultUse::InnerExpr(&Ty::Any.into()), right_expr)?;\n        let right_ty = right_node.result_ty();\n\n        // Optimise away comparisons between booleans and literal true\n        // This allows their type conditions to flow through\n        if try_to_bool(left_ty) == Some(true)\n            && ty::is_a::ty_ref_is_a(right_ty, &ty::Ty::Bool.into())\n        {\n            return Ok(right_node);\n        }\n\n        if try_to_bool(right_ty) == Some(true)\n            && ty::is_a::ty_ref_is_a(left_ty, &ty::Ty::Bool.into())\n        {\n            return Ok(left_node);\n        }\n\n        let left_is_literal = is_literal(left_ty);\n        let right_is_literal = is_literal(right_ty);\n        let is_divergent = left_node.is_divergent() || right_node.is_divergent();\n\n        if left_is_literal && right_is_literal && left_ty == right_ty {\n            // We were comparing literal types; this is a static true\n            let result_ty = if is_divergent {\n                Ty::never().into()\n            } else {\n                Ty::LitBool(true).into()\n            };\n\n            return Ok(InferredNode {\n                expr: keep_exprs_for_side_effects(\n                    iter::once(left_node.expr).chain(iter::once(right_node.expr)),\n                    hir::Expr {\n                        result_ty,\n                        kind: hir::ExprKind::Lit(Datum::Bool(span, true)),\n                    },\n                ),\n                type_conds: vec![],\n            });\n        };\n\n        let intersected_type = match ty::intersect::intersect_ty_refs(left_ty, right_ty) {\n            Ok(intersected_type) => intersected_type,\n            Err(ty::intersect::Error::Disjoint) => {\n                let result_ty = if is_divergent {\n                    Ty::never().into()\n                } else {\n                    Ty::LitBool(false).into()\n                };\n\n                return Ok(InferredNode {\n                    expr: keep_exprs_for_side_effects(\n                        iter::once(left_node.expr).chain(iter::once(right_node.expr)),\n                        hir::Expr {\n                            result_ty,\n                            kind: hir::ExprKind::Lit(Datum::Bool(span, false)),\n                        },\n                    ),\n                    type_conds: vec![],\n                });\n            }\n        };\n\n        let mut type_conds = vec![];\n\n        if let Some(override_local_id) = left_local_id {\n            type_conds.push(VarTypeCond {\n                when: NodeBool::True,\n                override_local_id,\n                override_type: intersected_type.clone(),\n            });\n\n            if right_is_literal {\n                let subtracted_type = ty::subtract::subtract_ty_refs(left_ty, right_ty);\n\n                type_conds.push(VarTypeCond {\n                    when: NodeBool::False,\n                    override_local_id,\n                    override_type: subtracted_type,\n                });\n            }\n        }\n\n        if let Some(override_local_id) = right_local_id {\n            type_conds.push(VarTypeCond {\n                when: NodeBool::True,\n                override_local_id,\n                override_type: intersected_type,\n            });\n\n            if left_is_literal {\n                let subtracted_type = ty::subtract::subtract_ty_refs(right_ty, left_ty);\n\n                type_conds.push(VarTypeCond {\n                    when: NodeBool::False,\n                    override_local_id,\n                    override_type: subtracted_type,\n                });\n            }\n        }\n\n        // Invert type conditions for comparisons between a boolean and non-true value\n        if ty::is_a::ty_ref_is_a(right_ty, &ty::Ty::Bool.into())\n            && ty::intersect::intersect_ty_refs(left_ty, &ty::Ty::LitBool(true).into()).is_err()\n        {\n            type_conds.extend(\n                right_node\n                    .type_conds\n                    .into_iter()\n                    .map(VarTypeCond::into_inverted),\n            );\n        } else if ty::is_a::ty_ref_is_a(left_ty, &ty::Ty::Bool.into())\n            && ty::intersect::intersect_ty_refs(right_ty, &ty::Ty::LitBool(true).into()).is_err()\n        {\n            type_conds.extend(\n                left_node\n                    .type_conds\n                    .into_iter()\n                    .map(VarTypeCond::into_inverted),\n            );\n        }\n\n        let result_ty = if is_divergent {\n            Ty::never().into()\n        } else {\n            Ty::Bool.into()\n        };\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty,\n                kind: hir::ExprKind::App(Box::new(hir::App {\n                    span,\n                    fun_expr,\n                    ty_args: TyArgs::empty(),\n                    fixed_arg_exprs: vec![left_node.expr, right_node.expr],\n                    rest_arg_expr: None,\n                })),\n            },\n            type_conds,\n        })\n    }\n\n    fn visit_app(\n        &mut self,\n        pv: &mut PurityVar,\n        result_use: &ResultUse<'_>,\n        app: hir::App<hir::Lowered>,\n    ) -> Result<InferredNode> {\n        let hir::App {\n            span,\n            fun_expr,\n            mut fixed_arg_exprs,\n            rest_arg_expr,\n            ..\n        } = app;\n\n        // The only type information we can feed back is that we want a function of a certain\n        // purity returning a certain value\n        let wanted_purity = match pv {\n            PurityVar::Free(_) => {\n                // We're inferring the purity; this application can have any purity\n                Purity::Impure.into()\n            }\n            PurityVar::Known(purity_type) => {\n                // We have a specific declared purity\n                purity_type.clone()\n            }\n        };\n\n        let wanted_fun_type =\n            ty::TopFun::new(wanted_purity, result_use.required_type().clone()).into();\n\n        let fun_node = self.visit_expr(pv, &ResultUse::InnerExpr(&wanted_fun_type), fun_expr)?;\n        let revealed_fun_type = fun_node.result_ty().clone();\n\n        match revealed_fun_type.resolve_to_ty() {\n            Ty::TopFun(_) => Err(Error::new(span, ErrorKind::TopFunApply(revealed_fun_type))),\n            Ty::TyPred(test_ty) => {\n                let wanted_arity = WantedArity::new(1, false);\n\n                match (fixed_arg_exprs.len(), rest_arg_expr) {\n                    (1, None) => {\n                        let subject_expr = fixed_arg_exprs.pop().unwrap();\n                        self.visit_fixed_ty_pred_app(pv, span, fun_node.expr, test_ty, subject_expr)\n                    }\n                    (0, Some(subject_list_expr)) => self.visit_rest_ty_pred_app(\n                        pv,\n                        span,\n                        fun_node.expr,\n                        test_ty,\n                        subject_list_expr,\n                    ),\n                    (supplied_arg_count, _) => Err(Error::new(\n                        span,\n                        ErrorKind::WrongArity(supplied_arg_count, wanted_arity),\n                    )),\n                }\n            }\n            Ty::EqPred => {\n                if fixed_arg_exprs.len() == 2 && rest_arg_expr.is_none() {\n                    let right_expr = fixed_arg_exprs.pop().unwrap();\n                    let left_expr = fixed_arg_exprs.pop().unwrap();\n\n                    self.visit_fixed_eq_pred_app(pv, span, fun_node.expr, left_expr, right_expr)\n                } else {\n                    let fun_app = FunApp {\n                        fun_expr: fun_node.expr,\n                        fixed_arg_exprs,\n                        rest_arg_expr,\n                    };\n\n                    self.visit_fun_app(pv, result_use, span, &ty::Fun::new_for_eq_pred(), fun_app)\n                }\n            }\n            Ty::Fun(fun_type) => {\n                let fun_app = FunApp {\n                    fun_expr: fun_node.expr,\n                    fixed_arg_exprs,\n                    rest_arg_expr,\n                };\n\n                self.visit_fun_app(pv, result_use, span, fun_type, fun_app)\n            }\n            _ => panic!(\"Unexpected type\"),\n        }\n    }\n\n    fn visit_let(\n        &mut self,\n        pv: &mut PurityVar,\n        result_use: &ResultUse<'_>,\n        hir_let: hir::Let<hir::Lowered>,\n    ) -> Result<InferredNode> {\n        let hir::Let {\n            span,\n            destruc,\n            value_expr,\n            body_expr,\n        } = hir_let;\n\n        let required_destruc_type = typeck::destruc::type_for_decl_destruc(&destruc, None);\n\n        // Pre-bind our variables to deal with recursive definitions\n        let self_local_id = typeck::destruc::visit_locals(&destruc, &mut |local_id, decl_type| {\n            let var_type = match decl_type {\n                hir::DeclTy::Known(poly_type) => VarType::Known(poly_type.clone()),\n                hir::DeclTy::Free => VarType::Recursive,\n            };\n\n            self.self_locals.insert(local_id, var_type);\n        });\n\n        let value_node = self.visit_expr_with_self_local_id(\n            pv,\n            &ResultUse::InnerExpr(&required_destruc_type),\n            value_expr,\n            self_local_id,\n        )?;\n\n        let free_ty_offset = self.destruc_value(&destruc, value_node.result_ty(), false);\n\n        let body_node = self.visit_expr(pv, result_use, body_expr)?;\n        let mut inferred_free_types = self.free_ty_polys.drain(free_ty_offset..);\n\n        let result_ty = if value_node.is_divergent() {\n            // Value was divergent\n            Ty::never().into()\n        } else {\n            body_node.result_ty().clone()\n        };\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty,\n                kind: hir::ExprKind::Let(Box::new(hir::Let {\n                    span,\n                    destruc: destruc::subst_destruc(&mut inferred_free_types, destruc),\n                    value_expr: value_node.expr,\n                    body_expr: body_node.expr,\n                })),\n            },\n            type_conds: body_node.type_conds,\n        })\n    }\n\n    fn visit_rust_fun(\n        &self,\n        result_use: &ResultUse<'_>,\n        rust_fun: Arc<rfi::Fun>,\n    ) -> Result<InferredNode> {\n        let span = rust_fun.span();\n\n        // Rust functions have their types validated by the RFI system when they're loaded\n        // We just need to make sure we satisfy `result_use` and convert to an `InferredNode`\n        let poly_type = Ty::Fun(Box::new(rust_fun.arret_fun_type().clone())).into();\n        ensure_is_a(span, &poly_type, result_use)?;\n\n        Ok(InferredNode {\n            expr: hir::Expr {\n                result_ty: poly_type,\n                kind: hir::ExprKind::RustFun(rust_fun),\n            },\n            type_conds: vec![],\n        })\n    }\n\n    fn visit_expr_with_self_local_id(\n        &mut self,\n        pv: &mut PurityVar,\n        result_use: &ResultUse<'_>,\n        expr: hir::Expr<hir::Lowered>,\n        self_local_id: Option<hir::LocalId>,\n    ) -> Result<InferredNode> {\n        use crate::hir::ExprKind;\n        match expr.kind {\n            ExprKind::Lit(datum) => self.visit_lit(result_use, datum),\n            ExprKind::Cond(cond) => self.visit_cond(pv, result_use, *cond),\n            ExprKind::Do(exprs) => self.visit_do(pv, result_use, exprs),\n            ExprKind::Fun(fun) => self.visit_fun(result_use, *fun, self_local_id),\n            ExprKind::RustFun(rust_fun) => self.visit_rust_fun(result_use, rust_fun),\n            ExprKind::TyPred(span, test_type) => self.visit_ty_pred(result_use, span, test_type),\n            ExprKind::EqPred(span) => self.visit_eq_pred(result_use, span),\n            ExprKind::RecordCons(span, record_cons) => {\n                self.visit_record_cons(result_use, span, record_cons)\n            }\n            ExprKind::FieldAccessor(field_accessor) => {\n                self.visit_field_accessor(result_use, field_accessor)\n            }\n            ExprKind::Let(hir_let) => self.visit_let(pv, result_use, *hir_let),\n            ExprKind::LocalRef(span, local_id) => self.visit_local_ref(result_use, span, local_id),\n            ExprKind::ExportRef(span, export_id) => {\n                self.visit_export_ref(result_use, span, export_id)\n            }\n            ExprKind::App(app) => self.visit_app(pv, result_use, *app),\n            ExprKind::Recur(recur) => self.visit_recur(pv, result_use, *recur),\n            ExprKind::MacroExpand(span, inner_expr) => self\n                .visit_expr_with_self_local_id(pv, result_use, *inner_expr, self_local_id)\n                .map(|inferred| InferredNode {\n                    expr: hir::Expr {\n                        result_ty: inferred.expr.result_ty.clone(),\n                        kind: ExprKind::MacroExpand(span, Box::new(inferred.expr)),\n                    },\n                    ..inferred\n                })\n                .map_err(|err| err.with_macro_invocation_span(span)),\n        }\n    }\n\n    fn visit_expr(\n        &mut self,\n        pv: &mut PurityVar,\n        result_use: &ResultUse<'_>,\n        expr: hir::Expr<hir::Lowered>,\n    ) -> Result<InferredNode> {\n        self.visit_expr_with_self_local_id(pv, result_use, expr, None)\n    }\n\n    fn destruc_scalar_value(\n        &mut self,\n        scalar: &destruc::Scalar<hir::Lowered>,\n        value_type: &ty::Ref<ty::Poly>,\n        is_param: bool,\n    ) -> usize {\n        let start_offset = self.free_ty_polys.len();\n\n        let free_ty_id = if *scalar.ty() == hir::DeclTy::Free {\n            Some(self.insert_free_ty(value_type.clone()))\n        } else {\n            None\n        };\n\n        if let Some(local_id) = *scalar.local_id() {\n            let var_type = if let (Some(free_ty_id), true) = (free_ty_id, is_param) {\n                VarType::ParamScalar(free_ty_id)\n            } else {\n                VarType::Known(value_type.clone())\n            };\n\n            self.self_locals.insert(local_id, var_type);\n        }\n\n        start_offset\n    }\n\n    fn destruc_rest_value(\n        &mut self,\n        rest: &destruc::Scalar<hir::Lowered>,\n        value_type_iter: ListIterator<'_, ty::Poly>,\n        is_param: bool,\n    ) {\n        let param_free_ty_id = if *rest.ty() == hir::DeclTy::Free {\n            // Start with member type as a guide\n            let member_type = value_type_iter.clone().collect_rest();\n\n            let free_ty_id = self.insert_free_ty(member_type);\n            Some(free_ty_id).filter(|_| is_param)\n        } else {\n            None\n        };\n\n        if let Some(local_id) = *rest.local_id() {\n            let var_type = if let Some(param_free_ty_id) = param_free_ty_id {\n                VarType::ParamRest(param_free_ty_id)\n            } else {\n                // If we're not a rest parameter we know our exact tail type. We can't subst\n                // the tail type in to the destruc because it only takes a member type.\n                // However, we can use the exact tail type whenever we reference the var.\n                VarType::Known(value_type_iter.tail_type().into())\n            };\n\n            self.self_locals.insert(local_id, var_type);\n        }\n    }\n\n    fn destruc_list_value(\n        &mut self,\n        list: &destruc::List<hir::Lowered>,\n        mut value_type_iter: ListIterator<'_, ty::Poly>,\n        is_param: bool,\n    ) -> usize {\n        let start_offset = self.free_ty_polys.len();\n\n        for fixed_destruc in list.fixed() {\n            let member_type = value_type_iter\n                .next()\n                .expect(\"Destructured value with unexpected type\");\n\n            self.destruc_value(fixed_destruc, member_type, is_param);\n        }\n\n        if let Some(rest) = list.rest() {\n            self.destruc_rest_value(rest, value_type_iter, is_param);\n        }\n\n        start_offset\n    }\n\n    fn destruc_value(\n        &mut self,\n        destruc: &destruc::Destruc<hir::Lowered>,\n        value_type: &ty::Ref<ty::Poly>,\n        is_param: bool,\n    ) -> usize {\n        match destruc {\n            destruc::Destruc::Scalar(_, scalar) => {\n                self.destruc_scalar_value(scalar, value_type, is_param)\n            }\n            destruc::Destruc::List(_, list) => {\n                let value_type_iter = ListIterator::try_new_from_ty_ref(value_type)\n                    .expect(\"Tried to destruc non-list\");\n                self.destruc_list_value(list, value_type_iter, is_param)\n            }\n        }\n    }\n\n    fn visit_def(&mut self, hir_def: hir::Def<hir::Lowered>) -> Result<hir::Def<hir::Inferred>> {\n        let hir::Def {\n            span,\n            macro_invocation_span,\n            destruc,\n            value_expr,\n        } = hir_def;\n\n        // Module definitions must be pure\n        let mut pv = PurityVar::Known(Purity::Pure.into());\n\n        // Mark all of our free typed variable as recursive\n        let self_local_id = typeck::destruc::visit_locals(&destruc, &mut |local_id, decl_type| {\n            if *decl_type == hir::DeclTy::Free {\n                self.self_locals.insert(local_id, VarType::Recursive);\n            }\n        });\n\n        let required_type = typeck::destruc::type_for_decl_destruc(&destruc, None);\n        let value_node = match self.visit_expr_with_self_local_id(\n            &mut pv,\n            &ResultUse::InnerExpr(&required_type),\n            value_expr,\n            self_local_id,\n        ) {\n            Ok(value_node) => value_node,\n            Err(error) => {\n                // Mark this def as an error so we can suppress cascade errors\n                typeck::destruc::visit_locals(&destruc, &mut |local_id, _| {\n                    self.self_locals.insert(local_id, VarType::Error);\n                });\n                return Err(error);\n            }\n        };\n\n        let free_ty_offset = self.destruc_value(&destruc, value_node.result_ty(), false);\n        let mut inferred_free_types = self.free_ty_polys.drain(free_ty_offset..);\n\n        Ok(hir::Def {\n            span,\n            macro_invocation_span,\n            destruc: destruc::subst_destruc(&mut inferred_free_types, destruc),\n            value_expr: value_node.expr,\n        })\n    }\n\n    fn recurse_into_def_id(&mut self, def_id: InputDefId) -> Result<()> {\n        let def_index = def_id.to_usize();\n\n        let previous_state = std::mem::replace(&mut self.input_defs[def_index], InputDef::Complete);\n\n        if let InputDef::Pending(def) = previous_state {\n            let inferred_def = self.visit_def(def)?;\n            self.complete_defs.push(inferred_def);\n        } else {\n            panic!(\"Tried to infer already complete def. An error previously occurred?\")\n        }\n\n        Ok(())\n    }\n\n    fn into_inferred_module(mut self) -> result::Result<InferredModule, Vec<Error>> {\n        let mut errs = vec![];\n        while let Some(def_state) = self.input_defs.pop() {\n            match def_state {\n                InputDef::Pending(def) => match self.visit_def(def) {\n                    Ok(inferred_def) => {\n                        self.complete_defs.push(inferred_def);\n                    }\n                    Err(err) => {\n                        // If this is due to a previous error it's just noise to report it\n                        if err.kind() != &ErrorKind::DependsOnError {\n                            errs.push(err);\n                        }\n                    }\n                },\n                InputDef::Complete => {}\n            }\n        }\n\n        if !errs.is_empty() {\n            return Err(errs);\n        }\n\n        let inferred_locals: InferredLocals = self\n            .self_locals\n            .into_iter()\n            .flat_map(|(local_id, var_type)| match var_type {\n                VarType::Known(poly) => Some((local_id, poly)),\n                _ => None,\n            })\n            .collect();\n\n        Ok(InferredModule {\n            inferred_locals,\n            defs: self.complete_defs,\n        })\n    }\n}\n\npub fn ensure_main_type(\n    fallback_span: Span,\n    complete_defs: &[hir::Def<hir::Inferred>],\n    main_local_id: hir::LocalId,\n    inferred_main_type: &ty::Ref<ty::Poly>,\n) -> Result<()> {\n    let expected_main_type = ty::Fun::new_for_main().into();\n\n    if !ty::is_a::ty_ref_is_a(inferred_main_type, &expected_main_type) {\n        use crate::reporting::LocTrace;\n\n        // Try to find where `(main!)` was defined\n        let main_loc_trace = complete_defs\n            .iter()\n            .find_map(|def| {\n                if let destruc::Destruc::Scalar(_, ref scalar) = def.destruc {\n                    if scalar.local_id() == &Some(main_local_id) {\n                        return Some(LocTrace::new(def.span, def.macro_invocation_span));\n                    }\n                }\n\n                None\n            })\n            // Fall back to the `Span` we were given\n            .unwrap_or_else(|| fallback_span.into());\n\n        return Err(Error::new_with_loc_trace(\n            main_loc_trace,\n            ErrorKind::IsNotTy(inferred_main_type.clone(), expected_main_type),\n        ));\n    };\n\n    Ok(())\n}\n\npub fn infer_module(\n    imported_inferred_vars: &InferredModuleVars,\n    defs: Vec<hir::Def<hir::Lowered>>,\n) -> result::Result<InferredModule, Vec<Error>> {\n    RecursiveDefsCtx::new(imported_inferred_vars, defs).into_inferred_module()\n}\n\npub fn infer_repl_expr(\n    all_inferred_vars: &InferredModuleVars,\n    expr: hir::Expr<hir::Lowered>,\n) -> Result<InferredNode> {\n    let mut rdcx = RecursiveDefsCtx::new(all_inferred_vars, vec![]);\n    let mut pv = PurityVar::Known(Purity::Impure.into());\n\n    rdcx.visit_expr(&mut pv, &ResultUse::InnerExpr(&Ty::Any.into()), expr)\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::hir::lowering::expr_for_str;\n    use arret_syntax::span::t2s;\n\n    fn type_for_expr(\n        required_type: &ty::Ref<ty::Poly>,\n        expr: hir::Expr<hir::Lowered>,\n    ) -> Result<ty::Ref<ty::Poly>> {\n        let imported_vars = HashMap::new();\n        let mut rdcx = RecursiveDefsCtx::new(&imported_vars, vec![]);\n\n        let mut pv = PurityVar::Known(Purity::Pure.into());\n\n        rdcx.visit_expr(&mut pv, &ResultUse::InnerExpr(required_type), expr)\n            .map(|node| node.expr.result_ty)\n    }\n\n    fn assert_type_for_expr(ty_str: &str, expr_str: &str) {\n        let expr = expr_for_str(expr_str);\n        let poly = hir::poly_for_str(ty_str);\n\n        assert_eq!(poly, type_for_expr(&Ty::Any.into(), expr).unwrap());\n    }\n\n    fn assert_constrained_type_for_expr(expected_ty_str: &str, expr_str: &str, guide_ty_str: &str) {\n        let expr = expr_for_str(expr_str);\n        let expected_poly = hir::poly_for_str(expected_ty_str);\n        let guide_poly = hir::poly_for_str(guide_ty_str);\n\n        assert_eq!(expected_poly, type_for_expr(&guide_poly, expr).unwrap());\n    }\n\n    fn assert_type_error(err: &Error, expr_str: &str) {\n        let expr = expr_for_str(expr_str);\n        assert_eq!(err, &type_for_expr(&Ty::Any.into(), expr).unwrap_err())\n    }\n\n    #[test]\n    fn literal_expr() {\n        assert_type_for_expr(\"Int\", \"1\");\n    }\n\n    #[test]\n    fn do_expr() {\n        assert_type_for_expr(\"'()\", \"(do)\");\n        assert_type_for_expr(\"Int\", \"(do 'one 'two 3)\");\n\n        // We have no diverging primitives so we can't test this case easily. This is covered in\n        // run-pass.\n    }\n\n    #[test]\n    fn cond_expr() {\n        assert_type_for_expr(\"'true-branch\", \"(if true 'true-branch 'false-branch)\");\n        assert_type_for_expr(\"'false-branch\", \"(if false 'true-branch 'false-branch)\");\n        assert_type_for_expr(\"(Bool -> Bool)\", \"(fn (x) (if x true false))\");\n\n        // This is a reduced version of `(and)`\n        // We shouldn't complain about the type in the false branch because it's unreachable\n        assert_constrained_type_for_expr(\"true\", \"(if true true false)\", \"true\");\n    }\n\n    #[test]\n    fn fun_expr() {\n        assert_type_for_expr(\"(-> ())\", \"(fn ())\");\n        assert_type_for_expr(\"(Any -> true)\", \"(fn (_) true)\");\n        assert_type_for_expr(\"(Str -> Str)\", \"(fn ([x Str]) x)\");\n\n        // We should feed our wanted type in to the function type\n        assert_constrained_type_for_expr(\"(Sym -> true)\", \"(fn (_) true)\", \"(Sym -> true)\");\n        assert_constrained_type_for_expr(\"(Sym -> Sym)\", \"(fn (x) x)\", \"(Sym -> Any))\");\n\n        // Function with free types being bound to an incompatible type\n        let j = \"(let [[f (Sym -> true)] (fn ([_ Str]) true)])\";\n        let t = \"                        ^^^^^^^^^^^^^^^^^^^  \";\n        let err = Error::new(\n            t2s(t),\n            ErrorKind::IsNotTy(\n                hir::poly_for_str(\"(Str -> true)\"),\n                hir::poly_for_str(\"(Sym -> true)\"),\n            ),\n        );\n        assert_type_error(&err, j);\n\n        // Function with a known type being bound to an incompatible type\n        let j = \"(let [[f (Sym -> true)] (fn ([_ Str]) -> true true)])\";\n        let t = \"                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^  \";\n        let err = Error::new(\n            t2s(t),\n            ErrorKind::IsNotTy(\n                hir::poly_for_str(\"(Str -> true)\"),\n                hir::poly_for_str(\"(Sym -> true)\"),\n            ),\n        );\n        assert_type_error(&err, j);\n\n        let j = \"(fn ([x Str]) -> Sym x)\";\n        let t = \"                 ^^^   \";\n        let u = \"                     ^ \";\n\n        let err = Error::new(\n            t2s(u),\n            ErrorKind::IsNotRetTy(IsNotRetTy::new(\n                hir::poly_for_str(\"Str\"),\n                hir::poly_for_str(\"Sym\"),\n                Some(t2s(t)),\n            )),\n        );\n        assert_type_error(&err, j);\n    }\n\n    #[test]\n    fn app_types() {\n        assert_type_for_expr(\"'foo\", \"((fn () 'foo))\");\n\n        assert_type_for_expr(\"true\", \"(sym? 'foo)\");\n        assert_type_for_expr(\"false\", \"(sym? false)\");\n\n        assert_type_for_expr(\"Int\", \"((fn #{A} ([value A]) -> A value) 1)\");\n        assert_type_for_expr(\"'foo\", \"((fn #{A} ([value A]) -> A value) & '(foo))\");\n\n        assert_type_for_expr(\n            \"(List & Bool)\",\n            \"((fn #{A} (& [rest A]) -> (List & A) rest) true false)\",\n        );\n\n        assert_type_for_expr(\n            \"Int\",\n            // This is essentially `(map)` without the use of lists\n            \"((fn #{I O} ([mapper (I -> O)] [i I]) -> O (mapper i)) (fn (x) x) 1))\",\n        );\n\n        assert_type_for_expr(\n            \"Int\",\n            // With the argument positions swapped\n            \"((fn #{I O} ([i I] [mapper (I -> O)]) -> O (mapper i)) 1 (fn (x) x)))\",\n        );\n\n        assert_type_for_expr(\n            \"Int\",\n            // With explicit type annotations\n            \"((fn #{I O} ([i I] [mapper (I -> O)]) -> O (mapper i)) 1 (fn ([x Int]) -> Int x)))\",\n        );\n    }\n\n    #[test]\n    fn recur_expr() {\n        assert_type_for_expr(\"'foo\", \"((fn ([x Int]) -> 'foo (recur x)) 1)\");\n\n        let j = \"((fn () -> () (recur) ()))\";\n        let t = \"              ^^^^^^^     \";\n        let err = Error::new(t2s(t), ErrorKind::NonTailRecur);\n        assert_type_error(&err, j);\n\n        let j = \"((fn () (recur)))\";\n        let t = \"        ^^^^^^^  \";\n        let err = Error::new(t2s(t), ErrorKind::RecurWithoutFunTypeDecl);\n        assert_type_error(&err, j);\n\n        let j = \"((fn (x) -> 'foo (recur x)) 1)\";\n        let t = \"                 ^^^^^^^^^    \";\n        let err = Error::new(t2s(t), ErrorKind::RecurWithoutFunTypeDecl);\n        assert_type_error(&err, j);\n\n        let j = \"((fn ([x Int]) (recur x)) 1)\";\n        let t = \"               ^^^^^^^^^    \";\n        let err = Error::new(t2s(t), ErrorKind::RecurWithoutFunTypeDecl);\n        assert_type_error(&err, j);\n    }\n\n    #[test]\n    fn app_purity() {\n        // An empty function is pure\n        assert_type_for_expr(\"(-> false)\", \"(fn () false)\");\n\n        // Calling a pure function in an inferred purity should leave us pure\n        assert_type_for_expr(\"(-> false)\", \"(fn () ((fn () -> false false)))\");\n\n        // Calling the impure function in an inferred purity should make us impure\n        assert_type_for_expr(\"(->! false)\", \"(fn () ((fn () ->! false false)))\");\n    }\n\n    #[test]\n    fn impure_app_within_pure() {\n        // Calling an impure function inside a function declared as pure should fail\n        let j = \"(fn () -> Bool ((fn () ->! false false)))\";\n        let t = \"                ^^^^^^^^^^^^^^^^^^^^^^^  \";\n\n        let err = Error::new(\n            t2s(t),\n            ErrorKind::IsNotPurity(hir::poly_for_str(\"(->! false)\"), Purity::Pure.into()),\n        );\n        assert_type_error(&err, j);\n    }\n\n    #[test]\n    fn too_many_args() {\n        let j = \"((fn ()) 1)\";\n        let t = \"^^^^^^^^^^^\";\n\n        let wanted_arity = WantedArity::new(0, false);\n        let err = Error::new(t2s(t), ErrorKind::WrongArity(1, wanted_arity));\n        assert_type_error(&err, j);\n    }\n\n    #[test]\n    fn not_enough_args() {\n        let j = \"((fn (_ _)) 1)\";\n        let t = \"^^^^^^^^^^^^^^\";\n\n        let wanted_arity = WantedArity::new(2, false);\n        let err = Error::new(t2s(t), ErrorKind::WrongArity(1, wanted_arity));\n        assert_type_error(&err, j);\n    }\n\n    #[test]\n    fn list_destruc() {\n        assert_type_for_expr(\"Int\", \"(let [(x) '(1)] x)\");\n        assert_type_for_expr(\n            \"(List true false)\",\n            \"(let [(_ & rest) '(1 true false)] rest)\",\n        );\n    }\n\n    #[test]\n    fn var_ref() {\n        assert_type_for_expr(\"Int\", \"(let [x 1] x)\")\n    }\n\n    #[test]\n    fn ty_pred() {\n        assert_type_for_expr(\"true\", \"(sym? & '(foo))\");\n        assert_type_for_expr(\"true\", \"(sym? 'foo)\");\n        assert_type_for_expr(\"false\", \"(int? & '(foo))\");\n        assert_type_for_expr(\"false\", \"(int? 'bar)\");\n    }\n\n    #[test]\n    fn eq_pred() {\n        assert_type_for_expr(\"true\", \"(= 'foo 'foo)\");\n        assert_type_for_expr(\"false\", \"(= 'bar 'foo)\");\n\n        // This looks stupid but we can only evaluate based on types. Both 1 and 2 are `Int`.\n        assert_type_for_expr(\"Bool\", \"(= 1 2)\");\n    }\n}\n"
  },
  {
    "path": "compiler/typeck/mod.rs",
    "content": "mod dce;\nmod destruc;\npub mod error;\npub mod infer;\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '3.4'\nservices:\n  build-env:\n    build:\n      context: .\n      target: build-env\n  repl:\n    build:\n      context: .\n      target: repl\n      args:\n        - vcs_ref=${BUILDKITE_COMMIT}\n"
  },
  {
    "path": "docs/language-design.md",
    "content": "# Language Design\n\n## Introduction\n\nArret is a strongly typed, pure functional [Lisp](https://en.wikipedia.org/wiki/Lisp_(programming_language)).\nIts mandatory typing distinguishes it from most other Lisp dialects.\nHowever, type inference and a relatively simple type system allow type declarations to be omitted in most places.\n\nThe base syntax is a subset of Clojure's [Extensible Data Notation](https://github.com/edn-format/edn).\nMany tools that support EDN such as [cljfmt](https://github.com/weavejester/cljfmt) will work without modification on Arret source code.\n\nMost of Arret's primitives and standard library functions are also modelled after [Clojure](https://clojure.org).\nClojure's functional design and mindshare make it a good source of inspiration.\nHowever, Arret does not intend to be source compatible with Clojure.\n\nAll data structures are immutable and [strictly evaluated](https://en.wikipedia.org/wiki/Eager_evaluation).\nThis is similar to [Elm](https://elm-lang.org) or [PureScript](http://www.purescript.org).\n\nIn the text below the crystal ball (🔮) indicates planned features of the language.\n\n## Basic Data Types\n\nThe basic data types are:\n\n- `Int` is a signed 64bit integer.\n- `Float` is a 64bit floating point value.\n  This is known as a “double” in some other languages.\n- `Num` is the union of `Int` and `Float`.\n  This allows mathematical functions to be generic over number types.\n  Specific numeric types should be used whenever possible to improve type inference and runtime performance.\n- `Bool` is the union of the `true` and `false` types.\n  Unlike most Lisps there is no concept of [truthy values](https://en.wikipedia.org/wiki/Truth_value#Computing);\n  constructs such as `(if)` will only accept `true` or `false` values for their condition.\n- `Str` is an immutable [UTF-8](https://en.wikipedia.org/wiki/UTF-8) string.\n- `Sym` is an [interned](https://en.wikipedia.org/wiki/String_interning) symbol.\n  These are used to represent both EDN symbols and identifiers.\n  Additionally, every symbol has its own literal type (named `'foo`) that can be used to construct ad-hoc unions.\n- `Char` is an [Unicode scalar value](http://www.unicode.org/glossary/#unicode_scalar_value).\n\n## Collections\n\nThe language provides immutable lists, vectors, maps and sets corresponding to the data types provided by EDN.\nThe `(List)`, `(Vector)`/`(Vectorof)`, `(Set)` and `(Map)` type constructors can be used to specify typed collections with a particular member type.\n\nList types can specify zero or more fixed members followed by a uniform rest type.\nFor example, `(List Int Int & Float)` indicates a list of at least two `Int`s followed by zero or more `Float`s.\nThis is closely related to how function arguments are specified.\n\nLists are the primary data type.\nMost collection functions are only provided for lists;\nother collections need to be temporarily converted to lists to use them.\nThe compiler aggressively attempts to optimise these temporary lists away - this makes lists fill the role of [iterators](https://en.wikipedia.org/wiki/Iterator) in other languages.\n\n## User Defined Types\n\nUsers can define their own types in three different ways:\n\n1. `(deftype)` can be used to create an alias of an existing type.\n2. The `(U)` type constructor can be used to define a union type.\n   Type predicates can be used to determine which member type a given value has.\n3. `(defrecord)` will create a new record type distinct from all other types.\n   These are also known as structs or product types in other languages.\n\n## Functions\n\nArret functions take zero or more parameters and return a single value.\n[Variadic](https://en.wikipedia.org/wiki/Variadic_function) functions are supported by using `& rest` to capture a list of the variable arguments.\n\nBy convention the empty list (`()` aka nil) is used to indicate no useful value is returned by the function.\nThis is used by functions that are only called for their side effects such as `(println!)`.\n\nAll functions are either impure or pure:\n\n- Pure functions are declared using the `->` function arrow.\n  They are not allowed to have side effects.\n  The compiler may evaluate them at compile time, remove or duplicate calls to them, etc.\n  Pure functions cannot call impure functions.\n- Impure functions are declared using the `->!` function arrow.\n  They are allowed to have side effects and will never be evaluated at compile time.\n  By convention they should have a name ending with `!`.\n\nIt's also possible for functions to be [polymorphic](https://en.wikipedia.org/wiki/Polymorphism_(computer_science)) on their purity.\nFor example, the higher-order functions `(map)` and `(filter)` are only impure if passed an impure function.\nBy convention these functions are named as if they were pure, i.e. without the `!` suffix.\n\n## Destructuring\n\nArret supports [destructuring](https://en.wikipedia.org/wiki/Assignment_(computer_science)#Parallel_assignment) lists for variable assignments and function arguments.\nThis can be used to emulate multiple return values by returning a fixed sized list of values.\nList destructures can be nested or use `& rest` syntax to capture the tail of a list.\n\nVector destructuring is unsupported as vector notation is already used for type annotations.\n🔮 Record or map destructuring may be possible in the future.\n\n## Macros\n\nArret provides a [hygienic](https://en.wikipedia.org/wiki/Hygienic_macro) macro system modelled after [Scheme R7RS](http://r7rs.org).\nThis is a powerful macro-by-example system that allows defining new language constructs and flow control patterns.\nIn fact, many language features such as `(defn)`, `(not)` and `(when)` are actually macros implemented on top of a small set of core primitives.\n\n🔮 One of the goals of Arret is to allow an additional type of macro implemented by user provided functions.\nThese would be pure functions taking a syntax tree and input and returning the replacement syntax tree.\n\n## Occurrence Typing\n\nArret requires that every value is known to have the correct type at compile time.\nFor example, a `Num` cannot be passed to a function expecting an `Int`.\nThis makes Arret [strongly typed](https://en.wikipedia.org/wiki/Strong_and_weak_typing) which distinguishes it from most Lisp dialects.\n\nOccurrence typing is used to allow many idiomatic Lisp constructs to work with strong typing.\nIt collects implicit type information from type predicates (e.g. `(str?)` and `(int?)`) and equality comparisons on variables.\nThe variables' types are automatically refined based on if the predicate succeeded or failed.\n\nThis is inspired by similar features in [Typed Racket](https://docs.racket-lang.org/ts-guide/) and [TypeScript](https://www.typescriptlang.org).\n\n## 🔮 Task & Actors\n\n*This section is unimplemented. It's included to explain other design decisions.*\n\nTasks are the atomic unit of concurrency and fault isolation.\nWhile Arret code cannot directly use threads it can spawn tasks which are run concurrently.\nTasks are scheduled by the runtime across the system's cores as [green threads](https://en.wikipedia.org/wiki/Green_threads).\n\nIf a task panics it will only terminate that task and allow the program to continue execution.\nThis is important as Arret intentionally doesn't implement exceptions, instead preferring error return values and panics.\n\nAn [actor system](https://en.wikipedia.org/wiki/Actor_model) is built on top of tasks.\nActors are tasks with the additional ability to send and receive messages.\nThey can keep internal state between messages which programs can use to implement a controlled form of mutable state."
  },
  {
    "path": "driver/Cargo.toml",
    "content": "[package]\nname = \"arret\"\nversion = \"0.1.0\"\nedition = \"2018\"\nauthors = [\"Ryan Cumming <etaoins@gmail.com>\"]\ndefault-run = \"arret\"\n\n[[bin]]\nname = \"arret\"\npath = \"main.rs\"\n\n[dependencies]\nclap = \"2\"\nrustyline = \"9\"\ndirectories-next = \"2\"\nansi_term = \"0.12\"\ncodespan-reporting = \"0.11\"\narret-syntax = { path = \"../syntax\" }\narret-compiler = { path = \"../compiler\" }"
  },
  {
    "path": "driver/main.rs",
    "content": "#![warn(clippy::all)]\n#![warn(rust_2018_idioms)]\n\nmod subcommand;\n\nuse std::sync::Arc;\nuse std::{env, path, process};\n\nuse arret_compiler::{find_arret_root, CompileCtx, FindArretRootError};\n\nconst ARRET_FILE_EXTENSION: &str = \".arret\";\n\nfn input_arg_to_source_file(\n    source_loader: &arret_compiler::SourceLoader,\n    input_param: &str,\n) -> arret_compiler::SourceFile {\n    if input_param == \"-\" {\n        use std::io::prelude::*;\n\n        let mut input_string = String::new();\n        std::io::stdin().read_to_string(&mut input_string).unwrap();\n\n        source_loader.load_string(\"<stdin>\".into(), input_string)\n    } else {\n        let input_path = path::Path::new(input_param);\n\n        source_loader\n            .load_path(input_path)\n            .expect(\"Unable to read input file\")\n    }\n}\n\nfn main() {\n    use arret_compiler::initialise_llvm;\n    use clap::{crate_version, App, AppSettings, Arg, SubCommand};\n\n    let matches = App::new(\"arret\")\n        .version(crate_version!())\n        .setting(AppSettings::SubcommandRequiredElseHelp)\n        .about(\"Compiler and REPL for the Arret language\")\n        .arg(\n            Arg::with_name(\"NOOPT\")\n                .long(\"no-llvm-opt\")\n                .takes_value(false)\n                .help(\"Disables LLVM optimisation\"),\n        )\n        .arg(\n            Arg::with_name(\"ARRET_ROOT\")\n                .long(\"arret-root\")\n                .takes_value(true)\n                .help(\"Path to the root of a built `etaoins/arret` repository\"),\n        )\n        .subcommand(\n            SubCommand::with_name(\"compile\")\n                .about(\"Compiles an Arret program to a standalone binary\")\n                .arg(\n                    Arg::with_name(\"INPUT\")\n                        .required(true)\n                        .help(\"Input source file\")\n                        .index(1),\n                )\n                .arg(\n                    Arg::with_name(\"OUTPUT\")\n                        .short(\"o\")\n                        .value_name(\"FILE\")\n                        .help(\"Output filename\")\n                        .long_help(\n                            \"Output filename.\\n\\\n                             Four special extensions are recognised to output intermediate formats:\\n\\\n                             \\n\\\n                             `.mir` will output a text representation of Arret's internal middle IR\\n\\\n                             `.ll` will output LLVM IR\\n\\\n                             `.s` will output assembler for the target architecture\\n\\\n                             `.o` will output an unlinked object file\"\n                        ),\n                )\n                .arg(\n                    Arg::with_name(\"DEBUG\")\n                        .short(\"g\")\n                        .long(\"debug-info\")\n                        .help(\"Generates debugging information\"),\n                )\n                .arg(\n                    Arg::with_name(\"TARGET\")\n                        .long(\"target\")\n                        .value_name(\"TRIPLE\")\n                        .help(\"Generates code for the given target\"),\n                ),\n        )\n        .subcommand(\n            SubCommand::with_name(\"eval\")\n                .about(\"Evaluates an Arret program once\")\n                .arg(\n                    Arg::with_name(\"INPUT\")\n                        .required(true)\n                        .help(\"Input source file\")\n                        .index(1),\n                ),\n        )\n        .subcommand(\n            SubCommand::with_name(\"repl\")\n                .about(\"Starts an interactive REPL\")\n                .arg(\n                    Arg::with_name(\"INCLUDE\")\n                        .short(\"i\")\n                        .long(\"include\")\n                        .value_name(\"FILE\")\n                        .help(\"Preloads a file before starting REPL\"),\n                ),\n        )\n        .get_matches();\n\n    let arret_root_dir = match find_arret_root(matches.value_of(\"ARRET_ROOT\")) {\n        Ok(arret_root) => arret_root,\n        Err(FindArretRootError::InvalidOption(invalid_option)) => {\n            eprintln!(\n                \"`{}` specified by the `--arret-root` option is not an Arret root directory\",\n                invalid_option.invalid_path().to_string_lossy(),\n            );\n            process::exit(1);\n        }\n        Err(FindArretRootError::InvalidEnvVar(invalid_env_var)) => {\n            eprintln!(\n                \"`{}` specified by the `{}` environment variable is not an Arret root directory\",\n                invalid_env_var.invalid_path().to_string_lossy(),\n                invalid_env_var.env_var_name(),\n            );\n            process::exit(1);\n        }\n        Err(FindArretRootError::NotFound) => {\n            eprintln!(\"Unable to find the Arret root directory\");\n            eprintln!(\"Either specify the `--arret-root` option or set the `ARRET_ROOT` environment variable\");\n            process::exit(1);\n        }\n    };\n\n    let enable_optimisations = !matches.is_present(\"NOOPT\");\n\n    if let Some(compile_matches) = matches.subcommand_matches(\"compile\") {\n        let package_paths = arret_compiler::PackagePaths::with_stdlib(\n            &arret_root_dir,\n            compile_matches.value_of(\"TARGET\"),\n        );\n\n        let ccx = CompileCtx::new(package_paths, enable_optimisations);\n\n        let input_arg = compile_matches.value_of(\"INPUT\").unwrap();\n        let input_file = input_arg_to_source_file(ccx.source_loader(), input_arg);\n\n        let output_path = path::Path::new(\n            if let Some(output_param) = compile_matches.value_of(\"OUTPUT\") {\n                output_param\n            } else if input_arg.ends_with(ARRET_FILE_EXTENSION) {\n                &input_arg[0..input_arg.len() - ARRET_FILE_EXTENSION.len()]\n            } else {\n                panic!(\n                    \"Can't determine output filename from input arg `{}`\",\n                    input_arg\n                );\n            },\n        );\n\n        let debug_info = compile_matches.is_present(\"DEBUG\");\n\n        let target_triple = compile_matches.value_of(\"TARGET\");\n        initialise_llvm(target_triple.is_some());\n\n        if !subcommand::compile::compile_input_file(\n            &ccx,\n            &input_file,\n            target_triple,\n            output_path,\n            debug_info,\n        ) {\n            process::exit(2);\n        }\n    } else if let Some(repl_matches) = matches.subcommand_matches(\"repl\") {\n        let package_paths = arret_compiler::PackagePaths::with_stdlib(&arret_root_dir, None);\n        let ccx = Arc::new(CompileCtx::new(package_paths, enable_optimisations));\n\n        initialise_llvm(false);\n\n        let include_path = repl_matches\n            .value_of(\"INCLUDE\")\n            .map(|include_param| path::Path::new(include_param).to_owned());\n\n        subcommand::repl::interactive_loop(ccx, include_path);\n    } else if let Some(eval_matches) = matches.subcommand_matches(\"eval\") {\n        let package_paths = arret_compiler::PackagePaths::with_stdlib(&arret_root_dir, None);\n        let ccx = CompileCtx::new(package_paths, enable_optimisations);\n\n        let input_param = eval_matches.value_of(\"INPUT\").unwrap();\n        let input_file = input_arg_to_source_file(ccx.source_loader(), input_param);\n\n        initialise_llvm(false);\n\n        if !subcommand::eval::eval_input_file(&ccx, &input_file) {\n            process::exit(2);\n        }\n    } else {\n        eprintln!(\"Sub-command not specified\");\n        process::exit(1);\n    }\n}\n"
  },
  {
    "path": "driver/subcommand/compile.rs",
    "content": "use std::{fs, path};\n\nuse codespan_reporting::diagnostic::Diagnostic;\n\nuse arret_syntax::span::FileId;\n\nuse arret_compiler::{emit_diagnostics_to_stderr, print_program_mir, CompileCtx};\n\n// We don't use this ourselves so overload it for the purposes of dumping MIR\nconst MIR_OUTPUT_TYPE: arret_compiler::OutputType = arret_compiler::OutputType::None;\n\nfn try_compile_input_file(\n    ccx: &CompileCtx,\n    options: arret_compiler::GenProgramOptions<'_>,\n    input_file: &arret_compiler::SourceFile,\n    output_path: &path::Path,\n    debug_info: bool,\n) -> Result<(), Vec<Diagnostic<FileId>>> {\n    let arret_compiler::EvaluableProgram {\n        ehx,\n        main_export_id,\n        linked_libraries,\n    } = arret_compiler::program_to_evaluable(ccx, input_file)?;\n\n    let mir_program = ehx.into_built_program(main_export_id)?;\n\n    if options.output_type() == MIR_OUTPUT_TYPE {\n        let mut output_file = fs::File::create(output_path).unwrap();\n        print_program_mir(&mut output_file, &mir_program, Some(ccx.source_loader())).unwrap();\n        return Ok(());\n    }\n\n    let debug_source_loader = if debug_info {\n        Some(ccx.source_loader())\n    } else {\n        None\n    };\n\n    arret_compiler::gen_program(\n        options,\n        &linked_libraries,\n        &mir_program,\n        output_path,\n        debug_source_loader,\n    );\n\n    Ok(())\n}\n\npub fn compile_input_file(\n    ccx: &CompileCtx,\n    input_file: &arret_compiler::SourceFile,\n    target_triple: Option<&str>,\n    output_path: &path::Path,\n    debug_info: bool,\n) -> bool {\n    use std::ffi;\n\n    let output_type = match output_path.extension().and_then(ffi::OsStr::to_str) {\n        Some(\"mir\") => MIR_OUTPUT_TYPE,\n        Some(\"ll\") => arret_compiler::OutputType::LlvmIr,\n        Some(\"s\") => arret_compiler::OutputType::Assembly,\n        Some(\"o\") => arret_compiler::OutputType::Object,\n        _ => arret_compiler::OutputType::Executable,\n    };\n\n    let options = arret_compiler::GenProgramOptions::new()\n        .with_target_triple(target_triple)\n        .with_output_type(output_type)\n        .with_llvm_opt(ccx.enable_optimisations());\n\n    let result = try_compile_input_file(ccx, options, input_file, output_path, debug_info);\n\n    if let Err(diagnostics) = result {\n        emit_diagnostics_to_stderr(ccx.source_loader(), diagnostics);\n        false\n    } else {\n        true\n    }\n}\n"
  },
  {
    "path": "driver/subcommand/eval.rs",
    "content": "use codespan_reporting::diagnostic::Diagnostic;\n\nuse arret_syntax::span::FileId;\n\nuse arret_compiler::{emit_diagnostics_to_stderr, CompileCtx};\n\nfn try_eval_input_file(\n    ccx: &CompileCtx,\n    input_file: &arret_compiler::SourceFile,\n) -> Result<(), Vec<Diagnostic<FileId>>> {\n    let arret_compiler::EvaluableProgram {\n        mut ehx,\n        main_export_id,\n        ..\n    } = arret_compiler::program_to_evaluable(ccx, input_file)?;\n\n    ehx.eval_main_fun(main_export_id)?;\n\n    Ok(())\n}\n\npub fn eval_input_file(ccx: &CompileCtx, input_file: &arret_compiler::SourceFile) -> bool {\n    let result = try_eval_input_file(ccx, input_file);\n\n    if let Err(diagnostics) = result {\n        emit_diagnostics_to_stderr(ccx.source_loader(), diagnostics);\n        false\n    } else {\n        true\n    }\n}\n"
  },
  {
    "path": "driver/subcommand/mod.rs",
    "content": "pub mod compile;\npub mod eval;\npub mod repl;\n"
  },
  {
    "path": "driver/subcommand/repl/arret_helper.rs",
    "content": "use std::borrow::Cow;\n\nuse ansi_term::{Colour, Style};\n\nuse rustyline::validate::{ValidationContext, ValidationResult};\n\nuse arret_syntax::datum::DataStr;\n\nuse super::command::{HELP_COMMAND, QUIT_COMMAND, TYPE_ONLY_PREFIX};\nuse super::syntax::{error_context_for_eol, error_for_line, MAXIMUM_PARSED_LINE_LEN};\n\n/// Completions that don't map to a bound value in scope\nconst UNBOUND_COMPLETIONS: &[&str] = &[\n    TYPE_ONLY_PREFIX,\n    QUIT_COMMAND,\n    HELP_COMMAND,\n    \"true\",\n    \"false\",\n    \"##NaN\",\n    \"##Inf\",\n    \"##-Inf\",\n];\n\n/// Implementation of Rustyline's `Helper` trait\npub struct ArretHelper {\n    all_names: Vec<DataStr>,\n}\n\nfn sorted_strings_prefixed_by<'a, T: AsRef<str>>(\n    haystack: &'a [T],\n    prefix: &'a str,\n) -> impl Iterator<Item = &'a T> + 'a {\n    // Use a binary search to find the start of the strings\n    let start_pos = match haystack.binary_search_by(|needle| needle.as_ref().cmp(prefix)) {\n        Ok(found) => found,\n        Err(insert_idx) => insert_idx,\n    };\n\n    haystack[start_pos..]\n        .iter()\n        // Once we stop matching prefixes we're done\n        .take_while(move |needle| needle.as_ref().starts_with(prefix))\n}\n\nimpl ArretHelper {\n    pub fn new(mut bound_names: Vec<DataStr>) -> ArretHelper {\n        bound_names.extend(UNBOUND_COMPLETIONS.iter().map(|unbound| (*unbound).into()));\n        bound_names.sort();\n\n        ArretHelper {\n            all_names: bound_names,\n        }\n    }\n}\n\nimpl rustyline::completion::Completer for ArretHelper {\n    type Candidate = String;\n\n    fn complete(\n        &self,\n        line: &str,\n        pos: usize,\n        _: &rustyline::Context<'_>,\n    ) -> rustyline::Result<(usize, Vec<String>)> {\n        use arret_syntax::parser::is_identifier_char;\n\n        let prefix_start = line[0..pos]\n            .rfind(|c| !is_identifier_char(c))\n            .map(|i| i + 1)\n            .unwrap_or(0);\n\n        let prefix = &line[prefix_start..pos];\n\n        let suffix = if line.len() > pos {\n            let suffix_end = line[pos..]\n                .find(|c| !is_identifier_char(c))\n                .map(|i| i + pos)\n                .unwrap_or_else(|| line.len());\n            &line[pos..suffix_end]\n        } else {\n            \"\"\n        };\n\n        let is_command = prefix.starts_with('/');\n        let is_first_identifier = pos == prefix.len();\n\n        if is_command && !is_first_identifier {\n            // Don't complete commands in illegal positions\n            return Ok((0, vec![]));\n        }\n\n        let options = sorted_strings_prefixed_by(&self.all_names, prefix)\n            .filter_map(|name| {\n                if name.ends_with(suffix) {\n                    Some((&name[0..name.len() - suffix.len()]).to_owned())\n                } else {\n                    None\n                }\n            })\n            .collect();\n\n        Ok((prefix_start, options))\n    }\n}\n\nimpl rustyline::hint::Hinter for ArretHelper {\n    type Hint = String;\n\n    fn hint(&self, line: &str, pos: usize, _: &rustyline::Context<'_>) -> Option<String> {\n        use arret_syntax::error::WithinContext;\n        use arret_syntax::parser::is_identifier_char;\n\n        let within_context = error_context_for_eol(line);\n\n        // If we're inside a string we shouldn't try to hint identifiers\n        if let Some(WithinContext::String(_)) = within_context {\n            return Some(\"\\\"\".to_owned());\n        }\n\n        let last_ident_start = line\n            .rfind(|c| !is_identifier_char(c))\n            .map(|i| i + 1)\n            .unwrap_or(0);\n\n        let last_ident = &line[last_ident_start..];\n\n        let is_command = last_ident.starts_with('/');\n        let is_first_identifier = pos == last_ident.len();\n\n        // Make sure we have at least one character and we don't complete commands mid-line\n        if !(last_ident.is_empty() || (is_command && !is_first_identifier)) {\n            for name in sorted_strings_prefixed_by(&self.all_names, last_ident) {\n                // Don't suggest ourselves\n                if name.len() != last_ident.len() {\n                    return Some(name[last_ident.len()..].to_owned());\n                }\n            }\n        }\n\n        within_context\n            .and_then(|within| within.expected_next())\n            .map(|en| en.close_char().to_string())\n    }\n}\n\nimpl rustyline::highlight::Highlighter for ArretHelper {\n    fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {\n        // See if we have an error\n        let error_span = error_for_line(line).and_then(|error| {\n            if let arret_syntax::error::ErrorKind::Eof(ec) = error.kind() {\n                // We'll already be hinting at the end of the line so point to the opening char\n                ec.open_char_span()\n            } else {\n                Some(error.span())\n            }\n        });\n\n        let error_span = if let Some(error_span) = error_span {\n            error_span\n        } else {\n            return line.into();\n        };\n\n        let error_start = error_span.start() as usize;\n        let error_end = error_span.end() as usize;\n\n        let prefix = &line[0..error_start];\n        let error = &line[error_start..error_end];\n        let suffix = &line[error_end..];\n\n        let error_style = Colour::Red.bold();\n        format!(\"{}{}{}\", prefix, error_style.paint(error), suffix).into()\n    }\n\n    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(\n        &'s self,\n        prompt: &'p str,\n        _default: bool,\n    ) -> Cow<'b, str> {\n        let prompt_style = Colour::Fixed(25); // DeepSkyBlue4 (#005faf)\n        prompt_style.paint(prompt).to_string().into()\n    }\n\n    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {\n        use arret_syntax::parser::is_identifier_char;\n\n        if hint.chars().next().map(is_identifier_char) == Some(true) {\n            // This is a name completion\n            let name_style = Style::new().dimmed();\n            name_style.paint(hint).to_string().into()\n        } else {\n            // This is an unexpected EOF hint\n            let unexpected_eof_style = Colour::Red.bold();\n            unexpected_eof_style.paint(hint).to_string().into()\n        }\n    }\n\n    fn highlight_char(&self, line: &str, _pos: usize) -> bool {\n        // Essentially any character can change highlighting:\n        //\n        // - Delimiters can change the structure of input\n        // - Identifier characters can make char literals (e.g. \\newline) change validity\n        line.len() <= MAXIMUM_PARSED_LINE_LEN\n    }\n}\n\nimpl rustyline::validate::Validator for ArretHelper {\n    fn validate(\n        &self,\n        ctx: &mut ValidationContext<'_>,\n    ) -> Result<ValidationResult, rustyline::error::ReadlineError> {\n        match error_context_for_eol(ctx.input()) {\n            Some(_) => Ok(ValidationResult::Incomplete),\n            None => Ok(ValidationResult::Valid(None)),\n        }\n    }\n}\n\nimpl rustyline::Helper for ArretHelper {}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    fn assert_sorted_strings_prefixed_by(\n        expected: &[&'static str],\n        haystack: &[&'static str],\n        needle: &'static str,\n    ) {\n        let expected_vec = expected.to_owned();\n        let actual_vec: Vec<&str> = sorted_strings_prefixed_by(haystack, needle)\n            .cloned()\n            .collect();\n\n        assert_eq!(expected_vec, actual_vec)\n    }\n\n    #[test]\n    fn sorted_strings_prefixed_by_empty() {\n        let haystack: &[&str] = &[];\n        assert_sorted_strings_prefixed_by(&[], haystack, \"foo\");\n    }\n\n    #[test]\n    fn sorted_strings_prefixed_by_missing_at_beginning() {\n        // \"foo\" would be before this one\n        let haystack = &[\"zoop\"];\n        assert_sorted_strings_prefixed_by(&[], haystack, \"foo\");\n    }\n\n    #[test]\n    fn sorted_strings_prefixed_by_missing_in_middle() {\n        // \"foo\" would be in the middle of these two\n        let haystack = &[\"bar\", \"zoop\"];\n        assert_sorted_strings_prefixed_by(&[], haystack, \"foo\");\n    }\n\n    #[test]\n    fn sorted_strings_prefixed_by_missing_at_end() {\n        // \"foo\" would be after of these two\n        let haystack = &[\"bar\", \"baz\"];\n        assert_sorted_strings_prefixed_by(&[], haystack, \"foo\");\n    }\n\n    #[test]\n    fn sorted_strings_prefixed_by_only_self() {\n        let haystack = &[\"bar\", \"baz\", \"foo\"];\n        assert_sorted_strings_prefixed_by(&[\"foo\"], haystack, \"foo\");\n    }\n\n    #[test]\n    fn strings_prefixed_by_only_other() {\n        let haystack = &[\"bar\", \"baz\", \"foobar\", \"foobaz\"];\n\n        assert_sorted_strings_prefixed_by(&[\"foobar\", \"foobaz\"], haystack, \"foo\");\n    }\n\n    #[test]\n    fn strings_prefixed_by_self_and_other() {\n        let haystack = &[\"bar\", \"baz\", \"foo\", \"foobar\", \"foobaz\", \"zoop\"];\n\n        assert_sorted_strings_prefixed_by(&[\"foo\", \"foobar\", \"foobaz\"], haystack, \"foo\");\n    }\n}\n"
  },
  {
    "path": "driver/subcommand/repl/command.rs",
    "content": "pub const TYPE_ONLY_PREFIX: &str = \"/type \";\npub const QUIT_COMMAND: &str = \"/quit\";\npub const HELP_COMMAND: &str = \"/help\";\n\npub enum ParsedCommand {\n    EvalValue(String),\n    EvalType(String),\n    Quit,\n    Other,\n}\n\npub fn parse_command(mut line: String) -> ParsedCommand {\n    match line.as_ref() {\n        _ if line.starts_with(TYPE_ONLY_PREFIX) => {\n            line.drain(0..TYPE_ONLY_PREFIX.len());\n            ParsedCommand::EvalType(line)\n        }\n        HELP_COMMAND => {\n            println!(\"Available REPL commands:\");\n            println!();\n            println!(\"/help                 Prints this summary\");\n            println!(\"/type <expression>    Evaluates the type of the given expression\");\n            println!(\"/quit                 Exits the REPL\");\n            ParsedCommand::Other\n        }\n        QUIT_COMMAND => ParsedCommand::Quit,\n        _ => ParsedCommand::EvalValue(line),\n    }\n}\n"
  },
  {
    "path": "driver/subcommand/repl/history.rs",
    "content": "use std::{fs, path};\n\n/// Gets the full path to where our REPL history should be stored\n///\n/// This does very little error handling as history is a \"nice to have\" feature\npub fn repl_history_path() -> Option<path::PathBuf> {\n    let project_dirs = directories_next::ProjectDirs::from(\"org.arret-lang\", \"\", \"arret\")?;\n    let data_dir = project_dirs.data_dir();\n\n    fs::create_dir_all(data_dir).ok()?;\n    Some(data_dir.join(\"repl-history\"))\n}\n"
  },
  {
    "path": "driver/subcommand/repl/mod.rs",
    "content": "mod arret_helper;\nmod command;\nmod history;\nmod syntax;\n\nuse std::io::prelude::*;\nuse std::io::BufReader;\nuse std::sync::Arc;\nuse std::{fs, path};\n\nuse ansi_term::{Colour, Style};\n\nuse arret_compiler::{emit_diagnostics_to_stderr, CompileCtx};\n\nuse arret_helper::ArretHelper;\nuse command::{parse_command, ParsedCommand};\nuse history::repl_history_path;\n\nconst PROMPT: &str = \"arret> \";\n\npub fn interactive_loop(ccx: Arc<CompileCtx>, include_path: Option<path::PathBuf>) {\n    use arret_compiler::repl::{EvalKind, EvaledExprValue, EvaledLine};\n    use rustyline::error::ReadlineError;\n\n    // Setup our REPL backend\n    let repl_ctx = arret_compiler::repl::ReplCtx::new(ccx.clone());\n\n    // Setup Rustyline\n    let mut rl = rustyline::Editor::<ArretHelper>::new();\n\n    // Import [stdlib base] so we have most useful things defined\n    let initial_import = \"(import [stdlib base])\".to_owned();\n    repl_ctx.send_line(initial_import, EvalKind::Value).unwrap();\n    let mut sent_prelude_lines = 1;\n\n    if let Some(include_path) = include_path {\n        let include_file = fs::File::open(include_path).unwrap();\n\n        // Import the include file line-by-line\n        for line in BufReader::new(include_file).lines() {\n            repl_ctx.send_line(line.unwrap(), EvalKind::Value).unwrap();\n            sent_prelude_lines += 1\n        }\n    }\n\n    // Load our history while the REPL engine is thinking\n    let history_path = repl_history_path();\n    if let Some(ref history_path) = history_path {\n        let _ = rl.load_history(history_path);\n    }\n\n    // Collect all the responses\n    for _ in 0..sent_prelude_lines {\n        match repl_ctx.receive_result() {\n            Ok(EvaledLine::Defs(bound_names)) => {\n                rl.set_helper(Some(ArretHelper::new(bound_names)));\n            }\n            Ok(_) => {}\n            Err(diagnostics) => emit_diagnostics_to_stderr(ccx.source_loader(), diagnostics),\n        }\n    }\n\n    // Configure our styles\n    let defs_style = Colour::Purple.bold();\n\n    let expr_arrow_style = Colour::Green.bold();\n    let type_style = Colour::Fixed(166); // DarkOrange3 (#d75f00)\n    let type_brackets_style = Style::new().dimmed();\n\n    loop {\n        let mut history_dirty = false;\n        let readline = rl.readline(PROMPT);\n\n        match readline {\n            Ok(line) => {\n                if !line.chars().all(char::is_whitespace) {\n                    history_dirty = rl.add_history_entry(line.clone());\n                }\n\n                let (eval_kind, input) = match parse_command(line) {\n                    ParsedCommand::EvalValue(input) => (EvalKind::Value, input),\n                    ParsedCommand::EvalType(input) => (EvalKind::Type, input),\n                    ParsedCommand::Quit => {\n                        break;\n                    }\n                    ParsedCommand::Other => {\n                        continue;\n                    }\n                };\n\n                repl_ctx.send_line(input, eval_kind).unwrap();\n\n                if history_dirty {\n                    // Write our history while the REPL engine is thinking\n                    if let Some(ref history_path) = history_path {\n                        let _ = rl.save_history(&history_path);\n                    }\n                }\n\n                match repl_ctx.receive_result() {\n                    Ok(EvaledLine::EmptyInput) => {}\n                    Ok(EvaledLine::Defs(bound_names)) => {\n                        // Refresh our completions\n                        rl.set_helper(Some(ArretHelper::new(bound_names)));\n\n                        println!(\"{}\", defs_style.paint(\"defined\"))\n                    }\n                    Ok(EvaledLine::ExprType(type_str)) => {\n                        println!(\n                            \"{} {}\",\n                            expr_arrow_style.paint(\"=>\"),\n                            type_style.paint(type_str)\n                        );\n                    }\n                    Ok(EvaledLine::ExprValue(evaled_expr)) => {\n                        let EvaledExprValue {\n                            value_str,\n                            type_str,\n                            type_is_literal,\n                        } = evaled_expr;\n\n                        if type_is_literal {\n                            println!(\n                                // => value\n                                \"{} {}\",\n                                expr_arrow_style.paint(\"=>\"),\n                                value_str,\n                            );\n                        } else {\n                            println!(\n                                // => [value Type]\n                                \"{} {}{} {}{}\",\n                                expr_arrow_style.paint(\"=>\"),\n                                type_brackets_style.paint(\"[\"),\n                                value_str,\n                                type_style.paint(type_str),\n                                type_brackets_style.paint(\"]\"),\n                            );\n                        }\n                    }\n                    Err(diagnostics) => {\n                        emit_diagnostics_to_stderr(ccx.source_loader(), diagnostics);\n                    }\n                }\n            }\n            Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break,\n            Err(other) => {\n                panic!(\"Readline error: {:?}\", other);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "driver/subcommand/repl/syntax.rs",
    "content": "use arret_syntax::span::ByteIndex;\n\nuse super::command::TYPE_ONLY_PREFIX;\n\n/// Maximum line length we'll provide parser hints and error highlighting for\n///\n/// This requires parsing the whole line and we don't support incremental reparsing. This means\n/// in the worst case of pasting a large line character-by-character we'll behave O(n!) with\n/// with the size of the pasted line. This seems like a reasonable cutoff where a human isn't\n/// typing the input.\npub const MAXIMUM_PARSED_LINE_LEN: usize = 512;\n\npub fn error_for_line(mut line: &str) -> Option<arret_syntax::error::Error> {\n    use arret_syntax::parser::datum_from_str_with_span_offset;\n\n    let span_offset = if line.starts_with(TYPE_ONLY_PREFIX) {\n        line = &line[TYPE_ONLY_PREFIX.len()..];\n        TYPE_ONLY_PREFIX.len()\n    } else {\n        0\n    };\n\n    // Is this a command?\n    if line.starts_with('/') ||\n    // Or empty?\n    line.chars().all(char::is_whitespace) ||\n    // Or is too large to parse interactively?\n    line.len() > MAXIMUM_PARSED_LINE_LEN\n    {\n        return None;\n    }\n\n    datum_from_str_with_span_offset(None, line, span_offset as ByteIndex).err()\n}\n\npub fn error_context_for_eol(line: &str) -> Option<arret_syntax::error::WithinContext> {\n    error_for_line(line).and_then(|error| {\n        if let arret_syntax::error::ErrorKind::Eof(within_context) = error.kind() {\n            Some(*within_context)\n        } else {\n            None\n        }\n    })\n}\n"
  },
  {
    "path": "driver/tests/integration/hello-world.arret",
    "content": "(import [stdlib base])\n\n(defn main! () ->! ()\n  (println! \"Hello, world!\"))\n"
  },
  {
    "path": "driver/tests/integration/run.sh",
    "content": "#!/bin/sh\nset -e\n\nHELLO_WORLD_SOURCE=driver/tests/integration/hello-world.arret\nEXPECTED_HELLO_WORLD_OUTPUT=\"Hello, world!\"\nTEMP_HELLO_WORLD_BINARY=target/hello-world\n\ntest_binary=${1:-cargo run}\n\nassert_outputs_hello_world()\n{\n    echo $1\n    output=$($1)\n    if [ \"${output}\" != \"${EXPECTED_HELLO_WORLD_OUTPUT}\" ]; then\n        >&2 echo \"expected '${EXPECTED_HELLO_WORLD_OUTPUT}', got '${output}'\"\n        exit 1\n    fi\n}\n\nassert_outputs_hello_world \"${test_binary} eval ${HELLO_WORLD_SOURCE}\"\n\nassert_outputs_hello_world \"${test_binary} eval -\" < ${HELLO_WORLD_SOURCE}\n\n${test_binary} compile ${HELLO_WORLD_SOURCE} -o \"${TEMP_HELLO_WORLD_BINARY}\"\nassert_outputs_hello_world \"${TEMP_HELLO_WORLD_BINARY}\"\nrm \"${TEMP_HELLO_WORLD_BINARY}\"\n"
  },
  {
    "path": "editors/code/.dockerignore",
    "content": "node_modules/\nout/\n.vscode-test/\n"
  },
  {
    "path": "editors/code/.eslintrc.yml",
    "content": "plugins:\n  - '@typescript-eslint'\n  - prettier\nextends:\n  - 'eslint:recommended'\n  - 'plugin:@typescript-eslint/eslint-recommended'\n  - 'plugin:@typescript-eslint/recommended'\n  - 'plugin:@typescript-eslint/recommended-requiring-type-checking'\n  - 'plugin:prettier/recommended'\nparserOptions:\n  project: ./tsconfig.json\nrules:\n  'prettier/prettier': error\n  'no-unused-vars': off\n  '@typescript-eslint/no-unused-vars':\n    - error\n    - argsIgnorePattern: '^_'\n"
  },
  {
    "path": "editors/code/.gitignore",
    "content": "yarn-error.log\nnode_modules/\nout/\n.vscode-test/\narret-*.vsix\n"
  },
  {
    "path": "editors/code/.vscode/launch.json",
    "content": "// A launch configuration that compiles the extension and then opens it inside a new window\n// Use IntelliSense to learn about possible attributes.\n// Hover to view descriptions of existing attributes.\n// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Run Extension\",\n      \"type\": \"extensionHost\",\n      \"request\": \"launch\",\n      \"runtimeExecutable\": \"${execPath}\",\n      \"args\": [\"--extensionDevelopmentPath=${workspaceFolder}\"],\n      \"outFiles\": [\"${workspaceFolder}/out/**/*.js\"],\n      \"preLaunchTask\": \"${defaultBuildTask}\"\n    },\n    {\n      \"name\": \"Extension Tests\",\n      \"type\": \"extensionHost\",\n      \"request\": \"launch\",\n      \"runtimeExecutable\": \"${execPath}\",\n      \"args\": [\n        \"--extensionDevelopmentPath=${workspaceFolder}\",\n        \"--extensionTestsPath=${workspaceFolder}/out/test/suite/index\"\n      ],\n      \"outFiles\": [\"${workspaceFolder}/out/test/**/*.js\"],\n      \"preLaunchTask\": \"${defaultBuildTask}\"\n    }\n  ]\n}\n"
  },
  {
    "path": "editors/code/.vscode/settings.json",
    "content": "// Place your settings in this file to overwrite default and user settings.\n{\n  \"files.exclude\": {\n    \"out\": false // set this to true to hide the \"out\" folder with the compiled JS files\n  },\n  \"search.exclude\": {\n    \"out\": true // set this to false to include \"out\" folder in search results\n  },\n  // Turn off tsc task auto detection since we have the necessary tasks as npm scripts\n  \"typescript.tsc.autoDetect\": \"off\"\n}\n"
  },
  {
    "path": "editors/code/.vscode/tasks.json",
    "content": "// See https://go.microsoft.com/fwlink/?LinkId=733558\n// for the documentation about the tasks.json format\n{\n  \"version\": \"2.0.0\",\n  \"tasks\": [\n    {\n      \"type\": \"npm\",\n      \"script\": \"watch\",\n      \"problemMatcher\": \"$tsc-watch\",\n      \"isBackground\": true,\n      \"presentation\": {\n        \"reveal\": \"never\"\n      },\n      \"group\": {\n        \"kind\": \"build\",\n        \"isDefault\": true\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "editors/code/Dockerfile",
    "content": "# Visual Studio Code currently uses Node 14.16\nFROM node:14-buster\n\nRUN \\\n  apt-get update && \\\n  apt-get -y install --no-install-recommends xvfb libnss3 libgtk-3-0 libxtst6 libxss1 libasound2 libsecret-1-0 libgbm1 && \\\n  apt-get clean\n\nWORKDIR /workdir/editors/code\n\nCOPY package.json yarn.lock tsconfig.json ./\nCOPY src/test/ src/test/\n\nRUN yarn install --frozen-lockfile\nRUN yarn compile && yarn vscode:download\n"
  },
  {
    "path": "editors/code/language-configuration.json",
    "content": "{\n  \"comments\": {\n    \"lineComment\": \";\"\n  },\n  \"brackets\": [\n    [\"{\", \"}\"],\n    [\"[\", \"]\"],\n    [\"(\", \")\"]\n  ],\n  \"autoClosingPairs\": [\n    [\"{\", \"}\"],\n    [\"[\", \"]\"],\n    [\"(\", \")\"],\n    { \"open\": \"\\\"\", \"close\": \"\\\"\", \"notIn\": [\"string\"] }\n  ],\n  \"surroundingPairs\": [\n    [\"{\", \"}\"],\n    [\"[\", \"]\"],\n    [\"(\", \")\"],\n    [\"\\\"\", \"\\\"\"]\n  ],\n  \"folding\": {\n    \"offSide\": true\n  }\n}\n"
  },
  {
    "path": "editors/code/package.json",
    "content": "{\n  \"name\": \"arret\",\n  \"displayName\": \"Arret\",\n  \"description\": \"Arret language support\",\n  \"version\": \"0.0.1\",\n  \"publisher\": \"etaoins\",\n  \"repository\": \"https://github.com/etaoins/arret\",\n  \"license\": \"Apache-2.0\",\n  \"engines\": {\n    \"vscode\": \"^1.60.0\"\n  },\n  \"categories\": [\n    \"Programming Languages\"\n  ],\n  \"activationEvents\": [\n    \"onLanguage:arret\"\n  ],\n  \"main\": \"./out/extension.js\",\n  \"keywords\": [\n    \"multi-root ready\"\n  ],\n  \"contributes\": {\n    \"languages\": [\n      {\n        \"id\": \"arret\",\n        \"aliases\": [\n          \"Arret\"\n        ],\n        \"extensions\": [\n          \".arret\"\n        ],\n        \"configuration\": \"./language-configuration.json\"\n      }\n    ],\n    \"grammars\": [\n      {\n        \"language\": \"arret\",\n        \"scopeName\": \"source.arret\",\n        \"path\": \"./syntaxes/arret.tmLanguage.json\"\n      }\n    ]\n  },\n  \"scripts\": {\n    \"vscode:download\": \"node ./out/test/downloadVsCode.js\",\n    \"vscode:install\": \"vsce package && code --install-extension arret-0.0.1.vsix\",\n    \"vscode:package\": \"vsce package\",\n    \"vscode:prepublish\": \"yarn run compile\",\n    \"compile\": \"tsc -p ./\",\n    \"watch\": \"tsc -watch -p ./\",\n    \"pretest\": \"yarn run compile\",\n    \"test\": \"node ./out/test/runTest.js\",\n    \"format\": \"eslint --fix 'src/**/*.ts'\",\n    \"lint\": \"eslint 'src/**/*.ts'\"\n  },\n  \"devDependencies\": {\n    \"@types/glob\": \"7.1.4\",\n    \"@types/mocha\": \"9.0.0\",\n    \"@types/node\": \"14.17.20\",\n    \"@types/vscode\": \"1.60.0\",\n    \"@typescript-eslint/eslint-plugin\": \"4.33.0\",\n    \"@typescript-eslint/parser\": \"4.33.0\",\n    \"eslint\": \"7.32.0\",\n    \"eslint-config-prettier\": \"8.3.0\",\n    \"eslint-plugin-prettier\": \"3.4.1\",\n    \"glob\": \"7.2.0\",\n    \"mocha\": \"9.1.3\",\n    \"prettier\": \"2.5.1\",\n    \"typescript\": \"4.4.4\",\n    \"vsce\": \"1.99.0\",\n    \"vscode-test\": \"1.6.1\"\n  },\n  \"dependencies\": {\n    \"vscode-languageclient\": \"7.0.0\"\n  }\n}\n"
  },
  {
    "path": "editors/code/src/extension.ts",
    "content": "import * as vscode from 'vscode';\nimport {\n  LanguageClient,\n  LanguageClientOptions,\n  ServerOptions,\n  Trace,\n} from 'vscode-languageclient/node';\n\nlet client: LanguageClient;\n\nexport const activate = (_context: vscode.ExtensionContext): void => {\n  const command = 'arret-lsp-server';\n\n  const serverOptions: ServerOptions = {\n    run: { command },\n    debug: { command },\n  };\n\n  const clientOptions: LanguageClientOptions = {\n    documentSelector: ['arret'],\n  };\n\n  client = new LanguageClient('arret', serverOptions, clientOptions);\n  client.trace = Trace.Verbose;\n\n  client.start();\n};\n\n// this method is called when your extension is deactivated\nexport const deactivate = (): Thenable<void> | undefined => {\n  if (!client) {\n    return;\n  }\n\n  return client.stop();\n};\n"
  },
  {
    "path": "editors/code/src/test/colorize-fixtures/sample.arret",
    "content": "(import [arret internal primitives])\n\n; Primitives\n(export def let fn if quote export defmacro letmacro macro-rules deftype lettype compile-error do =\n        defrecord letrecord recur)\n\n; Booleans\ntrue false\n\n; Numbers\n-10\n10\n-100\n100\n\n; Macros\n(defmacro cond (macro-rules\n  [() ()]\n  [(test-expr body-expr rest-clauses ...)\n   (if test-expr\n     body-expr\n     (cond rest-clauses ...))]))"
  },
  {
    "path": "editors/code/src/test/colorize-results/sample_arret.json",
    "content": "[\n\t{\n\t\t\"c\": \"(\",\n\t\t\"t\": \"source.arret meta.expression.arret punctuation.section.expression.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"import\",\n\t\t\"t\": \"source.arret meta.expression.arret keyword.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"keyword.control: #C586C0\",\n\t\t\t\"light_plus\": \"keyword.control: #AF00DB\",\n\t\t\t\"dark_vs\": \"keyword.control: #569CD6\",\n\t\t\t\"light_vs\": \"keyword.control: #0000FF\",\n\t\t\t\"hc_black\": \"keyword.control: #C586C0\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"[\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.vector.arret punctuation.section.vector.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"arret\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.vector.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.vector.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"internal\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.vector.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.vector.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"primitives\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.vector.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"]\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.vector.arret punctuation.section.vector.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \")\",\n\t\t\"t\": \"source.arret meta.expression.arret punctuation.section.expression.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \";\",\n\t\t\"t\": \"source.arret comment.line.semicolon.arret punctuation.definition.comment.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"comment: #6A9955\",\n\t\t\t\"light_plus\": \"comment: #008000\",\n\t\t\t\"dark_vs\": \"comment: #6A9955\",\n\t\t\t\"light_vs\": \"comment: #008000\",\n\t\t\t\"hc_black\": \"comment: #7CA668\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" Primitives\",\n\t\t\"t\": \"source.arret comment.line.semicolon.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"comment: #6A9955\",\n\t\t\t\"light_plus\": \"comment: #008000\",\n\t\t\t\"dark_vs\": \"comment: #6A9955\",\n\t\t\t\"light_vs\": \"comment: #008000\",\n\t\t\t\"hc_black\": \"comment: #7CA668\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"(\",\n\t\t\"t\": \"source.arret meta.expression.arret punctuation.section.expression.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"export\",\n\t\t\"t\": \"source.arret meta.expression.arret keyword.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"keyword.control: #C586C0\",\n\t\t\t\"light_plus\": \"keyword.control: #AF00DB\",\n\t\t\t\"dark_vs\": \"keyword.control: #569CD6\",\n\t\t\t\"light_vs\": \"keyword.control: #0000FF\",\n\t\t\t\"hc_black\": \"keyword.control: #C586C0\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"def\",\n\t\t\"t\": \"source.arret meta.expression.arret keyword.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"keyword.control: #C586C0\",\n\t\t\t\"light_plus\": \"keyword.control: #AF00DB\",\n\t\t\t\"dark_vs\": \"keyword.control: #569CD6\",\n\t\t\t\"light_vs\": \"keyword.control: #0000FF\",\n\t\t\t\"hc_black\": \"keyword.control: #C586C0\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"let\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"fn\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"if\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"quote\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"export\",\n\t\t\"t\": \"source.arret meta.expression.arret keyword.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"keyword.control: #C586C0\",\n\t\t\t\"light_plus\": \"keyword.control: #AF00DB\",\n\t\t\t\"dark_vs\": \"keyword.control: #569CD6\",\n\t\t\t\"light_vs\": \"keyword.control: #0000FF\",\n\t\t\t\"hc_black\": \"keyword.control: #C586C0\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"defmacro\",\n\t\t\"t\": \"source.arret meta.expression.arret keyword.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"keyword.control: #C586C0\",\n\t\t\t\"light_plus\": \"keyword.control: #AF00DB\",\n\t\t\t\"dark_vs\": \"keyword.control: #569CD6\",\n\t\t\t\"light_vs\": \"keyword.control: #0000FF\",\n\t\t\t\"hc_black\": \"keyword.control: #C586C0\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"letmacro\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"macro-rules\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"deftype\",\n\t\t\"t\": \"source.arret meta.expression.arret keyword.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"keyword.control: #C586C0\",\n\t\t\t\"light_plus\": \"keyword.control: #AF00DB\",\n\t\t\t\"dark_vs\": \"keyword.control: #569CD6\",\n\t\t\t\"light_vs\": \"keyword.control: #0000FF\",\n\t\t\t\"hc_black\": \"keyword.control: #C586C0\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"lettype\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"compile-error\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"do\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"=\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"        \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"defrecord\",\n\t\t\"t\": \"source.arret meta.expression.arret keyword.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"keyword.control: #C586C0\",\n\t\t\t\"light_plus\": \"keyword.control: #AF00DB\",\n\t\t\t\"dark_vs\": \"keyword.control: #569CD6\",\n\t\t\t\"light_vs\": \"keyword.control: #0000FF\",\n\t\t\t\"hc_black\": \"keyword.control: #C586C0\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"letrecord\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"recur\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \")\",\n\t\t\"t\": \"source.arret meta.expression.arret punctuation.section.expression.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \";\",\n\t\t\"t\": \"source.arret comment.line.semicolon.arret punctuation.definition.comment.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"comment: #6A9955\",\n\t\t\t\"light_plus\": \"comment: #008000\",\n\t\t\t\"dark_vs\": \"comment: #6A9955\",\n\t\t\t\"light_vs\": \"comment: #008000\",\n\t\t\t\"hc_black\": \"comment: #7CA668\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" Booleans\",\n\t\t\"t\": \"source.arret comment.line.semicolon.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"comment: #6A9955\",\n\t\t\t\"light_plus\": \"comment: #008000\",\n\t\t\t\"dark_vs\": \"comment: #6A9955\",\n\t\t\t\"light_vs\": \"comment: #008000\",\n\t\t\t\"hc_black\": \"comment: #7CA668\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"true\",\n\t\t\"t\": \"source.arret constant.language.boolean.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"constant.language: #569CD6\",\n\t\t\t\"light_plus\": \"constant.language: #0000FF\",\n\t\t\t\"dark_vs\": \"constant.language: #569CD6\",\n\t\t\t\"light_vs\": \"constant.language: #0000FF\",\n\t\t\t\"hc_black\": \"constant.language: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"false\",\n\t\t\"t\": \"source.arret constant.language.boolean.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"constant.language: #569CD6\",\n\t\t\t\"light_plus\": \"constant.language: #0000FF\",\n\t\t\t\"dark_vs\": \"constant.language: #569CD6\",\n\t\t\t\"light_vs\": \"constant.language: #0000FF\",\n\t\t\t\"hc_black\": \"constant.language: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \";\",\n\t\t\"t\": \"source.arret comment.line.semicolon.arret punctuation.definition.comment.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"comment: #6A9955\",\n\t\t\t\"light_plus\": \"comment: #008000\",\n\t\t\t\"dark_vs\": \"comment: #6A9955\",\n\t\t\t\"light_vs\": \"comment: #008000\",\n\t\t\t\"hc_black\": \"comment: #7CA668\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" Numbers\",\n\t\t\"t\": \"source.arret comment.line.semicolon.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"comment: #6A9955\",\n\t\t\t\"light_plus\": \"comment: #008000\",\n\t\t\t\"dark_vs\": \"comment: #6A9955\",\n\t\t\t\"light_vs\": \"comment: #008000\",\n\t\t\t\"hc_black\": \"comment: #7CA668\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"-10\",\n\t\t\"t\": \"source.arret constant.numeric.long.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"constant.numeric: #B5CEA8\",\n\t\t\t\"light_plus\": \"constant.numeric: #098658\",\n\t\t\t\"dark_vs\": \"constant.numeric: #B5CEA8\",\n\t\t\t\"light_vs\": \"constant.numeric: #098658\",\n\t\t\t\"hc_black\": \"constant.numeric: #B5CEA8\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"10\",\n\t\t\"t\": \"source.arret constant.numeric.long.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"constant.numeric: #B5CEA8\",\n\t\t\t\"light_plus\": \"constant.numeric: #098658\",\n\t\t\t\"dark_vs\": \"constant.numeric: #B5CEA8\",\n\t\t\t\"light_vs\": \"constant.numeric: #098658\",\n\t\t\t\"hc_black\": \"constant.numeric: #B5CEA8\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"-100\",\n\t\t\"t\": \"source.arret constant.numeric.long.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"constant.numeric: #B5CEA8\",\n\t\t\t\"light_plus\": \"constant.numeric: #098658\",\n\t\t\t\"dark_vs\": \"constant.numeric: #B5CEA8\",\n\t\t\t\"light_vs\": \"constant.numeric: #098658\",\n\t\t\t\"hc_black\": \"constant.numeric: #B5CEA8\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"100\",\n\t\t\"t\": \"source.arret constant.numeric.long.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"constant.numeric: #B5CEA8\",\n\t\t\t\"light_plus\": \"constant.numeric: #098658\",\n\t\t\t\"dark_vs\": \"constant.numeric: #B5CEA8\",\n\t\t\t\"light_vs\": \"constant.numeric: #098658\",\n\t\t\t\"hc_black\": \"constant.numeric: #B5CEA8\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \";\",\n\t\t\"t\": \"source.arret comment.line.semicolon.arret punctuation.definition.comment.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"comment: #6A9955\",\n\t\t\t\"light_plus\": \"comment: #008000\",\n\t\t\t\"dark_vs\": \"comment: #6A9955\",\n\t\t\t\"light_vs\": \"comment: #008000\",\n\t\t\t\"hc_black\": \"comment: #7CA668\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" Macros\",\n\t\t\"t\": \"source.arret comment.line.semicolon.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"comment: #6A9955\",\n\t\t\t\"light_plus\": \"comment: #008000\",\n\t\t\t\"dark_vs\": \"comment: #6A9955\",\n\t\t\t\"light_vs\": \"comment: #008000\",\n\t\t\t\"hc_black\": \"comment: #7CA668\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"(\",\n\t\t\"t\": \"source.arret meta.expression.arret punctuation.section.expression.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"defmacro\",\n\t\t\"t\": \"source.arret meta.expression.arret keyword.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"keyword.control: #C586C0\",\n\t\t\t\"light_plus\": \"keyword.control: #AF00DB\",\n\t\t\t\"dark_vs\": \"keyword.control: #569CD6\",\n\t\t\t\"light_vs\": \"keyword.control: #0000FF\",\n\t\t\t\"hc_black\": \"keyword.control: #C586C0\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"cond\",\n\t\t\"t\": \"source.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"(\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret punctuation.section.expression.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"macro-rules\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"  \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"[\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret punctuation.section.vector.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"(\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret punctuation.section.expression.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \")\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret punctuation.section.expression.end.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"(\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret punctuation.section.expression.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \")\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret punctuation.section.expression.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"]\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret punctuation.section.vector.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"  \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"[\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret punctuation.section.vector.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"(\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret punctuation.section.expression.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"test-expr\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret entity.name.function.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"entity.name.function: #DCDCAA\",\n\t\t\t\"light_plus\": \"entity.name.function: #795E26\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"entity.name.function: #DCDCAA\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"body-expr\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"rest-clauses\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"...\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \")\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret punctuation.section.expression.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"   \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"(\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret punctuation.section.expression.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"if\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"test-expr\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"     \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"body-expr\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"     \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"(\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.expression.arret punctuation.section.expression.begin.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"cond\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.expression.arret storage.control.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"storage: #569CD6\",\n\t\t\t\"light_plus\": \"storage: #0000FF\",\n\t\t\t\"dark_vs\": \"storage: #569CD6\",\n\t\t\t\"light_vs\": \"storage: #0000FF\",\n\t\t\t\"hc_black\": \"storage: #569CD6\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"rest-clauses\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.expression.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \" \",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.expression.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"...\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.expression.arret meta.symbol.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \")\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret meta.expression.arret punctuation.section.expression.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \")\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret meta.expression.arret punctuation.section.expression.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \"]\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret meta.vector.arret punctuation.section.vector.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \")\",\n\t\t\"t\": \"source.arret meta.expression.arret meta.expression.arret punctuation.section.expression.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t},\n\t{\n\t\t\"c\": \")\",\n\t\t\"t\": \"source.arret meta.expression.arret punctuation.section.expression.end.trailing.arret\",\n\t\t\"r\": {\n\t\t\t\"dark_plus\": \"default: #D4D4D4\",\n\t\t\t\"light_plus\": \"default: #000000\",\n\t\t\t\"dark_vs\": \"default: #D4D4D4\",\n\t\t\t\"light_vs\": \"default: #000000\",\n\t\t\t\"hc_black\": \"default: #FFFFFF\"\n\t\t}\n\t}\n]"
  },
  {
    "path": "editors/code/src/test/downloadVsCode.ts",
    "content": "import { downloadAndUnzipVSCode } from 'vscode-test';\n\nimport { VSCODE_VERSION } from './vsCodeVersion';\n\nasync function main(): Promise<void> {\n  try {\n    await downloadAndUnzipVSCode(VSCODE_VERSION);\n  } catch (err) {\n    console.error('Failed to download VS Code');\n    process.exit(1);\n  }\n}\n\nvoid main();\n"
  },
  {
    "path": "editors/code/src/test/runTest.ts",
    "content": "import * as path from 'path';\n\nimport { runTests } from 'vscode-test';\n\nimport { VSCODE_VERSION } from './vsCodeVersion';\n\nasync function main(): Promise<void> {\n  try {\n    // The folder containing the Extension Manifest package.json\n    // Passed to `--extensionDevelopmentPath`\n    const extensionDevelopmentPath = path.resolve(__dirname, '../../');\n\n    // The path to test runner\n    // Passed to --extensionTestsPath\n    const extensionTestsPath = path.resolve(__dirname, './suite/index');\n\n    // Download VS Code, unzip it and run the integration test\n    await runTests({\n      extensionDevelopmentPath,\n      extensionTestsPath,\n      version: VSCODE_VERSION,\n    });\n  } catch (err) {\n    console.error('Failed to run tests');\n    process.exit(1);\n  }\n}\n\nvoid main();\n"
  },
  {
    "path": "editors/code/src/test/suite/colorization.test.ts",
    "content": "/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n\nimport * as assert from 'assert';\nimport * as fs from 'fs';\nimport { join, basename, dirname } from 'path';\n\nimport { commands, Uri } from 'vscode';\n\nconst hasThemeChange = (\n  d: { [key: string]: unknown },\n  p: { [key: string]: unknown },\n): boolean => {\n  const keys = Object.keys(d);\n  for (const key of keys) {\n    if (d[key] !== p[key]) {\n      return true;\n    }\n  }\n  return false;\n};\n\nconst assertUnchangedTokens = (\n  testFixurePath: string,\n  done: (err?: unknown) => void,\n): Thenable<void> => {\n  const fileName = basename(testFixurePath);\n\n  return commands\n    .executeCommand('_workbench.captureSyntaxTokens', Uri.file(testFixurePath))\n    .then((data) => {\n      try {\n        const resultsFolderPath = join(\n          dirname(dirname(testFixurePath)),\n          'colorize-results',\n        );\n        if (!fs.existsSync(resultsFolderPath)) {\n          fs.mkdirSync(resultsFolderPath);\n        }\n        const resultPath = join(\n          resultsFolderPath,\n          fileName.replace('.', '_') + '.json',\n        );\n        if (fs.existsSync(resultPath)) {\n          const previousData = JSON.parse(\n            fs.readFileSync(resultPath).toString(),\n          );\n          try {\n            assert.deepEqual(data, previousData);\n          } catch (e) {\n            fs.writeFileSync(resultPath, JSON.stringify(data, null, '\\t'), {\n              flag: 'w',\n            });\n            if (\n              Array.isArray(data) &&\n              Array.isArray(previousData) &&\n              data.length === previousData.length\n            ) {\n              for (let i = 0; i < data.length; i++) {\n                const d = data[i];\n                const p = previousData[i];\n                if (d.c !== p.c || hasThemeChange(d.r, p.r)) {\n                  throw e;\n                }\n              }\n              // different but no tokenization ot color change: no failure\n            } else {\n              throw e;\n            }\n          }\n        } else {\n          fs.writeFileSync(resultPath, JSON.stringify(data, null, '\\t'));\n        }\n        done();\n      } catch (e) {\n        done(e);\n      }\n    }, done);\n};\n\nsuite('colorization', () => {\n  const extensionColorizeFixturePath = join(\n    __dirname,\n    '../../../src/test/colorize-fixtures',\n  );\n  if (fs.existsSync(extensionColorizeFixturePath)) {\n    const fixturesFiles = fs.readdirSync(extensionColorizeFixturePath);\n    fixturesFiles.forEach((fixturesFile) => {\n      // define a test for each fixture\n      test(fixturesFile, function (done) {\n        void assertUnchangedTokens(\n          join(extensionColorizeFixturePath, fixturesFile),\n          done,\n        );\n      });\n    });\n  }\n});\n"
  },
  {
    "path": "editors/code/src/test/suite/extension.test.ts",
    "content": "import * as assert from 'assert';\n\n// You can import and use all API from the 'vscode' module\n// as well as import your extension to test it\nimport * as vscode from 'vscode';\n// import * as myExtension from '../extension';\n\nsuite('Extension Test Suite', () => {\n  void vscode.window.showInformationMessage('Start all tests.');\n\n  test('Sample test', () => {\n    assert.equal(-1, [1, 2, 3].indexOf(5));\n    assert.equal(-1, [1, 2, 3].indexOf(0));\n  });\n});\n"
  },
  {
    "path": "editors/code/src/test/suite/index.ts",
    "content": "import * as path from 'path';\nimport * as Mocha from 'mocha';\nimport * as glob from 'glob';\n\nexport function run(): Promise<void> {\n  // Create the mocha test\n  const mocha = new Mocha({\n    ui: 'tdd',\n    color: true,\n  });\n\n  const testsRoot = path.resolve(__dirname, '..');\n\n  return new Promise((c, e) => {\n    glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {\n      if (err) {\n        return e(err);\n      }\n\n      // Add files to the test suite\n      files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f)));\n\n      try {\n        // Run the mocha test\n        mocha.run((failures) => {\n          if (failures > 0) {\n            e(new Error(`${failures} tests failed.`));\n          } else {\n            c();\n          }\n        });\n      } catch (err) {\n        e(err);\n      }\n    });\n  });\n}\n"
  },
  {
    "path": "editors/code/src/test/vsCodeVersion.ts",
    "content": "export const VSCODE_VERSION = '1.60.0';\n"
  },
  {
    "path": "editors/code/syntaxes/arret.tmLanguage.json",
    "content": "{\n  \"information_for_contributors\": [\n    \"This file is based on https://github.com/microsoft/vscode/blob/master/extensions/clojure/syntaxes/clojure.tmLanguage.json\"\n  ],\n  \"name\": \"Arret\",\n  \"scopeName\": \"source.arret\",\n  \"patterns\": [\n    {\n      \"include\": \"#comment\"\n    },\n    {\n      \"include\": \"#quoted-sexp\"\n    },\n    {\n      \"include\": \"#sexp\"\n    },\n    {\n      \"include\": \"#keyfn\"\n    },\n    {\n      \"include\": \"#string\"\n    },\n    {\n      \"include\": \"#vector\"\n    },\n    {\n      \"include\": \"#set\"\n    },\n    {\n      \"include\": \"#map\"\n    },\n    {\n      \"include\": \"#var\"\n    },\n    {\n      \"include\": \"#constants\"\n    },\n    {\n      \"include\": \"#namespace-symbol\"\n    },\n    {\n      \"include\": \"#symbol\"\n    }\n  ],\n  \"repository\": {\n    \"comment\": {\n      \"begin\": \"(?<!\\\\\\\\);\",\n      \"beginCaptures\": {\n        \"0\": {\n          \"name\": \"punctuation.definition.comment.arret\"\n        }\n      },\n      \"end\": \"$\",\n      \"name\": \"comment.line.semicolon.arret\"\n    },\n    \"constants\": {\n      \"patterns\": [\n        {\n          \"match\": \"(true|false)\",\n          \"name\": \"constant.language.boolean.arret\"\n        },\n        {\n          \"match\": \"(-?\\\\d+\\\\.\\\\d+([eE][+-]?\\\\d+)?)\",\n          \"name\": \"constant.numeric.double.arret\"\n        },\n        {\n          \"match\": \"(-?\\\\d+)\",\n          \"name\": \"constant.numeric.long.arret\"\n        },\n        {\n          \"include\": \"#keyword\"\n        }\n      ]\n    },\n    \"keyword\": {\n      \"match\": \"(?<=(\\\\s|\\\\(|\\\\[|\\\\{)):[\\\\w\\\\#\\\\.\\\\-\\\\_\\\\:\\\\+\\\\=\\\\>\\\\<\\\\/\\\\!\\\\?\\\\*]+(?=(\\\\s|\\\\)|\\\\]|\\\\}|\\\\,))\",\n      \"name\": \"constant.keyword.arret\"\n    },\n    \"keyfn\": {\n      \"patterns\": [\n        {\n          \"match\": \"(?<=(\\\\s|\\\\(|\\\\[|\\\\{))(if(-[-\\\\p{Ll}\\\\?]*)?|when(-[-\\\\p{Ll}]*)?|for(-[-\\\\p{Ll}]*)?|compile-error|cond|do|macro-rules|quote|letmacro|lettype|letrecord|let(-[-\\\\p{Ll}\\\\?]*)?|loop|recur|fn|([\\\\p{Ll}]*case))(?=(\\\\s|\\\\)|\\\\]|\\\\}))\",\n          \"name\": \"storage.control.arret\"\n        },\n        {\n          \"match\": \"(?<=(\\\\s|\\\\(|\\\\[|\\\\{))(import|export|defmacro|deftype|defmacro|(def[\\\\p{Ll}\\\\-]*))(?=(\\\\s|\\\\)|\\\\]|\\\\}))\",\n          \"name\": \"keyword.control.arret\"\n        }\n      ]\n    },\n    \"dynamic-variables\": {\n      \"match\": \"\\\\*[\\\\w\\\\.\\\\-\\\\_\\\\:\\\\+\\\\=\\\\>\\\\<\\\\!\\\\?\\\\d]+\\\\*\",\n      \"name\": \"meta.symbol.dynamic.arret\"\n    },\n    \"map\": {\n      \"begin\": \"(\\\\{)\",\n      \"beginCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.section.map.begin.arret\"\n        }\n      },\n      \"end\": \"(\\\\}(?=[\\\\}\\\\]\\\\)\\\\s]*(?:;|$)))|(\\\\})\",\n      \"endCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.section.map.end.trailing.arret\"\n        },\n        \"2\": {\n          \"name\": \"punctuation.section.map.end.arret\"\n        }\n      },\n      \"name\": \"meta.map.arret\",\n      \"patterns\": [\n        {\n          \"include\": \"$self\"\n        }\n      ]\n    },\n    \"quoted-sexp\": {\n      \"begin\": \"(['``]\\\\()\",\n      \"beginCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.section.expression.begin.arret\"\n        }\n      },\n      \"end\": \"(\\\\))$|(\\\\)(?=[\\\\}\\\\]\\\\)\\\\s]*(?:;|$)))|(\\\\))\",\n      \"endCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.section.expression.end.trailing.arret\"\n        },\n        \"2\": {\n          \"name\": \"punctuation.section.expression.end.trailing.arret\"\n        },\n        \"3\": {\n          \"name\": \"punctuation.section.expression.end.arret\"\n        }\n      },\n      \"name\": \"meta.quoted-expression.arret\",\n      \"patterns\": [\n        {\n          \"include\": \"$self\"\n        }\n      ]\n    },\n    \"set\": {\n      \"begin\": \"(\\\\#\\\\{)\",\n      \"beginCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.section.set.begin.arret\"\n        }\n      },\n      \"end\": \"(\\\\}(?=[\\\\}\\\\]\\\\)\\\\s]*(?:;|$)))|(\\\\})\",\n      \"endCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.section.set.end.trailing.arret\"\n        },\n        \"2\": {\n          \"name\": \"punctuation.section.set.end.arret\"\n        }\n      },\n      \"name\": \"meta.set.arret\",\n      \"patterns\": [\n        {\n          \"include\": \"$self\"\n        }\n      ]\n    },\n    \"sexp\": {\n      \"begin\": \"(\\\\()\",\n      \"beginCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.section.expression.begin.arret\"\n        }\n      },\n      \"end\": \"(\\\\))$|(\\\\)(?=[\\\\}\\\\]\\\\)\\\\s]*(?:;|$)))|(\\\\))\",\n      \"endCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.section.expression.end.trailing.arret\"\n        },\n        \"2\": {\n          \"name\": \"punctuation.section.expression.end.trailing.arret\"\n        },\n        \"3\": {\n          \"name\": \"punctuation.section.expression.end.arret\"\n        }\n      },\n      \"name\": \"meta.expression.arret\",\n      \"patterns\": [\n        {\n          \"include\": \"#keyfn\"\n        },\n        {\n          \"include\": \"#constants\"\n        },\n        {\n          \"include\": \"#vector\"\n        },\n        {\n          \"include\": \"#map\"\n        },\n        {\n          \"include\": \"#set\"\n        },\n        {\n          \"include\": \"#sexp\"\n        },\n        {\n          \"match\": \"(?<=\\\\()(.+?)(?=\\\\s|\\\\))\",\n          \"captures\": {\n            \"1\": {\n              \"name\": \"entity.name.function.arret\"\n            }\n          },\n          \"patterns\": [\n            {\n              \"include\": \"$self\"\n            }\n          ]\n        },\n        {\n          \"include\": \"$self\"\n        }\n      ]\n    },\n    \"string\": {\n      \"begin\": \"(?<!\\\\\\\\)(\\\")\",\n      \"beginCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.definition.string.begin.arret\"\n        }\n      },\n      \"end\": \"(\\\")\",\n      \"endCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.definition.string.end.arret\"\n        }\n      },\n      \"name\": \"string.quoted.double.arret\",\n      \"patterns\": [\n        {\n          \"match\": \"\\\\\\\\.\",\n          \"name\": \"constant.character.escape.arret\"\n        }\n      ]\n    },\n    \"namespace-symbol\": {\n      \"patterns\": [\n        {\n          \"match\": \"([\\\\p{L}\\\\.\\\\-\\\\_\\\\+\\\\=\\\\>\\\\<\\\\!\\\\?\\\\*][\\\\w\\\\.\\\\-\\\\_\\\\:\\\\+\\\\=\\\\>\\\\<\\\\!\\\\?\\\\*\\\\d]*)/\",\n          \"captures\": {\n            \"1\": {\n              \"name\": \"meta.symbol.namespace.arret\"\n            }\n          }\n        }\n      ]\n    },\n    \"symbol\": {\n      \"patterns\": [\n        {\n          \"match\": \"([\\\\p{L}\\\\.\\\\-\\\\_\\\\+\\\\=\\\\>\\\\<\\\\!\\\\?\\\\*][\\\\w\\\\.\\\\-\\\\_\\\\:\\\\+\\\\=\\\\>\\\\<\\\\!\\\\?\\\\*\\\\d]*)\",\n          \"name\": \"meta.symbol.arret\"\n        }\n      ]\n    },\n    \"var\": {\n      \"match\": \"(?<=(\\\\s|\\\\(|\\\\[|\\\\{)\\\\#)'[\\\\w\\\\.\\\\-\\\\_\\\\:\\\\+\\\\=\\\\>\\\\<\\\\/\\\\!\\\\?\\\\*]+(?=(\\\\s|\\\\)|\\\\]|\\\\}))\",\n      \"name\": \"meta.var.arret\"\n    },\n    \"vector\": {\n      \"begin\": \"(\\\\[)\",\n      \"beginCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.section.vector.begin.arret\"\n        }\n      },\n      \"end\": \"(\\\\](?=[\\\\}\\\\]\\\\)\\\\s]*(?:;|$)))|(\\\\])\",\n      \"endCaptures\": {\n        \"1\": {\n          \"name\": \"punctuation.section.vector.end.trailing.arret\"\n        },\n        \"2\": {\n          \"name\": \"punctuation.section.vector.end.arret\"\n        }\n      },\n      \"name\": \"meta.vector.arret\",\n      \"patterns\": [\n        {\n          \"include\": \"$self\"\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "editors/code/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"target\": \"es2019\",\n    \"outDir\": \"out\",\n    \"sourceMap\": true,\n    \"rootDir\": \"src\",\n    \"strict\": true\n  },\n  \"exclude\": [\"node_modules\", \".vscode-test\"]\n}\n"
  },
  {
    "path": "lsp-server/Cargo.toml",
    "content": "[package]\nname = \"arret-lsp-server\"\nversion = \"0.1.0\"\nedition = \"2018\"\nauthors = [\"Ryan Cumming <etaoins@gmail.com>\"]\n\n[[bin]]\nname = \"arret-lsp-server\"\npath = \"main.rs\"\n\n[dependencies]\n# This matches the version range in `codespan-lsp`\nlsp-types = \"0.84\"\narret-syntax = { path = \"../syntax\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0\"\n\n[dependencies.tokio]\nversion = \"1.14\"\nfeatures = [\n  \"rt\",\n  \"rt-multi-thread\",\n  \"io-util\",\n  \"io-std\",\n  \"macros\",\n  \"sync\"\n]"
  },
  {
    "path": "lsp-server/capabilities.rs",
    "content": "pub fn server_capabilities() -> lsp_types::ServerCapabilities {\n    lsp_types::ServerCapabilities {\n        text_document_sync: Some(lsp_types::TextDocumentSyncCapability::Options(\n            lsp_types::TextDocumentSyncOptions {\n                open_close: Some(true),\n                change: Some(lsp_types::TextDocumentSyncKind::Incremental),\n                ..Default::default()\n            },\n        )),\n        workspace: Some(lsp_types::WorkspaceCapability {\n            workspace_folders: Some(lsp_types::WorkspaceFolderCapability {\n                supported: Some(true),\n                change_notifications: Some(\n                    lsp_types::WorkspaceFolderCapabilityChangeNotifications::Bool(true),\n                ),\n            }),\n        }),\n        ..Default::default()\n    }\n}\n"
  },
  {
    "path": "lsp-server/handler/mod.rs",
    "content": "mod text_synchronisation;\nuse text_synchronisation::*;\n\nmod workspace;\nuse workspace::*;\n\nuse lsp_types::notification::Notification as LspNotification;\n\nuse crate::json_rpc::{ErrorCode, Notification, Request, Response};\nuse crate::session::State;\n\n/// Trait for handling notifications\npub trait SyncNotificationHandler {\n    type Notification: LspNotification;\n\n    fn handle(state: &mut State, params: <Self::Notification as LspNotification>::Params);\n}\n\nmacro_rules! build_notification_dispatcher {\n    ($name:ident, { $( $sync_handler:ty ),* }) => {\n        pub fn $name(state: &mut State, notification: Notification) {\n            match notification.method.as_str() {\n                $(\n                    <$sync_handler as SyncNotificationHandler>::Notification::METHOD => {\n                        let params = serde_json::from_value(notification.params)\n                            .expect(\"Could not parse notification params\");\n\n                        <$sync_handler as SyncNotificationHandler>::handle(state, params);\n                    }\n                )*,\n                other => {\n                    // Allow optional notifications\n                    if !other.starts_with(\"$/\") {\n                        eprintln!(\"Unexpected notification method '{}'\", notification.method);\n                    }\n                }\n            }\n        }\n    };\n}\n\nbuild_notification_dispatcher!(handle_non_lifecycle_notification, {\n    DidOpenTextDocumentHandler,\n    DidChangeTextDocumentHandler,\n    DidCloseTextDocumentHandler,\n    DidChangeWorkspaceFoldersHandler\n});\n\npub fn handle_non_lifecycle_request(_state: &mut State, request: Request) -> Response {\n    // We only support lifecycle requests at the moment\n    Response::new_err(request.id, ErrorCode::MethodNotFound, \"Method not found\")\n}\n"
  },
  {
    "path": "lsp-server/handler/text_synchronisation.rs",
    "content": "use std::sync::Arc;\n\nuse crate::handler::SyncNotificationHandler;\nuse crate::model::Document;\nuse crate::session::State;\nuse crate::watcher::DocumentWatcher;\n\npub struct DidOpenTextDocumentHandler;\n\nimpl SyncNotificationHandler for DidOpenTextDocumentHandler {\n    type Notification = lsp_types::notification::DidOpenTextDocument;\n\n    fn handle(state: &mut State, params: lsp_types::DidOpenTextDocumentParams) {\n        let text_document = params.text_document;\n\n        let document = Arc::new(Document::new(text_document.version, text_document.text));\n\n        state.syntax_watcher.did_open(&text_document.uri, &document);\n\n        state\n            .documents\n            .insert(text_document.uri.to_string(), document);\n    }\n}\n\npub struct DidChangeTextDocumentHandler;\n\nimpl SyncNotificationHandler for DidChangeTextDocumentHandler {\n    type Notification = lsp_types::notification::DidChangeTextDocument;\n\n    fn handle(state: &mut State, params: lsp_types::DidChangeTextDocumentParams) {\n        let lsp_types::DidChangeTextDocumentParams {\n            text_document,\n            content_changes,\n        } = params;\n\n        let orig_document =\n            if let Some(document) = state.documents.remove(text_document.uri.as_str()) {\n                document\n            } else {\n                eprintln!(\n                    \"Received change notification for unknown document {}\",\n                    text_document.uri\n                );\n                return;\n            };\n\n        let new_document =\n            content_changes\n                .into_iter()\n                .fold(\n                    orig_document,\n                    |prev_document, content_change| match content_change.range {\n                        Some(range) => {\n                            match prev_document.with_range_edit(\n                                text_document.version,\n                                range,\n                                &content_change.text,\n                            ) {\n                                Ok(new_document) => Arc::new(new_document),\n                                Err(()) => {\n                                    eprintln!(\n                                        \"Could not find range to replace in {}\",\n                                        text_document.uri\n                                    );\n\n                                    prev_document\n                                }\n                            }\n                        }\n                        None => Arc::new(Document::new(text_document.version, content_change.text)),\n                    },\n                );\n\n        state\n            .syntax_watcher\n            .did_change(&text_document.uri, &new_document);\n\n        state\n            .documents\n            .insert(text_document.uri.to_string(), new_document);\n    }\n}\n\npub struct DidCloseTextDocumentHandler;\n\nimpl SyncNotificationHandler for DidCloseTextDocumentHandler {\n    type Notification = lsp_types::notification::DidCloseTextDocument;\n\n    fn handle(state: &mut State, params: lsp_types::DidCloseTextDocumentParams) {\n        let text_document = params.text_document;\n\n        state.documents.remove(text_document.uri.as_str());\n        state.syntax_watcher.did_close(&text_document.uri);\n    }\n}\n"
  },
  {
    "path": "lsp-server/handler/workspace.rs",
    "content": "use std::sync::Arc;\n\nuse crate::handler::SyncNotificationHandler;\nuse crate::model::Workspace;\nuse crate::session::State;\n\npub struct DidChangeWorkspaceFoldersHandler;\n\nimpl SyncNotificationHandler for DidChangeWorkspaceFoldersHandler {\n    type Notification = lsp_types::notification::DidChangeWorkspaceFolders;\n\n    fn handle(state: &mut State, params: lsp_types::DidChangeWorkspaceFoldersParams) {\n        let lsp_types::DidChangeWorkspaceFoldersParams { event } = params;\n\n        for added in event.added {\n            state\n                .workspaces\n                .insert(added.uri.to_string(), Arc::new(Workspace::new(added.name)));\n        }\n\n        for removed in event.removed {\n            state.workspaces.remove(removed.uri.as_str());\n        }\n    }\n}\n"
  },
  {
    "path": "lsp-server/json_rpc.rs",
    "content": "use serde::{Deserialize, Serialize};\n\n// This was originally stolen from rust-analyzer/lsp-server\n\n#[repr(i32)]\npub enum ErrorCode {\n    ServerNotInitialized = -32002,\n    InvalidRequest = -32600,\n    MethodNotFound = -32601,\n}\n\n#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]\n#[serde(untagged)]\npub enum ClientMessage {\n    Request(Request),\n    Notification(Notification),\n}\n\nimpl From<Request> for ClientMessage {\n    fn from(request: Request) -> ClientMessage {\n        ClientMessage::Request(request)\n    }\n}\n\nimpl From<Notification> for ClientMessage {\n    fn from(notification: Notification) -> ClientMessage {\n        ClientMessage::Notification(notification)\n    }\n}\n\n#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]\n#[serde(untagged)]\npub enum ServerMessage {\n    Response(Response),\n    Notification(Notification),\n}\n\nimpl From<Response> for ServerMessage {\n    fn from(response: Response) -> ServerMessage {\n        ServerMessage::Response(response)\n    }\n}\n\nimpl From<Notification> for ServerMessage {\n    fn from(notification: Notification) -> ServerMessage {\n        ServerMessage::Notification(notification)\n    }\n}\n\n#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]\npub struct Notification {\n    pub method: String,\n    pub params: serde_json::Value,\n}\n\nimpl Notification {\n    pub fn new(method: impl Into<String>, params: impl Serialize) -> Self {\n        Notification {\n            method: method.into(),\n            params: serde_json::to_value(params).expect(\"Could not serialise notification\"),\n        }\n    }\n\n    pub fn new_lsp<N>(params: N::Params) -> Self\n    where\n        N: lsp_types::notification::Notification,\n        N::Params: Serialize,\n    {\n        Self::new(N::METHOD, params)\n    }\n}\n\n#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]\n#[serde(untagged)]\nenum IdRepr {\n    U64(u64),\n    String(String),\n}\n\n#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]\n#[serde(transparent)]\npub struct RequestId(IdRepr);\n\nimpl From<u64> for RequestId {\n    fn from(id: u64) -> RequestId {\n        RequestId(IdRepr::U64(id))\n    }\n}\n\nimpl From<String> for RequestId {\n    fn from(id: String) -> RequestId {\n        RequestId(IdRepr::String(id))\n    }\n}\n\n#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]\npub struct Request {\n    pub id: RequestId,\n    pub method: String,\n    pub params: serde_json::Value,\n}\n\nimpl Request {\n    #[cfg(test)]\n    pub fn new(id: RequestId, method: impl Into<String>, params: impl Serialize) -> Self {\n        Request {\n            id,\n            method: method.into(),\n            params: serde_json::to_value(params).expect(\"Could not serialise request\"),\n        }\n    }\n\n    #[cfg(test)]\n    pub fn new_lsp<N>(id: RequestId, params: N::Params) -> Self\n    where\n        N: lsp_types::request::Request,\n        N::Params: Serialize,\n    {\n        Self::new(id, N::METHOD, params)\n    }\n}\n\n#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]\npub struct Response {\n    pub id: RequestId,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub result: Option<serde_json::Value>,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub error: Option<ResponseError>,\n}\n\n#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]\npub struct ResponseError {\n    pub code: i32,\n    pub message: String,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub data: Option<serde_json::Value>,\n}\n\nimpl Response {\n    pub fn new_ok(id: RequestId, result: impl Serialize) -> Response {\n        Response {\n            id,\n            result: Some(serde_json::to_value(result).expect(\"Could not serialise result\")),\n            error: None,\n        }\n    }\n\n    pub fn new_err(id: RequestId, code: ErrorCode, message: impl Into<String>) -> Response {\n        let error = ResponseError {\n            code: code as i32,\n            message: message.into(),\n            data: None,\n        };\n\n        Response {\n            id,\n            result: None,\n            error: Some(error),\n        }\n    }\n}\n"
  },
  {
    "path": "lsp-server/main.rs",
    "content": "mod capabilities;\nmod handler;\nmod json_rpc;\nmod model;\nmod session;\nmod transport;\nmod watcher;\n\nuse tokio::io;\n\n#[tokio::main]\nasync fn main() -> Result<(), ()> {\n    let reader = io::BufReader::new(io::stdin());\n    let writer = io::stdout();\n\n    let connection = transport::bytestream::create_connection(reader, writer);\n    session::run(connection).await\n}\n"
  },
  {
    "path": "lsp-server/model/document.rs",
    "content": "use arret_syntax::span::Span;\n\n#[derive(Debug)]\npub struct Document {\n    version: i32,\n    text: String,\n    line_offsets: Vec<usize>,\n}\n\nfn line_offsets_for_str(source: &str) -> Vec<usize> {\n    std::iter::once(0)\n        .chain(source.match_indices('\\n').map(|(i, _)| i + 1))\n        .collect()\n}\n\nimpl Document {\n    pub fn new(version: i32, text: String) -> Document {\n        Document {\n            version,\n            line_offsets: line_offsets_for_str(&text),\n            text,\n        }\n    }\n\n    /// Returns a new instance of the document with specified range replaced\n    pub fn with_range_edit(\n        &self,\n        new_version: i32,\n        range: lsp_types::Range,\n        new_range_text: &str,\n    ) -> Result<Document, ()> {\n        let start_offset = if let Some(start_offset) = self.position_to_offset(range.start) {\n            start_offset\n        } else {\n            return Err(());\n        };\n\n        let end_offset = self.position_to_offset(range.end);\n\n        // Rebuild the new text\n        let mut new_text = self.text[..start_offset].to_string() + new_range_text;\n        if let Some(end_offset) = end_offset {\n            new_text += &self.text[end_offset..];\n        }\n\n        // Preserve the line offsets from before the edit\n        let mut new_line_offsets = (&self.line_offsets[..=range.start.line as usize]).to_vec();\n\n        // Add the line offsets inside the new range\n        new_line_offsets.extend(\n            new_range_text\n                .match_indices('\\n')\n                .map(|(i, _)| i + start_offset + 1),\n        );\n\n        if let Some(end_offset) = end_offset {\n            // Shift the remaining offsets to account for the size of the new range\n            let previous_len = end_offset - start_offset;\n            new_line_offsets.extend(\n                self.line_offsets[range.end.line as usize + 1..]\n                    .iter()\n                    .map(|i| i + new_range_text.len() - previous_len),\n            )\n        }\n\n        Ok(Document {\n            version: new_version,\n            line_offsets: new_line_offsets,\n            text: new_text,\n        })\n    }\n\n    /// Returns the document version\n    pub fn version(&self) -> i32 {\n        self.version\n    }\n\n    /// Returns the document text\n    pub fn text(&self) -> &str {\n        self.text.as_ref()\n    }\n\n    /// Returns an LSP `Range` for the given `arret-syntax` S`pan`\n    pub fn span_to_range(&self, span: Span) -> lsp_types::Range {\n        lsp_types::Range {\n            start: self.offset_to_position(span.start() as usize),\n            end: self.offset_to_position(span.end() as usize),\n        }\n    }\n\n    /// Returns the position for the given byte offset\n    pub fn offset_to_position(&self, offset: usize) -> lsp_types::Position {\n        let line = match self\n            .line_offsets\n            .binary_search_by(|line_start| line_start.cmp(&offset))\n        {\n            Ok(line) => line,\n            Err(line) => line - 1,\n        };\n\n        let line_start = self.line_offsets[line];\n        let character: usize = self.text[line_start..offset]\n            .chars()\n            .map(|c| c.len_utf16())\n            .sum();\n\n        lsp_types::Position {\n            line: line as u32,\n            character: character as u32,\n        }\n    }\n\n    /// Returns the byte offset for the given position\n    fn position_to_offset(&self, position: lsp_types::Position) -> Option<usize> {\n        // Lines are already computed\n        let line_offset = *self.line_offsets.get(position.line as usize)?;\n\n        if position.character == 0 {\n            return Some(line_offset);\n        }\n\n        let mut utf16_chars_remaining = position.character as usize;\n\n        for (char_offset, c) in self.text[line_offset..].char_indices() {\n            utf16_chars_remaining -= c.len_utf16();\n\n            if utf16_chars_remaining == 0 {\n                return Some(line_offset + char_offset + c.len_utf8());\n            }\n        }\n\n        // Ran out of string\n        None\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    fn assert_consistency(doc: &Document) {\n        assert_eq!(line_offsets_for_str(&doc.text), doc.line_offsets);\n    }\n\n    #[test]\n    fn test_positions() {\n        let doc = Document::new(1, \"Hello 💣\\nNext line\\n\".into());\n\n        assert_eq!(\n            lsp_types::Position {\n                line: 0,\n                character: 0\n            },\n            doc.offset_to_position(0)\n        );\n\n        assert_eq!(\n            lsp_types::Position {\n                line: 0,\n                character: 6\n            },\n            doc.offset_to_position(6)\n        );\n\n        assert_eq!(\n            lsp_types::Position {\n                line: 0,\n                character: 8\n            },\n            doc.offset_to_position(10)\n        );\n\n        assert_eq!(\n            lsp_types::Position {\n                line: 1,\n                character: 0\n            },\n            doc.offset_to_position(11)\n        );\n\n        assert_eq!(\n            lsp_types::Position {\n                line: 2,\n                character: 0\n            },\n            doc.offset_to_position(21)\n        );\n    }\n\n    #[test]\n    fn test_append_to_empty() {\n        let doc = Document::new(1, \"\".into())\n            .with_range_edit(\n                2,\n                lsp_types::Range {\n                    start: lsp_types::Position {\n                        line: 0,\n                        character: 0,\n                    },\n                    end: lsp_types::Position {\n                        line: 0,\n                        character: 7,\n                    },\n                },\n                \"abc-123\",\n            )\n            .unwrap();\n\n        assert_eq!(&doc.text, \"abc-123\");\n        assert_consistency(&doc);\n    }\n\n    #[test]\n    fn test_append_to_line() {\n        let doc = Document::new(1, \"Hello\".into())\n            .with_range_edit(\n                2,\n                lsp_types::Range {\n                    start: lsp_types::Position {\n                        line: 0,\n                        character: 5,\n                    },\n                    end: lsp_types::Position {\n                        line: 0,\n                        character: 5,\n                    },\n                },\n                \", world!\",\n            )\n            .unwrap();\n\n        assert_eq!(&doc.text, \"Hello, world!\");\n        assert_consistency(&doc);\n    }\n\n    #[test]\n    fn test_erase_all() {\n        let doc = Document::new(1, \"abc-123\".into())\n            .with_range_edit(\n                2,\n                lsp_types::Range {\n                    start: lsp_types::Position {\n                        line: 0,\n                        character: 0,\n                    },\n                    end: lsp_types::Position {\n                        line: 0,\n                        character: 7,\n                    },\n                },\n                \"\",\n            )\n            .unwrap();\n\n        assert_eq!(&doc.text, \"\");\n        assert_consistency(&doc);\n    }\n\n    #[test]\n    fn test_replace_line() {\n        let doc = Document::new(1, \"hello\\nnebraska\\n\".into())\n            .with_range_edit(\n                2,\n                lsp_types::Range {\n                    start: lsp_types::Position {\n                        line: 1,\n                        character: 0,\n                    },\n                    end: lsp_types::Position {\n                        line: 1,\n                        character: 8,\n                    },\n                },\n                \"world\",\n            )\n            .unwrap();\n\n        assert_eq!(&doc.text, \"hello\\nworld\\n\");\n        assert_consistency(&doc);\n    }\n\n    #[test]\n    fn test_insert_line() {\n        let doc = Document::new(1, \"hello\\nworld\\n\".into())\n            .with_range_edit(\n                2,\n                lsp_types::Range {\n                    start: lsp_types::Position {\n                        line: 1,\n                        character: 0,\n                    },\n                    end: lsp_types::Position {\n                        line: 1,\n                        character: 0,\n                    },\n                },\n                \"entire\\n\",\n            )\n            .unwrap();\n\n        assert_eq!(&doc.text, \"hello\\nentire\\nworld\\n\");\n        assert_consistency(&doc);\n    }\n\n    #[test]\n    fn test_delete_line() {\n        let doc = Document::new(1, \"hello\\nentire\\nworld\\n\".into())\n            .with_range_edit(\n                2,\n                lsp_types::Range {\n                    start: lsp_types::Position {\n                        line: 1,\n                        character: 0,\n                    },\n                    end: lsp_types::Position {\n                        line: 2,\n                        character: 0,\n                    },\n                },\n                \"\",\n            )\n            .unwrap();\n\n        assert_eq!(&doc.text, \"hello\\nworld\\n\");\n        assert_consistency(&doc);\n    }\n\n    #[test]\n    fn test_delete_utf16() {\n        let doc = Document::new(1, \"Defuse 💣 me\".into())\n            .with_range_edit(\n                2,\n                lsp_types::Range {\n                    start: lsp_types::Position {\n                        line: 0,\n                        character: 7,\n                    },\n                    end: lsp_types::Position {\n                        line: 0,\n                        character: 10,\n                    },\n                },\n                \"\",\n            )\n            .unwrap();\n\n        assert_eq!(&doc.text, \"Defuse me\");\n        assert_consistency(&doc);\n    }\n}\n"
  },
  {
    "path": "lsp-server/model/mod.rs",
    "content": "mod document;\nmod workspace;\n\npub use document::Document;\npub use workspace::Workspace;\n"
  },
  {
    "path": "lsp-server/model/workspace.rs",
    "content": "#[derive(Debug)]\npub struct Workspace {\n    _name: String,\n}\n\nimpl Workspace {\n    pub fn new(name: String) -> Workspace {\n        Workspace { _name: name }\n    }\n}\n"
  },
  {
    "path": "lsp-server/session.rs",
    "content": "use std::collections::HashMap;\nuse std::sync::Arc;\n\nuse tokio::sync::mpsc;\n\nuse crate::capabilities::server_capabilities;\nuse crate::handler;\nuse crate::json_rpc::{ClientMessage, ErrorCode, Response, ServerMessage};\nuse crate::model::{Document, Workspace};\nuse crate::transport::Connection;\nuse crate::watcher::SyntaxWatcher;\n\npub struct State {\n    pub documents: HashMap<String, Arc<Document>>,\n    pub workspaces: HashMap<String, Arc<Workspace>>,\n    pub syntax_watcher: SyntaxWatcher,\n}\n\nimpl State {\n    fn new(\n        outgoing: mpsc::Sender<ServerMessage>,\n        initialize_params: lsp_types::InitializeParams,\n    ) -> State {\n        let initial_workspaces = initialize_params\n            .workspace_folders\n            .map(|workspace_folders| {\n                workspace_folders\n                    .into_iter()\n                    .map(|workspace_folder| {\n                        (\n                            workspace_folder.uri.to_string(),\n                            Arc::new(Workspace::new(workspace_folder.name)),\n                        )\n                    })\n                    .collect()\n            })\n            .unwrap_or_else(HashMap::new);\n\n        State {\n            documents: HashMap::new(),\n            workspaces: initial_workspaces,\n            syntax_watcher: SyntaxWatcher::new(outgoing),\n        }\n    }\n\n    async fn shutdown(self) {\n        self.syntax_watcher.shutdown().await;\n    }\n}\n\npub fn create_initialize_response() -> lsp_types::InitializeResult {\n    lsp_types::InitializeResult {\n        server_info: Some(lsp_types::ServerInfo {\n            name: \"arret-lsp-server\".to_owned(),\n            version: option_env!(\"CARGO_PKG_VERSION\").map(str::to_owned),\n        }),\n        capabilities: server_capabilities(),\n    }\n}\n\n/// Runs a session loop against the provided connection\n///\n/// On a clean exit (`shutdown` followed by `exit`) this will return `Ok`, otherwise it will return\n/// `Err`.\npub async fn run(connection: Connection) -> Result<(), ()> {\n    let Connection {\n        mut incoming,\n        outgoing,\n    } = connection;\n\n    /// Receives an incoming message or returns `Err` if the receive channel is closed\n    ///\n    /// This will cause us to exit uncleanly if our connection closes unexpectedly.\n    macro_rules! recv_or_return_err {\n        () => {\n            match incoming.recv().await {\n                Some(incoming_message) => incoming_message,\n                None => {\n                    eprintln!(\"Connection unexpectedly closed\");\n                    return Err(());\n                }\n            }\n        };\n    }\n\n    /// Sends the outgoing message or returns `Err` if the send channel is closed\n    macro_rules! send_or_return_err {\n        ($outgoing_message:expr) => {\n            if outgoing.send($outgoing_message.into()).await.is_err() {\n                eprintln!(\"Connection unexpectedly closed\");\n                return Err(());\n            }\n        };\n    }\n\n    // Wait for initialize\n    let initialize_request = loop {\n        match recv_or_return_err!() {\n            ClientMessage::Notification(notification) => {\n                if notification.method == \"exit\" {\n                    // Unclean exit\n                    return Err(());\n                }\n            }\n            ClientMessage::Request(request) if request.method.as_str() == \"initialize\" => {\n                break request;\n            }\n            ClientMessage::Request(request) => {\n                send_or_return_err!(Response::new_err(\n                    request.id,\n                    ErrorCode::ServerNotInitialized,\n                    \"Server not initialized\"\n                ));\n            }\n        }\n    };\n\n    let params: lsp_types::InitializeParams = serde_json::from_value(initialize_request.params)\n        .expect(\"Could not parse initialize request params\");\n\n    let mut state = State::new(outgoing.clone(), params);\n\n    let initialize_response = create_initialize_response();\n    send_or_return_err!(Response::new_ok(\n        initialize_request.id.clone(),\n        initialize_response\n    ));\n\n    // Process normal messages until we receive a shutdown request\n    loop {\n        match recv_or_return_err!() {\n            ClientMessage::Notification(notification) if notification.method == \"initialized\" => {\n                // Nothing do to\n            }\n            ClientMessage::Notification(notification) if notification.method == \"exit\" => {\n                // Tear down our state or we'll likely to panic if there are concurrent operations\n                state.shutdown().await;\n\n                // Unclean exit\n                return Err(());\n            }\n            ClientMessage::Notification(notification) => {\n                handler::handle_non_lifecycle_notification(&mut state, notification);\n            }\n            ClientMessage::Request(request) if request.method == \"shutdown\" => {\n                send_or_return_err!(Response::new_ok(request.id, ()));\n                break;\n            }\n            ClientMessage::Request(request) => {\n                send_or_return_err!(handler::handle_non_lifecycle_request(&mut state, request));\n            }\n        }\n    }\n\n    // Cleanly shutdown our state\n    state.shutdown().await;\n\n    // Wait for exit\n    loop {\n        match recv_or_return_err!() {\n            ClientMessage::Notification(notification) => {\n                if notification.method == \"exit\" {\n                    return Ok(());\n                }\n            }\n            ClientMessage::Request(request) => {\n                send_or_return_err!(Response::new_err(\n                    request.id,\n                    ErrorCode::InvalidRequest,\n                    \"Shutting down\"\n                ));\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use std::future::Future;\n\n    use tokio::sync::mpsc;\n\n    use crate::json_rpc::{Notification, Request, RequestId, ServerMessage};\n\n    struct TestSession<F>\n    where\n        F: Future<Output = Result<(), ()>>,\n    {\n        outgoing: mpsc::Receiver<ServerMessage>,\n        incoming: mpsc::Sender<ClientMessage>,\n        exit_future: F,\n    }\n\n    fn run_test_session() -> TestSession<impl Future<Output = Result<(), ()>>> {\n        let (send_outgoing, recv_outgoing) = mpsc::channel::<ServerMessage>(4);\n        let (send_incoming, recv_incoming) = mpsc::channel::<ClientMessage>(4);\n\n        let session = run(Connection {\n            outgoing: send_outgoing,\n            incoming: recv_incoming,\n        });\n\n        TestSession {\n            outgoing: recv_outgoing,\n            incoming: send_incoming,\n            exit_future: session,\n        }\n    }\n\n    fn expect_response(server_message: ServerMessage) -> Response {\n        if let ServerMessage::Response(response) = server_message {\n            response\n        } else {\n            panic!(\"Expected response, got {:?}\", server_message);\n        }\n    }\n\n    #[allow(deprecated)]\n    #[tokio::test]\n    async fn test_clean_lifecycle() {\n        let TestSession {\n            mut outgoing,\n            incoming,\n            exit_future,\n        } = run_test_session();\n\n        tokio::spawn(async move {\n            // We should return an error for messages before initialization\n            incoming\n                .send(Request::new_lsp::<lsp_types::request::Shutdown>(123.into(), ()).into())\n                .await\n                .unwrap();\n\n            let response = expect_response(outgoing.recv().await.unwrap());\n\n            assert_eq!(\n                Response::new_err(\n                    123.into(),\n                    ErrorCode::ServerNotInitialized,\n                    \"Server not initialized\"\n                ),\n                response,\n            );\n\n            // Now initialize\n            let initialize_params = lsp_types::InitializeParams {\n                process_id: None,\n                root_path: None,\n                root_uri: None,\n                initialization_options: None,\n                capabilities: Default::default(),\n                trace: None,\n                workspace_folders: None,\n                client_info: None,\n            };\n\n            incoming\n                .send(\n                    Request::new_lsp::<lsp_types::request::Initialize>(\n                        \"123\".to_owned().into(),\n                        initialize_params,\n                    )\n                    .into(),\n                )\n                .await\n                .unwrap();\n\n            let response = expect_response(outgoing.recv().await.unwrap());\n\n            // Don't assert the exact body\n            assert_eq!(response.id, RequestId::from(\"123\".to_owned()));\n            assert!(response.error.is_none());\n\n            // Send initialized notification\n            incoming\n                .send(\n                    Notification::new_lsp::<lsp_types::notification::Initialized>(\n                        lsp_types::InitializedParams {},\n                    )\n                    .into(),\n                )\n                .await\n                .unwrap();\n\n            // Now shutdown for real\n            incoming\n                .send(Request::new_lsp::<lsp_types::request::Shutdown>(456.into(), ()).into())\n                .await\n                .unwrap();\n\n            let response = expect_response(outgoing.recv().await.unwrap());\n\n            assert_eq!(Response::new_ok(456.into(), ()), response,);\n\n            // We should return an error on duplicate shutdown\n            incoming\n                .send(\n                    Request::new_lsp::<lsp_types::request::Shutdown>(\"456\".to_owned().into(), ())\n                        .into(),\n                )\n                .await\n                .unwrap();\n\n            let response = expect_response(outgoing.recv().await.unwrap());\n\n            assert_eq!(\n                Response::new_err(\n                    \"456\".to_owned().into(),\n                    ErrorCode::InvalidRequest,\n                    \"Shutting down\"\n                ),\n                response,\n            );\n\n            // And send exit notification\n            incoming\n                .send(Notification::new_lsp::<lsp_types::notification::Exit>(()).into())\n                .await\n                .unwrap();\n        });\n\n        // This should be considered a clean exit\n        assert!(exit_future.await.is_ok());\n    }\n}\n"
  },
  {
    "path": "lsp-server/transport/bytestream.rs",
    "content": "use tokio::io;\nuse tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt};\nuse tokio::sync::mpsc;\n\nuse crate::json_rpc::{ClientMessage, ServerMessage};\nuse crate::transport::Connection;\n\nfn parse_header_line(header_line: &str) -> (String, String) {\n    let mut parts = header_line.splitn(2, ':');\n\n    let name = parts\n        .next()\n        .expect(\"Did not find header name\")\n        .trim()\n        .to_ascii_lowercase();\n\n    let value = parts\n        .next()\n        .expect(\"Did not find header value\")\n        .trim()\n        .to_owned();\n\n    (name, value)\n}\n\n/// Waits for the passed I/O future and `break`s from the current loop if the pipe is broken\n///\n/// This is useful to propagate closing `stdout`/`stdin` by closing the respective MPSC channel.\nmacro_rules! break_on_broken_pipe {\n    ($io_future:expr, $message:expr) => {\n        if let Err(err) = $io_future.await {\n            if err.kind() == io::ErrorKind::BrokenPipe {\n                break;\n            } else {\n                panic!(\"{}: {:?}\", $message, err);\n            }\n        }\n    };\n}\n\npub fn create_connection(\n    mut reader: impl io::AsyncBufRead + Unpin + Send + 'static,\n    mut writer: impl io::AsyncWrite + Unpin + Send + 'static,\n) -> Connection {\n    // Allow some concurrency with the session but 4 message is a bit excessive\n    // This allows for backpressure on `stdin`/`stdout`\n    let (send_outgoing, mut recv_outgoing) = mpsc::channel::<ServerMessage>(4);\n    let (send_incoming, recv_incoming) = mpsc::channel::<ClientMessage>(4);\n\n    // Write all our responses out sequentially\n    tokio::spawn(async move {\n        while let Some(response) = recv_outgoing.recv().await {\n            let response_bytes =\n                serde_json::to_vec(&response).expect(\"Could not serialise response\");\n\n            break_on_broken_pipe!(\n                writer.write_all(\n                    format!(\"Content-Length: {}\\r\\n\\r\\n\", response_bytes.len()).as_bytes()\n                ),\n                \"Could not write response header\"\n            );\n\n            break_on_broken_pipe!(\n                writer.write_all(&response_bytes),\n                \"Could not write response body\"\n            );\n\n            break_on_broken_pipe!(writer.flush(), \"Could not flush writer\");\n        }\n    });\n\n    tokio::spawn(async move {\n        loop {\n            let mut content_length: Option<usize> = None;\n            let mut line_buffer = String::new();\n\n            // Read the header\n            loop {\n                break_on_broken_pipe!(\n                    reader.read_line(&mut line_buffer),\n                    \"Could not read header line from stdin\"\n                );\n\n                if line_buffer == \"\\r\\n\" {\n                    // Read full header\n                    break;\n                }\n\n                let (name, value) = parse_header_line(&line_buffer);\n                if name == \"content-length\" {\n                    content_length = Some(value.parse().expect(\"Cannot parse Content-Length\"));\n                }\n\n                line_buffer.clear();\n            }\n\n            let content_length = content_length.expect(\"Header had no Content-Length\");\n\n            // Read the entire content\n            let mut read_buffer = Vec::<u8>::new();\n            read_buffer.resize(content_length, 0);\n\n            break_on_broken_pipe!(\n                reader.read_exact(&mut read_buffer),\n                \"Could not read body from stdin\"\n            );\n\n            let client_message: ClientMessage =\n                serde_json::from_slice(&read_buffer).expect(\"Invalid JSON\");\n\n            if send_incoming.send(client_message).await.is_err() {\n                // Channel closed\n                break;\n            }\n        }\n    });\n\n    Connection {\n        incoming: recv_incoming,\n        outgoing: send_outgoing,\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use crate::json_rpc::Notification;\n\n    #[tokio::test]\n    async fn test_happy_recv_notification() {\n        let body = br#\"{\"jsonrpc\":\"2.0\",\"method\":\"initialized\",\"params\":{}}\"#;\n\n        let mut message = format!(\"Content-Length: {}\\r\\n\", body.len()).into_bytes();\n        message.extend_from_slice(b\"\\r\\n\");\n        message.extend_from_slice(body);\n\n        let Connection { mut incoming, .. } = create_connection(\n            io::BufReader::new(std::io::Cursor::new(message)),\n            Vec::new(),\n        );\n\n        let client_message = incoming.recv().await.unwrap();\n        assert_eq!(\n            ClientMessage::Notification(Notification::new_lsp::<\n                lsp_types::notification::Initialized,\n            >(lsp_types::InitializedParams {})),\n            client_message,\n        );\n    }\n}\n"
  },
  {
    "path": "lsp-server/transport/mod.rs",
    "content": "pub mod bytestream;\n\nuse tokio::sync::mpsc;\n\nuse crate::json_rpc::{ClientMessage, ServerMessage};\n\npub struct Connection {\n    /// Channel producing incoming JSON-RPC messages\n    pub incoming: mpsc::Receiver<ClientMessage>,\n\n    /// Channel accepting outgoing JSON-RPC messages\n    pub outgoing: mpsc::Sender<ServerMessage>,\n}\n"
  },
  {
    "path": "lsp-server/watcher/mod.rs",
    "content": "mod syntax;\npub use syntax::*;\n\nuse std::sync::Arc;\n\nuse crate::model::Document;\n\n/// Trait for a loosely coupled component that watches document events\npub trait DocumentWatcher {\n    /// Called when a document is opened with the specified initial contents\n    fn did_open(&mut self, _url: &lsp_types::Url, _document: &Arc<Document>) {}\n\n    /// Called when a document has changed with the updated contents\n    fn did_change(&mut self, _url: &lsp_types::Url, _document: &Arc<Document>) {}\n\n    /// Called when a document has closed\n    fn did_close(&mut self, _url: &lsp_types::Url) {}\n}\n"
  },
  {
    "path": "lsp-server/watcher/syntax.rs",
    "content": "use std::collections::HashMap;\nuse std::sync::Arc;\n\nuse tokio::sync::{mpsc, watch};\nuse tokio::task;\n\nuse arret_syntax::parser::data_from_str;\n\nuse crate::json_rpc::{Notification, ServerMessage};\nuse crate::model::Document;\nuse crate::watcher::DocumentWatcher;\n\nfn syntax_diagnostics_for_document(\n    url: &lsp_types::Url,\n    document: &Document,\n) -> Vec<lsp_types::Diagnostic> {\n    match data_from_str(None, document.text()) {\n        Ok(_) => vec![],\n        Err(error) => {\n            let within = error.kind().within_context();\n\n            let mut related_information = vec![];\n\n            if let Some(within) = within {\n                if let Some(open_char_span) = within.open_char_span() {\n                    related_information.push(lsp_types::DiagnosticRelatedInformation {\n                        location: lsp_types::Location {\n                            uri: url.clone(),\n                            range: document.span_to_range(open_char_span),\n                        },\n                        message: format!(\"{} starts here\", within.description()),\n                    });\n                }\n\n                if let Some(expected_next) = within.expected_next() {\n                    related_information.push(lsp_types::DiagnosticRelatedInformation {\n                        location: lsp_types::Location {\n                            uri: url.clone(),\n                            range: document.span_to_range(error.span()),\n                        },\n                        message: expected_next.description(),\n                    });\n                }\n            }\n\n            vec![lsp_types::Diagnostic {\n                range: document.span_to_range(error.span()),\n                severity: Some(lsp_types::DiagnosticSeverity::Error),\n                message: error.kind().message(),\n                related_information: Some(related_information),\n                source: Some(\"arret-syntax\".to_owned()),\n                ..Default::default()\n            }]\n        }\n    }\n}\n\nstruct DocumentTask {\n    send_change: watch::Sender<Arc<Document>>,\n    join_handle: task::JoinHandle<()>,\n}\n\nimpl DocumentTask {\n    pub fn new(\n        outgoing: mpsc::Sender<ServerMessage>,\n        url: lsp_types::Url,\n        initial_document: Arc<Document>,\n    ) -> DocumentTask {\n        let (send_change, mut receive_change) = watch::channel(initial_document);\n\n        let join_handle = tokio::spawn(async move {\n            loop {\n                let document = receive_change.borrow().clone();\n                let diagnostics = syntax_diagnostics_for_document(&url, &document);\n\n                if outgoing\n                    .send(\n                        Notification::new_lsp::<lsp_types::notification::PublishDiagnostics>(\n                            lsp_types::PublishDiagnosticsParams {\n                                uri: url.clone(),\n                                version: Some(document.version()),\n                                diagnostics,\n                            },\n                        )\n                        .into(),\n                    )\n                    .await\n                    .is_err()\n                {\n                    break;\n                }\n\n                if receive_change.changed().await.is_err() {\n                    break;\n                }\n            }\n        });\n\n        DocumentTask {\n            send_change,\n            join_handle,\n        }\n    }\n\n    fn did_change(&self, document: Arc<Document>) {\n        self.send_change\n            .send(document)\n            .expect(\"Could not send change to document syntax task\");\n    }\n\n    async fn shutdown(self) {\n        drop(self.send_change);\n        self.join_handle\n            .await\n            .expect(\"Document syntax task panicked\");\n    }\n}\n\npub struct SyntaxWatcher {\n    outgoing: mpsc::Sender<ServerMessage>,\n    document_tasks: HashMap<String, DocumentTask>,\n}\n\nimpl SyntaxWatcher {\n    pub fn new(outgoing: mpsc::Sender<ServerMessage>) -> SyntaxWatcher {\n        SyntaxWatcher {\n            outgoing,\n            document_tasks: HashMap::new(),\n        }\n    }\n\n    pub async fn shutdown(self) {\n        let document_task_futures: Vec<_> = self\n            .document_tasks\n            .into_iter()\n            .map(|(_, task)| task.shutdown())\n            .collect();\n\n        for document_task_future in document_task_futures {\n            document_task_future.await;\n        }\n    }\n}\n\nimpl DocumentWatcher for SyntaxWatcher {\n    fn did_open(&mut self, url: &lsp_types::Url, document: &Arc<Document>) {\n        self.document_tasks.insert(\n            url.to_string(),\n            DocumentTask::new(self.outgoing.clone(), url.clone(), Arc::clone(document)),\n        );\n    }\n\n    fn did_change(&mut self, url: &lsp_types::Url, document: &Arc<Document>) {\n        if let Some(document_task) = self.document_tasks.get(url.as_str()) {\n            document_task.did_change(Arc::clone(document));\n        }\n    }\n\n    fn did_close(&mut self, url: &lsp_types::Url) {\n        self.document_tasks.remove(url.as_str());\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn correct_document_diagnostics() {\n        let url = lsp_types::Url::parse(\"file:///foo/bar\").unwrap();\n        let doc = Document::new(1, \"('hello-world)\".to_owned());\n\n        let diags = syntax_diagnostics_for_document(&url, &doc);\n\n        assert_eq!(Vec::<lsp_types::Diagnostic>::new(), diags);\n    }\n\n    #[test]\n    fn missing_delimiter_diagnostics() {\n        let url = lsp_types::Url::parse(\"file:///foo/bar\").unwrap();\n        let doc = Document::new(1, \"('hello-world\".to_owned());\n\n        let diags = syntax_diagnostics_for_document(&url, &doc);\n\n        assert_eq!(\n            vec![lsp_types::Diagnostic {\n                range: lsp_types::Range {\n                    start: lsp_types::Position {\n                        line: 0,\n                        character: 13,\n                    },\n                    end: lsp_types::Position {\n                        line: 0,\n                        character: 13,\n                    }\n                },\n                severity: Some(lsp_types::DiagnosticSeverity::Error),\n                message: \"unexpected end of file while parsing list\".into(),\n                related_information: Some(vec![\n                    lsp_types::DiagnosticRelatedInformation {\n                        location: lsp_types::Location {\n                            uri: url.clone(),\n                            range: lsp_types::Range {\n                                start: lsp_types::Position {\n                                    line: 0,\n                                    character: 0,\n                                },\n                                end: lsp_types::Position {\n                                    line: 0,\n                                    character: 1,\n                                }\n                            },\n                        },\n                        message: \"list starts here\".to_owned(),\n                    },\n                    lsp_types::DiagnosticRelatedInformation {\n                        location: lsp_types::Location {\n                            uri: url,\n                            range: lsp_types::Range {\n                                start: lsp_types::Position {\n                                    line: 0,\n                                    character: 13,\n                                },\n                                end: lsp_types::Position {\n                                    line: 0,\n                                    character: 13,\n                                }\n                            },\n                        },\n                        message: \"expected datum or `)`\".to_owned(),\n                    }\n                ]),\n                source: Some(\"arret-syntax\".to_owned()),\n                ..Default::default()\n            }],\n            diags\n        );\n    }\n\n    #[test]\n    fn unsupported_character_diagnostics() {\n        let url = lsp_types::Url::parse(\"file:///foo/bar\").unwrap();\n        let doc = Document::new(1, \"\\\\newline \\\\madeup\".to_owned());\n\n        let diags = syntax_diagnostics_for_document(&url, &doc);\n\n        assert_eq!(\n            vec![lsp_types::Diagnostic {\n                range: lsp_types::Range {\n                    start: lsp_types::Position {\n                        line: 0,\n                        character: 10,\n                    },\n                    end: lsp_types::Position {\n                        line: 0,\n                        character: 16,\n                    }\n                },\n                severity: Some(lsp_types::DiagnosticSeverity::Error),\n                message: \"unsupported character\".into(),\n                related_information: Some(vec![]),\n                source: Some(\"arret-syntax\".to_owned()),\n                ..Default::default()\n            }],\n            diags\n        );\n    }\n}\n"
  },
  {
    "path": "rfi-derive/Cargo.toml",
    "content": "[package]\nname = \"arret-rfi-derive\"\nversion = \"0.1.0\"\nedition = \"2018\"\nauthors = [\"Ryan Cumming <etaoins@gmail.com>\"]\n\n[lib]\npath = \"lib.rs\"\nproc-macro = true\n\n[dependencies]\nquote = \"1\"\nproc-macro2 = \"1\"\n\n[dependencies.syn]\nversion = \"1\"\nfeatures = [\"full\"]"
  },
  {
    "path": "rfi-derive/lib.rs",
    "content": "#![warn(clippy::all)]\n#![warn(rust_2018_idioms)]\n\n#[macro_use]\nextern crate quote;\n\nuse syn::{parse_macro_input, ItemFn, Token};\n\nfn arg_is_task(arg: &syn::PatType) -> bool {\n    if let syn::Type::Reference(_) = *arg.ty {\n    } else {\n        return false;\n    };\n\n    if let syn::Pat::Ident(ref pat_ident) = *arg.pat {\n        pat_ident.ident == \"task\"\n    } else {\n        false\n    }\n}\n\n/// Annotates a Rust function to be exported via `arret_runtime::define_rust_module!`\n///\n/// This takes a single metadata string containing the full Arret type of the function. This is used\n/// to express concepts in Arret that don't exist in Rust. These include rest arguments and function\n/// purity.\n///\n/// The annotated Rust function can optionally take a `arret_runtime::task::Task` as its first\n/// parameter. An attempt will be made to encode the types of the remaining parameters but only\n/// certain primitive types and `arret_runtime::boxed` values are allowed.\n#[proc_macro_attribute]\npub fn rust_fun(\n    attrs: proc_macro::TokenStream,\n    input: proc_macro::TokenStream,\n) -> proc_macro::TokenStream {\n    let attrs: proc_macro2::TokenStream = attrs.into();\n\n    let mut attrs_iter = attrs.into_iter();\n    let arret_type = attrs_iter.next().expect(\"Arret type expected\");\n\n    if attrs_iter.next().is_some() {\n        panic!(\"unexpected tokens after Arret type\");\n    }\n\n    // Parse the input tokens into a syntax tree\n    let mut input_fn = parse_macro_input!(input as ItemFn);\n    let mut input_sig = &mut input_fn.sig;\n    let vis = input_fn.vis.clone();\n\n    // Rename the function so the descriptor can take its original name\n    let entry_point_name = format!(\"arret_{}_entry_point\", input_sig.ident);\n    let entry_point_ident = proc_macro2::Ident::new(&entry_point_name, input_sig.ident.span());\n    let descriptor_ident = std::mem::replace(&mut input_sig.ident, entry_point_ident);\n\n    // RFI assumes a C ABI\n    input_sig.abi = Some(syn::Abi {\n        extern_token: Token![extern](input_sig.ident.span()),\n        name: Some(syn::LitStr::new(\"C\", input_sig.ident.span())),\n    });\n\n    let takes_task = input_sig\n        .inputs\n        .first()\n        .map(|arg| match arg {\n            syn::FnArg::Typed(typed) => arg_is_task(typed),\n            _ => false,\n        })\n        .unwrap_or(false);\n\n    let mut param_iter = input_sig.inputs.iter();\n    if takes_task {\n        param_iter.next();\n    }\n\n    let param_types = param_iter.map(|arg| match arg {\n        syn::FnArg::Typed(typed) => typed.ty.clone(),\n        _ => panic!(\"unexpected arg type\"),\n    });\n\n    let ret_type = match input_sig.output {\n        syn::ReturnType::Default => quote!(()),\n        syn::ReturnType::Type(_, ref ret_type) => quote!(#ret_type),\n    };\n\n    // Build the output, possibly using quasi-quotation\n    let expanded = quote! {\n        #[allow(non_upper_case_globals)]\n        #vis const #descriptor_ident: RustFun = RustFun {\n            arret_type: #arret_type,\n            takes_task: #takes_task,\n            params: &[#(\n                <#param_types as ::arret_runtime::abitype::EncodeAbiType>::PARAM_ABI_TYPE\n            ),*],\n            ret: <#ret_type as ::arret_runtime::abitype::EncodeRetAbiType>::RET_ABI_TYPE,\n            symbol: #entry_point_name,\n        };\n\n        #[no_mangle]\n        #input_fn\n    };\n\n    expanded.into()\n}\n"
  },
  {
    "path": "runtime/Cargo.toml",
    "content": "[package]\nname = \"arret-runtime\"\nversion = \"0.1.0\"\nedition = \"2018\"\nauthors = [\"Ryan Cumming <etaoins@gmail.com>\"]\n\n[lib]\npath = \"lib.rs\"\ncrate-type = [\"lib\"]\n"
  },
  {
    "path": "runtime/abitype.rs",
    "content": "//! Type encoding for Rust types\n//!\n//! This is a system of traits used to encode Rust types in a form understandable by the Arret\n//! compiler. It's used to ensure type safety across the RFI boundary.\n\nuse crate::binding::Never;\nuse crate::boxed;\nuse crate::boxed::refs;\nuse crate::callback;\n\n#[derive(Debug, PartialEq, Eq, Hash, Clone)]\npub enum BoxedAbiType {\n    Any,\n    UniqueTagged(boxed::TypeTag),\n    Union(&'static str, &'static [boxed::TypeTag]),\n    Vector(&'static BoxedAbiType),\n    List(&'static BoxedAbiType),\n    Pair(&'static BoxedAbiType),\n    Set(&'static BoxedAbiType),\n    Map(&'static BoxedAbiType, &'static BoxedAbiType),\n}\n\npub const TOP_LIST_BOXED_ABI_TYPE: BoxedAbiType = BoxedAbiType::List(&BoxedAbiType::Any);\n\n/// Encoded type for any boxed or unboxed value\n#[derive(Debug, PartialEq, Eq, Hash, Clone)]\npub enum AbiType {\n    /// Unboxed boolean value\n    ///\n    /// This is identical to [`bool`] in Rust and C++\n    Bool,\n\n    /// Unboxed character value\n    ///\n    /// This is identical to [`char`] in Rust and `wchar_t` in C++\n    Char,\n\n    /// Unboxed 64bit float\n    ///\n    /// This is identical to [`f64`] in Rust and `double` in C++\n    Float,\n\n    /// Unboxed signed 64bit integer\n    ///\n    /// This is identical to in [`i64`] in Rust and `std::int64_t` in C++\n    Int,\n\n    /// Interned integer for a `Sym`\n    ///\n    /// While this corresponds to [`InternedSym`](crate::intern::InternedSym) it's currently only\n    /// used internally by the compiler.\n    InternedSym,\n\n    /// [Boxed value](crate::boxed)\n    Boxed(BoxedAbiType),\n\n    /// [Callback function](crate::callback)\n    Callback(&'static callback::EntryPointAbiType),\n}\n\n#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]\npub enum ParamCapture {\n    /// Indicates the capture of this parameter should be automatically inferred\n    Auto,\n    /// Explicitly specifies that this parameter is never captured\n    Never,\n    /// Explicitly specifies that this parameter is captured\n    Always,\n}\n\n#[derive(Debug, PartialEq, Eq, Hash, Clone)]\npub struct ParamAbiType {\n    pub abi_type: AbiType,\n    pub capture: ParamCapture,\n}\n\n#[derive(Debug, PartialEq, Eq, Hash, Clone)]\npub enum RetAbiType {\n    Void,\n    Never,\n    Inhabited(AbiType),\n}\n\npub trait EncodeAbiType {\n    const ABI_TYPE: AbiType;\n    /// Controls the capture type when this is used as a parameter\n    const PARAM_CAPTURE: ParamCapture = ParamCapture::Never;\n\n    const PARAM_ABI_TYPE: ParamAbiType = ParamAbiType {\n        abi_type: Self::ABI_TYPE,\n        capture: Self::PARAM_CAPTURE,\n    };\n}\n\nimpl EncodeAbiType for f64 {\n    const ABI_TYPE: AbiType = AbiType::Float;\n}\n\nimpl EncodeAbiType for i64 {\n    const ABI_TYPE: AbiType = AbiType::Int;\n}\n\nimpl EncodeAbiType for char {\n    const ABI_TYPE: AbiType = AbiType::Char;\n}\n\nimpl EncodeAbiType for bool {\n    const ABI_TYPE: AbiType = AbiType::Bool;\n}\n\nimpl<T: boxed::Boxed> EncodeAbiType for refs::Gc<T>\nwhere\n    T: EncodeBoxedAbiType,\n{\n    const ABI_TYPE: AbiType = AbiType::Boxed(T::BOXED_ABI_TYPE);\n    const PARAM_CAPTURE: ParamCapture = ParamCapture::Auto;\n}\n\nimpl<T: boxed::Boxed> EncodeAbiType for refs::NoCapture<T>\nwhere\n    T: EncodeBoxedAbiType,\n{\n    const ABI_TYPE: AbiType = AbiType::Boxed(T::BOXED_ABI_TYPE);\n    const PARAM_CAPTURE: ParamCapture = ParamCapture::Never;\n}\n\nimpl<T: boxed::Boxed> EncodeAbiType for refs::Capture<T>\nwhere\n    T: EncodeBoxedAbiType,\n{\n    const ABI_TYPE: AbiType = AbiType::Boxed(T::BOXED_ABI_TYPE);\n    const PARAM_CAPTURE: ParamCapture = ParamCapture::Always;\n}\n\nimpl<F> EncodeAbiType for callback::Callback<F>\nwhere\n    F: callback::EncodeEntryPointAbiType,\n{\n    const ABI_TYPE: AbiType = AbiType::Callback(&F::ENTRY_POINT_ABI_TYPE);\n}\n\npub trait EncodeBoxedAbiType {\n    const BOXED_ABI_TYPE: BoxedAbiType;\n}\n\npub trait EncodeRetAbiType {\n    const RET_ABI_TYPE: RetAbiType;\n}\n\nimpl<T: EncodeAbiType> EncodeRetAbiType for T {\n    const RET_ABI_TYPE: RetAbiType = RetAbiType::Inhabited(Self::ABI_TYPE);\n}\n\nimpl EncodeRetAbiType for () {\n    const RET_ABI_TYPE: RetAbiType = RetAbiType::Void;\n}\n\nimpl EncodeRetAbiType for Never {\n    const RET_ABI_TYPE: RetAbiType = RetAbiType::Never;\n}\n\nimpl From<boxed::TypeTag> for BoxedAbiType {\n    fn from(type_tag: boxed::TypeTag) -> BoxedAbiType {\n        type_tag.to_boxed_abi_type()\n    }\n}\n\nimpl BoxedAbiType {\n    pub fn into_abi_type(self) -> AbiType {\n        AbiType::Boxed(self)\n    }\n}\n\nimpl From<boxed::TypeTag> for AbiType {\n    fn from(type_tag: boxed::TypeTag) -> AbiType {\n        type_tag.to_boxed_abi_type().into_abi_type()\n    }\n}\n\nimpl From<BoxedAbiType> for AbiType {\n    fn from(boxed_abi_type: BoxedAbiType) -> AbiType {\n        boxed_abi_type.into_abi_type()\n    }\n}\n\nimpl AbiType {\n    pub fn into_ret_abi_type(self) -> RetAbiType {\n        RetAbiType::Inhabited(self)\n    }\n\n    pub fn into_param_abi_type(self) -> ParamAbiType {\n        let capture = match self {\n            AbiType::Boxed(_) => ParamCapture::Auto,\n            _ => ParamCapture::Never,\n        };\n\n        ParamAbiType {\n            abi_type: self,\n            capture,\n        }\n    }\n\n    pub fn may_contain_gc_refs(&self) -> bool {\n        matches!(\n            self,\n            AbiType::Boxed(_) | AbiType::InternedSym | AbiType::Callback(_)\n        )\n    }\n}\n\nimpl From<boxed::TypeTag> for ParamAbiType {\n    fn from(type_tag: boxed::TypeTag) -> ParamAbiType {\n        type_tag\n            .to_boxed_abi_type()\n            .into_abi_type()\n            .into_param_abi_type()\n    }\n}\n\nimpl From<BoxedAbiType> for ParamAbiType {\n    fn from(boxed_abi_type: BoxedAbiType) -> ParamAbiType {\n        boxed_abi_type.into_abi_type().into_param_abi_type()\n    }\n}\n\nimpl From<AbiType> for ParamAbiType {\n    fn from(abi_type: AbiType) -> ParamAbiType {\n        abi_type.into_param_abi_type()\n    }\n}\n\nimpl From<boxed::TypeTag> for RetAbiType {\n    fn from(type_tag: boxed::TypeTag) -> RetAbiType {\n        type_tag\n            .to_boxed_abi_type()\n            .into_abi_type()\n            .into_ret_abi_type()\n    }\n}\nimpl From<BoxedAbiType> for RetAbiType {\n    fn from(boxed_abi_type: BoxedAbiType) -> RetAbiType {\n        boxed_abi_type.into_abi_type().into_ret_abi_type()\n    }\n}\n\nimpl From<AbiType> for RetAbiType {\n    fn from(abi_type: AbiType) -> RetAbiType {\n        abi_type.into_ret_abi_type()\n    }\n}\n"
  },
  {
    "path": "runtime/binding.rs",
    "content": "//! Macros and types for defining Rust RFI modules\n\nuse crate::abitype::{ParamAbiType, RetAbiType};\n\n#[allow(unused)]\nuse crate::abitype::{EncodeAbiType, EncodeRetAbiType};\n\n#[derive(Debug)]\npub struct RustFun {\n    pub arret_type: &'static str,\n    pub takes_task: bool,\n    pub params: &'static [ParamAbiType],\n    pub ret: RetAbiType,\n    pub symbol: &'static str,\n}\n\npub type RustExports = &'static [(&'static str, &'static RustFun)];\n\n// TODO: Replace with ! once it's stable\npub enum Never {}\n\n/// Defines a new Arret module implemented at Rust\n///\n/// Each Arret package can have an optional Rust module accessible as `(import [package-name\n/// rust])`. These are loaded both at compile-time to support constant evaluation and linked against\n/// compiled programs.\n///\n/// The first argument should be an identifier in the form of `ARRET_{PACKAGE_NAME}_RUST_EXPORTS`\n/// where `{PACKAGE_NAME}` is the uppercased name of the package. For example, the package `stdlib`\n/// uses `ARRET_STDLIB_RUST_EXPORTS`. This must be unique to prevent symbol conflicts when loading\n/// Rust modules.\n///\n/// The second argument is a mapping of export names to Rust functions. These are defined using\n/// the `rfi_derive::rust_fun` attribute macro.\n#[macro_export]\nmacro_rules! define_rust_module {\n    ($exports_sym:ident, { $( $export_name:expr => $desc_name:ident ),* }) => {\n        #[no_mangle]\n        pub static $exports_sym: RustExports = &[\n            $(\n                ($export_name, &$desc_name)\n            ),*\n        ];\n    };\n}\n"
  },
  {
    "path": "runtime/boxed/heap/collect.rs",
    "content": "//! Functionality for garbage collecting heaps\n//!\n//! This is a basic tracing, moving garbage collector. It doesn't support generations or concurrent\n//! collection. Every collection starts with a strong pass followed by an optional weak pass.\n\nuse std::ptr;\n\nuse crate::boxed;\nuse crate::boxed::heap::Heap;\nuse crate::boxed::refs::Gc;\nuse crate::boxed::{AllocType, BoxSize, Boxed, TypeTag};\nuse crate::intern::InternedSym;\n\n#[repr(C, align(16))]\nstruct ForwardingCell {\n    header: boxed::Header,\n    new_location: Gc<boxed::Any>,\n}\n\n/// Strong pass from an old [`Heap`] in to a new [`Heap`]\n///\n/// [`visit_box`](StrongPass::visit_box) should be called for each GC root that needs to be moved to\n/// the new heap. Once all roots have been visited [`into_new_heap`](StrongPass::into_new_heap) will\n/// return the new [`Heap`] or [`into_weak_pass`](StrongPass::into_weak_pass) will start an optional\n/// weak pass.\npub struct StrongPass {\n    old_heap: Heap,\n    new_heap: Heap,\n}\n\nimpl StrongPass {\n    /// Consumes an existing heap to begin a garbage collection pass\n    pub fn new(old_heap: Heap) -> StrongPass {\n        let type_info = old_heap.type_info().clone_for_collect_garbage();\n\n        StrongPass {\n            old_heap,\n            new_heap: Heap::new(type_info, Heap::DEFAULT_CAPACITY),\n        }\n    }\n\n    /// Continues as a weak reference pass\n    pub fn into_weak_pass(self) -> WeakPass {\n        WeakPass {\n            _old_heap: self.old_heap,\n            new_heap: self.new_heap,\n        }\n    }\n\n    /// Finishes garbage collection by returning the new heap\n    pub fn into_new_heap(self) -> Heap {\n        let mut new_heap = self.new_heap;\n        new_heap.save_len_at_gc();\n        new_heap\n    }\n\n    /// Visits a garbage collected box as a strong root\n    pub fn visit_box<T: Boxed>(&mut self, box_ref: &mut Gc<T>) {\n        let any_box_ref = unsafe { &mut *(box_ref as *mut _ as *mut Gc<boxed::Any>) };\n        Self::visit_any_box(&self.old_heap, &mut self.new_heap, any_box_ref);\n    }\n\n    fn move_box_to_new_heap(new_heap: &mut Heap, box_ref: &mut Gc<boxed::Any>, size: BoxSize) {\n        // Allocate and copy to the new heap\n        let dest_location = new_heap.alloc_cells(size.cell_count());\n        unsafe {\n            ptr::copy_nonoverlapping(box_ref.as_ptr(), dest_location, size.cell_count());\n        }\n\n        let forward_alloc_type = match size {\n            BoxSize::Size16 => AllocType::HeapForward16,\n            BoxSize::Size32 => AllocType::HeapForward32,\n        };\n\n        // Create a forwarding cell\n        let forwarding_cell = ForwardingCell {\n            header: boxed::Header {\n                // This is arbitrary but could be useful for debugging\n                type_tag: box_ref.header.type_tag,\n                alloc_type: forward_alloc_type,\n            },\n            new_location: unsafe { Gc::new(dest_location) },\n        };\n\n        // Overwrite the previous box location\n        unsafe {\n            ptr::copy_nonoverlapping(\n                &forwarding_cell as *const ForwardingCell as *const boxed::Any,\n                box_ref.as_ptr() as *mut boxed::Any,\n                1,\n            );\n        }\n\n        // Update the box_ref\n        *box_ref = unsafe { Gc::new(dest_location) };\n    }\n\n    /// Re-interns the symbol on a new heap\n    fn visit_interned_sym(old_heap: &Heap, new_heap: &mut Heap, interned_sym: &mut InternedSym) {\n        let old_interner = old_heap.type_info().interner();\n        let new_interner = new_heap.type_info_mut().interner_mut();\n\n        let sym_name = old_interner.unintern(interned_sym);\n        *interned_sym = new_interner.intern(sym_name);\n    }\n\n    fn visit_any_box(old_heap: &Heap, new_heap: &mut Heap, mut box_ref: &mut Gc<boxed::Any>) {\n        // This loop is used for ad-hoc tail recursion when visiting Pairs and FunThunks\n        // Everything else will return at the bottom of the loop\n        loop {\n            match box_ref.header.alloc_type {\n                AllocType::Const => {\n                    // Return when encountering a const box; they cannot move and cannot refer to the heap\n                    return;\n                }\n                AllocType::HeapForward16 | AllocType::HeapForward32 => {\n                    // This has already been moved to a new location\n                    let forwarding_cell = unsafe { &*(box_ref.as_ptr() as *const ForwardingCell) };\n                    *box_ref = forwarding_cell.new_location;\n                    return;\n                }\n                AllocType::Heap16 => {\n                    Self::move_box_to_new_heap(new_heap, box_ref, BoxSize::Size16);\n                }\n                AllocType::Heap32 => {\n                    Self::move_box_to_new_heap(new_heap, box_ref, BoxSize::Size32);\n                }\n                AllocType::Stack => {\n                    // Stack boxes cannot move but they may point to heap boxes\n                }\n            }\n\n            match box_ref.header.type_tag {\n                TypeTag::Sym => {\n                    let sym_ref = unsafe { &mut *(box_ref.as_mut_ptr() as *mut boxed::Sym) };\n                    Self::visit_interned_sym(old_heap, new_heap, sym_ref.interned_mut());\n                }\n                TypeTag::Pair => {\n                    let pair_ref =\n                        unsafe { &mut *(box_ref.as_mut_ptr() as *mut boxed::Pair<boxed::Any>) };\n\n                    Self::visit_any_box(old_heap, new_heap, &mut pair_ref.head);\n\n                    // Start again with the tail of the list\n                    box_ref = unsafe {\n                        &mut *(&mut pair_ref.rest as *mut Gc<boxed::List<boxed::Any>>\n                            as *mut Gc<boxed::Any>)\n                    };\n                    continue;\n                }\n                TypeTag::Vector => {\n                    let vec_ref =\n                        unsafe { &mut *(box_ref.as_mut_ptr() as *mut boxed::Vector<boxed::Any>) };\n\n                    vec_ref.visit_mut_elements(&mut |elem_ref| {\n                        Self::visit_any_box(old_heap, new_heap, elem_ref);\n                    });\n                }\n                TypeTag::FunThunk => {\n                    let fun_thunk_ref =\n                        unsafe { &mut *(box_ref.as_mut_ptr() as *mut boxed::FunThunk) };\n\n                    // Start again with the captures\n                    box_ref = unsafe { &mut *(&mut fun_thunk_ref.captures as *mut Gc<boxed::Any>) };\n                    continue;\n                }\n                TypeTag::Record => {\n                    use crate::boxed::types::field_value::FieldGcRef;\n\n                    let record_ref = unsafe { &mut *(box_ref.as_mut_ptr() as *mut boxed::Record) };\n                    for field_gc_ref in record_ref.field_gc_refs(old_heap) {\n                        match field_gc_ref {\n                            FieldGcRef::Boxed(field_box_ref) => {\n                                Self::visit_any_box(old_heap, new_heap, field_box_ref);\n                            }\n                            FieldGcRef::InternedSym(interned_sym) => {\n                                Self::visit_interned_sym(old_heap, new_heap, interned_sym);\n                            }\n                        }\n                    }\n                }\n                _ => {}\n            }\n\n            return;\n        }\n    }\n}\n\n/// Weak pass of a collection to a new [`Heap`]\n///\n/// This will return the location of cells that have been moved to the new heap or [`None`] for\n/// cells that were not visited during the strong pass.\npub struct WeakPass {\n    // We need the old heap to remain allocated so we can follow pointers for old cells\n    _old_heap: Heap,\n    new_heap: Heap,\n}\n\nimpl WeakPass {\n    /// Finishes garbage collection by returning the new [`Heap`]\n    pub fn into_new_heap(self) -> Heap {\n        let mut new_heap = self.new_heap;\n        new_heap.save_len_at_gc();\n        new_heap\n    }\n\n    /// Visits a garbage collected box\n    ///\n    /// If the box was moved during the strong pass its new location will be returned. Otherwise,\n    /// [`None`] will be returned.\n    pub fn new_heap_ref_for<T: Boxed>(&self, boxed: Gc<T>) -> Option<Gc<T>> {\n        let any_boxed = unsafe { boxed.cast::<boxed::Any>() };\n        self.new_heap_any_ref_for(any_boxed)\n            .map(|new_box| unsafe { new_box.cast::<T>() })\n    }\n\n    fn new_heap_any_ref_for(&self, box_ref: Gc<boxed::Any>) -> Option<Gc<boxed::Any>> {\n        match box_ref.header.alloc_type {\n            AllocType::Const | AllocType::Stack => {\n                // These aren't managed by the GC; their pointer remains valid\n                Some(box_ref)\n            }\n            AllocType::HeapForward16 | AllocType::HeapForward32 => {\n                // This has already been moved to a new location\n                let forwarding_cell = unsafe { &*(box_ref.as_ptr() as *const ForwardingCell) };\n                Some(forwarding_cell.new_location)\n            }\n            AllocType::Heap16 | AllocType::Heap32 => None,\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::{Int, List, Str};\n\n    #[test]\n    fn simple_collect() {\n        let mut old_heap = Heap::empty();\n\n        let mut hello = Str::new(&mut old_heap, \"HELLO\");\n        let mut world = Str::new(&mut old_heap, \"WORLD\");\n\n        assert_eq!(\"HELLO\", hello.as_str());\n        assert_eq!(\"WORLD\", world.as_str());\n        assert_eq!(2, old_heap.len());\n\n        // Root everything\n        let mut all_strong = StrongPass::new(old_heap);\n        all_strong.visit_box(&mut hello);\n        all_strong.visit_box(&mut world);\n\n        let all_heap = all_strong.into_new_heap();\n        assert_eq!(\"HELLO\", hello.as_str());\n        assert_eq!(\"WORLD\", world.as_str());\n        assert_eq!(2, all_heap.len());\n\n        // Take aliases to hello and world to simulate weak reference\n        let hello_alias = hello;\n        let world_alias = world;\n\n        // Root just one string\n        let mut one_strong = StrongPass::new(all_heap);\n        one_strong.visit_box(&mut hello);\n\n        // Start a weak pass\n        let one_weak = one_strong.into_weak_pass();\n        assert!(one_weak.new_heap_ref_for(hello_alias).is_some());\n        assert!(one_weak.new_heap_ref_for(world_alias).is_none());\n\n        let one_heap = one_weak.into_new_heap();\n        assert_eq!(\"HELLO\", hello.as_str());\n        assert_eq!(1, one_heap.len());\n\n        // Root nothing\n        let zero_heap = StrongPass::new(one_heap).into_new_heap();\n        assert_eq!(0, zero_heap.len());\n    }\n\n    #[test]\n    fn sym_collect() {\n        use crate::boxed::Sym;\n\n        let mut old_heap = Heap::empty();\n\n        let inline_name = \"Hello\";\n        let indexed_name = \"This is too long; it will be indexed to the heap's intern table\";\n\n        let mut inline = Sym::new(&mut old_heap, inline_name);\n        let mut indexed = Sym::new(&mut old_heap, indexed_name);\n        assert_eq!(2, old_heap.len());\n\n        let mut all_strong = StrongPass::new(old_heap);\n        all_strong.visit_box(&mut inline);\n        all_strong.visit_box(&mut indexed);\n\n        let all_heap = all_strong.into_new_heap();\n\n        assert_eq!(inline_name, inline.name(&all_heap));\n        assert_eq!(indexed_name, indexed.name(&all_heap));\n        assert_eq!(2, all_heap.len());\n    }\n\n    #[test]\n    fn list_collect() {\n        use std::mem;\n\n        // Three 1 cell integers + three pairs\n        const PAIR_CELLS: usize =\n            mem::size_of::<boxed::Pair<boxed::Any>>() / mem::size_of::<boxed::Any>();\n\n        const EXPECTED_HEAP_SIZE: usize = 3 + (3 * PAIR_CELLS);\n\n        let mut old_heap = Heap::empty();\n\n        let mut boxed_list = List::from_values(&mut old_heap, [1, 2, 3].iter().cloned(), Int::new);\n        assert_eq!(EXPECTED_HEAP_SIZE, old_heap.len());\n\n        assert_eq!(3, boxed_list.len());\n\n        let mut all_strong = StrongPass::new(old_heap);\n        all_strong.visit_box(&mut boxed_list);\n\n        let all_heap = all_strong.into_new_heap();\n        assert_eq!(3, boxed_list.len());\n        assert_eq!(EXPECTED_HEAP_SIZE, all_heap.len());\n\n        let mut boxed_list_iter = boxed_list.iter();\n        for expected_num in &[1, 2, 3] {\n            if let Some(boxed_int) = boxed_list_iter.next() {\n                assert_eq!(*expected_num, boxed_int.value());\n            } else {\n                panic!(\"Iterator unexpectedly ended\");\n            }\n        }\n    }\n\n    #[test]\n    fn vector_collect() {\n        // Try empty, 1 cell inline, 2 cell inline, and large vectors\n        let test_contents: [&[i64]; 4] = [&[], &[1], &[1, 2, 3], &[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]];\n\n        for &test_content in &test_contents {\n            let mut old_heap = Heap::empty();\n            let mut boxed_vec =\n                boxed::Vector::from_values(&mut old_heap, test_content.iter().cloned(), Int::new);\n\n            let mut all_strong = StrongPass::new(old_heap);\n            all_strong.visit_box(&mut boxed_vec);\n\n            // Need to give this a name so it doesn't Drop\n            let _all_heap = all_strong.into_new_heap();\n\n            let mut boxed_list_iter = boxed_vec.iter();\n            assert_eq!(test_content.len(), boxed_list_iter.len());\n\n            for expected_num in test_content {\n                if let Some(boxed_int) = boxed_list_iter.next() {\n                    assert_eq!(*expected_num, boxed_int.value());\n                } else {\n                    panic!(\"Iterator unexpectedly ended\");\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/heap/mod.rs",
    "content": "pub mod collect;\npub mod type_info;\n\nuse std::{cmp, mem, ptr};\n\nuse crate::boxed::heap::type_info::TypeInfo;\nuse crate::boxed::refs::Gc;\nuse crate::boxed::{AllocType, Any, Boxed};\nuse crate::intern::{AsInterner, Interner};\n\n/// Allocated segment of garbage collected memory\n///\n/// This has a gross pointer-based representation to allow use as a bump allocator from generated\n/// native code.\n#[repr(C)]\npub struct Segment {\n    next: *mut Any,\n    end: *const Any,\n    backing_vec: Vec<Any>,\n}\n\n/// Heap of garbage collected boxes\n#[repr(C)]\npub struct Heap {\n    current_segment: Segment,\n    full_segments: Vec<Segment>,\n    type_info: TypeInfo,\n    len_at_last_gc: usize,\n}\n\nimpl Segment {\n    /// Creates a new segment with capacity for `count` cells\n    fn with_capacity(count: usize) -> Segment {\n        // Allow for an extra cell of red zone capacity.\n        //\n        // This is because variable sized boxes will have a Rust size of 32 bytes, but we might\n        // allocate them only 16 bytes in the segment. If this happens at the end of the segment we\n        // could create a pointer to a Rust object extending past the allocation. This is UB and\n        // could generate code that e.g. does a vector load into unallocated memory.\n        let mut backing_vec = Vec::with_capacity(count + 1);\n\n        let next: *mut Any = backing_vec.as_mut_ptr();\n\n        Segment {\n            next,\n            end: unsafe { next.add(count) },\n            backing_vec,\n        }\n    }\n\n    /// Returns contiguous memory for holding `count` cells\n    ///\n    /// If the segment is full this will return [`None`]\n    fn alloc_cells(&mut self, count: usize) -> Option<*mut Any> {\n        let current_next = self.next;\n\n        // Convert to an integer to avoid the UB of creating a pointer out of bounds.\n        let new_next = (self.next as usize) + (count * mem::size_of::<Any>());\n\n        if new_next > self.end as usize {\n            None\n        } else {\n            self.next = new_next as *mut Any;\n            Some(current_next)\n        }\n    }\n\n    /// Returns the number of allocated cells\n    fn len(&self) -> usize {\n        // TODO: Replace with `offset_from` once its stable\n        (self.next as usize - self.backing_vec.as_ptr() as usize) / mem::size_of::<Any>()\n    }\n}\n\nimpl Drop for Segment {\n    fn drop(&mut self) {\n        let mut current = self.backing_vec.as_mut_ptr();\n        while current < self.next as *mut Any {\n            unsafe {\n                match (*current).header.alloc_type {\n                    AllocType::Heap16 | AllocType::Heap32 => ptr::drop_in_place(current),\n                    AllocType::HeapForward16 | AllocType::HeapForward32 => {}\n                    AllocType::Const | AllocType::Stack => {\n                        unreachable!(\"Unexpected alloc type in heap\")\n                    }\n                }\n\n                match (*current).header.alloc_type {\n                    AllocType::Heap16 | AllocType::HeapForward16 => {\n                        current = current.add(1);\n                    }\n                    AllocType::Heap32 | AllocType::HeapForward32 => {\n                        current = current.add(2);\n                    }\n                    AllocType::Const | AllocType::Stack => {\n                        unreachable!(\"Unexpected alloc type in heap\")\n                    }\n                }\n            }\n        }\n    }\n}\n\nimpl Heap {\n    /// Capacity of the initial segment and all overflow segments\n    const DEFAULT_SEGMENT_CAPACITY: usize = 1024;\n\n    /// Default capacity of the heap\n    const DEFAULT_CAPACITY: usize = Self::DEFAULT_SEGMENT_CAPACITY;\n\n    /// Returns an empty heap with a default capacity\n    pub fn empty() -> Heap {\n        Self::new(TypeInfo::empty(), Self::DEFAULT_CAPACITY)\n    }\n\n    /// Returns a new heap with the given type information and capacity\n    pub fn new(type_info: TypeInfo, count: usize) -> Heap {\n        Heap {\n            current_segment: Segment::with_capacity(count),\n            full_segments: vec![],\n            type_info,\n            len_at_last_gc: 0,\n        }\n    }\n\n    /// Hints if this heap should be garbage collected\n    ///\n    /// This is a heuristic based on the number of allocations since the last GC cycle.\n    pub fn should_collect(&self) -> bool {\n        let maximum_len = std::cmp::max(Self::DEFAULT_SEGMENT_CAPACITY, self.len_at_last_gc) * 2;\n        self.len() > maximum_len\n    }\n\n    fn save_len_at_gc(&mut self) {\n        self.len_at_last_gc = self.len();\n    }\n\n    /// Allocates space for `count` contiguous cells\n    pub fn alloc_cells(&mut self, count: usize) -> *mut Any {\n        if let Some(alloc) = self.current_segment.alloc_cells(count) {\n            return alloc;\n        }\n\n        // Make sure we allocate enough to satisfy the request\n        let capacity = cmp::max(count, Self::DEFAULT_SEGMENT_CAPACITY);\n\n        // Build a new segment and allocate from it\n        let mut new_segment = Segment::with_capacity(capacity);\n        let alloc = new_segment.alloc_cells(count).unwrap();\n\n        // Switch the segment and track the old one for finalisation\n        let previous_segment = mem::replace(&mut self.current_segment, new_segment);\n        self.full_segments.push(previous_segment);\n\n        alloc\n    }\n\n    /// Returns the runtime type information associated with the heap\n    pub fn type_info(&self) -> &TypeInfo {\n        &self.type_info\n    }\n\n    /// Returns a mutable reference to the runtime type information associated with the heap\n    pub fn type_info_mut(&mut self) -> &mut TypeInfo {\n        &mut self.type_info\n    }\n\n    /// Returns the number of allocated cells\n    pub fn len(&self) -> usize {\n        let full_len: usize = self.full_segments.iter().map(Segment::len).sum();\n        self.current_segment.len() + full_len\n    }\n\n    /// Returns true if the heap contains no boxes\n    pub fn is_empty(&self) -> bool {\n        self.current_segment.len() == 0 && self.full_segments.is_empty()\n    }\n\n    /// Places a new boxed value on the heap\n    pub fn place_box<T: Boxed>(&mut self, boxed: T) -> Gc<T> {\n        let heap_size = boxed\n            .header()\n            .alloc_type()\n            .to_heap_box_size()\n            .expect(\"non-heap alloc type\");\n\n        let needed_cells = heap_size.cell_count();\n\n        let insert_at = self.alloc_cells(needed_cells);\n\n        unsafe {\n            ptr::copy_nonoverlapping(&boxed as *const T as *const Any, insert_at, needed_cells);\n        }\n\n        // Make sure we don't drop the stack version\n        mem::forget(boxed);\n        unsafe { Gc::new(insert_at as *const T) }\n    }\n}\n\nimpl Default for Heap {\n    fn default() -> Self {\n        Self::empty()\n    }\n}\n\n/// Object that can be used as a heap\npub trait AsHeap {\n    /// Returns this object as a heap\n    fn as_heap(&self) -> &Heap;\n\n    /// Returns this object as a mutable heap\n    fn as_heap_mut(&mut self) -> &mut Heap;\n}\n\nimpl AsHeap for Heap {\n    fn as_heap(&self) -> &Heap {\n        self\n    }\n\n    fn as_heap_mut(&mut self) -> &mut Heap {\n        self\n    }\n}\n\nimpl AsInterner for Heap {\n    fn as_interner(&self) -> &Interner {\n        self.type_info().interner()\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn basic_alloc() {\n        use crate::boxed::Str;\n\n        let mut heap = Heap::new(TypeInfo::empty(), 2);\n\n        let string1 = Str::new(&mut heap, \"HELLO\");\n        let string2 = Str::new(&mut heap, \"WORLD\");\n\n        assert_eq!(\"HELLO\", string1.as_str());\n        assert_eq!(\"WORLD\", string2.as_str());\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/heap/type_info.rs",
    "content": "//! Container for runtime type information for boxed data\n\nuse crate::class_map::ClassMap;\nuse crate::intern::{AsInterner, Interner};\n\n/// Contains associated runtime type information for boxed data\n///\n/// This is a container for [`Interner`] and [`ClassMap`].\npub struct TypeInfo {\n    interner: Interner,\n    class_map: ClassMap,\n}\n\nimpl TypeInfo {\n    /// Constructs type information with the given components\n    pub fn new(interner: Interner, class_map: ClassMap) -> TypeInfo {\n        TypeInfo {\n            interner,\n            class_map,\n        }\n    }\n\n    /// Constructs empty type information\n    pub fn empty() -> TypeInfo {\n        Self::new(Interner::new(), ClassMap::empty())\n    }\n\n    /// Returns a clone of this type information suitable for garbage collection\n    pub fn clone_for_collect_garbage(&self) -> Self {\n        Self {\n            interner: self.interner.clone_for_collect_garbage(),\n            class_map: self.class_map.clone(),\n        }\n    }\n\n    /// Returns the symbol interner\n    pub fn interner(&self) -> &Interner {\n        &self.interner\n    }\n\n    /// Returns a mutable reference to the symbol interner\n    pub fn interner_mut(&mut self) -> &mut Interner {\n        &mut self.interner\n    }\n\n    /// Returns the class map\n    pub fn class_map(&self) -> &ClassMap {\n        &self.class_map\n    }\n\n    /// Returns a mutable reference to the class map\n    pub fn class_map_mut(&mut self) -> &mut ClassMap {\n        &mut self.class_map\n    }\n}\n\nimpl AsInterner for TypeInfo {\n    fn as_interner(&self) -> &Interner {\n        self.interner()\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/mod.rs",
    "content": "#![warn(missing_docs)]\n\n//! Boxed values and heaps\n//!\n//! This contains the implementation of our garbage collector and the types it can manage. Some\n//! types (such as `Int` and `Float`) have corresponding unboxed representations and are only boxed\n//! for the purposes of runtime dynamic typing. Complex values (such as `Vector` and `Sym`) have no\n//! unboxed representation.\n//!\n//! Boxes can also be placed on the stack on in static constants instead of the heap. This is of\n//! limited value to Rust code but is frequently used by the compiler to avoid the overhead of\n//! allocation and garbage collection.\n\nmod heap;\npub mod refs;\nmod types;\n\nuse std::hash::{Hash, Hasher};\nuse std::{fmt, ptr};\n\nuse crate::abitype::{BoxedAbiType, EncodeBoxedAbiType};\nuse crate::boxed::refs::Gc;\n\npub use crate::boxed::heap::{collect, type_info};\npub use crate::boxed::heap::{AsHeap, Heap};\npub use crate::boxed::types::char::Char;\npub use crate::boxed::types::field_value::{FieldValue, FieldValueIter};\npub use crate::boxed::types::float::Float;\npub use crate::boxed::types::fun::{Captures, FunThunk, ThunkEntry};\npub use crate::boxed::types::int::Int;\npub use crate::boxed::types::list::{List, ListSubtype, Nil, Pair, NIL_INSTANCE};\npub use crate::boxed::types::map::Map;\npub use crate::boxed::types::record::{Record, RecordClassId, RecordStorage};\npub use crate::boxed::types::record_data::RecordData;\npub use crate::boxed::types::set::Set;\npub use crate::boxed::types::str::{Str, StrStorage};\npub use crate::boxed::types::sym::Sym;\npub use crate::boxed::types::vector::Vector;\n\n/// Prelude of common traits useful for working with boxed values\npub mod prelude {\n    pub use super::AsHeap;\n    pub use super::Boxed;\n    pub use super::DistinctTagged;\n    pub use super::HashInHeap;\n    pub use super::PartialEqInHeap;\n}\n\n/// Size of a boxed value in bytes\n#[derive(PartialEq, Debug, Copy, Clone)]\npub enum BoxSize {\n    /// 16 byte boxed value\n    Size16,\n    /// 32 byte boxed value\n    Size32,\n}\n\nimpl BoxSize {\n    /// Returns the number of 16 byte cells required by this box size\n    pub fn cell_count(self) -> usize {\n        match self {\n            BoxSize::Size16 => 1,\n            BoxSize::Size32 => 2,\n        }\n    }\n\n    /// Returns the corresponding `AllocType` if this box was allocated on the heap\n    pub fn to_heap_alloc_type(self) -> AllocType {\n        match self {\n            BoxSize::Size16 => AllocType::Heap16,\n            BoxSize::Size32 => AllocType::Heap32,\n        }\n    }\n}\n\n/// Allocation type for boxed values\n#[repr(u8)]\n#[derive(Debug, PartialEq, Clone, Copy)]\npub enum AllocType {\n    /// Static constant value\n    Const,\n    /// Stack allocated value of unknown length\n    Stack,\n    /// Heap allocated 16 byte value\n    Heap16,\n    /// Heap allocated 32 byte value\n    Heap32,\n\n    /// Box pointing to a new 16 byte heap location\n    ///\n    /// This is a temporary type used during garbage collection.\n    HeapForward16,\n\n    /// Box pointing to a new 32 byte heap location\n    ///\n    /// This is a temporary type used during garbage collection.\n    HeapForward32,\n}\n\nimpl AllocType {\n    /// Returns the corresponding `BoxSize` if this type is heap allocated\n    pub fn to_heap_box_size(self) -> Option<BoxSize> {\n        match self {\n            AllocType::Heap16 => Some(BoxSize::Size16),\n            AllocType::Heap32 => Some(BoxSize::Size32),\n            _ => None,\n        }\n    }\n}\n\n/// Header for common boxed value metadata\n#[repr(C)]\n#[derive(Debug, Clone, Copy)]\npub struct Header {\n    type_tag: TypeTag,\n    alloc_type: AllocType,\n}\n\nimpl Header {\n    /// Returns a new header for the given type tag and allocation type\n    pub fn new(type_tag: TypeTag, alloc_type: AllocType) -> Header {\n        Header {\n            type_tag,\n            alloc_type,\n        }\n    }\n\n    /// Returns the constant type tag for this value\n    pub fn type_tag(self) -> TypeTag {\n        self.type_tag\n    }\n\n    /// Return the allocation type for this value\n    pub fn alloc_type(self) -> AllocType {\n        self.alloc_type\n    }\n}\n\n/// Equivalent of [`PartialEq`] that receives an additional [`Heap`] parameter\n///\n/// This is required for types that require additional metadata from the heap to perform equality\n/// checks.\npub trait PartialEqInHeap {\n    /// Returns true if the values are equal\n    ///\n    /// Both values will be in the same heap.\n    fn eq_in_heap(&self, heap: &Heap, other: &Self) -> bool;\n}\n\nimpl<T> PartialEqInHeap for T\nwhere\n    T: PartialEq,\n{\n    fn eq_in_heap(&self, _heap: &Heap, other: &Self) -> bool {\n        self.eq(other)\n    }\n}\n\n/// Equivalent of [`Hash`] that receives an additional [`Heap`] parameter\n///\n/// This is required for types that require additional metadata from the heap to calculate hashes.\npub trait HashInHeap {\n    /// Feeds this value into the given [`Hasher`]\n    fn hash_in_heap<H: Hasher>(&self, heap: &Heap, state: &mut H);\n}\n\nimpl<T> HashInHeap for T\nwhere\n    T: Hash,\n{\n    fn hash_in_heap<H: Hasher>(&self, _heap: &Heap, state: &mut H) {\n        self.hash(state)\n    }\n}\n\n/// Boxed value\n///\n/// Boxes can be allocated on the stack, heap or a static constant. Every box is tagged with a\n/// top-level type.\npub trait Boxed: Sized + PartialEqInHeap + HashInHeap + fmt::Debug {\n    /// Casts this value to an `Any` reference\n    fn as_any_ref(&self) -> Gc<Any> {\n        unsafe { Gc::new(&*(self as *const Self as *const Any)) }\n    }\n\n    /// Returns the header of the box\n    fn header(&self) -> Header {\n        self.as_any_ref().header\n    }\n}\n\nimpl EncodeBoxedAbiType for Any {\n    const BOXED_ABI_TYPE: BoxedAbiType = BoxedAbiType::Any;\n}\n\n/// Marks that this boxed struct has a specific constant type tag\n///\n/// For example, [`Vector<Str>`] is `ConstTagged` because it always has a type tag of `Vector`. As\n/// a counterexample, [`Num`] is not because it could either have an `Int` or `Float` type tag.\n///\n/// In mathematical terms this can be thought of as the struct being surjective to the type tag.\npub trait ConstTagged: Boxed {\n    /// Type tag for values of this type\n    const TYPE_TAG: TypeTag;\n}\n\n/// Indicates that this boxed struct does not share type tags with unrelated types\n///\n/// For example, [`Num`] is `DistinctTagged` because it only shares type tags with `Any`, `Float`\n/// and `Int` which are all either subtypes or supertypes. As a counterexample, [`Vector<Str>`] is\n/// not because it shares a type tag with [`Vector<Sym>`].\n///\n/// In mathematical terms this can be thought of as the struct being injective to the type tag\npub trait DistinctTagged: Boxed {\n    /// Returns if the passed type tag corresponds to this type\n    fn has_tag(type_tag: TypeTag) -> bool;\n}\n\n/// Marks that every boxed value with `TYPE_TAG` corresponds to this boxed struct\n///\n/// For example, [`Str`] is `UniqueTagged` because no other struct has the type tag of `Str`. As a\n/// counterexample, `Vector<Str>` is not because it shares a type tag with `Vector<Sym>`.\n///\n/// In mathematical terms this can be thought of as the struct being bijective with the type tag.\npub trait UniqueTagged: ConstTagged + DistinctTagged {}\n\nimpl<T: UniqueTagged> EncodeBoxedAbiType for T {\n    const BOXED_ABI_TYPE: BoxedAbiType = BoxedAbiType::UniqueTagged(T::TYPE_TAG);\n}\n\nmacro_rules! define_const_tagged_boxes {\n    ($($name:ident),*) => {\n        /// Tag byte identifying top-level types\n        #[repr(u8)]\n        #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]\n        pub enum TypeTag {\n            $(\n                #[allow(missing_docs)]\n                $name\n            ),*\n        }\n\n        /// Static list of all possible type tags\n        ///\n        /// This is guaranteed to be sorted\n        pub const ALL_TYPE_TAGS: &'static [TypeTag] = &[\n            $( TypeTag::$name ),*\n        ];\n\n        impl TypeTag {\n            /// Returns a string representation for the type\n            pub fn to_str(self) -> &'static str {\n                match self {\n                    $(\n                        TypeTag::$name => {\n                            stringify!($name)\n                        }\n                    )*\n                }\n            }\n        }\n\n        $(\n            impl ConstTagged for $name {\n                const TYPE_TAG: TypeTag = TypeTag::$name;\n            }\n\n            impl DistinctTagged for $name {\n                fn has_tag(type_tag: TypeTag) -> bool {\n                    Self::TYPE_TAG == type_tag\n                }\n            }\n        )*\n\n        define_supertype!(\n            /// Supertype of all boxed types\n            Any,\n            AnySubtype, DistinctTagged, as_any_ref, { $($name),* });\n    }\n}\n\nimpl TypeTag {\n    /// Returns the boxed ABI type corresponding to this type tag\n    pub fn to_boxed_abi_type(self) -> BoxedAbiType {\n        BoxedAbiType::UniqueTagged(self)\n    }\n\n    /// Returns a header for a constant boxed values of this type\n    pub fn to_const_header(self) -> Header {\n        Header::new(self, AllocType::Const)\n    }\n\n    /// Returns a header for heap allocated values of this type and size\n    pub fn to_heap_header(self, box_size: BoxSize) -> Header {\n        Header::new(self, box_size.to_heap_alloc_type())\n    }\n}\n\nmacro_rules! define_singleton_box {\n    (\n        $(#[$struct_docs:meta])*\n        $type_name:ident,\n        $(#[$static_docs:meta])*\n        $static_name:ident,\n        $export_name:expr\n    ) => {\n        $(#[$struct_docs])*\n        #[repr(C, align(16))]\n        #[derive(Debug)]\n        pub struct $type_name {\n            header: Header,\n        }\n\n        impl Boxed for $type_name {}\n        impl UniqueTagged for $type_name {}\n\n        $(#[$static_docs])*\n        #[export_name = $export_name]\n        pub static $static_name: $type_name = $type_name {\n            header: Header {\n                type_tag: $type_name::TYPE_TAG,\n                alloc_type: AllocType::Const,\n            },\n        };\n\n        impl PartialEq for $type_name {\n            fn eq(&self, _: &$type_name) -> bool {\n                // This is tricky - we're a singleton so if the types match we must be equal\n                true\n            }\n        }\n\n        impl Hash for $type_name {\n            fn hash<H: Hasher>(&self, state: &mut H) {\n                Self::TYPE_TAG.hash(state);\n                state.write_usize(&$static_name as *const $type_name as usize);\n            }\n        }\n    };\n}\n\nmacro_rules! define_supertype {\n    (\n        $(#[$docs:meta])*\n        $name:ident,\n        $subtype_enum:ident, $subtype_trait:ident, $as_enum_ref:ident, { $($member:ident),* }\n    ) => {\n        $(#[$docs])*\n        #[repr(C, align(16))]\n        pub struct $name {\n            header: Header,\n        }\n\n        impl Boxed for $name {}\n\n        impl DistinctTagged for $name {\n            fn has_tag(type_tag: TypeTag) -> bool {\n                [$( TypeTag::$member ),*].contains(&type_tag)\n            }\n        }\n\n        impl $name {\n            /// Returns a subtype of this value based on its type tag\n            pub fn as_subtype(&self) -> $subtype_enum<'_> {\n                #[allow(unreachable_patterns)]\n                match self.header.type_tag {\n                    $(\n                        TypeTag::$member => {\n                            $subtype_enum::$member(unsafe {\n                                &*(self as *const $name as *const $member)\n                            })\n                        }\n                    )*\n                    other => {\n                        unreachable!(\"Unexpected type tag: {:?}\", other);\n                    }\n                }\n            }\n\n            /// Tries to downcast this reference to a subtype based on its type tag\n            pub fn downcast_ref<T: $subtype_trait>(&self) -> Option<Gc<T>>\n            {\n                if T::has_tag(self.header.type_tag) {\n                    Some(unsafe { Gc::new(&*(self as *const $name as *const T)) })\n                } else {\n                    None\n                }\n            }\n        }\n\n        impl HashInHeap for $name {\n            fn hash_in_heap<H: Hasher>(&self, heap: &Heap, state: &mut H) {\n                match self.as_subtype() {\n                    $(\n                        $subtype_enum::$member(subtype) => {\n                            subtype.hash_in_heap(heap, state)\n                        }\n                    )*\n                }\n            }\n        }\n\n        impl PartialEqInHeap for $name {\n            fn eq_in_heap(&self, heap: &Heap, other: &$name) -> bool {\n                match (self.as_subtype(), other.as_subtype()) {\n                    $(\n                        ($subtype_enum::$member(self_value), $subtype_enum::$member(other_value)) => {\n                            self_value.eq_in_heap(heap, other_value)\n                        }\n                    ),*\n                    _ => false\n                }\n            }\n        }\n\n        impl fmt::Debug for $name {\n            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n                match self.as_subtype() {\n                    $(\n                        $subtype_enum::$member(subtype) => {\n                            subtype.fmt(formatter)\n                        }\n                    )*\n                }\n            }\n        }\n\n        impl Drop for $name {\n            fn drop(&mut self) {\n                // Cast to the correct type so Rust knows which Drop implementation to call\n                match self.as_subtype() {\n                    $(\n                        $subtype_enum::$member(subtype) => {\n                            unsafe {\n                                ptr::drop_in_place(subtype as *const $member as *mut $member);\n                            }\n                        }\n                    )*\n                }\n            }\n        }\n\n        /// Possible subtypes of this supertype\n        #[derive(Debug)]\n        pub enum $subtype_enum<'a> {\n            $(\n                #[allow(missing_docs)]\n                $member(&'a $member)\n            ),*\n        }\n    }\n}\n\nmacro_rules! define_tagged_union {\n    (\n        $(#[$struct_docs:meta])*\n        $name:ident,\n        $(#[$subtype_docs:meta])*\n        $subtype_enum:ident,\n        $subtype_trait:ident, $as_enum_ref:ident, { $($member:ident),* }\n    ) => {\n        define_supertype!(\n            $(#[$struct_docs])*\n            $name,\n            $subtype_enum, $subtype_trait, $as_enum_ref, { $($member),* }\n        );\n\n        $(#[$subtype_docs])*\n        pub trait $subtype_trait : DistinctTagged {}\n\n        $(\n            impl $member {\n                /// Casts this value to its supertype\n                pub fn $as_enum_ref(&self) -> Gc<$name> {\n                    unsafe { Gc::new(&*(self as *const Self as *const $name)) }\n                }\n            }\n\n            impl $subtype_trait for $member {}\n        )*\n\n        impl EncodeBoxedAbiType for $name {\n            const BOXED_ABI_TYPE: BoxedAbiType = BoxedAbiType::Union(stringify!($name), &[\n                $( $member::TYPE_TAG ),*\n            ]);\n        }\n    };\n}\n\ndefine_const_tagged_boxes! {\n    Float,\n    Int,\n    Char,\n    Str,\n    Sym,\n    Pair,\n    Nil,\n    True,\n    False,\n    Vector,\n    FunThunk,\n    Record,\n    Set,\n    Map\n}\n\ndefine_singleton_box!(\n    /// Boolean true\n    True,\n    /// Static constant instance of [`True`]\n    TRUE_INSTANCE,\n    \"ARRET_TRUE\"\n);\n\ndefine_singleton_box!(\n    /// Boolean false\n    False,\n    /// Static constant instance of [`False`]\n    FALSE_INSTANCE,\n    \"ARRET_FALSE\"\n);\n\ndefine_tagged_union!(\n    /// Union of numeric types\n    Num,\n    /// Possible subtypes of [`Num`]\n    NumSubtype,\n    NumMember, as_num_ref, {\n        Int,\n        Float\n    }\n);\n\ndefine_tagged_union!(\n    /// Union of boolean types\n    Bool,\n    /// Possible subtypes of [`Bool`]\n    BoolSubtype,\n    BoolMember, as_bool_ref, {\n        True,\n        False\n    }\n);\n\nimpl Bool {\n    /// Returns the singleton box corresponding the boolean value\n    pub fn singleton_ref(value: bool) -> Gc<Bool> {\n        if value {\n            TRUE_INSTANCE.as_bool_ref()\n        } else {\n            FALSE_INSTANCE.as_bool_ref()\n        }\n    }\n\n    /// Returns the unboxed value of this boolean\n    pub fn as_bool(&self) -> bool {\n        match self.as_subtype() {\n            BoolSubtype::True(_) => true,\n            BoolSubtype::False(_) => false,\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use std::mem;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(2, mem::size_of::<Header>());\n        assert_eq!(16, mem::size_of::<Nil>());\n        assert_eq!(16, mem::size_of::<True>());\n        assert_eq!(16, mem::size_of::<False>());\n    }\n\n    #[test]\n    fn downcast_ref() {\n        let mut heap = Heap::empty();\n\n        let box_float = Float::new(&mut heap, 2.0);\n        let box_float_as_any = box_float.as_any_ref();\n\n        assert!(!box_float_as_any.downcast_ref::<Int>().is_some());\n        assert!(box_float_as_any.downcast_ref::<Float>().is_some());\n    }\n\n    #[test]\n    fn as_tagged() {\n        let mut heap = Heap::empty();\n\n        let box_float = Float::new(&mut heap, 2.0);\n        let box_float_as_any = box_float.as_any_ref();\n\n        if let AnySubtype::Float(_) = box_float_as_any.as_subtype() {\n        } else {\n            panic!(\"Failed to get tagged representation\")\n        }\n    }\n\n    #[test]\n    fn any_equality() {\n        let mut heap = Heap::empty();\n\n        let box_two = Float::new(&mut heap, 2.0);\n        let box_two_as_any = box_two.as_any_ref();\n\n        let box_three = Float::new(&mut heap, 3.0);\n        let box_three_as_any = box_three.as_any_ref();\n\n        assert!(box_two_as_any.eq_in_heap(&heap, &box_two_as_any));\n        assert!(!box_two_as_any.eq_in_heap(&heap, &box_three_as_any));\n\n        #[allow(clippy::eq_op)]\n        {\n            assert_eq!(TRUE_INSTANCE, TRUE_INSTANCE);\n        }\n    }\n\n    #[test]\n    fn any_fmt_debug() {\n        let mut heap = Heap::empty();\n\n        let boxed_one = Int::new(&mut heap, 1);\n        let boxed_one_as_any = boxed_one.as_any_ref();\n        assert_eq!(\"Int(1)\", format!(\"{:?}\", boxed_one_as_any));\n    }\n\n    #[test]\n    fn union_types() {\n        let mut heap = Heap::empty();\n\n        let box_float = Float::new(&mut heap, 2.0);\n        let box_float_as_any = box_float.as_any_ref();\n\n        if let Some(stack_num) = box_float_as_any.downcast_ref::<Num>() {\n            if let NumSubtype::Float(_) = stack_num.as_subtype() {\n            } else {\n                panic!(\"Couldn't get tagged Float from Num\");\n            }\n\n            assert!(!stack_num.downcast_ref::<Int>().is_some());\n            assert!(stack_num.downcast_ref::<Float>().is_some());\n        } else {\n            panic!(\"Float was not a Num\");\n        }\n\n        let box_str = Str::new(&mut heap, \"Test!\");\n        let box_str_as_any = box_str.as_any_ref();\n\n        assert!(!box_str_as_any.downcast_ref::<Num>().is_some());\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/refs.rs",
    "content": "//! References to boxed values\n//!\n//! These are all transparent; they're used for either readability or marker traits.\n\nuse std::ops::Deref;\nuse std::ptr;\nuse std::{fmt, hash};\n\nuse crate::boxed::Boxed;\n\n/// Reference to a garbage collected value\n///\n/// This is not memory safe and does not GC root; it's just sugar for a raw pointer.\n#[repr(transparent)]\npub struct Gc<T: Boxed> {\n    inner: ptr::NonNull<T>,\n}\n\n// Manual Clone implementation to work around Rust issue #26925\nimpl<T: Boxed> Clone for Gc<T> {\n    fn clone(&self) -> Self {\n        Gc { inner: self.inner }\n    }\n}\n\nimpl<T: Boxed> Copy for Gc<T> {}\n\nimpl<T: Boxed> Deref for Gc<T> {\n    type Target = T;\n    fn deref(&self) -> &T {\n        unsafe { self.inner.as_ref() }\n    }\n}\n\nimpl<T: Boxed> Gc<T> {\n    /// Returns a new instance wrapping a pointer to a garbage collected box\n    ///\n    /// # Safety\n    /// The requires a valid pointer to a box\n    pub unsafe fn new(ptr: *const T) -> Gc<T> {\n        Gc {\n            inner: ptr::NonNull::new_unchecked(ptr as *mut T),\n        }\n    }\n\n    /// Unchecked cast to the passed type\n    ///\n    /// # Safety\n    /// The requires the box to be of the asserted type\n    pub unsafe fn cast<U: Boxed>(self) -> Gc<U> {\n        Gc {\n            inner: self.inner.cast::<U>(),\n        }\n    }\n\n    /// Returns a pointer to the garbage collected box\n    pub fn as_ptr(self) -> *const T {\n        self.inner.as_ptr()\n    }\n\n    /// Returns a mutable to the garbage collected box\n    pub(super) fn as_mut_ptr(self) -> *mut T {\n        self.inner.as_ptr()\n    }\n}\n\nimpl<T: Boxed> PartialEq for Gc<T>\nwhere\n    T: PartialEq,\n{\n    fn eq(&self, other: &Gc<T>) -> bool {\n        unsafe { *self.as_ptr() == *other.as_ptr() }\n    }\n}\n\nimpl<T> Eq for Gc<T> where T: Boxed + Eq {}\n\nimpl<T: Boxed> hash::Hash for Gc<T>\nwhere\n    T: hash::Hash,\n{\n    fn hash<H: hash::Hasher>(&self, state: &mut H) {\n        unsafe { (*self.as_ptr()).hash(state) }\n    }\n}\n\nimpl<T: Boxed> fmt::Debug for Gc<T>\nwhere\n    T: fmt::Debug,\n{\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        unsafe { (*self.as_ptr()).fmt(formatter) }\n    }\n}\n\nmacro_rules! define_marker_ref {\n    (\n        $(#[$docs:meta])*\n        $ref_name:ident\n    ) => {\n        $(#[$docs])*\n        #[repr(transparent)]\n        pub struct $ref_name<T: Boxed> {\n            inner: ptr::NonNull<T>,\n        }\n\n        impl<T: Boxed> Deref for $ref_name<T> {\n            type Target = T;\n            fn deref(&self) -> &T {\n                unsafe { self.inner.as_ref() }\n            }\n        }\n\n        impl<T: Boxed> From<$ref_name<T>> for Gc<T> {\n            fn from(marker_ref: $ref_name<T>) -> Gc<T> {\n                Gc {\n                    inner: marker_ref.inner,\n                }\n            }\n        }\n    };\n}\n\ndefine_marker_ref!(\n    /// Special marker ref for parameters that are explicitly not captured\n    ///\n    /// This can be used for performance-sensitive functions where the compiler cannot prove the\n    /// parameter can't be captured.\n    NoCapture\n);\n\ndefine_marker_ref!(\n    /// Special marker ref for parameters that are explicitly captured\n    ///\n    /// Capturing GC managed values is usually not allowed as the captured values become invisible\n    /// to the garbage collector and will become invalid on the next collection cycle. This is\n    /// intended for use by special runtime functions that expose their captured values to the\n    /// collector via an internal mechanism.\n    Capture\n);\n"
  },
  {
    "path": "runtime/boxed/types/char.rs",
    "content": "use std::fmt;\nuse std::hash::{Hash, Hasher};\n\nuse crate::boxed::*;\n\n/// Boxed Unicode character\n///\n/// This corresponds more precisely to a\n/// [Unicode scalar value](http://www.unicode.org/glossary/#unicode_scalar_value).\n#[repr(C, align(16))]\npub struct Char {\n    header: Header,\n    value: char,\n}\n\nimpl Boxed for Char {}\nimpl UniqueTagged for Char {}\n\nimpl Char {\n    /// Constructs a new character\n    pub fn new(heap: &mut impl AsHeap, value: char) -> Gc<Char> {\n        heap.as_heap_mut().place_box(Char {\n            header: Self::TYPE_TAG.to_heap_header(Self::size()),\n            value,\n        })\n    }\n\n    /// Returns the box size for characters\n    pub fn size() -> BoxSize {\n        BoxSize::Size16\n    }\n\n    /// Returns the unboxed value of this character\n    pub fn value(&self) -> char {\n        self.value\n    }\n}\n\nimpl PartialEq for Char {\n    fn eq(&self, other: &Char) -> bool {\n        self.value == other.value\n    }\n}\n\nimpl Hash for Char {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Self::TYPE_TAG.hash(state);\n        self.value().hash(state)\n    }\n}\n\nimpl fmt::Debug for Char {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        write!(formatter, \"Char({:?})\", self.value)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::heap::Heap;\n    use std::mem;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(16, mem::size_of::<Char>());\n    }\n\n    #[test]\n    fn equality() {\n        let mut heap = Heap::empty();\n\n        let boxed_a1 = Char::new(&mut heap, 'a');\n        let boxed_a2 = Char::new(&mut heap, 'a');\n        let boxed_b = Char::new(&mut heap, 'b');\n\n        assert_ne!(boxed_a1, boxed_b);\n        assert_eq!(boxed_a1, boxed_a2);\n    }\n\n    #[test]\n    fn fmt_debug() {\n        let mut heap = Heap::empty();\n\n        let boxed_a = Char::new(&mut heap, 'a');\n        assert_eq!(\"Char('a')\", format!(\"{:?}\", boxed_a));\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/field_value.rs",
    "content": "use std::hash::{Hash, Hasher};\nuse std::ptr;\n\nuse crate::boxed;\nuse crate::boxed::prelude::*;\nuse crate::boxed::refs::Gc;\nuse crate::class_map;\nuse crate::intern::InternedSym;\n\n/// Field within a record value\npub enum FieldValue {\n    /// Unboxed boolean\n    Bool(bool),\n    /// Unboxed Unicode character\n    Char(char),\n    /// Unboxed 64bit floating point value\n    Float(f64),\n    /// Unboxed 64bit signed integer\n    Int(i64),\n    /// Interned symbol\n    InternedSym(InternedSym),\n    /// Boxed garbage collected value\n    Boxed(Gc<boxed::Any>),\n}\n\npub(crate) enum FieldGcRef {\n    InternedSym(&'static mut InternedSym),\n    Boxed(&'static mut Gc<boxed::Any>),\n}\n\nimpl PartialEqInHeap for FieldValue {\n    fn eq_in_heap(&self, heap: &boxed::Heap, other: &FieldValue) -> bool {\n        match (self, other) {\n            (FieldValue::Bool(sv), FieldValue::Bool(ov)) => sv == ov,\n            (FieldValue::Char(sv), FieldValue::Char(ov)) => sv == ov,\n            (FieldValue::Float(sv), FieldValue::Float(ov)) => sv == ov,\n            (FieldValue::Int(sv), FieldValue::Int(ov)) => sv == ov,\n            (FieldValue::InternedSym(sv), FieldValue::InternedSym(ov)) => sv == ov,\n            (FieldValue::Boxed(sv), FieldValue::Boxed(ov)) => sv.eq_in_heap(heap, ov),\n            _ => false,\n        }\n    }\n}\n\nimpl HashInHeap for FieldValue {\n    fn hash_in_heap<H: Hasher>(&self, heap: &boxed::Heap, state: &mut H) {\n        match self {\n            FieldValue::Bool(v) => (*v).hash(state),\n            FieldValue::Char(v) => (*v).hash(state),\n            FieldValue::Float(v) => {\n                // See `boxed::Float::hash`\n                if *v == 0.0 {\n                    state.write_u64((0.0f64).to_bits())\n                } else {\n                    state.write_u64(v.to_bits());\n                }\n            }\n            FieldValue::Int(v) => (*v).hash(state),\n            FieldValue::InternedSym(v) => (*v).hash(state),\n            FieldValue::Boxed(v) => v.hash_in_heap(heap, state),\n        }\n    }\n}\n\n/// Iterates over fields in a record value\npub struct FieldValueIter<'cm> {\n    pub(super) classmap_field_iter: class_map::FieldIterator<'cm>,\n    pub(super) record_data: *const u8,\n}\n\nimpl<'cm> Iterator for FieldValueIter<'cm> {\n    type Item = FieldValue;\n\n    fn next(&mut self) -> Option<FieldValue> {\n        use class_map::FieldType;\n\n        self.classmap_field_iter\n            .next()\n            .map(|classmap_field| unsafe {\n                let field_base_ptr = self.record_data.add(classmap_field.offset());\n\n                match classmap_field.field_type() {\n                    FieldType::Bool => FieldValue::Bool(*(field_base_ptr as *const bool)),\n                    FieldType::Char => FieldValue::Char(*(field_base_ptr as *const char)),\n                    FieldType::Float => FieldValue::Float(*(field_base_ptr as *const f64)),\n                    FieldType::Int => FieldValue::Int(*(field_base_ptr as *const i64)),\n                    FieldType::InternedSym => {\n                        FieldValue::InternedSym(*(field_base_ptr as *const InternedSym))\n                    }\n                    FieldType::Boxed => {\n                        FieldValue::Boxed(*(field_base_ptr as *const Gc<boxed::Any>))\n                    }\n                }\n            })\n    }\n}\n\npub(crate) struct FieldGcRefIter<'cm> {\n    pub(super) classmap_field_iter: class_map::FieldIterator<'cm>,\n    pub(super) record_data: *const u8,\n}\n\nimpl<'cm> FieldGcRefIter<'cm> {\n    pub(crate) fn empty() -> FieldGcRefIter<'static> {\n        FieldGcRefIter {\n            classmap_field_iter: class_map::FieldIterator::empty(),\n            record_data: ptr::null(),\n        }\n    }\n}\n\nimpl<'cm> Iterator for FieldGcRefIter<'cm> {\n    type Item = FieldGcRef;\n\n    fn next(&mut self) -> Option<FieldGcRef> {\n        while let Some(classmap_field) = self.classmap_field_iter.next() {\n            unsafe {\n                use class_map::FieldType;\n                let field_base_ptr = self.record_data.add(classmap_field.offset());\n\n                match classmap_field.field_type() {\n                    FieldType::InternedSym => {\n                        return Some(FieldGcRef::InternedSym(\n                            &mut *(field_base_ptr as *mut InternedSym),\n                        ));\n                    }\n                    FieldType::Boxed => {\n                        return Some(FieldGcRef::Boxed(\n                            &mut *(field_base_ptr as *mut Gc<boxed::Any>),\n                        ));\n                    }\n                    _ => {}\n                }\n            }\n        }\n\n        None\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/float.rs",
    "content": "use std::fmt;\nuse std::hash::{Hash, Hasher};\n\nuse crate::boxed::refs::Gc;\nuse crate::boxed::*;\n\n/// Boxed 64bit floating point value\n#[repr(C, align(16))]\npub struct Float {\n    header: Header,\n    value: f64,\n}\n\nimpl Boxed for Float {}\nimpl UniqueTagged for Float {}\n\nimpl Float {\n    /// Constructs a new float\n    pub fn new(heap: &mut impl AsHeap, value: f64) -> Gc<Float> {\n        heap.as_heap_mut().place_box(Float {\n            header: Self::TYPE_TAG.to_heap_header(Self::size()),\n            value,\n        })\n    }\n\n    /// Returns the box size for floats\n    pub fn size() -> BoxSize {\n        BoxSize::Size16\n    }\n\n    /// Returns the unboxed value of this float\n    pub fn value(&self) -> f64 {\n        self.value\n    }\n}\n\nimpl PartialEq for Float {\n    fn eq(&self, other: &Float) -> bool {\n        self.value() == other.value()\n    }\n}\n\nimpl Hash for Float {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Self::TYPE_TAG.hash(state);\n\n        let value = self.value();\n        if value == 0.0 {\n            // 0.0 == -0.0 so they need to hash to the same value\n            state.write_u64((0.0f64).to_bits())\n        } else {\n            // NaNs will mostly map to the same value which is allowed but also a collision\n            state.write_u64(value.to_bits());\n        }\n    }\n}\n\nimpl fmt::Debug for Float {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        write!(formatter, \"Float({:?})\", self.value)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::heap::Heap;\n    use std::mem;\n\n    fn calc_hash(value: f64) -> u64 {\n        use std::collections::hash_map::DefaultHasher;\n\n        let mut heap = Heap::empty();\n        let boxed_float = Float::new(&mut heap, value);\n\n        let mut hasher = DefaultHasher::new();\n        boxed_float.hash(&mut hasher);\n        hasher.finish()\n    }\n\n    #[test]\n    fn sizes() {\n        assert_eq!(16, mem::size_of::<Float>());\n    }\n\n    #[test]\n    fn equality() {\n        let mut heap = Heap::empty();\n\n        let boxed_one1 = Float::new(&mut heap, 1.0);\n        let boxed_one2 = Float::new(&mut heap, 1.0);\n        let boxed_two = Float::new(&mut heap, 2.0);\n\n        assert_ne!(boxed_one1, boxed_two);\n        assert_eq!(boxed_one1, boxed_one2);\n    }\n\n    #[test]\n    fn hash() {\n        let minus_zero_hash = calc_hash(-0.0);\n        let plus_zero_hash = calc_hash(0.0);\n        let plus_one_hash = calc_hash(1.0);\n\n        assert_ne!(plus_one_hash, minus_zero_hash);\n        assert_eq!(plus_zero_hash, minus_zero_hash);\n    }\n\n    #[test]\n    fn fmt_debug() {\n        let mut heap = Heap::empty();\n\n        let boxed_one = Float::new(&mut heap, 1.0);\n        assert_eq!(\"Float(1.0)\", format!(\"{:?}\", boxed_one));\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/fun.rs",
    "content": "use std::fmt;\nuse std::hash::{Hash, Hasher};\n\nuse crate::boxed::refs::Gc;\nuse crate::boxed::*;\nuse crate::task;\n\n/// Opaque type for a function's captures\n///\n/// This has a meaning specific to the implementation of the function. This may be a dummy value\n/// (typically [`Nil`]) for functions that don't capture, a single boxed value or a collection of\n/// multiple boxed values. The only external contract is that it must be a boxed value to allow for\n/// garbage collection.\npub type Captures = Gc<Any>;\n\n/// Entry point for executing a function\npub type ThunkEntry = extern \"C\" fn(&mut task::Task, Captures, Gc<Any>) -> Gc<Any>;\n\n/// Boxed function value with optional captures\n///\n/// This is typically used in places where functions are used as values or stored in collections.\n/// For example, placing a function in a list will create a `FunThunk`. When taking an function as a\n/// parameter to an RFI function it's typically better to use a typed\n/// [`callback::Callback`](crate::callback::Callback).\n#[repr(C, align(16))]\npub struct FunThunk {\n    header: Header,\n    pub(crate) captures: Captures,\n    entry: ThunkEntry,\n}\n\nimpl Boxed for FunThunk {}\nimpl UniqueTagged for FunThunk {}\n\nimpl FunThunk {\n    /// Constructs a new function value with the given captures and entry point\n    pub fn new(heap: &mut impl AsHeap, captures: Captures, entry: ThunkEntry) -> Gc<FunThunk> {\n        heap.as_heap_mut().place_box(FunThunk {\n            header: Self::TYPE_TAG.to_heap_header(Self::size()),\n            captures,\n            entry,\n        })\n    }\n\n    /// Returns the box size for functions\n    pub fn size() -> BoxSize {\n        BoxSize::Size32\n    }\n\n    /// Applies this function on the passed task with the given arguments\n    pub fn apply(&self, task: &mut task::Task, arg_list: Gc<Any>) -> Gc<Any> {\n        (self.entry)(task, self.captures, arg_list)\n    }\n}\n\nimpl PartialEq for FunThunk {\n    fn eq(&self, _: &FunThunk) -> bool {\n        // There is no reliable way to compare functions so they're always inequal\n        false\n    }\n}\n\nimpl Eq for FunThunk {}\n\nimpl Hash for FunThunk {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Self::TYPE_TAG.hash(state);\n        state.write_usize(self as *const _ as usize);\n    }\n}\n\nimpl fmt::Debug for FunThunk {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        write!(formatter, \"FunThunk({:p})\", self)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed;\n    use crate::boxed::heap::Heap;\n    use std::mem;\n\n    extern \"C\" fn identity_entry(\n        _: &mut task::Task,\n        _captures: Captures,\n        rest: Gc<Any>,\n    ) -> Gc<Any> {\n        rest\n    }\n\n    extern \"C\" fn return_42_entry(\n        task: &mut task::Task,\n        _captures: Captures,\n        _rest: Gc<Any>,\n    ) -> Gc<Any> {\n        Int::new(task, 32).as_any_ref()\n    }\n\n    #[test]\n    fn sizes() {\n        assert_eq!(32, mem::size_of::<FunThunk>());\n    }\n\n    #[test]\n    fn equality() {\n        let mut heap = Heap::empty();\n\n        let nil_captures = boxed::NIL_INSTANCE.as_any_ref();\n        let boxed_identity1 = FunThunk::new(&mut heap, nil_captures, identity_entry);\n        let boxed_identity2 = FunThunk::new(&mut heap, nil_captures, identity_entry);\n        let boxed_return = FunThunk::new(&mut heap, nil_captures, return_42_entry);\n\n        assert_ne!(boxed_identity1, boxed_return);\n        // We use pointer identity for now\n        assert_ne!(boxed_identity1, boxed_identity2);\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/int.rs",
    "content": "use std::fmt;\nuse std::hash::{Hash, Hasher};\n\nuse crate::boxed::refs::Gc;\nuse crate::boxed::*;\n\n/// Boxed 64bit signed integer\n#[repr(C, align(16))]\npub struct Int {\n    header: Header,\n    value: i64,\n}\n\nimpl Boxed for Int {}\nimpl UniqueTagged for Int {}\n\nimpl Int {\n    /// Constructs a new integer\n    pub fn new(heap: &mut impl AsHeap, value: i64) -> Gc<Int> {\n        heap.as_heap_mut().place_box(Int {\n            header: Self::TYPE_TAG.to_heap_header(Self::size()),\n            value,\n        })\n    }\n\n    /// Returns the box size for integers\n    pub fn size() -> BoxSize {\n        BoxSize::Size16\n    }\n\n    /// Returns the unboxed value of this integer\n    pub fn value(&self) -> i64 {\n        self.value\n    }\n}\n\nimpl PartialEq for Int {\n    fn eq(&self, other: &Int) -> bool {\n        self.value() == other.value()\n    }\n}\n\nimpl Hash for Int {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Self::TYPE_TAG.hash(state);\n        self.value().hash(state)\n    }\n}\n\nimpl fmt::Debug for Int {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        write!(formatter, \"Int({:?})\", self.value)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::heap::Heap;\n    use std::mem;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(16, mem::size_of::<Int>());\n    }\n\n    #[test]\n    fn equality() {\n        let mut heap = Heap::empty();\n\n        let boxed_one1 = Int::new(&mut heap, 1);\n        let boxed_one2 = Int::new(&mut heap, 1);\n        let boxed_two = Int::new(&mut heap, 2);\n\n        assert_ne!(boxed_one1, boxed_two);\n        assert_eq!(boxed_one1, boxed_one2);\n    }\n\n    #[test]\n    fn fmt_debug() {\n        let mut heap = Heap::empty();\n\n        let boxed_one = Int::new(&mut heap, 1);\n        assert_eq!(\"Int(1)\", format!(\"{:?}\", boxed_one));\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/list.rs",
    "content": "use std::fmt;\nuse std::hash::{Hash, Hasher};\nuse std::iter::FusedIterator;\nuse std::marker::PhantomData;\n\nuse crate::abitype::{BoxedAbiType, EncodeBoxedAbiType};\nuse crate::boxed::refs::Gc;\nuse crate::boxed::*;\n\n/// Non-empty list\n#[repr(C, align(16))]\npub struct Pair<T: Boxed = Any> {\n    header: Header,\n    list_len: i64,\n    pub(crate) head: Gc<T>,\n    pub(crate) rest: Gc<List<T>>,\n}\n\nimpl<T: Boxed> Boxed for Pair<T> {}\nimpl<T: Boxed> EncodeBoxedAbiType for Pair<T>\nwhere\n    T: EncodeBoxedAbiType,\n{\n    const BOXED_ABI_TYPE: BoxedAbiType = BoxedAbiType::Pair(&T::BOXED_ABI_TYPE);\n}\n\nimpl<T: Boxed> Pair<T> {\n    /// Constructs a pair with the given `head` and `rest`\n    pub fn new(heap: &mut impl AsHeap, head: Gc<T>, rest: Gc<List<T>>) -> Gc<Pair<T>> {\n        heap.as_heap_mut().place_box(Pair {\n            header: Pair::TYPE_TAG.to_heap_header(Self::size()),\n            head,\n            rest,\n            list_len: (rest.len() + 1) as i64,\n        })\n    }\n\n    /// Returns the box size for pairs\n    pub fn size() -> BoxSize {\n        BoxSize::Size32\n    }\n\n    /// Returns the length of the list this pair is the head of\n    ///\n    /// Note that this must be at least 1.\n    pub fn len(&self) -> usize {\n        self.list_len as usize\n    }\n\n    /// Returns false\n    pub fn is_empty(&self) -> bool {\n        // This is to make Clippy happy since we have `len`\n        false\n    }\n\n    /// Returns the head value\n    pub fn head(&self) -> Gc<T> {\n        self.head\n    }\n\n    /// Returns the tail list\n    pub fn rest(&self) -> Gc<List<T>> {\n        self.rest\n    }\n\n    /// Casts this pair to a non-empty list\n    pub fn as_list_ref(&self) -> Gc<List<T>> {\n        unsafe { Gc::new(&*(self as *const _ as *const List<T>)) }\n    }\n}\n\nimpl<T: Boxed> PartialEqInHeap for Pair<T> {\n    fn eq_in_heap(&self, heap: &Heap, rhs: &Pair<T>) -> bool {\n        self.head.eq_in_heap(heap, &rhs.head) && self.rest.eq_in_heap(heap, &rhs.rest)\n    }\n}\n\nimpl<T: Boxed> HashInHeap for Pair<T> {\n    fn hash_in_heap<H: Hasher>(&self, task: &Heap, state: &mut H) {\n        TypeTag::Pair.hash(state);\n        self.head().hash_in_heap(task, state);\n        self.rest().hash_in_heap(task, state);\n    }\n}\n\nimpl<T: Boxed> fmt::Debug for Pair<T> {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        self.as_list_ref().fmt(formatter)\n    }\n}\n\n/// List of boxed values\n///\n/// This allows O(n) access to its elements. It has the benefit of allowing constant time prepends\n/// while sharing the tail of the existing list.\n#[repr(C, align(16))]\npub struct List<T: Boxed = Any> {\n    header: Header,\n    list_len: i64,\n    phantom: PhantomData<T>,\n}\n\nimpl<T: Boxed> Boxed for List<T> {}\n\nimpl DistinctTagged for List<Any> {\n    fn has_tag(type_tag: TypeTag) -> bool {\n        [TypeTag::Pair, TypeTag::Nil].contains(&type_tag)\n    }\n}\n\nimpl<T: Boxed> EncodeBoxedAbiType for List<T>\nwhere\n    T: EncodeBoxedAbiType,\n{\n    const BOXED_ABI_TYPE: BoxedAbiType = BoxedAbiType::List(&T::BOXED_ABI_TYPE);\n}\n\n/// Possible subtypes of [`List`]\npub enum ListSubtype<'a, T: Boxed> {\n    /// Non-empty list\n    Pair(&'a Pair<T>),\n    /// Empty list\n    Nil,\n}\n\nimpl<T: Boxed> List<T> {\n    /// Constructs a new fixed sized list containing the passed `elems`\n    pub fn new(heap: &mut impl AsHeap, elems: impl ExactSizeIterator<Item = Gc<T>>) -> Gc<List<T>> {\n        Self::new_with_tail(heap, elems, Self::empty())\n    }\n\n    /// Constructs a list with a head of `elems` and the specified tail list\n    pub fn new_with_tail(\n        heap: &mut impl AsHeap,\n        elems: impl ExactSizeIterator<Item = Gc<T>>,\n        tail: Gc<List<T>>,\n    ) -> Gc<List<T>> {\n        let elems_len = elems.len();\n        let tail_len = tail.len();\n\n        if elems_len == 0 {\n            return tail;\n        }\n\n        // Allocate the entire list at once\n        let heap_alloc = heap\n            .as_heap_mut()\n            .alloc_cells(Pair::<T>::size().cell_count() * elems_len);\n\n        unsafe {\n            let pair_alloc = heap_alloc as *mut Pair<T>;\n\n            for (i, head) in elems.enumerate() {\n                let elems_remaining = elems_len - i;\n\n                let rest = if elems_remaining == 1 {\n                    tail\n                } else {\n                    (&*pair_alloc.add(i + 1)).as_list_ref()\n                };\n\n                *pair_alloc.add(i) = Pair {\n                    header: Pair::TYPE_TAG.to_heap_header(Pair::<T>::size()),\n                    head,\n                    rest,\n                    list_len: (elems_remaining + tail_len) as i64,\n                };\n            }\n\n            Gc::new(pair_alloc as *const List<T>)\n        }\n    }\n\n    /// Returns an empty list\n    pub fn empty() -> Gc<List<T>> {\n        unsafe { Gc::new(&NIL_INSTANCE as *const Nil as *const List<T>) }\n    }\n\n    /// Creates a list by constructing an iterator of values\n    pub fn from_values<V, F>(\n        heap: &mut impl AsHeap,\n        values: impl Iterator<Item = V>,\n        cons: F,\n    ) -> Gc<List<T>>\n    where\n        F: Fn(&mut Heap, V) -> Gc<T>,\n    {\n        let heap = heap.as_heap_mut();\n\n        let elems: Vec<Gc<T>> = values.map(|v| cons(heap, v)).collect();\n        Self::new(heap, elems.into_iter())\n    }\n\n    /// Returns a subtype of this list based on its type tag\n    pub fn as_subtype(&self) -> ListSubtype<'_, T> {\n        match self.header.type_tag {\n            TypeTag::Pair => {\n                ListSubtype::Pair(unsafe { &*(self as *const List<T> as *const Pair<T>) })\n            }\n            TypeTag::Nil => ListSubtype::Nil,\n            other => {\n                unreachable!(\"Unexpected type tag: {:?}\", other);\n            }\n        }\n    }\n\n    /// Returns the length of the list\n    pub fn len(&self) -> usize {\n        self.list_len as usize\n    }\n\n    /// Returns true if the list is empty\n    pub fn is_empty(&self) -> bool {\n        self.header.type_tag == TypeTag::Nil\n    }\n\n    /// Returns an iterator to the list's values\n    pub fn iter(&self) -> ListIterator<T> {\n        ListIterator {\n            head: unsafe { Gc::new(self as *const Self) },\n        }\n    }\n}\n\nimpl<T: Boxed> PartialEqInHeap for List<T> {\n    fn eq_in_heap(&self, heap: &Heap, other: &List<T>) -> bool {\n        if self.len() != other.len() {\n            return false;\n        }\n\n        self.iter()\n            .zip(other.iter())\n            .all(|(self_value, other_value)| self_value.eq_in_heap(heap, &other_value))\n    }\n}\n\nimpl<T: Boxed> HashInHeap for List<T> {\n    fn hash_in_heap<H: Hasher>(&self, heap: &Heap, state: &mut H) {\n        match self.as_subtype() {\n            ListSubtype::Pair(pair) => pair.hash_in_heap(heap, state),\n            ListSubtype::Nil => NIL_INSTANCE.hash_in_heap(heap, state),\n        }\n    }\n}\n\nimpl<T: Boxed> fmt::Debug for List<T> {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        formatter.write_str(\"List(\")?;\n        formatter.debug_list().entries(self.iter()).finish()?;\n        formatter.write_str(\")\")\n    }\n}\n\npub struct ListIterator<T: Boxed> {\n    head: Gc<List<T>>,\n}\n\nimpl<T: Boxed> Iterator for ListIterator<T> {\n    type Item = Gc<T>;\n\n    fn next(&mut self) -> Option<Gc<T>> {\n        // If we use `head` directly the borrow checker gets suspicious\n        let head = unsafe { &*(self.head.as_ptr()) };\n\n        match head.as_subtype() {\n            ListSubtype::Pair(pair) => {\n                self.head = pair.rest;\n                Some(pair.head)\n            }\n            ListSubtype::Nil => None,\n        }\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        (self.head.len(), Some(self.head.len()))\n    }\n}\n\nimpl<T: Boxed> ExactSizeIterator for ListIterator<T> {}\nimpl<T: Boxed> FusedIterator for ListIterator<T> {}\n\n/// Empty list\n#[repr(C, align(16))]\n#[derive(Debug)]\npub struct Nil {\n    header: Header,\n    list_len: usize,\n}\n\n/// Static constant instance of [`Nil`]\n#[export_name = \"ARRET_NIL\"]\npub static NIL_INSTANCE: Nil = Nil {\n    header: Header {\n        type_tag: TypeTag::Nil,\n        alloc_type: AllocType::Const,\n    },\n    list_len: 0,\n};\n\nimpl Boxed for Nil {}\nimpl UniqueTagged for Nil {}\n\nimpl PartialEq for Nil {\n    fn eq(&self, _: &Nil) -> bool {\n        true\n    }\n}\n\nimpl Hash for Nil {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Self::TYPE_TAG.hash(state);\n        state.write_usize(&NIL_INSTANCE as *const _ as usize);\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::heap::Heap;\n    use crate::boxed::Int;\n    use std::mem;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(16, mem::size_of::<Nil>());\n        assert_eq!(16, mem::size_of::<List<Any>>());\n        assert_eq!(32, mem::size_of::<Pair<Any>>());\n    }\n\n    #[test]\n    fn equality() {\n        use crate::boxed::Int;\n\n        let mut heap = Heap::empty();\n\n        let forward_list1 = List::from_values(&mut heap, [1, 2, 3].iter().cloned(), Int::new);\n        let forward_list2 = List::from_values(&mut heap, [1, 2, 3].iter().cloned(), Int::new);\n        let reverse_list = List::from_values(&mut heap, [3, 2, 1].iter().cloned(), Int::new);\n\n        assert!(!forward_list1.eq_in_heap(&heap, &reverse_list));\n        assert!(forward_list1.eq_in_heap(&heap, &forward_list2));\n    }\n\n    #[test]\n    fn fmt_debug() {\n        let mut heap = Heap::empty();\n        let forward_list = List::from_values(&mut heap, [1, 2, 3].iter().cloned(), Int::new);\n\n        assert_eq!(\n            \"List([Int(1), Int(2), Int(3)])\",\n            format!(\"{:?}\", forward_list)\n        );\n    }\n\n    #[test]\n    fn construct_and_iter() {\n        let mut heap = Heap::empty();\n\n        let boxed_list = List::from_values(&mut heap, [1, 2, 3].iter().cloned(), Int::new);\n\n        let mut boxed_list_iter = boxed_list.iter();\n        assert_eq!(3, boxed_list_iter.len());\n\n        for expected_num in &[1, 2, 3] {\n            if let Some(boxed_int) = boxed_list_iter.next() {\n                assert_eq!(*expected_num, boxed_int.value());\n            } else {\n                panic!(\"Iterator unexpectedly ended\");\n            }\n        }\n\n        assert_eq!(0, boxed_list_iter.len());\n        assert!(!boxed_list_iter.next().is_some());\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/map.rs",
    "content": "use std::fmt;\nuse std::hash::{Hash, Hasher};\nuse std::marker::PhantomData;\n\nuse crate::abitype::{BoxedAbiType, EncodeBoxedAbiType};\nuse crate::boxed::refs::Gc;\nuse crate::boxed::*;\n\n/// Immutable map of boxed values\n#[repr(C, align(16))]\npub struct Map<K: Boxed = Any, V: Boxed = Any> {\n    header: Header,\n    _key: PhantomData<K>,\n    _value: PhantomData<V>,\n}\n\nimpl<K: Boxed, V: Boxed> Boxed for Map<K, V> {}\n\nimpl<K: Boxed, V: Boxed> Map<K, V> {\n    /// Constructs a new map with the given values\n    pub fn new(\n        heap: &mut impl AsHeap,\n        values: impl ExactSizeIterator<Item = (Gc<K>, Gc<V>)>,\n    ) -> Gc<Map<K, V>> {\n        if values.len() != 0 {\n            todo!(\"non-empty maps\");\n        }\n\n        heap.as_heap_mut().place_box(Map {\n            header: Map::TYPE_TAG.to_heap_header(Self::size()),\n            _key: PhantomData,\n            _value: PhantomData,\n        })\n    }\n\n    /// Constructs a map by constructing an iterator of values\n    pub fn from_values<T, F>(\n        heap: &mut impl AsHeap,\n        values: impl ExactSizeIterator<Item = T>,\n        _cons: F,\n    ) -> Gc<Map<K, V>>\n    where\n        F: Fn(&mut Heap, T) -> (Gc<K>, Gc<V>),\n    {\n        if values.len() != 0 {\n            todo!(\"non-empty maps\");\n        }\n\n        heap.as_heap_mut().place_box(Map {\n            header: Map::TYPE_TAG.to_heap_header(Self::size()),\n            _key: PhantomData,\n            _value: PhantomData,\n        })\n    }\n\n    /// Returns the box size for maps\n    pub fn size() -> BoxSize {\n        BoxSize::Size16\n    }\n\n    /// Return if the map is empty\n    pub fn is_empty(&self) -> bool {\n        true\n    }\n\n    /// Returns the number of the entries in the map\n    pub fn len(&self) -> usize {\n        0\n    }\n\n    /// Returns an iterator over the entries in map\n    pub fn iter(&self) -> impl Iterator<Item = (Gc<K>, Gc<V>)> + '_ {\n        std::iter::empty()\n    }\n}\n\nimpl<K: Boxed, V: Boxed> PartialEqInHeap for Map<K, V> {\n    fn eq_in_heap(&self, _heap: &Heap, _other: &Map<K, V>) -> bool {\n        // Both maps must be empty\n        true\n    }\n}\n\nimpl<K: Boxed, V: Boxed> HashInHeap for Map<K, V> {\n    fn hash_in_heap<H: Hasher>(&self, _heap: &Heap, state: &mut H) {\n        TypeTag::Map.hash(state);\n    }\n}\n\nimpl<K: Boxed, V: Boxed> fmt::Debug for Map<K, V> {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        formatter.write_str(\"Map(\")?;\n        formatter.debug_list().entries(self.iter()).finish()?;\n        formatter.write_str(\")\")\n    }\n}\n\nimpl<K: Boxed, V: Boxed> EncodeBoxedAbiType for Map<K, V>\nwhere\n    K: EncodeBoxedAbiType,\n    V: EncodeBoxedAbiType,\n{\n    const BOXED_ABI_TYPE: BoxedAbiType = BoxedAbiType::Map(&K::BOXED_ABI_TYPE, &V::BOXED_ABI_TYPE);\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use std::mem;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(16, mem::size_of::<Map<Any>>());\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/mod.rs",
    "content": "pub mod char;\npub mod field_value;\npub mod float;\npub mod fun;\npub mod int;\npub mod list;\npub mod map;\npub mod record;\npub mod record_data;\npub mod set;\npub mod shared_str;\npub mod str;\npub mod sym;\npub mod vector;\n"
  },
  {
    "path": "runtime/boxed/types/record.rs",
    "content": "use std::alloc;\nuse std::hash::{Hash, Hasher};\nuse std::mem::MaybeUninit;\nuse std::{fmt, mem};\n\nuse crate::boxed::refs::Gc;\nuse crate::boxed::types::field_value::FieldGcRefIter;\nuse crate::boxed::*;\n\n/// Numeric ID indicating which class the record belongs to\n///\n/// This is used to distinguish record types before each other.\npub type RecordClassId = u32;\n\n#[repr(C)]\nstruct RecordHeader {\n    header: Header,\n    inline_byte_len: u8,\n    may_contain_gc_refs: bool,\n    class_id: RecordClassId,\n}\n\n/// Describes the storage of a record's data\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum RecordStorage {\n    /// Record data is stored inline in a box of the given size\n    Inline(BoxSize),\n    /// Record data is stored out-of-line in a 32 byte box\n    External,\n}\n\nimpl RecordStorage {\n    /// Returns the box size for a record storage\n    pub fn box_size(self) -> BoxSize {\n        match self {\n            RecordStorage::Inline(box_size) => box_size,\n            RecordStorage::External => BoxSize::Size32,\n        }\n    }\n}\n\n/// User-defined record type\n#[repr(C, align(16))]\npub struct Record {\n    record_header: RecordHeader,\n    padding: [u8; Record::MAX_INLINE_BYTES],\n}\n\nimpl Boxed for Record {}\n\nimpl Record {\n    /// Maximum number of bytes that can be stored directly in a box\n    pub const MAX_INLINE_BYTES: usize = 24;\n\n    /// Inline byte length used for external vectors\n    pub const EXTERNAL_INLINE_LEN: u8 = (Self::MAX_INLINE_BYTES as u8) + 1;\n\n    /// Alignment of our inline record data in bytes\n    const INLINE_DATA_ALIGNMENT: usize = 8;\n\n    /// Constructs a new record of the given class and initialises it with the passed data\n    pub fn new(heap: &mut impl AsHeap, class_id: RecordClassId, data: RecordData) -> Gc<Record> {\n        let storage = Self::storage_for_data_layout(data.layout());\n        let box_size = storage.box_size();\n\n        let boxed = unsafe {\n            match storage {\n                RecordStorage::External => {\n                    mem::transmute(ExternalRecord::new(box_size, class_id, data))\n                }\n                RecordStorage::Inline(_) => {\n                    mem::transmute(InlineRecord::new(box_size, class_id, data))\n                }\n            }\n        };\n\n        heap.as_heap_mut().place_box(boxed)\n    }\n\n    /// Returns the storage for given data layout\n    pub fn storage_for_data_layout(data_layout: Option<alloc::Layout>) -> RecordStorage {\n        match data_layout {\n            None => RecordStorage::Inline(BoxSize::Size16),\n            Some(data_layout) => {\n                if data_layout.align() > Self::INLINE_DATA_ALIGNMENT {\n                    // Requires more alignment than our inline data provides\n                    return RecordStorage::External;\n                }\n\n                match data_layout.size() {\n                    0..=8 => RecordStorage::Inline(BoxSize::Size16),\n                    9..=Record::MAX_INLINE_BYTES => RecordStorage::Inline(BoxSize::Size32),\n                    _ => RecordStorage::External,\n                }\n            }\n        }\n    }\n\n    /// Returns the class ID for the record\n    pub fn class_id(&self) -> RecordClassId {\n        self.record_header.class_id\n    }\n\n    /// Returns an iterator over the record's field values\n    pub fn field_values<'cm>(&self, heap: &'cm Heap) -> FieldValueIter<'cm> {\n        let classmap_class = heap\n            .type_info()\n            .class_map()\n            .class_for_record_class_id(self.class_id());\n\n        FieldValueIter {\n            classmap_field_iter: classmap_class.field_iter(),\n            record_data: self.data_ptr(),\n        }\n    }\n\n    pub(crate) fn field_gc_refs<'cm>(&mut self, heap: &'cm Heap) -> FieldGcRefIter<'cm> {\n        if !self.record_header.may_contain_gc_refs {\n            return FieldGcRefIter::empty();\n        }\n\n        let classmap_class = heap\n            .type_info()\n            .class_map()\n            .class_for_record_class_id(self.class_id());\n\n        FieldGcRefIter {\n            classmap_field_iter: classmap_class.field_iter(),\n            record_data: self.data_ptr(),\n        }\n    }\n\n    fn data_ptr(&self) -> *const u8 {\n        match self.as_repr() {\n            Repr::Inline(inline) => inline.inline_data.as_ptr() as *const u8,\n            Repr::External(external) => external.external_data.as_ptr(),\n        }\n    }\n\n    fn is_empty(&self) -> bool {\n        self.record_header.inline_byte_len == 0\n    }\n\n    fn is_inline(&self) -> bool {\n        self.record_header.inline_byte_len <= Self::MAX_INLINE_BYTES as u8\n    }\n\n    fn as_repr(&self) -> Repr<'_> {\n        if self.is_inline() {\n            Repr::Inline(unsafe { &*(self as *const Record as *const InlineRecord) })\n        } else {\n            Repr::External(unsafe { &*(self as *const Record as *const ExternalRecord) })\n        }\n    }\n\n    fn as_repr_mut(&mut self) -> ReprMut<'_> {\n        if self.is_inline() {\n            ReprMut::Inline(unsafe { &mut *(self as *mut Record as *mut InlineRecord) })\n        } else {\n            ReprMut::External(unsafe { &mut *(self as *mut Record as *mut ExternalRecord) })\n        }\n    }\n}\n\nimpl PartialEqInHeap for Record {\n    fn eq_in_heap(&self, heap: &Heap, other: &Record) -> bool {\n        if self.class_id() != other.class_id() {\n            return false;\n        }\n\n        if self.is_empty() {\n            return true;\n        }\n\n        self.field_values(heap)\n            .zip(other.field_values(heap))\n            .all(|(self_field, other_field)| self_field.eq_in_heap(heap, &other_field))\n    }\n}\n\nimpl HashInHeap for Record {\n    fn hash_in_heap<H: Hasher>(&self, heap: &Heap, state: &mut H) {\n        Self::TYPE_TAG.hash(state);\n        self.class_id().hash(state);\n\n        if !self.is_empty() {\n            for field in self.field_values(heap) {\n                field.hash_in_heap(heap, state);\n            }\n        }\n    }\n}\n\nimpl fmt::Debug for Record {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        write!(formatter, \"Record({:?})\", self.class_id())\n    }\n}\n\n#[repr(C, align(16))]\nstruct InlineRecord {\n    record_header: RecordHeader,\n    inline_data: MaybeUninit<[u8; Record::MAX_INLINE_BYTES]>,\n}\n\nimpl InlineRecord {\n    fn new(box_size: BoxSize, class_id: RecordClassId, data: RecordData) -> InlineRecord {\n        let header = Record::TYPE_TAG.to_heap_header(box_size);\n\n        unsafe {\n            let mut inline_data = mem::MaybeUninit::<[u8; Record::MAX_INLINE_BYTES]>::uninit();\n\n            if let Some(data_layout) = data.layout() {\n                ptr::copy(\n                    data.as_ptr(),\n                    inline_data.as_mut_ptr() as *mut _,\n                    data_layout.size(),\n                );\n            }\n\n            InlineRecord {\n                record_header: RecordHeader {\n                    header,\n                    inline_byte_len: match data.layout() {\n                        Some(layout) => layout.size() as u8,\n                        None => 0,\n                    },\n                    // This is conservative - we don't know if there are GC refs or not\n                    may_contain_gc_refs: data.layout().is_some(),\n                    class_id,\n                },\n                inline_data,\n            }\n        }\n    }\n}\n\n#[repr(C, align(16))]\nstruct ExternalRecord {\n    record_header: RecordHeader,\n    external_data: RecordData,\n}\n\nimpl ExternalRecord {\n    fn new(box_size: BoxSize, class_id: RecordClassId, data: RecordData) -> ExternalRecord {\n        let header = Record::TYPE_TAG.to_heap_header(box_size);\n\n        ExternalRecord {\n            record_header: RecordHeader {\n                header,\n                inline_byte_len: std::u8::MAX,\n                // This is conservative - we don't know if there are GC refs or not\n                may_contain_gc_refs: true,\n                class_id,\n            },\n\n            external_data: data,\n        }\n    }\n}\n\nenum Repr<'a> {\n    Inline(&'a InlineRecord),\n    External(&'a ExternalRecord),\n}\n\nenum ReprMut<'a> {\n    Inline(&'a mut InlineRecord),\n    External(&'a mut ExternalRecord),\n}\n\nimpl Drop for Record {\n    fn drop(&mut self) {\n        match self.as_repr_mut() {\n            ReprMut::Inline(_) => {\n                // Do nothing here; we might've been allocated as a 16 byte box so we can't read\n                // the whole thing.\n            }\n            ReprMut::External(external) => unsafe { ptr::drop_in_place(external) },\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::heap::Heap;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(32, mem::size_of::<Record>());\n        assert_eq!(32, mem::size_of::<InlineRecord>());\n        assert_eq!(32, mem::size_of::<ExternalRecord>());\n    }\n\n    #[test]\n    fn equality() {\n        let mut heap = Heap::empty();\n\n        let record_class_one_instance1 = Record::new(&mut heap, 1, RecordData::empty());\n        let record_class_one_instance2 = Record::new(&mut heap, 1, RecordData::empty());\n        let record_class_two_instance1 = Record::new(&mut heap, 2, RecordData::empty());\n\n        assert!(record_class_one_instance1.eq_in_heap(&heap, &record_class_one_instance2));\n        assert!(!record_class_one_instance1.eq_in_heap(&heap, &record_class_two_instance1));\n    }\n\n    #[test]\n    fn fmt_debug() {\n        let mut heap = Heap::empty();\n\n        let boxed_one = Record::new(&mut heap, 1, RecordData::empty());\n        assert_eq!(\"Record(1)\", format!(\"{:?}\", boxed_one));\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/record_data.rs",
    "content": "use std::alloc;\n\n/// Allocation to store a record's data\n#[repr(C)]\npub struct RecordData {\n    data_ptr: *mut u8,\n    compact_layout: u64,\n}\n\nimpl RecordData {\n    /// Constructs an empty record data\n    pub fn empty() -> Self {\n        Self::alloc(None)\n    }\n\n    /// Allocates record data for the given layout\n    pub fn alloc(data_layout: Option<alloc::Layout>) -> Self {\n        unsafe {\n            Self {\n                data_ptr: match data_layout {\n                    Some(data_layout) => alloc::alloc(data_layout),\n                    None => std::ptr::null_mut(),\n                },\n                compact_layout: Self::alloc_layout_to_compact(data_layout),\n            }\n        }\n    }\n\n    /// Returns a pointer to the record data\n    pub fn as_ptr(&self) -> *const u8 {\n        self.data_ptr\n    }\n\n    /// Returns a mutable pointer to the record data\n    pub fn as_mut_ptr(&mut self) -> *mut u8 {\n        self.data_ptr\n    }\n\n    /// Returns the layout for the record data, or `None` if the data is empty\n    pub fn layout(&self) -> Option<alloc::Layout> {\n        Self::compact_to_alloc_layout(self.compact_layout)\n    }\n\n    /// Converts an [`alloc::Layout`] to a compact representation\n    ///\n    /// This is intended for use by the compiler.\n    pub fn alloc_layout_to_compact(alloc_layout: Option<alloc::Layout>) -> u64 {\n        match alloc_layout {\n            None => 0,\n            Some(alloc_layout) => {\n                // This allows for alignments up to 2^16 and sizes up to 2^48\n                ((alloc_layout.align() as u64) & 0xFFFF) | ((alloc_layout.size() as u64) << 16)\n            }\n        }\n    }\n\n    fn compact_to_alloc_layout(input: u64) -> Option<alloc::Layout> {\n        if input == 0 {\n            None\n        } else {\n            let align = (input & 0xFFFF) as usize;\n            let size = (input >> 16) as usize;\n\n            unsafe { Some(alloc::Layout::from_size_align_unchecked(size, align)) }\n        }\n    }\n}\n\nimpl Drop for RecordData {\n    fn drop(&mut self) {\n        if let Some(data_layout) = Self::compact_to_alloc_layout(self.compact_layout) {\n            unsafe {\n                alloc::dealloc(self.data_ptr as *mut u8, data_layout);\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn test_alloc_layout_to_u64() {\n        let u8_layout = alloc::Layout::new::<u8>();\n        let u32_layout = alloc::Layout::new::<u32>();\n        let u64_layout = alloc::Layout::new::<u64>();\n        let empty_array_layout = alloc::Layout::new::<[char; 0]>();\n        let large_array_layout = alloc::Layout::new::<[f64; 10000]>();\n\n        for layout in &[\n            u8_layout,\n            u32_layout,\n            u64_layout,\n            empty_array_layout,\n            large_array_layout,\n        ] {\n            assert_eq!(\n                Some(*layout),\n                RecordData::compact_to_alloc_layout(RecordData::alloc_layout_to_compact(Some(\n                    *layout\n                ))),\n            )\n        }\n\n        assert_eq!(\n            None,\n            RecordData::compact_to_alloc_layout(RecordData::alloc_layout_to_compact(None)),\n        )\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/set.rs",
    "content": "use std::collections::hash_map::DefaultHasher;\nuse std::hash::{Hash, Hasher};\nuse std::mem::MaybeUninit;\nuse std::{fmt, marker, mem};\n\nuse crate::abitype::{BoxedAbiType, EncodeBoxedAbiType};\nuse crate::boxed::refs::Gc;\nuse crate::boxed::*;\n\nconst MAX_16BYTE_INLINE_LEN: usize = (16 - 8) / mem::size_of::<Gc<Any>>();\nconst MAX_32BYTE_INLINE_LEN: usize = (32 - 8) / mem::size_of::<Gc<Any>>();\n\n/// Describes the storage of a set's data\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum SetStorage {\n    /// Set data is stored inline in a box of the given size\n    Inline(BoxSize),\n    /// Set data is stored out-of-line in a 32 byte box\n    External,\n}\n\nimpl SetStorage {\n    /// Returns the box size for a set storage\n    pub fn box_size(self) -> BoxSize {\n        match self {\n            SetStorage::Inline(box_size) => box_size,\n            SetStorage::External => BoxSize::Size32,\n        }\n    }\n}\n\n/// Immutable set of boxed values\n///\n/// This is semantically similar to a map of values to the unit type\n#[repr(C, align(16))]\npub struct Set<T: Boxed = Any> {\n    header: Header,\n    inline_len: u32,\n    padding: [u8; 24],\n    phantom: marker::PhantomData<T>,\n}\n\nimpl<T: Boxed> Boxed for Set<T> {}\n\nimpl<T: Boxed> Set<T> {\n    /// Maximum element length of an inline set\n    pub const MAX_INLINE_LEN: usize = MAX_32BYTE_INLINE_LEN;\n\n    /// Inline element length used for external sets\n    pub const EXTERNAL_INLINE_LEN: u32 = (Self::MAX_INLINE_LEN as u32) + 1;\n\n    /// Constructs a new set with the passed boxed values\n    pub fn new(heap: &mut impl AsHeap, values: impl ExactSizeIterator<Item = Gc<T>>) -> Gc<Set<T>> {\n        let heap = heap.as_heap_mut();\n\n        // Calculate the hash of our values\n        let mut hashed_values: Vec<(u64, Gc<T>)> = values\n            .map(|v| {\n                let mut state = DefaultHasher::new();\n                v.hash_in_heap(heap, &mut state);\n                (state.finish(), v)\n            })\n            .collect();\n\n        // Make the values sorted and unique\n        hashed_values.sort_by_key(|(hash, _)| *hash);\n        hashed_values\n            .dedup_by(|(hash1, v1), (hash2, v2)| hash1 == hash2 && v1.eq_in_heap(heap, v2));\n\n        let storage = Self::storage_for_element_len(hashed_values.len());\n        let header = Set::TYPE_TAG.to_heap_header(storage.box_size());\n\n        let boxed = unsafe {\n            match storage {\n                SetStorage::External => mem::transmute(ExternalSet::new(header, hashed_values)),\n                SetStorage::Inline(_) => mem::transmute(InlineSet::new(header, hashed_values)),\n            }\n        };\n\n        heap.place_box(boxed)\n    }\n\n    /// Returns the storage for given element length\n    fn storage_for_element_len(len: usize) -> SetStorage {\n        const MIN_32BYTE_INLINE_LEN: usize = MAX_16BYTE_INLINE_LEN + 1;\n\n        match len {\n            0..=MAX_16BYTE_INLINE_LEN => SetStorage::Inline(BoxSize::Size16),\n            MIN_32BYTE_INLINE_LEN..=MAX_32BYTE_INLINE_LEN => SetStorage::Inline(BoxSize::Size32),\n            _ => {\n                // Too big to fit inline; this needs to be external\n                SetStorage::External\n            }\n        }\n    }\n\n    /// Constructs a set by constructing an iterator of values\n    pub fn from_values<V, F>(\n        heap: &mut impl AsHeap,\n        values: impl Iterator<Item = V>,\n        cons: F,\n    ) -> Gc<Set<T>>\n    where\n        F: Fn(&mut Heap, V) -> Gc<T>,\n    {\n        let heap = heap.as_heap_mut();\n\n        let elems: Vec<Gc<T>> = values.map(|v| cons(heap, v)).collect();\n        Self::new(heap, elems.into_iter())\n    }\n\n    fn is_inline(&self) -> bool {\n        self.inline_len <= (Self::MAX_INLINE_LEN as u32)\n    }\n\n    fn as_repr(&self) -> Repr<'_, T> {\n        if self.is_inline() {\n            Repr::Inline(unsafe { &*(self as *const Set<T> as *const InlineSet<T>) })\n        } else {\n            Repr::External(unsafe { &*(self as *const Set<T> as *const ExternalSet<T>) })\n        }\n    }\n\n    fn as_repr_mut(&mut self) -> ReprMut<'_, T> {\n        if self.is_inline() {\n            ReprMut::Inline(unsafe { &mut *(self as *mut Set<T> as *mut InlineSet<T>) })\n        } else {\n            ReprMut::External(unsafe { &mut *(self as *mut Set<T> as *mut ExternalSet<T>) })\n        }\n    }\n\n    /// Returns the length of the set\n    pub fn len(&self) -> usize {\n        match self.as_repr() {\n            Repr::Inline(inline) => inline.len(),\n            Repr::External(external) => external.len(),\n        }\n    }\n\n    /// Returns true if the set is empty\n    pub fn is_empty(&self) -> bool {\n        self.inline_len == 0\n    }\n\n    /// Returns true if the passed value is included in the set\n    pub fn contains(&self, heap: &Heap, value: &Gc<T>) -> bool {\n        match self.as_repr() {\n            Repr::Inline(inline) => inline.contains(heap, value),\n            Repr::External(external) => external.contains(heap, value),\n        }\n    }\n\n    /// Returns an iterator over the set\n    pub fn iter<'a>(&'a self) -> Box<dyn ExactSizeIterator<Item = Gc<T>> + 'a> {\n        // TODO: It would be nice not to box here\n        match self.as_repr() {\n            Repr::Inline(inline) => Box::new(inline.iter()),\n            Repr::External(external) => Box::new(external.iter().copied()),\n        }\n    }\n\n    /// Returns if this set is a subset of the passed set\n    pub fn is_subset(&self, heap: &Heap, other: &Set<T>) -> bool {\n        match (self.as_repr(), other.as_repr()) {\n            (Repr::External(external_self), Repr::External(external_other)) => {\n                // Use optimised external/external logic\n                external_self.is_subset(heap, external_other)\n            }\n            _ => {\n                if self.len() > other.len() {\n                    return false;\n                }\n\n                for self_value in self.iter() {\n                    if !other.contains(heap, &self_value) {\n                        return false;\n                    }\n                }\n\n                true\n            }\n        }\n    }\n}\n\nimpl<T: Boxed> PartialEqInHeap for Set<T> {\n    fn eq_in_heap(&self, heap: &Heap, other: &Set<T>) -> bool {\n        match (self.as_repr(), other.as_repr()) {\n            (Repr::Inline(self_inline), Repr::Inline(other_inline)) => {\n                self_inline.eq_in_heap(heap, other_inline)\n            }\n            (Repr::External(self_external), Repr::External(other_external)) => {\n                self_external.eq_in_heap(heap, other_external)\n            }\n            _ => false,\n        }\n    }\n}\n\nimpl<T: Boxed> HashInHeap for Set<T> {\n    fn hash_in_heap<H: Hasher>(&self, heap: &Heap, state: &mut H) {\n        match self.as_repr() {\n            Repr::Inline(inline) => inline.hash_in_heap(heap, state),\n            Repr::External(external) => external.hash_in_heap(heap, state),\n        }\n    }\n}\n\nimpl<T: Boxed> fmt::Debug for Set<T> {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        formatter.write_str(\"Set(\")?;\n        formatter.debug_list().entries(self.iter()).finish()?;\n        formatter.write_str(\")\")\n    }\n}\n\nimpl<T: Boxed> EncodeBoxedAbiType for Set<T>\nwhere\n    T: EncodeBoxedAbiType,\n{\n    const BOXED_ABI_TYPE: BoxedAbiType = BoxedAbiType::Set(&T::BOXED_ABI_TYPE);\n}\n\n#[repr(C, align(16))]\npub struct InlineSet<T: Boxed> {\n    header: Header,\n    inline_len: u32,\n    values: [MaybeUninit<Gc<T>>; MAX_32BYTE_INLINE_LEN],\n}\n\nimpl<T: Boxed> InlineSet<T> {\n    fn new(header: Header, hashed_values: Vec<(u64, Gc<T>)>) -> InlineSet<T> {\n        let inline_len = hashed_values.len();\n\n        let mut inline_values = [MaybeUninit::uninit(); MAX_32BYTE_INLINE_LEN];\n\n        for (inline_value, (_, value)) in inline_values.iter_mut().zip(hashed_values) {\n            *inline_value = MaybeUninit::new(value);\n        }\n\n        InlineSet {\n            header,\n            inline_len: inline_len as u32,\n            values: inline_values,\n        }\n    }\n\n    fn len(&self) -> usize {\n        self.inline_len as usize\n    }\n\n    fn iter(&self) -> impl ExactSizeIterator<Item = Gc<T>> + '_ {\n        self.values[0..self.inline_len as usize]\n            .iter()\n            .map(|value| unsafe { value.assume_init() })\n    }\n\n    fn contains(&self, heap: &Heap, value: &Gc<T>) -> bool {\n        self.iter().any(|v| v.eq_in_heap(heap, value))\n    }\n\n    fn eq_in_heap(&self, heap: &Heap, other: &InlineSet<T>) -> bool {\n        if self.len() != other.len() {\n            return false;\n        }\n\n        self.iter()\n            .zip(other.iter())\n            .all(|(self_value, other_value)| self_value.eq_in_heap(heap, &other_value))\n    }\n\n    fn hash_in_heap<H: Hasher>(&self, heap: &Heap, state: &mut H) {\n        TypeTag::Set.hash(state);\n        state.write_usize(self.len());\n        for value in self.iter() {\n            value.hash_in_heap(heap, state);\n        }\n    }\n}\n\n#[repr(C, align(16))]\npub struct ExternalSet<T: Boxed> {\n    header: Header,\n    inline_len: u32,\n    sorted_hashed_values: Vec<(u64, Gc<T>)>,\n}\n\nimpl<T: Boxed> ExternalSet<T> {\n    fn new(header: Header, sorted_hashed_values: Vec<(u64, Gc<T>)>) -> ExternalSet<T> {\n        ExternalSet {\n            header,\n            inline_len: Set::<T>::EXTERNAL_INLINE_LEN,\n            sorted_hashed_values,\n        }\n    }\n\n    fn len(&self) -> usize {\n        self.sorted_hashed_values.len()\n    }\n\n    fn iter(&self) -> impl ExactSizeIterator<Item = &Gc<T>> {\n        self.sorted_hashed_values.iter().map(|(_, v)| v)\n    }\n\n    fn contains(&self, heap: &Heap, needle_value: &Gc<T>) -> bool {\n        // Hash our value\n        let mut state = DefaultHasher::new();\n        needle_value.hash_in_heap(heap, &mut state);\n        let needle_hash = state.finish();\n\n        // Do a binary search for the index\n        // This will return an arbitrary matching index if there are multiple matches\n        let arbitrary_index = if let Ok(i) = self\n            .sorted_hashed_values\n            .binary_search_by_key(&needle_hash, |(haystack_hash, _)| *haystack_hash)\n        {\n            i\n        } else {\n            return false;\n        };\n\n        // Search forwards through hash collisions, including the arbitrary index\n        let mut forwards_index = arbitrary_index;\n        loop {\n            let (hackstack_hash, haystack_value) = self.sorted_hashed_values[forwards_index];\n\n            if hackstack_hash != needle_hash {\n                break;\n            }\n            if haystack_value.eq_in_heap(heap, needle_value) {\n                return true;\n            }\n\n            forwards_index += 1;\n            if forwards_index >= self.sorted_hashed_values.len() {\n                break;\n            }\n        }\n\n        // Search backwards through hash collisions\n        let mut backwards_index = arbitrary_index;\n        while backwards_index > 0 {\n            backwards_index -= 1;\n            let (hackstack_hash, haystack_value) = self.sorted_hashed_values[backwards_index];\n\n            if hackstack_hash != needle_hash {\n                break;\n            }\n            if haystack_value.eq_in_heap(heap, needle_value) {\n                return true;\n            }\n        }\n\n        false\n    }\n\n    /// Returns if this set is a subset of the passed set\n    fn is_subset(&self, heap: &Heap, other: &ExternalSet<T>) -> bool {\n        let mut self_iter = self.sorted_hashed_values.iter();\n        let mut other_iter = other.sorted_hashed_values.iter();\n\n        loop {\n            let (self_hash, self_value) = if let Some(entry) = self_iter.next() {\n                entry\n            } else {\n                // No more elements left to check\n                return true;\n            };\n\n            // Try to find the element in the other set\n            loop {\n                let (other_hash, other_value) = if let Some(entry) = other_iter.next() {\n                    entry\n                } else {\n                    // Ran past the end of the other set\n                    return false;\n                };\n\n                if self_iter.len() > other_iter.len() {\n                    // Not enough items remaining in the other set\n                    return false;\n                } else if other_hash == self_hash && other_value.eq_in_heap(heap, self_value) {\n                    // Found corresponding element\n                    break;\n                } else if other_hash > self_hash {\n                    // We've gone past where the corresponding element should be\n                    return false;\n                }\n            }\n        }\n    }\n\n    fn eq_in_heap(&self, heap: &Heap, other: &ExternalSet<T>) -> bool {\n        if self.len() != other.len() {\n            return false;\n        }\n\n        self.sorted_hashed_values\n            .iter()\n            .zip(other.sorted_hashed_values.iter())\n            .all(|((self_hash, self_value), (other_hash, other_value))| {\n                self_hash == other_hash && self_value.eq_in_heap(heap, other_value)\n            })\n    }\n\n    fn hash_in_heap<H: Hasher>(&self, _: &Heap, state: &mut H) {\n        TypeTag::Set.hash(state);\n        state.write_usize(self.len());\n\n        // Instead of recursing into values, use their pre-calculated hash\n        for (hash, _) in self.sorted_hashed_values.iter() {\n            state.write_u64(*hash);\n        }\n    }\n}\n\nenum Repr<'a, T: Boxed> {\n    Inline(&'a InlineSet<T>),\n    External(&'a ExternalSet<T>),\n}\n\nenum ReprMut<'a, T: Boxed> {\n    Inline(&'a mut InlineSet<T>),\n    External(&'a mut ExternalSet<T>),\n}\n\nimpl<T: Boxed> Drop for Set<T> {\n    fn drop(&mut self) {\n        match self.as_repr_mut() {\n            ReprMut::Inline(_) => {\n                // Do nothing here; we might've been allocated as a 16 byte box so we can't read\n                // the whole thing.\n            }\n            ReprMut::External(external) => unsafe {\n                // Call `ExternalSet`'s drop implementation\n                ptr::drop_in_place(external);\n            },\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::heap::Heap;\n    use std::mem;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(32, mem::size_of::<Set<Any>>());\n        assert_eq!(32, mem::size_of::<InlineSet<Any>>());\n        assert_eq!(32, mem::size_of::<ExternalSet<Any>>());\n    }\n\n    #[test]\n    fn inline_equality() {\n        use crate::boxed::Int;\n\n        let mut heap = Heap::empty();\n\n        let boxed1 = Int::new(&mut heap, 1);\n        let boxed2 = Int::new(&mut heap, 2);\n        let boxed3 = Int::new(&mut heap, 3);\n\n        let forward_set1 = Set::new(&mut heap, IntoIterator::into_iter([boxed1, boxed2, boxed3]));\n\n        let forward_set2 = Set::new(\n            &mut heap,\n            IntoIterator::into_iter([boxed1, boxed2, boxed2, boxed3]),\n        );\n\n        let reverse_set = Set::new(&mut heap, IntoIterator::into_iter([boxed3, boxed2, boxed1]));\n\n        let partial_set = Set::new(&mut heap, IntoIterator::into_iter([boxed1, boxed3]));\n\n        assert!(forward_set1.eq_in_heap(&heap, &reverse_set));\n        assert!(forward_set1.eq_in_heap(&heap, &forward_set2));\n        assert!(!forward_set1.eq_in_heap(&heap, &partial_set));\n    }\n\n    #[test]\n    fn inline_contains() {\n        use crate::boxed::Int;\n\n        let mut heap = Heap::empty();\n\n        let boxed1 = Int::new(&mut heap, 1);\n        let boxed2 = Int::new(&mut heap, 2);\n        let boxed3 = Int::new(&mut heap, 3);\n        let boxed4 = Int::new(&mut heap, 4);\n        let boxed5 = Int::new(&mut heap, 5);\n\n        let empty_set = Set::<Int>::new(&mut heap, std::iter::empty());\n        let odd_set = Set::new(&mut heap, IntoIterator::into_iter([boxed1, boxed3, boxed5]));\n        let even_set = Set::new(&mut heap, IntoIterator::into_iter([boxed2, boxed4]));\n\n        assert!(!empty_set.contains(&heap, &boxed1));\n        assert!(odd_set.contains(&heap, &boxed1));\n        assert!(!even_set.contains(&heap, &boxed1));\n\n        assert!(!empty_set.contains(&heap, &boxed2));\n        assert!(!odd_set.contains(&heap, &boxed2));\n        assert!(even_set.contains(&heap, &boxed2));\n\n        assert!(!empty_set.contains(&heap, &boxed3));\n        assert!(odd_set.contains(&heap, &boxed3));\n        assert!(!even_set.contains(&heap, &boxed3));\n    }\n\n    #[test]\n    fn external_equality() {\n        use crate::boxed::Int;\n\n        let mut heap = Heap::empty();\n\n        let boxed1 = Int::new(&mut heap, 1);\n        let boxed2 = Int::new(&mut heap, 2);\n        let boxed3 = Int::new(&mut heap, 3);\n        let boxed4 = Int::new(&mut heap, 4);\n        let boxed5 = Int::new(&mut heap, 5);\n\n        let forward_set = Set::new(\n            &mut heap,\n            IntoIterator::into_iter([boxed1, boxed2, boxed3, boxed4, boxed5]),\n        );\n\n        let reverse_set = Set::new(\n            &mut heap,\n            IntoIterator::into_iter([boxed5, boxed4, boxed3, boxed2, boxed1]),\n        );\n\n        let inline_set = Set::new(\n            &mut heap,\n            IntoIterator::into_iter([boxed1, boxed2, boxed3, boxed4]),\n        );\n\n        let empty_set = Set::<Int>::new(&mut heap, std::iter::empty());\n\n        assert!(forward_set.eq_in_heap(&heap, &reverse_set));\n        assert!(!forward_set.eq_in_heap(&heap, &inline_set));\n        assert!(!forward_set.eq_in_heap(&heap, &empty_set));\n    }\n    #[test]\n    fn external_contains() {\n        use crate::boxed::Int;\n\n        let mut heap = Heap::empty();\n\n        let boxed1 = Int::new(&mut heap, 1);\n        let boxed2 = Int::new(&mut heap, 2);\n        let boxed3 = Int::new(&mut heap, 3);\n        let boxed4 = Int::new(&mut heap, 4);\n        let boxed5 = Int::new(&mut heap, 5);\n        let boxed6 = Int::new(&mut heap, 6);\n        let boxed7 = Int::new(&mut heap, 7);\n        let boxed8 = Int::new(&mut heap, 8);\n\n        let empty_set = Set::<Int>::new(&mut heap, std::iter::empty());\n\n        let odd_set = Set::new(\n            &mut heap,\n            IntoIterator::into_iter([boxed1, boxed3, boxed5, boxed7]),\n        );\n\n        let even_set = Set::new(\n            &mut heap,\n            IntoIterator::into_iter([boxed2, boxed4, boxed6, boxed8]),\n        );\n\n        assert!(!empty_set.contains(&heap, &boxed1));\n        assert!(odd_set.contains(&heap, &boxed1));\n        assert!(!even_set.contains(&heap, &boxed1));\n\n        assert!(!empty_set.contains(&heap, &boxed2));\n        assert!(!odd_set.contains(&heap, &boxed2));\n        assert!(even_set.contains(&heap, &boxed2));\n\n        assert!(!empty_set.contains(&heap, &boxed3));\n        assert!(odd_set.contains(&heap, &boxed3));\n        assert!(!even_set.contains(&heap, &boxed3));\n    }\n\n    #[test]\n    fn subset() {\n        use crate::boxed::Int;\n\n        let mut heap = Heap::empty();\n\n        let boxed1 = Int::new(&mut heap, 1);\n        let boxed2 = Int::new(&mut heap, 2);\n        let boxed3 = Int::new(&mut heap, 3);\n        let boxed4 = Int::new(&mut heap, 4);\n        let boxed5 = Int::new(&mut heap, 5);\n        let boxed6 = Int::new(&mut heap, 6);\n        let boxed7 = Int::new(&mut heap, 7);\n        let boxed8 = Int::new(&mut heap, 8);\n\n        let empty_set = Set::<Int>::new(&mut heap, std::iter::empty());\n        let one_set = Set::new(&mut heap, IntoIterator::into_iter([boxed1]));\n        let odd_set = Set::new(\n            &mut heap,\n            IntoIterator::into_iter([boxed1, boxed3, boxed5, boxed7]),\n        );\n        let even_set = Set::new(\n            &mut heap,\n            IntoIterator::into_iter([boxed2, boxed4, boxed6, boxed8]),\n        );\n        let full_set = Set::new(\n            &mut heap,\n            vec![\n                boxed1, boxed2, boxed3, boxed4, boxed5, boxed6, boxed7, boxed8,\n            ]\n            .into_iter(),\n        );\n\n        assert!(empty_set.is_subset(&heap, &empty_set));\n        assert!(empty_set.is_subset(&heap, &one_set));\n        assert!(empty_set.is_subset(&heap, &odd_set));\n        assert!(empty_set.is_subset(&heap, &even_set));\n        assert!(empty_set.is_subset(&heap, &full_set));\n\n        assert!(!one_set.is_subset(&heap, &empty_set));\n        assert!(one_set.is_subset(&heap, &one_set));\n        assert!(one_set.is_subset(&heap, &odd_set));\n        assert!(!one_set.is_subset(&heap, &even_set));\n        assert!(one_set.is_subset(&heap, &full_set));\n\n        assert!(!odd_set.is_subset(&heap, &empty_set));\n        assert!(!odd_set.is_subset(&heap, &one_set));\n        assert!(odd_set.is_subset(&heap, &odd_set));\n        assert!(!odd_set.is_subset(&heap, &even_set));\n        assert!(odd_set.is_subset(&heap, &full_set));\n\n        assert!(!even_set.is_subset(&heap, &empty_set));\n        assert!(!even_set.is_subset(&heap, &one_set));\n        assert!(!even_set.is_subset(&heap, &odd_set));\n        assert!(even_set.is_subset(&heap, &even_set));\n        assert!(even_set.is_subset(&heap, &full_set));\n\n        assert!(!full_set.is_subset(&heap, &empty_set));\n        assert!(!full_set.is_subset(&heap, &one_set));\n        assert!(!full_set.is_subset(&heap, &odd_set));\n        assert!(!full_set.is_subset(&heap, &even_set));\n        assert!(full_set.is_subset(&heap, &full_set));\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/shared_str.rs",
    "content": "use std::sync::atomic::{fence, AtomicU64, Ordering};\nuse std::{alloc, ptr};\n\n/// Reference count used for global constants created by codegen\nconst GLOBAL_CONSTANT_REFCOUNT: u64 = std::u64::MAX;\n\n/// Header for shared string data\n///\n/// This is only separated out to make it easier to calculate allocation sizes.\n#[repr(C)]\nstruct DataHeader {\n    ref_count: AtomicU64,\n    len: u64,\n}\n\n/// Internal shared string data\n#[repr(C)]\nstruct SharedStrData {\n    header: DataHeader,\n    // This is actually variable length\n    data: [u8; 1],\n}\n\nimpl SharedStrData {\n    fn new(value: &str) -> *mut SharedStrData {\n        unsafe {\n            let layout = Self::layout_for_byte_len(value.len());\n            let shared_str = alloc::alloc(layout) as *mut SharedStrData;\n\n            (*shared_str).header = DataHeader {\n                ref_count: AtomicU64::new(1),\n                len: value.len() as u64,\n            };\n\n            ptr::copy(\n                value.as_ptr(),\n                &mut (*shared_str).data[0] as *mut _,\n                value.len(),\n            );\n\n            shared_str\n        }\n    }\n\n    fn as_str(&self) -> &str {\n        unsafe {\n            let utf8 = std::slice::from_raw_parts(&self.data[0], self.header.len as usize);\n            std::str::from_utf8_unchecked(utf8)\n        }\n    }\n\n    fn layout_for_byte_len(len: usize) -> alloc::Layout {\n        alloc::Layout::new::<DataHeader>()\n            // We have a static length of 1 so we need to allocate at least that\n            .extend(alloc::Layout::array::<u8>(std::cmp::max(1, len)).unwrap())\n            .unwrap()\n            .0\n            .pad_to_align()\n    }\n\n    fn is_global_constant(&self) -> bool {\n        // This doesn't need any ordering; constants can't become non-constant and vice-versa\n        self.header.ref_count.load(Ordering::Relaxed) == GLOBAL_CONSTANT_REFCOUNT\n    }\n\n    /// Atomically takes a new reference to the string data\n    fn take_ref(&mut self) -> *mut SharedStrData {\n        if self.is_global_constant() {\n            return self;\n        }\n\n        // In the case of refing to pass to another thread is sufficient to make the refcount\n        // increment itself visible. In the case of one thread incrementing and then decrementing\n        // later the decrement itself will enforce memory ordering. This ensures other threads\n        // won't falsely destroy the instance.\n        self.header.ref_count.fetch_add(1, Ordering::Relaxed);\n        self\n    }\n\n    /// Atomically releases a reference to the string data\n    ///\n    /// It's unsafe to use the string data after calling this function.\n    unsafe fn release_ref(&mut self) {\n        if self.is_global_constant() {\n            return;\n        }\n\n        let should_destroy = self.header.ref_count.fetch_sub(1, Ordering::Release) == 1;\n\n        if should_destroy {\n            // Make sure the memory operations from this delete are strictly after the fetch_sub\n            fence(Ordering::Acquire);\n\n            alloc::dealloc(\n                self as *mut Self as *mut u8,\n                Self::layout_for_byte_len(self.header.len as usize),\n            );\n        }\n    }\n}\n\n/// Smart pointer for string data\n///\n/// This is morally equivalent to `Arc<str>` except it has a fixed FFI representation. This allows\n/// codegen to create `SharedStr` instances.\n#[repr(transparent)]\npub(super) struct SharedStr {\n    data: *mut SharedStrData,\n}\n\nimpl SharedStr {\n    pub(super) fn new(value: &str) -> SharedStr {\n        SharedStr {\n            data: SharedStrData::new(value),\n        }\n    }\n\n    /// Returns the shared string slice\n    pub(super) fn as_str(&self) -> &str {\n        unsafe { (*self.data).as_str() }\n    }\n}\n\nimpl From<&str> for SharedStr {\n    fn from(value: &str) -> SharedStr {\n        SharedStr::new(value)\n    }\n}\n\nimpl Clone for SharedStr {\n    fn clone(&self) -> SharedStr {\n        unsafe {\n            SharedStr {\n                data: (*self.data).take_ref(),\n            }\n        }\n    }\n}\n\nimpl Drop for SharedStr {\n    fn drop(&mut self) {\n        unsafe {\n            (*self.data).release_ref();\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn construction() {\n        let empty = SharedStr::new(\"\");\n        assert_eq!(\"\", empty.as_str());\n\n        let hello_world = SharedStr::new(\"Hello, world!\");\n        assert_eq!(\"Hello, world!\", hello_world.as_str());\n    }\n\n    #[test]\n    fn cloning() {\n        #[allow(clippy::redundant_clone)]\n        let hello_clone = SharedStr::new(\"Hello, clone!\").clone();\n        assert_eq!(\"Hello, clone!\", hello_clone.as_str());\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/str.rs",
    "content": "use std::hash::{Hash, Hasher};\nuse std::mem::MaybeUninit;\nuse std::{fmt, mem, ptr};\n\nuse crate::boxed::types::shared_str::SharedStr;\nuse crate::boxed::*;\n\n/// Describes the storage of a string's data\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum StrStorage {\n    /// String data is stored inline in a box of the given size\n    Inline(BoxSize),\n    /// String data is stored out-of-line in a 16 byte box\n    External,\n}\n\nimpl StrStorage {\n    /// Returns the box size for a string storage\n    pub fn box_size(self) -> BoxSize {\n        match self {\n            StrStorage::Inline(box_size) => box_size,\n            StrStorage::External => BoxSize::Size16,\n        }\n    }\n}\n\n/// String value encoded as UTF-8\n#[repr(C, align(16))]\npub struct Str {\n    header: Header,\n    inline_byte_len: u8,\n    padding: [u8; Str::MAX_INLINE_BYTES],\n}\n\nimpl Boxed for Str {}\nimpl UniqueTagged for Str {}\n\nimpl Str {\n    /// Maximum number of bytes that can be stored directly in the box\n    pub const MAX_INLINE_BYTES: usize = 29;\n\n    /// Inline byte length used for external strings\n    pub const EXTERNAL_INLINE_BYTE_LEN: u8 = (Self::MAX_INLINE_BYTES as u8) + 1;\n\n    /// Constructs a new string\n    pub fn new(heap: &mut impl AsHeap, value: &str) -> Gc<Str> {\n        let storage = Self::storage_for_byte_len(value.len());\n        let header = Self::TYPE_TAG.to_heap_header(storage.box_size());\n\n        let boxed = unsafe {\n            match storage {\n                StrStorage::External => mem::transmute(ExternalStr::new(header, value)),\n                StrStorage::Inline(_) => mem::transmute(InlineStr::new(header, value)),\n            }\n        };\n\n        heap.as_heap_mut().place_box(boxed)\n    }\n\n    /// Returns the storage for given string byte length\n    pub fn storage_for_byte_len(len: usize) -> StrStorage {\n        match len {\n            0..=13 => StrStorage::Inline(BoxSize::Size16),\n            14..=Str::MAX_INLINE_BYTES => StrStorage::Inline(BoxSize::Size32),\n            _ => {\n                // Too big to fit inline; this needs to be external\n                StrStorage::External\n            }\n        }\n    }\n\n    fn is_inline(&self) -> bool {\n        self.inline_byte_len != Self::EXTERNAL_INLINE_BYTE_LEN as u8\n    }\n\n    fn as_repr(&self) -> Repr<'_> {\n        if self.is_inline() {\n            Repr::Inline(unsafe { &*(self as *const Str as *const InlineStr) })\n        } else {\n            Repr::External(unsafe { &*(self as *const Str as *const ExternalStr) })\n        }\n    }\n\n    fn as_repr_mut(&mut self) -> ReprMut<'_> {\n        if self.is_inline() {\n            ReprMut::Inline(unsafe { &mut *(self as *mut Str as *mut InlineStr) })\n        } else {\n            ReprMut::External(unsafe { &mut *(self as *mut Str as *mut ExternalStr) })\n        }\n    }\n\n    /// Returns the string's content as a slice\n    pub fn as_str(&self) -> &str {\n        match self.as_repr() {\n            Repr::Inline(inline) => inline.as_str(),\n            Repr::External(external) => external.shared_str.as_str(),\n        }\n    }\n}\n\nimpl PartialEq for Str {\n    fn eq(&self, other: &Str) -> bool {\n        self.as_str() == other.as_str()\n    }\n}\n\nimpl Hash for Str {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Self::TYPE_TAG.hash(state);\n        self.as_str().hash(state);\n    }\n}\n\nimpl fmt::Debug for Str {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        write!(formatter, \"Str({:?})\", self.as_str())\n    }\n}\n\nimpl Drop for Str {\n    fn drop(&mut self) {\n        match self.as_repr_mut() {\n            ReprMut::Inline(_) => {\n                // Do nothing here; we might've been allocated as a 16 byte box so we can't read\n                // the whole thing.\n            }\n            ReprMut::External(external) => unsafe {\n                ptr::drop_in_place(external);\n            },\n        }\n    }\n}\n\n#[repr(C, align(16))]\nstruct InlineStr {\n    header: Header,\n    inline_byte_len: u8,\n    inline_bytes: MaybeUninit<[u8; Str::MAX_INLINE_BYTES]>,\n}\n\nimpl InlineStr {\n    fn new(header: Header, value: &str) -> InlineStr {\n        unsafe {\n            let mut inline_bytes = mem::MaybeUninit::<[u8; Str::MAX_INLINE_BYTES]>::uninit();\n\n            ptr::copy(\n                value.as_ptr(),\n                inline_bytes.as_mut_ptr() as *mut _,\n                value.len(),\n            );\n\n            InlineStr {\n                header,\n                inline_byte_len: value.len() as u8,\n                inline_bytes,\n            }\n        }\n    }\n\n    fn as_utf8(&self) -> &[u8] {\n        use std::slice;\n        unsafe {\n            slice::from_raw_parts(\n                self.inline_bytes.as_ptr() as *const u8,\n                self.inline_byte_len as usize,\n            )\n        }\n    }\n\n    fn as_str(&self) -> &str {\n        use std::str;\n        unsafe { str::from_utf8_unchecked(self.as_utf8()) }\n    }\n}\n\n#[repr(C, align(16))]\nstruct ExternalStr {\n    header: Header,\n    // Once we've determined we're not inline this has no useful value\n    inline_byte_len: u8,\n    shared_str: SharedStr,\n    padding: [u64; 2],\n}\n\nimpl ExternalStr {\n    fn new(header: Header, value: &str) -> ExternalStr {\n        ExternalStr {\n            header,\n            inline_byte_len: Str::EXTERNAL_INLINE_BYTE_LEN,\n            shared_str: value.into(),\n            padding: [0, 0],\n        }\n    }\n}\n\nenum Repr<'a> {\n    Inline(&'a InlineStr),\n    External(&'a ExternalStr),\n}\n\nenum ReprMut<'a> {\n    Inline(&'a mut InlineStr),\n    External(&'a mut ExternalStr),\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::heap::Heap;\n    use std::mem;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(32, mem::size_of::<ExternalStr>());\n        assert_eq!(32, mem::size_of::<InlineStr>());\n        assert_eq!(32, mem::size_of::<Str>());\n    }\n\n    #[test]\n    fn equality() {\n        let mut heap = Heap::empty();\n\n        let boxed_one1 = Str::new(&mut heap, \"one\");\n        let boxed_one2 = Str::new(&mut heap, \"one\");\n        let boxed_two = Str::new(&mut heap, \"two\");\n\n        assert_ne!(boxed_one1, boxed_two);\n        assert_eq!(boxed_one1, boxed_one2);\n    }\n\n    #[test]\n    fn fmt_debug() {\n        let mut heap = Heap::empty();\n\n        let boxed_one = Str::new(&mut heap, \"one\");\n        assert_eq!(r#\"Str(\"one\")\"#, format!(\"{:?}\", boxed_one));\n    }\n\n    #[test]\n    fn round_trip() {\n        let mut heap = Heap::empty();\n\n        for &test_str in &[\n            \"\",\n            \"1\",\n            \"smallinline\",\n            \"largerinlinethattakes32bytes\",\n            \"This definitely will not fit in any inline string\",\n        ] {\n            let boxed_string = Str::new(&mut heap, test_str);\n            assert_eq!(test_str, boxed_string.as_str());\n        }\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/sym.rs",
    "content": "use std::fmt;\nuse std::hash::{Hash, Hasher};\n\nuse crate::boxed::*;\nuse crate::intern::{AsInterner, InternedSym};\n\n/// Interned symbol\n///\n/// Symbols are immutable strings typically used as keywords or identifiers.\n#[repr(C, align(16))]\npub struct Sym {\n    header: Header,\n    // TODO: We have room to fit a u32 hash value here which should help with re-interning heap\n    // indexed symbols in new heaps\n    pub(crate) interned: InternedSym,\n}\n\nimpl Boxed for Sym {}\nimpl UniqueTagged for Sym {}\n\nimpl Sym {\n    /// Constructs a new symbol with a specified name\n    pub fn new(heap: &mut impl AsHeap, value: &str) -> Gc<Sym> {\n        let heap = heap.as_heap_mut();\n        let interned = heap.type_info_mut().interner_mut().intern(value);\n        Self::from_interned_sym(heap, interned)\n    }\n\n    /// Constructs a new symbol with an interned symbol\n    pub fn from_interned_sym(heap: &mut impl AsHeap, interned: InternedSym) -> Gc<Sym> {\n        let heap = heap.as_heap_mut();\n\n        heap.place_box(Sym {\n            header: Self::TYPE_TAG.to_heap_header(Self::size()),\n            interned,\n        })\n    }\n\n    /// Returns the box size for symbols\n    pub fn size() -> BoxSize {\n        BoxSize::Size16\n    }\n\n    /// Returns the name of the symbol\n    ///\n    /// `interner` is required to unintern the name. It must be the same interner used to construct\n    /// the symbol.\n    pub fn name<'a>(&'a self, interner: &'a impl AsInterner) -> &'a str {\n        interner.as_interner().unintern(&self.interned)\n    }\n\n    /// Returns the interned symbol value\n    pub fn interned(&self) -> InternedSym {\n        self.interned\n    }\n\n    /// Returns a mutable reference to the interned symbol value\n    pub(crate) fn interned_mut(&mut self) -> &mut InternedSym {\n        &mut self.interned\n    }\n}\n\nimpl PartialEq for Sym {\n    fn eq(&self, other: &Sym) -> bool {\n        self.interned == other.interned\n    }\n}\n\nimpl Hash for Sym {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Self::TYPE_TAG.hash(state);\n        self.interned.hash(state);\n    }\n}\n\nimpl fmt::Debug for Sym {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        write!(formatter, \"Sym({:?})\", self.interned)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::heap::Heap;\n    use std::mem;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(16, mem::size_of::<Sym>());\n    }\n\n    #[test]\n    fn equality() {\n        let mut heap = Heap::empty();\n\n        let boxed_one1 = Sym::new(&mut heap, \"one\");\n        let boxed_one2 = Sym::new(&mut heap, \"one\");\n        let boxed_two = Sym::new(&mut heap, \"two\");\n\n        assert_ne!(boxed_one1, boxed_two);\n        assert_eq!(boxed_one1, boxed_one2);\n    }\n\n    #[test]\n    fn fmt_debug() {\n        let mut heap = Heap::empty();\n\n        let boxed_one = Sym::new(&mut heap, \"one\");\n        assert_eq!(r#\"Sym('one)\"#, format!(\"{:?}\", boxed_one));\n    }\n}\n"
  },
  {
    "path": "runtime/boxed/types/vector.rs",
    "content": "use std::hash::{Hash, Hasher};\nuse std::mem::MaybeUninit;\nuse std::{fmt, marker, mem};\n\nuse crate::abitype::{BoxedAbiType, EncodeBoxedAbiType};\nuse crate::boxed::refs::Gc;\nuse crate::boxed::*;\nuse crate::persistent::Vector as PersistentVector;\n\nconst MAX_16BYTE_INLINE_LEN: usize = (16 - 8) / mem::size_of::<Gc<Any>>();\nconst MAX_32BYTE_INLINE_LEN: usize = (32 - 8) / mem::size_of::<Gc<Any>>();\n\nconst EXTERNAL_INLINE_LEN: u32 = (MAX_32BYTE_INLINE_LEN + 1) as u32;\n\n/// Describes the storage of a vector's data\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum VectorStorage {\n    /// Vector data is stored inline in a box of the given size\n    Inline(BoxSize),\n    /// Vector data is stored out-of-line in a 32 byte box\n    External,\n}\n\nimpl VectorStorage {\n    /// Returns the box size for a vector storage\n    pub fn box_size(self) -> BoxSize {\n        match self {\n            VectorStorage::Inline(box_size) => box_size,\n            VectorStorage::External => BoxSize::Size32,\n        }\n    }\n}\n\n/// Immutable vector of boxed values\n///\n/// This allows random access to any of its values.\n#[repr(C, align(16))]\npub struct Vector<T: Boxed = Any> {\n    header: Header,\n    inline_len: u32,\n    padding: [u8; 24],\n    phantom: marker::PhantomData<T>,\n}\n\nimpl<T: Boxed> Boxed for Vector<T> {}\n\nimpl<T: Boxed> Vector<T> {\n    /// Maximum element length of an inline vector\n    pub const MAX_INLINE_LEN: usize = MAX_32BYTE_INLINE_LEN;\n\n    /// Inline element length used for external vectors\n    pub const EXTERNAL_INLINE_LEN: u32 = (Self::MAX_INLINE_LEN as u32) + 1;\n\n    /// Constructs a new vector with the passed boxed values\n    pub fn new(\n        heap: &mut impl AsHeap,\n        values: impl ExactSizeIterator<Item = Gc<T>>,\n    ) -> Gc<Vector<T>> {\n        let storage = Self::storage_for_element_len(values.len());\n        let header = Vector::TYPE_TAG.to_heap_header(storage.box_size());\n\n        let boxed = unsafe {\n            match storage {\n                VectorStorage::External => mem::transmute(ExternalVector::new(header, values)),\n                VectorStorage::Inline(_) => mem::transmute(InlineVector::new(header, values)),\n            }\n        };\n\n        heap.as_heap_mut().place_box(boxed)\n    }\n\n    /// Returns the storage for given element length\n    fn storage_for_element_len(len: usize) -> VectorStorage {\n        const MIN_32BYTE_INLINE_LEN: usize = MAX_16BYTE_INLINE_LEN + 1;\n\n        match len {\n            0..=MAX_16BYTE_INLINE_LEN => VectorStorage::Inline(BoxSize::Size16),\n            MIN_32BYTE_INLINE_LEN..=MAX_32BYTE_INLINE_LEN => VectorStorage::Inline(BoxSize::Size32),\n            _ => {\n                // Too big to fit inline; this needs to be external\n                VectorStorage::External\n            }\n        }\n    }\n\n    /// Constructs a vector by constructing an iterator of values\n    pub fn from_values<V, F>(\n        heap: &mut impl AsHeap,\n        values: impl Iterator<Item = V>,\n        cons: F,\n    ) -> Gc<Vector<T>>\n    where\n        F: Fn(&mut Heap, V) -> Gc<T>,\n    {\n        let heap = heap.as_heap_mut();\n\n        let elems: Vec<Gc<T>> = values.map(|v| cons(heap, v)).collect();\n        Self::new(heap, elems.into_iter())\n    }\n\n    fn is_inline(&self) -> bool {\n        self.inline_len <= (Self::MAX_INLINE_LEN as u32)\n    }\n\n    fn as_repr(&self) -> Repr<'_, T> {\n        if self.is_inline() {\n            Repr::Inline(unsafe { &*(self as *const Vector<T> as *const InlineVector<T>) })\n        } else {\n            Repr::External(unsafe { &*(self as *const Vector<T> as *const ExternalVector<T>) })\n        }\n    }\n\n    fn as_repr_mut(&mut self) -> ReprMut<'_, T> {\n        if self.is_inline() {\n            ReprMut::Inline(unsafe { &mut *(self as *mut Vector<T> as *mut InlineVector<T>) })\n        } else {\n            ReprMut::External(unsafe { &mut *(self as *mut Vector<T> as *mut ExternalVector<T>) })\n        }\n    }\n\n    /// Returns the length of the vector\n    pub fn len(&self) -> usize {\n        match self.as_repr() {\n            Repr::Inline(inline) => inline.inline_len as usize,\n            Repr::External(external) => external.values.len(),\n        }\n    }\n\n    /// Returns true if the vector is empty\n    pub fn is_empty(&self) -> bool {\n        self.inline_len == 0\n    }\n\n    /// Return an element as the provided index\n    pub fn get(&self, index: usize) -> Option<Gc<T>> {\n        match self.as_repr() {\n            Repr::Inline(inline) => inline.get(index),\n            Repr::External(external) => external.values.get(index),\n        }\n    }\n\n    /// Returns an iterator over the vector\n    pub fn iter<'a>(&'a self) -> Box<(dyn ExactSizeIterator<Item = Gc<T>> + 'a)> {\n        match self.as_repr() {\n            Repr::Inline(inline) => Box::new(inline.iter()),\n            Repr::External(external) => Box::new(external.values.iter()),\n        }\n    }\n\n    /// Returns a new vector with the element at the given index replaced\n    pub fn assoc(&self, heap: &mut impl AsHeap, index: usize, value: Gc<T>) -> Gc<Vector<T>> {\n        match self.as_repr() {\n            Repr::Inline(inline) => {\n                let mut values = inline.values;\n\n                values[index] = MaybeUninit::new(value);\n                Vector::new(\n                    heap,\n                    values[0..self.len()]\n                        .iter()\n                        .map(|value| unsafe { value.assume_init() }),\n                )\n            }\n            Repr::External(external) => {\n                let boxed = unsafe {\n                    mem::transmute(ExternalVector {\n                        header: Vector::TYPE_TAG.to_heap_header(VectorStorage::External.box_size()),\n                        inline_len: EXTERNAL_INLINE_LEN,\n                        values: external.values.assoc(index, value),\n                    })\n                };\n\n                heap.as_heap_mut().place_box(boxed)\n            }\n        }\n    }\n\n    /// Appends the elements in the passed vector and returns a new vector\n    pub fn append(&self, heap: &mut impl AsHeap, other: Gc<Vector<T>>) -> Gc<Vector<T>> {\n        if self.is_empty() {\n            other\n        } else {\n            self.extend(heap, other.iter())\n        }\n    }\n\n    /// Returns a new vector extended with the values in the passed iterator\n    pub fn extend(\n        &self,\n        heap: &mut impl AsHeap,\n        new_values: impl ExactSizeIterator<Item = Gc<T>>,\n    ) -> Gc<Vector<T>> {\n        if new_values.len() == 0 {\n            return unsafe { Gc::new(self) };\n        }\n\n        match self.as_repr() {\n            Repr::External(self_external) => {\n                let new_values = self_external.values.extend(new_values);\n\n                let boxed = unsafe {\n                    mem::transmute(ExternalVector {\n                        header: Vector::TYPE_TAG.to_heap_header(VectorStorage::External.box_size()),\n                        inline_len: EXTERNAL_INLINE_LEN,\n                        values: new_values,\n                    })\n                };\n\n                heap.as_heap_mut().place_box(boxed)\n            }\n            _ => {\n                let values: Vec<_> = self.iter().chain(new_values).collect();\n                Self::new(heap, values.into_iter())\n            }\n        }\n    }\n\n    /// Takes the first `count` items from the vector\n    pub fn take(&self, heap: &mut impl AsHeap, count: usize) -> Gc<Vector<T>> {\n        let new_len = std::cmp::min(self.len(), count);\n\n        if new_len <= Self::MAX_INLINE_LEN {\n            return Self::new(heap, self.iter().take(count));\n        }\n\n        match self.as_repr() {\n            Repr::External(self_external) => {\n                let boxed = unsafe {\n                    mem::transmute(ExternalVector {\n                        header: Vector::TYPE_TAG.to_heap_header(VectorStorage::External.box_size()),\n                        inline_len: EXTERNAL_INLINE_LEN,\n                        values: self_external.values.take(count),\n                    })\n                };\n\n                heap.as_heap_mut().place_box(boxed)\n            }\n            // Shouldn't be reachable but is easy to handle\n            _ => Self::new(heap, self.iter().take(count)),\n        }\n    }\n\n    pub(crate) fn visit_mut_elements<F>(&mut self, visitor: &mut F)\n    where\n        F: FnMut(&mut Gc<T>),\n    {\n        match self.as_repr_mut() {\n            ReprMut::Inline(inline) => {\n                for element in inline.iter_mut() {\n                    visitor(element);\n                }\n            }\n            ReprMut::External(external) => external.values.visit_mut_elements(visitor),\n        }\n    }\n}\n\nimpl<T: Boxed> PartialEqInHeap for Vector<T> {\n    fn eq_in_heap(&self, heap: &Heap, other: &Vector<T>) -> bool {\n        if self.len() != other.len() {\n            return false;\n        }\n\n        self.iter()\n            .zip(other.iter())\n            .all(|(self_value, other_value)| self_value.eq_in_heap(heap, &other_value))\n    }\n}\n\nimpl<T: Boxed> HashInHeap for Vector<T> {\n    fn hash_in_heap<H: Hasher>(&self, heap: &Heap, state: &mut H) {\n        TypeTag::Vector.hash(state);\n        state.write_usize(self.len());\n        for value in self.iter() {\n            value.hash_in_heap(heap, state);\n        }\n    }\n}\n\nimpl<T: Boxed> fmt::Debug for Vector<T> {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        formatter.write_str(\"Vector(\")?;\n        formatter.debug_list().entries(self.iter()).finish()?;\n        formatter.write_str(\")\")\n    }\n}\n\nimpl<T: Boxed> EncodeBoxedAbiType for Vector<T>\nwhere\n    T: EncodeBoxedAbiType,\n{\n    const BOXED_ABI_TYPE: BoxedAbiType = BoxedAbiType::Vector(&T::BOXED_ABI_TYPE);\n}\n\n#[repr(C, align(16))]\npub struct InlineVector<T: Boxed> {\n    header: Header,\n    inline_len: u32,\n    values: [MaybeUninit<Gc<T>>; MAX_32BYTE_INLINE_LEN],\n}\n\nimpl<T: Boxed> InlineVector<T> {\n    fn new(header: Header, values: impl ExactSizeIterator<Item = Gc<T>>) -> InlineVector<T> {\n        let inline_len = values.len();\n\n        let mut inline_values = [MaybeUninit::uninit(); MAX_32BYTE_INLINE_LEN];\n\n        for (inline_value, value) in inline_values.iter_mut().zip(values) {\n            *inline_value = MaybeUninit::new(value);\n        }\n\n        InlineVector {\n            header,\n            inline_len: inline_len as u32,\n            values: inline_values,\n        }\n    }\n\n    fn iter(&self) -> impl ExactSizeIterator<Item = Gc<T>> + '_ {\n        self.values[0..self.inline_len as usize]\n            .iter()\n            .map(|value| unsafe { value.assume_init() })\n    }\n\n    fn iter_mut<'a>(&'a mut self) -> impl ExactSizeIterator<Item = &mut Gc<T>> + 'a {\n        self.values[0..self.inline_len as usize]\n            .iter_mut()\n            .map(|value| unsafe { &mut *value.as_mut_ptr() })\n    }\n\n    fn get(&self, index: usize) -> Option<Gc<T>> {\n        if index > self.inline_len as usize {\n            None\n        } else {\n            Some(unsafe { self.values[index].assume_init() })\n        }\n    }\n}\n\n#[repr(C, align(16))]\npub struct ExternalVector<T: Boxed> {\n    header: Header,\n    inline_len: u32,\n    values: PersistentVector<Gc<T>>,\n}\n\nimpl<T: Boxed> ExternalVector<T> {\n    fn new(header: Header, values: impl ExactSizeIterator<Item = Gc<T>>) -> ExternalVector<T> {\n        ExternalVector {\n            header,\n            inline_len: Vector::<T>::EXTERNAL_INLINE_LEN,\n            values: PersistentVector::new(values),\n        }\n    }\n}\n\nenum Repr<'a, T: Boxed> {\n    Inline(&'a InlineVector<T>),\n    External(&'a ExternalVector<T>),\n}\n\nenum ReprMut<'a, T: Boxed> {\n    Inline(&'a mut InlineVector<T>),\n    External(&'a mut ExternalVector<T>),\n}\n\nimpl<T: Boxed> Drop for Vector<T> {\n    fn drop(&mut self) {\n        match self.as_repr_mut() {\n            ReprMut::Inline(_) => {\n                // Do nothing here; we might've been allocated as a 16 byte box so we can't read\n                // the whole thing.\n            }\n            ReprMut::External(external) => unsafe {\n                // Call `ExternalVector`'s drop implementation\n                ptr::drop_in_place(external);\n            },\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::heap::Heap;\n    use std::mem;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(32, mem::size_of::<Vector<Any>>());\n        assert_eq!(32, mem::size_of::<InlineVector<Any>>());\n        assert_eq!(32, mem::size_of::<ExternalVector<Any>>());\n    }\n\n    #[test]\n    fn equality() {\n        use crate::boxed::Int;\n\n        let mut heap = Heap::empty();\n\n        let boxed1 = Int::new(&mut heap, 1);\n        let boxed2 = Int::new(&mut heap, 2);\n        let boxed3 = Int::new(&mut heap, 3);\n\n        let forward_vec1 =\n            Vector::new(&mut heap, IntoIterator::into_iter([boxed1, boxed2, boxed3]));\n\n        let forward_vec2 =\n            Vector::new(&mut heap, IntoIterator::into_iter([boxed1, boxed2, boxed3]));\n\n        let reverse_vec = Vector::new(&mut heap, IntoIterator::into_iter([boxed3, boxed2, boxed1]));\n\n        assert!(!forward_vec1.eq_in_heap(&heap, &reverse_vec));\n        assert!(forward_vec1.eq_in_heap(&heap, &forward_vec2));\n    }\n\n    #[test]\n    fn fmt_debug() {\n        use crate::boxed::Int;\n\n        let mut heap = Heap::empty();\n\n        let forward_vec = Vector::from_values(&mut heap, [1, 2, 3].iter().cloned(), Int::new);\n\n        assert_eq!(\n            \"Vector([Int(1), Int(2), Int(3)])\",\n            format!(\"{:?}\", forward_vec)\n        );\n    }\n}\n"
  },
  {
    "path": "runtime/callback.rs",
    "content": "#![warn(missing_docs)]\n\n//! Typed callback functions\n\nuse crate::abitype;\nuse crate::boxed;\nuse crate::task::Task;\n\n/// Typed callback function\n///\n/// This is typically when taking a callback as a parameter to an RFI function. This is not a\n/// proper boxed value and can neither be stored in a collection or returned as a value. For those\n/// cases [`boxed::FunThunk`] should be used instead.\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct Callback<F>\nwhere\n    F: Copy,\n{\n    captures: boxed::Captures,\n    entry_point: F,\n}\n\nimpl<F> Callback<F>\nwhere\n    F: Copy,\n{\n    /// Returns the captures for this callback\n    pub fn captures(&self) -> boxed::Captures {\n        self.captures\n    }\n}\n\n/// Encoding of an entry point's ABI type\n///\n/// This is used internally by the compiler as a mechanism for reflecting the Rust function type.\n#[derive(Debug, PartialEq, Eq, Hash, Clone)]\npub struct EntryPointAbiType {\n    /// Types of the entry point's parameters\n    ///\n    /// This is not an [`abitype::ParamAbiType`] as captures should be determined by the callback\n    /// implementation, not the callback's type.\n    pub params: &'static [abitype::AbiType],\n\n    /// Type of the entry point's return value\n    pub ret: abitype::RetAbiType,\n}\n\n/// Trait used to encode a Rust function type as an [`EntryPointAbiType`]\npub trait EncodeEntryPointAbiType: Copy {\n    /// Corresponding [`EntryPointAbiType`] for this Rust function type\n    const ENTRY_POINT_ABI_TYPE: EntryPointAbiType;\n}\n\nmacro_rules! define_generic_entry_point {\n    ( $( $generic_param:ident ),* ) => {\n        impl<R, $( $generic_param ),*> EncodeEntryPointAbiType for for<'s> extern \"C\" fn(&'s mut Task, captures: boxed::Captures, $( $generic_param ),* ) -> R\n        where\n            R: abitype::EncodeRetAbiType,\n            $( $generic_param: abitype::EncodeAbiType ),*\n        {\n            const ENTRY_POINT_ABI_TYPE: EntryPointAbiType = EntryPointAbiType {\n                params: &[$( $generic_param::ABI_TYPE ),*],\n                ret: R::RET_ABI_TYPE,\n            };\n        }\n\n        impl<R, $( $generic_param ),*> Callback<for<'s> extern \"C\" fn(&'s mut Task, captures: boxed::Captures, $( $generic_param ),* ) -> R>\n        where\n            R: abitype::EncodeRetAbiType,\n            $( $generic_param: abitype::EncodeAbiType ),*\n        {\n            /// Applies this callback inside the given [`Task`] and returns its value\n            ///\n            /// It's important that the callback was created inside the passed `task` or undefined\n            /// behaviour may result.\n            #[allow(unused)]\n            #[allow(non_snake_case)]\n            #[allow(clippy::too_many_arguments)]\n            pub fn apply(&self, task: &mut Task, $( $generic_param: $generic_param ),*) -> R {\n                (self.entry_point)(task, self.captures, $( $generic_param ),*)\n            }\n        }\n    }\n}\n\ndefine_generic_entry_point!();\ndefine_generic_entry_point!(A);\ndefine_generic_entry_point!(A, B);\ndefine_generic_entry_point!(A, B, C);\ndefine_generic_entry_point!(A, B, C, D);\ndefine_generic_entry_point!(A, B, C, D, E);\ndefine_generic_entry_point!(A, B, C, D, E, F);\ndefine_generic_entry_point!(A, B, C, D, E, F, G);\ndefine_generic_entry_point!(A, B, C, D, E, F, G, H);\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::boxed::refs::*;\n\n    #[test]\n    fn encode_entry_point_abi_type() {\n        let empty_abi_type = <extern \"C\" fn(&mut Task, boxed::Captures) as EncodeEntryPointAbiType>::ENTRY_POINT_ABI_TYPE;\n        assert_eq!(abitype::RetAbiType::Void, empty_abi_type.ret);\n        assert!(empty_abi_type.params.is_empty());\n\n        let two_param_abi_type = <extern \"C\" fn(\n            &mut Task,\n            boxed::Captures,\n            i64,\n            Gc<boxed::Int>,\n        ) -> bool as EncodeEntryPointAbiType>::ENTRY_POINT_ABI_TYPE;\n        assert_eq!(\n            abitype::AbiType::Bool.into_ret_abi_type(),\n            two_param_abi_type.ret\n        );\n        assert_eq!(\n            &[abitype::AbiType::Int, boxed::TypeTag::Int.into()],\n            two_param_abi_type.params\n        );\n    }\n}\n"
  },
  {
    "path": "runtime/class_map.rs",
    "content": "use std::marker::PhantomData;\nuse std::ptr;\n\nuse crate::abitype;\nuse crate::boxed::RecordClassId;\n\n/// Minimal type information for a class' field\n///\n/// This is used to annotate values with information to support equality, hashing and garbage\n/// collection.\n#[derive(Copy, Clone, PartialEq, Eq)]\n#[repr(u8)]\npub enum FieldType {\n    Bool = 0,\n    Char = 1,\n    Float = 2,\n    Int = 3,\n    InternedSym = 4,\n    Boxed = 5,\n}\n\nimpl FieldType {\n    pub fn from_abi_type(abi_type: &abitype::AbiType) -> Self {\n        match abi_type {\n            abitype::AbiType::Bool => FieldType::Bool,\n            abitype::AbiType::Char => FieldType::Char,\n            abitype::AbiType::Float => FieldType::Float,\n            abitype::AbiType::Int => FieldType::Int,\n            abitype::AbiType::InternedSym => FieldType::InternedSym,\n            abitype::AbiType::Boxed(_) => FieldType::Boxed,\n            abitype::AbiType::Callback(_) => {\n                unimplemented!(\"callback record fields\");\n            }\n        }\n    }\n}\n\n/// Type information for a class' field\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct Field {\n    offset: u32,\n    field_type: FieldType,\n    is_last: bool,\n}\n\nimpl Field {\n    /// Constructs a new instance with the given field type and offset\n    pub fn new(field_type: FieldType, offset: usize) -> Field {\n        Self {\n            offset: offset as u32,\n            field_type,\n            is_last: false,\n        }\n    }\n\n    /// Type information for the class field\n    pub fn field_type(self) -> FieldType {\n        self.field_type\n    }\n\n    /// Offset in bytes of the class field from the start of the record data\n    pub fn offset(self) -> usize {\n        self.offset as usize\n    }\n\n    /// Returns if this is the last field in the class\n    pub fn is_last(self) -> bool {\n        self.is_last\n    }\n}\n\n/// Type information for a class\n#[repr(transparent)]\n#[derive(Clone, Copy)]\npub struct ClassRef<'a> {\n    fields: *const Field,\n    phantom_lifetime: PhantomData<&'a Field>,\n}\n\nimpl<'a> ClassRef<'a> {\n    /// Returns if the type contains no fields\n    pub fn is_empty(self) -> bool {\n        self.fields.is_null()\n    }\n\n    /// Returns an iterator over the class' fields\n    pub fn field_iter(self) -> FieldIterator<'a> {\n        FieldIterator {\n            fields: self.fields,\n            phantom_lifetime: PhantomData,\n        }\n    }\n}\n\n/// Owned version of [`ClassRef`]\n///\n/// This is used for classes that are built during compile time.\n#[derive(Clone)]\npub struct BoxedClass {\n    fields: Box<[Field]>,\n}\n\nimpl BoxedClass {\n    /// Constructs a new instance containing the provided fields\n    pub fn from_fields(fields_iter: impl Iterator<Item = Field>) -> Self {\n        let mut fields: Box<[Field]> = fields_iter.collect();\n        if let Some(last_field) = fields.last_mut() {\n            last_field.is_last = true;\n        }\n\n        BoxedClass { fields }\n    }\n\n    pub fn as_ref(&self) -> ClassRef<'_> {\n        ClassRef {\n            fields: if self.fields.is_empty() {\n                std::ptr::null()\n            } else {\n                self.fields.as_ptr()\n            },\n            phantom_lifetime: PhantomData,\n        }\n    }\n}\n\n/// Basic iterator of class fields\npub struct FieldIterator<'a> {\n    fields: *const Field,\n    phantom_lifetime: PhantomData<&'a Field>,\n}\n\nimpl<'a> FieldIterator<'a> {\n    pub fn empty() -> FieldIterator<'static> {\n        FieldIterator {\n            fields: ptr::null(),\n            phantom_lifetime: PhantomData,\n        }\n    }\n}\n\nimpl<'a> Iterator for FieldIterator<'a> {\n    type Item = Field;\n\n    fn next(&mut self) -> Option<Field> {\n        if self.fields.is_null() {\n            return None;\n        }\n\n        let next_field = unsafe { *self.fields };\n\n        if next_field.is_last {\n            self.fields = ptr::null();\n        } else {\n            self.fields = unsafe { self.fields.add(1) };\n        }\n\n        Some(next_field)\n    }\n}\n\n/// Mapping of [record class IDs](RecordClassId) to [classes](ClassRef)\n#[repr(C)]\n#[derive(Clone)]\npub struct ClassMap {\n    const_classes: *const ClassRef<'static>,\n    dynamic_classes: Vec<BoxedClass>,\n}\n\nimpl ClassMap {\n    const DYNAMIC_RECORD_CLASS_ID_BASE: RecordClassId = 1u32 << 30;\n\n    /// Constructs a new instance containing no classes\n    pub fn empty() -> ClassMap {\n        Self::with_const_classes(std::ptr::null())\n    }\n\n    /// Constructs a new instance with the provided constant classes\n    pub(crate) fn with_const_classes(const_classes: *const ClassRef<'static>) -> ClassMap {\n        Self {\n            const_classes,\n            dynamic_classes: vec![],\n        }\n    }\n\n    /// Registers a new class and returns a distinct [`RecordClassId`]\n    pub fn push_dynamic_class(&mut self, boxed_class: BoxedClass) -> RecordClassId {\n        let record_class_id =\n            (self.dynamic_classes.len() as RecordClassId) + Self::DYNAMIC_RECORD_CLASS_ID_BASE;\n\n        self.dynamic_classes.push(boxed_class);\n        record_class_id\n    }\n\n    /// Returns a class reference for a given [`RecordClassId`]\n    pub fn class_for_record_class_id(&self, record_class_id: RecordClassId) -> ClassRef<'_> {\n        if record_class_id >= Self::DYNAMIC_RECORD_CLASS_ID_BASE {\n            let dynamic_class_index =\n                (record_class_id - Self::DYNAMIC_RECORD_CLASS_ID_BASE) as usize;\n\n            self.dynamic_classes[dynamic_class_index].as_ref()\n        } else {\n            unsafe { *self.const_classes.add(record_class_id as usize) }\n        }\n    }\n}\n"
  },
  {
    "path": "runtime/compiler_support.rs",
    "content": "//! Internal functions called by compiled Arret code\n//!\n//! Calls to these functions are generated by the compiler. They should not be called from user\n//! code.\n\n#![allow(clippy::missing_safety_doc)]\n\nuse std::{alloc, panic, process};\n\nuse crate::boxed;\nuse crate::boxed::prelude::*;\nuse crate::boxed::refs::Gc;\nuse crate::boxed::type_info::TypeInfo;\nuse crate::class_map::{ClassMap, ClassRef};\nuse crate::intern::{Interner, RawGlobalNames};\nuse crate::task::Task;\n\ntype TaskEntry = extern \"C\" fn(&mut Task);\n\n#[export_name = \"arret_runtime_launch_task\"]\npub unsafe extern \"C\" fn launch_task(\n    global_names: *const RawGlobalNames,\n    classmap_classes: *const ClassRef<'static>,\n    entry: TaskEntry,\n) {\n    let interner = Interner::with_global_names(global_names);\n    let class_map = ClassMap::with_const_classes(classmap_classes);\n\n    let type_info = TypeInfo::new(interner, class_map);\n    let mut task = Task::with_type_info(type_info);\n\n    if let Err(err) = panic::catch_unwind(panic::AssertUnwindSafe(|| entry(&mut task))) {\n        if let Some(message) = err.downcast_ref::<String>() {\n            eprintln!(\"{}\", message);\n        } else {\n            eprintln!(\"Unexpected panic type\");\n        };\n\n        process::exit(1);\n    };\n}\n\n#[export_name = \"arret_runtime_alloc_cells\"]\npub extern \"C\" fn alloc_cells(task: &mut Task, count: u32) -> *mut boxed::Any {\n    task.heap_mut().alloc_cells(count as usize)\n}\n\n#[export_name = \"arret_runtime_alloc_record_data\"]\npub extern \"C\" fn alloc_record_data(size: u64, align: u32) -> *mut u8 {\n    unsafe {\n        let layout = alloc::Layout::from_size_align_unchecked(size as usize, align as usize);\n        alloc::alloc(layout)\n    }\n}\n\n#[export_name = \"arret_runtime_equals\"]\npub extern \"C\" fn equals(task: &Task, lhs: Gc<boxed::Any>, rhs: Gc<boxed::Any>) -> bool {\n    lhs.eq_in_heap(task.as_heap(), &rhs)\n}\n\n#[export_name = \"arret_runtime_panic_with_string\"]\npub unsafe extern \"C\" fn panic_with_string(\n    task: &mut Task,\n    message_bytes: *const u8,\n    message_len: u32,\n) {\n    let message_vec: Vec<u8> =\n        std::slice::from_raw_parts(message_bytes, message_len as usize).into();\n\n    task.panic(String::from_utf8_unchecked(message_vec));\n}\n"
  },
  {
    "path": "runtime/intern.rs",
    "content": "//! Interned symbols\n//!\n//! This uses a fixed 8 byte representation for interned symbol. They are associated with a\n//! particular `Interner` instance which can return the original [`prim@str`] name of the symbol.\n//! Interned symbols from the same `Interner` can be compared directly without a reference to\n//! the `Interner` instance.\n//!\n//! Symbol names of 8 bytes or less are encoded directly in the `InternedSym`` instance without\n//! storing the name in the `Interner`. They are padded with a constant invalid UTF-8 sequence so\n//! the length of the inline name can be recovered.\n//!\n//! The encoding for names larger than 8 bytes uses an index in to a [`Vec`] stored in the\n//! `Interner`. The indexed representation is invalid UTF-8 so it cannot collide with a valid\n//! symbol name.\n\nuse std::collections::HashMap;\nuse std::hash::{Hash, Hasher};\nuse std::rc::Rc;\nuse std::{fmt, ptr, str};\n\n// UTF-8 sequences cannot start with 10xxxxxxx. This is pattern for the last continuation byte,\n// but any 1 byte sequences are encoded directly. We can use these values freely without colliding\n// with inline names.\nconst INLINE_FILL_BYTE: u8 = 0x80;\nconst LOCAL_INDEXED_FLAG: u8 = 0x81;\nconst GLOBAL_INDEXED_FLAG: u8 = 0x82;\n\nconst INLINE_SIZE: usize = 8;\n\n#[repr(C)]\npub struct RawGlobalNames {\n    len: u32,\n    names: [GlobalName; 1],\n}\n\n#[repr(C)]\nstruct GlobalName {\n    name_byte_len: u64,\n    name_bytes: *const u8,\n}\n\nimpl GlobalName {\n    fn as_str(&self) -> &str {\n        unsafe {\n            let byte_slice =\n                std::slice::from_raw_parts(self.name_bytes, self.name_byte_len as usize);\n            std::str::from_utf8_unchecked(byte_slice)\n        }\n    }\n}\n\n#[repr(align(8))]\n#[derive(Copy, Clone)]\nstruct InternedIndexed {\n    flag_byte: u8,\n    _padding: [u8; 3],\n    name_index: u32,\n}\n\n#[repr(align(8))]\n#[derive(Copy, Clone)]\nstruct InternedInline {\n    name_bytes: [u8; INLINE_SIZE],\n}\n\nimpl InternedInline {\n    fn as_str(&self) -> &str {\n        // Find the first fill byte. If none is found assume our full inline size.\n        let len = self\n            .name_bytes\n            .iter()\n            .position(|byte| *byte == INLINE_FILL_BYTE)\n            .unwrap_or(INLINE_SIZE);\n\n        unsafe { str::from_utf8_unchecked(&self.name_bytes[0..len]) }\n    }\n}\n\n#[repr(align(8))]\n#[derive(Copy, Clone)]\npub union InternedSym {\n    indexed: InternedIndexed,\n    inline: InternedInline,\n    raw: u64,\n}\n\nenum InternedRepr<'a> {\n    Inline(&'a InternedInline),\n    LocalIndexed(&'a InternedIndexed),\n    GlobalIndexed(&'a InternedIndexed),\n}\n\nimpl InternedSym {\n    /// Tries to return an inline interned Sym\n    ///\n    /// This can be accomplished without an [`Interner`] as we don't need to add a name to the\n    /// [`Interner`]'s index.\n    pub fn try_from_inline_name(name: &str) -> Option<InternedSym> {\n        if name.len() <= INLINE_SIZE {\n            let mut interned_inline = InternedInline {\n                name_bytes: [INLINE_FILL_BYTE; INLINE_SIZE],\n            };\n\n            unsafe {\n                ptr::copy_nonoverlapping(\n                    name.as_ptr(),\n                    &mut interned_inline.name_bytes[0] as *mut u8,\n                    name.len(),\n                );\n            }\n\n            Some(InternedSym {\n                inline: interned_inline,\n            })\n        } else {\n            None\n        }\n    }\n\n    pub fn from_global_index(index: u32) -> InternedSym {\n        InternedSym {\n            indexed: InternedIndexed {\n                flag_byte: GLOBAL_INDEXED_FLAG,\n                _padding: [0; 3],\n                name_index: index,\n            },\n        }\n    }\n\n    pub fn from_local_index(index: u32) -> InternedSym {\n        InternedSym {\n            indexed: InternedIndexed {\n                flag_byte: LOCAL_INDEXED_FLAG,\n                _padding: [0; 3],\n                name_index: index,\n            },\n        }\n    }\n\n    pub fn to_raw_u64(self) -> u64 {\n        unsafe { self.raw }\n    }\n\n    fn repr(&self) -> InternedRepr<'_> {\n        unsafe {\n            match self.indexed.flag_byte {\n                LOCAL_INDEXED_FLAG => InternedRepr::LocalIndexed(&self.indexed),\n                GLOBAL_INDEXED_FLAG => InternedRepr::GlobalIndexed(&self.indexed),\n                _ => InternedRepr::Inline(&self.inline),\n            }\n        }\n    }\n}\n\nimpl PartialEq for InternedSym {\n    fn eq(&self, other: &InternedSym) -> bool {\n        unsafe { self.raw == other.raw }\n    }\n}\n\nimpl Eq for InternedSym {}\n\nimpl Hash for InternedSym {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        unsafe {\n            state.write(&self.inline.name_bytes);\n        }\n    }\n}\n\nimpl fmt::Debug for InternedSym {\n    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        match self.repr() {\n            InternedRepr::LocalIndexed(indexed) | InternedRepr::GlobalIndexed(indexed) => {\n                // We don't have access to the `Interner` so we can't print our interned value\n                write!(formatter, \"`{:x}\", indexed.name_index)\n            }\n            InternedRepr::Inline(inline) => write!(formatter, \"'{}\", inline.as_str()),\n        }\n    }\n}\n\npub struct Interner {\n    names: Vec<Rc<str>>,\n    name_to_interned: HashMap<Rc<str>, InternedSym>,\n    /// Contains the highest static index + 1\n    static_index_watermark: u32,\n    global_names: Option<&'static [GlobalName]>,\n}\n\nimpl Interner {\n    pub fn new() -> Interner {\n        Interner {\n            names: vec![],\n            name_to_interned: HashMap::new(),\n            static_index_watermark: 0,\n            global_names: None,\n        }\n    }\n\n    /// Creates a new `Interner` with a global names struct produced by codegen\n    ///\n    /// # Safety\n    /// `raw_global_names` must be a pointer to a valid [`RawGlobalNames`]\n    pub unsafe fn with_global_names(raw_global_names: *const RawGlobalNames) -> Interner {\n        // Convert from our codegened layout to Rust\n        let global_names = raw_global_names.as_ref().map(|raw_global_names| {\n            std::slice::from_raw_parts(&raw_global_names.names[0], raw_global_names.len as usize)\n        });\n\n        Interner {\n            names: vec![],\n            name_to_interned: HashMap::new(),\n            static_index_watermark: 0,\n            global_names,\n        }\n    }\n\n    fn lookup_global_name(&mut self, name: &str) -> Option<InternedSym> {\n        self.global_names.and_then(|global_names| {\n            global_names\n                .binary_search_by(|global_name| global_name.as_str().cmp(name))\n                .ok()\n                .map(|index| InternedSym::from_global_index(index as u32))\n        })\n    }\n\n    /// Interns a symbol with the given name\n    ///\n    /// The `InternedSym` must be referenced by a boxed `Sym` before the next GC cycle.\n    pub fn intern(&mut self, name: &str) -> InternedSym {\n        if let Some(inline_interned) = InternedSym::try_from_inline_name(name) {\n            return inline_interned;\n        };\n\n        // See if this has already been interned locally or is a cached global name\n        if let Some(interned) = self.name_to_interned.get(name) {\n            return *interned;\n        }\n\n        // See if this is in our global names\n        if let Some(interned) = self.lookup_global_name(name) {\n            // Cache this so we don't have to iterate to find the name again\n            self.name_to_interned.insert(name.into(), interned);\n            return interned;\n        }\n\n        let shared_name: Rc<str> = name.into();\n\n        let index = self.names.len() as u32;\n        self.names.push(shared_name.clone());\n\n        let interned = InternedSym::from_local_index(index);\n        self.name_to_interned.insert(shared_name, interned);\n\n        interned\n    }\n\n    /// Interns a static symbol with the given name\n    ///\n    /// This should only be used where it's not possible to GC root the [`InternedSym`]. This is\n    /// currently only used by the JIT where we can't track [`InternedSym`] references in the\n    /// generated code.\n    pub fn intern_static(&mut self, name: &str) -> InternedSym {\n        let interned_sym = self.intern(name);\n\n        if let InternedRepr::LocalIndexed(indexed_sym) = interned_sym.repr() {\n            self.static_index_watermark = indexed_sym.name_index + 1;\n        }\n\n        interned_sym\n    }\n\n    pub fn unintern<'a>(&'a self, interned: &'a InternedSym) -> &'a str {\n        match interned.repr() {\n            InternedRepr::LocalIndexed(indexed) => &self.names[indexed.name_index as usize],\n            InternedRepr::GlobalIndexed(indexed) => {\n                self.global_names.unwrap()[indexed.name_index as usize].as_str()\n            }\n            InternedRepr::Inline(inline) => inline.as_str(),\n        }\n    }\n\n    /// Returns a clone of this interner usable for garbage collection\n    ///\n    /// This preserves the index of all static [`InternedSym`]s.\n    pub(crate) fn clone_for_collect_garbage(&self) -> Self {\n        if self.static_index_watermark == 0 {\n            // Avoid iterating over our HashMap\n            return Self::new();\n        };\n\n        let static_index_watermark = self.static_index_watermark;\n\n        let names = self.names[0..static_index_watermark as usize].to_vec();\n        let name_to_interned = self\n            .name_to_interned\n            .iter()\n            .filter_map(|(name, interned)| {\n                if let InternedRepr::LocalIndexed(indexed) = interned.repr() {\n                    if indexed.name_index < self.static_index_watermark {\n                        return Some((name.clone(), *interned));\n                    }\n                }\n\n                None\n            })\n            .collect();\n\n        Interner {\n            names,\n            name_to_interned,\n            static_index_watermark,\n            global_names: self.global_names,\n        }\n    }\n}\n\nimpl Default for Interner {\n    fn default() -> Interner {\n        Self::new()\n    }\n}\n\n/// Type that can be converted to an [`Interner`]\npub trait AsInterner {\n    /// Returns this instance as an [`Interner`]\n    fn as_interner(&self) -> &Interner;\n}\n\nimpl AsInterner for Interner {\n    fn as_interner(&self) -> &Interner {\n        self\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use std::mem;\n\n    #[test]\n    fn sizes() {\n        assert_eq!(8, mem::size_of::<InternedIndexed>());\n        assert_eq!(8, mem::size_of::<InternedInline>());\n        assert_eq!(8, mem::size_of::<InternedSym>());\n    }\n\n    #[test]\n    fn equality() {\n        let inline_name = \"inline\";\n        let index_name = \"This must be longer than eight bytes\";\n\n        let mut interner = Interner::new();\n\n        let intern_inline1 = interner.intern(inline_name);\n        let intern_inline2 = interner.intern(inline_name);\n        assert_eq!(intern_inline1, intern_inline2);\n\n        let intern_index1 = interner.intern(index_name);\n        let intern_index2 = interner.intern(index_name);\n        assert_eq!(intern_index1, intern_index2);\n\n        // These should not be equal\n        assert_ne!(intern_inline1, intern_index1);\n    }\n\n    #[test]\n    fn fmt_debug() {\n        let mut interner = Interner::new();\n\n        let intern_inline = interner.intern(\"inline\");\n        assert_eq!(\"'inline\", format!(\"{:?}\", intern_inline));\n\n        let intern_indexed = interner.intern(\"This is very long and can't be stored inline\");\n        assert_eq!(\"`0\", format!(\"{:?}\", intern_indexed));\n    }\n\n    #[test]\n    fn roundtrip() {\n        let mut interner = Interner::new();\n\n        let test_names = [\n            \"\",\n            \"short1\",\n            \"short2\",\n            \"exactly8\",\n            \"Hello, world!\",\n            \"This is another long test string\",\n        ];\n\n        let mut previous_interneds = vec![];\n        for &name in &test_names {\n            let interned = interner.intern(name);\n            assert_eq!(name, interner.unintern(&interned));\n\n            // Make sure we don't equal any of our previous interned symbols\n            assert!(!previous_interneds.contains(&interned));\n            previous_interneds.push(interned);\n        }\n    }\n\n    #[test]\n    fn clone_for_collect_garbage() {\n        let mut interner = Interner::new();\n        interner.intern(\"one                \");\n        interner.intern(\"two                \");\n        interner.intern(\"three              \");\n\n        assert_eq!(3, interner.names.len());\n        assert_eq!(3, interner.name_to_interned.len());\n\n        // No static symbols; we should collect everything\n        interner = interner.clone_for_collect_garbage();\n        assert_eq!(0, interner.names.len());\n        assert_eq!(0, interner.name_to_interned.len());\n\n        interner.intern(\"one                \");\n        interner.intern_static(\"two         \");\n        interner.intern(\"three              \");\n\n        // We need to preserve the second symbol\n        interner = interner.clone_for_collect_garbage();\n        assert_eq!(2, interner.names.len());\n        assert_eq!(2, interner.name_to_interned.len());\n\n        // We should be able to \"promote\" an existing symbol to static\n        interner.intern(\"one-two-three-four\");\n        interner.intern_static(\"one-two-three-four\");\n\n        assert_eq!(3, interner.names.len());\n        assert_eq!(3, interner.name_to_interned.len());\n\n        interner = interner.clone_for_collect_garbage();\n        assert_eq!(3, interner.names.len());\n        assert_eq!(3, interner.name_to_interned.len());\n    }\n}\n"
  },
  {
    "path": "runtime/lib.rs",
    "content": "#![warn(clippy::all)]\n#![warn(rust_2018_idioms)]\n\npub mod abitype;\npub mod binding;\npub mod boxed;\npub mod callback;\npub mod class_map;\npub mod compiler_support;\npub mod intern;\npub mod persistent;\npub mod task;\n"
  },
  {
    "path": "runtime/persistent/mod.rs",
    "content": "pub mod vector;\n\npub use vector::Vector;\n"
  },
  {
    "path": "runtime/persistent/vector.rs",
    "content": "use std::mem::MaybeUninit;\nuse std::sync::atomic::AtomicU64;\nuse std::sync::atomic::Ordering;\nuse std::{alloc, ptr, sync};\n\n/// Reference count used for global constants created by codegen\npub const GLOBAL_CONSTANT_REFCOUNT: u64 = std::u64::MAX;\n\nconst TRIE_RADIX: u32 = 5;\npub const NODE_SIZE: usize = 1 << TRIE_RADIX;\nconst LEVEL_MASK: usize = (1 << TRIE_RADIX) - 1;\n\n#[cfg(test)]\nuse std::cell::RefCell;\n\n#[cfg(test)]\nthread_local! {\n    static ALLOCATED_BRANCHES: RefCell<isize> = RefCell::new(0);\n}\n\n#[cfg(test)]\nthread_local! {\n    static ALLOCATED_LEAVES: RefCell<isize> = RefCell::new(0);\n}\n\n#[repr(C)]\npub struct Vector<T>\nwhere\n    T: Copy,\n{\n    size: u64,\n    root: *const Node<T>,\n    tail: *const Node<T>,\n}\n\nimpl<T> Vector<T>\nwhere\n    T: Copy,\n{\n    pub fn new(values: impl ExactSizeIterator<Item = T>) -> Self {\n        let empty_vec = Vector {\n            size: 0,\n            root: std::ptr::null(),\n            tail: std::ptr::null(),\n        };\n\n        empty_vec.extend(values)\n    }\n\n    pub fn len(&self) -> usize {\n        self.size as usize\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Pushes a new leaf containing `added_elements` additional elements\n    fn push_leaf(&self, leaf: *const Node<T>, added_elements: u64) -> Vector<T> {\n        let new_size = self.size + added_elements;\n        debug_assert!(new_size & (LEVEL_MASK as u64) == 0);\n\n        let new_root = match unsafe { self.root.as_ref() } {\n            None => {\n                // We're the first root node\n                leaf\n            }\n            Some(old_root) => {\n                let old_depth = Self::trie_depth(self.trie_size());\n                let new_depth = Self::trie_depth(self.trie_size() + NODE_SIZE);\n\n                if old_depth == new_depth {\n                    old_root.push_leaf(old_depth, new_size as usize - 1, leaf)\n                } else {\n                    // Need to add a new level\n                    let mut root_children: [*const Node<T>; NODE_SIZE] = [ptr::null(); NODE_SIZE];\n                    root_children[0] = Node::take_ptr_ref(self.root);\n                    root_children[1] = Node::new_chain(leaf, old_depth);\n\n                    Node::new_branch(root_children)\n                }\n            }\n        };\n\n        Self {\n            size: new_size,\n            root: new_root,\n            tail: ptr::null(),\n        }\n    }\n\n    pub fn get(&self, index: usize) -> Option<T> {\n        if index >= self.len() {\n            return None;\n        }\n\n        let leaf_node = self.get_leaf(index);\n        unsafe { Some((*leaf_node).elements.leaf[index & LEVEL_MASK].assume_init()) }\n    }\n\n    pub fn take(&self, count: usize) -> Vector<T> {\n        let new_len = std::cmp::min(count, self.len());\n        if new_len == self.len() {\n            return self.clone();\n        }\n\n        let new_trie_size = Self::trie_size_for_len(new_len);\n        let new_root = if new_trie_size > 0 {\n            let old_depth = Self::trie_depth(self.trie_size());\n            let new_depth = Self::trie_depth(new_trie_size);\n\n            // Drill down until we find our new root\n            let new_root = (new_depth..old_depth)\n                .fold(self.root, |root, _| unsafe { (*root).elements.branch[0] });\n\n            Node::take_ptr_ref(new_root)\n        } else {\n            ptr::null()\n        };\n\n        let new_tail_size = Self::tail_size_for_len(new_len);\n        let new_tail = if new_tail_size > 0 {\n            Node::take_ptr_ref(self.get_leaf(new_trie_size))\n        } else {\n            ptr::null()\n        };\n\n        Self {\n            size: new_len as u64,\n            root: new_root,\n            tail: new_tail,\n        }\n    }\n\n    fn get_leaf(&self, index: usize) -> *const Node<T> {\n        if index >= self.tail_offset() {\n            self.tail\n        } else {\n            let depth = Self::trie_depth(self.trie_size());\n            unsafe { (&*self.root).get_leaf(depth, index) }\n        }\n    }\n\n    pub fn assoc(&self, index: usize, value: T) -> Vector<T> {\n        if index >= self.len() {\n            panic!(\"element {} of out bounds\", index);\n        }\n\n        if index >= self.tail_offset() {\n            let mut new_elements = [MaybeUninit::uninit(); NODE_SIZE];\n\n            // Copy the previous leaf elements\n            new_elements[..self.tail_size()]\n                .copy_from_slice(unsafe { &(*self.tail).elements.leaf[..self.tail_size()] });\n\n            // Overwrite the element\n            new_elements[index - self.tail_offset()] = MaybeUninit::new(value);\n\n            return Self {\n                size: self.size,\n                root: Node::take_ptr_ref(self.root),\n                tail: Node::new_leaf(new_elements),\n            };\n        }\n\n        let depth = Self::trie_depth(self.trie_size());\n        let new_root = unsafe { (&*self.root).assoc_value(depth, index, value) };\n\n        Self {\n            size: self.size,\n            root: new_root,\n            tail: Node::take_ptr_ref(self.tail),\n        }\n    }\n\n    pub fn iter(&self) -> impl ExactSizeIterator<Item = T> + '_ {\n        Iter {\n            vec: self,\n            index: 0,\n            current_leaf: self.get_leaf(0),\n        }\n    }\n\n    pub fn extend(&self, mut values: impl ExactSizeIterator<Item = T>) -> Vector<T> {\n        // This is a three step process:\n        //\n        // 1. Fill the existing tail with values\n        // 2. Push whole NODE_SIZE leaves while enough values remain\n        // 3. Place the rest of the values in the tail\n        //\n        // We can run out of values at any phase and return the finished vector\n\n        if values.len() == 0 {\n            return self.clone();\n        }\n\n        let mut vec_acc = if let Some(tail_ref) = unsafe { self.tail.as_ref() } {\n            let old_tail_size = self.tail_size();\n            let mut tail_elements = [MaybeUninit::uninit(); NODE_SIZE];\n\n            unsafe {\n                tail_elements[..old_tail_size]\n                    .copy_from_slice(&tail_ref.elements.leaf[..old_tail_size]);\n            }\n\n            let fill_size = std::cmp::min(NODE_SIZE - old_tail_size, values.len());\n            let new_tail_size = old_tail_size + fill_size;\n\n            for tail_element in tail_elements.iter_mut().skip(old_tail_size).take(fill_size) {\n                *tail_element = MaybeUninit::new(values.next().unwrap());\n            }\n\n            let new_leaf = Node::new_leaf(tail_elements);\n\n            if new_tail_size != NODE_SIZE {\n                // We only affected the tail\n                return Self {\n                    size: self.size + (fill_size as u64),\n                    root: Node::take_ptr_ref(self.root),\n                    tail: new_leaf,\n                };\n            }\n\n            self.push_leaf(new_leaf, fill_size as u64)\n        } else {\n            self.clone()\n        };\n\n        while values.len() >= NODE_SIZE {\n            let mut trie_elements = [MaybeUninit::uninit(); NODE_SIZE];\n            for trie_element in &mut trie_elements {\n                *trie_element = MaybeUninit::new(values.next().unwrap());\n            }\n\n            vec_acc = vec_acc.push_leaf(Node::new_leaf(trie_elements), NODE_SIZE as u64)\n        }\n\n        let tail_size = values.len();\n        if tail_size > 0 {\n            let mut tail_elements = [MaybeUninit::uninit(); NODE_SIZE];\n            for (tail_element, value) in tail_elements.iter_mut().zip(values) {\n                *tail_element = MaybeUninit::new(value);\n            }\n\n            vec_acc.size += tail_size as u64;\n            vec_acc.tail = Node::new_leaf(tail_elements);\n        }\n\n        vec_acc\n    }\n\n    /// Visits each mutable element of the array\n    ///\n    /// This skips global constants\n    pub(crate) fn visit_mut_elements<F>(&mut self, visitor: &mut F)\n    where\n        F: FnMut(&mut T),\n    {\n        unsafe {\n            if let Some(tail_ref) = (self.tail as *mut Node<T>).as_mut() {\n                tail_ref.visit_mut_elements(0, self.tail_size(), visitor);\n            }\n        }\n\n        unsafe {\n            if let Some(root_ref) = (self.root as *mut Node<T>).as_mut() {\n                let trie_size = self.trie_size();\n                root_ref.visit_mut_elements(Self::trie_depth(trie_size), trie_size, visitor);\n            }\n        }\n    }\n\n    /// Size of the trie portion of the `Vector`\n    ///\n    /// This is always a multiple of `NODE_SIZE`\n    fn trie_size(&self) -> usize {\n        Self::trie_size_for_len(self.len())\n    }\n\n    fn trie_size_for_len(len: usize) -> usize {\n        len - Self::tail_size_for_len(len)\n    }\n\n    /// Size of the tail portion of the `Vector`\n    ///\n    /// This is always less than `NODE_SIZE`\n    fn tail_size(&self) -> usize {\n        Self::tail_size_for_len(self.len())\n    }\n\n    fn tail_size_for_len(len: usize) -> usize {\n        len % NODE_SIZE\n    }\n\n    /// Index of the first element in the tail portion\n    fn tail_offset(&self) -> usize {\n        self.len() - self.tail_size()\n    }\n\n    /// Returns the trie depth for trie of the given size\n    fn trie_depth(trie_size: usize) -> u32 {\n        if trie_size <= 1 {\n            // The root is the only node\n            return 0;\n        }\n\n        (63 - (trie_size as u64 - 1).leading_zeros()) / TRIE_RADIX\n    }\n}\n\nimpl<T> Drop for Vector<T>\nwhere\n    T: Copy,\n{\n    fn drop(&mut self) {\n        unsafe {\n            Node::release_ptr_ref(self.root, Self::trie_depth(self.trie_size()));\n            Node::release_ptr_ref(self.tail, 0);\n        }\n    }\n}\n\nunion NodeElements<T>\nwhere\n    T: Copy,\n{\n    leaf: [MaybeUninit<T>; NODE_SIZE],\n    branch: [*const Node<T>; NODE_SIZE],\n}\n\n#[repr(C)]\nstruct Node<T>\nwhere\n    T: Copy,\n{\n    ref_count: AtomicU64,\n    elements: NodeElements<T>,\n}\n\nimpl<T> Node<T>\nwhere\n    T: Copy,\n{\n    fn new_leaf(elements: [MaybeUninit<T>; NODE_SIZE]) -> *const Node<T> {\n        #[cfg(test)]\n        ALLOCATED_LEAVES.with(|counter| *counter.borrow_mut() += 1);\n\n        let layout = alloc::Layout::new::<Self>();\n\n        unsafe {\n            let node = alloc::alloc(layout) as *mut Node<T>;\n\n            (*node).ref_count = AtomicU64::new(1);\n            (*node).elements.leaf = elements;\n\n            node\n        }\n    }\n\n    fn new_branch(elements: [*const Node<T>; NODE_SIZE]) -> *const Node<T> {\n        #[cfg(test)]\n        ALLOCATED_BRANCHES.with(|counter| *counter.borrow_mut() += 1);\n\n        let layout = alloc::Layout::new::<Self>();\n        debug_assert!(!elements[0].is_null());\n\n        unsafe {\n            let node = alloc::alloc(layout) as *mut Node<T>;\n\n            (*node).ref_count = AtomicU64::new(1);\n            (*node).elements.branch = elements;\n\n            node\n        }\n    }\n\n    fn new_chain(leaf_node: *const Node<T>, remaining_depth: u32) -> *const Node<T> {\n        if remaining_depth == 0 {\n            return leaf_node;\n        }\n\n        // Create a one level intermediate node with a single branch\n        let rest_tail = Self::new_chain(leaf_node, remaining_depth - 1);\n\n        let mut intermediate_elements = [ptr::null::<Node<T>>(); NODE_SIZE];\n        intermediate_elements[0] = rest_tail;\n\n        Self::new_branch(intermediate_elements)\n    }\n\n    fn get_leaf(&self, remaining_depth: u32, index: usize) -> *const Node<T> {\n        if remaining_depth == 0 {\n            return self as *const Node<T>;\n        }\n\n        let level_radix = TRIE_RADIX * remaining_depth;\n        let branch_index = (index >> level_radix) & LEVEL_MASK;\n\n        unsafe { (&*self.elements.branch[branch_index]).get_leaf(remaining_depth - 1, index) }\n    }\n\n    fn assoc_value(&self, remaining_depth: u32, index: usize, value: T) -> *const Node<T> {\n        if remaining_depth == 0 {\n            // Replace the leaf value\n            let mut new_elements = unsafe { self.elements.leaf };\n            new_elements[index & LEVEL_MASK] = MaybeUninit::new(value);\n\n            return Self::new_leaf(new_elements);\n        }\n\n        let level_radix = TRIE_RADIX * remaining_depth;\n        let branch_index = (index >> level_radix) & LEVEL_MASK;\n\n        // Replace the branch value\n        let new_subtree = unsafe {\n            (&*self.elements.branch[branch_index]).assoc_value(remaining_depth - 1, index, value)\n        };\n\n        let mut new_elements: [*const Node<T>; NODE_SIZE] = [ptr::null(); NODE_SIZE];\n\n        for (i, new_element) in new_elements.iter_mut().enumerate() {\n            unsafe {\n                *new_element = if i == branch_index {\n                    new_subtree\n                } else {\n                    Node::take_ptr_ref(self.elements.branch[i])\n                };\n            }\n        }\n\n        Self::new_branch(new_elements)\n    }\n\n    fn push_leaf(\n        &self,\n        remaining_depth: u32,\n        last_index: usize,\n        leaf: *const Node<T>,\n    ) -> *const Node<T> {\n        if remaining_depth == 0 {\n            return leaf;\n        }\n\n        let level_radix = TRIE_RADIX * remaining_depth;\n        let branch_index = (last_index >> level_radix) & LEVEL_MASK;\n\n        // Replace the branch value\n        let new_subtree = unsafe {\n            match self.elements.branch[branch_index].as_ref() {\n                Some(branch) => branch.push_leaf(remaining_depth - 1, last_index, leaf),\n                None => Self::new_chain(leaf, remaining_depth - 1),\n            }\n        };\n\n        let mut new_elements: [*const Node<T>; NODE_SIZE] = [ptr::null(); NODE_SIZE];\n\n        for (new_element, old_element) in new_elements\n            .iter_mut()\n            .zip(unsafe { self.elements.branch }.iter())\n            .take(branch_index)\n        {\n            *new_element = Node::take_ptr_ref(*old_element)\n        }\n\n        new_elements[branch_index] = new_subtree;\n        Self::new_branch(new_elements)\n    }\n\n    fn is_global_constant(&self) -> bool {\n        self.ref_count.load(Ordering::Relaxed) == GLOBAL_CONSTANT_REFCOUNT\n    }\n\n    fn take_ptr_ref(self_ptr: *const Node<T>) -> *const Node<T> {\n        if let Some(self_ref) = unsafe { self_ptr.as_ref() } {\n            if !self_ref.is_global_constant() {\n                self_ref.ref_count.fetch_add(1, Ordering::Relaxed);\n            }\n\n            self_ptr\n        } else {\n            ptr::null()\n        }\n    }\n\n    /// Atomically releases a reference to the node\n    unsafe fn release_ptr_ref(self_ptr: *const Node<T>, depth: u32) {\n        let self_ref = if let Some(self_ref) = self_ptr.as_ref() {\n            self_ref\n        } else {\n            return;\n        };\n\n        if self_ref.is_global_constant() {\n            return;\n        }\n\n        let should_destroy = self_ref.ref_count.fetch_sub(1, Ordering::Release) == 1;\n\n        if should_destroy {\n            sync::atomic::fence(Ordering::Acquire);\n\n            if depth > 0 {\n                for i in 0..NODE_SIZE {\n                    Self::release_ptr_ref(self_ref.elements.branch[i], depth - 1);\n                }\n\n                #[cfg(test)]\n                ALLOCATED_BRANCHES.with(|counter| *counter.borrow_mut() -= 1);\n            } else {\n                #[cfg(test)]\n                ALLOCATED_LEAVES.with(|counter| *counter.borrow_mut() -= 1);\n            }\n\n            alloc::dealloc(\n                self_ref as *const Self as *mut u8,\n                alloc::Layout::new::<Self>(),\n            );\n        }\n    }\n\n    /// Visits up to `remaining_elements` mutable elements, returning the new remaining count\n    fn visit_mut_elements<F>(\n        &mut self,\n        remaining_depth: u32,\n        mut remaining_elements: usize,\n        visitor: &mut F,\n    ) -> usize\n    where\n        F: FnMut(&mut T),\n    {\n        if self.is_global_constant() {\n            // We're a global constant; skip us\n            return remaining_elements.saturating_sub(NODE_SIZE << (remaining_depth * TRIE_RADIX));\n        }\n\n        if remaining_depth == 0 {\n            let leaf_size = std::cmp::min(remaining_elements, NODE_SIZE);\n\n            unsafe {\n                for element in self.elements.leaf.iter_mut().take(leaf_size) {\n                    visitor(&mut (*element.as_mut_ptr()));\n                }\n            }\n\n            return remaining_elements - leaf_size;\n        }\n\n        for branch in unsafe { self.elements.branch.iter() } {\n            unsafe {\n                remaining_elements = (&mut *(*branch as *mut Node<T>)).visit_mut_elements(\n                    remaining_depth - 1,\n                    remaining_elements,\n                    visitor,\n                );\n            }\n\n            if remaining_elements == 0 {\n                return 0;\n            }\n        }\n\n        remaining_elements\n    }\n}\n\nstruct Iter<'a, T>\nwhere\n    T: Copy,\n{\n    vec: &'a Vector<T>,\n    index: usize,\n    current_leaf: *const Node<T>,\n}\n\nimpl<'a, T> Iterator for Iter<'a, T>\nwhere\n    T: Copy,\n{\n    type Item = T;\n\n    fn next(&mut self) -> Option<T> {\n        if self.index >= self.vec.len() {\n            return None;\n        }\n\n        let item =\n            unsafe { (*self.current_leaf).elements.leaf[self.index & LEVEL_MASK].assume_init() };\n\n        self.index += 1;\n        if self.index & LEVEL_MASK == 0 {\n            // Lookup the next node\n            self.current_leaf = self.vec.get_leaf(self.index);\n        }\n\n        Some(item)\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        let exact_size = self.vec.size as usize - self.index;\n\n        (exact_size, Some(exact_size))\n    }\n}\n\nimpl<'a, T> ExactSizeIterator for Iter<'a, T> where T: Copy {}\n\nimpl<T> Clone for Vector<T>\nwhere\n    T: Copy,\n{\n    fn clone(&self) -> Self {\n        Vector {\n            size: self.size,\n            root: Node::take_ptr_ref(self.root),\n            tail: Node::take_ptr_ref(self.tail),\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use std::iter;\n\n    fn assert_nodes_deallocated<T>(block: T)\n    where\n        T: FnOnce(),\n    {\n        assert_eq!(\n            0,\n            ALLOCATED_BRANCHES.with(|counter| *counter.borrow()),\n            \"branches allocated before beginning of test\"\n        );\n\n        assert_eq!(\n            0,\n            ALLOCATED_LEAVES.with(|counter| *counter.borrow()),\n            \"leaves allocated before beginning of test\"\n        );\n\n        block();\n\n        assert_eq!(\n            0,\n            ALLOCATED_BRANCHES.with(|counter| *counter.borrow()),\n            \"branches still allocated after end of test\"\n        );\n\n        assert_eq!(\n            0,\n            ALLOCATED_LEAVES.with(|counter| *counter.borrow()),\n            \"leaves still allocated after end of test\"\n        );\n    }\n\n    #[test]\n    fn tail_only_vector() {\n        assert_nodes_deallocated(|| {\n            let empty_vec = Vector::<i32>::new(iter::empty());\n\n            assert_eq!(0, empty_vec.len());\n            assert!(empty_vec.is_empty());\n\n            let one_vec = empty_vec.extend(iter::once(0));\n\n            // Make sure `empty_vec` is still intact\n            assert_eq!(0, empty_vec.len());\n            assert!(empty_vec.is_empty());\n            assert_eq!(None, empty_vec.get(0));\n\n            assert_eq!(1, one_vec.len());\n            assert!(!one_vec.is_empty());\n            assert_eq!(Some(0), one_vec.get(0));\n\n            // Try modifying the original one item vec\n            let mutated_vec = one_vec.assoc(0, 31337);\n\n            assert_eq!(1, mutated_vec.len());\n            assert!(!mutated_vec.is_empty());\n            assert_eq!(Some(31337), mutated_vec.get(0));\n            assert_eq!(Some(0), one_vec.get(0));\n        });\n    }\n\n    #[test]\n    fn extended_one_level_vector() {\n        assert_nodes_deallocated(|| {\n            const TEST_LEN: usize = 48;\n\n            let mut test_vec = Vector::<usize>::new(iter::empty());\n\n            for i in 0..TEST_LEN {\n                assert_eq!(i, test_vec.len());\n                test_vec = test_vec.extend(iter::once(i));\n            }\n\n            // Check the contents manually\n            for i in 0..TEST_LEN {\n                assert_eq!(Some(i), test_vec.get(i));\n            }\n\n            // Check the contents with an iterator\n            {\n                let test_iter = test_vec.iter();\n                assert_eq!(TEST_LEN, test_iter.len());\n\n                for (actual, expected) in test_vec.iter().enumerate() {\n                    assert_eq!(expected, actual);\n                }\n            }\n        })\n    }\n\n    #[test]\n    fn extended_two_level_vector() {\n        assert_nodes_deallocated(|| {\n            const TEST_LEN: usize = 128;\n\n            let mut test_vec = Vector::<usize>::new(iter::empty());\n\n            for i in 0..TEST_LEN {\n                assert_eq!(i, test_vec.len());\n                test_vec = test_vec.extend(iter::once(i));\n            }\n\n            // Check the contents manually\n            for i in 0..TEST_LEN {\n                assert_eq!(Some(i), test_vec.get(i));\n            }\n\n            // Check the contents with an iterator\n            {\n                let test_iter = test_vec.iter();\n                assert_eq!(TEST_LEN, test_iter.len());\n\n                for (actual, expected) in test_vec.iter().enumerate() {\n                    assert_eq!(expected, actual);\n                }\n            }\n\n            // Check the contents using take\n            for i in (0..TEST_LEN).step_by(3) {\n                let head_vec = test_vec.take(i);\n                assert_eq!(i, head_vec.len());\n\n                if i > 0 {\n                    assert_eq!(Some(0), head_vec.get(0));\n                    assert_eq!(Some(i - 1), head_vec.get(i - 1));\n                }\n            }\n        })\n    }\n\n    #[test]\n    fn initialised_three_level_vector() {\n        assert_nodes_deallocated(|| {\n            const TEST_LEN: usize = 2087;\n\n            let mut test_vec = Vector::<usize>::new(0..TEST_LEN);\n            assert_eq!(TEST_LEN, test_vec.len());\n\n            // Check the contents manually\n            for i in 0..TEST_LEN {\n                assert_eq!(Some(i), test_vec.get(i));\n            }\n\n            // Check the contents with an iterator\n            {\n                let test_iter = test_vec.iter();\n                assert_eq!(TEST_LEN, test_iter.len());\n\n                for (actual, expected) in test_vec.iter().enumerate() {\n                    assert_eq!(expected, actual);\n                }\n            }\n\n            // Manually reverse the vector\n            for i in (0..TEST_LEN).rev() {\n                test_vec = test_vec.assoc(i, TEST_LEN - i - 1);\n            }\n\n            // Make sure it's reversed\n            for i in 0..TEST_LEN {\n                assert_eq!(Some(TEST_LEN - i - 1), test_vec.get(i));\n            }\n\n            // Reverse the vector back by mutable ref\n            test_vec.visit_mut_elements(&mut |element| {\n                *element = TEST_LEN - *element - 1;\n            });\n\n            // Check the contents using take\n            for i in (0..TEST_LEN).step_by(7) {\n                let head_vec = test_vec.take(i);\n                assert_eq!(i, head_vec.len());\n\n                for (actual, expected) in head_vec.iter().enumerate() {\n                    assert_eq!(expected, actual);\n                }\n            }\n        })\n    }\n\n    #[test]\n    fn vector_extend() {\n        assert_nodes_deallocated(|| {\n            let start_vec = Vector::<usize>::new(1..4);\n            let extended_vec = start_vec.extend(4..7);\n\n            let all_values: Vec<usize> = extended_vec.iter().collect();\n            assert_eq!(vec![1, 2, 3, 4, 5, 6], all_values);\n\n            let zero_extended_vec = extended_vec.extend(iter::empty());\n            assert_eq!(6, zero_extended_vec.len());\n        })\n    }\n}\n"
  },
  {
    "path": "runtime/task.rs",
    "content": "#![warn(missing_docs)]\n\n//! Isolated tasks of execution\n\nuse std::panic;\n\nuse crate::binding::Never;\nuse crate::boxed::prelude::*;\nuse crate::boxed::type_info::TypeInfo;\nuse crate::boxed::Heap;\n\n/// Isolated task of execution\n///\n/// All Arret and RFI code must run inside a task. It provides a dedicated garbage collected\n/// [`Heap`] as well as an isolation boundary against panics. A task is inherently single threaded;\n/// it's not possible for one task to be executing on multiple threads at the same time.\npub struct Task {\n    heap: Heap,\n}\n\nimpl Task {\n    const DEFAULT_CAPACITY: usize = 32;\n\n    /// Creates a new empty task\n    pub fn new() -> Task {\n        Self::with_type_info(TypeInfo::empty())\n    }\n\n    pub(crate) fn with_type_info(type_info: TypeInfo) -> Task {\n        Self {\n            heap: Heap::new(type_info, Self::DEFAULT_CAPACITY),\n        }\n    }\n\n    /// Returns this task's dedicated heap\n    pub fn heap(&self) -> &Heap {\n        &self.heap\n    }\n\n    /// Returns a mutable reference to this task's dedicated heap\n    pub fn heap_mut(&mut self) -> &mut Heap {\n        &mut self.heap\n    }\n\n    /// Panics the current task\n    ///\n    /// This destroys the current task and invokes any cleanup required.\n    pub fn panic(&mut self, message: String) -> Never {\n        // Using `resume_unwind` accomplishes two things:\n        //\n        // 1. Avoids printing the panic info to stderr as this is an \"intentional\" panic\n        // 2. Skips incrementing our panic count. This is important for compile time evaluation of\n        //    panics. The compiler and stdlib have a different panic count due to being in separate\n        //    binaries. With a normal `panic!` the panic count will be increment on the stdlib and\n        //    decremented in the compiler. On the second panic the stdlib thinks it's already\n        //    panicking and aborts. This is a hacky workaround.\n        //\n        // TODO: Fix panics uniformly and remove this method. If we panic inside e.g. Rust stdlib\n        // we won't follow this path.\n        panic::resume_unwind(Box::new(message));\n    }\n}\n\nimpl Default for Task {\n    fn default() -> Task {\n        Task::new()\n    }\n}\n\nimpl AsHeap for Task {\n    fn as_heap(&self) -> &Heap {\n        &self.heap\n    }\n\n    fn as_heap_mut(&mut self) -> &mut Heap {\n        &mut self.heap\n    }\n}\n"
  },
  {
    "path": "runtime-syntax/Cargo.toml",
    "content": "[package]\nname = \"arret-runtime-syntax\"\nversion = \"0.1.0\"\nedition = \"2018\"\nauthors = [\"Ryan Cumming <etaoins@gmail.com>\"]\n\n[lib]\npath = \"lib.rs\"\ncrate-type = [\"lib\"]\n\n[dependencies]\narret-syntax = { path = \"../syntax\" }\narret-runtime = { path = \"../runtime\" }\n"
  },
  {
    "path": "runtime-syntax/lib.rs",
    "content": "//! This crate contains functionality for dealing with EDN at runtime\n\n#![warn(clippy::all)]\n#![warn(rust_2018_idioms)]\n\npub mod reader;\npub mod writer;\n"
  },
  {
    "path": "runtime-syntax/reader.rs",
    "content": "use arret_syntax::datum::Datum;\n\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\nuse arret_runtime::boxed::refs::Gc;\n\n/// Places a syntax datum on a box heap\npub fn box_syntax_datum(heap: &mut impl boxed::AsHeap, datum: &Datum) -> Gc<boxed::Any> {\n    match datum {\n        Datum::Bool(_, value) => boxed::Bool::singleton_ref(*value).as_any_ref(),\n        Datum::Int(_, val) => boxed::Int::new(heap, *val).as_any_ref(),\n        Datum::Float(_, val) => boxed::Float::new(heap, *val).as_any_ref(),\n        Datum::Char(_, val) => boxed::Char::new(heap, *val).as_any_ref(),\n        Datum::Str(_, val) => boxed::Str::new(heap, val.as_ref()).as_any_ref(),\n        Datum::Sym(_, val) => boxed::Sym::new(heap, val.as_ref()).as_any_ref(),\n        Datum::List(_, vs) => {\n            boxed::List::from_values(heap, vs.iter(), box_syntax_datum).as_any_ref()\n        }\n        Datum::Vector(_, vs) => {\n            boxed::Vector::from_values(heap, vs.iter(), box_syntax_datum).as_any_ref()\n        }\n        Datum::Set(_, vs) => {\n            boxed::Set::from_values(heap, vs.iter(), box_syntax_datum).as_any_ref()\n        }\n        Datum::Map(_, vs) => boxed::Map::from_values(heap, vs.iter(), |heap, (key, value)| {\n            (box_syntax_datum(heap, key), box_syntax_datum(heap, value))\n        })\n        .as_any_ref(),\n    }\n}\n\n// This is indirectly tested by `writer`\n"
  },
  {
    "path": "runtime-syntax/writer.rs",
    "content": "use std::io::{Result, Write};\n\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::intern::InternedSym;\n\nmacro_rules! process_escaped_chars {\n    ($w:ident, $source:ident, $( $pattern:pat => $escape:expr ),*) => {\n        // Try to write sequential unescaped characters in chunks\n        // This is especially important if $w isn't buffered\n        let mut last_escape_end = 0;\n        for (index, c) in $source.char_indices() {\n            match c {\n                $(\n                    $pattern => {\n                        $w.write_all(&$source.as_bytes()[last_escape_end..index])?;\n                        last_escape_end = index + c.len_utf8();\n                        ($escape)?;\n                    }\n                ),* ,\n                _ => {}\n            };\n        }\n\n        $w.write_all(&$source.as_bytes()[last_escape_end..])?;\n    }\n}\n\nfn write_escaped_str(w: &mut dyn Write, source: &str) -> Result<()> {\n    process_escaped_chars!(w, source,\n        '\\t' => write!(w, \"\\\\t\"),\n        '\\r' => write!(w, \"\\\\r\"),\n        '\\n' => write!(w, \"\\\\n\"),\n        '\\\\' => write!(w, \"\\\\\\\\\"),\n        '\"' => write!(w, \"\\\\\\\"\"),\n        c @ '\\u{0}'..='\\u{19}' => write!(w, \"\\\\x{:X};\", c as u32)\n    );\n\n    Ok(())\n}\n\nfn write_boxed_seq(\n    w: &mut dyn Write,\n    heap: &impl AsHeap,\n    elems: impl Iterator<Item = Gc<boxed::Any>>,\n) -> Result<()> {\n    let mut has_prev = false;\n    for elem in elems {\n        if has_prev {\n            write!(w, \" \")?;\n        } else {\n            has_prev = true;\n        }\n\n        write_boxed(w, heap, elem)?;\n    }\n\n    Ok(())\n}\n\nfn write_boxed_map(\n    w: &mut dyn Write,\n    heap: &impl AsHeap,\n    elems: impl Iterator<Item = (Gc<boxed::Any>, Gc<boxed::Any>)>,\n) -> Result<()> {\n    write!(w, \"{{\")?;\n\n    let mut has_prev = false;\n    for (key, value) in elems {\n        if has_prev {\n            write!(w, \", \")?;\n        } else {\n            has_prev = true;\n        }\n\n        write_boxed(w, heap, key)?;\n        write!(w, \" \")?;\n        write_boxed(w, heap, value)?;\n    }\n\n    write!(w, \"}}\")?;\n    Ok(())\n}\n\nfn write_char(w: &mut dyn Write, c: char) -> Result<()> {\n    match c {\n        '\\n' => write!(w, \"\\\\newline\"),\n        '\\r' => write!(w, \"\\\\return\"),\n        ' ' => write!(w, \"\\\\space\"),\n        '\\t' => write!(w, \"\\\\tab\"),\n        '\\u{21}'..='\\u{126}' => write!(w, \"\\\\{}\", c),\n        other => write!(w, \"\\\\u{:04X}\", other as u32),\n    }\n}\n\n#[allow(clippy::float_cmp)]\nfn write_float(w: &mut dyn Write, f: f64) -> Result<()> {\n    if f.is_nan() {\n        write!(w, \"##NaN\")\n    } else if f.is_infinite() {\n        if f.is_sign_positive() {\n            write!(w, \"##Inf\")\n        } else {\n            write!(w, \"##-Inf\")\n        }\n    } else if f == 0.0 && f.is_sign_negative() {\n        write!(w, \"-0.0\")\n    } else if (f as i64 as f64) == f {\n        // This is has no fractional part; force a .0 to mark it as a float\n        write!(w, \"{:.1}\", f)\n    } else {\n        write!(w, \"{:.}\", f)\n    }\n}\n\nfn write_interned_sym(\n    w: &mut dyn Write,\n    heap: &impl AsHeap,\n    interned_sym: InternedSym,\n) -> Result<()> {\n    // TODO: We don't support quoted/raw symbols as EDN doesn't\n    // This assumes the symbol is a valid identifier\n    write!(\n        w,\n        \"{}\",\n        heap.as_heap()\n            .type_info()\n            .interner()\n            .unintern(&interned_sym)\n    )\n}\n\nfn write_record(w: &mut dyn Write, heap: &impl AsHeap, record: &boxed::Record) -> Result<()> {\n    use boxed::FieldValue;\n\n    // TODO: Print our source name\n    write!(w, \"#record(\")?;\n\n    let mut has_prev = false;\n    for field in record.field_values(heap.as_heap()) {\n        if has_prev {\n            write!(w, \" \")?;\n        } else {\n            has_prev = true;\n        }\n\n        match field {\n            FieldValue::Bool(true) => write!(w, \"true\")?,\n            FieldValue::Bool(false) => write!(w, \"false\")?,\n            FieldValue::Char(c) => write_char(w, c)?,\n            FieldValue::Float(f) => write_float(w, f)?,\n            FieldValue::Int(i) => write!(w, \"{}\", i)?,\n            FieldValue::InternedSym(interned_sym) => write_interned_sym(w, heap, interned_sym)?,\n            FieldValue::Boxed(boxed) => write_boxed(w, heap, boxed)?,\n        }\n    }\n\n    write!(w, \")\")\n}\n\n/// Writes a representation of the passed box to the writer\npub fn write_boxed(w: &mut dyn Write, heap: &impl AsHeap, any_ref: Gc<boxed::Any>) -> Result<()> {\n    use arret_runtime::boxed::AnySubtype;\n\n    match any_ref.as_subtype() {\n        AnySubtype::True(_) => write!(w, \"true\"),\n        AnySubtype::False(_) => write!(w, \"false\"),\n        AnySubtype::Nil(_) => write!(w, \"()\"),\n        AnySubtype::Int(int_ref) => write!(w, \"{}\", int_ref.value()),\n        AnySubtype::Sym(sym) => write_interned_sym(w, heap, sym.interned()),\n        AnySubtype::Float(float_ref) => write_float(w, float_ref.value()),\n        AnySubtype::Pair(list) => {\n            write!(w, \"(\")?;\n            write_boxed_seq(w, heap, list.as_list_ref().iter())?;\n            write!(w, \")\")\n        }\n        AnySubtype::Vector(vec) => {\n            write!(w, \"[\")?;\n            write_boxed_seq(w, heap, vec.iter())?;\n            write!(w, \"]\")\n        }\n        AnySubtype::Set(set) => {\n            write!(w, \"#{{\")?;\n            write_boxed_seq(w, heap, set.iter())?;\n            write!(w, \"}}\")\n        }\n        AnySubtype::Char(char_ref) => write_char(w, char_ref.value()),\n        AnySubtype::Str(s) => {\n            write!(w, \"\\\"\")?;\n            write_escaped_str(w, s.as_str())?;\n            write!(w, \"\\\"\")\n        }\n        AnySubtype::FunThunk(_) => write!(w, \"#fn\"),\n        AnySubtype::Record(record) => write_record(w, heap, record),\n        AnySubtype::Map(map) => write_boxed_map(w, heap, map.iter()),\n    }\n}\n\n/// Writes a pretty-printed representation of the passed box to the writer\npub fn pretty_print_boxed(write: &mut dyn Write, heap: &impl AsHeap, any_ref: Gc<boxed::Any>) {\n    match any_ref.as_subtype() {\n        boxed::AnySubtype::Str(string) => {\n            write.write_all(string.as_str().as_bytes()).unwrap();\n        }\n        boxed::AnySubtype::Char(c) => {\n            let mut buffer = [0; 4];\n            write\n                .write_all(c.value().encode_utf8(&mut buffer).as_bytes())\n                .unwrap();\n        }\n        boxed::AnySubtype::Sym(sym) => {\n            write\n                .write_all(sym.name(heap.as_heap()).as_bytes())\n                .unwrap();\n        }\n        _ => {\n            write_boxed(write, heap.as_heap(), any_ref).unwrap();\n        }\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    fn string_for_boxed(heap: &boxed::Heap, any_ref: Gc<boxed::Any>) -> String {\n        use std::str;\n\n        let mut output_buf: Vec<u8> = vec![];\n        write_boxed(&mut output_buf, heap, any_ref).unwrap();\n        str::from_utf8(output_buf.as_slice()).unwrap().to_owned()\n    }\n\n    fn assert_write(heap: &mut boxed::Heap, expected: &'static str, any_ref: Gc<boxed::Any>) {\n        use crate::reader;\n        use arret_syntax::parser::datum_from_str;\n\n        let first_output = string_for_boxed(heap, any_ref);\n        assert_eq!(expected, first_output);\n\n        // Try to round trip this to make sure our output and tests are sane\n        let reparsed_syntax = datum_from_str(None, &first_output).unwrap();\n        let reboxed_ref = reader::box_syntax_datum(heap, &reparsed_syntax);\n\n        let second_output = string_for_boxed(heap, reboxed_ref);\n        assert_eq!(expected, second_output);\n    }\n\n    #[test]\n    fn bools() {\n        let mut heap = boxed::Heap::empty();\n        assert_write(&mut heap, \"false\", boxed::FALSE_INSTANCE.as_any_ref());\n        assert_write(&mut heap, \"true\", boxed::TRUE_INSTANCE.as_any_ref());\n    }\n\n    #[test]\n    fn ints() {\n        let mut heap = boxed::Heap::empty();\n\n        let boxed_zero = boxed::Int::new(&mut heap, 0);\n        assert_write(&mut heap, \"0\", boxed_zero.as_any_ref());\n\n        let boxed_positive = boxed::Int::new(&mut heap, 120);\n        assert_write(&mut heap, \"120\", boxed_positive.as_any_ref());\n\n        let boxed_negative = boxed::Int::new(&mut heap, -120);\n        assert_write(&mut heap, \"-120\", boxed_negative.as_any_ref());\n    }\n\n    #[test]\n    fn floats() {\n        let mut heap = boxed::Heap::empty();\n\n        let test_floats = [\n            (\"0.0\", 0.0),\n            (\"-0.0\", -0.0),\n            (\"120.0\", 120.0),\n            (\"0.25\", 0.25),\n            (\"-120.0\", -120.0),\n            (\"9007199254740992.0\", 9_007_199_254_740_992.0),\n            (\"##NaN\", std::f64::NAN),\n            (\"##Inf\", std::f64::INFINITY),\n            (\"##-Inf\", std::f64::NEG_INFINITY),\n        ];\n\n        for (expected, f) in &test_floats {\n            let boxed_float = boxed::Float::new(&mut heap, *f);\n            assert_write(&mut heap, expected, boxed_float.as_any_ref());\n        }\n    }\n\n    #[test]\n    fn sym() {\n        let mut heap = boxed::Heap::empty();\n\n        let boxed_foo = boxed::Sym::new(&mut heap, \"foo\");\n        assert_write(&mut heap, \"foo\", boxed_foo.as_any_ref());\n\n        let boxed_bar = boxed::Sym::new(&mut heap, \"bar\");\n        assert_write(&mut heap, \"bar\", boxed_bar.as_any_ref());\n    }\n\n    #[test]\n    fn lists() {\n        let mut heap = boxed::Heap::empty();\n\n        let empty_list = boxed::List::from_values(&mut heap, [].iter().cloned(), boxed::Int::new);\n        assert_write(&mut heap, \"()\", empty_list.as_any_ref());\n\n        let one_list = boxed::List::from_values(&mut heap, [1].iter().cloned(), boxed::Int::new);\n        assert_write(&mut heap, \"(1)\", one_list.as_any_ref());\n\n        let three_list =\n            boxed::List::from_values(&mut heap, [1, 2, 3].iter().cloned(), boxed::Int::new);\n        assert_write(&mut heap, \"(1 2 3)\", three_list.as_any_ref());\n    }\n\n    #[test]\n    fn vectors() {\n        let mut heap = boxed::Heap::empty();\n\n        let empty_vector =\n            boxed::Vector::from_values(&mut heap, [].iter().cloned(), boxed::Int::new);\n        assert_write(&mut heap, \"[]\", empty_vector.as_any_ref());\n\n        let one_vector =\n            boxed::Vector::from_values(&mut heap, [1].iter().cloned(), boxed::Int::new);\n        assert_write(&mut heap, \"[1]\", one_vector.as_any_ref());\n\n        let three_vector =\n            boxed::Vector::from_values(&mut heap, [1, 2, 3].iter().cloned(), boxed::Int::new);\n        assert_write(&mut heap, \"[1 2 3]\", three_vector.as_any_ref());\n    }\n\n    #[test]\n    fn chars() {\n        let mut heap = boxed::Heap::empty();\n\n        let test_chars = [\n            (\"\\\\newline\", '\\n'),\n            (\"\\\\return\", '\\r'),\n            (\"\\\\space\", ' '),\n            (\"\\\\tab\", '\\t'),\n            (\"\\\\a\", 'a'),\n            (\"\\\\A\", 'A'),\n            (\"\\\\(\", '('),\n            (\"\\\\u03BB\", '\\u{03bb}'),\n        ];\n\n        for (expected, c) in &test_chars {\n            let boxed_char = boxed::Char::new(&mut heap, *c);\n            assert_write(&mut heap, expected, boxed_char.as_any_ref());\n        }\n    }\n\n    #[test]\n    fn strings() {\n        let mut heap = boxed::Heap::empty();\n\n        let test_strings = [\n            (r#\"\"\"\"#, \"\"),\n            (r#\"\"Hello, world!\"\"#, \"Hello, world!\"),\n            (r#\"\"Hello\\\"World\"\"#, \"Hello\\\"World\"),\n            (r#\"\"Hello\\\\World\"\"#, \"Hello\\\\World\"),\n            (r#\"\"Tab\\t\"\"#, \"Tab\\t\"),\n            (r#\"\"\\n\\nnewline\"\"#, \"\\n\\nnewline\"),\n            (r#\"\"carriage: \\r\"\"#, \"carriage: \\r\"),\n            (r#\"\"lλ\"\"#, \"lλ\"),\n            (r#\"\"\\x0;null!\"\"#, \"\\u{0}null!\"),\n            (\n                r#\"\"The word \\\"recursion\\\" has many meanings.\"\"#,\n                r#\"The word \"recursion\" has many meanings.\"#,\n            ),\n        ];\n\n        for (expected, s) in &test_strings {\n            let boxed_char = boxed::Str::new(&mut heap, *s);\n            assert_write(&mut heap, expected, boxed_char.as_any_ref());\n        }\n    }\n}\n"
  },
  {
    "path": "stdlib/arret/base.arret",
    "content": "(import [arret internal primitives])\n(export def let fn if quote export defmacro letmacro macro-rules deftype lettype compile-error do =\n        defrecord letrecord recur)\n\n(import [arret internal types])\n(export Any Bool Str Sym Int Float Num Char List Vector Vectorof Setof Map U Record -> ->! str? sym?\n        bool? num? int? float? char? list? vector? set? map? fn? nil? record?)\n\n(import [stdlib rust])\n(export length panic panic! print! println! print-str write! writeln! write-str read-str exit! cons\n        map filter some? every? fold concat take reverse repeat int float < <= == > >= + * - / rem\n        quot sqrt vector vector-length vector->list vector-ref vector-assoc vector-append\n        vector-extend vector-take hash set set-length set->list set-contains? bit-and bit-or\n        bit-xor bit-not bit-shift-left bit-shift-right unsigned-bit-shift-right)\n\n(export defn)\n(defmacro defn (macro-rules\n  [(destruc fn-data ...) (def destruc (fn fn-data ...))]\n))\n\n(export list)\n(defn list #{A} (& [l A]) -> (List & A)\n  l)\n\n(export when)\n(defmacro when (macro-rules\n  [(test body-data ...) (if test (do body-data ...) ())]\n))\n\n(export when-not)\n(defmacro when-not (macro-rules\n  [(test body-data ...) (if test () (do body-data ...))]\n))\n\n(export cond)\n(defmacro cond (macro-rules\n  [() ()]\n  ; Intentionally don't allow a tail here so we throw an error with unreachable clauses\n  [(:else body-expr) body-expr]\n  [(test-expr body-expr rest-clauses ...)\n   (if test-expr\n     body-expr\n     (cond rest-clauses ...))]\n))\n\n(export if-not)\n(defmacro if-not (macro-rules\n  [(test-expr false-expr true-expr) (if test-expr true-expr false-expr)]\n))\n\n(export comment)\n(defmacro comment (macro-rules\n  [(_ ...) '()]\n))\n\n(export and)\n(defmacro and (macro-rules\n  [() true]\n  [(test) test]\n  [(test1 test2 ...)\n   (if test1 (and test2 ...) false)]\n))\n\n(export or)\n(defmacro or (macro-rules\n  [() false]\n  [(test) test]\n  [(test1 test2 ...)\n   (if test1 true (or test2 ...))]\n))\n\n(export not)\n(defmacro not (macro-rules\n  [(test) (if test false true)]\n))\n\n; This is a macro to support occurrence typing\n(export not=)\n(defmacro not= (macro-rules\n  [(lhs rhs) (not (= lhs rhs))]\n))\n\n(export ann)\n(defmacro ann (macro-rules\n [(val Type) (let [[typed-val Type] val] typed-val)]\n))\n\n(export first)\n(defn first #{T} (([v T] & _)) -> T\n  v)\n\n(export second)\n(defn second #{T} ((_ [v T] & _)) -> T\n  v)\n\n(export nth)\n(defn nth #{T} ([l (List & T)] [i Int]) -> T\n  (cond\n    (nil? l) (panic \"index past end of list\")\n    (<= i 0) (first l)\n    :else (recur (rest l) (dec i))))\n\n(export rest)\n(defn rest #{T} ((_ & [tail T])) -> (List & T)\n  tail)\n\n(export zero?)\n(defn zero? ([n Num]) -> Bool\n  (if (int? n)\n    (= n 0)\n    (= n 0.0)))\n\n(export pos?)\n(defn pos? ([n Num]) -> Bool\n  (if (int? n)\n    (> n 0)\n    (> n 0.0)))\n\n(export neg?)\n(defn neg? ([n Num]) -> Bool\n  (if (int? n)\n    (< n 0)\n    (< n 0.0)))\n\n(export nan?)\n(defn nan? ([f Float]) -> Bool\n  (not= f f))\n\n(export infinite?)\n(defn infinite? ([f Float]) -> Bool\n  (or (= f ##Inf) (= f ##-Inf)))\n\n(export even?)\n(defn even? ([v Int]) -> Bool\n  (zero? (rem v 2)))\n\n(export odd?)\n(defn odd? ([v Int]) -> Bool\n  (not (zero? (rem v 2))))\n\n(export inc)\n(defn inc ([i Int]) -> Int\n  (+ i 1))\n\n(export dec)\n(defn dec ([i Int]) -> Int\n  (- i 1))\n\n(defmacro defextrema (macro-rules\n  [(name operator)\n   (defn name #{[N Num]} ([first N] & [rest N]) -> N\n     (fold (fn ([acc N] [next N]) -> N\n             (cond\n               (and (float? next) (nan? next)) next\n               (and (float? acc) (nan? acc)) acc\n               (operator next acc) next\n               :else acc))\n           first rest))]\n))\n\n\n(export min)\n(defextrema min <)\n\n(export max)\n(defextrema max >)\n\n(export true?)\n(defn true? ([v Any]) -> Bool\n  (= true v))\n\n(export false?)\n(defn false? ([v Any]) -> Bool\n  (= false v))\n\n(export any?)\n(defn any? ([_ Any]) -> true\n  true)\n\n(export identity)\n(defn identity #{T} ([v T]) -> T\n  v)\n\n(export constantly)\n(defn constantly #{T} ([v T]) -> (& Any -> T)\n  (fn (& _) v))\n\n; We're polymorphic over both the needle and haystack to build type-specific equality checks\n(export member?)\n(defn member? #{N H} ([needle N] [haystack (List & H)]) -> Bool\n  (if (nil? haystack)\n    false\n    (or\n      (= needle (first haystack))\n      (recur needle (rest haystack)))))\n\n(export drop)\n(defn drop #{T} ([i Int] [l (List & T)]) -> (List & T)\n  (cond\n    (<= i 0) l\n    (nil? l) l\n    :else (recur (dec i) (rest l))))\n\n(export drop-last)\n(defn drop-last #{T} ([i Int] [l (List & T)]) -> (List & T)\n  (take (- (length l) i) l))\n\n(export ->>)\n(defmacro ->> (macro-rules\n  [(initial) initial]\n  [(initial (first-fn args ...) rest ...)\n    (->> (first-fn args ... initial) rest ...)]\n))"
  },
  {
    "path": "stdlib/arret/set.arret",
    "content": "(import [stdlib base])\n(import (:only [stdlib rust] subset?))\n\n(export subset?)\n\n(export superset?)\n(defn superset? #{T} ([superset (Setof T)] [subset (Setof T)]) -> Bool\n  (subset? subset superset))"
  },
  {
    "path": "stdlib/arret/test.arret",
    "content": "(import [stdlib base])\n\n; Explicitly don't export `fn-op-categories`; make callers use our assertions instead\n(import (:only [stdlib rust] black-box black-box! heap-alloc-count fn-op-categories))\n(export black-box black-box! heap-alloc-count)\n\n(export black-box-untyped!)\n(defn black-box-untyped! ([input Any]) ->! Any\n  (black-box! input))\n\n(export assert-eq!)\n(defmacro assert-eq! (macro-rules\n  [(expected-expr actual-expr)\n    (let [expected expected-expr actual actual-expr]\n      (when-not (= expected actual)\n        (panic! \"`\" expected \"` does not equal `\" actual \"`\")))]))\n\n(export assert-ne!)\n(defmacro assert-ne! (macro-rules\n  [(expected-expr actual-expr)\n    (let [expected expected-expr actual actual-expr]\n      (when (= expected actual)\n        (panic! \"`\" expected \"` equals `\" actual \"`\")))]))\n\n(export assert-fn-contains-op!)\n(defmacro assert-fn-contains-op! (macro-rules\n  [(op-category test-fn)\n    (when-not (member? op-category (fn-op-categories test-fn))\n      (panic! \"expected built function to contain an op of category `\" op-category \"`\"))]))\n\n(export assert-fn-doesnt-contain-op!)\n(defmacro assert-fn-doesnt-contain-op! (macro-rules\n  [(op-category test-fn)\n    (when (member? op-category (fn-op-categories test-fn))\n      (panic! \"built function unexpectedly contained an op of category `\" op-category \"`\"))]))\n\n(export assert-fn-returns-constant!)\n(defmacro assert-fn-returns-constant! (macro-rules\n  [(test-fn)\n    (when-not (every?\n        #(member? % '(:const-box :const-cast-box :const-reg :cast-boxed :ret))\n        (fn-op-categories test-fn))\n      (panic! \"built function unexpectedly returns non-constant\"))]))"
  },
  {
    "path": "stdlib/rust/Cargo.toml",
    "content": "[package]\nname = \"arret-stdlib\"\nversion = \"0.1.0\"\nedition = \"2018\"\nauthors = [\"Ryan Cumming <etaoins@gmail.com>\"]\n\n[lib]\nname = \"stdlib\"\npath = \"lib.rs\"\ncrate-type = [\"cdylib\", \"staticlib\"]\n\n[dependencies]\narret-syntax = { path = \"../../syntax\" }\narret-runtime = { path = \"../../runtime\" }\narret-runtime-syntax = { path = \"../../runtime-syntax\" }\narret-rfi-derive = { path = \"../../rfi-derive\" }"
  },
  {
    "path": "stdlib/rust/bitwise.rs",
    "content": "use arret_runtime::binding::*;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::task::Task;\n\n#[arret_rfi_derive::rust_fun(\"(Int Int & Int -> Int)\")]\npub fn stdlib_bit_and(lhs: i64, rhs: i64, rest: Gc<boxed::List<boxed::Int>>) -> i64 {\n    rest.iter().fold(lhs & rhs, |acc, i| acc & i.value())\n}\n\n#[arret_rfi_derive::rust_fun(\"(Int Int & Int -> Int)\")]\npub fn stdlib_bit_or(lhs: i64, rhs: i64, rest: Gc<boxed::List<boxed::Int>>) -> i64 {\n    rest.iter().fold(lhs | rhs, |acc, i| acc | i.value())\n}\n\n#[arret_rfi_derive::rust_fun(\"(Int Int & Int -> Int)\")]\npub fn stdlib_bit_xor(lhs: i64, rhs: i64, rest: Gc<boxed::List<boxed::Int>>) -> i64 {\n    rest.iter().fold(lhs ^ rhs, |acc, i| acc ^ i.value())\n}\n\n#[arret_rfi_derive::rust_fun(\"(Int -> Int)\")]\npub fn stdlib_bit_not(val: i64) -> i64 {\n    !val\n}\n\n#[arret_rfi_derive::rust_fun(\"(Int Int -> Int)\")]\npub fn stdlib_bit_shift_left(task: &mut Task, val: i64, bit_count: i64) -> i64 {\n    if bit_count < 0 {\n        task.panic(format!(\"shift left by negative bit count {}\", bit_count));\n    } else if bit_count > 64 {\n        task.panic(format!(\"shift left by {} bits exceeds 64 bits\", bit_count));\n    }\n\n    val << (bit_count as u32)\n}\n\n#[arret_rfi_derive::rust_fun(\"(Int Int -> Int)\")]\npub fn stdlib_bit_shift_right(task: &mut Task, val: i64, bit_count: i64) -> i64 {\n    if bit_count < 0 {\n        task.panic(format!(\"shift right by negative bit count {}\", bit_count));\n    } else if bit_count > 64 {\n        task.panic(format!(\"shift right by {} bits exceeds 64 bits\", bit_count));\n    }\n\n    val >> (bit_count as u32)\n}\n\n#[arret_rfi_derive::rust_fun(\"(Int Int -> Int)\")]\npub fn stdlib_unsigned_bit_shift_right(task: &mut Task, val: i64, bit_count: i64) -> i64 {\n    if bit_count < 0 {\n        task.panic(format!(\"shift right by negative bit count {}\", bit_count));\n    } else if bit_count > 64 {\n        task.panic(format!(\"shift right by {} bits exceeds 64 bits\", bit_count));\n    }\n\n    (val as u64 >> (bit_count as u32)) as i64\n}\n"
  },
  {
    "path": "stdlib/rust/hash.rs",
    "content": "use std::collections::hash_map::DefaultHasher;\nuse std::hash::Hasher as _;\n\nuse arret_runtime::binding::*;\n\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::task::Task;\n\n#[arret_rfi_derive::rust_fun(\"(Any -> Int)\")]\npub fn stdlib_hash(task: &mut Task, input: Gc<boxed::Any>) -> i64 {\n    let mut state = DefaultHasher::new();\n\n    input.hash_in_heap(task.heap(), &mut state);\n    state.finish() as i64\n}\n"
  },
  {
    "path": "stdlib/rust/lib.rs",
    "content": "#![warn(clippy::all)]\n#![warn(rust_2018_idioms)]\n\n#[macro_use]\nextern crate arret_runtime;\n\npub mod list;\nuse crate::list::*;\n\npub mod math;\nuse crate::math::*;\n\npub mod number;\nuse crate::number::*;\n\npub mod testing;\nuse crate::testing::*;\n\npub mod vector;\nuse crate::vector::*;\n\npub mod write;\nuse crate::write::*;\n\npub mod read;\nuse crate::read::*;\n\npub mod hash;\nuse crate::hash::*;\n\npub mod set;\nuse crate::set::*;\n\npub mod bitwise;\nuse crate::bitwise::*;\n\nuse arret_runtime_syntax::writer::pretty_print_boxed;\n\nuse arret_runtime::binding::*;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::task::Task;\n\npub fn panic_common(task: &mut Task, values: Gc<boxed::List<boxed::Any>>) -> Never {\n    use std::str;\n\n    let mut output = Vec::<u8>::new();\n    for value in values.iter() {\n        pretty_print_boxed(&mut output, task, value)\n    }\n\n    let message = str::from_utf8(output.as_slice()).unwrap().to_owned();\n    task.panic(message)\n}\n\n#[arret_rfi_derive::rust_fun(\"(& Any -> (U))\")]\npub fn stdlib_panic(task: &mut Task, values: Gc<boxed::List<boxed::Any>>) -> Never {\n    panic_common(task, values)\n}\n\n#[arret_rfi_derive::rust_fun(\"(& Any ->! (U))\")]\npub fn stdlib_panic_impure(task: &mut Task, values: Gc<boxed::List<boxed::Any>>) -> Never {\n    panic_common(task, values)\n}\n\n#[arret_rfi_derive::rust_fun(\"(Int ->! (U))\")]\npub fn stdlib_exit(exit_code: i64) {\n    use std::process::exit;\n    exit(exit_code as i32);\n}\n\ndefine_rust_module!(ARRET_STDLIB_RUST_EXPORTS, {\n    \"panic\" => stdlib_panic,\n    \"panic!\" => stdlib_panic_impure,\n    \"exit!\" => stdlib_exit,\n\n    \"print!\" => stdlib_print,\n    \"println!\" => stdlib_println,\n    \"print-str\" => stdlib_print_str,\n    \"write!\" => stdlib_write,\n    \"writeln!\" => stdlib_writeln,\n    \"write-str\" => stdlib_write_str,\n\n    \"read-str\" => stdlib_read_str,\n\n    \"length\" => stdlib_length,\n    \"map\" => stdlib_map,\n    \"filter\" => stdlib_filter,\n    \"some?\" => stdlib_some_p,\n    \"every?\" => stdlib_every_p,\n    \"fold\" => stdlib_fold,\n    \"cons\" => stdlib_cons,\n    \"concat\" => stdlib_concat,\n    \"take\" => stdlib_take,\n    \"reverse\" => stdlib_reverse,\n    \"repeat\" => stdlib_repeat,\n\n    \"float\" => stdlib_float,\n    \"int\" => stdlib_int,\n    \"<\" => stdlib_num_lt,\n    \"<=\" => stdlib_num_le,\n    \"==\" => stdlib_num_eq,\n    \">\" => stdlib_num_gt,\n    \">=\" => stdlib_num_ge,\n\n    \"+\" => stdlib_add,\n    \"*\" => stdlib_mul,\n    \"-\" => stdlib_sub,\n    \"/\" => stdlib_div,\n    \"quot\" => stdlib_quot,\n    \"rem\" => stdlib_rem,\n    \"sqrt\" => stdlib_sqrt,\n\n    \"black-box\" => stdlib_black_box,\n    \"black-box!\" => stdlib_black_box_impure,\n    \"heap-alloc-count\" => stdlib_heap_alloc_count,\n    \"fn-op-categories\" => stdlib_fn_op_categories,\n\n    \"vector\" => stdlib_vector,\n    \"vector-length\" => stdlib_vector_length,\n    \"vector->list\" => stdlib_vector_to_list,\n    \"vector-ref\" => stdlib_vector_ref,\n    \"vector-assoc\" => stdlib_vector_assoc,\n    \"vector-extend\" => stdlib_vector_extend,\n    \"vector-append\" => stdlib_vector_append,\n    \"vector-take\" => stdlib_vector_take,\n\n    \"hash\" => stdlib_hash,\n\n    \"set\" => stdlib_set,\n    \"set-length\" => stdlib_set_length,\n    \"set->list\" => stdlib_set_to_list,\n    \"set-contains?\" => stdlib_set_contains_p,\n    \"subset?\" => stdlib_subset_p,\n\n    \"bit-and\" => stdlib_bit_and,\n    \"bit-or\" => stdlib_bit_or,\n    \"bit-xor\" => stdlib_bit_xor,\n    \"bit-not\" => stdlib_bit_not,\n    \"bit-shift-left\" => stdlib_bit_shift_left,\n    \"bit-shift-right\" => stdlib_bit_shift_right,\n    \"unsigned-bit-shift-right\" => stdlib_unsigned_bit_shift_right\n});\n"
  },
  {
    "path": "stdlib/rust/list.rs",
    "content": "use arret_runtime::binding::*;\n\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::callback;\nuse arret_runtime::task::Task;\n\n#[arret_rfi_derive::rust_fun(\"((List & Any) -> Int)\")]\npub fn stdlib_length(input: Gc<boxed::List<boxed::Any>>) -> i64 {\n    input.len() as i64\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{H T} H (List & T) -> (List H & T))\")]\npub fn stdlib_cons(\n    task: &mut Task,\n    head: Gc<boxed::Any>,\n    tail: Gc<boxed::List<boxed::Any>>,\n) -> Gc<boxed::Pair<boxed::Any>> {\n    boxed::Pair::new(task, head, tail)\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{I O [->_ ->!]} (I ->_ O) (List & I) ->_ (List & O))\")]\npub fn stdlib_map(\n    task: &mut Task,\n    mapper: callback::Callback<\n        extern \"C\" fn(&mut Task, boxed::Captures, Gc<boxed::Any>) -> Gc<boxed::Any>,\n    >,\n    input: Gc<boxed::List<boxed::Any>>,\n) -> Gc<boxed::List<boxed::Any>> {\n    let output_vec: Vec<Gc<boxed::Any>> =\n        input.iter().map(|elem| mapper.apply(task, elem)).collect();\n\n    boxed::List::new(task, output_vec.into_iter())\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T [->_ ->!]} (T ->_ Bool) (List & T) ->_ (List & T))\")]\npub fn stdlib_filter(\n    task: &mut Task,\n    filter: callback::Callback<extern \"C\" fn(&mut Task, boxed::Captures, Gc<boxed::Any>) -> bool>,\n    input: Gc<boxed::List<boxed::Any>>,\n) -> Gc<boxed::List<boxed::Any>> {\n    let output_vec: Vec<Gc<boxed::Any>> = input\n        .iter()\n        .filter(|elem| filter.apply(task, *elem))\n        .collect();\n\n    boxed::List::new(task, output_vec.into_iter())\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T [->_ ->!]} (T ->_ Bool) (List & T) ->_ Bool)\")]\npub fn stdlib_some_p(\n    task: &mut Task,\n    pred: callback::Callback<extern \"C\" fn(&mut Task, boxed::Captures, Gc<boxed::Any>) -> bool>,\n    input: Gc<boxed::List<boxed::Any>>,\n) -> bool {\n    input.iter().any(|elem| pred.apply(task, elem))\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T [->_ ->!]} (T ->_ Bool) (List & T) ->_ Bool)\")]\npub fn stdlib_every_p(\n    task: &mut Task,\n    pred: callback::Callback<extern \"C\" fn(&mut Task, boxed::Captures, Gc<boxed::Any>) -> bool>,\n    input: Gc<boxed::List<boxed::Any>>,\n) -> bool {\n    input.iter().all(|elem| pred.apply(task, elem))\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{I O [->_ ->!]} (O I ->_ O) O (List & I) ->_ O)\")]\npub fn stdlib_fold(\n    task: &mut Task,\n    folder: callback::Callback<\n        extern \"C\" fn(&mut Task, boxed::Captures, Gc<boxed::Any>, Gc<boxed::Any>) -> Gc<boxed::Any>,\n    >,\n    initial: Gc<boxed::Any>,\n    input: Gc<boxed::List<boxed::Any>>,\n) -> Gc<boxed::Any> {\n    input\n        .iter()\n        .fold(initial, |acc, elem| folder.apply(task, acc, elem))\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} & (List & T) -> (List & T))\")]\npub fn stdlib_concat(\n    task: &mut Task,\n    lists: Gc<boxed::List<boxed::List<boxed::Any>>>,\n) -> Gc<boxed::List<boxed::Any>> {\n    let mut list_iter = lists.iter();\n\n    match list_iter.len() {\n        0 => boxed::List::empty(),\n        1 => list_iter.next().unwrap(),\n        2 => {\n            // Avoid building a temporary `Vec`\n            let head_list = list_iter.next().unwrap();\n            let tail_list = list_iter.next().unwrap();\n\n            boxed::List::new_with_tail(task, head_list.iter(), tail_list)\n        }\n        _ => {\n            let mut head_values = vec![];\n            while list_iter.len() > 1 {\n                head_values.extend(list_iter.next().unwrap().iter());\n            }\n\n            // We can reuse our tail\n            boxed::List::new_with_tail(task, head_values.into_iter(), list_iter.next().unwrap())\n        }\n    }\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} Int (List & T) -> (List & T))\")]\npub fn stdlib_take(\n    task: &mut Task,\n    count: i64,\n    input: Gc<boxed::List<boxed::Any>>,\n) -> Gc<boxed::List<boxed::Any>> {\n    let usize_count = if count < 0 { 0 } else { count as usize };\n    boxed::List::new(task, input.iter().take(usize_count))\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} (List & T) -> (List & T))\")]\npub fn stdlib_reverse(\n    task: &mut Task,\n    input: Gc<boxed::List<boxed::Any>>,\n) -> Gc<boxed::List<boxed::Any>> {\n    let output_vec: Vec<Gc<boxed::Any>> = input.iter().collect();\n    boxed::List::new(task, output_vec.into_iter().rev())\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} Int T -> (List & T))\")]\npub fn stdlib_repeat(\n    task: &mut Task,\n    count: i64,\n    value: Gc<boxed::Any>,\n) -> Gc<boxed::List<boxed::Any>> {\n    struct RepeatIter<T: boxed::Boxed> {\n        count: i64,\n        value: Gc<T>,\n    }\n\n    impl<T: boxed::Boxed> Iterator for RepeatIter<T> {\n        type Item = Gc<T>;\n\n        fn next(&mut self) -> Option<Gc<T>> {\n            if self.count > 0 {\n                self.count -= 1;\n                Some(self.value)\n            } else {\n                None\n            }\n        }\n\n        fn size_hint(&self) -> (usize, Option<usize>) {\n            if self.count < 0 {\n                (0, Some(0))\n            } else {\n                (self.count as usize, Some(self.count as usize))\n            }\n        }\n    }\n\n    impl<T: boxed::Boxed> ExactSizeIterator for RepeatIter<T> {}\n\n    boxed::List::new(task, RepeatIter { count, value })\n}\n"
  },
  {
    "path": "stdlib/rust/math.rs",
    "content": "use arret_runtime::binding::*;\n\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::task::Task;\n\nfn fold_float_op<FR>(\n    task: &mut Task,\n    operands_iter: impl Iterator<Item = Gc<boxed::Num>>,\n    initial_value: f64,\n    float_reduce: FR,\n) -> Gc<boxed::Float>\nwhere\n    FR: Fn(f64, f64) -> f64,\n{\n    let mut float_acc = initial_value;\n\n    for operand in operands_iter {\n        match operand.as_subtype() {\n            boxed::NumSubtype::Int(int_ref) => {\n                float_acc = float_reduce(float_acc, int_ref.value() as f64);\n            }\n            boxed::NumSubtype::Float(float_ref) => {\n                // Convert to float and break\n                float_acc = float_reduce(float_acc, float_ref.value());\n            }\n        }\n    }\n\n    boxed::Float::new(task, float_acc)\n}\n\nfn fold_num_op<IR, FR>(\n    task: &mut Task,\n    op_name: &'static str,\n    mut operands_iter: impl Iterator<Item = Gc<boxed::Num>>,\n    initial_value: i64,\n    int_reduce: IR,\n    float_reduce: FR,\n) -> Gc<boxed::Num>\nwhere\n    IR: Fn(i64, i64) -> Option<i64>,\n    FR: Fn(f64, f64) -> f64,\n{\n    // Accumulate as an integer for as long as possible\n    let mut int_acc = initial_value;\n\n    while let Some(operand) = operands_iter.next() {\n        match operand.as_subtype() {\n            boxed::NumSubtype::Int(int_ref) => {\n                if let Some(reduced_int) = int_reduce(int_acc, int_ref.value()) {\n                    int_acc = reduced_int;\n                } else {\n                    task.panic(format!(\"attempt to {} with overflow\", op_name));\n                }\n            }\n            boxed::NumSubtype::Float(float_ref) => {\n                // Switch to float\n                let float_acc = float_reduce(int_acc as f64, float_ref.value());\n                return fold_float_op(task, operands_iter, float_acc, float_reduce).as_num_ref();\n            }\n        }\n    }\n\n    boxed::Int::new(task, int_acc).as_num_ref()\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{[N Num]} N & N -> N)\")]\npub fn stdlib_add(\n    task: &mut Task,\n    initial_num: Gc<boxed::Num>,\n    rest: Gc<boxed::List<boxed::Num>>,\n) -> Gc<boxed::Num> {\n    use std::iter;\n    use std::ops::Add;\n\n    fold_num_op(\n        task,\n        \"add\",\n        iter::once(initial_num).chain(rest.iter()),\n        0,\n        i64::checked_add,\n        f64::add,\n    )\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{[N Num]} N & N -> N)\")]\npub fn stdlib_mul(\n    task: &mut Task,\n    initial_num: Gc<boxed::Num>,\n    rest: Gc<boxed::List<boxed::Num>>,\n) -> Gc<boxed::Num> {\n    use std::iter;\n    use std::ops::Mul;\n\n    fold_num_op(\n        task,\n        \"multiply\",\n        iter::once(initial_num).chain(rest.iter()),\n        1,\n        i64::checked_mul,\n        f64::mul,\n    )\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{[N Num]} N & N -> N)\")]\npub fn stdlib_sub(\n    task: &mut Task,\n    initial_num: Gc<boxed::Num>,\n    rest: Gc<boxed::List<boxed::Num>>,\n) -> Gc<boxed::Num> {\n    use std::ops::Sub;\n\n    match initial_num.as_subtype() {\n        boxed::NumSubtype::Int(int_ref) => {\n            if rest.is_empty() {\n                boxed::Int::new(task, -int_ref.value()).as_num_ref()\n            } else {\n                fold_num_op(\n                    task,\n                    \"subtract\",\n                    rest.iter(),\n                    int_ref.value(),\n                    i64::checked_sub,\n                    f64::sub,\n                )\n            }\n        }\n        boxed::NumSubtype::Float(float_ref) => {\n            if rest.is_empty() {\n                boxed::Float::new(task, -float_ref.value()).as_num_ref()\n            } else {\n                fold_float_op(task, rest.iter(), float_ref.value(), f64::sub).as_num_ref()\n            }\n        }\n    }\n}\n\n#[arret_rfi_derive::rust_fun(\"(Float & Float -> Float)\")]\npub fn stdlib_div(initial_float: f64, rest: Gc<boxed::List<boxed::Float>>) -> f64 {\n    if rest.is_empty() {\n        initial_float.recip()\n    } else {\n        let mut acc = initial_float;\n        for operand in rest.iter() {\n            acc /= operand.value()\n        }\n\n        acc\n    }\n}\n\n#[arret_rfi_derive::rust_fun(\"(Int Int -> Int)\")]\npub fn stdlib_quot(task: &mut Task, numerator: i64, denominator: i64) -> i64 {\n    match numerator.checked_div(denominator) {\n        Some(result) => result,\n        None => {\n            task.panic(\"division by zero\".to_owned());\n            unreachable!(\"returned from panic\")\n        }\n    }\n}\n\n#[arret_rfi_derive::rust_fun(\"(Int Int -> Int)\")]\npub fn stdlib_rem(task: &mut Task, numerator: i64, denominator: i64) -> i64 {\n    match numerator.checked_rem(denominator) {\n        Some(result) => result,\n        None => {\n            task.panic(\"division by zero\".to_owned());\n            unreachable!(\"returned from panic\")\n        }\n    }\n}\n\n#[arret_rfi_derive::rust_fun(\"(Float -> Float)\")]\npub fn stdlib_sqrt(radicand: f64) -> f64 {\n    radicand.sqrt()\n}\n"
  },
  {
    "path": "stdlib/rust/number.rs",
    "content": "use arret_runtime::binding::*;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::task::Task;\n\nfn compare_nums<IC, FC>(\n    initial: Gc<boxed::Num>,\n    rest: Gc<boxed::List<boxed::Num>>,\n    int_comparator: IC,\n    float_comparator: FC,\n) -> bool\nwhere\n    IC: Fn(&i64, &i64) -> bool,\n    FC: Fn(&f64, &f64) -> bool,\n{\n    let mut left = initial;\n\n    for right in rest.iter() {\n        use boxed::NumSubtype;\n\n        let result = match (left.as_subtype(), right.as_subtype()) {\n            (NumSubtype::Int(left_ref), NumSubtype::Int(right_ref)) => {\n                int_comparator(&left_ref.value(), &right_ref.value())\n            }\n            (NumSubtype::Float(left_ref), NumSubtype::Int(right_ref)) => {\n                float_comparator(&left_ref.value(), &(right_ref.value() as f64))\n            }\n            (NumSubtype::Int(left_ref), NumSubtype::Float(right_ref)) => {\n                float_comparator(&(left_ref.value() as f64), &right_ref.value())\n            }\n            (NumSubtype::Float(left_ref), NumSubtype::Float(right_ref)) => {\n                float_comparator(&left_ref.value(), &right_ref.value())\n            }\n        };\n\n        if !result {\n            return false;\n        }\n\n        left = right;\n    }\n\n    true\n}\n\n#[arret_rfi_derive::rust_fun(\"(Num -> Float)\")]\npub fn stdlib_float(input: Gc<boxed::Num>) -> f64 {\n    match input.as_subtype() {\n        boxed::NumSubtype::Int(int_ref) => int_ref.value() as f64,\n        boxed::NumSubtype::Float(float_ref) => float_ref.value(),\n    }\n}\n\n#[arret_rfi_derive::rust_fun(\"(Num -> Int)\")]\npub fn stdlib_int(task: &mut Task, input: Gc<boxed::Num>) -> i64 {\n    match input.as_subtype() {\n        boxed::NumSubtype::Int(int_ref) => int_ref.value(),\n        boxed::NumSubtype::Float(float_ref) => {\n            let float_val = float_ref.value();\n\n            if float_val.is_nan() {\n                task.panic(format!(\n                    \"Float value `{}` is not a number; cannot convert to Int\",\n                    float_val\n                ));\n            } else if float_val.is_infinite() {\n                task.panic(format!(\n                    \"Float value `{}` is infinite; cannot convert to Int\",\n                    float_val\n                ));\n            }\n\n            float_val as i64\n        }\n    }\n}\n\n#[arret_rfi_derive::rust_fun(\"(Num & Num -> Bool)\")]\npub fn stdlib_num_lt(initial: Gc<boxed::Num>, rest: Gc<boxed::List<boxed::Num>>) -> bool {\n    compare_nums(initial, rest, i64::lt, f64::lt)\n}\n\n#[arret_rfi_derive::rust_fun(\"(Num & Num -> Bool)\")]\npub fn stdlib_num_le(initial: Gc<boxed::Num>, rest: Gc<boxed::List<boxed::Num>>) -> bool {\n    compare_nums(initial, rest, i64::le, f64::le)\n}\n\n#[arret_rfi_derive::rust_fun(\"(Num & Num -> Bool)\")]\npub fn stdlib_num_eq(initial: Gc<boxed::Num>, rest: Gc<boxed::List<boxed::Num>>) -> bool {\n    compare_nums(initial, rest, i64::eq, f64::eq)\n}\n\n#[arret_rfi_derive::rust_fun(\"(Num & Num -> Bool)\")]\npub fn stdlib_num_gt(initial: Gc<boxed::Num>, rest: Gc<boxed::List<boxed::Num>>) -> bool {\n    compare_nums(initial, rest, i64::gt, f64::gt)\n}\n\n#[arret_rfi_derive::rust_fun(\"(Num & Num -> Bool)\")]\npub fn stdlib_num_ge(initial: Gc<boxed::Num>, rest: Gc<boxed::List<boxed::Num>>) -> bool {\n    compare_nums(initial, rest, i64::ge, f64::ge)\n}\n"
  },
  {
    "path": "stdlib/rust/read.rs",
    "content": "use arret_syntax::parser::datum_from_str;\n\nuse arret_runtime::binding::*;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::task::Task;\n\nuse arret_runtime_syntax::reader;\n\n#[arret_rfi_derive::rust_fun(\"(Str -> Any)\")]\npub fn stdlib_read_str(task: &mut Task, edn_str: Gc<boxed::Str>) -> Gc<boxed::Any> {\n    let parsed_syntax = datum_from_str(None, edn_str.as_str()).unwrap();\n    reader::box_syntax_datum(task, &parsed_syntax)\n}\n"
  },
  {
    "path": "stdlib/rust/set.rs",
    "content": "use arret_runtime::binding::*;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::task::Task;\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} & T -> (Setof T))\")]\npub fn stdlib_set(\n    task: &mut Task,\n    values: Gc<boxed::List<boxed::Any>>,\n) -> Gc<boxed::Set<boxed::Any>> {\n    boxed::Set::new(task, values.iter())\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} (Setof T) T -> Bool)\")]\npub fn stdlib_set_contains_p(\n    task: &mut Task,\n    set: Gc<boxed::Set<boxed::Any>>,\n    needle: Gc<boxed::Any>,\n) -> bool {\n    set.contains(task.heap(), &needle)\n}\n\n#[arret_rfi_derive::rust_fun(\"((Setof Any) -> Int)\")]\npub fn stdlib_set_length(set: Gc<boxed::Set<boxed::Any>>) -> i64 {\n    set.len() as i64\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} (Setof T) -> (List & T))\")]\npub fn stdlib_set_to_list(\n    task: &mut Task,\n    set: Gc<boxed::Set<boxed::Any>>,\n) -> Gc<boxed::List<boxed::Any>> {\n    boxed::List::new(task, set.iter())\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} (Setof T) (Setof T) -> Bool)\")]\npub fn stdlib_subset_p(\n    task: &mut Task,\n    subset: Gc<boxed::Set<boxed::Any>>,\n    superset: Gc<boxed::Set<boxed::Any>>,\n) -> bool {\n    subset.is_subset(task.heap(), &superset)\n}\n"
  },
  {
    "path": "stdlib/rust/testing.rs",
    "content": "use arret_runtime::binding::*;\n\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::prelude::*;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::callback;\nuse arret_runtime::task::Task;\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} T -> T)\")]\npub fn stdlib_black_box(value: Gc<boxed::Any>) -> Gc<boxed::Any> {\n    value\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} T ->! T)\")]\npub fn stdlib_black_box_impure(value: Gc<boxed::Any>) -> Gc<boxed::Any> {\n    value\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{[->_ ->!] T} (->_ T) ->_ (List Int T))\")]\npub fn stdlib_heap_alloc_count(\n    task: &mut Task,\n    block: callback::Callback<extern \"C\" fn(&mut Task, boxed::Captures) -> Gc<boxed::Any>>,\n) -> Gc<boxed::List<boxed::Any>> {\n    let before_len = task.heap().len();\n    let ret = block.apply(task);\n    let after_len = task.heap().len();\n\n    let alloc_count = boxed::Int::new(task, (after_len - before_len) as i64);\n    boxed::List::new(task, [alloc_count.as_any_ref(), ret].iter().cloned())\n}\n\n// TODO: This should return a `Set` once they're better supported\n#[arret_rfi_derive::rust_fun(\"((... ->! Any) -> (List & Sym))\")]\npub fn stdlib_fn_op_categories(_value: Gc<boxed::FunThunk>) -> Gc<boxed::List<boxed::Sym>> {\n    panic!(\"cannot call `(fn-op-categories)` at runtime\")\n}\n"
  },
  {
    "path": "stdlib/rust/vector.rs",
    "content": "use arret_runtime::binding::*;\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::task::Task;\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} & T -> (Vectorof T))\")]\npub fn stdlib_vector(\n    task: &mut Task,\n    values: Gc<boxed::List<boxed::Any>>,\n) -> Gc<boxed::Vector<boxed::Any>> {\n    boxed::Vector::new(task, values.iter())\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} (Vectorof T) Int -> T)\")]\npub fn stdlib_vector_ref(\n    task: &mut Task,\n    vector: Gc<boxed::Vector<boxed::Any>>,\n    index: i64,\n) -> Gc<boxed::Any> {\n    let usize_index = if index < 0 {\n        task.panic(format!(\"index {} is negative\", index));\n        unreachable!(\"returned from panic\")\n    } else {\n        index as usize\n    };\n\n    match vector.get(usize_index) {\n        Some(value) => value,\n        None => {\n            task.panic(format!(\n                \"index {} out of bounds for vector of length {}\",\n                usize_index,\n                vector.len()\n            ));\n            unreachable!(\"returned from panic\")\n        }\n    }\n}\n\n#[arret_rfi_derive::rust_fun(\"((Vectorof Any) -> Int)\")]\npub fn stdlib_vector_length(vector: Gc<boxed::Vector<boxed::Any>>) -> i64 {\n    vector.len() as i64\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} (Vectorof T) -> (List & T))\")]\npub fn stdlib_vector_to_list(\n    task: &mut Task,\n    vector: Gc<boxed::Vector<boxed::Any>>,\n) -> Gc<boxed::List<boxed::Any>> {\n    boxed::List::new(task, vector.iter())\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} (Vectorof T) Int T -> (Vectorof T))\")]\npub fn stdlib_vector_assoc(\n    task: &mut Task,\n    vector: Gc<boxed::Vector<boxed::Any>>,\n    index: i64,\n    value: Gc<boxed::Any>,\n) -> Gc<boxed::Vector<boxed::Any>> {\n    let usize_index = if index < 0 {\n        task.panic(format!(\"index {} is negative\", index));\n        unreachable!(\"returned from panic\")\n    } else {\n        index as usize\n    };\n\n    if usize_index >= vector.len() {\n        task.panic(format!(\n            \"index {} out of bounds for vector of length {}\",\n            usize_index,\n            vector.len()\n        ));\n        unreachable!(\"returned from panic\")\n    }\n\n    vector.assoc(task, usize_index, value)\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} & (Vectorof T) -> (Vectorof T))\")]\npub fn stdlib_vector_append(\n    task: &mut Task,\n    vectors: Gc<boxed::List<boxed::Vector<boxed::Any>>>,\n) -> Gc<boxed::Vector<boxed::Any>> {\n    let mut vectors_iter = vectors.iter();\n\n    let first_vector = if let Some(first_vector) = vectors_iter.next() {\n        first_vector\n    } else {\n        return boxed::Vector::new(task, std::iter::empty());\n    };\n\n    vectors_iter.fold(first_vector, |v1, v2| v1.append(task, v2))\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} (Vectorof T) & T -> (Vectorof T))\")]\npub fn stdlib_vector_extend(\n    task: &mut Task,\n    vector: Gc<boxed::Vector<boxed::Any>>,\n    new_values: Gc<boxed::List<boxed::Any>>,\n) -> Gc<boxed::Vector<boxed::Any>> {\n    vector.extend(task, new_values.iter())\n}\n\n#[arret_rfi_derive::rust_fun(\"(All #{T} Int (Vectorof T) -> (Vectorof T))\")]\npub fn stdlib_vector_take(\n    task: &mut Task,\n    count: i64,\n    input: Gc<boxed::Vector<boxed::Any>>,\n) -> Gc<boxed::Vector<boxed::Any>> {\n    let usize_count = if count < 0 { 0 } else { count as usize };\n    input.take(task, usize_count)\n}\n"
  },
  {
    "path": "stdlib/rust/write.rs",
    "content": "use std::io;\nuse std::io::prelude::*;\n\nuse arret_runtime::binding::*;\n\nuse arret_runtime::boxed;\nuse arret_runtime::boxed::refs::Gc;\nuse arret_runtime::task::Task;\n\nfn pretty_print_common(\n    task: &mut Task,\n    values: Gc<boxed::List<boxed::Any>>,\n    output: &mut dyn Write,\n) {\n    for value in values.iter() {\n        arret_runtime_syntax::writer::pretty_print_boxed(output, task, value);\n    }\n}\n\nfn write_boxed_common(\n    task: &mut Task,\n    values: Gc<boxed::List<boxed::Any>>,\n    output: &mut dyn Write,\n) {\n    let mut is_first = true;\n    for value in values.iter() {\n        if !is_first {\n            output.write_all(&[b' ']).unwrap();\n        }\n\n        arret_runtime_syntax::writer::write_boxed(output, task, value).unwrap();\n        is_first = false;\n    }\n}\n\n#[arret_rfi_derive::rust_fun(\"(& Any ->! ())\")]\npub fn stdlib_print(task: &mut Task, values: Gc<boxed::List<boxed::Any>>) {\n    let stdout = io::stdout();\n    let mut output = stdout.lock();\n\n    pretty_print_common(task, values, &mut output);\n}\n\n#[arret_rfi_derive::rust_fun(\"(& Any ->! ())\")]\npub fn stdlib_println(task: &mut Task, values: Gc<boxed::List<boxed::Any>>) {\n    let stdout = io::stdout();\n    let mut output = stdout.lock();\n\n    pretty_print_common(task, values, &mut output);\n    output.write_all(&[b'\\n']).unwrap();\n}\n\n#[arret_rfi_derive::rust_fun(\"(& Any ->! ())\")]\npub fn stdlib_write(task: &mut Task, values: Gc<boxed::List<boxed::Any>>) {\n    let stdout = io::stdout();\n    let mut output = stdout.lock();\n\n    write_boxed_common(task, values, &mut output);\n}\n\n#[arret_rfi_derive::rust_fun(\"(& Any ->! ())\")]\npub fn stdlib_writeln(task: &mut Task, values: Gc<boxed::List<boxed::Any>>) {\n    let stdout = io::stdout();\n    let mut output = stdout.lock();\n\n    write_boxed_common(task, values, &mut output);\n    output.write_all(&[b'\\n']).unwrap();\n}\n\n#[arret_rfi_derive::rust_fun(\"(& Any -> Str)\")]\npub fn stdlib_print_str(task: &mut Task, values: Gc<boxed::List<boxed::Any>>) -> Gc<boxed::Str> {\n    let mut output: Vec<u8> = vec![];\n    pretty_print_common(task, values, &mut output);\n\n    boxed::Str::new(\n        task,\n        std::str::from_utf8(&output).expect(\"wrote invalid UTF-8\"),\n    )\n}\n\n#[arret_rfi_derive::rust_fun(\"(& Any -> Str)\")]\npub fn stdlib_write_str(task: &mut Task, values: Gc<boxed::List<boxed::Any>>) -> Gc<boxed::Str> {\n    let mut output: Vec<u8> = vec![];\n    write_boxed_common(task, values, &mut output);\n\n    boxed::Str::new(\n        task,\n        std::str::from_utf8(&output).expect(\"wrote invalid UTF-8\"),\n    )\n}\n"
  },
  {
    "path": "syntax/Cargo.toml",
    "content": "[package]\nname = \"arret-syntax\"\nversion = \"0.1.0\"\nedition = \"2018\"\nauthors = [\"Ryan Cumming <etaoins@gmail.com>\"]\n\n[lib]\npath = \"lib.rs\"\ncrate-type = [\"lib\"]"
  },
  {
    "path": "syntax/anon_fun.rs",
    "content": "use crate::datum::Datum;\nuse crate::error::{Error, ErrorKind, Result};\nuse crate::span::Span;\n\nstruct FoundArity {\n    fixed_args: u8,\n    has_rest: bool,\n}\n\n/// Visits all arg literals replacing `%` with `%1` and tracking our arity\nfn visit_arg_literals(found_arity: &mut FoundArity, datum: Datum) -> Result<Datum> {\n    match datum {\n        Datum::Sym(span, name) => {\n            if let Some(arg_literal) = name.strip_prefix('%') {\n                match arg_literal {\n                    \"\" => {\n                        // We need to rewrite this to %1 in case it's also referred to by that name\n                        found_arity.fixed_args = std::cmp::max(found_arity.fixed_args, 1);\n                        Ok(Datum::Sym(span, \"%1\".into()))\n                    }\n                    \"&\" => {\n                        found_arity.has_rest = true;\n                        Ok(Datum::Sym(span, name))\n                    }\n                    other => other\n                        .parse::<u8>()\n                        .map(|parsed_number| {\n                            found_arity.fixed_args =\n                                std::cmp::max(found_arity.fixed_args, parsed_number);\n                            Datum::Sym(span, name)\n                        })\n                        .map_err(|_| Error::new(span, ErrorKind::InvalidArgLiteral)),\n                }\n            } else {\n                Ok(Datum::Sym(span, name))\n            }\n        }\n        Datum::List(span, content) => {\n            let replaced_content = content\n                .into_vec()\n                .into_iter()\n                .map(|body_datum| visit_arg_literals(found_arity, body_datum))\n                .collect::<Result<Vec<Datum>>>()?;\n\n            Ok(Datum::List(span, replaced_content.into()))\n        }\n        other => Ok(other),\n    }\n}\n\n/// Converts body data from a `#()` reader macro in to an anonymous function\npub fn convert_anon_fun(outer_span: Span, body_data: impl Iterator<Item = Datum>) -> Result<Datum> {\n    use std::iter;\n\n    let mut found_arity = FoundArity {\n        fixed_args: 0,\n        has_rest: false,\n    };\n\n    let replaced_body = body_data\n        .map(|body_datum| visit_arg_literals(&mut found_arity, body_datum))\n        .collect::<Result<Vec<Datum>>>()?;\n\n    let mut param_list: Vec<Datum> = (0..found_arity.fixed_args)\n        .map(|param_index| {\n            let param_ordinal = param_index + 1;\n            Datum::Sym(outer_span, format!(\"%{}\", param_ordinal).into())\n        })\n        .collect();\n\n    if found_arity.has_rest {\n        param_list.extend(\n            iter::once(Datum::Sym(outer_span, \"&\".into()))\n                .chain(iter::once(Datum::Sym(outer_span, \"%&\".into()))),\n        );\n    }\n\n    let expanded_fun = vec![\n        Datum::Sym(outer_span, \"fn\".into()),\n        Datum::List(outer_span, param_list.into()),\n        Datum::List(outer_span, replaced_body.into()),\n    ];\n\n    Ok(Datum::List(outer_span, expanded_fun.into()))\n}\n\n/////////\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::parser::data_from_str;\n    use crate::span::t2s;\n\n    #[test]\n    fn empty_fun() {\n        let j = \"\";\n        let t = \"\";\n\n        let body_data = data_from_str(None, j).unwrap();\n        let outer_span = t2s(t);\n\n        let expected = Datum::List(\n            outer_span,\n            Box::new([\n                Datum::Sym(outer_span, \"fn\".into()),\n                Datum::List(outer_span, Box::new([])),\n                Datum::List(outer_span, Box::new([])),\n            ]),\n        );\n\n        assert_eq!(\n            expected,\n            convert_anon_fun(outer_span, body_data.into_iter()).unwrap()\n        );\n    }\n\n    #[test]\n    fn one_arg_fun() {\n        let j = \"%\";\n        let t = \"^\";\n\n        let body_data = data_from_str(None, j).unwrap();\n        let outer_span = t2s(t);\n\n        let expected = Datum::List(\n            outer_span,\n            Box::new([\n                Datum::Sym(outer_span, \"fn\".into()),\n                Datum::List(outer_span, Box::new([Datum::Sym(outer_span, \"%1\".into())])),\n                Datum::List(\n                    outer_span,\n                    Box::new([\n                        // This is converted to %1\n                        Datum::Sym(t2s(t), \"%1\".into()),\n                    ]),\n                ),\n            ]),\n        );\n\n        assert_eq!(\n            expected,\n            convert_anon_fun(outer_span, body_data.into_iter()).unwrap()\n        );\n    }\n\n    #[test]\n    fn two_arg_fun() {\n        let j = \"%1 %2\";\n        let t = \"^^^^^\";\n        let u = \"^^   \";\n        let v = \"   ^^\";\n\n        let body_data = data_from_str(None, j).unwrap();\n        let outer_span = t2s(t);\n\n        let expected = Datum::List(\n            outer_span,\n            Box::new([\n                Datum::Sym(outer_span, \"fn\".into()),\n                Datum::List(\n                    outer_span,\n                    Box::new([\n                        Datum::Sym(outer_span, \"%1\".into()),\n                        Datum::Sym(outer_span, \"%2\".into()),\n                    ]),\n                ),\n                Datum::List(\n                    outer_span,\n                    Box::new([\n                        Datum::Sym(t2s(u), \"%1\".into()),\n                        Datum::Sym(t2s(v), \"%2\".into()),\n                    ]),\n                ),\n            ]),\n        );\n\n        assert_eq!(\n            expected,\n            convert_anon_fun(outer_span, body_data.into_iter()).unwrap()\n        );\n    }\n\n    #[test]\n    fn rest_fun() {\n        let j = \"%1 %&\";\n        let t = \"^^^^^\";\n        let u = \"^^   \";\n        let v = \"   ^^\";\n\n        let body_data = data_from_str(None, j).unwrap();\n        let outer_span = t2s(t);\n\n        let expected = Datum::List(\n            outer_span,\n            Box::new([\n                Datum::Sym(outer_span, \"fn\".into()),\n                Datum::List(\n                    outer_span,\n                    Box::new([\n                        Datum::Sym(outer_span, \"%1\".into()),\n                        Datum::Sym(outer_span, \"&\".into()),\n                        Datum::Sym(outer_span, \"%&\".into()),\n                    ]),\n                ),\n                Datum::List(\n                    outer_span,\n                    Box::new([\n                        Datum::Sym(t2s(u), \"%1\".into()),\n                        Datum::Sym(t2s(v), \"%&\".into()),\n                    ]),\n                ),\n            ]),\n        );\n\n        assert_eq!(\n            expected,\n            convert_anon_fun(outer_span, body_data.into_iter()).unwrap()\n        );\n    }\n}\n"
  },
  {
    "path": "syntax/datum.rs",
    "content": "use std::sync::Arc;\n\nuse crate::span::Span;\n\npub type DataStr = Arc<str>;\n\n#[derive(PartialEq, Debug, Clone)]\npub enum Datum {\n    Bool(Span, bool),\n    Char(Span, char),\n    Int(Span, i64),\n    Float(Span, f64),\n    List(Span, Box<[Datum]>),\n    Str(Span, DataStr),\n    Sym(Span, DataStr),\n    Vector(Span, Box<[Datum]>),\n    Map(Span, Box<[(Datum, Datum)]>),\n    Set(Span, Box<[Datum]>),\n}\n\nimpl Datum {\n    pub fn span(&self) -> Span {\n        match self {\n            Datum::Bool(span, _)\n            | Datum::Char(span, _)\n            | Datum::Int(span, _)\n            | Datum::Float(span, _)\n            | Datum::List(span, _)\n            | Datum::Str(span, _)\n            | Datum::Sym(span, _)\n            | Datum::Vector(span, _)\n            | Datum::Map(span, _)\n            | Datum::Set(span, _) => *span,\n        }\n    }\n\n    pub fn description(&self) -> &'static str {\n        match self {\n            Datum::Bool(_, true) => \"boolean true\",\n            Datum::Bool(_, false) => \"boolean false\",\n            Datum::Char(_, _) => \"character\",\n            Datum::Int(_, _) => \"integer\",\n            Datum::Float(_, _) => \"floating point number\",\n            Datum::Str(_, _) => \"string\",\n            Datum::Sym(_, name) => {\n                if name.starts_with(':') {\n                    \"keyword\"\n                } else {\n                    \"symbol\"\n                }\n            }\n            Datum::List(_, vs) if vs.is_empty() => \"empty list\",\n            Datum::List(_, _) => \"list\",\n\n            Datum::Vector(_, vs) if vs.is_empty() => \"empty vector\",\n            Datum::Vector(_, _) => \"vector\",\n\n            Datum::Set(_, vs) if vs.is_empty() => \"empty set\",\n            Datum::Set(_, _) => \"set\",\n\n            Datum::Map(_, vs) if vs.is_empty() => \"empty map\",\n            Datum::Map(_, _) => \"map\",\n        }\n    }\n}\n"
  },
  {
    "path": "syntax/error.rs",
    "content": "use std::error;\nuse std::fmt;\nuse std::fmt::Display;\nuse std::result;\n\nuse crate::span::Span;\n\n/// (Spanned)[`Span`] syntax error\n#[derive(Debug, Clone, PartialEq)]\npub struct Error {\n    span: Span,\n    pub(crate) kind: ErrorKind,\n}\n\nimpl Error {\n    pub fn new(span: Span, kind: ErrorKind) -> Error {\n        Error { span, kind }\n    }\n\n    pub fn kind(&self) -> &ErrorKind {\n        &self.kind\n    }\n\n    pub fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl error::Error for Error {}\n\nimpl Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(&self.kind().message())\n    }\n}\n\n/// Syntax error without (span)[`Span`] information\n#[derive(Debug, Clone, PartialEq)]\npub enum ErrorKind {\n    Eof(WithinContext),\n    UnsupportedDispatch,\n    UnsupportedChar,\n    InvalidCodePoint,\n    UnsupportedStringEscape,\n    IntegerOverflow,\n    InvalidFloat,\n    UnexpectedChar(char, WithinContext),\n    UnevenMap,\n    InvalidArgLiteral,\n}\n\nimpl ErrorKind {\n    /// Returns a string describing the error\n    pub fn message(&self) -> String {\n        match self {\n            ErrorKind::Eof(ref within) => format!(\n                \"unexpected end of file while parsing {}\",\n                within.description()\n            ),\n            ErrorKind::UnsupportedDispatch => \"unsupported dispatch\".to_owned(),\n            ErrorKind::UnsupportedChar => \"unsupported character\".to_owned(),\n            ErrorKind::InvalidCodePoint => \"invalid code point\".to_owned(),\n            ErrorKind::UnsupportedStringEscape => \"unsupported string escape\".to_owned(),\n            ErrorKind::IntegerOverflow => \"integer literal does not fit in i64\".to_owned(),\n            ErrorKind::InvalidFloat => \"unable to parse float\".to_owned(),\n            ErrorKind::UnexpectedChar(c, within) => {\n                format!(\"unexpected `{}` while parsing {}\", c, within.description())\n            }\n            ErrorKind::UnevenMap => \"map literal must have an even number of values\".to_owned(),\n            ErrorKind::InvalidArgLiteral => {\n                \"arg literal must be `%`, `%{integer}` or `%&`\".to_owned()\n            }\n        }\n    }\n\n    /// Returns the context this error was encountered in\n    ///\n    /// This is used to disambiguate errors that can occur in multiple contexts. Other error types\n    /// (e.g. `InvalidFloat`) require no additional context.\n    pub fn within_context(&self) -> Option<WithinContext> {\n        match self {\n            ErrorKind::Eof(within) | ErrorKind::UnexpectedChar(_, within) => Some(*within),\n            _ => None,\n        }\n    }\n}\n\npub type Result<T> = result::Result<T, Error>;\n\n/// Describes the content an error occurred within, with optional starting span\n#[derive(Debug, PartialEq, Clone, Copy)]\npub enum WithinContext {\n    List(Span),\n    Vector(Span),\n    Set(Span),\n    Map(Span),\n    String(Span),\n    Identifier,\n    Datum,\n    Dispatch,\n    QuoteEscape,\n    CodePoint,\n}\n\nimpl WithinContext {\n    /// Returns a description of the content that was being parsed\n    pub fn description(&self) -> &'static str {\n        match self {\n            WithinContext::List(_) => \"list\",\n            WithinContext::Vector(_) => \"vector\",\n            WithinContext::Set(_) => \"set\",\n            WithinContext::Map(_) => \"map\",\n            WithinContext::String(_) => \"string literal\",\n            WithinContext::Identifier => \"identifier\",\n            WithinContext::Datum => \"datum\",\n            WithinContext::Dispatch => \"dispatch\",\n            WithinContext::QuoteEscape => \"quote escape\",\n            WithinContext::CodePoint => \"code point\",\n        }\n    }\n\n    /// Returns the normally expected in this context\n    pub fn expected_next(&self) -> Option<ExpectedNext> {\n        match self {\n            WithinContext::List(_) => Some(ExpectedNext::List),\n            WithinContext::Vector(_) => Some(ExpectedNext::Vector),\n            WithinContext::Set(_) => Some(ExpectedNext::Set),\n            WithinContext::Map(_) => Some(ExpectedNext::Map),\n            WithinContext::String(_) => Some(ExpectedNext::String),\n            _ => None,\n        }\n    }\n\n    /// Returns the character opening the sequence or string\n    pub fn open_char_span(&self) -> Option<Span> {\n        match self {\n            WithinContext::List(span)\n            | WithinContext::Vector(span)\n            | WithinContext::Set(span)\n            | WithinContext::Map(span)\n            | WithinContext::String(span) => Some(*span),\n            _ => None,\n        }\n    }\n}\n\n/// Describes the content normally expected within the content\n#[derive(Debug, PartialEq, Clone, Copy)]\npub enum ExpectedNext {\n    List,\n    Vector,\n    Set,\n    Map,\n    String,\n}\n\nimpl ExpectedNext {\n    /// Returns the character that would terminate this sequence or string\n    pub fn close_char(self) -> char {\n        match self {\n            ExpectedNext::List => ')',\n            ExpectedNext::Vector => ']',\n            ExpectedNext::Set => '}',\n            ExpectedNext::Map => '}',\n            ExpectedNext::String => '\"',\n        }\n    }\n\n    pub fn description(self) -> String {\n        match self {\n            ExpectedNext::String => \"expected `\\\"`\".to_owned(),\n            other => format!(\"expected datum or `{}`\", other.close_char()),\n        }\n    }\n}\n"
  },
  {
    "path": "syntax/lib.rs",
    "content": "#![warn(clippy::all)]\n#![warn(rust_2018_idioms)]\n\nmod anon_fun;\npub mod datum;\npub mod error;\npub mod parser;\npub mod span;\n"
  },
  {
    "path": "syntax/parser.rs",
    "content": "use crate::datum::Datum;\nuse crate::error::{Error, ErrorKind, Result, WithinContext};\nuse crate::span::{ByteIndex, FileId, Span};\n\npub fn data_from_str_with_span_offset(\n    file_id: Option<FileId>,\n    s: &str,\n    span_offset: ByteIndex,\n) -> Result<Vec<Datum>> {\n    Parser::from_str(file_id, s, span_offset).parse_data()\n}\n\npub fn data_from_str(file_id: Option<FileId>, s: &str) -> Result<Vec<Datum>> {\n    data_from_str_with_span_offset(file_id, s, 0)\n}\n\npub fn datum_from_str_with_span_offset(\n    file_id: Option<FileId>,\n    s: &str,\n    span_offset: ByteIndex,\n) -> Result<Datum> {\n    Parser::from_str(file_id, s, span_offset).parse_datum()\n}\n\npub fn datum_from_str(file_id: Option<FileId>, s: &str) -> Result<Datum> {\n    datum_from_str_with_span_offset(file_id, s, 0)\n}\n\nfn is_whitespace(c: char) -> bool {\n    matches!(c, ',' | ' ' | '\\n' | '\\t' | '\\r')\n}\n\npub fn is_identifier_char(c: char) -> bool {\n    matches!(c,\n        'A'..='Z' | 'a'..='z' | '0'..='9' |\n        // Punctuation allowed at beginning of an identifier\n        '.' | '*' | '+' | '!' | '-' | '_' | '?' | '$' | '%' | '&' | '=' | '<' | '>' | ':' |\n        // Punctuation allowed anywhere\n        '#' |\n        // We don't support namespacing so we treat this as a normal char\n        '/'\n    )\n}\n\npub struct Parser<'input> {\n    file_id: Option<FileId>,\n    input: &'input str,\n    consumed_bytes: ByteIndex,\n}\n\nimpl<'input> Parser<'input> {\n    fn from_str(file_id: Option<FileId>, input: &'input str, span_offset: ByteIndex) -> Self {\n        Parser {\n            file_id,\n            input,\n            consumed_bytes: span_offset,\n        }\n    }\n\n    fn eof_err(&self, within: WithinContext) -> Error {\n        let eof_pos = self.consumed_bytes + (self.input.len() as ByteIndex);\n\n        Error::new(\n            Span::new(self.file_id, eof_pos, eof_pos),\n            ErrorKind::Eof(within),\n        )\n    }\n\n    fn peek_char(&mut self, within: WithinContext) -> Result<char> {\n        self.input\n            .chars()\n            .next()\n            .ok_or_else(|| self.eof_err(within))\n    }\n\n    fn peek_nth_char(&mut self, i: usize, within: WithinContext) -> Result<char> {\n        self.input\n            .chars()\n            .nth(i)\n            .ok_or_else(|| self.eof_err(within))\n    }\n\n    fn eat_bytes(&mut self, count: usize) {\n        self.input = &self.input[count..];\n        self.consumed_bytes += count as ByteIndex;\n    }\n\n    fn consume_char(&mut self, within: WithinContext) -> Result<char> {\n        let mut char_indices = self.input.char_indices();\n\n        match char_indices.next() {\n            Some((_, c)) => {\n                let next_index = char_indices\n                    .next()\n                    .map(|t| t.0)\n                    .unwrap_or_else(|| self.input.len());\n                self.eat_bytes(next_index);\n\n                Ok(c)\n            }\n\n            None => Err(self.eof_err(within)),\n        }\n    }\n\n    fn skip_until_non_whitespace(&mut self, within: WithinContext) -> Result<char> {\n        loop {\n            self.consume_while(is_whitespace);\n\n            match self.peek_char(within)? {\n                ';' => {\n                    self.consume_until(|c| c == '\\n');\n                }\n                '#' => {\n                    match self.peek_nth_char(1, within) {\n                        Ok('_') => {\n                            // Discard the #_ and the following datum\n                            self.eat_bytes(2);\n                            self.parse_datum()?;\n                        }\n                        _ => {\n                            break Ok('#');\n                        }\n                    }\n                }\n                other => {\n                    break Ok(other);\n                }\n            }\n        }\n    }\n\n    fn consume_until<T>(&mut self, predicate: T) -> (Span, &str)\n    where\n        T: FnMut(char) -> bool,\n    {\n        let start = self.consumed_bytes;\n        let last_index = self\n            .input\n            .find(predicate)\n            .unwrap_or_else(|| self.input.len());\n        let (consumed, remaining_input) = self.input.split_at(last_index);\n\n        self.input = remaining_input;\n        self.consumed_bytes += last_index as ByteIndex;\n\n        (\n            Span::new(self.file_id, start, self.consumed_bytes),\n            consumed,\n        )\n    }\n\n    fn consume_while<T>(&mut self, mut predicate: T) -> (Span, &str)\n    where\n        T: FnMut(char) -> bool,\n    {\n        self.consume_until(|c| !predicate(c))\n    }\n\n    fn capture_span<F, R>(&mut self, block: F) -> (Span, R)\n    where\n        F: FnOnce(&mut Parser<'_>) -> R,\n    {\n        let start = self.consumed_bytes;\n        let result = block(self);\n        let end = self.consumed_bytes;\n\n        (Span::new(self.file_id, start, end), result)\n    }\n\n    fn parse_num(&mut self) -> Result<Datum> {\n        enum State {\n            Sign,\n            Whole,\n            Fractional,\n        }\n\n        let mut state: State = State::Sign;\n\n        let (span, digits) = self.consume_while(|c| match state {\n            State::Sign => match c {\n                '+' | '-' | '0'..='9' => {\n                    state = State::Whole;\n                    true\n                }\n                _ => false,\n            },\n            State::Whole => match c {\n                '.' => {\n                    state = State::Fractional;\n                    true\n                }\n                '0'..='9' => true,\n                _ => false,\n            },\n            State::Fractional => matches!(c, '0'..='9'),\n        });\n\n        match state {\n            State::Sign => Err(Error::new(span, ErrorKind::InvalidFloat)),\n\n            State::Whole => digits\n                .parse::<i64>()\n                .map_err(|_| Error::new(span, ErrorKind::IntegerOverflow))\n                .map(|i| Datum::Int(span, i)),\n\n            State::Fractional => digits\n                .parse::<f64>()\n                .map_err(|_| Error::new(span, ErrorKind::InvalidFloat))\n                .map(|f| Datum::Float(span, f)),\n        }\n    }\n\n    fn parse_symbolic_float(&mut self) -> Result<Datum> {\n        let (span, symbolic_name) = self.consume_while(is_identifier_char);\n\n        let float_value = match symbolic_name {\n            \"#NaN\" => std::f64::NAN,\n            \"#Inf\" => std::f64::INFINITY,\n            \"#-Inf\" => std::f64::NEG_INFINITY,\n            _ => {\n                return Err(Error::new(span, ErrorKind::UnsupportedDispatch));\n            }\n        };\n\n        Ok(Datum::Float(\n            // Cover the initial #\n            Span::new(self.file_id, span.start() - 1, span.end()),\n            float_value,\n        ))\n    }\n\n    fn parse_signed_num_or_symbol(&mut self) -> Result<Datum> {\n        match self.peek_nth_char(1, WithinContext::Identifier) {\n            Ok(digit) if digit.is_ascii_digit() => self.parse_num(),\n            Ok(_)\n            | Err(Error {\n                kind: ErrorKind::Eof(_),\n                ..\n            }) => self.parse_identifier(WithinContext::Identifier),\n            Err(other) => Err(other),\n        }\n    }\n\n    fn parse_char(&mut self) -> Result<Datum> {\n        let (span, c) = self.capture_span(|s| {\n            // Consume the \\\n            s.eat_bytes(1);\n\n            // Consume the character name\n            let (span, char_name) =\n                s.consume_until(|c| c == ')' || c == ']' || c == '}' || is_whitespace(c));\n\n            let mut char_name_chars = char_name.chars();\n            if let Some(first_char) = char_name_chars.next() {\n                if char_name_chars.next().is_none() {\n                    // There is only a single character; return it\n                    return Ok(first_char);\n                }\n\n                if first_char == 'u' {\n                    // This is a hex code point\n                    let hex_string = &char_name[1..];\n                    let code_point = u32::from_str_radix(hex_string, 16)\n                        .map_err(|_| Error::new(span, ErrorKind::UnsupportedChar))?;\n\n                    return std::char::from_u32(code_point)\n                        .ok_or_else(|| Error::new(span, ErrorKind::InvalidCodePoint));\n                }\n            }\n\n            match char_name {\n                \"newline\" => Ok('\\n'),\n                \"return\" => Ok('\\r'),\n                \"space\" => Ok(' '),\n                \"tab\" => Ok('\\t'),\n                _ => Err(Error::new(span, ErrorKind::UnsupportedChar)),\n            }\n        });\n\n        c.map(|c| Datum::Char(span, c))\n    }\n\n    fn parse_dispatch(&mut self) -> Result<Datum> {\n        // Consume the #\n        // This means we need to adjust our spans below to cover it for reporting\n        self.eat_bytes(1);\n\n        match self.peek_char(WithinContext::Dispatch)? {\n            '{' => self.parse_set(),\n            '(' => self.parse_anon_fun(),\n            '#' => self.parse_symbolic_float(),\n            _ => {\n                let (span, _) = self.capture_span(|s| s.consume_char(WithinContext::Dispatch));\n\n                Err(Error::new(\n                    Span::new(self.file_id, span.start() - 1, span.end()),\n                    ErrorKind::UnsupportedDispatch,\n                ))\n            }\n        }\n    }\n\n    fn parse_seq<F>(&mut self, terminator: char, make_ec: F) -> Result<Vec<Datum>>\n    where\n        F: FnOnce(Span) -> WithinContext,\n    {\n        // Consume the opening bracket\n        let (open_bracket_span, _) = self.capture_span(|s| {\n            s.eat_bytes(1);\n        });\n        let ec = make_ec(open_bracket_span);\n\n        let mut content = Vec::new();\n\n        // Keep eating data until we hit the terminator\n        loop {\n            let next_char = self.skip_until_non_whitespace(ec)?;\n            if next_char == terminator {\n                // End of the sequence\n                self.eat_bytes(1);\n                break Ok(content);\n            } else {\n                content.push(self.parse_datum_starting_with(next_char, ec)?);\n            }\n        }\n    }\n\n    fn parse_list(&mut self) -> Result<Datum> {\n        let (outer_span, contents) = self.capture_span(|s| s.parse_seq(')', WithinContext::List));\n\n        contents.map(|contents| Datum::List(outer_span, contents.into()))\n    }\n\n    fn parse_vector(&mut self) -> Result<Datum> {\n        let (outer_span, contents) = self.capture_span(|s| s.parse_seq(']', WithinContext::Vector));\n\n        contents.map(|contents| Datum::Vector(outer_span, contents.into()))\n    }\n\n    fn parse_map(&mut self) -> Result<Datum> {\n        // First get the contents without splitting pairwise\n        let (span, unpaired_contents) = self.capture_span(|s| s.parse_seq('}', WithinContext::Map));\n\n        let unpaired_contents = unpaired_contents?;\n        if unpaired_contents.len() % 2 == 1 {\n            return Err(Error::new(span, ErrorKind::UnevenMap));\n        }\n\n        let mut paired_contents = Vec::with_capacity(unpaired_contents.len() / 2);\n        let mut unpaired_contents_iter = unpaired_contents.into_iter();\n        while let Some(key) = unpaired_contents_iter.next() {\n            let value = unpaired_contents_iter.next().unwrap();\n            paired_contents.push((key, value));\n        }\n\n        Ok(Datum::Map(span, paired_contents.into_boxed_slice()))\n    }\n\n    fn parse_set(&mut self) -> Result<Datum> {\n        let (outer_span, contents) = self.capture_span(|s| s.parse_seq('}', WithinContext::Set));\n\n        contents.map(|contents| {\n            Datum::Set(\n                // Cover the # in our span\n                Span::new(self.file_id, outer_span.start() - 1, outer_span.end()),\n                contents.into(),\n            )\n        })\n    }\n\n    fn parse_anon_fun(&mut self) -> Result<Datum> {\n        use crate::anon_fun::convert_anon_fun;\n\n        let (outer_span, body_contents) =\n            self.capture_span(|s| s.parse_seq(')', WithinContext::List));\n\n        let body_contents = body_contents?;\n\n        convert_anon_fun(\n            // Cover the # in our span\n            Span::new(self.file_id, outer_span.start() - 1, outer_span.end()),\n            body_contents.into_iter(),\n        )\n    }\n\n    fn parse_quote_escape(&mut self) -> Result<char> {\n        let escape_start = self.consumed_bytes as ByteIndex;\n\n        match self.consume_char(WithinContext::QuoteEscape)? {\n            't' => Ok('\\t'),\n            'r' => Ok('\\r'),\n            'n' => Ok('\\n'),\n            '\\\\' => Ok('\\\\'),\n            '\"' => Ok('\"'),\n            'x' => {\n                let (span, hex_string) = self.consume_until(|c| c == ';');\n                let code_point = u32::from_str_radix(hex_string, 16);\n\n                let code_point =\n                    code_point.map_err(|_| Error::new(span, ErrorKind::UnsupportedChar))?;\n\n                if self.consume_char(WithinContext::CodePoint)? != ';' {\n                    return Err(Error::new(span, ErrorKind::UnsupportedChar));\n                }\n\n                std::char::from_u32(code_point)\n                    .ok_or_else(|| Error::new(span, ErrorKind::InvalidCodePoint))\n            }\n            _ => Err(Error::new(\n                Span::new(self.file_id, escape_start, self.consumed_bytes),\n                ErrorKind::UnsupportedStringEscape,\n            )),\n        }\n    }\n\n    fn parse_string(&mut self) -> Result<Datum> {\n        let (span, contents) = self.capture_span(|s| {\n            let (open_quote_span, _) = s.capture_span(|s| {\n                // Eat the opening quote\n                s.eat_bytes(1);\n            });\n\n            let mut contents = String::new();\n            loop {\n                let (_, unescaped_contents) = s.consume_until(|c| c == '\"' || c == '\\\\');\n                contents.push_str(unescaped_contents);\n\n                match s.consume_char(WithinContext::String(open_quote_span))? {\n                    '\"' => {\n                        return Ok(contents);\n                    }\n                    '\\\\' => contents.push(s.parse_quote_escape()?),\n                    _ => {\n                        unreachable!(\"Shouldn't be here\");\n                    }\n                }\n            }\n        });\n        contents.map(|contents| Datum::Str(span, contents.into()))\n    }\n\n    fn parse_identifier(&mut self, within: WithinContext) -> Result<Datum> {\n        let (span, content) = self.consume_while(is_identifier_char);\n\n        if content.is_empty() {\n            let (span, next_char) = self.capture_span(|s| s.consume_char(within));\n            return Err(Error::new(\n                span,\n                ErrorKind::UnexpectedChar(next_char?, within),\n            ));\n        }\n\n        match content {\n            \"true\" => Ok(Datum::Bool(span, true)),\n            \"false\" => Ok(Datum::Bool(span, false)),\n            _ => Ok(Datum::Sym(span, content.into())),\n        }\n    }\n\n    fn parse_symbol_shorthand(&mut self, expansion: &str) -> Result<Datum> {\n        let (outer_span, (shorthand_span, quoted_datum)) = self.capture_span(|s| {\n            let (shorthand_span, _) = s.capture_span(|s| {\n                // Discard the shorthand. Note this must be ASCII.\n                s.eat_bytes(1);\n            });\n\n            (shorthand_span, s.parse_datum())\n        });\n\n        quoted_datum.map(|quoted_datum| {\n            Datum::List(\n                outer_span,\n                Box::new([Datum::Sym(shorthand_span, expansion.into()), quoted_datum]),\n            )\n        })\n    }\n\n    fn parse_datum_starting_with(&mut self, c: char, within: WithinContext) -> Result<Datum> {\n        match c {\n            '(' => self.parse_list(),\n            '[' => self.parse_vector(),\n            '{' => self.parse_map(),\n            '0'..='9' => self.parse_num(),\n            '-' | '+' => self.parse_signed_num_or_symbol(),\n            '\\'' => self.parse_symbol_shorthand(\"quote\"),\n            '\"' => self.parse_string(),\n            '\\\\' => self.parse_char(),\n            '#' => self.parse_dispatch(),\n            _ => self.parse_identifier(within),\n        }\n    }\n\n    fn parse_datum(&mut self) -> Result<Datum> {\n        let ec = WithinContext::Datum;\n\n        let start_char = self.skip_until_non_whitespace(ec)?;\n        self.parse_datum_starting_with(start_char, ec)\n    }\n\n    fn parse_data(&mut self) -> Result<Vec<Datum>> {\n        let mut datum_vec = Vec::new();\n\n        // Keep eating datums until we hit EOF\n        loop {\n            match self.parse_datum() {\n                Ok(datum) => {\n                    datum_vec.push(datum);\n                }\n                Err(err) if err.kind() == &ErrorKind::Eof(WithinContext::Datum) => {\n                    break Ok(datum_vec)\n                }\n                Err(err) => break Err(err),\n            }\n        }\n    }\n}\n\n/////////\n\n#[allow(clippy::many_single_char_names)]\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::span::t2s;\n\n    fn whole_str_span(v: &str) -> Span {\n        Span::new(None, 0, v.len() as ByteIndex)\n    }\n\n    #[test]\n    fn bool_datum() {\n        let j = \"false\";\n        let t = \"^^^^^\";\n        let expected = Datum::Bool(t2s(t), false);\n\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"true\";\n        let t = \"^^^^\";\n        let expected = Datum::Bool(t2s(t), true);\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"     false\";\n        let t = \"     ^^^^^\";\n        let expected = Datum::Bool(t2s(t), false);\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"\\ttrue\\t\";\n        let t = \"\\t^^^^\\t\";\n        let expected = Datum::Bool(t2s(t), true);\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \" trueorfalse  \";\n        let t = \" ^^^^^^^^^^^  \";\n        let expected = Datum::Sym(t2s(t), \"trueorfalse\".into());\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n    }\n\n    #[test]\n    fn list_datum() {\n        let j = \"() ; with a comment\";\n        let t = \"^^                 \";\n        let expected = Datum::List(t2s(t), Box::new([]));\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"( true   false )\";\n        let t = \"^^^^^^^^^^^^^^^^\";\n        let u = \"  ^^^^          \";\n        let v = \"         ^^^^^  \";\n\n        let expected = Datum::List(\n            t2s(t),\n            Box::new([Datum::Bool(t2s(u), true), Datum::Bool(t2s(v), false)]),\n        );\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"(1, 2, (3))\";\n        let t = \"^^^^^^^^^^^\";\n        let u = \" ^         \";\n        let v = \"    ^      \";\n        let w = \"       ^^^ \";\n        let x = \"        ^  \";\n\n        let expected = Datum::List(\n            t2s(t),\n            Box::new([\n                Datum::Int(t2s(u), 1),\n                Datum::Int(t2s(v), 2),\n                Datum::List(t2s(w), Box::new([Datum::Int(t2s(x), 3)])),\n            ]),\n        );\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"(true\";\n        let t = \"    >\";\n        let u = \"^    \";\n        let err = Error::new(t2s(t), ErrorKind::Eof(WithinContext::List(t2s(u))));\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n\n        let j = \")\";\n        let t = \"^\";\n        let err = Error::new(t2s(t), ErrorKind::UnexpectedChar(')', WithinContext::Datum));\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n\n        let j = \"(]\";\n        let t = \"^ \";\n        let u = \" ^\";\n        let err = Error::new(\n            t2s(u),\n            ErrorKind::UnexpectedChar(']', WithinContext::List(t2s(t))),\n        );\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n    }\n\n    #[test]\n    fn vector_datum() {\n        let j = \"  []\";\n        let t = \"  ^^\";\n        let expected = Datum::Vector(t2s(t), Box::new([]));\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"[ true   (true false) ]\";\n        let t = \"^^^^^^^^^^^^^^^^^^^^^^^\";\n        let u = \"  ^^^^                 \";\n        let v = \"         ^^^^^^^^^^^^  \";\n        let w = \"          ^^^^         \";\n        let x = \"               ^^^^^   \";\n\n        let expected = Datum::Vector(\n            t2s(t),\n            Box::new([\n                Datum::Bool(t2s(u), true),\n                Datum::List(\n                    t2s(v),\n                    Box::new([Datum::Bool(t2s(w), true), Datum::Bool(t2s(x), false)]),\n                ),\n            ]),\n        );\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"[true []\";\n        let t = \"       >\";\n        let u = \"^       \";\n        let err = Error::new(t2s(t), ErrorKind::Eof(WithinContext::Vector(t2s(u))));\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n\n        let j = \"]\";\n        let t = \"^\";\n        let err = Error::new(t2s(t), ErrorKind::UnexpectedChar(']', WithinContext::Datum));\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n    }\n\n    #[test]\n    fn symbol_datum() {\n        for &test_symbol in &[\n            \"HELLO\",\n            \"HELLO123\",\n            \"predicate?\",\n            \"mutate!\",\n            \"from->to\",\n            \"!$%&*+-./:<=>?\",\n            // These are nearly numbers\n            \".\",\n            \"+\",\n            \"+.\",\n            \"+.5\",\n            \"-\",\n            \"-.\",\n            \"-.5\",\n        ] {\n            let s = whole_str_span(test_symbol);\n            let expected = Datum::Sym(s, test_symbol.into());\n\n            assert_eq!(expected, datum_from_str(None, test_symbol).unwrap());\n        }\n    }\n\n    #[test]\n    fn keyword_symbol_datum() {\n        for &test_symbol in &[\":HELLO\", \":HELLO123\", \":predicate?\", \":mutate!\"] {\n            let s = whole_str_span(test_symbol);\n            let expected = Datum::Sym(s, test_symbol.into());\n\n            assert_eq!(expected, datum_from_str(None, test_symbol).unwrap());\n        }\n    }\n\n    #[test]\n    fn string_datum() {\n        let test_strings = [\n            (r#\"\"\"\"#, \"\"),\n            (r#\"\"Hello, world!\"\"#, \"Hello, world!\"),\n            (r#\"\"Hello\\\"World\"\"#, \"Hello\\\"World\"),\n            (r#\"\"Hello\\\\World\"\"#, \"Hello\\\\World\"),\n            (r#\"\"Tab\\t\"\"#, \"Tab\\t\"),\n            (r#\"\"\\nnewline\"\"#, \"\\nnewline\"),\n            (r#\"\"carriage: \\r\"\"#, \"carriage: \\r\"),\n            (r#\"\"Space\\x20;Bar\"\"#, \"Space Bar\"),\n            (r#\"\"l\\x03BB;\"\"#, \"l\\u{03bb}\"),\n            (r#\"\"\\x0;null!\"\"#, \"\\u{0000}null!\"),\n            (\n                r#\"\"The word \\\"recursion\\\" has many meanings.\"\"#,\n                r#\"The word \"recursion\" has many meanings.\"#,\n            ),\n        ];\n\n        for (test_string, expected_contents) in &test_strings {\n            let s = whole_str_span(test_string);\n            let expected = Datum::Str(s, (*expected_contents).into());\n\n            assert_eq!(expected, datum_from_str(None, test_string).unwrap());\n        }\n\n        let j = r#\" \"foo \"#;\n        let t = r#\"     >\"#;\n        let u = r#\" ^    \"#;\n        let err = Error::new(t2s(t), ErrorKind::Eof(WithinContext::String(t2s(u))));\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n\n        let j = r#\"\"\\p\"\"#;\n        let t = r#\"  ^ \"#;\n        let err = Error::new(t2s(t), ErrorKind::UnsupportedStringEscape);\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n    }\n\n    #[test]\n    fn char_datum() {\n        let test_chars = [\n            (\"\\\\newline\", '\\u{0a}'),\n            (\"\\\\return\", '\\u{0d}'),\n            (\"\\\\space\", '\\u{20}'),\n            (\"\\\\tab\", '\\u{09}'),\n            (\"\\\\a\", 'a'),\n            (\"\\\\A\", 'A'),\n            (\"\\\\(\", '('),\n            (\"\\\\☃\", '\\u{2603}'),\n            (\"\\\\u03BB\", '\\u{03bb}'),\n        ];\n\n        for (j, expected_char) in &test_chars {\n            let s = whole_str_span(j);\n            let expected = Datum::Char(s, *expected_char);\n\n            assert_eq!(expected, datum_from_str(None, j).unwrap());\n        }\n\n        let j = r#\"\\SPACE\"#;\n        let t = r#\" ^^^^^\"#;\n        let err = Error::new(t2s(t), ErrorKind::UnsupportedChar);\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n\n        let j = r#\"\\u110000\"#;\n        let t = r#\" ^^^^^^^\"#;\n        let err = Error::new(t2s(t), ErrorKind::InvalidCodePoint);\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n\n        let j = r#\"[\\newline]\"#;\n        let t = r#\" ^^^^^^^^ \"#;\n        let expected = Datum::Vector(whole_str_span(j), Box::new([Datum::Char(t2s(t), '\\n')]));\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n    }\n\n    #[test]\n    fn int_datum() {\n        let test_ints = [\n            (\"0\", 0),\n            (\"000\", 0),\n            (\"1000\", 1000),\n            (\"+1000\", 1000),\n            (\"-1000\", -1000),\n            (\"9223372036854775807\", 9223372036854775807),\n            (\"+9223372036854775807\", 9223372036854775807),\n            (\"-9223372036854775808\", -9223372036854775808),\n        ];\n\n        for &(j, expected_int) in &test_ints {\n            let s = whole_str_span(j);\n            let expected = Datum::Int(s, expected_int);\n\n            assert_eq!(expected, datum_from_str(None, j).unwrap());\n        }\n\n        let j = \"10223372036854775807\";\n        let t = \"^^^^^^^^^^^^^^^^^^^^\";\n        let err = Error::new(t2s(t), ErrorKind::IntegerOverflow);\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n\n        let j = \"-10223372036854775807\";\n        let t = \"^^^^^^^^^^^^^^^^^^^^^\";\n        let err = Error::new(t2s(t), ErrorKind::IntegerOverflow);\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n\n        let j = \"4545894549584910223372036854775807\";\n        let t = \"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\";\n        let err = Error::new(t2s(t), ErrorKind::IntegerOverflow);\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n    }\n\n    #[test]\n    fn float_datum() {\n        let test_floats = [\n            (\"0.\", 0.0),\n            (\"0.0\", 0.0),\n            (\"000.000\", 0.0),\n            (\"+16.\", 16.0),\n            (\"+16.5\", 16.5),\n            (\"+016.500\", 16.5),\n            (\"-32.\", -32.0),\n            (\"-32.25\", -32.25),\n            (\"-032.2500\", -32.25),\n            (\"##Inf\", std::f64::INFINITY),\n            (\"##-Inf\", std::f64::NEG_INFINITY),\n        ];\n\n        for &(j, expected_float) in &test_floats {\n            let s = whole_str_span(j);\n            let expected = Datum::Float(s, expected_float);\n\n            assert_eq!(expected, datum_from_str(None, j).unwrap());\n        }\n\n        // This can't be compared using normal equality\n        if let Datum::Float(_, f) = datum_from_str(None, \"##NaN\").unwrap() {\n            assert!(f.is_nan());\n        } else {\n            panic!(\"Expected ##NaN to parse as float\");\n        }\n    }\n\n    #[test]\n    fn map_datum() {\n        let j = \"{}\";\n        let t = \"^^\";\n        let expected = Datum::Map(t2s(t), Box::new([]));\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"{ 1,2 ,, 3  4}\";\n        let t = \"^^^^^^^^^^^^^^\";\n        let u = \"  ^           \";\n        let v = \"    ^         \";\n        let w = \"         ^    \";\n        let x = \"            ^ \";\n\n        let expected_contents = Box::new([\n            (Datum::Int(t2s(u), 1), Datum::Int(t2s(v), 2)),\n            (Datum::Int(t2s(w), 3), Datum::Int(t2s(x), 4)),\n        ]);\n        let expected = Datum::Map(t2s(t), expected_contents);\n\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"{1 {2 3}}\";\n        let t = \"^^^^^^^^^\";\n        let u = \" ^       \";\n        let v = \"   ^^^^^ \";\n        let w = \"    ^    \";\n        let x = \"      ^  \";\n\n        let inner_contents = Box::new([(Datum::Int(t2s(w), 2), Datum::Int(t2s(x), 3))]);\n        let inner = Datum::Map(t2s(v), inner_contents);\n\n        let outer_contents = Box::new([(Datum::Int(t2s(u), 1), inner)]);\n        let expected = Datum::Map(t2s(t), outer_contents);\n\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"{1}\";\n        let t = \"^^^\";\n        let err = Error::new(t2s(t), ErrorKind::UnevenMap);\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n    }\n\n    #[test]\n    fn set_datum() {\n        let j = \"#{}\";\n        let t = \"^^^\";\n        let expected = Datum::Set(t2s(t), Box::new([]));\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"#{ 1 2  3 4}\";\n        let t = \"^^^^^^^^^^^^\";\n        let u = \"   ^        \";\n        let v = \"     ^      \";\n        let w = \"        ^   \";\n        let x = \"          ^ \";\n\n        let expected_contents = Box::new([\n            Datum::Int(t2s(u), 1),\n            Datum::Int(t2s(v), 2),\n            Datum::Int(t2s(w), 3),\n            Datum::Int(t2s(x), 4),\n        ]);\n        let expected = Datum::Set(t2s(t), expected_contents);\n\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"#{1 #{2 3}}\";\n        let t = \"^^^^^^^^^^^\";\n        let u = \"  ^        \";\n        let v = \"    ^^^^^^ \";\n        let w = \"      ^    \";\n        let x = \"        ^  \";\n\n        let inner_contents = Box::new([(Datum::Int(t2s(w), 2)), (Datum::Int(t2s(x), 3))]);\n        let inner = Datum::Set(t2s(v), inner_contents);\n\n        let outer_contents = Box::new([Datum::Int(t2s(u), 1), inner]);\n        let expected = Datum::Set(t2s(t), outer_contents);\n\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n    }\n\n    #[test]\n    fn quote_shorthand() {\n        let j = \"'foo\";\n        let t = \"^^^^\";\n        let u = \"^   \";\n        let v = \" ^^^\";\n\n        let expected = Datum::List(\n            t2s(t),\n            Box::new([\n                Datum::Sym(t2s(u), \"quote\".into()),\n                Datum::Sym(t2s(v), \"foo\".into()),\n            ]),\n        );\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"' (1 2 3)\";\n        let t = \"^^^^^^^^^\";\n        let u = \"^        \";\n        let v = \"  ^^^^^^^\";\n        let w = \"   ^     \";\n        let x = \"     ^   \";\n        let y = \"       ^ \";\n\n        let expected = Datum::List(\n            t2s(t),\n            Box::new([\n                Datum::Sym(t2s(u), \"quote\".into()),\n                Datum::List(\n                    t2s(v),\n                    Box::new([\n                        Datum::Int(t2s(w), 1),\n                        Datum::Int(t2s(x), 2),\n                        Datum::Int(t2s(y), 3),\n                    ]),\n                ),\n            ]),\n        );\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"'\";\n        let t = \">\";\n        let err = Error::new(t2s(t), ErrorKind::Eof(WithinContext::Datum));\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n    }\n\n    #[test]\n    fn unsupported_dispatch() {\n        let j = r#\"#loop\"#;\n        let t = r#\"^^   \"#;\n        let err = Error::new(t2s(t), ErrorKind::UnsupportedDispatch);\n\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n\n        let j = \"#\";\n        let t = \">\";\n        let err = Error::new(t2s(t), ErrorKind::Eof(WithinContext::Dispatch));\n\n        assert_eq!(err, datum_from_str(None, j).unwrap_err());\n    }\n\n    #[test]\n    fn datum_comment() {\n        let j = \"(Hello #_(you jerk))\";\n        let t = \"^^^^^^^^^^^^^^^^^^^^\";\n        let u = \" ^^^^^              \";\n\n        let expected = Datum::List(t2s(t), Box::new([Datum::Sym(t2s(u), \"Hello\".into())]));\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n\n        let j = \"(Hello #_  you jerk)\";\n        let t = \"^^^^^^^^^^^^^^^^^^^^\";\n        let u = \" ^^^^^              \";\n        let v = \"               ^^^^ \";\n\n        let expected = Datum::List(\n            t2s(t),\n            Box::new([\n                Datum::Sym(t2s(u), \"Hello\".into()),\n                Datum::Sym(t2s(v), \"jerk\".into()),\n            ]),\n        );\n        assert_eq!(expected, datum_from_str(None, j).unwrap());\n    }\n\n    #[test]\n    fn multiple_data() {\n        let j = \" 1  #_two 3  \";\n        let t = \" ^           \";\n        let u = \"          ^  \";\n\n        let expected = vec![Datum::Int(t2s(t), 1), Datum::Int(t2s(u), 3)];\n        assert_eq!(expected, data_from_str(None, j).unwrap());\n\n        let j = \"(true)))\";\n        let t = \"      ^ \";\n        let err = Error::new(t2s(t), ErrorKind::UnexpectedChar(')', WithinContext::Datum));\n        assert_eq!(err, data_from_str(None, j).unwrap_err());\n\n        let j = \"(true\";\n        let t = \"    >\";\n        let u = \"^    \";\n\n        let err = Error::new(t2s(t), ErrorKind::Eof(WithinContext::List(t2s(u))));\n        assert_eq!(err, data_from_str(None, j).unwrap_err());\n    }\n}\n"
  },
  {
    "path": "syntax/span.rs",
    "content": "use std::num::NonZeroU32;\nuse std::ops::Range;\n\npub type FileId = NonZeroU32;\npub type ByteIndex = u32;\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct Span {\n    file_id: Option<FileId>,\n    start: ByteIndex,\n    end: ByteIndex,\n}\n\nimpl Span {\n    pub const fn new(file_id: Option<FileId>, start: ByteIndex, end: ByteIndex) -> Self {\n        Self {\n            file_id,\n            start,\n            end,\n        }\n    }\n\n    pub const fn from_str(file_id: Option<FileId>, s: &str) -> Self {\n        Self {\n            file_id,\n            start: 0,\n            end: s.len() as ByteIndex,\n        }\n    }\n\n    pub fn file_id(&self) -> Option<FileId> {\n        self.file_id\n    }\n\n    pub fn start(&self) -> ByteIndex {\n        self.start\n    }\n\n    pub fn end(&self) -> ByteIndex {\n        self.end\n    }\n\n    pub fn byte_range(&self) -> Range<usize> {\n        self.start as usize..self.end as usize\n    }\n\n    pub fn contains(&self, other: Span) -> bool {\n        self.file_id == other.file_id && self.start() <= other.start() && self.end() >= other.end()\n    }\n}\n\n// This isn't #[cfg(test)] because it's used in other crates\npub fn t2s(v: &str) -> Span {\n    let (start, end) = if v.is_empty() {\n        // Used for empty files\n        (0, 0)\n    } else if let Some(zero_size_off) = v.find('>') {\n        let byte_pos = (zero_size_off + 1) as ByteIndex;\n        (byte_pos, byte_pos)\n    } else {\n        let start = v.find('^').expect(\"Positioning character not found\") as ByteIndex;\n        let end = v.rfind('^').map(|i| i + 1).unwrap() as ByteIndex;\n\n        (start, end)\n    };\n\n    Span::new(None, start, end)\n}\n"
  }
]