This commit is contained in:
Richard Feldman 2019-10-15 21:16:27 -04:00
parent 59bdb21ea2
commit 422f8cc37a
23 changed files with 2094 additions and 1119 deletions

8
BUILDING_FROM_SOURCE.md Normal file
View File

@ -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)

167
Cargo.lock generated
View File

@ -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)" = "<none>"
"checksum inkwell_internal_macros 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)" = "<none>"
"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"

View File

@ -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"

View File

@ -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<Problem>,
pub problems: Vec<'a, Problem<'a>>,
/// Variants either declared in this module, or imported.
pub variants: ImMap<Symbol, Located<Box<str>>>,
pub variants: ImMap<Symbol<'a>, Located<&'a str>>,
/// Former closures converted to top-level procedures.
pub procedures: MutMap<Symbol, Procedure>,
pub procedures: MutMap<Symbol<'a>, Procedure<'a>>,
pub arena: &'a Bump,
pub subs: Subs<'a>,
}
impl Env {
pub fn new(home: String, declared_variants: ImMap<Symbol, Located<Box<str>>>) -> Env {
impl<'a> Env<'a> {
pub fn new(
arena: &'a Bump,
home: &'a str,
declared_variants: ImMap<Symbol<'a>, 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<Located<Pattern>>,
body: Located<Expr>,
symbol: Symbol<'a>,
args: &'a [Located<Pattern<'a>>],
body: Located<Expr<'a>>,
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.

View File

@ -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<str>),
Char(char), // OBSOLETE
List(Vec<Located<Expr>>),
EmptyList,
Int(Variable, i64),
Float(Variable, f64),
Str(Variable, &'a str),
List(Variable, &'a [Located<Expr<'a>>]),
// 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<str>, Located<Expr>)>, Box<str>),
FunctionPointer(Variable, Symbol<'a>),
// Pattern Matching
Case(Box<Located<Expr>>, Vec<(Located<Pattern>, Located<Expr>)>),
Assign(Vec<(Located<Pattern>, Located<Expr>)>, Box<Located<Expr>>),
Case(
Variable,
&'a Located<Expr<'a>>,
&'a [(Located<Pattern<'a>>, Located<Expr<'a>>)],
),
Define(
Variable,
&'a [(Located<Pattern<'a>>, Located<Expr<'a>>)],
&'a Located<Expr<'a>>,
),
// Application
Call(Box<Located<Expr>>, Vec<Located<Expr>>),
ApplyVariant(Symbol, Option<Vec<Located<Expr>>>),
Call(Variable, &'a Located<Expr<'a>>, &'a [Located<Expr<'a>>]),
// This has to be separate from Call so we can do precedence reordering
Operator(
Variable,
&'a (Located<Expr<'a>>, Located<Operator>, Located<Expr<'a>>),
),
// Product Types
EmptyRecord,
// Sugar
If(Box<Located<Expr>>, Box<Located<Expr>>, Box<Located<Expr>>),
Operator(Box<Located<Expr>>, Located<Operator>, Box<Located<Expr>>),
Record(Variable, &'a [Located<(&'a str, Located<Expr<'a>>)>]),
// Compiles, but will crash if reached
RuntimeError(RuntimeError),
RuntimeError(Variable, RuntimeError<'a>),
}

View File

@ -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<Ident, (Symbol, Region)>,
declared_variants: &ImMap<Symbol, Located<Box<str>>>,
declared_idents: &ImMap<Ident, (Symbol<'a>, Region)>,
declared_variants: &ImMap<Symbol<'a>, Located<&'a str>>,
) -> (
Located<Expr>,
Output,
Vec<Problem>,
MutMap<Symbol, Procedure>,
Located<Expr<'a>>,
Output<'a>,
Vec<'a, Problem<'a>>,
MutMap<Symbol<'a>, 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<Symbol>,
pub struct Output<'a> {
pub references: References<'a>,
pub tail_call: Option<Symbol<'a>>,
}
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<Expr>, Output) {
) -> (Located<Expr<'a>>, 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<Symbol> {
let successors = |symbol: &Symbol<'a>| -> ImSet<Symbol<'a>> {
let (_, references) = refs_by_assignment.get(symbol).unwrap();
local_successors(&references, &env.procedures)
};
let assigned_symbols: Vec<Symbol> = can_assignments_by_symbol
.keys()
.into_iter()
.map(Symbol::clone)
.collect();
let mut assigned_symbols: Vec<Symbol<'a>> = 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<Located<Ident>> =
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<Ident>> = 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<Located<Pattern>> = 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<Located<Pattern>> =
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<Symbol, Procedure>,
) -> ImSet<Symbol> {
fn local_successors<'a>(
references: &'a References<'a>,
procedures: &'a MutMap<Symbol<'a>, Procedure<'a>>,
) -> ImSet<Symbol<'a>> {
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<Symbol, Procedure>) -> ImSet<Symbol> {
fn call_successors<'a>(
call_symbol: &'a Symbol<'a>,
procedures: &'a MutMap<Symbol<'a>, Procedure<'a>>,
) -> ImSet<Symbol<'a>> {
// 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<Symbol, Procedure>)
}
}
fn references_from_local<T>(
assigned_symbol: Symbol,
visited: &mut MutSet<Symbol>,
refs_by_assignment: &MutMap<Symbol, (T, References)>,
procedures: &MutMap<Symbol, Procedure>,
) -> References {
fn references_from_local<'a, T>(
assigned_symbol: Symbol<'a>,
visited: &'a mut MutSet<Symbol<'a>>,
refs_by_assignment: &'a MutMap<Symbol<'a>, (T, References<'a>)>,
procedures: &'a MutMap<Symbol<'a>, 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<T>(
///
/// Example: the cycle (c ---> a ---> b) becomes (a ---> b ---> c)
pub fn sort_cyclic_idents<'a, I>(
arena: &'a Bump,
loc_idents: Vec<Located<Ident>>,
ordered_idents: &mut I,
) -> Vec<Located<Ident>>
) -> Vec<'a, Located<Ident>>
where
I: Iterator<Item = &'a Ident>,
{
@ -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<T>(
call_symbol: Symbol,
visited: &mut MutSet<Symbol>,
refs_by_assignment: &MutMap<Symbol, (T, References)>,
procedures: &MutMap<Symbol, Procedure>,
) -> References {
fn references_from_call<'a, T>(
call_symbol: Symbol<'a>,
visited: &'a mut MutSet<Symbol<'a>>,
refs_by_assignment: &'a MutMap<Symbol, (T, References<'a>)>,
procedures: &'a MutMap<Symbol, Procedure<'a>>,
) -> References<'a> {
match procedures.get(&call_symbol) {
Some(procedure) => {
let mut answer = procedure.references.clone();
@ -961,11 +1003,15 @@ fn references_from_call<T>(
}
}
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<Item = &'a Located<ast::Pattern<'a>>>,
{
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<Ident, (Symbol, Regi
/// If it could not be found, return it unchanged as an Err.
#[inline(always)] // This is shared code between Var and InterpolatedStr; it was inlined when handwritten
fn resolve_ident(
env: &Env,
scope: &Scope,
fn resolve_ident<'a>(
env: &'a Env<'a>,
scope: &'a Scope<'a>,
ident: Ident,
references: &mut References,
) -> Result<Symbol, Ident> {
references: &'a mut References<'a>,
) -> Result<Symbol<'a>, 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<Located<Expr>>,
fn new_op_expr<'a>(
env: &'a mut Env<'a>,
left: Located<Expr<'a>>,
op: Located<Operator>,
right: Box<Located<Expr>>,
) -> Located<Expr> {
right: Located<Expr<'a>>,
) -> Located<Expr<'a>> {
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<Expr>) -> Located<Expr> {
fn apply_precedence_and_associativity<'a>(
env: &'a mut Env<'a>,
expr: Located<Expr<'a>>,
) -> Located<Expr<'a>> {
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<Expr>) -> 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<Box<Located<Expr>>> = Vec::new();
let mut op_stack: Vec<Located<Operator>> = Vec::new();
let arena = env.arena;
let mut infixes = Infixes::new(&expr);
let mut arg_stack: Vec<&'a Located<Expr>> = Vec::new_in(arena);
let mut op_stack: Vec<Located<Operator>> = 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<Expr>) -> 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<Expr>) -> 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<Expr>) -> 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<Expr>) -> 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<Expr>) -> Loc
}
#[derive(Debug, Clone, PartialEq)]
enum InfixToken {
Arg(Box<Located<Expr>>),
enum InfixToken<'a> {
Arg(&'a Located<Expr<'a>>),
Op(Located<Operator>),
}
@ -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<Box<Located<Expr>>>,
remaining_expr: Option<&'a Located<Expr<'a>>>,
/// Cached operator from a previous iteration
next_op: Option<Located<Operator>>,
}
impl Infixes {
fn new(expr: Located<Expr>) -> Infixes {
impl<'a> Infixes<'a> {
fn new(expr: &'a Located<Expr<'a>>) -> 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<InfixToken> {
fn next(&mut self) -> Option<InfixToken<'a>> {
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<Problem>) -> Expr {
fn float_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> {
// Ignore underscores.
match raw.replace("_", "").parse::<f64>() {
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<Problem>) -> Expr {
fn int_from_parsed<'a>(raw: &'a str, env: &'a mut Env<'a>) -> Expr<'a> {
// Ignore underscores.
match raw.replace("_", "").parse::<i64>() {
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<Problem>) -> 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<Problem>) -> 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<Problem>) -> 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)
}
}
}

