├── .gitattributes ├── .github └── workflows │ ├── ci.yaml │ └── mdbook.yml ├── .gitignore ├── .nvmrc ├── .vscode ├── launch.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── book ├── .gitignore ├── book.toml └── src │ ├── SUMMARY.md │ ├── chapter_1.md │ ├── faq.md │ ├── formality_core.md │ ├── formality_core │ ├── collections.md │ ├── constructors.md │ ├── debug.md │ ├── faq.md │ ├── impl_parse.md │ ├── judgment_fn.md │ ├── lang.md │ ├── parse.md │ ├── terms.md │ └── variables.md │ └── intro.md ├── crates ├── formality-check │ ├── Cargo.toml │ └── src │ │ ├── adts.rs │ │ ├── coherence.rs │ │ ├── fns.rs │ │ ├── impls.rs │ │ ├── lib.rs │ │ ├── traits.rs │ │ └── where_clauses.rs ├── formality-core │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── binder.rs │ │ ├── cast.rs │ │ ├── collections.rs │ │ ├── derive_links.rs │ │ ├── fixed_point.rs │ │ ├── fixed_point │ │ └── stack.rs │ │ ├── fold.rs │ │ ├── judgment.rs │ │ ├── judgment │ │ ├── assertion.rs │ │ ├── proven_set.rs │ │ ├── test_fallible.rs │ │ ├── test_filtered.rs │ │ └── test_reachable.rs │ │ ├── language.rs │ │ ├── lib.rs │ │ ├── parse.rs │ │ ├── parse │ │ ├── parser.rs │ │ ├── parser │ │ │ └── left_recursion.rs │ │ └── test.rs │ │ ├── substitution.rs │ │ ├── term.rs │ │ ├── test_util.rs │ │ ├── util.rs │ │ ├── variable.rs │ │ ├── variable │ │ ├── cast_impls.rs │ │ └── debug_impls.rs │ │ └── visit.rs ├── formality-macros │ ├── Cargo.toml │ ├── READE.md │ └── src │ │ ├── as_methods.rs │ │ ├── attrs.rs │ │ ├── cast.rs │ │ ├── constructors.rs │ │ ├── custom.rs │ │ ├── debug.rs │ │ ├── fixed_point.rs │ │ ├── fold.rs │ │ ├── lib.rs │ │ ├── parse.rs │ │ ├── precedence.rs │ │ ├── spec.rs │ │ ├── term.rs │ │ ├── test.rs │ │ ├── variable.rs │ │ └── visit.rs ├── formality-prove │ ├── Cargo.toml │ └── src │ │ ├── db.rs │ │ ├── decls.rs │ │ ├── lib.rs │ │ ├── prove.rs │ │ ├── prove │ │ ├── combinators.rs │ │ ├── constraints.rs │ │ ├── env.rs │ │ ├── is_local.rs │ │ ├── minimize.rs │ │ ├── minimize │ │ │ └── test.rs │ │ ├── negation.rs │ │ ├── prove_after.rs │ │ ├── prove_eq.rs │ │ ├── prove_normalize.rs │ │ ├── prove_via.rs │ │ ├── prove_wc.rs │ │ ├── prove_wc_list.rs │ │ └── prove_wf.rs │ │ ├── test.rs │ │ ├── test │ │ ├── adt_wf.rs │ │ ├── eq_assumptions.rs │ │ ├── eq_partial_eq.rs │ │ ├── exists_constraints.rs │ │ ├── expanding.rs │ │ ├── is_local.rs │ │ ├── magic_copy.rs │ │ ├── occurs_check.rs │ │ ├── simple_impl.rs │ │ └── universes.rs │ │ └── test_util.rs ├── formality-rust │ ├── Cargo.toml │ └── src │ │ ├── grammar.rs │ │ ├── grammar │ │ └── mir.rs │ │ ├── lib.rs │ │ ├── prove.rs │ │ ├── test.rs │ │ └── trait_binder.rs ├── formality-smir │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── formality-types │ ├── Cargo.toml │ └── src │ ├── grammar.rs │ ├── grammar │ ├── binder.rs │ ├── consts.rs │ ├── consts │ │ └── valtree.rs │ ├── formulas.rs │ ├── ids.rs │ ├── kinded.rs │ ├── ty.rs │ ├── ty │ │ ├── debug_impls.rs │ │ ├── parse_impls.rs │ │ └── term_impls.rs │ └── wc.rs │ ├── lib.rs │ └── visit.rs ├── examples └── formality-eg │ ├── grammar.rs │ ├── grammar │ └── test.rs │ ├── main.rs │ └── type_system.rs ├── fixme_tests ├── basic--impl_Debug_for_i32.rs ├── basic--supertraits-hr.rs ├── basic--supertraits.rs ├── coinductive.rs ├── impl-vs-trait-fn.rs └── wf-impl--supertrait-required.rs ├── reformat-logs.py ├── rust-toolchain.toml ├── src ├── lib.rs ├── main.rs └── test │ ├── coherence_orphan.rs │ ├── coherence_overlap.rs │ ├── consts.rs │ ├── decl_safety.rs │ ├── functions.rs │ ├── mod.rs │ └── well_formed_trait_ref.rs ├── tests ├── associated_type_normalization.rs ├── coherence_overlap.rs ├── judgment-error-reporting │ ├── cyclic_judgment.rs │ ├── fallible.rs │ ├── grammar.rs │ └── main.rs ├── parser-torture-tests │ ├── ambiguity.rs │ ├── commit_points.rs │ ├── grammar.rs │ ├── left_associative.rs │ ├── main.rs │ ├── none_associative.rs │ ├── path.rs │ └── right_associative.rs ├── parser_var_id_ambiguity.rs └── projection.rs └── triagebot.toml /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | rust-test: 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, macos-latest, windows-latest] 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Check formatting of all crates in the workspace 19 | uses: actions-rs/cargo@v1 20 | with: 21 | command: fmt 22 | args: --all -- --check 23 | - name: Run cargo test --all 24 | uses: actions-rs/cargo@v1 25 | with: 26 | command: test 27 | args: --all 28 | - name: Run cargo test --all-targets 29 | uses: actions-rs/cargo@v1 30 | with: 31 | command: test 32 | args: --all-targets 33 | 34 | -------------------------------------------------------------------------------- /.github/workflows/mdbook.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a mdBook site to GitHub Pages 2 | # 3 | # To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html 4 | # 5 | name: Deploy mdBook site to Pages 6 | 7 | on: 8 | # Runs on pushes targeting the default branch 9 | push: 10 | branches: ["main"] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 16 | permissions: 17 | contents: read 18 | pages: write 19 | id-token: write 20 | 21 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 22 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 23 | concurrency: 24 | group: "pages" 25 | cancel-in-progress: false 26 | 27 | jobs: 28 | # Build job 29 | build: 30 | runs-on: ubuntu-latest 31 | env: 32 | MDBOOK_VERSION: 0.4.36 33 | steps: 34 | - uses: actions/checkout@v4 35 | - name: Install mdBook 36 | run: | 37 | curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh 38 | rustup update 39 | cargo install --version ${MDBOOK_VERSION} mdbook 40 | - name: Setup Pages 41 | id: pages 42 | uses: actions/configure-pages@v5 43 | - name: Build with mdBook 44 | run: cd book; mdbook build 45 | - name: Upload artifact 46 | uses: actions/upload-pages-artifact@v3 47 | with: 48 | path: ./book/book 49 | 50 | # Deployment job 51 | deploy: 52 | environment: 53 | name: github-pages 54 | url: ${{ steps.deployment.outputs.page_url }} 55 | runs-on: ubuntu-latest 56 | needs: build 57 | steps: 58 | - name: Deploy to GitHub Pages 59 | id: deployment 60 | uses: actions/deploy-pages@v4 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book/book/ 2 | target 3 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v16.8.0 2 | 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug", 11 | "program": "${workspaceFolder}/", 12 | "args": [], 13 | "cwd": "${workspaceFolder}" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "./crates/formality-core/Cargo.toml" 4 | ] 5 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "a-mir-formality" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | description = "Model of the Rust type system maintained by the Rust types team (in development)" 7 | homepage = "https://rust-lang.github.io/a-mir-formality/" 8 | repository = "https://github.com/rust-lang/a-mir-formality/" 9 | readme = "README.md" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [package.metadata.rust-analyzer] 14 | rustc_private=true 15 | 16 | [dev-dependencies] 17 | pretty_assertions = "1.3.0" 18 | expect-test = "1.4.0" 19 | formality-macros = { version = "0.1.0", path = "crates/formality-macros" } 20 | formality-core = { version = "0.1.0", path = "crates/formality-core" } 21 | tracing = "0.1.40" 22 | 23 | 24 | [dependencies] 25 | anyhow = "1" 26 | clap = { version = "4.0.9", features = ["derive"] } 27 | formality-rust = { version = "0.1.0", path = "crates/formality-rust" } 28 | formality-types = { version = "0.1.0", path = "crates/formality-types" } 29 | formality-check = { version = "0.1.0", path = "crates/formality-check" } 30 | formality-prove = { version = "0.1.0", path = "crates/formality-prove" } 31 | formality-core = { version = "0.1.0", path = "crates/formality-core" } 32 | formality-smir = { version = "0.1.0", path = "crates/formality-smir" } 33 | expect-test = "1.4.0" 34 | 35 | [workspace] 36 | members = [ 37 | "crates/formality-macros", 38 | "crates/formality-core", 39 | "crates/formality-types", 40 | "crates/formality-check", 41 | "crates/formality-rust", 42 | "crates/formality-prove", 43 | "crates/formality-smir", 44 | ] 45 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # a-mir-formality 2 | 3 | This repository is an early-stage experimental project that aims to be a complete, authoritative formal model of the [Rust MIR](https://rustc-dev-guide.rust-lang.org/mir/index.html). 4 | Presuming these experiments bear fruit, the intention is to bring this model into Rust as an RFC 5 | and develop it as an official part of the language definition. 6 | 7 | ## Quickstart guide 8 | 9 | Like any Rust project: 10 | 11 | * Clone 12 | * `cargo test --all` 13 | 14 | ## Layers of formality 15 | 16 | Formality is structured into several layers. These layers are meant to also map 17 | fairly closely onto chalk and the eventual Rust trait solver implementation. 18 | Ideally, one should be able to map back and forth between formality and the code 19 | with ease. 20 | 21 | * **formality-check**: Defines the top-level routines for checking Rust programs. 22 | `check_all_crates` is effectively the `main`, so it's a good place to start reading. 23 | * **formality-core**: Defines logging macros. 24 | * **formality-macros**: Defines procedural macros like `#[term]` as well as various derives. 25 | These are used to generate the boilerplate code for parsing, pretty printing, folding, etc. 26 | * **formality-prove**: Defines the rules for proving goals (e.g., is this trait implemented?) 27 | * **formality-rust:** This is the "Rust declarations" layer, defining Rust 28 | "top-level items" and their semantics. This includes crates, structs, traits, 29 | impls, but excludes function bodies. 30 | * **formality-types:** This is the "types" layer, defining Rust types and 31 | functions for equating/relating them. The representation is meant to cover 32 | all Rust types, but is optimized for extracting their "essential properties". 33 | -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Niko Matsakis"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "A MIR Formality" 7 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Intro](./intro.md) 4 | - [`formality_core`: the Formality system](./formality_core.md) 5 | - [Defining your lang](./formality_core/lang.md) 6 | - [Defining terms with the `term` macro](./formality_core/terms.md) 7 | - [Parsing](./formality_core/parse.md) 8 | - [Customizing debug](./formality_core/debug.md) 9 | - [Constructors](./formality_core/constructors.md) 10 | - [Variables](./formality_core/variables.md) 11 | - [Collections](./formality_core/collections.md) 12 | - [Judgment functions and inference rules](./formality_core/judgment_fn.md) 13 | - [FAQ and troubleshooting](./formality_core/faq.md) 14 | -------------------------------------------------------------------------------- /book/src/chapter_1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | -------------------------------------------------------------------------------- /book/src/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ and troubleshooting 2 | -------------------------------------------------------------------------------- /book/src/formality_core.md: -------------------------------------------------------------------------------- 1 | # `formality_core`: the Formality system 2 | 3 | `a-mir-formality` is build on the formality core system, 4 | defined by the `formality_core` crate. 5 | Formality core is a mildly opnionated series of macros, derives, and types 6 | that let you write high-level Rust code 7 | in a way that resembles standard type theory notation. 8 | Its primary purpose is to be used by a-mir-formality 9 | but it can be used for other projects. 10 | 11 | -------------------------------------------------------------------------------- /book/src/formality_core/collections.md: -------------------------------------------------------------------------------- 1 | # Collections 2 | 3 | When using formality, it's best to use the following collection types: 4 | 5 | * for sequences, use the standard `Vec` type 6 | * `formality_core::Set` -- equivalent to `BTreeSet` but shorter. We use a `BTreeSet` because it has deterministic ordering for all operations. 7 | * `formality_core::Map` -- equivalent to `BTreeMap` but shorter. We use a `BTreeMap` because it has deterministic ordering for all operations. 8 | 9 | ## Macros 10 | 11 | We also define macros: 12 | 13 | * `seq![...]` -- equivalent to `vec![]` but permits flattening with `..` notation, as described below 14 | * `set![...]` -- like `seq!`, but produces a `Set` 15 | 16 | In these macros you can "flatten" things that support `IntoIterator`, so `set![..a, ..b]` will effectively perform a set union of `a` and `b`. 17 | 18 | ## Casting between collections and tuples 19 | 20 | It is possible to upcast from variable tuple types to produce collections: 21 | 22 | * A `Vec` can be upcast to a `Vec` if `E1: Upcast`. 23 | * A `Set` can be upcast to a `Set` if `E1: Upcast`. 24 | * Tuples of elements (e.g., `(E1, E2)` or `(E1, E2, E3)`) can be **upcast** to sets up to a fixed arity. 25 | * Sets and vectors can be **downcast** to `()` and `(E, C)`, where `()` succeeds only for empty collections, and `(E, C)` extracts the first element `E` and a collection `C` with all remaining elements (note that elements in sets are always ordered, so the first element is well defined there). This is useful when writing judgment rules that operate over sequences and sets. -------------------------------------------------------------------------------- /book/src/formality_core/constructors.md: -------------------------------------------------------------------------------- 1 | # Constructors 2 | 3 | Unless you include `#[customize(constructors)]`, the `#[term]` macro automatically creates constructors as follows: 4 | 5 | - For a `struct`, defines a `new` method that takes an `impl Upcast` for each of your fields. 6 | - For an `enum`, defines a method per variant that has fields (converted to snake-case). 7 | - If the name of the variant is a Rust keyword like `Struct`, the method will be called `struct_`. 8 | - We do not generate constructors for variants with no arguments. 9 | -------------------------------------------------------------------------------- /book/src/formality_core/debug.md: -------------------------------------------------------------------------------- 1 | # Customizing the debug 2 | 3 | By default, the `#[term]` macro will generate a `Debug` impl that is guided by the `#[grammar]` attributes on your type (see the [parsing](./parse.md) section for more details). But sometimes you want to generate custom logic. You can include a `#[customize(debug)]` declaration to allow that. Most of the type, when you do this, you will also want to [customize parsing](./parse.md#customizing-the-parse), as the `RigidTy` does: 4 | 5 | ```rust 6 | {{#include ../../../crates/formality-types/src/grammar/ty.rs:RigidTy_decl}} 7 | ``` 8 | 9 | Now you must simply implement `Debug` in the usual way. Here is the `RigidTy` declaration: 10 | 11 | 12 | ```rust 13 | {{#include ../../../crates/formality-types/src/grammar/ty/debug_impls.rs:RigidTy_impl}} 14 | ``` -------------------------------------------------------------------------------- /book/src/formality_core/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ and troubleshooting 2 | 3 | ## Why am I getting errors about undefined references to `crate::FormalityLang`? 4 | 5 | The various derive macros need to know what language you are working in. 6 | To figure this out, they reference `crate::FormalityLang`, which you must define. 7 | See the [chapter on defining your language](./lang.md) for more details. 8 | -------------------------------------------------------------------------------- /book/src/formality_core/impl_parse.md: -------------------------------------------------------------------------------- 1 | # Implementing Parse by hand 2 | 3 | The generic `#[term]` macro generates a simple parser, but sometimes you want more flexibility. 4 | Here is the code that implements the parsing of Rust types: 5 | 6 | ```rust 7 | {{#include ../../../crates/formality-types/src/grammar/ty/parse_impls.rs:ty_parse_impl}} 8 | ``` 9 | -------------------------------------------------------------------------------- /book/src/formality_core/lang.md: -------------------------------------------------------------------------------- 1 | # Defining your language 2 | 3 | The very first thing you do to define your own language is to use the `formality_core::declare_language!` macro. 4 | You use it like so: 5 | 6 | ```rust 7 | {{#include ../../../crates/formality-types/src/lib.rs:declare_rust_language}} 8 | ``` 9 | 10 | The `declare_language` macro will create a module with the name you specify (here, `rust`). 11 | You have to tell it a few things: 12 | 13 | * The `NAME` of your language, a string for debugging. 14 | * An enum defining the *kinds* of variables in your language; see the [variables](./variables.md) chapter for more information. For Rust, the kinds are types, lifetimes, and constants. 15 | * An enum defining a *parameter*, which is the terms that can be used to represent the *value* of a variable; see the [variables](./variables.md) chapter for more information. 16 | * Two characters `BINDER_OPEN` and `BINDER_CLOSED` defining the opening and closing characters for binders, e.g., `<` and `>`, to use when parsing. 17 | 18 | ## Contents of the language module 19 | 20 | The language module you create has various items in it: 21 | 22 | * A `struct FormalityLang` that defines your language. Some of the contains of `formality_core` (notably the traits that involve bound variables) are 23 | 24 | ## Specifying the language for a crate 25 | 26 | That module will contain a language struct named `FormalityLang`. 27 | Other parts of the formality system (e.g., autoderives and the like) 28 | will need to know the current language you are defining, 29 | and they expect to find it at `crate::FormalityLang`. 30 | Best practice is to add a `use` at the top of your crate defining your language. 31 | For example, the `formality_types` crate has: 32 | 33 | ```rust 34 | {{#include ../../../crates/formality-types/src/lib.rs:use_rust_language}} 35 | ``` 36 | 37 | and other crates like `formality_rust` have: 38 | 39 | ```rust 40 | {{#include ../../../crates/formality-rust/src/lib.rs:use_rust_language}} 41 | ``` 42 | -------------------------------------------------------------------------------- /book/src/formality_core/terms.md: -------------------------------------------------------------------------------- 1 | # Defining terms with the `term` macro 2 | 3 | There are two or three key things to understand. The first is the [`#[term]`][term] macro. This is a procedural macro that you can attach to a `struct` or `enum` declaration that represents a piece of Rust syntax or part of the trait checking rules. It auto-derives a bunch of traits and functionality... 4 | 5 | [term]: https://github.com/rust-lang/a-mir-formality/blob/bca36ecd069d6bdff77bffbb628ae3b2ef4f8ef7/crates/formality-macros/src/term.rs#L14-L36 6 | 7 | * rules for parsing from a string 8 | * a `Debug` impl to serialize back out 9 | * folding and substitution 10 | * upcasting and downcasting impls for convenient type conversion 11 | 12 | For some types, we opt not to use `#[term]`, and instead implement the traits by hand. There are also derives so you can derive some of the traits but not all. 13 | 14 | ### Using `#[term]` 15 | 16 | Let's do a simple example. If we had a really simple language, we might define expressions like this: 17 | 18 | ```rust 19 | #[term] 20 | enum Expr { 21 | #[cast] 22 | Variable(Variable), 23 | 24 | #[grammar($v0 + $v1)] 25 | Add(Box, Box), 26 | } 27 | 28 | #[term($name)] 29 | struct Variable { 30 | name: String 31 | } 32 | ``` 33 | 34 | The `#[term]` macro says that these are terms and we should generate all the boilerplate automatically. Note that it will generate code that references `crate::FormalityLang` so be sure to [define your language appropriately](./lang.md). 35 | 36 | The `#[term]` also accepts some internal annotations: 37 | 38 | * `#[cast]` can be applied on an enum variant with a single argument. It says that this variant represents an "is-a" relationship and hence we should generate upcast/downcast impls to allow conversion. In this case, a variable is a kind of expression -- i.e,. wrapping a variable up into an expression doesn't carry any semantic meaning -- so we want variables to be upcastable to expressions (and expressions to be downcast to variables). The `#[cast]` attribute will therefore generate an impl `Variable: Upcast` that lets you convert a variable to an expression, and a downcast impl `Expr: Downcast` that lets you try to convert from an expression to a variable. Downcasting is *fallible*, which means that the downcast will only succeed if this is an `Expr::Variable`. If this is a `Expr::Add`, the downcast would return `None`. 39 | * There is a special case version of `#[cast]` called `#[variable]`. It indicates that the variant represents a (type) variable -- we are not using it here because this an expression variable. Variables are rather specially in folding/parsing to allow for substitution, binders, etc. 40 | * `#[grammar]` tells the parser and pretty printer how to parse this variant. The `$v0` and `$v1` mean "recursively parse the first and second arguments". This will parse a `Box`, which of course is implemented to just parse an `Expr`. 41 | 42 | If you are annotating a struct, the `#[term]` just accepts the grammar directly, so `#[term($name)] struct Variable` means "to parse a variable, just parse the name field (a string)". 43 | 44 | We could also define types and an environment, perhaps something like 45 | 46 | ```rust 47 | #[term] 48 | enum Type { 49 | Integer, // Default grammar is just the word `integer` 50 | String // Default grammar is just the word `string` 51 | } 52 | 53 | #[term] // Default grammar is just `env($bindings)` 54 | struct Env { 55 | bindings: Set<(Variable, Type)> 56 | } 57 | ``` 58 | 59 | You can see that the `#[term]` macro will generate some default parsing rules if you don't say anything. 60 | 61 | We can then write code like 62 | 63 | ```rust 64 | let env: Env = term("env({(x, integer)})"); 65 | ``` 66 | 67 | This will parse the string, panicking if either the string cannot be parsed or or if it is ambiguous (can be parsing in mutiple ways). This is super useful in tests. 68 | 69 | These terms are just Rust types, so you can define methods in the usual way, e.g. this `Env::get` method will search for a variable named `v`: 70 | 71 | ```rust 72 | impl Env { 73 | pub fn get(&self, v: &Variable) -> Option<&Type> { 74 | self.bindings.iter() 75 | .filter(|b| &b.0 == v) 76 | .map(|b| &b.1) 77 | .next() 78 | } 79 | } 80 | ``` 81 | -------------------------------------------------------------------------------- /book/src/formality_core/variables.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | -------------------------------------------------------------------------------- /book/src/intro.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | -------------------------------------------------------------------------------- /crates/formality-check/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "formality-check" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | formality-types = { path = "../formality-types" } 10 | formality-macros = { path = "../formality-macros" } 11 | formality-core = { path = "../formality-core" } 12 | formality-rust = { path = "../formality-rust" } 13 | formality-prove = { path = "../formality-prove" } 14 | tracing = "0.1" 15 | contracts = "0.6.3" 16 | anyhow = "1.0.66" 17 | fn-error-context = "0.2.0" 18 | itertools = "0.10.5" 19 | 20 | [dev-dependencies] 21 | expect-test = "1.4.0" 22 | -------------------------------------------------------------------------------- /crates/formality-check/src/adts.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use anyhow::bail; 4 | use formality_prove::Env; 5 | use formality_rust::grammar::{Adt, AdtBoundData, Field, Variant}; 6 | use formality_types::grammar::Fallible; 7 | 8 | impl super::Check<'_> { 9 | pub(super) fn check_adt(&self, adt: &Adt) -> Fallible<()> { 10 | let Adt { id: _, binder } = adt; 11 | 12 | // names is used to check that there are no name conflicts 13 | let mut names = HashSet::new(); 14 | for Variant { name, fields } in &adt.binder.peek().variants { 15 | if !names.insert((name, None)) { 16 | bail!("variant \"{name:?}\" defined multiple times"); 17 | } 18 | let vname = name; 19 | for Field { name, ty: _ } in fields { 20 | if !names.insert((vname, Some(name))) { 21 | bail!("field \"{name:?}\" of variant \"{vname:?}\" defined multiple times"); 22 | } 23 | } 24 | } 25 | 26 | let mut env = Env::default(); 27 | 28 | let AdtBoundData { 29 | where_clauses, 30 | variants, 31 | } = env.instantiate_universally(binder); 32 | 33 | self.prove_where_clauses_well_formed(&env, &where_clauses, &where_clauses)?; 34 | 35 | for Variant { name: _, fields } in &variants { 36 | for Field { name: _, ty } in fields { 37 | self.prove_goal(&env, &where_clauses, ty.well_formed())?; 38 | } 39 | } 40 | 41 | Ok(()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/formality-check/src/fns.rs: -------------------------------------------------------------------------------- 1 | use formality_prove::Env; 2 | use formality_rust::{ 3 | grammar::{Fn, FnBoundData}, 4 | prove::ToWcs, 5 | }; 6 | use formality_types::grammar::{Fallible, Wcs}; 7 | 8 | use crate::Check; 9 | 10 | impl Check<'_> { 11 | pub(crate) fn check_free_fn(&self, f: &Fn) -> Fallible<()> { 12 | self.check_fn(&Env::default(), Wcs::t(), f) 13 | } 14 | 15 | pub(crate) fn check_fn( 16 | &self, 17 | in_env: &Env, 18 | in_assumptions: impl ToWcs, 19 | f: &Fn, 20 | ) -> Fallible<()> { 21 | let in_assumptions = in_assumptions.to_wcs(); 22 | assert!(in_env.only_universal_variables() && in_env.encloses((&in_assumptions, f))); 23 | 24 | let mut env = in_env.clone(); 25 | 26 | let Fn { id: _, binder } = f; 27 | 28 | let FnBoundData { 29 | input_tys, 30 | output_ty, 31 | where_clauses, 32 | body: _, 33 | } = env.instantiate_universally(binder); 34 | 35 | let fn_assumptions: Wcs = (in_assumptions, &where_clauses).to_wcs(); 36 | 37 | self.prove_where_clauses_well_formed(&env, &fn_assumptions, &where_clauses)?; 38 | 39 | for input_ty in &input_tys { 40 | self.prove_goal(&env, &fn_assumptions, input_ty.well_formed())?; 41 | } 42 | 43 | self.prove_goal(&env, &fn_assumptions, output_ty.well_formed())?; 44 | 45 | Ok(()) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /crates/formality-check/src/traits.rs: -------------------------------------------------------------------------------- 1 | use anyhow::bail; 2 | use fn_error_context::context; 3 | use formality_core::Set; 4 | use formality_prove::Env; 5 | use formality_rust::grammar::{ 6 | AssociatedTy, AssociatedTyBoundData, Fn, Trait, TraitBoundData, TraitItem, WhereClause, 7 | }; 8 | use formality_types::grammar::Fallible; 9 | 10 | impl super::Check<'_> { 11 | #[context("check_trait({:?})", t.id)] 12 | pub(super) fn check_trait(&self, t: &Trait) -> Fallible<()> { 13 | let Trait { 14 | safety: _, 15 | id: _, 16 | binder, 17 | } = t; 18 | let mut env = Env::default(); 19 | 20 | let TraitBoundData { 21 | where_clauses, 22 | trait_items, 23 | } = env.instantiate_universally(&binder.explicit_binder); 24 | 25 | self.check_trait_items_have_unique_names(&trait_items)?; 26 | 27 | self.prove_where_clauses_well_formed(&env, &where_clauses, &where_clauses)?; 28 | 29 | for trait_item in &trait_items { 30 | self.check_trait_item(&env, &where_clauses, trait_item)?; 31 | } 32 | 33 | Ok(()) 34 | } 35 | 36 | fn check_trait_items_have_unique_names(&self, trait_items: &[TraitItem]) -> Fallible<()> { 37 | let mut functions = Set::new(); 38 | let mut associated_types = Set::new(); 39 | for trait_item in trait_items { 40 | match trait_item { 41 | TraitItem::Fn(f) => { 42 | if !functions.insert(&f.id) { 43 | bail!("the function name `{:?}` is defined multiple times", f.id); 44 | } 45 | } 46 | TraitItem::AssociatedTy(associated_ty) => { 47 | let AssociatedTy { id, .. } = associated_ty; 48 | if !associated_types.insert(id) { 49 | bail!( 50 | "the associated type name `{:?}` is defined multiple times", 51 | id 52 | ); 53 | } 54 | } 55 | } 56 | } 57 | Ok(()) 58 | } 59 | 60 | fn check_trait_item( 61 | &self, 62 | env: &Env, 63 | where_clauses: &[WhereClause], 64 | trait_item: &TraitItem, 65 | ) -> Fallible<()> { 66 | match trait_item { 67 | TraitItem::Fn(v) => self.check_fn_in_trait(env, where_clauses, v), 68 | TraitItem::AssociatedTy(v) => self.check_associated_ty(env, where_clauses, v), 69 | } 70 | } 71 | 72 | fn check_fn_in_trait(&self, env: &Env, where_clauses: &[WhereClause], f: &Fn) -> Fallible<()> { 73 | self.check_fn(env, where_clauses, f) 74 | } 75 | 76 | fn check_associated_ty( 77 | &self, 78 | trait_env: &Env, 79 | trait_where_clauses: &[WhereClause], 80 | associated_ty: &AssociatedTy, 81 | ) -> Fallible<()> { 82 | let mut env = trait_env.clone(); 83 | 84 | let AssociatedTy { id: _, binder } = associated_ty; 85 | let AssociatedTyBoundData { 86 | ensures: _, 87 | where_clauses, 88 | } = env.instantiate_universally(binder); 89 | 90 | self.prove_where_clauses_well_formed( 91 | &env, 92 | (trait_where_clauses, &where_clauses), 93 | &where_clauses, 94 | )?; 95 | 96 | // FIXME: Do we prove ensures WF? And what do we assume when we do so? 97 | 98 | Ok(()) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /crates/formality-check/src/where_clauses.rs: -------------------------------------------------------------------------------- 1 | use fn_error_context::context; 2 | use formality_prove::Env; 3 | use formality_rust::{grammar::WhereClause, prove::ToWcs}; 4 | use formality_types::grammar::{Fallible, Wcs}; 5 | 6 | impl super::Check<'_> { 7 | #[context("prove_where_clauses_well_formed({where_clauses:?})")] 8 | pub(crate) fn prove_where_clauses_well_formed( 9 | &self, 10 | env: &Env, 11 | assumptions: impl ToWcs, 12 | where_clauses: &[WhereClause], 13 | ) -> Fallible<()> { 14 | let wcs: Wcs = where_clauses 15 | .into_iter() 16 | .flat_map(|wc| wc.well_formed().into_iter()) 17 | .collect(); 18 | self.prove_goal(env, assumptions, wcs) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /crates/formality-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "formality-core" 3 | version = "0.1.1" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | description = "Language-independent formality system used by a-mir-formality" 7 | homepage = "https://rust-lang.github.io/a-mir-formality/" 8 | repository = "https://github.com/rust-lang/a-mir-formality/" 9 | readme = "README.md" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | lazy_static = "1.4.0" 15 | env_logger = "0.10.0" 16 | stacker = "0.1.15" 17 | tracing = "0.1" 18 | tracing-subscriber = {version = "0.3", default-features = false, features = ["env-filter", "fmt"]} 19 | tracing-tree = { version = "0.2" } 20 | formality-macros = { version = "0.1.0", path = "../formality-macros" } 21 | anyhow = "1.0.75" 22 | contracts = "0.6.3" 23 | final_fn = "0.1.0" 24 | itertools = "0.12.0" 25 | either = "1.9.0" 26 | expect-test = "1.4.1" 27 | regex = "1.10.2" 28 | 29 | [dev-dependencies] 30 | expect-test = "1.4.1" 31 | -------------------------------------------------------------------------------- /crates/formality-core/README.md: -------------------------------------------------------------------------------- 1 | # formality-core 2 | 3 | `formality_core` is the language independent part of a-mir-formality. 4 | It can be reused by other projects looking to model formal semantics of languages besides Rust. -------------------------------------------------------------------------------- /crates/formality-core/src/derive_links.rs: -------------------------------------------------------------------------------- 1 | //! This module is a total hack. The Fold procedural macro references it. 2 | //! It's the only way I can find to have the procedural macro generate 3 | //! references to the Fold trait that work both in this crate and others. 4 | //! Other crates that wish to use the Fold macro must re-export this module. 5 | 6 | pub use crate::cast::DowncastTo; 7 | pub use crate::cast::UpcastFrom; 8 | pub use crate::fixed_point; 9 | pub use crate::fold::Fold; 10 | pub use crate::fold::SubstitutionFn; 11 | pub use crate::parse; 12 | pub use crate::term::Term; 13 | pub use crate::variable::Variable; 14 | pub use crate::visit::Visit; 15 | -------------------------------------------------------------------------------- /crates/formality-core/src/fixed_point.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::fmt::Debug; 3 | use std::hash::Hash; 4 | use std::thread::LocalKey; 5 | 6 | mod stack; 7 | pub use stack::FixedPointStack; 8 | 9 | pub fn fixed_point( 10 | tracing_span: impl Fn(&Input) -> tracing::Span, 11 | storage: &'static LocalKey>>, 12 | args: Input, 13 | default_value: impl Fn(&Input) -> Output, 14 | next_value: impl FnMut(Input) -> Output, 15 | ) -> Output 16 | where 17 | Input: Value, 18 | Output: Value, 19 | { 20 | stacker::maybe_grow(32 * 1024, 1024 * 1024, || { 21 | FixedPoint { 22 | tracing_span, 23 | storage, 24 | default_value, 25 | next_value, 26 | } 27 | .apply(args) 28 | }) 29 | } 30 | 31 | struct FixedPoint 32 | where 33 | Input: Value, 34 | Output: Value, 35 | { 36 | tracing_span: TracingSpan, 37 | storage: &'static LocalKey>>, 38 | default_value: DefaultValue, 39 | next_value: NextValue, 40 | } 41 | 42 | pub trait Value: Clone + Eq + Debug + Hash + 'static {} 43 | impl Value for T {} 44 | 45 | impl 46 | FixedPoint 47 | where 48 | Input: Value, 49 | Output: Value, 50 | DefaultValue: Fn(&Input) -> Output, 51 | NextValue: FnMut(Input) -> Output, 52 | TracingSpan: Fn(&Input) -> tracing::Span, 53 | { 54 | fn apply(&mut self, input: Input) -> Output { 55 | if let Some(r) = self.with_stack(|stack| stack.search(&input)) { 56 | tracing::debug!("recursive call to {:?}, yielding {:?}", input, r); 57 | return r; 58 | } 59 | 60 | self.with_stack(|stack| { 61 | let default_value = (self.default_value)(&input); 62 | stack.push(&input, default_value); 63 | }); 64 | 65 | loop { 66 | let span = (self.tracing_span)(&input); 67 | let _guard = span.enter(); 68 | let output = (self.next_value)(input.clone()); 69 | tracing::debug!(?output); 70 | if !self.with_stack(|stack| stack.update_output(&input, output)) { 71 | break; 72 | } else { 73 | tracing::debug!("output is different from previous iteration, re-executing until fixed point is reached"); 74 | } 75 | } 76 | 77 | self.with_stack(|stack| stack.pop(&input)) 78 | } 79 | 80 | fn with_stack(&self, f: impl FnOnce(&mut FixedPointStack) -> R) -> R { 81 | self.storage.with(|v| f(&mut *v.borrow_mut())) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /crates/formality-core/src/fixed_point/stack.rs: -------------------------------------------------------------------------------- 1 | use super::Value; 2 | 3 | pub struct FixedPointStack { 4 | entries: Vec>, 5 | } 6 | 7 | impl Default for FixedPointStack { 8 | fn default() -> Self { 9 | Self { 10 | entries: Default::default(), 11 | } 12 | } 13 | } 14 | 15 | struct StackEntry { 16 | /// Input. 17 | input: Input, 18 | 19 | /// Current output, updated during computation as we approach a fixed point. 20 | output: Output, 21 | 22 | /// Initially false; set to true when the outputs of this rule 23 | /// are observed while it is being evaluated. 24 | has_dependents: bool, 25 | } 26 | 27 | impl FixedPointStack 28 | where 29 | Input: Value, 30 | Output: Value, 31 | { 32 | /// Access the top frame on the stack, which should be for `input`. 33 | fn top_frame(&mut self, input: &Input) -> &mut StackEntry { 34 | let top = self.entries.last_mut().unwrap(); 35 | assert_eq!(top.input, *input); 36 | top 37 | } 38 | 39 | /// Search backwards through the stack, looking for the given input. 40 | /// 41 | /// If it is found, return `Some` with the current outputs, and mark it 42 | /// as needing fixed point iteration. 43 | /// 44 | /// If not, return `None`. 45 | /// 46 | /// The fixed-point mark is returned when the stack is [popped](`Self::pop`) and is used 47 | /// as part of the fixed point algorithm. 48 | pub fn search(&mut self, input: &Input) -> Option { 49 | for entry in &mut self.entries { 50 | if entry.input == *input { 51 | entry.has_dependents = true; 52 | return Some(entry.output.clone()); 53 | } 54 | } 55 | 56 | None 57 | } 58 | 59 | /// Push an entry onto the stack, indicating it is currently being evaluated. 60 | /// There must not already be an entry for `input`. 61 | pub fn push(&mut self, input: &Input, output: Output) { 62 | assert!(self.search(input).is_none()); 63 | 64 | self.entries.push(StackEntry { 65 | input: input.clone(), 66 | output, 67 | has_dependents: false, 68 | }); 69 | } 70 | 71 | /// Add outputs to the top-most stack entry, which must be for `input`. 72 | /// Returns true if another iteration is needed before reaching a fixed point. 73 | pub fn update_output(&mut self, input: &Input, output: Output) -> bool { 74 | let top = self.top_frame(input); 75 | if top.output == output { 76 | return false; 77 | } 78 | 79 | top.output = output; 80 | top.has_dependents 81 | } 82 | 83 | /// Pops the top entry from the stack, returning the saved outputs. 84 | pub fn pop(&mut self, input: &Input) -> Output { 85 | let top = self.entries.pop().unwrap(); 86 | assert_eq!(top.input, *input); 87 | top.output 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /crates/formality-core/src/fold.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::{ 4 | cast::Upcast, 5 | collections::Set, 6 | language::{CoreParameter, HasKind, Language}, 7 | variable::CoreVariable, 8 | visit::CoreVisit, 9 | }; 10 | 11 | /// Invoked for each variable that we find when folding, ignoring variables bound by binders 12 | /// that we traverse. The arguments are as follows: 13 | /// 14 | /// * ParameterKind -- the kind of term in which the variable appeared (type vs lifetime, etc) 15 | /// * Variable -- the variable we encountered 16 | pub type SubstitutionFn<'a, L: Language> = 17 | &'a mut dyn FnMut(CoreVariable) -> Option>; 18 | 19 | pub trait CoreFold: Sized + CoreVisit { 20 | /// Replace uses of variables with values from the substitution. 21 | fn substitute(&self, substitution_fn: SubstitutionFn<'_, L>) -> Self; 22 | 23 | /// Produce a version of this term where any debruijn indices which appear free are incremented by one. 24 | fn shift_in(&self) -> Self { 25 | self.substitute(&mut |v| Some(v.shift_in().upcast())) 26 | } 27 | 28 | /// Replace all appearances of free variable `v` with `p`. 29 | fn replace_free_var( 30 | &self, 31 | v: impl Upcast>, 32 | p: impl Upcast>, 33 | ) -> Self { 34 | let v: CoreVariable = v.upcast(); 35 | let p: CoreParameter = p.upcast(); 36 | assert!(v.is_free()); 37 | assert!(v.kind() == p.kind()); 38 | self.substitute(&mut |v1| if v == v1 { Some(p.clone()) } else { None }) 39 | } 40 | } 41 | 42 | impl> CoreFold for Vec { 43 | fn substitute(&self, substitution_fn: SubstitutionFn<'_, L>) -> Self { 44 | self.iter().map(|e| e.substitute(substitution_fn)).collect() 45 | } 46 | } 47 | 48 | impl + Ord> CoreFold for Set { 49 | fn substitute(&self, substitution_fn: SubstitutionFn<'_, L>) -> Self { 50 | self.iter().map(|e| e.substitute(substitution_fn)).collect() 51 | } 52 | } 53 | 54 | impl> CoreFold for Option { 55 | fn substitute(&self, substitution_fn: SubstitutionFn<'_, L>) -> Self { 56 | self.as_ref().map(|e| e.substitute(substitution_fn)) 57 | } 58 | } 59 | 60 | impl> CoreFold for Arc { 61 | fn substitute(&self, substitution_fn: SubstitutionFn<'_, L>) -> Self { 62 | let data = T::substitute(self, substitution_fn); 63 | Arc::new(data) 64 | } 65 | } 66 | 67 | impl CoreFold for usize { 68 | fn substitute(&self, _substitution_fn: SubstitutionFn<'_, L>) -> Self { 69 | *self 70 | } 71 | } 72 | 73 | impl CoreFold for u32 { 74 | fn substitute(&self, _substitution_fn: SubstitutionFn<'_, L>) -> Self { 75 | *self 76 | } 77 | } 78 | 79 | impl CoreFold for () { 80 | fn substitute(&self, _substitution_fn: SubstitutionFn<'_, L>) -> Self {} 81 | } 82 | 83 | impl, B: CoreFold> CoreFold for (A, B) { 84 | fn substitute(&self, substitution_fn: SubstitutionFn<'_, L>) -> Self { 85 | let (a, b) = self; 86 | (a.substitute(substitution_fn), b.substitute(substitution_fn)) 87 | } 88 | } 89 | 90 | impl, B: CoreFold, C: CoreFold> CoreFold for (A, B, C) { 91 | fn substitute(&self, substitution_fn: SubstitutionFn<'_, L>) -> Self { 92 | let (a, b, c) = self; 93 | ( 94 | a.substitute(substitution_fn), 95 | b.substitute(substitution_fn), 96 | c.substitute(substitution_fn), 97 | ) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /crates/formality-core/src/judgment/assertion.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | /// Helper trait for assertions in judgments. 4 | /// For each `assert(x)`, we will invoke `JudgmentAssertion::assert`. 5 | /// This allows us to support both booleans and results. 6 | pub trait JudgmentAssertion { 7 | fn assert(self, expr: &str); 8 | } 9 | 10 | impl JudgmentAssertion for Result<(), E> { 11 | #[track_caller] 12 | fn assert(self, expr: &str) { 13 | match self { 14 | Ok(()) => (), 15 | Err(e) => panic!("judgment assertion failed: `{expr}` got {e:?}"), 16 | } 17 | } 18 | } 19 | 20 | impl JudgmentAssertion for bool { 21 | #[track_caller] 22 | fn assert(self, expr: &str) { 23 | assert!(self, "judgment assertion failed: `{expr}` is false"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/formality-core/src/judgment/test_fallible.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | //! Test that we permit the use of `?` in `let` and `if let` 4 | 5 | use anyhow::bail; 6 | 7 | use crate::cast_impl; 8 | use crate::judgment_fn; 9 | use crate::Fallible; 10 | 11 | #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug, Hash)] 12 | struct Check { 13 | x: u32, 14 | } 15 | 16 | cast_impl!(Check); 17 | 18 | impl Check { 19 | fn is(&self, x: u32) -> Fallible { 20 | if self.x == x { 21 | Ok(x) 22 | } else { 23 | bail!("expected {} got {}", self.x, x) 24 | } 25 | } 26 | } 27 | 28 | judgment_fn!( 29 | fn jfn(c: Check, x: u32) => u32 { 30 | debug(c, x) 31 | assert(x < 100) 32 | assert(check_x(x)) 33 | 34 | ( 35 | (let y = c.is(x)?) 36 | --------------------------------------- ("rule") 37 | (jfn(c, x) => y) 38 | ) 39 | 40 | 41 | ( 42 | (if let 44 = c.is(x)?) 43 | --------------------------------------- ("other-rule") 44 | (jfn(c, x) => 45) 45 | ) 46 | } 47 | ); 48 | 49 | fn check_x(x: u32) -> Fallible<()> { 50 | if x > 75 { 51 | bail!("invalid x: {x}") 52 | } 53 | Ok(()) 54 | } 55 | 56 | #[test] 57 | fn is_equal_22() { 58 | jfn(Check { x: 22 }, 22).assert_ok(expect_test::expect![[r#" 59 | { 60 | 22, 61 | } 62 | "#]]); 63 | } 64 | 65 | #[test] 66 | fn is_equal_44() { 67 | jfn(Check { x: 44 }, 44).assert_ok(expect_test::expect![[r#" 68 | { 69 | 44, 70 | 45, 71 | } 72 | "#]]); 73 | } 74 | 75 | #[test] 76 | fn is_not_equal() { 77 | jfn(Check { x: 22 }, 23).assert_err(expect_test::expect![[r#" 78 | judgment `jfn { c: Check { x: 22 }, x: 23 }` failed at the following rule(s): 79 | the rule "other-rule" failed at step #0 (src/file.rs:LL:CC) because 80 | expected 22 got 23 81 | the rule "rule" failed at step #0 (src/file.rs:LL:CC) because 82 | expected 22 got 23"#]]); 83 | } 84 | 85 | #[test] 86 | #[should_panic(expected = "judgment assertion failed: `x < 100` is false")] 87 | fn bool_assertion_fails() { 88 | let _ = jfn(Check { x: 22 }, 110); 89 | } 90 | 91 | #[test] 92 | #[should_panic(expected = "judgment assertion failed: `check_x(x)` got invalid x")] 93 | fn result_assertion_fails() { 94 | let _ = jfn(Check { x: 22 }, 76); 95 | } 96 | -------------------------------------------------------------------------------- /crates/formality-core/src/judgment/test_filtered.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use std::sync::Arc; 4 | 5 | use crate::cast_impl; 6 | use crate::judgment_fn; 7 | 8 | #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug, Hash)] 9 | struct Graph { 10 | edges: Vec<(u32, u32)>, 11 | } 12 | 13 | cast_impl!(Graph); 14 | 15 | impl Graph { 16 | fn successors(&self, n: u32) -> Vec { 17 | self.edges 18 | .iter() 19 | .flat_map(|(a, b)| if *a == n { Some(*b) } else { None }) 20 | .collect() 21 | } 22 | } 23 | 24 | judgment_fn!( 25 | fn transitive_reachable(g: Arc, node: u32) => u32 { 26 | debug(node, g) 27 | 28 | ( 29 | (graph.successors(a) => b) 30 | (if b % 2 == 0) 31 | --------------------------------------- ("base") 32 | (transitive_reachable(graph, a) => b) 33 | ) 34 | 35 | ( 36 | (transitive_reachable(&graph, a) => b)! 37 | (transitive_reachable(&graph, b) => c) 38 | (if c % 2 == 0) 39 | --------------------------------------- ("transitive") 40 | (transitive_reachable(graph, a) => c) 41 | ) 42 | } 43 | ); 44 | 45 | #[test] 46 | fn judgment() { 47 | let graph = Arc::new(Graph { 48 | edges: vec![(0, 1), (1, 2), (2, 4), (2, 3), (3, 6), (4, 8), (8, 10)], 49 | }); 50 | 51 | transitive_reachable(&graph, 0).assert_err(expect_test::expect![[r#" 52 | judgment `transitive_reachable { node: 0, g: Graph { edges: [(0, 1), (1, 2), (2, 4), (2, 3), (3, 6), (4, 8), (8, 10)] } }` failed at the following rule(s): 53 | the rule "base" failed at step #1 (src/file.rs:LL:CC) because 54 | condition evaluted to false: `b % 2 == 0`"#]]); 55 | 56 | transitive_reachable(&graph, 2).assert_ok(expect_test::expect![[r#" 57 | { 58 | 4, 59 | 8, 60 | 10, 61 | } 62 | "#]]); 63 | } 64 | -------------------------------------------------------------------------------- /crates/formality-core/src/judgment/test_reachable.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use crate::{cast_impl, judgment_fn}; 4 | use formality_macros::test; 5 | use std::sync::Arc; 6 | 7 | #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug, Hash)] 8 | struct Graph { 9 | edges: Vec<(u32, u32)>, 10 | } 11 | 12 | cast_impl!(Graph); 13 | 14 | impl Graph { 15 | fn successors(&self, n: u32) -> Vec { 16 | self.edges 17 | .iter() 18 | .flat_map(|(a, b)| if *a == n { Some(*b) } else { None }) 19 | .collect() 20 | } 21 | } 22 | 23 | judgment_fn! { 24 | fn transitive_reachable( 25 | graph: Arc, 26 | from: u32, 27 | ) => u32 { 28 | debug(from, graph) 29 | 30 | ( 31 | (graph.successors(start) => s) 32 | --------------------------------------- ("base") 33 | (transitive_reachable(graph, start) => s) 34 | ) 35 | 36 | ( 37 | (transitive_reachable(&graph, a) => b)! 38 | (transitive_reachable(&graph, b) => c) 39 | --------------------------------------- ("transitive") 40 | (transitive_reachable(graph, a) => c) 41 | ) 42 | } 43 | } 44 | 45 | #[test] 46 | fn judgment() { 47 | let graph = Arc::new(Graph { 48 | edges: vec![(0, 1), (1, 2), (2, 0), (2, 3)], 49 | }); 50 | 51 | transitive_reachable(graph, 0).assert_ok(expect_test::expect![[r#" 52 | { 53 | 0, 54 | 1, 55 | 2, 56 | 3, 57 | } 58 | "#]]); 59 | } 60 | -------------------------------------------------------------------------------- /crates/formality-core/src/language.rs: -------------------------------------------------------------------------------- 1 | use crate::cast::UpcastFrom; 2 | use crate::term::CoreTerm; 3 | use crate::variable::{CoreBoundVar, CoreExistentialVar, CoreUniversalVar, CoreVariable}; 4 | use std::fmt::Debug; 5 | use std::hash::Hash; 6 | 7 | /// The definition of a "language" 8 | pub trait Language: 'static + Copy + Ord + Hash + Debug + Default { 9 | /// Name of the language, e.g., `"Rust"` 10 | const NAME: &'static str; 11 | 12 | /// An enum defining the *kinds* of generic parameters (e.g., for Rust, 13 | /// types, lifetimes, and constants). 14 | type Kind: Copy + CoreTerm; 15 | 16 | /// An enum defining the *value* of a generic parameter (e.g., a 17 | /// type, a lifetime, etc) 18 | type Parameter: HasKind 19 | + CoreTerm 20 | + UpcastFrom> 21 | + UpcastFrom> 22 | + UpcastFrom> 23 | + UpcastFrom>; 24 | 25 | /// The token (typically `<`) used to open binders. 26 | const BINDING_OPEN: char; 27 | 28 | /// The token (typically `>`) used to open binders. 29 | const BINDING_CLOSE: char; 30 | 31 | /// Keywords to disallow as identifiers everywhere. 32 | /// It is possible to do positional keywords too, if you want, 33 | /// but it requires custom parsing impls for your types. 34 | /// No fun. 35 | const KEYWORDS: &'static [&'static str]; 36 | } 37 | 38 | /// For consistency with types like `CoreVariable`, we write `CoreKind` instead of `Kind`. 39 | pub type CoreKind = L::Kind; 40 | 41 | /// For consistency with types like `CoreVariable`, we write `CoreParameter` instead of `Parameter`. 42 | pub type CoreParameter = L::Parameter; 43 | 44 | pub trait HasKind { 45 | fn kind(&self) -> CoreKind; 46 | } 47 | -------------------------------------------------------------------------------- /crates/formality-core/src/parse/test.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crates/formality-core/src/term.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Debug, hash::Hash, sync::Arc}; 2 | 3 | use crate::{ 4 | binder::CoreBinder, 5 | cast::{DowncastFrom, Upcast}, 6 | collections::Set, 7 | fold::CoreFold, 8 | language::Language, 9 | parse::CoreParse, 10 | }; 11 | 12 | pub trait CoreTerm: 13 | Clone 14 | + CoreFold 15 | + CoreParse 16 | + Ord 17 | + Eq 18 | + Hash 19 | + Debug 20 | + Upcast 21 | + DowncastFrom 22 | + 'static 23 | + Sized 24 | { 25 | } 26 | 27 | impl> CoreTerm for Vec {} 28 | 29 | impl> CoreTerm for Set {} 30 | 31 | impl> CoreTerm for Option {} 32 | 33 | impl> CoreTerm for Arc {} 34 | 35 | impl CoreTerm for usize {} 36 | 37 | impl CoreTerm for u32 {} 38 | 39 | impl, B: CoreTerm> CoreTerm for (A, B) {} 40 | 41 | impl> CoreTerm for CoreBinder {} 42 | 43 | impl CoreTerm for () {} 44 | -------------------------------------------------------------------------------- /crates/formality-core/src/test_util.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for writing tests against formality-core. 2 | //! We include utility functions for two recommended testing patterns. 3 | //! 4 | //! # Writing `#[test]` tests 5 | //! 6 | //! We recommend the following: 7 | //! 8 | //! 1. Use [tracing][] for your own logging statements. 9 | //! 2. Add `use formality_core::test` at the top of the file, which enables logging and lets you use `RUST_LOG=debug` or `RUST_LOG=trace` to debug your tests. 10 | //! 3. If asserting the result of a judgment, call the `assert_ok` or `assert_err` methods on the returned [`ProvenSet`](crate::proven_set::ProvenSet) value. 11 | //! 4. If invoking a function that returns `Fallible<()>`, adds calls to the `assert_ok` or `assert_err` methods in the [`ResultTestExt`] trait. 12 | //! 5. All the `assert_ok`/`assert_err` methods take a [`expect_test::Expect`][] value, which you can produce with `expect_test::expect![]` macro. 13 | //! If using rust-analyzer, you can place the cursor on the `expect` token and run `rust-analyzer: run` to update the expected value 14 | //! automatically (or use `UPDATE_EXPECT=1` when running from the command line). 15 | //! 16 | //! Example: 17 | //! 18 | //! ```rust 19 | //! use formality_core::test; 20 | //! use formality_core::Fallible; 21 | //! use formality_core::test_util::ResultTestExt; 22 | //! 23 | //! #[test] 24 | //! fn example_test() { 25 | //! let result: Fallible = Ok(123); 26 | //! result.assert_ok(expect_test::expect!["123"]); 27 | //! } 28 | //! ``` 29 | //! 30 | //! # Writing standalone tests with `ui_test` 31 | //! 32 | //! TODO: nicely package up `ui_test` to do normalization etc. 33 | //! 34 | //! [ui_test]: https://crates.io/crates/ui_test 35 | //! [expect-test]: https://crates.io/crates/expect-test 36 | //! [tracing]: https://crates.io/crates/tracing 37 | 38 | use std::fmt::{Debug, Display}; 39 | 40 | /// Converts `s` to a string and replaces path/line/column patterns like `src/blah/foo.rs:22:33` in the string 41 | /// with `"src/file.rs:LL:CC"`. This makes error messages resilient against changes to the source code. 42 | pub fn normalize_paths(s: impl Display) -> String { 43 | let s = s.to_string(); 44 | let re = regex::Regex::new(r"\([^()]+.rs:\d+:\d+\)").unwrap(); 45 | re.replace_all(&s, "(src/file.rs:LL:CC)").to_string() 46 | } 47 | 48 | /// Extension methods for writing tests on functions that return [`Fallible`](crate::Fallible) values. 49 | pub trait ResultTestExt { 50 | /// Given a `Fallible` value, assert that its debug representation matches the expected value. 51 | /// If the result is an error it is propagated through to the return value. 52 | fn assert_ok(self, expect: expect_test::Expect); 53 | 54 | /// Given a `Fallible` value, assert that it is an error with the given string (after normalization). 55 | /// Returns `Ok(())` if the assertion succeeds, or panics if the assertion fails. 56 | fn assert_err(self, expect: expect_test::Expect); 57 | 58 | /// Given a `Fallible` value, assert that it is an error with the given string (after normalization). 59 | /// Also assert that each of the strings in `must_have` appears somewhere within. 60 | /// Returns `Ok(())` if the assertion succeeds, or panics if the assertion fails. 61 | fn assert_has_err(self, expect: expect_test::Expect, must_have: &[&str]); 62 | } 63 | 64 | impl ResultTestExt for Result 65 | where 66 | T: Debug, 67 | E: Debug, 68 | { 69 | #[track_caller] 70 | fn assert_ok(self, expect: expect_test::Expect) { 71 | match self { 72 | Ok(v) => { 73 | expect.assert_eq(&format!("{v:?}")); 74 | } 75 | Err(e) => { 76 | panic!("expected `Ok`, got `Err`: {e:?}"); 77 | } 78 | } 79 | } 80 | 81 | #[track_caller] 82 | fn assert_err(self, expect: expect_test::Expect) { 83 | self.assert_has_err(expect, &[]); 84 | } 85 | 86 | #[track_caller] 87 | fn assert_has_err(self, expect: expect_test::Expect, must_have: &[&str]) { 88 | match self { 89 | Ok(v) => panic!("expected `Err`, got `Ok`: {v:?}"), 90 | Err(e) => { 91 | let output = normalize_paths(format!("{e:?}")); 92 | 93 | expect.assert_eq(&output); 94 | 95 | for s in must_have { 96 | assert!(output.contains(s), "did not find {s:?} in the output"); 97 | } 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /crates/formality-core/src/util.rs: -------------------------------------------------------------------------------- 1 | /// Returns true if `t` is the default value for `t`. 2 | /// Used by the "derive" code for `Debug`. 3 | pub fn is_default(t: &T) -> bool 4 | where 5 | T: Default + Eq, 6 | { 7 | let default_value: T = Default::default(); 8 | default_value == *t 9 | } 10 | -------------------------------------------------------------------------------- /crates/formality-core/src/variable/cast_impls.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::cast_impl; 3 | 4 | cast_impl!(impl(L: Language) CoreVariable); 5 | cast_impl!(impl(L: Language) CoreBoundVar); 6 | cast_impl!(impl(L: Language) CoreExistentialVar); 7 | cast_impl!(impl(L: Language) CoreUniversalVar); 8 | cast_impl!(impl(L: Language) CoreVariable(L)::ExistentialVar(CoreExistentialVar)); 9 | cast_impl!(impl(L: Language) CoreVariable(L)::BoundVar(CoreBoundVar)); 10 | cast_impl!(impl(L: Language) CoreVariable(L)::UniversalVar(CoreUniversalVar)); 11 | -------------------------------------------------------------------------------- /crates/formality-core/src/variable/debug_impls.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | impl std::fmt::Debug for CoreVariable { 4 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 5 | match self { 6 | Self::UniversalVar(arg0) => write!(f, "{:?}", arg0), 7 | Self::ExistentialVar(arg0) => write!(f, "{:?}", arg0), 8 | Self::BoundVar(arg0) => write!(f, "{:?}", arg0), 9 | } 10 | } 11 | } 12 | 13 | impl std::fmt::Debug for CoreUniversalVar { 14 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 15 | let CoreUniversalVar { var_index, kind } = self; 16 | write!(f, "!{:?}_{:?}", kind, var_index) 17 | } 18 | } 19 | 20 | impl std::fmt::Debug for CoreExistentialVar { 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 22 | let CoreExistentialVar { var_index, kind } = self; 23 | write!(f, "?{:?}_{:?}", kind, var_index) 24 | } 25 | } 26 | 27 | impl std::fmt::Debug for VarIndex { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | write!(f, "{}", self.index) 30 | } 31 | } 32 | 33 | impl std::fmt::Debug for CoreBoundVar { 34 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 35 | match self { 36 | CoreBoundVar { 37 | debruijn: None, 38 | var_index, 39 | kind, 40 | } => write!(f, "^{:?}_{:?}", kind, var_index), 41 | CoreBoundVar { 42 | debruijn: Some(db), 43 | var_index, 44 | kind, 45 | } => write!(f, "^{:?}{:?}_{:?}", kind, db.index, var_index), 46 | } 47 | } 48 | } 49 | 50 | impl std::fmt::Debug for DebruijnIndex { 51 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 52 | write!(f, "^{}", self.index) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /crates/formality-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "formality-macros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | description = "Macros used by a-mir-formality and formality-core" 7 | homepage = "https://rust-lang.github.io/a-mir-formality/" 8 | repository = "https://github.com/rust-lang/a-mir-formality/" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | quote = "1.0.21" 17 | proc-macro2 = "1.0" 18 | syn = { version = "2.0", features = ["full"] } 19 | synstructure = "0.13.0" 20 | tracing = "0.1" 21 | convert_case = "0.6.0" 22 | 23 | [dev-dependencies] 24 | expect-test = "1.4.0" 25 | -------------------------------------------------------------------------------- /crates/formality-macros/READE.md: -------------------------------------------------------------------------------- 1 | # formality-macros 2 | 3 | Procedural macros used by the formality system. 4 | Don't import this crate directly. 5 | Check out formality-core instead. -------------------------------------------------------------------------------- /crates/formality-macros/src/as_methods.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crates/formality-macros/src/attrs.rs: -------------------------------------------------------------------------------- 1 | //! Functions to manipulate the custom attributes that guide our macros in various ways. 2 | 3 | use syn::{spanned::Spanned, Attribute, DeriveInput}; 4 | 5 | use crate::{custom::Customize, precedence::Precedence, variable::Variable}; 6 | 7 | /// Checks for any kind of attribute that indicates an "is-a" relationship, 8 | /// e.g. `#[cast]` and `#[variable]`. 9 | /// 10 | /// See [`has_cast_attr`][] and [`has_variable_attr`][] for more details. 11 | pub(crate) fn has_isa_attr(attrs: &[Attribute]) -> bool { 12 | has_cast_attr(attrs) || has_variable_attr(attrs) 13 | } 14 | 15 | /// The `#[cast]` attribute is placed on enum variants with a single binding, 16 | /// like `enum Foo { #[cast] Bar(Bar), ... }`. It indicates that `Foo` is an 17 | /// extension of `Bar`, so we should permit upcasts from `Bar` to `Foo` 18 | /// and generally ignore the fact that `Bar` is a variant of `Foo` (for example, 19 | /// in debug print-outs, we don't print `Bar(...)`, we just print `...`). 20 | pub(crate) fn has_cast_attr(attrs: &[Attribute]) -> bool { 21 | attrs.iter().any(|a| a.path().is_ident("cast")) 22 | } 23 | 24 | /// The `#[variable]` attribute is a special-case of `#[cast]` that is used 25 | /// for variables. It can only be used on a single-entry variant in some type 26 | /// that is up/down-castable to the language's `Parameter` type (e.g., in Rust, 27 | /// this could be used in `Ty`, `Lifetime`, or `Const`). 28 | /// 29 | /// `#[variable]` has subtle effects on folding and parsing: 30 | /// 31 | /// * When folding a variable variant, we apply the substitution function, which yields 32 | /// a Parameter. We then downcast that to the type needed. If that downcast 33 | /// fails, we panic, as that indicates an ill-kinded substitution. 34 | /// * When parsing a variable variant, we parse an identifier and then check the in-scope 35 | /// bindings for variables with that name. If any are found, we extract the result 36 | /// (a parameter) and downcast it. If the resulting downcast fails, that is considered 37 | /// a parse error (ill-kinded term). 38 | pub(crate) fn has_variable_attr(attrs: &[Attribute]) -> bool { 39 | attrs.iter().any(|a| a.path().is_ident("variable")) 40 | } 41 | 42 | /// Extract a `#[variable]` attribute (if any) 43 | pub(crate) fn variable(attrs: &[Attribute]) -> syn::Result> { 44 | parse_attr_named(attrs, "variable") 45 | } 46 | 47 | /// Extract a `#[precedence]` level, defaults to 0 48 | pub(crate) fn precedence(attrs: &[Attribute]) -> syn::Result { 49 | Ok(parse_attr_named(attrs, "precedence")?.unwrap_or_default()) 50 | } 51 | 52 | /// Extracts any customization attribute from a list of attributes. 53 | pub(crate) fn customize(attrs: &[Attribute]) -> syn::Result { 54 | Ok(parse_attr_named(attrs, "customize")?.unwrap_or_default()) 55 | } 56 | 57 | fn parse_attr_named(attrs: &[Attribute], name: &str) -> syn::Result> 58 | where 59 | T: syn::parse::Parse, 60 | { 61 | let mut v: Vec = attrs 62 | .iter() 63 | .filter(|a| a.path().is_ident(name)) 64 | .map(|a| a.parse_args()) 65 | .collect::>()?; 66 | 67 | if v.len() > 1 { 68 | Err(syn::Error::new( 69 | attrs 70 | .iter() 71 | .filter(|a| a.path().is_ident(name)) 72 | .nth(1) 73 | .unwrap() 74 | .path() 75 | .span(), 76 | format!("multiple `{}` attributes", name), 77 | )) 78 | } else if v.len() == 1 { 79 | Ok(Some(v.pop().unwrap())) 80 | } else { 81 | Ok(None) 82 | } 83 | } 84 | 85 | /// Removes all attributes from the input that are specific to formality. 86 | pub(crate) fn remove_formality_attributes(input: &mut DeriveInput) { 87 | remove_formality_attributes_from_vec(&mut input.attrs); 88 | if let syn::Data::Enum(v) = &mut input.data { 89 | for variant in &mut v.variants { 90 | remove_formality_attributes_from_vec(&mut variant.attrs); 91 | } 92 | } 93 | } 94 | 95 | fn remove_formality_attributes_from_vec(attrs: &mut Vec) { 96 | attrs.retain(|attr| { 97 | !attr.path().is_ident("grammar") 98 | && !attr.path().is_ident("cast") 99 | && !attr.path().is_ident("variable") 100 | && !attr.path().is_ident("customize") 101 | && !attr.path().is_ident("precedence") 102 | }); 103 | } 104 | -------------------------------------------------------------------------------- /crates/formality-macros/src/constructors.rs: -------------------------------------------------------------------------------- 1 | use convert_case::{Case, Casing}; 2 | use proc_macro2::{Ident, TokenStream}; 3 | use quote::quote_spanned; 4 | use synstructure::VariantInfo; 5 | 6 | const RUST_KEYWORDS: &[&str] = &[ 7 | "mut", "true", "false", "const", "static", "ref", "struct", "enum", "trait", "union", "fn", 8 | "use", "return", "move", "let", "break", "loop", "continue", "await", "if", "for", "unsafe", 9 | ]; 10 | 11 | /// Create methods to build this type. 12 | /// 13 | /// For a struct, we create a single `new` method that takes each field. 14 | /// 15 | /// For an enum, we create methods named after each variant. 16 | pub(crate) fn constructor_methods(s: synstructure::Structure) -> TokenStream { 17 | match s.ast().data { 18 | syn::Data::Struct(_) => derive_new_for_struct(s), 19 | syn::Data::Enum(_) => derive_new_for_variants(s), 20 | syn::Data::Union(_) => Default::default(), 21 | } 22 | } 23 | 24 | fn derive_new_for_struct(s: synstructure::Structure<'_>) -> TokenStream { 25 | derive_new_for_variant( 26 | &s, 27 | &s.variants()[0], 28 | &Ident::new("new", s.ast().ident.span()), 29 | ) 30 | } 31 | 32 | fn derive_new_for_variants(s: synstructure::Structure<'_>) -> TokenStream { 33 | s.variants() 34 | .iter() 35 | .map(|v| { 36 | let mut fn_name = v.ast().ident.to_string().to_case(Case::Snake); 37 | if RUST_KEYWORDS.iter().any(|&kw| kw == fn_name) { 38 | fn_name.push('_'); 39 | } 40 | let fn_name = Ident::new(&fn_name, v.ast().ident.span()); 41 | derive_new_for_variant(&s, v, &fn_name) 42 | }) 43 | .collect() 44 | } 45 | 46 | fn derive_new_for_variant( 47 | s: &synstructure::Structure<'_>, 48 | v: &VariantInfo<'_>, 49 | fn_name: &Ident, 50 | ) -> TokenStream { 51 | let type_name = &s.ast().ident; 52 | let (impl_generics, type_generics, where_clauses) = s.ast().generics.split_for_impl(); 53 | 54 | // If there are no bindings, not worth it. 55 | if v.bindings().is_empty() { 56 | return TokenStream::default(); 57 | } 58 | 59 | let binding_names = v.bindings().iter().map(|b| &b.binding).collect::>(); 60 | let binding_types = v.bindings().iter().map(|b| &b.ast().ty).collect::>(); 61 | let construct = v.construct(|_b, i| { 62 | let name = binding_names[i]; 63 | quote_spanned!( 64 | binding_names[i].span() => 65 | formality_core::Upcast::upcast(#name) 66 | ) 67 | }); 68 | 69 | quote_spanned! { v.ast().ident.span() => 70 | #[allow(dead_code)] 71 | impl #impl_generics #type_name #type_generics 72 | where #where_clauses 73 | { 74 | pub fn #fn_name( 75 | #( 76 | #binding_names: impl formality_core::Upcast<#binding_types>, 77 | )* 78 | ) -> Self { 79 | #construct 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /crates/formality-macros/src/custom.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | 3 | #[derive(Default, Debug)] 4 | pub(crate) struct Customize { 5 | pub parse: bool, 6 | pub debug: bool, 7 | pub constructors: bool, 8 | pub fold: bool, 9 | pub visit: bool, 10 | } 11 | 12 | impl syn::parse::Parse for Customize { 13 | fn parse(input: syn::parse::ParseStream) -> syn::Result { 14 | let mut result = Customize::default(); 15 | 16 | let token_stream: TokenStream = input.parse()?; 17 | let mut tokens = token_stream.into_iter(); 18 | 19 | while let Some(token) = tokens.next() { 20 | macro_rules! match_customization_field_or_error { 21 | ($($ident:ident,)*) => { 22 | match token { 23 | $( 24 | proc_macro2::TokenTree::Ident(ident) if ident == stringify!($ident) => { 25 | if result.$ident { 26 | return Err(syn::Error::new(ident.span(), &format!("already customizing {}", stringify!($ident)))); 27 | } 28 | result.$ident = true; 29 | } 30 | )* 31 | 32 | _ => { 33 | return Err(syn::Error::new( 34 | token.span(), 35 | "unexpected token in customization", 36 | )); 37 | } 38 | } 39 | }; 40 | } 41 | 42 | match_customization_field_or_error! { 43 | parse, 44 | debug, 45 | constructors, 46 | fold, 47 | visit, 48 | } 49 | 50 | if let Some(token) = tokens.next() { 51 | match token { 52 | proc_macro2::TokenTree::Punct(p) if p.as_char() == ',' => (), 53 | _ => { 54 | return Err(syn::Error::new( 55 | token.span(), 56 | "unexpected token in customization", 57 | )); 58 | } 59 | } 60 | } else { 61 | break; 62 | } 63 | } 64 | 65 | Ok(result) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /crates/formality-macros/src/fold.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro2::TokenStream; 4 | use quote::quote; 5 | 6 | use crate::attrs::has_variable_attr; 7 | 8 | pub(crate) fn derive_fold(mut s: synstructure::Structure) -> TokenStream { 9 | s.underscore_const(true); 10 | s.bind_with(|_| synstructure::BindStyle::Move); 11 | 12 | let substitute_body = s.each_variant(|vi| { 13 | let bindings = vi.bindings(); 14 | if has_variable_attr(vi.ast().attrs) { 15 | if bindings.len() != 1 { 16 | return syn::Error::new( 17 | vi.ast().ident.span(), 18 | "#[variable] can only be used on variants with 1 binding", 19 | ) 20 | .into_compile_error(); 21 | } 22 | 23 | let bi = &bindings[0]; 24 | 25 | quote! { 26 | if let Some(p) = substitution_fn(*#bi) { 27 | formality_core::Downcast::downcast(&p) 28 | .unwrap_or_else(|| panic!("ill-kinded value `{:?}`", p)) 29 | } else { 30 | Clone::clone(self) 31 | } 32 | } 33 | } else { 34 | vi.construct(|_, index| { 35 | let bind = &bindings[index]; 36 | quote! { 37 | CoreFold::substitute(#bind, substitution_fn) 38 | } 39 | }) 40 | } 41 | }); 42 | 43 | // s.add_bounds(synstructure::AddBounds::None); 44 | s.gen_impl(quote! { 45 | use formality_core::{fold::CoreFold, fold::SubstitutionFn}; 46 | 47 | gen impl CoreFold for @Self { 48 | fn substitute(&self, substitution_fn: SubstitutionFn<'_, crate::FormalityLang>) -> Self { 49 | match self { 50 | #substitute_body 51 | } 52 | } 53 | } 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /crates/formality-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::{Span, TokenStream}; 2 | use quote::quote; 3 | use spec::FormalitySpec; 4 | // use syn::DeriveInput; 5 | 6 | extern crate proc_macro; 7 | 8 | mod as_methods; 9 | mod attrs; 10 | mod cast; 11 | mod constructors; 12 | mod custom; 13 | mod debug; 14 | mod fixed_point; 15 | mod fold; 16 | mod parse; 17 | mod precedence; 18 | mod spec; 19 | mod term; 20 | mod test; 21 | mod variable; 22 | mod visit; 23 | 24 | // synstructure::decl_derive!([Fold] => fold::derive_fold); 25 | // synstructure::decl_derive!([Parse, attributes(grammar)] => parse::derive_parse); 26 | 27 | #[proc_macro_attribute] 28 | pub fn term(args: TokenStream, input: TokenStream) -> TokenStream { 29 | let spec = if args.is_empty() { 30 | None 31 | } else { 32 | Some(syn::parse_macro_input!(args as FormalitySpec)) 33 | }; 34 | let input = syn::parse_macro_input!(input as syn::DeriveInput); 35 | 36 | match term::term(spec, input) { 37 | Ok(s) => s.into(), 38 | Err(e) => e.into_compile_error().into(), 39 | } 40 | } 41 | 42 | synstructure::decl_derive!([Visit] => visit::derive_visit); 43 | 44 | #[proc_macro_attribute] 45 | pub fn fixed_point(args: TokenStream, input: TokenStream) -> TokenStream { 46 | let args = syn::parse_macro_input!(args as fixed_point::FixedPointArgs); 47 | let input = syn::parse_macro_input!(input as syn::ItemFn); 48 | match fixed_point::fixed_point(args, input) { 49 | Ok(s) => quote!(#s).into(), 50 | Err(e) => e.into_compile_error().into(), 51 | } 52 | } 53 | 54 | #[proc_macro_attribute] 55 | pub fn test(args: TokenStream, input: TokenStream) -> TokenStream { 56 | let input = syn::parse_macro_input!(input as syn::ItemFn); 57 | match test::test(args, input) { 58 | Ok(s) => quote!(#[::core::prelude::v1::test] #s).into(), 59 | Err(e) => e.into_compile_error().into(), 60 | } 61 | } 62 | 63 | /// Invoked like `respan!(X (Y...))` -- produces `Y...` with span from `X`. 64 | #[proc_macro] 65 | pub fn respan(t: TokenStream) -> TokenStream { 66 | let mut t = t.into_iter(); 67 | let dummy = t.next().unwrap(); 68 | let stream = t.next().unwrap(); 69 | return match stream { 70 | proc_macro::TokenTree::Group(g) => set_span(pick_span(dummy), g.stream()), 71 | _ => unreachable!(), 72 | }; 73 | 74 | fn pick_span(dummy: proc_macro::TokenTree) -> Span { 75 | match dummy { 76 | // Careful! The compiler creates groups for fragments 77 | // (e.g., $x:expr) that have the span of the macro that parsed them. 78 | // We want the span of the fragment's contents in that case. 79 | proc_macro::TokenTree::Group(g) 80 | if g.delimiter() == proc_macro::Delimiter::None && !g.stream().is_empty() => 81 | { 82 | g.stream().into_iter().next().unwrap().span() 83 | } 84 | _ => dummy.span(), 85 | } 86 | } 87 | 88 | fn set_span(span: Span, tokens: TokenStream) -> TokenStream { 89 | tokens 90 | .into_iter() 91 | .map(|token| match token { 92 | proc_macro::TokenTree::Group(group) => { 93 | let mut fixed = 94 | proc_macro::Group::new(group.delimiter(), set_span(span, group.stream())); 95 | fixed.set_span(span); 96 | proc_macro::TokenTree::Group(fixed) 97 | } 98 | mut token => { 99 | token.set_span(span); 100 | token 101 | } 102 | }) 103 | .collect() 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /crates/formality-macros/src/precedence.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use proc_macro2::{Ident, Literal, Span, TokenStream}; 4 | use quote::quote; 5 | use syn::spanned::Spanned; 6 | 7 | #[derive(Debug, Default)] 8 | pub(crate) enum Precedence { 9 | #[default] 10 | Defaulted, 11 | Parsed { 12 | level: usize, 13 | 14 | /// Will be either the name of a construct method on the 15 | /// `Parser::Associativity` type: `left``, `right``, or `none`. 16 | associativity: Ident, 17 | }, 18 | } 19 | 20 | impl Precedence { 21 | pub fn expr(&self) -> TokenStream { 22 | match self { 23 | Precedence::Defaulted => quote!(formality_core::parse::Precedence::default()), 24 | Precedence::Parsed { 25 | level, 26 | associativity, 27 | } => { 28 | let level = Literal::usize_unsuffixed(*level); 29 | quote!(formality_core::parse::Precedence::#associativity(#level)) 30 | } 31 | } 32 | } 33 | } 34 | 35 | impl syn::parse::Parse for Precedence { 36 | fn parse(input: syn::parse::ParseStream) -> syn::Result { 37 | let token_stream: TokenStream = input.parse()?; 38 | let span = token_stream.span(); 39 | let mut tokens = token_stream.into_iter().peekable(); 40 | 41 | let Some(token) = tokens.next() else { 42 | return Err(syn::Error::new(span, "precedence expected")); 43 | }; 44 | 45 | let level; 46 | match token { 47 | proc_macro2::TokenTree::Literal(l) => { 48 | let l_str = l.to_string(); 49 | match usize::from_str(&l_str) { 50 | Ok(l) => level = l, 51 | Err(_) => return Err(syn::Error::new(l.span(), "integer precedence expected")), 52 | } 53 | } 54 | 55 | _ => { 56 | return Err(syn::Error::new( 57 | token.span(), 58 | "unexpected token in precedence", 59 | )); 60 | } 61 | } 62 | 63 | const VALID_ASSOCIATIVITIES: &[&str] = &["left", "right", "none"]; 64 | let associativity = if let Some(comma_token) = tokens.next() { 65 | match &comma_token { 66 | proc_macro2::TokenTree::Punct(punct) if punct.as_char() == ',' => { 67 | match tokens.next() { 68 | Some(proc_macro2::TokenTree::Ident(ident)) 69 | if VALID_ASSOCIATIVITIES 70 | .iter() 71 | .any(|a| *a == ident.to_string()) => 72 | { 73 | ident 74 | } 75 | 76 | _ => { 77 | return Err(syn::Error::new( 78 | comma_token.span(), 79 | format!( 80 | "expected valid associativity after comma, one of `{:?}`", 81 | VALID_ASSOCIATIVITIES 82 | ), 83 | )); 84 | } 85 | } 86 | } 87 | 88 | _ => { 89 | return Err(syn::Error::new( 90 | comma_token.span(), 91 | "extra `,` followed by associativity", 92 | )); 93 | } 94 | } 95 | } else { 96 | Ident::new("left", Span::call_site()) 97 | }; 98 | 99 | if let Some(token) = tokens.next() { 100 | return Err(syn::Error::new(token.span(), "extra tokens")); 101 | } 102 | 103 | Ok(Precedence::Parsed { 104 | level, 105 | associativity, 106 | }) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /crates/formality-macros/src/term.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | use syn::DeriveInput; 4 | 5 | use crate::{ 6 | attrs::{self, remove_formality_attributes}, 7 | cast::{downcast_impls, upcast_impls}, 8 | constructors::constructor_methods, 9 | debug::derive_debug_with_spec, 10 | fold::derive_fold, 11 | parse::derive_parse_with_spec, 12 | spec::FormalitySpec, 13 | visit::derive_visit, 14 | }; 15 | 16 | pub fn term(spec: Option, mut input: DeriveInput) -> syn::Result { 17 | let customize = attrs::customize(&input.attrs)?; 18 | let fold_impl = if customize.fold { 19 | Default::default() 20 | } else { 21 | derive_fold(synstructure::Structure::new(&input)) 22 | }; 23 | let visit_impl = if customize.visit { 24 | Default::default() 25 | } else { 26 | derive_visit(synstructure::Structure::new(&input)) 27 | }; 28 | let parse_impl = if customize.parse { 29 | Default::default() 30 | } else { 31 | derive_parse_with_spec(synstructure::Structure::new(&input), spec.as_ref())? 32 | }; 33 | let debug_impl = if customize.debug { 34 | Default::default() 35 | } else { 36 | derive_debug_with_spec(synstructure::Structure::new(&input), spec.as_ref()) 37 | }; 38 | let term_impl = derive_term(synstructure::Structure::new(&input)); 39 | let downcast_impls = downcast_impls(synstructure::Structure::new(&input)); 40 | let upcast_impls = upcast_impls(synstructure::Structure::new(&input)); 41 | let constructors = if customize.constructors { 42 | Default::default() 43 | } else { 44 | constructor_methods(synstructure::Structure::new(&input)) 45 | }; 46 | remove_formality_attributes(&mut input); 47 | 48 | Ok(quote! { 49 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 50 | #input 51 | 52 | #fold_impl 53 | #visit_impl 54 | #parse_impl 55 | #debug_impl 56 | #term_impl 57 | #(#downcast_impls)* 58 | #(#upcast_impls)* 59 | #constructors 60 | }) 61 | } 62 | 63 | fn derive_term(mut s: synstructure::Structure) -> TokenStream { 64 | s.underscore_const(true); 65 | s.bind_with(|_| synstructure::BindStyle::Move); 66 | 67 | // s.add_bounds(synstructure::AddBounds::None); 68 | s.gen_impl(quote! { 69 | use formality_core::term::CoreTerm; 70 | 71 | gen impl CoreTerm for @Self { 72 | } 73 | }) 74 | } 75 | -------------------------------------------------------------------------------- /crates/formality-macros/src/test.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | 3 | pub(crate) fn test(_args: TokenStream, mut item_fn: syn::ItemFn) -> syn::Result { 4 | let original_fn_body = item_fn.block.clone(); 5 | item_fn.block = syn::parse_quote!({ 6 | formality_core::with_tracing_logs(move || { 7 | #original_fn_body 8 | }) 9 | }); 10 | Ok(item_fn) 11 | } 12 | -------------------------------------------------------------------------------- /crates/formality-macros/src/variable.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | 3 | /// The contents of a `#[variable]` attribute. Currently just a kind. 4 | pub struct Variable { 5 | pub kind: syn::Expr, 6 | } 7 | 8 | impl syn::parse::Parse for Variable { 9 | fn parse(input: syn::parse::ParseStream) -> syn::Result { 10 | let kind: syn::Expr = input.parse()?; 11 | 12 | let tokens: TokenStream = input.parse()?; 13 | let mut tokens = tokens.into_iter(); 14 | if let Some(token) = tokens.next() { 15 | return Err(syn::Error::new(token.span(), "extra tokens")); 16 | } 17 | 18 | Ok(Variable { kind }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /crates/formality-macros/src/visit.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro2::TokenStream; 4 | use quote::quote; 5 | 6 | pub(crate) fn derive_visit(mut s: synstructure::Structure) -> TokenStream { 7 | s.underscore_const(true); 8 | s.bind_with(|_| synstructure::BindStyle::Move); 9 | 10 | let free_variables_body = s.each( 11 | |field| quote!(output.extend(<_ as CoreVisit>::free_variables(#field))), 12 | ); 13 | 14 | let size_body = 15 | s.each(|field| quote!(__sum += <_ as CoreVisit>::size(#field))); 16 | 17 | let assert_valid_body = 18 | s.each(|field| quote!(<_ as CoreVisit>::assert_valid(#field))); 19 | 20 | // s.add_bounds(synstructure::AddBounds::None); 21 | s.gen_impl(quote! { 22 | use formality_core::{visit::CoreVisit, variable::CoreVariable}; 23 | 24 | gen impl CoreVisit for @Self { 25 | fn free_variables(&self) -> Vec> { 26 | let mut output = vec![]; 27 | match self { 28 | #free_variables_body 29 | } 30 | output 31 | } 32 | 33 | fn size(&self) -> usize { 34 | let mut __sum = 0; 35 | __sum += 1; 36 | match self { 37 | #size_body 38 | } 39 | __sum 40 | } 41 | 42 | fn assert_valid(&self) { 43 | match self { 44 | #assert_valid_body 45 | } 46 | } 47 | } 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /crates/formality-prove/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "formality-prove" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | derive-new = "0.5.9" 10 | formality-types = { path = "../formality-types" } 11 | formality-macros = { path = "../formality-macros" } 12 | formality-core = { path = "../formality-core" } 13 | tracing = "0.1" 14 | contracts = "0.6.3" 15 | anyhow = "1.0.66" 16 | 17 | [dev-dependencies] 18 | expect-test = "1.4.0" 19 | -------------------------------------------------------------------------------- /crates/formality-prove/src/db.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crates/formality-prove/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Defines the language used by derive(term) and friends. 2 | use formality_types::rust::FormalityLang; 3 | 4 | mod db; 5 | mod decls; 6 | mod prove; 7 | 8 | pub use decls::*; 9 | pub use prove::prove; 10 | pub use prove::Constraints; 11 | pub use prove::{is_definitely_not_proveable, may_not_be_provable, negation_via_failure}; 12 | pub use prove::{Bias, Env}; 13 | 14 | #[cfg(test)] 15 | mod test; 16 | 17 | pub mod test_util; 18 | -------------------------------------------------------------------------------- /crates/formality-prove/src/prove.rs: -------------------------------------------------------------------------------- 1 | mod combinators; 2 | mod constraints; 3 | mod env; 4 | mod is_local; 5 | mod minimize; 6 | mod negation; 7 | mod prove_after; 8 | mod prove_eq; 9 | mod prove_normalize; 10 | mod prove_via; 11 | mod prove_wc; 12 | mod prove_wc_list; 13 | mod prove_wf; 14 | 15 | pub use constraints::Constraints; 16 | use formality_core::judgment::{FailedRule, TryIntoIter}; 17 | use formality_core::visit::CoreVisit; 18 | use formality_core::{set, ProvenSet, Upcast}; 19 | use formality_types::grammar::Wcs; 20 | use tracing::Level; 21 | 22 | use crate::decls::Decls; 23 | 24 | pub use self::env::{Bias, Env}; 25 | use self::prove_wc_list::prove_wc_list; 26 | pub use negation::{is_definitely_not_proveable, may_not_be_provable, negation_via_failure}; 27 | 28 | /// Top-level entry point for proving things; other rules recurse to this one. 29 | pub fn prove( 30 | decls: impl Upcast, 31 | env: impl Upcast, 32 | assumptions: impl Upcast, 33 | goal: impl Upcast, 34 | ) -> ProvenSet { 35 | let decls: Decls = decls.upcast(); 36 | let env: Env = env.upcast(); 37 | let assumptions: Wcs = assumptions.upcast(); 38 | let goal: Wcs = goal.upcast(); 39 | 40 | let (env, (assumptions, goal), min) = minimize::minimize(env, (assumptions, goal)); 41 | 42 | let span = tracing::span!(Level::DEBUG, "prove", ?goal, ?assumptions, ?env, ?decls); 43 | let _guard = span.enter(); 44 | 45 | let term_in = (&assumptions, &goal); 46 | if term_in.size() > decls.max_size { 47 | tracing::debug!( 48 | "term has size {} which exceeds max size of {}", 49 | term_in.size(), 50 | decls.max_size 51 | ); 52 | return ProvenSet::singleton(Constraints::none(env).ambiguous()); 53 | } 54 | 55 | assert!(env.encloses(term_in)); 56 | 57 | struct ProveFailureLabel(String); 58 | let label = ProveFailureLabel(format!( 59 | "prove {{ goal: {goal:?}, assumptions: {assumptions:?}, env: {env:?}, decls: {decls:?} }}" 60 | )); 61 | impl std::fmt::Debug for ProveFailureLabel { 62 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 63 | f.write_str(&self.0) 64 | } 65 | } 66 | let result_set = 67 | match prove_wc_list(decls, &env, assumptions, goal).try_into_iter(|| "".to_string()) { 68 | Ok(s) => ProvenSet::from_iter(s), 69 | Err(e) => ProvenSet::failed_rules(label, set![FailedRule::new(e)]), 70 | }; 71 | 72 | tracing::debug!(?result_set); 73 | 74 | result_set.map(|r| { 75 | assert!(r.is_valid_extension_of(&env)); 76 | min.reconstitute(r) 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /crates/formality-prove/src/prove/combinators.rs: -------------------------------------------------------------------------------- 1 | use crate::decls::Decls; 2 | use formality_core::ProvenSet; 3 | use formality_types::rust::Term; 4 | 5 | use super::{Constraints, Env}; 6 | 7 | pub fn zip( 8 | decls: &Decls, 9 | env: &Env, 10 | context: &C, 11 | mut a: Vec, 12 | mut b: Vec, 13 | op: &impl Fn(Decls, Env, C, A, B) -> ProvenSet, 14 | ) -> ProvenSet 15 | where 16 | A: Term, 17 | B: Term, 18 | C: Term, 19 | { 20 | assert_eq!(a.len(), b.len()); 21 | 22 | if a.is_empty() && b.is_empty() { 23 | return ProvenSet::singleton(Constraints::none(env)); 24 | } 25 | 26 | let a0 = a.remove(0); 27 | let b0 = b.remove(0); 28 | op(decls.clone(), env.clone(), context.clone(), a0, b0).flat_map(|c1| { 29 | let context = c1.substitution().apply(context); 30 | let a = c1.substitution().apply(&a); 31 | let b = c1.substitution().apply(&b); 32 | zip(decls, c1.env(), &context, a, b, op).map(move |c2| c1.seq(c2)) 33 | }) 34 | } 35 | 36 | pub fn for_all( 37 | decls: &Decls, 38 | env: &Env, 39 | context: &C, 40 | a: &[A], 41 | op: &impl Fn(Decls, Env, C, A) -> ProvenSet, 42 | ) -> ProvenSet 43 | where 44 | A: Term, 45 | C: Term, 46 | { 47 | if a.is_empty() { 48 | return ProvenSet::singleton(Constraints::none(env)); 49 | } 50 | 51 | let a0 = a[0].clone(); 52 | let a_remaining: Vec = a[1..].to_vec(); 53 | op(decls.clone(), env.clone(), context.clone(), a0).flat_map(|c1| { 54 | let context = c1.substitution().apply(context); 55 | let a_remaining = c1.substitution().apply(&a_remaining); 56 | for_all(decls, c1.env(), &context, &a_remaining, op).map(move |c2| c1.seq(c2)) 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /crates/formality-prove/src/prove/minimize/test.rs: -------------------------------------------------------------------------------- 1 | use expect_test::expect; 2 | use formality_core::To; 3 | use formality_macros::test; 4 | use formality_types::{ 5 | grammar::{Binder, Parameter, ScalarId, Ty}, 6 | rust::term, 7 | }; 8 | 9 | use crate::prove::{Constraints, Env}; 10 | 11 | use super::minimize; 12 | 13 | #[test] 14 | fn minimize_a() { 15 | let env = Env::default(); 16 | let term: Binder> = term(" [A, C]"); 17 | let (env, subst) = env.existential_substitution(&term); 18 | let term = term.instantiate_with(&subst).unwrap(); 19 | 20 | expect!["(Env { variables: [?ty_1, ?ty_2, ?ty_3], bias: Soundness }, [?ty_1, ?ty_3])"] 21 | .assert_eq(&format!("{:?}", (&env, &term))); 22 | 23 | let (mut env_min, term_min, m) = minimize(env, term); 24 | 25 | expect!["(Env { variables: [?ty_0, ?ty_1], bias: Soundness }, [?ty_0, ?ty_1])"] 26 | .assert_eq(&format!("{:?}", (&env_min, &term_min))); 27 | 28 | let ty0 = term_min[0].as_variable().unwrap(); 29 | let ty1 = term_min[1].as_variable().unwrap(); 30 | let ty2 = env_min.insert_fresh_before( 31 | formality_types::grammar::ParameterKind::Ty, 32 | env_min.universe(ty1), 33 | ); 34 | 35 | let c_min = Constraints { 36 | env: env_min, 37 | known_true: true, 38 | substitution: vec![(ty1, ty2.to::()), (ty0, ScalarId::U32.to::())] 39 | .into_iter() 40 | .collect(), 41 | }; 42 | let c = m.reconstitute(c_min); 43 | 44 | expect![[r#" 45 | Constraints { 46 | env: Env { 47 | variables: [ 48 | ?ty_1, 49 | ?ty_2, 50 | ?ty_4, 51 | ?ty_3, 52 | ], 53 | bias: Soundness, 54 | }, 55 | known_true: true, 56 | substitution: { 57 | ?ty_1 => u32, 58 | ?ty_3 => ?ty_4, 59 | }, 60 | } 61 | "#]] 62 | .assert_debug_eq(&c); 63 | } 64 | -------------------------------------------------------------------------------- /crates/formality-prove/src/prove/negation.rs: -------------------------------------------------------------------------------- 1 | use crate::{Bias, Constraints, Env}; 2 | use formality_core::{fold::CoreFold, ProvenSet, Upcast}; 3 | use formality_types::{ 4 | grammar::{Substitution, Variable, Wcs}, 5 | rust::FormalityLang, 6 | }; 7 | 8 | /// This succeeds if `f` definitely fails: there are no possible 9 | /// ways to prove its body. 10 | /// 11 | /// This fails if `f' is ambiguous, so it is biases towards soundness. 12 | /// It never succeeds if `f` may actually hold, but may fail even if 13 | /// `f` does not hold. For this to be correct the body of `f` must 14 | /// be complete: it must only fail if there is definitely no solution. 15 | /// 16 | /// Proving `∀X. not(F(X))` is the same as proving `not(∃X. F(X))`, so 17 | /// this replaces all universal variables with existantials. 18 | #[tracing::instrument(level = "Debug", skip_all)] 19 | pub fn is_definitely_not_proveable>( 20 | env: &Env, 21 | assumptions: impl Upcast, 22 | data: T, 23 | f: impl FnOnce(Env, Wcs, T) -> ProvenSet, 24 | ) -> ProvenSet { 25 | assert!(env.bias() == Bias::Soundness); 26 | 27 | // Require only universal variables, so e.g. `forall not (T: Foo)`. 28 | // For those, we can convert universal and existential and try to prove 29 | // the inverse (so try to prove `exists (T: Foo)`, with a bias towards 30 | // completeness. If that fails, then we know that for all `T`, `T: Foo` is false. 31 | // 32 | // But with existential we can do no such trick. Given `exists not (T: Foo)`, 33 | // certainly if we can prove `forall (T: Foo)` then we know this is false. 34 | // But if we can't prove it, it doesn't tell us that there exists a `T` 35 | // where `not (T: Foo)`, only that we could not prove it is true for all `T` 36 | // (put another way, it may still be true for all T, we just couldn't prove it). 37 | // 38 | // (FIXME(nikomatsakis): This sounds suspicious, if we *truly* handle bias correctly, 39 | // this might not hold; I suspect we do not, which is why I wonder if we can 40 | // frame this another way, such as "known not to hold".) 41 | assert!(env.only_universal_variables()); 42 | negation_via_failure(env, assumptions, data, f) 43 | } 44 | 45 | /// This fails if `f` definitely succeeds. There is no way to prove `f`. 46 | /// This should only be used if we're currently biased towards completeness 47 | /// as it returns success even if there's still ambiguity. 48 | #[tracing::instrument(level = "Debug", skip_all)] 49 | pub fn may_not_be_provable>( 50 | env: &Env, 51 | assumptions: impl Upcast, 52 | data: T, 53 | f: impl FnOnce(Env, Wcs, T) -> ProvenSet, 54 | ) -> ProvenSet { 55 | assert!(env.bias() == Bias::Completeness); 56 | negation_via_failure(env, assumptions, data, f) 57 | } 58 | 59 | pub fn negation_via_failure>( 60 | env: &Env, 61 | assumptions: impl Upcast, 62 | data: T, 63 | f: impl FnOnce(Env, Wcs, T) -> ProvenSet, 64 | ) -> ProvenSet { 65 | let assumptions: Wcs = assumptions.upcast(); 66 | tracing::debug!(?assumptions, ?data); 67 | 68 | let flipped_bias = match env.bias() { 69 | Bias::Soundness => Bias::Completeness, 70 | Bias::Completeness => Bias::Soundness, 71 | }; 72 | 73 | // As we prove the negation, we have to flip the bias and 74 | // quantifiers of all variables. 75 | let mut flipped_env = Env::new_with_bias(flipped_bias); 76 | let flip_quantification: Substitution = env 77 | .variables() 78 | .iter() 79 | .map(|v| { 80 | let v1: Variable = if v.is_universal() { 81 | flipped_env.fresh_existential(v.kind()).upcast() 82 | } else { 83 | flipped_env.fresh_universal(v.kind()).upcast() 84 | }; 85 | (v, v1) 86 | }) 87 | .collect(); 88 | 89 | let flipped_assumptions = flip_quantification.apply(&assumptions); 90 | let flipped_data = flip_quantification.apply(&data); 91 | 92 | let cs = f(flipped_env, flipped_assumptions, flipped_data); 93 | match cs.into_set() { 94 | Ok(s) => { 95 | if let Some(constraints) = s 96 | .iter() 97 | .find(|constraints| constraints.unconditionally_true()) 98 | { 99 | ProvenSet::failed( 100 | "negation_via_failure", 101 | format!("found an unconditionally true solution {constraints:?}"), 102 | ) 103 | } else { 104 | tracing::debug!("ambiguous `negation_via_failure`, solutions: {s:?}"); 105 | ProvenSet::singleton(Constraints::none(env).ambiguous()) 106 | } 107 | } 108 | 109 | Err(err) => { 110 | tracing::debug!("Proved `negation_via_failure`, error = {err}"); 111 | ProvenSet::singleton(Constraints::none(env)) 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /crates/formality-prove/src/prove/prove_after.rs: -------------------------------------------------------------------------------- 1 | use formality_core::judgment_fn; 2 | use formality_types::grammar::Wcs; 3 | 4 | use crate::{decls::Decls, prove::prove}; 5 | 6 | use super::constraints::Constraints; 7 | 8 | judgment_fn! { 9 | pub fn prove_after( 10 | _decls: Decls, 11 | constraints: Constraints, 12 | assumptions: Wcs, 13 | goal: Wcs, 14 | ) => Constraints { 15 | debug(constraints, goal, assumptions) 16 | 17 | ( 18 | (let (assumptions, goal) = c1.substitution().apply(&(assumptions, goal))) 19 | (prove(decls, c1.env(), assumptions, goal) => c2) 20 | --- ("prove_after") 21 | (prove_after(decls, c1, assumptions, goal) => c1.seq(c2)) 22 | ) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crates/formality-prove/src/prove/prove_via.rs: -------------------------------------------------------------------------------- 1 | use formality_core::judgment_fn; 2 | use formality_types::grammar::{WcData, Wcs}; 3 | 4 | use crate::{ 5 | decls::Decls, 6 | prove::{constraints::Constraints, env::Env, prove, prove_after::prove_after}, 7 | }; 8 | 9 | judgment_fn! { 10 | /// Check whether the where-clause `via` (which is one of the `assumptions` that are in in scope) 11 | /// can be used to prove `goal` (the thing we are trying to prove). 12 | /// 13 | /// This is equivalent to the "elaboration" of the environment that takes place in rustc, 14 | /// but done lazilly. For example, if you have `where T: Eq` then you can clearly prove `T: Eq` 15 | /// but you can also prove `T: PartialEq` because `trait Eq: PartialEq`. 16 | pub fn prove_via( 17 | _decls: Decls, 18 | env: Env, 19 | assumptions: Wcs, 20 | via: WcData, 21 | goal: WcData, 22 | ) => Constraints { 23 | debug(goal, via, assumptions, env) 24 | 25 | ( 26 | // `c` = "clause", the name for something that we are assuming is true. 27 | (let (skel_c, parameters_c) = pred_1.debone()) 28 | // `g` = "goal, the name for something that we are trying to prove. 29 | (let (skel_g, parameters_g) = pred_2.debone()) 30 | (if skel_c == skel_g)! 31 | (prove(decls, env, assumptions, Wcs::all_eq(parameters_c, parameters_g)) => c) 32 | ----------------------------- ("predicate-congruence-axiom") 33 | (prove_via(decls, env, assumptions, WcData::Predicate(pred_1), WcData::Predicate(pred_2)) => c) 34 | ) 35 | 36 | ( 37 | (let (skel_c, parameters_c) = rel_1.debone()) 38 | (let (skel_g, parameters_g) = rel_2.debone()) 39 | (if skel_c == skel_g) 40 | (if parameters_c == parameters_g)! // for relations, we require 100% match 41 | ----------------------------- ("relation-axiom") 42 | (prove_via(_decls, env, _assumptions, WcData::Relation(rel_1), WcData::Relation(rel_2)) => Constraints::none(env)) 43 | ) 44 | 45 | // If you have `where for<'a> T: Trait<'a>` then you can prove `T: Trait<'b>` for any `'b`. 46 | ( 47 | (let (env, subst) = env.existential_substitution(&binder)) 48 | (let via1 = binder.instantiate_with(&subst).unwrap()) 49 | // Try to prove `T: Trait == goal`. 50 | (prove_via(decls, env, assumptions, via1, goal) => c) 51 | ----------------------------- ("forall") 52 | (prove_via(decls, env, assumptions, WcData::ForAll(binder), goal) => c.pop_subst(&subst)) 53 | ) 54 | 55 | // If you have `where if (T: Debug) T: Foo` (not in Rust but it should be...)... 56 | ( 57 | // if the goal is `T: Foo`... 58 | (prove_via(&decls, env, &assumptions, wc_consequence, goal) => c) 59 | // ...and we can prove `T: Debug`... then it holds. 60 | (prove_after(&decls, c, &assumptions, &wc_condition) => c) 61 | ----------------------------- ("implies") 62 | (prove_via(decls, env, assumptions, WcData::Implies(wc_condition, wc_consequence), goal) => c) 63 | ) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /crates/formality-prove/src/prove/prove_wc_list.rs: -------------------------------------------------------------------------------- 1 | use formality_core::{judgment_fn, Cons}; 2 | use formality_types::grammar::Wcs; 3 | 4 | use crate::{ 5 | decls::Decls, 6 | prove::{constraints::Constraints, prove_after::prove_after}, 7 | }; 8 | 9 | use super::{env::Env, prove_wc::prove_wc}; 10 | 11 | judgment_fn! { 12 | pub fn prove_wc_list( 13 | _decls: Decls, 14 | env: Env, 15 | assumptions: Wcs, 16 | goal: Wcs, 17 | ) => Constraints { 18 | debug(goal, assumptions, env) 19 | 20 | assert(env.encloses((&assumptions, &goal))) 21 | 22 | ( 23 | --- ("none") 24 | (prove_wc_list(_decls, env, _assumptions, ()) => Constraints::none(env)) 25 | ) 26 | 27 | ( 28 | (prove_wc(&decls, env, &assumptions, wc0) => c) 29 | (prove_after(&decls, c, &assumptions, &wcs1) => c) 30 | --- ("some") 31 | (prove_wc_list(decls, env, assumptions, Cons(wc0, wcs1)) => c) 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/formality-prove/src/prove/prove_wf.rs: -------------------------------------------------------------------------------- 1 | use formality_core::{judgment_fn, ProvenSet}; 2 | use formality_types::grammar::{ 3 | AliasName, AliasTy, ConstData, LtData, Parameter, Parameters, RigidName, RigidTy, UniversalVar, 4 | Wcs, 5 | }; 6 | 7 | use crate::{ 8 | decls::Decls, 9 | prove::{combinators::for_all, prove_after::prove_after}, 10 | }; 11 | 12 | use super::{constraints::Constraints, env::Env}; 13 | 14 | judgment_fn! { 15 | pub fn prove_wf( 16 | _decls: Decls, 17 | env: Env, 18 | assumptions: Wcs, 19 | goal: Parameter, 20 | ) => Constraints { 21 | debug(goal, assumptions, env) 22 | 23 | assert(env.encloses((&assumptions, &goal))) 24 | 25 | ( 26 | // Always assume that universal variables are WF. This is debatable, it implies 27 | // that we ensure by construction that the values we infer for existential variables 28 | // are WF. An alternative would be to add explicit assumptions into the environment 29 | // for every universal variable. That just seems tedious. 30 | --- ("universal variables") 31 | (prove_wf(_decls, env, _assumptions, UniversalVar { .. }) => Constraints::none(env)) 32 | ) 33 | 34 | ( 35 | (for_all(&decls, &env, &assumptions, ¶meters, &prove_wf) => c) 36 | --- ("tuples") 37 | (prove_wf(decls, env, assumptions, RigidTy { name: RigidName::Tuple(_), parameters }) => c) 38 | ) 39 | 40 | ( 41 | (for_all(&decls, &env, &assumptions, ¶meters, &prove_wf) => c) 42 | --- ("integers and booleans") 43 | (prove_wf(decls, env, assumptions, RigidTy { name: RigidName::ScalarId(_), parameters }) => c) 44 | ) 45 | 46 | ( 47 | (for_all(&decls, &env, &assumptions, ¶meters, &prove_wf) => c) 48 | (let t = decls.adt_decl(&adt_id)) 49 | (let t = t.binder.instantiate_with(¶meters).unwrap()) 50 | (prove_after(&decls, c, &assumptions, t.where_clause) => c) 51 | --- ("ADT") 52 | (prove_wf(decls, env, assumptions, RigidTy { name: RigidName::AdtId(adt_id), parameters }) => c) 53 | ) 54 | 55 | ( 56 | --- ("static lifetime") 57 | (prove_wf(_decls, env, _assumptions, LtData::Static) => Constraints::none(env)) 58 | ) 59 | 60 | ( 61 | (prove_wf(&decls, &env, &assumptions, ty) => c) 62 | --- ("rigid constants") 63 | (prove_wf(decls, env, assumptions, ConstData::Value(_, ty)) => c) 64 | ) 65 | 66 | ( 67 | (prove_alias_wf(&decls, &env, &assumptions, name, parameters) => c) 68 | --- ("aliases") 69 | (prove_wf(decls, env, assumptions, AliasTy { name, parameters }) => c) 70 | ) 71 | } 72 | } 73 | 74 | pub fn prove_alias_wf( 75 | decls: &Decls, 76 | env: &Env, 77 | assumptions: &Wcs, 78 | _name: AliasName, 79 | parameters: Parameters, 80 | ) -> ProvenSet { 81 | // FIXME: verify self type implements trait 82 | for_all(decls, env, assumptions, ¶meters, &prove_wf) 83 | } 84 | -------------------------------------------------------------------------------- /crates/formality-prove/src/test.rs: -------------------------------------------------------------------------------- 1 | mod adt_wf; 2 | mod eq_assumptions; 3 | mod eq_partial_eq; 4 | mod exists_constraints; 5 | mod expanding; 6 | mod is_local; 7 | mod magic_copy; 8 | mod occurs_check; 9 | mod simple_impl; 10 | mod universes; 11 | -------------------------------------------------------------------------------- /crates/formality-prove/src/test/adt_wf.rs: -------------------------------------------------------------------------------- 1 | use expect_test::expect; 2 | use formality_core::test; 3 | use formality_types::{ 4 | grammar::{Parameter, Relation, Wcs}, 5 | rust::term, 6 | }; 7 | 8 | use crate::{decls::Decls, prove::prove, Env}; 9 | 10 | fn decls() -> Decls { 11 | Decls { 12 | trait_decls: vec![term("trait Foo where {}")], 13 | impl_decls: vec![term("impl Foo(u32) where {}")], 14 | adt_decls: vec![term("adt X where {Foo(T)}")], 15 | ..Decls::empty() 16 | } 17 | } 18 | 19 | #[test] 20 | fn well_formed_adt() { 21 | let assumptions: Wcs = Wcs::t(); 22 | let goal: Parameter = term("X"); 23 | let constraints = prove( 24 | decls(), 25 | Env::default(), 26 | assumptions, 27 | Relation::WellFormed(goal), 28 | ); 29 | expect![[r#" 30 | { 31 | Constraints { 32 | env: Env { 33 | variables: [], 34 | bias: Soundness, 35 | }, 36 | known_true: true, 37 | substitution: {}, 38 | }, 39 | } 40 | "#]] 41 | .assert_debug_eq(&constraints); 42 | } 43 | 44 | #[test] 45 | fn not_well_formed_adt() { 46 | let assumptions: Wcs = Wcs::t(); 47 | let goal: Parameter = term("X"); 48 | prove( 49 | decls(), 50 | Env::default(), 51 | assumptions, 52 | Relation::WellFormed(goal), 53 | ).assert_err(expect![[r#" 54 | judgment `prove { goal: {@ wf(X)}, assumptions: {}, env: Env { variables: [], bias: Soundness }, decls: decls(222, [trait Foo ], [impl Foo(u32)], [], [], [], [adt X where {Foo(^ty0_0)}], {}, {}) }` failed at the following rule(s): 55 | failed at (src/file.rs:LL:CC) because 56 | judgment `prove_wc_list { goal: {@ wf(X)}, assumptions: {}, env: Env { variables: [], bias: Soundness } }` failed at the following rule(s): 57 | the rule "some" failed at step #0 (src/file.rs:LL:CC) because 58 | judgment `prove_wc { goal: @ wf(X), assumptions: {}, env: Env { variables: [], bias: Soundness } }` failed at the following rule(s): 59 | the rule "parameter well formed" failed at step #0 (src/file.rs:LL:CC) because 60 | judgment `prove_wf { goal: X, assumptions: {}, env: Env { variables: [], bias: Soundness } }` failed at the following rule(s): 61 | the rule "ADT" failed at step #3 (src/file.rs:LL:CC) because 62 | judgment `prove_after { constraints: Constraints { env: Env { variables: [], bias: Soundness }, known_true: true, substitution: {} }, goal: {Foo(u64)}, assumptions: {} }` failed at the following rule(s): 63 | the rule "prove_after" failed at step #1 (src/file.rs:LL:CC) because 64 | judgment `prove { goal: {Foo(u64)}, assumptions: {}, env: Env { variables: [], bias: Soundness }, decls: decls(222, [trait Foo ], [impl Foo(u32)], [], [], [], [adt X where {Foo(^ty0_0)}], {}, {}) }` failed at the following rule(s): 65 | failed at (src/file.rs:LL:CC) because 66 | judgment `prove_wc_list { goal: {Foo(u64)}, assumptions: {}, env: Env { variables: [], bias: Soundness } }` failed at the following rule(s): 67 | the rule "some" failed at step #0 (src/file.rs:LL:CC) because 68 | judgment `prove_wc { goal: Foo(u64), assumptions: {}, env: Env { variables: [], bias: Soundness } }` failed at the following rule(s): 69 | the rule "trait implied bound" failed at step #0 (src/file.rs:LL:CC) because 70 | expression evaluated to an empty collection: `decls.trait_invariants()`"#]]); 71 | } 72 | -------------------------------------------------------------------------------- /crates/formality-prove/src/test/exists_constraints.rs: -------------------------------------------------------------------------------- 1 | use expect_test::expect; 2 | use formality_macros::test; 3 | use formality_types::rust::term; 4 | 5 | use crate::decls::Decls; 6 | 7 | use crate::test_util::test_prove; 8 | 9 | /// Simple example decls consisting only of two trait declarations. 10 | fn decls() -> Decls { 11 | Decls { 12 | trait_decls: vec![term("trait Foo where {}")], 13 | impl_decls: vec![term("impl Foo(Vec) where {}")], 14 | ..Decls::empty() 15 | } 16 | } 17 | 18 | /// Test that `exists Foo(U)` yields `U = Vec` for some fresh `X` 19 | #[test] 20 | fn exists_u_for_t() { 21 | test_prove(decls(), term("exists {} => {Foo(U)}")).assert_ok(expect![[r#" 22 | { 23 | Constraints { env: Env { variables: [?ty_2, ?ty_1], bias: Soundness }, known_true: true, substitution: {?ty_1 => Vec} }, 24 | } 25 | "#]]); 26 | } 27 | -------------------------------------------------------------------------------- /crates/formality-prove/src/test/expanding.rs: -------------------------------------------------------------------------------- 1 | use expect_test::expect; 2 | use formality_macros::test; 3 | use formality_types::rust::term; 4 | 5 | use crate::{test_util::test_prove, Decls}; 6 | 7 | /// Simple example decls consisting only of two trait declarations. 8 | fn decls() -> Decls { 9 | Decls { 10 | max_size: 10, 11 | trait_decls: vec![term("trait Debug where {}")], 12 | impl_decls: vec![term("impl Debug(Vec) where {Debug(T)}")], 13 | ..Decls::empty() 14 | } 15 | } 16 | 17 | /// There is no U that is equal to all T. 18 | #[test] 19 | fn expanding() { 20 | test_prove(decls(), term("exists {} => {Debug(T)}")).assert_ok(expect![[r#" 21 | { 22 | Constraints { env: Env { variables: [?ty_0], bias: Soundness }, known_true: false, substitution: {} }, 23 | } 24 | "#]]); 25 | } 26 | -------------------------------------------------------------------------------- /crates/formality-prove/src/test/is_local.rs: -------------------------------------------------------------------------------- 1 | use expect_test::expect; 2 | use formality_macros::test; 3 | use formality_types::rust::term; 4 | 5 | use crate::decls::Decls; 6 | 7 | use crate::test_util::test_prove; 8 | 9 | #[test] 10 | fn test_forall_not_local() { 11 | test_prove( 12 | Decls::empty(), 13 | term("{} => {for @IsLocal(Debug(T))}"), 14 | ).assert_err( 15 | expect![[r#" 16 | judgment `prove { goal: {for @ IsLocal(Debug(^ty0_0))}, assumptions: {}, env: Env { variables: [], bias: Soundness }, decls: decls(222, [], [], [], [], [], [], {}, {}) }` failed at the following rule(s): 17 | failed at (src/file.rs:LL:CC) because 18 | judgment `prove_wc_list { goal: {for @ IsLocal(Debug(^ty0_0))}, assumptions: {}, env: Env { variables: [], bias: Soundness } }` failed at the following rule(s): 19 | the rule "some" failed at step #0 (src/file.rs:LL:CC) because 20 | judgment `prove_wc { goal: for @ IsLocal(Debug(^ty0_0)), assumptions: {}, env: Env { variables: [], bias: Soundness } }` failed at the following rule(s): 21 | the rule "forall" failed at step #2 (src/file.rs:LL:CC) because 22 | judgment `prove_wc { goal: @ IsLocal(Debug(!ty_1)), assumptions: {}, env: Env { variables: [!ty_1], bias: Soundness } }` failed at the following rule(s): 23 | the rule "trait ref is local" failed at step #0 (src/file.rs:LL:CC) because 24 | judgment `is_local_trait_ref { goal: Debug(!ty_1), assumptions: {}, env: Env { variables: [!ty_1], bias: Soundness } }` failed at the following rule(s): 25 | the rule "local trait" failed at step #0 (src/file.rs:LL:CC) because 26 | condition evaluted to false: `decls.is_local_trait_id(&goal.trait_id)` 27 | decls = decls(222, [], [], [], [], [], [], {}, {}) 28 | &goal.trait_id = Debug"#]]); 29 | } 30 | 31 | #[test] 32 | fn test_exists_not_local() { 33 | test_prove( 34 | Decls::empty(), 35 | term("exists {} => {@IsLocal(Debug(T))}"), 36 | ) 37 | .assert_ok(expect![[r#" 38 | { 39 | Constraints { env: Env { variables: [?ty_1], bias: Soundness }, known_true: false, substitution: {} }, 40 | } 41 | "#]]) 42 | } 43 | -------------------------------------------------------------------------------- /crates/formality-prove/src/test/simple_impl.rs: -------------------------------------------------------------------------------- 1 | use expect_test::expect; 2 | use formality_macros::test; 3 | use formality_types::grammar::Wc; 4 | use formality_types::rust::term; 5 | 6 | use crate::{decls::Decls, prove::prove}; 7 | 8 | /// Simple example decls consisting only of two trait declarations. 9 | fn decls() -> Decls { 10 | Decls { 11 | trait_decls: vec![term("trait Debug where {}")], 12 | impl_decls: vec![ 13 | term("impl Debug(Vec) where {Debug(T)}"), 14 | term("impl Debug(u32) where {}"), 15 | ], 16 | ..Decls::empty() 17 | } 18 | } 19 | 20 | #[test] 21 | fn vec_u32_debug() { 22 | let goal: Wc = term("Debug(Vec)"); 23 | prove(decls(), (), (), goal).assert_ok(expect![[r#" 24 | { 25 | Constraints { env: Env { variables: [], bias: Soundness }, known_true: true, substitution: {} }, 26 | } 27 | "#]]); 28 | } 29 | 30 | #[test] 31 | fn vec_vec_u32_debug() { 32 | let goal: Wc = term("Debug(Vec>)"); 33 | prove(decls(), (), (), goal).assert_ok(expect![[r#" 34 | { 35 | Constraints { env: Env { variables: [], bias: Soundness }, known_true: true, substitution: {} }, 36 | } 37 | "#]]); 38 | } 39 | -------------------------------------------------------------------------------- /crates/formality-prove/src/test/universes.rs: -------------------------------------------------------------------------------- 1 | use expect_test::expect; 2 | use formality_macros::test; 3 | use formality_types::rust::term; 4 | 5 | use crate::decls::Decls; 6 | 7 | use crate::test_util::test_prove; 8 | 9 | /// There is no U that is equal to all T. 10 | #[test] 11 | fn exists_u_for_t() { 12 | let decls = Decls::empty(); 13 | test_prove(decls, term("exists {} => {for T = U}")).assert_err( 14 | expect![[r#" 15 | judgment `prove { goal: {for ^ty0_0 = ?ty_0}, assumptions: {}, env: Env { variables: [?ty_0], bias: Soundness }, decls: decls(222, [], [], [], [], [], [], {}, {}) }` failed at the following rule(s): 16 | failed at (src/file.rs:LL:CC) because 17 | judgment `prove_wc_list { goal: {for ^ty0_0 = ?ty_0}, assumptions: {}, env: Env { variables: [?ty_0], bias: Soundness } }` failed at the following rule(s): 18 | the rule "some" failed at step #0 (src/file.rs:LL:CC) because 19 | judgment `prove_wc { goal: for ^ty0_0 = ?ty_0, assumptions: {}, env: Env { variables: [?ty_0], bias: Soundness } }` failed at the following rule(s): 20 | the rule "forall" failed at step #2 (src/file.rs:LL:CC) because 21 | judgment `prove_wc { goal: !ty_1 = ?ty_0, assumptions: {}, env: Env { variables: [?ty_0, !ty_1], bias: Soundness } }` failed at the following rule(s): 22 | the rule "eq" failed at step #0 (src/file.rs:LL:CC) because 23 | judgment `prove_eq { a: !ty_1, b: ?ty_0, assumptions: {}, env: Env { variables: [?ty_0, !ty_1], bias: Soundness } }` failed at the following rule(s): 24 | the rule "symmetric" failed at step #0 (src/file.rs:LL:CC) because 25 | judgment `prove_eq { a: ?ty_0, b: !ty_1, assumptions: {}, env: Env { variables: [?ty_0, !ty_1], bias: Soundness } }` failed at the following rule(s): 26 | the rule "existential" failed at step #0 (src/file.rs:LL:CC) because 27 | judgment `prove_existential_var_eq { v: ?ty_0, b: !ty_1, assumptions: {}, env: Env { variables: [?ty_0, !ty_1], bias: Soundness } }` failed at the following rule(s): 28 | the rule "existential-nonvar" failed at step #0 (src/file.rs:LL:CC) because 29 | pattern `None` did not match value `Some(!ty_1)` 30 | the rule "existential-universal" failed at step #0 (src/file.rs:LL:CC) because 31 | condition evaluted to false: `env.universe(p) < env.universe(v)`"#]]); 32 | } 33 | 34 | /// There is U that is equal to some T. 35 | #[test] 36 | fn for_t_exists_u() { 37 | let decls = Decls { 38 | trait_decls: vec![term("trait Test where {}")], 39 | impl_decls: vec![term("impl Test(X, Y) where {X = Y}")], 40 | ..Decls::empty() 41 | }; 42 | 43 | test_prove(decls, term("{} => {for Test(T, T)}")).assert_ok(expect![[r#" 44 | { 45 | Constraints { env: Env { variables: [], bias: Soundness }, known_true: true, substitution: {} }, 46 | } 47 | "#]]); 48 | } 49 | -------------------------------------------------------------------------------- /crates/formality-prove/src/test_util.rs: -------------------------------------------------------------------------------- 1 | use crate::prove::Bias; 2 | use formality_core::ProvenSet; 3 | use formality_macros::term; 4 | use formality_types::grammar::{Binder, Wcs}; 5 | use std::sync::Arc; 6 | 7 | use crate::{ 8 | decls::Decls, 9 | prove::{prove, Constraints, Env}, 10 | }; 11 | 12 | /// Useful assertions for use in tests. 13 | #[term] 14 | pub enum TestAssertionPart { 15 | #[grammar(forall $v0)] 16 | ForAll(Binder>), 17 | #[grammar(exists $v0)] 18 | Exists(Binder>), 19 | #[grammar($v0 => $v1)] 20 | Prove(Wcs, Wcs), 21 | } 22 | 23 | #[term] 24 | pub enum TestAssertion { 25 | #[cast] 26 | Default(Arc), 27 | #[grammar(coherence_mode $v0)] 28 | CoherenceMode(Arc), 29 | } 30 | 31 | /// `t` represents some set of existential bindings combined with (assumptions, goals). 32 | /// Returns the constraints that result from proving assumptions/goals. These will reference 33 | /// existential variables created for the bindings, so they're really just suitable for 34 | /// using with expect. 35 | pub fn test_prove(decls: Decls, assertion: Arc) -> ProvenSet { 36 | let (mut assertion, bias) = match &*assertion { 37 | TestAssertion::Default(assertion) => (assertion.clone(), Bias::Soundness), 38 | TestAssertion::CoherenceMode(assertion) => (assertion.clone(), Bias::Completeness), 39 | }; 40 | 41 | let mut env = Env::new_with_bias(bias); 42 | 43 | loop { 44 | match &*assertion { 45 | TestAssertionPart::ForAll(binder) => { 46 | let (env1, subst) = env.universal_substitution(binder); 47 | let assertion1 = binder.instantiate_with(&subst).unwrap(); 48 | env = env1; 49 | assertion = assertion1; 50 | } 51 | 52 | TestAssertionPart::Exists(binder) => { 53 | let (env1, subst) = env.existential_substitution(binder); 54 | let assertion1 = binder.instantiate_with(&subst).unwrap(); 55 | env = env1; 56 | assertion = assertion1; 57 | } 58 | 59 | TestAssertionPart::Prove(assumptions, goals) => { 60 | return prove(decls, env, assumptions, goals); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /crates/formality-rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "formality-rust" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | formality-types = { path = "../formality-types" } 10 | formality-macros = { path = "../formality-macros" } 11 | formality-core = { path = "../formality-core" } 12 | formality-prove = { path = "../formality-prove" } 13 | anyhow = "1.0.66" 14 | tracing = "0.1" 15 | 16 | [dev-dependencies] 17 | expect-test = "1.4.0" 18 | -------------------------------------------------------------------------------- /crates/formality-rust/src/grammar/mir.rs: -------------------------------------------------------------------------------- 1 | use super::VariantId; 2 | use formality_core::{Downcast, DowncastFrom, Upcast}; 3 | use formality_macros::term; 4 | use formality_types::grammar::{AdtId, Binder, FieldId, FnId, Lt, Parameter, RefKind, Ty}; 5 | 6 | #[term(mir($binder))] 7 | pub struct MirFnBody { 8 | /// The binder binds existential lifetimes that appear in types 9 | /// but whose values are not computed by earlier phases. 10 | pub binder: Binder, 11 | } 12 | 13 | #[term] 14 | pub struct LocalsAndBlocks { 15 | pub local_decls: Vec, 16 | pub basic_block_decls: Vec, 17 | } 18 | 19 | #[term(($mutability $name: $ty))] 20 | pub struct LocalDecl { 21 | pub name: LocalId, 22 | pub ty: Ty, 23 | pub mutability: RefKind, 24 | } 25 | 26 | formality_core::id!(LocalId); 27 | formality_core::id!(BasicBlockId); 28 | 29 | #[term] 30 | pub struct BasicBlockDecl { 31 | pub id: BasicBlockId, 32 | pub statements: Vec, 33 | pub terminator: Terminator, 34 | } 35 | 36 | #[term] 37 | pub enum Statement { 38 | #[grammar(($v0 = $v1))] 39 | Assign(Place, Rvalue), 40 | // SetDiscriminant(Place, VariantId), 41 | Noop, 42 | FakeRead(Place), 43 | } 44 | 45 | #[term] 46 | pub enum Rvalue { 47 | Use(Operand), 48 | Repeat(Operand, Constant), 49 | Ref(Lt, RefKind, Place), 50 | AddrOf(RefKind, Place), 51 | Len(Place), 52 | Apply(Operand, BinaryOp, Operand), 53 | Checked(Operand, BinaryOp, Operand), 54 | Aggregate(AggregateKind, Vec), 55 | Cast(Operand, Ty), 56 | } 57 | 58 | #[term] 59 | pub enum BinaryOp { 60 | #[cast] 61 | Math(BinaryMathOp), 62 | #[cast] 63 | Comparison(BinaryComparisonOp), 64 | } 65 | 66 | #[term] 67 | pub enum BinaryMathOp { 68 | #[grammar(+)] 69 | Add, 70 | #[grammar(-)] 71 | Subtract, 72 | #[grammar(*)] 73 | Multiply, 74 | #[grammar(/)] 75 | Divide, 76 | } 77 | 78 | #[term] 79 | pub enum BinaryComparisonOp { 80 | #[grammar(<)] 81 | LessThan, 82 | #[grammar(<=)] 83 | LessEqual, 84 | #[grammar(>)] 85 | GreaterThan, 86 | #[grammar(>=)] 87 | GreaterEqual, 88 | } 89 | 90 | #[term] 91 | pub enum AggregateKind { 92 | Tuple, 93 | Adt(AdtId, VariantId, Vec), 94 | } 95 | 96 | #[term] 97 | pub enum Terminator { 98 | Goto(BasicBlockId), 99 | Resume, 100 | Abort, 101 | Return, 102 | Unreachable, 103 | Drop(Place, Vec), 104 | DropAndReplace(Place, Vec), 105 | Call(Operand, Vec, Place, Vec), 106 | } 107 | 108 | #[term] 109 | pub enum Operand { 110 | Move(Place), 111 | Copy(Place), 112 | Const(Constant), 113 | } 114 | 115 | #[term] 116 | pub enum Constant { 117 | Number(usize), 118 | True, 119 | False, 120 | FnPtr(FnId, Vec), 121 | Tuple(Vec), 122 | } 123 | 124 | #[term(($local_id $*projections))] 125 | pub struct Place { 126 | pub local_id: LocalId, 127 | pub projections: Vec, 128 | } 129 | 130 | #[term] 131 | pub enum Projection { 132 | #[grammar(*)] 133 | Deref, 134 | 135 | #[grammar($v0)] 136 | Field(FieldId), 137 | 138 | #[grammar([$v0])] 139 | Index(LocalId), 140 | 141 | #[grammar((as $v0))] 142 | Downcast(VariantId), 143 | } 144 | 145 | #[term] 146 | pub enum PlaceTy { 147 | #[cast] 148 | Ty(Ty), 149 | 150 | VariantTy(Ty, VariantId), 151 | } 152 | 153 | impl DowncastFrom for PlaceTy { 154 | fn downcast_from(t: &Parameter) -> Option { 155 | let t: Ty = t.downcast()?; 156 | Some(t.upcast()) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /crates/formality-rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | // ANCHOR: use_rust_language 2 | // Defines the language used by derive(term) and friends. 3 | use formality_types::rust::FormalityLang; 4 | // ANCHOR_END: use_rust_language 5 | 6 | pub mod grammar; 7 | pub mod prove; 8 | mod test; 9 | mod trait_binder; 10 | -------------------------------------------------------------------------------- /crates/formality-rust/src/test.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use formality_macros::test; 4 | use formality_types::rust::term; 5 | 6 | use crate::grammar::Program; 7 | 8 | #[test] 9 | fn test_parse_rust_like_trait_impl_syntax() { 10 | let r: Program = term( 11 | "[ 12 | crate core { 13 | impl PartialEq for B { 14 | 15 | } 16 | } 17 | ]", 18 | ); 19 | 20 | // Note: the for etc are correctly accounted. 21 | expect_test::expect![[r#" 22 | Program { 23 | crates: [ 24 | Crate { 25 | id: core, 26 | items: [ 27 | TraitImpl( 28 | TraitImpl { 29 | safety: Safe, 30 | binder: Binder { 31 | kinds: [ 32 | Ty, 33 | Ty, 34 | ], 35 | term: TraitImplBoundData { 36 | trait_id: PartialEq, 37 | self_ty: Ty { 38 | data: Variable( 39 | ^ty0_1, 40 | ), 41 | }, 42 | trait_parameters: [ 43 | Ty( 44 | Ty { 45 | data: Variable( 46 | ^ty0_0, 47 | ), 48 | }, 49 | ), 50 | ], 51 | where_clauses: [], 52 | impl_items: [], 53 | }, 54 | }, 55 | }, 56 | ), 57 | ], 58 | }, 59 | ], 60 | } 61 | "#]] 62 | .assert_debug_eq(&r); 63 | } 64 | 65 | #[test] 66 | fn test_parse_rust_like_trait_syntax() { 67 | let r: Program = term( 68 | "[ 69 | crate core { 70 | trait Foo where A : Bar { 71 | 72 | } 73 | } 74 | ]", 75 | ); 76 | 77 | // Note: two type parameters, and the 0th one is self: 78 | expect_test::expect![[r#" 79 | Program { 80 | crates: [ 81 | Crate { 82 | id: core, 83 | items: [ 84 | Trait( 85 | Trait { 86 | safety: Safe, 87 | id: Foo, 88 | binder: where ^ty0_1 : Bar <^ty0_0> { }, 89 | }, 90 | ), 91 | ], 92 | }, 93 | ], 94 | } 95 | "#]] 96 | .assert_debug_eq(&r); 97 | } 98 | 99 | #[test] 100 | fn test_parse_rust_like_struct_syntax() { 101 | let r: Program = term( 102 | "[ 103 | crate core { 104 | struct Foo { 105 | a : A, 106 | } 107 | } 108 | ]", 109 | ); 110 | 111 | expect_test::expect![[r#" 112 | Program { 113 | crates: [ 114 | Crate { 115 | id: core, 116 | items: [ 117 | Struct( 118 | Struct { 119 | id: Foo, 120 | binder: Binder { 121 | kinds: [ 122 | Ty, 123 | ], 124 | term: StructBoundData { 125 | where_clauses: [], 126 | fields: [ 127 | Field { 128 | name: Id( 129 | a, 130 | ), 131 | ty: Ty { 132 | data: Variable( 133 | ^ty0_0, 134 | ), 135 | }, 136 | }, 137 | ], 138 | }, 139 | }, 140 | }, 141 | ), 142 | ], 143 | }, 144 | ], 145 | } 146 | "#]] 147 | .assert_debug_eq(&r); 148 | } 149 | -------------------------------------------------------------------------------- /crates/formality-rust/src/trait_binder.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use formality_core::{ 4 | fold::CoreFold, 5 | parse::{Binding, CoreParse, ParseResult, Parser, Scope}, 6 | term::CoreTerm, 7 | visit::CoreVisit, 8 | DowncastTo, UpcastFrom, 9 | }; 10 | use formality_types::{ 11 | grammar::{Binder, BoundVar, ParameterKind}, 12 | rust::Term, 13 | }; 14 | 15 | use crate::{grammar::TraitBinder, FormalityLang}; 16 | 17 | impl TraitBinder 18 | where 19 | T: Term, 20 | { 21 | pub fn open(&self) -> (Vec, T) { 22 | self.explicit_binder.open() 23 | } 24 | } 25 | 26 | impl CoreTerm for TraitBinder where T: Term {} 27 | 28 | impl DowncastTo> for TraitBinder 29 | where 30 | T: Term, 31 | { 32 | fn downcast_to(&self) -> Option> { 33 | Some(self.clone()) 34 | } 35 | } 36 | 37 | impl UpcastFrom> for TraitBinder 38 | where 39 | T: Term, 40 | { 41 | fn upcast_from(term: TraitBinder) -> Self { 42 | term 43 | } 44 | } 45 | 46 | impl CoreVisit for TraitBinder 47 | where 48 | T: Term, 49 | { 50 | fn free_variables(&self) -> Vec { 51 | self.explicit_binder.free_variables() 52 | } 53 | 54 | fn size(&self) -> usize { 55 | self.explicit_binder.size() 56 | } 57 | 58 | fn assert_valid(&self) { 59 | self.explicit_binder.assert_valid() 60 | } 61 | } 62 | 63 | impl CoreFold for TraitBinder 64 | where 65 | T: Term, 66 | { 67 | fn substitute( 68 | &self, 69 | substitution_fn: formality_core::fold::SubstitutionFn<'_, FormalityLang>, 70 | ) -> Self { 71 | TraitBinder { 72 | explicit_binder: self.explicit_binder.substitute(substitution_fn), 73 | } 74 | } 75 | } 76 | 77 | impl Debug for TraitBinder 78 | where 79 | T: Term, 80 | { 81 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 82 | // In Debug output, we include the `Self` to avoid confusion -- is this good? 83 | write!(f, "{:?}", self.explicit_binder) 84 | } 85 | } 86 | 87 | impl CoreParse for TraitBinder 88 | where 89 | T: Term, 90 | { 91 | #[tracing::instrument(level = "trace", ret)] 92 | fn parse<'t>(scope: &Scope, text: &'t str) -> ParseResult<'t, Self> { 93 | Parser::single_variant(scope, text, "TraitBinder", |p| { 94 | let mut bindings = match p.expect_char('<') { 95 | Ok(()) => { 96 | let bindings: Vec> = p.comma_nonterminal()?; 97 | p.expect_char('>')?; 98 | bindings 99 | } 100 | Err(_) => { 101 | // If we don't see a `<`, assume there are no add'l bound variables. 102 | vec![] 103 | } 104 | }; 105 | 106 | // insert the `Self` binding at position 0 107 | let bound_var = BoundVar::fresh(ParameterKind::Ty); 108 | bindings.insert( 109 | 0, 110 | Binding { 111 | name: "Self".to_string(), 112 | bound_var, 113 | }, 114 | ); 115 | 116 | // parse the contents with those names in scope 117 | let scope1 = scope.with_bindings(bindings.iter().map(|b| (&b.name, &b.bound_var))); 118 | let data: T = p.with_scope(scope1, |p| p.nonterminal())?; 119 | 120 | let bound_vars: Vec = bindings.iter().map(|b| b.bound_var).collect(); 121 | let explicit_binder = Binder::new(bound_vars, data); 122 | 123 | Ok(TraitBinder { explicit_binder }) 124 | }) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /crates/formality-smir/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "formality-smir" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | formality-types = { path = "../formality-types" } 10 | -------------------------------------------------------------------------------- /crates/formality-smir/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | 3 | use formality_types::grammar::ParameterKind; 4 | 5 | /// This import is needed, because `stable_mir` on its own doesn't have the `scoped_tls` rlib. 6 | extern crate rustc_driver; 7 | /// Access to the pre-0.1 stable_mir crate 8 | extern crate stable_mir; 9 | 10 | /// Trait used to convert from Stable MIR to Formality types. 11 | pub trait ToFormality { 12 | /// The formality representation of the stable MIR type implementing ToFormality. 13 | type T; 14 | /// Converts an object to the equivalent Formality representation. 15 | fn formality(&self) -> Self::T; 16 | } 17 | 18 | impl ToFormality for stable_mir::ty::GenericParamDefKind { 19 | type T = ParameterKind; 20 | 21 | fn formality(&self) -> Self::T { 22 | match self { 23 | stable_mir::ty::GenericParamDefKind::Lifetime => ParameterKind::Lt, 24 | stable_mir::ty::GenericParamDefKind::Type { .. } => ParameterKind::Ty, 25 | stable_mir::ty::GenericParamDefKind::Const { .. } => ParameterKind::Const, 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/formality-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "formality-types" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.65" 10 | lazy_static = "1.4.0" 11 | formality-core = { path = "../formality-core" } 12 | tracing = "0.1" 13 | contracts = "0.6.3" 14 | stacker = "0.1.15" 15 | extension-trait = "1.0.1" 16 | 17 | [dev-dependencies] 18 | expect-test = "1.4.0" 19 | -------------------------------------------------------------------------------- /crates/formality-types/src/grammar.rs: -------------------------------------------------------------------------------- 1 | mod consts; 2 | mod formulas; 3 | mod ids; 4 | mod kinded; 5 | mod ty; 6 | mod wc; 7 | 8 | pub use crate::rust::grammar::*; 9 | pub use consts::*; 10 | pub use formulas::*; 11 | pub use ids::*; 12 | pub use kinded::*; 13 | pub use ty::*; 14 | pub use wc::*; 15 | -------------------------------------------------------------------------------- /crates/formality-types/src/grammar/consts.rs: -------------------------------------------------------------------------------- 1 | mod valtree; 2 | 3 | use super::{Parameter, Ty, Variable}; 4 | use formality_core::{term, DowncastTo, Upcast, UpcastFrom}; 5 | use std::sync::Arc; 6 | pub use valtree::*; 7 | 8 | #[term] 9 | #[cast] 10 | #[customize(constructors)] // FIXME: figure out upcasts with arc or special-case 11 | pub struct Const { 12 | data: Arc, 13 | } 14 | impl Const { 15 | pub fn data(&self) -> &ConstData { 16 | &self.data 17 | } 18 | 19 | pub fn new(data: impl Upcast) -> Self { 20 | Self { 21 | data: Arc::new(data.upcast()), 22 | } 23 | } 24 | 25 | pub fn valtree(vt: impl Upcast, ty: impl Upcast) -> Self { 26 | Self::new(ConstData::Value(vt.upcast(), ty.upcast())) 27 | } 28 | 29 | pub fn as_variable(&self) -> Option { 30 | match self.data() { 31 | ConstData::Value(_, _) => None, 32 | ConstData::Variable(var) => Some(*var), 33 | } 34 | } 35 | 36 | pub fn as_value(&self) -> Option<(ValTree, Ty)> { 37 | match self.data() { 38 | ConstData::Value(v, t) => Some((v.clone(), t.clone())), 39 | ConstData::Variable(_) => None, 40 | } 41 | } 42 | } 43 | 44 | #[term] 45 | #[customize(parse)] 46 | pub enum ConstData { 47 | Value(ValTree, Ty), 48 | 49 | #[variable] 50 | Variable(Variable), 51 | } 52 | 53 | impl DowncastTo for Const { 54 | fn downcast_to(&self) -> Option { 55 | Some(self.data().clone()) 56 | } 57 | } 58 | 59 | impl DowncastTo for Parameter { 60 | fn downcast_to(&self) -> Option { 61 | match self { 62 | Parameter::Ty(_) | Parameter::Lt(_) => None, 63 | Parameter::Const(c) => Some(c.clone()), 64 | } 65 | } 66 | } 67 | 68 | impl DowncastTo for Parameter { 69 | fn downcast_to(&self) -> Option { 70 | let c: Const = self.downcast_to()?; 71 | c.downcast_to() 72 | } 73 | } 74 | 75 | #[term] 76 | pub enum Bool { 77 | #[grammar(true)] 78 | True, 79 | #[grammar(false)] 80 | False, 81 | } 82 | 83 | impl UpcastFrom for ConstData { 84 | fn upcast_from(term: Bool) -> Self { 85 | ConstData::Value(term.upcast(), Ty::bool()) 86 | } 87 | } 88 | 89 | impl UpcastFrom for Const { 90 | fn upcast_from(term: Bool) -> Self { 91 | let c: ConstData = term.upcast(); 92 | Const::new(c) 93 | } 94 | } 95 | 96 | impl UpcastFrom for Parameter { 97 | fn upcast_from(term: Const) -> Self { 98 | Self::Const(term) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /crates/formality-types/src/grammar/consts/valtree.rs: -------------------------------------------------------------------------------- 1 | use formality_core::Visit; 2 | 3 | use formality_core::{Upcast, UpcastFrom}; 4 | 5 | use super::Bool; 6 | 7 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Visit)] 8 | pub enum ValTree { 9 | Leaf(Scalar), 10 | Branches(Vec), 11 | } 12 | 13 | impl std::fmt::Debug for ValTree { 14 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 15 | match self { 16 | Self::Leaf(s) => s.fmt(f), 17 | Self::Branches(branches) => branches.fmt(f), 18 | } 19 | } 20 | } 21 | 22 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Visit)] 23 | pub struct Scalar { 24 | bits: u128, 25 | } 26 | 27 | impl std::fmt::Debug for Scalar { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | self.bits.fmt(f) 30 | } 31 | } 32 | 33 | impl Scalar { 34 | pub fn new(bits: u128) -> Self { 35 | Self { bits } 36 | } 37 | } 38 | 39 | impl UpcastFrom for ValTree { 40 | fn upcast_from(term: Bool) -> Self { 41 | Scalar::upcast_from(term).upcast() 42 | } 43 | } 44 | 45 | impl UpcastFrom for ValTree { 46 | fn upcast_from(s: Scalar) -> Self { 47 | Self::Leaf(s) 48 | } 49 | } 50 | 51 | impl UpcastFrom for ValTree { 52 | fn upcast_from(s: Self) -> Self { 53 | s 54 | } 55 | } 56 | 57 | impl UpcastFrom for Scalar { 58 | fn upcast_from(term: Bool) -> Self { 59 | Scalar { bits: term as u128 } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /crates/formality-types/src/grammar/ids.rs: -------------------------------------------------------------------------------- 1 | use formality_core::id; 2 | 3 | id!(FnId); 4 | id!(AdtId); 5 | id!(TraitId); 6 | id!(AssociatedItemId); 7 | id!(CrateId); 8 | id!(FieldId); 9 | -------------------------------------------------------------------------------- /crates/formality-types/src/grammar/kinded.rs: -------------------------------------------------------------------------------- 1 | use formality_core::Upcast; 2 | 3 | use super::{BoundVar, Lt, LtData, ParameterKind, Ty, TyData}; 4 | 5 | /// Trait implemented by the various kinds of generic parameters. 6 | /// Used in some of the fluent APIs for creating binders to select 7 | /// the kind of generic variable that will be instantiated. 8 | pub trait Kinded { 9 | fn instantiate() -> (Vec, Self); 10 | } 11 | 12 | impl Kinded for Ty { 13 | fn instantiate() -> (Vec, Self) { 14 | let bvar = BoundVar::fresh(ParameterKind::Ty); 15 | (vec![bvar], TyData::Variable(bvar.upcast()).upcast()) 16 | } 17 | } 18 | 19 | impl Kinded for Lt { 20 | fn instantiate() -> (Vec, Self) { 21 | let bvar = BoundVar::fresh(ParameterKind::Lt); 22 | (vec![bvar], LtData::Variable(bvar.upcast()).upcast()) 23 | } 24 | } 25 | 26 | impl Kinded for (A, B) { 27 | fn instantiate() -> (Vec, Self) { 28 | let (a_names, a) = A::instantiate(); 29 | let (b_names, b) = B::instantiate(); 30 | ((a_names, b_names).upcast(), (a, b)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/formality-types/src/grammar/ty/debug_impls.rs: -------------------------------------------------------------------------------- 1 | use super::{AliasName, AliasTy, AssociatedTyName, Parameter, RefKind, RigidName, RigidTy}; 2 | use std::fmt::Debug; 3 | 4 | // ANCHOR: RigidTy_impl 5 | impl Debug for RigidTy { 6 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 7 | let RigidTy { name, parameters } = self; 8 | match name { 9 | RigidName::AdtId(name) => { 10 | write!( 11 | f, 12 | "{:?}{:?}", 13 | name, 14 | PrettyParameters::new("<", ">", parameters) 15 | ) 16 | } 17 | RigidName::ScalarId(s) if parameters.is_empty() => { 18 | write!(f, "{:?}", s) 19 | } 20 | RigidName::Ref(RefKind::Shared) if parameters.len() == 2 => { 21 | write!(f, "&{:?} {:?}", parameters[0], parameters[1]) 22 | } 23 | RigidName::Ref(RefKind::Mut) if parameters.len() == 2 => { 24 | write!(f, "&mut {:?} {:?}", parameters[0], parameters[1]) 25 | } 26 | RigidName::Tuple(arity) if parameters.len() == *arity => { 27 | if *arity != 0 { 28 | write!(f, "{:?}", PrettyParameters::new("(", ")", parameters)) 29 | } else { 30 | // PrettyParameters would skip the separators 31 | // for 0 arity 32 | write!(f, "()") 33 | } 34 | } 35 | _ => { 36 | write!(f, "{:?}{:?}", name, PrettyParameters::angle(parameters)) 37 | } 38 | } 39 | } 40 | } 41 | // ANCHOR_END: RigidTy_impl 42 | 43 | impl Debug for AliasTy { 44 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 45 | let AliasTy { name, parameters } = self; 46 | match name { 47 | AliasName::AssociatedTyId(AssociatedTyName { 48 | trait_id, 49 | item_id, 50 | item_arity, 51 | }) => { 52 | let (trait_parameters, item_parameters) = 53 | parameters.split_at(parameters.len() - item_arity); 54 | let (self_parameter, other_parameters) = trait_parameters.split_at(1); 55 | // Grr, wish we would remember the number of parameters assigned to each position. 56 | write!( 57 | f, 58 | "<{:?} as {:?}{:?}>::{:?}{:?}", 59 | self_parameter[0], 60 | trait_id, 61 | PrettyParameters::angle(other_parameters), 62 | item_id, 63 | PrettyParameters::angle(item_parameters), 64 | ) 65 | } 66 | } 67 | } 68 | } 69 | 70 | struct PrettyParameters<'a> { 71 | open: &'a str, 72 | close: &'a str, 73 | p: &'a [Parameter], 74 | } 75 | impl<'a> PrettyParameters<'a> { 76 | fn new(open: &'a str, close: &'a str, p: &'a [Parameter]) -> Self { 77 | Self { open, close, p } 78 | } 79 | 80 | fn angle(p: &'a [Parameter]) -> Self { 81 | Self::new("<", ">", p) 82 | } 83 | } 84 | 85 | impl Debug for PrettyParameters<'_> { 86 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 87 | if self.p.is_empty() { 88 | Ok(()) 89 | } else { 90 | write!(f, "{}", self.open)?; 91 | write!(f, "{:?}", self.p[0])?; 92 | for p in &self.p[1..] { 93 | write!(f, ", {:?}", p)?; 94 | } 95 | write!(f, "{}", self.close)?; 96 | Ok(()) 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /crates/formality-types/src/grammar/ty/term_impls.rs: -------------------------------------------------------------------------------- 1 | use crate::grammar::{Parameter, ValTree}; 2 | use crate::FormalityLang; 3 | use formality_core::{ 4 | fold::{CoreFold, SubstitutionFn}, 5 | language::CoreKind, 6 | }; 7 | 8 | use super::ParameterKind; 9 | 10 | impl formality_core::language::HasKind for Parameter { 11 | fn kind(&self) -> CoreKind { 12 | match self { 13 | Parameter::Ty(_) => ParameterKind::Ty, 14 | Parameter::Lt(_) => ParameterKind::Lt, 15 | Parameter::Const(_) => ParameterKind::Const, 16 | } 17 | } 18 | } 19 | 20 | impl CoreFold for ValTree { 21 | fn substitute(&self, _substitution_fn: SubstitutionFn<'_, FormalityLang>) -> Self { 22 | self.clone() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crates/formality-types/src/grammar/wc.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use formality_core::{ 4 | cast_impl, set, term, Cons, DowncastFrom, DowncastTo, Set, Upcast, UpcastFrom, Upcasted, 5 | }; 6 | 7 | use super::{Binder, BoundVar, Parameter, Predicate, Relation, TraitRef}; 8 | 9 | #[term($set)] 10 | #[derive(Default)] 11 | pub struct Wcs { 12 | set: Set, 13 | } 14 | 15 | impl Wcs { 16 | pub fn t() -> Self { 17 | set![].upcast() 18 | } 19 | 20 | /// Goal(s) to prove `a` and `b` are equal (they must have equal length) 21 | pub fn all_eq(a: impl Upcast>, b: impl Upcast>) -> Wcs { 22 | let a: Vec = a.upcast(); 23 | let b: Vec = b.upcast(); 24 | assert_eq!(a.len(), b.len()); 25 | a.into_iter() 26 | .zip(b) 27 | .map(|(a, b)| Relation::equals(a, b)) 28 | .upcasted() 29 | .collect() 30 | } 31 | } 32 | 33 | impl<'w> IntoIterator for &'w Wcs { 34 | type Item = Wc; 35 | 36 | type IntoIter = Box + 'w>; 37 | 38 | fn into_iter(self) -> Self::IntoIter { 39 | Box::new(self.set.iter().cloned()) 40 | } 41 | } 42 | 43 | impl IntoIterator for Wcs { 44 | type Item = Wc; 45 | 46 | type IntoIter = Box>; 47 | 48 | fn into_iter(self) -> Self::IntoIter { 49 | Box::new(self.set.into_iter()) 50 | } 51 | } 52 | 53 | impl FromIterator for Wcs { 54 | fn from_iter>(iter: T) -> Self { 55 | Wcs { 56 | set: iter.into_iter().collect(), 57 | } 58 | } 59 | } 60 | 61 | macro_rules! tuple_upcast { 62 | ($($name:ident),*) => { 63 | #[allow(non_snake_case)] 64 | impl<$($name,)*> UpcastFrom<($($name,)*)> for Wcs 65 | where 66 | $($name: Upcast,)* 67 | { 68 | fn upcast_from(($($name,)*): ($($name,)*)) -> Self { 69 | let c = None.into_iter(); 70 | $( 71 | let $name: Wcs = $name.upcast(); 72 | let c = c.chain($name); 73 | )* 74 | c.collect() 75 | } 76 | } 77 | } 78 | } 79 | 80 | tuple_upcast!(A, B); 81 | tuple_upcast!(A, B, C); 82 | tuple_upcast!(A, B, C, D); 83 | 84 | impl DowncastTo> for Wcs { 85 | fn downcast_to(&self) -> Option> { 86 | let Cons(wc, set) = self.set.downcast_to()?; 87 | Some(Cons(wc, set.upcast())) 88 | } 89 | } 90 | 91 | impl UpcastFrom<()> for Wcs { 92 | fn upcast_from((): ()) -> Self { 93 | Wcs::default() 94 | } 95 | } 96 | 97 | impl DowncastTo<()> for Wcs { 98 | fn downcast_to(&self) -> Option<()> { 99 | if self.set.is_empty() { 100 | Some(()) 101 | } else { 102 | None 103 | } 104 | } 105 | } 106 | 107 | #[term($data)] 108 | pub struct Wc { 109 | data: Arc, 110 | } 111 | 112 | impl Wc { 113 | pub fn data(&self) -> &WcData { 114 | &self.data 115 | } 116 | 117 | pub fn for_all(names: &[BoundVar], data: impl Upcast) -> Self { 118 | WcData::ForAll(Binder::new(names, data.upcast())).upcast() 119 | } 120 | } 121 | 122 | #[term] 123 | pub enum WcData { 124 | /// Means the built-in relation holds. 125 | #[cast] 126 | Relation(Relation), 127 | 128 | /// Means the predicate holds. 129 | #[cast] 130 | Predicate(Predicate), 131 | 132 | // Equivalent to `for<'a>` except that it can also express `for` and so forth: 133 | // means `$v0` is true for any value of the bound variables (e.g., `'a` or `T`). 134 | #[grammar(for $v0)] 135 | ForAll(Binder), 136 | 137 | #[grammar(if $v0 $v1)] 138 | Implies(Wcs, Wc), 139 | } 140 | 141 | // --- 142 | 143 | impl UpcastFrom for Wc { 144 | fn upcast_from(v: WcData) -> Self { 145 | Wc { data: Arc::new(v) } 146 | } 147 | } 148 | 149 | impl UpcastFrom for WcData { 150 | fn upcast_from(v: Wc) -> Self { 151 | v.data().clone() 152 | } 153 | } 154 | 155 | impl DowncastFrom for WcData { 156 | fn downcast_from(t: &Wc) -> Option { 157 | Some(t.data().clone()) 158 | } 159 | } 160 | 161 | // --- 162 | 163 | cast_impl!((TraitRef) <: (Predicate) <: (WcData)); 164 | cast_impl!((Relation) <: (WcData) <: (Wc)); 165 | cast_impl!((Predicate) <: (WcData) <: (Wc)); 166 | cast_impl!((TraitRef) <: (WcData) <: (Wc)); 167 | 168 | impl UpcastFrom for Wcs { 169 | fn upcast_from(term: Wc) -> Self { 170 | Wcs { set: set![term] } 171 | } 172 | } 173 | 174 | impl DowncastTo for Wcs { 175 | fn downcast_to(&self) -> Option { 176 | if self.set.len() == 1 { 177 | self.set.iter().next().cloned() 178 | } else { 179 | None 180 | } 181 | } 182 | } 183 | 184 | cast_impl!((Relation) <: (Wc) <: (Wcs)); 185 | cast_impl!((Predicate) <: (Wc) <: (Wcs)); 186 | cast_impl!((TraitRef) <: (Wc) <: (Wcs)); 187 | -------------------------------------------------------------------------------- /crates/formality-types/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | pub mod grammar; 4 | 5 | // ANCHOR: declare_rust_language 6 | formality_core::declare_language! { 7 | pub mod rust { 8 | const NAME = "Rust"; 9 | type Kind = crate::grammar::ParameterKind; 10 | type Parameter = crate::grammar::Parameter; 11 | const BINDING_OPEN = '<'; 12 | const BINDING_CLOSE = '>'; 13 | const KEYWORDS = [ 14 | "mut", 15 | "struct", 16 | "enum", 17 | "union", 18 | "const", 19 | "true", 20 | "false", 21 | "static", 22 | ]; 23 | } 24 | } 25 | // ANCHOR_END: declare_rust_language 26 | 27 | // ANCHOR: use_rust_language 28 | use crate::rust::FormalityLang; 29 | // ANCHOR_END: use_rust_language 30 | -------------------------------------------------------------------------------- /crates/formality-types/src/visit.rs: -------------------------------------------------------------------------------- 1 | use formality_core::visit::CoreVisit; 2 | 3 | use crate::grammar::{Lt, Ty, Variable}; 4 | 5 | use crate::FormalityLang as Rust; 6 | -------------------------------------------------------------------------------- /examples/formality-eg/grammar.rs: -------------------------------------------------------------------------------- 1 | use formality_core::{ 2 | cast_impl, language::HasKind, term, Downcast, DowncastTo, Upcast, UpcastFrom, 3 | }; 4 | use std::sync::Arc; 5 | 6 | pub use crate::eg::grammar::*; 7 | 8 | #[cfg(test)] 9 | mod test; 10 | 11 | #[term] 12 | #[derive(Copy)] 13 | pub enum Kind { 14 | #[grammar(type)] 15 | Ty, 16 | } 17 | 18 | #[term] 19 | pub enum Parameter { 20 | #[cast] 21 | Ty(Ty), 22 | } 23 | 24 | #[term] 25 | pub enum Ty { 26 | Integer, 27 | 28 | #[cast] 29 | StructTy(StructTy), 30 | 31 | #[variable(Kind::Ty)] 32 | Var(Variable), 33 | } 34 | 35 | #[term($id $)] 36 | pub struct StructTy { 37 | pub id: StructId, 38 | pub parameters: Vec, 39 | } 40 | 41 | #[term(struct $id $bound)] 42 | pub struct StructDecl { 43 | id: StructId, 44 | bound: Binder, 45 | } 46 | 47 | #[term({ $,fields })] 48 | pub struct StructBoundData { 49 | fields: Vec, 50 | } 51 | 52 | #[term($name : $ty)] 53 | pub struct FieldDecl { 54 | name: FieldId, 55 | ty: Ty, 56 | } 57 | 58 | #[term(fn $id $bound)] 59 | pub struct FnDecl { 60 | id: FnId, 61 | bound: Binder, 62 | } 63 | 64 | #[term($(fn_parameters) -> $return_ty { $body })] 65 | pub struct FnBoundData { 66 | pub fn_parameters: Vec, 67 | pub return_ty: Ty, 68 | pub body: Expr, 69 | } 70 | 71 | #[term($id : $ty)] 72 | pub struct LocalVariableDecl { 73 | pub id: LocalVarId, 74 | pub ty: Ty, 75 | } 76 | 77 | #[term] 78 | pub enum Expr { 79 | #[cast] 80 | LocalVar(LocalVarId), 81 | 82 | #[cast] 83 | IntegerLiteral(usize), 84 | 85 | #[grammar($v0 { $,v1 })] 86 | StructLiteral(StructTy, Vec), 87 | 88 | #[grammar($v0 + $v1)] 89 | #[precedence(1)] 90 | Add(Arc, Arc), 91 | 92 | #[grammar($v0 - $v1)] 93 | #[precedence(1)] 94 | Sub(Arc, Arc), 95 | 96 | #[grammar($v0 * $v1)] 97 | #[precedence(2)] 98 | Mul(Arc, Arc), 99 | 100 | #[grammar($v0 / $v1)] 101 | #[precedence(2)] 102 | Div(Arc, Arc), 103 | 104 | #[grammar(($v0))] 105 | Paren(Arc), 106 | 107 | #[grammar(let $v0 = $v1 in $v2)] 108 | LetIn(LocalVarId, Arc, Arc), 109 | } 110 | 111 | #[term($id : $expr)] 112 | pub struct FieldExpr { 113 | pub id: FieldId, 114 | pub expr: Expr, 115 | } 116 | 117 | formality_core::id!(StructId); 118 | formality_core::id!(FieldId); 119 | formality_core::id!(FnId); 120 | formality_core::id!(LocalVarId); 121 | 122 | cast_impl!((BoundVar) <: (Variable) <: (Parameter)); 123 | cast_impl!((ExistentialVar) <: (Variable) <: (Parameter)); 124 | cast_impl!((UniversalVar) <: (Variable) <: (Parameter)); 125 | 126 | impl HasKind for Parameter { 127 | fn kind(&self) -> Kind { 128 | match self { 129 | Parameter::Ty(_) => Kind::Ty, 130 | } 131 | } 132 | } 133 | 134 | impl UpcastFrom for Parameter { 135 | fn upcast_from(term: Variable) -> Self { 136 | match term.kind() { 137 | Kind::Ty => Ty::Var(term).upcast(), 138 | } 139 | } 140 | } 141 | 142 | impl DowncastTo for Parameter { 143 | fn downcast_to(&self) -> Option { 144 | match self { 145 | Parameter::Ty(ty) => ty.downcast(), 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /examples/formality-eg/grammar/test.rs: -------------------------------------------------------------------------------- 1 | use formality_core::test; 2 | 3 | use crate::eg::term; 4 | 5 | use super::{Expr, StructDecl, Ty}; 6 | 7 | #[test] 8 | fn test_struct_decl() { 9 | let r: StructDecl = term("struct Point { x: integer, y: integer }"); 10 | expect_test::expect![[r#" 11 | StructDecl { 12 | id: Point, 13 | bound: Binder { 14 | kinds: [], 15 | term: StructBoundData { 16 | fields: [ 17 | FieldDecl { 18 | name: x, 19 | ty: Integer, 20 | }, 21 | FieldDecl { 22 | name: y, 23 | ty: Integer, 24 | }, 25 | ], 26 | }, 27 | }, 28 | } 29 | "#]] 30 | .assert_debug_eq(&r); 31 | } 32 | 33 | #[test] 34 | fn test_struct_ty_empty_args() { 35 | let r: Ty = term("Point"); 36 | expect_test::expect![[r#" 37 | StructTy( 38 | StructTy { 39 | id: Point, 40 | parameters: [], 41 | }, 42 | ) 43 | "#]] 44 | .assert_debug_eq(&r); 45 | } 46 | 47 | #[test] 48 | fn test_struct_ty_no_args() { 49 | let r: Ty = term("Point"); 50 | expect_test::expect![[r#" 51 | StructTy( 52 | StructTy { 53 | id: Point, 54 | parameters: [], 55 | }, 56 | ) 57 | "#]] 58 | .assert_debug_eq(&r); 59 | } 60 | 61 | #[test] 62 | fn test_vec_int_ty() { 63 | let r: Ty = term("Vec"); 64 | expect_test::expect![[r#" 65 | StructTy( 66 | StructTy { 67 | id: Vec, 68 | parameters: [ 69 | Ty( 70 | Integer, 71 | ), 72 | ], 73 | }, 74 | ) 75 | "#]] 76 | .assert_debug_eq(&r); 77 | } 78 | 79 | #[test] 80 | fn test_expression() { 81 | let r: Expr = term("3 + 5 * 6"); 82 | expect_test::expect![[r#" 83 | Add( 84 | IntegerLiteral( 85 | 3, 86 | ), 87 | Mul( 88 | IntegerLiteral( 89 | 5, 90 | ), 91 | IntegerLiteral( 92 | 6, 93 | ), 94 | ), 95 | ) 96 | "#]] 97 | .assert_debug_eq(&r); 98 | } 99 | -------------------------------------------------------------------------------- /examples/formality-eg/main.rs: -------------------------------------------------------------------------------- 1 | mod grammar; 2 | mod type_system; 3 | 4 | formality_core::declare_language! { 5 | mod eg { 6 | const NAME = "Eg"; 7 | type Kind = crate::grammar::Kind; 8 | type Parameter = crate::grammar::Parameter; 9 | const BINDING_OPEN = '<'; 10 | const BINDING_CLOSE = '>'; 11 | const KEYWORDS = [ 12 | "struct", 13 | "fn", 14 | "let", 15 | "in", 16 | "integer", 17 | ]; 18 | } 19 | } 20 | 21 | // Default language for our crate 22 | use eg::FormalityLang; 23 | use formality_core::Fallible; 24 | 25 | fn main() -> Fallible<()> { 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /examples/formality-eg/type_system.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fixme_tests/basic--impl_Debug_for_i32.rs: -------------------------------------------------------------------------------- 1 | #![cfg(FIXME)] 2 | #![allow(non_snake_case)] 3 | 4 | const PROGRAM: &str = "[ 5 | crate core { 6 | trait Debug { } 7 | 8 | impl Debug for i32 { } 9 | } 10 | ]"; 11 | 12 | #[test] 13 | #[ignore] 14 | fn test_i32() { 15 | expect_test::expect![[r#" 16 | Ok( 17 | yes, 18 | ) 19 | "#]] 20 | .assert_debug_eq(&formality_rust::test_can_prove_where_clause( 21 | PROGRAM, 22 | "i32: Debug", 23 | )); 24 | } 25 | 26 | #[test] 27 | #[ignore] 28 | fn test_u32() { 29 | expect_test::expect![[r#" 30 | Ok( 31 | no, 32 | ) 33 | "#]] 34 | .assert_debug_eq(&formality_rust::test_can_prove_where_clause( 35 | PROGRAM, 36 | "u32: Debug", 37 | )); 38 | } 39 | -------------------------------------------------------------------------------- /fixme_tests/basic--supertraits-hr.rs: -------------------------------------------------------------------------------- 1 | #![cfg(FIXME)] 2 | #![allow(non_snake_case)] 3 | 4 | const PROGRAM: &str = "[ 5 | crate core { 6 | trait Sub where for Self: Super { } 7 | trait Super { } 8 | } 9 | ]"; 10 | 11 | #[test] 12 | #[ignore] 13 | fn test_implies() { 14 | expect_test::expect![[r#" 15 | Ok( 16 | yes, 17 | ) 18 | "#]] 19 | .assert_debug_eq(&formality_rust::test_can_prove_goal( 20 | PROGRAM, 21 | "for_all( implies([Sub(T)], for_all( Super(T, x))))", 22 | )); 23 | } 24 | -------------------------------------------------------------------------------- /fixme_tests/basic--supertraits.rs: -------------------------------------------------------------------------------- 1 | #![cfg(FIXME)] 2 | #![allow(non_snake_case)] 3 | 4 | const PROGRAM: &str = "[ 5 | crate core { 6 | trait Eq where Self: PartialEq { } 7 | trait PartialEq { } 8 | 9 | // ComparableBase is a supertype, but `T: Eq` is not. 10 | trait Comparable where T: Eq, Self: ComparableBase { } 11 | trait ComparableBase { } 12 | } 13 | ]"; 14 | 15 | #[test] 16 | #[ignore] 17 | fn test_implies() { 18 | expect_test::expect![[r#" 19 | Ok( 20 | yes, 21 | ) 22 | "#]] 23 | .assert_debug_eq(&formality_rust::test_can_prove_goal( 24 | PROGRAM, 25 | "for_all( implies([Eq(T)], PartialEq(T)))", 26 | )); 27 | } 28 | 29 | #[test] 30 | #[ignore] 31 | fn test_implies_rev() { 32 | expect_test::expect![[r#" 33 | Ok( 34 | no, 35 | ) 36 | "#]] 37 | .assert_debug_eq(&formality_rust::test_can_prove_goal( 38 | PROGRAM, 39 | "for_all( implies([PartialEq(T)], Eq(T)))", 40 | )); 41 | } 42 | 43 | #[test] 44 | #[ignore] 45 | fn test_non_supertrait_not_implied() { 46 | expect_test::expect![[r#" 47 | Ok( 48 | no, 49 | ) 50 | "#]] 51 | .assert_debug_eq(&formality_rust::test_can_prove_goal( 52 | PROGRAM, 53 | "for_all( implies([Comparable(T, U)], Eq(U)))", 54 | )); 55 | } 56 | 57 | #[test] 58 | #[ignore] 59 | fn test_comparable_implies_comparable_base() { 60 | expect_test::expect![[r#" 61 | Ok( 62 | yes, 63 | ) 64 | "#]] 65 | .assert_debug_eq(&formality_rust::test_can_prove_goal( 66 | PROGRAM, 67 | "for_all( implies([Comparable(T, U)], ComparableBase(T)))", 68 | )); 69 | } 70 | -------------------------------------------------------------------------------- /fixme_tests/impl-vs-trait-fn.rs: -------------------------------------------------------------------------------- 1 | #![cfg(FIXME)] 2 | #![allow(non_snake_case)] 3 | 4 | #[test] 5 | #[ignore] 6 | fn absolutely_same() { 7 | const PROGRAM: &str = "[ 8 | crate core { 9 | trait Debug {} 10 | trait Display {} 11 | 12 | trait Get { 13 | fn get(&mut l T) -> () where [T: Debug]; 14 | } 15 | 16 | impl Get for () { 17 | fn get(&mut l T) -> () where T: Debug { 18 | trusted 19 | } 20 | } 21 | } 22 | ]"; 23 | 24 | expect_test::expect![[r#" 25 | Ok( 26 | (), 27 | ) 28 | "#]] 29 | .assert_debug_eq(&formality_rust::test_program_ok(PROGRAM)); 30 | } 31 | 32 | #[test] 33 | #[ignore] 34 | fn different_self_type_mut_vs_sh() { 35 | const PROGRAM: &str = "[ 36 | crate core { 37 | trait Debug {} 38 | trait Display {} 39 | 40 | trait Get { 41 | fn get(&mut l T) -> () where [T: Debug]; 42 | // -------- 43 | } 44 | 45 | impl Get for () { 46 | fn get(&l T) -> () where T: Debug { 47 | // ---- 48 | trusted 49 | } 50 | } 51 | } 52 | ]"; 53 | 54 | expect_test::expect![[r#" 55 | Err( 56 | Error { 57 | context: "check_trait_impl(impl <> Get((rigid tuple(0))) { fn get [(rigid &(shared) ^lt0_1 ^ty0_0)] -> (rigid tuple(0)) where [well_formed((rigid &(shared) ^lt0_1 ^ty0_0)), well_formed((rigid tuple(0))), is_implemented(Debug(^ty0_0))] })", 58 | source: Error { 59 | context: "check_fn_in_impl", 60 | source: "could not prove `sub((rigid &(mut) !ltU(2)_1 !tyU(2)_0), (rigid &(shared) !ltU(2)_1 !tyU(2)_0))` given `[\n well_formed((rigid &(shared) !ltU(2)_1 !tyU(2)_0)),\n well_formed((rigid tuple(0))),\n is_implemented(Debug(!tyU(2)_0)),\n]`", 61 | }, 62 | }, 63 | ) 64 | "#]] 65 | .assert_debug_eq(&formality_rust::test_program_ok(PROGRAM)); 66 | } 67 | 68 | #[test] 69 | #[ignore] 70 | fn different_arg_type_u32_vs_i32() { 71 | const PROGRAM: &str = "[ 72 | crate core { 73 | trait Debug {} 74 | trait Display {} 75 | 76 | trait Get { 77 | fn get(&mut l T, u32) -> () where [T: Debug]; 78 | // --- 79 | } 80 | 81 | impl Get for () { 82 | fn get(&mut l T, i32) -> () where T: Debug { 83 | // --- 84 | trusted 85 | } 86 | } 87 | } 88 | ]"; 89 | 90 | expect_test::expect![[r#" 91 | Err( 92 | Error { 93 | context: "check_trait_impl(impl <> Get((rigid tuple(0))) { fn get [(rigid &(mut) ^lt0_1 ^ty0_0), (rigid (scalar i32))] -> (rigid tuple(0)) where [well_formed((rigid &(mut) ^lt0_1 ^ty0_0)), well_formed((rigid (scalar i32))), well_formed((rigid tuple(0))), is_implemented(Debug(^ty0_0))] })", 94 | source: Error { 95 | context: "check_fn_in_impl", 96 | source: "could not prove `sub((rigid (scalar u32)), (rigid (scalar i32)))` given `[\n well_formed((rigid &(mut) !ltU(2)_1 !tyU(2)_0)),\n well_formed((rigid (scalar i32))),\n well_formed((rigid tuple(0))),\n is_implemented(Debug(!tyU(2)_0)),\n]`", 97 | }, 98 | }, 99 | ) 100 | "#]] 101 | .assert_debug_eq(&formality_rust::test_program_ok(PROGRAM)); 102 | } 103 | -------------------------------------------------------------------------------- /fixme_tests/wf-impl--supertrait-required.rs: -------------------------------------------------------------------------------- 1 | #![cfg(FIXME)] 2 | #![allow(non_snake_case)] 3 | 4 | #[test] 5 | #[ignore] 6 | fn test_one_impl() { 7 | const PROGRAM: &str = "[ 8 | crate core { 9 | trait Eq where Self: PartialEq { } 10 | trait PartialEq { } 11 | impl Eq for u32 { } 12 | } 13 | ]"; 14 | 15 | expect_test::expect![[r#" 16 | Err( 17 | Error { 18 | context: "check_trait_impl(impl <> Eq((rigid (scalar u32))) { })", 19 | source: "could not prove `is_implemented(Eq((rigid (scalar u32))))` given `[]`", 20 | }, 21 | ) 22 | "#]] 23 | .assert_debug_eq(&formality_rust::test_program_ok(PROGRAM)); 24 | } 25 | 26 | #[test] 27 | #[ignore] 28 | fn test_both_impls() { 29 | const PROGRAM: &str = "[ 30 | crate core { 31 | trait Eq where Self: PartialEq { } 32 | trait PartialEq { } 33 | impl Eq for u32 { } 34 | impl PartialEq for u32 { } 35 | } 36 | ]"; 37 | 38 | expect_test::expect![[r#" 39 | Ok( 40 | (), 41 | ) 42 | "#]] 43 | .assert_debug_eq(&formality_rust::test_program_ok(PROGRAM)); 44 | } 45 | -------------------------------------------------------------------------------- /reformat-logs.py: -------------------------------------------------------------------------------- 1 | #!/bin/python3 2 | # coding=utf-8 3 | # 4 | # This script adjusts the `trace` output from racket to be more compatible 5 | # with vscode's automatic code folding. 6 | # 7 | # Emits spaces and adds in a `>` and a `<` at the start of each line. 8 | 9 | from pdb import line_prefix 10 | import re 11 | import sys 12 | import fileinput 13 | 14 | # When things get deeply nested, racket's tracing 15 | # prints like `> > > >[10]`, where the `10` represents 16 | # the total number of characteres of indent depth. 17 | COUNT_RE = re.compile(r"^((c?[<> ]*)\[(\d+)\]) (.*)$") 18 | 19 | # Otherwise it prints like `> > >Foo` when first 20 | # entering Foo and `< < > > Bar` -- I *think* that 23 | # is when `Bar` is a subtask of `Foo`. 24 | INDENT_RE = re.compile(r"^(c?[<> ]*)(.*)$") 25 | 26 | # Backtraces sometimes appear in the middle of text and should be reproduced just as is. 27 | ERROR_RE = re.compile( 28 | r"(?:^[a-zA-Z]|^ errortrace\.\.\.|^ /|^ context\.\.\.|^ \.\.\.)") 29 | 30 | 31 | class Section: 32 | def __init__(self, input_indent, input_ident_count, output_indent): 33 | # number of lines of indent in the actual input text 34 | self.input_indent_count = input_ident_count 35 | 36 | # input header 37 | if '>' in input_indent: 38 | self.header = '>' 39 | else: 40 | self.header = '<' 41 | 42 | # number of spaces we indented by in the output 43 | self.output_indent_indent = output_indent 44 | self.lines = 0 45 | pass 46 | 47 | def print_header(self, text): 48 | print_line(self.output_indent_indent, self.header, text) 49 | 50 | def print_body(self, input_indent_count, text): 51 | output_indent = input_indent_count - \ 52 | self.input_indent_count + self.output_indent_indent 53 | if self.lines == 0: 54 | # putting this character lets you fold the contents of a new 55 | # block without folding children 56 | header = ' ⋮' 57 | self.lines += 1 58 | else: 59 | header = ' ' 60 | 61 | print_line(output_indent, header, text) 62 | 63 | 64 | def main(): 65 | current_section = None 66 | 67 | for line in fileinput.input(): 68 | # Backtraces should just print exactly as is. 69 | # mo = ERROR_RE.match(line) 70 | # if mo: 71 | # sys.stdout.write(line) 72 | # continue 73 | 74 | mo = COUNT_RE.match(line) 75 | if mo: 76 | input_indent_count = len(mo.group(1)) 77 | input_indent = mo.group(2) 78 | output_indent_count = int(mo.group(3)) + 1 79 | text = mo.group(4) 80 | current_section = Section( 81 | input_indent, input_indent_count, output_indent_count 82 | ) 83 | current_section.print_header(text) 84 | continue 85 | 86 | mo = INDENT_RE.match(line) 87 | if not mo: 88 | raise "bad line: {}".format(line) 89 | indent = mo.group(1) 90 | text = mo.group(2) 91 | if not indent or indent.isspace(): 92 | if current_section: 93 | current_section.print_body(len(indent), text) 94 | else: 95 | print_line(len(indent), "", text) 96 | else: 97 | current_section = Section( 98 | indent, len(indent), len(indent) 99 | ) 100 | current_section.print_header(text) 101 | 102 | 103 | def print_line(count, header, text): 104 | print("{}{}{}".format(" " * count, header, text)) 105 | 106 | 107 | def adjust_count(new_header, old_count, count): 108 | # when redex prints "> > >[18]", we adjust that to be 18 spaces, 109 | # but the subsequent lines only have a smaller amount of whitespace, 110 | # so we have to make it match. 111 | if '>' not in new_header and '<' not in new_header: 112 | if old_count > count: 113 | return old_count 114 | return count 115 | 116 | 117 | def indent_header(old_header, indent): 118 | if '>' in indent: 119 | return '>' 120 | elif '<' in indent: 121 | return '<' 122 | elif old_header == '>': 123 | # putting this character lets you fold the contents of a new 124 | # block without folding children 125 | return ' ⋮' 126 | else: 127 | return ' ' 128 | 129 | 130 | main() 131 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2024-06-16" 3 | components = ["rustc-dev", "rustfmt", "cargo", "llvm-tools"] 4 | profile = "minimal" 5 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | #![feature(control_flow_enum)] 3 | 4 | #[macro_use] 5 | extern crate rustc_smir; 6 | extern crate rustc_driver; 7 | extern crate rustc_interface; 8 | extern crate rustc_middle; 9 | extern crate stable_mir; 10 | 11 | use std::{path::PathBuf, sync::Arc}; 12 | 13 | use clap::Parser; 14 | use formality_check::check_all_crates; 15 | use formality_core::Set; 16 | use formality_prove::{test_util::TestAssertion, Constraints}; 17 | use formality_rust::grammar::Program; 18 | use formality_types::rust::try_term; 19 | 20 | #[cfg(test)] 21 | mod test; 22 | 23 | #[derive(Parser, Debug)] 24 | #[command(author, version, about, long_about = None)] 25 | struct Args { 26 | #[arg(long)] 27 | print_rust: bool, 28 | 29 | #[arg(long, default_value = "")] 30 | error_format: String, 31 | 32 | #[arg(long, default_value = "")] 33 | crate_type: String, 34 | 35 | #[arg(long, default_value = "")] 36 | edition: String, 37 | 38 | #[arg(long)] 39 | out_dir: Option, 40 | 41 | input_path: String, 42 | } 43 | 44 | pub fn main() -> anyhow::Result<()> { 45 | let args = Args::parse(); 46 | let input: String = std::fs::read_to_string(&args.input_path)?; 47 | let program: Program = try_term(&input)?; 48 | 49 | if args.print_rust { 50 | eprintln!("{:#?}", program); 51 | } 52 | 53 | check_all_crates(&program) 54 | } 55 | 56 | #[macro_export] 57 | macro_rules! assert_ok { 58 | ($input:tt $expect:expr) => {{ 59 | use formality_core::test_util::ResultTestExt; 60 | $crate::test_program_ok(stringify!($input)).assert_ok($expect); 61 | }}; 62 | } 63 | 64 | #[macro_export] 65 | macro_rules! assert_err { 66 | ($input:tt [$($must_have:expr,)*] $expect:expr) => {{ 67 | use formality_core::test_util::ResultTestExt; 68 | $crate::test_program_ok(stringify!($input)).assert_has_err($expect, &[$($must_have,)*]); 69 | }}; 70 | } 71 | 72 | pub fn test_program_ok(input: &str) -> anyhow::Result<()> { 73 | let program: Program = try_term(input)?; 74 | check_all_crates(&program) 75 | } 76 | 77 | pub fn test_where_clause(program: &str, assertion: &str) -> anyhow::Result> { 78 | formality_core::with_tracing_logs(|| { 79 | let program: Program = try_term(program)?; 80 | check_all_crates(&program)?; 81 | let assertion: Arc = try_term(assertion)?; 82 | let decls = program.to_prove_decls(); 83 | Ok(formality_prove::test_util::test_prove(decls, assertion).into_set()?) 84 | }) 85 | } 86 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() -> anyhow::Result<()> { 2 | formality_core::with_tracing_logs(a_mir_formality::main) 3 | } 4 | -------------------------------------------------------------------------------- /src/test/decl_safety.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | #[test] 4 | fn unsafe_trait() { 5 | crate::assert_ok!( 6 | //@check-pass 7 | [ 8 | crate baguette { 9 | unsafe trait Foo {} 10 | unsafe impl Foo for u32 {} 11 | } 12 | ] 13 | 14 | expect_test::expect!["()"] 15 | ) 16 | } 17 | 18 | #[test] 19 | fn safe_trait() { 20 | crate::assert_ok!( 21 | //@check-pass 22 | [ 23 | crate baguette { 24 | safe trait Foo {} 25 | safe impl Foo for u32 {} 26 | } 27 | ] 28 | 29 | expect_test::expect!["()"] 30 | ) 31 | } 32 | 33 | #[test] 34 | fn unsafe_trait_negative_impl() { 35 | crate::assert_ok!( 36 | //@check-pass 37 | [ 38 | crate baguette { 39 | unsafe trait Foo {} 40 | impl !Foo for u32 {} 41 | } 42 | ] 43 | 44 | expect_test::expect!["()"] 45 | ) 46 | } 47 | 48 | #[test] 49 | fn unsafe_trait_negative_impl_mismatch() { 50 | crate::assert_err!( 51 | [ 52 | crate baguette { 53 | unsafe trait Foo {} 54 | unsafe impl !Foo for u32 {} 55 | } 56 | ] 57 | 58 | [ /* TODO */ ] 59 | 60 | expect_test::expect![[r#" 61 | check_neg_trait_impl(unsafe impl ! Foo for u32 {}) 62 | 63 | Caused by: 64 | negative impls cannot be unsafe"#]] 65 | ) 66 | } 67 | 68 | #[test] 69 | fn safe_trait_negative_impl_mismatch() { 70 | crate::assert_err!( 71 | [ 72 | crate baguette { 73 | trait Foo {} 74 | unsafe impl !Foo for u32 {} 75 | } 76 | ] 77 | 78 | [ /* TODO */ ] 79 | 80 | expect_test::expect![[r#" 81 | check_neg_trait_impl(unsafe impl ! Foo for u32 {}) 82 | 83 | Caused by: 84 | negative impls cannot be unsafe"#]] 85 | ) 86 | } 87 | 88 | #[test] 89 | fn unsafe_trait_mismatch() { 90 | crate::assert_err!( 91 | [ 92 | crate baguette { 93 | unsafe trait Foo {} 94 | impl Foo for u32 {} 95 | } 96 | ] 97 | 98 | [ /* TODO */ ] 99 | 100 | expect_test::expect![[r#" 101 | check_trait_impl(impl Foo for u32 { }) 102 | 103 | Caused by: 104 | the trait `Foo` requires an `unsafe impl` declaration"#]] 105 | ) 106 | } 107 | 108 | #[test] 109 | fn safe_trait_mismatch() { 110 | crate::assert_err!( 111 | [ 112 | crate baguette { 113 | trait Foo {} 114 | unsafe impl Foo for u32 {} 115 | } 116 | ] 117 | 118 | [ /* TODO */ ] 119 | 120 | expect_test::expect![[r#" 121 | check_trait_impl(unsafe impl Foo for u32 { }) 122 | 123 | Caused by: 124 | implementing the trait `Foo` is not unsafe"#]] 125 | ) 126 | } 127 | -------------------------------------------------------------------------------- /src/test/functions.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | #[test] 4 | fn ok() { 5 | crate::assert_ok!( 6 | // Test functions, function's arguments, and function's returns 7 | //@check-pass 8 | [ 9 | crate Foo { 10 | // fn simple_fn() {} 11 | fn simple_fn() -> () { trusted } 12 | 13 | // fn one_arg(_: T) {} 14 | fn one_arg(T) -> () { trusted } 15 | 16 | // fn one_ret(_: T) {} 17 | fn one_ret() -> T { trusted } 18 | 19 | // fn arg_ret(_: T) -> U {} 20 | fn arg_ret(T) -> U { trusted } 21 | 22 | // fn multi_arg_ret(_: T, _: Y) -> (U, I) {} 23 | fn multi_arg_ret(T, Y) -> (U, I) { trusted } 24 | } 25 | ] 26 | 27 | expect_test::expect!["()"] 28 | ) 29 | } 30 | 31 | #[test] 32 | fn lifetime() { 33 | crate::assert_err!( 34 | // Test lifetimes on function 35 | [ 36 | crate Foo { 37 | // fn one_lt_arg<'a, T>(_: &'a T) -> () {} 38 | fn one_lt_arg(&a T) -> () { trusted } 39 | } 40 | ] 41 | 42 | [ /* TODO */ ] 43 | 44 | expect_test::expect![[r#" 45 | judgment `prove { goal: {@ wf(&!lt_0 !ty_1)}, assumptions: {}, env: Env { variables: [!lt_0, !ty_1], bias: Soundness }, decls: decls(222, [], [], [], [], [], [], {}, {}) }` failed at the following rule(s): 46 | failed at (src/file.rs:LL:CC) because 47 | judgment `prove_wc_list { goal: {@ wf(&!lt_0 !ty_1)}, assumptions: {}, env: Env { variables: [!lt_0, !ty_1], bias: Soundness } }` failed at the following rule(s): 48 | the rule "some" failed at step #0 (src/file.rs:LL:CC) because 49 | judgment `prove_wc { goal: @ wf(&!lt_0 !ty_1), assumptions: {}, env: Env { variables: [!lt_0, !ty_1], bias: Soundness } }` failed at the following rule(s): 50 | the rule "parameter well formed" failed at step #0 (src/file.rs:LL:CC) because 51 | judgment had no applicable rules: `prove_wf { goal: &!lt_0 !ty_1, assumptions: {}, env: Env { variables: [!lt_0, !ty_1], bias: Soundness } }`"#]] 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /tests/associated_type_normalization.rs: -------------------------------------------------------------------------------- 1 | use a_mir_formality::test_where_clause; 2 | use formality_core::{test, test_util::ResultTestExt}; 3 | 4 | const MIRROR: &str = "[ 5 | crate core { 6 | trait Mirror { 7 | type Assoc : []; 8 | } 9 | 10 | impl Mirror for T { 11 | type Assoc = T; 12 | } 13 | } 14 | ]"; 15 | 16 | #[test] 17 | fn test_mirror_normalizes_u32_to_u32() { 18 | test_where_clause(MIRROR, "exists {} => {::Assoc = T}").assert_ok( 19 | expect_test::expect!["{Constraints { env: Env { variables: [?ty_1], bias: Soundness }, known_true: true, substitution: {?ty_1 => u32} }, Constraints { env: Env { variables: [?ty_1], bias: Soundness }, known_true: true, substitution: {?ty_1 => ::Assoc} }}"], 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /tests/coherence_overlap.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] // we embed type names into the names for our test functions 2 | 3 | use a_mir_formality::test_program_ok; 4 | use formality_core::test_util::ResultTestExt; 5 | use formality_macros::test; 6 | 7 | #[test] 8 | fn test_overlap_normalize_alias_to_LocalType() { 9 | // `LocalTrait` has a blanket impl for all `T: Iterator` 10 | // and then an impl for `::T`... 11 | 12 | let gen_program = |addl: &str| { 13 | const BASE_PROGRAM: &str = "[ 14 | crate core { 15 | trait Iterator { 16 | } 17 | 18 | trait Mirror { 19 | type T : []; 20 | } 21 | 22 | impl Mirror for A { 23 | type T = A; 24 | } 25 | 26 | struct LocalType {} 27 | 28 | trait LocalTrait { } 29 | 30 | impl LocalTrait for T where T: Iterator { } 31 | 32 | impl LocalTrait for ::T { } 33 | 34 | ADDITIONAL 35 | } 36 | ]"; 37 | 38 | BASE_PROGRAM.replace("ADDITIONAL", addl) 39 | }; 40 | 41 | // ...on its own, this is OK. Figuring this out, though, requires proving 42 | // `::T: Iterator` which requires normalizing 43 | // the alias to `LocalType`... 44 | 45 | test_program_ok(&gen_program("")).assert_ok(expect_test::expect!["()"]); 46 | 47 | // ...but it's an error if LocalType implements Iterator (figuring *this* out also 48 | // requires normalizing). 49 | 50 | test_program_ok(&gen_program("impl Iterator for LocalType {}")).assert_err( 51 | expect_test::expect![[r#" 52 | impls may overlap: 53 | impl LocalTrait for ^ty0_0 where ^ty0_0 : Iterator { } 54 | impl LocalTrait for ::T { }"#]], 55 | ); 56 | } 57 | 58 | #[test] 59 | fn test_overlap_alias_not_normalizable() { 60 | // `LocalTrait` has a blanket impl for all `T: Iterator` 61 | // and then an impl for `::T`... 62 | 63 | let gen_program = |addl: &str| { 64 | const BASE_PROGRAM: &str = "[ 65 | crate core { 66 | trait Iterator { 67 | } 68 | 69 | trait Mirror { 70 | type T : []; 71 | } 72 | 73 | impl Mirror for A { 74 | type T = A; 75 | } 76 | 77 | struct LocalType {} 78 | 79 | trait LocalTrait { } 80 | 81 | impl LocalTrait for T where T: Iterator { } 82 | 83 | impl LocalTrait for ::T where T: Mirror { } 84 | 85 | ADDITIONAL 86 | } 87 | ]"; 88 | 89 | BASE_PROGRAM.replace("ADDITIONAL", addl) 90 | }; 91 | 92 | // ...you get an error here, because a downstream crate could implement 93 | // trait for some local type, in which case it would overlap. 94 | 95 | test_program_ok(&gen_program("")).assert_err(expect_test::expect![[r#" 96 | impls may overlap: 97 | impl LocalTrait for ^ty0_0 where ^ty0_0 : Iterator { } 98 | impl LocalTrait for <^ty0_0 as Mirror>::T where ^ty0_0 : Mirror { }"#]]); 99 | 100 | // ...and if there is at least one Iterator impl, we also flag an error. 101 | 102 | test_program_ok(&gen_program("impl Iterator for u32 {}")).assert_err(expect_test::expect![[ 103 | r#" 104 | impls may overlap: 105 | impl LocalTrait for ^ty0_0 where ^ty0_0 : Iterator { } 106 | impl LocalTrait for <^ty0_0 as Mirror>::T where ^ty0_0 : Mirror { }"# 107 | ]]); 108 | } 109 | -------------------------------------------------------------------------------- /tests/judgment-error-reporting/fallible.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use formality_core::{judgment_fn, term, test, test_util::ResultTestExt, Fallible}; 3 | 4 | #[term] 5 | enum Ty { 6 | Class { name: ClassName }, 7 | } 8 | 9 | formality_core::id!(ClassName); 10 | 11 | judgment_fn! { 12 | fn sub( 13 | a: Ty, 14 | b: Ty, 15 | ) => () { 16 | debug(a, b) 17 | 18 | ( 19 | (if name_a == name_b) 20 | ---------------------- ("same class") 21 | (sub(Ty::Class { name: name_a }, Ty::Class { name: name_b }) => ()) 22 | ) 23 | } 24 | } 25 | 26 | fn check_sub(a: Ty, b: Ty) -> Fallible<()> { 27 | Ok(sub(a, b) 28 | .check_proven() 29 | .with_context(|| format!("check_sub"))?) 30 | } 31 | 32 | #[test] 33 | fn test() { 34 | let foo = Ty::Class { 35 | name: ClassName::new("Foo"), 36 | }; 37 | let bar = Ty::Class { 38 | name: ClassName::new("Bar"), 39 | }; 40 | // Demonstrates a multi-line error rendered by anyhow. 41 | check_sub(foo, bar).assert_err(expect_test::expect![[r#" 42 | check_sub 43 | 44 | Caused by: 45 | judgment `sub { a: class(Foo), b: class(Bar) }` failed at the following rule(s): 46 | the rule "same class" failed at step #0 (src/file.rs:LL:CC) because 47 | condition evaluted to false: `name_a == name_b` 48 | name_a = Foo 49 | name_b = Bar"#]]); 50 | } 51 | -------------------------------------------------------------------------------- /tests/judgment-error-reporting/grammar.rs: -------------------------------------------------------------------------------- 1 | use crate::jer::{ 2 | grammar::{BoundVar, ExistentialVar, UniversalVar, Variable}, 3 | FormalityLang, 4 | }; 5 | use formality_core::{language::HasKind, term}; 6 | 7 | // Create a dummy kind/parameter -- we're not using these for the torture 8 | // tests, but we need them. 9 | 10 | #[term] 11 | #[derive(Copy)] 12 | pub enum DummyKind { 13 | Ty, 14 | } 15 | 16 | #[term] 17 | pub enum DummyParameter { 18 | #[cast] 19 | Ty(DummyTy), 20 | } 21 | 22 | #[term] 23 | pub enum DummyTy { 24 | #[variable(DummyKind::Ty)] 25 | Variable(Variable), 26 | } 27 | 28 | formality_core::cast_impl!((BoundVar) <: (Variable) <: (DummyTy)); 29 | formality_core::cast_impl!((ExistentialVar) <: (Variable) <: (DummyTy)); 30 | formality_core::cast_impl!((UniversalVar) <: (Variable) <: (DummyTy)); 31 | formality_core::cast_impl!((Variable) <: (DummyTy) <: (DummyParameter)); 32 | formality_core::cast_impl!((BoundVar) <: (DummyTy) <: (DummyParameter)); 33 | formality_core::cast_impl!((ExistentialVar) <: (DummyTy) <: (DummyParameter)); 34 | formality_core::cast_impl!((UniversalVar) <: (DummyTy) <: (DummyParameter)); 35 | 36 | impl HasKind for DummyParameter { 37 | fn kind(&self) -> formality_core::language::CoreKind { 38 | match self { 39 | DummyParameter::Ty(_) => DummyKind::Ty, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/judgment-error-reporting/main.rs: -------------------------------------------------------------------------------- 1 | use formality_core::Fallible; 2 | 3 | formality_core::declare_language! { 4 | mod jer { 5 | const NAME = "JER"; 6 | type Kind = crate::grammar::DummyKind; 7 | type Parameter = crate::grammar::DummyParameter; 8 | const BINDING_OPEN = '<'; 9 | const BINDING_CLOSE = '>'; 10 | const KEYWORDS = [ 11 | "struct", 12 | "fn", 13 | "let", 14 | "in", 15 | "integer", 16 | ]; 17 | } 18 | } 19 | 20 | use jer::FormalityLang; 21 | 22 | mod cyclic_judgment; 23 | mod fallible; 24 | mod grammar; 25 | 26 | fn main() -> Fallible<()> { 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /tests/parser-torture-tests/ambiguity.rs: -------------------------------------------------------------------------------- 1 | use formality_core::{term, test}; 2 | use std::sync::Arc; 3 | 4 | #[test] 5 | fn reduce_reduce_ok() { 6 | #[term] 7 | pub enum Root { 8 | #[cast] 9 | ClassTy(ClassTy), 10 | #[cast] 11 | Perm(Perm), 12 | } 13 | 14 | #[term($perm $class_id)] 15 | pub struct ClassTy { 16 | perm: Perm, 17 | class_id: Id, 18 | } 19 | 20 | #[term] 21 | pub enum Perm { 22 | My, 23 | Our, 24 | } 25 | 26 | formality_core::id!(Id); 27 | 28 | let term: Root = crate::ptt::term("my String"); 29 | expect_test::expect![[r#" 30 | ClassTy( 31 | ClassTy { 32 | perm: My, 33 | class_id: String, 34 | }, 35 | ) 36 | "#]] 37 | .assert_debug_eq(&term); 38 | } 39 | 40 | #[test] 41 | #[should_panic(expected = "ambiguous parse")] 42 | fn reduce_reduce_ambig() { 43 | #[term] 44 | pub enum Root { 45 | #[grammar($v0)] 46 | OneId(Id), 47 | #[grammar($v0 $v1)] 48 | TwoId(Id, Id), 49 | #[grammar($v0 $v1)] 50 | TwoRr(Arc, Arc), 51 | } 52 | 53 | formality_core::id!(Id); 54 | 55 | // This will panic. It could be parsed in multiple ways 56 | // (using a variant of Reverse Polish Notation) and none is obviously 57 | // better than the other: 58 | // 59 | // Root = ((Id Root::OneId) (Id Id Root::TwoId) Root::TwoRr) 60 | // Root = (Id Id Root::TwoId) (Id Root::OneId) Root::TwoRr) 61 | // Root = ((Id Root::OneId) (Id Root::OneId) (Id Root::OneId) Root::TwoRr) 62 | let term: Root = crate::ptt::term("a b c"); 63 | expect_test::expect![[r#" 64 | a b c 65 | "#]] 66 | .assert_debug_eq(&term); 67 | } 68 | -------------------------------------------------------------------------------- /tests/parser-torture-tests/commit_points.rs: -------------------------------------------------------------------------------- 1 | //! Test to demonstrate the value of commit points. 2 | //! We distinguish three states when parsing a nonterminal `X`: 3 | //! 4 | //! * Failed completely -- there is no `X` here 5 | //! * Almost succeeded -- there is a `X` here, but it has syntax errors 6 | //! * Succeeded -- there is an `X` here 7 | //! 8 | //! Distinguishing the first two is an art, not a science. 9 | //! 10 | //! A typical parser combinator just distinguishes the first and the last 11 | //! and doesn't have a concept of "almost succeeded", but this makes for 12 | //! significantly worse error messages and less predictable parsing. 13 | //! 14 | //! By default, we say that a parse "almost" succeeds if it consumes 15 | //! any tokens at all. This corresponds to LL(1) grammars. But sometimes 16 | //! it's not good enough! 17 | //! 18 | //! In this test, the `Place` grammar consumes as many `. ` projections 19 | //! as it can. But if we consider consuming `.` alone to be enough for a 20 | //! projection to "almost" succeed, we can't parse `$expr.let`. Note that `let` 21 | //! is a keyword, so that is not parsable as a `Place`. 22 | use formality_core::{term, test}; 23 | use std::sync::Arc; 24 | 25 | #[term] 26 | pub enum Expr { 27 | #[cast] 28 | Place(Place), 29 | 30 | #[grammar($v0 . let)] 31 | Let(Arc), 32 | } 33 | 34 | #[term($var $*projections)] 35 | pub struct Place { 36 | var: Id, 37 | projections: Vec, 38 | } 39 | 40 | #[term] 41 | pub enum Projection { 42 | #[grammar(. $v0 $!)] 43 | Field(Id), 44 | } 45 | 46 | formality_core::id!(Id); 47 | 48 | /// Check that we can parse `a.b` as a `Field`` 49 | #[test] 50 | fn test_parse_field() { 51 | let e: Expr = crate::ptt::term("a.b"); 52 | expect_test::expect![[r#" 53 | Place( 54 | Place { 55 | var: a, 56 | projections: [ 57 | Field( 58 | b, 59 | ), 60 | ], 61 | }, 62 | ) 63 | "#]] 64 | .assert_debug_eq(&e); 65 | } 66 | 67 | /// Check that we can parse `a.let` as a `Let`` 68 | #[test] 69 | fn test_parse_let() { 70 | let e: Expr = crate::ptt::term("a.let"); 71 | expect_test::expect![[r#" 72 | Let( 73 | Place( 74 | Place { 75 | var: a, 76 | projections: [], 77 | }, 78 | ), 79 | ) 80 | "#]] 81 | .assert_debug_eq(&e); 82 | } 83 | -------------------------------------------------------------------------------- /tests/parser-torture-tests/grammar.rs: -------------------------------------------------------------------------------- 1 | use crate::ptt::{ 2 | grammar::{BoundVar, ExistentialVar, UniversalVar, Variable}, 3 | FormalityLang, 4 | }; 5 | use formality_core::{language::HasKind, term}; 6 | 7 | // Create a dummy kind/parameter -- we're not using these for the torture 8 | // tests, but we need them. 9 | 10 | #[term] 11 | #[derive(Copy)] 12 | pub enum DummyKind { 13 | Ty, 14 | } 15 | 16 | #[term] 17 | pub enum DummyParameter { 18 | #[cast] 19 | Ty(DummyTy), 20 | } 21 | 22 | #[term] 23 | pub enum DummyTy { 24 | #[variable(DummyKind::Ty)] 25 | Variable(Variable), 26 | } 27 | 28 | formality_core::cast_impl!((BoundVar) <: (Variable) <: (DummyTy)); 29 | formality_core::cast_impl!((ExistentialVar) <: (Variable) <: (DummyTy)); 30 | formality_core::cast_impl!((UniversalVar) <: (Variable) <: (DummyTy)); 31 | formality_core::cast_impl!((Variable) <: (DummyTy) <: (DummyParameter)); 32 | formality_core::cast_impl!((BoundVar) <: (DummyTy) <: (DummyParameter)); 33 | formality_core::cast_impl!((ExistentialVar) <: (DummyTy) <: (DummyParameter)); 34 | formality_core::cast_impl!((UniversalVar) <: (DummyTy) <: (DummyParameter)); 35 | 36 | impl HasKind for DummyParameter { 37 | fn kind(&self) -> formality_core::language::CoreKind { 38 | match self { 39 | DummyParameter::Ty(_) => DummyKind::Ty, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/parser-torture-tests/left_associative.rs: -------------------------------------------------------------------------------- 1 | use formality_core::{term, test}; 2 | use std::sync::Arc; 3 | 4 | // ANCHOR: Expr 5 | #[term] 6 | pub enum Expr { 7 | #[cast] 8 | Id(Id), 9 | 10 | #[grammar($v0 + $v1)] 11 | #[precedence(1)] 12 | Add(Arc, Arc), 13 | 14 | #[grammar($v0 * $v1)] 15 | #[precedence(2)] 16 | Mul(Arc, Arc), 17 | } 18 | 19 | formality_core::id!(Id); 20 | // ANCHOR_END: Expr 21 | 22 | #[test] 23 | fn add_mul() { 24 | let term: Expr = crate::ptt::term("a + b * c"); 25 | expect_test::expect![[r#" 26 | Add( 27 | Id( 28 | a, 29 | ), 30 | Mul( 31 | Id( 32 | b, 33 | ), 34 | Id( 35 | c, 36 | ), 37 | ), 38 | ) 39 | "#]] 40 | .assert_debug_eq(&term); 41 | } 42 | 43 | #[test] 44 | fn mul_add() { 45 | let term: Expr = crate::ptt::term("a * b + c"); 46 | expect_test::expect![[r#" 47 | Add( 48 | Mul( 49 | Id( 50 | a, 51 | ), 52 | Id( 53 | b, 54 | ), 55 | ), 56 | Id( 57 | c, 58 | ), 59 | ) 60 | "#]] 61 | .assert_debug_eq(&term); 62 | } 63 | 64 | #[test] 65 | fn add_add() { 66 | let term: Expr = crate::ptt::term("a + b + c"); 67 | expect_test::expect![[r#" 68 | Add( 69 | Add( 70 | Id( 71 | a, 72 | ), 73 | Id( 74 | b, 75 | ), 76 | ), 77 | Id( 78 | c, 79 | ), 80 | ) 81 | "#]] 82 | .assert_debug_eq(&term); 83 | } 84 | 85 | #[test] 86 | fn mul_mul() { 87 | let term: Expr = crate::ptt::term("a * b * c"); 88 | expect_test::expect![[r#" 89 | Mul( 90 | Mul( 91 | Id( 92 | a, 93 | ), 94 | Id( 95 | b, 96 | ), 97 | ), 98 | Id( 99 | c, 100 | ), 101 | ) 102 | "#]] 103 | .assert_debug_eq(&term); 104 | } 105 | 106 | #[test] 107 | fn mul_mul_mul() { 108 | let term: Expr = crate::ptt::term("a * b * c * d"); 109 | expect_test::expect![[r#" 110 | Mul( 111 | Mul( 112 | Mul( 113 | Id( 114 | a, 115 | ), 116 | Id( 117 | b, 118 | ), 119 | ), 120 | Id( 121 | c, 122 | ), 123 | ), 124 | Id( 125 | d, 126 | ), 127 | ) 128 | "#]] 129 | .assert_debug_eq(&term); 130 | } 131 | 132 | #[test] 133 | fn add_add_mul_add() { 134 | let term: Expr = crate::ptt::term("a + b + c * d + e"); 135 | expect_test::expect![[r#" 136 | Add( 137 | Add( 138 | Add( 139 | Id( 140 | a, 141 | ), 142 | Id( 143 | b, 144 | ), 145 | ), 146 | Mul( 147 | Id( 148 | c, 149 | ), 150 | Id( 151 | d, 152 | ), 153 | ), 154 | ), 155 | Id( 156 | e, 157 | ), 158 | ) 159 | "#]] 160 | .assert_debug_eq(&term); 161 | } 162 | -------------------------------------------------------------------------------- /tests/parser-torture-tests/main.rs: -------------------------------------------------------------------------------- 1 | mod ambiguity; 2 | mod commit_points; 3 | mod grammar; 4 | mod left_associative; 5 | mod none_associative; 6 | mod path; 7 | mod right_associative; 8 | 9 | formality_core::declare_language! { 10 | mod ptt { 11 | const NAME = "PTT"; 12 | type Kind = crate::grammar::DummyKind; 13 | type Parameter = crate::grammar::DummyParameter; 14 | const BINDING_OPEN = '<'; 15 | const BINDING_CLOSE = '>'; 16 | const KEYWORDS = [ 17 | "struct", 18 | "fn", 19 | "let", 20 | "in", 21 | "integer", 22 | ]; 23 | } 24 | } 25 | 26 | /// Used to parse `text` when we expect some remainder 27 | fn expect_remainder(text: &str) -> (T, &str) 28 | where 29 | T: CoreParse, 30 | { 31 | match T::parse(&Default::default(), text) { 32 | Ok(parse) => { 33 | let (value, remainder) = parse.finish(); 34 | assert!( 35 | !remainder.is_empty(), 36 | "expected to have remainder text, but parsed entire term `{text:?}`" 37 | ); 38 | (value, remainder) 39 | } 40 | Err(errs) => panic!("encountered unexpected parse error: {errs:#?}"), 41 | } 42 | } 43 | 44 | // Default language for our crate 45 | use formality_core::{parse::CoreParse, Fallible}; 46 | use ptt::FormalityLang; 47 | 48 | fn main() -> Fallible<()> { 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /tests/parser-torture-tests/none_associative.rs: -------------------------------------------------------------------------------- 1 | use formality_core::{term, test}; 2 | use std::sync::Arc; 3 | 4 | use crate::expect_remainder; 5 | 6 | #[term] 7 | pub enum Expr { 8 | #[cast] 9 | Id(Id), 10 | 11 | #[grammar($v0 + $v1)] 12 | #[precedence(1, none)] 13 | Add(Arc, Arc), 14 | 15 | #[grammar($v0 * $v1)] 16 | #[precedence(2, none)] 17 | Mul(Arc, Arc), 18 | } 19 | 20 | formality_core::id!(Id); 21 | 22 | #[test] 23 | fn add_mul() { 24 | let term: Expr = crate::ptt::term("a + b * c"); 25 | expect_test::expect![[r#" 26 | Add( 27 | Id( 28 | a, 29 | ), 30 | Mul( 31 | Id( 32 | b, 33 | ), 34 | Id( 35 | c, 36 | ), 37 | ), 38 | ) 39 | "#]] 40 | .assert_debug_eq(&term); 41 | } 42 | 43 | #[test] 44 | fn mul_add() { 45 | let term: Expr = crate::ptt::term("a * b + c"); 46 | expect_test::expect![[r#" 47 | Add( 48 | Mul( 49 | Id( 50 | a, 51 | ), 52 | Id( 53 | b, 54 | ), 55 | ), 56 | Id( 57 | c, 58 | ), 59 | ) 60 | "#]] 61 | .assert_debug_eq(&term); 62 | } 63 | 64 | #[test] 65 | fn add_add() { 66 | let term = expect_remainder::("a + b + c"); 67 | expect_test::expect![[r#" 68 | ( 69 | Add( 70 | Id( 71 | a, 72 | ), 73 | Id( 74 | b, 75 | ), 76 | ), 77 | " + c", 78 | ) 79 | "#]] 80 | .assert_debug_eq(&term); 81 | } 82 | 83 | #[test] 84 | fn mul_mul() { 85 | let term = expect_remainder::("a * b * c"); 86 | expect_test::expect![[r#" 87 | ( 88 | Mul( 89 | Id( 90 | a, 91 | ), 92 | Id( 93 | b, 94 | ), 95 | ), 96 | " * c", 97 | ) 98 | "#]] 99 | .assert_debug_eq(&term); 100 | } 101 | 102 | #[test] 103 | fn mul_mul_mul() { 104 | let term = expect_remainder::("a * b * c * d"); 105 | expect_test::expect![[r#" 106 | ( 107 | Mul( 108 | Id( 109 | a, 110 | ), 111 | Id( 112 | b, 113 | ), 114 | ), 115 | " * c * d", 116 | ) 117 | "#]] 118 | .assert_debug_eq(&term); 119 | } 120 | 121 | #[test] 122 | fn add_add_mul_add() { 123 | let term = expect_remainder::("a + b + c * d + e"); 124 | expect_test::expect![[r#" 125 | ( 126 | Add( 127 | Id( 128 | a, 129 | ), 130 | Id( 131 | b, 132 | ), 133 | ), 134 | " + c * d + e", 135 | ) 136 | "#]] 137 | .assert_debug_eq(&term); 138 | } 139 | -------------------------------------------------------------------------------- /tests/parser-torture-tests/path.rs: -------------------------------------------------------------------------------- 1 | use formality_core::{term, test}; 2 | use std::sync::Arc; 3 | 4 | // ANCHOR: path 5 | #[term] 6 | pub enum Path { 7 | #[cast] 8 | Id(Id), 9 | 10 | #[grammar($v0 . $v1)] 11 | Field(Arc, Id), 12 | 13 | #[grammar($v0 [ $v1 ])] 14 | Index(Arc, Arc), 15 | } 16 | 17 | formality_core::id!(Id); 18 | // ANCHOR_END: path 19 | 20 | #[test] 21 | fn path() { 22 | let term: Path = crate::ptt::term("a.b[c.d].e"); 23 | expect_test::expect![[r#" 24 | Field( 25 | Index( 26 | Field( 27 | Id( 28 | a, 29 | ), 30 | b, 31 | ), 32 | Field( 33 | Id( 34 | c, 35 | ), 36 | d, 37 | ), 38 | ), 39 | e, 40 | ) 41 | "#]] 42 | .assert_debug_eq(&term); 43 | } 44 | -------------------------------------------------------------------------------- /tests/parser-torture-tests/right_associative.rs: -------------------------------------------------------------------------------- 1 | use formality_core::{term, test}; 2 | use std::sync::Arc; 3 | 4 | #[term] 5 | pub enum Expr { 6 | #[cast] 7 | Id(Id), 8 | 9 | #[grammar($v0 + $v1)] 10 | #[precedence(1, right)] 11 | Add(Arc, Arc), 12 | 13 | #[grammar($v0 * $v1)] 14 | #[precedence(2, right)] 15 | Mul(Arc, Arc), 16 | } 17 | 18 | formality_core::id!(Id); 19 | 20 | #[test] 21 | fn add_mul() { 22 | let term: Expr = crate::ptt::term("a + b * c"); 23 | expect_test::expect![[r#" 24 | Add( 25 | Id( 26 | a, 27 | ), 28 | Mul( 29 | Id( 30 | b, 31 | ), 32 | Id( 33 | c, 34 | ), 35 | ), 36 | ) 37 | "#]] 38 | .assert_debug_eq(&term); 39 | } 40 | 41 | #[test] 42 | fn mul_add() { 43 | let term: Expr = crate::ptt::term("a * b + c"); 44 | expect_test::expect![[r#" 45 | Add( 46 | Mul( 47 | Id( 48 | a, 49 | ), 50 | Id( 51 | b, 52 | ), 53 | ), 54 | Id( 55 | c, 56 | ), 57 | ) 58 | "#]] 59 | .assert_debug_eq(&term); 60 | } 61 | 62 | #[test] 63 | fn add_add() { 64 | let term: Expr = crate::ptt::term("a + b + c"); 65 | expect_test::expect![[r#" 66 | Add( 67 | Id( 68 | a, 69 | ), 70 | Add( 71 | Id( 72 | b, 73 | ), 74 | Id( 75 | c, 76 | ), 77 | ), 78 | ) 79 | "#]] 80 | .assert_debug_eq(&term); 81 | } 82 | 83 | #[test] 84 | fn mul_mul() { 85 | let term: Expr = crate::ptt::term("a * b * c"); 86 | expect_test::expect![[r#" 87 | Mul( 88 | Id( 89 | a, 90 | ), 91 | Mul( 92 | Id( 93 | b, 94 | ), 95 | Id( 96 | c, 97 | ), 98 | ), 99 | ) 100 | "#]] 101 | .assert_debug_eq(&term); 102 | } 103 | 104 | #[test] 105 | fn mul_mul_mul() { 106 | let term: Expr = crate::ptt::term("a * b * c * d"); 107 | expect_test::expect![[r#" 108 | Mul( 109 | Id( 110 | a, 111 | ), 112 | Mul( 113 | Id( 114 | b, 115 | ), 116 | Mul( 117 | Id( 118 | c, 119 | ), 120 | Id( 121 | d, 122 | ), 123 | ), 124 | ), 125 | ) 126 | "#]] 127 | .assert_debug_eq(&term); 128 | } 129 | 130 | #[test] 131 | fn add_add_mul_add() { 132 | let term: Expr = crate::ptt::term("a + b + c * d + e"); 133 | expect_test::expect![[r#" 134 | Add( 135 | Id( 136 | a, 137 | ), 138 | Add( 139 | Id( 140 | b, 141 | ), 142 | Add( 143 | Mul( 144 | Id( 145 | c, 146 | ), 147 | Id( 148 | d, 149 | ), 150 | ), 151 | Id( 152 | e, 153 | ), 154 | ), 155 | ), 156 | ) 157 | "#]] 158 | .assert_debug_eq(&term); 159 | } 160 | -------------------------------------------------------------------------------- /tests/parser_var_id_ambiguity.rs: -------------------------------------------------------------------------------- 1 | //! Test for how we handle ambiguity between a variable and an identifier in the parser. 2 | //! This can't be part of parser-torture-tests because it requires settting up a custom variable. 3 | 4 | use std::sync::Arc; 5 | 6 | use formality_core::{Downcast, DowncastTo, Upcast, UpcastFrom}; 7 | // Default language for our crate 8 | use formality_core::{language::HasKind, term}; 9 | use ptt::grammar::{Binder, BoundVar, ExistentialVar, UniversalVar, Variable}; 10 | use ptt::FormalityLang; 11 | 12 | formality_core::declare_language! { 13 | mod ptt { 14 | const NAME = "PTT"; 15 | type Kind = crate::Kind; 16 | type Parameter = crate::Parameter; 17 | const BINDING_OPEN = '<'; 18 | const BINDING_CLOSE = '>'; 19 | const KEYWORDS = [ 20 | ]; 21 | } 22 | } 23 | 24 | #[term] 25 | #[derive(Copy)] 26 | pub enum Kind { 27 | Ty, 28 | Perm, 29 | } 30 | 31 | #[term] 32 | pub enum Parameter { 33 | #[cast] 34 | Ty(Ty), 35 | 36 | #[cast] 37 | Perm(Perm), 38 | } 39 | 40 | #[term] 41 | pub enum Ty { 42 | #[variable(Kind::Ty)] 43 | Variable(Variable), 44 | 45 | #[cast] 46 | Id(Id), 47 | 48 | #[grammar($v0 $v1)] 49 | Apply(Perm, Arc), 50 | 51 | #[grammar($v0 :: $v1)] 52 | Assoc(Arc, Id), 53 | } 54 | 55 | #[term] 56 | pub enum Perm { 57 | #[variable(Kind::Perm)] 58 | Variable(Variable), 59 | } 60 | 61 | formality_core::id!(Id); 62 | 63 | formality_core::cast_impl!((BoundVar) <: (Variable) <: (Parameter)); 64 | formality_core::cast_impl!((ExistentialVar) <: (Variable) <: (Parameter)); 65 | formality_core::cast_impl!((UniversalVar) <: (Variable) <: (Parameter)); 66 | 67 | impl UpcastFrom for Parameter { 68 | fn upcast_from(term: Variable) -> Self { 69 | match term.kind() { 70 | Kind::Ty => Ty::Variable(term).upcast(), 71 | Kind::Perm => Perm::Variable(term).upcast(), 72 | } 73 | } 74 | } 75 | 76 | impl DowncastTo for Parameter { 77 | fn downcast_to(&self) -> Option { 78 | match self { 79 | Parameter::Ty(ty) => ty.downcast(), 80 | Parameter::Perm(perm) => perm.downcast(), 81 | } 82 | } 83 | } 84 | 85 | impl HasKind for Parameter { 86 | fn kind(&self) -> formality_core::language::CoreKind { 87 | match self { 88 | Parameter::Ty(_) => Kind::Ty, 89 | Parameter::Perm(_) => Kind::Perm, 90 | } 91 | } 92 | } 93 | 94 | #[test] 95 | fn parse_var() { 96 | let value: Binder = ptt::term(" T"); 97 | expect_test::expect![[r#" 98 | Binder { 99 | kinds: [ 100 | Ty, 101 | ], 102 | term: Variable( 103 | ^ty0_0, 104 | ), 105 | } 106 | "#]] 107 | .assert_debug_eq(&value); 108 | } 109 | 110 | #[test] 111 | fn parse_id() { 112 | let value: Binder = ptt::term(" i32"); 113 | expect_test::expect![[r#" 114 | Binder { 115 | kinds: [ 116 | Ty, 117 | ], 118 | term: Id( 119 | i32, 120 | ), 121 | } 122 | "#]] 123 | .assert_debug_eq(&value); 124 | } 125 | 126 | #[test] 127 | fn parse_assoc() { 128 | let value: Binder = ptt::term(" i32::T"); 129 | expect_test::expect![[r#" 130 | Binder { 131 | kinds: [ 132 | Ty, 133 | ], 134 | term: Assoc( 135 | Id( 136 | i32, 137 | ), 138 | T, 139 | ), 140 | } 141 | "#]] 142 | .assert_debug_eq(&value); 143 | } 144 | 145 | /// Test for the case where the ambiguity occurs after some 146 | /// reductions. In this case, we parse P (the variable) as a Perm 147 | /// and then integrate that into the type, but we could also parse 148 | /// P as a type itself. 149 | #[test] 150 | fn parse_apply() { 151 | let value: Binder = ptt::term(" P i32"); 152 | expect_test::expect![[r#" 153 | Binder { 154 | kinds: [ 155 | Perm, 156 | ], 157 | term: Apply( 158 | Variable( 159 | ^perm0_0, 160 | ), 161 | Id( 162 | i32, 163 | ), 164 | ), 165 | } 166 | "#]] 167 | .assert_debug_eq(&value); 168 | } 169 | 170 | /// Test for the case where the ambiguity occurs after some 171 | /// reductions. In this case, we parse P (the variable) as a Perm 172 | /// and then integrate that into the type, but we could also parse 173 | /// P as a type itself. 174 | #[test] 175 | fn parse_parameter() { 176 | let value: Binder = ptt::term(" P"); 177 | expect_test::expect![[r#" 178 | Binder { 179 | kinds: [ 180 | Perm, 181 | ], 182 | term: Perm( 183 | Variable( 184 | ^perm0_0, 185 | ), 186 | ), 187 | } 188 | "#]] 189 | .assert_debug_eq(&value); 190 | } 191 | -------------------------------------------------------------------------------- /tests/projection.rs: -------------------------------------------------------------------------------- 1 | use a_mir_formality::test_where_clause; 2 | use formality_core::test_util::ResultTestExt; 3 | 4 | const NORMALIZE_BASIC: &str = "[ 5 | crate test { 6 | trait Iterator { 7 | type Item : []; 8 | } 9 | 10 | struct Vec {} 11 | 12 | struct Foo {} 13 | 14 | impl Iterator for Vec { 15 | type Item = T; 16 | } 17 | } 18 | ]"; 19 | 20 | #[test] 21 | fn normalize_basic() { 22 | test_where_clause( 23 | NORMALIZE_BASIC, 24 | "forall exists {} => { as Iterator>::Item = U }", 25 | ) 26 | .assert_ok(expect_test::expect!["{Constraints { env: Env { variables: [!ty_1, ?ty_2], bias: Soundness }, known_true: true, substitution: {?ty_2 => as Iterator>::Item} }, Constraints { env: Env { variables: [!ty_1, ?ty_2], bias: Soundness }, known_true: true, substitution: {?ty_2 => !ty_1} }}"]); 27 | 28 | test_where_clause( 29 | NORMALIZE_BASIC, 30 | "forall {} => { Iterator(Vec), as Iterator>::Item = T }", 31 | ) 32 | .assert_ok(expect_test::expect!["{Constraints { env: Env { variables: [!ty_1], bias: Soundness }, known_true: true, substitution: {} }}"]); 33 | 34 | test_where_clause( 35 | NORMALIZE_BASIC, 36 | "forall { Iterator(T), ::Item = Foo } => { ::Item = Foo }", 37 | ).assert_ok( 38 | expect_test::expect!["{Constraints { env: Env { variables: [!ty_1], bias: Soundness }, known_true: true, substitution: {} }}"] 39 | ); 40 | 41 | test_where_clause( 42 | NORMALIZE_BASIC, 43 | "forall exists { Iterator(T) } => { ::Item = U }", 44 | ) 45 | .assert_ok(expect_test::expect!["{Constraints { env: Env { variables: [!ty_1, ?ty_2], bias: Soundness }, known_true: true, substitution: {?ty_2 => ::Item} }}"]); 46 | 47 | test_where_clause( 48 | NORMALIZE_BASIC, 49 | "forall { Iterator(T) } => { ::Item = ::Item }", 50 | ) 51 | .assert_ok(expect_test::expect!["{Constraints { env: Env { variables: [!ty_1], bias: Soundness }, known_true: true, substitution: {} }}"]); 52 | 53 | test_where_clause( 54 | NORMALIZE_BASIC, 55 | "forall exists { Iterator(T) } => { ::Item = ::Item }", 56 | ).assert_ok( 57 | expect_test::expect!["{Constraints { env: Env { variables: [!ty_1, ?ty_2], bias: Soundness }, known_true: true, substitution: {?ty_2 => !ty_1} }, Constraints { env: Env { variables: [!ty_1, ?ty_3, ?ty_2], bias: Soundness }, known_true: true, substitution: {?ty_2 => Vec<::Item>, ?ty_3 => ::Item} }}"]); 58 | } 59 | 60 | const NORMALIZE_INTO_ITERATOR: &str = "[ 61 | crate test { 62 | trait IntoIterator { 63 | type Item : []; 64 | } 65 | 66 | trait Iterator { 67 | type Item : []; 68 | } 69 | 70 | struct Vec {} 71 | 72 | struct Foo {} 73 | 74 | impl IntoIterator for Vec { 75 | type Item = T; 76 | } 77 | 78 | impl IntoIterator for T where T: Iterator { 79 | type Item = ::Item; 80 | } 81 | } 82 | ]"; 83 | 84 | #[test] 85 | fn normalize_into_iterator() { 86 | test_where_clause( 87 | NORMALIZE_INTO_ITERATOR, 88 | "forall exists {} => { as IntoIterator>::Item = U }", 89 | ) 90 | .assert_ok(expect_test::expect!["{Constraints { env: Env { variables: [!ty_1, ?ty_2], bias: Soundness }, known_true: true, substitution: {?ty_2 => as IntoIterator>::Item} }, Constraints { env: Env { variables: [!ty_1, ?ty_2], bias: Soundness }, known_true: true, substitution: {?ty_2 => !ty_1} }}"]); 91 | } 92 | 93 | const PROJECTION_EQUALITY: &str = "[ 94 | crate test { 95 | trait Trait1<> { 96 | type Type : []; 97 | } 98 | trait Trait2 {} 99 | impl Trait2 for U where U: Trait1<>, ::Type => T {} 100 | struct S {} 101 | impl Trait1<> for S { 102 | type Type = u32; 103 | } 104 | } 105 | ]"; 106 | 107 | #[test] 108 | fn projection_equality() { 109 | test_where_clause( 110 | PROJECTION_EQUALITY, 111 | "exists {} => { Trait1(S), >::Type = U }", 112 | ) 113 | .assert_ok(expect_test::expect!["{Constraints { env: Env { variables: [?ty_1], bias: Soundness }, known_true: true, substitution: {?ty_1 => u32} }, Constraints { env: Env { variables: [?ty_1], bias: Soundness }, known_true: true, substitution: {?ty_1 => ::Type} }}"]); 114 | 115 | test_where_clause(PROJECTION_EQUALITY, "exists {} => { Trait2(S, U) }").assert_ok( 116 | expect_test::expect!["{Constraints { env: Env { variables: [?ty_1], bias: Soundness }, known_true: true, substitution: {?ty_1 => u32} }, Constraints { env: Env { variables: [?ty_1], bias: Soundness }, known_true: true, substitution: {?ty_1 => ::Type} }}"], 117 | ); 118 | } 119 | -------------------------------------------------------------------------------- /triagebot.toml: -------------------------------------------------------------------------------- 1 | [assign] 2 | --------------------------------------------------------------------------------