├── .dir-locals.el ├── .github └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── CONTRIBUTING.md ├── COPYRIGHT ├── Cargo.lock ├── Cargo.toml ├── GLOSSARY.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── RELEASES.md ├── book ├── .gitignore ├── book.toml ├── mermaid-init.js ├── mermaid.css ├── mermaid.min.js └── src │ ├── SUMMARY.md │ ├── bibliography.md │ ├── canonical_queries.md │ ├── canonical_queries │ └── canonicalization.md │ ├── clauses.md │ ├── clauses │ ├── coherence.md │ ├── goals_and_clauses.md │ ├── implied_bounds.md │ ├── lowering_rules.md │ ├── opaque_types.md │ ├── type_equality.md │ ├── well_known_traits.md │ └── wf.md │ ├── contribution_guide.md │ ├── engine.md │ ├── engine │ ├── logic.md │ ├── logic │ │ └── coinduction.md │ ├── major_concepts.md │ └── slg.md │ ├── glossary.md │ ├── publishing.md │ ├── recursive.md │ ├── recursive │ ├── coinduction.md │ ├── inductive_cycles.md │ ├── search_graph.md │ └── stack.md │ ├── repl.md │ ├── todo.md │ ├── types.md │ ├── types │ ├── operations.md │ ├── operations │ │ └── fold.md │ ├── role_of_interner.md │ ├── rust_lifetimes.md │ ├── rust_types.md │ └── rust_types │ │ ├── alias.md │ │ └── application_ty.md │ ├── what_is_chalk.md │ └── what_is_chalk │ ├── crates.md │ ├── repl.md │ └── walkthrough.md ├── chalk-derive ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── chalk-engine ├── Cargo.toml ├── README.md └── src │ ├── README.md │ ├── context.rs │ ├── derived.rs │ ├── forest.rs │ ├── lib.rs │ ├── logic.rs │ ├── normalize_deep.rs │ ├── simplify.rs │ ├── slg.rs │ ├── slg │ ├── aggregate.rs │ └── resolvent.rs │ ├── solve.rs │ ├── stack.rs │ ├── strand.rs │ ├── table.rs │ └── tables.rs ├── chalk-integration ├── Cargo.toml ├── README.md └── src │ ├── db.rs │ ├── error.rs │ ├── interner.rs │ ├── lib.rs │ ├── lowering.rs │ ├── lowering │ ├── env.rs │ └── program_lowerer.rs │ ├── program.rs │ ├── program_environment.rs │ ├── query.rs │ ├── test_macros.rs │ └── tls.rs ├── chalk-ir ├── Cargo.toml ├── README.md └── src │ ├── cast.rs │ ├── could_match.rs │ ├── debug.rs │ ├── fold.rs │ ├── fold │ ├── binder_impls.rs │ ├── boring_impls.rs │ ├── in_place.rs │ ├── shift.rs │ └── subst.rs │ ├── interner.rs │ ├── lib.rs │ ├── visit.rs │ ├── visit │ ├── binder_impls.rs │ ├── boring_impls.rs │ └── visitors.rs │ └── zip.rs ├── chalk-parse ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── ast.rs │ ├── lib.rs │ └── parser.lalrpop ├── chalk-recursive ├── Cargo.toml ├── README.md └── src │ ├── combine.rs │ ├── fixed_point.rs │ ├── fixed_point │ ├── cache.rs │ ├── search_graph.rs │ └── stack.rs │ ├── fulfill.rs │ ├── lib.rs │ ├── recursive.rs │ └── solve.rs ├── chalk-solve ├── Cargo.toml ├── README.md └── src │ ├── clauses.rs │ ├── clauses │ ├── builder.rs │ ├── builtin_traits.rs │ ├── builtin_traits │ │ ├── clone.rs │ │ ├── copy.rs │ │ ├── coroutine.rs │ │ ├── discriminant_kind.rs │ │ ├── fn_family.rs │ │ ├── pointee.rs │ │ ├── sized.rs │ │ ├── tuple.rs │ │ └── unsize.rs │ ├── dyn_ty.rs │ ├── env_elaborator.rs │ ├── generalize.rs │ ├── program_clauses.rs │ └── super_traits.rs │ ├── coherence.rs │ ├── coherence │ ├── orphan.rs │ └── solve.rs │ ├── coinductive_goal.rs │ ├── display.rs │ ├── display │ ├── bounds.rs │ ├── identifiers.rs │ ├── items.rs │ ├── render_trait.rs │ ├── state.rs │ ├── stub.rs │ ├── ty.rs │ └── utils.rs │ ├── ext.rs │ ├── goal_builder.rs │ ├── infer.rs │ ├── infer │ ├── canonicalize.rs │ ├── instantiate.rs │ ├── invert.rs │ ├── test.rs │ ├── ucanonicalize.rs │ ├── unify.rs │ └── var.rs │ ├── lib.rs │ ├── logging.rs │ ├── logging_db.rs │ ├── logging_db │ └── id_collector.rs │ ├── rust_ir.rs │ ├── solve.rs │ ├── solve │ ├── test │ │ └── bench.rs │ └── truncate.rs │ ├── split.rs │ └── wf.rs ├── libstd.chalk ├── releases-template.hbs ├── src └── main.rs ├── tests ├── display │ ├── assoc_ty.rs │ ├── built_ins.rs │ ├── const_.rs │ ├── dyn_.rs │ ├── enum_.rs │ ├── fn_.rs │ ├── formatting.rs │ ├── impl_.rs │ ├── lifetimes.rs │ ├── mod.rs │ ├── opaque_ty.rs │ ├── self_.rs │ ├── struct_.rs │ ├── trait_.rs │ ├── unique_names.rs │ ├── util.rs │ └── where_clauses.rs ├── integration │ ├── mod.rs │ └── panic.rs ├── lib.rs ├── logging_db │ ├── mod.rs │ └── util.rs ├── lowering │ └── mod.rs ├── test │ ├── ambiguity_issue_727.rs │ ├── arrays.rs │ ├── auto_traits.rs │ ├── bench.rs │ ├── closures.rs │ ├── coherence.rs │ ├── coherence_goals.rs │ ├── coinduction.rs │ ├── constants.rs │ ├── coroutines.rs │ ├── cycle.rs │ ├── discriminant_kind.rs │ ├── dispatch_from_dyn.rs │ ├── existential_types.rs │ ├── fn_def.rs │ ├── foreign_types.rs │ ├── functions.rs │ ├── implied_bounds.rs │ ├── impls.rs │ ├── lifetimes.rs │ ├── misc.rs │ ├── mod.rs │ ├── negation.rs │ ├── never.rs │ ├── numerics.rs │ ├── object_safe.rs │ ├── opaque_types.rs │ ├── pointee.rs │ ├── projection.rs │ ├── refs.rs │ ├── scalars.rs │ ├── slices.rs │ ├── string.rs │ ├── subtype.rs │ ├── tuples.rs │ ├── type_flags.rs │ ├── unify.rs │ ├── unpin.rs │ ├── unsize.rs │ ├── wf_goals.rs │ └── wf_lowering.rs └── test_util.rs └── triagebot.toml /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((rust-mode (rust-format-on-save . t))) 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | merge_group: 4 | pull_request: 5 | 6 | jobs: 7 | test: 8 | name: Test 9 | runs-on: ubuntu-latest 10 | continue-on-error: ${{ matrix.rust == 'nightly' }} 11 | strategy: 12 | matrix: 13 | rust: [stable, nightly] 14 | steps: 15 | - name: Checkout the source code 16 | uses: actions/checkout@v2 17 | with: 18 | fetch-depth: 1 19 | 20 | - name: Install Rust toolchain 21 | uses: actions-rs/toolchain@v1 22 | with: 23 | toolchain: ${{ matrix.rust }} 24 | profile: minimal 25 | override: true 26 | 27 | - name: Build chalk-engine without default features 28 | run: cd chalk-engine && cargo build --no-default-features 29 | 30 | - name: Build chalk-engine with all features 31 | run: cd chalk-engine && cargo build --all-features 32 | 33 | - name: Execute tests for all crates in the workspace 34 | run: cargo test --all 35 | 36 | - name: Install mdbook 37 | run: | 38 | cd book 39 | curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.5/mdbook-v0.4.5-x86_64-unknown-linux-gnu.tar.gz | tar xz 40 | # Add the book directory to the $PATH 41 | echo "$GITHUB_WORKSPACE/book" >> $GITHUB_PATH 42 | 43 | - name: Install mdbook-mermaid 44 | run: cargo install mdbook-mermaid 45 | 46 | - name: Execute tests for Chalk book 47 | run: cd book && ./mdbook test 48 | 49 | - name: Build Chalk book 50 | run: cd book && ./mdbook build 51 | 52 | - name: Build documentation for all crates in the workspace 53 | run: cargo doc --all --document-private-items --no-deps 54 | 55 | - name: Upload documentation to GitHub Pages 56 | run: | 57 | touch target/doc/.nojekyll 58 | curl -LsSf https://raw.githubusercontent.com/rust-lang/simpleinfra/master/setup-deploy-keys/src/deploy.rs | rustc - -o /tmp/deploy 59 | cp -r book/book/html target/doc/book 60 | (cd target/doc && /tmp/deploy) 61 | env: 62 | GITHUB_DEPLOY_KEY: ${{ secrets.GITHUB_DEPLOY_KEY }} 63 | if: matrix.rust == 'stable' && github.ref == 'refs/heads/master' 64 | 65 | fmt: 66 | name: Format 67 | runs-on: ubuntu-latest 68 | steps: 69 | - name: Checkout the source code 70 | uses: actions/checkout@v2 71 | with: 72 | fetch-depth: 1 73 | 74 | - name: Install Rust toolchain 75 | uses: actions-rs/toolchain@v1 76 | with: 77 | toolchain: stable 78 | profile: minimal 79 | override: true 80 | components: rustfmt 81 | 82 | - name: Check formatting of all crates in the workspace 83 | run: cargo fmt --all -- --check 84 | 85 | mdbook-linkcheck: 86 | name: Book link check 87 | runs-on: ubuntu-latest 88 | if: github.ref != 'refs/heads/master' 89 | steps: 90 | - name: Checkout the source code 91 | uses: actions/checkout@v2 92 | with: 93 | fetch-depth: 1 94 | 95 | - name: Install mdbook 96 | run: | 97 | cd book 98 | curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.5/mdbook-v0.4.5-x86_64-unknown-linux-gnu.tar.gz | tar xz 99 | # Add the book directory to the $PATH 100 | echo "$GITHUB_WORKSPACE/book" >> $GITHUB_PATH 101 | 102 | - name: Install mdbook-linkcheck 103 | run: cd book && curl -L https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/download/v0.7.0/mdbook-linkcheck-v0.7.0-x86_64-unknown-linux-gnu.tar.gz | tar xz 104 | 105 | - name: Build Chalk book 106 | run: cd book && ./mdbook build 107 | 108 | conclusion: 109 | needs: [test, fmt] 110 | # !cancelled() executes the job regardless of whether the previous jobs passed, failed or get skipped. 111 | if: ${{ !cancelled() }} 112 | runs-on: ubuntu-latest 113 | steps: 114 | - name: Conclusion 115 | run: | 116 | # Print the dependent jobs to see them in the CI log 117 | jq -C <<< '${{ toJson(needs) }}' 118 | # Check if all jobs that we depend on (in the needs array) were successful. 119 | jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 120 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | workflow_dispatch: # We can add version input when 1.0 is released and scheduled releases are removed 4 | schedule: 5 | - cron: "0 0 * * 0" # midnight UTC on Sunday 6 | 7 | jobs: 8 | publish: 9 | name: Publish 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v2 15 | with: 16 | ssh-key: ${{ secrets.PUBLISH_DEPLOY_KEY }} 17 | fetch-depth: 0 18 | 19 | - name: Install Rust toolchain 20 | uses: actions-rs/toolchain@v1 21 | with: 22 | toolchain: stable 23 | profile: minimal 24 | override: true 25 | 26 | - name: Install cargo-workspaces 27 | uses: actions-rs/install@v0.1 28 | with: 29 | crate: cargo-workspaces 30 | version: 0.2.44 31 | 32 | - name: Install Node (for changelog generation) 33 | uses: actions/setup-node@v2 34 | with: 35 | node-version: 16 36 | 37 | - name: Release 38 | env: 39 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 40 | shell: bash 41 | run: | 42 | # Check if we can skip releasing a new version 43 | # (there are no changes and the job was not manually triggered) 44 | export CHANGED=$(cargo workspaces changed --include-merged-tags --ignore-changes "**/Cargo.toml") 45 | if [[ -z "$CHANGED" && "$GITHUB_EVENT_NAME" != "workflow_dispatch" ]]; then 46 | # Nothing has changed, so don't publish a new version 47 | echo "No changes detected, skipping publish." 48 | exit 0 49 | fi 50 | 51 | # Update version 52 | git config --global user.email "runner@gha.local" 53 | git config --global user.name "Github Action" 54 | cargo workspaces -v version -ay --force '*' --include-merged-tags --no-git-commit --exact patch 55 | export VERSION=$(cargo pkgid | sed -E 's/.*#(.*)/\1/g') 56 | 57 | # Update changelog 58 | npm install -g auto-changelog@2.2.1 59 | auto-changelog --output RELEASES.md \ 60 | --starting-version v0.11.0 \ 61 | --latest-version "$VERSION" \ 62 | --merge-pattern 'Auto merge of #(\d+) - .+\n\n(.+)' \ 63 | --template releases-template.hbs 64 | 65 | # Commit and publish 66 | git commit -am "Release $VERSION" 67 | git tag "v$VERSION" 68 | cargo workspaces -v publish --from-git --skip-published 69 | git push --tags 70 | cargo workspaces -v version -ay --force '*' --include-merged-tags --no-git-tag --pre-id dev preminor 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.rlib 4 | *.dll 5 | *.exe 6 | TAGS 7 | /*/Cargo.lock 8 | /*/target/ 9 | *~ 10 | target 11 | chalk-parse/src/parser.rs 12 | 13 | ## IDE files 14 | /.idea/ 15 | /.vscode/ 16 | 17 | ## Files used in changelog generation 18 | package.json 19 | package-lock.json 20 | node_modules 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to chalk 2 | 3 | Thank you for your interest in contributing to chalk! There are many ways to 4 | contribute, and we appreciate all of them. 5 | 6 | * [Bug Reports](#bug-reports) 7 | * [Running and Debugging](#running-and-debugging) 8 | * [Pull Requests](#pull-requests) 9 | * [Writing Documentation](#writing-documentation) 10 | * [Helpful Links and Information](#helpful-links-and-information) 11 | 12 | If you'd like to contribute, consider joining the [Traits Working Group][traits-working-group]. 13 | We hang out on the [rust-lang zulip][rust-lang-zulip] in the [#wg-traits][wg-traits-stream] stream. 14 | 15 | As a reminder, all contributors are expected to follow our [Code of Conduct][coc]. 16 | 17 | [traits-working-group]: https://rust-lang.github.io/compiler-team/working-groups/traits/ 18 | [rust-lang-zulip]:https://rust-lang.zulipchat.com 19 | [wg-traits-stream]: https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits 20 | [coc]: https://www.rust-lang.org/conduct.html 21 | 22 | ## Bug Reports 23 | [bug-reports]: #bug-reports 24 | 25 | While bugs are unfortunate, they're a reality in software. We can't fix what we 26 | don't know about, so please report liberally. If you're not sure if something 27 | is a bug or not, feel free to file a bug anyway. 28 | 29 | If you have the chance, before reporting a bug, please search existing issues, 30 | as it's possible that someone else has already reported your error. This doesn't 31 | always work, and sometimes it's hard to know what to search for, so consider 32 | this extra credit. We won't mind if you accidentally file a duplicate report. 33 | 34 | Sometimes, a backtrace is helpful, and so including that is nice. To get 35 | a backtrace, set the `RUST_BACKTRACE` environment variable to a value 36 | other than `0`. The easiest way to do this is to invoke `chalk` like this: 37 | 38 | ```bash 39 | $ RUST_BACKTRACE=1 chalk ... 40 | ``` 41 | 42 | ## Running and Debugging 43 | [running-and-debugging]: #running-and-debugging 44 | There is a repl mainly for debugging purposes which can be run by `cargo run`. Some basic examples are in [libstd.chalk](libstd.chalk): 45 | ```bash 46 | $ cargo run 47 | ?- load libstd.chalk 48 | ?- Vec>: Clone 49 | Unique; substitution [], lifetime constraints [] 50 | ``` 51 | 52 | More logging can be enabled by setting the `CHALK_DEBUG` environment variable. Set `CHALK_DEBUG=1` to see `info!(...)` output, and `CHALK_DEBUG=2` to see `debug!(...)` output as well. 53 | 54 | ## Pull Requests 55 | [pull-requests]: #pull-requests 56 | 57 | Pull requests are the primary mechanism we use to change Rust. GitHub itself 58 | has some [great documentation][pull-request-documentation] on using the Pull Request feature. 59 | We use the "fork and pull" model [described here][development-models], where 60 | contributors push changes to their personal fork and create pull requests to 61 | bring those changes into the source repository. 62 | 63 | Please make pull requests against the `master` branch. 64 | 65 | [pull-request-documentation]: https://help.github.com/articles/about-pull-requests/ 66 | [development-models]: https://help.github.com/articles/about-collaborative-development-models/ 67 | 68 | ## Writing Documentation 69 | [writing-documentation]: #writing-documentation 70 | 71 | Documentation improvements are very welcome. Documentation pull requests 72 | function in the same way as other pull requests. 73 | 74 | You can find documentation style guidelines in [RFC 1574][rfc1574]. 75 | 76 | [rfc1574]: https://github.com/rust-lang/rfcs/blob/master/text/1574-more-api-documentation-conventions.md#appendix-a-full-conventions-text 77 | 78 | ## Helpful Links and Information 79 | [Helpful Links and Information]: #helpful-links-and-information 80 | 81 | ### Blog posts 82 | There are several [blog posts][blog-posts] which describe the ideas and 83 | machinery inside of chalk. 84 | 85 | [blog-posts]: README.md#blog-posts 86 | 87 | ### Glossary 88 | 89 | In addition to the blog posts there is a [glossary](GLOSSARY.md) explaining some 90 | of the terminology used in chalk. 91 | 92 | ### Trait solving in rustc-dev-guide 93 | The rustc-dev-guide describes [new-style trait solving][trait-solving], which is slowly replacing the old trait resolution. 94 | 95 | [trait-solving]: https://rustc-dev-guide.rust-lang.org/traits/chalk.html 96 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chalk" 3 | version = "0.104.0-dev.0" 4 | description = "Model of the Rust trait system" 5 | license = "MIT OR Apache-2.0" 6 | authors = ["Rust Compiler Team", "Chalk developers"] 7 | repository = "https://github.com/rust-lang/chalk" 8 | readme = "README.md" 9 | keywords = ["compiler", "traits", "prolog"] 10 | edition = "2018" 11 | publish = false 12 | 13 | [features] 14 | bench = [] 15 | 16 | [dependencies] 17 | docopt = "1.1.0" 18 | itertools = "0.12.0" 19 | rustyline = { version = "12.0.0", default-features = false } 20 | salsa = "0.16.0" 21 | serde = "1.0" 22 | serde_derive = "1.0" 23 | 24 | chalk-derive = { version = "0.104.0-dev.0", path = "chalk-derive" } 25 | chalk-engine = { version = "0.104.0-dev.0", path = "chalk-engine" } 26 | chalk-ir = { version = "0.104.0-dev.0", path = "chalk-ir" } 27 | chalk-solve = { version = "0.104.0-dev.0", path = "chalk-solve" } 28 | chalk-recursive = { version = "0.104.0-dev.0", path = "chalk-recursive" } 29 | chalk-parse = { version = "0.104.0-dev.0", path = "chalk-parse" } 30 | chalk-integration = { version = "0.104.0-dev.0", path = "chalk-integration" } 31 | 32 | [workspace] 33 | 34 | [dev-dependencies] 35 | # used for program_writer test errors 36 | diff = "0.1" 37 | expect-test = "1.4.1" 38 | pretty_assertions = "1.4.0" 39 | regex = "1" 40 | -------------------------------------------------------------------------------- /GLOSSARY.md: -------------------------------------------------------------------------------- 1 | # Glossary 2 | 3 | Please see [Appendix A: Glossary and terminology](`https://rust-lang.github.io/chalk/book/glossary.html`) in Chalk book. 4 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Chalk contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/rust-lang/chalk/workflows/CI/badge.svg)](https://github.com/rust-lang/chalk/actions?workflow=CI) 2 | [![Chalk Book](https://img.shields.io/badge/book-chalk-blue.svg)](https://rust-lang.github.io/chalk/book/) 3 | [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://rust-lang.github.io/chalk/chalk/) 4 | 5 | # chalk 6 | 7 | Chalk is a library that implements the Rust trait system, based on [Prolog-ish][Prolog] logic rules. 8 | 9 | See the [Chalk book](https://rust-lang.github.io/chalk/book/) for more information. 10 | 11 | ## FAQ 12 | 13 | **How does chalk relate to rustc?** The plan is to have rustc use the 14 | `chalk-solve` crate (in this repo) to answer questions about Rust programs, for 15 | example, "Does `Vec` implement `Debug`?". Internally, chalk converts 16 | Rust-specific information into logic and uses a logic engine to find the answer 17 | to the original query. For more details, see 18 | [this explanation in the chalk book][chalk-lowering-details]. 19 | 20 | **Where does the name come from?** `chalk` is named after [Chalkidiki], the area where [Aristotle] was 21 | born. Since Prolog is a logic programming language, this seemed a 22 | suitable reference. 23 | 24 | [Prolog]: https://en.wikipedia.org/wiki/Prolog 25 | [Chalkidiki]: https://en.wikipedia.org/wiki/Chalkidiki 26 | [Aristotle]: https://en.wikipedia.org/wiki/Aristotle 27 | [chalk-lowering-details]: https://rust-lang.github.io/chalk/book/#chalk-works-by-converting-rust-goals-into-logical-inference-rules 28 | 29 | ## Blog posts 30 | [blog-posts]: #blog-posts 31 | Here are some blog posts talking about chalk: 32 | 33 | - [Lowering Rust Traits to Logic](https://smallcultfollowing.com/babysteps/blog/2017/01/26/lowering-rust-traits-to-logic/) 34 | - Explains the basic concepts at play 35 | - [Unification in Chalk, Part 1](https://smallcultfollowing.com/babysteps/blog/2017/03/25/unification-in-chalk-part-1/) 36 | - An introduction to unification 37 | - [Unification in Chalk, Part 2](https://smallcultfollowing.com/babysteps/blog/2017/04/23/unification-in-chalk-part-2/) 38 | - Extending the system for associated types 39 | - [Negative reasoning in Chalk](https://aturon.github.io/blog/2017/04/24/negative-chalk/) 40 | - How to prove that something is not true 41 | - [Query structure in chalk](https://smallcultfollowing.com/babysteps/blog/2017/05/25/query-structure-in-chalk/) 42 | - The basic chalk query structure, with pointers into the chalk implementation 43 | - [Cyclic queries in chalk](https://smallcultfollowing.com/babysteps/blog/2017/09/12/tabling-handling-cyclic-queries-in-chalk/) 44 | - Handling cyclic relations and enabling the implementation of implied bounds and other long-desired features in an elegant way 45 | 46 | ## REPL 47 | 48 | There is a repl mainly for debugging purposes which can be run by `cargo run`. Some basic examples are in [libstd.chalk](libstd.chalk): 49 | ```bash 50 | $ cargo run 51 | ?- load libstd.chalk 52 | ?- Vec>: Clone 53 | Unique; substitution [], lifetime constraints [] 54 | ``` 55 | 56 | ## Contributing 57 | 58 | If you'd like to contribute, consider joining the [Traits Working Group][working-group]. 59 | We hang out on the [rust-lang zulip][rust-lang-zulip] in the [#wg-traits][wg-traits-stream] stream. 60 | 61 | See [the contributing chapter][contributing] in the chalk book for more info. 62 | 63 | [working-group]: https://rust-lang.github.io/compiler-team/working-groups/traits/ 64 | [rust-lang-zulip]:https://rust-lang.zulipchat.com 65 | [wg-traits-stream]: https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits 66 | [contributing]: https://rust-lang.github.io/chalk/book/contribution_guide.html 67 | -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = [] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | 7 | [preprocessor.mermaid] 8 | command = "mdbook-mermaid" 9 | renderer = ["html"] 10 | 11 | [output.html] 12 | additional-css = ["mermaid.css"] 13 | additional-js = ["mermaid.min.js", "mermaid-init.js"] 14 | 15 | [output.linkcheck] 16 | follow-web-links = true 17 | warning-policy = "error" 18 | optional = true 19 | exclude = [ 20 | # This even returns 403 on real browsers with Cloudflare's verify human challenge 21 | 'dl\.acm\.org', 22 | ] 23 | 24 | # Workaround for GitHub docs returning 403 response: https://github.com/github/docs/issues/17358 25 | [output.linkcheck.http-headers] 26 | "https://help.github.com" = ["accept-encoding: deflate, gzip, br"] 27 | -------------------------------------------------------------------------------- /book/mermaid-init.js: -------------------------------------------------------------------------------- 1 | mermaid.initialize({ 2 | startOnLoad: true, 3 | theme: ['coal', 'navy', 'ayu'].includes(theme) ? 'dark' : 'default', 4 | }); 5 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [What is Chalk?](./what_is_chalk.md) 4 | - [Walkthrough](./what_is_chalk/walkthrough.md) 5 | - [Crates](./what_is_chalk/crates.md) 6 | - [REPL](./what_is_chalk/repl.md) 7 | - [Contribution guide](./contribution_guide.md) 8 | - [Representing and manipulating types](./types.md) 9 | - [The `Interner`](./types/role_of_interner.md) 10 | - [Rust types](./types/rust_types.md) 11 | - [Alias types](./types/rust_types/alias.md) 12 | - [Application types](./types/rust_types/application_ty.md) 13 | - [Rust lifetimes](./types/rust_lifetimes.md) 14 | - [Operations](./types/operations.md) 15 | - [TypeFoldable and the TypeFolder trait](./types/operations/fold.md) 16 | - [Lowering Rust IR to logic](./clauses.md) 17 | - [Goals and clauses](./clauses/goals_and_clauses.md) 18 | - [Type equality and unification](./clauses/type_equality.md) 19 | - [Implied bounds](./clauses/implied_bounds.md) 20 | - [Lowering rules](./clauses/lowering_rules.md) 21 | - [Opaque types (impl Trait)](./clauses/opaque_types.md) 22 | - [Well known traits](./clauses/well_known_traits.md) 23 | - [Well-formedness checking](./clauses/wf.md) 24 | - [Coherence](./clauses/coherence.md) 25 | - [Canonical queries](./canonical_queries.md) 26 | - [Canonicalization](./canonical_queries/canonicalization.md) 27 | - [Chalk engine](./engine.md) 28 | - [Major concepts](./engine/major_concepts.md) 29 | - [Logic](./engine/logic.md) 30 | - [Coinduction](./engine/logic/coinduction.md) 31 | - [SLG Solver](./engine/slg.md) 32 | - [Chalk recursive solver](./recursive.md) 33 | - [The stack](./recursive/stack.md) 34 | - [Inductive cycles](./recursive/inductive_cycles.md) 35 | - [The search graph and caching](./recursive/search_graph.md) 36 | - [Coinduction](./recursive/coinduction.md) 37 | 38 | --- 39 | 40 | [Appendix A: Glossary and terminology](./glossary.md) 41 | [Appendix B: Bibliography](./bibliography.md) 42 | [Appendix C: Incomplete chapters](./todo.md) 43 | [Appendix D: Publishing Chalk](./publishing.md) 44 | -------------------------------------------------------------------------------- /book/src/bibliography.md: -------------------------------------------------------------------------------- 1 | # Bibliography 2 | 3 | If you'd like to read more background material, here are some 4 | recommended texts and papers: 5 | 6 | ## Blog Posts 7 | 8 | * [Lowering Rust traits to logic](https://smallcultfollowing.com/babysteps/blog/2017/01/26/lowering-rust-traits-to-logic/) 9 | * [Unification in Chalk, part 1](https://smallcultfollowing.com/babysteps/blog/2017/03/25/unification-in-chalk-part-1/) 10 | * [Unification in Chalk, part 2](https://smallcultfollowing.com/babysteps/blog/2017/04/23/unification-in-chalk-part-2/) 11 | * [Negative reasoning in Chalk](https://aturon.github.io/blog/2017/04/24/negative-chalk/) 12 | * [Query structure in chalk](https://smallcultfollowing.com/babysteps/blog/2017/05/25/query-structure-in-chalk/) 13 | * [Cyclic queries in chalk](https://smallcultfollowing.com/babysteps/blog/2017/09/12/tabling-handling-cyclic-queries-in-chalk/) 14 | * [An on-demand SLG solver for chalk](https://smallcultfollowing.com/babysteps/blog/2018/01/31/an-on-demand-slg-solver-for-chalk/) 15 | 16 | ## Papers 17 | 18 | 19 | 20 | ["A proof procedure for the logic of Hereditary Harrop formulas"][pphhf], 21 | by Gopalan Nadathur. This paper covers the basics of universes, 22 | environments, and Lambda Prolog-style proof search. Quite readable. 23 | 24 | [pphhf]: https://dl.acm.org/citation.cfm?id=868380 25 | 26 | 27 | 28 | ["A new formulation of tabled resolution with delay"][nftrd], by 29 | Theresa Swift. This paper gives a kind of abstract treatment of the 30 | SLG formulation that is the basis for our on-demand solver. 31 | 32 | [nftrd]: https://dl.acm.org/citation.cfm?id=651202 33 | 34 | 35 | ## Books 36 | * "Introduction to Formal Logic", Peter Smith 37 | * "Handbook of Practical Logic and Automated Reasoning", John Harrison 38 | * "Types and Programming Languages", Benjamin C. Pierce 39 | * [Programming with Higher-order Logic][phl], by Dale Miller and Gopalan 40 | Nadathur, covers the key concepts of Lambda prolog. Although it's a 41 | slim little volume, it's the kind of book where you learn something 42 | new every time you open it. 43 | 44 | [phl]: https://www.amazon.com/Programming-Higher-Order-Logic-Dale-Miller/dp/052187940X 45 | 46 | -------------------------------------------------------------------------------- /book/src/clauses/opaque_types.md: -------------------------------------------------------------------------------- 1 | # Opaque types (impl Trait) 2 | 3 | This chapter describes how "opaque types" are modeled in chalk. Opaque types are 4 | the underlying concept used to implement "existential `impl Trait`" in Rust. 5 | They don't have a direct surface syntax, but uses of `impl Trait` in particular 6 | source locations create a hidden opaque type: 7 | 8 | ```rust,ignore 9 | fn as_u32s<'a, T: Copy + Into>( 10 | elements: &'a [T], 11 | ) -> impl Iterator + 'a { 12 | elements.iter().cloned().map(|e| -> u32 { e.into() }) 13 | } 14 | 15 | #fn main() { 16 | # let x: &[u16] = &[1, 2, 3]; 17 | # let y = as_u32s(&x); 18 | # for e in y { 19 | # println!("e = {}", e); 20 | # } 21 | #} 22 | ``` 23 | 24 | Conceptually, the function `as_u32s` is desugared to return a reference to an 25 | *opaque type*, let's call it `AsU32sReturn` (note that this is not valid 26 | Rust syntax): 27 | 28 | ```rust,ignore 29 | opaque type AsU32sReturn<'a, T>: IntoIterator + 'a 30 | where 31 | T: Copy + Into; 32 | 33 | fn as_u32s<'a, T: Copy + Into>( 34 | elements: &'a [T], 35 | ) -> AsU32sReturn<'a, T> { 36 | ... 37 | } 38 | ``` 39 | 40 | Opaque types are a kind of type alias. They are called *opaque* because, unlike 41 | an ordinary type alias, most Rust code (e.g., the callers of `as_u32s`) doesn't 42 | know what type `AsU32sReturn` represents. It only knows what traits that type 43 | implements (e.g., `IntoIterator`). The actual type that is inferred 44 | for `AsU32sReturn` is called the "hidden type". 45 | 46 | ## Chalk syntax for an opaque type declaration 47 | 48 | Although the above is not valid Rust syntax, it is quite close to the 49 | format that chalk unit tests use, which looks something like this: 50 | 51 | ```rust,ignore 52 | opaque type OpaqueTypeName: /* bounds */ 53 | where 54 | /* where clauses */ 55 | = /* hidden type */; 56 | ``` 57 | 58 | A chalk opaque type declaration has several parts: 59 | 60 | * The **name** `OpaqueTypeName`, which is the name we use to refer to the opaque type 61 | within the chalk file. In real Rust opaque types are not explicitly declared 62 | and hence they are identified just by internal ids (i.e., they are anonymous 63 | in the same way that a closure type is anonymous), so this is just for unit 64 | testing. 65 | * The **generic parameters** `P0..Pn`. In real Rust, these parameters are inherited 66 | from the context in which the `impl Trait` appeared. In our example, these 67 | parameters come from the surrounding function. Note that in real Rust the set 68 | of generic parameters is a *subset* of those that appear on the surrounding 69 | function: in particular, lifetime parameters may not appear unless they explicitly 70 | appear in the opaque type's bounds. 71 | * The **bounds**, which would be `IntoIterator + 'a` in our example. 72 | These are traits that the *hidden type* (see below) is supposed to implement. 73 | They come from the `impl IntoIterator + 'a` type. Even when the hidden 74 | type is, well, hidden, we can assume that the bounds hold. 75 | * The **where clauses**, which would be `T: Copy` and `T: Into` in our 76 | example. These are conditions that must hold on `V0..Vn` for 77 | `OpaqueTypeName` to be a valid type. 78 | * Note that this contrasts with bounds: bounds are things that the hidden type must meet 79 | but which the rest of the code can assume to be true. Where clauses are things 80 | that the rest of the code must prove to be true in order to use the opaque type. 81 | In our example, then, a type like `AsU32sReturn<'a, String>` would be invalid 82 | because `String: Copy` does not hold. 83 | 84 | ## Representing opaque types in chalk types 85 | 86 | We represent opaque types as a kind of **[type alias]**. Like any type alias, 87 | we have to define the conditions in which they can be normalized: 88 | 89 | [type alias]: ../types/rust_types/alias.md 90 | 91 | ## Placeholder rules 92 | -------------------------------------------------------------------------------- /book/src/engine.md: -------------------------------------------------------------------------------- 1 | # Chalk engine 2 | 3 | The `chalk-engine` crate is the core PROLOG-like solver for logical 4 | predicates. Importantly, it is very general and not specific to Rust, 5 | Rust types, or Rust logic. 6 | 7 | ## Implemented PROLOG concepts 8 | 9 | The engine implements the following PROLOG logic concepts. Some of these 10 | have been published on previously, and some are `Chalk`-specific. This isn't 11 | necessarily an exhaustive list: 12 | - Basic logic 13 | - Negation 14 | - Floundering 15 | - Coinductive solving 16 | 17 | ## Note 18 | 19 | Throughout most of this chapter, the specifics in regards to 20 | `Canonicalization` and `UCanonicalization` are avoided. These are important 21 | concepts to understand, but don't particularly help to understand how 22 | `chalk-engine` *works*. In a few places, it may be highlighted if it *is* 23 | important. 24 | -------------------------------------------------------------------------------- /book/src/publishing.md: -------------------------------------------------------------------------------- 1 | # Publishing Chalk 2 | 3 | **Note: this is mostly only useful for maintainers** 4 | 5 | The following crates get published to crates.io: 6 | - `chalk-derive` 7 | - `chalk-engine` 8 | - `chalk-ir` 9 | - `chalk-recursive` 10 | - `chalk-solve` 11 | 12 | The following crates get versioned without publishing: 13 | - `chalk-parse` 14 | - `chalk-integration` 15 | - `chalk` (root directory) 16 | 17 | ## Release Automation 18 | Releases are fully automated. Once a week (Sunday at midnight UTC) a GitHub 19 | Actions job is executed which generates the changelog, bumps crate versions, and 20 | publishes the crates. If there have not been any changes since the last version, 21 | the release is skipped. However, if the job is manually triggered then the 22 | release will be published even if there are no changes. 23 | 24 | The release pipeline is located in [`publish.yml`]. 25 | 26 | [`publish.yml`]: https://github.com/rust-lang/chalk/blob/master/.github/workflows/publish.yml 27 | 28 | ### Changelog Generation 29 | The changelog is generated using [`auto-changelog`] and is stored in 30 | [`RELEASES.md`]. The template used for the changelog is in 31 | [`releases-template.hbs`]. 32 | 33 | [`auto-changelog`]: https://www.npmjs.com/package/auto-changelog 34 | [`RELEASES.md`]: https://github.com/rust-lang/chalk/blob/master/RELEASES.md 35 | [`releases-template.hbs`]: https://github.com/rust-lang/chalk/blob/master/releases-template.hbs 36 | -------------------------------------------------------------------------------- /book/src/recursive.md: -------------------------------------------------------------------------------- 1 | # Chalk recursive solver 2 | 3 | The recursive solver, as its name suggests, is a logic solver that works 4 | "recursively". In particular, its basic structure is a function like: 5 | 6 | ```rust,ignore 7 | fn(Goal) -> Solution 8 | ``` 9 | 10 | where the Goal is some [canonical goal](./canonical_queries.md) and 11 | the Solution is a result like: 12 | 13 | * Provable(S): meaning the goal is provable and it is provably exactly (and 14 | only) for the substitution S. S is a set of values for the inference variables 15 | that appear in the goal. So if we had a goal like `Vec: Foo`, and we 16 | returned `Provable(?X = u32)`, it would mean that only `Vec: Foo` and not 17 | any other sort of vector (e.g., `Vec: Foo` does not hold). 18 | * Ambiguous(S): meaning that we can't prove whether or not the goal is true. 19 | This can sometimes come with a substitution S, which offers suggested values 20 | for the inference variables that might make it provable. 21 | * Error: the goal cannot be proven. 22 | 23 | ## Recursion: pros and cons 24 | 25 | The recursive solver is so-called because, in the process of solving one goal, 26 | it will "recurse" to solve another. Consider an example like this: 27 | 28 | ```rust,ignore 29 | trait A { } 30 | impl A for Vec { } 31 | impl A for u32 { } 32 | impl A for i32 { } 33 | ``` 34 | 35 | which results in program clauses like: 36 | 37 | ```notrust 38 | forall { Implemented(Vec: A) :- Implemented(T: A) } 39 | Implemented(u32: A) 40 | Implemented(i32: A) 41 | ``` 42 | 43 | First, suppose that we have a goal like `Implemented(Vec: A)`. This would 44 | proceed like so: 45 | 46 | * `Solve(Implemented(Vec: A))` 47 | * `Solve(Implemented(u64: A))` 48 | * returns `Error` 49 | * returns `Error` 50 | 51 | In other words, the recursive solver would start by applying the first rule, 52 | which would cause us recursively try to solve `Implemented(u64: A)`. This would 53 | yield an Error result, because there are no applicable rules, and that error 54 | would propagate back up, causing the entire attempt at proving things to fail. 55 | 56 | Next, consider `Implemented(Vec: A)`. This would proceed like so: 57 | 58 | * `Solve(Implemented(Vec: A))` 59 | * `Solve(Implemented(u32: A))` 60 | * returns `Provable` with no substitution (no variables) 61 | * returns `Provable` 62 | 63 | Finally, consider `Implemented(Vec: A)`. This is more interesting because it 64 | has a variable: 65 | 66 | * `Solve(Implemented(Vec: A))` 67 | * `Solve(Implemented(?X: A))` 68 | * finds two viable solutions, returns `Ambiguous` 69 | * returns `Ambiguous` 70 | 71 | ## Recursion and completeness 72 | 73 | One side-effect of the recursive solver's structure is that it 74 | cannot solve find solutions in some cases where a traditional 75 | Prolog solver would be successful. Consider this example: 76 | 77 | ```rust 78 | trait A { } 79 | trait B { } 80 | 81 | impl A for Vec { } 82 | 83 | impl A for u32 { } 84 | impl B for u32 { } 85 | 86 | impl A for i32 { } 87 | impl B for i8 { } 88 | ``` 89 | 90 | In the recursive solver, with a goal of `Implemented(Vec: A)`, we 91 | recursively try to prove `Implemented(?X: A)` and `Implemented(?X: B)`, which 92 | are both ambiguous, and we get stuck there. 93 | 94 | The [SLG solver] in contrast starts by exploring `?X = u32` and finds 95 | that it works, and then later tries to explore `?X = i32` and finds that it 96 | fails (because `i32: B` is not true). 97 | 98 | [SLG solver]: ./engine.md 99 | -------------------------------------------------------------------------------- /book/src/recursive/stack.md: -------------------------------------------------------------------------------- 1 | # The stack 2 | 3 | The first "layer" of the recursive solver is the [`Stack`]. It is really just 4 | what it sounds like: a stack that stores each thing that the recursive solver is 5 | solving. Initially, it contains only one item, the root goal that was given by 6 | the user. 7 | 8 | [`Stack`]: https://rust-lang.github.io/chalk/chalk_recursive/fixed_point/stack/struct.Stack.html 9 | 10 | Each frame on the stack has an associated [`StackDepth`], which is basically an 11 | index that increases (so 0 is the top of the stack, 1 is the next thing pushed, 12 | etc). 13 | 14 | [`StackDepth`]: https://rust-lang.github.io/chalk/chalk_recursive/fixed_point/stack/struct.StackDepth.html 15 | 16 | ## How the recursive solver works at the highest level 17 | 18 | At the highest level, the recursive solver works like so. 19 | 20 | * Push the initial goal `G0` onto the stack. 21 | * Find all the program clauses `G1 :- G2...Gn` that could apply to the goal `G0`. 22 | * For each program clause, unify `G1` and `G0`. If that succeeds, then recursively try to prove each goal `Gi` in the list `G2..Gn`: 23 | * If proving `Gi` yields an error, return an error. 24 | * If proving `Gi` yields an ambiguity, keep going, but remember that we got an ambiguous result. 25 | * If proving `Gi` succeeded, apply the resulting answer to our inference variables and keep going. 26 | * At the end, if any result proved ambiguous, return ambiguous, otherwise construct the final answer and return success. 27 | 28 | ## Example 29 | 30 | ```rust 31 | trait A { } 32 | trait B { } 33 | 34 | impl A for Vec { } 35 | 36 | impl B for u32 { } 37 | ``` 38 | 39 | Imagine we are trying to prove `Implemented(Vec: A)`. There is one unbound 40 | inference variable here, `?X`. We will ultimately get the result `Provable(?X = 41 | u32)`. But how do we find it? 42 | 43 | * Initially we are solving `Implemented(Vec: A)` 44 | * we find one applicable program clause, `forall { Implemented(Vec: A) :- Implemented(T: B) }`. 45 | * after unification, the list of subgoals is `[Implemented(?X: B)]`. 46 | * we recursively try to solve `Implemented(?X: B)` 47 | * we find one applicable program clause, `Implemented(u32: B)`. 48 | * after unification, `?X = u32`, but there are no more subgoals. 49 | * we return the answer `Provable(?X = u32)`. 50 | * we apply the substitution `?X = u32`, and find there are no more subgoals. 51 | * we return the answer `Provable(?X = u32)`. 52 | 53 | ## Why do we need the stack? 54 | 55 | You may have noticed that the description above never seemed to use the [`Stack`], 56 | it only relied on the program stack. That's because I left out any discussion 57 | of cycles. In fact, the [`Stack`] data structure does mirror the program stack, 58 | it just adds some extra information we use in resolving cycles. We'll discuss 59 | cycles in the next chapter, when we discuss the [search graph]. 60 | 61 | ## Figuring out if something is on the stack 62 | 63 | The stack itself never stores the goal associated with a particular entry. That 64 | information is found in the [search graph], which will be covered in detail in 65 | the next section. For now it suffices to say that the search graph maps from 66 | "some goal that we are currently solving" to "information about that goal", and 67 | one of the bits of information is the [`StackDepth`] of its entry on the stack 68 | (if any). 69 | 70 | Therefore, when we are about to start solving some (canonical) goal G, we can 71 | detect a cycle by checking in the [search graph] to see whether G has an associated 72 | [`StackDepth`]. If so, it must be on the stack already (and we can set the 73 | [`cycle`] field to true...but I get ahead of myself, read the next chapters 74 | to learn more about that). 75 | 76 | [search graph]: ./search_graph.md 77 | [`cycle`]: https://rust-lang.github.io/chalk/chalk_recursive/fixed_point/stack/struct.StackEntry.html#structfield.cycle -------------------------------------------------------------------------------- /book/src/repl.md: -------------------------------------------------------------------------------- 1 | # REPL 2 | -------------------------------------------------------------------------------- /book/src/todo.md: -------------------------------------------------------------------------------- 1 | ## Incomplete chapters 2 | 3 | Some topics yet to be written: 4 | 5 | - Elaborate on the proof procedure 6 | - SLG solving – introduce negative reasoning 7 | - Go over how trait impls are selected and checked 8 | - Add a section on higher-ranked trait bounds 9 | -------------------------------------------------------------------------------- /book/src/types.md: -------------------------------------------------------------------------------- 1 | # Representing and manipulating Rust types 2 | 3 | ## Intermediate representations 4 | 5 | Intermediate representations (IR) are used to represent parts of Rust programs such as traits and impls. 6 | 7 | Chalk contains three levels of IR: 8 | 9 | - The **AST**. This is used purely for writing test cases 10 | with a Rust-like syntax. This is consumed by **lowering** code, which 11 | takes AST and produces **Rust IR** (the next bullet point). 12 | - The **Rust IR**. This is a "HIR-like" notation that defines the 13 | interesting properties of things like traits, impls, and structs. 14 | It is an input to the **rules** code, which produces **Chalk IR** (the next bullet point). 15 | - The **Chalk IR**. This is most "Prolog-like" of the various IRs. It 16 | contains the definition of **types** as well as prolog-like concepts 17 | such as goals (things that must be proven true) and clauses (things 18 | that are assumed to be true). 19 | 20 | 21 | ## Goal of the chalk-ir crate 22 | 23 | To have an ergonomic, flexible library that can abstractly represent 24 | Rust types and logical predicates. The library should be expose a 25 | "minimal" set of types that is nonetheless able to capture the full 26 | range of Rust types. "Minimal" here means that some of the surface 27 | differences in Rust types -- e.g., the distinction between built-in 28 | types like `u32` and user-defined types like a struct -- ought to be 29 | minimized, so that code that works with these types (e.g., trait 30 | solving) can focus on the most important differences. 31 | 32 | ## Goal: support embedding and a variety of contexts 33 | 34 | One of our goals is to create a type representation that can be 35 | readily embedded into a variety of contexts. Most specifically, we 36 | would like to be able to embed into rustc and rust-analyzer, and 37 | permit those two projects to use distinct memory management 38 | strategies. This is primarily achieved via the `Interner` trait. 39 | 40 | Initially, at least in rustc, the goal is to be able to easily and 41 | "reasonably efficiently" convert back and forth between rustc's native 42 | type representation and chalk's representation. Once chalk's design 43 | has stabilized, however, the goal would be for rustc to adopt this 44 | format as its "native" representation. 45 | 46 | Note that even if the chalk type library were used everywhere, 47 | however, it would still be useful for rustc to be able to control the 48 | memory management strategy. (In other words, different consumers might 49 | wish to use it in different ways.) 50 | 51 | ## Note on status 52 | 53 | At the moment, this documentation is a "proposal". That means that it 54 | diverges in some places from what is actually implemented. It has also 55 | not been thoroughly discussed by the Rust compiler team as a whole. 56 | 57 | Here is a (partial) list of some things that have to be adapted in 58 | Chalk as of today to match this document: 59 | 60 | * Extract `TypeName` into something opaque to chalk-ir. 61 | * Dyn type equality should probably be driven by entailment. 62 | * Projections need to be renamed to aliases. 63 | * The variant we use for impl traits should be removed and folded into type aliases. 64 | * Remove placeholders and projection placeholders from apply and create placeholder types. 65 | * Move `Error` from a `TypeName` to its own variant. 66 | * Introduce `GeneratorWitness` into chalk 67 | * Complete transition from `ForAll` to `Fn` in chalk 68 | -------------------------------------------------------------------------------- /book/src/types/operations.md: -------------------------------------------------------------------------------- 1 | # Operations 2 | 3 | This chapter describes various patterns and utilities for manipulating 4 | Rust types. 5 | -------------------------------------------------------------------------------- /book/src/types/role_of_interner.md: -------------------------------------------------------------------------------- 1 | ## The role of the `Interner` 2 | 3 | Most everything in the IR is parameterized by the [`Interner`] trait: 4 | 5 | [`Interner`]: https://rust-lang.github.io/chalk/chalk_ir/interner/trait.Interner.html 6 | 7 | ```rust,ignore 8 | trait Interner: Copy + Clone + Debug + Eq + Ord { 9 | .. 10 | } 11 | ``` 12 | 13 | We'll go over the details later, but for now it suffices to say that 14 | the interner is defined by the embedder and can be used to control 15 | (to a certain extent) the actual representation of types, goals, and 16 | other things in memory. For example, the `Interner` trait could be 17 | used to intern all the types, as rustc does, or it could be used to 18 | `Box` them instead, as the chalk testing harness currently does. 19 | 20 | ### Controlling representation with `Interner` 21 | 22 | The purpose of the [`Interner`] trait is to give control over how 23 | types and other bits of chalk-ir are represented in memory. This is 24 | done via an "indirection" strategy. We'll explain that strategy here 25 | in terms of [`Ty`] and [`TyKind`], the two types used to represent 26 | Rust types, but the same pattern is repeated for many other things. 27 | 28 | [`Interner`]: https://rust-lang.github.io/chalk/chalk_ir/interner/trait.Interner.html 29 | [`Ty`]: https://rust-lang.github.io/chalk/chalk_ir/struct.Ty.html 30 | [`TyKind`]: https://rust-lang.github.io/chalk/chalk_ir/enum.TyKind.html 31 | 32 | Types are represented by a [`Ty`] type and the [`TyKind`] enum. 33 | There is no *direct* connection between them. The link is rather made 34 | by the [`Interner`] trait, via the [`InternedTy`] associated type: 35 | 36 | [`Ty`]: https://rust-lang.github.io/chalk/chalk_ir/struct.Ty.html 37 | [`TyKind`]: https://rust-lang.github.io/chalk/chalk_ir/enum.TyKind.html 38 | [`InternedTy`]: https://rust-lang.github.io/chalk/chalk_ir/interner/trait.Interner.html#associatedtype.InternedType 39 | 40 | ```rust,ignore 41 | struct Ty(I::InternedTy); 42 | enum TyKind { .. } 43 | ``` 44 | 45 | The way this works is that the [`Interner`] trait has an associated 46 | type [`InternedTy`] and two related methods, [`intern_ty`] and [`ty_data`]: 47 | 48 | [`intern_ty`]: https://rust-lang.github.io/chalk/chalk_ir/interner/trait.Interner.html#tymethod.intern_ty 49 | [`ty_data`]: https://rust-lang.github.io/chalk/chalk_ir/interner/trait.Interner.html#tymethod.ty_data 50 | 51 | ```rust,ignore 52 | trait Interner { 53 | type InternedTy; 54 | 55 | fn intern_ty(&self, data: &TyKind) -> Self::InternedTy; 56 | fn ty_data(data: &Self::InternedTy) -> &TyData; 57 | } 58 | ``` 59 | 60 | However, as a user you are not meant to use these directly. Rather, 61 | they are encapsulated in methods on the [`Ty`] and [`TyKind`] types: 62 | 63 | ```rust,ignore 64 | impl Ty { 65 | fn data(&self) -> &TyKind { 66 | I::lookup_ty(self) 67 | } 68 | } 69 | ``` 70 | 71 | and 72 | 73 | ```rust,ignore 74 | impl TyKind { 75 | fn intern(&self, i: &I) -> Ty { 76 | Ty(i.intern_ty(self)) 77 | } 78 | } 79 | ``` 80 | 81 | Note that there is an assumption here that [`ty_data`] needs no 82 | context. This effectively constrains the [`InternedTy`] representation 83 | to be a `Box` or `&` type. To be more general, at the cost of some 84 | convenience, we could make that a method as well, so that one would 85 | invoke `ty.data(i)` instead of just `ty.data()`. This would permit us 86 | to use (for example) integers to represent interned types, which might 87 | be nice (e.g., to permit using generational indices). 88 | -------------------------------------------------------------------------------- /book/src/types/rust_lifetimes.md: -------------------------------------------------------------------------------- 1 | # Rust lifetimes 2 | 3 | Lifetimes are represented by the `Lifetime` and `LifetimeData` 4 | types. As with types, the actual representation of a lifetime is 5 | defined by the associated type `I::InternedLifetime`. 6 | 7 | ### The `LifetimeData` variants 8 | 9 | This section covers the variants we use to categorize lifetimes. 10 | 11 | #### Variants and their equivalents in Rust syntax 12 | 13 | | Chalk variant | Example Rust types | 14 | | ------------- | ------------------ | 15 | | `BoundVar` | the `'a` in a type like `for<'a> fn(&'a u8)`, before it is instantiated | 16 | | `InferenceVar` | a lifetime whose value is being inferred | 17 | | `Placeholder` | how we represent `'a` when type checking `fn foo<'a>() { .. }` | 18 | | `Static` | the lifetime `'static` | 19 | -------------------------------------------------------------------------------- /book/src/types/rust_types/alias.md: -------------------------------------------------------------------------------- 1 | # Alias types 2 | 3 | **Alias types** are used in chalk to handle a number of distinct Rust 4 | concepts: 5 | 6 | * Explicit type aliases like `type Foo = u32` (in theory) 7 | * Associated types like `impl Iterator for Foo { type Item = Bar }` 8 | * Opaque types generated by impl Traits, like `type Foo = impl Iterator` 9 | or `fn foo() -> impl Iterator`. 10 | 11 | What all these aliases have in common is that they let the user write the name 12 | of one type that turns out to be *equivalent* to another, although the 13 | equivalent type is not always known: 14 | 15 | * In an explicit type alias like `type Foo = u32`, the user writes `Foo` 16 | but it is always known to be equivalent to `u32` 17 | * In an associated type, the user might write ` as 18 | Iterator>::Item`, but the compiler knows that can be *normalized* (see below) 19 | to `u32`. In generic functions, though, you might have a type like `T::Item` 20 | where we *can't* normalize, because we don't know what `T` is. Even in that 21 | case, though, we still know that `T::Item: Sized`, because that bound is 22 | [declared in the `Iterator` trait][Iterator::Item] (by default, as it 23 | happens). We describe how both cases are handled in more detail in the [section on associated types](../../clauses/type_equality.html). 24 | * In an opaque type like `type Foo = impl Iterator`, the user might 25 | write `Foo` (which indirectly references the opaque type) but they never get 26 | to rely on the precise underlying type. However, when generating code, the 27 | *compiler* does need to be able to normalize `Foo` to the precise underlying 28 | type, so normalization still does occur. We describe this in more detail in the [opaque types](../../clauses/opaque_types.html) section. 29 | 30 | [Iterator::Item]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#associatedtype.Item 31 | 32 | ## How aliases work 33 | 34 | All aliases have a few parts: 35 | 36 | * The *Alias* type, which represents what the user wrote directly, but where there is 37 | some underlying type. 38 | * *Normalization* rules, which indicate when the alias type can be converted 39 | into its underlying type. 40 | * A corresponding *Placeholder* type, which is used in cases where the alias **cannot** be 41 | converted into its underlying type. 42 | 43 | ## Equating an alias 44 | 45 | Alias types are integrated specially into unification. Whenever there is an 46 | attempt to unify an *Alias* type `A` with some other type `T`, we generate an 47 | `AliasEq` that must be solved: 48 | 49 | ```notrust 50 | AliasEq(A = T) 51 | ``` 52 | 53 | The rules for how to solve an `AliasEq` goal will be generated by lowering the alias 54 | definition, and depend a bit on the kind of alias. We describe that lowering in the 55 | [clauses](../../clauses.md) section. 56 | 57 | ## Alias placeholders 58 | 59 | For each kind of alias (except for explicit type aliases), there is also a 60 | corresponding *placeholder* variant in the [`TyKind`] enum. In those cases 61 | where we cannot normalize the alias to something specific, it can be equated to 62 | the placeholder type (see e.g. [`AssociatedType`], which is the placeholder 63 | variant for associated type projections). Note that placeholders are 64 | *application types* -- unlike an alias, a placeholder is only known to be equal 65 | with itself, just like an application type. 66 | 67 | [`TyKind`]: https://rust-lang.github.io/chalk/chalk_ir/enum.TyKind.html 68 | [`AssociatedType`]: https://rust-lang.github.io/chalk/chalk_ir/enum.TyKind.html#variant.AssociatedType 69 | 70 | -------------------------------------------------------------------------------- /book/src/types/rust_types/application_ty.md: -------------------------------------------------------------------------------- 1 | # Application types 2 | 3 | [`TyKind`] variants that consist of some type-specific info ("type name") 4 | and a substitution are usually referred to as application types. 5 | These include most of the "normal Rust types", such as `Vec` and `(f32, u32)`. 6 | Such types are only "equal" to themselves (modulo aliases, see below). 7 | Scalar types (and some others) also fall into this category, despite having no 8 | substitutions: we treat them as having zero-length substitutions. 9 | Note that we group together *both* user-defined structs/enums/unions (like `Vec`) 10 | as well as built-in types like `f32`, which effectively behave the 11 | same. 12 | 13 | We used to have application types in chalk as a separate notion in the codebase, 14 | but have since moved away from that; nevertheless, the term is still useful in discussions. 15 | 16 | [`TyKind`]: https://rust-lang.github.io/chalk/chalk_ir/enum.TyKind.html 17 | 18 | ## Notable application types 19 | 20 | ### Coroutine 21 | 22 | A `Coroutine` represents a Rust coroutine. There are three major components 23 | to a coroutine: 24 | 25 | * Upvars - similar to closure upvars, they reference values outside of the coroutine, 26 | and are stored across all yield points. 27 | * Resume/yield/return types - the types produced/consumed by various coroutine methods. 28 | These are not stored in the coroutine across yield points - they are only 29 | used when the coroutine is running. 30 | * Coroutine witness - see the `Coroutine Witness` section below. 31 | 32 | Of these types, only upvars and resume/yield/return are stored directly in `CoroutineDatum` 33 | (which is accessed via `RustIrDatabase`). The coroutine witness is implicitly associated with 34 | the coroutine by virtue of sharing the same `CoroutineId`. It is only used when determining 35 | auto trait impls, where it is considered a 'constituent type'. 36 | 37 | For example: 38 | 39 | ```rust,ignore 40 | // This is not "real" syntax at the moment. 41 | fn gen() -> Bar { 42 | let a = yield 0usize; 43 | use(a) 44 | } 45 | 46 | fn use(_: usize) -> Bar {} 47 | ``` 48 | 49 | The type of yield would be `usize`, the resume type would be the type of `a` and the return type 50 | would be `Bar`. 51 | 52 | ### Coroutine witness types 53 | 54 | The `CoroutineWitness` variant represents the coroutine witness of 55 | the coroutine with id `CoroutineId`. 56 | 57 | The coroutine witness contains multiple witness types, 58 | which represent the types that may be part of a coroutine 59 | state - that is, the types of all variables that may be live across 60 | a `yield` point. 61 | 62 | Unlike other types, witnesses include bound, existential 63 | lifetimes, which refer to lifetimes within the suspended stack frame. 64 | You can think of it as a type like `exists<'a> { (T...) }`. 65 | As an example, imagine that a type that isn't `Send` lives across a `yield`, then the coroutine 66 | itself can't be `Send`. 67 | 68 | Witnesses have a binder for the erased lifetime(s), which must be 69 | handled specifically in equating and so forth. In many ways, 70 | witnesses are also quite similar to `Function` types, and it is not 71 | out of the question that these two could be unified; however, they 72 | are quite distinct semantically and so that would be an annoying 73 | mismatch in other parts of the system. Witnesses are also similar 74 | to a `Dyn` type, in that they represent an existential type, but 75 | in contrast to `Dyn`, what we know here is not a *predicate* but 76 | rather some upper bound on the set of types contained within. 77 | -------------------------------------------------------------------------------- /book/src/what_is_chalk.md: -------------------------------------------------------------------------------- 1 | # What is Chalk? 2 | 3 | > Chalk is under heavy development, so if any of these links are broken or if 4 | > any of the information is inconsistent with the code or outdated, please 5 | > [open an issue][issues] so we can fix it. If you are able to fix the 6 | > issue yourself, we would love your contribution! 7 | 8 | Chalk is a library that implements the Rust trait system. The implementation is 9 | meant to be practical and usable, but also high-level enough to map easily to a 10 | full specification. It is also meant to be an independent library that can be 11 | integrated both into the main rustc compiler and also other programs and 12 | contexts. 13 | 14 | [issues]: https://github.com/rust-lang/chalk/issues 15 | 16 | ## High-level view of how chalk works 17 | 18 | ```mermaid 19 | graph TD 20 | Query["Does `Vec<u8>` implement `Debug`?"] 21 | HighLevelInfo["How is the trait `Debug` declared?"] 22 | Response["Yes, `Vec<u8>` implements `Debug`."] 23 | Chalk 24 | Query --> Chalk 25 | HighLevelInfo --> Chalk 26 | Chalk --> Response 27 | ``` 28 | 29 | Chalk is designed to answer queries about traits, such as "Does the type `Vec` implement `Debug`"? (Yes!). It can in some cases give inference feedback, such as "Is there a unique type `T` such that `str: AsRef`"? In that case, the answer might be "Yes, `T = str`." 30 | 31 | To do this, it takes as input key information about a Rust program, such as: 32 | 33 | * For a given trait, what are its type parameters, where clauses, and associated items 34 | * For a given impl, what are the types that appear in the impl header 35 | * For a given struct, what are the types of its fields 36 | 37 | ## Chalk works by converting Rust goals into logical inference rules 38 | 39 | Internally, Chalk works by converting the Rust-specific information, like traits 40 | and impls, into *logical predicates*. This process is called "lowering", and you 41 | can learn more about it in the [*Lowering to Logic*][lowering-to-logic] and 42 | [*Lowering Rules*][lowering-rules]) sections. 43 | 44 | [lowering-rules]: ./clauses/lowering_rules.html 45 | [lowering-to-logic]: ./clauses.html 46 | 47 | After lowering to logical predicates, Chalk then deploys a *logical solver* to 48 | find the answer to the original query; this solver is similar to a Prolog 49 | engine, though different in its particulars. 50 | 51 | The following sequence diagram helps to illustrate the flow of information that occurs 52 | when Chalk is solving a particular goal. It involves three participants: 53 | 54 | * The **host program**, which might be rustc, rust-analyzer, or chalk's internal 55 | testing harness. The host program, importantly, only thinks about things in 56 | **Rust terms**, like traits and impls. 57 | * The **chalk-solve** crate, which converts between Rust terms and logical clauses. 58 | * The **logic engine** layer, which knows how to solve logical clauses but knows nothing specific to Rust. 59 | 60 | ```mermaid 61 | sequenceDiagram 62 | participant rustc as host program 63 | participant chalkSolve as chalk-solve 64 | participant chalkEngine as logic engine 65 | rustc->>chalkSolve: Does Vec[u32] implement Debug? 66 | chalkSolve->>chalkEngine: (Vec[u32]: Debug)? 67 | chalkEngine->>chalkSolve: What clauses can I use? 68 | chalkSolve->>rustc: What is the definition of `Debug`?
(via RustIrDatabase) 69 | rustc-->>chalkSolve: `trait Debug { .. }`
(a TraitDatum) 70 | chalkSolve->>rustc: What impls are there for Vec? 71 | rustc-->>chalkSolve: `impl[T]: Debug] Debug for Vec[T]`
(an ImplDatum) 72 | Note right of chalkSolve: "lowers" rust
declarations to logic 73 | chalkSolve-->>chalkEngine: (Vec[T]: Debug) :- (T: Debug) 74 | chalkSolve-->>chalkEngine: ... and other clauses ... 75 | activate chalkEngine 76 | Note right of chalkEngine: explores each clause
to see if it works 77 | chalkEngine-->>chalkSolve: (Vec[u32]: Debug) is provable 78 | deactivate chalkEngine 79 | chalkSolve-->>rustc: Yes, Vec[u32] implements Debug 80 | ``` 81 | 82 | ## Chalk repl 83 | 84 | In addition to being embedded into host programs, chalk also has its own testing 85 | harness along with an associated REPL. This allows us to write unit tests that 86 | use a "Rust-like" syntax. The REPL then makes it easy to experiment and get a 87 | better feel for how chalk works. See the [walkthrough] for more details. 88 | 89 | [walkthrough]: what_is_chalk/walkthrough.html 90 | 91 | -------------------------------------------------------------------------------- /book/src/what_is_chalk/crates.md: -------------------------------------------------------------------------------- 1 | # Crate breakdown 2 | 3 | Chalk is broken up into a number of crates. This chapter explains the 4 | role of each crate. This crate structure helps to serve Chalk's two goals: 5 | 6 | * To serve as the trait engine for compilers and tools like rustc and rust-analyzer 7 | * To be usable as a standalone REPL and testing harness 8 | 9 | ## Crates for embedding chalk into other programs 10 | 11 | The following crates are "public facing" crates that you may use when embedding chalk into 12 | other programs: 13 | 14 | * The `chalk-solve` crate, which defines the IR representing Rust concepts like 15 | traits and impls and the rules that translate Rust IR into logical predicates. 16 | * The `chalk-ir` crate, which defines the IR representing types and logical predicates. 17 | 18 | The following crate is an implementation detail, used internally by `chalk-solve`: 19 | 20 | * The `chalk-engine` crate, which defines the actual engine that solves logical predicate. This 21 | engine is quite general and not really specific to Rust. 22 | * The `chalk-derive` crate defines custom derives for the `chalk_ir::fold::TypeFoldable` trait and other 23 | such things. 24 | 25 | ## Crates for standalone REPL and testing 26 | 27 | The following crates are used to define the REPL and internal testing 28 | harness. These crates build on the crates above. Essentially, they 29 | define a kind of "minimal embedding" of chalk. 30 | 31 | * The `chalk-parser` crate can parse Rust syntax to produce an AST. 32 | * The `chalk-integration` crate can take that AST and use it to drive the 33 | `chalk-solve` crate above. The AST is converted into Rust IR by a process 34 | called "lowering". 35 | * Finally, the main `chalk` crate, along with the testing crate in the 36 | `tests` directory, define the actual entry points. 37 | 38 | ## The chalk-solve crate 39 | 40 | | The `chalk-solve` crate | | 41 | | ----------------------- | --------------------- | 42 | | Purpose: | to solve a given goal | 43 | | Depends on IR: | chalk-ir and rust-ir | 44 | | Context required: | `RustIrDatabase` | 45 | 46 | The `chalk-solve` crate exposes a key type called `Solver`. This is a 47 | solver that, given a goal (expressed in chalk-ir) will solve the goal 48 | and yield up a `Solution`. The solver caches intermediate data between 49 | invocations, so solving the same goal twice in a row (or solving goals 50 | with common subgoals) is faster. 51 | 52 | The solver is configured by a type that implements the 53 | `RustIrDatabase` trait. This trait contains some callbacks that 54 | provide needed context for the solver -- notably, the solver can ask: 55 | 56 | - **What are the program clauses that might solve given rule?** This 57 | is answered by the code in the chalk-solve crate. 58 | - **Is this trait coinductive?** This is answered by the chalk-ir. 59 | 60 | 61 | ## The chalk-engine crate 62 | 63 | | The `chalk-engine` crate | | 64 | | ------------------------ | -------------------------------- | 65 | | Purpose: | define the base solving strategy | 66 | | IR: | none | 67 | | Context required: | `Context` trait | 68 | 69 | For the purposes of chalk, the `chalk-engine` crate is effectively 70 | encapsulated by `chalk-solve`. It defines the base SLG engine. It is 71 | written in a very generic style that knows next to nothing about Rust 72 | itself. The engine can be configured via the traits defined in 73 | `chalk_engine::context::Context`, which contain (for example) 74 | associated types that define what a goal or clause is, as well as 75 | functions that operate on those things. 76 | -------------------------------------------------------------------------------- /book/src/what_is_chalk/repl.md: -------------------------------------------------------------------------------- 1 | # REPL 2 | 3 | There is a repl mainly for debugging purposes which can be run by `cargo run`. Some basic examples are in [libstd.chalk](https://github.com/rust-lang/chalk/blob/master/libstd.chalk): 4 | ```bash 5 | $ cargo run 6 | ?- load libstd.chalk 7 | ?- Vec>: Clone 8 | Unique; substitution [], lifetime constraints [] 9 | ``` 10 | -------------------------------------------------------------------------------- /chalk-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chalk-derive" 3 | version = "0.104.0-dev.0" 4 | description = "A helper crate for use by chalk crates for `derive` macros." 5 | license = "MIT OR Apache-2.0" 6 | authors = ["Rust Compiler Team", "Chalk developers"] 7 | repository = "https://github.com/rust-lang/chalk" 8 | readme = "README.md" 9 | keywords = ["compiler", "traits", "prolog"] 10 | edition = "2018" 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | synstructure = "0.13.0" 17 | quote = "1.0" 18 | proc-macro2 = "1.0" 19 | syn = { version = "2.0", features = ["full"] } 20 | -------------------------------------------------------------------------------- /chalk-derive/README.md: -------------------------------------------------------------------------------- 1 | A helper crate for use by chalk crates for `derive` macros. 2 | 3 | See [Github](https://github.com/rust-lang/chalk) for up-to-date information. 4 | -------------------------------------------------------------------------------- /chalk-engine/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chalk-engine" 3 | version = "0.104.0-dev.0" 4 | description = "Core trait engine from Chalk project" 5 | license = "MIT OR Apache-2.0" 6 | authors = ["Rust Compiler Team", "Chalk developers"] 7 | repository = "https://github.com/rust-lang/chalk" 8 | readme = "README.md" 9 | keywords = ["compiler", "traits", "prolog"] 10 | edition = "2018" 11 | 12 | [features] 13 | default = [] 14 | 15 | [dependencies] 16 | rustc-hash = { version = "1.1.0" } 17 | tracing = "0.1" 18 | 19 | chalk-derive = { version = "0.104.0-dev.0", path = "../chalk-derive" } 20 | chalk-ir = { version = "0.104.0-dev.0", path = "../chalk-ir" } 21 | chalk-solve = { version = "0.104.0-dev.0", path = "../chalk-solve" } 22 | 23 | [dev-dependencies] 24 | chalk-integration = { path = "../chalk-integration" } 25 | -------------------------------------------------------------------------------- /chalk-engine/README.md: -------------------------------------------------------------------------------- 1 | The core crate for Chalk. 2 | 3 | See [Github](https://github.com/rust-lang/chalk) for up-to-date information. 4 | -------------------------------------------------------------------------------- /chalk-engine/src/context.rs: -------------------------------------------------------------------------------- 1 | //! Defines traits used to embed the chalk-engine in another crate. 2 | //! 3 | //! chalk and rustc both define types which implement the traits in this 4 | //! module. This allows each user of chalk-engine to define their own 5 | //! `DomainGoal` type, add arena lifetime parameters, and more. See 6 | //! [`Context`] trait for a list of types. 7 | 8 | use crate::CompleteAnswer; 9 | use chalk_ir::interner::Interner; 10 | use chalk_ir::Substitution; 11 | use std::fmt::Debug; 12 | 13 | pub enum AnswerResult { 14 | /// The next available answer. 15 | Answer(CompleteAnswer), 16 | 17 | /// No answer could be returned because there are no more solutions. 18 | NoMoreSolutions, 19 | 20 | /// No answer could be returned because the goal has floundered. 21 | Floundered, 22 | 23 | // No answer could be returned *yet*, because we exceeded our 24 | // quantum (`should_continue` returned false). 25 | QuantumExceeded, 26 | } 27 | 28 | impl AnswerResult { 29 | pub fn is_answer(&self) -> bool { 30 | matches!(self, Self::Answer(_)) 31 | } 32 | 33 | pub fn answer(self) -> CompleteAnswer { 34 | match self { 35 | Self::Answer(answer) => answer, 36 | _ => panic!("Not an answer."), 37 | } 38 | } 39 | 40 | pub fn is_no_more_solutions(&self) -> bool { 41 | matches!(self, Self::NoMoreSolutions) 42 | } 43 | 44 | pub fn is_quantum_exceeded(&self) -> bool { 45 | matches!(self, Self::QuantumExceeded) 46 | } 47 | } 48 | 49 | impl Debug for AnswerResult { 50 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 51 | match self { 52 | AnswerResult::Answer(answer) => write!(fmt, "{:?}", answer), 53 | AnswerResult::Floundered => write!(fmt, "Floundered"), 54 | AnswerResult::NoMoreSolutions => write!(fmt, "None"), 55 | AnswerResult::QuantumExceeded => write!(fmt, "QuantumExceeded"), 56 | } 57 | } 58 | } 59 | 60 | pub trait AnswerStream { 61 | /// Gets the next answer for a given goal, but doesn't increment the answer index. 62 | /// Calling this or `next_answer` again will give the same answer. 63 | fn peek_answer(&mut self, should_continue: impl Fn() -> bool) -> AnswerResult; 64 | 65 | /// Gets the next answer for a given goal, incrementing the answer index. 66 | /// Calling this or `peek_answer` again will give the next answer. 67 | fn next_answer(&mut self, should_continue: impl Fn() -> bool) -> AnswerResult; 68 | 69 | /// Invokes `test` with each possible future answer, returning true immediately 70 | /// if we find any answer for which `test` returns true. 71 | fn any_future_answer(&self, test: impl Fn(&Substitution) -> bool) -> bool; 72 | } 73 | -------------------------------------------------------------------------------- /chalk-engine/src/derived.rs: -------------------------------------------------------------------------------- 1 | // These impls for PartialEq, Eq, etc are written by hand. This is 2 | // because the `#[derive()]` would add requirements onto the context 3 | // object that are not needed. 4 | 5 | use super::*; 6 | use std::cmp::{Eq, PartialEq}; 7 | use std::hash::{Hash, Hasher}; 8 | use std::mem; 9 | 10 | /////////////////////////////////////////////////////////////////////////// 11 | 12 | impl PartialEq for Literal { 13 | fn eq(&self, other: &Literal) -> bool { 14 | match (self, other) { 15 | (Literal::Positive(goal1), Literal::Positive(goal2)) 16 | | (Literal::Negative(goal1), Literal::Negative(goal2)) => goal1 == goal2, 17 | 18 | _ => false, 19 | } 20 | } 21 | } 22 | 23 | impl Eq for Literal {} 24 | 25 | impl Hash for Literal { 26 | fn hash(&self, state: &mut H) { 27 | mem::discriminant(self).hash(state); 28 | match self { 29 | Literal::Positive(goal) | Literal::Negative(goal) => { 30 | goal.hash(state); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chalk-engine/src/forest.rs: -------------------------------------------------------------------------------- 1 | use crate::context::{AnswerResult, AnswerStream}; 2 | use crate::logic::RootSearchFail; 3 | use crate::slg::SlgContextOps; 4 | use crate::table::AnswerIndex; 5 | use crate::tables::Tables; 6 | use crate::{TableIndex, TimeStamp}; 7 | 8 | use chalk_ir::interner::Interner; 9 | use chalk_ir::{Goal, InEnvironment, Substitution, UCanonical}; 10 | use tracing::debug; 11 | 12 | pub(crate) struct Forest { 13 | pub(crate) tables: Tables, 14 | 15 | /// This is a clock which always increases. It is 16 | /// incremented every time a new subgoal is followed. 17 | /// This effectively gives us way to track what depth 18 | /// and loop a table or strand was last followed. 19 | pub(crate) clock: TimeStamp, 20 | } 21 | 22 | impl Forest { 23 | pub fn new() -> Self { 24 | Forest { 25 | tables: Tables::new(), 26 | clock: TimeStamp::default(), 27 | } 28 | } 29 | 30 | // Gets the next clock TimeStamp. This will never decrease. 31 | pub(crate) fn increment_clock(&mut self) -> TimeStamp { 32 | self.clock.increment(); 33 | self.clock 34 | } 35 | 36 | /// Returns a "solver" for a given goal in the form of an 37 | /// iterator. Each time you invoke `next`, it will do the work to 38 | /// extract one more answer. These answers are cached in between 39 | /// invocations. Invoking `next` fewer times is preferable =) 40 | pub fn iter_answers<'f>( 41 | &'f mut self, 42 | context: &'f SlgContextOps<'f, I>, 43 | goal: &UCanonical>>, 44 | ) -> impl AnswerStream + 'f { 45 | let table = self.get_or_create_table_for_ucanonical_goal(context, goal.clone()); 46 | let answer = AnswerIndex::ZERO; 47 | ForestSolver { 48 | forest: self, 49 | context, 50 | table, 51 | answer, 52 | } 53 | } 54 | } 55 | 56 | struct ForestSolver<'me, I: Interner> { 57 | forest: &'me mut Forest, 58 | context: &'me SlgContextOps<'me, I>, 59 | table: TableIndex, 60 | answer: AnswerIndex, 61 | } 62 | 63 | impl<'me, I: Interner> AnswerStream for ForestSolver<'me, I> { 64 | /// # Panics 65 | /// 66 | /// Panics if a negative cycle was detected. 67 | fn peek_answer(&mut self, should_continue: impl Fn() -> bool) -> AnswerResult { 68 | loop { 69 | match self 70 | .forest 71 | .root_answer(self.context, self.table, self.answer) 72 | { 73 | Ok(answer) => { 74 | debug!(answer = ?(&answer)); 75 | return AnswerResult::Answer(answer); 76 | } 77 | 78 | Err(RootSearchFail::InvalidAnswer) => { 79 | self.answer.increment(); 80 | } 81 | Err(RootSearchFail::Floundered) => { 82 | return AnswerResult::Floundered; 83 | } 84 | 85 | Err(RootSearchFail::NoMoreSolutions) => { 86 | return AnswerResult::NoMoreSolutions; 87 | } 88 | 89 | Err(RootSearchFail::QuantumExceeded) => { 90 | if !should_continue() { 91 | return AnswerResult::QuantumExceeded; 92 | } 93 | } 94 | 95 | Err(RootSearchFail::NegativeCycle) => { 96 | // Negative cycles *ought* to be avoided by construction. Hence panic 97 | // if we find one, as that likely indicates a problem in the chalk-solve 98 | // lowering rules. (In principle, we could propagate this error out, 99 | // and let chalk-solve do the asserting, but that seemed like it would 100 | // complicate the function signature more than it's worth.) 101 | panic!("negative cycle was detected"); 102 | } 103 | } 104 | } 105 | } 106 | 107 | fn next_answer(&mut self, should_continue: impl Fn() -> bool) -> AnswerResult { 108 | let answer = self.peek_answer(should_continue); 109 | self.answer.increment(); 110 | answer 111 | } 112 | 113 | fn any_future_answer(&self, test: impl Fn(&Substitution) -> bool) -> bool { 114 | self.forest.any_future_answer(self.table, self.answer, test) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /chalk-engine/src/solve.rs: -------------------------------------------------------------------------------- 1 | use crate::context::{AnswerResult, AnswerStream}; 2 | use crate::forest::Forest; 3 | use crate::slg::aggregate::AggregateOps; 4 | use crate::slg::SlgContextOps; 5 | use chalk_ir::interner::Interner; 6 | use chalk_ir::{Canonical, ConstrainedSubst, Goal, InEnvironment, UCanonical}; 7 | use chalk_solve::{RustIrDatabase, Solution, Solver, SubstitutionResult}; 8 | 9 | use std::fmt; 10 | 11 | pub struct SLGSolver { 12 | pub(crate) forest: Forest, 13 | pub(crate) max_size: usize, 14 | pub(crate) expected_answers: Option, 15 | } 16 | 17 | impl SLGSolver { 18 | pub fn new(max_size: usize, expected_answers: Option) -> Self { 19 | Self { 20 | forest: Forest::new(), 21 | max_size, 22 | expected_answers, 23 | } 24 | } 25 | } 26 | 27 | impl fmt::Debug for SLGSolver { 28 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 29 | write!(fmt, "SLGSolver") 30 | } 31 | } 32 | 33 | impl Solver for SLGSolver { 34 | fn solve( 35 | &mut self, 36 | program: &dyn RustIrDatabase, 37 | goal: &UCanonical>>, 38 | ) -> Option> { 39 | let ops = SlgContextOps::new(program, self.max_size, self.expected_answers); 40 | ops.make_solution(goal, self.forest.iter_answers(&ops, goal), || true) 41 | } 42 | 43 | fn solve_limited( 44 | &mut self, 45 | program: &dyn RustIrDatabase, 46 | goal: &UCanonical>>, 47 | should_continue: &dyn std::ops::Fn() -> bool, 48 | ) -> Option> { 49 | let ops = SlgContextOps::new(program, self.max_size, self.expected_answers); 50 | ops.make_solution(goal, self.forest.iter_answers(&ops, goal), should_continue) 51 | } 52 | 53 | fn solve_multiple( 54 | &mut self, 55 | program: &dyn RustIrDatabase, 56 | goal: &UCanonical>>, 57 | f: &mut dyn FnMut(SubstitutionResult>>, bool) -> bool, 58 | ) -> bool { 59 | let ops = SlgContextOps::new(program, self.max_size, self.expected_answers); 60 | let mut answers = self.forest.iter_answers(&ops, goal); 61 | loop { 62 | let subst = match answers.next_answer(|| true) { 63 | AnswerResult::Answer(answer) => { 64 | if !answer.ambiguous { 65 | SubstitutionResult::Definite(answer.subst) 66 | } else if answer 67 | .subst 68 | .value 69 | .subst 70 | .is_identity_subst(ops.program().interner()) 71 | { 72 | SubstitutionResult::Floundered 73 | } else { 74 | SubstitutionResult::Ambiguous(answer.subst) 75 | } 76 | } 77 | AnswerResult::Floundered => SubstitutionResult::Floundered, 78 | AnswerResult::NoMoreSolutions => { 79 | return true; 80 | } 81 | AnswerResult::QuantumExceeded => continue, 82 | }; 83 | 84 | if !f(subst, !answers.peek_answer(|| true).is_no_more_solutions()) { 85 | return false; 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /chalk-engine/src/strand.rs: -------------------------------------------------------------------------------- 1 | use crate::table::AnswerIndex; 2 | use crate::{ExClause, TableIndex, TimeStamp}; 3 | use std::fmt::Debug; 4 | 5 | use chalk_derive::HasInterner; 6 | use chalk_ir::fold::{FallibleTypeFolder, TypeFoldable}; 7 | use chalk_ir::interner::Interner; 8 | use chalk_ir::{Canonical, DebruijnIndex, UniverseMap}; 9 | 10 | #[derive(Clone, Debug, HasInterner)] 11 | pub(crate) struct Strand { 12 | pub(super) ex_clause: ExClause, 13 | 14 | /// Index into `ex_clause.subgoals`. 15 | pub(crate) selected_subgoal: Option, 16 | 17 | pub(crate) last_pursued_time: TimeStamp, 18 | } 19 | 20 | pub(crate) type CanonicalStrand = Canonical>; 21 | 22 | #[derive(Clone, Debug)] 23 | pub(crate) struct SelectedSubgoal { 24 | /// The index of the subgoal in `ex_clause.subgoals` 25 | pub(crate) subgoal_index: usize, 26 | 27 | /// The index of the table that we created or found for this subgoal 28 | pub(super) subgoal_table: TableIndex, 29 | 30 | /// Index of the answer we should request next from the table 31 | pub(crate) answer_index: AnswerIndex, 32 | 33 | /// Maps the universes of the subgoal to the canonical universes 34 | /// used in the table 35 | pub(crate) universe_map: UniverseMap, 36 | } 37 | 38 | impl TypeFoldable for Strand { 39 | fn try_fold_with( 40 | self, 41 | folder: &mut dyn FallibleTypeFolder, 42 | outer_binder: DebruijnIndex, 43 | ) -> Result { 44 | Ok(Strand { 45 | ex_clause: self.ex_clause.try_fold_with(folder, outer_binder)?, 46 | last_pursued_time: self.last_pursued_time, 47 | selected_subgoal: self.selected_subgoal, 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /chalk-engine/src/tables.rs: -------------------------------------------------------------------------------- 1 | use crate::table::Table; 2 | use crate::TableIndex; 3 | use rustc_hash::FxHashMap; 4 | use std::ops::{Index, IndexMut}; 5 | 6 | use chalk_ir::interner::Interner; 7 | use chalk_ir::{Goal, InEnvironment, UCanonical}; 8 | 9 | /// See `Forest`. 10 | #[derive(Debug)] 11 | pub(crate) struct Tables { 12 | /// Maps from a canonical goal to the index of its table. 13 | table_indices: FxHashMap>>, TableIndex>, 14 | 15 | /// Table: as described above, stores the key information for each 16 | /// tree in the forest. 17 | tables: Vec>, 18 | } 19 | 20 | impl Tables { 21 | pub(crate) fn new() -> Tables { 22 | Tables { 23 | table_indices: FxHashMap::default(), 24 | tables: Vec::default(), 25 | } 26 | } 27 | 28 | /// The index that will be given to the next table to be inserted. 29 | pub(super) fn next_index(&self) -> TableIndex { 30 | TableIndex { 31 | value: self.tables.len(), 32 | } 33 | } 34 | 35 | pub(super) fn insert(&mut self, table: Table) -> TableIndex { 36 | let goal = table.table_goal.clone(); 37 | let index = self.next_index(); 38 | self.tables.push(table); 39 | self.table_indices.insert(goal, index); 40 | index 41 | } 42 | 43 | pub(super) fn index_of( 44 | &self, 45 | literal: &UCanonical>>, 46 | ) -> Option { 47 | self.table_indices.get(literal).cloned() 48 | } 49 | } 50 | 51 | impl Index for Tables { 52 | type Output = Table; 53 | 54 | fn index(&self, index: TableIndex) -> &Table { 55 | &self.tables[index.value] 56 | } 57 | } 58 | 59 | impl IndexMut for Tables { 60 | fn index_mut(&mut self, index: TableIndex) -> &mut Table { 61 | &mut self.tables[index.value] 62 | } 63 | } 64 | 65 | impl<'a, I: Interner> IntoIterator for &'a mut Tables { 66 | type IntoIter = <&'a mut Vec> as IntoIterator>::IntoIter; 67 | type Item = <&'a mut Vec> as IntoIterator>::Item; 68 | 69 | fn into_iter(self) -> Self::IntoIter { 70 | IntoIterator::into_iter(&mut self.tables) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /chalk-integration/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chalk-integration" 3 | version = "0.104.0-dev.0" 4 | license = "MIT OR Apache-2.0" 5 | description = "Sample solver setup for Chalk" 6 | authors = ["Rust Compiler Team", "Chalk developers"] 7 | repository = "https://github.com/rust-lang/chalk" 8 | keywords = ["compiler", "traits", "prolog"] 9 | edition = "2018" 10 | publish = false 11 | 12 | [dependencies] 13 | string_cache = "0.8.0" 14 | salsa = "0.16.0" 15 | tracing = "0.1" 16 | 17 | chalk-derive = { version = "0.104.0-dev.0", path = "../chalk-derive" } 18 | chalk-ir = { version = "0.104.0-dev.0", path = "../chalk-ir" } 19 | chalk-solve = { version = "0.104.0-dev.0", path = "../chalk-solve" } 20 | chalk-recursive = { version = "0.104.0-dev.0", path = "../chalk-recursive" } 21 | chalk-engine = { version = "0.104.0-dev.0", path = "../chalk-engine" } 22 | chalk-parse = { version = "0.104.0-dev.0", path = "../chalk-parse" } 23 | indexmap = "2" 24 | -------------------------------------------------------------------------------- /chalk-integration/README.md: -------------------------------------------------------------------------------- 1 | A library that takes AST from `chalk-parse` and uses it to drive `chalk-solve`. 2 | 3 | See [Github](https://github.com/rust-lang/chalk) for up-to-date information. 4 | -------------------------------------------------------------------------------- /chalk-integration/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "1024"] 2 | #![cfg_attr(feature = "bench", feature(test))] 3 | 4 | pub mod db; 5 | pub mod error; 6 | pub mod interner; 7 | pub mod lowering; 8 | pub mod program; 9 | pub mod program_environment; 10 | pub mod query; 11 | pub mod test_macros; 12 | pub mod tls; 13 | 14 | use chalk_engine::solve::SLGSolver; 15 | use chalk_ir::interner::HasInterner; 16 | use chalk_ir::Binders; 17 | use chalk_recursive::{Cache, RecursiveSolver}; 18 | use chalk_solve::Solver; 19 | use interner::ChalkIr; 20 | 21 | pub use interner::{Identifier, RawId}; 22 | 23 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 24 | pub enum TypeSort { 25 | Adt, 26 | FnDef, 27 | Closure, 28 | Trait, 29 | Opaque, 30 | Coroutine, 31 | } 32 | 33 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 34 | pub struct Unit; 35 | 36 | impl HasInterner for Unit { 37 | type Interner = ChalkIr; 38 | } 39 | 40 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] 41 | pub struct TypeKind { 42 | pub sort: TypeSort, 43 | pub name: Identifier, 44 | pub binders: Binders, 45 | } 46 | 47 | #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] 48 | pub enum SolverChoice { 49 | /// Run the SLG solver, producing a Solution. 50 | SLG { 51 | max_size: usize, 52 | expected_answers: Option, 53 | }, 54 | /// Run the recursive solver. 55 | Recursive { 56 | overflow_depth: usize, 57 | caching_enabled: bool, 58 | max_size: usize, 59 | }, 60 | } 61 | 62 | impl SolverChoice { 63 | /// Returns specific SLG parameters. 64 | pub fn slg(max_size: usize, expected_answers: Option) -> Self { 65 | SolverChoice::SLG { 66 | max_size, 67 | expected_answers, 68 | } 69 | } 70 | 71 | /// Returns the default SLG parameters. 72 | pub fn slg_default() -> Self { 73 | SolverChoice::slg(10, None) 74 | } 75 | 76 | /// Returns the default recursive solver setup. 77 | pub fn recursive_default() -> Self { 78 | SolverChoice::Recursive { 79 | overflow_depth: 100, 80 | caching_enabled: true, 81 | max_size: 30, 82 | } 83 | } 84 | 85 | /// Returns a recursive solver with specific parameters. 86 | pub fn recursive(max_size: usize, overflow_depth: usize) -> Self { 87 | SolverChoice::Recursive { 88 | overflow_depth, 89 | caching_enabled: true, 90 | max_size, 91 | } 92 | } 93 | 94 | pub fn into_solver(self) -> Box> { 95 | match self { 96 | SolverChoice::SLG { 97 | max_size, 98 | expected_answers, 99 | } => Box::new(SLGSolver::new(max_size, expected_answers)), 100 | SolverChoice::Recursive { 101 | overflow_depth, 102 | caching_enabled, 103 | max_size, 104 | } => Box::new(RecursiveSolver::new( 105 | overflow_depth, 106 | max_size, 107 | if caching_enabled { 108 | Some(Cache::default()) 109 | } else { 110 | None 111 | }, 112 | )), 113 | } 114 | } 115 | } 116 | 117 | impl Default for SolverChoice { 118 | fn default() -> Self { 119 | SolverChoice::slg(10, None) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /chalk-integration/src/program_environment.rs: -------------------------------------------------------------------------------- 1 | use crate::interner::ChalkIr; 2 | use chalk_ir::ProgramClause; 3 | 4 | #[derive(Clone, Debug, PartialEq, Eq)] 5 | pub struct ProgramEnvironment { 6 | /// Compiled forms of the above: 7 | pub program_clauses: Vec>, 8 | } 9 | 10 | impl ProgramEnvironment { 11 | pub fn new(program_clauses: Vec>) -> Self { 12 | Self { program_clauses } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chalk-ir/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chalk-ir" 3 | version = "0.104.0-dev.0" 4 | description = "Chalk's internal representation of types, goals, and clauses" 5 | license = "MIT OR Apache-2.0" 6 | authors = ["Rust Compiler Team", "Chalk developers"] 7 | repository = "https://github.com/rust-lang/chalk" 8 | readme = "README.md" 9 | keywords = ["compiler", "traits", "prolog"] 10 | edition = "2018" 11 | 12 | [dependencies] 13 | bitflags = "2.4.1" 14 | chalk-derive = { version = "0.104.0-dev.0", path = "../chalk-derive" } 15 | -------------------------------------------------------------------------------- /chalk-ir/README.md: -------------------------------------------------------------------------------- 1 | A rust type library for chalk. 2 | 3 | See [Github](https://github.com/rust-lang/chalk) for up-to-date information. 4 | -------------------------------------------------------------------------------- /chalk-ir/src/fold/binder_impls.rs: -------------------------------------------------------------------------------- 1 | //! This module contains impls of `TypeFoldable` for those types that 2 | //! introduce binders. 3 | //! 4 | //! The more interesting impls of `TypeFoldable` remain in the `fold` module. 5 | 6 | use crate::*; 7 | 8 | impl TypeFoldable for FnPointer { 9 | fn try_fold_with( 10 | self, 11 | folder: &mut dyn FallibleTypeFolder, 12 | outer_binder: DebruijnIndex, 13 | ) -> Result { 14 | let FnPointer { 15 | num_binders, 16 | substitution, 17 | sig, 18 | } = self; 19 | Ok(FnPointer { 20 | num_binders, 21 | substitution: substitution.try_fold_with(folder, outer_binder.shifted_in())?, 22 | sig: FnSig { 23 | abi: sig.abi, 24 | safety: sig.safety, 25 | variadic: sig.variadic, 26 | }, 27 | }) 28 | } 29 | } 30 | 31 | impl TypeFoldable for Binders 32 | where 33 | T: HasInterner + TypeFoldable, 34 | I: Interner, 35 | { 36 | fn try_fold_with( 37 | self, 38 | folder: &mut dyn FallibleTypeFolder, 39 | outer_binder: DebruijnIndex, 40 | ) -> Result { 41 | let Binders { 42 | binders: self_binders, 43 | value: self_value, 44 | } = self; 45 | let value = self_value.try_fold_with(folder, outer_binder.shifted_in())?; 46 | let binders = VariableKinds { 47 | interned: self_binders.interned().clone(), 48 | }; 49 | Ok(Binders::new(binders, value)) 50 | } 51 | } 52 | 53 | impl TypeFoldable for Canonical 54 | where 55 | I: Interner, 56 | T: HasInterner + TypeFoldable, 57 | { 58 | fn try_fold_with( 59 | self, 60 | folder: &mut dyn FallibleTypeFolder, 61 | outer_binder: DebruijnIndex, 62 | ) -> Result { 63 | let Canonical { 64 | binders: self_binders, 65 | value: self_value, 66 | } = self; 67 | let value = self_value.try_fold_with(folder, outer_binder.shifted_in())?; 68 | let binders = CanonicalVarKinds { 69 | interned: self_binders.interned().clone(), 70 | }; 71 | Ok(Canonical { binders, value }) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /chalk-ir/src/visit/binder_impls.rs: -------------------------------------------------------------------------------- 1 | //! This module contains impls of `TypeVisitable` for those types that 2 | //! introduce binders. 3 | //! 4 | //! The more interesting impls of `TypeVisitable` remain in the `visit` module. 5 | 6 | use crate::interner::HasInterner; 7 | use crate::{ 8 | Binders, Canonical, ControlFlow, DebruijnIndex, FnPointer, Interner, TypeVisitable, TypeVisitor, 9 | }; 10 | 11 | impl TypeVisitable for FnPointer { 12 | fn visit_with( 13 | &self, 14 | visitor: &mut dyn TypeVisitor, 15 | outer_binder: DebruijnIndex, 16 | ) -> ControlFlow { 17 | self.substitution 18 | .visit_with(visitor, outer_binder.shifted_in()) 19 | } 20 | } 21 | 22 | impl TypeVisitable for Binders 23 | where 24 | T: HasInterner + TypeVisitable, 25 | { 26 | fn visit_with( 27 | &self, 28 | visitor: &mut dyn TypeVisitor, 29 | outer_binder: DebruijnIndex, 30 | ) -> ControlFlow { 31 | self.value.visit_with(visitor, outer_binder.shifted_in()) 32 | } 33 | } 34 | 35 | impl TypeVisitable for Canonical 36 | where 37 | I: Interner, 38 | T: HasInterner + TypeVisitable, 39 | { 40 | fn visit_with( 41 | &self, 42 | visitor: &mut dyn TypeVisitor, 43 | outer_binder: DebruijnIndex, 44 | ) -> ControlFlow { 45 | self.value.visit_with(visitor, outer_binder.shifted_in()) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /chalk-ir/src/visit/visitors.rs: -------------------------------------------------------------------------------- 1 | //! TypeVisitor helpers 2 | 3 | use crate::{BoundVar, ControlFlow, DebruijnIndex, Interner, TypeVisitable, TypeVisitor}; 4 | 5 | /// TypeVisitor extensions. 6 | pub trait VisitExt: TypeVisitable { 7 | /// Check whether there are free (non-bound) variables. 8 | fn has_free_vars(&self, interner: I) -> bool { 9 | let flow = self.visit_with( 10 | &mut FindFreeVarsVisitor { interner }, 11 | DebruijnIndex::INNERMOST, 12 | ); 13 | matches!(flow, ControlFlow::Break(_)) 14 | } 15 | } 16 | 17 | impl VisitExt for T where T: TypeVisitable {} 18 | 19 | struct FindFreeVarsVisitor { 20 | interner: I, 21 | } 22 | 23 | impl TypeVisitor for FindFreeVarsVisitor { 24 | type BreakTy = (); 25 | 26 | fn as_dyn(&mut self) -> &mut dyn TypeVisitor { 27 | self 28 | } 29 | 30 | fn interner(&self) -> I { 31 | self.interner 32 | } 33 | 34 | fn visit_free_var( 35 | &mut self, 36 | _bound_var: BoundVar, 37 | _outer_binder: DebruijnIndex, 38 | ) -> ControlFlow<()> { 39 | ControlFlow::Break(()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /chalk-parse/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chalk-parse" 3 | version = "0.104.0-dev.0" 4 | description = "Parser for the Chalk project" 5 | license = "MIT OR Apache-2.0" 6 | authors = ["Rust Compiler Team", "Chalk developers"] 7 | repository = "https://github.com/rust-lang/chalk" 8 | readme = "README.md" 9 | keywords = ["compiler", "traits", "prolog"] 10 | build = "build.rs" # LALRPOP preprocessing 11 | edition = "2018" 12 | publish = false 13 | 14 | [build-dependencies.lalrpop] 15 | version = "0.20" 16 | features = ["lexer"] 17 | 18 | [dependencies] 19 | lalrpop-util = "0.20" 20 | regex = "1.5" 21 | string_cache = "0.8.0" 22 | -------------------------------------------------------------------------------- /chalk-parse/README.md: -------------------------------------------------------------------------------- 1 | Parser for the Chalk standalone trait system implementation. 2 | 3 | See [Github](https://github.com/rust-lang/chalk) for up-to-date information. 4 | -------------------------------------------------------------------------------- /chalk-parse/build.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop; 2 | 3 | fn main() { 4 | lalrpop::process_root().unwrap(); 5 | } 6 | -------------------------------------------------------------------------------- /chalk-parse/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "1024"] 2 | #![allow(unused_parens)] 3 | 4 | #[macro_use] 5 | extern crate lalrpop_util; 6 | 7 | pub mod ast; 8 | #[rustfmt::skip] 9 | lalrpop_mod!(pub parser); 10 | 11 | use lalrpop_util::ParseError; 12 | 13 | type Result = std::result::Result>; 14 | 15 | pub fn parse_program(text: &str) -> Result { 16 | parser::ProgramParser::new() 17 | .parse(text) 18 | .map_err(|e| format!("parse error: {}", e).into()) 19 | } 20 | 21 | pub fn parse_ty(text: &str) -> Result { 22 | parser::TyParser::new() 23 | .parse(text) 24 | .map_err(|e| format!("error parsing `{}`: {}", text, e).into()) 25 | } 26 | 27 | pub fn parse_goal(text: &str) -> Result> { 28 | parser::GoalParser::new().parse(text).map_err(|e| { 29 | let mut output = format!("parse error: {}", &e); 30 | if let Some(s) = match e { 31 | ParseError::InvalidToken { location } => { 32 | Some(position_string(text, location, location + 1)) 33 | } 34 | ParseError::UnrecognizedToken { 35 | token: (start, _, end), 36 | .. 37 | } => Some(position_string(text, start, end)), 38 | ParseError::ExtraToken { 39 | token: (start, _, end), 40 | .. 41 | } => Some(position_string(text, start, end)), 42 | _ => None, 43 | } { 44 | output.push('\n'); 45 | output += &s; 46 | } 47 | output.into() 48 | }) 49 | } 50 | 51 | fn position_string(text: &str, start: usize, end: usize) -> String { 52 | let text = text.replace('\n', " ").replace('\r', " "); 53 | let mut output = format!("position: `{}`", text); 54 | output += &" ".repeat(11 + start); 55 | output += &"^".repeat(end - start); 56 | output.push('\n'); 57 | output 58 | } 59 | -------------------------------------------------------------------------------- /chalk-recursive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chalk-recursive" 3 | version = "0.104.0-dev.0" 4 | description = "Recursive solver for the Chalk project" 5 | license = "MIT OR Apache-2.0" 6 | authors = ["Rust Compiler Team", "Chalk developers"] 7 | repository = "https://github.com/rust-lang/chalk" 8 | readme = "README.md" 9 | keywords = ["compiler", "traits", "prolog"] 10 | edition = "2018" 11 | 12 | [dependencies] 13 | rustc-hash = { version = "1.1.0" } 14 | tracing = "0.1" 15 | 16 | chalk-derive = { version = "0.104.0-dev.0", path = "../chalk-derive" } 17 | chalk-ir = { version = "0.104.0-dev.0", path = "../chalk-ir" } 18 | chalk-solve = { version = "0.104.0-dev.0", path = "../chalk-solve", default-features = false } 19 | 20 | [dev-dependencies] 21 | chalk-integration = { path = "../chalk-integration" } 22 | 23 | [features] 24 | default = ["tracing-full"] 25 | 26 | tracing-full = ["chalk-solve/tracing-full"] 27 | -------------------------------------------------------------------------------- /chalk-recursive/README.md: -------------------------------------------------------------------------------- 1 | A crate housing the recursive solver for chalk. 2 | 3 | See [Github](https://github.com/rust-lang/chalk) for up-to-date information. 4 | -------------------------------------------------------------------------------- /chalk-recursive/src/combine.rs: -------------------------------------------------------------------------------- 1 | use chalk_solve::Solution; 2 | use tracing::debug; 3 | 4 | use chalk_ir::interner::Interner; 5 | use chalk_ir::{ClausePriority, DomainGoal, GenericArg}; 6 | 7 | #[tracing::instrument(level = "Debug", skip(interner))] 8 | pub(super) fn with_priorities( 9 | interner: I, 10 | domain_goal: &DomainGoal, 11 | a: Solution, 12 | prio_a: ClausePriority, 13 | b: Solution, 14 | prio_b: ClausePriority, 15 | ) -> (Solution, ClausePriority) { 16 | let result = match (prio_a, prio_b, a, b) { 17 | (ClausePriority::High, ClausePriority::Low, higher, lower) 18 | | (ClausePriority::Low, ClausePriority::High, lower, higher) => { 19 | // if we have a high-priority solution and a low-priority solution, 20 | // the high-priority solution overrides *if* they are both for the 21 | // same inputs -- we don't want a more specific high-priority 22 | // solution overriding a general low-priority one. Currently inputs 23 | // only matter for projections; in a goal like `AliasEq(::Type = ?1)`, ?0 is the input. 25 | let inputs_higher = calculate_inputs(interner, domain_goal, &higher); 26 | let inputs_lower = calculate_inputs(interner, domain_goal, &lower); 27 | if inputs_higher == inputs_lower { 28 | debug!( 29 | "preferring solution: {:?} over {:?} because of higher prio", 30 | higher, lower 31 | ); 32 | (higher, ClausePriority::High) 33 | } else { 34 | (higher.combine(lower, interner), ClausePriority::High) 35 | } 36 | } 37 | (_, _, a, b) => (a.combine(b, interner), prio_a), 38 | }; 39 | debug!(?result, "combined result"); 40 | result 41 | } 42 | 43 | fn calculate_inputs( 44 | interner: I, 45 | domain_goal: &DomainGoal, 46 | solution: &Solution, 47 | ) -> Vec> { 48 | if let Some(subst) = solution.constrained_subst(interner) { 49 | let subst_goal = subst.value.subst.apply(domain_goal.clone(), interner); 50 | subst_goal.inputs(interner) 51 | } else { 52 | domain_goal.inputs(interner) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /chalk-recursive/src/fixed_point/cache.rs: -------------------------------------------------------------------------------- 1 | use rustc_hash::FxHashMap; 2 | use std::fmt::Debug; 3 | use std::hash::Hash; 4 | use std::sync::{Arc, Mutex}; 5 | use tracing::debug; 6 | use tracing::instrument; 7 | /// The "cache" stores results for goals that we have completely solved. 8 | /// Things are added to the cache when we have completely processed their 9 | /// result, and it can be shared amongst many solvers. 10 | pub struct Cache 11 | where 12 | K: Hash + Eq + Debug, 13 | V: Debug + Clone, 14 | { 15 | data: Arc>>, 16 | } 17 | struct CacheData 18 | where 19 | K: Hash + Eq + Debug, 20 | V: Debug + Clone, 21 | { 22 | cache: FxHashMap, 23 | } 24 | 25 | impl Cache 26 | where 27 | K: Hash + Eq + Debug, 28 | V: Debug + Clone, 29 | { 30 | pub fn new() -> Self { 31 | Self::default() 32 | } 33 | 34 | /// Record a cache result. 35 | #[instrument(skip(self))] 36 | pub fn insert(&self, goal: K, result: V) { 37 | let mut data = self.data.lock().unwrap(); 38 | data.cache.insert(goal, result); 39 | } 40 | 41 | /// Record a cache result. 42 | pub fn get(&self, goal: &K) -> Option { 43 | let data = self.data.lock().unwrap(); 44 | if let Some(result) = data.cache.get(goal) { 45 | debug!(?goal, ?result, "Cache hit"); 46 | Some(result.clone()) 47 | } else { 48 | debug!(?goal, "Cache miss"); 49 | None 50 | } 51 | } 52 | } 53 | 54 | impl Clone for Cache 55 | where 56 | K: Hash + Eq + Debug, 57 | V: Debug + Clone, 58 | { 59 | fn clone(&self) -> Self { 60 | Self { 61 | data: self.data.clone(), 62 | } 63 | } 64 | } 65 | 66 | impl Default for Cache 67 | where 68 | K: Hash + Eq + Debug, 69 | V: Debug + Clone, 70 | { 71 | fn default() -> Self { 72 | Self { 73 | data: Default::default(), 74 | } 75 | } 76 | } 77 | 78 | impl Default for CacheData 79 | where 80 | K: Hash + Eq + Debug, 81 | V: Debug + Clone, 82 | { 83 | fn default() -> Self { 84 | Self { 85 | cache: Default::default(), 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /chalk-recursive/src/fixed_point/stack.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ops::Index; 3 | use std::ops::IndexMut; 4 | use std::usize; 5 | 6 | pub(super) struct Stack { 7 | // program: Arc, 8 | entries: Vec, 9 | overflow_depth: usize, 10 | } 11 | 12 | #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] 13 | pub(super) struct StackDepth { 14 | depth: usize, 15 | } 16 | 17 | /// The data we actively keep for each goal on the stack. 18 | pub(super) struct StackEntry { 19 | /// Was this a coinductive goal? 20 | coinductive_goal: bool, 21 | 22 | /// Initially false, set to true when some subgoal depends on us. 23 | cycle: bool, 24 | } 25 | 26 | impl Stack { 27 | pub(super) fn new( 28 | // program: &Arc, 29 | overflow_depth: usize, 30 | ) -> Self { 31 | Stack { 32 | // program: program.clone(), 33 | entries: vec![], 34 | overflow_depth, 35 | } 36 | } 37 | 38 | pub(super) fn is_empty(&self) -> bool { 39 | self.entries.is_empty() 40 | } 41 | 42 | pub(super) fn push(&mut self, coinductive_goal: bool) -> StackDepth { 43 | let depth = StackDepth { 44 | depth: self.entries.len(), 45 | }; 46 | 47 | if depth.depth >= self.overflow_depth { 48 | // This should perhaps be a result or something, though 49 | // really I'd prefer to move to subgoal abstraction for 50 | // guaranteeing termination. -nmatsakis 51 | panic!("overflow depth reached") 52 | } 53 | 54 | self.entries.push(StackEntry { 55 | coinductive_goal, 56 | cycle: false, 57 | }); 58 | depth 59 | } 60 | 61 | pub(super) fn pop(&mut self, depth: StackDepth) { 62 | assert_eq!( 63 | depth.depth + 1, 64 | self.entries.len(), 65 | "mismatched stack push/pop" 66 | ); 67 | self.entries.pop(); 68 | } 69 | 70 | /// True iff there exist at least one coinductive goal 71 | /// and one inductive goal each from the top of the stack 72 | /// down to (and including) the given depth. 73 | pub(super) fn mixed_inductive_coinductive_cycle_from(&self, depth: StackDepth) -> bool { 74 | let coinductive_count = self.entries[depth.depth..] 75 | .iter() 76 | .filter(|entry| entry.coinductive_goal) 77 | .count(); 78 | let total_count = self.entries.len() - depth.depth; 79 | let any_coinductive = coinductive_count != 0; 80 | let any_inductive = coinductive_count != total_count; 81 | any_coinductive && any_inductive 82 | } 83 | } 84 | 85 | impl StackEntry { 86 | pub(super) fn flag_cycle(&mut self) { 87 | self.cycle = true; 88 | } 89 | 90 | pub(super) fn read_and_reset_cycle_flag(&mut self) -> bool { 91 | mem::replace(&mut self.cycle, false) 92 | } 93 | } 94 | 95 | impl Index for Stack { 96 | type Output = StackEntry; 97 | 98 | fn index(&self, depth: StackDepth) -> &StackEntry { 99 | &self.entries[depth.depth] 100 | } 101 | } 102 | 103 | impl IndexMut for Stack { 104 | fn index_mut(&mut self, depth: StackDepth) -> &mut StackEntry { 105 | &mut self.entries[depth.depth] 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /chalk-recursive/src/lib.rs: -------------------------------------------------------------------------------- 1 | use chalk_ir::{Goal, InEnvironment, UCanonical}; 2 | 3 | pub type UCanonicalGoal = UCanonical>>; 4 | 5 | mod combine; 6 | mod fixed_point; 7 | mod fulfill; 8 | mod recursive; 9 | pub mod solve; 10 | 11 | pub use fixed_point::Cache; 12 | pub use recursive::RecursiveSolver; 13 | -------------------------------------------------------------------------------- /chalk-solve/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chalk-solve" 3 | version = "0.104.0-dev.0" 4 | description = "Combines the chalk-engine with chalk-ir" 5 | license = "MIT OR Apache-2.0" 6 | authors = ["Rust Compiler Team", "Chalk developers"] 7 | repository = "https://github.com/rust-lang/chalk" 8 | readme = "README.md" 9 | keywords = ["compiler", "traits", "prolog"] 10 | edition = "2018" 11 | 12 | [dependencies] 13 | ena = "0.14.0" 14 | itertools = "0.12.0" 15 | petgraph = "0.6.4" 16 | tracing = "0.1" 17 | tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter"] } 18 | tracing-tree = { version = "0.3", optional = true } 19 | rustc-hash = { version = "1.1.0" } 20 | 21 | chalk-derive = { version = "0.104.0-dev.0", path = "../chalk-derive" } 22 | chalk-ir = { version = "0.104.0-dev.0", path = "../chalk-ir" } 23 | indexmap = "2" 24 | 25 | [dev-dependencies] 26 | chalk-integration = { path = "../chalk-integration" } 27 | 28 | [features] 29 | default = ["tracing-full"] 30 | 31 | tracing-full = ["tracing-subscriber", "tracing-tree"] 32 | -------------------------------------------------------------------------------- /chalk-solve/README.md: -------------------------------------------------------------------------------- 1 | A library that defines the rules that translates Rust IR to logical predicates. 2 | 3 | See [Github](https://github.com/rust-lang/chalk) for up-to-date information. 4 | -------------------------------------------------------------------------------- /chalk-solve/src/clauses/builtin_traits/clone.rs: -------------------------------------------------------------------------------- 1 | use crate::clauses::ClauseBuilder; 2 | use crate::{Interner, RustIrDatabase, TraitRef}; 3 | use chalk_ir::{CanonicalVarKinds, Floundered, TyKind}; 4 | 5 | use super::copy::add_copy_program_clauses; 6 | 7 | pub fn add_clone_program_clauses( 8 | db: &dyn RustIrDatabase, 9 | builder: &mut ClauseBuilder<'_, I>, 10 | trait_ref: TraitRef, 11 | ty: TyKind, 12 | binders: &CanonicalVarKinds, 13 | ) -> Result<(), Floundered> { 14 | // Implement Clone for types that automatically implement Copy 15 | add_copy_program_clauses(db, builder, trait_ref, ty, binders) 16 | } 17 | -------------------------------------------------------------------------------- /chalk-solve/src/clauses/builtin_traits/copy.rs: -------------------------------------------------------------------------------- 1 | use crate::clauses::builtin_traits::needs_impl_for_tys; 2 | use crate::clauses::ClauseBuilder; 3 | use crate::{Interner, RustIrDatabase, TraitRef}; 4 | use chalk_ir::{CanonicalVarKinds, Floundered, Substitution, TyKind, TyVariableKind, VariableKind}; 5 | use std::iter; 6 | use tracing::instrument; 7 | 8 | fn push_tuple_copy_conditions( 9 | db: &dyn RustIrDatabase, 10 | builder: &mut ClauseBuilder<'_, I>, 11 | trait_ref: TraitRef, 12 | arity: usize, 13 | substitution: &Substitution, 14 | ) { 15 | // Empty tuples are always Copy 16 | if arity == 0 { 17 | builder.push_fact(trait_ref); 18 | return; 19 | } 20 | 21 | let interner = db.interner(); 22 | 23 | needs_impl_for_tys( 24 | db, 25 | builder, 26 | trait_ref, 27 | substitution 28 | .iter(interner) 29 | .map(|param| param.assert_ty_ref(interner).clone()), 30 | ); 31 | } 32 | 33 | #[instrument(skip(db, builder))] 34 | pub fn add_copy_program_clauses( 35 | db: &dyn RustIrDatabase, 36 | builder: &mut ClauseBuilder<'_, I>, 37 | trait_ref: TraitRef, 38 | ty: TyKind, 39 | binders: &CanonicalVarKinds, 40 | ) -> Result<(), Floundered> { 41 | match ty { 42 | TyKind::Tuple(arity, ref substitution) => { 43 | push_tuple_copy_conditions(db, builder, trait_ref, arity, substitution) 44 | } 45 | TyKind::Array(ty, _) => { 46 | needs_impl_for_tys(db, builder, trait_ref, iter::once(ty)); 47 | } 48 | TyKind::FnDef(_, _) => { 49 | builder.push_fact(trait_ref); 50 | } 51 | TyKind::Closure(closure_id, ref substitution) => { 52 | let closure_fn_substitution = db.closure_fn_substitution(closure_id, substitution); 53 | let upvars = db.closure_upvars(closure_id, substitution); 54 | let upvars = upvars.substitute(db.interner(), &closure_fn_substitution); 55 | needs_impl_for_tys(db, builder, trait_ref, Some(upvars).into_iter()); 56 | } 57 | 58 | // these impls are in libcore 59 | TyKind::Ref(_, _, _) 60 | | TyKind::Raw(_, _) 61 | | TyKind::Scalar(_) 62 | | TyKind::Never 63 | | TyKind::Str => {} 64 | 65 | TyKind::Adt(_, _) 66 | | TyKind::AssociatedType(_, _) 67 | | TyKind::Slice(_) 68 | | TyKind::OpaqueType(_, _) 69 | | TyKind::Foreign(_) 70 | | TyKind::Coroutine(_, _) 71 | | TyKind::CoroutineWitness(_, _) 72 | | TyKind::Error => {} 73 | 74 | TyKind::Function(_) => builder.push_fact(trait_ref), 75 | 76 | TyKind::InferenceVar(_, TyVariableKind::Float) 77 | | TyKind::InferenceVar(_, TyVariableKind::Integer) => builder.push_fact(trait_ref), 78 | 79 | TyKind::BoundVar(bound_var) => { 80 | let var_kind = &binders.at(db.interner(), bound_var.index).kind; 81 | match var_kind { 82 | VariableKind::Ty(TyVariableKind::Integer) 83 | | VariableKind::Ty(TyVariableKind::Float) => builder.push_fact(trait_ref), 84 | 85 | // Don't know enough 86 | VariableKind::Ty(TyVariableKind::General) => return Err(Floundered), 87 | 88 | VariableKind::Const(_) | VariableKind::Lifetime => {} 89 | } 90 | } 91 | 92 | // Don't know enough 93 | TyKind::InferenceVar(_, TyVariableKind::General) => return Err(Floundered), 94 | 95 | // These should be handled elsewhere 96 | TyKind::Alias(_) | TyKind::Dyn(_) | TyKind::Placeholder(_) => {} 97 | }; 98 | Ok(()) 99 | } 100 | -------------------------------------------------------------------------------- /chalk-solve/src/clauses/builtin_traits/coroutine.rs: -------------------------------------------------------------------------------- 1 | use crate::clauses::ClauseBuilder; 2 | use crate::rust_ir::WellKnownTrait; 3 | use crate::{Interner, RustIrDatabase, TraitRef}; 4 | use chalk_ir::cast::Cast; 5 | use chalk_ir::{AliasTy, Floundered, Normalize, ProjectionTy, Substitution, Ty, TyKind}; 6 | 7 | /// Add implicit impls of the coroutine trait, i.e., add a clause that all coroutines implement 8 | /// `Coroutine` and clauses for `Coroutine`'s associated types. 9 | pub fn add_coroutine_program_clauses( 10 | db: &dyn RustIrDatabase, 11 | builder: &mut ClauseBuilder<'_, I>, 12 | self_ty: Ty, 13 | ) -> Result<(), Floundered> { 14 | let interner = db.interner(); 15 | 16 | match self_ty.kind(interner) { 17 | TyKind::Coroutine(id, substitution) => { 18 | let coroutine_datum = db.coroutine_datum(*id); 19 | let coroutine_io_datum = coroutine_datum 20 | .input_output 21 | .clone() 22 | .substitute(interner, &substitution); 23 | 24 | let trait_id = db.well_known_trait_id(WellKnownTrait::Coroutine).unwrap(); 25 | let trait_datum = db.trait_datum(trait_id); 26 | assert_eq!( 27 | trait_datum.associated_ty_ids.len(), 28 | 2, 29 | "Coroutine trait should have exactly two associated types, found {:?}", 30 | trait_datum.associated_ty_ids 31 | ); 32 | 33 | let substitution = Substitution::from_iter( 34 | interner, 35 | &[ 36 | self_ty.cast(interner), 37 | coroutine_io_datum.resume_type.cast(interner), 38 | ], 39 | ); 40 | 41 | // coroutine: Coroutine 42 | builder.push_fact(TraitRef { 43 | trait_id, 44 | substitution: substitution.clone(), 45 | }); 46 | 47 | // `Coroutine::Yield` 48 | let yield_id = trait_datum.associated_ty_ids[0]; 49 | let yield_alias = AliasTy::Projection(ProjectionTy { 50 | associated_ty_id: yield_id, 51 | substitution: substitution.clone(), 52 | }); 53 | builder.push_fact(Normalize { 54 | alias: yield_alias, 55 | ty: coroutine_io_datum.yield_type, 56 | }); 57 | 58 | // `Coroutine::Return` 59 | let return_id = trait_datum.associated_ty_ids[1]; 60 | let return_alias = AliasTy::Projection(ProjectionTy { 61 | associated_ty_id: return_id, 62 | substitution, 63 | }); 64 | builder.push_fact(Normalize { 65 | alias: return_alias, 66 | ty: coroutine_io_datum.return_type, 67 | }); 68 | 69 | Ok(()) 70 | } 71 | 72 | // Coroutine trait is non-enumerable 73 | TyKind::InferenceVar(..) | TyKind::BoundVar(_) | TyKind::Alias(..) => Err(Floundered), 74 | _ => Ok(()), 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /chalk-solve/src/clauses/builtin_traits/discriminant_kind.rs: -------------------------------------------------------------------------------- 1 | use crate::clauses::ClauseBuilder; 2 | use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}; 3 | use chalk_ir::{ 4 | AliasTy, Floundered, Normalize, ProjectionTy, Substitution, Ty, TyKind, TyVariableKind, 5 | }; 6 | 7 | pub fn add_discriminant_clauses( 8 | db: &dyn RustIrDatabase, 9 | builder: &mut ClauseBuilder<'_, I>, 10 | self_ty: Ty, 11 | ) -> Result<(), Floundered> { 12 | let interner = db.interner(); 13 | 14 | let can_determine_discriminant = match self_ty.data(interner).kind { 15 | TyKind::Adt(..) 16 | | TyKind::Array(..) 17 | | TyKind::Tuple(..) 18 | | TyKind::Slice(..) 19 | | TyKind::Raw(..) 20 | | TyKind::Ref(..) 21 | | TyKind::Scalar(_) 22 | | TyKind::Str 23 | | TyKind::Never 24 | | TyKind::FnDef(..) 25 | | TyKind::Coroutine(..) 26 | | TyKind::Closure(..) 27 | | TyKind::CoroutineWitness(..) 28 | | TyKind::Foreign(_) 29 | | TyKind::Dyn(_) 30 | | TyKind::Function(..) 31 | | TyKind::InferenceVar(_, TyVariableKind::Integer) 32 | | TyKind::InferenceVar(_, TyVariableKind::Float) => true, 33 | TyKind::OpaqueType(..) 34 | | TyKind::Alias(_) 35 | | TyKind::BoundVar(_) 36 | | TyKind::Placeholder(_) 37 | | TyKind::AssociatedType(..) 38 | | TyKind::Error 39 | | TyKind::InferenceVar(..) => false, 40 | }; 41 | 42 | let trait_id = db 43 | .well_known_trait_id(WellKnownTrait::DiscriminantKind) 44 | .unwrap(); 45 | let trait_datum = db.trait_datum(trait_id); 46 | 47 | let associated_ty_id = trait_datum.associated_ty_ids[0]; 48 | let substitution = Substitution::from1(interner, self_ty.clone()); 49 | 50 | let trait_ref = TraitRef { 51 | trait_id, 52 | substitution: substitution.clone(), 53 | }; 54 | 55 | builder.push_fact(trait_ref); 56 | 57 | if !can_determine_discriminant { 58 | return Ok(()); 59 | } 60 | 61 | let disc_ty = db.discriminant_type(self_ty); 62 | 63 | let normalize = Normalize { 64 | alias: AliasTy::Projection(ProjectionTy { 65 | associated_ty_id, 66 | substitution, 67 | }), 68 | ty: disc_ty, 69 | }; 70 | 71 | builder.push_fact(normalize); 72 | 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /chalk-solve/src/clauses/builtin_traits/sized.rs: -------------------------------------------------------------------------------- 1 | use std::iter; 2 | 3 | use crate::clauses::builtin_traits::needs_impl_for_tys; 4 | use crate::clauses::ClauseBuilder; 5 | use crate::{Interner, RustIrDatabase, TraitRef}; 6 | use chalk_ir::{ 7 | AdtId, CanonicalVarKinds, Floundered, Substitution, TyKind, TyVariableKind, VariableKind, 8 | }; 9 | 10 | use super::last_field_of_struct; 11 | 12 | fn push_adt_sized_conditions( 13 | db: &dyn RustIrDatabase, 14 | builder: &mut ClauseBuilder<'_, I>, 15 | trait_ref: TraitRef, 16 | adt_id: AdtId, 17 | substitution: &Substitution, 18 | ) { 19 | // We only need to check last field of the struct here. Rest of the fields and cases are handled in WF. 20 | let last_field_ty = last_field_of_struct(db, adt_id, substitution).into_iter(); 21 | needs_impl_for_tys(db, builder, trait_ref, last_field_ty); 22 | } 23 | 24 | fn push_tuple_sized_conditions( 25 | db: &dyn RustIrDatabase, 26 | builder: &mut ClauseBuilder<'_, I>, 27 | trait_ref: TraitRef, 28 | arity: usize, 29 | substitution: &Substitution, 30 | ) { 31 | // Empty tuples are always Sized 32 | if arity == 0 { 33 | builder.push_fact(trait_ref); 34 | return; 35 | } 36 | 37 | let interner = db.interner(); 38 | 39 | // To check if a tuple is Sized, we only have to look at its last element. 40 | // This is because the WF checks for tuples require that all the other elements must be Sized. 41 | let last_elem_ty = substitution 42 | .iter(interner) 43 | .last() 44 | .unwrap() 45 | .ty(interner) 46 | .unwrap() 47 | .clone(); 48 | 49 | needs_impl_for_tys(db, builder, trait_ref, iter::once(last_elem_ty)); 50 | } 51 | 52 | pub fn add_sized_program_clauses( 53 | db: &dyn RustIrDatabase, 54 | builder: &mut ClauseBuilder<'_, I>, 55 | trait_ref: TraitRef, 56 | ty: TyKind, 57 | binders: &CanonicalVarKinds, 58 | ) -> Result<(), Floundered> { 59 | match ty { 60 | TyKind::Adt(adt_id, ref substitution) => { 61 | push_adt_sized_conditions(db, builder, trait_ref, adt_id, substitution) 62 | } 63 | TyKind::Tuple(arity, ref substitution) => { 64 | push_tuple_sized_conditions(db, builder, trait_ref, arity, substitution) 65 | } 66 | TyKind::Array(_, _) 67 | | TyKind::Never 68 | | TyKind::Closure(_, _) 69 | | TyKind::FnDef(_, _) 70 | | TyKind::Scalar(_) 71 | | TyKind::Raw(_, _) 72 | | TyKind::Coroutine(_, _) 73 | | TyKind::CoroutineWitness(_, _) 74 | | TyKind::Ref(_, _, _) => builder.push_fact(trait_ref), 75 | 76 | TyKind::AssociatedType(_, _) 77 | | TyKind::Slice(_) 78 | | TyKind::OpaqueType(_, _) 79 | | TyKind::Str 80 | | TyKind::Foreign(_) 81 | | TyKind::Error => {} 82 | 83 | TyKind::Function(_) 84 | | TyKind::InferenceVar(_, TyVariableKind::Float) 85 | | TyKind::InferenceVar(_, TyVariableKind::Integer) => builder.push_fact(trait_ref), 86 | 87 | TyKind::BoundVar(bound_var) => { 88 | let var_kind = &binders.at(db.interner(), bound_var.index).kind; 89 | match var_kind { 90 | VariableKind::Ty(TyVariableKind::Integer) 91 | | VariableKind::Ty(TyVariableKind::Float) => builder.push_fact(trait_ref), 92 | 93 | // Don't know enough 94 | VariableKind::Ty(TyVariableKind::General) => return Err(Floundered), 95 | 96 | VariableKind::Const(_) | VariableKind::Lifetime => {} 97 | } 98 | } 99 | 100 | // We don't know enough here 101 | TyKind::InferenceVar(_, TyVariableKind::General) => return Err(Floundered), 102 | 103 | // These would be handled elsewhere 104 | TyKind::Placeholder(_) | TyKind::Dyn(_) | TyKind::Alias(_) => {} 105 | } 106 | Ok(()) 107 | } 108 | -------------------------------------------------------------------------------- /chalk-solve/src/clauses/builtin_traits/tuple.rs: -------------------------------------------------------------------------------- 1 | use crate::clauses::ClauseBuilder; 2 | use crate::rust_ir::WellKnownTrait; 3 | use crate::{Interner, RustIrDatabase, TraitRef}; 4 | use chalk_ir::{Floundered, Substitution, Ty, TyKind}; 5 | 6 | /// Add implicit impl for the `Tuple` trait for all tuples 7 | pub fn add_tuple_program_clauses( 8 | db: &dyn RustIrDatabase, 9 | builder: &mut ClauseBuilder<'_, I>, 10 | self_ty: Ty, 11 | ) -> Result<(), Floundered> { 12 | let interner = db.interner(); 13 | 14 | match self_ty.kind(interner) { 15 | TyKind::Tuple(..) => { 16 | let trait_id = db.well_known_trait_id(WellKnownTrait::Tuple).unwrap(); 17 | 18 | builder.push_fact(TraitRef { 19 | trait_id, 20 | substitution: Substitution::from1(interner, self_ty), 21 | }); 22 | 23 | Ok(()) 24 | } 25 | 26 | // Tuple trait is non-enumerable 27 | TyKind::InferenceVar(..) | TyKind::BoundVar(_) | TyKind::Alias(..) => Err(Floundered), 28 | _ => Ok(()), 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chalk-solve/src/clauses/dyn_ty.rs: -------------------------------------------------------------------------------- 1 | use super::{builder::ClauseBuilder, generalize}; 2 | use crate::RustIrDatabase; 3 | use chalk_ir::{cast::Cast, interner::Interner, Ty, TyKind, WhereClause}; 4 | 5 | /// If the self type `S` of an `Implemented` goal is a `dyn trait` type, we wish 6 | /// to generate program-clauses that indicates that it implements its own 7 | /// traits. For example, a `dyn Write` type implements `Write` and so on. 8 | /// 9 | /// To see how this works, consider as an example the type `dyn Fn(&u8)`. This 10 | /// is really shorthand for `dyn for<'a> Fn<(&'a u8), Output = ()>`, and we 11 | /// represent that type as something like this: 12 | /// 13 | /// ```ignore 14 | /// dyn(exists { 15 | /// forall<'a> { Implemented(T: Fn<'a>) }, 16 | /// forall<'a> { AliasEq(>::Output, ()) }, 17 | /// }) 18 | /// ``` 19 | /// 20 | /// so what we will do is to generate one program clause for each of the 21 | /// conditions. Thus we get two program clauses: 22 | /// 23 | /// ```ignore 24 | /// forall<'a> { Implemented(dyn Fn(&u8): Fn<(&'a u8)>) } 25 | /// ``` 26 | /// 27 | /// and 28 | /// 29 | /// ```ignore 30 | /// forall<'a> { AliasEq(>::Output, ()) }, 31 | /// ``` 32 | pub(super) fn build_dyn_self_ty_clauses( 33 | db: &dyn RustIrDatabase, 34 | builder: &mut ClauseBuilder<'_, I>, 35 | self_ty: Ty, 36 | ) { 37 | let interner = db.interner(); 38 | let dyn_ty = match self_ty.kind(interner) { 39 | TyKind::Dyn(dyn_ty) => dyn_ty.clone(), 40 | _ => return, 41 | }; 42 | let generalized_dyn_ty = generalize::Generalize::apply(db.interner(), dyn_ty); 43 | 44 | // Here, `self_ty` is the `dyn Fn(&u8)`, and `dyn_ty` is the `exists { .. 45 | // }` clauses shown above. 46 | 47 | // Turn free BoundVars in the type into new existentials. E.g. 48 | // we might get some `dyn Foo`, and we don't want to return 49 | // a clause with a free variable. We can instead return a 50 | // slightly more general clause by basically turning this into 51 | // `exists dyn Foo`. 52 | 53 | builder.push_binders(generalized_dyn_ty, |builder, dyn_ty| { 54 | for exists_qwc in dyn_ty.bounds.map_ref(|r| r.iter(interner)) { 55 | // Replace the `T` from `exists { .. }` with `self_ty`, 56 | // yielding clases like 57 | // 58 | // ``` 59 | // forall<'a> { Implemented(dyn Fn(&u8): Fn<(&'a u8)>) } 60 | // ``` 61 | let qwc = exists_qwc 62 | .cloned() 63 | .substitute(interner, &[self_ty.clone().cast(interner)]); 64 | 65 | builder.push_binders(qwc, |builder, bound| match &bound { 66 | // For the implemented traits, we need to elaborate super traits and add where clauses from the trait 67 | WhereClause::Implemented(trait_ref) => { 68 | super::super_traits::push_trait_super_clauses( 69 | builder.db, 70 | builder, 71 | trait_ref.clone(), 72 | ) 73 | } 74 | // FIXME: Associated item bindings are just taken as facts (?) 75 | WhereClause::AliasEq(_) => builder.push_fact(bound), 76 | WhereClause::LifetimeOutlives(..) => {} 77 | WhereClause::TypeOutlives(..) => {} 78 | }); 79 | } 80 | }); 81 | } 82 | -------------------------------------------------------------------------------- /chalk-solve/src/clauses/env_elaborator.rs: -------------------------------------------------------------------------------- 1 | use super::program_clauses::ToProgramClauses; 2 | use crate::clauses::builder::ClauseBuilder; 3 | use crate::clauses::{match_alias_ty, match_ty}; 4 | use crate::DomainGoal; 5 | use crate::FromEnv; 6 | use crate::ProgramClause; 7 | use crate::RustIrDatabase; 8 | use crate::Ty; 9 | use crate::{debug_span, TyKind}; 10 | use chalk_ir::interner::Interner; 11 | use chalk_ir::visit::{TypeVisitable, TypeVisitor}; 12 | use chalk_ir::{DebruijnIndex, Environment}; 13 | use rustc_hash::FxHashSet; 14 | use std::ops::ControlFlow; 15 | use tracing::instrument; 16 | 17 | /// When proving a `FromEnv` goal, we elaborate all `FromEnv` goals 18 | /// found in the environment. 19 | /// 20 | /// For example, when `T: Clone` is in the environment, we can prove 21 | /// `T: Copy` by adding the clauses from `trait Clone`, which includes 22 | /// the rule `FromEnv(T: Copy) :- FromEnv(T: Clone)` 23 | pub(super) fn elaborate_env_clauses( 24 | db: &dyn RustIrDatabase, 25 | in_clauses: &[ProgramClause], 26 | out: &mut FxHashSet>, 27 | environment: &Environment, 28 | ) { 29 | let mut this_round = vec![]; 30 | let builder = &mut ClauseBuilder::new(db, &mut this_round); 31 | let mut elaborater = EnvElaborator { 32 | db, 33 | builder, 34 | environment, 35 | }; 36 | in_clauses.visit_with(&mut elaborater, DebruijnIndex::INNERMOST); 37 | out.extend(this_round); 38 | } 39 | 40 | struct EnvElaborator<'me, 'builder, I: Interner> { 41 | db: &'me dyn RustIrDatabase, 42 | builder: &'builder mut ClauseBuilder<'me, I>, 43 | environment: &'me Environment, 44 | } 45 | 46 | impl<'me, 'builder, I: Interner> TypeVisitor for EnvElaborator<'me, 'builder, I> { 47 | type BreakTy = (); 48 | 49 | fn as_dyn(&mut self) -> &mut dyn TypeVisitor { 50 | self 51 | } 52 | 53 | fn interner(&self) -> I { 54 | self.db.interner() 55 | } 56 | #[instrument(level = "debug", skip(self, _outer_binder))] 57 | fn visit_ty(&mut self, ty: &Ty, _outer_binder: DebruijnIndex) -> ControlFlow<()> { 58 | match ty.kind(self.interner()) { 59 | TyKind::Alias(alias_ty) => match_alias_ty(self.builder, self.environment, alias_ty), 60 | TyKind::Placeholder(_) => {} 61 | 62 | // FIXME(#203) -- We haven't fully figured out the implied 63 | // bounds story around `dyn Trait` types. 64 | TyKind::Dyn(_) => (), 65 | 66 | TyKind::Function(_) | TyKind::BoundVar(_) | TyKind::InferenceVar(_, _) => (), 67 | 68 | _ => { 69 | // This shouldn't fail because of the above clauses 70 | match_ty(self.builder, self.environment, ty) 71 | .map_err(|_| ()) 72 | .unwrap() 73 | } 74 | } 75 | ControlFlow::Continue(()) 76 | } 77 | 78 | fn visit_domain_goal( 79 | &mut self, 80 | domain_goal: &DomainGoal, 81 | outer_binder: DebruijnIndex, 82 | ) -> ControlFlow<()> { 83 | if let DomainGoal::FromEnv(from_env) = domain_goal { 84 | debug_span!("visit_domain_goal", ?from_env); 85 | match from_env { 86 | FromEnv::Trait(trait_ref) => { 87 | let trait_datum = self.db.trait_datum(trait_ref.trait_id); 88 | 89 | trait_datum.to_program_clauses(self.builder, self.environment); 90 | 91 | // If we know that `T: Iterator`, then we also know 92 | // things about `::Item`, so push those 93 | // implied bounds too: 94 | for &associated_ty_id in &trait_datum.associated_ty_ids { 95 | self.db 96 | .associated_ty_data(associated_ty_id) 97 | .to_program_clauses(self.builder, self.environment); 98 | } 99 | ControlFlow::Continue(()) 100 | } 101 | FromEnv::Ty(ty) => ty.visit_with(self, outer_binder), 102 | } 103 | } else { 104 | ControlFlow::Continue(()) 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /chalk-solve/src/clauses/generalize.rs: -------------------------------------------------------------------------------- 1 | //! This gets rid of free variables in a type by replacing them by fresh bound 2 | //! ones. We use this when building clauses that contain types passed to 3 | //! `program_clauses`; these may contain variables, and just copying those 4 | //! variables verbatim leads to problems. Instead, we return a slightly more 5 | //! general program clause, with new variables in those places. This can only 6 | //! happen with `dyn Trait` currently; that's the only case where we use the 7 | //! types passed to `program_clauses` in the clauses we generate. 8 | 9 | use chalk_derive::FallibleTypeFolder; 10 | use chalk_ir::{ 11 | fold::{TypeFoldable, TypeFolder}, 12 | interner::{HasInterner, Interner}, 13 | Binders, BoundVar, Const, ConstData, ConstValue, DebruijnIndex, Lifetime, LifetimeData, Ty, 14 | TyKind, TyVariableKind, VariableKind, VariableKinds, 15 | }; 16 | use rustc_hash::FxHashMap; 17 | 18 | #[derive(FallibleTypeFolder)] 19 | pub struct Generalize { 20 | binders: Vec>, 21 | mapping: FxHashMap, 22 | interner: I, 23 | } 24 | 25 | impl Generalize { 26 | pub fn apply(interner: I, value: T) -> Binders 27 | where 28 | T: HasInterner + TypeFoldable, 29 | { 30 | let mut generalize = Generalize { 31 | binders: Vec::new(), 32 | mapping: FxHashMap::default(), 33 | interner, 34 | }; 35 | let value = value 36 | .try_fold_with(&mut generalize, DebruijnIndex::INNERMOST) 37 | .unwrap(); 38 | Binders::new( 39 | VariableKinds::from_iter(interner, generalize.binders), 40 | value, 41 | ) 42 | } 43 | } 44 | 45 | impl TypeFolder for Generalize { 46 | fn as_dyn(&mut self) -> &mut dyn TypeFolder { 47 | self 48 | } 49 | 50 | fn fold_free_var_ty(&mut self, bound_var: BoundVar, outer_binder: DebruijnIndex) -> Ty { 51 | let binder_vec = &mut self.binders; 52 | let new_index = self.mapping.entry(bound_var).or_insert_with(|| { 53 | let i = binder_vec.len(); 54 | binder_vec.push(VariableKind::Ty(TyVariableKind::General)); 55 | i 56 | }); 57 | let new_var = BoundVar::new(outer_binder, *new_index); 58 | TyKind::BoundVar(new_var).intern(TypeFolder::interner(self)) 59 | } 60 | 61 | fn fold_free_var_const( 62 | &mut self, 63 | ty: Ty, 64 | bound_var: BoundVar, 65 | outer_binder: DebruijnIndex, 66 | ) -> Const { 67 | let binder_vec = &mut self.binders; 68 | let new_index = self.mapping.entry(bound_var).or_insert_with(|| { 69 | let i = binder_vec.len(); 70 | binder_vec.push(VariableKind::Const(ty.clone())); 71 | i 72 | }); 73 | let new_var = BoundVar::new(outer_binder, *new_index); 74 | ConstData { 75 | ty, 76 | value: ConstValue::BoundVar(new_var), 77 | } 78 | .intern(TypeFolder::interner(self)) 79 | } 80 | 81 | fn fold_free_var_lifetime( 82 | &mut self, 83 | bound_var: BoundVar, 84 | outer_binder: DebruijnIndex, 85 | ) -> Lifetime { 86 | let binder_vec = &mut self.binders; 87 | let new_index = self.mapping.entry(bound_var).or_insert_with(|| { 88 | let i = binder_vec.len(); 89 | binder_vec.push(VariableKind::Lifetime); 90 | i 91 | }); 92 | let new_var = BoundVar::new(outer_binder, *new_index); 93 | LifetimeData::BoundVar(new_var).intern(TypeFolder::interner(self)) 94 | } 95 | 96 | fn interner(&self) -> I { 97 | self.interner 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /chalk-solve/src/coherence/orphan.rs: -------------------------------------------------------------------------------- 1 | use crate::coherence::CoherenceError; 2 | use crate::ext::GoalExt; 3 | use crate::solve::Solver; 4 | use crate::RustIrDatabase; 5 | use chalk_ir::cast::*; 6 | use chalk_ir::interner::Interner; 7 | use chalk_ir::*; 8 | use tracing::{debug, instrument}; 9 | 10 | // Test if a local impl violates the orphan rules. 11 | // 12 | // For `impl Trait for MyType` we generate: 13 | // 14 | // forall { LocalImplAllowed(MyType: Trait) } 15 | // 16 | // This must be provable in order to pass the orphan check. 17 | #[instrument(level = "debug", skip(db, solver))] 18 | pub fn perform_orphan_check( 19 | db: &dyn RustIrDatabase, 20 | solver: &mut dyn Solver, 21 | impl_id: ImplId, 22 | ) -> Result<(), CoherenceError> { 23 | let impl_datum = db.impl_datum(impl_id); 24 | debug!(?impl_datum); 25 | 26 | let impl_allowed: Goal = impl_datum 27 | .binders 28 | .map_ref(|bound_impl| { 29 | // Ignoring the polarization of the impl's polarized trait ref 30 | DomainGoal::LocalImplAllowed(bound_impl.trait_ref.clone()) 31 | }) 32 | .cast(db.interner()); 33 | 34 | let canonical_goal = &impl_allowed.into_closed_goal(db.interner()); 35 | let is_allowed = solver.solve(db, canonical_goal).is_some(); 36 | debug!("overlaps = {:?}", is_allowed); 37 | 38 | if !is_allowed { 39 | let trait_id = impl_datum.trait_id(); 40 | return Err(CoherenceError::FailedOrphanCheck(trait_id)); 41 | } 42 | 43 | Ok(()) 44 | } 45 | -------------------------------------------------------------------------------- /chalk-solve/src/coinductive_goal.rs: -------------------------------------------------------------------------------- 1 | use crate::RustIrDatabase; 2 | use chalk_ir::interner::Interner; 3 | use chalk_ir::*; 4 | 5 | pub trait IsCoinductive { 6 | /// A goal G has coinductive semantics if proving G is allowed to 7 | /// assume G is true (very roughly speaking). In the case of 8 | /// chalk-ir, this is true for goals of the form `T: AutoTrait`, 9 | /// or if it is of the form `WellFormed(T: Trait)` where `Trait` 10 | /// is any trait. The latter is needed for dealing with WF 11 | /// requirements and cyclic traits, which generates cycles in the 12 | /// proof tree which must not be rejected but instead must be 13 | /// treated as a success. 14 | fn is_coinductive(&self, db: &dyn RustIrDatabase) -> bool; 15 | } 16 | 17 | impl IsCoinductive for Goal { 18 | fn is_coinductive(&self, db: &dyn RustIrDatabase) -> bool { 19 | let interner = db.interner(); 20 | match self.data(interner) { 21 | GoalData::DomainGoal(DomainGoal::Holds(wca)) => match wca { 22 | WhereClause::Implemented(tr) => { 23 | db.trait_datum(tr.trait_id).is_auto_trait() 24 | || db.trait_datum(tr.trait_id).is_coinductive_trait() 25 | } 26 | WhereClause::AliasEq(..) => false, 27 | WhereClause::LifetimeOutlives(..) => false, 28 | WhereClause::TypeOutlives(..) => false, 29 | }, 30 | GoalData::DomainGoal(DomainGoal::WellFormed(WellFormed::Trait(..))) => true, 31 | GoalData::Quantified(QuantifierKind::ForAll, goal) => { 32 | goal.skip_binders().is_coinductive(db) 33 | } 34 | _ => false, 35 | } 36 | } 37 | } 38 | 39 | impl IsCoinductive for UCanonical>> { 40 | fn is_coinductive(&self, db: &dyn RustIrDatabase) -> bool { 41 | self.canonical.value.goal.is_coinductive(db) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /chalk-solve/src/display/identifiers.rs: -------------------------------------------------------------------------------- 1 | //! Writer logic for simple IDs 2 | //! 3 | //! `RenderAsRust` impls for identifiers which are either too small or too 4 | //! shared to belong anywhere else belong here. 5 | use std::fmt::{Formatter, Result}; 6 | 7 | use chalk_ir::interner::Interner; 8 | use chalk_ir::*; 9 | 10 | use super::{render_trait::RenderAsRust, state::InternalWriterState}; 11 | 12 | impl RenderAsRust for AdtId { 13 | fn fmt(&self, s: &InternalWriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { 14 | // TODO: use debug methods? 15 | write!( 16 | f, 17 | "{}", 18 | s.alias_for_adt_id_name(self.0, s.db().adt_name(*self)) 19 | ) 20 | } 21 | } 22 | 23 | impl RenderAsRust for TraitId { 24 | fn fmt(&self, s: &InternalWriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { 25 | // TODO: use debug methods? 26 | write!( 27 | f, 28 | "{}", 29 | s.alias_for_id_name(self.0, s.db().trait_name(*self)) 30 | ) 31 | } 32 | } 33 | 34 | impl RenderAsRust for AssocTypeId { 35 | fn fmt(&self, s: &InternalWriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { 36 | // TODO: use debug methods? 37 | write!( 38 | f, 39 | "{}", 40 | s.alias_for_id_name(self.0, s.db().assoc_type_name(*self)) 41 | ) 42 | } 43 | } 44 | 45 | impl RenderAsRust for OpaqueTyId { 46 | fn fmt(&self, s: &InternalWriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { 47 | // TODO: use debug methods? 48 | write!( 49 | f, 50 | "{}", 51 | s.alias_for_id_name(self.0, s.db().opaque_type_name(*self)) 52 | ) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /chalk-solve/src/display/render_trait.rs: -------------------------------------------------------------------------------- 1 | //! `RenderAsRust` trait and related utils. 2 | use std::fmt::{Display, Formatter, Result}; 3 | 4 | use chalk_ir::interner::Interner; 5 | 6 | use super::state::InternalWriterState; 7 | 8 | /// Displays `RenderAsRust` data. 9 | /// 10 | /// This is a utility struct for making `RenderAsRust` nice to use with rust format macros. 11 | pub(in crate::display) struct DisplayRenderAsRust<'a, I: Interner, T> { 12 | s: &'a InternalWriterState<'a, I>, 13 | rar: &'a T, 14 | } 15 | 16 | impl> Display for DisplayRenderAsRust<'_, I, T> { 17 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 18 | self.rar.fmt(self.s, f) 19 | } 20 | } 21 | 22 | pub(in crate::display) trait RenderAsRust { 23 | fn fmt(&self, s: &InternalWriterState<'_, I>, f: &mut Formatter<'_>) -> Result; 24 | fn display<'a>(&'a self, s: &'a InternalWriterState<'a, I>) -> DisplayRenderAsRust<'a, I, Self> 25 | where 26 | Self: Sized, 27 | { 28 | DisplayRenderAsRust { s, rar: self } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chalk-solve/src/display/utils.rs: -------------------------------------------------------------------------------- 1 | //! Render utilities which don't belong anywhere else. 2 | use std::fmt::{Display, Formatter, Result}; 3 | 4 | pub fn as_display) -> Result>(f: F) -> impl Display { 5 | struct ClosureDisplay) -> Result>(F); 6 | 7 | impl) -> Result> Display for ClosureDisplay { 8 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 9 | self.0(f) 10 | } 11 | } 12 | 13 | ClosureDisplay(f) 14 | } 15 | 16 | macro_rules! write_joined_non_empty_list { 17 | ($f:expr,$template:tt,$list:expr,$sep:expr) => {{ 18 | let mut x = $list.into_iter().peekable(); 19 | if x.peek().is_some() { 20 | write!($f, $template, x.format($sep)) 21 | } else { 22 | Ok(()) 23 | } 24 | }}; 25 | } 26 | 27 | /// Processes a name given by an [`Interner`][chalk_ir::interner::Interner] debug 28 | /// method into something usable by the `display` module. 29 | /// 30 | /// This is specifically useful when implementing 31 | /// [`RustIrDatabase`][crate::RustIrDatabase] `name_*` methods. 32 | pub fn sanitize_debug_name(func: impl Fn(&mut Formatter<'_>) -> Option) -> String { 33 | use std::fmt::Write; 34 | 35 | // First, write the debug method contents to a String. 36 | let mut debug_out = String::new(); 37 | // ignore if the result is `None`, as we can just as easily tell by looking 38 | // to see if anything was written to `debug_out`. 39 | write!( 40 | debug_out, 41 | "{}", 42 | as_display(|fmt| { func(fmt).unwrap_or(Ok(())) }) 43 | ) 44 | .expect("expected writing to a String to succeed"); 45 | if debug_out.is_empty() { 46 | return "Unknown".to_owned(); 47 | } 48 | 49 | // now the actual sanitization 50 | debug_out.replace(|c: char| !c.is_ascii_alphanumeric(), "_") 51 | } 52 | -------------------------------------------------------------------------------- /chalk-solve/src/infer/instantiate.rs: -------------------------------------------------------------------------------- 1 | use chalk_ir::fold::*; 2 | use chalk_ir::interner::HasInterner; 3 | use std::fmt::Debug; 4 | use tracing::instrument; 5 | 6 | use super::*; 7 | 8 | impl InferenceTable { 9 | /// Given the binders from a canonicalized value C, returns a 10 | /// substitution S mapping each free variable in C to a fresh 11 | /// inference variable. This substitution can then be applied to 12 | /// C, which would be equivalent to 13 | /// `self.instantiate_canonical(v)`. 14 | pub(super) fn fresh_subst( 15 | &mut self, 16 | interner: I, 17 | binders: &[CanonicalVarKind], 18 | ) -> Substitution { 19 | Substitution::from_iter( 20 | interner, 21 | binders.iter().map(|kind| { 22 | let param_infer_var = kind.map_ref(|&ui| self.new_variable(ui)); 23 | param_infer_var.to_generic_arg(interner) 24 | }), 25 | ) 26 | } 27 | 28 | /// Variant on `instantiate` that takes a `Canonical`. 29 | pub fn instantiate_canonical(&mut self, interner: I, bound: Canonical) -> T 30 | where 31 | T: HasInterner + TypeFoldable + Debug, 32 | { 33 | let subst = self.fresh_subst(interner, bound.binders.as_slice(interner)); 34 | subst.apply(bound.value, interner) 35 | } 36 | 37 | /// Instantiates `arg` with fresh existential variables in the 38 | /// given universe; the kinds of the variables are implied by 39 | /// `binders`. This is used to apply a universally quantified 40 | /// clause like `forall X, 'Y. P => Q`. Here the `binders` 41 | /// argument is referring to `X, 'Y`. 42 | fn instantiate_in( 43 | &mut self, 44 | interner: I, 45 | universe: UniverseIndex, 46 | binders: impl Iterator>, 47 | arg: T, 48 | ) -> T 49 | where 50 | T: TypeFoldable, 51 | { 52 | let binders: Vec<_> = binders 53 | .map(|pk| CanonicalVarKind::new(pk, universe)) 54 | .collect(); 55 | let subst = self.fresh_subst(interner, &binders); 56 | subst.apply(arg, interner) 57 | } 58 | 59 | /// Variant on `instantiate_in` that takes a `Binders`. 60 | #[instrument(level = "debug", skip(self, interner))] 61 | pub fn instantiate_binders_existentially(&mut self, interner: I, arg: Binders) -> T 62 | where 63 | T: TypeFoldable + HasInterner, 64 | { 65 | let (value, binders) = arg.into_value_and_skipped_binders(); 66 | 67 | let max_universe = self.max_universe; 68 | self.instantiate_in( 69 | interner, 70 | max_universe, 71 | binders.iter(interner).cloned(), 72 | value, 73 | ) 74 | } 75 | 76 | #[instrument(level = "debug", skip(self, interner))] 77 | pub fn instantiate_binders_universally(&mut self, interner: I, arg: Binders) -> T 78 | where 79 | T: TypeFoldable + HasInterner, 80 | { 81 | let (value, binders) = arg.into_value_and_skipped_binders(); 82 | 83 | let mut lazy_ui = None; 84 | let mut ui = || { 85 | lazy_ui.unwrap_or_else(|| { 86 | let ui = self.new_universe(); 87 | lazy_ui = Some(ui); 88 | ui 89 | }) 90 | }; 91 | let parameters: Vec<_> = binders 92 | .iter(interner) 93 | .cloned() 94 | .enumerate() 95 | .map(|(idx, pk)| { 96 | let placeholder_idx = PlaceholderIndex { ui: ui(), idx }; 97 | match pk { 98 | VariableKind::Lifetime => { 99 | let lt = placeholder_idx.to_lifetime(interner); 100 | lt.cast(interner) 101 | } 102 | VariableKind::Ty(_) => placeholder_idx.to_ty(interner).cast(interner), 103 | VariableKind::Const(ty) => { 104 | placeholder_idx.to_const(interner, ty).cast(interner) 105 | } 106 | } 107 | }) 108 | .collect(); 109 | Subst::apply(interner, ¶meters, value) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /chalk-solve/src/logging.rs: -------------------------------------------------------------------------------- 1 | /// Run an action with a tracing log subscriber. The logging level is loaded 2 | /// from `CHALK_DEBUG`. 3 | #[cfg(feature = "tracing-full")] 4 | pub fn with_tracing_logs(action: impl FnOnce() -> T) -> T { 5 | use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; 6 | use tracing_tree::HierarchicalLayer; 7 | let filter = EnvFilter::from_env("CHALK_DEBUG"); 8 | let subscriber = Registry::default() 9 | .with(filter) 10 | .with(HierarchicalLayer::new(2).with_writer(std::io::stdout)); 11 | tracing::subscriber::with_default(subscriber, action) 12 | } 13 | 14 | /// Run an action with a tracing log subscriber. The logging level is loaded 15 | /// from `CHALK_DEBUG`. 16 | #[cfg(not(feature = "tracing-full"))] 17 | pub fn with_tracing_logs(action: impl FnOnce() -> T) -> T { 18 | action() 19 | } 20 | -------------------------------------------------------------------------------- /chalk-solve/src/solve/test/bench.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarking tests. 2 | 3 | extern crate test; 4 | use self::test::Bencher; 5 | 6 | use crate::db::ChalkDatabase; 7 | use crate::query::{ProgramSolverChoice, ProgramText}; 8 | use chalk_solve::SolverChoice; 9 | use ir; 10 | use std::sync::Arc; 11 | 12 | use super::{assert_result, parse_and_lower_goal, parse_and_lower_program}; 13 | 14 | fn run_bench( 15 | program_text: &str, 16 | solver_choice: SolverChoice, 17 | goal_text: &str, 18 | bencher: &mut Bencher, 19 | expected: &str, 20 | ) { 21 | ChalkDatabase::with_program(Arc::new(program_text.to_string()), solver_choice, |db| { 22 | let program = db.lowered_program().unwrap(); 23 | let env = db.environment().unwrap(); 24 | ir::tls::set_current_program(&program, || { 25 | let goal = parse_and_lower_goal(&program, goal_text).unwrap(); 26 | let peeled_goal = goal.into_peeled_goal(); 27 | 28 | // Execute once to get an expected result. 29 | let result = solver_choice.solve_root_goal(&env, &peeled_goal); 30 | 31 | // Check expectation. 32 | assert_result(&result, expected); 33 | 34 | // Then do it many times to measure time. 35 | bencher.iter(|| solver_choice.solve_root_goal(&env, &peeled_goal)); 36 | }); 37 | }); 38 | } 39 | 40 | const CYCLEY: &str = " 41 | trait AsRef { } 42 | trait Clone { } 43 | trait Copy where Self: Clone { } 44 | trait Sized { } 45 | 46 | struct i32 { } 47 | impl Copy for i32 { } 48 | impl Clone for i32 { } 49 | impl Sized for i32 { } 50 | 51 | struct u32 { } 52 | impl Copy for u32 { } 53 | impl Clone for u32 { } 54 | impl Sized for u32 { } 55 | 56 | struct Rc { } 57 | impl Clone for Rc { } 58 | impl Sized for Rc { } 59 | 60 | struct Box { } 61 | impl AsRef for Box where T: Sized { } 62 | impl Clone for Box where T: Clone { } 63 | impl Sized for Box { } 64 | 65 | // Meant to be [T] 66 | struct Slice where T: Sized { } 67 | impl Sized for Slice { } 68 | impl AsRef> for Slice where T: Sized { } 69 | 70 | struct Vec where T: Sized { } 71 | impl AsRef> for Vec where T: Sized { } 72 | impl AsRef> for Vec where T: Sized { } 73 | impl Clone for Vec where T: Clone, T: Sized { } 74 | impl Sized for Vec where T: Sized { } 75 | 76 | trait SliceExt 77 | where ::Item: Clone 78 | { 79 | type Item; 80 | } 81 | 82 | impl SliceExt for Slice 83 | where T: Clone 84 | { 85 | type Item = T; 86 | } 87 | "; 88 | 89 | const CYCLEY_GOAL: &str = " 90 | forall { 91 | if ( 92 | as SliceExt>::Item: Clone; 93 | as SliceExt>::Item: Sized; 94 | T: Clone; 95 | T: Sized 96 | ) { 97 | T: Sized 98 | } 99 | } 100 | "; 101 | 102 | #[bench] 103 | fn cycley_slg(b: &mut Bencher) { 104 | run_bench( 105 | CYCLEY, 106 | SolverChoice::SLG { max_size: 20 }, 107 | CYCLEY_GOAL, 108 | b, 109 | "Unique", 110 | ); 111 | } 112 | -------------------------------------------------------------------------------- /libstd.chalk: -------------------------------------------------------------------------------- 1 | // Some basic examples you can use with the repl. Try this 2 | // (you type the parts that go after the `?-`): 3 | // 4 | // cargo run 5 | // ?- load libstd.chalk 6 | // ?- Vec>: Clone 7 | 8 | trait AsRef { } 9 | trait Clone { } 10 | trait Copy where Self: Clone { } 11 | trait Sized { } 12 | 13 | impl Copy for i32 { } 14 | impl Clone for i32 { } 15 | impl Sized for i32 { } 16 | 17 | impl Copy for u32 { } 18 | impl Clone for u32 { } 19 | impl Sized for u32 { } 20 | 21 | struct Rc { } 22 | impl Clone for Rc { } 23 | impl Sized for Rc { } 24 | 25 | #[fundamental] 26 | struct Box { } 27 | impl AsRef for Box where T: Sized { } 28 | impl Clone for Box where T: Clone { } 29 | impl Sized for Box { } 30 | 31 | // Meant to be [T] 32 | struct Slice where T: Sized { } 33 | impl AsRef> for Slice where T: Sized { } 34 | 35 | struct Vec where T: Sized { } 36 | impl AsRef> for Vec where T: Sized { } 37 | impl AsRef> for Vec where T: Sized { } 38 | impl Clone for Vec where T: Clone, T: Sized { } 39 | impl Sized for Vec where T: Sized { } 40 | -------------------------------------------------------------------------------- /releases-template.hbs: -------------------------------------------------------------------------------- 1 | ### Changelog 2 | 3 | All notable changes to this project will be documented in this file. Dates are displayed in UTC. 4 | 5 | {{#unless options.hideCredit}} 6 | Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog) 7 | (Note: versions before 0.11.0 were manually generated). 8 | {{/unless}} 9 | 10 | {{#each releases}} 11 | {{#if href}} 12 | ###{{#unless major}}#{{/unless}} [{{title}}]({{href}}) 13 | {{else}} 14 | #### {{title}} 15 | {{/if}} 16 | 17 | {{#if tag}} 18 | > {{niceDate}} 19 | {{/if}} 20 | 21 | {{#if summary}} 22 | {{summary}} 23 | {{/if}} 24 | 25 | {{#each merges}} 26 | - {{#if commit.breaking}}**Breaking change:** {{/if}}{{message}}{{#if href}} [`#{{id}}`]({{href}}){{/if}} 27 | {{/each}} 28 | 29 | {{/each}} 30 | -------------------------------------------------------------------------------- /tests/display/const_.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_const_generics() { 3 | // Test we render const generic parameters correctly in a variety of places. 4 | reparse_test!( 5 | program { 6 | struct Usize { } 7 | struct Bar { } 8 | trait Foo { } 9 | trait AssocTy { 10 | type Type; 11 | } 12 | impl Foo for Bar { } 13 | impl AssocTy for Bar { 14 | type Type = Usize; 15 | } 16 | opaque type Gee: Foo = Usize; 17 | } 18 | ); 19 | } 20 | 21 | #[test] 22 | fn test_basic_const_values_in_impls() { 23 | // Test we render const values correctly in impls. 24 | reparse_test!( 25 | program { 26 | struct Foo { } 27 | trait Bar { } 28 | impl Bar for Foo<0> { } 29 | impl Bar for Foo<1> { } 30 | impl Bar for Foo<2> { } 31 | } 32 | ); 33 | } 34 | 35 | #[test] 36 | fn test_basic_const_values_in_opaque_ty_values() { 37 | // Test we render const values correctly in opaque type values. 38 | reparse_test!( 39 | program { 40 | struct Foo { } 41 | opaque type Zed = Foo<0>; 42 | } 43 | ); 44 | } 45 | 46 | #[test] 47 | fn test_basic_const_values_in_assoc_ty_values() { 48 | // Test we render const values correctly in associated type values. 49 | reparse_test!( 50 | program { 51 | struct Foo { } 52 | trait Bar { 53 | type Assoc; 54 | } 55 | impl Bar for Foo<0> { 56 | type Assoc = Foo<1>; 57 | } 58 | } 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /tests/display/dyn_.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_dyn_forall_in_impl() { 3 | // Test we render `dyn forall` types correctly (and with the correct 4 | // lifetime names) in impl blocks. 5 | reparse_test!( 6 | program { 7 | trait Foo<'t> {} 8 | trait Bar<'a> {} 9 | impl<'t> Foo<'t> for dyn forall<'a> Bar<'a> + 't {} 10 | } 11 | ); 12 | } 13 | 14 | #[test] 15 | fn test_dyn_forall_in_struct() { 16 | // Test we render `dyn forall` types correctly (and with the correct 17 | // lifetime names) in struct fields. 18 | reparse_test!( 19 | program { 20 | struct Foo<'t> { 21 | field: dyn forall<'a> Baz<'a> + 't 22 | } 23 | trait Baz<'a> {} 24 | } 25 | ); 26 | } 27 | 28 | #[test] 29 | fn test_dyn_forall_multiple_parameters() { 30 | // Test we render `dyn forall` types with multiple lifetimes correctly, and 31 | // with the correct lifetime names. 32 | reparse_test!( 33 | program { 34 | struct Foo<'t> { 35 | field: dyn forall<'a, 'b> Bix<'a, 'b> + 't 36 | } 37 | trait Bix<'a, 'b> {} 38 | } 39 | ); 40 | } 41 | 42 | #[test] 43 | fn test_multiple_forall_one_dyn() { 44 | // Test we render `dyn forall A + forall B` correctly. 45 | reparse_test!( 46 | program { 47 | struct Foo<'t> { 48 | field1: dyn forall<'a> Bex<'a> + forall<'b> Byx<'b> + 't, 49 | field2: dyn forall<'a, 'b> Bux<'a, 'b> + forall<'b, 'c> Brx<'b, 'c> + 't 50 | } 51 | trait Bex<'a> {} 52 | trait Byx<'a> {} 53 | trait Bux<'a, 'b> {} 54 | trait Brx<'a, 'b> {} 55 | } 56 | ); 57 | } 58 | 59 | #[test] 60 | fn test_dyn_forall_with_trait_referencing_outer_lifetime() { 61 | // Test we can render a trait inside a `dyn forall` referencing an outer 62 | // lifetime correctly (in other words, test for debrujin index errors). 63 | reparse_test!( 64 | program { 65 | struct Foo<'a> { 66 | field: dyn forall<'b> Bpx<'a, 'b> + 'a 67 | } 68 | trait Bpx<'a, 'b> {} 69 | } 70 | ); 71 | } 72 | 73 | #[test] 74 | fn test_simple_dyn() { 75 | // Test that we print `dyn Trait` types correctly. 76 | reparse_test!( 77 | program { 78 | struct Foo<'a> { 79 | field: dyn Bax + 'a 80 | } 81 | trait Bax {} 82 | } 83 | ); 84 | } 85 | 86 | #[test] 87 | fn test_simple_dyn_referencing_outer_generic_parameters() { 88 | // Test that we print `dyn Trait` referencing outer generic parameters correctly. 89 | reparse_test!( 90 | program { 91 | struct Foo<'a> { 92 | field: dyn Bix<'a> + 'a 93 | } 94 | trait Bix<'a> {} 95 | } 96 | ); 97 | } 98 | -------------------------------------------------------------------------------- /tests/display/enum_.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_simple_enum() { 3 | reparse_test!( 4 | program { 5 | enum Foo {} 6 | } 7 | ); 8 | } 9 | 10 | #[test] 11 | fn test_enum_generics() { 12 | reparse_test!( 13 | program { 14 | enum Foo {} 15 | enum Bar {} 16 | } 17 | ); 18 | } 19 | 20 | #[test] 21 | fn test_enum_bounds() { 22 | // Test printing where clauses 23 | reparse_test!( 24 | program { 25 | enum Foo where T: Trait {} 26 | trait Trait {} 27 | } 28 | ); 29 | } 30 | 31 | #[test] 32 | fn test_enum_fields() { 33 | // Test printing enums with fields, enum fields with fields, and enum 34 | // generics in enum fields. 35 | reparse_test!( 36 | program { 37 | enum Foo {} 38 | enum Bar {} 39 | enum Baz { 40 | A { 41 | x: Foo, 42 | b: Bar, 43 | y: Foo 44 | }, 45 | B(u32), 46 | } 47 | } 48 | ); 49 | } 50 | 51 | #[test] 52 | fn test_enum_keywords() { 53 | reparse_test!( 54 | program { 55 | #[upstream] 56 | enum UpstreamFoo {} 57 | 58 | #[fundamental] 59 | enum FundamentalFoo {} 60 | 61 | #[phantom_data] 62 | enum PhantomFoo {} 63 | 64 | #[upstream] 65 | #[fundamental] 66 | #[phantom_data] 67 | enum Bar {} 68 | } 69 | ); 70 | } 71 | 72 | #[test] 73 | fn test_enum_repr() { 74 | reparse_test!( 75 | program { 76 | #[repr(C)] 77 | enum CFoo {} 78 | 79 | #[repr(packed)] 80 | enum PackedFoo {} 81 | 82 | // Test all orderings of multiple `repr()` attributes 83 | 84 | #[repr(C)] 85 | #[repr(packed)] 86 | enum CPackedFoo {} 87 | 88 | #[repr(packed)] 89 | #[repr(C)] 90 | enum PackedCFoo {} 91 | } 92 | ); 93 | } 94 | 95 | #[test] 96 | fn test_enum_repr_and_keywords_ordered_correctly() { 97 | // Test that when we print both `repr` and another keyword, we order them in 98 | // a way accepted by the parser. 99 | reparse_test!( 100 | program { 101 | #[upstream] 102 | #[repr(C)] 103 | enum UpstreamCFoo {} 104 | } 105 | ); 106 | } 107 | -------------------------------------------------------------------------------- /tests/display/fn_.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_basic_fn_def() { 3 | // Test printing simple function definitions 4 | reparse_test!( 5 | program { 6 | struct Foo {} 7 | fn nothing(); 8 | fn takes_foo(v: Foo); 9 | fn gives_foo() -> Foo; 10 | fn bar(a: Foo, _: Foo) -> Foo; 11 | } 12 | ); 13 | } 14 | 15 | #[test] 16 | fn test_generic_fn_def() { 17 | // Test printing generics in function definitions 18 | reparse_test!( 19 | program { 20 | struct Foo {} 21 | struct Bar {} 22 | fn identity(arg: T) -> T; 23 | fn transform(a: Foo) -> Bar; 24 | fn wrap(v: T) -> Foo; 25 | } 26 | ); 27 | } 28 | 29 | #[test] 30 | fn test_const_generic_fn_def() { 31 | // Test printing const generics in function definitions 32 | reparse_test!( 33 | program { 34 | fn uses_n(arg: [T; N]); 35 | } 36 | ); 37 | } 38 | 39 | #[test] 40 | fn test_opaque_ty_with_fn_def() { 41 | // Test printing opaque types in function definitions 42 | reparse_test!( 43 | program { 44 | opaque type Bar = (); 45 | fn gives_bar() -> Bar; 46 | fn receives_bar(param: Bar) -> (); 47 | } 48 | ); 49 | } 50 | 51 | // These `test_fn_as_type_*` tests test various usages of fn types 52 | 53 | // We do not yet support "fn def" types, which this uses. 54 | #[test] 55 | #[ignore] 56 | fn test_fn_as_type_in_functions() { 57 | //(TODO: cover remaining places when functionality is implemented) 58 | 59 | // Test printing an fn type in a function definitions parameters and return 60 | // type. 61 | reparse_test!( 62 | program { 63 | fn foo(arg: u32); 64 | fn baz(foo) -> u32; 65 | fn bar() -> foo; 66 | } 67 | ); 68 | } 69 | 70 | // We do not yet support "fn def" types, which this uses. 71 | #[test] 72 | #[ignore] 73 | fn test_fn_as_type_in_opaque_ty_value() { 74 | // Test printing an fn type as an opaque type's hidden value 75 | reparse_test!( 76 | program { 77 | trait Bar {} 78 | fn foo(); 79 | impl Bar for Foo {} 80 | opaque type Zed: Bar = foo; 81 | } 82 | ); 83 | } 84 | 85 | // We do not yet support "fn def" types, which this uses. 86 | #[test] 87 | #[ignore] 88 | fn test_fn_as_type_in_struct_field() { 89 | // Test printing an fn type as a struct type's field 90 | reparse_test!( 91 | program { 92 | fn foo(); 93 | struct Vi { 94 | field: foo 95 | } 96 | } 97 | ); 98 | } 99 | -------------------------------------------------------------------------------- /tests/display/impl_.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_negative_auto_trait_impl() { 3 | // Test we can render negative impls. 4 | reparse_test!( 5 | program { 6 | struct Foo { } 7 | #[auto] 8 | trait Baz {} 9 | impl !Baz for Foo { } 10 | } 11 | ); 12 | } 13 | 14 | #[test] 15 | fn test_generic_impl() { 16 | // Tests we can print generics in impl blocks 17 | reparse_test!( 18 | program { 19 | trait Baz {} 20 | impl Baz for T {} 21 | } 22 | ); 23 | } 24 | 25 | #[test] 26 | fn test_impl_for_generic_adt() { 27 | // Test that we can refer to impl-introduced generics in the impl decl 28 | reparse_test!( 29 | program { 30 | trait Bar {} 31 | impl Bar for G {} 32 | } 33 | ); 34 | } 35 | 36 | #[test] 37 | fn test_upstream_impl_keyword() { 38 | // Test we print the "upstream" keyword. 39 | reparse_test!( 40 | program { 41 | struct Bar {} 42 | trait Foo {} 43 | #[upstream] 44 | impl Foo for Bar {} 45 | } 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /tests/display/lifetimes.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_various_forall() { 3 | // Test we print lifetime vars introduced by 'forall' in various situations. 4 | reparse_test!( 5 | program { 6 | struct Foo<'b> where forall<'a> Foo<'a>: Baz<'a> { } 7 | trait Baz<'a> {} 8 | trait Bax<'a> {} 9 | trait Biz { 10 | type Bex: forall<'a> Bax<'a>; 11 | } 12 | impl<'a> Baz<'a> for for<'b> fn(Foo<'b>) { } 13 | impl<'a> Bax<'a> for fn(Foo<'a>) { } 14 | impl<'a> Bax<'a> for dyn forall<'b> Baz<'b> + 'a { } 15 | } 16 | ); 17 | } 18 | 19 | #[test] 20 | fn test_lifetimes_in_structs() { 21 | // Test printing lifetimes introduced by structs. 22 | reparse_test!( 23 | program { 24 | struct Foo<'b> { } 25 | trait Baz<'a> {} 26 | impl<'a> Baz<'a> for Foo<'a> { } 27 | } 28 | ); 29 | } 30 | 31 | #[test] 32 | fn test_lifetime_outlives() { 33 | // Test printing lifetime outlives where clauses in a few places they can appear. 34 | reparse_test!( 35 | program { 36 | struct Foo<'a, 'b> 37 | where 38 | 'a: 'b 39 | { } 40 | 41 | trait Baz<'a, 'b> 42 | where 43 | 'a: 'b 44 | { } 45 | 46 | impl<'a, 'b, 'c> Baz<'a, 'b> for Foo<'a, 'c> 47 | where 48 | 'a: 'c, 49 | 'b: 'c 50 | { } 51 | } 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /tests/display/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod util; 3 | 4 | mod assoc_ty; 5 | mod built_ins; 6 | mod const_; 7 | mod dyn_; 8 | mod enum_; 9 | mod fn_; 10 | mod formatting; 11 | mod impl_; 12 | mod lifetimes; 13 | mod opaque_ty; 14 | mod self_; 15 | mod struct_; 16 | mod trait_; 17 | mod unique_names; 18 | mod where_clauses; 19 | 20 | use self::util::*; 21 | -------------------------------------------------------------------------------- /tests/display/opaque_ty.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn opaque_types() { 3 | // Test printing opaque type declarations, opaque types in associated types, 4 | // and opaque types in impls. 5 | reparse_test!( 6 | program { 7 | struct Bar {} 8 | trait Buz {} 9 | trait Baz { 10 | type Hi; 11 | } 12 | impl Buz for Bar {} 13 | impl Baz for Foo { 14 | type Hi = Foo; 15 | } 16 | opaque type Foo: Buz = Bar; 17 | } 18 | ); 19 | } 20 | 21 | #[test] 22 | fn opaque_ty_no_bounds() { 23 | // Test printing opaque types without any bounds 24 | reparse_test!( 25 | program { 26 | opaque type Foo = (); 27 | } 28 | ); 29 | } 30 | 31 | #[test] 32 | fn test_generic_opaque_types() { 33 | // Test printing opaque types with generic parameters 34 | reparse_test!( 35 | program { 36 | struct Foo {} 37 | trait Bar {} 38 | opaque type Baz: Bar = Foo; 39 | 40 | struct Fou {} 41 | struct Unit {} 42 | trait Bau {} 43 | opaque type Boz: Bau = Fou; 44 | } 45 | ); 46 | } 47 | 48 | #[test] 49 | fn test_opaque_type_as_type_value() { 50 | // Test printing an opaque type as the value for an associated type 51 | reparse_test!( 52 | program { 53 | struct Foo {} 54 | trait Bar {} 55 | trait Fuzz { 56 | type Assoc: Bar; 57 | } 58 | impl Bar for Foo {} 59 | impl Fuzz for Foo { 60 | type Assoc = Bax; 61 | } 62 | opaque type Bax: Bar = Foo; 63 | } 64 | ); 65 | } 66 | 67 | #[test] 68 | fn test_opaque_type_in_fn_ptr() { 69 | // Test printing an opaque type as the parameter for a fn ptr type 70 | reparse_test!( 71 | program { 72 | struct Foo {} 73 | trait Bar {} 74 | trait Faz { 75 | type Assoc; 76 | } 77 | impl Faz for Foo { 78 | type Assoc = fn(Baz); 79 | } 80 | opaque type Baz: Bar = Foo; 81 | } 82 | ); 83 | } 84 | 85 | #[test] 86 | fn test_generic_opaque_type_as_value() { 87 | // Test printing a generic opaque type as an associated type's value 88 | reparse_test!( 89 | program { 90 | struct Foo {} 91 | trait Bar {} 92 | trait Fizz { 93 | type Assoc: Bar; 94 | } 95 | impl Bar for Foo {} 96 | impl Fizz for Foo { 97 | type Assoc = Baz; 98 | } 99 | opaque type Baz: Bar = Foo; 100 | } 101 | ); 102 | } 103 | 104 | #[test] 105 | fn test_generic_opaque_type_in_fn_ptr() { 106 | // Test printing a generic opaque type as an fn ptr's parameter 107 | reparse_test!( 108 | program { 109 | struct Foo {} 110 | trait Bar {} 111 | trait Faz { 112 | type Assoc; 113 | } 114 | impl Faz for Foo { 115 | type Assoc = fn(Baz); 116 | } 117 | impl Bar for Foo { } 118 | opaque type Baz: Bar = Foo; 119 | } 120 | ); 121 | } 122 | 123 | #[test] 124 | fn multiple_bounds() { 125 | // Test printing an opaque type with multiple bounds 126 | reparse_test!( 127 | program { 128 | struct Baz {} 129 | trait Foo {} 130 | trait Fuu {} 131 | opaque type Bar: Foo + Fuu = Baz; 132 | } 133 | ); 134 | } 135 | -------------------------------------------------------------------------------- /tests/display/struct_.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_simple_struct() { 3 | // Test simplest struct 4 | reparse_test!( 5 | program { 6 | struct Foo {} 7 | } 8 | ); 9 | } 10 | 11 | #[test] 12 | fn test_generic_struct() { 13 | // Test printing struct generics 14 | reparse_test!( 15 | program { 16 | struct Foo {} 17 | struct Bar {} 18 | } 19 | ); 20 | } 21 | 22 | #[test] 23 | fn test_struct_where_clauses() { 24 | // Test printing struct where clauses 25 | reparse_test!( 26 | program { 27 | struct Foo where T: Trait {} 28 | trait Trait {} 29 | } 30 | ); 31 | } 32 | 33 | #[test] 34 | fn test_struct_fields() { 35 | // Test printing fields in a struct 36 | reparse_test!( 37 | program { 38 | struct Foo {} 39 | struct Bar {} 40 | struct Baz { 41 | x: Foo, 42 | b: Bar 43 | } 44 | } 45 | ); 46 | } 47 | 48 | #[test] 49 | fn test_struct_generic_fields() { 50 | // Test printing fields which reference a struct's generics 51 | reparse_test!( 52 | program { 53 | struct Foo<'a, T, U> { 54 | x: (U, T), 55 | y: &'a (), 56 | } 57 | } 58 | ); 59 | } 60 | 61 | #[test] 62 | fn test_struct_keywords() { 63 | // Test each struct keyword, as well as the combination. 64 | reparse_test!( 65 | program { 66 | #[upstream] 67 | struct UpstreamFoo {} 68 | 69 | #[fundamental] 70 | struct FundamentalFoo {} 71 | 72 | #[phantom_data] 73 | struct PhantomFoo {} 74 | 75 | #[upstream] 76 | #[fundamental] 77 | #[phantom_data] 78 | struct Bar {} 79 | } 80 | ); 81 | } 82 | 83 | #[test] 84 | fn test_struct_repr() { 85 | // Test each struct repr, as well as the combination of two in any ordering. 86 | reparse_test!( 87 | program { 88 | #[repr(C)] 89 | struct CFoo {} 90 | 91 | #[repr(packed)] 92 | struct PackedFoo {} 93 | 94 | #[repr(C)] 95 | #[repr(packed)] 96 | struct CPackedFoo {} 97 | 98 | #[repr(packed)] 99 | #[repr(C)] 100 | struct PackedCFoo {} 101 | } 102 | ); 103 | } 104 | 105 | #[test] 106 | fn test_struct_repr_with_flags() { 107 | // Test printing both a repr and a flag (to ensure we get the ordering between them right). 108 | reparse_test!( 109 | program { 110 | #[upstream] 111 | #[repr(C)] 112 | struct UpstreamCFoo {} 113 | } 114 | ); 115 | } 116 | -------------------------------------------------------------------------------- /tests/display/trait_.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn test_simple_trait() { 5 | // Simplest test for 'trait' 6 | reparse_test!( 7 | program { 8 | trait Foo {} 9 | } 10 | ); 11 | } 12 | 13 | #[test] 14 | fn test_generic_trait() { 15 | // Test we can print generics introduced by a trait 16 | reparse_test!( 17 | program { 18 | trait Foo {} 19 | trait Bar {} 20 | } 21 | ); 22 | } 23 | 24 | #[test] 25 | fn test_trait_where_clauses() { 26 | // Test printing trait where clauses 27 | reparse_test!( 28 | program { 29 | trait Foo where T: Trait {} 30 | trait Trait {} 31 | } 32 | ); 33 | } 34 | 35 | #[test] 36 | fn test_basic_trait_impl() { 37 | // Test simplest trait implementation 38 | reparse_test!( 39 | program { 40 | struct Foo { } 41 | trait Bar {} 42 | impl Bar for Foo { } 43 | } 44 | ); 45 | } 46 | 47 | #[test] 48 | fn test_trait_flags() { 49 | // Test every individual flag that can appear on a trait, as well as the 50 | // combination of all of them. We test the combination to ensure that we 51 | // satisfy any ordering requirements present. 52 | let flags = vec![ 53 | "auto", 54 | "marker", 55 | "upstream", 56 | "fundamental", 57 | "non_enumerable", 58 | "coinductive", 59 | "object_safe", 60 | ]; 61 | reparse_test(&format!( 62 | "{}trait Hello {{}}", 63 | flags 64 | .iter() 65 | .map(|f| format!("#[{}]", f)) 66 | .collect::>() 67 | .join("\n") 68 | )); 69 | for flag in flags { 70 | reparse_test(&format!( 71 | " 72 | #[{0}] 73 | trait Hello_{0} {{}} 74 | ", 75 | flag 76 | )); 77 | } 78 | } 79 | 80 | #[test] 81 | fn test_wellknown_traits() { 82 | // Test all possible `#[lang]` attributes on traits. 83 | let well_knowns = vec![ 84 | "sized", "copy", "clone", "drop", "fn_once", "fn_mut", "fn", "unsize", 85 | ]; 86 | for flag in well_knowns { 87 | reparse_test(&format!( 88 | " 89 | #[lang({0})] 90 | trait Hello_{0} {{}} 91 | ", 92 | flag 93 | )); 94 | } 95 | } 96 | 97 | #[test] 98 | fn test_lang_with_flag() { 99 | // Test we output the correct ordering when printing a trait with both flags 100 | // and a #[lang] attribute. 101 | reparse_test!( 102 | program { 103 | #[auto] 104 | #[lang(sized)] 105 | trait Foo { 106 | 107 | } 108 | } 109 | ); 110 | } 111 | -------------------------------------------------------------------------------- /tests/integration/mod.rs: -------------------------------------------------------------------------------- 1 | mod panic; 2 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod test_util; 3 | #[macro_use] 4 | mod test; 5 | 6 | mod display; 7 | mod logging_db; 8 | mod lowering; 9 | 10 | mod integration; 11 | -------------------------------------------------------------------------------- /tests/test/ambiguity_issue_727.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn issue_727_1() { 5 | test!( 6 | program { 7 | #[upstream] #[non_enumerable] #[lang(sized)] 8 | trait Sized {} 9 | 10 | #[non_enumerable] #[object_safe] 11 | trait Database {} 12 | 13 | #[non_enumerable] 14 | trait QueryGroup 15 | where 16 | Self: Sized, 17 | { 18 | type DynDb: Database + HasQueryGroup; 19 | } 20 | 21 | #[non_enumerable] #[object_safe] 22 | trait HasQueryGroup 23 | where 24 | Self: Database, 25 | G: QueryGroup, 26 | G: Sized, 27 | { } 28 | 29 | #[non_enumerable] #[object_safe] 30 | trait HelloWorld 31 | where 32 | Self: HasQueryGroup, 33 | { } 34 | 35 | struct HelloWorldStorage {} 36 | 37 | impl QueryGroup for HelloWorldStorage { 38 | type DynDb = dyn HelloWorld + 'static; 39 | } 40 | impl HelloWorld for DB 41 | where 42 | DB: Database, 43 | DB: HasQueryGroup, 44 | DB: Sized, 45 | { } 46 | } 47 | 48 | goal { 49 | forall { 50 | if (FromEnv(T: Database); FromEnv(T: HasQueryGroup); FromEnv(T: Sized)) { 51 | T: HelloWorld 52 | } 53 | } 54 | } yields[SolverChoice::slg_default()] { // ok 55 | expect![["Unique"]] 56 | } yields[SolverChoice::recursive_default()] { // fails: "Ambiguous; no inference guidance" 57 | expect![["Unique"]] 58 | } 59 | ); 60 | } 61 | 62 | #[test] 63 | fn issue_727_2() { 64 | test!( 65 | program { 66 | #[non_enumerable] #[object_safe] 67 | trait Database {} 68 | 69 | #[non_enumerable] 70 | trait QueryGroup 71 | { 72 | type DynDb: Database + HasQueryGroup; 73 | } 74 | 75 | #[non_enumerable] #[object_safe] 76 | trait HasQueryGroup 77 | where 78 | Self: Database, 79 | G: QueryGroup, 80 | { } 81 | 82 | struct HelloWorldStorage {} 83 | 84 | impl QueryGroup for HelloWorldStorage { 85 | type DynDb = dyn HasQueryGroup + 'static; 86 | } 87 | } 88 | 89 | goal { 90 | forall { 91 | if (FromEnv(T: HasQueryGroup)) { 92 | T: Database 93 | } 94 | } 95 | } yields[SolverChoice::slg_default()] { 96 | expect![["Unique"]] 97 | } yields[SolverChoice::recursive_default()] { 98 | expect![[r#"Ambiguous; no inference guidance"#]] // FIXME rust-lang/chalk#727 99 | } 100 | ); 101 | } 102 | 103 | #[test] 104 | fn issue_727_3() { 105 | test!( 106 | program { 107 | #[non_enumerable] 108 | trait Database {} 109 | 110 | #[non_enumerable] 111 | trait HasQueryGroup 112 | where 113 | Self: Database, 114 | { } 115 | 116 | struct HelloWorldStorage {} 117 | 118 | impl Database for HelloWorldStorage { } 119 | } 120 | 121 | goal { 122 | forall { 123 | if (FromEnv(HelloWorldStorage: HasQueryGroup); FromEnv(HelloWorldStorage: HasQueryGroup)) { 124 | HelloWorldStorage: Database 125 | } 126 | } 127 | } yields[SolverChoice::slg_default()] { 128 | expect![["Unique"]] 129 | } yields[SolverChoice::recursive_default()] { 130 | expect![["Unique"]] 131 | } 132 | ); 133 | } 134 | -------------------------------------------------------------------------------- /tests/test/arrays.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn arrays_are_sized() { 5 | test! { 6 | program { 7 | #[lang(sized)] 8 | trait Sized { } 9 | } 10 | 11 | goal { 12 | forall { 13 | [u32; N]: Sized 14 | } 15 | } yields { 16 | expect![["Unique"]] 17 | } 18 | 19 | } 20 | } 21 | 22 | #[test] 23 | fn arrays_are_copy_if_element_copy() { 24 | test! { 25 | program { 26 | #[lang(copy)] 27 | trait Copy { } 28 | 29 | struct Foo { } 30 | impl Copy for Foo { } 31 | } 32 | 33 | goal { 34 | forall { 35 | [Foo; N]: Copy 36 | } 37 | } yields { 38 | expect![["Unique"]] 39 | } 40 | } 41 | } 42 | 43 | #[test] 44 | fn arrays_are_not_copy_if_element_not_copy() { 45 | test! { 46 | program { 47 | #[lang(copy)] 48 | trait Copy { } 49 | 50 | struct Foo { } 51 | } 52 | 53 | goal { 54 | forall { 55 | [Foo; N]: Copy 56 | } 57 | } yields { 58 | expect![["No possible solution"]] 59 | } 60 | } 61 | } 62 | 63 | #[test] 64 | fn arrays_are_clone_if_element_clone() { 65 | test! { 66 | program { 67 | #[lang(clone)] 68 | trait Clone { } 69 | 70 | struct Foo { } 71 | impl Clone for Foo { } 72 | } 73 | 74 | goal { 75 | forall { 76 | [Foo; N]: Clone 77 | } 78 | } yields { 79 | expect![["Unique"]] 80 | } 81 | } 82 | } 83 | 84 | #[test] 85 | fn arrays_are_not_clone_if_element_not_clone() { 86 | test! { 87 | program { 88 | #[lang(clone)] 89 | trait Clone { } 90 | 91 | struct Foo { } 92 | } 93 | 94 | goal { 95 | forall { 96 | [Foo; N]: Clone 97 | } 98 | } yields { 99 | expect![["No possible solution"]] 100 | } 101 | } 102 | } 103 | 104 | #[test] 105 | fn arrays_are_well_formed_if_elem_sized() { 106 | test! { 107 | program { 108 | #[lang(sized)] 109 | trait Sized { } 110 | } 111 | 112 | goal { 113 | forall { 114 | if (T: Sized) { 115 | WellFormed([T; N]) 116 | } 117 | } 118 | } yields { 119 | expect![["Unique"]] 120 | } 121 | 122 | goal { 123 | forall { 124 | WellFormed([T; N]) 125 | } 126 | } yields { 127 | expect![["No possible solution"]] 128 | } 129 | 130 | goal { 131 | exists { 132 | WellFormed([T; N]) 133 | } 134 | } yields { 135 | expect![["Ambiguous; no inference guidance"]] 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /tests/test/bench.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarking tests. 2 | 3 | use self::test::Bencher; 4 | 5 | use chalk_ir; 6 | use chalk_solve::ext::*; 7 | use chalk_solve::SolverChoice; 8 | 9 | use super::{assert_result, parse_and_lower_goal, parse_and_lower_program_with_env}; 10 | 11 | fn run_bench( 12 | program_text: &str, 13 | solver_choice: SolverChoice, 14 | goal_text: &str, 15 | bencher: &mut Bencher, 16 | expected: &str, 17 | ) { 18 | let (program, env) = parse_and_lower_program_with_env(program_text, solver_choice).unwrap(); 19 | chalk_ir::tls::set_current_program(&program, || { 20 | let goal = parse_and_lower_goal(&program, goal_text).unwrap(); 21 | let peeled_goal = goal.into_peeled_goal(); 22 | 23 | // Execute once to get an expected result. 24 | let result = solver_choice.solve_root_goal(&env, &peeled_goal); 25 | 26 | // Check expectation. 27 | assert_result(&result, expected); 28 | 29 | // Then do it many times to measure time. 30 | bencher.iter(|| solver_choice.solve_root_goal(&env, &peeled_goal)); 31 | }); 32 | } 33 | 34 | const CYCLEY: &str = " 35 | trait AsRef { } 36 | trait Clone { } 37 | trait Copy where Self: Clone { } 38 | trait Sized { } 39 | 40 | struct i32 { } 41 | impl Copy for i32 { } 42 | impl Clone for i32 { } 43 | impl Sized for i32 { } 44 | 45 | struct u32 { } 46 | impl Copy for u32 { } 47 | impl Clone for u32 { } 48 | impl Sized for u32 { } 49 | 50 | struct Rc { } 51 | impl Clone for Rc { } 52 | impl Sized for Rc { } 53 | 54 | struct Box { } 55 | impl AsRef for Box where T: Sized { } 56 | impl Clone for Box where T: Clone { } 57 | impl Sized for Box { } 58 | 59 | // Meant to be [T] 60 | struct Slice where T: Sized { } 61 | impl Sized for Slice { } 62 | impl AsRef> for Slice where T: Sized { } 63 | 64 | struct Vec where T: Sized { } 65 | impl AsRef> for Vec where T: Sized { } 66 | impl AsRef> for Vec where T: Sized { } 67 | impl Clone for Vec where T: Clone, T: Sized { } 68 | impl Sized for Vec where T: Sized { } 69 | 70 | trait SliceExt 71 | where ::Item: Clone 72 | { 73 | type Item; 74 | } 75 | 76 | impl SliceExt for Slice 77 | where T: Clone 78 | { 79 | type Item = T; 80 | } 81 | "; 82 | 83 | const CYCLEY_GOAL: &str = " 84 | forall { 85 | if ( 86 | as SliceExt>::Item: Clone; 87 | as SliceExt>::Item: Sized; 88 | T: Clone; 89 | T: Sized 90 | ) { 91 | T: Sized 92 | } 93 | } 94 | "; 95 | 96 | #[bench] 97 | fn cycley_slg(b: &mut Bencher) { 98 | run_bench( 99 | CYCLEY, 100 | SolverChoice::SLG { max_size: 20 }, 101 | CYCLEY_GOAL, 102 | b, 103 | "Unique", 104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /tests/test/constants.rs: -------------------------------------------------------------------------------- 1 | //! Tests related to const generics. 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn single_impl() { 7 | test! { 8 | program { 9 | struct S {} 10 | 11 | trait Trait {} 12 | 13 | impl Trait for S<3> {} 14 | } 15 | 16 | goal { 17 | exists { 18 | S: Trait 19 | } 20 | } yields { 21 | expect![["Unique; substitution [?0 := 3]"]] 22 | } 23 | 24 | goal { 25 | S<3>: Trait 26 | } yields { 27 | expect![["Unique"]] 28 | } 29 | 30 | goal { 31 | S<5>: Trait 32 | } yields { 33 | expect![["No possible solution"]] 34 | } 35 | 36 | 37 | goal { 38 | forall { 39 | S: Trait 40 | } 41 | } yields { 42 | expect![["No possible solution"]] 43 | } 44 | 45 | } 46 | } 47 | 48 | #[test] 49 | fn multi_impl() { 50 | test! { 51 | program { 52 | struct S {} 53 | 54 | trait Trait {} 55 | 56 | impl Trait for S<3> {} 57 | impl Trait for S<5> {} 58 | } 59 | 60 | goal { 61 | exists { 62 | S: Trait 63 | } 64 | } yields { 65 | expect![["Ambiguous; no inference guidance"]] 66 | } 67 | 68 | goal { 69 | forall { 70 | S: Trait 71 | } 72 | } yields { 73 | expect![["No possible solution"]] 74 | } 75 | 76 | } 77 | } 78 | 79 | #[test] 80 | fn generic_impl() { 81 | test! { 82 | program { 83 | struct S {} 84 | 85 | trait Trait {} 86 | 87 | impl Trait for S {} 88 | } 89 | 90 | goal { 91 | exists { 92 | S: Trait 93 | } 94 | } yields { 95 | expect![["Unique; for { substitution [?0 := ^0.0] }"]] 96 | } 97 | 98 | goal { 99 | forall { 100 | S: Trait 101 | } 102 | } yields { 103 | expect![["Unique"]] 104 | } 105 | } 106 | } 107 | 108 | #[test] 109 | fn placeholders_eq() { 110 | test! { 111 | goal { 112 | forall { 113 | C = D 114 | } 115 | } yields { 116 | expect![["No possible solution"]] 117 | } 118 | 119 | goal { 120 | exists { 121 | forall { 122 | C = D 123 | } 124 | } 125 | } yields { 126 | expect![["No possible solution"]] 127 | } 128 | 129 | goal { 130 | forall { 131 | exists { 132 | C = D 133 | } 134 | } 135 | } yields { 136 | expect![["Unique; substitution [?0 := !1_0]"]] 137 | } 138 | 139 | goal { 140 | forall { 141 | exists { 142 | C1 = D1, C2 = D2, D1 = D2 143 | } 144 | } 145 | } yields { 146 | expect![["No possible solution"]] 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /tests/test/coroutines.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn coroutine_test() { 5 | test! { 6 | program { 7 | #[auto] trait Send { } 8 | 9 | #[lang(coroutine)] 10 | trait Coroutine { 11 | type Yield; 12 | type Return; 13 | } 14 | 15 | struct StructOne {} 16 | struct NotSend {} 17 | struct SendSameLifetime<'a, 'b, T> { val: &'a T, other: &'b T } 18 | impl<'a, T> Send for SendSameLifetime<'a, 'a, T> {} 19 | 20 | struct SendAnyLifetime<'a, 'b, T> { val: &'a u8, other: &'b u8, field: T } 21 | 22 | impl !Send for NotSend {} 23 | struct StructThree<'a> { val: &'a () } 24 | 25 | coroutine empty_gen<>[resume = (), yield = ()] { 26 | upvars [] 27 | witnesses [] 28 | } 29 | 30 | coroutine upvar_lifetime_restrict[resume = (), yield = ()] { 31 | upvars [T; StructOne] 32 | witnesses exists<'a, 'b> [SendSameLifetime<'a, 'b, T>] 33 | } 34 | 35 | coroutine send_any_lifetime[resume = (), yield = ()] { 36 | upvars [] 37 | witnesses exists<'a, 'b> [SendAnyLifetime<'a, 'b, T>; u8] 38 | } 39 | 40 | coroutine not_send_resume_yield<>[resume = NotSend, yield = NotSend] { 41 | upvars [] 42 | witnesses [] 43 | } 44 | 45 | coroutine gen_with_types[resume = U, yield = StructOne] -> NotSend { 46 | upvars [] 47 | witnesses [] 48 | } 49 | } 50 | 51 | goal { 52 | WellFormed(empty_gen) 53 | } yields { 54 | expect![["Unique"]] 55 | } 56 | 57 | goal { 58 | empty_gen: Send 59 | } yields { 60 | expect![["Unique"]] 61 | } 62 | 63 | goal { 64 | empty_gen: Coroutine<()> 65 | } yields { 66 | expect![["Unique"]] 67 | } 68 | 69 | goal { 70 | forall { 71 | gen_with_types: Coroutine 72 | } 73 | } yields { 74 | expect![["Unique"]] 75 | } 76 | 77 | goal { 78 | forall { 79 | Normalize( as Coroutine>::Yield -> StructOne) 80 | } 81 | } yields { 82 | expect![["Unique"]] 83 | } 84 | 85 | goal { 86 | forall { 87 | Normalize( as Coroutine>::Return -> NotSend) 88 | } 89 | } yields { 90 | expect![["Unique"]] 91 | } 92 | 93 | goal { 94 | forall { 95 | upvar_lifetime_restrict: Send 96 | } 97 | } yields { 98 | expect![["No possible solution"]] 99 | } 100 | 101 | goal { 102 | forall { 103 | if (T: Send) { 104 | upvar_lifetime_restrict: Send 105 | } 106 | } 107 | } yields { 108 | expect![["Unique; lifetime constraints [InEnvironment { environment: Env([]), goal: '!2_0: '!2_1 }, InEnvironment { environment: Env([]), goal: '!2_1: '!2_0 }]"]] 109 | } 110 | 111 | goal { 112 | not_send_resume_yield: Send 113 | } yields { 114 | expect![["Unique"]] 115 | } 116 | 117 | goal { 118 | forall { 119 | if (T: Send) { 120 | send_any_lifetime: Send 121 | } 122 | } 123 | } yields { 124 | expect![["Unique"]] 125 | } 126 | 127 | goal { 128 | forall { 129 | send_any_lifetime: Send 130 | } 131 | } yields { 132 | expect![["No possible solution"]] 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /tests/test/foreign_types.rs: -------------------------------------------------------------------------------- 1 | //! Tests for foreign types 2 | 3 | use super::*; 4 | 5 | // foreign types don't implement any builtin traits 6 | #[test] 7 | fn foreign_ty_trait_impl() { 8 | test! { 9 | program { 10 | extern type A; 11 | trait Foo {} 12 | impl Foo for A {} 13 | } 14 | 15 | goal { A: Foo } yields { expect![["Unique"]] } 16 | } 17 | } 18 | 19 | #[test] 20 | fn foreign_ty_lowering() { 21 | lowering_success! { 22 | program { 23 | extern type A; 24 | } 25 | } 26 | } 27 | 28 | // foreign types are always well-formed 29 | #[test] 30 | fn foreign_ty_is_well_formed() { 31 | test! { 32 | program { 33 | extern type A; 34 | } 35 | 36 | goal { WellFormed(A) } yields { expect![["Unique"]] } 37 | } 38 | } 39 | 40 | // foreign types don't implement any builtin traits 41 | #[test] 42 | fn foreign_ty_is_not_sized() { 43 | test! { 44 | program { 45 | #[lang(sized)] trait Sized {} 46 | extern type A; 47 | } 48 | 49 | goal { not { A: Sized } } yields { expect![["Unique"]] } 50 | } 51 | } 52 | 53 | // foreign types don't implement any builtin traits 54 | #[test] 55 | fn foreign_ty_is_not_copy() { 56 | test! { 57 | program { 58 | #[lang(copy)] trait Copy {} 59 | extern type A; 60 | } 61 | 62 | goal { not { A: Copy } } yields { expect![["Unique"]] } 63 | } 64 | } 65 | 66 | // foreign types don't implement any builtin traits 67 | #[test] 68 | fn foreign_ty_is_not_clone() { 69 | test! { 70 | program { 71 | #[lang(clone)] trait Clone {} 72 | extern type A; 73 | } 74 | 75 | goal { not { A: Clone } } yields { expect![["Unique"]] } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/test/implied_bounds.rs: -------------------------------------------------------------------------------- 1 | //! Tests related to the implied bounds rules. 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn implied_bounds() { 7 | test! { 8 | program { 9 | trait Clone { } 10 | trait Iterator where Self: Clone { type Item; } 11 | struct Struct { } 12 | } 13 | 14 | goal { 15 | forall { 16 | if (T: Iterator) { 17 | T: Clone 18 | } 19 | } 20 | } yields { 21 | expect![["Unique"]] 22 | } 23 | } 24 | } 25 | 26 | #[test] 27 | fn gat_implied_bounds() { 28 | test! { 29 | program { 30 | trait Clone { } 31 | trait Foo { type Item: Clone; } 32 | struct Struct { } 33 | } 34 | 35 | goal { 36 | forall { 37 | if (T: Foo = V>) { 38 | V: Clone 39 | } 40 | } 41 | } yields { 42 | expect![["Unique"]] 43 | } 44 | } 45 | 46 | test! { 47 | program { 48 | trait Clone { } 49 | trait Foo { type Item; } 50 | struct Struct { } 51 | } 52 | 53 | goal { 54 | forall { 55 | if (T: Foo = V>) { 56 | // Without the bound Item: Clone, there is no way to infer this. 57 | V: Clone 58 | } 59 | } 60 | } yields { 61 | expect![["No possible solution"]] 62 | } 63 | } 64 | 65 | test! { 66 | program { 67 | trait Fn { } 68 | struct Ref<'a, T> { } 69 | trait Sized { } 70 | 71 | trait Foo { 72 | type Item: forall<'a> Fn> + Sized; 73 | } 74 | } 75 | 76 | goal { 77 | forall { 78 | if (Type: Foo) { 79 | forall<'a, T> { 80 | ::Item: Fn> 81 | } 82 | } 83 | } 84 | } yields { 85 | expect![["Unique"]] 86 | } 87 | } 88 | } 89 | 90 | #[test] 91 | fn implied_from_env() { 92 | test! { 93 | program { 94 | trait Clone { } 95 | trait Foo { type Item; } 96 | } 97 | 98 | goal { 99 | forall { 100 | if (FromEnv(>::Item)) { 101 | FromEnv(T: Foo) 102 | } 103 | } 104 | } yields { 105 | expect![["Unique"]] 106 | } 107 | 108 | goal { 109 | forall { 110 | if (FromEnv(>::Item)) { 111 | FromEnv(T: Clone) 112 | } 113 | } 114 | } yields { 115 | expect![["No possible solution"]] 116 | } 117 | } 118 | } 119 | 120 | #[test] 121 | fn higher_ranked_implied_bounds() { 122 | test! { 123 | program { 124 | trait Foo<'a> { } 125 | trait Bar where forall<'a> Self: Foo<'a> { } 126 | } 127 | 128 | goal { 129 | forall { 130 | if (T: Bar) { 131 | forall<'a> { 132 | T: Foo<'a> 133 | } 134 | } 135 | } 136 | } yields { 137 | expect![["Unique"]] 138 | } 139 | } 140 | 141 | test! { 142 | program { 143 | trait Foo { } 144 | trait Bar where forall Self: Foo { } 145 | } 146 | 147 | goal { 148 | forall { 149 | if (T: Bar) { 150 | forall { 151 | T: Foo 152 | } 153 | } 154 | } 155 | } yields { 156 | expect![["Unique"]] 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /tests/test/lifetimes.rs: -------------------------------------------------------------------------------- 1 | //! Tests for various concrete lifetimes 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn static_lowering() { 7 | lowering_success! { 8 | program { 9 | struct A<'a> where 'a: 'static {} 10 | trait B<'a> where 'a: 'static {} 11 | fn foo(a: &'static ()); 12 | } 13 | } 14 | } 15 | 16 | #[test] 17 | fn erased_lowering() { 18 | lowering_success! { 19 | program { 20 | struct A<'a> where 'a: 'erased {} 21 | trait B<'a> where 'a: 'erased {} 22 | fn foo(a: &'erased ()); 23 | } 24 | } 25 | } 26 | 27 | #[test] 28 | fn static_outlives() { 29 | test! { 30 | program { 31 | trait Foo<'a> where 'a: 'static {} 32 | struct Bar {} 33 | 34 | impl<'a> Foo<'a> for Bar where 'a: 'static {} 35 | } 36 | 37 | goal { 38 | exists<'a> { 39 | Bar: Foo<'a> 40 | } 41 | } yields { 42 | expect![["Unique; for { substitution [?0 := '^0.0], lifetime constraints [InEnvironment { environment: Env([]), goal: '^0.0: 'static }] }"]] 43 | } 44 | 45 | goal { 46 | forall<'a> { 47 | Bar: Foo<'a> 48 | } 49 | } yields { 50 | expect![["Unique; lifetime constraints [InEnvironment { environment: Env([]), goal: '!1_0: 'static }]"]] 51 | } 52 | } 53 | } 54 | 55 | #[test] 56 | fn erased_outlives() { 57 | test! { 58 | program { 59 | trait Foo<'a> where 'a: 'erased {} 60 | struct Bar {} 61 | 62 | impl<'a> Foo<'a> for Bar where 'a: 'erased {} 63 | } 64 | 65 | goal { 66 | exists<'a> { 67 | Bar: Foo<'a> 68 | } 69 | } yields { 70 | expect![["Unique; for { substitution [?0 := '^0.0], lifetime constraints [InEnvironment { environment: Env([]), goal: '^0.0: ' }] }"]] 71 | } 72 | 73 | goal { 74 | forall<'a> { 75 | Bar: Foo<'a> 76 | } 77 | } yields { 78 | expect![["Unique; lifetime constraints [InEnvironment { environment: Env([]), goal: '!1_0: ' }]"]] 79 | } 80 | } 81 | } 82 | 83 | #[test] 84 | fn static_impls() { 85 | test! { 86 | program { 87 | struct Foo {} 88 | trait Bar {} 89 | impl<'a> Bar for &'a Foo {} 90 | } 91 | 92 | goal { 93 | &'static Foo: Bar 94 | } yields { 95 | expect![["Unique"]] 96 | } 97 | 98 | goal { 99 | forall<'a> { &'a Foo: Bar } 100 | } yields { 101 | expect![["Unique"]] 102 | } 103 | 104 | goal { 105 | exists<'a> { &'a Foo: Bar } 106 | } yields { 107 | expect![["Unique; for { substitution [?0 := '^0.0] }"]] 108 | } 109 | } 110 | } 111 | 112 | #[test] 113 | fn erased_impls() { 114 | test! { 115 | program { 116 | struct Foo {} 117 | trait Bar {} 118 | impl<'a> Bar for &'a Foo {} 119 | } 120 | 121 | goal { 122 | &'erased Foo: Bar 123 | } yields { 124 | expect![["Unique"]] 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tests/test/never.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn never_is_well_formed() { 5 | test! { 6 | goal { 7 | WellFormed(!) 8 | } yields { 9 | expect![["Unique"]] 10 | } 11 | } 12 | } 13 | 14 | #[test] 15 | fn never_is_sized() { 16 | test! { 17 | program { 18 | #[lang(sized)] 19 | trait Sized { } 20 | } 21 | goal { 22 | !: Sized 23 | } yields { 24 | expect![["Unique"]] 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/test/object_safe.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn object_safe_flag() { 5 | test! { 6 | program { 7 | #[object_safe] 8 | trait Foo {} 9 | trait Bar {} 10 | } 11 | 12 | goal { ObjectSafe(Foo) } yields { expect![["Unique"]] } 13 | goal { not { ObjectSafe(Bar) } } yields { expect![["Unique"]] } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/test/pointee.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn last_field_metadata() { 5 | test! { 6 | program { 7 | #[lang(pointee_trait)] 8 | trait Pointee { 9 | type Metadata; 10 | } 11 | 12 | struct S { 13 | field1: i32, 14 | field2: [i32], 15 | } 16 | } 17 | 18 | goal { 19 | Normalize(<(i32, str) as Pointee>::Metadata -> usize) 20 | } yields { 21 | expect![["Unique"]] 22 | } 23 | 24 | goal { 25 | Normalize(<(u8, i64) as Pointee>::Metadata -> ()) 26 | } yields { 27 | expect![["Unique"]] 28 | } 29 | 30 | goal { 31 | Normalize(<() as Pointee>::Metadata -> ()) 32 | } yields { 33 | expect![["Unique"]] 34 | } 35 | 36 | goal { 37 | Normalize(::Metadata -> usize) 38 | } yields { 39 | expect![["Unique"]] 40 | } 41 | 42 | goal { 43 | Normalize(<((), S) as Pointee>::Metadata -> usize) 44 | } yields { 45 | expect![["Unique"]] 46 | } 47 | } 48 | } 49 | 50 | #[test] 51 | fn primitives() { 52 | test! { 53 | program { 54 | #[lang(pointee_trait)] 55 | trait Pointee { 56 | type Metadata; 57 | } 58 | } 59 | 60 | goal { 61 | Normalize(::Metadata -> usize) 62 | } yields { 63 | expect![["Unique"]] 64 | } 65 | 66 | goal { 67 | Normalize(::Metadata -> ()) 68 | } yields { 69 | expect![["No possible solution"]] 70 | } 71 | 72 | goal { 73 | Normalize(::Metadata -> ()) 74 | } yields { 75 | expect![["Unique"]] 76 | } 77 | 78 | goal { 79 | Normalize(::Metadata -> ()) 80 | } yields { 81 | expect![["Unique"]] 82 | } 83 | } 84 | } 85 | 86 | #[test] 87 | fn everything_is_pointee() { 88 | test! { 89 | program { 90 | #[lang(pointee_trait)] 91 | trait Pointee { 92 | type Metadata; 93 | } 94 | } 95 | 96 | goal { 97 | forall { 98 | T: Pointee 99 | } 100 | } yields { 101 | expect![["Unique"]] 102 | } 103 | 104 | goal { 105 | forall { 106 | Normalize(::Metadata -> usize) 107 | } 108 | } yields { 109 | expect![["No possible solution"]] 110 | } 111 | } 112 | } 113 | 114 | #[test] 115 | fn slices() { 116 | test! { 117 | program { 118 | #[lang(pointee_trait)] 119 | trait Pointee { 120 | type Metadata; 121 | } 122 | 123 | struct S {} 124 | } 125 | 126 | goal { 127 | [S]: Pointee 128 | } yields { 129 | expect![["Unique"]] 130 | } 131 | 132 | goal { 133 | Normalize(<[S] as Pointee>::Metadata -> usize) 134 | } yields { 135 | expect![["Unique"]] 136 | } 137 | 138 | goal { 139 | Normalize(<[S] as Pointee>::Metadata -> ()) 140 | } yields { 141 | expect![["No possible solution"]] 142 | } 143 | 144 | goal { 145 | forall { 146 | Normalize(<[T] as Pointee>::Metadata -> usize) 147 | } 148 | } yields { 149 | expect![["Unique"]] 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /tests/test/refs.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn immut_refs_are_well_formed() { 5 | test! { 6 | program { 7 | struct A { } 8 | } 9 | 10 | goal { 11 | forall<'a, T> { 12 | WellFormed(&'a T) 13 | } 14 | } yields { 15 | expect![["Unique; lifetime constraints [InEnvironment { environment: Env([]), goal: !1_1: '!1_0 }]"]] 16 | } 17 | 18 | goal { 19 | exists<'a> { 20 | WellFormed(&'a A) 21 | } 22 | } yields { 23 | expect![["Unique; for { substitution [?0 := '^0.0], lifetime constraints [InEnvironment { environment: Env([]), goal: A: '^0.0 }] }"]] 24 | } 25 | } 26 | } 27 | 28 | #[test] 29 | fn immut_refs_are_sized() { 30 | test! { 31 | program { 32 | #[lang(sized)] 33 | trait Sized { } 34 | } 35 | 36 | goal { 37 | forall<'a, T> { &'a T: Sized } 38 | } yields { 39 | expect![["Unique"]] 40 | } 41 | } 42 | } 43 | 44 | #[test] 45 | fn mut_refs_are_well_formed() { 46 | test! { 47 | goal { 48 | forall<'a, T> { WellFormed(&'a mut T) } 49 | } yields { 50 | expect![["Unique; lifetime constraints [InEnvironment { environment: Env([]), goal: !1_1: '!1_0 }]"]] 51 | } 52 | } 53 | } 54 | 55 | #[test] 56 | fn mut_refs_are_sized() { 57 | test! { 58 | program { 59 | #[lang(sized)] 60 | trait Sized { } 61 | } 62 | 63 | goal { 64 | forall<'a, T> { &'a mut T: Sized } 65 | } yields { 66 | expect![["Unique"]] 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/test/slices.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn slices_are_not_sized() { 5 | test! { 6 | program { 7 | #[lang(sized)] 8 | trait Sized { } 9 | } 10 | 11 | goal { 12 | forall { not { [T]: Sized } } 13 | } yields { 14 | expect![["Unique"]] 15 | } 16 | } 17 | } 18 | 19 | #[test] 20 | fn slices_are_well_formed_if_elem_sized() { 21 | test! { 22 | program { 23 | #[lang(sized)] 24 | trait Sized { } 25 | } 26 | 27 | goal { 28 | forall { if (T: Sized) { WellFormed([T]) } } 29 | } yields { 30 | expect![["Unique"]] 31 | } 32 | 33 | goal { 34 | forall { WellFormed([T]) } 35 | } yields { 36 | expect![["No possible solution"]] 37 | } 38 | } 39 | } 40 | 41 | #[test] 42 | fn slices_are_not_copy() { 43 | test! { 44 | program { 45 | #[lang(copy)] 46 | trait Copy { } 47 | } 48 | 49 | goal { 50 | forall { not { [T]: Copy } } 51 | } yields { 52 | expect![["Unique"]] 53 | } 54 | } 55 | } 56 | 57 | #[test] 58 | fn slices_are_not_clone() { 59 | test! { 60 | program { 61 | #[lang(clone)] 62 | trait Clone { } 63 | } 64 | 65 | goal { 66 | forall { not { [T]: Clone } } 67 | } yields { 68 | expect![["Unique"]] 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/test/string.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn str_trait_impl() { 5 | test! { 6 | program { 7 | trait Foo {} 8 | impl Foo for str {} 9 | } 10 | 11 | goal { str: Foo } yields { expect![["Unique"]] } 12 | } 13 | } 14 | 15 | #[test] 16 | fn str_is_well_formed() { 17 | test! { 18 | goal { WellFormed(str) } yields { expect![["Unique"]] } 19 | } 20 | } 21 | 22 | #[test] 23 | fn str_is_not_sized() { 24 | test! { 25 | program { 26 | #[lang(sized)] trait Sized {} 27 | } 28 | 29 | goal { not { str: Sized } } yields { expect![["Unique"]] } 30 | } 31 | } 32 | 33 | #[test] 34 | fn str_is_not_copy() { 35 | test! { 36 | program { 37 | #[lang(copy)] trait Copy {} 38 | } 39 | 40 | goal { not { str: Copy } } yields { expect![["Unique"]] } 41 | } 42 | } 43 | 44 | #[test] 45 | fn str_is_not_clone() { 46 | test! { 47 | program { 48 | #[lang(clone)] trait Clone {} 49 | } 50 | 51 | goal { not { str: Clone } } yields { expect![["Unique"]] } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/test/type_flags.rs: -------------------------------------------------------------------------------- 1 | use chalk_integration::interner::ChalkIr; 2 | use chalk_integration::{empty_substitution, lifetime, ty}; 3 | use chalk_ir::cast::Cast; 4 | use chalk_ir::{PlaceholderIndex, TyKind, TypeFlags, UniverseIndex}; 5 | 6 | #[test] 7 | fn placeholder_ty_flags_correct() { 8 | let placeholder_ty = ty!(placeholder 0); 9 | assert_eq!( 10 | placeholder_ty.data(ChalkIr).flags, 11 | TypeFlags::HAS_TY_PLACEHOLDER 12 | ); 13 | } 14 | 15 | #[test] 16 | fn opaque_ty_flags_correct() { 17 | let opaque_ty = TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { 18 | opaque_ty_id: chalk_ir::OpaqueTyId(chalk_integration::interner::RawId { index: 0 }), 19 | substitution: chalk_ir::Substitution::from_iter( 20 | ChalkIr, 21 | Some( 22 | chalk_ir::ConstData { 23 | ty: TyKind::Placeholder(PlaceholderIndex { 24 | ui: chalk_ir::UniverseIndex::ROOT, 25 | idx: 0, 26 | }) 27 | .intern(ChalkIr), 28 | value: chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(0)), 29 | } 30 | .intern(ChalkIr) 31 | .cast(ChalkIr), 32 | ), 33 | ), 34 | })) 35 | .intern(ChalkIr); 36 | assert_eq!( 37 | opaque_ty.data(ChalkIr).flags, 38 | TypeFlags::HAS_TY_OPAQUE 39 | | TypeFlags::HAS_CT_INFER 40 | | TypeFlags::STILL_FURTHER_SPECIALIZABLE 41 | | TypeFlags::HAS_TY_PLACEHOLDER 42 | ); 43 | } 44 | 45 | #[test] 46 | fn dyn_ty_flags_correct() { 47 | let internal_ty = TyKind::Scalar(chalk_ir::Scalar::Bool).intern(ChalkIr); 48 | let projection_ty = chalk_ir::ProjectionTy { 49 | associated_ty_id: chalk_ir::AssocTypeId(chalk_integration::interner::RawId { index: 0 }), 50 | substitution: empty_substitution!(), 51 | }; 52 | let bounds = chalk_ir::Binders::>::empty( 53 | ChalkIr, 54 | chalk_ir::QuantifiedWhereClauses::from_iter( 55 | ChalkIr, 56 | vec![chalk_ir::Binders::>::empty( 57 | ChalkIr, 58 | chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { 59 | ty: internal_ty, 60 | alias: chalk_ir::AliasTy::Projection(projection_ty), 61 | }), 62 | )], 63 | ), 64 | ); 65 | let dyn_ty = chalk_ir::DynTy { 66 | lifetime: lifetime!(placeholder 5), 67 | bounds, 68 | }; 69 | let ty = TyKind::Dyn(dyn_ty).intern(ChalkIr); 70 | assert_eq!( 71 | ty.data(ChalkIr).flags, 72 | TypeFlags::HAS_TY_PROJECTION 73 | | TypeFlags::HAS_RE_PLACEHOLDER 74 | | TypeFlags::HAS_FREE_LOCAL_REGIONS 75 | | TypeFlags::HAS_FREE_REGIONS 76 | ); 77 | } 78 | 79 | #[test] 80 | fn flagless_ty_has_no_flags() { 81 | let ty = TyKind::Str.intern(ChalkIr); 82 | assert_eq!(ty.data(ChalkIr).flags, TypeFlags::empty()); 83 | 84 | let fn_ty = TyKind::Function(chalk_ir::FnPointer { 85 | num_binders: 0, 86 | substitution: chalk_ir::FnSubst(empty_substitution!()), 87 | sig: chalk_ir::FnSig { 88 | abi: chalk_integration::interner::ChalkFnAbi::Rust, 89 | safety: chalk_ir::Safety::Safe, 90 | variadic: false, 91 | }, 92 | }) 93 | .intern(ChalkIr); 94 | assert_eq!(fn_ty.data(ChalkIr).flags, TypeFlags::empty()); 95 | } 96 | 97 | #[test] 98 | fn static_and_bound_lifetimes() { 99 | let substitutions = chalk_ir::Substitution::from_iter( 100 | ChalkIr, 101 | vec![ 102 | chalk_ir::GenericArgData::Lifetime(chalk_ir::LifetimeData::Static.intern(ChalkIr)) 103 | .intern(ChalkIr), 104 | chalk_ir::GenericArgData::Lifetime(lifetime!(bound 5)).intern(ChalkIr), 105 | ], 106 | ); 107 | 108 | let ty = TyKind::Adt( 109 | chalk_ir::AdtId(chalk_integration::interner::RawId { index: 0 }), 110 | substitutions, 111 | ) 112 | .intern(ChalkIr); 113 | 114 | assert_eq!( 115 | ty.data(ChalkIr).flags, 116 | TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_LATE_BOUND 117 | ); 118 | } 119 | -------------------------------------------------------------------------------- /tests/test/unpin.rs: -------------------------------------------------------------------------------- 1 | //! Tests targeting the Unpin trait 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn unpin_lowering() { 7 | lowering_success! { 8 | program { 9 | #[auto] #[lang(unpin)] trait Unpin { } 10 | enum A { Variant } 11 | struct B { } 12 | impl !Unpin for A {} 13 | impl Unpin for B {} 14 | } 15 | } 16 | } 17 | 18 | #[test] 19 | fn unpin_auto_trait() { 20 | test! { 21 | program { 22 | #[auto] #[lang(unpin)] trait Unpin { } 23 | struct A { } 24 | } 25 | 26 | goal { 27 | A: Unpin 28 | } yields { 29 | expect![["Unique"]] 30 | } 31 | } 32 | } 33 | 34 | #[test] 35 | fn unpin_negative() { 36 | test! { 37 | program { 38 | #[auto] #[lang(unpin)] trait Unpin { } 39 | struct A { } 40 | impl !Unpin for A {} 41 | } 42 | 43 | goal { 44 | A: Unpin 45 | } yields { 46 | expect![["No possible solution"]] 47 | } 48 | } 49 | } 50 | 51 | #[test] 52 | fn unpin_inherit_negative() { 53 | test! { 54 | program { 55 | #[auto] #[lang(unpin)] trait Unpin { } 56 | struct A { } 57 | impl !Unpin for A {} 58 | struct B { a: A } 59 | } 60 | 61 | goal { 62 | B: Unpin 63 | } yields { 64 | expect![["No possible solution"]] 65 | } 66 | } 67 | } 68 | 69 | #[test] 70 | fn unpin_overwrite() { 71 | test! { 72 | program { 73 | #[auto] #[lang(unpin)] trait Unpin { } 74 | struct A { } 75 | impl !Unpin for A {} 76 | struct B { a: A } 77 | impl Unpin for B {} 78 | } 79 | 80 | goal { 81 | B: Unpin 82 | } yields { 83 | expect![["Unique"]] 84 | } 85 | } 86 | } 87 | 88 | #[test] 89 | fn coroutine_unpin() { 90 | test! { 91 | program { 92 | #[auto] #[lang(unpin)] trait Unpin { } 93 | struct A { } 94 | impl !Unpin for A {} 95 | 96 | coroutine static static_gen<>[resume = (), yield = ()] { 97 | upvars [] 98 | witnesses [] 99 | } 100 | 101 | coroutine movable_gen<>[resume = (), yield = ()] { 102 | upvars [] 103 | witnesses [] 104 | } 105 | 106 | coroutine movable_with_pin<>[resume = (), yield = ()] { 107 | upvars [A] 108 | witnesses [] 109 | } 110 | } 111 | 112 | goal { 113 | static_gen: Unpin 114 | } yields { 115 | expect![["No possible solution"]] 116 | } 117 | 118 | goal { 119 | movable_gen: Unpin 120 | } yields { 121 | expect![["Unique"]] 122 | } 123 | 124 | goal { 125 | movable_with_pin: Unpin 126 | } yields { 127 | expect![["Unique"]] 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /tests/test/wf_goals.rs: -------------------------------------------------------------------------------- 1 | //! Tests for `WellFormed(_)` goals and clauses 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn struct_wf() { 7 | test! { 8 | program { 9 | struct Foo where T: Eq { } 10 | struct Bar { } 11 | struct Baz { } 12 | 13 | trait Eq { } 14 | 15 | impl Eq for Baz { } 16 | impl Eq for Foo where T: Eq { } 17 | } 18 | 19 | goal { 20 | WellFormed(Foo) 21 | } yields { 22 | expect![["No possible solution"]] 23 | } 24 | 25 | goal { 26 | WellFormed(Foo) 27 | } yields { 28 | expect![["Unique"]] 29 | } 30 | 31 | goal { 32 | WellFormed(Foo>) 33 | } yields { 34 | expect![["Unique"]] 35 | } 36 | } 37 | } 38 | 39 | #[test] 40 | fn enum_wf() { 41 | test! { 42 | program { 43 | enum Foo where T: Eq { } 44 | enum Bar { } 45 | enum Baz { } 46 | 47 | trait Eq { } 48 | 49 | impl Eq for Baz { } 50 | impl Eq for Foo where T: Eq { } 51 | } 52 | 53 | goal { 54 | WellFormed(Foo) 55 | } yields { 56 | expect![["No possible solution"]] 57 | } 58 | 59 | goal { 60 | WellFormed(Foo) 61 | } yields { 62 | expect![["Unique"]] 63 | } 64 | 65 | goal { 66 | WellFormed(Foo>) 67 | } yields { 68 | expect![["Unique"]] 69 | } 70 | } 71 | } 72 | 73 | #[test] 74 | fn recursive_where_clause_on_type() { 75 | test! { 76 | program { 77 | trait Bar { } 78 | trait Foo where Self: Bar { } 79 | 80 | struct S where S: Foo { } 81 | 82 | impl Foo for S { } 83 | } 84 | 85 | goal { 86 | WellFormed(S) 87 | } yields { 88 | expect![["No possible solution"]] 89 | } 90 | } 91 | } 92 | 93 | #[test] 94 | fn drop_compatible() { 95 | test! { 96 | program { 97 | #[lang(drop)] 98 | trait Drop { } 99 | 100 | struct S { } 101 | } 102 | 103 | goal { 104 | compatible { not { exists { S: Drop } } } 105 | } yields { 106 | expect![["Unique"]] 107 | } 108 | } 109 | } 110 | 111 | #[test] 112 | fn placeholder_wf() { 113 | test! { 114 | goal { 115 | forall { WellFormed(T) } 116 | } yields { 117 | expect![["Unique"]] 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /tests/test_util.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_macros)] 2 | 3 | macro_rules! lowering_success { 4 | (program $program:tt) => { 5 | let program_text = stringify!($program); 6 | assert!(program_text.starts_with("{")); 7 | assert!(program_text.ends_with("}")); 8 | let result = chalk_solve::logging::with_tracing_logs(|| { 9 | chalk_integration::db::ChalkDatabase::with( 10 | &program_text[1..program_text.len() - 1], 11 | chalk_integration::SolverChoice::default(), 12 | ) 13 | .checked_program() 14 | }); 15 | if let Err(ref e) = result { 16 | println!("lowering error: {}", e); 17 | } 18 | assert!(result.is_ok()); 19 | }; 20 | } 21 | 22 | macro_rules! lowering_error { 23 | (program $program:tt error_msg { $expected:expr }) => { 24 | let program_text = stringify!($program); 25 | assert!(program_text.starts_with("{")); 26 | assert!(program_text.ends_with("}")); 27 | let error = chalk_solve::logging::with_tracing_logs(|| { 28 | chalk_integration::db::ChalkDatabase::with( 29 | &program_text[1..program_text.len() - 1], 30 | chalk_integration::SolverChoice::default(), 31 | ) 32 | .checked_program() 33 | .unwrap_err() 34 | .to_string() 35 | }); 36 | let expected = $expected.to_string(); 37 | crate::test_util::assert_same(&error, &expected); 38 | }; 39 | } 40 | 41 | pub fn assert_same(result: &str, expected: &str) { 42 | println!("expected:\n{}", expected); 43 | println!("actual:\n{}", result); 44 | 45 | let expected1: String = expected.chars().filter(|w| !w.is_whitespace()).collect(); 46 | let result1: String = result.chars().filter(|w| !w.is_whitespace()).collect(); 47 | assert!(!expected1.is_empty(), "Expectation cannot be empty!"); 48 | if !result1.starts_with(&expected1) { 49 | let prefix = &result1[..std::cmp::min(result1.len(), expected1.len())]; 50 | // These will never be equal, which will cause a nice error message 51 | // to be displayed 52 | pretty_assertions::assert_eq!(expected1, prefix); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /triagebot.toml: -------------------------------------------------------------------------------- 1 | [assign] --------------------------------------------------------------------------------