View File

@ -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<Located<Pattern>>),
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<Located<Pattern<'a>>>),
IntLiteral(Variable, i64),
FloatLiteral(Variable, f64),
ExactString(Variable, &'a str),
EmptyRecordLiteral(Variable),
Underscore(Variable),
// Runtime Exceptions
Shadowed(Located<Ident>),
@ -38,13 +39,13 @@ pub enum PatternType {
CaseBranch,
}
pub fn canonicalize_pattern(
env: &mut Env,
scope: &mut Scope,
pattern_type: &PatternType,
loc_pattern: &Located<ast::Pattern>,
shadowable_idents: &mut ImMap<Ident, (Symbol, Region)>,
) -> Located<Pattern> {
pub fn canonicalize_pattern<'a>(
env: &'a mut Env<'a>,
scope: &'a mut Scope<'a>,
pattern_type: &'a PatternType,
loc_pattern: &'a Located<ast::Pattern<'a>>,
shadowable_idents: &'a mut ImMap<Ident, (Symbol<'a>, Region)>,
) -> Located<Pattern<'a>> {
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)

View File

@ -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<Ident>),
UnrecognizedFunctionName(Located<Ident>),
UnrecognizedConstant(Located<Ident>),
@ -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<Located<Ident>>),
RuntimeError(RuntimeError),
CircularAssignment(Vec<'a, Located<Ident>>),
RuntimeError(RuntimeError<'a>),
}
#[derive(Clone, Debug, PartialEq)]
@ -26,20 +27,20 @@ pub enum PrecedenceProblem {
}
#[derive(Clone, Debug, PartialEq)]
pub enum RuntimeError {
InvalidPrecedence(PrecedenceProblem, Box<Located<Expr>>),
pub enum RuntimeError<'a> {
InvalidPrecedence(PrecedenceProblem, &'a Located<Expr<'a>>),
UnrecognizedFunctionName(Located<Ident>),
UnrecognizedConstant(Located<Ident>),
UnrecognizedVariant(Located<VariantName>),
FloatOutsideRange(Box<str>),
IntOutsideRange(Box<str>),
InvalidHex(std::num::ParseIntError, Box<str>),
InvalidOctal(std::num::ParseIntError, Box<str>),
InvalidBinary(std::num::ParseIntError, Box<str>),
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<Located<Ident>>,
Vec<(Located<Pattern>, Located<Expr>)>,
Box<Located<Expr>>,
Vec<'a, Located<Ident>>,
Vec<'a, (Located<Pattern<'a>>, Located<Expr<'a>>)>,
&'a Located<Expr<'a>>,
),
/// When the author specifies a type annotation but no implementation

View File

@ -5,22 +5,22 @@ use collections::ImSet;
use region::{Located, Region};
#[derive(Clone, Debug, PartialEq)]
pub struct Procedure {
pub name: Option<String>,
pub struct Procedure<'a> {
pub name: Option<&'a str>,
pub is_self_tail_recursive: bool,
pub definition: Region,
pub args: Vec<Located<Pattern>>,
pub body: Located<Expr>,
pub references: References,
pub args: &'a [Located<Pattern<'a>>],
pub body: Located<Expr<'a>>,
pub references: References<'a>,
}
impl Procedure {
impl<'a> Procedure<'a> {
pub fn new(
definition: Region,
args: Vec<Located<Pattern>>,
body: Located<Expr>,
references: References,
) -> Procedure {
args: &'a [Located<Pattern<'a>>],
body: Located<Expr<'a>>,
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<Symbol>,
pub globals: ImSet<Symbol>,
pub variants: ImSet<Symbol>,
pub calls: ImSet<Symbol>,
pub struct References<'a> {
pub locals: ImSet<Symbol<'a>>,
pub globals: ImSet<Symbol<'a>>,
pub variants: ImSet<Symbol<'a>>,
pub calls: ImSet<Symbol<'a>>,
}
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)
}
}

