├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .gitattributes ├── .github └── workflows │ ├── ante-ls.yml │ └── ante.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── TODO.md ├── ante-ls ├── Cargo.toml └── src │ ├── main.rs │ └── util.rs ├── build.rs ├── crates.nix ├── default.nix ├── examples ├── codegen │ ├── bitwise.an │ ├── builtin_float.an │ ├── builtin_int.an │ ├── closure_basic.an │ ├── closure_nested.an │ ├── closure_return.an │ ├── data.an │ ├── effects │ │ ├── add_effect.an │ │ ├── count_effect.an │ │ ├── generators_basic.an │ │ ├── generators_complex.an │ │ ├── handle_multiple_effects.an │ │ └── state.an │ ├── explicit_curry.an │ ├── fib.an │ ├── function_call.an │ ├── hello.an │ ├── implicit_import.an │ ├── int_string_cast.an │ ├── iter.an │ ├── logical.an │ ├── loop.an │ ├── map.an │ ├── monomorphise_all.an │ ├── mutability.an │ ├── pass_by_ref.an │ ├── string_append.an │ ├── string_builder.an │ ├── tuples.an │ └── vec_basic.an ├── nameresolution │ ├── Library.an │ ├── T.an │ ├── Trait.an │ ├── U.an │ ├── conflictingimport.an │ ├── effects.an │ ├── errors.an │ ├── functions.an │ ├── import.an │ ├── named_constructor.an │ ├── redeclare.an │ ├── type_decl.an │ └── unused_warning.an ├── parsing │ ├── apply_operators.an │ ├── effects.an │ ├── explicit_curry.an │ ├── hello.an │ ├── indent.an │ ├── invalid_integer_literal_suffix.an │ ├── irrefutable_pattern.an │ ├── loop.an │ ├── match.an │ ├── math.an │ ├── named_constructor.an │ ├── parse_error.an │ ├── semicolon_inference.an │ ├── string_interpolation.an │ ├── trait_decl.an │ ├── type_decl.an │ └── with_effect.an ├── regressions │ ├── 103_size_of_ptr.an │ ├── 104_print_twice.an │ ├── 119_top_level_subscopes.an │ ├── 129_int_defaulting_generalization.an │ ├── 130_lambda_int_defaulting.an │ ├── 146_invalid_int_type.an │ ├── 159_two_trait_functions.an │ ├── 201_mutable_variables_copy.an │ ├── 217_filter.an │ ├── 80_empty_match.an │ ├── 85_type_defaulting_rules.an │ └── 96_rebind_origin.an └── typechecking │ ├── bind.an │ ├── completeness_checking.an │ ├── count_effect.an │ ├── effects.an │ ├── extern.an │ ├── field_diagnostics.an │ ├── functor_and_monad.an │ ├── generalization.an │ ├── given_constraint_error.an │ ├── impl.an │ ├── instantiation.an │ ├── int_inference.an │ ├── member_access.an │ ├── multiple_matching_impls.an │ ├── mutual_recursion.an │ ├── mutual_recursion_rigid.an │ ├── named_constructor.an │ ├── repeated_traits.an │ ├── rigid.an │ ├── trait_fundep_result.an │ ├── trait_generalization.an │ ├── trait_impls.an │ ├── trait_propagation.an │ └── type_annotations.an ├── examples_failing ├── 141_recursive_closure.an ├── 142_incorrect_defaulting.an └── capture_by_ref.an ├── flake.lock ├── flake.nix ├── rustfmt.toml ├── shell.nix ├── src ├── cache │ ├── counter.rs │ ├── dependency_graph.rs │ ├── mod.rs │ └── unsafecache.rs ├── cli.rs ├── cranelift_backend │ ├── builtin.rs │ ├── context.rs │ ├── decisiontree.rs │ ├── mod.rs │ └── module.rs ├── error │ ├── location.rs │ └── mod.rs ├── frontend.rs ├── hir │ ├── capabilities.rs │ ├── closures.rs │ ├── decision_tree_monomorphisation.rs │ ├── definitions.rs │ ├── mod.rs │ ├── monomorphisation │ │ ├── effects.rs │ │ └── mod.rs │ ├── printer.rs │ └── types.rs ├── lexer │ ├── mod.rs │ └── token.rs ├── lib.rs ├── lifetimes │ └── mod.rs ├── llvm │ ├── builtin.rs │ ├── decisiontree.rs │ └── mod.rs ├── main.rs ├── mir │ ├── ir │ │ ├── block.rs │ │ ├── function.rs │ │ ├── instruction.rs │ │ ├── ir.rs │ │ ├── mod.rs │ │ └── value.rs │ └── mod.rs ├── nameresolution │ ├── builtin.rs │ ├── free_variables.rs │ ├── mod.rs │ ├── scope.rs │ └── visitor.rs ├── parser │ ├── ast.rs │ ├── combinators.rs │ ├── desugar.rs │ ├── error.rs │ ├── mod.rs │ └── pretty_printer.rs ├── types │ ├── effects.rs │ ├── mod.rs │ ├── mutual_recursion.rs │ ├── pattern.rs │ ├── traitchecker.rs │ ├── traits.rs │ ├── typechecker.rs │ ├── typed.rs │ └── typeprinter.rs └── util │ ├── id.rs │ ├── logging.rs │ ├── mod.rs │ ├── timing.rs │ ├── trustme.rs │ └── vecmap.rs ├── stdlib ├── HashMap.an ├── StringBuilder.an ├── Vec.an ├── future │ ├── abort.an │ ├── str.an │ └── stream.an └── prelude.an └── tests └── golden_tests.rs /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/plc-lang/rust-llvm:latest 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y git iproute2 procps lsb-release 5 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "dockerfile": "Dockerfile" 4 | }, 5 | 6 | "customizations": { 7 | "vscode": { 8 | "extensions": [ 9 | "rust-lang.rust-analyzer", 10 | "jfecher.ante" 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.an linguist-language=scala 3 | -------------------------------------------------------------------------------- /.github/workflows/ante-ls.yml: -------------------------------------------------------------------------------- 1 | name: Ante-LS 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | submodules: 'true' 21 | - name: Build 22 | working-directory: ./ante-ls 23 | run: cargo build --no-default-features 24 | -------------------------------------------------------------------------------- /.github/workflows/ante.yml: -------------------------------------------------------------------------------- 1 | name: Ante 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | submodules: 'true' 21 | - name: Build 22 | run: cargo build --no-default-features 23 | - name: Run tests 24 | run: cargo test --no-default-features 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode 3 | 4 | # Nix build artifacts 5 | /result 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "minicoro"] 2 | path = minicoro 3 | url = https://github.com/jfecher/minicoro 4 | [submodule "aminicoro"] 5 | path = aminicoro 6 | url = https://github.com/jfecher/minicoro 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ante" 3 | version = "0.1.1" 4 | authors = ["Jake Fecher "] 5 | edition = "2021" 6 | build = "build.rs" 7 | rust-version = "1.81.0" 8 | 9 | [lib] 10 | name = "ante" 11 | 12 | [workspace] 13 | members = [ "ante-ls" ] 14 | 15 | [dependencies] 16 | aminicoro = { path = "./aminicoro" } 17 | clap = { version = "4", features = ["derive"] } 18 | colored = "2.0.0" 19 | mimalloc = { version = "0.1.29", default-features = false } 20 | difference = "2.0.0" 21 | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm18-1"], optional = true } 22 | cranelift = "0.86.1" 23 | cranelift-module = "0.86.1" 24 | cranelift-jit = "0.86.1" 25 | cranelift-object = "0.86.1" 26 | target-lexicon = "0.12.4" 27 | clap_complete = "4" 28 | petgraph = "0.6.4" 29 | 30 | [dev-dependencies] 31 | goldentests = "1.3.0" 32 | 33 | [profile.release] 34 | lto = "fat" 35 | codegen-units = 1 36 | panic = "abort" 37 | debug = true 38 | 39 | [features] 40 | default = ["llvm"] 41 | llvm = ["inkwell"] 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Jake Fecher 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ante 2 | 3 | [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fjfecher%2Fante%2Fbadge&style=flat)](https://actions-badge.atrox.dev/jfecher/ante/goto) 4 | 5 | --- 6 | 7 | Ante is a low-level functional language for exploring refinement types, lifetime inference, and 8 | other fun features. Here's a quick taste: 9 | 10 | ```scala 11 | // No lifetime variables 12 | foo (x: !shared Bar) (y: &a) : a can Fail given Clone a = 13 | // Algebraic effects 14 | if not valid x then fail () 15 | 16 | // Safe, aliasable mutable references 17 | baz x x 18 | 19 | // Traits 20 | clone y 21 | ``` 22 | 23 | In general, ante is low-level (no GC, values aren't boxed by default) while also trying to 24 | be as readable as possible by encouraging high-level approaches that can be optimized with 25 | low-level details later on. 26 | 27 | See the [website](https://antelang.org), [language tour](https://antelang.org/docs/language/), 28 | and [roadmap](https://antelang.org/docs/roadmap) for more information. 29 | 30 | --- 31 | 32 | ### Contributing 33 | 34 | The compiler is still in a rather early state so any contributors are greatly welcome. 35 | Feel free to contribute to either any known issues/improvements or any standard library 36 | additions you think may be useful. 37 | 38 | Each file in the codebase is prefixed with a module comment explaining the purpose of 39 | the file and any algorithms used. `src/main.rs` is a good place to start reading. 40 | 41 | Make sure any PRs pass the tests in the `examples` directory. These tests have commands 42 | in them which the [goldentests](https://github.com/jfecher/golden-tests) library uses 43 | to run the ante compiler and check its output for each file against the expected output 44 | contained within comments of that file. 45 | 46 | [**Good first issues**](https://github.com/jfecher/ante/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) 47 | to contribute to 48 | 49 | --- 50 | 51 | ### Community 52 | 53 | The best place to follow ante's development is in the official discord: https://discord.gg/BN97fKnEH2. 54 | There is also the mostly inactive subreddit at [/r/ante](https://reddit.com/r/ante) which is mainly used for questions about the language rather 55 | than development updates. You can also feel free to file issues or ask questions on this repository. 56 | 57 | --- 58 | 59 | ### Building 60 | 61 | Ante currently optionally requires llvm 18.0 while building. If you already have this installed with 62 | sources, you may be fine building with `cargo install --path .` alone. If cargo complains 63 | about not finding any suitable llvm version, you can either choose to build ante without 64 | the llvm backend via `cargo install --path . --no-default-features` or you can build llvm from 65 | source via [CMake](#CMake) as covered in the next sections. 66 | 67 | #### Linux and Mac 68 | 69 | The easiest method to install llvm 18.0 would be through your package manager, making sure to install any `-dev` packages 70 | if they are available for your distro. Once installed, if `cargo b` still cannot find the right version of llvm, you may 71 | need to make sure to set the `LLVM_SYS_180_PREFIX` to the path llvm was installed to: 72 | 73 | ```bash 74 | $ LLVM_SYS_180_PREFIX=$(llvm-config --obj-root) 75 | ``` 76 | 77 | If your distro ships a version other than llvm 18.0 you can try changing the inkwell dependency Ante's Cargo.toml. 78 | This dependency controls the llvm version expected and by default it is: 79 | 80 | ```toml 81 | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm18-0"], optional = true } 82 | ``` 83 | 84 | Change the quoted llvm portion to `"llvm-16-0"` for example to build with llvm 16.0. Also don't forget that after changing 85 | this version the environment variable's name will be different, using llvm 16.0 for example it would be `LLVM_SYS_160_PREFIX`. 86 | It is likely that versions older than this will not work since there have been API changes in LLVM itself and inkwell. 15.0 itself 87 | is also unverified. 88 | 89 | If this method does not work you will have to try building llvm from source via cmake. See the [CMake](#CMake) section below. 90 | Alternatively, you can build with only cranelift as a backend via `cargo install --path . --no-default-features`. 91 | 92 | ##### Nix 93 | 94 | Ante is available in the unstable branch of the [nixpkgs repository](https://search.nixos.org/packages?channel=unstable&show=ante&type=packages&query=ante). 95 | 96 | The project itself provides build instructions for the [Nix package manager](https://nixos.org/). 97 | Those can be used for the most recent version of the compiler, or for working on it. 98 | 99 | To enter the development environment, run either `nix-shell` or `nix develop` depending on whether you are using nix 100 | with [flakes](https://wiki.nixos.org/wiki/Flakes) and [nix command](https://wiki.nixos.org/wiki/Nix_command) enabled or not. 101 | Then you can build and run the project with `cargo` as described at the top of this section. 102 | 103 | Beyond that, the project will also build with `nix-build` / `nix build`, meaning you can install it on your system using 104 | the provided overlay or play around with the compiler via `nix shell github:jfecher/ante`. 105 | 106 | #### Windows 107 | 108 | Note: LLVM is notoriously difficult to build on windows. If you're a windows user who has tried 109 | the following and still cannot build llvm, I highly recommend trying out ante without the llvm 110 | backend via `cargo install --path . --no-default-features`. Since the llvm binaries do not ship 111 | with the appropriate library files on windows, you will have to build from source via [CMake](#CMake) 112 | 113 | ##### CMake 114 | 115 | If the above steps don't work for you, you can try [building llvm from source 116 | with cmake](https://www.llvm.org/docs/CMake.html). If you're on windows, this 117 | requires you to have Visual Studio 2017 or later installed already. 118 | 119 | ``` 120 | $ git clone https://github.com/llvm/llvm-project --branch=release/16.x 121 | $ mkdir llvm-build 122 | $ cd llvm-build 123 | $ cmake ../llvm-project/llvm 124 | ``` 125 | 126 | At this point, cmake may show an error that it failed to find z3 or the windows SDK, in 127 | which case you may need to install them. For the windows SDK, you can install it 128 | via the Visual Studio Installer (under **Modify -> Individual Components**). I used 129 | version 10.0.17763.0, though it is likely newer versions will work as well. Rerun 130 | the last cmake command to test that everything is installed right. Once this is 131 | done, move on to compiling llvm and ante: 132 | 133 | ``` 134 | $ cmake --build . 135 | $ cmake --build . --target install 136 | $ cd .. 137 | $ set LLVM_SYS_160_PREFIX=/absolute/path/to/llvm-build 138 | $ cargo build 139 | ``` 140 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - Kind checking! 2 | - Locations should be stored in a `types::traits::Impl` for better error messages for trait errors 3 | - Audit uses of `typechecker::unify` to maybe specialize them to improve error messages for type errors 4 | - Improve parser error messages 5 | - Improve type checker error messages 6 | - 'type mismatch' should be more specific 7 | - wrong number of parameters used points to definition rather than callsite or both 8 | - Declare-before use for impls 9 | - Support variadic functions in cranelift backend. Goal: support `extern printf: (ref char) ... -> int` 10 | -------------------------------------------------------------------------------- /ante-ls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ante-ls" 3 | version = "0.1.1" 4 | edition = "2021" 5 | rust-version = "1.81" 6 | 7 | [dependencies] 8 | ante = { path = "..", default-features = false } 9 | dashmap = "5.5.3" 10 | env_logger = "0.10.2" 11 | futures = "0.3.30" 12 | im-rc = "15.1.0" 13 | log = "0.4.20" 14 | ropey = "1.6.1" 15 | serde = { version = "1.0.195", features = ["derive"] } 16 | serde_json = "1.0.111" 17 | tokio = { version = "1.35.1", features = ["full"] } 18 | tower-lsp = "0.20.0" 19 | -------------------------------------------------------------------------------- /ante-ls/src/util.rs: -------------------------------------------------------------------------------- 1 | use ante::{error::location::Locatable, parser::ast::Ast}; 2 | use ropey::Rope; 3 | use tower_lsp::lsp_types::*; 4 | 5 | pub fn node_at_index<'a>(ast: &'a Ast<'a>, idx: usize) -> &'a Ast<'a> { 6 | let mut ast = ast; 7 | loop { 8 | match ast { 9 | Ast::Assignment(a) => { 10 | if a.lhs.locate().contains_index(&idx) { 11 | ast = &a.lhs; 12 | } else if a.rhs.locate().contains_index(&idx) { 13 | ast = &a.rhs; 14 | } else { 15 | break; 16 | } 17 | }, 18 | Ast::Definition(d) => { 19 | if d.pattern.locate().contains_index(&idx) { 20 | ast = &d.pattern; 21 | } else if d.expr.locate().contains_index(&idx) { 22 | ast = &d.expr; 23 | } else { 24 | break; 25 | } 26 | }, 27 | Ast::EffectDefinition(_) => { 28 | break; 29 | }, 30 | Ast::Extern(_) => { 31 | break; 32 | }, 33 | Ast::FunctionCall(f) => { 34 | if let Some(arg) = f.args.iter().find(|&arg| arg.locate().contains_index(&idx)) { 35 | ast = arg; 36 | } else if f.function.locate().contains_index(&idx) { 37 | ast = &f.function; 38 | } else { 39 | break; 40 | } 41 | }, 42 | Ast::Handle(h) => { 43 | if let Some(branch) = h.branches.iter().find_map(|(pat, body)| { 44 | if pat.locate().contains_index(&idx) { 45 | return Some(pat); 46 | }; 47 | if body.locate().contains_index(&idx) { 48 | return Some(body); 49 | }; 50 | None 51 | }) { 52 | ast = branch; 53 | } else if h.expression.locate().contains_index(&idx) { 54 | ast = &h.expression; 55 | } else { 56 | break; 57 | } 58 | }, 59 | Ast::If(i) => { 60 | if i.condition.locate().contains_index(&idx) { 61 | ast = &i.condition; 62 | } else if i.then.locate().contains_index(&idx) { 63 | ast = &i.then; 64 | } else if i.otherwise.locate().contains_index(&idx) { 65 | ast = &i.otherwise; 66 | } else { 67 | break; 68 | } 69 | }, 70 | Ast::Import(_) => { 71 | break; 72 | }, 73 | Ast::Lambda(l) => { 74 | if let Some(arg) = l.args.iter().find(|&arg| arg.locate().contains_index(&idx)) { 75 | ast = arg; 76 | } else if l.body.locate().contains_index(&idx) { 77 | ast = &l.body; 78 | } else { 79 | break; 80 | } 81 | }, 82 | Ast::Literal(_) => { 83 | break; 84 | }, 85 | Ast::Match(m) => { 86 | if let Some(branch) = m.branches.iter().find_map(|(pat, body)| { 87 | if pat.locate().contains_index(&idx) { 88 | return Some(pat); 89 | }; 90 | if body.locate().contains_index(&idx) { 91 | return Some(body); 92 | }; 93 | None 94 | }) { 95 | ast = branch; 96 | } else { 97 | break; 98 | } 99 | }, 100 | Ast::MemberAccess(m) => { 101 | if m.lhs.locate().contains_index(&idx) { 102 | ast = &m.lhs; 103 | } else { 104 | break; 105 | } 106 | }, 107 | Ast::NamedConstructor(n) => { 108 | let sequence = match n.sequence.as_ref() { 109 | Ast::Sequence(s) => Some(s), 110 | _ => None, 111 | }; 112 | 113 | if let Some(stmt) = 114 | sequence.and_then(|s| s.statements.iter().find(|stmt| stmt.locate().contains_index(&idx))) 115 | { 116 | ast = stmt; 117 | } else if n.constructor.locate().contains_index(&idx) { 118 | ast = &n.constructor; 119 | } else { 120 | break; 121 | } 122 | }, 123 | Ast::Return(r) => { 124 | if r.expression.locate().contains_index(&idx) { 125 | ast = &r.expression; 126 | } else { 127 | break; 128 | } 129 | }, 130 | Ast::Sequence(s) => { 131 | if let Some(stmt) = s.statements.iter().find(|&stmt| stmt.locate().contains_index(&idx)) { 132 | ast = stmt; 133 | } else { 134 | break; 135 | } 136 | }, 137 | Ast::TraitDefinition(_) => { 138 | break; 139 | }, 140 | Ast::TraitImpl(t) => { 141 | if let Some(def) = t.definitions.iter().find_map(|def| { 142 | if def.pattern.locate().contains_index(&idx) { 143 | return Some(&def.pattern); 144 | }; 145 | if def.expr.locate().contains_index(&idx) { 146 | return Some(&def.expr); 147 | }; 148 | None 149 | }) { 150 | ast = def; 151 | } else { 152 | break; 153 | } 154 | }, 155 | Ast::TypeAnnotation(t) => { 156 | if t.lhs.locate().contains_index(&idx) { 157 | ast = &t.lhs; 158 | } else { 159 | break; 160 | } 161 | }, 162 | Ast::TypeDefinition(_) => { 163 | break; 164 | }, 165 | Ast::Variable(_) => { 166 | break; 167 | }, 168 | Ast::Reference(reference) => { 169 | if reference.expression.locate().contains_index(&idx) { 170 | ast = &reference.expression; 171 | } else { 172 | break; 173 | } 174 | }, 175 | } 176 | } 177 | ast 178 | } 179 | 180 | pub fn position_to_index(position: Position, rope: &Rope) -> Result { 181 | let line = position.line as usize; 182 | let line = rope.try_line_to_char(line)?; 183 | Ok(line + position.character as usize) 184 | } 185 | 186 | pub fn index_to_position(index: usize, rope: &Rope) -> Result { 187 | let line = rope.try_char_to_line(index)?; 188 | let char = index - rope.line_to_char(line); 189 | Ok(Position { line: line as u32, character: char as u32 }) 190 | } 191 | 192 | pub fn lsp_range_to_rope_range(range: Range, rope: &Rope) -> Result, ropey::Error> { 193 | let start = position_to_index(range.start, rope)?; 194 | let end = position_to_index(range.end, rope)?; 195 | Ok(start..end) 196 | } 197 | 198 | pub fn rope_range_to_lsp_range(range: std::ops::Range, rope: &Rope) -> Result { 199 | let start = index_to_position(range.start, rope)?; 200 | let end = index_to_position(range.end, rope)?; 201 | Ok(Range { start, end }) 202 | } 203 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() -> std::io::Result<()> { 4 | if option_env!("ANTE_STDLIB_DIR").is_none() { 5 | let cur_dir = env::current_dir()?; 6 | println!("cargo:rustc-env=ANTE_STDLIB_DIR={}/stdlib", cur_dir.to_str().unwrap()); 7 | } 8 | 9 | if option_env!("ANTE_MINICORO_PATH").is_none() { 10 | let cur_dir = env::current_dir()?; 11 | println!("cargo:rustc-env=ANTE_MINICORO_PATH={}/aminicoro/minicoro.c", cur_dir.to_str().unwrap()); 12 | } 13 | 14 | Ok(()) 15 | } 16 | -------------------------------------------------------------------------------- /crates.nix: -------------------------------------------------------------------------------- 1 | { 2 | perSystem = { pkgs, lib, config, ... }: { 3 | nci = 4 | let 5 | llvmPackages = pkgs.llvmPackages_16; 6 | major = lib.versions.major llvmPackages.llvm.version; 7 | minor = lib.versions.minor llvmPackages.llvm.version; 8 | llvm-sys-ver = "${major}${builtins.substring 0 1 minor}"; 9 | env = { "LLVM_SYS_${llvm-sys-ver}_PREFIX" = llvmPackages.llvm.dev; }; 10 | 11 | stdlib = pkgs.stdenv.mkDerivation { 12 | pname = "ante-stdlib"; 13 | version = config.packages.ante.version; 14 | src = ./stdlib; 15 | phases = [ "unpackPhase" "installPhase" ]; 16 | installPhase = '' 17 | find . -type f -exec install -Dm644 "{}" -t $out/lib \; 18 | ''; 19 | }; 20 | in 21 | { 22 | toolchainConfig = { 23 | channel = "stable"; 24 | components = [ "rust-analyzer" "clippy" "rustfmt" "rust-src" ]; 25 | }; 26 | 27 | projects.ante = { 28 | export = false; 29 | path = ./.; 30 | }; 31 | 32 | crates = { 33 | ante-ls = { 34 | profiles.release.features = [ ]; 35 | drvConfig.mkDerivation = { 36 | preBuild = '' 37 | export ANTE_STDLIB_DIR=${stdlib}/lib 38 | ''; 39 | }; 40 | }; 41 | 42 | ante = { 43 | depsDrvConfig = { 44 | inherit env; 45 | }; 46 | drvConfig = { 47 | inherit env; 48 | mkDerivation = { 49 | nativeBuildInputs = [ pkgs.installShellFiles ]; 50 | buildInputs = lib.attrValues 51 | { 52 | inherit (pkgs) 53 | libffi 54 | libxml2 55 | ncurses; 56 | } ++ [ llvmPackages.llvm stdlib ]; 57 | 58 | postPatch = '' 59 | substituteInPlace tests/golden_tests.rs --replace \ 60 | 'target/debug' "target/$(rustc -vV | sed -n 's|host: ||p')/release" 61 | ''; 62 | 63 | preBuild = '' 64 | export ANTE_STDLIB_DIR=${stdlib}/lib 65 | ''; 66 | 67 | postInstall = '' 68 | installShellCompletion --cmd ante \ 69 | --bash <($out/bin/ante --shell-completion bash) \ 70 | --fish <($out/bin/ante --shell-completion fish) \ 71 | --zsh <($out/bin/ante --shell-completion zsh) 72 | ''; 73 | }; 74 | }; 75 | }; 76 | }; 77 | }; 78 | }; 79 | } 80 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | (import 2 | ( 3 | let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in 4 | fetchTarball { 5 | url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; 6 | sha256 = lock.nodes.flake-compat.locked.narHash; 7 | } 8 | ) 9 | { src = ./.; } 10 | ).defaultNix 11 | -------------------------------------------------------------------------------- /examples/codegen/bitwise.an: -------------------------------------------------------------------------------- 1 | 2 | x = 3 3 | y = 5 4 | 5 | print (band x y) 6 | print (bor x y) 7 | print (bxor x y) 8 | print (bnot x) 9 | 10 | // args: --delete-binary 11 | // expected stdout: 12 | // 1 13 | // 7 14 | // 6 15 | // -4 16 | -------------------------------------------------------------------------------- /examples/codegen/builtin_float.an: -------------------------------------------------------------------------------- 1 | 2 | ////////// F64 //////// 3 | 4 | add_f64 = 3.0 + 4.0 5 | sub_f64 = 7.0 - 8.0 6 | mul_f64 = 11.0 * 12.0 7 | div_f64 = 15.0 / 16.0 8 | // mod_float = 10.0 % 3.0 9 | 10 | print add_f64 11 | print sub_f64 12 | print mul_f64 13 | print div_f64 14 | // print mod_float 15 | 16 | ////////// F32 //////// 17 | add_f32 = 3.0f + 4.0f 18 | sub_f32 = 7.0f - 8.0f 19 | mul_f32 = 11.0f * 12.0f 20 | div_f32 = 15.0f / 16.0f 21 | print add_f32 22 | print sub_f32 23 | print mul_f32 24 | print div_f32 25 | 26 | // F32 -> F64 -> F32 roundtrip cast should preserve the original value 27 | x = 1.0f / 7.0f 28 | print x 29 | print (cast (cast x : F64) : F32) 30 | 31 | // args: --delete-binary 32 | // expected stdout: 33 | // 7.0 34 | // -1.0 35 | // 132.0 36 | // 0.9375 37 | // 7.0 38 | // -1.0 39 | // 132.0 40 | // 0.9375 41 | // 0.1428 42 | // 0.1428 43 | 44 | // We used to check 1.0 for print mod_float but 45 | // cranelift does not support modulus for floats 46 | // so we can no longer support this operation at all. 47 | -------------------------------------------------------------------------------- /examples/codegen/builtin_int.an: -------------------------------------------------------------------------------- 1 | 2 | test_int x = 3 | print (x + x) 4 | print (x - x - x) 5 | print (x * x) 6 | print (x / x) 7 | print (x % x) 8 | 9 | test_int 3_i8 10 | test_int 3_u8 11 | 12 | test_int 3_i16 13 | test_int 3_u16 14 | 15 | test_int 3_i32 16 | test_int 3_u32 17 | 18 | test_int 3_i64 19 | test_int 3_u64 20 | 21 | test_int 3_isz 22 | test_int 3_usz 23 | 24 | // args: --delete-binary 25 | // expected stdout: 26 | // 6 27 | // -3 28 | // 9 29 | // 1 30 | // 0 31 | // 6 32 | // 253 33 | // 9 34 | // 1 35 | // 0 36 | // 6 37 | // -3 38 | // 9 39 | // 1 40 | // 0 41 | // 6 42 | // 65533 43 | // 9 44 | // 1 45 | // 0 46 | // 6 47 | // -3 48 | // 9 49 | // 1 50 | // 0 51 | // 6 52 | // 4294967293 53 | // 9 54 | // 1 55 | // 0 56 | // 6 57 | // -3 58 | // 9 59 | // 1 60 | // 0 61 | // 6 62 | // 18446744073709551613 63 | // 9 64 | // 1 65 | // 0 66 | // 6 67 | // -3 68 | // 9 69 | // 1 70 | // 0 71 | // 6 72 | // 18446744073709551613 73 | // 9 74 | // 1 75 | // 0 76 | -------------------------------------------------------------------------------- /examples/codegen/closure_basic.an: -------------------------------------------------------------------------------- 1 | main () = 2 | foo = "hi there" 3 | stuff () = print foo 4 | stuff () 5 | 6 | main () 7 | 8 | // args: --delete-binary 9 | // expected stdout: 10 | // hi there 11 | -------------------------------------------------------------------------------- /examples/codegen/closure_nested.an: -------------------------------------------------------------------------------- 1 | foo () = 2 | x = "test" 3 | bar () = 4 | baz () = x 5 | baz () 6 | 7 | bar () 8 | 9 | print (foo ()) 10 | 11 | // args: --delete-binary 12 | // expected stdout: test 13 | -------------------------------------------------------------------------------- /examples/codegen/closure_return.an: -------------------------------------------------------------------------------- 1 | get_closure () = 2 | foo = "hi there" 3 | stuff () = print foo 4 | stuff 5 | 6 | foo = get_closure () 7 | foo () 8 | 9 | // args: --delete-binary 10 | // expected stdout: 11 | // hi there 12 | -------------------------------------------------------------------------------- /examples/codegen/data.an: -------------------------------------------------------------------------------- 1 | type T = a: I32, b: String 2 | 3 | t = T 1 "one" 4 | printne "t = " 5 | printne (t.a) 6 | printne ", " 7 | 8 | puts t.b.c_string 9 | 10 | s = Some 2 11 | 12 | match s 13 | | Some x -> 14 | printne "s = " 15 | print x 16 | | None -> print "s = None" 17 | 18 | // args: --delete-binary 19 | // expected stdout: 20 | // t = 1, one 21 | // s = 2 22 | -------------------------------------------------------------------------------- /examples/codegen/effects/add_effect.an: -------------------------------------------------------------------------------- 1 | effect Add with 2 | add: U32 -> Unit 3 | 4 | foo () can Add = 5 | print "foo" 6 | add 1 7 | add 2 8 | 9 | bar () = 10 | print "bar" 11 | add 3 12 | add 4 13 | 14 | sum_all () = 15 | handle 16 | foo () 17 | bar () 18 | 0 19 | | add y -> 20 | print "add called" 21 | r = resume () 22 | print "add resume finished" 23 | y + r 24 | 25 | // 10 26 | sum_all () |> print 27 | 28 | // args: --delete-binary 29 | // expected stdout: 30 | // foo 31 | // add called 32 | // add called 33 | // bar 34 | // add called 35 | // add called 36 | // add resume finished 37 | // add resume finished 38 | // add resume finished 39 | // add resume finished 40 | // 10 41 | -------------------------------------------------------------------------------- /examples/codegen/effects/count_effect.an: -------------------------------------------------------------------------------- 1 | effect Count with 2 | count: Unit -> Unit 3 | 4 | foo () = 5 | count () 6 | repeat 10 fn _ -> count () 7 | 8 | handle_count f = 9 | handle 10 | f () 11 | 0 12 | | count () -> 1 + resume () 13 | 14 | handle_count foo |> print 15 | 16 | // args: --delete-binary 17 | // expected stdout: 11 18 | -------------------------------------------------------------------------------- /examples/codegen/effects/generators_basic.an: -------------------------------------------------------------------------------- 1 | effect Emit a with 2 | emit: a -> Unit 3 | 4 | iota (n: U32) = loop (i = 0) -> 5 | if i < n then 6 | emit i 7 | recur (i + 1) 8 | 9 | for stream f = 10 | handle stream () 11 | | emit x -> 12 | f x 13 | resume () 14 | 15 | for (fn () -> iota 10) print 16 | 17 | // args: --delete-binary 18 | // expected stdout: 19 | // 0 20 | // 1 21 | // 2 22 | // 3 23 | // 4 24 | // 5 25 | // 6 26 | // 7 27 | // 8 28 | // 9 29 | -------------------------------------------------------------------------------- /examples/codegen/effects/generators_complex.an: -------------------------------------------------------------------------------- 1 | effect Emit a with 2 | emit: a -> Unit 3 | 4 | iota (n: U32) = loop (i = 0) -> 5 | if i < n then 6 | emit i 7 | recur (i + 1) 8 | 9 | filter (stream: Unit -> Unit can Emit a) (f: a -> Bool pure) = 10 | handle stream () 11 | | emit x -> 12 | if f x then 13 | emit x 14 | resume () 15 | 16 | map (stream: Unit -> Unit can Emit a) (f: a -> b pure): Unit can Emit b = 17 | handle stream () 18 | | emit x -> 19 | emit (f x) 20 | resume () 21 | 22 | enumerate (stream: Unit -> Unit can Emit a): Unit can Emit (U32, a) = 23 | // Bug: can't mutably capture i in handle branch if it is declared as `mut i` 24 | i = !0u32 25 | handle stream () 26 | | emit x -> 27 | emit (i, x) 28 | i := @i + 1 29 | resume () 30 | 31 | for (stream: Unit -> Unit can Emit a) (f: a -> Unit pure) = 32 | handle stream () 33 | | emit x -> 34 | f x 35 | resume () 36 | 37 | even x = x % 2 == 0 38 | 39 | my_print (x1: !U32, U32) = 40 | x = @first x1 41 | y = second x1 42 | print "${x}, ${y}" 43 | 44 | iota 10 45 | with filter even 46 | with map (_ * 2u32) 47 | with enumerate 48 | with for my_print 49 | 50 | // args: --delete-binary 51 | // expected stdout: 52 | // 0, 0 53 | // 1, 4 54 | // 2, 8 55 | // 3, 12 56 | // 4, 16 57 | -------------------------------------------------------------------------------- /examples/codegen/effects/handle_multiple_effects.an: -------------------------------------------------------------------------------- 1 | 2 | effect Fail with 3 | fail: Unit -> a 4 | 5 | effect Read a with 6 | read: Unit -> a 7 | 8 | read_or_zero f = 9 | handle Some <| f () 10 | | fail () -> None 11 | | read () -> resume 5u32 12 | 13 | foo () = 14 | if false then fail () 15 | read () 16 | 17 | print <| read_or_zero foo 18 | 19 | // args: --delete-binary 20 | // expected stdout: 5 21 | -------------------------------------------------------------------------------- /examples/codegen/effects/state.an: -------------------------------------------------------------------------------- 1 | effect Use a with 2 | get: Unit -> a 3 | set: a -> Unit 4 | 5 | use_state (y: Int a) = 6 | x = get () : Int a 7 | set (x + y) 8 | state = get () : Int a 9 | print "Ending state = ${state}" 10 | state 11 | 12 | state (f: Unit -> a can Use s) (initial: s): a = 13 | mut state0 = initial 14 | mut state = !state0 15 | handle f () 16 | | get () -> resume @state 17 | | set new_state -> 18 | // Set is inferred incorrectly without this type hint 19 | state := new_state : s 20 | resume () 21 | 22 | use_state 10_i32 with state 1 23 | use_state 20_u64 with state 2 24 | 25 | // args: --delete-binary 26 | // expected stdout: 27 | // Ending state = 11 28 | // Ending state = 22 29 | -------------------------------------------------------------------------------- /examples/codegen/explicit_curry.an: -------------------------------------------------------------------------------- 1 | 2 | foo a b c = 3 | a + b + c 4 | 5 | plus2 = foo _ 2 _ 6 | plus4 = foo 2 2 _ 7 | 8 | print <| plus4 2 9 | print <| plus2 38 2 10 | 11 | 4 |> (_ + 2) |> print 12 | 13 | // args: --delete-binary 14 | // expected stdout: 15 | // 6 16 | // 42 17 | // 6 18 | -------------------------------------------------------------------------------- /examples/codegen/fib.an: -------------------------------------------------------------------------------- 1 | fib n = 2 | if n < 2 then n 3 | else fib (n - 2) + fib (n - 1) 4 | 5 | print (fib 10) 6 | 7 | // args: --delete-binary 8 | // expected stdout: 55 9 | -------------------------------------------------------------------------------- /examples/codegen/function_call.an: -------------------------------------------------------------------------------- 1 | show x = puts x.c_string 2 | 3 | show "foo" 4 | 5 | // args: --delete-binary 6 | // expected stdout: foo 7 | -------------------------------------------------------------------------------- /examples/codegen/hello.an: -------------------------------------------------------------------------------- 1 | puts "Hello, World!".c_string 2 | 3 | // args: --delete-binary 4 | // expected stdout: Hello, World! 5 | -------------------------------------------------------------------------------- /examples/codegen/implicit_import.an: -------------------------------------------------------------------------------- 1 | mut v = Vec.empty () 2 | 3 | Vec.push !v 3i32 4 | Vec.push !v 4 5 | 6 | print v 7 | 8 | // args: --delete-binary 9 | // expected stdout: [3, 4] 10 | -------------------------------------------------------------------------------- /examples/codegen/int_string_cast.an: -------------------------------------------------------------------------------- 1 | debug str = 2 | print <| 3 | "string: " ++ 4 | str ++ 5 | " len: " ++ 6 | cast str.length 7 | 8 | debug <| cast (255: U8) 9 | 10 | debug <| cast -1024 11 | 12 | debug <| cast 0 13 | 14 | debug <| cast 10 15 | 16 | // args: --delete-binary 17 | // expected stdout: 18 | // string: 255 len: 3 19 | // string: -1024 len: 5 20 | // string: 0 len: 1 21 | // string: 10 len: 2 22 | -------------------------------------------------------------------------------- /examples/codegen/iter.an: -------------------------------------------------------------------------------- 1 | 2 | trait MyIterable it -> elem with 3 | mynext : it -> Maybe (elem, it) 4 | 5 | // Define iterating on I32s as counting down until 0 6 | impl MyIterable I32 I32 with 7 | mynext x = 8 | if x <= 0 then None 9 | else Some (x, x - 1) 10 | 11 | my_iter iterable f = 12 | match mynext iterable 13 | | None -> () 14 | | Some (elem, rest) -> 15 | f elem 16 | my_iter rest f 17 | 18 | my_iter 10 print 19 | 20 | // args: --delete-binary 21 | // expected stdout: 22 | // 10 23 | // 9 24 | // 8 25 | // 7 26 | // 6 27 | // 5 28 | // 4 29 | // 3 30 | // 2 31 | // 1 32 | -------------------------------------------------------------------------------- /examples/codegen/logical.an: -------------------------------------------------------------------------------- 1 | 2 | print (true and true or false and false) 3 | 4 | // args: --delete-binary 5 | // expected stdout: true 6 | -------------------------------------------------------------------------------- /examples/codegen/loop.an: -------------------------------------------------------------------------------- 1 | start = 10 2 | seq_sum = loop start (sum = 0) -> 3 | if start == 0 then sum 4 | else recur (start - 1) (sum + start) 5 | 6 | print seq_sum 7 | 8 | loop (collatz = 7) -> 9 | if collatz == 1 then 10 | print "" 11 | return () 12 | printne collatz 13 | printne " " 14 | if collatz % 2 == 0 then recur (collatz / 2) 15 | else recur (collatz * 3 + 1) 16 | 17 | s = 0 18 | loop (s: I32) (t: I32 = 0) -> 19 | printne s 20 | printne t 21 | if s == 3 then return () 22 | else recur (s + 1) (t + 1) 23 | 24 | // args: --delete-binary 25 | // expected stdout: 26 | // 55 27 | // 7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 28 | // 00112233 29 | -------------------------------------------------------------------------------- /examples/codegen/map.an: -------------------------------------------------------------------------------- 1 | import HashMap 2 | 3 | impl Hash U8 with 4 | hash x = cast x 5 | 6 | mut map = empty () 7 | 8 | // Test several inserts. HashMap is linear-scan so it 9 | // should keep resizing so that it is at least half-empty 10 | // (ie. its capacity should be >= 2 * its length). 11 | insert !map 1u8 3u16 12 | insert !map 2 6 13 | insert !map 3 9 14 | insert !map 4 12 15 | insert !map 5 15 16 | insert !map 6 18 17 | insert !map 7 21 18 | 19 | print map 20 | 21 | // args: --delete-binary 22 | // expected stdout: 23 | // [1 -> 3, 2 -> 6, 3 -> 9, 4 -> 12, 5 -> 15, 6 -> 18, 7 -> 21] 24 | -------------------------------------------------------------------------------- /examples/codegen/monomorphise_all.an: -------------------------------------------------------------------------------- 1 | 2 | foo x = x 3 | 4 | bar x = foo x 5 | 6 | baz x = bar x 7 | 8 | baz "hi" 9 | 10 | // args: --delete-binary 11 | // expected stdout: 12 | -------------------------------------------------------------------------------- /examples/codegen/mutability.an: -------------------------------------------------------------------------------- 1 | mut num = 32 2 | print num 3 | num := 7 4 | print num 5 | 6 | mutate !num 7 | print num 8 | 9 | mutate (n: !I32) = 10 | x = double @n 11 | n := x 12 | 13 | double x = x * 2 14 | 15 | // args: --delete-binary 16 | // expected stdout: 17 | // 32 18 | // 7 19 | // 14 20 | -------------------------------------------------------------------------------- /examples/codegen/pass_by_ref.an: -------------------------------------------------------------------------------- 1 | type S = message: String 2 | 3 | impl Print S with 4 | printne s = printne s.message 5 | 6 | // If the compiler decides to pass by reference, the code becomes: 7 | // void foo(const S *x, S *y) { 8 | // ... 9 | // } 10 | // which can mutate x when x == y if not careful 11 | foo (x: S) (y: S) = 12 | printne "x before: " 13 | print x 14 | mut z = y 15 | z.!message := "modifying y" 16 | printne "x after: " 17 | print x 18 | 19 | mut x = S "original x string" 20 | foo x x 21 | 22 | // args: --delete-binary 23 | // expected stdout: 24 | // x before: original x string 25 | // x after: original x string 26 | -------------------------------------------------------------------------------- /examples/codegen/string_append.an: -------------------------------------------------------------------------------- 1 | a = "你好" 2 | print (a ++ ",世界") 3 | 4 | // args: --delete-binary 5 | // expected stdout: 你好,世界 -------------------------------------------------------------------------------- /examples/codegen/string_builder.an: -------------------------------------------------------------------------------- 1 | import StringBuilder 2 | 3 | mut sb: StringBuilder = empty () 4 | reserve !sb 10 5 | append !sb "你好," 6 | append !sb " World!" 7 | 8 | print (to_string sb) 9 | 10 | // args: --delete-binary 11 | // expected stdout: 你好, World! 12 | -------------------------------------------------------------------------------- /examples/codegen/tuples.an: -------------------------------------------------------------------------------- 1 | 2 | foo (a, b, _) = 3 | _: string = a 4 | print b 5 | 6 | foo ("test", 2, 'c') 7 | 8 | foo ("hi", 7, 2.0, 3, 4, 5) 9 | 10 | // args: --delete-binary 11 | // expected stdout: 12 | // 2 13 | // 7 14 | -------------------------------------------------------------------------------- /examples/codegen/vec_basic.an: -------------------------------------------------------------------------------- 1 | import Vec.empty push 2 | 3 | mut v = empty () 4 | 5 | push !v 3i32 6 | push !v 4 7 | 8 | print v 9 | 10 | // args: --delete-binary 11 | // expected stdout: [3, 4] 12 | -------------------------------------------------------------------------------- /examples/nameresolution/Library.an: -------------------------------------------------------------------------------- 1 | 2 | library_int = 5 3 | 4 | library_fn _ = 2 5 | 6 | // args: --check 7 | // expected stdout: 8 | -------------------------------------------------------------------------------- /examples/nameresolution/T.an: -------------------------------------------------------------------------------- 1 | import U 2 | 3 | // Recursive module imports are allowed! 4 | t x = u x 5 | 6 | // args: --check 7 | // expected stdout: 8 | -------------------------------------------------------------------------------- /examples/nameresolution/Trait.an: -------------------------------------------------------------------------------- 1 | trait Foo a b with 2 | foo: a -> b 3 | bar: a 4 | 5 | impl Foo I32 String with 6 | foo _x = "test" 7 | baz = 2 // error: baz not in foo 8 | // error: missing definition of foo 9 | 10 | foo 3 11 | bar : Unit 12 | 13 | // args: --check 14 | // expected stderr: 15 | // Trait.an:7:5 error: baz is not required by Foo 16 | // baz = 2 // error: baz not in foo 17 | // 18 | // Trait.an:5:1 error: impl is missing a definition for bar 19 | // impl Foo I32 String with 20 | -------------------------------------------------------------------------------- /examples/nameresolution/U.an: -------------------------------------------------------------------------------- 1 | import T 2 | 3 | // Module imports can form a cycle! 4 | u x = t x 5 | 6 | // args: --check 7 | // expected stdout: 8 | -------------------------------------------------------------------------------- /examples/nameresolution/conflictingimport.an: -------------------------------------------------------------------------------- 1 | import Library 2 | 3 | library_int = 1 4 | 5 | library_fn _ = 3 6 | 7 | // args: --check 8 | // expected stderr: 9 | // conflictingimport.an:1:1 error: import shadows previous definition of library_fn 10 | // import Library 11 | // 12 | // conflictingimport.an:5:1 note: library_fn was previously defined here 13 | // library_fn _ = 3 14 | // 15 | // conflictingimport.an:1:1 error: import shadows previous definition of library_int 16 | // import Library 17 | // 18 | // conflictingimport.an:3:1 note: library_int was previously defined here 19 | // library_int = 1 20 | -------------------------------------------------------------------------------- /examples/nameresolution/effects.an: -------------------------------------------------------------------------------- 1 | 2 | effect Three with 3 | one: Unit -> Unit 4 | two: I32 -> I32 5 | three: I32 - I32 -> I32 6 | 7 | effect State a with 8 | get: Unit -> a 9 | put: a -> Unit 10 | 11 | 12 | handle () 13 | | one () -> () 14 | | one () -> () 15 | 16 | 17 | handle () 18 | | get () -> resume () 19 | | put () -> () 20 | 21 | handle () 22 | | put () -> () 23 | | three 0 1 -> () 24 | 25 | // args: --check 26 | // expected stderr: 27 | // effects.an:12:1 error: Handler is missing 2 cases: two, three 28 | // handle () 29 | // 30 | // effects.an:21:1 error: Handler is missing 3 cases: one, two, get 31 | // handle () 32 | -------------------------------------------------------------------------------- /examples/nameresolution/errors.an: -------------------------------------------------------------------------------- 1 | not_an_error = 1 2 | 3 | 4 | not_an_error is_an_error 5 | 6 | fn a b -> a + c + b 7 | 8 | foo a b = 9 | bar c d = 10 | b 11 | 12 | () 13 | 14 | a = 2 15 | a = 3 // already declared 16 | 17 | // args: --check 18 | // expected stderr: 19 | // errors.an:15:1 error: a is already in scope 20 | // a = 3 // already declared 21 | // 22 | // errors.an:14:1 note: a was previously defined here 23 | // a = 2 24 | // 25 | // errors.an:4:14 error: No declaration for `is_an_error` was found in scope 26 | // not_an_error is_an_error 27 | // 28 | // errors.an:6:15 error: No declaration for `c` was found in scope 29 | // fn a b -> a + c + b 30 | // 31 | // errors.an:9:9 warning: c is unused (prefix name with _ to silence this warning) 32 | // bar c d = 33 | // 34 | // errors.an:9:11 warning: d is unused (prefix name with _ to silence this warning) 35 | // bar c d = 36 | // 37 | // errors.an:8:5 warning: a is unused (prefix name with _ to silence this warning) 38 | // foo a b = 39 | // 40 | // errors.an:9:5 warning: bar is unused (prefix name with _ to silence this warning) 41 | // bar c d = 42 | -------------------------------------------------------------------------------- /examples/nameresolution/functions.an: -------------------------------------------------------------------------------- 1 | 2 | recursion n f = 3 | if n > 0 then 4 | f () 5 | recursion (n - 1) f 6 | 7 | type List a = | Nil | Cons a (List a) 8 | 9 | map l f = 10 | match l 11 | | Nil -> Nil 12 | | Cons x xs -> Cons (f x) (map xs f) 13 | 14 | // args: --check 15 | // expected stdout: 16 | -------------------------------------------------------------------------------- /examples/nameresolution/import.an: -------------------------------------------------------------------------------- 1 | import Library 2 | 3 | foo = fn _ b -> b 4 | 5 | foo 4 library_int 6 | foo 5 (library_fn ()) 7 | // args: --check 8 | // expected stdout: -------------------------------------------------------------------------------- /examples/nameresolution/named_constructor.an: -------------------------------------------------------------------------------- 1 | type T a b = 2 | x: a 3 | y: b 4 | 5 | x = 4 6 | 7 | t1 = T with x = 3, z = 5 8 | t2 = T with y = 3.2, x 9 | 10 | // Declarations should not leak from the named constructor 11 | z = y * 2.0 12 | 13 | // args: --check 14 | // expected stderr: 15 | // named_constructor.an:7:6 error: Missing fields: y 16 | // t1 = T with x = 3, z = 5 17 | // 18 | // named_constructor.an:7:20 error: z is not a struct field 19 | // t1 = T with x = 3, z = 5 20 | // 21 | // named_constructor.an:11:5 error: No declaration for `y` was found in scope 22 | // z = y * 2.0 23 | 24 | -------------------------------------------------------------------------------- /examples/nameresolution/redeclare.an: -------------------------------------------------------------------------------- 1 | a = 1 2 | a = 2 3 | a = 3 4 | 5 | foo _ = 6 | a = 4 7 | a = 5 8 | () 9 | 10 | // args: --check 11 | // expected stderr: 12 | // redeclare.an:2:1 error: a is already in scope 13 | // a = 2 14 | // 15 | // redeclare.an:1:1 note: a was previously defined here 16 | // a = 1 17 | // 18 | // redeclare.an:3:1 error: a is already in scope 19 | // a = 3 20 | // 21 | // redeclare.an:2:1 note: a was previously defined here 22 | // a = 2 23 | // 24 | // redeclare.an:6:5 warning: a is unused (prefix name with _ to silence this warning) 25 | // a = 4 26 | // 27 | // redeclare.an:5:1 warning: foo is unused (prefix name with _ to silence this warning) 28 | // foo _ = 29 | // 30 | // redeclare.an:7:5 warning: a is unused (prefix name with _ to silence this warning) 31 | // a = 5 32 | -------------------------------------------------------------------------------- /examples/nameresolution/type_decl.an: -------------------------------------------------------------------------------- 1 | 2 | type Struct1 = a:I32, b:F64, c:String 3 | 4 | type Thingy = Struct1 5 | 6 | type Generic a b = first: a, second: b 7 | 8 | type Struct2 t = 9 | a: Thingy 10 | b: Generic t Thingy 11 | 12 | type Union1 a b = | Variant1 | Variant2 13 | 14 | type Option a = 15 | | Just a 16 | | Nothing 17 | 18 | t = Just 1 19 | 20 | type MyRef a = &a 21 | 22 | // args: --check 23 | // expected stdout: 24 | -------------------------------------------------------------------------------- /examples/nameresolution/unused_warning.an: -------------------------------------------------------------------------------- 1 | 2 | id x = x 3 | 4 | id x = error 5 | 6 | // args: --check 7 | // expected stderr: 8 | // unused_warning.an:4:1 error: id is already in scope 9 | // id x = error 10 | // 11 | // unused_warning.an:2:1 note: id was previously defined here 12 | // id x = x 13 | // 14 | // unused_warning.an:4:8 error: No declaration for `error` was found in scope 15 | // id x = error 16 | // 17 | // unused_warning.an:4:4 warning: x is unused (prefix name with _ to silence this warning) 18 | // id x = error 19 | -------------------------------------------------------------------------------- /examples/parsing/apply_operators.an: -------------------------------------------------------------------------------- 1 | 2 | a |> foo 3 | b |> foo 2 4 | 5 | a |> foo b c |> bar d e 6 | bar d e <| foo b c <| a 7 | 8 | 1, 2 |> baz 9 | baz <| 1, 2 10 | 11 | 1, 2 |> baz <| 3, 4 12 | 13 | // args: --parse 14 | // expected stdout: 15 | // (foo a); 16 | // (foo b 2); 17 | // (bar (foo a b c) d e); 18 | // (bar (foo a b c) d e); 19 | // (baz (',' 1 2)); 20 | // (baz (',' 1 2)); 21 | // (baz (',' 1 2) (',' 3 4)) -------------------------------------------------------------------------------- /examples/parsing/effects.an: -------------------------------------------------------------------------------- 1 | 2 | effect Log with log: String -> Unit 3 | 4 | effect Use a with 5 | get: Unit -> a 6 | set: a - i32 -> Unit 7 | 8 | 9 | handle log "test1" 10 | | log s -> print s 11 | 12 | // Test parsing of pattern matching within handle 13 | handle log "test2" 14 | | log "a" -> print "case 1" 15 | | log "b" -> print "case 2" 16 | | log _ -> print "case 3" 17 | 18 | handle foo + bar 19 | | set 0 a -> resume () 20 | | get () -> foo resume 1 // test 'resume' is parsed as a normal identifier 21 | | set _ b -> resume () 22 | 23 | handle () 24 | | return _ -> "foo" 25 | | log _ -> "impossible" 26 | 27 | map1 (l: List a) (f: a -> b can Log) : List b = () 28 | 29 | map2 (l: List a) (f: a -> b pure) : List b = () 30 | 31 | map3 (l: List a) (f: a -> b pure) : List b pure = () 32 | 33 | map4 (l: List a) (f: a -> b) : List b can Log = () 34 | 35 | // args: --parse 36 | // expected stdout: 37 | // (effect Log with 38 | // (: log (String -> Unit)) 39 | // ); 40 | // (effect Use a with 41 | // (: get (Unit -> a)) 42 | // (: set (a i32 -> Unit)) 43 | // ); 44 | // (handle (log "test1") ((log _$0) 45 | // (s = _$0); 46 | // (print s) 47 | // )); 48 | // (handle (log "test2") ((log _$0) (match _$0 ("a" (print "case 1")) ("b" (print "case 2")) (_ (print "case 3"))))); 49 | // (handle ('+' foo bar) ((get _$0) (match _$0 (() (foo resume 1)))) ((set _$0 _$1) (match (',' _$0 _$1) ((',' 0 a) (resume ())) ((',' _ b) (resume ()))))); 50 | // (handle () ((log _$0) 51 | // (_ = _$0); 52 | // "impossible" 53 | // ) ((return _$0) 54 | // (_ = _$0); 55 | // "foo" 56 | // )); 57 | // (map1 = (fn (: l (List a)) (: f (a -> b can Log)) : (List b) -> ())); 58 | // (map2 = (fn (: l (List a)) (: f (a -> b pure)) : (List b) -> ())); 59 | // (map3 = (fn (: l (List a)) (: f (a -> b pure)) : (List b) pure -> ())); 60 | // (map4 = (fn (: l (List a)) (: f (a -> b)) : (List b) can Log -> ())) 61 | -------------------------------------------------------------------------------- /examples/parsing/explicit_curry.an: -------------------------------------------------------------------------------- 1 | 2 | foo _ a 3 | bar 1 _ 3 4 | 5 | baz _ "test" _ 6 | 7 | qux 1 (_, 32) 8 | quux 3 (_) _ 9 | 10 | 2 + _ 11 | 12 | // args: --parse 13 | // expected stdout: 14 | // (fn $1 -> (foo $1 a)); 15 | // (fn $1 -> (bar 1 $1 3)); 16 | // (fn $1 $2 -> (baz $1 "test" $2)); 17 | // (qux 1 (fn $1 -> (',' $1 32))); 18 | // (fn $1 $2 -> (quux 3 $1 $2)); 19 | // (fn $1 -> ('+' 2 $1)) 20 | -------------------------------------------------------------------------------- /examples/parsing/hello.an: -------------------------------------------------------------------------------- 1 | print "Hello, World!" 2 | 3 | // args: --parse 4 | // expected stdout: 5 | // (print "Hello, World!") 6 | -------------------------------------------------------------------------------- /examples/parsing/indent.an: -------------------------------------------------------------------------------- 1 | 2 | foo a b = 3 | fn c d -> 4 | 3 5 | 6 | // These should all result in the same parse tree 7 | when1 condition expr = if condition then 8 | expr 9 | 10 | when2 condition expr = 11 | if condition then expr 12 | 13 | when3 condition expr = 14 | if condition then 15 | expr 16 | 17 | when4 condition expr = 18 | if condition 19 | then expr 20 | 21 | when5 condition expr = 22 | if condition 23 | then expr 24 | 25 | // args: --parse 26 | // expected stdout: 27 | // (foo = (fn a b -> (fn c d -> 3))); 28 | // (when1 = (fn condition expr -> (if condition then 29 | // expr; 30 | // () 31 | // else ()))); 32 | // (when2 = (fn condition expr -> (if condition then 33 | // expr; 34 | // () 35 | // else ()))); 36 | // (when3 = (fn condition expr -> (if condition then 37 | // expr; 38 | // () 39 | // else ()))); 40 | // (when4 = (fn condition expr -> (if condition then 41 | // expr; 42 | // () 43 | // else ()))); 44 | // (when5 = (fn condition expr -> (if condition then 45 | // expr; 46 | // () 47 | // else ()))) 48 | -------------------------------------------------------------------------------- /examples/parsing/invalid_integer_literal_suffix.an: -------------------------------------------------------------------------------- 1 | 3_2_fdsa 2 | 3 | // args: --check 4 | // expected stderr: 5 | // invalid_integer_literal_suffix.an:1:1 error: Invalid suffix after integer literal. Expected an integer type like i32 or a space to separate the two tokens 6 | // 3_2_fdsa 7 | -------------------------------------------------------------------------------- /examples/parsing/irrefutable_pattern.an: -------------------------------------------------------------------------------- 1 | 2 | foo () = () 3 | 4 | bar (a, b, c) = (a, b, c) 5 | 6 | baz (a, b) c = () 7 | 8 | // args: --parse 9 | // expected stdout: 10 | // (foo = (fn () -> ())); 11 | // (bar = (fn (',' a (',' b c)) -> (',' a (',' b c)))); 12 | // (baz = (fn (',' a b) c -> ())) 13 | -------------------------------------------------------------------------------- /examples/parsing/loop.an: -------------------------------------------------------------------------------- 1 | 2 | a = 0 3 | b = 1 4 | loop a (b: I32) (c: I32 = 2) (d = 3) -> 5 | recur a b c d 6 | 7 | // args: --parse 8 | // expected stdout: 9 | // (a = 0); 10 | // (b = 1); 11 | // (match () (() 12 | // (recur = (fn a (: b I32) (: c I32) d -> (recur a b c d))); 13 | // (recur a (: b I32) 2 3) 14 | // )) 15 | -------------------------------------------------------------------------------- /examples/parsing/match.an: -------------------------------------------------------------------------------- 1 | 2 | "before" 3 | 4 | match test "foo" "bar" 5 | | 2 3 -> "foo" 6 | 7 | | "bar" -> "bar" 8 | | "block" -> 9 | if true then 1 else 2 10 | 11 | | "last branch" -> 12 | match "nested" 13 | | "hi!" -> "yo" 14 | 15 | "after" 16 | 17 | match "empty" 18 | 19 | "done" 20 | 21 | // args: --parse 22 | // expected stdout: 23 | // "before"; 24 | // (match (test "foo" "bar") ((2 3) "foo") ("bar" "bar") ("block" (if true then 1 else 2)) ("last branch" (match "nested" ("hi!" "yo")))); 25 | // "after"; 26 | // (match "empty"); 27 | // "done" 28 | -------------------------------------------------------------------------------- /examples/parsing/math.an: -------------------------------------------------------------------------------- 1 | 2 | 1 + 2 * 3 / 4 * 5 - 6 3 | 4 | (1 + 2) * 3 / 4 * (5 - 6) 5 | 6 | double x = x * 2 7 | 8 | foo a b = 1 + a * b 9 | 10 | // args: --parse 11 | // expected stdout: 12 | // ('-' ('+' 1 ('*' ('/' ('*' 2 3) 4) 5)) 6); 13 | // ('*' ('/' ('*' ('+' 1 2) 3) 4) ('-' 5 6)); 14 | // (double = (fn x -> ('*' x 2))); 15 | // (foo = (fn a b -> ('+' 1 ('*' a b)))) 16 | -------------------------------------------------------------------------------- /examples/parsing/named_constructor.an: -------------------------------------------------------------------------------- 1 | // Explicit names 2 | T with b = "one", a = 1 3 | 4 | T with 5 | b = "one" 6 | a = 1 7 | 8 | // Implicit names 9 | T with b, a 10 | 11 | T with 12 | foo 13 | bar 14 | 15 | // Path names 16 | T.U.V with a, b 17 | 18 | // Tuples and exotic indentation 19 | T with a = (1, 2) 20 | 21 | T with a = 1, 22 | b = 3 23 | 24 | T with a = (1, 2), 25 | b = (3, 4), c = 5, 26 | d = 6 27 | 28 | T with foo, 29 | bar, 30 | baz 31 | 32 | T with 33 | foo = ( 1 34 | , 2 35 | , 3 36 | ) 37 | bar = "bar" 38 | 39 | // args: --parse 40 | // expected stdout: 41 | // (T with b = "one", a = 1); 42 | // (T with b = "one", a = 1); 43 | // (T with b = b, a = a); 44 | // (T with foo = foo, bar = bar); 45 | // (T.U.V with a = a, b = b); 46 | // (T with a = (',' 1 2)); 47 | // (T with a = 1, b = 3); 48 | // (T with a = (',' 1 2), b = (',' 3 4), c = 5, d = 6); 49 | // (T with foo = foo, bar = bar, baz = baz); 50 | // (T with foo = (',' 1 (',' 2 3)), bar = "bar") 51 | -------------------------------------------------------------------------------- /examples/parsing/parse_error.an: -------------------------------------------------------------------------------- 1 | 2 | if true else 3 | 4 | bar a b = if then else 5 | 6 | // args: --parse --no-color 7 | // expected stderr: 8 | // parse_error.an:2:9 error: Parser expected 'then' here 9 | // if true else 10 | // ^^^^ 11 | -------------------------------------------------------------------------------- /examples/parsing/semicolon_inference.an: -------------------------------------------------------------------------------- 1 | 2 | // Most ante expressions end on the same line, but the 3 | // line is continued automatically if the next line is 4 | // indented. This rule does not occur if the parser 5 | // is expecting a block of code, e.g. after an `=` 6 | // or `then` token. 7 | 8 | normal = 1 9 | 10 | continued = 1 11 | - 1 12 | 13 | precedence_works = 2 * 14 | 3 + 4 15 | * 5 16 | 17 | foo 18 | bar 19 | baz 20 | (if true then 21 | 2 22 | 3 23 | else 4) 24 | 25 | // args: --parse 26 | // expected stdout: 27 | // (normal = 1); 28 | // (continued = ('-' 1 1)); 29 | // (precedence_works = ('+' ('*' 2 3) ('*' 4 5))); 30 | // (foo bar baz (if true then 31 | // 2; 32 | // 3 33 | // else 4)) 34 | -------------------------------------------------------------------------------- /examples/parsing/string_interpolation.an: -------------------------------------------------------------------------------- 1 | shout x = "${x}!" 2 | 3 | bar = "nested interpolations" 4 | 5 | print 6 | "I can do line breaks${ 7 | shout "\nAny expressions" 8 | }${ 9 | shout "\nEven ${bar}" 10 | }" 11 | 12 | // args: --parse 13 | // expected stdout: 14 | // (shout = (fn x -> (: ('++' (cast x) "!") String))); 15 | // (bar = "nested interpolations"); 16 | // (print ('++' ('++' "I can do line breaks" (cast (shout " 17 | // Any expressions"))) (cast (shout ('++' " 18 | // Even " (cast bar)))))) 19 | 20 | -------------------------------------------------------------------------------- /examples/parsing/trait_decl.an: -------------------------------------------------------------------------------- 1 | 2 | trait Show a with 3 | show: a -> String 4 | 5 | trait Cast a b -> c with 6 | cast: a -> b 7 | 8 | // A collection c of elements of type e 9 | // The element type is uniquely determined by the collection's type c 10 | trait Collection c -> e with 11 | push: c - e -> c 12 | pop: c -> Maybe e 13 | empty: c 14 | 15 | 16 | impl Show String with 17 | show s = s 18 | 19 | impl Cast String String String given Show String with 20 | cast s = show s 21 | 22 | impl Collection (a -> a) unit with 23 | push c e = c 24 | pop c = () 25 | empty = fn a -> a 26 | 27 | // args: --parse 28 | // expected stdout: 29 | // (trait Show a with 30 | // (: show (a -> String)) 31 | // ); 32 | // (trait Cast a b -> c with 33 | // (: cast (a -> b)) 34 | // ); 35 | // (trait Collection c -> e with 36 | // (: push (c e -> c)) 37 | // (: pop (c -> (Maybe e))) 38 | // (: empty c) 39 | // ); 40 | // (impl Show String with 41 | // (show = (fn s -> s)) 42 | // ); 43 | // (impl Cast String String String given (Show String) with 44 | // (cast = (fn s -> (show s))) 45 | // ); 46 | // (impl Collection (a -> a) unit with 47 | // (push = (fn c e -> c)) 48 | // (pop = (fn c -> ())) 49 | // (empty = (fn a -> a)) 50 | // ) 51 | -------------------------------------------------------------------------------- /examples/parsing/type_decl.an: -------------------------------------------------------------------------------- 1 | 2 | type Struct1 = a:I32, b:F64, c:String 3 | 4 | type Struct2 t = 5 | a: Thingy 6 | b: Generic t Thingy 7 | 8 | type Union1 a b = | Variant1 | Variant2 9 | 10 | type Maybe a = 11 | | Some a 12 | | None 13 | 14 | type List a = | Nil | Cons a (&List a) 15 | 16 | 17 | type UniquePtr a = &a 18 | 19 | t = 3 : I32 20 | 21 | // args: --parse 22 | // expected stdout: 23 | // (type Struct1 = a: I32, b: F64, c: String); 24 | // (type Struct2 t = a: Thingy, b: (Generic t Thingy)); 25 | // (type Union1 ab = | Variant1 | Variant2 ); 26 | // (type Maybe a = | Some a| None ); 27 | // (type List a = | Nil | Cons a (& (List a))); 28 | // (type UniquePtr a = (& a)); 29 | // (t = (: 3 I32)) 30 | -------------------------------------------------------------------------------- /examples/parsing/with_effect.an: -------------------------------------------------------------------------------- 1 | iota 10 2 | with filter even 3 | with map (_ * 2) 4 | with for print 5 | 6 | // args: --parse 7 | // expected stdout: 8 | // (for (fn () -> (map (fn () -> (filter (fn () -> (iota 10)) even)) (fn $1 -> ('*' $1 2)))) print) 9 | -------------------------------------------------------------------------------- /examples/regressions/103_size_of_ptr.an: -------------------------------------------------------------------------------- 1 | 2 | boom () : Maybe (Ptr Char) = None 3 | _ = boom () 4 | 5 | // args: --delete-binary 6 | // expected stdout: 7 | -------------------------------------------------------------------------------- /examples/regressions/104_print_twice.an: -------------------------------------------------------------------------------- 1 | import HashMap 2 | 3 | impl Hash U8 with 4 | hash x = cast x 5 | 6 | mut map = empty () 7 | insert !map 1u8 3u16 8 | 9 | print map 10 | print map 11 | 12 | // args: --delete-binary 13 | // expected stdout: 14 | // [1 -> 3] 15 | // [1 -> 3] 16 | -------------------------------------------------------------------------------- /examples/regressions/119_top_level_subscopes.an: -------------------------------------------------------------------------------- 1 | if true then 2 | f () = print "hi" 3 | g () = f () 4 | g () 5 | 6 | // args: --delete-binary 7 | // expected stdout: hi 8 | -------------------------------------------------------------------------------- /examples/regressions/129_int_defaulting_generalization.an: -------------------------------------------------------------------------------- 1 | a = 1, 2 2 | b = a 3 | c = b 4 | 5 | // The original bug can still be triggered using functions 6 | // f () = b 7 | // e = f () 8 | 9 | first b + 2u64 10 | second b + 2i64 11 | 12 | 13 | // args: --check --show-types 14 | // expected stdout: 15 | // a : U64, I64 16 | // b : U64, I64 17 | // c : U64, I64 18 | 19 | // Expected results with the function block uncommented: 20 | // f : (forall a. (Unit -> (U64, I64) can a)) 21 | // e : (U64, I64) 22 | -------------------------------------------------------------------------------- /examples/regressions/130_lambda_int_defaulting.an: -------------------------------------------------------------------------------- 1 | impl Iterator U64 U64 with 2 | next x = 3 | if x <= 0 4 | then None 5 | else Some (x - 1, x) 6 | 7 | type TakeWhile i a = 8 | iter: i 9 | pred: a -> Bool pure 10 | 11 | impl Iterator (TakeWhile i a) a given Iterator i a with 12 | next tw = 13 | match next tw.iter 14 | | Some (iter, new) -> 15 | if tw.pred new 16 | then Some (TakeWhile iter tw.pred, new) 17 | else None 18 | | None -> None 19 | 20 | i = TakeWhile 8u64 (_ >= 5) 21 | iter i print 22 | 23 | // args: --delete-binary 24 | // expected stdout: 25 | // 8 26 | // 7 27 | // 6 28 | // 5 29 | -------------------------------------------------------------------------------- /examples/regressions/146_invalid_int_type.an: -------------------------------------------------------------------------------- 1 | a: Int String = 3 2 | 3 | // args: --check 4 | // expected stderr: 5 | // 146_invalid_int_type.an:1:4 error: Type String is not an integer type 6 | // a: Int String = 3 7 | -------------------------------------------------------------------------------- /examples/regressions/159_two_trait_functions.an: -------------------------------------------------------------------------------- 1 | 2 | trait MultiTrait a with 3 | one: Unit -> Unit 4 | two: Unit -> Unit 5 | 6 | impl MultiTrait Unit with 7 | one _ = () 8 | two _ = () 9 | 10 | one () 11 | 12 | // args: --delete-binary 13 | -------------------------------------------------------------------------------- /examples/regressions/201_mutable_variables_copy.an: -------------------------------------------------------------------------------- 1 | mut x = 4 2 | y = x 3 | print y 4 | x := 5 5 | print y 6 | 7 | // This used to print 4, 5 8 | // args: --delete-binary 9 | // expected stdout: 10 | // 4 11 | // 4 12 | -------------------------------------------------------------------------------- /examples/regressions/217_filter.an: -------------------------------------------------------------------------------- 1 | effect Emit a with 2 | emit: a -> Unit 3 | 4 | iota (n: U32) = loop (i = 0) -> 5 | if i < n then 6 | emit i 7 | recur (i + 1) 8 | 9 | // Removing the `can` here used to panic 10 | filter (stream: Unit -> Unit) (f: a -> Bool pure) = 11 | handle 12 | stream () 13 | | emit x -> 14 | if f x then 15 | emit x 16 | resume () 17 | 18 | for (stream: Unit -> Unit can Emit a) (f: a -> Unit pure) = 19 | handle 20 | stream () 21 | | emit x -> 22 | f x 23 | resume () 24 | 25 | zero_to_nine () = 26 | iota 10 27 | 28 | filtered () = 29 | filter zero_to_nine (fn x -> x % 2 == 0) 30 | 31 | for filtered print 32 | 33 | // args: --delete-binary 34 | // expected stdout: 35 | // 0 36 | // 2 37 | // 4 38 | // 6 39 | // 8 40 | -------------------------------------------------------------------------------- /examples/regressions/80_empty_match.an: -------------------------------------------------------------------------------- 1 | // #80: match expression fails to assign variables when there are no constructors to match against 2 | // Before the fix this crashed during codegen with an internal error message 3 | 4 | match 3 5 | | x -> x 6 | 7 | // args: --delete-binary 8 | // expected stdout: 9 | -------------------------------------------------------------------------------- /examples/regressions/85_type_defaulting_rules.an: -------------------------------------------------------------------------------- 1 | // #85: Compiler crash when compiling some polymorphic integer operations 2 | // Before the fix this crashed during codegen with: 3 | // thread 'main' panicked at 'internal error: entered unreachable code: There is no code in a trait definition that can be codegen'd. 4 | // No cached impl for printne 50: (unit -> unit)', src/llvm/mod.rs:438:17 5 | 6 | count_down n f = 7 | if n > 0 then 8 | f n 9 | count_down (n - 1) f 10 | 11 | main () = 12 | // Making this mutable helps prevent inlining 13 | mut offset = 1 14 | count_down 3 fn x -> 15 | print (x + offset) 16 | 17 | print_offset _ = print offset 18 | closure = count_down _ print_offset 19 | closure 2 20 | 21 | main () 22 | 23 | // args: --delete-binary 24 | // expected stdout: 25 | // 4 26 | // 3 27 | // 2 28 | // 1 29 | // 1 30 | -------------------------------------------------------------------------------- /examples/regressions/96_rebind_origin.an: -------------------------------------------------------------------------------- 1 | 2 | origin foo = 3 | cast foo : U64 4 | 5 | twice foo bar = 6 | origin foo 7 | origin bar 8 | 9 | twice 1u8 2u16 10 | 11 | // args: --delete-binary 12 | // expected stdout: 13 | -------------------------------------------------------------------------------- /examples/typechecking/bind.an: -------------------------------------------------------------------------------- 1 | bind opt f = 2 | match opt 3 | | Some x -> f x 4 | | None -> None 5 | 6 | ret x = Some x 7 | 8 | x = Some 10i32 9 | 10 | add_one x = 11 | bind x (fn x -> ret (x + 1i32)) 12 | 13 | // args: --check --show-types 14 | // expected stdout: 15 | // add_one : Maybe I32 -> Maybe I32 pure 16 | // bind : forall a b c d. (Maybe d - (d => Maybe b can c) -> Maybe b can c) 17 | // ret : forall a. (a -> Maybe a pure) 18 | // x : Maybe I32 19 | -------------------------------------------------------------------------------- /examples/typechecking/completeness_checking.an: -------------------------------------------------------------------------------- 1 | 2 | match None 3 | | None -> 1 4 | 5 | match (2, None) 6 | | (0, None) -> () 7 | | (_, Some _) -> () 8 | 9 | // This one is complete! 10 | match None 11 | | Some (Some _) -> 0 12 | | Some _ -> 1 13 | | None -> 2 14 | 15 | // Redundant patterns 16 | match (1, 2) 17 | | (1, _) -> 0 18 | | (1, 2) -> 1 19 | 20 | match (true, true) 21 | | (false, true) -> 0 22 | | (true, false) -> 1 23 | 24 | match (1, 2, 3, 4) 25 | | (1, 2) -> 1 26 | | (_, _, _, _) -> 3 27 | 28 | // args: --check 29 | // TODO: First error can be improved. Should be "Missing case Some _" 30 | // expected stderr: 31 | // completeness_checking.an:2:1 error: Missing case _ 32 | // match None 33 | // 34 | // completeness_checking.an:5:1 error: Missing case (_, None) 35 | // match (2, None) 36 | // 37 | // completeness_checking.an:18:4 warning: Unreachable pattern 38 | // | (1, 2) -> 1 39 | // 40 | // completeness_checking.an:16:1 error: Missing case (_ : Int, _) 41 | // match (1, 2) 42 | // 43 | // completeness_checking.an:20:1 error: Missing case (true, true) 44 | // match (true, true) 45 | // 46 | // completeness_checking.an:20:1 error: Missing case (false, false) 47 | // match (true, true) 48 | // 49 | // completeness_checking.an:25:4 error: This pattern of type Int a, Int b does not match the type Int a, Int b, Int c, Int d that is being matched on 50 | // | (1, 2) -> 1 51 | -------------------------------------------------------------------------------- /examples/typechecking/count_effect.an: -------------------------------------------------------------------------------- 1 | effect Count with 2 | count: Unit -> Unit 3 | 4 | foo () = 5 | count () 6 | repeat 10 fn _ -> count () 7 | 8 | handle_count f = 9 | handle 10 | f () 11 | 0 12 | | count () -> 1 + resume () 13 | 14 | handle_count foo 15 | 16 | // args: --check --show-types 17 | // expected stdout: 18 | // count : Unit -> Unit can Count 19 | // foo : Unit -> Unit can Count 20 | // handle_count : forall a b c d. ((Unit => a can Count, d) -> Int c can d) 21 | // given Add (Int c) 22 | -------------------------------------------------------------------------------- /examples/typechecking/effects.an: -------------------------------------------------------------------------------- 1 | effect Log with 2 | log: String -> Unit 3 | 4 | effect Use a with 5 | get: Unit -> a 6 | set: a -> Unit 7 | 8 | // Expect `can Use Unit` and not `can Log` 9 | handle_basic () = 10 | handle log "test1" 11 | | log v -> set v 12 | 13 | use_resume () = 14 | handle () 15 | // Test these two 'resume's refer to two different variables 16 | // with incompatible types 17 | | get () -> resume 3i32 18 | | set _ -> resume () 19 | 20 | does_use x = 21 | y = get () 22 | set (y + x) 23 | 24 | // args: --check --show-types 25 | // expected stdout: 26 | // does_use : forall a. (a -> Unit can Use a) 27 | // given Add a 28 | // get : forall a. (Unit -> a can Use a) 29 | // handle_basic : Unit -> Unit can Use String 30 | // log : String -> Unit can Log 31 | // set : forall a. (a -> Unit can Use a) 32 | // use_resume : Unit -> Unit pure 33 | -------------------------------------------------------------------------------- /examples/typechecking/extern.an: -------------------------------------------------------------------------------- 1 | 2 | extern foo: a -> b 3 | 4 | extern 5 | add: I32 - I32 -> I32 6 | puts2: String -> Unit 7 | exit2: I32 -> never_returns 8 | 9 | foo 1 10 | puts2 "test" 11 | exit2 0 12 | 13 | // args: --check --show-types 14 | // expected stdout: 15 | // add : I32 - I32 -> I32 pure 16 | // exit2 : forall never_returns. (I32 -> never_returns pure) 17 | // foo : forall a b. (a -> b pure) 18 | // puts2 : String -> Unit pure 19 | -------------------------------------------------------------------------------- /examples/typechecking/field_diagnostics.an: -------------------------------------------------------------------------------- 1 | tuple = (1, 1) 2 | 3 | _ = (&&tuple).first 4 | 5 | _ = tuple.!first 6 | _ = tuple.!missing 7 | 8 | imm_ref = &tuple 9 | _ = imm_ref.!first 10 | _ = imm_ref.!missing 11 | 12 | mut mut_tuple = (1, 1) 13 | 14 | _ = mut_tuple.&missing 15 | _ = mut_tuple.!missing 16 | 17 | mut_ref = !mut_tuple 18 | 19 | _ = mut_ref.&missing 20 | _ = mut_ref.!missing 21 | 22 | _ = (!&tuple).!first 23 | 24 | // flags: --check 25 | // expected stderr: 26 | // field_diagnostics.an:3:6 error: &shared &shared Int c, Int d has no field 'first' of type { first: b, .. } 27 | // _ = (&&tuple).first 28 | // 29 | // field_diagnostics.an:5:5 error: Cannot mutably reference `tuple`. It was declared as immutable 30 | // _ = tuple.!first 31 | // 32 | // field_diagnostics.an:6:5 error: Int a, Int b has no field 'missing' of type { missing: b, .. } 33 | // _ = tuple.!missing 34 | // 35 | // field_diagnostics.an:9:5 error: Expected a mutable reference but found &shared Int b, Int c instead 36 | // _ = imm_ref.!first 37 | // 38 | // field_diagnostics.an:10:5 error: &shared Int b, Int c has no field 'missing' of type { missing: b, .. } 39 | // _ = imm_ref.!missing 40 | // 41 | // field_diagnostics.an:14:5 error: Int a, Int b has no field 'missing' of type { missing: b, .. } 42 | // _ = mut_tuple.&missing 43 | // 44 | // field_diagnostics.an:15:5 error: Int a, Int b has no field 'missing' of type { missing: b, .. } 45 | // _ = mut_tuple.!missing 46 | // 47 | // field_diagnostics.an:19:5 error: !shared Int b, Int c has no field 'missing' of type { missing: b, .. } 48 | // _ = mut_ref.&missing 49 | // 50 | // field_diagnostics.an:20:5 error: !shared Int b, Int c has no field 'missing' of type { missing: b, .. } 51 | // _ = mut_ref.!missing 52 | // 53 | // field_diagnostics.an:22:6 error: !shared &shared Int c, Int d has no field 'first' of type { first: b, .. } 54 | // _ = (!&tuple).!first 55 | -------------------------------------------------------------------------------- /examples/typechecking/functor_and_monad.an: -------------------------------------------------------------------------------- 1 | 2 | trait Functor f with 3 | map: f a - (a -> b) -> f b 4 | 5 | trait Monad m with 6 | wrap: a -> m a 7 | bind: m a - (a -> m b) -> m b 8 | 9 | 10 | impl Functor Maybe with 11 | map m f = 12 | match m 13 | | Some x -> Some (f x) 14 | | None -> None 15 | 16 | impl Monad Maybe with 17 | wrap a = Some a 18 | 19 | bind m f = 20 | match m 21 | | Some x -> f x 22 | | None -> None 23 | 24 | // args: --check --show-types 25 | // expected stdout: 26 | // bind : forall m c a b. (m a - (a -> m b can c) -> m b can c) 27 | // given Monad m 28 | // map : forall f c a b. (f a - (a -> b can c) -> f b can c) 29 | // given Functor f 30 | // wrap : forall m a. (a -> m a pure) 31 | // given Monad m 32 | -------------------------------------------------------------------------------- /examples/typechecking/generalization.an: -------------------------------------------------------------------------------- 1 | foo x = 2 | y = fn _ -> x 3 | y 4 | 5 | // type should be a -> (b -> a) 6 | 7 | // args: --check --show-types 8 | // expected stdout: 9 | // foo : forall a b. (a -> b => a pure pure) 10 | -------------------------------------------------------------------------------- /examples/typechecking/given_constraint_error.an: -------------------------------------------------------------------------------- 1 | 2 | impl Print a given DoesNotExist a with 3 | printne _ = () 4 | 5 | // args: --check 6 | // expected stderr: 7 | // given_constraint_error.an:2:20 error: Trait DoesNotExist was not found in scope 8 | // impl Print a given DoesNotExist a with 9 | -------------------------------------------------------------------------------- /examples/typechecking/impl.an: -------------------------------------------------------------------------------- 1 | 2 | trait Foo a with 3 | foo: a - a -> a 4 | 5 | impl Foo I32 with 6 | foo _a b = b 7 | 8 | impl Foo F64 with 9 | foo _a b = b 10 | 11 | 12 | a = foo 1 2i32 13 | b = foo 1.0 2.0 14 | c = foo "one" "two" 15 | 16 | // args: --check --show-types 17 | // expected stderr: 18 | // impl.an:14:5 error: No impl found for Foo String 19 | // c = foo "one" "two" 20 | // 21 | 22 | // expected stdout: 23 | // a : I32 24 | // b : F64 25 | // c : String 26 | // foo : forall a. (a - a -> a pure) 27 | // given Foo a 28 | -------------------------------------------------------------------------------- /examples/typechecking/instantiation.an: -------------------------------------------------------------------------------- 1 | 2 | add m n = fn f -> fn x -> m f (n f x) // (a -> b -> c) -> (a -> d -> b) -> a -> d -> c 3 | 4 | one f x = f x // (a -> b) -> a -> b 5 | two1 f x = f (f x) // (a -> a) -> a -> a 6 | 7 | two2 = add one one 8 | 9 | // id is polymorphic, expect no error here 10 | id 3 11 | id "four" 12 | 13 | id x = x 14 | 15 | // args: --check --show-types 16 | // expected stdout: 17 | // add : forall a b c d e f g. ((a - c => f can e) - (a - b => c can e) -> a => b => f can e pure pure) 18 | // id : forall a. (a -> a pure) 19 | // one : forall a b c d. ((b => a can c) - b -> a can c) 20 | // two1 : forall a b c. ((a => a can c) - a -> a can c) 21 | // two2 : (a => a can c) => a => a can c pure 22 | -------------------------------------------------------------------------------- /examples/typechecking/int_inference.an: -------------------------------------------------------------------------------- 1 | print 2 2 | 3 | // This example is actually more problematic than it initially appears. 4 | // Because integer literals with no suffix have the polymorphic type 5 | // `a given Int a` and print has the type `forall a. (a -> unit given Print a)` 6 | // Then even with defaulting rules for `Int a` to `i32` we can still get 7 | // a "No impl found for Print a" error if the Print trait constraint is 8 | // resolved before the Int trait constraint is. 9 | // 10 | // The current solution of this is to sort out Int trait constraints to solve 11 | // them before other trait constraints. 12 | 13 | // args: --check 14 | // expected stdout: 15 | -------------------------------------------------------------------------------- /examples/typechecking/member_access.an: -------------------------------------------------------------------------------- 1 | type FooBar = foo:I32, bar:String 2 | type Foo = foo:F64 3 | type Bar = bar:Char 4 | 5 | stringify (s: String) = s 6 | 7 | foo_and_bar a b = 8 | a.foo 9 | stringify b.bar 10 | 11 | foobar = FooBar 1 "one" 12 | foo = Foo 2.0 13 | bar = Bar '3' 14 | 15 | foo_and_bar foobar foobar 16 | foo_and_bar foo bar 17 | 18 | // args: --check --show-types 19 | // expected stderr: 20 | // member_access.an:16:17 error: Expected argument of type { bar: String, .. }, but found Bar 21 | // foo_and_bar foo bar 22 | // 23 | 24 | // expected stdout: 25 | // Bar : Char -> Bar pure 26 | // Foo : F64 -> Foo pure 27 | // FooBar : I32 - String -> FooBar pure 28 | // bar : Bar 29 | // foo : Foo 30 | // foo_and_bar : forall a b c. ({ foo: a, ..b } - { bar: String, ..c } -> String pure) 31 | // foobar : FooBar 32 | // stringify : String -> String pure 33 | -------------------------------------------------------------------------------- /examples/typechecking/multiple_matching_impls.an: -------------------------------------------------------------------------------- 1 | 2 | type Thing = nothing: Unit 3 | 4 | trait Foo a with 5 | foo: a -> Unit 6 | 7 | trait Bar a with 8 | bar: a -> Unit 9 | 10 | trait Baz a with 11 | baz: a -> Unit 12 | 13 | // The call from which the `Foo a` constraint arises 14 | foo (Thing ()) 15 | 16 | // Candidate 1 17 | impl Foo a given Bar a with 18 | foo a = bar a 19 | 20 | impl Bar a given Baz a with 21 | bar a = baz a 22 | 23 | impl Baz Thing with 24 | baz _ = () 25 | 26 | 27 | // Does not match 28 | impl Foo a given Print a with 29 | foo _ = () 30 | 31 | 32 | // Candidate 2 33 | impl Foo Thing with 34 | foo _ = () 35 | 36 | // args: --check 37 | // expected stderr: 38 | // multiple_matching_impls.an:14:1 error: 2 matching impls found for Foo Thing 39 | // foo (Thing ()) 40 | // 41 | // multiple_matching_impls.an:17:1 note: Candidate 1 42 | // impl Foo a given Bar a with 43 | // 44 | // multiple_matching_impls.an:33:1 note: Candidate 2 45 | // impl Foo Thing with 46 | -------------------------------------------------------------------------------- /examples/typechecking/mutual_recursion.an: -------------------------------------------------------------------------------- 1 | // Multiple mutually-recursive functions must be typechecked in tandem 2 | 3 | is_even n = 4 | if n == 0 then true else 5 | print n 6 | not is_odd (n - 1) 7 | 8 | is_odd n = 9 | n == 1 or not is_even (n - 1) 10 | 11 | is_even 4 12 | 13 | // args: --check --show-types 14 | 15 | // Both of these definitions should have the `Print (int a)` constraint. 16 | // TODO: is_odd here uses `forall a c.` instead of `forall a b.` 17 | 18 | // expected stdout: 19 | // is_even : forall a. (Int a -> Bool pure) 20 | // given Eq (Int a), Print (Int a), Sub (Int a) 21 | // is_odd : forall a. (Int a -> Bool pure) 22 | // given Eq (Int a), Print (Int a), Sub (Int a) 23 | -------------------------------------------------------------------------------- /examples/typechecking/mutual_recursion_rigid.an: -------------------------------------------------------------------------------- 1 | // Mutually recursive definitions which type check when partially annotated despite 2 | // `t != u` and neither ex1a nor ex1b being generalized when ex1b is called within ex1a. 3 | // To do this we need to initialize them with partially generalized types: 4 | // ex1a: forall t. t -> b1 -> r1 5 | // ex1b: forall u. u -> b2 -> r2 6 | // Such that b1, b2, r1, and r2 can still be further refined. 7 | ex1a (a: t) b = 8 | ex1b a b 9 | 0u32 10 | 11 | ex1b (a: u) b = 12 | ex1a a b 13 | 1u32 14 | 15 | 16 | // Second example 17 | trait T1 a with trait1: a -> Unit 18 | trait T2 a with trait2: a -> Unit 19 | 20 | ex2a (a: t) b = 21 | ex2b a b 22 | trait1 a 23 | 24 | ex2b (a: u) b = 25 | ex2c a b 26 | trait2 a 27 | 28 | ex2c (a: v) b = 29 | ex2a a b 30 | 31 | // args: --check --show-types 32 | // expected stdout: 33 | // ex1a : forall t a. (t - a -> U32 pure) 34 | // ex1b : forall t a. (t - a -> U32 pure) 35 | // ex2a : forall t a. (t - a -> Unit pure) 36 | // given T1 t, T2 t 37 | // ex2b : forall t a. (t - a -> Unit pure) 38 | // given T1 t, T2 t 39 | // ex2c : forall t a. (t - a -> Unit pure) 40 | // given T1 t, T2 t 41 | // trait1 : forall a. (a -> Unit pure) 42 | // given T1 a 43 | // trait2 : forall a. (a -> Unit pure) 44 | // given T2 a 45 | -------------------------------------------------------------------------------- /examples/typechecking/named_constructor.an: -------------------------------------------------------------------------------- 1 | type Foo a b= 2 | x: a 3 | y: b 4 | 5 | hello_foo y = Foo with x = "Hello World", y 6 | 7 | foo = hello_foo 42 8 | 9 | // args: --check --show-types 10 | // expected stdout: 11 | // Foo : forall a b. (a - b -> Foo a b pure) 12 | // foo : Foo String (Int a) 13 | // hello_foo : forall a. (a -> Foo String a pure) 14 | -------------------------------------------------------------------------------- /examples/typechecking/repeated_traits.an: -------------------------------------------------------------------------------- 1 | foo a = 2 | print a 3 | print a 4 | 5 | // Make sure output is not "... given Print a, Print a" 6 | // args: --check --show-types 7 | // expected stdout: 8 | // foo : forall a. (a -> Unit pure) 9 | // given Print a 10 | -------------------------------------------------------------------------------- /examples/typechecking/rigid.an: -------------------------------------------------------------------------------- 1 | 2 | ex1 (x: a): b = x + 1u32 3 | 4 | // This is a bug/limitation of the current system. 5 | // This currently produces no type errors despite the type given being too general. 6 | ex2: a -> b = 7 | fn x -> x 8 | 9 | // args: --check --show-types 10 | // expected stdout: 11 | // ex1 : forall a b. (a -> b pure) 12 | // given Add a 13 | // ex2 : forall a. (a -> a pure) 14 | // 15 | // expected stderr: 16 | // rigid.an:2:21 error: Expected argument of type a, but found U32 17 | // ex1 (x: a): b = x + 1u32 18 | -------------------------------------------------------------------------------- /examples/typechecking/trait_fundep_result.an: -------------------------------------------------------------------------------- 1 | 2 | trait Foo a -> b with 3 | foo: a -> b 4 | 5 | impl Foo I32 String with 6 | foo _ = "hi" 7 | 8 | str = foo 0i32 9 | 10 | // args: --check --show-types 11 | // expected stdout: 12 | // foo : forall a b. (a -> b pure) 13 | // given Foo a b 14 | // str : String 15 | -------------------------------------------------------------------------------- /examples/typechecking/trait_generalization.an: -------------------------------------------------------------------------------- 1 | a = "test".c_string 2 | 3 | // args: --check --show-types 4 | // expected stdout: a : Ptr Char 5 | -------------------------------------------------------------------------------- /examples/typechecking/trait_impls.an: -------------------------------------------------------------------------------- 1 | 2 | trait Bar a -> b with 3 | bar : a a -> b 4 | 5 | 6 | impl Bar I32 String with 7 | bar _ _ = "good" 8 | 9 | impl Bar I32 I32 with 10 | bar _ _ = "bad return type" 11 | 12 | impl Bar I32 Char String F64 Unit with 13 | bar _ _ = "bad impl args" 14 | 15 | 16 | 17 | impl Bar I32 F64 with 18 | bar _ _ = 23.2 // Should be an error, we already have a Bar I32 19 | 20 | foo a b = bar a b 21 | 22 | baz (x: String) y = bar x y 23 | bar 3 2 24 | 25 | bar "four" "five" 26 | 27 | // args: --check --show-types 28 | // expected stderr: 29 | // trait_impls.an:12:1 error: impl has 5 type arguments but Bar requires 2 30 | // impl Bar I32 Char String F64 Unit with 31 | // 32 | // expected stdout: 33 | // bar : (none) 34 | // baz : (none) 35 | // foo : (none) 36 | -------------------------------------------------------------------------------- /examples/typechecking/trait_propagation.an: -------------------------------------------------------------------------------- 1 | 2 | extern bar : a 3 | 4 | trait Baz a with baz : a -> Unit 5 | 6 | foo () = baz bar 7 | 8 | // This test ensures the Baz constraint is solved in foo 9 | // rather than being propagated up into its signature just 10 | // because it has a typevariable. 11 | // 12 | // args: --check --show-types 13 | // expected stdout: 14 | // bar : forall a. a 15 | // baz : forall a. (a -> Unit pure) 16 | // given Baz a 17 | // foo : Unit -> Unit pure 18 | 19 | // expected stderr: 20 | // trait_propagation.an:6:10 error: No impl found for Baz a 21 | // foo () = baz bar 22 | -------------------------------------------------------------------------------- /examples/typechecking/type_annotations.an: -------------------------------------------------------------------------------- 1 | 2 | foo (_: I32) (_: String) : Char = 'a' 3 | 4 | bar: a - a -> I32 = 5 | fn _ b -> b 6 | 7 | baz (a: Usz) : Ptr a = 8 | transmute a 9 | 10 | exit2 = exit 11 | puts2: (Ptr Char) -> I32 = puts 12 | 13 | exit2 "test" 14 | puts2 15 | 16 | // args: --check --show-types 17 | // TODO: This first error is duplicated 18 | // expected stderr: 19 | // type_annotations.an:4:1 error: Pattern type a - b -> b pure does not match the annotated type a - a -> I32 pure 20 | // bar: a - a -> I32 = 21 | // 22 | // type_annotations.an:4:1 error: Pattern type a - b -> b pure does not match the annotated type a - a -> I32 pure 23 | // bar: a - a -> I32 = 24 | // 25 | // type_annotations.an:13:7 error: Expected argument of type I32, but found String 26 | // exit2 "test" 27 | 28 | // expected stdout: 29 | // bar : forall a b. (b - a -> a pure) 30 | // baz : forall a. (Usz -> Ptr a pure) 31 | // exit2 : I32 -> Unit pure 32 | // foo : I32 - String -> Char pure 33 | // puts2 : Ptr Char -> I32 pure 34 | -------------------------------------------------------------------------------- /examples_failing/141_recursive_closure.an: -------------------------------------------------------------------------------- 1 | f v = loop (x = v / 2.0) -> 2 | if x < 2.0 then x 3 | else 4 | print v 5 | recur (x / 2.0) 6 | 7 | x = f 123.0 8 | 9 | // args: --delete-binary 10 | // expected stdout: 11 | // 123.0 12 | // 123.0 13 | // 123.0 14 | // 123.0 15 | // 123.0 16 | -------------------------------------------------------------------------------- /examples_failing/142_incorrect_defaulting.an: -------------------------------------------------------------------------------- 1 | s : U32 = 8 2 | 3 | g () = 4 | Range 0 s 5 | 6 | r = g () 7 | print r.end 8 | 9 | // args: --delete-binary 10 | // expected stdout: 8 11 | -------------------------------------------------------------------------------- /examples_failing/capture_by_ref.an: -------------------------------------------------------------------------------- 1 | main () = 2 | mut x = 0 3 | x_ref = !x 4 | 5 | closure () = 6 | x_ref := 5 7 | x_copy = @x_ref 8 | 9 | print "x_ref = ${x_copy}" //=> 5 10 | 11 | // x is captured by copy here instead of by ref 12 | print "x = ${x}" //=> 0, should be 5 13 | 14 | closure () 15 | 16 | main () 17 | 18 | // args: --delete-binary 19 | // expected stdout: 20 | // 5 21 | // 5 22 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "crane": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1699217310, 7 | "narHash": "sha256-xpW3VFUG7yE6UE6Wl0dhqencuENSkV7qpnpe9I8VbPw=", 8 | "owner": "ipetkov", 9 | "repo": "crane", 10 | "rev": "d535642bbe6f377077f7c23f0febb78b1463f449", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "ipetkov", 15 | "ref": "v0.15.0", 16 | "repo": "crane", 17 | "type": "github" 18 | } 19 | }, 20 | "dream2nix": { 21 | "inputs": { 22 | "nixpkgs": [ 23 | "nci", 24 | "nixpkgs" 25 | ], 26 | "purescript-overlay": "purescript-overlay", 27 | "pyproject-nix": "pyproject-nix" 28 | }, 29 | "locked": { 30 | "lastModified": 1705584120, 31 | "narHash": "sha256-zyzrci+3XzP/7yXgu/aTXiF+RNQPoQhLO/aGlU2nLkc=", 32 | "owner": "nix-community", 33 | "repo": "dream2nix", 34 | "rev": "4f2a64a66feb94c55c97d39b34cda5d63c008945", 35 | "type": "github" 36 | }, 37 | "original": { 38 | "owner": "nix-community", 39 | "repo": "dream2nix", 40 | "type": "github" 41 | } 42 | }, 43 | "flake-compat": { 44 | "locked": { 45 | "lastModified": 1696426674, 46 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 47 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 48 | "revCount": 57, 49 | "type": "tarball", 50 | "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz" 51 | }, 52 | "original": { 53 | "type": "tarball", 54 | "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" 55 | } 56 | }, 57 | "mk-naked-shell": { 58 | "flake": false, 59 | "locked": { 60 | "lastModified": 1681286841, 61 | "narHash": "sha256-3XlJrwlR0nBiREnuogoa5i1b4+w/XPe0z8bbrJASw0g=", 62 | "owner": "yusdacra", 63 | "repo": "mk-naked-shell", 64 | "rev": "7612f828dd6f22b7fb332cc69440e839d7ffe6bd", 65 | "type": "github" 66 | }, 67 | "original": { 68 | "owner": "yusdacra", 69 | "repo": "mk-naked-shell", 70 | "type": "github" 71 | } 72 | }, 73 | "nci": { 74 | "inputs": { 75 | "crane": "crane", 76 | "dream2nix": "dream2nix", 77 | "mk-naked-shell": "mk-naked-shell", 78 | "nixpkgs": [ 79 | "nixpkgs" 80 | ], 81 | "parts": [ 82 | "parts" 83 | ], 84 | "rust-overlay": "rust-overlay", 85 | "treefmt": "treefmt" 86 | }, 87 | "locked": { 88 | "lastModified": 1705644727, 89 | "narHash": "sha256-MFzTUNV0hT/FBuKLhb9ASkA+icHQPEpCr9d1nRMv8gQ=", 90 | "owner": "yusdacra", 91 | "repo": "nix-cargo-integration", 92 | "rev": "f41f616a7c0b11686e22971116ad2ce4a9c70b47", 93 | "type": "github" 94 | }, 95 | "original": { 96 | "owner": "yusdacra", 97 | "repo": "nix-cargo-integration", 98 | "type": "github" 99 | } 100 | }, 101 | "nixpkgs": { 102 | "locked": { 103 | "lastModified": 1705496572, 104 | "narHash": "sha256-rPIe9G5EBLXdBdn9ilGc0nq082lzQd0xGGe092R/5QE=", 105 | "owner": "nixos", 106 | "repo": "nixpkgs", 107 | "rev": "842d9d80cfd4560648c785f8a4e6f3b096790e19", 108 | "type": "github" 109 | }, 110 | "original": { 111 | "owner": "nixos", 112 | "ref": "nixos-unstable", 113 | "repo": "nixpkgs", 114 | "type": "github" 115 | } 116 | }, 117 | "parts": { 118 | "inputs": { 119 | "nixpkgs-lib": [ 120 | "nixpkgs" 121 | ] 122 | }, 123 | "locked": { 124 | "lastModified": 1704982712, 125 | "narHash": "sha256-2Ptt+9h8dczgle2Oo6z5ni5rt/uLMG47UFTR1ry/wgg=", 126 | "owner": "hercules-ci", 127 | "repo": "flake-parts", 128 | "rev": "07f6395285469419cf9d078f59b5b49993198c00", 129 | "type": "github" 130 | }, 131 | "original": { 132 | "owner": "hercules-ci", 133 | "repo": "flake-parts", 134 | "type": "github" 135 | } 136 | }, 137 | "purescript-overlay": { 138 | "inputs": { 139 | "nixpkgs": [ 140 | "nci", 141 | "dream2nix", 142 | "nixpkgs" 143 | ], 144 | "slimlock": "slimlock" 145 | }, 146 | "locked": { 147 | "lastModified": 1696022621, 148 | "narHash": "sha256-eMjFmsj2G1E0Q5XiibUNgFjTiSz0GxIeSSzzVdoN730=", 149 | "owner": "thomashoneyman", 150 | "repo": "purescript-overlay", 151 | "rev": "047c7933abd6da8aa239904422e22d190ce55ead", 152 | "type": "github" 153 | }, 154 | "original": { 155 | "owner": "thomashoneyman", 156 | "repo": "purescript-overlay", 157 | "type": "github" 158 | } 159 | }, 160 | "pyproject-nix": { 161 | "flake": false, 162 | "locked": { 163 | "lastModified": 1702448246, 164 | "narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=", 165 | "owner": "davhau", 166 | "repo": "pyproject.nix", 167 | "rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb", 168 | "type": "github" 169 | }, 170 | "original": { 171 | "owner": "davhau", 172 | "ref": "dream2nix", 173 | "repo": "pyproject.nix", 174 | "type": "github" 175 | } 176 | }, 177 | "root": { 178 | "inputs": { 179 | "flake-compat": "flake-compat", 180 | "nci": "nci", 181 | "nixpkgs": "nixpkgs", 182 | "parts": "parts" 183 | } 184 | }, 185 | "rust-overlay": { 186 | "flake": false, 187 | "locked": { 188 | "lastModified": 1705630663, 189 | "narHash": "sha256-f+kcR17ZtwMyCEtNAfpD0Mv6qObNKoJ41l+6deoaXi8=", 190 | "owner": "oxalica", 191 | "repo": "rust-overlay", 192 | "rev": "47cac072a313d9cce884b9ea418d2bf712fa23dd", 193 | "type": "github" 194 | }, 195 | "original": { 196 | "owner": "oxalica", 197 | "repo": "rust-overlay", 198 | "type": "github" 199 | } 200 | }, 201 | "slimlock": { 202 | "inputs": { 203 | "nixpkgs": [ 204 | "nci", 205 | "dream2nix", 206 | "purescript-overlay", 207 | "nixpkgs" 208 | ] 209 | }, 210 | "locked": { 211 | "lastModified": 1688610262, 212 | "narHash": "sha256-Wg0ViDotFWGWqKIQzyYCgayeH8s4U1OZcTiWTQYdAp4=", 213 | "owner": "thomashoneyman", 214 | "repo": "slimlock", 215 | "rev": "b5c6cdcaf636ebbebd0a1f32520929394493f1a6", 216 | "type": "github" 217 | }, 218 | "original": { 219 | "owner": "thomashoneyman", 220 | "repo": "slimlock", 221 | "type": "github" 222 | } 223 | }, 224 | "treefmt": { 225 | "inputs": { 226 | "nixpkgs": [ 227 | "nci", 228 | "nixpkgs" 229 | ] 230 | }, 231 | "locked": { 232 | "lastModified": 1705596546, 233 | "narHash": "sha256-5CFL2QdueAuhPzt0d1TXpJvkoUJ01AAZOUuQSQcBaPA=", 234 | "owner": "numtide", 235 | "repo": "treefmt-nix", 236 | "rev": "5861b733f22ae4c192c7d207bae515d3bb77a6c0", 237 | "type": "github" 238 | }, 239 | "original": { 240 | "owner": "numtide", 241 | "repo": "treefmt-nix", 242 | "type": "github" 243 | } 244 | } 245 | }, 246 | "root": "root", 247 | "version": 7 248 | } 249 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"; 4 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 5 | nci = { 6 | url = "github:yusdacra/nix-cargo-integration"; 7 | inputs = { 8 | nixpkgs.follows = "nixpkgs"; 9 | parts.follows = "parts"; 10 | }; 11 | }; 12 | parts = { 13 | url = "github:hercules-ci/flake-parts"; 14 | inputs.nixpkgs-lib.follows = "nixpkgs"; 15 | }; 16 | }; 17 | 18 | outputs = inputs@{ parts, nci, ... }: 19 | parts.lib.mkFlake { inherit inputs; } { 20 | systems = [ 21 | "x86_64-linux" 22 | "aarch64-linux" 23 | "x86_64-darwin" 24 | "aarch64-darwin" 25 | ]; 26 | 27 | imports = [ 28 | parts.flakeModules.easyOverlay 29 | nci.flakeModule 30 | ./crates.nix 31 | ]; 32 | 33 | perSystem = { config, ... }: 34 | let 35 | anteCrate = config.nci.outputs.ante; 36 | ante-lsCrate = config.nci.outputs.ante-ls; 37 | in 38 | { 39 | overlayAttrs = { 40 | inherit (config.packages) 41 | ante 42 | ante-ls; 43 | }; 44 | packages = rec { 45 | default = ante; 46 | ante = anteCrate.packages.release; 47 | ante-ls = ante-lsCrate.packages.release; 48 | }; 49 | devShells.default = anteCrate.devShell.overrideAttrs (_: { 50 | shellHook = '' 51 | PATH=$PATH:$PWD/target/debug 52 | ''; 53 | }); 54 | }; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | fn_params_layout = "Compressed" 2 | match_block_trailing_comma = true 3 | newline_style = "Unix" 4 | use_field_init_shorthand = true 5 | use_try_shorthand = true 6 | use_small_heuristics = "Max" 7 | max_width = 120 8 | 9 | # unstable: 10 | # wrap_comments = true 11 | # normalize_comments = true 12 | # unstable_features = true 13 | # condense_wildcard_suffixes = true 14 | # overflow_delimited_expr = true 15 | # comment_width = 100 16 | # normalize_doc_attributes = true 17 | # format_code_in_doc_comments = true 18 | # format_macro_matchers = true 19 | # imports_granularity = "Crate" 20 | # group_imports = "StdExternalCrate" 21 | # reorder_impl_items = true 22 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | (import 2 | ( 3 | let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in 4 | fetchTarball { 5 | url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; 6 | sha256 = lock.nodes.flake-compat.locked.narHash; 7 | } 8 | ) 9 | { src = ./.; } 10 | ).shellNix 11 | -------------------------------------------------------------------------------- /src/cache/counter.rs: -------------------------------------------------------------------------------- 1 | use crate::types::traits::TraitConstraintId; 2 | 3 | // This can be generalized to more types if needed. 4 | #[derive(Debug, Default)] 5 | pub struct TraitConstraintCounter(u32); 6 | 7 | impl TraitConstraintCounter { 8 | pub fn next(&mut self) -> TraitConstraintId { 9 | let current = self.0; 10 | self.0 += 1; 11 | TraitConstraintId(current) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/cache/dependency_graph.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::ModuleCache; 4 | use crate::cache::DefinitionInfoId; 5 | use petgraph::dot::{Config, Dot}; 6 | use petgraph::graph::{DiGraph, NodeIndex}; 7 | 8 | #[derive(Default, Debug)] 9 | pub struct DependencyGraph { 10 | current_definition: Option, 11 | graph: DiGraph, 12 | node_map: HashMap, 13 | } 14 | 15 | impl DependencyGraph { 16 | fn get_or_add_node(&mut self, id: DefinitionInfoId) -> NodeIndex { 17 | if let Some(index) = self.node_map.get(&id) { 18 | *index 19 | } else { 20 | let index = self.graph.add_node(id); 21 | self.node_map.insert(id, index); 22 | index 23 | } 24 | } 25 | 26 | // Prints the graph in graphviz format 27 | #[allow(unused)] 28 | pub fn dbg_print(&self, cache: &ModuleCache) { 29 | let named = self.graph.map(|_, node| cache[*node].name.clone(), |_, edge| *edge); 30 | let dot = Dot::with_config(&named, &[Config::EdgeNoLabel]); 31 | println!("{dot:?}"); 32 | } 33 | 34 | pub fn set_definition(&mut self, definition: DefinitionInfoId) { 35 | self.current_definition = Some(definition); 36 | } 37 | 38 | // add an edge when a global is referenced 39 | pub fn add_edge(&mut self, dependency: DefinitionInfoId) { 40 | if let Some(dependent) = self.current_definition { 41 | let dependent = self.get_or_add_node(dependent); 42 | let dependency = self.get_or_add_node(dependency); 43 | self.graph.update_edge(dependent, dependency, ()); 44 | } 45 | } 46 | 47 | pub fn enter_definition(&mut self) -> Option { 48 | self.current_definition 49 | } 50 | 51 | pub fn exit_definition(&mut self, reset_definition: Option) { 52 | self.current_definition = reset_definition; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/cache/unsafecache.rs: -------------------------------------------------------------------------------- 1 | //! unsafecache.rs - Provides a container whose elements are never freed 2 | //! and can thus hand out references for any lifetime. Used to store 3 | //! the Ast after parsing. 4 | use std::cell::UnsafeCell; 5 | use std::marker::{PhantomData, PhantomPinned}; 6 | use std::pin::Pin; 7 | 8 | /// A container whose elements are never freed until the program ends. 9 | /// Since these elements are not freed, they can be retrieved via references 10 | /// with any desired lifetime. Note that this type is unsafe to use if the 11 | /// UnsafeCache itself lives longer than any references it passes out. 12 | #[derive(Debug)] 13 | pub struct UnsafeCache<'a, T: 'a> { 14 | cache: Vec>>>, 15 | lifetime: PhantomData<&'a T>, 16 | 17 | /// Ensures we cannot move out of the cache, this would invalidate existing references. 18 | #[allow(dead_code)] 19 | no_pin: PhantomPinned, 20 | } 21 | 22 | impl<'a, T> UnsafeCache<'a, T> { 23 | pub fn get_mut(&self, index: usize) -> Option<&'a mut T> { 24 | let value = self.cache.get(index)?; 25 | // SAFETY: the contained value is guaranteed to never be deallocated until `self` is, 26 | // since we neither expose method removing values from the `inner`, nor expose any 27 | // option to mutate the containing Box. The lifetime should be fine, though this 28 | // does permit multiple mutable references to a given element 29 | unsafe { value.get().as_mut() } 30 | } 31 | 32 | /// Push a new element to the cache and return its index 33 | pub fn push(&mut self, t: T) -> usize { 34 | let len = self.cache.len(); 35 | self.cache.push(Box::pin(UnsafeCell::new(t))); 36 | len 37 | } 38 | } 39 | 40 | impl<'a, T> Default for UnsafeCache<'a, T> { 41 | fn default() -> UnsafeCache<'a, T> { 42 | UnsafeCache { cache: vec![], lifetime: PhantomData, no_pin: PhantomPinned } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::{Parser, ValueEnum, ValueHint}; 2 | use clap_complete::Shell; 3 | 4 | #[derive(Parser, Debug)] 5 | pub struct Completions { 6 | #[arg(long)] 7 | pub shell_completion: Shell, 8 | } 9 | 10 | #[derive(Parser, Debug)] 11 | #[command(author, version, about, long_about = None)] 12 | pub struct Cli { 13 | /// Path to the source file 14 | #[arg(value_hint=ValueHint::FilePath)] 15 | pub file: String, 16 | 17 | /// Print out the input file annotated with inferred lifetimes of heap allocations 18 | #[arg(long, short = 'L')] 19 | pub show_lifetimes: bool, 20 | 21 | /// Lex the file and output the resulting list of tokens 22 | #[arg(long, short, group = "compile_mode")] 23 | pub lex: bool, 24 | 25 | /// Parse the file and output the resulting Ast 26 | #[arg(long, short, group = "compile_mode")] 27 | pub parse: bool, 28 | 29 | /// Check the file for errors without compiling 30 | #[arg(long, short, group = "compile_mode")] 31 | pub check: bool, 32 | 33 | /// Build the resulting binary without running it afterward 34 | #[arg(long, short, group = "compile_mode")] 35 | pub build: bool, 36 | 37 | /// Tells the compiler to create something other than an executable 38 | #[arg(long, short, group = "compile_mode")] 39 | pub emit: Option, 40 | 41 | /// Specify the backend to use ('llvm' or 'cranelift'). Note that cranelift is only for debug builds. 42 | /// Ante will use cranelift by default for debug builds and llvm by default for optimized builds, 43 | /// unless overridden by this flag 44 | #[arg(long)] 45 | pub backend: Option, 46 | 47 | /// Sets the current optimization level from 0 (no optimization) to 3 (aggressive optimization). 48 | /// Set to s or z to optimize for size. 49 | #[arg(short = 'O', default_value = "0", value_parser = validate_opt_argument)] 50 | pub opt_level: char, 51 | 52 | /// Use plaintext and an indicator line instead of color for pointing out error locations 53 | #[arg(long)] 54 | pub no_color: bool, 55 | 56 | /// Delete the resulting binary after compiling 57 | #[arg(long, short, group = "compile_mode")] 58 | pub delete_binary: bool, 59 | 60 | /// Print out the time each compiler pass takes for the given program 61 | #[arg(long)] 62 | pub show_time: bool, 63 | 64 | /// Print out the type of each definition 65 | #[arg(long, short = 't')] 66 | pub show_types: bool, 67 | } 68 | 69 | #[derive(Debug, PartialEq, Eq, Copy, Clone, ValueEnum)] 70 | pub enum EmitTarget { 71 | /// LLVM-IR or Cranelift IR depending on the selected backend 72 | Ir, 73 | 74 | /// Ante's post-monomorphisation HIR representation 75 | Hir, 76 | } 77 | 78 | #[derive(Debug, PartialEq, Eq, Copy, Clone, ValueEnum)] 79 | pub enum Backend { 80 | Cranelift, 81 | Llvm, 82 | } 83 | 84 | fn validate_opt_argument(arg: &str) -> Result { 85 | match arg { 86 | "0" | "1" | "2" | "3" | "s" | "z" => Ok(arg.chars().next().unwrap()), 87 | _ => Err("Argument to -O must be one of: 0, 1, 2, 3, s, or z"), 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/cranelift_backend/decisiontree.rs: -------------------------------------------------------------------------------- 1 | use cranelift::prelude::Value as CraneliftValue; 2 | use cranelift::{ 3 | frontend::FunctionBuilder, 4 | prelude::{Block, InstBuilder, JumpTableData, TrapCode}, 5 | }; 6 | 7 | use crate::{hir, util::fmap}; 8 | 9 | use super::{ 10 | context::{Context, Value}, 11 | CodeGen, 12 | }; 13 | 14 | impl<'ast> Context<'ast> { 15 | pub fn codegen_match(&mut self, match_: &'ast hir::Match, builder: &mut FunctionBuilder) -> Value { 16 | let branches = fmap(&match_.branches, |_| builder.create_block()); 17 | let end_block = self.new_block_with_arg(&match_.result_type, builder); 18 | 19 | self.codegen_subtree(&match_.decision_tree, &branches, builder); 20 | 21 | for (branch, block) in match_.branches.iter().zip(branches) { 22 | builder.seal_block(block); 23 | if let Some(values) = self.eval_all_in_block(branch, block, builder) { 24 | builder.ins().jump(end_block, &values); 25 | } 26 | } 27 | 28 | builder.switch_to_block(end_block); 29 | builder.seal_block(end_block); 30 | let end_values = builder.block_params(end_block); 31 | self.array_to_value(end_values, &match_.result_type) 32 | } 33 | 34 | fn codegen_subtree(&mut self, tree: &'ast hir::DecisionTree, branches: &[Block], builder: &mut FunctionBuilder) { 35 | match tree { 36 | hir::DecisionTree::Leaf(n) => { 37 | builder.ins().jump(branches[*n], &[]); 38 | }, 39 | hir::DecisionTree::Definition(definition, subtree) => { 40 | definition.codegen(self, builder); 41 | self.codegen_subtree(subtree, branches, builder); 42 | }, 43 | hir::DecisionTree::Switch { int_to_switch_on, cases, else_case } => { 44 | let int_to_switch_on = int_to_switch_on.eval_single(self, builder); 45 | self.build_switch(int_to_switch_on, cases, else_case, branches, builder); 46 | }, 47 | } 48 | } 49 | 50 | fn build_switch( 51 | &mut self, int_to_switch_on: CraneliftValue, cases: &'ast [(u32, hir::DecisionTree)], 52 | else_case: &'ast Option>, branches: &[Block], builder: &mut FunctionBuilder, 53 | ) { 54 | let mut cases = fmap(cases, |(tag, subtree)| { 55 | let new_block = builder.create_block(); 56 | (*tag as i64, new_block, subtree) 57 | }); 58 | 59 | let else_block = builder.create_block(); 60 | let jump_table_data = create_jump_table_data(&mut cases, else_block); 61 | let jump_table = builder.create_jump_table(jump_table_data); 62 | builder.ins().br_table(int_to_switch_on, else_block, jump_table); 63 | 64 | // Fill in new blocks only after creating the jump table. 65 | // Cranelift enforces we cannot switch out of partially filled blocks. 66 | self.codegen_cases(else_block, else_case, cases, branches, builder); 67 | } 68 | 69 | fn codegen_cases( 70 | &mut self, else_block: Block, else_case: &'ast Option>, 71 | cases: Vec<(i64, Block, &'ast hir::DecisionTree)>, branches: &[Block], builder: &mut FunctionBuilder, 72 | ) { 73 | builder.switch_to_block(else_block); 74 | if let Some(subtree) = else_case { 75 | self.codegen_subtree(subtree, branches, builder); 76 | } else { 77 | builder.ins().trap(TrapCode::UnreachableCodeReached); 78 | } 79 | builder.seal_block(else_block); 80 | 81 | for (_, block, subtree) in cases { 82 | builder.switch_to_block(block); 83 | self.codegen_subtree(subtree, branches, builder); 84 | builder.seal_block(block); 85 | } 86 | } 87 | } 88 | 89 | fn create_jump_table_data(cases: &mut Vec<(i64, Block, &hir::DecisionTree)>, else_block: Block) -> JumpTableData { 90 | // Sorting unstably doesn't matter here, the type checker's DecisionTree generation 91 | // ensures there are no duplicate tag values. 92 | // TODO: Can we merge sorting and filling in missing cases into 1 step? 93 | cases.sort_unstable_by_key(|(tag, _, _)| *tag); 94 | 95 | let mut data = JumpTableData::new(); 96 | for (tag, block, _) in cases { 97 | // Arbitrary limit, should be fixed to use if statements instead, assuming table is 98 | // sparse. 99 | if *tag > 1000 { 100 | eprintln!("Cranelift backend: Warning: tried to create a jump table with >1000 entries. This is inefficient and should be changed"); 101 | } 102 | // Fill in any gaps with the match_all_case 103 | for _ in data.len()..*tag as usize { 104 | data.push_entry(else_block); 105 | } 106 | data.push_entry(*block); 107 | } 108 | 109 | data 110 | } 111 | -------------------------------------------------------------------------------- /src/cranelift_backend/module.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use cranelift::prelude::{isa, settings, Configurable}; 4 | use cranelift_jit::JITBuilder; 5 | use cranelift_module::{DataContext, FuncId, Linkage, Module}; 6 | use cranelift_object::ObjectBuilder; 7 | 8 | use crate::util; 9 | 10 | #[allow(clippy::large_enum_variant)] 11 | pub enum DynModule { 12 | Jit(cranelift_jit::JITModule), 13 | Static(cranelift_object::ObjectModule), 14 | } 15 | 16 | impl DynModule { 17 | pub fn new(output_name: String, use_jit: bool) -> Self { 18 | let mut settings = settings::builder(); 19 | 20 | // Cranelift-jit currently only supports PIC on x86-64 and 21 | // will panic by default if it is enabled on other platforms 22 | if cfg!(not(target_arch = "x86_64")) { 23 | settings.set("use_colocated_libcalls", "false").unwrap(); 24 | settings.set("is_pic", "false").unwrap(); 25 | } 26 | 27 | let shared_flags = settings::Flags::new(settings); 28 | 29 | // TODO: Should we use cranelift_native here to get the native target instead? 30 | let target_isa = isa::lookup(target_lexicon::Triple::host()).unwrap().finish(shared_flags).unwrap(); 31 | 32 | let libcall_names = cranelift_module::default_libcall_names(); 33 | 34 | if use_jit { 35 | let mut builder = JITBuilder::with_isa(target_isa, libcall_names); 36 | builder.symbol("mco_coro_init", aminicoro::mco_coro_init as *const u8); 37 | builder.symbol("mco_coro_free", aminicoro::mco_coro_free as *const u8); 38 | builder.symbol("mco_coro_is_suspended", aminicoro::mco_coro_is_suspended as *const u8); 39 | builder.symbol("mco_coro_push", aminicoro::mco_coro_push as *const u8); 40 | builder.symbol("mco_coro_pop", aminicoro::mco_coro_pop as *const u8); 41 | builder.symbol("mco_coro_suspend", aminicoro::mco_coro_suspend as *const u8); 42 | builder.symbol("mco_coro_resume", aminicoro::mco_coro_resume as *const u8); 43 | DynModule::Jit(cranelift_jit::JITModule::new(builder)) 44 | } else { 45 | let builder = ObjectBuilder::new(target_isa, output_name, libcall_names); 46 | DynModule::Static(cranelift_object::ObjectModule::new(builder.unwrap())) 47 | } 48 | } 49 | 50 | pub fn finish(&mut self) { 51 | match self { 52 | DynModule::Jit(module) => { 53 | module.finalize_definitions(); 54 | }, 55 | DynModule::Static(_module) => {}, 56 | } 57 | } 58 | 59 | pub fn run(self, main_id: FuncId, output_file: &Path) { 60 | match self { 61 | DynModule::Jit(module) => { 62 | let main = module.get_finalized_function(main_id); 63 | let main: fn() -> i32 = unsafe { std::mem::transmute(main) }; 64 | main(); 65 | }, 66 | DynModule::Static(module) => { 67 | let product = module.finish(); 68 | let text = product.object.write().unwrap(); 69 | std::fs::write(output_file, text).unwrap(); 70 | 71 | let executable = util::binary_name(output_file.to_string_lossy().as_ref()); 72 | util::link(output_file.to_string_lossy().as_ref(), &executable); 73 | }, 74 | } 75 | } 76 | } 77 | 78 | macro_rules! dispatch_on_module { 79 | ( $expr_name:expr, $function:expr $(, $($args:expr),* )? ) => ({ 80 | match $expr_name { 81 | DynModule::Jit(module) => $function(module $(, $($args),* )? ), 82 | DynModule::Static(module) => $function(module $(, $($args),* )? ), 83 | } 84 | }); 85 | } 86 | 87 | impl Module for DynModule { 88 | fn isa(&self) -> &dyn cranelift::prelude::isa::TargetIsa { 89 | dispatch_on_module!(self, Module::isa) 90 | } 91 | 92 | fn declarations(&self) -> &cranelift_module::ModuleDeclarations { 93 | dispatch_on_module!(self, Module::declarations) 94 | } 95 | 96 | fn declare_function( 97 | &mut self, name: &str, linkage: Linkage, signature: &cranelift::codegen::ir::Signature, 98 | ) -> cranelift_module::ModuleResult { 99 | dispatch_on_module!(self, Module::declare_function, name, linkage, signature) 100 | } 101 | 102 | fn declare_anonymous_function( 103 | &mut self, signature: &cranelift::codegen::ir::Signature, 104 | ) -> cranelift_module::ModuleResult { 105 | dispatch_on_module!(self, Module::declare_anonymous_function, signature) 106 | } 107 | 108 | fn declare_data( 109 | &mut self, name: &str, linkage: Linkage, writable: bool, tls: bool, 110 | ) -> cranelift_module::ModuleResult { 111 | dispatch_on_module!(self, Module::declare_data, name, linkage, writable, tls) 112 | } 113 | 114 | fn declare_anonymous_data( 115 | &mut self, writable: bool, tls: bool, 116 | ) -> cranelift_module::ModuleResult { 117 | dispatch_on_module!(self, Module::declare_anonymous_data, writable, tls) 118 | } 119 | 120 | fn define_function( 121 | &mut self, func: FuncId, ctx: &mut cranelift::codegen::Context, 122 | ) -> cranelift_module::ModuleResult { 123 | dispatch_on_module!(self, Module::define_function, func, ctx) 124 | } 125 | 126 | fn define_function_bytes( 127 | &mut self, func: FuncId, bytes: &[u8], relocs: &[cranelift::codegen::MachReloc], 128 | ) -> cranelift_module::ModuleResult { 129 | dispatch_on_module!(self, Module::define_function_bytes, func, bytes, relocs) 130 | } 131 | 132 | fn define_data( 133 | &mut self, data: cranelift_module::DataId, data_ctx: &DataContext, 134 | ) -> cranelift_module::ModuleResult<()> { 135 | dispatch_on_module!(self, Module::define_data, data, data_ctx) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/error/location.rs: -------------------------------------------------------------------------------- 1 | //! location.rs - Defines the Location struct for storing source 2 | //! Locations throughout the compiler. Most notably, these locations 3 | //! are passed around throughout the parser and are stored in each 4 | //! Ast node, along with several structs in the ModuleCache. 5 | use std::path::Path; 6 | 7 | /// A given Position in a file. These are usually used as 8 | /// start positions for a Location struct. 9 | /// 10 | /// TODO: remove line and column fields to make Position smaller 11 | /// and faster. These can be computed on demand while issuing 12 | /// error messages. Since Locations are used pervasively in the 13 | /// lexer and parser, this would likely speed up compilation. 14 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 15 | pub struct Position { 16 | pub index: usize, 17 | pub line: u32, 18 | pub column: u16, 19 | } 20 | 21 | impl Position { 22 | /// The first position in a file 23 | pub fn begin() -> Position { 24 | Position { index: 0, line: 1, column: 1 } 25 | } 26 | 27 | /// Increment the position 1 character forward 28 | pub fn advance(&mut self, char_len: usize, passed_newline: bool) { 29 | if passed_newline { 30 | self.line += 1; 31 | self.column = 1; 32 | } else { 33 | self.column += 1; 34 | } 35 | self.index += char_len; 36 | } 37 | } 38 | 39 | /// An ending position. Error reporting doesn't need to report 40 | /// the ending line/column of an error so it isn't stored here. 41 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 42 | pub struct EndPosition { 43 | pub index: usize, 44 | } 45 | 46 | impl EndPosition { 47 | pub fn new(index: usize) -> EndPosition { 48 | EndPosition { index } 49 | } 50 | } 51 | 52 | /// A source location for a given Ast node or other construct. 53 | /// The 'c lifetime refers to the ModuleCache which stores 54 | /// the file paths. 55 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 56 | pub struct Location<'c> { 57 | pub filename: &'c Path, 58 | pub start: Position, 59 | pub end: EndPosition, 60 | } 61 | 62 | impl<'c> Ord for Location<'c> { 63 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 64 | (self.start, self.end).cmp(&(other.start, other.end)) 65 | } 66 | } 67 | 68 | impl<'c> PartialOrd for Location<'c> { 69 | fn partial_cmp(&self, other: &Self) -> Option { 70 | Some(self.cmp(other)) 71 | } 72 | } 73 | 74 | impl<'c> Location<'c> { 75 | pub fn new(filename: &'c Path, start: Position, end: EndPosition) -> Location<'c> { 76 | Location { filename, start, end } 77 | } 78 | 79 | /// Returns a location to an item that is built into the compiler and is not 80 | /// actually present in any source code. Care should be taken when defining 81 | /// these types to ensure errors presented to users don't point to the non-existant 82 | /// source location. Example of builtins are the string type and the '.' trait family. 83 | pub fn builtin() -> Location<'c> { 84 | let start = Position { index: 0, line: 0, column: 0 }; 85 | let end = EndPosition { index: 0 }; 86 | // TODO: update to reference prelude_path with appropriate lifetimes 87 | Location::new(Path::new("stdlib/prelude.an"), start, end) 88 | } 89 | 90 | pub fn length(&self) -> usize { 91 | self.end.index - self.start.index 92 | } 93 | 94 | /// Unify the two Locations, returning a new Location that starts at the minimum 95 | /// of both starting points and ends at the maximum of both end points. 96 | pub fn union(&self, other: Location<'c>) -> Location<'c> { 97 | let start = if self.start.index < other.start.index { self.start } else { other.start }; 98 | let end = if self.end.index < other.end.index { other.end } else { self.end }; 99 | 100 | Location { filename: self.filename, start, end } 101 | } 102 | 103 | #[allow(dead_code)] 104 | pub fn contains_index(&self, idx: &usize) -> bool { 105 | (self.start.index..self.end.index).contains(idx) 106 | } 107 | } 108 | 109 | /// A trait representing anything that has a Location 110 | pub trait Locatable<'a> { 111 | fn locate(&self) -> Location<'a>; 112 | } 113 | -------------------------------------------------------------------------------- /src/frontend.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use crate::cache::ModuleCache; 4 | use crate::lexer::Lexer; 5 | use crate::nameresolution::NameResolver; 6 | use crate::parser; 7 | use crate::types; 8 | use crate::util; 9 | 10 | #[derive(PartialEq)] 11 | pub enum FrontendPhase { 12 | Lex, 13 | Parse, 14 | TypeCheck, 15 | } 16 | 17 | pub enum FrontendResult { 18 | Done, 19 | ContinueCompilation, 20 | Errors, 21 | } 22 | 23 | pub fn check<'a>( 24 | filename: &'a Path, main_file_contents: String, cache: &mut ModuleCache<'a>, phase: FrontendPhase, show_time: bool, 25 | ) -> FrontendResult { 26 | util::timing::time_passes(show_time); 27 | 28 | // Phase 1: Lexing 29 | util::timing::start_time("Lexing"); 30 | let tokens = Lexer::new(filename, &main_file_contents).collect::>(); 31 | 32 | if phase == FrontendPhase::Lex { 33 | tokens.iter().for_each(|(token, _)| println!("{}", token)); 34 | return FrontendResult::Done; 35 | } 36 | 37 | // Phase 2: Parsing 38 | util::timing::start_time("Parsing"); 39 | 40 | let root = match parser::parse(&tokens) { 41 | Ok(root) => root, 42 | Err(parse_error) => { 43 | // Parse errors are currently always fatal 44 | cache.push_full_diagnostic(parse_error.into_diagnostic()); 45 | return FrontendResult::Errors; 46 | }, 47 | }; 48 | 49 | if phase == FrontendPhase::Parse { 50 | println!("{}", root); 51 | return FrontendResult::Done; 52 | } 53 | 54 | // Phase 3: Name resolution 55 | // Timing for name resolution is within the start method to 56 | // break up the declare and define passes 57 | NameResolver::start(root, cache); 58 | 59 | if cache.error_count() != 0 { 60 | return FrontendResult::Errors; 61 | } 62 | 63 | // Phase 4: Type inference 64 | util::timing::start_time("Type Inference"); 65 | let ast = cache.parse_trees.get_mut(0).unwrap(); 66 | types::typechecker::infer_ast(ast, cache); 67 | 68 | if cache.error_count() != 0 { 69 | FrontendResult::Errors 70 | } else { 71 | FrontendResult::ContinueCompilation 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/hir/capabilities.rs: -------------------------------------------------------------------------------- 1 | 2 | fn convert_capabilities() { 3 | 4 | } 5 | -------------------------------------------------------------------------------- /src/hir/definitions.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{cache::DefinitionInfoId, types}; 4 | 5 | use super::monomorphisation::Definition; 6 | 7 | pub struct Definitions { 8 | // This is a rather inefficient representation with duplication 9 | // to prevent locals of one type overwriting locals of the same type 10 | // on different instantiations of a function, which can happen in some 11 | // rare instances. 12 | all: DefinitionMap, 13 | local: Vec, 14 | } 15 | 16 | type DefinitionMap = HashMap; 17 | 18 | type DefinitionKey = (DefinitionInfoId, DefinitionType); 19 | 20 | impl Definitions { 21 | pub fn new() -> Self { 22 | Self { all: HashMap::new(), local: vec![HashMap::new()] } 23 | } 24 | 25 | pub fn get(&self, id: DefinitionInfoId, typ: types::Type) -> Option<&Definition> { 26 | let locals = self.local.last().unwrap(); 27 | if let Some(definition) = locals.get(&(id, DefinitionType(typ.clone()))) { 28 | return Some(definition); 29 | } 30 | 31 | self.all.get(&(id, DefinitionType(typ.clone()))) 32 | } 33 | 34 | pub fn insert(&mut self, id: DefinitionInfoId, typ: types::Type, definition: Definition) { 35 | let locals = self.local.last_mut().unwrap(); 36 | locals.insert((id, DefinitionType(typ.clone())), definition.clone()); 37 | self.all.insert((id, DefinitionType(typ)), definition); 38 | } 39 | 40 | pub fn push_local_scope(&mut self) { 41 | self.local.push(HashMap::new()); 42 | } 43 | 44 | pub fn pop_local_scope(&mut self) { 45 | self.local.pop(); 46 | } 47 | } 48 | 49 | /// Wrapper around types::Type to change hashing behavior. 50 | /// We now treat all type variables as if they are compatible 51 | #[derive(Eq)] 52 | struct DefinitionType(types::Type); 53 | 54 | impl std::hash::Hash for DefinitionType { 55 | fn hash(&self, state: &mut H) { 56 | self.0.traverse_no_follow(|typ| { 57 | std::mem::discriminant(typ).hash(state); 58 | 59 | // Hash any extra information, not including type variable ids. 60 | match typ { 61 | types::Type::Primitive(primitive) => primitive.hash(state), 62 | types::Type::UserDefined(id) => id.hash(state), 63 | types::Type::TypeVariable(_) => (), // Do nothing 64 | types::Type::NamedGeneric(..) => (), // Do nothing 65 | types::Type::Function(_) => (), 66 | types::Type::TypeApplication(_, _) => (), 67 | types::Type::Ref { sharedness, mutability, lifetime: _ } => { 68 | sharedness.hash(state); 69 | mutability.hash(state); 70 | }, 71 | types::Type::Struct(field_names, _) => { 72 | for name in field_names { 73 | name.hash(state); 74 | } 75 | }, 76 | types::Type::Effects(set) => { 77 | for (id, _) in &set.effects { 78 | id.hash(state); 79 | } 80 | }, 81 | types::Type::Tag(tag) => tag.hash(state), 82 | } 83 | }) 84 | } 85 | } 86 | 87 | // TODO: May need a more complex, try-unify scheme to remember positions of compatible type variables 88 | impl PartialEq for DefinitionType { 89 | fn eq(&self, other: &Self) -> bool { 90 | definition_type_eq(&self.0, &other.0) 91 | } 92 | } 93 | 94 | fn definition_type_eq(a: &types::Type, b: &types::Type) -> bool { 95 | use types::Type; 96 | match (a, b) { 97 | (Type::Primitive(primitive1), Type::Primitive(primitive2)) => primitive1 == primitive2, 98 | (Type::UserDefined(id1), Type::UserDefined(id2)) => id1 == id2, 99 | (Type::TypeVariable(_), Type::TypeVariable(_)) => true, // Do nothing 100 | // This will monomorphize separate definitions for polymorphically-owned references 101 | // which is undesired. Defaulting them to shared/owned though can change behavior 102 | // if traits are involved. 103 | ( 104 | Type::Ref { sharedness: shared1, mutability: mutable1, .. }, 105 | Type::Ref { sharedness: shared2, mutability: mutable2, .. }, 106 | ) => definition_type_eq(shared1, shared2) && definition_type_eq(mutable1, mutable2), 107 | (Type::Function(f1), Type::Function(f2)) => { 108 | if f1.parameters.len() != f2.parameters.len() { 109 | return false; 110 | } 111 | f1.parameters.iter().zip(&f2.parameters).all(|(p1, p2)| definition_type_eq(p1, p2)) 112 | && definition_type_eq(&f1.environment, &f2.environment) 113 | && definition_type_eq(&f1.return_type, &f2.return_type) 114 | && definition_type_eq(&f1.effects, &f2.effects) 115 | }, 116 | (Type::TypeApplication(constructor1, args1), Type::TypeApplication(constructor2, args2)) => { 117 | if args1.len() != args2.len() { 118 | return false; 119 | } 120 | args1.iter().zip(args2).all(|(p1, p2)| definition_type_eq(p1, p2)) 121 | && definition_type_eq(constructor1, constructor2) 122 | }, 123 | (Type::Struct(field_names1, _), Type::Struct(field_names2, _)) => { 124 | if field_names1.len() != field_names2.len() { 125 | return false; 126 | } 127 | field_names1 128 | .iter() 129 | .zip(field_names2) 130 | .all(|((name1, t1), (name2, t2))| name1 == name2 && definition_type_eq(t1, t2)) 131 | }, 132 | (Type::Effects(set1), Type::Effects(set2)) => { 133 | if set1.effects.len() != set2.effects.len() { 134 | return false; 135 | } 136 | set1.effects.iter().zip(&set2.effects).all(|((id1, args1), (id2, args2))| { 137 | if args1.len() != args2.len() { 138 | return false; 139 | } 140 | id1 == id2 && args1.iter().zip(args2).all(|(t1, t2)| definition_type_eq(t1, t2)) 141 | }) 142 | }, 143 | (Type::Tag(tag1), Type::Tag(tag2)) => tag1 == tag2, 144 | (Type::NamedGeneric(id1, _), Type::NamedGeneric(id2, _)) => id1 == id2, 145 | (othera, otherb) => { 146 | assert_ne!(std::mem::discriminant(othera), std::mem::discriminant(otherb), "ICE: Missing match case"); 147 | false 148 | }, 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/hir/types.rs: -------------------------------------------------------------------------------- 1 | use crate::{lexer::token::FloatKind, util::fmap}; 2 | 3 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 4 | pub enum IntegerKind { 5 | I8, 6 | I16, 7 | I32, 8 | I64, 9 | Isz, 10 | U8, 11 | U16, 12 | U32, 13 | U64, 14 | Usz, 15 | } 16 | 17 | impl IntegerKind { 18 | pub fn size_in_bits(self) -> u64 { 19 | use IntegerKind::*; 20 | match self { 21 | I8 | U8 => 8, 22 | I16 | U16 => 16, 23 | I32 | U32 => 32, 24 | I64 | U64 => 64, 25 | Isz | Usz => std::mem::size_of::<*const i8>() as u64 * 8, 26 | } 27 | } 28 | 29 | pub fn is_unsigned(self) -> bool { 30 | use IntegerKind::*; 31 | match self { 32 | I8 | I16 | I32 | I64 | Isz => false, 33 | U8 | U16 | U32 | U64 | Usz => true, 34 | } 35 | } 36 | } 37 | 38 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 39 | pub enum PrimitiveType { 40 | Integer(IntegerKind), 41 | Float(FloatKind), 42 | Char, 43 | Boolean, 44 | Unit, 45 | Pointer, // An opaque pointer type 46 | Continuation, 47 | } 48 | 49 | #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 50 | pub struct FunctionType { 51 | pub parameters: Vec, 52 | pub return_type: Box, 53 | pub is_varargs: bool, 54 | } 55 | 56 | impl FunctionType { 57 | pub fn new(parameters: Vec, return_type: Type) -> Self { 58 | Self { parameters, return_type: Box::new(return_type), is_varargs: false } 59 | } 60 | } 61 | 62 | /// A HIR type representation. 63 | /// Removes all references to generics and user-defined types. 64 | /// Union variants are also absent, being represented by a struct 65 | /// value and a cast to a different struct type of the largest variant. 66 | #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] 67 | pub enum Type { 68 | Primitive(PrimitiveType), 69 | Function(FunctionType), 70 | 71 | /// Tuples have a TypeId to allow for struct recursion 72 | Tuple(Vec), 73 | } 74 | 75 | impl Type { 76 | pub fn unit() -> Self { 77 | Type::Primitive(PrimitiveType::Unit) 78 | } 79 | 80 | pub fn continuation() -> Self { 81 | Type::Primitive(PrimitiveType::Continuation) 82 | } 83 | 84 | pub fn pointer() -> Self { 85 | Type::Primitive(PrimitiveType::Pointer) 86 | } 87 | 88 | pub fn into_function(self) -> Option { 89 | match self { 90 | Type::Function(f) => Some(f), 91 | _ => None, 92 | } 93 | } 94 | 95 | pub fn size_in_bytes(&self) -> u64 { 96 | match self { 97 | Type::Primitive(p) => match p { 98 | PrimitiveType::Integer(integer) => integer.size_in_bits() / 8, 99 | PrimitiveType::Float(float) => match float { 100 | FloatKind::F32 => 4, 101 | FloatKind::F64 => 8, 102 | }, 103 | PrimitiveType::Char => 1, 104 | PrimitiveType::Boolean => 1, 105 | PrimitiveType::Unit => 1, 106 | PrimitiveType::Pointer | PrimitiveType::Continuation => std::mem::size_of::<*const i8>() as u64, 107 | }, 108 | Type::Function(_) => std::mem::size_of::<*const i8>() as u64, 109 | Type::Tuple(elements) => elements.iter().map(|element| element.size_in_bytes()).sum(), 110 | } 111 | } 112 | 113 | /// The type of the Builtin::ContinuationInit 114 | pub fn continuation_init_type() -> FunctionType { 115 | // mco_coro* mco_coro_init(void(*f)(mco_coro*)); 116 | let init_function_arg = Type::Function(FunctionType::new(vec![Type::continuation()], Type::unit())); 117 | FunctionType::new(vec![init_function_arg], Type::continuation()) 118 | } 119 | 120 | /// The type of the Builtin::ContinuationIsSuspended 121 | pub fn continuation_is_suspended_type() -> FunctionType { 122 | // char mco_coro_is_suspended(mco_coro*, k); 123 | FunctionType::new(vec![Type::continuation()], Type::Primitive(PrimitiveType::Boolean)) 124 | } 125 | 126 | /// The type of the Builtin::ContinuationPush 127 | pub fn continuation_push_type() -> FunctionType { 128 | // void mco_coro_push(mco_coro* k, const void* data, size_t data_size); 129 | let usz = Type::Primitive(PrimitiveType::Integer(IntegerKind::Usz)); 130 | FunctionType::new(vec![Type::continuation(), Type::pointer(), usz], Type::unit()) 131 | } 132 | 133 | /// The type of the Builtin::ContinuationPop 134 | pub fn continuation_pop_type() -> FunctionType { 135 | // void mco_coro_pop(mco_coro* k, void* data, size_t data_size); 136 | let usz = Type::Primitive(PrimitiveType::Integer(IntegerKind::Usz)); 137 | FunctionType::new(vec![Type::continuation(), Type::pointer(), usz], Type::unit()) 138 | } 139 | 140 | /// The type of the Builtin::ContinuationSuspend 141 | pub fn continuation_suspend_type() -> FunctionType { 142 | // void mco_coro_suspend(mco_coro* k); 143 | FunctionType::new(vec![Type::continuation()], Type::unit()) 144 | } 145 | 146 | /// The type of the Builtin::ContinuationResume 147 | pub fn continuation_resume_type() -> FunctionType { 148 | // void mco_coro_resume(mco_coro* k); 149 | FunctionType::new(vec![Type::continuation()], Type::unit()) 150 | } 151 | 152 | /// The type of the Builtin::ContinuationFree 153 | pub fn continuation_free_type() -> FunctionType { 154 | // void mco_coro_free(mco_coro* k); 155 | FunctionType::new(vec![Type::continuation()], Type::unit()) 156 | } 157 | } 158 | 159 | impl std::fmt::Display for Type { 160 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 161 | match self { 162 | Type::Primitive(p) => match p { 163 | PrimitiveType::Integer(kind) => kind.fmt(f), 164 | PrimitiveType::Float(kind) => kind.fmt(f), 165 | PrimitiveType::Char => write!(f, "Char"), 166 | PrimitiveType::Boolean => write!(f, "Bool"), 167 | PrimitiveType::Unit => write!(f, "Unit"), 168 | PrimitiveType::Pointer => write!(f, "Ptr"), 169 | PrimitiveType::Continuation => write!(f, "Cont"), 170 | }, 171 | Type::Function(function) => write!(f, "({})", function), 172 | Type::Tuple(elems) => { 173 | let elems = fmap(elems, ToString::to_string); 174 | write!(f, "{{{}}}", elems.join(", ")) 175 | }, 176 | } 177 | } 178 | } 179 | 180 | impl std::fmt::Display for FunctionType { 181 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 182 | for param in &self.parameters { 183 | write!(f, "{} -> ", param)?; 184 | } 185 | if self.is_varargs { 186 | write!(f, "... -> ")?; 187 | } 188 | write!(f, "{}", self.return_type) 189 | } 190 | } 191 | 192 | impl std::fmt::Display for IntegerKind { 193 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 194 | match self { 195 | IntegerKind::I8 => write!(f, "i8"), 196 | IntegerKind::I16 => write!(f, "i16"), 197 | IntegerKind::I32 => write!(f, "i32"), 198 | IntegerKind::I64 => write!(f, "i64"), 199 | IntegerKind::Isz => write!(f, "isz"), 200 | IntegerKind::U8 => write!(f, "u8"), 201 | IntegerKind::U16 => write!(f, "u16"), 202 | IntegerKind::U32 => write!(f, "u32"), 203 | IntegerKind::U64 => write!(f, "u64"), 204 | IntegerKind::Usz => write!(f, "usz"), 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod parser; 3 | pub mod lexer; 4 | 5 | #[macro_use] 6 | pub mod util; 7 | pub mod frontend; 8 | 9 | #[macro_use] 10 | pub mod error; 11 | pub mod cache; 12 | 13 | #[macro_use] 14 | pub mod hir; 15 | mod lifetimes; 16 | pub mod nameresolution; 17 | pub mod types; 18 | -------------------------------------------------------------------------------- /src/lifetimes/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::cache::ModuleCache; 2 | use crate::parser::ast::Ast; 3 | 4 | #[allow(unused)] 5 | pub fn infer<'c>(_ast: &mut Ast<'c>, _cache: &mut ModuleCache<'c>) {} 6 | -------------------------------------------------------------------------------- /src/llvm/decisiontree.rs: -------------------------------------------------------------------------------- 1 | //! llvm/decisiontree.rs - Defines how to codegen a decision tree 2 | //! via `codegen_tree`. This decisiontree is the result of compiling 3 | //! a match expression into a decisiontree during type inference. 4 | use crate::hir; 5 | use crate::llvm::{CodeGen, Generator}; 6 | use crate::util::fmap; 7 | 8 | use inkwell::basic_block::BasicBlock; 9 | use inkwell::values::BasicValueEnum; 10 | 11 | impl<'g> Generator<'g> { 12 | pub fn codegen_tree(&mut self, match_expr: &hir::Match) -> BasicValueEnum<'g> { 13 | let current_function = self.current_function(); 14 | let end_block = self.context.append_basic_block(current_function, "match_end"); 15 | 16 | let branch_blocks = fmap(&match_expr.branches, |_| self.context.append_basic_block(current_function, "")); 17 | 18 | self.codegen_subtree(&match_expr.decision_tree, &branch_blocks); 19 | 20 | let mut typ = None; 21 | let incoming = branch_blocks 22 | .into_iter() 23 | .zip(match_expr.branches.iter()) 24 | .filter_map(|(block, branch)| { 25 | self.builder.position_at_end(block); 26 | let (value_type, result) = self.codegen_branch(branch, end_block); 27 | typ = Some(value_type); 28 | result 29 | }) 30 | .collect::>(); 31 | 32 | self.builder.position_at_end(end_block); 33 | let phi = self.builder.build_phi(typ.unwrap(), "match_result").expect("Could not build phi"); 34 | 35 | // Inkwell forces us to pass a &[(&dyn BasicValue, _)] which prevents us from 36 | // passing an entire Vec since we'd also need to store the basic values in another 37 | // vec to be able to have a Vec hold references to them to begin with. 38 | for (value, block) in incoming { 39 | phi.add_incoming(&[(&value, block)]); 40 | } 41 | 42 | phi.as_basic_value() 43 | } 44 | 45 | /// Recurse on the given DecisionTree, codegening each switch and remembering 46 | /// all the Leaf nodes that have already been compiled, since these may be 47 | /// repeated in the same DecisionTree. 48 | fn codegen_subtree(&mut self, tree: &hir::DecisionTree, branches: &[BasicBlock<'g>]) { 49 | match tree { 50 | hir::DecisionTree::Leaf(n) => { 51 | self.builder 52 | .build_unconditional_branch(branches[*n]) 53 | .expect("Could not create br during codegen_subtree"); 54 | }, 55 | hir::DecisionTree::Definition(definition, subtree) => { 56 | definition.codegen(self); 57 | self.codegen_subtree(subtree, branches); 58 | }, 59 | hir::DecisionTree::Switch { int_to_switch_on, cases, else_case } => { 60 | let int_to_switch_on = int_to_switch_on.codegen(self); 61 | self.build_switch(int_to_switch_on, cases, else_case, branches); 62 | }, 63 | } 64 | } 65 | 66 | fn build_switch( 67 | &mut self, int_to_switch_on: BasicValueEnum<'g>, cases: &[(u32, hir::DecisionTree)], 68 | else_case: &Option>, branches: &[BasicBlock<'g>], 69 | ) { 70 | let starting_block = self.current_block(); 71 | let cases = fmap(cases, |(tag, subtree)| { 72 | let tag = self.context.i8_type().const_int(*tag as u64, true); 73 | let new_block = self.insert_into_new_block(""); 74 | self.codegen_subtree(subtree, branches); 75 | (tag, new_block) 76 | }); 77 | 78 | let else_block = self.insert_into_new_block("match_else"); 79 | 80 | if let Some(subtree) = else_case { 81 | self.codegen_subtree(subtree, branches); 82 | } else { 83 | self.builder.build_unreachable().expect("Could not create unreachable during build_switch"); 84 | } 85 | 86 | self.builder.position_at_end(starting_block); 87 | let tag = int_to_switch_on.into_int_value(); 88 | self.builder.build_switch(tag, else_block, &cases).expect("Could not build switch"); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! main.rs - The entry point for the Ante compiler. 2 | //! Handles command-line argument parsing and dataflow between 3 | //! each compiler phase. The compiler as a whole is separated into 4 | //! the following phases (in order): 5 | //! 6 | //! lexing -> parsing -> name resolution -> type inference -> monomorphisation -> codegen 7 | //! 8 | //! Each phase corresponds to a source folder with roughly the same name (though the codegen 9 | //! folder is named "llvm"), and each phase after parsing operates by traversing the AST. 10 | //! This AST traversal is usually defined in the mod.rs file for that phase and is a good 11 | //! place to start if you're trying to learn how that phase works. An exception is type 12 | //! inference which has its AST pass defined in types/typechecker.rs rather than types/mod.rs. 13 | //! Note that sometimes "phases" are sometimes called "passes" and vice-versa - the terms are 14 | //! interchangeable. 15 | #[macro_use] 16 | mod parser; 17 | mod lexer; 18 | 19 | #[macro_use] 20 | mod util; 21 | mod frontend; 22 | 23 | #[macro_use] 24 | mod error; 25 | mod cache; 26 | mod cli; 27 | 28 | #[macro_use] 29 | mod hir; 30 | mod cranelift_backend; 31 | mod lifetimes; 32 | 33 | #[allow(unused)] 34 | mod mir; 35 | mod nameresolution; 36 | mod types; 37 | 38 | #[cfg(feature = "llvm")] 39 | mod llvm; 40 | 41 | use cache::ModuleCache; 42 | use cli::{Backend, Cli, Completions, EmitTarget}; 43 | use frontend::{check, FrontendPhase, FrontendResult}; 44 | 45 | use clap::{CommandFactory, Parser}; 46 | use clap_complete as clap_cmp; 47 | use std::collections::HashMap; 48 | use std::fs::File; 49 | use std::io::{stdout, BufReader, Read}; 50 | use std::path::Path; 51 | 52 | #[global_allocator] 53 | static ALLOCATOR: mimalloc::MiMalloc = mimalloc::MiMalloc; 54 | 55 | /// Called when the "--check --show-types" command-line flags are given. 56 | /// Iterates through each Definition from the first compiled module (so excluding imports) 57 | /// and prints the type and required traits for each. 58 | fn print_definition_types(cache: &ModuleCache) { 59 | let resolver = cache.name_resolvers.get_mut(0).unwrap(); 60 | let mut definitions = resolver.exports.definitions.iter().collect::>(); 61 | 62 | // Make sure the output has a deterministic order for testing 63 | definitions.sort(); 64 | 65 | for (name, definition_id) in definitions { 66 | let info = &cache[*definition_id]; 67 | 68 | if let Some(typ) = &info.typ { 69 | let type_string = types::typeprinter::show_type_and_traits( 70 | name, 71 | typ, 72 | &info.required_traits, 73 | &info.trait_info, 74 | cache, 75 | true, 76 | ); 77 | println!("{}", type_string); 78 | } else { 79 | println!("{} : (none)", name); 80 | } 81 | } 82 | } 83 | 84 | fn print_completions(gen: G) { 85 | let mut cmd = Cli::command(); 86 | let name = cmd.get_name().to_string(); 87 | clap_cmp::generate(gen, &mut cmd, name, &mut stdout()); 88 | } 89 | 90 | /// Convenience macro for unwrapping a Result or printing an error message and returning () on Err. 91 | macro_rules! expect {( $result:expr , $fmt_string:expr $( , $($msg:tt)* )? ) => ({ 92 | match $result { 93 | Ok(t) => t, 94 | Err(_) => { 95 | print!($fmt_string $( , $($msg)* )? ); 96 | return (); 97 | }, 98 | } 99 | });} 100 | 101 | pub fn main() { 102 | if let Ok(Completions { shell_completion }) = Completions::try_parse() { 103 | print_completions(shell_completion); 104 | } else { 105 | compile(Cli::parse()) 106 | } 107 | } 108 | 109 | fn compile(args: Cli) { 110 | // Setup the cache and read from the first file 111 | let filename = Path::new(&args.file); 112 | let file = File::open(filename); 113 | let file = expect!(file, "Could not open file {}\n", filename.display()); 114 | let mut reader = BufReader::new(file); 115 | let mut contents = String::new(); 116 | expect!(reader.read_to_string(&mut contents), "Failed to read {} into a string\n", filename.display()); 117 | 118 | let filename = if filename.is_relative() { 119 | let cwd = expect!(std::env::current_dir(), "Could not get current directory\n"); 120 | expect!(cwd.join(filename).canonicalize(), "Could not canonicalize {}\n", filename.display()) 121 | } else { 122 | expect!(filename.canonicalize(), "Could not canonicalize {}\n", filename.display()) 123 | }; 124 | let parent = filename.parent().unwrap(); 125 | 126 | let file_cache = HashMap::from([(filename.clone(), contents.clone())]); 127 | 128 | let mut cache = ModuleCache::new(parent, file_cache); 129 | 130 | error::color_output(!args.no_color); 131 | 132 | let phase = if args.lex { 133 | FrontendPhase::Lex 134 | } else if args.parse { 135 | FrontendPhase::Parse 136 | } else { 137 | FrontendPhase::TypeCheck 138 | }; 139 | 140 | match check(&filename, contents, &mut cache, phase, args.show_time) { 141 | FrontendResult::Done => return, 142 | FrontendResult::ContinueCompilation => (), 143 | FrontendResult::Errors => { 144 | cache.display_diagnostics(); 145 | 146 | if args.show_types { 147 | print_definition_types(&cache); 148 | } 149 | return; 150 | }, 151 | } 152 | 153 | let ast = cache.parse_trees.get_mut(0).unwrap(); 154 | 155 | if args.show_types { 156 | print_definition_types(&cache); 157 | } 158 | 159 | if args.check || cache.error_count() != 0 { 160 | return; 161 | } 162 | 163 | let hir = hir::monomorphise(ast, cache); 164 | if args.emit == Some(EmitTarget::Hir) { 165 | println!("{}", hir); 166 | return; 167 | } 168 | 169 | // Phase 5: Lifetime inference 170 | // util::timing::start_time("Lifetime Inference"); 171 | // lifetimes::infer(ast, &mut cache); 172 | 173 | // if args.show_lifetimes { 174 | // println!("{}", ast); 175 | // } 176 | 177 | // Phase 6: Codegen 178 | let default_backend = if args.opt_level == '0' { Backend::Cranelift } else { Backend::Llvm }; 179 | let backend = args.backend.unwrap_or(default_backend); 180 | 181 | match backend { 182 | Backend::Cranelift => cranelift_backend::run(&filename, hir, &args), 183 | Backend::Llvm => { 184 | if cfg!(feature = "llvm") { 185 | #[cfg(feature = "llvm")] 186 | llvm::run(&filename, hir, &args); 187 | } else { 188 | eprintln!("The llvm backend is required for non-debug builds. Recompile ante with --features 'llvm' to enable optimized builds."); 189 | } 190 | }, 191 | } 192 | 193 | // Print out the time each compiler pass took to complete if the --show-time flag was passed 194 | util::timing::show_timings(); 195 | } 196 | -------------------------------------------------------------------------------- /src/mir/ir/block.rs: -------------------------------------------------------------------------------- 1 | use crate::util::Id; 2 | 3 | use super::{ 4 | instruction::{InstructionId, TerminatorInstruction}, 5 | Type, 6 | }; 7 | 8 | pub struct Block { 9 | parameters: Vec, 10 | instructions: Vec, 11 | terminator: TerminatorInstruction, 12 | } 13 | 14 | pub type BlockId = Id; 15 | -------------------------------------------------------------------------------- /src/mir/ir/function.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::util::{Id, VecMap}; 4 | 5 | use super::{ 6 | instruction::{Instruction, InstructionId}, 7 | Block, BlockId, Globals, 8 | }; 9 | 10 | pub struct Function { 11 | name: String, 12 | entry_block: BlockId, 13 | 14 | globals: Rc, 15 | blocks: VecMap, 16 | instructions: VecMap, 17 | } 18 | 19 | pub type FunctionId = Id; 20 | -------------------------------------------------------------------------------- /src/mir/ir/instruction.rs: -------------------------------------------------------------------------------- 1 | use crate::util::Id; 2 | 3 | use super::{BlockId, Type, Value}; 4 | 5 | pub type InstructionId = Id; 6 | 7 | pub enum Instruction { 8 | Builtin(Builtin), 9 | Call { function: Value, args: Vec }, 10 | 11 | Allocate(Type), 12 | Load(Value), 13 | Store { address: Value, value: Value }, 14 | } 15 | 16 | pub enum TerminatorInstruction { 17 | Jump { dest: BlockId, args: Vec }, 18 | JumpIf { cond: Value, then: BlockId, else_: BlockId }, 19 | Switch { int_value: Value, cases: Vec, else_: Option }, 20 | Return { args: Vec }, 21 | } 22 | 23 | pub enum Builtin { 24 | AddInt(Value, Value), 25 | AddFloat(Value, Value), 26 | 27 | SubInt(Value, Value), 28 | SubFloat(Value, Value), 29 | 30 | MulInt(Value, Value), 31 | MulFloat(Value, Value), 32 | 33 | DivSigned(Value, Value), 34 | DivUnsigned(Value, Value), 35 | DivFloat(Value, Value), 36 | 37 | ModSigned(Value, Value), 38 | ModUnsigned(Value, Value), 39 | ModFloat(Value, Value), 40 | 41 | LessSigned(Value, Value), 42 | LessUnsigned(Value, Value), 43 | LessFloat(Value, Value), 44 | 45 | EqInt(Value, Value), 46 | EqFloat(Value, Value), 47 | EqChar(Value, Value), 48 | EqBool(Value, Value), 49 | 50 | SignExtend(Value, Type), 51 | ZeroExtend(Value, Type), 52 | 53 | SignedToFloat(Value, Type), 54 | UnsignedToFloat(Value, Type), 55 | FloatToSigned(Value, Type), 56 | FloatToUnsigned(Value, Type), 57 | FloatPromote(Value), 58 | FloatDemote(Value), 59 | 60 | BitwiseAnd(Value, Value), 61 | BitwiseOr(Value, Value), 62 | BitwiseXor(Value, Value), 63 | BitwiseNot(Value), 64 | 65 | Truncate(Value, Type), 66 | Deref(Value, Type), 67 | Offset(Value, Value, Type), 68 | Transmute(Value, Type), 69 | 70 | /// Allocate space for the given value on the stack, and store it there. Return the stack address 71 | StackAlloc(Value), 72 | } 73 | -------------------------------------------------------------------------------- /src/mir/ir/ir.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/mir/ir/mod.rs: -------------------------------------------------------------------------------- 1 | mod block; 2 | mod function; 3 | mod instruction; 4 | mod value; 5 | 6 | pub use crate::hir::Type; 7 | use crate::util::VecMap; 8 | pub use block::{Block, BlockId}; 9 | pub use function::{Function, FunctionId}; 10 | pub use instruction::{Builtin, Instruction, InstructionId, TerminatorInstruction}; 11 | pub use value::Value; 12 | 13 | pub struct Mir { 14 | functions: VecMap, 15 | main: FunctionId, 16 | } 17 | 18 | pub struct Globals { 19 | function_types: VecMap, 20 | } 21 | -------------------------------------------------------------------------------- /src/mir/ir/value.rs: -------------------------------------------------------------------------------- 1 | use crate::util::Id; 2 | 3 | use super::{BlockId, FunctionId, InstructionId}; 4 | 5 | pub enum Value { 6 | Result(InstructionId, /*result index*/ u32), 7 | Constant(ConstantId), 8 | Parameter(BlockId, /*parameter index*/ u32), 9 | Function(FunctionId), 10 | } 11 | 12 | pub struct Constant {} 13 | 14 | pub type ConstantId = Id; 15 | -------------------------------------------------------------------------------- /src/mir/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ir; 2 | -------------------------------------------------------------------------------- /src/nameresolution/builtin.rs: -------------------------------------------------------------------------------- 1 | //! nameresolution/builtin.rs - Helpers for importing the prelude 2 | //! and defining some builtin symbols such as the `string` type (which 3 | //! is builtin because otherwise string literals may be used before the 4 | //! actual string type is defined) and the `builtin` function which is 5 | //! used by codegen to stand in place of primitive operations like adding 6 | //! integers together. 7 | use crate::cache::{DefinitionInfoId, DefinitionKind, ModuleCache}; 8 | use crate::error::location::Location; 9 | use crate::lexer::token::{IntegerKind, Token}; 10 | use crate::nameresolution::{declare_module, define_module, NameResolver}; 11 | use crate::types::effects::EffectSet; 12 | use crate::types::{ 13 | Field, FunctionType, GeneralizedType, LetBindingLevel, PrimitiveType, Type, TypeInfoBody, PAIR_TYPE, STRING_TYPE, 14 | }; 15 | 16 | use std::collections::HashSet; 17 | use std::path::PathBuf; 18 | 19 | /// The DefinitionInfoId of the `builtin` symbol is defined to be 20 | /// the first id. This invariant needs to be maintained by the 21 | /// `define_builtins` function here being called before any other 22 | /// symbol is defined. This is asserted to be the case within that function. 23 | pub const BUILTIN_ID: DefinitionInfoId = DefinitionInfoId(0); 24 | 25 | /// DefinitionInfoId for the string constructor to construct a string from its raw parts 26 | pub const STRING_ID: DefinitionInfoId = DefinitionInfoId(1); 27 | 28 | /// DefinitionInfoId for the pair constructor `,` to construct values like (1, 2) 29 | pub const PAIR_ID: DefinitionInfoId = DefinitionInfoId(2); 30 | 31 | /// Defines the builtin symbols: 32 | /// - `type String = c_string: Ptr Char, length: Usz` 33 | /// - `builtin : String -> a` used by the codegen pass to implement 34 | /// codegen of builtin operations such as adding integers. 35 | /// 36 | /// This function needs to be called before any other DefinitionInfoId is 37 | /// created, otherwise the `builtin` symbol will have the wrong id. If this 38 | /// happens, this function will assert at runtime. 39 | pub fn define_builtins(cache: &mut ModuleCache) { 40 | // Define builtin : forall a. string -> a imported only into the prelude to define 41 | // builtin operations by name. The specific string arguments are matched on in src/llvm/builtin.rs 42 | let id = cache.push_definition("builtin", true, Location::builtin()); 43 | assert_eq!(id, BUILTIN_ID); 44 | 45 | let string_type = define_string(cache); 46 | define_pair(cache); 47 | 48 | let a = cache.next_type_variable_id(LetBindingLevel(1)); 49 | let e = cache.next_type_variable_id(LetBindingLevel(1)); 50 | 51 | let builtin_fn_type = Type::Function(FunctionType { 52 | parameters: vec![string_type], 53 | return_type: Box::new(Type::TypeVariable(a)), 54 | environment: Box::new(Type::UNIT), 55 | effects: Box::new(Type::Effects(EffectSet::pure())), 56 | has_varargs: true, 57 | }); 58 | 59 | let builtin_type = GeneralizedType::PolyType(vec![a, e], builtin_fn_type); 60 | cache.definition_infos[id.0].typ = Some(builtin_type); 61 | } 62 | 63 | /// The prelude is currently stored (along with the rest of the stdlib) in the 64 | /// user's config directory since it is a cross-platform concept that doesn't 65 | /// require administrator priviledges. 66 | pub fn prelude_path() -> PathBuf { 67 | crate::util::stdlib_dir().join("prelude.an") 68 | } 69 | 70 | pub fn import_prelude(resolver: &mut NameResolver, cache: &mut ModuleCache<'_>) { 71 | if resolver.filepath == prelude_path() { 72 | // If we're in the prelude include the built-in symbol "builtin" to define primitives 73 | resolver.current_scope().definitions.insert("builtin".into(), BUILTIN_ID); 74 | } else { 75 | // Otherwise, import the prelude itself 76 | let prelude_dir = prelude_path(); 77 | if let Some(id) = declare_module(&prelude_dir, cache, Location::builtin()) { 78 | let exports = define_module(id, cache, Location::builtin()).unwrap(); 79 | resolver.current_scope().import(exports, cache, Location::builtin(), &HashSet::new()); 80 | } 81 | } 82 | 83 | // Manually insert some builtins as if they were defined in the prelude 84 | resolver.current_scope().types.insert(Token::Comma.to_string(), PAIR_TYPE); 85 | resolver.current_scope().definitions.insert(Token::Comma.to_string(), PAIR_ID); 86 | resolver.current_scope().definitions.insert("String".into(), STRING_ID); 87 | } 88 | 89 | /// Defining the 'string' type is a bit different than most other builtins. Since 'string' has 90 | /// its own dedicated keyword it need not be imported into scope like each impl of + or - does. 91 | /// 92 | /// The builtin string type is defined here as: 93 | /// 94 | /// type string = c_string: Ptr char, length: usz 95 | fn define_string(cache: &mut ModuleCache) -> Type { 96 | let location = Location::builtin(); 97 | 98 | let ptr_type = Type::Primitive(PrimitiveType::Ptr); 99 | let char_type = Type::Primitive(PrimitiveType::CharType); 100 | let c_string_type = Type::TypeApplication(Box::new(ptr_type), vec![char_type]); 101 | 102 | let length_type = Type::int(IntegerKind::Usz); 103 | 104 | let name = "String".to_string(); 105 | let string_id = cache.push_type_info(name.clone(), vec![], location); 106 | assert_eq!(string_id, STRING_TYPE); 107 | let string = Type::UserDefined(STRING_TYPE); 108 | 109 | let fields = TypeInfoBody::Struct(vec![ 110 | Field { name: "c_string".into(), field_type: c_string_type.clone(), location }, 111 | Field { name: "length".into(), field_type: length_type.clone(), location }, 112 | ]); 113 | 114 | let constructor = cache.push_definition(&name, true, location); 115 | assert_eq!(constructor, STRING_ID); 116 | 117 | let effects = cache.next_type_variable_id(LetBindingLevel(1)); 118 | 119 | let constructor_type = Type::Function(FunctionType { 120 | parameters: vec![c_string_type, length_type], 121 | return_type: Box::new(string.clone()), 122 | environment: Box::new(Type::UNIT), 123 | effects: Box::new(Type::Effects(EffectSet::pure())), 124 | has_varargs: false, 125 | }); 126 | 127 | let polytype = GeneralizedType::PolyType(vec![effects], constructor_type); 128 | 129 | cache.definition_infos[constructor.0].typ = Some(polytype); 130 | cache.definition_infos[constructor.0].definition = Some(DefinitionKind::TypeConstructor { name, tag: None }); 131 | 132 | cache.type_infos[string_id.0].body = fields; 133 | string 134 | } 135 | 136 | /// The builtin pair type is defined here as: 137 | /// 138 | /// type (,) a b = first: a, second: b 139 | fn define_pair(cache: &mut ModuleCache) { 140 | let location = Location::builtin(); 141 | 142 | let level = LetBindingLevel(0); 143 | let a = cache.next_type_variable_id(level); 144 | let b = cache.next_type_variable_id(level); 145 | 146 | let name = Token::Comma.to_string(); 147 | let pair = cache.push_type_info(name.clone(), vec![a, b], location); 148 | assert_eq!(pair, PAIR_TYPE); 149 | 150 | cache.type_infos[pair.0].body = TypeInfoBody::Struct(vec![ 151 | Field { name: "first".into(), field_type: Type::TypeVariable(a), location }, 152 | Field { name: "second".into(), field_type: Type::TypeVariable(b), location }, 153 | ]); 154 | 155 | // The type is defined, now we define the constructor 156 | let parameters = vec![Type::TypeVariable(a), Type::TypeVariable(b)]; 157 | let pair = Box::new(Type::UserDefined(pair)); 158 | let pair_a_b = Box::new(Type::TypeApplication(pair, parameters.clone())); 159 | 160 | let e = cache.next_type_variable_id(level); 161 | 162 | let constructor_type = Type::Function(FunctionType { 163 | parameters, 164 | return_type: pair_a_b, 165 | environment: Box::new(Type::UNIT), 166 | effects: Box::new(Type::Effects(EffectSet::pure())), 167 | has_varargs: false, 168 | }); 169 | 170 | let constructor_type = GeneralizedType::PolyType(vec![a, b, e], constructor_type); 171 | 172 | // and now register a new type constructor in the cache with the given type 173 | let id = cache.push_definition(&name, true, location); 174 | assert_eq!(id, PAIR_ID); 175 | let constructor = DefinitionKind::TypeConstructor { name, tag: None }; 176 | 177 | cache.definition_infos[id.0].typ = Some(constructor_type); 178 | cache.definition_infos[id.0].definition = Some(constructor); 179 | } 180 | -------------------------------------------------------------------------------- /src/nameresolution/free_variables.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, HashSet}; 2 | 3 | use crate::{ 4 | cache::{DefinitionInfoId, ModuleCache}, 5 | parser::ast, 6 | types::{typed::Typed, Type}, 7 | }; 8 | 9 | impl<'c> ast::Handle<'c> { 10 | pub fn find_free_variables(&self, cache: &ModuleCache<'c>) -> BTreeMap { 11 | let mut context = Context::new(cache); 12 | self.find_free_vars(&mut context); 13 | for variable in context.nonfree_variables { 14 | context.free_variables.remove(&variable); 15 | } 16 | context.free_variables 17 | } 18 | } 19 | 20 | impl<'c> ast::Ast<'c> { 21 | pub fn find_free_variables(&self, cache: &ModuleCache<'c>) -> BTreeMap { 22 | let mut context = Context::new(cache); 23 | self.find_free_vars(&mut context); 24 | for variable in context.nonfree_variables { 25 | context.free_variables.remove(&variable); 26 | } 27 | context.free_variables 28 | } 29 | } 30 | 31 | struct Context<'local, 'cache> { 32 | /// This is a BTreeMap so that collecting it into a Vec later yields a 33 | /// deterministic ordering. 34 | free_variables: BTreeMap, 35 | nonfree_variables: HashSet, 36 | cache: &'local ModuleCache<'cache>, 37 | } 38 | 39 | impl<'local, 'cache> Context<'local, 'cache> { 40 | fn new(cache: &'local ModuleCache<'cache>) -> Self { 41 | Self { cache, free_variables: Default::default(), nonfree_variables: Default::default() } 42 | } 43 | } 44 | 45 | trait FreeVars { 46 | fn find_free_vars(&self, ctx: &mut Context); 47 | 48 | fn remove_vars_defined_here(&self, ctx: &mut Context) { 49 | let mut remove_context = Context::new(ctx.cache); 50 | self.find_free_vars(&mut remove_context); 51 | 52 | for id in remove_context.free_variables.into_keys() { 53 | ctx.nonfree_variables.insert(id); 54 | } 55 | } 56 | } 57 | 58 | impl<'c> FreeVars for ast::Ast<'c> { 59 | fn find_free_vars(&self, ctx: &mut Context) { 60 | dispatch_on_expr!(self, FreeVars::find_free_vars, ctx) 61 | } 62 | } 63 | 64 | impl<'c> FreeVars for ast::Literal<'c> { 65 | fn find_free_vars(&self, _ctx: &mut Context) { 66 | // Nothing to do 67 | } 68 | } 69 | 70 | impl<'c> FreeVars for ast::Variable<'c> { 71 | fn find_free_vars(&self, ctx: &mut Context) { 72 | let id = self.definition.unwrap(); 73 | 74 | if !ctx.cache.definition_infos[id.0].global && !ctx.free_variables.contains_key(&id) { 75 | let t = self.get_type().unwrap().clone(); 76 | ctx.free_variables.insert(id, t); 77 | } 78 | } 79 | } 80 | 81 | impl<'c> FreeVars for ast::Lambda<'c> { 82 | fn find_free_vars(&self, ctx: &mut Context) { 83 | self.body.find_free_vars(ctx); 84 | 85 | for parameter in &self.args { 86 | parameter.remove_vars_defined_here(ctx); 87 | } 88 | } 89 | } 90 | 91 | impl<'c> FreeVars for ast::FunctionCall<'c> { 92 | fn find_free_vars(&self, ctx: &mut Context) { 93 | self.function.find_free_vars(ctx); 94 | for arg in &self.args { 95 | arg.find_free_vars(ctx); 96 | } 97 | } 98 | } 99 | 100 | impl<'c> FreeVars for ast::Definition<'c> { 101 | fn find_free_vars(&self, ctx: &mut Context) { 102 | self.expr.find_free_vars(ctx); 103 | self.pattern.remove_vars_defined_here(ctx); 104 | } 105 | } 106 | 107 | impl<'c> FreeVars for ast::If<'c> { 108 | fn find_free_vars(&self, ctx: &mut Context) { 109 | self.condition.find_free_vars(ctx); 110 | self.then.find_free_vars(ctx); 111 | self.otherwise.find_free_vars(ctx); 112 | } 113 | } 114 | 115 | impl<'c> FreeVars for ast::Match<'c> { 116 | fn find_free_vars(&self, ctx: &mut Context) { 117 | self.expression.find_free_vars(ctx); 118 | 119 | for (pattern, branch) in &self.branches { 120 | branch.find_free_vars(ctx); 121 | pattern.remove_vars_defined_here(ctx); 122 | } 123 | } 124 | } 125 | 126 | impl<'c> FreeVars for ast::TypeDefinition<'c> { 127 | fn find_free_vars(&self, _ctx: &mut Context) { 128 | // Nothing to do 129 | } 130 | } 131 | 132 | impl<'c> FreeVars for ast::TypeAnnotation<'c> { 133 | fn find_free_vars(&self, ctx: &mut Context) { 134 | self.lhs.find_free_vars(ctx); 135 | } 136 | } 137 | 138 | impl<'c> FreeVars for ast::Import<'c> { 139 | fn find_free_vars(&self, _ctx: &mut Context) { 140 | // Nothing to do 141 | } 142 | } 143 | 144 | impl<'c> FreeVars for ast::TraitDefinition<'c> { 145 | fn find_free_vars(&self, _ctx: &mut Context) { 146 | // Nothing to do 147 | } 148 | } 149 | 150 | impl<'c> FreeVars for ast::TraitImpl<'c> { 151 | fn find_free_vars(&self, _ctx: &mut Context) { 152 | // Nothing to do 153 | } 154 | } 155 | 156 | impl<'c> FreeVars for ast::Return<'c> { 157 | fn find_free_vars(&self, ctx: &mut Context) { 158 | self.expression.find_free_vars(ctx); 159 | } 160 | } 161 | 162 | impl<'c> FreeVars for ast::Sequence<'c> { 163 | fn find_free_vars(&self, ctx: &mut Context) { 164 | for statement in &self.statements { 165 | statement.find_free_vars(ctx); 166 | } 167 | } 168 | } 169 | 170 | impl<'c> FreeVars for ast::Extern<'c> { 171 | fn find_free_vars(&self, _ctx: &mut Context) { 172 | // Nothing to do 173 | } 174 | } 175 | 176 | impl<'c> FreeVars for ast::MemberAccess<'c> { 177 | fn find_free_vars(&self, ctx: &mut Context) { 178 | self.lhs.find_free_vars(ctx); 179 | } 180 | } 181 | 182 | impl<'c> FreeVars for ast::Assignment<'c> { 183 | fn find_free_vars(&self, ctx: &mut Context) { 184 | self.lhs.find_free_vars(ctx); 185 | self.rhs.find_free_vars(ctx); 186 | } 187 | } 188 | 189 | impl<'c> FreeVars for ast::EffectDefinition<'c> { 190 | fn find_free_vars(&self, _ctx: &mut Context) { 191 | // Nothing to do 192 | } 193 | } 194 | 195 | impl<'c> FreeVars for ast::Handle<'c> { 196 | fn find_free_vars(&self, ctx: &mut Context) { 197 | self.expression.find_free_vars(ctx); 198 | 199 | for (pattern, branch) in &self.branches { 200 | branch.find_free_vars(ctx); 201 | pattern.remove_vars_defined_here(ctx); 202 | } 203 | 204 | for resume in &self.resumes { 205 | ctx.free_variables.remove(resume); 206 | } 207 | } 208 | } 209 | 210 | impl<'c> FreeVars for ast::NamedConstructor<'c> { 211 | fn find_free_vars(&self, ctx: &mut Context) { 212 | self.sequence.find_free_vars(ctx); 213 | } 214 | } 215 | 216 | impl<'c> FreeVars for ast::Reference<'c> { 217 | fn find_free_vars(&self, ctx: &mut Context) { 218 | self.expression.find_free_vars(ctx); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/nameresolution/visitor.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/parser/error.rs: -------------------------------------------------------------------------------- 1 | //! parser/error.rs - Defines the ParseError type and the formatting shown 2 | //! when printing this error to stderr. 3 | use super::combinators::Input; 4 | use crate::error::location::{Locatable, Location}; 5 | use crate::error::{Diagnostic, DiagnosticKind as D}; 6 | use crate::lexer::token::{LexerError, Token}; 7 | use crate::util::fmap; 8 | 9 | #[derive(Debug)] 10 | pub enum ParseError<'a> { 11 | /// A parsing error may not be fatal if it can be ignored because 12 | /// e.g. the parser is currently within an `or([...])` combinator that 13 | /// succeeds if any of the parsers in its array succeed. 14 | Fatal(Box>), 15 | 16 | /// Expected any of the given tokens, but found... whatever is at the 17 | /// source Location instead 18 | Expected(Vec, Location<'a>), 19 | 20 | /// Failed while in the given parsing rule. E.g. "failed to parse a type". 21 | /// Due to backtracking this error is somewhat rare since the parser tends 22 | /// to backtrack trying to parse something else instead of failing in the 23 | /// rule that parsed the furthest. Proper usage of !<- (or `no_backtracking`) 24 | /// helps mediate this somewhat. 25 | InRule(&'static str, Location<'a>), 26 | 27 | /// Found a Token::Invalid issued by the lexer, containing some LexerError. 28 | /// These errors are always wrapped in a Fatal. 29 | LexerError(LexerError, Location<'a>), 30 | } 31 | 32 | pub type ParseResult<'local, 'cache, T> = Result<(Input<'local, 'cache>, T, Location<'cache>), ParseError<'cache>>; 33 | 34 | impl<'a> Locatable<'a> for ParseError<'a> { 35 | fn locate(&self) -> Location<'a> { 36 | match self { 37 | ParseError::Fatal(error) => error.locate(), 38 | ParseError::Expected(_, location) => *location, 39 | ParseError::InRule(_, location) => *location, 40 | ParseError::LexerError(_, location) => *location, 41 | } 42 | } 43 | } 44 | 45 | impl<'a> ParseError<'a> { 46 | pub fn into_diagnostic(self) -> Diagnostic<'a> { 47 | match self { 48 | ParseError::Fatal(error) => error.into_diagnostic(), 49 | ParseError::Expected(tokens, location) => { 50 | let tokens = fmap(&tokens, ToString::to_string); 51 | Diagnostic::new(location, D::ParserExpected(tokens)) 52 | }, 53 | ParseError::InRule(rule, location) => Diagnostic::new(location, D::ParserErrorInRule(rule)), 54 | ParseError::LexerError(error, location) => Diagnostic::new(location, D::LexerError(error.to_string())), 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/types/mutual_recursion.rs: -------------------------------------------------------------------------------- 1 | use crate::cache::MutualRecursionId; 2 | 3 | use crate::types::GeneralizedType; 4 | use crate::{ 5 | cache::{DefinitionInfoId, DefinitionKind, ModuleCache, VariableId}, 6 | error::location::Locatable, 7 | error::DiagnosticKind as D, 8 | parser::ast, 9 | types::{ 10 | traitchecker, 11 | typechecker::{bind_irrefutable_pattern, find_all_typevars}, 12 | typed::Typed, 13 | }, 14 | util::trustme, 15 | }; 16 | 17 | use super::{ 18 | traits::{Callsite, RequiredTrait, TraitConstraints}, 19 | Type, 20 | }; 21 | 22 | pub(super) fn try_generalize_definition<'c>( 23 | definition: &mut ast::Definition<'c>, t: Type, traits: TraitConstraints, cache: &mut ModuleCache<'c>, 24 | ) -> TraitConstraints { 25 | if !should_generalize(&definition.expr, cache) { 26 | return traits; 27 | } 28 | 29 | let pattern = definition.pattern.as_mut(); 30 | match is_mutually_recursive(pattern, cache) { 31 | MutualRecursionResult::No => { 32 | let typevars_in_fn = find_all_typevars(pattern.get_type().unwrap(), false, cache); 33 | 34 | let exposed_traits = traitchecker::resolve_traits(traits, &typevars_in_fn, cache); 35 | bind_irrefutable_pattern(pattern, &t, &exposed_traits, true, cache); 36 | vec![] 37 | }, 38 | MutualRecursionResult::YesGeneralizeLater => traits, 39 | MutualRecursionResult::YesGeneralizeNow(id) => { 40 | // Generalize all the mutually recursive definitions at once 41 | for id in cache.mutual_recursion_sets[id.0].definitions.clone() { 42 | let info = &mut cache.definition_infos[id.0]; 43 | info.undergoing_type_inference = false; 44 | 45 | let t = info.typ.as_ref().unwrap().remove_forall().clone(); 46 | 47 | let definition = match &mut info.definition { 48 | Some(DefinitionKind::Definition(definition)) => trustme::extend_lifetime(*definition), 49 | _ => unreachable!(), 50 | }; 51 | 52 | let pattern = &mut definition.pattern.as_mut(); 53 | let typevars_in_fn = find_all_typevars(pattern.get_type().unwrap(), false, cache); 54 | 55 | let mut exposed_traits = traitchecker::resolve_traits(traits.clone(), &typevars_in_fn, cache); 56 | let callsites = &cache[id].mutually_recursive_variables; 57 | 58 | exposed_traits.append(&mut update_callsites(exposed_traits.clone(), callsites)); 59 | bind_irrefutable_pattern(pattern, &t, &exposed_traits, true, cache); 60 | } 61 | 62 | let root = cache.mutual_recursion_sets[id.0].root_definition; 63 | cache[root].undergoing_type_inference = false; 64 | let typevars_in_fn = find_all_typevars(pattern.get_type().unwrap(), false, cache); 65 | 66 | let mut exposed_traits = traitchecker::resolve_traits(traits, &typevars_in_fn, cache); 67 | let callsites = &cache[root].mutually_recursive_variables; 68 | 69 | exposed_traits.append(&mut update_callsites(exposed_traits.clone(), callsites)); 70 | bind_irrefutable_pattern(pattern, &t, &exposed_traits, true, cache); 71 | 72 | vec![] 73 | }, 74 | } 75 | } 76 | 77 | fn update_callsites(exposed_traits: Vec, callsites: &Vec) -> Vec { 78 | let mut ret = Vec::with_capacity(exposed_traits.len() * callsites.len()); 79 | 80 | for callsite in callsites { 81 | ret.extend(exposed_traits.iter().cloned().map(|mut exposed| { 82 | if exposed.callsite.id() != *callsite { 83 | exposed.callsite = match exposed.callsite { 84 | Callsite::Direct(_) => Callsite::Indirect(*callsite, vec![exposed.signature.id]), 85 | Callsite::Indirect(_, mut ids) => { 86 | ids.push(exposed.signature.id); 87 | Callsite::Indirect(*callsite, ids) 88 | }, 89 | }; 90 | } 91 | exposed 92 | })); 93 | } 94 | 95 | ret 96 | } 97 | 98 | /// True if the expression can be generalized. Generalizing expressions 99 | /// will cause them to be re-evaluated whenever they're used with new types, 100 | /// so generalization should be limited to when this would be expected by 101 | /// users (functions) or when it would not be noticeable (variables). 102 | pub(super) fn should_generalize(ast: &ast::Ast, cache: &ModuleCache) -> bool { 103 | match ast { 104 | ast::Ast::Variable(variable) => { 105 | // Don't generalize definitions only referring to variables unless the variable 106 | // is polymorphic. This prevents generalization of 'weak' type variables which would 107 | // be resolved to a concrete type later. See issue #129 108 | let info = &cache[variable.definition.unwrap()]; 109 | matches!(info.typ.as_ref(), Some(GeneralizedType::PolyType(..))) 110 | }, 111 | ast::Ast::Lambda(lambda) => lambda.closure_environment.is_empty(), 112 | _ => false, 113 | } 114 | } 115 | 116 | enum MutualRecursionResult { 117 | No, 118 | YesGeneralizeLater, 119 | YesGeneralizeNow(MutualRecursionId), 120 | } 121 | 122 | impl MutualRecursionResult { 123 | fn combine(self, other: Self) -> Self { 124 | use MutualRecursionResult::*; 125 | match (self, other) { 126 | (No, other) | (other, No) => other, 127 | 128 | (YesGeneralizeNow(id1), YesGeneralizeNow(id2)) => { 129 | assert_eq!(id1, id2); 130 | YesGeneralizeNow(id1) 131 | }, 132 | (YesGeneralizeNow(id), _) | (_, YesGeneralizeNow(id)) => YesGeneralizeNow(id), 133 | 134 | (YesGeneralizeLater, YesGeneralizeLater) => YesGeneralizeLater, 135 | } 136 | } 137 | } 138 | 139 | pub(super) fn definition_is_mutually_recursive(definition: DefinitionInfoId, cache: &ModuleCache) -> bool { 140 | let info = &cache[definition]; 141 | info.mutually_recursive_set.is_some() 142 | } 143 | 144 | fn is_mutually_recursive<'a>(pattern: &ast::Ast<'a>, cache: &mut ModuleCache<'a>) -> MutualRecursionResult { 145 | use ast::Ast::*; 146 | match pattern { 147 | Literal(_) => MutualRecursionResult::No, 148 | Variable(variable) => { 149 | let definition_id = variable.definition.unwrap(); 150 | let info = &cache.definition_infos[definition_id.0]; 151 | match info.mutually_recursive_set { 152 | None => MutualRecursionResult::No, 153 | Some(id) if cache.mutual_recursion_sets[id.0].root_definition == definition_id => { 154 | MutualRecursionResult::YesGeneralizeNow(id) 155 | }, 156 | Some(_) => MutualRecursionResult::YesGeneralizeLater, 157 | } 158 | }, 159 | TypeAnnotation(annotation) => is_mutually_recursive(&annotation.lhs, cache), 160 | FunctionCall(call) => { 161 | call.args.iter().fold(MutualRecursionResult::No, |a, b| a.combine(is_mutually_recursive(b, cache))) 162 | }, 163 | _ => { 164 | cache.push_diagnostic(pattern.locate(), D::InvalidSyntaxInIrrefutablePattern); 165 | MutualRecursionResult::No 166 | }, 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/types/traits.rs: -------------------------------------------------------------------------------- 1 | //! traits.rs - Defines the core data structures for trait inference. 2 | //! 3 | //! Trait inference is a part of type inference which determines: 4 | //! 1. Which traits are required for a given Definition to be compiled 5 | //! 2. When a `ast::Variable` is encountered whose Definition has some required traits 6 | //! whether these traits should be propagated up to be required for the current definition 7 | //! or whether they should be solved in place instead. 8 | //! 3. Solving trait constraints, yielding the impl that should be used for that specific 9 | //! constraint and attaching this impl to the relevant callsite variable. 10 | //! 11 | //! For more information on the trait inference part of the type inference pass, 12 | //! see `types/traitchecker.rs` for the file defining the pass itself. 13 | //! 14 | //! This module defines the three types that are core to trait inference: 15 | //! 1. RequiredTrait - A trait required for a definition to be compiled. Note that required 16 | //! traits are just that - they only require the trait is present for the definition to be 17 | //! compiled, they do not require a specific impl to be used. For example the function 18 | //! `my_print a = print a` would have the RequiredTrait `Print a` which could be given any 19 | //! matching impl when `my_print` is later used at its callsite. 20 | //! 2. RequiredImpl - An instantiated version of a RequiredTrait stored at the callsite of a 21 | //! function/variable that should point to a certain impl for a trait. When `my_print` is 22 | //! later called with `my_print 3`, `Print i32` will be a RequiredImpl. Note that RequiredImpls 23 | //! may still have type variables for blanket impls. 24 | //! 3. TraitConstraint - TraitConstraints are what are actually passed around during the majority 25 | //! of the type inference pass, undergoing unification until the outer `ast::Definition` 26 | //! finishes compiling and trait inference needs to decide if each TraitConstraint needs to be 27 | //! propagated up to that `ast::Definition` as a RequiredTrait or solved in place. 28 | //! 29 | //! These types are mostly useful for their data they hold - they only have a few simple 30 | //! methods on them for displaying them or converting between them. 31 | use crate::cache::{ImplInfoId, ImplScopeId, ModuleCache, TraitInfoId, VariableId}; 32 | use crate::error::location::Location; 33 | use crate::types::typechecker::find_all_typevars; 34 | use crate::types::{Type, TypeVariableId}; 35 | 36 | use super::typeprinter::ConstraintSignaturePrinter; 37 | 38 | /// Trait constraints do not map to anything. Instead, 39 | /// they provide a way to map an impl through multiple 40 | /// functions as if it were passed as arguments. 41 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 42 | pub struct TraitConstraintId(pub u32); 43 | 44 | /// A (trait) ConstraintSignature contains the signature 45 | /// of a trait constraint - the trait it refers to and the type 46 | /// arguments it requires - in addition to a unique ID identifying 47 | /// this constraint. 48 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 49 | pub struct ConstraintSignature { 50 | pub trait_id: TraitInfoId, 51 | pub args: Vec, 52 | pub id: TraitConstraintId, 53 | } 54 | 55 | /// A trait required for a Definition to be compiled. 56 | /// The specific impl to use is unknown to the definition since 57 | /// different impls may be used at different callsites. 58 | /// RequiredImpls are the callsite version of this. 59 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 60 | pub struct RequiredTrait { 61 | pub signature: ConstraintSignature, 62 | 63 | pub callsite: Callsite, 64 | } 65 | 66 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 67 | pub enum Callsite { 68 | /// This required trait originates from the given variable inside the function. 69 | /// That variable's definition should be replaced with the selected impl's. 70 | Direct(VariableId), 71 | 72 | /// This required trait originates from a definition outside of the current 73 | /// function where it has the given TraitConstraintId. It is required transitively 74 | /// to the current function by the given variable callsite. 75 | Indirect(VariableId, Vec), 76 | } 77 | 78 | impl Callsite { 79 | pub fn id(&self) -> VariableId { 80 | match self { 81 | Callsite::Direct(callsite) => *callsite, 82 | Callsite::Indirect(callsite, _) => *callsite, 83 | } 84 | } 85 | } 86 | 87 | /// An instantiated version of a RequiredTrait that is stored 88 | /// in ast::Variable nodes. These point to specific impls to use. 89 | #[derive(Debug, Clone)] 90 | pub struct RequiredImpl { 91 | /// The specific trait impl to map the callsite to 92 | pub binding: ImplInfoId, 93 | pub callsite: Callsite, 94 | } 95 | 96 | /// The trait/impl constrait passed around during type inference. 97 | /// - If at the end of a function an impl constraint contains a type 98 | /// variable that escapes the current function (ie. is used in a 99 | /// parameter or return type of the function) then it is turned into a 100 | /// RequiredTrait for the function's DefinitionInfo. 101 | /// - If no type variables escape outside of the current function then 102 | /// an impl is searched for. 103 | /// - If one is found it is stored in a RequiredImpl for the ast::Variable 104 | /// node to reference while compiling that variable's definition. 105 | /// - If an impl is not found a compile error for no matching impl is issued. 106 | #[derive(Debug, Clone)] 107 | pub struct TraitConstraint { 108 | pub required: RequiredTrait, 109 | pub scope: ImplScopeId, 110 | } 111 | 112 | pub type TraitConstraints = Vec; 113 | 114 | impl RequiredTrait { 115 | pub fn as_constraint(&self, scope: ImplScopeId, callsite: VariableId, id: TraitConstraintId) -> TraitConstraint { 116 | let mut required = self.clone(); 117 | required.callsite = Callsite::Indirect(callsite, vec![self.signature.id]); 118 | required.signature.id = id; 119 | TraitConstraint { required, scope } 120 | } 121 | 122 | pub fn find_all_typevars(&self, cache: &ModuleCache<'_>) -> Vec { 123 | self.signature.find_all_typevars(cache) 124 | } 125 | 126 | pub fn display<'a, 'b>(&self, cache: &'a ModuleCache<'b>) -> ConstraintSignaturePrinter<'a, 'b> { 127 | self.signature.display(cache) 128 | } 129 | 130 | #[allow(dead_code)] 131 | pub fn debug<'a, 'b>(&self, cache: &'a ModuleCache<'b>) -> ConstraintSignaturePrinter<'a, 'b> { 132 | self.signature.debug(cache) 133 | } 134 | } 135 | 136 | impl ConstraintSignature { 137 | pub fn find_all_typevars(&self, cache: &ModuleCache<'_>) -> Vec { 138 | let mut typevars = vec![]; 139 | for typ in &self.args { 140 | typevars.append(&mut find_all_typevars(typ, false, cache)); 141 | } 142 | typevars 143 | } 144 | } 145 | 146 | impl TraitConstraint { 147 | /// Creates a TraitConstraint from the ConstraintSignature of the 'given' clause 148 | /// of a trait impl and the constraint from the impl itself. These constraints are always Callsite::Indirect. 149 | pub fn impl_given_constraint( 150 | inner_id: TraitConstraintId, trait_id: TraitInfoId, args: Vec, impl_constraint: &TraitConstraint, 151 | cache: &mut ModuleCache, 152 | ) -> TraitConstraint { 153 | let id = cache.next_trait_constraint_id(); 154 | let signature = ConstraintSignature { trait_id, args, id }; 155 | 156 | let callsite = match &impl_constraint.required.callsite { 157 | Callsite::Direct(var) => Callsite::Indirect(*var, vec![inner_id]), 158 | Callsite::Indirect(var, ids) => { 159 | let mut ids = ids.clone(); 160 | ids.push(inner_id); 161 | Callsite::Indirect(*var, ids) 162 | }, 163 | }; 164 | 165 | let required = RequiredTrait { signature, callsite }; 166 | TraitConstraint { required, scope: impl_constraint.scope } 167 | } 168 | 169 | pub fn trait_id(&self) -> TraitInfoId { 170 | self.required.signature.trait_id 171 | } 172 | 173 | pub fn args(&self) -> &[Type] { 174 | &self.required.signature.args 175 | } 176 | 177 | pub fn args_mut(&mut self) -> &mut [Type] { 178 | &mut self.required.signature.args 179 | } 180 | 181 | pub fn into_required_trait(self) -> RequiredTrait { 182 | self.required 183 | } 184 | 185 | pub fn into_required_impl(self, binding: ImplInfoId) -> RequiredImpl { 186 | RequiredImpl { binding, callsite: self.required.callsite } 187 | } 188 | 189 | /// Get the location of the callsite where this TraitConstraint arose from 190 | pub fn locate<'c>(&self, cache: &ModuleCache<'c>) -> Location<'c> { 191 | cache[self.required.callsite.id()].location 192 | } 193 | 194 | pub fn display<'a, 'c>(&self, cache: &'a ModuleCache<'c>) -> ConstraintSignaturePrinter<'a, 'c> { 195 | self.clone().into_required_trait().display(cache) 196 | } 197 | 198 | #[allow(dead_code)] 199 | pub fn debug<'a, 'c>(&self, cache: &'a ModuleCache<'c>) -> ConstraintSignaturePrinter<'a, 'c> { 200 | self.clone().into_required_trait().debug(cache) 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/types/typed.rs: -------------------------------------------------------------------------------- 1 | //! typed.rs - Defines a simple trait for getting and setting 2 | //! the type of something. Currently this is only implemented for 3 | //! AST nodes. 4 | use crate::parser::ast::*; 5 | use crate::types::Type; 6 | 7 | pub trait Typed { 8 | fn get_type(&self) -> Option<&Type>; 9 | fn set_type(&mut self, typ: Type); 10 | } 11 | 12 | impl<'a> Typed for Ast<'a> { 13 | fn get_type(&self) -> Option<&Type> { 14 | dispatch_on_expr!(self, Typed::get_type) 15 | } 16 | 17 | fn set_type(&mut self, typ: Type) { 18 | dispatch_on_expr!(self, Typed::set_type, typ) 19 | } 20 | } 21 | 22 | macro_rules! impl_typed_for { 23 | ( $name:tt ) => { 24 | impl<'a> Typed for $name<'a> { 25 | fn get_type(&self) -> Option<&Type> { 26 | self.typ.as_ref() 27 | } 28 | 29 | fn set_type(&mut self, typ: Type) { 30 | self.typ = Some(typ); 31 | } 32 | } 33 | }; 34 | } 35 | 36 | impl_typed_for!(Literal); 37 | impl_typed_for!(Variable); 38 | impl_typed_for!(Lambda); 39 | impl_typed_for!(FunctionCall); 40 | impl_typed_for!(Definition); 41 | impl_typed_for!(If); 42 | impl_typed_for!(Match); 43 | impl_typed_for!(TypeDefinition); 44 | impl_typed_for!(TypeAnnotation); 45 | impl_typed_for!(Import); 46 | impl_typed_for!(TraitDefinition); 47 | impl_typed_for!(TraitImpl); 48 | impl_typed_for!(Return); 49 | impl_typed_for!(Sequence); 50 | impl_typed_for!(Extern); 51 | impl_typed_for!(MemberAccess); 52 | impl_typed_for!(Assignment); 53 | impl_typed_for!(EffectDefinition); 54 | impl_typed_for!(Handle); 55 | impl_typed_for!(NamedConstructor); 56 | impl_typed_for!(Reference); 57 | -------------------------------------------------------------------------------- /src/util/id.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 4 | pub struct Id { 5 | index: u32, 6 | tag: PhantomData, 7 | } 8 | 9 | impl Id { 10 | pub fn new(index: u32) -> Self { 11 | Self { index, tag: PhantomData } 12 | } 13 | } 14 | 15 | impl From for Id { 16 | fn from(value: u32) -> Self { 17 | Self::new(value) 18 | } 19 | } 20 | 21 | impl From> for u32 { 22 | fn from(value: Id) -> Self { 23 | value.index 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/util/logging.rs: -------------------------------------------------------------------------------- 1 | //! logging.rs - Simple tree-based logging for debugging. 2 | //! Useful to trace the compiler's control flow through various programs. 3 | #![allow(dead_code)] 4 | #![allow(unused_macros)] 5 | 6 | use std::sync::atomic::AtomicUsize; 7 | use std::sync::atomic::Ordering::SeqCst; 8 | 9 | pub static LOG_LEVEL: AtomicUsize = AtomicUsize::new(0); 10 | 11 | pub struct Logger; 12 | 13 | /// Prints out a log line prepended with the current indent level 14 | macro_rules! log { ( $fmt_string:expr $( , $($msg:tt)* )? ) => ({ 15 | let seq_cst = std::sync::atomic::Ordering::SeqCst; 16 | 17 | print!("{}", " ".repeat($crate::util::logging::LOG_LEVEL.load(seq_cst))); 18 | println!($fmt_string $( , $($msg)* )?); 19 | $crate::util::logging::Logger 20 | });} 21 | 22 | impl Logger { 23 | /// Starts a log block, causing all logs within the given function to be 24 | /// indented more than the logs outside of it. Useful for tracing control 25 | /// flow for recursive functions. 26 | pub fn block(self, f: F) -> T 27 | where 28 | F: FnOnce() -> T, 29 | { 30 | LOG_LEVEL.fetch_add(2, SeqCst); 31 | let result = f(); 32 | LOG_LEVEL.fetch_sub(2, SeqCst); 33 | result 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | //! util/mod.rs - Various utility functions used throughout the compiler. 2 | //! Mostly consists of convenience functions for iterators such as `fmap`. 3 | use std::{collections::BTreeSet, fmt::Display, path::PathBuf, process::Command}; 4 | 5 | #[macro_use] 6 | pub mod logging; 7 | mod id; 8 | pub mod timing; 9 | pub mod trustme; 10 | mod vecmap; 11 | 12 | pub use id::Id; 13 | pub use vecmap::VecMap; 14 | 15 | /// Equivalent to .iter().map(f).collect() 16 | pub fn fmap(iterable: T, f: F) -> Vec 17 | where 18 | T: IntoIterator, 19 | F: FnMut(T::Item) -> U, 20 | { 21 | iterable.into_iter().map(f).collect() 22 | } 23 | 24 | /// Equivalent to option.as_ref().unwrap().clone() 25 | pub fn unwrap_clone(option: &Option) -> T { 26 | option.as_ref().unwrap().clone() 27 | } 28 | 29 | /// Convert each element to a String and join them with the given delimiter 30 | pub fn join_with(items: impl IntoIterator, delimiter: &str) -> String { 31 | fmap(items, |t| format!("{}", t)).join(delimiter) 32 | } 33 | 34 | /// Deduplicate the vec without changing the ordering of its elements 35 | pub fn dedup(vec: Vec) -> Vec { 36 | if vec.len() <= 1 { 37 | vec 38 | } else { 39 | let mut seen = BTreeSet::new(); 40 | let mut result = Vec::with_capacity(vec.len()); 41 | for value in vec { 42 | if !seen.contains(&value) { 43 | seen.insert(value); 44 | result.push(value); 45 | } 46 | } 47 | result 48 | } 49 | } 50 | 51 | pub fn link(object_filename: &str, binary_filename: &str) { 52 | // call gcc to compile the bitcode to a binary 53 | let output = format!("-o{}", binary_filename); 54 | let mut child = Command::new("gcc") 55 | .arg(object_filename) 56 | .arg(minicoro_path()) 57 | .arg("-Wno-everything") 58 | .arg("-O0") 59 | .arg("-lm") 60 | .arg(output) 61 | .spawn() 62 | .unwrap(); 63 | 64 | // remove the temporary bitcode file 65 | child.wait().unwrap(); 66 | std::fs::remove_file(object_filename).unwrap(); 67 | } 68 | 69 | /// Returns the default name of the outputted binary file 70 | /// as a result of compiling the program with the given entry module. 71 | pub fn binary_name(module_name: &str) -> String { 72 | if cfg!(target_os = "windows") { 73 | PathBuf::from(module_name).with_extension("exe").to_string_lossy().into() 74 | } else { 75 | PathBuf::from(module_name).with_extension("").to_string_lossy().into() 76 | } 77 | } 78 | 79 | fn minicoro_path() -> &'static str { 80 | match option_env!("ANTE_MINICORO_PATH") { 81 | Some(path) => path, 82 | None => panic!("ANTE_MINICORO_PATH is not set"), 83 | } 84 | } 85 | 86 | pub fn stdlib_dir() -> PathBuf { 87 | match option_env!("ANTE_STDLIB_DIR") { 88 | Some(env) => std::fs::canonicalize(env).unwrap(), 89 | None => panic!("ANTE_STDLIB_DIR is not set"), 90 | } 91 | } 92 | 93 | macro_rules! expect_opt {( $result:expr , $fmt_string:expr $( , $($msg:tt)* )? ) => ({ 94 | match $result { 95 | Some(t) => t, 96 | None => panic!($fmt_string $( , $($msg)* )? ), 97 | } 98 | });} 99 | -------------------------------------------------------------------------------- /src/util/timing.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::sync::atomic::{AtomicBool, Ordering}; 3 | use std::time::{Duration, Instant}; 4 | 5 | struct PassTimings { 6 | current_pass_name: String, 7 | current_pass_start_time: Instant, 8 | all_passes: Vec, 9 | } 10 | 11 | #[derive(Clone)] 12 | struct PassDuration { 13 | pass_name: String, 14 | duration: Duration, 15 | } 16 | 17 | static TIME_PASSES: AtomicBool = AtomicBool::new(false); 18 | 19 | thread_local! { 20 | static PASSES: RefCell> = RefCell::new(None); 21 | } 22 | 23 | /// Set whether the time! macro should print out the timings of each pass or not 24 | pub fn time_passes(should_time: bool) { 25 | TIME_PASSES.store(should_time, Ordering::Relaxed); 26 | } 27 | 28 | /// Start timing the given pass 29 | pub fn start_time(pass_name: &str) { 30 | if TIME_PASSES.load(Ordering::Relaxed) { 31 | PASSES.with(|passes| { 32 | let mut passes = passes.borrow_mut(); 33 | match passes.as_mut() { 34 | Some(time) => time.all_passes.push(time.current_pass_duration()), 35 | None => *passes = Some(PassTimings::new()), 36 | }; 37 | let time = passes.as_mut().unwrap(); 38 | time.current_pass_name = pass_name.to_string(); 39 | time.current_pass_start_time = Instant::now(); 40 | }) 41 | } 42 | } 43 | 44 | pub fn show_timings() { 45 | if TIME_PASSES.load(Ordering::Relaxed) { 46 | PASSES.with(|timings| { 47 | let mut timings = timings.borrow_mut(); 48 | let timings = timings.as_mut().unwrap(); 49 | let final_pass = timings.current_pass_duration(); 50 | timings.all_passes.push(final_pass); 51 | 52 | timings.all_passes.iter().for_each(|pass| pass.show()); 53 | 54 | println!("{}\nTotals:\n", "-".repeat(33)); 55 | 56 | let aggregate = timings.aggregate_pass_timings(); 57 | aggregate.iter().for_each(|pass| pass.show()); 58 | }) 59 | } 60 | } 61 | 62 | impl PassTimings { 63 | fn new() -> PassTimings { 64 | PassTimings { current_pass_name: String::new(), current_pass_start_time: Instant::now(), all_passes: vec![] } 65 | } 66 | 67 | fn current_pass_duration(&self) -> PassDuration { 68 | PassDuration { pass_name: self.current_pass_name.to_string(), duration: self.current_pass_start_time.elapsed() } 69 | } 70 | 71 | /// Combine the Durations of all passes with the same name 72 | fn aggregate_pass_timings(&self) -> Vec { 73 | let mut aggregate: Vec = vec![]; 74 | 75 | for pass in self.all_passes.iter() { 76 | match aggregate.iter_mut().find(|previous| previous.pass_name == pass.pass_name) { 77 | Some(previous) => previous.duration += pass.duration, 78 | None => aggregate.push(pass.clone()), 79 | } 80 | } 81 | 82 | aggregate 83 | } 84 | } 85 | 86 | impl PassDuration { 87 | fn show(&self) { 88 | let millis = self.duration.as_millis(); 89 | 90 | let time_string = 91 | if millis != 0 { format!("{}ms", millis) } else { format!("{}μs", self.duration.as_micros()) }; 92 | 93 | println!("{: <25} - {}", self.pass_name, time_string); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/util/trustme.rs: -------------------------------------------------------------------------------- 1 | //! This module is the home for any vastly unsafe function. 2 | //! This module should only be used as a last resort. 3 | //! 4 | //! Currently, these functions are only used when retrieving the 5 | //! definition of variables from the cache to separate the mutability/lifetime 6 | //! of the definition from that of the cache. These should be able 7 | //! to be removed if the AST nodes ever become arena allocated and 8 | //! a key can be used from the arena instead of a direct reference in the ModuleCache. 9 | 10 | pub fn extend_lifetime<'b, T>(x: &mut T) -> &'b mut T { 11 | unsafe { std::mem::transmute(x) } 12 | } 13 | 14 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 15 | pub fn make_mut<'a, T>(x: *const T) -> &'a mut T { 16 | #[allow(clippy::transmute_ptr_to_ref)] 17 | unsafe { 18 | std::mem::transmute(x) 19 | } 20 | } 21 | 22 | pub fn make_mut_ref<'b, T>(x: &T) -> &'b mut T { 23 | #[allow(mutable_transmutes)] 24 | unsafe { 25 | std::mem::transmute(x) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/util/vecmap.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | use std::marker::PhantomData; 3 | 4 | /// A dense map from keys of type K to values of type V. 5 | /// 6 | /// Only values are stored internally, and keys correspond 7 | /// to the integer indices these values are stored at. To keep 8 | /// these indices consistent, this map is append-only. 9 | pub struct VecMap { 10 | data: Vec, 11 | tag: PhantomData, 12 | } 13 | 14 | impl VecMap { 15 | pub fn new() -> Self { 16 | Self { data: Vec::new(), tag: PhantomData } 17 | } 18 | 19 | pub fn len(&self) -> u32 { 20 | self.data.len() as u32 21 | } 22 | 23 | pub fn push(&mut self, element: V) -> K 24 | where 25 | K: From, 26 | { 27 | let index = self.len(); 28 | self.data.push(element); 29 | index.into() 30 | } 31 | 32 | pub fn iter(&self) -> impl Iterator { 33 | self.data.iter() 34 | } 35 | } 36 | 37 | impl Default for VecMap { 38 | fn default() -> Self { 39 | VecMap { data: Vec::new(), tag: PhantomData } 40 | } 41 | } 42 | 43 | impl, V> std::ops::Index for VecMap { 44 | type Output = V; 45 | 46 | fn index(&self, index: K) -> &Self::Output { 47 | &self.data[index.into() as usize] 48 | } 49 | } 50 | 51 | impl, V> std::ops::IndexMut for VecMap { 52 | fn index_mut(&mut self, index: K) -> &mut Self::Output { 53 | &mut self.data[index.into() as usize] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /stdlib/HashMap.an: -------------------------------------------------------------------------------- 1 | // A linear-scan mutable HashMap using a resize factor of 2.0 2 | type HashMap k v = 3 | len: Usz 4 | capacity: Usz 5 | entries: Ptr (Entry k v) 6 | 7 | type Entry k v = 8 | key: k 9 | value: v 10 | occupied: Bool 11 | tombstone: Bool 12 | 13 | trait Hash t with 14 | hash: t -> U64 15 | 16 | empty () = HashMap 0 0 (null ()) 17 | 18 | clear (map: !HashMap k v) : Unit = 19 | if map.capacity != 0 then 20 | repeat map.capacity fn i -> 21 | mut entry = deref_ptr <| offset map.entries i 22 | entry.!occupied := false 23 | entry.!tombstone := false 24 | 25 | resize (map: !HashMap k v) (new_capacity: Usz) : Unit = 26 | if new_capacity > map.capacity then 27 | new_memory = calloc new_capacity (size_of (MkType : Type (Entry k v))) 28 | 29 | if new_memory == null () then 30 | panic "Ran out of memory while resizing HashMap" 31 | 32 | mut new_map = HashMap (map.len) new_capacity new_memory 33 | new_map_ref = !new_map 34 | 35 | repeat (map.capacity) fn i -> 36 | entry: Entry k v = map.entries.[i] 37 | if entry.occupied then 38 | insert new_map_ref entry.key entry.value 39 | 40 | map.!capacity := new_capacity 41 | map.!entries := new_memory 42 | 43 | // Should we resize this map when pushing another element? 44 | should_resize (map: HashMap k v) : Bool = 45 | scale_factor = 2 46 | (map.len + 1) * scale_factor > map.capacity 47 | 48 | insert (map: !HashMap k v) (key: k) (value: v) : Unit = 49 | if should_resize @map then 50 | resize map ((map.capacity + 1) * 2) 51 | 52 | iter_until (map: !HashMap k v) (key: k) (value: v) (start: Usz) (end: Usz) : Bool = 53 | if start >= end then 54 | false 55 | else 56 | entry_ptr = offset (map.entries) start 57 | entry = transmute entry_ptr : !Entry k v 58 | if entry.occupied then 59 | iter_until map key value (start + 1) end 60 | else 61 | entry := Entry key value true false 62 | true 63 | 64 | h = cast (hash key) : Usz % map.capacity 65 | 66 | if not iter_until map key value h (map.capacity) then 67 | if not iter_until map key value 0 h then 68 | panic "Failed to insert entry into map" 69 | 70 | map.!len := map.len + 1usz 71 | 72 | // TODO: Name resolution issue preventing this from being visible to iter_until 73 | // if it is defined within get_entry 74 | type EntryResult k v = 75 | | Found (Ptr (Entry k v)) 76 | | NotFound 77 | | NotFinished 78 | 79 | get_entry (map: HashMap k v) (key: k) : Maybe (Ptr (Entry k v)) = 80 | // Return whether to continue by wrapping around the end, and the value if found 81 | iter_until (map: HashMap k v) (key: k) (start: Usz) (end: Usz) : EntryResult k v = 82 | if start >= end then 83 | NotFinished 84 | else 85 | entry_ptr = offset map.entries start 86 | entry = deref_ptr entry_ptr 87 | if entry.occupied and entry.key == key then 88 | Found entry_ptr 89 | else if entry.tombstone then 90 | iter_until map key (start + 1) end 91 | else 92 | NotFound 93 | 94 | h = cast (hash key) : Usz % map.capacity 95 | match iter_until map key h map.capacity 96 | | Found value -> Some value 97 | | NotFound -> None 98 | | NotFinished -> 99 | match iter_until map key 0 h 100 | | Found value -> Some value 101 | | _ -> None 102 | 103 | get (map: HashMap k v) (key: k) : Maybe v = 104 | match get_entry map key 105 | | Some entry -> Some (deref_ptr entry).value 106 | | None -> None 107 | 108 | 109 | remove (map: !HashMap k v) (key: k) : Maybe v = 110 | match get_entry (@map) key 111 | | None -> None 112 | | Some e2 -> 113 | entry = ptr_to_ref e2 : !Entry k v 114 | entry.!occupied := false 115 | entry.!tombstone := true 116 | map.!len := map.len - 1 117 | Some entry.value 118 | 119 | impl Print (HashMap k v) given Print k, Print v with 120 | printne map = printne_map map 121 | 122 | printne_map (map: HashMap k v) = 123 | putchar '[' 124 | print_helper map false 0 125 | putchar ']' 126 | 127 | print_helper (map: HashMap k v) (did_first: Bool) (i: Usz) = 128 | if i < map.capacity then 129 | entry: Entry k v = map.entries.[i] 130 | if entry.occupied then 131 | if did_first then 132 | printne ", " 133 | 134 | printne entry.key 135 | printne " -> " 136 | printne entry.value 137 | 138 | printed = did_first or entry.occupied 139 | print_helper map printed (i + 1) 140 | -------------------------------------------------------------------------------- /stdlib/StringBuilder.an: -------------------------------------------------------------------------------- 1 | type StringBuilder = 2 | data: Ptr Char 3 | length: Usz 4 | cap: Usz 5 | 6 | empty () = StringBuilder (null ()) 0 0 7 | 8 | // reserve space for at least n additional characters 9 | reserve (s: !StringBuilder) (n: Usz) : Unit = 10 | if s.length + n > s.cap then 11 | new_size = s.cap + n 12 | ptr = realloc s.data new_size 13 | 14 | if ptr == null () then 15 | print "Error in reserving more elements for Vec" 16 | return () 17 | 18 | s.!data := ptr 19 | s.!cap := new_size 20 | 21 | // append a string 22 | append (s: !StringBuilder) (new_str: String) : Unit = 23 | reserve s new_str.length 24 | memcpy (cast (cast s.data + s.length)) new_str.c_string (cast (new_str.length+1)) 25 | s.!length := s.length + new_str.length 26 | 27 | // convert to string 28 | to_string (s: StringBuilder) : String = 29 | String (s.data) (s.length) 30 | 31 | impl Print StringBuilder with 32 | printne (s: StringBuilder) : Unit = 33 | print (to_string s) 34 | -------------------------------------------------------------------------------- /stdlib/Vec.an: -------------------------------------------------------------------------------- 1 | type Vec t = 2 | data: Ptr t 3 | len: Usz 4 | cap: Usz 5 | 6 | empty () = Vec (null ()) 0 0 7 | 8 | of iterable = 9 | mut v = empty () 10 | fill !v iterable 11 | v 12 | 13 | is_empty v = v.len == 0 14 | 15 | indices (v: Vec a) = Range 0 v.len 16 | 17 | len v = v.len 18 | 19 | capacity v = v.cap 20 | 21 | //Fill Vec with items from the iterable 22 | fill (v: !Vec t) iterable : Unit = 23 | iter iterable (push v _) 24 | 25 | //reserve numElements in Vec v, elements will be uninitialized 26 | reserve (v: !Vec t) (numElems: Usz) : Unit = 27 | if v.len + numElems > v.cap then 28 | size = (v.cap + numElems) * size_of (MkType: Type t) 29 | ptr = realloc (v.data) size 30 | 31 | if ptr == null () then 32 | print "Error in reserving more elements for Vec" 33 | return () 34 | 35 | v.!data := ptr 36 | v.!cap := v.cap + numElems 37 | 38 | //push an element onto the end of the vector. 39 | //resizes if necessary 40 | push (v: !Vec t) (elem: t) : Unit = 41 | if v.len >= v.cap then 42 | reserve v (if v.cap == 0usz then 1 else v.cap) 43 | 44 | array_insert v.data v.len elem 45 | v.!len := v.len + 1usz 46 | 47 | //pop the last element off if it exists 48 | //this will never resize the vector. 49 | pop (v: !Vec t) : Maybe t = 50 | if is_empty v then None 51 | else 52 | v.!len := v.len - 1 53 | Some (v.data.[v.len]) 54 | 55 | //remove the element at the given index and return it. 56 | //will error if the index is out of bounds. 57 | remove_index (v: !Vec t) (idx:Usz) : t = 58 | if idx == v.len - 1 then 59 | v.!len := v.len - 1 60 | else if idx >= 0 and idx < v.len - 1 then 61 | iter_range idx (v.len - 1) fn i -> 62 | array_insert v.data i (v.data.[i+1]) 63 | 64 | v.!len := v.len - 1 65 | else 66 | panic "Vec.remove_index: index ${idx} out of bounds for Vec of length ${v.len}" 67 | 68 | v.data.[v.len] 69 | 70 | //remove the first instance of the given element from 71 | //the vector or none if the element was not found. 72 | //Uses == to determine element equality. 73 | //returns the index where the element was found. 74 | remove_first (v: !Vec t) (elem: t) : Maybe Usz = 75 | loop (i = 0) -> 76 | if i >= v.len then 77 | None 78 | else if elem == v.data.[i] then 79 | remove_index v i 80 | Some i 81 | else recur (i + 1) 82 | 83 | //Remove the given indices from the vector 84 | //Expects the indices to be in sorted order. 85 | //Will error if any index is out of bounds. 86 | remove_indices (v: !Vec t) (idxs: Vec Usz) : Unit = 87 | mut moved = 0 88 | iter (indices idxs) fn i -> 89 | cur = idxs.data.[i] 90 | 91 | if cur < 0 or cur >= v.len then 92 | panic "Vec.remove: index $cur out of bounds for Vec of length ${v.len}" 93 | 94 | moved := @moved + 1 95 | if i != idxs.len - 1 then 96 | nxt = idxs.data.[i + 1] 97 | iter_range (cur + 1) nxt fn j -> 98 | array_insert v.data (j - @moved) (v.data.[j]) 99 | else 100 | iter_range (cur + 1) v.len fn j -> 101 | array_insert v.data (j - @moved) (v.data.[j]) 102 | 103 | v.!len := v.len - @moved 104 | 105 | //remove all matching elements from the vector and 106 | //return the number of elements removed. 107 | //Uses = to determine element equality. 108 | remove_all (v: !Vec t) (elem: t) : Usz = 109 | mut idxs = empty () 110 | iter (indices @v) fn i -> 111 | if elem == v.data.[i] then 112 | push idxs i 113 | 114 | remove_indices v @idxs 115 | idxs.len 116 | 117 | 118 | //Remove an element by swapping it with the last element in O(1) time. 119 | //Returns true if a swap was performed or false otherwise. 120 | //Will not swap if the given index is the index of the last element. 121 | swap_last (v: !Vec t) (idx:Usz) : Bool = 122 | if idx >= v.len or idx + 1 == v.len then false 123 | else 124 | v.!len := v.len - 1 125 | array_insert v.data idx (v.data.[v.len]) 126 | true 127 | 128 | 129 | // type VecIter t = 130 | // view: Ref t 131 | // idx: Usz 132 | // len: Usz 133 | // 134 | // impl Iterable (Vec t) (VecIter t) t with 135 | // into_iter v = VecIter v.data 0usz v.len 136 | // 137 | // impl Iterator (VecIter t) t with 138 | // cur_elem v = v.view#v.idx 139 | // advance v = VecIter v.view (v.idx + 1usz) v.len 140 | // has_next v = v.idx < v.len 141 | // 142 | // 143 | impl Print (Vec t) given Print t with 144 | printne v = 145 | printne "[" 146 | print_helper v 0 147 | printne "]" 148 | 149 | print_helper (v: Vec q) (i: Usz) : Unit = 150 | if i < v.len then 151 | // TODO: This fails to find the correct impl without 152 | // this type annotation 153 | printne ((v.data.[i]) : q) 154 | if i + 1 != v.len then 155 | printne ", " 156 | 157 | print_helper v (i + 1) 158 | 159 | impl Extract (Vec t) Usz t with 160 | (.[) v i = v.data.[i] 161 | 162 | // impl Insert (Vec t) Usz t with 163 | // insert v i x = v.data#i := x 164 | // 165 | // impl In t (Vec t) with 166 | // (in) elem v = 167 | // for e in v do 168 | // if e == elem then 169 | // return true 170 | // false 171 | // 172 | // impl Eq (Vec t) with 173 | // (==) l r = 174 | // if l.len != r.len then 175 | // return false 176 | // 177 | // for i in 0 .. cast l.len do 178 | // if l.data#i != r.data#i then 179 | // return false 180 | // true 181 | -------------------------------------------------------------------------------- /stdlib/future/abort.an: -------------------------------------------------------------------------------- 1 | effect Fail with 2 | fail: Unit -> a 3 | 4 | try (f: Unit -> a can Fail) : Maybe a = 5 | handle f () 6 | | return a -> Some a 7 | | fail () -> None 8 | 9 | try_or (default: a) (f: Unit -> a can Fail) : a = 10 | try f else default 11 | 12 | assert (cond: Bool) : Unit can Fail = 13 | if not cond then fail () 14 | 15 | /// Retry a function until it succeeds (doesn't call fail). 16 | /// This should be used with functions using a Fail effect 17 | /// along with other effects. Otherwise, it will loop forever. 18 | /// 19 | /// ``` 20 | /// get_input () -> string can IO = ... 21 | /// parse (s: string) -> u32 can Fail = ... 22 | /// 23 | /// number = retry_until_success $$ 24 | /// input = get_input () 25 | /// parse input 26 | /// ``` 27 | retry_until_success (f: Unit -> a can Fail) : a = 28 | handle f () 29 | | return a -> a 30 | | fail () -> retry_until_success f 31 | -------------------------------------------------------------------------------- /stdlib/future/str.an: -------------------------------------------------------------------------------- 1 | import Vec 2 | 3 | reverse (s: String) : String = 4 | mut buf = malloc (s.len + 1usz) 5 | 6 | i = loop (i = 0) -> 7 | if i < s.len then 8 | buf.[i] := s.[s.len - i - 1usz] 9 | recur (i + 1) 10 | else i 11 | 12 | buf.[i] := '\0' 13 | String buf i 14 | 15 | 16 | split (s: String) (c: String) : Vec String = 17 | mut v = Vec.empty () 18 | 19 | i, j = loop (subend = 0) (substart = 0) -> 20 | if subend < s.len then 21 | substart = if s.[subend] == c then 22 | push v (substr s substart subend) 23 | subend + 1 24 | else 25 | substart 26 | 27 | recur (subend + 1) substart 28 | else 29 | subend, substart 30 | 31 | 32 | push v (substr s j i) 33 | v 34 | 35 | substr (s: String) (begin: Usz) (end: Usz) : String = 36 | if end > s.len or begin >= s.len then 37 | return "" 38 | 39 | len = end - begin 40 | mut buf = malloc (len + 1) 41 | buf.[end] := '\0' 42 | 43 | loop (i = 0) -> 44 | b = begin + i 45 | if b < end then 46 | buf.[i] := s.[b] 47 | recur (i + 1) 48 | 49 | String buf len 50 | -------------------------------------------------------------------------------- /stdlib/future/stream.an: -------------------------------------------------------------------------------- 1 | // A Stream library for after algebraic effects are implemented in ante. 2 | // Until then this serves only as an example design of some future language features. 3 | 4 | effect Emit a with 5 | emit: a -> Unit 6 | 7 | trait Stream t a with 8 | stream: t -> Unit can Emit a 9 | 10 | impl Stream (Unit -> Unit can Emit a) a with 11 | stream f = f () 12 | 13 | impl Stream (Vec a) a with 14 | stream vec = 15 | indices () = range 0 (len vec) 16 | map indices (vec.[_]) 17 | 18 | for (s: Stream _ a) (f: a -> b): Unit = 19 | handle stream s 20 | | emit a -> 21 | f a 22 | resume () 23 | 24 | filter_map (s: Stream _ a) (f: a -> Maybe b): Unit can Emit b = 25 | handle stream s 26 | | emit a -> 27 | match f a 28 | | Some b -> emit b 29 | | None -> () 30 | resume () 31 | 32 | map s f = filter_map s fn x -> Some (f x) 33 | 34 | filter s f = filter_map s fn x -> if f x then x 35 | 36 | fold (s: Stream _ a) (acc: b) (f: a - b -> b): b = 37 | handle stream s 38 | | return _ -> acc 39 | | emit value -> 40 | x = f value acc 41 | fold resume x f 42 | 43 | map2 (sa: Stream _ a) (sb: Stream _ b) (f: a - b -> c): Unit can Emit c = 44 | handle stream sa 45 | | emit a -> 46 | resume_a = resume 47 | handle stream sb 48 | | emit b -> 49 | emit (f a b) 50 | map2 resume_a resume 51 | 52 | zip a b = map2 a b (,) 53 | -------------------------------------------------------------------------------- /tests/golden_tests.rs: -------------------------------------------------------------------------------- 1 | use goldentests::{TestConfig, TestResult}; 2 | 3 | #[test] 4 | fn goldentests() -> TestResult<()> { 5 | let config = TestConfig::new("target/debug/ante", "examples", "// "); 6 | config.run_tests() 7 | } 8 | 9 | /// Codegen tests are run with cranelift since no backend is explicitly specified. 10 | /// We want to test all backends so re-run them here with `--backend=llvm`. 11 | #[test] 12 | fn llvm_codegen() -> TestResult<()> { 13 | let mut config = TestConfig::new("target/debug/ante", "examples/codegen", "// "); 14 | config.base_args = "--backend=llvm".to_owned(); 15 | config.run_tests() 16 | } 17 | --------------------------------------------------------------------------------