diff --git a/BUILDING_FROM_SOURCE.md b/BUILDING_FROM_SOURCE.md new file mode 100644 index 0000000000..9b177cc718 --- /dev/null +++ b/BUILDING_FROM_SOURCE.md @@ -0,0 +1,8 @@ +# Building the Roc compiler from source + + +## Installing LLVM + +To build the compiler, you need LLVM installed somwhere on your `PATH`. + +For Ubuntu, I used the `Automatic installation script` at [apt.llvm.org](https://apt.llvm.org) diff --git a/Cargo.lock b/Cargo.lock index 5463109bc6..8e15ac894c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,6 +36,21 @@ name = "byteorder" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cargo_toml" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cfg-if" version = "0.1.6" @@ -59,6 +74,20 @@ name = "dogged" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "enum-methods" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "env_logger" version = "0.6.2" @@ -126,6 +155,31 @@ dependencies = [ "unindent 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "inkwell" +version = "0.1.0" +source = "git+https://github.com/TheDan64/inkwell?branch=llvm8-0#d0f5c1e198853bc06d8427fbafb7b068032d1d1a" +dependencies = [ + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "inkwell_internal_macros 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "inkwell_internal_macros" +version = "0.1.0" +source = "git+https://github.com/TheDan64/inkwell?branch=llvm8-0#d0f5c1e198853bc06d8427fbafb7b068032d1d1a" +dependencies = [ + "cargo_toml 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lazy_static" version = "1.3.0" @@ -136,6 +190,18 @@ name = "libc" version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "llvm-sys" +version = "80.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "log" version = "0.4.8" @@ -264,6 +330,14 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro2" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quickcheck" version = "0.8.5" @@ -285,6 +359,11 @@ dependencies = [ "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "quote" version = "0.6.13" @@ -293,6 +372,14 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" version = "0.6.5" @@ -423,6 +510,7 @@ dependencies = [ "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "indoc 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -453,6 +541,21 @@ name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "serde" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sized-chunks" version = "0.3.0" @@ -461,6 +564,16 @@ dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.15.40" @@ -471,6 +584,24 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "0.3.6" @@ -479,16 +610,34 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "toml" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unindent" version = "0.1.3" @@ -520,10 +669,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum cargo_toml 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "097f5ce64ba566a83d9d914fd005de1e5937fdd57d8c5d99a7593040955d75a9" +"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum dogged 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2638df109789fe360f0d9998c5438dd19a36678aaf845e46f285b688b1a1657a" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" "checksum fraction 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1055159ac82fb210c813303f716b6c8db57ace9d5ec2dbbc2e1d7a864c1dd74e" @@ -532,8 +685,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0197597d095c0d11107975d3175173f810ee572c2501ff4de64f4f3f119806" "checksum indoc 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1f59f228c76fda6ecd8dab79683039a7054c748587f682a911094f473647bd6" "checksum indoc-impl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "63f070ef080db3601c1a0ecc75c7bb35104cc0ce2d7c4e049952a96a61d8933b" +"checksum inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)" = "" +"checksum inkwell_internal_macros 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)" = "" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +"checksum llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2110cd4daf9cd8e39dd3b933b1a2a2ac7315e91f7c92b3a20beab526c63b5978" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" @@ -549,9 +705,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6" "checksum proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "982a35d1194084ba319d65c4a68d24ca28f5fdb5b8bc20899e4eef8641ea5178" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0" "checksum quickcheck 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9c35d9c36a562f37eca96e79f66d5fd56eefbc22560dacc4a864cabd2d277456" "checksum quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d7dfc1c4a1e048f5cc7d36a4c4118dfcf31d217c79f4b9a61bad65d68185752c" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" @@ -568,11 +727,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" +"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" "checksum sized-chunks 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2a2eb3fe454976eefb479f78f9b394d34d661b647c6326a3a6e66f68bb12c26" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)" = "bc945221ccf4a7e8c31222b9d1fc77aefdd6638eb901a6ce457a3dc29d4c31e8" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum unindent 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "834b4441326c660336850c5c0926cc20548e848967a5f57bc20c2b741c8d41f4" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" diff --git a/Cargo.toml b/Cargo.toml index 499bc7ce15..4d4a274d9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ fraction = "0.6.2" num = "0.2.0" fxhash = "0.2.1" bumpalo = "2.6.0" +inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm8-0" } [dev-dependencies] pretty_assertions = "0.5.1" diff --git a/src/can/env.rs b/src/can/env.rs index 232f585bd2..f2578efbb1 100644 --- a/src/can/env.rs +++ b/src/can/env.rs @@ -1,3 +1,5 @@ +use bumpalo::collections::Vec; +use bumpalo::Bump; use can::expr::Expr; use can::pattern::Pattern; use can::problem::Problem; @@ -5,44 +7,55 @@ use can::procedure::{Procedure, References}; use can::symbol::Symbol; use collections::{ImMap, MutMap}; use region::{Located, Region}; +use subs::Subs; /// The canonicalization environment for a particular module. -pub struct Env { +pub struct Env<'a> { /// The module's path. Unqualified references to identifiers and variant names are assumed /// to be relative to this path. - pub home: String, + pub home: &'a str, /// Problems we've encountered along the way, which will be reported to the user at the end. - pub problems: Vec, + pub problems: Vec<'a, Problem<'a>>, /// Variants either declared in this module, or imported. - pub variants: ImMap>>, + pub variants: ImMap, Located<&'a str>>, /// Former closures converted to top-level procedures. - pub procedures: MutMap, + pub procedures: MutMap, Procedure<'a>>, + + pub arena: &'a Bump, + + pub subs: Subs<'a>, } -impl Env { - pub fn new(home: String, declared_variants: ImMap>>) -> Env { +impl<'a> Env<'a> { + pub fn new( + arena: &'a Bump, + home: &'a str, + declared_variants: ImMap, Located<&'a str>>, + ) -> Env<'a> { Env { home, variants: declared_variants, - problems: Vec::new(), + problems: Vec::new_in(arena), procedures: MutMap::default(), + arena, + subs: Subs::new(arena), } } - pub fn problem(&mut self, problem: Problem) -> () { + pub fn problem(&mut self, problem: Problem<'a>) -> () { self.problems.push(problem) } pub fn register_closure( &mut self, - symbol: Symbol, - args: Vec>, - body: Located, + symbol: Symbol<'a>, + args: &'a [Located>], + body: Located>, definition: Region, - references: References, + references: References<'a>, ) -> () { // We can't if the closure is self tail recursive yet, because it doesn't know its final name yet. // (Assign sets that.) Assume this is false, and let Assign change it to true after it sets final name. diff --git a/src/can/expr.rs b/src/can/expr.rs index f019cf16d9..48fa274ecd 100644 --- a/src/can/expr.rs +++ b/src/can/expr.rs @@ -4,42 +4,46 @@ use can::symbol::Symbol; use operator::Operator; use region::Located; use std::i64; +use subs::Variable; #[derive(Clone, Debug, PartialEq)] -pub enum Expr { +pub enum Expr<'a> { // Literals - Int(i64), - Float(f64), - Str(Box), - Char(char), // OBSOLETE - List(Vec>), - EmptyList, + Int(Variable, i64), + Float(Variable, f64), + Str(Variable, &'a str), + List(Variable, &'a [Located>]), // Lookups - Var(Symbol), + Var(Variable, Symbol<'a>), /// Works the same as Var, but has an important marking purpose. /// See 13623e3f5f65ea2d703cf155f16650c1e8246502 for the bug this fixed. - FunctionPointer(Symbol), - /// We have a separate variant for this so that we can report errors - /// (including type errors later) in the context of the sugar rather than - /// confusingly talking about the desugared version the user can't see. - InterpolatedStr(Vec<(Box, Located)>, Box), + FunctionPointer(Variable, Symbol<'a>), // Pattern Matching - Case(Box>, Vec<(Located, Located)>), - Assign(Vec<(Located, Located)>, Box>), + Case( + Variable, + &'a Located>, + &'a [(Located>, Located>)], + ), + Define( + Variable, + &'a [(Located>, Located>)], + &'a Located>, + ), // Application - Call(Box>, Vec>), - ApplyVariant(Symbol, Option>>), + Call(Variable, &'a Located>, &'a [Located>]), + + // This has to be separate from Call so we can do precedence reordering + Operator( + Variable, + &'a (Located>, Located, Located>), + ), // Product Types - EmptyRecord, - - // Sugar - If(Box>, Box>, Box>), - Operator(Box>, Located, Box>), + Record(Variable, &'a [Located<(&'a str, Located>)>]), // Compiles, but will crash if reached - RuntimeError(RuntimeError), + RuntimeError(Variable, RuntimeError<'a>), } diff --git a/src/can/mod.rs b/src/can/mod.rs index 3ef7abdc5f..0b7532e245 100644 --- a/src/can/mod.rs +++ b/src/can/mod.rs @@ -7,6 +7,8 @@ use self::problem::RuntimeError::*; use self::procedure::{Procedure, References}; use self::scope::Scope; use self::symbol::Symbol; +use bumpalo::collections::Vec; +use bumpalo::Bump; use collections::{ImMap, ImSet, MutMap, MutSet}; use graph::{strongly_connected_component, topological_sort}; use ident::Ident; @@ -26,23 +28,24 @@ pub mod string; pub mod symbol; pub fn canonicalize_declaration<'a>( - home: String, - name: &str, + arena: &'a Bump, + home: &'a str, + name: &'a str, region: Region, expr: &'a ast::Expr<'a>, - declared_idents: &ImMap, - declared_variants: &ImMap>>, + declared_idents: &ImMap, Region)>, + declared_variants: &ImMap, Located<&'a str>>, ) -> ( - Located, - Output, - Vec, - MutMap, + Located>, + Output<'a>, + Vec<'a, Problem<'a>>, + MutMap, Procedure<'a>>, ) { // If we're canonicalizing the declaration `foo = ...` inside the `Main` module, // scope_prefix will be "Main$foo$" and its first closure will be named "Main$foo$0" let scope_prefix = format!("{}${}$", home, name); - let mut scope = Scope::new(scope_prefix, declared_idents.clone()); - let mut env = Env::new(home, declared_variants.clone()); + let mut scope = Scope::new(&scope_prefix, declared_idents.clone()); + let mut env = Env::new(&arena, home, declared_variants.clone()); let (mut new_loc_expr, output) = canonicalize(&mut env, &mut scope, region, expr); // Apply operator precedence and associativity rules once, after canonicalization is @@ -56,13 +59,13 @@ pub fn canonicalize_declaration<'a>( } #[derive(Clone, Debug, PartialEq)] -pub struct Output { - pub references: References, - pub tail_call: Option, +pub struct Output<'a> { + pub references: References<'a>, + pub tail_call: Option>, } -impl Output { - pub fn new() -> Output { +impl<'a> Output<'a> { + pub fn new() -> Output<'a> { Output { references: References::new(), tail_call: None, @@ -71,31 +74,31 @@ impl Output { } fn canonicalize<'a>( - env: &mut Env, - scope: &mut Scope, + env: &'a mut Env<'a>, + scope: &'a mut Scope<'a>, region: Region, expr: &'a ast::Expr<'a>, -) -> (Located, Output) { +) -> (Located>, Output<'a>) { use self::Expr::*; let (expr, output) = match expr { - ast::Expr::Int(string) => (int_from_parsed(string, &mut env.problems), Output::new()), - ast::Expr::Float(string) => (float_from_parsed(string, &mut env.problems), Output::new()), + ast::Expr::Int(string) => (int_from_parsed(string, env), Output::new()), + ast::Expr::Float(string) => (float_from_parsed(string, env), Output::new()), ast::Expr::Record(fields) => { if fields.is_empty() { - (EmptyRecord, Output::new()) + (Record(env.subs.mk_flex_var(), &[]), Output::new()) } else { panic!("TODO canonicalize nonempty record"); } } - ast::Expr::Str(string) => (Str(string.clone().into()), Output::new()), + ast::Expr::Str(string) => (Str(env.subs.mk_flex_var(), string), Output::new()), ast::Expr::List(elems) => { let mut output = Output::new(); if elems.is_empty() { - (EmptyList, output) + (List(env.subs.mk_flex_var(), &[]), output) } else { - let mut can_elems = Vec::with_capacity(elems.len()); + let mut can_elems = Vec::with_capacity_in(elems.len(), &env.arena); for loc_elem in elems.iter() { let (can_expr, elem_out) = @@ -109,7 +112,10 @@ fn canonicalize<'a>( // A list literal is never a tail call! output.tail_call = None; - (List(can_elems), output) + ( + List(env.subs.mk_flex_var(), can_elems.into_bump_slice()), + output, + ) } } @@ -144,8 +150,8 @@ fn canonicalize<'a>( // Canonicalize the function expression and its arguments let (fn_expr, mut output) = canonicalize(env, scope, loc_fn.region.clone(), &loc_fn.value); - let mut args = Vec::new(); - let mut outputs = Vec::new(); + let mut args = Vec::new_in(&env.arena); + let mut outputs = Vec::new_in(&env.arena); for loc_arg in loc_args.iter() { let (arg_expr, arg_out) = @@ -156,13 +162,17 @@ fn canonicalize<'a>( } match &fn_expr.value { - &Var(ref sym) => { + &Var(_, ref sym) => { output.references.calls.insert(sym.clone()); } _ => (), }; - let expr = Call(Box::new(fn_expr), args); + let expr = Call( + env.subs.mk_flex_var(), + env.arena.alloc(fn_expr), + args.into_bump_slice(), + ); for arg_out in outputs { output.references = output.references.union(arg_out.references); @@ -187,9 +197,9 @@ fn canonicalize<'a>( // because it's the only one that can call a function by name. output.tail_call = match loc_op.value { Pizza => match &right_expr.value { - &Var(ref sym) => Some(sym.clone()), - &Call(ref loc_boxed_expr, _) => match &loc_boxed_expr.value { - Var(sym) => Some(sym.clone()), + &Var(_, sym) => Some(sym.clone()), + &Call(_, loc_boxed_expr, _) => match &loc_boxed_expr.value { + Var(_, sym) => Some(sym.clone()), _ => None, }, _ => None, @@ -197,7 +207,10 @@ fn canonicalize<'a>( _ => None, }; - let expr = Operator(Box::new(left_expr), loc_op.clone(), Box::new(right_expr)); + let expr = Operator( + env.subs.mk_flex_var(), + env.arena.alloc((left_expr, loc_op.clone(), right_expr)), + ); (expr, output) } @@ -205,7 +218,7 @@ fn canonicalize<'a>( let mut output = Output::new(); let ident = Ident::new(module_parts, name); let can_expr = match resolve_ident(&env, &scope, ident, &mut output.references) { - Ok(symbol) => Var(symbol), + Ok(symbol) => Var(env.subs.mk_flex_var(), symbol), Err(ident) => { let loc_ident = Located { region: region.clone(), @@ -214,7 +227,7 @@ fn canonicalize<'a>( env.problem(Problem::UnrecognizedConstant(loc_ident.clone())); - RuntimeError(UnrecognizedConstant(loc_ident)) + RuntimeError(env.subs.mk_flex_var(), UnrecognizedConstant(loc_ident)) } }; @@ -311,6 +324,7 @@ fn canonicalize<'a>( // Add the assigned identifiers to scope. If there's a collision, it means there // was shadowing, which will be handled later. let assigned_idents: Vec<(Ident, (Symbol, Region))> = idents_from_patterns( + &env.arena, defs.clone().iter().flat_map(|(_, def)| match def { Def::AnnotationOnly(_region) => None, Def::BodyOnly(loc_pattern, _expr) => Some(loc_pattern), @@ -331,7 +345,7 @@ fn canonicalize<'a>( // block. Order of assignments doesn't matter, thanks to referential transparency! let (opt_loc_pattern, (loc_can_expr, can_output)) = match def { Def::AnnotationOnly(loc_annotation) => { - let value = Expr::RuntimeError(NoImplementation); + let value = Expr::RuntimeError(env.subs.mk_flex_var(), NoImplementation); let loc_expr = Located { value, region: loc_annotation.region.clone(), @@ -382,15 +396,15 @@ fn canonicalize<'a>( // Only assignments of the form (foo = ...) can be closure declarations or self tail calls. ( &ast::Pattern::Identifier(ref name), - &Pattern::Identifier(ref assigned_symbol), - &FunctionPointer(ref symbol), + &Pattern::Identifier(_, ref assigned_symbol), + &FunctionPointer(_, ref symbol), ) => { // Since everywhere in the code it'll be referred to by its assigned name, // remove its generated name from the procedure map. (We'll re-insert it later.) let mut procedure = env.procedures.remove(&symbol).unwrap(); // The original ident name will be used for debugging and stack traces. - procedure.name = Some(name.to_string()); + procedure.name = Some(name); // The closure is self tail recursive iff it tail calls itself (by assigned name). procedure.is_self_tail_recursive = match &can_output.tail_call { @@ -414,17 +428,17 @@ fn canonicalize<'a>( // Return a reference to the assigned symbol, since the auto-generated one no // longer references any entry in the procedure map! - Var(assigned_symbol.clone()) + Var(env.subs.mk_flex_var(), assigned_symbol.clone()) } _ => loc_can_expr.value, }; - let mut assigned_symbols = Vec::new(); + let mut assigned_symbols = Vec::new_in(env.arena); // Store the referenced locals in the refs_by_assignment map, so we can later figure out // which assigned names reference each other. for (ident, (symbol, region)) in - idents_from_patterns(std::iter::once(loc_pattern), &scope) + idents_from_patterns(&env.arena, std::iter::once(loc_pattern), &scope) { let refs = // Functions' references don't count in assignments. @@ -527,56 +541,79 @@ fn canonicalize<'a>( // This way, during code gen, no assignment will refer to a value that hasn't been initialized yet. // As a bonus, the topological sort also reveals any cycles between the assignments, allowing // us to give a CircularAssignment error. - let successors = |symbol: &Symbol| -> ImSet { + let successors = |symbol: &Symbol<'a>| -> ImSet> { let (_, references) = refs_by_assignment.get(symbol).unwrap(); local_successors(&references, &env.procedures) }; - let assigned_symbols: Vec = can_assignments_by_symbol - .keys() - .into_iter() - .map(Symbol::clone) - .collect(); + let mut assigned_symbols: Vec> = Vec::new_in(env.arena); + + for symbol in can_assignments_by_symbol.keys().into_iter() { + assigned_symbols.push(symbol.clone()) + } match topological_sort(assigned_symbols.as_slice(), successors) { Ok(sorted_symbols) => { - let can_assignments = sorted_symbols - .into_iter() - .rev() // Topological sort gives us the reverse of the sorting we want! - .map(|symbol| can_assignments_by_symbol.get(&symbol).unwrap().clone()) - .collect(); + let mut can_assignments = Vec::new_in(env.arena); - (Assign(can_assignments, Box::new(ret_expr)), output) + for symbol in sorted_symbols + .into_iter() + // Topological sort gives us the reverse of the sorting we want! + .rev() + { + can_assignments + .push(can_assignments_by_symbol.get(&symbol).unwrap().clone()); + } + + ( + Define( + env.subs.mk_flex_var(), + can_assignments.into_bump_slice(), + env.arena.alloc(ret_expr), + ), + output, + ) } Err(node_in_cycle) => { // We have one node we know is in the cycle. // We want to show the entire cycle in the error message, so expand it out. - let mut loc_idents_in_cycle: Vec> = - strongly_connected_component(&node_in_cycle, successors) - .into_iter() - .rev() // Strongly connected component gives us the reverse of the sorting we want! - .map(|symbol| refs_by_assignment.get(&symbol).unwrap().0.clone()) - .collect(); + let mut loc_idents_in_cycle: Vec<'a, Located> = Vec::new_in(env.arena); + for symbol in strongly_connected_component(&node_in_cycle, successors) + .into_iter() + // Strongly connected component gives us the reverse of the sorting we want! + .rev() + { + loc_idents_in_cycle + .push(refs_by_assignment.get(&symbol).unwrap().0.clone()); + } + + // Sort them to make the report more helpful. loc_idents_in_cycle = sort_cyclic_idents( + &env.arena, loc_idents_in_cycle, &mut assigned_idents.iter().map(|(ident, _)| ident), ); env.problem(Problem::CircularAssignment(loc_idents_in_cycle.clone())); - let can_assignments = can_assignments_by_symbol - .values() - .map(|tuple| tuple.clone()) - .collect(); + let mut can_assignments = + Vec::with_capacity_in(can_assignments_by_symbol.len(), env.arena); + + for tuple in can_assignments_by_symbol.values() { + can_assignments.push(tuple.clone()); + } ( - RuntimeError(CircularAssignment( - loc_idents_in_cycle, - can_assignments, - Box::new(ret_expr), - )), + RuntimeError( + env.subs.mk_flex_var(), + CircularAssignment( + loc_idents_in_cycle, + can_assignments, + env.arena.alloc(ret_expr), + ), + ), output, ) } @@ -598,29 +635,30 @@ fn canonicalize<'a>( let mut scope = scope.clone(); let arg_idents: Vec<(Ident, (Symbol, Region))> = - idents_from_patterns(loc_arg_patterns.iter(), &scope); + idents_from_patterns(&env.arena, loc_arg_patterns.iter(), &scope); // Add the arguments' idents to scope.idents. If there's a collision, // it means there was shadowing, which will be handled later. scope.idents = union_pairs(scope.idents, arg_idents.iter()); - let can_args: Vec> = loc_arg_patterns - .into_iter() - .map(|loc_pattern| { - // Exclude the current ident from shadowable_idents; you can't shadow yourself! - // (However, still include it in scope, because you *can* recursively refer to yourself.) - let mut shadowable_idents = scope.idents.clone(); - remove_idents(&loc_pattern.value, &mut shadowable_idents); + let can_args: Vec> = + Vec::with_capacity_in(loc_arg_patterns.len(), env.arena); + + for loc_pattern in loc_arg_patterns.into_iter() { + // Exclude the current ident from shadowable_idents; you can't shadow yourself! + // (However, still include it in scope, because you *can* recursively refer to yourself.) + let mut shadowable_idents = scope.idents.clone(); + remove_idents(&loc_pattern.value, &mut shadowable_idents); + + can_args.push(canonicalize_pattern( + env, + &mut scope, + &FunctionArg, + &loc_pattern, + &mut shadowable_idents, + )) + } - canonicalize_pattern( - env, - &mut scope, - &FunctionArg, - &loc_pattern, - &mut shadowable_idents, - ) - }) - .collect(); let (loc_body_expr, mut output) = canonicalize( env, &mut scope, @@ -650,14 +688,14 @@ fn canonicalize<'a>( // Register it as a top-level procedure in the Env! env.register_closure( symbol.clone(), - can_args, + can_args.into_bump_slice(), loc_body_expr, region.clone(), output.references.clone(), ); // Always return a function pointer, in case that's how the closure is being used (e.g. with Apply). - (FunctionPointer(symbol), output) + (FunctionPointer(env.subs.mk_flex_var(), symbol), output) } //ast::Expr::Case(loc_cond, branches) => { @@ -738,9 +776,9 @@ fn canonicalize<'a>( // (expr, output) //} - ast::Expr::HexInt(string) => (hex_from_parsed(string, &mut env.problems), Output::new()), - ast::Expr::BinaryInt(string) => (bin_from_parsed(string, &mut env.problems), Output::new()), - ast::Expr::OctalInt(string) => (oct_from_parsed(string, &mut env.problems), Output::new()), + ast::Expr::HexInt(string) => (hex_from_parsed(string, env), Output::new()), + ast::Expr::BinaryInt(string) => (bin_from_parsed(string, env), Output::new()), + ast::Expr::OctalInt(string) => (oct_from_parsed(string, env), Output::new()), ast::Expr::SpaceBefore(sub_expr, _spaces) => { return canonicalize(env, scope, region, sub_expr); } @@ -795,10 +833,10 @@ where map } -fn local_successors( - references: &References, - procedures: &MutMap, -) -> ImSet { +fn local_successors<'a>( + references: &'a References<'a>, + procedures: &'a MutMap, Procedure<'a>>, +) -> ImSet> { let mut answer = references.locals.clone(); for call_symbol in references.calls.iter() { @@ -808,7 +846,10 @@ fn local_successors( answer } -fn call_successors(call_symbol: &Symbol, procedures: &MutMap) -> ImSet { +fn call_successors<'a>( + call_symbol: &'a Symbol<'a>, + procedures: &'a MutMap, Procedure<'a>>, +) -> ImSet> { // TODO (this comment should be moved to a GH issue) this may cause an infinite loop if 2 procedures reference each other; may need to track visited procedures! match procedures.get(call_symbol) { Some(procedure) => { @@ -822,21 +863,21 @@ fn call_successors(call_symbol: &Symbol, procedures: &MutMap) } } -fn references_from_local( - assigned_symbol: Symbol, - visited: &mut MutSet, - refs_by_assignment: &MutMap, - procedures: &MutMap, -) -> References { +fn references_from_local<'a, T>( + assigned_symbol: Symbol<'a>, + visited: &'a mut MutSet>, + refs_by_assignment: &'a MutMap, (T, References<'a>)>, + procedures: &'a MutMap, Procedure<'a>>, +) -> References<'a> { match refs_by_assignment.get(&assigned_symbol) { Some((_, refs)) => { - let mut answer = References::new(); + let mut answer: References<'a> = References::new(); visited.insert(assigned_symbol); for local in refs.locals.iter() { if !visited.contains(&local) { - let other_refs = references_from_local( + let other_refs: References<'a> = references_from_local( local.clone(), visited, refs_by_assignment, @@ -876,9 +917,10 @@ fn references_from_local( /// /// Example: the cycle (c ---> a ---> b) becomes (a ---> b ---> c) pub fn sort_cyclic_idents<'a, I>( + arena: &'a Bump, loc_idents: Vec>, ordered_idents: &mut I, -) -> Vec> +) -> Vec<'a, Located> where I: Iterator, { @@ -891,8 +933,8 @@ where }) .unwrap(); - let mut answer = Vec::with_capacity(loc_idents.len()); - let mut end = Vec::with_capacity(loc_idents.len()); + let mut answer = Vec::with_capacity_in(loc_idents.len(), arena); + let mut end = Vec::with_capacity_in(loc_idents.len(), arena); let mut encountered_first_ident = false; for loc_ident in loc_idents { @@ -913,12 +955,12 @@ where answer } -fn references_from_call( - call_symbol: Symbol, - visited: &mut MutSet, - refs_by_assignment: &MutMap, - procedures: &MutMap, -) -> References { +fn references_from_call<'a, T>( + call_symbol: Symbol<'a>, + visited: &'a mut MutSet>, + refs_by_assignment: &'a MutMap)>, + procedures: &'a MutMap>, +) -> References<'a> { match procedures.get(&call_symbol) { Some(procedure) => { let mut answer = procedure.references.clone(); @@ -961,11 +1003,15 @@ fn references_from_call( } } -fn idents_from_patterns<'a, I>(loc_patterns: I, scope: &Scope) -> Vec<(Ident, (Symbol, Region))> +fn idents_from_patterns<'a, I>( + arena: &'a Bump, + loc_patterns: I, + scope: &'a Scope<'a>, +) -> Vec<'a, (Ident, (Symbol<'a>, Region))> where I: Iterator>>, { - let mut answer = Vec::new(); + let mut answer = Vec::new_in(arena); for loc_pattern in loc_patterns { add_idents_from_pattern(&loc_pattern.region, &loc_pattern.value, scope, &mut answer); @@ -976,10 +1022,10 @@ where /// helper function for idents_from_patterns fn add_idents_from_pattern<'a>( - region: &Region, - pattern: &ast::Pattern<'a>, - scope: &Scope, - answer: &mut Vec<(Ident, (Symbol, Region))>, + region: &'a Region, + pattern: &'a ast::Pattern<'a>, + scope: &'a Scope<'a>, + answer: &'a mut Vec<(Ident, (Symbol<'a>, Region))>, ) { use parse::ast::Pattern::*; @@ -1067,12 +1113,12 @@ fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap( + env: &'a Env<'a>, + scope: &'a Scope<'a>, ident: Ident, - references: &mut References, -) -> Result { + references: &'a mut References<'a>, +) -> Result, Ident> { if scope.idents.contains_key(&ident) { let recognized = match ident { Ident::Unqualified(name) => { @@ -1096,7 +1142,7 @@ fn resolve_ident( match ident { Ident::Unqualified(name) => { // Try again, this time using the current module as the path. - let qualified = Ident::Qualified(env.home.clone(), name.clone()); + let qualified = Ident::Qualified(env.home.clone().to_string(), name.clone()); if scope.idents.contains_key(&qualified) { let symbol = Symbol::new(&env.home, &name); @@ -1142,11 +1188,12 @@ fn resolve_ident( // Precedence logic adapted from Gluon by Markus Westerlind, MIT licensed // https://github.com/gluon-lang/gluon // Thank you, Markus! -fn new_op_expr( - left: Box>, +fn new_op_expr<'a>( + env: &'a mut Env<'a>, + left: Located>, op: Located, - right: Box>, -) -> Located { + right: Located>, +) -> Located> { let new_region = Region { start_line: left.region.start_line, start_col: left.region.start_col, @@ -1154,7 +1201,7 @@ fn new_op_expr( end_line: right.region.end_line, end_col: right.region.end_col, }; - let new_expr = Expr::Operator(left, op, right); + let new_expr = Expr::Operator(env.subs.mk_flex_var(), env.arena.alloc((left, op, right))); Located { value: new_expr, @@ -1170,7 +1217,10 @@ fn new_op_expr( /// By design, Roc neither allows custom operators nor has any built-in operators with /// the same precedence and different associativity, so this operation always succeeds /// and can never produce any user-facing errors. -fn apply_precedence_and_associativity(env: &mut Env, expr: Located) -> Located { +fn apply_precedence_and_associativity<'a>( + env: &'a mut Env<'a>, + expr: Located>, +) -> Located> { use can::problem::PrecedenceProblem::*; use operator::Associativity::*; use std::cmp::Ordering; @@ -1179,9 +1229,10 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located) -> Loc // arena bump allocation for Infixes, arg_stack, and op_stack. As long as we // allocate each element inside arg_stack outside the arena, this should end // up being a decent bit more efficient. - let mut infixes = Infixes::new(expr); - let mut arg_stack: Vec>> = Vec::new(); - let mut op_stack: Vec> = Vec::new(); + let arena = env.arena; + let mut infixes = Infixes::new(&expr); + let mut arg_stack: Vec<&'a Located> = Vec::new_in(arena); + let mut op_stack: Vec> = Vec::new_in(arena); while let Some(token) = infixes.next() { match token { @@ -1196,7 +1247,8 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located) -> Loc let left = arg_stack.pop().unwrap(); infixes.next_op = Some(next_op); - arg_stack.push(Box::new(new_op_expr(left, stack_op, right))); + arg_stack + .push(arena.alloc(new_op_expr(env, *left, stack_op, *right))); } Ordering::Greater => { @@ -1216,8 +1268,9 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located) -> Loc let left = arg_stack.pop().unwrap(); infixes.next_op = Some(next_op); - arg_stack - .push(Box::new(new_op_expr(left, stack_op, right))); + arg_stack.push( + arena.alloc(new_op_expr(env, *left, stack_op, *right)), + ); } (RightAssociative, RightAssociative) => { @@ -1235,12 +1288,12 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located) -> Loc let right = arg_stack.pop().unwrap(); let left = arg_stack.pop().unwrap(); - let broken_expr = new_op_expr(left, next_op, right); + let broken_expr = new_op_expr(env, *left, next_op, *right); let region = broken_expr.region.clone(); - let value = Expr::RuntimeError(InvalidPrecedence( - problem, - Box::new(broken_expr), - )); + let value = Expr::RuntimeError( + env.subs.mk_flex_var(), + InvalidPrecedence(problem, arena.alloc(broken_expr)), + ); return Located { region, value }; } @@ -1270,7 +1323,7 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located) -> Loc let right = arg_stack.pop().unwrap(); let left = arg_stack.pop().unwrap(); - arg_stack.push(Box::new(new_op_expr(left, op, right))); + arg_stack.push(arena.alloc(new_op_expr(env, *left, op, *right))); } assert_eq!(arg_stack.len(), 1); @@ -1279,8 +1332,8 @@ fn apply_precedence_and_associativity(env: &mut Env, expr: Located) -> Loc } #[derive(Debug, Clone, PartialEq)] -enum InfixToken { - Arg(Box>), +enum InfixToken<'a> { + Arg(&'a Located>), Op(Located), } @@ -1308,116 +1361,113 @@ enum InfixToken { /// Op: - /// Arg: 8 /// ``` -struct Infixes { +struct Infixes<'a> { /// The next part of the expression that we need to flatten - remaining_expr: Option>>, + remaining_expr: Option<&'a Located>>, /// Cached operator from a previous iteration next_op: Option>, } -impl Infixes { - fn new(expr: Located) -> Infixes { +impl<'a> Infixes<'a> { + fn new(expr: &'a Located>) -> Infixes<'a> { Infixes { - remaining_expr: Some(Box::new(expr)), + remaining_expr: Some(expr), next_op: None, } } } -impl Iterator for Infixes { - type Item = InfixToken; +impl<'a> Iterator for Infixes<'a> { + type Item = InfixToken<'a>; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option> { match self.next_op.take() { Some(op) => Some(InfixToken::Op(op)), - None => self.remaining_expr.take().map(|boxed_expr| { - let expr = *boxed_expr; + None => self.remaining_expr.take().map(|expr| match expr.value { + Expr::Operator(_, (left, op, right)) => { + self.remaining_expr = Some(right); + self.next_op = Some(*op); - match expr.value { - Expr::Operator(left, op, right) => { - self.remaining_expr = Some(right); - self.next_op = Some(op); - - InfixToken::Arg(left) - } - _ => InfixToken::Arg(Box::new(expr)), + InfixToken::Arg(left) } + _ => InfixToken::Arg(expr), }), } } } #[inline(always)] -fn float_from_parsed<'a>(raw: &str, problems: &mut Vec) -> Expr { + +fn float_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> { // Ignore underscores. match raw.replace("_", "").parse::() { - Ok(float) if float.is_finite() => Expr::Float(float), + Ok(float) if float.is_finite() => Expr::Float(env.subs.mk_flex_var(), float), _ => { let runtime_error = FloatOutsideRange(raw.into()); - problems.push(Problem::RuntimeError(runtime_error.clone())); + env.problem(Problem::RuntimeError(runtime_error.clone())); - Expr::RuntimeError(runtime_error) + Expr::RuntimeError(env.subs.mk_flex_var(), runtime_error) } } } #[inline(always)] -fn int_from_parsed<'a>(raw: &str, problems: &mut Vec) -> Expr { +fn int_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> { // Ignore underscores. match raw.replace("_", "").parse::() { - Ok(int) => Expr::Int(int), + Ok(int) => Expr::Int(env.subs.mk_flex_var(), int), Err(_) => { let runtime_error = IntOutsideRange(raw.into()); - problems.push(Problem::RuntimeError(runtime_error.clone())); + env.problem(Problem::RuntimeError(runtime_error.clone())); - Expr::RuntimeError(runtime_error) + Expr::RuntimeError(env.subs.mk_flex_var(), runtime_error) } } } #[inline(always)] -fn hex_from_parsed<'a>(raw: &str, problems: &mut Vec) -> Expr { +fn hex_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> { // Ignore underscores. match i64::from_str_radix(raw.replace("_", "").as_str(), 16) { - Ok(int) => Expr::Int(int), + Ok(int) => Expr::Int(env.subs.mk_flex_var(), int), Err(parse_err) => { let runtime_error = InvalidHex(parse_err, raw.into()); - problems.push(Problem::RuntimeError(runtime_error.clone())); + env.problem(Problem::RuntimeError(runtime_error.clone())); - Expr::RuntimeError(runtime_error) + Expr::RuntimeError(env.subs.mk_flex_var(), runtime_error) } } } #[inline(always)] -fn oct_from_parsed<'a>(raw: &str, problems: &mut Vec) -> Expr { +fn oct_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> { // Ignore underscores. match i64::from_str_radix(raw.replace("_", "").as_str(), 8) { - Ok(int) => Expr::Int(int), + Ok(int) => Expr::Int(env.subs.mk_flex_var(), int), Err(parse_err) => { let runtime_error = InvalidOctal(parse_err, raw.into()); - problems.push(Problem::RuntimeError(runtime_error.clone())); + env.problem(Problem::RuntimeError(runtime_error.clone())); - Expr::RuntimeError(runtime_error) + Expr::RuntimeError(env.subs.mk_flex_var(), runtime_error) } } } #[inline(always)] -fn bin_from_parsed<'a>(raw: &str, problems: &mut Vec) -> Expr { +fn bin_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> { // Ignore underscores. match i64::from_str_radix(raw.replace("_", "").as_str(), 2) { - Ok(int) => Expr::Int(int), + Ok(int) => Expr::Int(env.subs.mk_flex_var(), int), Err(parse_err) => { let runtime_error = InvalidBinary(parse_err, raw.into()); - problems.push(Problem::RuntimeError(runtime_error.clone())); + env.problem(Problem::RuntimeError(runtime_error.clone())); - Expr::RuntimeError(runtime_error) + Expr::RuntimeError(env.subs.mk_flex_var(), runtime_error) } } } diff --git a/src/can/pattern.rs b/src/can/pattern.rs index 1d90ff4f77..f0ec777c02 100644 --- a/src/can/pattern.rs +++ b/src/can/pattern.rs @@ -6,19 +6,20 @@ use collections::ImMap; use ident::{Ident, VariantName}; use parse::ast; use region::{Located, Region}; +use subs::Variable; /// A pattern, including possible problems (e.g. shadowing) so that /// codegen can generate a runtime error if this pattern is reached. #[derive(Clone, Debug, PartialEq)] -pub enum Pattern { - Identifier(Symbol), - Variant(Symbol), - AppliedVariant(Symbol, Vec>), - IntLiteral(i64), - FloatLiteral(f64), - ExactString(String), - EmptyRecordLiteral, - Underscore, +pub enum Pattern<'a> { + Identifier(Variable, Symbol<'a>), + Variant(Variable, Symbol<'a>), + AppliedVariant(Variable, Symbol<'a>, Vec>>), + IntLiteral(Variable, i64), + FloatLiteral(Variable, f64), + ExactString(Variable, &'a str), + EmptyRecordLiteral(Variable), + Underscore(Variable), // Runtime Exceptions Shadowed(Located), @@ -38,13 +39,13 @@ pub enum PatternType { CaseBranch, } -pub fn canonicalize_pattern( - env: &mut Env, - scope: &mut Scope, - pattern_type: &PatternType, - loc_pattern: &Located, - shadowable_idents: &mut ImMap, -) -> Located { +pub fn canonicalize_pattern<'a>( + env: &'a mut Env<'a>, + scope: &'a mut Scope<'a>, + pattern_type: &'a PatternType, + loc_pattern: &'a Located>, + shadowable_idents: &'a mut ImMap, Region)>, +) -> Located> { use self::PatternType::*; use can::ast::Pattern::*; @@ -78,7 +79,7 @@ pub fn canonicalize_pattern( None => { // Make sure we aren't shadowing something in the home module's scope. let qualified_ident = - Ident::Qualified(env.home.clone(), unqualified_ident.name()); + Ident::Qualified(env.home.to_string(), unqualified_ident.name()); match scope.idents.get(&qualified_ident) { Some((_, region)) => { @@ -113,7 +114,7 @@ pub fn canonicalize_pattern( .insert(new_ident.clone(), symbol_and_region.clone()); shadowable_idents.insert(new_ident, symbol_and_region); - Pattern::Identifier(symbol) + Pattern::Identifier(env.subs.mk_flex_var(), symbol) } } } @@ -156,7 +157,7 @@ pub fn canonicalize_pattern( if env.variants.contains_key(&symbol) { // No problems; the qualified variant name was in scope! - Pattern::Variant(symbol) + Pattern::Variant(env.subs.mk_flex_var(), symbol) } else { let loc_name = Located { region: region.clone(), @@ -184,7 +185,7 @@ pub fn canonicalize_pattern( // ptype @ Assignment | ptype @ FunctionArg => unsupported_pattern(env, *ptype, region), // }, &Underscore => match pattern_type { - CaseBranch | FunctionArg => Pattern::Underscore, + CaseBranch | FunctionArg => Pattern::Underscore(env.subs.mk_flex_var()), Assignment => unsupported_pattern(env, Assignment, region.clone()), }, @@ -200,7 +201,11 @@ pub fn canonicalize_pattern( /// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't /// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern. -fn unsupported_pattern<'a>(env: &mut Env, pattern_type: PatternType, region: Region) -> Pattern { +fn unsupported_pattern<'a>( + env: &'a mut Env<'a>, + pattern_type: PatternType, + region: Region, +) -> Pattern<'a> { env.problem(Problem::UnsupportedPattern(pattern_type, region.clone())); Pattern::UnsupportedPattern(region) diff --git a/src/can/problem.rs b/src/can/problem.rs index 17b848afea..ea670fc7b4 100644 --- a/src/can/problem.rs +++ b/src/can/problem.rs @@ -1,3 +1,4 @@ +use bumpalo::collections::Vec; use can::expr::Expr; use can::pattern::{Pattern, PatternType}; use ident::{Ident, VariantName}; @@ -6,7 +7,7 @@ use region::{Located, Region}; /// Problems that can occur in the course of canonicalization. #[derive(Clone, Debug, PartialEq)] -pub enum Problem { +pub enum Problem<'a> { Shadowing(Located), UnrecognizedFunctionName(Located), UnrecognizedConstant(Located), @@ -16,8 +17,8 @@ pub enum Problem { PrecedenceProblem(PrecedenceProblem), // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments! UnsupportedPattern(PatternType, Region), - CircularAssignment(Vec>), - RuntimeError(RuntimeError), + CircularAssignment(Vec<'a, Located>), + RuntimeError(RuntimeError<'a>), } #[derive(Clone, Debug, PartialEq)] @@ -26,20 +27,20 @@ pub enum PrecedenceProblem { } #[derive(Clone, Debug, PartialEq)] -pub enum RuntimeError { - InvalidPrecedence(PrecedenceProblem, Box>), +pub enum RuntimeError<'a> { + InvalidPrecedence(PrecedenceProblem, &'a Located>), UnrecognizedFunctionName(Located), UnrecognizedConstant(Located), UnrecognizedVariant(Located), - FloatOutsideRange(Box), - IntOutsideRange(Box), - InvalidHex(std::num::ParseIntError, Box), - InvalidOctal(std::num::ParseIntError, Box), - InvalidBinary(std::num::ParseIntError, Box), + FloatOutsideRange(&'a str), + IntOutsideRange(&'a str), + InvalidHex(std::num::ParseIntError, &'a str), + InvalidOctal(std::num::ParseIntError, &'a str), + InvalidBinary(std::num::ParseIntError, &'a str), CircularAssignment( - Vec>, - Vec<(Located, Located)>, - Box>, + Vec<'a, Located>, + Vec<'a, (Located>, Located>)>, + &'a Located>, ), /// When the author specifies a type annotation but no implementation diff --git a/src/can/procedure.rs b/src/can/procedure.rs index abf61ef7aa..aaf855fa6b 100644 --- a/src/can/procedure.rs +++ b/src/can/procedure.rs @@ -5,22 +5,22 @@ use collections::ImSet; use region::{Located, Region}; #[derive(Clone, Debug, PartialEq)] -pub struct Procedure { - pub name: Option, +pub struct Procedure<'a> { + pub name: Option<&'a str>, pub is_self_tail_recursive: bool, pub definition: Region, - pub args: Vec>, - pub body: Located, - pub references: References, + pub args: &'a [Located>], + pub body: Located>, + pub references: References<'a>, } -impl Procedure { +impl<'a> Procedure<'a> { pub fn new( definition: Region, - args: Vec>, - body: Located, - references: References, - ) -> Procedure { + args: &'a [Located>], + body: Located>, + references: References<'a>, + ) -> Procedure<'a> { Procedure { name: None, is_self_tail_recursive: false, @@ -36,15 +36,15 @@ impl Procedure { /// to determine how assignments shuold be ordered. We want builds to be reproducible, /// so it's important that building the same code gives the same order every time! #[derive(Clone, Debug, PartialEq)] -pub struct References { - pub locals: ImSet, - pub globals: ImSet, - pub variants: ImSet, - pub calls: ImSet, +pub struct References<'a> { + pub locals: ImSet>, + pub globals: ImSet>, + pub variants: ImSet>, + pub calls: ImSet>, } -impl References { - pub fn new() -> References { +impl<'a> References<'a> { + pub fn new() -> References<'a> { References { locals: ImSet::default(), globals: ImSet::default(), @@ -53,7 +53,7 @@ impl References { } } - pub fn union(mut self, other: References) -> Self { + pub fn union(mut self, other: References<'a>) -> Self { self.locals = self.locals.union(other.locals); self.globals = self.globals.union(other.globals); self.variants = self.variants.union(other.variants); @@ -62,11 +62,11 @@ impl References { self } - pub fn has_local(&self, symbol: &Symbol) -> bool { + pub fn has_local(&self, symbol: &Symbol<'a>) -> bool { self.locals.contains(symbol) } - pub fn has_variant(&self, symbol: &Symbol) -> bool { + pub fn has_variant(&self, symbol: &Symbol<'a>) -> bool { self.variants.contains(symbol) } } diff --git a/src/can/scope.rs b/src/can/scope.rs index 7ee6aefe99..4b89567898 100644 --- a/src/can/scope.rs +++ b/src/can/scope.rs @@ -4,14 +4,17 @@ use ident::Ident; use region::Region; #[derive(Clone, Debug, PartialEq)] -pub struct Scope { - pub idents: ImMap, - symbol_prefix: String, +pub struct Scope<'a> { + pub idents: ImMap, Region)>, + symbol_prefix: &'a str, next_unique_id: u64, } -impl Scope { - pub fn new(symbol_prefix: String, declared_idents: ImMap) -> Scope { +impl<'a> Scope<'a> { + pub fn new( + symbol_prefix: &'a str, + declared_idents: ImMap, Region)>, + ) -> Scope<'a> { Scope { symbol_prefix, @@ -23,11 +26,11 @@ impl Scope { } } - pub fn symbol(&self, name: &str) -> Symbol { + pub fn symbol(&'a self, name: &'a str) -> Symbol<'a> { Symbol::new(&self.symbol_prefix, name) } - pub fn gen_unique_symbol(&mut self) -> Symbol { + pub fn gen_unique_symbol(&mut self) -> Symbol<'a> { self.next_unique_id = self.next_unique_id + 1; Symbol::new(&self.symbol_prefix, &self.next_unique_id.to_string()) diff --git a/src/can/symbol.rs b/src/can/symbol.rs index 52fc8f1c0b..f9e7457da0 100644 --- a/src/can/symbol.rs +++ b/src/can/symbol.rs @@ -3,14 +3,14 @@ use ident::VariantName; /// A globally unique identifier, used for both vars and variants. /// It will be used directly in code gen. #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Symbol(String); +pub struct Symbol<'a>(&'a str); -impl Symbol { - pub fn new(prefix: &str, name: &str) -> Symbol { - Symbol(format!("{}{}", prefix, name)) +impl<'a> Symbol<'a> { + pub fn new(prefix: &'a str, name: &'a str) -> Symbol<'a> { + Symbol(&format!("{}{}", prefix, name)) } - pub fn from_variant(variant_name: &VariantName, home: &str) -> Symbol { + pub fn from_variant(variant_name: &'a VariantName, home: &'a str) -> Symbol<'a> { match &variant_name { &VariantName::Unqualified(ref name) => Symbol::new(home, name), @@ -19,8 +19,8 @@ impl Symbol { } } -impl Into for Symbol { - fn into(self) -> String { +impl<'a> Into<&'a str> for Symbol<'a> { + fn into(self) -> &'a str { let Symbol(string) = self; string diff --git a/src/constrain.rs b/src/constrain.rs index 8e149b6daf..cf7b881d8c 100644 --- a/src/constrain.rs +++ b/src/constrain.rs @@ -1,14 +1,14 @@ -use can::expr::Expr::{self, *}; -use can::pattern::Pattern; +use can::expr::Expr; +// use can::pattern::Pattern; use can::procedure::Procedure; -use can::symbol::Symbol; +// use can::symbol::Symbol; use collections::ImMap; -use operator::{ArgSide, Operator}; -use region::{Located, Region}; -use subs::{Subs, Variable}; -use types::Constraint::{self, *}; -use types::Type::{self, *}; -use types::{Expected, Expected::*, LetConstraint, Reason}; +// use operator::{ArgSide, Operator}; +use region::Located; +use subs::Subs; +use types::Constraint; +use types::Expected; +use types::Type; /// This lets us share bound type variables between nested annotations, e.g. /// @@ -19,583 +19,585 @@ use types::{Expected, Expected::*, LetConstraint, Reason}; /// 42 /// /// In elm/compiler this is called RTV - the "Rigid Type Variables" dictionary. -type BoundTypeVars = ImMap; +type BoundTypeVars<'a> = ImMap<&'a str, Type<'a>>; -pub fn constrain( - bound_vars: &BoundTypeVars, - subs: &mut Subs, - loc_expr: Located, - expected: Expected, -) -> Constraint { - let region = loc_expr.region; +pub fn constrain<'a>( + bound_vars: &'a BoundTypeVars<'a>, + subs: &'a mut Subs<'a>, + loc_expr: Located>, + expected: Expected>, +) -> Constraint<'a> { + panic!("TODO inline constrain"); + // let region = loc_expr.region; - match loc_expr.value { - Int(_) => int_literal(subs, expected, region), - Float(_) => float_literal(subs, expected, region), - Str(_) => Eq(string(), expected, region), - InterpolatedStr(pairs, _) => { - let mut constraints = Vec::with_capacity(pairs.len() + 1); + // match loc_expr.value { + // Int(_) => int_literal(subs, expected, region), + // Float(_) => float_literal(subs, expected, region), + // Str(_) => Eq(string(), expected, region), + // InterpolatedStr(pairs, _) => { + // let mut constraints = Vec::with_capacity(pairs.len() + 1); - for (_, loc_interpolated_expr) in pairs { - let expected_str = ForReason( - Reason::InterpolatedStringVar, - string(), - loc_interpolated_expr.region.clone(), - ); - let constraint = constrain(bound_vars, subs, loc_interpolated_expr, expected_str); + // for (_, loc_interpolated_expr) in pairs { + // let expected_str = ForReason( + // Reason::InterpolatedStringVar, + // string(), + // loc_interpolated_expr.region.clone(), + // ); + // let constraint = constrain(bound_vars, subs, loc_interpolated_expr, expected_str); - constraints.push(constraint); - } + // constraints.push(constraint); + // } - constraints.push(Eq(string(), expected, region)); + // constraints.push(Eq(string(), expected, region)); - And(constraints) - } - EmptyRecord => Eq(EmptyRec, expected, region), - EmptyList => Eq(empty_list(subs.mk_flex_var()), expected, region), - List(elems) => list(elems, bound_vars, subs, expected, region), - Var(sym) | FunctionPointer(sym) => Lookup(sym, expected, region), - Assign(assignments, ret_expr) => { - let ret_con = constrain(bound_vars, subs, *ret_expr, expected); + // And(constraints) + // } + // EmptyRecord => Eq(EmptyRec, expected, region), + // EmptyList => Eq(empty_list(subs.mk_flex_var()), expected, region), + // List(elems) => list(elems, bound_vars, subs, expected, region), + // Var(sym) | FunctionPointer(sym) => Lookup(sym, expected, region), + // Assign(assignments, ret_expr) => { + // let ret_con = constrain(bound_vars, subs, *ret_expr, expected); - if assignments.len() == 1 { - // Don't bother allocating a Vec of them if there's only one! - let (loc_pattern, loc_expr) = assignments.into_iter().next().unwrap(); + // if assignments.len() == 1 { + // // Don't bother allocating a Vec of them if there's only one! + // let (loc_pattern, loc_expr) = assignments.into_iter().next().unwrap(); - constrain_def(loc_pattern, loc_expr, bound_vars, subs, ret_con) - } else { - constrain_defs(assignments, bound_vars, subs, ret_con) - } - } - Call(box_loc_fn_expr, args) => { - constrain_call(bound_vars, subs, *box_loc_fn_expr, args, expected, region) - } - Expr::Operator(l_box_loc_expr, loc_op, r_box_loc_expr) => constrain_op( - bound_vars, - subs, - *l_box_loc_expr, - loc_op, - *r_box_loc_expr, - expected, - region, - ), - _ => panic!("TODO constraints for {:?}", loc_expr.value), - } + // constrain_def(loc_pattern, loc_expr, bound_vars, subs, ret_con) + // } else { + // constrain_defs(assignments, bound_vars, subs, ret_con) + // } + // } + // Call(box_loc_fn_expr, args) => { + // constrain_call(bound_vars, subs, *box_loc_fn_expr, args, expected, region) + // } + // Expr::Operator(l_box_loc_expr, loc_op, r_box_loc_expr) => constrain_op( + // bound_vars, + // subs, + // *l_box_loc_expr, + // loc_op, + // *r_box_loc_expr, + // expected, + // region, + // ), + // _ => panic!("TODO constraints for {:?}", loc_expr.value), + // } } -fn constrain_op( - bound_vars: &BoundTypeVars, - subs: &mut Subs, - l_loc_expr: Located, - loc_op: Located, - r_loc_expr: Located, - expected: Expected, - region: Region, -) -> Constraint { - let op = loc_op.value; - let op_types = Type::for_operator(op); - // TODO use fn_var - let _fn_var = subs.mk_flex_var(); - let ret_var = subs.mk_flex_var(); - let ret_type = Variable(ret_var); - let ret_reason = Reason::OperatorRet(op); - let expected_ret_type = ForReason(ret_reason, op_types.ret, region.clone()); +// fn constrain_op( +// bound_vars: &BoundTypeVars, +// subs: &mut Subs, +// l_loc_expr: Located, +// loc_op: Located, +// r_loc_expr: Located, +// expected: Expected, +// region: Region, +// ) -> Constraint { +// let op = loc_op.value; +// let op_types = Type::for_operator(op); +// // TODO use fn_var +// let _fn_var = subs.mk_flex_var(); +// let ret_var = subs.mk_flex_var(); +// let ret_type = Variable(ret_var); +// let ret_reason = Reason::OperatorRet(op); +// let expected_ret_type = ForReason(ret_reason, op_types.ret, region.clone()); - let (_l_var, l_con) = constrain_op_arg( - ArgSide::Left, - bound_vars, - subs, - op, - op_types.left, - l_loc_expr, - ); - let (_r_var, r_con) = constrain_op_arg( - ArgSide::Right, - bound_vars, - subs, - op, - op_types.right, - r_loc_expr, - ); +// let (_l_var, l_con) = constrain_op_arg( +// ArgSide::Left, +// bound_vars, +// subs, +// op, +// op_types.left, +// l_loc_expr, +// ); +// let (_r_var, r_con) = constrain_op_arg( +// ArgSide::Right, +// bound_vars, +// subs, +// op, +// op_types.right, +// r_loc_expr, +// ); - // TODO occurs check! - // let vars = vec![fn_var, ret_var, l_var, r_var]; - // return $ exists (funcVar:resultVar:argVars) $ CAnd ... +// // TODO occurs check! +// // let vars = vec![fn_var, ret_var, l_var, r_var]; +// // return $ exists (funcVar:resultVar:argVars) $ CAnd ... - And(vec![ - // the constraint from constrain on l_expr, expecting its hardcoded type - l_con, - // the constraint from constrain on r_expr, expecting its hardcoded type - r_con, - // The operator's args and return type should be its hardcoded types - Eq(ret_type.clone(), expected_ret_type, region.clone()), - // Finally, link the operator's return type to the given expected type - Eq(ret_type, expected, region), - ]) +// And(vec![ +// // the constraint from constrain on l_expr, expecting its hardcoded type +// l_con, +// // the constraint from constrain on r_expr, expecting its hardcoded type +// r_con, +// // The operator's args and return type should be its hardcoded types +// Eq(ret_type.clone(), expected_ret_type, region.clone()), +// // Finally, link the operator's return type to the given expected type +// Eq(ret_type, expected, region), +// ]) +// } + +// #[inline(always)] +// fn constrain_op_arg( +// arg_side: ArgSide, +// bound_vars: &BoundTypeVars, +// subs: &mut Subs, +// op: Operator, +// typ: Type, +// loc_arg: Located, +// ) -> (Variable, Constraint) { +// let region = loc_arg.region.clone(); +// let arg_var = subs.mk_flex_var(); +// let arg_type = Variable(arg_var); +// let reason = Reason::OperatorArg(op, arg_side); +// let expected_arg = ForReason(reason, typ, region.clone()); +// let arg_con = And(vec![ +// // Recursively constrain the variable +// constrain(bound_vars, subs, loc_arg, NoExpectation(arg_type.clone())), +// // The variable should ultimately equal the hardcoded expected type +// Eq(arg_type, expected_arg, region), +// ]); + +// (arg_var, arg_con) +// } + +// fn constrain_call( +// bound_vars: &BoundTypeVars, +// subs: &mut Subs, +// loc_expr: Located, +// args: Vec>, +// expected: Expected, +// region: Region, +// ) -> Constraint { +// // The expression that evaluates to the function being called, e.g. `foo` in +// // (foo) bar baz +// let fn_var = subs.mk_flex_var(); +// let fn_type = Variable(fn_var); +// let fn_region = loc_expr.region.clone(); +// let fn_expected = NoExpectation(fn_type.clone()); +// let fn_con = constrain(bound_vars, subs, loc_expr, fn_expected); +// let fn_reason = +// // TODO look up the name and use NamedFnArg if possible. +// Reason::AnonymousFnCall(args.len() as u8); + +// // The function's return type +// let ret_var = subs.mk_flex_var(); +// let ret_type = Variable(ret_var); + +// // This will be used in the occurs check +// let mut vars = Vec::with_capacity(2 + args.len()); + +// vars.push(fn_var); +// vars.push(ret_var); + +// let mut arg_types = Vec::with_capacity(args.len()); +// let mut arg_cons = Vec::with_capacity(args.len()); + +// for (index, loc_arg) in args.into_iter().enumerate() { +// let region = loc_arg.region.clone(); +// let arg_var = subs.mk_flex_var(); +// let arg_type = Variable(arg_var); +// let reason = +// // TODO look up the name and use NamedFnArg if possible. +// Reason::AnonymousFnArg(index as u8); +// let expected_arg = ForReason(reason, arg_type.clone(), region.clone()); +// let arg_con = constrain(bound_vars, subs, loc_arg, expected_arg); + +// vars.push(arg_var); +// arg_types.push(arg_type); +// arg_cons.push(arg_con); +// } + +// // TODO occurs check! +// // return $ exists vars $ CAnd ... + +// let expected_fn_type = ForReason( +// fn_reason, +// Function(arg_types, Box::new(ret_type.clone())), +// region.clone(), +// ); + +// And(vec![ +// fn_con, +// Eq(fn_type, expected_fn_type, fn_region), +// And(arg_cons), +// Eq(ret_type, expected, region), +// ]) +// } + +// pub fn constrain_defs( +// assignments: Vec<(Located, Located)>, +// bound_vars: &BoundTypeVars, +// subs: &mut Subs, +// ret_con: Constraint, +// ) -> Constraint { +// let rigid_info = Info::with_capacity(assignments.len()); +// let mut flex_info = Info::with_capacity(assignments.len()); + +// for (loc_pattern, loc_expr) in assignments { +// let mut state = PatternState { +// assignment_types: ImMap::default(), +// vars: Vec::with_capacity(1), +// reversed_constraints: Vec::with_capacity(1), +// }; +// let pattern_var = subs.mk_flex_var(); +// let pattern_type = Type::Variable(pattern_var); + +// flex_info.vars.push(pattern_var); + +// state.add_pattern(loc_pattern.clone(), NoExpectation(pattern_type.clone())); +// state.reversed_constraints.reverse(); + +// let assignments_constraint = And(state.reversed_constraints); + +// // Set up types for the expr we're assigned to. +// let expr_var = subs.mk_flex_var(); +// let expr_type = Type::Variable(expr_var); + +// // Any time there's a lookup on this symbol in the outer Let, +// // it should result in this expression's type. After all, this +// // is the type to which this symbol is assigned! +// add_pattern_to_lookup_types( +// loc_pattern, +// &mut flex_info.assignment_types, +// expr_type.clone(), +// ); + +// let expr_con = constrain(bound_vars, subs, loc_expr, NoExpectation(expr_type)); +// let def_con = Let(Box::new(LetConstraint { +// rigid_vars: Vec::new(), +// flex_vars: state.vars, +// assignment_types: state.assignment_types, +// assignments_constraint, +// ret_constraint: expr_con, +// })); + +// flex_info.constraints.push(def_con); +// } + +// // Rigid constraint +// Let(Box::new(LetConstraint { +// rigid_vars: rigid_info.vars, +// flex_vars: Vec::new(), +// assignment_types: rigid_info.assignment_types, +// assignments_constraint: +// // Flex constraint +// Let(Box::new(LetConstraint { +// rigid_vars: Vec::new(), +// flex_vars: flex_info.vars, +// assignment_types: flex_info.assignment_types.clone(), +// assignments_constraint: +// // Final flex constraints +// Let(Box::new(LetConstraint { +// rigid_vars: Vec::new(), +// flex_vars: Vec::new(), +// assignment_types: flex_info.assignment_types, +// assignments_constraint: True, +// ret_constraint: And(flex_info.constraints) +// })), +// ret_constraint: And(vec![And(rigid_info.constraints), ret_con]) +// })), +// ret_constraint: True, +// })) +// } + +// struct Info { +// pub vars: Vec, +// pub constraints: Vec, +// pub assignment_types: ImMap>, +// } + +// impl Info { +// pub fn with_capacity(capacity: usize) -> Self { +// Info { +// vars: Vec::with_capacity(capacity), +// constraints: Vec::with_capacity(capacity), +// assignment_types: ImMap::default(), +// } +// } +// } + +// fn empty_list(var: Variable) -> Type { +// builtin_type("List", "List", vec![Type::Variable(var)]) +// } + +// fn string() -> Type { +// builtin_type("Str", "Str", Vec::new()) +// } + +// fn _num(var: Variable) -> Type { +// builtin_type("Num", "Num", vec![Type::Variable(var)]) +// } + +// fn list( +// loc_elems: Vec>, +// bound_vars: &BoundTypeVars, +// subs: &mut Subs, +// expected: Expected, +// region: Region, +// ) -> Constraint { +// let list_var = subs.mk_flex_var(); // `v` in the type (List v) +// let list_type = Type::Variable(list_var); +// let mut constraints = Vec::with_capacity(1 + (loc_elems.len() * 2)); + +// for loc_elem in loc_elems { +// let elem_var = subs.mk_flex_var(); +// let elem_type = Variable(elem_var); +// let elem_expected = NoExpectation(elem_type.clone()); +// let elem_constraint = constrain(bound_vars, subs, loc_elem, elem_expected); +// let list_elem_constraint = Eq( +// list_type.clone(), +// ForReason(Reason::ElemInList, elem_type, region.clone()), +// region.clone(), +// ); + +// constraints.push(elem_constraint); +// constraints.push(list_elem_constraint); +// } + +// constraints.push(Eq( +// builtin_type("List", "List", vec![list_type]), +// expected, +// region, +// )); + +// And(constraints) +// } + +// #[inline(always)] +// fn int_literal(subs: &mut Subs, expected: Expected, region: Region) -> Constraint { +// let typ = number_literal_type("Int", "Integer"); +// let reason = Reason::IntLiteral; + +// num_literal(typ, reason, subs, expected, region) +// } + +// #[inline(always)] +// fn float_literal(subs: &mut Subs, expected: Expected, region: Region) -> Constraint { +// let typ = number_literal_type("Float", "FloatingPoint"); +// let reason = Reason::FloatLiteral; + +// num_literal(typ, reason, subs, expected, region) +// } + +// #[inline(always)] +// fn num_literal( +// literal_type: Type, +// reason: Reason, +// subs: &mut Subs, +// expected: Expected, +// region: Region, +// ) -> Constraint { +// let num_var = subs.mk_flex_var(); +// let num_type = Variable(num_var); +// let expected_literal = ForReason(reason, literal_type, region.clone()); + +// And(vec![ +// Eq(num_type.clone(), expected_literal, region.clone()), +// Eq(num_type, expected, region.clone()), +// ]) +// } + +// #[inline(always)] +// fn number_literal_type(module_name: &str, type_name: &str) -> Type { +// builtin_type( +// "Num", +// "Num", +// vec![builtin_type(module_name, type_name, Vec::new())], +// ) +// } + +// #[inline(always)] +// fn builtin_type(module_name: &str, type_name: &str, args: Vec) -> Type { +// Type::Apply(module_name.to_string(), type_name.to_string(), args) +// } + +// pub fn constrain_def( +// loc_pattern: Located, +// loc_expr: Located, +// bound_vars: &BoundTypeVars, +// subs: &mut Subs, +// ret_constraint: Constraint, +// ) -> Constraint { +// let mut state = PatternState { +// assignment_types: ImMap::default(), +// vars: Vec::with_capacity(1), +// reversed_constraints: Vec::with_capacity(1), +// }; +// let mut vars = Vec::with_capacity(state.vars.capacity()); +// let pattern_var = subs.mk_flex_var(); +// let pattern_type = Type::Variable(pattern_var); + +// state.add_pattern(loc_pattern.clone(), NoExpectation(pattern_type.clone())); + +// vars.push(pattern_var); + +// // Set up types for the expr we're assigned to. +// let expr_var = subs.mk_flex_var(); +// let expr_type = Type::Variable(expr_var); + +// // These types are *only* for the current pattern. In contrast, the ones +// // in PatternState represent +// let mut lookup_types: ImMap> = ImMap::default(); + +// // Any time there's a lookup on this symbol in the outer Let, +// // it should result in this expression's type. After all, this +// // is the type to which this symbol is assigned! +// add_pattern_to_lookup_types(loc_pattern, &mut lookup_types, expr_type.clone()); + +// state.reversed_constraints.reverse(); + +// Let(Box::new(LetConstraint { +// rigid_vars: Vec::new(), +// flex_vars: vars, +// assignments_constraint: +// // This nested constraint represents the actually constrained expr. +// Let(Box::new(LetConstraint { +// rigid_vars: Vec::new(), +// flex_vars: state.vars, +// assignment_types: state.assignment_types, +// assignments_constraint: And(state.reversed_constraints), +// ret_constraint: constrain(bound_vars, subs, loc_expr, NoExpectation(expr_type)) +// })), +// assignment_types: lookup_types, +// ret_constraint, +// })) +// } + +// fn add_pattern_to_lookup_types( +// loc_pattern: Located, +// lookup_types: &mut ImMap>, +// expr_type: Type, +// ) { +// let region = loc_pattern.region; + +// match loc_pattern.value { +// Pattern::Identifier(symbol) => { +// let loc_type = Located { +// region, +// value: expr_type, +// }; + +// lookup_types.insert(symbol, loc_type); +// } +// _ => panic!("TODO constrain patterns other than Identifier"), +// } +// } + +pub fn constrain_procedure<'a>( + bound_vars: &'a BoundTypeVars<'a>, + subs: &'a mut Subs<'a>, + proc: Procedure<'a>, + expected: Expected>, +) -> Constraint<'a> { + panic!("TODO inline constrain_procedure"); + // let mut state = PatternState { + // assignment_types: ImMap::default(), + // vars: Vec::with_capacity(proc.args.len()), + // reversed_constraints: Vec::with_capacity(1), + // }; + + // let args = constrain_args(proc.args.into_iter(), subs, &mut state); + // let body_type = NoExpectation(args.ret_type); + // let ret_constraint = constrain(bound_vars, subs, proc.body, body_type); + + // state.reversed_constraints.reverse(); + + // let assignments_constraint = And(state.reversed_constraints); + + // // panic!("TODO occurs check"); + + // And(vec![ + // Let(Box::new(LetConstraint { + // rigid_vars: Vec::new(), + // flex_vars: state.vars, + // assignment_types: state.assignment_types, + // assignments_constraint, + // ret_constraint, + // })), + // Eq(args.typ, expected, proc.definition), + // ]) } -#[inline(always)] -fn constrain_op_arg( - arg_side: ArgSide, - bound_vars: &BoundTypeVars, - subs: &mut Subs, - op: Operator, - typ: Type, - loc_arg: Located, -) -> (Variable, Constraint) { - let region = loc_arg.region.clone(); - let arg_var = subs.mk_flex_var(); - let arg_type = Variable(arg_var); - let reason = Reason::OperatorArg(op, arg_side); - let expected_arg = ForReason(reason, typ, region.clone()); - let arg_con = And(vec![ - // Recursively constrain the variable - constrain(bound_vars, subs, loc_arg, NoExpectation(arg_type.clone())), - // The variable should ultimately equal the hardcoded expected type - Eq(arg_type, expected_arg, region), - ]); - - (arg_var, arg_con) -} - -fn constrain_call( - bound_vars: &BoundTypeVars, - subs: &mut Subs, - loc_expr: Located, - args: Vec>, - expected: Expected, - region: Region, -) -> Constraint { - // The expression that evaluates to the function being called, e.g. `foo` in - // (foo) bar baz - let fn_var = subs.mk_flex_var(); - let fn_type = Variable(fn_var); - let fn_region = loc_expr.region.clone(); - let fn_expected = NoExpectation(fn_type.clone()); - let fn_con = constrain(bound_vars, subs, loc_expr, fn_expected); - let fn_reason = - // TODO look up the name and use NamedFnArg if possible. - Reason::AnonymousFnCall(args.len() as u8); - - // The function's return type - let ret_var = subs.mk_flex_var(); - let ret_type = Variable(ret_var); - - // This will be used in the occurs check - let mut vars = Vec::with_capacity(2 + args.len()); - - vars.push(fn_var); - vars.push(ret_var); - - let mut arg_types = Vec::with_capacity(args.len()); - let mut arg_cons = Vec::with_capacity(args.len()); - - for (index, loc_arg) in args.into_iter().enumerate() { - let region = loc_arg.region.clone(); - let arg_var = subs.mk_flex_var(); - let arg_type = Variable(arg_var); - let reason = - // TODO look up the name and use NamedFnArg if possible. - Reason::AnonymousFnArg(index as u8); - let expected_arg = ForReason(reason, arg_type.clone(), region.clone()); - let arg_con = constrain(bound_vars, subs, loc_arg, expected_arg); - - vars.push(arg_var); - arg_types.push(arg_type); - arg_cons.push(arg_con); - } - - // TODO occurs check! - // return $ exists vars $ CAnd ... - - let expected_fn_type = ForReason( - fn_reason, - Function(arg_types, Box::new(ret_type.clone())), - region.clone(), - ); - - And(vec![ - fn_con, - Eq(fn_type, expected_fn_type, fn_region), - And(arg_cons), - Eq(ret_type, expected, region), - ]) -} - -pub fn constrain_defs( - assignments: Vec<(Located, Located)>, - bound_vars: &BoundTypeVars, - subs: &mut Subs, - ret_con: Constraint, -) -> Constraint { - let rigid_info = Info::with_capacity(assignments.len()); - let mut flex_info = Info::with_capacity(assignments.len()); - - for (loc_pattern, loc_expr) in assignments { - let mut state = PatternState { - assignment_types: ImMap::default(), - vars: Vec::with_capacity(1), - reversed_constraints: Vec::with_capacity(1), - }; - let pattern_var = subs.mk_flex_var(); - let pattern_type = Type::Variable(pattern_var); - - flex_info.vars.push(pattern_var); - - state.add_pattern(loc_pattern.clone(), NoExpectation(pattern_type.clone())); - state.reversed_constraints.reverse(); - - let assignments_constraint = And(state.reversed_constraints); - - // Set up types for the expr we're assigned to. - let expr_var = subs.mk_flex_var(); - let expr_type = Type::Variable(expr_var); - - // Any time there's a lookup on this symbol in the outer Let, - // it should result in this expression's type. After all, this - // is the type to which this symbol is assigned! - add_pattern_to_lookup_types( - loc_pattern, - &mut flex_info.assignment_types, - expr_type.clone(), - ); - - let expr_con = constrain(bound_vars, subs, loc_expr, NoExpectation(expr_type)); - let def_con = Let(Box::new(LetConstraint { - rigid_vars: Vec::new(), - flex_vars: state.vars, - assignment_types: state.assignment_types, - assignments_constraint, - ret_constraint: expr_con, - })); - - flex_info.constraints.push(def_con); - } - - // Rigid constraint - Let(Box::new(LetConstraint { - rigid_vars: rigid_info.vars, - flex_vars: Vec::new(), - assignment_types: rigid_info.assignment_types, - assignments_constraint: - // Flex constraint - Let(Box::new(LetConstraint { - rigid_vars: Vec::new(), - flex_vars: flex_info.vars, - assignment_types: flex_info.assignment_types.clone(), - assignments_constraint: - // Final flex constraints - Let(Box::new(LetConstraint { - rigid_vars: Vec::new(), - flex_vars: Vec::new(), - assignment_types: flex_info.assignment_types, - assignments_constraint: True, - ret_constraint: And(flex_info.constraints) - })), - ret_constraint: And(vec![And(rigid_info.constraints), ret_con]) - })), - ret_constraint: True, - })) -} - -struct Info { - pub vars: Vec, - pub constraints: Vec, - pub assignment_types: ImMap>, -} - -impl Info { - pub fn with_capacity(capacity: usize) -> Self { - Info { - vars: Vec::with_capacity(capacity), - constraints: Vec::with_capacity(capacity), - assignment_types: ImMap::default(), - } - } -} - -fn empty_list(var: Variable) -> Type { - builtin_type("List", "List", vec![Type::Variable(var)]) -} - -fn string() -> Type { - builtin_type("Str", "Str", Vec::new()) -} - -fn _num(var: Variable) -> Type { - builtin_type("Num", "Num", vec![Type::Variable(var)]) -} - -fn list( - loc_elems: Vec>, - bound_vars: &BoundTypeVars, - subs: &mut Subs, - expected: Expected, - region: Region, -) -> Constraint { - let list_var = subs.mk_flex_var(); // `v` in the type (List v) - let list_type = Type::Variable(list_var); - let mut constraints = Vec::with_capacity(1 + (loc_elems.len() * 2)); - - for loc_elem in loc_elems { - let elem_var = subs.mk_flex_var(); - let elem_type = Variable(elem_var); - let elem_expected = NoExpectation(elem_type.clone()); - let elem_constraint = constrain(bound_vars, subs, loc_elem, elem_expected); - let list_elem_constraint = Eq( - list_type.clone(), - ForReason(Reason::ElemInList, elem_type, region.clone()), - region.clone(), - ); - - constraints.push(elem_constraint); - constraints.push(list_elem_constraint); - } - - constraints.push(Eq( - builtin_type("List", "List", vec![list_type]), - expected, - region, - )); - - And(constraints) -} - -#[inline(always)] -fn int_literal(subs: &mut Subs, expected: Expected, region: Region) -> Constraint { - let typ = number_literal_type("Int", "Integer"); - let reason = Reason::IntLiteral; - - num_literal(typ, reason, subs, expected, region) -} - -#[inline(always)] -fn float_literal(subs: &mut Subs, expected: Expected, region: Region) -> Constraint { - let typ = number_literal_type("Float", "FloatingPoint"); - let reason = Reason::FloatLiteral; - - num_literal(typ, reason, subs, expected, region) -} - -#[inline(always)] -fn num_literal( - literal_type: Type, - reason: Reason, - subs: &mut Subs, - expected: Expected, - region: Region, -) -> Constraint { - let num_var = subs.mk_flex_var(); - let num_type = Variable(num_var); - let expected_literal = ForReason(reason, literal_type, region.clone()); - - And(vec![ - Eq(num_type.clone(), expected_literal, region.clone()), - Eq(num_type, expected, region.clone()), - ]) -} - -#[inline(always)] -fn number_literal_type(module_name: &str, type_name: &str) -> Type { - builtin_type( - "Num", - "Num", - vec![builtin_type(module_name, type_name, Vec::new())], - ) -} - -#[inline(always)] -fn builtin_type(module_name: &str, type_name: &str, args: Vec) -> Type { - Type::Apply(module_name.to_string(), type_name.to_string(), args) -} - -pub fn constrain_def( - loc_pattern: Located, - loc_expr: Located, - bound_vars: &BoundTypeVars, - subs: &mut Subs, - ret_constraint: Constraint, -) -> Constraint { - let mut state = PatternState { - assignment_types: ImMap::default(), - vars: Vec::with_capacity(1), - reversed_constraints: Vec::with_capacity(1), - }; - let mut vars = Vec::with_capacity(state.vars.capacity()); - let pattern_var = subs.mk_flex_var(); - let pattern_type = Type::Variable(pattern_var); - - state.add_pattern(loc_pattern.clone(), NoExpectation(pattern_type.clone())); - - vars.push(pattern_var); - - // Set up types for the expr we're assigned to. - let expr_var = subs.mk_flex_var(); - let expr_type = Type::Variable(expr_var); - - // These types are *only* for the current pattern. In contrast, the ones - // in PatternState represent - let mut lookup_types: ImMap> = ImMap::default(); - - // Any time there's a lookup on this symbol in the outer Let, - // it should result in this expression's type. After all, this - // is the type to which this symbol is assigned! - add_pattern_to_lookup_types(loc_pattern, &mut lookup_types, expr_type.clone()); - - state.reversed_constraints.reverse(); - - Let(Box::new(LetConstraint { - rigid_vars: Vec::new(), - flex_vars: vars, - assignments_constraint: - // This nested constraint represents the actually constrained expr. - Let(Box::new(LetConstraint { - rigid_vars: Vec::new(), - flex_vars: state.vars, - assignment_types: state.assignment_types, - assignments_constraint: And(state.reversed_constraints), - ret_constraint: constrain(bound_vars, subs, loc_expr, NoExpectation(expr_type)) - })), - assignment_types: lookup_types, - ret_constraint, - })) -} - -fn add_pattern_to_lookup_types( - loc_pattern: Located, - lookup_types: &mut ImMap>, - expr_type: Type, -) { - let region = loc_pattern.region; - - match loc_pattern.value { - Pattern::Identifier(symbol) => { - let loc_type = Located { - region, - value: expr_type, - }; - - lookup_types.insert(symbol, loc_type); - } - _ => panic!("TODO constrain patterns other than Identifier"), - } -} - -pub fn constrain_procedure( - bound_vars: &BoundTypeVars, - subs: &mut Subs, - proc: Procedure, - expected: Expected, -) -> Constraint { - let mut state = PatternState { - assignment_types: ImMap::default(), - vars: Vec::with_capacity(proc.args.len()), - reversed_constraints: Vec::with_capacity(1), - }; - - let args = constrain_args(proc.args.into_iter(), subs, &mut state); - let body_type = NoExpectation(args.ret_type); - let ret_constraint = constrain(bound_vars, subs, proc.body, body_type); - - state.reversed_constraints.reverse(); - - let assignments_constraint = And(state.reversed_constraints); - - // panic!("TODO occurs check"); - - And(vec![ - Let(Box::new(LetConstraint { - rigid_vars: Vec::new(), - flex_vars: state.vars, - assignment_types: state.assignment_types, - assignments_constraint, - ret_constraint, - })), - Eq(args.typ, expected, proc.definition), - ]) -} - -struct Args { - pub vars: Vec, - pub typ: Type, - pub ret_type: Type, -} - -fn constrain_args(args: I, subs: &mut Subs, state: &mut PatternState) -> Args -where - I: Iterator>, -{ - let (mut vars, arg_types) = patterns_to_variables(args.into_iter(), subs, state); - - let ret_var = subs.mk_flex_var(); - let ret_type = Type::Variable(ret_var); - - vars.push(ret_var); - - let typ = Type::Function(arg_types, Box::new(ret_type.clone())); - - Args { - vars, - typ, - ret_type, - } -} - -fn patterns_to_variables( - patterns: I, - subs: &mut Subs, - state: &mut PatternState, -) -> (Vec, Vec) -where - I: Iterator>, -{ - let mut vars = Vec::with_capacity(state.vars.capacity()); - let mut pattern_types = Vec::with_capacity(state.vars.capacity()); - - for loc_pattern in patterns { - let pattern_var = subs.mk_flex_var(); - let pattern_type = Type::Variable(pattern_var); - - state.add_pattern(loc_pattern, NoExpectation(pattern_type.clone())); - - vars.push(pattern_var); - pattern_types.push(pattern_type); - } - - (vars, pattern_types) -} - -struct PatternState { - assignment_types: ImMap>, - vars: Vec, - reversed_constraints: Vec, -} - -impl PatternState { - pub fn add_pattern(&mut self, loc_pattern: Located, expected: Expected) { - use can::pattern::Pattern::*; - - let region = loc_pattern.region; - - match loc_pattern.value { - Identifier(symbol) => self.add_to_assignment_types(region, symbol, expected), - Underscore => (), - _ => panic!("TODO other patterns"), - } - } - - fn add_to_assignment_types( - &mut self, - region: Region, - symbol: Symbol, - expected: Expected, - ) { - self.assignment_types.insert( - symbol, - Located { - region, - value: expected.get_type(), - }, - ); - } -} +// struct Args { +// pub vars: Vec, +// pub typ: Type, +// pub ret_type: Type, +// } + +// fn constrain_args(args: I, subs: &mut Subs, state: &mut PatternState) -> Args +// where +// I: Iterator>, +// { +// let (mut vars, arg_types) = patterns_to_variables(args.into_iter(), subs, state); + +// let ret_var = subs.mk_flex_var(); +// let ret_type = Type::Variable(ret_var); + +// vars.push(ret_var); + +// let typ = Type::Function(arg_types, Box::new(ret_type.clone())); + +// Args { +// vars, +// typ, +// ret_type, +// } +// } + +// fn patterns_to_variables( +// patterns: I, +// subs: &mut Subs, +// state: &mut PatternState, +// ) -> (Vec, Vec) +// where +// I: Iterator>, +// { +// let mut vars = Vec::with_capacity(state.vars.capacity()); +// let mut pattern_types = Vec::with_capacity(state.vars.capacity()); + +// for loc_pattern in patterns { +// let pattern_var = subs.mk_flex_var(); +// let pattern_type = Type::Variable(pattern_var); + +// state.add_pattern(loc_pattern, NoExpectation(pattern_type.clone())); + +// vars.push(pattern_var); +// pattern_types.push(pattern_type); +// } + +// (vars, pattern_types) +// } + +// struct PatternState { +// assignment_types: ImMap>, +// vars: Vec, +// reversed_constraints: Vec, +// } + +// impl PatternState { +// pub fn add_pattern(&mut self, loc_pattern: Located, expected: Expected) { +// use can::pattern::Pattern::*; + +// let region = loc_pattern.region; + +// match loc_pattern.value { +// Identifier(symbol) => self.add_to_assignment_types(region, symbol, expected), +// Underscore => (), +// _ => panic!("TODO other patterns"), +// } +// } + +// fn add_to_assignment_types( +// &mut self, +// region: Region, +// symbol: Symbol, +// expected: Expected, +// ) { +// self.assignment_types.insert( +// symbol, +// Located { +// region, +// value: expected.get_type(), +// }, +// ); +// } +// } diff --git a/src/gen/mod.rs b/src/gen/mod.rs new file mode 100644 index 0000000000..fad1eb3676 --- /dev/null +++ b/src/gen/mod.rs @@ -0,0 +1,633 @@ +//! This is an example of the [Kaleidoscope tutorial](https://llvm.org/docs/tutorial/) +//! made in Rust, using Inkwell. +//! Currently, all features up to the [7th chapter](https://llvm.org/docs/tutorial/LangImpl07.html) +//! are available. +//! This example is supposed to be ran as a executable, which launches a REPL. +//! The source code is in the following order: +//! - Lexer, +//! - Parser, +//! - Emitter, +//! - Program. +//! +//! Both the `Parser` and the `Emitter` may fail, in which case they would return +//! an error represented by `Result`, for easier error reporting. + +extern crate inkwell; + +use std::borrow::Borrow; +use std::collections::HashMap; +use std::io::Write; +// use std::iter::Peekable; +// use std::ops::DerefMut; +// use std::str::Chars; + +use inkwell::builder::Builder; +use inkwell::context::Context; +use inkwell::module::Module; +use inkwell::passes::PassManager; +use inkwell::types::BasicTypeEnum; +use inkwell::values::{BasicValueEnum, FloatValue, FunctionValue, PointerValue}; +use inkwell::FloatPredicate; + +// ====================================================================================== +// LEXER ================================================================================ +// ====================================================================================== + +/// Represents a primitive syntax token. +#[derive(Debug, Clone)] +pub enum Token { + Binary, + Comma, + Comment, + Def, + Else, + EOF, + Extern, + For, + Ident(String), + If, + In, + LParen, + Number(f64), + Op(char), + RParen, + Then, + Unary, + Var, +} + +/// Defines the prototype (name and parameters) of a function. +#[derive(Debug)] +pub struct Prototype { + pub name: String, + pub args: Vec, +} + +/// Defines a user-defined or external function. +#[derive(Debug)] +pub struct Function { + pub prototype: Prototype, + pub body: Option, + pub is_anon: bool, +} + +pub struct Emitter<'a> { + pub context: &'a Context, + pub builder: &'a Builder, + pub fpm: &'a PassManager, + pub module: &'a Module, + pub function: &'a Function, + + variables: HashMap, + fn_value_opt: Option, +} + +#[derive(Debug)] +pub enum Expr { + Binary { + op: char, + left: Box, + right: Box, + }, + + Call { + fn_name: String, + args: Vec, + }, + + Conditional { + cond: Box, + consequence: Box, + alternative: Box, + }, + + For { + var_name: String, + start: Box, + end: Box, + step: Option>, + body: Box, + }, + + Number(f64), + + Variable(String), + + VarIn { + variables: Vec<(String, Option)>, + body: Box, + }, +} + +impl<'a> Emitter<'a> { + /// Gets a defined function given its name. + #[inline] + fn get_function(&self, name: &str) -> Option { + self.module.get_function(name) + } + + /// Returns the `FunctionValue` representing the function being compiled. + #[inline] + fn fn_value(&self) -> FunctionValue { + // TODO shouldn't there be like a stack of these? What about functions inside functions? + // Also, do we even need this? + self.fn_value_opt.unwrap() + } + + /// Creates a new stack allocation instruction in the entry block of the function. + fn create_entry_block_alloca(&self, name: &str) -> PointerValue { + let builder = self.context.create_builder(); + + let entry = self.fn_value().get_first_basic_block().unwrap(); + + match entry.get_first_instruction() { + Some(first_instr) => builder.position_before(&first_instr), + None => builder.position_at_end(&entry), + } + + builder.build_alloca(self.context.f64_type(), name) + } + + /// Compiles the specified `Expr` into an LLVM `FloatValue`. + fn compile_expr(&mut self, expr: &Expr) -> Result { + match *expr { + // Expr::IntLiteral(num) => Ok(self.context.i64_type().const_int(num as u64, false)), + Expr::Number(num) => Ok(self.context.f64_type().const_float(num)), + + Expr::Variable(ref name) => match self.variables.get(name.as_str()) { + Some(var) => Ok(self + .builder + .build_load(*var, name.as_str()) + .into_float_value()), + None => Err("Could not find a matching variable."), + }, + + Expr::VarIn { + ref variables, + ref body, + } => { + let mut old_bindings = Vec::new(); + + for &(ref var_name, ref initializer) in variables { + let var_name = var_name.as_str(); + + let initial_val = match *initializer { + Some(ref init) => self.compile_expr(init)?, + None => self.context.f64_type().const_float(0.), + }; + + let alloca = self.create_entry_block_alloca(var_name); + + self.builder.build_store(alloca, initial_val); + + if let Some(old_binding) = self.variables.remove(var_name) { + old_bindings.push(old_binding); + } + + self.variables.insert(var_name.to_string(), alloca); + } + + let body = self.compile_expr(body)?; + + for binding in old_bindings { + self.variables + .insert(binding.get_name().to_str().unwrap().to_string(), binding); + } + + Ok(body) + } + + Expr::Binary { + op, + ref left, + ref right, + } => { + if op == '=' { + // handle assignement + let var_name = match *left.borrow() { + Expr::Variable(ref var_name) => var_name, + _ => { + return Err("Expected variable as left-hand operator of assignement."); + } + }; + + let var_val = self.compile_expr(right)?; + let var = self + .variables + .get(var_name.as_str()) + .ok_or("Undefined variable.")?; + + self.builder.build_store(*var, var_val); + + Ok(var_val) + } else { + let lhs = self.compile_expr(left)?; + let rhs = self.compile_expr(right)?; + + match op { + '+' => Ok(self.builder.build_float_add(lhs, rhs, "tmpadd")), + '-' => Ok(self.builder.build_float_sub(lhs, rhs, "tmpsub")), + '*' => Ok(self.builder.build_float_mul(lhs, rhs, "tmpmul")), + '/' => Ok(self.builder.build_float_div(lhs, rhs, "tmpdiv")), + '<' => Ok({ + let cmp = self.builder.build_float_compare( + FloatPredicate::ULT, + lhs, + rhs, + "tmpcmp", + ); + + self.builder.build_unsigned_int_to_float( + cmp, + self.context.f64_type(), + "tmpbool", + ) + }), + '>' => Ok({ + let cmp = self.builder.build_float_compare( + FloatPredicate::ULT, + rhs, + lhs, + "tmpcmp", + ); + + self.builder.build_unsigned_int_to_float( + cmp, + self.context.f64_type(), + "tmpbool", + ) + }), + + custom => { + let mut name = String::from("binary"); + + name.push(custom); + + match self.get_function(name.as_str()) { + Some(fun) => { + match self + .builder + .build_call(fun, &[lhs.into(), rhs.into()], "tmpbin") + .try_as_basic_value() + .left() + { + Some(value) => Ok(value.into_float_value()), + None => Err("Invalid call produced."), + } + } + + None => Err("Undefined binary operator."), + } + } + } + } + } + + Expr::Call { + ref fn_name, + ref args, + } => match self.get_function(fn_name.as_str()) { + Some(fun) => { + let mut compiled_args = Vec::with_capacity(args.len()); + + for arg in args { + compiled_args.push(self.compile_expr(arg)?); + } + + let argsv: Vec = compiled_args + .iter() + .by_ref() + .map(|&val| val.into()) + .collect(); + + match self + .builder + .build_call(fun, argsv.as_slice(), "tmp") + .try_as_basic_value() + .left() + { + Some(value) => Ok(value.into_float_value()), + None => Err("Invalid call produced."), + } + } + None => Err("Unknown function."), + }, + + Expr::Conditional { + ref cond, + ref consequence, + ref alternative, + } => { + let parent = self.fn_value(); + let zero_const = self.context.f64_type().const_float(0.0); + + // create condition by comparing without 0.0 and returning an int + let cond = self.compile_expr(cond)?; + let cond = self.builder.build_float_compare( + FloatPredicate::ONE, + cond, + zero_const, + "ifcond", + ); + + // build branch + let then_bb = self.context.append_basic_block(&parent, "then"); + let else_bb = self.context.append_basic_block(&parent, "else"); + let cont_bb = self.context.append_basic_block(&parent, "ifcont"); + + self.builder + .build_conditional_branch(cond, &then_bb, &else_bb); + + // build then block + self.builder.position_at_end(&then_bb); + let then_val = self.compile_expr(consequence)?; + self.builder.build_unconditional_branch(&cont_bb); + + let then_bb = self.builder.get_insert_block().unwrap(); + + // build else block + self.builder.position_at_end(&else_bb); + let else_val = self.compile_expr(alternative)?; + self.builder.build_unconditional_branch(&cont_bb); + + let else_bb = self.builder.get_insert_block().unwrap(); + + // emit merge block + self.builder.position_at_end(&cont_bb); + + let phi = self.builder.build_phi(self.context.f64_type(), "iftmp"); + + phi.add_incoming(&[(&then_val, &then_bb), (&else_val, &else_bb)]); + + Ok(phi.as_basic_value().into_float_value()) + } + + Expr::For { + ref var_name, + ref start, + ref end, + ref step, + ref body, + } => { + let parent = self.fn_value(); + + let start_alloca = self.create_entry_block_alloca(var_name); + let start = self.compile_expr(start)?; + + self.builder.build_store(start_alloca, start); + + // go from current block to loop block + let loop_bb = self.context.append_basic_block(&parent, "loop"); + + self.builder.build_unconditional_branch(&loop_bb); + self.builder.position_at_end(&loop_bb); + + let old_val = self.variables.remove(var_name.as_str()); + + self.variables.insert(var_name.to_owned(), start_alloca); + + // emit body + self.compile_expr(body)?; + + // emit step + let step = match *step { + Some(ref step) => self.compile_expr(step)?, + None => self.context.f64_type().const_float(1.0), + }; + + // compile end condition + let end_cond = self.compile_expr(end)?; + + let curr_var = self.builder.build_load(start_alloca, var_name); + let next_var = + self.builder + .build_float_add(curr_var.into_float_value(), step, "nextvar"); + + self.builder.build_store(start_alloca, next_var); + + let end_cond = self.builder.build_float_compare( + FloatPredicate::ONE, + end_cond, + self.context.f64_type().const_float(0.0), + "loopcond", + ); + let after_bb = self.context.append_basic_block(&parent, "afterloop"); + + self.builder + .build_conditional_branch(end_cond, &loop_bb, &after_bb); + self.builder.position_at_end(&after_bb); + + self.variables.remove(var_name); + + if let Some(val) = old_val { + self.variables.insert(var_name.to_owned(), val); + } + + Ok(self.context.f64_type().const_float(0.0)) + } + } + } + + /// Compiles the specified `Prototype` into an extern LLVM `FunctionValue`. + fn compile_prototype(&self, proto: &Prototype) -> Result { + let ret_type = self.context.f64_type(); + let args_types = std::iter::repeat(ret_type) + .take(proto.args.len()) + .map(|f| f.into()) + .collect::>(); + let args_types = args_types.as_slice(); + + let fn_type = self.context.f64_type().fn_type(args_types, false); + let fn_val = self.module.add_function(proto.name.as_str(), fn_type, None); + + // set arguments names + for (i, arg) in fn_val.get_param_iter().enumerate() { + arg.into_float_value().set_name(proto.args[i].as_str()); + } + + // finally return built prototype + Ok(fn_val) + } + + /// Compiles the specified `Function` into an LLVM `FunctionValue`. + fn compile_fn(&mut self) -> Result { + let proto = &self.function.prototype; + let function = self.compile_prototype(proto)?; + + // got external function, returning only compiled prototype + if self.function.body.is_none() { + return Ok(function); + } + + let entry = self.context.append_basic_block(&function, "entry"); + + self.builder.position_at_end(&entry); + + // update fn field + self.fn_value_opt = Some(function); + + // build variables map + self.variables.reserve(proto.args.len()); + + for (i, arg) in function.get_param_iter().enumerate() { + let arg_name = proto.args[i].as_str(); + let alloca = self.create_entry_block_alloca(arg_name); + + self.builder.build_store(alloca, arg); + + self.variables.insert(proto.args[i].clone(), alloca); + } + + // compile body + let body = self.compile_expr(self.function.body.as_ref().unwrap())?; + + self.builder.build_return(Some(&body)); + + // return the whole thing after verification and optimization + if function.verify(true) { + self.fpm.run_on(&function); + + Ok(function) + } else { + unsafe { + function.delete(); + } + + Err("Invalid generated function.") + } + } + + /// Compiles the specified `Function` in the given `Context` and using the specified `Builder`, `PassManager`, and `Module`. + pub fn compile( + context: &'a Context, + builder: &'a Builder, + pass_manager: &'a PassManager, + module: &'a Module, + function: &Function, + ) -> Result { + let mut compiler = Emitter { + context: context, + builder: builder, + fpm: pass_manager, + module: module, + function: function, + fn_value_opt: None, + variables: HashMap::new(), + }; + + compiler.compile_fn() + } +} + +// ====================================================================================== +// PROGRAM ============================================================================== +// ====================================================================================== + +// macro used to print & flush without printing a new line +macro_rules! print_flush { + ( $( $x:expr ),* ) => { + print!( $($x, )* ); + + std::io::stdout().flush().expect("Could not flush to standard output."); + }; +} + +#[no_mangle] +pub extern "C" fn putchard(x: f64) -> f64 { + print_flush!("{}", x as u8 as char); + x +} + +#[no_mangle] +pub extern "C" fn printd(x: f64) -> f64 { + println!("{}", x); + x +} + +// Adding the functions above to a global array, +// so Rust compiler won't remove them. +#[used] +static EXTERNAL_FNS: [extern "C" fn(f64) -> f64; 2] = [putchard, printd]; + +#[test] +fn gen() { + use inkwell::OptimizationLevel; + // use self::inkwell::support::add_symbol; + + let context = Context::create(); + let module = context.create_module("repl"); + let builder = context.create_builder(); + + // Create FPM + let fpm = PassManager::create(&module); + + fpm.add_instruction_combining_pass(); + fpm.add_reassociate_pass(); + fpm.add_gvn_pass(); + fpm.add_cfg_simplification_pass(); + fpm.add_basic_alias_analysis_pass(); + fpm.add_promote_memory_to_register_pass(); + fpm.add_instruction_combining_pass(); + fpm.add_reassociate_pass(); + + fpm.initialize(); + + // make module + let module = context.create_module("tmp"); + + let name = "main".to_string(); + let prototype = Prototype { + name, + args: Vec::new(), + }; + let function = Function { + prototype, + body: Some(Expr::Number(12345.0)), + is_anon: false, + }; + + // TODO remove this dead code + for prev in &Vec::new() { + Emitter::compile(&context, &builder, &fpm, &module, prev) + .expect("Cannot re-add previously compiled function."); + } + + // TODO write a function which takes a context and expr (etc) and panics + // if the given expr is not a function expr, and then takes the contents + // of that function and writes them all to the context. + // + // For now, it needs to support only Float values. + // + // The function doesn't need to take any arguments yet. In fact, it can + // just return 12345. + // + // The whole key here is that we want a main function which looks like: + // + // fn main() -> f64 { return 12345; } + // + // we want fn_val to be equal to that, so we can name it "main" and run it. + + // make main(), a function which returns an f64 + + Emitter::compile(&context, &builder, &fpm, &module, &function).expect("Error compiling main"); + + // let fn_type = context.f64_type().fn_type(&Vec::new(), false); + // let function = module.add_function(proto.name.as_str(), fn_type, None); + + // make execution engine + let ee = module + .create_jit_execution_engine(OptimizationLevel::None) + .unwrap(); + + let maybe_fn = unsafe { ee.get_function:: f64>("main") }; + let compiled_fn = match maybe_fn { + Ok(f) => f, + Err(err) => { + panic!("!> Error during execution: {:?}", err); + } + }; + + unsafe { + panic!("=> {}", compiled_fn.call()); + } +} diff --git a/src/infer.rs b/src/infer.rs index df77541f11..4bd3931e54 100644 --- a/src/infer.rs +++ b/src/infer.rs @@ -10,34 +10,35 @@ use types::Constraint; use types::Expected::*; use types::Type::*; -pub fn infer_expr( - subs: &mut Subs, - loc_expr: Located, - procedures: MutMap, -) -> Content { - let bound_vars = ImMap::default(); - let mut env = ImMap::default(); - let mut constraints = Vec::with_capacity(1 + procedures.len()); +pub fn infer_expr<'a>( + subs: &'a mut Subs<'a>, + loc_expr: Located>, + procedures: MutMap, Procedure<'a>>, +) -> Content<'a> { + panic!("TODO re-constrain procedures."); + // let bound_vars = ImMap::default(); + // let mut env = ImMap::default(); + // let mut constraints = Vec::with_capacity(1 + procedures.len()); // First add constraints for all the procedures - for (symbol, proc) in procedures { - let variable = subs.mk_flex_var(); - let expected = NoExpectation(Variable(variable)); + // for (symbol, proc) in procedures { + // let variable = subs.mk_flex_var(); + // let expected = NoExpectation(Variable(variable)); - constraints.push(constrain_procedure(&bound_vars, subs, proc, expected)); + // constraints.push(constrain_procedure(&bound_vars, subs, proc, expected)); - // Record this procedure in the env; variable lookups may reference it! - env.insert(symbol, variable); - } + // // Record this procedure in the env; variable lookups may reference it! + // env.insert(symbol, variable); + // } // Next, constrain the expression. - let variable = subs.mk_flex_var(); - let expected = NoExpectation(Variable(variable)); - let constraint = constrain(&bound_vars, subs, loc_expr, expected); + // let variable = subs.mk_flex_var(); + // let expected = NoExpectation(Variable(variable)); + // let constraint = constrain(&bound_vars, subs, loc_expr, expected); - constraints.push(constraint); + // constraints.push(constraint); + // + // solve(&env, subs, &Constraint::And(constraints.into_bump_slice())); - solve(&env, subs, Constraint::And(constraints)); - - subs.get(variable).content + // subs.get(variable).content } diff --git a/src/lib.rs b/src/lib.rs index 54787cf593..fbdef85776 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,10 +19,13 @@ pub mod subs; pub mod types; pub mod unify; +pub mod gen; + extern crate bumpalo; extern crate fraction; extern crate fxhash; extern crate im_rc; +extern crate inkwell; extern crate num; #[macro_use] diff --git a/src/operator.rs b/src/operator.rs index f5fa56619b..878bbeb6a4 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -1,4 +1,5 @@ use self::Operator::*; +use can::symbol::Symbol; use std::cmp::Ordering; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -73,6 +74,27 @@ impl Operator { Pizza => 1, } } + + pub fn desugar(&self) -> Symbol<'static> { + match self { + Caret => Symbol::new("Num", "pow"), + Star => Symbol::new("Num", "mul"), + Slash => Symbol::new("Float", "div"), + DoubleSlash => Symbol::new("Int", "div"), + Percent => Symbol::new("Num", "rem"), + DoublePercent => Symbol::new("Num", "mod"), + Plus => Symbol::new("Num", "plus"), + Minus => Symbol::new("Num", "sub"), + Equals => Symbol::new("Bool", "isEqual"), + LessThan => Symbol::new("Num", "isLessThan"), + GreaterThan => Symbol::new("Num", "isGreaterThan"), + LessThanOrEq => Symbol::new("Num", "isLessThanOrEqualTo"), + GreaterThanOrEq => Symbol::new("Num", "isGreaterThanOrEqualTo"), + And => Symbol::new("Bool", "and"), + Or => Symbol::new("Bool", "or"), + Pizza => panic!("Cannot desugar the |> operator"), + } + } } impl PartialOrd for Operator { diff --git a/src/pretty_print_types.rs b/src/pretty_print_types.rs index 0c4f1bf62b..235afc47f9 100644 --- a/src/pretty_print_types.rs +++ b/src/pretty_print_types.rs @@ -5,142 +5,143 @@ static EMPTY_RECORD: &'static str = "{}"; pub fn content_to_string(content: Content, subs: &mut Subs) -> String { let mut buf = String::new(); + panic!("TODO restore content_to_string()"); - write_content(content, subs, &mut buf, false); + // write_content(content, subs, &mut buf, false); buf } -fn write_content(content: Content, subs: &mut Subs, buf: &mut String, use_parens: bool) { - use subs::Content::*; +// fn write_content(content: Content, subs: &mut Subs, buf: &mut String, use_parens: bool) { +// use subs::Content::*; - match content { - FlexVar(Some(name)) => buf.push_str(&name), - FlexVar(None) => buf.push_str(WILDCARD), - RigidVar(name) => buf.push_str(&name), - Structure(flat_type) => write_flat_type(flat_type, subs, buf, use_parens), - Error(_) => buf.push_str(""), - } -} +// match content { +// FlexVar(Some(name)) => buf.push_str(&name), +// FlexVar(None) => buf.push_str(WILDCARD), +// RigidVar(name) => buf.push_str(&name), +// Structure(flat_type) => write_flat_type(flat_type, subs, buf, use_parens), +// Error(_) => buf.push_str(""), +// } +// } -fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_parens: bool) { - use subs::FlatType::*; +// fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_parens: bool) { +// use subs::FlatType::*; - match flat_type { - Apply(module_name, type_name, args) => { - write_apply(module_name, type_name, args, subs, buf, use_parens) - } - EmptyRecord => buf.push_str(EMPTY_RECORD), - Func(args, ret) => write_fn(args, ret, subs, buf, use_parens), - Operator(l_arg, r_arg, ret) => write_fn(vec![l_arg, r_arg], ret, subs, buf, use_parens), - Erroneous(problem) => { - buf.push_str(&format!("", problem)); - } - } -} +// match flat_type { +// Apply(module_name, type_name, args) => { +// write_apply(module_name, type_name, args, subs, buf, use_parens) +// } +// EmptyRecord => buf.push_str(EMPTY_RECORD), +// Func(args, ret) => write_fn(args, ret, subs, buf, use_parens), +// Operator(l_arg, r_arg, ret) => write_fn(vec![l_arg, r_arg], ret, subs, buf, use_parens), +// Erroneous(problem) => { +// buf.push_str(&format!("", problem)); +// } +// } +// } -fn write_apply( - module_name: String, - type_name: String, - args: Vec, - subs: &mut Subs, - buf: &mut String, - use_parens: bool, -) { - let write_parens = use_parens && !args.is_empty(); +// fn write_apply( +// module_name: String, +// type_name: String, +// args: Vec, +// subs: &mut Subs, +// buf: &mut String, +// use_parens: bool, +// ) { +// let write_parens = use_parens && !args.is_empty(); - // Hardcoded type aliases - if module_name == "Str" && type_name == "Str" { - buf.push_str("Str"); - } else if module_name == "Num" && type_name == "Num" { - let arg = args - .into_iter() - .next() - .unwrap_or_else(|| panic!("Num did not have any type parameters somehow.")); - let arg_content = subs.get(arg).content; - let mut arg_param = String::new(); +// // Hardcoded type aliases +// if module_name == "Str" && type_name == "Str" { +// buf.push_str("Str"); +// } else if module_name == "Num" && type_name == "Num" { +// let arg = args +// .into_iter() +// .next() +// .unwrap_or_else(|| panic!("Num did not have any type parameters somehow.")); +// let arg_content = subs.get(arg).content; +// let mut arg_param = String::new(); - write_content(arg_content, subs, &mut arg_param, true); +// write_content(arg_content, subs, &mut arg_param, true); - if arg_param == "Int.Integer" { - buf.push_str("Int"); - } else if arg_param == "Float.FloatingPoint" { - buf.push_str("Float"); - } else { - if write_parens { - buf.push_str("("); - } +// if arg_param == "Int.Integer" { +// buf.push_str("Int"); +// } else if arg_param == "Float.FloatingPoint" { +// buf.push_str("Float"); +// } else { +// if write_parens { +// buf.push_str("("); +// } - buf.push_str("Num "); - buf.push_str(&arg_param); +// buf.push_str("Num "); +// buf.push_str(&arg_param); - if write_parens { - buf.push_str(")"); - } - } - } else if module_name == "List" && type_name == "List" { - if write_parens { - buf.push_str("("); - } +// if write_parens { +// buf.push_str(")"); +// } +// } +// } else if module_name == "List" && type_name == "List" { +// if write_parens { +// buf.push_str("("); +// } - buf.push_str("List "); +// buf.push_str("List "); - let arg = args - .into_iter() - .next() - .unwrap_or_else(|| panic!("List did not have any type parameters somehow.")); - let arg_content = subs.get(arg).content; +// let arg = args +// .into_iter() +// .next() +// .unwrap_or_else(|| panic!("List did not have any type parameters somehow.")); +// let arg_content = subs.get(arg).content; - write_content(arg_content, subs, buf, true); +// write_content(arg_content, subs, buf, true); - if write_parens { - buf.push_str(")"); - } - } else { - if write_parens { - buf.push_str("("); - } +// if write_parens { +// buf.push_str(")"); +// } +// } else { +// if write_parens { +// buf.push_str("("); +// } - buf.push_str(&format!("{}.{}", module_name, type_name)); +// buf.push_str(&format!("{}.{}", module_name, type_name)); - for arg in args { - buf.push_str(" "); - write_content(subs.get(arg).content, subs, buf, true); - } +// for arg in args { +// buf.push_str(" "); +// write_content(subs.get(arg).content, subs, buf, true); +// } - if write_parens { - buf.push_str(")"); - } - } -} +// if write_parens { +// buf.push_str(")"); +// } +// } +// } -fn write_fn( - args: Vec, - ret: Variable, - subs: &mut Subs, - buf: &mut String, - use_parens: bool, -) { - let mut needs_comma = false; +// fn write_fn( +// args: Vec, +// ret: Variable, +// subs: &mut Subs, +// buf: &mut String, +// use_parens: bool, +// ) { +// let mut needs_comma = false; - if use_parens { - buf.push_str("("); - } +// if use_parens { +// buf.push_str("("); +// } - for arg in args { - if needs_comma { - buf.push_str(", "); - } else { - needs_comma = true; - } +// for arg in args { +// if needs_comma { +// buf.push_str(", "); +// } else { +// needs_comma = true; +// } - write_content(subs.get(arg).content, subs, buf, false); - } +// write_content(subs.get(arg).content, subs, buf, false); +// } - buf.push_str(" -> "); - write_content(subs.get(ret).content, subs, buf, false); +// buf.push_str(" -> "); +// write_content(subs.get(ret).content, subs, buf, false); - if use_parens { - buf.push_str(")"); - } -} +// if use_parens { +// buf.push_str(")"); +// } +// } diff --git a/src/solve.rs b/src/solve.rs index ce14e6afe3..c3b3b53371 100644 --- a/src/solve.rs +++ b/src/solve.rs @@ -1,31 +1,19 @@ -// type Env = -// Map.Map Name.Name Variable - -// type Pools = -// MVector.IOVector [Variable] - -// data State = -// State -// { _env :: Env -// , _mark :: Mark -// , _errors :: [Error.Error] -// } - +use bumpalo::collections::Vec; use can::symbol::Symbol; use collections::ImMap; use subs::{Content, Descriptor, FlatType, Subs, Variable}; use types::Constraint::{self, *}; use types::Type::{self, *}; -type Env = ImMap; +type Env<'a> = ImMap, Variable>; -pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) { +pub fn solve<'a>(env: &'a Env<'a>, subs: &'a mut Subs<'a>, constraint: &'a Constraint<'a>) { // println!("\nSolving:\n\n\t{:?}\n\n", constraint); match constraint { True => (), Eq(typ, expected_type, _region) => { // TODO use region? - let actual = type_to_variable(subs, typ); + let actual = type_to_variable(subs, typ.clone()); let expected = type_to_variable(subs, expected_type.get_type()); subs.union(actual, expected); @@ -41,7 +29,7 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) { subs.union(actual, expected); } And(sub_constraints) => { - for sub_constraint in sub_constraints { + for sub_constraint in sub_constraints.iter() { solve(env, subs, sub_constraint); } } @@ -52,11 +40,11 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) { True => { // If the return expression is guaranteed to solve, // solve the assignments themselves and move on. - solve(env, subs, let_con.assignments_constraint) + solve(env, subs, &let_con.assignments_constraint) } ret_con => { // Solve the assignments' constraints first. - solve(env, subs, let_con.assignments_constraint); + solve(env, subs, &let_con.assignments_constraint); // Add a variable for each assignment to the env. let mut new_env = env.clone(); @@ -75,7 +63,7 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) { // Now solve the body, using the new env which includes // the assignments' name-to-variable mappings. - solve(&new_env, subs, ret_con); + solve(&new_env, subs, &ret_con); // TODO do an occurs check for each of the assignments! } @@ -84,16 +72,25 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) { } } -fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable { +fn type_to_variable<'a>(subs: &'a mut Subs<'a>, typ: Type<'a>) -> Variable { match typ { Variable(var) => var, - Apply(module_name, name, arg_types) => { - let args: Vec = arg_types - .into_iter() - .map(|arg| type_to_variable(subs, arg)) - .collect(); + Apply { + module_name, + name, + args, + } => { + let mut arg_vars = Vec::with_capacity_in(args.len(), subs.arena); - let flat_type = FlatType::Apply(module_name, name, args); + for arg in args { + arg_vars.push(type_to_variable(subs, arg.clone())) + } + + let flat_type = FlatType::Apply { + module_name, + name, + args: arg_vars.into_bump_slice(), + }; let content = Content::Structure(flat_type); subs.fresh(Descriptor::from(content)) @@ -103,13 +100,16 @@ fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable { subs.fresh(Descriptor::from(content)) } - Function(arg_types, ret_type) => { - let arg_vars = arg_types - .into_iter() - .map(|arg_type| type_to_variable(subs, arg_type)) - .collect(); + Function(args, ret_type) => { + let mut arg_vars: Vec<'a, Variable> = Vec::with_capacity_in(args.len(), subs.arena); + + for arg in args { + arg_vars.push(type_to_variable(subs, arg.clone())) + } + let ret_var = type_to_variable(subs, *ret_type); - let content = Content::Structure(FlatType::Func(arg_vars, ret_var)); + let content: Content<'a> = + Content::Structure(FlatType::Func(arg_vars.into_bump_slice(), ret_var)); subs.fresh(Descriptor::from(content)) } diff --git a/src/subs.rs b/src/subs.rs index 2f95c866fb..d0214458e7 100644 --- a/src/subs.rs +++ b/src/subs.rs @@ -1,11 +1,13 @@ +use bumpalo::Bump; use ena::unify::{InPlace, UnificationTable, UnifyKey}; use std::fmt; use types::Problem; use unify; #[derive(Debug)] -pub struct Subs { +pub struct Subs<'a> { utable: UnificationTable>, + pub arena: &'a Bump, } #[derive(Copy, PartialEq, Eq, Clone)] @@ -25,7 +27,7 @@ impl fmt::Debug for Variable { } impl UnifyKey for Variable { - type Value = Descriptor; + type Value = Descriptor<'static>; fn index(&self) -> u32 { self.0 @@ -40,19 +42,20 @@ impl UnifyKey for Variable { } } -impl Subs { - pub fn new() -> Self { +impl<'a> Subs<'a> { + pub fn new(arena: &'a Bump) -> Self { Subs { utable: UnificationTable::default(), + arena, } } - pub fn fresh(&mut self, value: Descriptor) -> Variable { + pub fn fresh(&'a mut self, value: Descriptor<'a>) -> Variable { self.utable.new_key(value) } /// Unions two keys without the possibility of failure. - pub fn union(&mut self, left: Variable, right: Variable) { + pub fn union(&'a mut self, left: Variable, right: Variable) { let l_root = self.utable.get_root_key(left.into()); let r_root = self.utable.get_root_key(right.into()); @@ -63,11 +66,11 @@ impl Subs { } } - pub fn get(&mut self, key: Variable) -> Descriptor { + pub fn get(&'a mut self, key: Variable) -> Descriptor<'a> { self.utable.probe_value(key) } - pub fn set(&mut self, key: Variable, r_value: Descriptor) { + pub fn set(&'a mut self, key: Variable, r_value: Descriptor<'a>) { let l_key = self.utable.get_root_key(key.into()); let unified = unify::unify_var_val(self, l_key, &r_value); @@ -101,25 +104,25 @@ impl Subs { } #[inline(always)] -fn flex_var_descriptor() -> Descriptor { +fn flex_var_descriptor() -> Descriptor<'static> { Descriptor::from(unnamed_flex_var()) } #[inline(always)] -fn unnamed_flex_var() -> Content { +fn unnamed_flex_var() -> Content<'static> { Content::FlexVar(None) } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Descriptor { - pub content: Content, +pub struct Descriptor<'a> { + pub content: Content<'a>, pub rank: usize, pub mark: u32, pub copy: Option, } -impl From for Descriptor { - fn from(content: Content) -> Self { +impl<'a> From> for Descriptor<'a> { + fn from(content: Content<'a>) -> Descriptor<'a> { Descriptor { content, rank: 0, @@ -130,21 +133,21 @@ impl From for Descriptor { } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum Content { - FlexVar(Option /* name */), - RigidVar(String /* name */), - Structure(FlatType), +pub enum Content<'a> { + FlexVar(Option<&'a str> /* name */), + RigidVar(&'a str /* name */), + Structure(FlatType<'a>), Error(Problem), } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum FlatType { - Apply( - String, /* module name */ - String, /* type name */ - Vec, - ), - Func(Vec, Variable), +pub enum FlatType<'a> { + Apply { + module_name: &'a str, + name: &'a str, + args: &'a [Variable], + }, + Func(&'a [Variable], Variable), Operator(Variable, Variable, Variable), Erroneous(Problem), EmptyRecord, diff --git a/src/types.rs b/src/types.rs index 5c9b069a14..59bc0ebd16 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,3 +1,4 @@ +use bumpalo::{self, Bump}; use can::symbol::Symbol; use collections::ImMap; use operator::{ArgSide, Operator}; @@ -20,25 +21,29 @@ pub const MOD_DEFAULT: &'static str = "Default"; pub const TYPE_NUM: &'static str = "Num"; #[derive(PartialEq, Eq, Debug, Clone)] -pub enum Type { +pub enum Type<'a> { EmptyRec, /// A function. The types of its arguments, then the type of its return value. - Function(Vec, Box), - Operator(Box), + Function(&'a [Type<'a>], &'a Type<'a>), + Operator(&'a OperatorType<'a>), /// Applying a type to some arguments (e.g. Map.Map String Int) - Apply(ModuleName, String, Vec), + Apply { + module_name: &'a str, + name: &'a str, + args: &'a [Type<'a>], + }, Variable(Variable), /// A type error, which will code gen to a runtime error Erroneous(Problem), } -impl Type { - pub fn for_operator(op: Operator) -> OperatorType { +impl<'a> Type<'a> { + pub fn for_operator(arena: &'a Bump, op: Operator) -> OperatorType<'a> { use self::Operator::*; match op { - Slash => op_type(Type::float(), Type::float(), Type::float()), - DoubleSlash => op_type(Type::int(), Type::int(), Type::int()), + Slash => op_type(Type::float(arena), Type::float(arena), Type::float(arena)), + DoubleSlash => op_type(Type::int(arena), Type::int(arena), Type::int(arena)), // TODO actually, don't put these in types.rs - instead, replace them // with an equivalence to their corresponding stdlib functions - e.g. // Slash generates a new variable and an Eq constraint with Float.div. @@ -46,45 +51,61 @@ impl Type { } } - pub fn num(args: Vec) -> Self { - Type::Apply(MOD_NUM.to_string(), TYPE_NUM.to_string(), args) + pub fn num(args: &'a [Type<'a>]) -> Self { + Type::Apply { + module_name: MOD_NUM, + name: TYPE_NUM, + args, + } } - pub fn float() -> Self { - let floating_point = Type::Apply( - MOD_FLOAT.to_string(), - "FloatingPoint".to_string(), - Vec::new(), - ); + pub fn float(arena: &'a Bump) -> Self { + let floating_point = Type::Apply { + module_name: MOD_FLOAT, + name: "FloatingPoint", + args: &[], + }; - Type::num(vec![floating_point]) + Type::num(bumpalo::vec![in &arena; floating_point].into_bump_slice()) } - pub fn int() -> Self { - let integer = Type::Apply(MOD_INT.to_string(), "Integer".to_string(), Vec::new()); + pub fn int(arena: &'a Bump) -> Self { + let integer = Type::Apply { + module_name: MOD_INT, + name: "Integer", + args: &[], + }; - Type::num(vec![integer]) + Type::num(bumpalo::vec![in &arena; integer].into_bump_slice()) } pub fn string() -> Self { - Type::Apply(MOD_STR.to_string(), "Str".to_string(), Vec::new()) + Type::Apply { + module_name: MOD_STR, + name: "Str", + args: &[], + } } /// This is needed to constrain `if` conditionals pub fn bool() -> Self { - Type::Apply(MOD_DEFAULT.to_string(), "Bool".to_string(), Vec::new()) + Type::Apply { + module_name: MOD_DEFAULT, + name: "Bool", + args: &[], + } } } -fn op_type(left: Type, right: Type, ret: Type) -> OperatorType { +fn op_type<'a>(left: Type<'a>, right: Type<'a>, ret: Type<'a>) -> OperatorType<'a> { OperatorType { left, right, ret } } #[derive(PartialEq, Eq, Debug, Clone)] -pub struct OperatorType { - pub left: Type, - pub right: Type, - pub ret: Type, +pub struct OperatorType<'a> { + pub left: Type<'a>, + pub right: Type<'a>, + pub ret: Type<'a>, } #[derive(Debug, Clone)] @@ -117,21 +138,21 @@ pub enum Reason { } #[derive(Debug, Clone)] -pub enum Constraint { - Eq(Type, Expected, Region), - Lookup(Symbol, Expected, Region), +pub enum Constraint<'a> { + Eq(Type<'a>, Expected>, Region), + Lookup(Symbol<'a>, Expected>, Region), True, // Used for things that always unify, e.g. blanks and runtime errors - Let(Box), - And(Vec), + Let(&'a LetConstraint<'a>), + And(&'a [Constraint<'a>]), } #[derive(Debug, Clone)] -pub struct LetConstraint { - pub rigid_vars: Vec, - pub flex_vars: Vec, - pub assignment_types: ImMap>, - pub assignments_constraint: Constraint, - pub ret_constraint: Constraint, +pub struct LetConstraint<'a> { + pub rigid_vars: &'a [Variable], + pub flex_vars: &'a [Variable], + pub assignment_types: ImMap, Located>>, + pub assignments_constraint: Constraint<'a>, + pub ret_constraint: Constraint<'a>, } #[derive(PartialEq, Eq, Debug, Clone)] diff --git a/src/unify.rs b/src/unify.rs index 266af77dbd..1815dd7224 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -1,22 +1,35 @@ +use bumpalo::collections::Vec; use subs::Content::{self, *}; use subs::{Descriptor, FlatType, Subs, Variable}; use types::Problem; #[inline(always)] -pub fn unify_vars(subs: &mut Subs, left_key: Variable, right_key: Variable) -> Descriptor { +pub fn unify_vars<'a>( + subs: &'a mut Subs<'a>, + left_key: Variable, + right_key: Variable, +) -> Descriptor<'a> { let right = subs.get(right_key); unify_var_val(subs, left_key, &right) } #[inline(always)] -pub fn unify_var_val(subs: &mut Subs, left_key: Variable, right: &Descriptor) -> Descriptor { +pub fn unify_var_val<'a>( + subs: &'a mut Subs<'a>, + left_key: Variable, + right: &'a Descriptor<'a>, +) -> Descriptor<'a> { let left = subs.get(left_key); unify(subs, &left, right) } -pub fn unify(subs: &mut Subs, left: &Descriptor, right: &Descriptor) -> Descriptor { +pub fn unify<'a>( + subs: &'a mut Subs<'a>, + left: &'a Descriptor<'a>, + right: &'a Descriptor<'a>, +) -> Descriptor<'a> { let answer = match left.content { FlexVar(ref opt_name) => unify_flex(opt_name, &right.content), RigidVar(ref name) => unify_rigid(name, &right.content), @@ -27,13 +40,15 @@ pub fn unify(subs: &mut Subs, left: &Descriptor, right: &Descriptor) -> Descript } }; - // println!("\nUnifying:\n\n\t{:?}\n\n\t{:?}\n\n\t-----\n\n\t{:?}\n\n", left.content, right.content, answer.content); - answer } #[inline(always)] -fn unify_structure(subs: &mut Subs, flat_type: &FlatType, other: &Content) -> Descriptor { +fn unify_structure<'a>( + subs: &'a mut Subs<'a>, + flat_type: &'a FlatType, + other: &'a Content<'a>, +) -> Descriptor<'a> { match other { FlexVar(_) => { // If the other is flex, Structure wins! @@ -55,16 +70,33 @@ fn unify_structure(subs: &mut Subs, flat_type: &FlatType, other: &Content) -> De } #[inline(always)] -fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descriptor { +fn unify_flat_type<'a>( + subs: &'a mut Subs<'a>, + left: &'a FlatType<'a>, + right: &'a FlatType<'a>, +) -> Descriptor<'a> { use subs::FlatType::*; match (left, right) { (EmptyRecord, EmptyRecord) => from_content(Structure(left.clone())), - (Apply(l_module_name, l_type_name, l_args), Apply(r_module_name, r_type_name, r_args)) - if l_module_name == r_module_name && l_type_name == r_type_name => - { + ( + Apply { + module_name: l_module_name, + name: l_type_name, + args: l_args, + }, + Apply { + module_name: r_module_name, + name: r_type_name, + args: r_args, + }, + ) if l_module_name == r_module_name && l_type_name == r_type_name => { let args = unify_args(subs, l_args.iter(), r_args.iter()); - let flat_type = Apply(l_module_name.clone(), l_type_name.clone(), args); + let flat_type = Apply { + module_name: l_module_name, + name: l_type_name, + args, + }; from_content(Structure(flat_type)) } @@ -93,26 +125,27 @@ fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descri } } -fn unify_args<'a, I>(subs: &mut Subs, left_iter: I, right_iter: I) -> Vec +fn unify_args<'a, I>(subs: &'a mut Subs<'a>, left_iter: I, right_iter: I) -> &'a [Variable] where I: Iterator, { - left_iter - .zip(right_iter) - .map(|(l_var, r_var)| { - // Look up the descriptors we have for these variables, and unify them. - let descriptor = unify_vars(subs, l_var.clone(), r_var.clone()); + let mut answer = Vec::new_in(subs.arena); - // set r_var to be the unioned value, then union l_var to r_var - subs.set(r_var.clone(), descriptor); - subs.union(l_var.clone(), r_var.clone()); + for (l_var, r_var) in left_iter.zip(right_iter) { + // Look up the descriptors we have for these variables, and unify them. + let descriptor = unify_vars(subs, l_var.clone(), r_var.clone()); - r_var.clone() - }) - .collect() + // set r_var to be the unioned value, then union l_var to r_var + subs.set(r_var.clone(), descriptor); + subs.union(l_var.clone(), r_var.clone()); + + answer.push(r_var.clone()) + } + + answer.into_bump_slice() } -fn union_vars(subs: &mut Subs, l_var: Variable, r_var: Variable) -> Variable { +fn union_vars<'a>(subs: &'a mut Subs<'a>, l_var: Variable, r_var: Variable) -> Variable { // Look up the descriptors we have for these variables, and unify them. let descriptor = unify_vars(subs, l_var.clone(), r_var.clone()); @@ -124,7 +157,7 @@ fn union_vars(subs: &mut Subs, l_var: Variable, r_var: Variable) -> Variable { } #[inline(always)] -fn unify_rigid(name: &String, other: &Content) -> Descriptor { +fn unify_rigid<'a>(name: &'a str, other: &'a Content<'a>) -> Descriptor<'a> { match other { FlexVar(_) => { // If the other is flex, rigid wins! @@ -143,7 +176,7 @@ fn unify_rigid(name: &String, other: &Content) -> Descriptor { } #[inline(always)] -fn unify_flex(opt_name: &Option, other: &Content) -> Descriptor { +fn unify_flex<'a>(opt_name: &'a Option<&'a str>, other: &'a Content<'a>) -> Descriptor<'a> { match other { FlexVar(None) => { // If both are flex, and only left has a name, keep the name around. @@ -159,7 +192,7 @@ fn unify_flex(opt_name: &Option, other: &Content) -> Descriptor { /// TODO this was f/k/a merge() - got rid of the rank stuff...good idea? Bad? /// TODO it used to be { rank: std::cmp::min(left_rank, right_rank), ... } -fn from_content(content: Content) -> Descriptor { +fn from_content<'a>(content: Content<'a>) -> Descriptor<'a> { Descriptor { content, rank: 0, diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 801c26e3ee..6945be6ae7 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -53,7 +53,9 @@ pub fn can_expr_with( }); let home = "Test".to_string(); + let arena = Bump::new(); let (loc_expr, output, problems, procedures) = can::canonicalize_declaration( + arena, home, name, Region::zero(), diff --git a/tests/test_infer.rs b/tests/test_infer.rs index 18dbd88d0a..01fe0363ec 100644 --- a/tests/test_infer.rs +++ b/tests/test_infer.rs @@ -9,6 +9,7 @@ mod helpers; #[cfg(test)] mod test_infer { + use bumpalo::Bump; use helpers::can_expr; use roc::infer::infer_expr; use roc::pretty_print_types::content_to_string; @@ -18,8 +19,9 @@ mod test_infer { // HELPERS fn infer_eq(src: &str, expected: &str) { + let arena = Bump::new(); let (expr, _, _, procedures) = can_expr(src); - let mut subs = Subs::new(); + let mut subs = Subs::new(&arena); let content = infer_expr(&mut subs, Located::new(0, 0, 0, 0, expr), procedures); let actual_str = content_to_string(content, &mut subs);