View File

@ -4,14 +4,17 @@ use ident::Ident;
use region::Region;
#[derive(Clone, Debug, PartialEq)]
pub struct Scope {
pub idents: ImMap<Ident, (Symbol, Region)>,
symbol_prefix: String,
pub struct Scope<'a> {
pub idents: ImMap<Ident, (Symbol<'a>, Region)>,
symbol_prefix: &'a str,
next_unique_id: u64,
}
impl Scope {
pub fn new(symbol_prefix: String, declared_idents: ImMap<Ident, (Symbol, Region)>) -> Scope {
impl<'a> Scope<'a> {
pub fn new(
symbol_prefix: &'a str,
declared_idents: ImMap<Ident, (Symbol<'a>, 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())

View File

@ -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<String> for Symbol {
fn into(self) -> String {
impl<'a> Into<&'a str> for Symbol<'a> {
fn into(self) -> &'a str {
let Symbol(string) = self;
string

File diff suppressed because it is too large Load Diff

633
src/gen/mod.rs Normal file
View File

@ -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<T, &'static str>`, 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<String>,
}
/// Defines a user-defined or external function.
#[derive(Debug)]
pub struct Function {
pub prototype: Prototype,
pub body: Option<Expr>,
pub is_anon: bool,
}
pub struct Emitter<'a> {
pub context: &'a Context,
pub builder: &'a Builder,
pub fpm: &'a PassManager<FunctionValue>,
pub module: &'a Module,
pub function: &'a Function,
variables: HashMap<String, PointerValue>,
fn_value_opt: Option<FunctionValue>,
}
#[derive(Debug)]
pub enum Expr {
Binary {
op: char,
left: Box<Expr>,
right: Box<Expr>,
},
Call {
fn_name: String,
args: Vec<Expr>,
},
Conditional {
cond: Box<Expr>,
consequence: Box<Expr>,
alternative: Box<Expr>,
},
For {
var_name: String,
start: Box<Expr>,
end: Box<Expr>,
step: Option<Box<Expr>>,
body: Box<Expr>,
},
Number(f64),
Variable(String),
VarIn {
variables: Vec<(String, Option<Expr>)>,
body: Box<Expr>,
},
}
impl<'a> Emitter<'a> {
/// Gets a defined function given its name.
#[inline]
fn get_function(&self, name: &str) -> Option<FunctionValue> {
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<FloatValue, &'static str> {
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<BasicValueEnum> = 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<FunctionValue, &'static str> {
let ret_type = self.context.f64_type();
let args_types = std::iter::repeat(ret_type)
.take(proto.args.len())
.map(|f| f.into())
.collect::<Vec<BasicTypeEnum>>();
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<FunctionValue, &'static str> {
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<FunctionValue>,
module: &'a Module,
function: &Function,
) -> Result<FunctionValue, &'static str> {
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::<unsafe extern "C" fn() -> f64>("main") };
let compiled_fn = match maybe_fn {
Ok(f) => f,
Err(err) => {
panic!("!> Error during execution: {:?}", err);
}
};
unsafe {
panic!("=> {}", compiled_fn.call());
}
}

View File

@ -10,34 +10,35 @@ use types::Constraint;
use types::Expected::*;
use types::Type::*;
pub fn infer_expr(
subs: &mut Subs,
loc_expr: Located<Expr>,
procedures: MutMap<Symbol, Procedure>,
) -> 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<Expr<'a>>,
procedures: MutMap<Symbol<'a>, 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
}

View File

@ -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]

View File

@ -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 {

View File

@ -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("<type mismatch>"),
}
}
// 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("<type mismatch>"),
// }
// }
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!("<Type Mismatch: {:?}>", 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!("<Type Mismatch: {:?}>", problem));
// }
// }
// }
fn write_apply(
module_name: String,
type_name: String,
args: Vec<Variable>,
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<Variable>,
// 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<Variable>,
ret: Variable,
subs: &mut Subs,
buf: &mut String,
use_parens: bool,
) {
let mut needs_comma = false;
// fn write_fn(
// args: Vec<Variable>,
// 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(")");
// }
// }

View File

@ -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<Symbol, Variable>;
type Env<'a> = ImMap<Symbol<'a>, 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<Variable> = 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))
}

View File

@ -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<InPlace<Variable>>,
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<Variable>,
}
impl From<Content> for Descriptor {
fn from(content: Content) -> Self {
impl<'a> From<Content<'a>> for Descriptor<'a> {
fn from(content: Content<'a>) -> Descriptor<'a> {
Descriptor {
content,
rank: 0,
@ -130,21 +133,21 @@ impl From<Content> for Descriptor {
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Content {
FlexVar(Option<String> /* 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<Variable>,
),
Func(Vec<Variable>, 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,

View File

@ -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<Type>, Box<Type>),
Operator(Box<OperatorType>),
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<Type>),
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<Type>) -> 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<Type>, Region),
Lookup(Symbol, Expected<Type>, Region),
pub enum Constraint<'a> {
Eq(Type<'a>, Expected<Type<'a>>, Region),
Lookup(Symbol<'a>, Expected<Type<'a>>, Region),
True, // Used for things that always unify, e.g. blanks and runtime errors
Let(Box<LetConstraint>),
And(Vec<Constraint>),
Let(&'a LetConstraint<'a>),
And(&'a [Constraint<'a>]),
}
#[derive(Debug, Clone)]
pub struct LetConstraint {
pub rigid_vars: Vec<Variable>,
pub flex_vars: Vec<Variable>,
pub assignment_types: ImMap<Symbol, Located<Type>>,
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<Symbol<'a>, Located<Type<'a>>>,
pub assignments_constraint: Constraint<'a>,
pub ret_constraint: Constraint<'a>,
}
#[derive(PartialEq, Eq, Debug, Clone)]

View File

@ -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<Variable>
fn unify_args<'a, I>(subs: &'a mut Subs<'a>, left_iter: I, right_iter: I) -> &'a [Variable]
where
I: Iterator<Item = &'a Variable>,
{
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<String>, 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<String>, 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,

View File

@ -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(),

View File

@ -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);