Merge remote-tracking branch 'origin/trunk' into small-str

This commit is contained in:
Richard Feldman 2020-09-16 02:23:06 -04:00
commit 5d3645350d
33 changed files with 2597 additions and 902 deletions

2
.envrc
View file

@ -151,4 +151,4 @@ validate_version() {
return 1
}
use_nix -s shell.nix -w ./nix/nixpkgs-version.json
use_nix -s shell.nix

View file

@ -42,12 +42,13 @@ Installing LLVM's prebuilt binaries doesn't seem to be enough for the `llvm-sys`
on Windows. After lots of help from [**@IanMacKenzie**](https://github.com/IanMacKenzie) (thank you, Ian!), here's what worked for me:
1. I downloaded and installed [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16) (a full Visual Studio install should work tool; the Build Tools are just the CLI tools, which is all I wanted)
2. In the installation configuration, under "additional components" I had to check both "C++ ATL for latest v142 build tools (x86 & x64)" and also "C++/CLI support for v142 build tools"
3. I launched the "x64 Native Tools Command Prompt for Visual Studio 2019" application (note: not the similarly-named "x86" one!)
4. I followed most of the steps under LLVM's [building from source instructions](https://github.com/llvm/llvm-project#getting-the-source-code-and-building-llvm) up to the `cmake -G ...` command, which didn't work for me. Instead, at that point I did the following step.
5. I ran `cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ../llvm` to generate a NMake makefile.
6. Once that completed, I ran `nmake` to build LLVM. (This took about 2 hours on my laptop.)
7. Finally, I set an environment variable `LLVM_SYS_100_PREFIX` to point to the `build` directory where I ran the `cmake` command.
1. In the installation configuration, under "additional components" I had to check both "C++ ATL for latest v142 build tools (x86 & x64)" and also "C++/CLI support for v142 build tools"
1. I launched the "x64 Native Tools Command Prompt for Visual Studio 2019" application (note: not the similarly-named "x86" one!)
1. Make sure [Python 2.7](https://www.python.org/) and [CMake 3.17](http://cmake.org/) are installed on your system.
1. I followed most of the steps under LLVM's [building from source instructions](https://github.com/llvm/llvm-project#getting-the-source-code-and-building-llvm) up to the `cmake -G ...` command, which didn't work for me. Instead, at that point I did the following step.
1. I ran `cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ../llvm` to generate a NMake makefile.
1. Once that completed, I ran `nmake` to build LLVM. (This took about 2 hours on my laptop.)
1. Finally, I set an environment variable `LLVM_SYS_100_PREFIX` to point to the `build` directory where I ran the `cmake` command.
Once all that was done, `cargo` ran successfully for Roc!

454
Cargo.lock generated
View file

@ -2,9 +2,9 @@
# It is not intended for manual editing.
[[package]]
name = "ab_glyph"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "600c69210313b93607f32ac23a62bc9c20522f8d6bba88aef6db27657162fe78"
checksum = "5b50c188ff14b5a6efeb38eee8ccbc505cdf61e347a3d5eb04dc55d74ae4f20e"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser",
@ -12,9 +12,9 @@ dependencies = [
[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b7e4e8cf778db814365e46839949ca74df4efb10e87ba4913e6ec5967ef0285"
checksum = "2692800d602527d2b8fea50036119c37df74ab565b10e285706a3dcec0ec3e16"
[[package]]
name = "aho-corasick"
@ -88,7 +88,7 @@ version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c69a8137596e84c22d57f3da1b5de1d4230b1742a710091c85f4d7ce50f00f38"
dependencies = [
"libloading 0.6.2",
"libloading",
]
[[package]]
@ -116,9 +116,9 @@ checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
[[package]]
name = "autocfg"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
@ -141,6 +141,27 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
name = "bstr"
version = "0.2.13"
@ -159,6 +180,12 @@ version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.3.4"
@ -193,9 +220,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.58"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381"
[[package]]
name = "cfg-if"
@ -205,9 +232,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "clap"
version = "2.33.1"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"bitflags",
"textwrap",
@ -238,9 +265,9 @@ source = "git+https://github.com/rtfeldman/clap#e1d83a78804a271b053d4d21f69b67f7
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
]
[[package]]
@ -365,7 +392,7 @@ checksum = "70daa7ceec6cf143990669a04c7df13391d55fb27bd4079d252fca774ba244d8"
dependencies = [
"atty",
"cast",
"clap 2.33.1",
"clap 2.33.3",
"criterion-plot",
"csv",
"itertools",
@ -409,12 +436,12 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
dependencies = [
"cfg-if",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
@ -434,7 +461,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"cfg-if",
"crossbeam-utils",
"lazy_static",
@ -460,7 +487,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"cfg-if",
"lazy_static",
]
@ -489,12 +516,12 @@ dependencies = [
[[package]]
name = "d3d12"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1324bc4eae34f03b0ced586da5ae2b1ab46acfdae68b5b26d2e23dadae376a2"
checksum = "d0a60cceb22c7c53035f8980524fdc7f17cf49681a3c154e6757d30afbec6ec4"
dependencies = [
"bitflags",
"libloading 0.6.2",
"libloading",
"winapi 0.3.9",
]
@ -504,9 +531,9 @@ version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
]
[[package]]
@ -515,6 +542,15 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array",
]
[[package]]
name = "dispatch"
version = "0.2.0"
@ -533,7 +569,19 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76"
dependencies = [
"libloading 0.6.2",
"libloading",
]
[[package]]
name = "docs"
version = "0.1.0"
dependencies = [
"fs_extra",
"handlebars",
"pulldown-cmark",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
@ -544,9 +592,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "either"
version = "1.5.3"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
[[package]]
name = "encode_unicode"
@ -577,6 +625,12 @@ dependencies = [
"termcolor",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fnv"
version = "1.0.7"
@ -598,6 +652,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "fs_extra"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
@ -675,9 +735,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
dependencies = [
"proc-macro-hack",
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
]
[[package]]
@ -725,10 +785,19 @@ dependencies = [
]
[[package]]
name = "getrandom"
version = "0.1.14"
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
dependencies = [
"typenum",
]
[[package]]
name = "getrandom"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if",
"libc",
@ -748,14 +817,14 @@ dependencies = [
[[package]]
name = "gfx-backend-dx11"
version = "0.5.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92de0ddc0fde1a89b2a0e92dcc6bbb554bd34af0135e53a28d5ef064611094a4"
checksum = "32d95d5fddfa596c0628be117a16979b273f676b4e5a037a417365f274349123"
dependencies = [
"bitflags",
"gfx-auxil",
"gfx-hal",
"libloading 0.5.2",
"libloading",
"log",
"parking_lot",
"range-alloc",
@ -768,9 +837,9 @@ dependencies = [
[[package]]
name = "gfx-backend-dx12"
version = "0.5.8"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05218b5c94539f22ac7d6feb4b2482431b89f6cc897132494701ac48619218d7"
checksum = "5959ce9dd48e7e04c71128024c3e76e86aa6fb6d83e48aad6819a1f7afae52e4"
dependencies = [
"bitflags",
"d3d12",
@ -921,14 +990,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
[[package]]
name = "hashbrown"
version = "0.8.1"
name = "handlebars"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb"
checksum = "5deefd4816fb852b1ff3cb48f6c41da67be2d0e1d20b26a7a3b076da11f064b1"
dependencies = [
"autocfg 1.0.0",
"log",
"pest",
"pest_derive",
"quick-error 2.0.0",
"serde",
"serde_json",
]
[[package]]
name = "hashbrown"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
[[package]]
name = "heck"
version = "0.3.1"
@ -962,7 +1042,7 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
"quick-error 1.2.3",
]
[[package]]
@ -995,11 +1075,11 @@ dependencies = [
[[package]]
name = "indexmap"
version = "1.5.0"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7"
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"hashbrown",
]
@ -1020,9 +1100,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0"
dependencies = [
"proc-macro-hack",
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
"unindent",
]
@ -1094,9 +1174,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "js-sys"
version = "0.3.42"
version = "0.3.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52732a3d3ad72c58ad2dc70624f9c17b46ecd0943b9a4f1ee37c4c18c5d983e2"
checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8"
dependencies = [
"wasm-bindgen",
]
@ -1119,32 +1199,23 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.2.1"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.73"
version = "0.2.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
[[package]]
name = "libloading"
version = "0.5.2"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
dependencies = [
"cc",
"winapi 0.3.9",
]
[[package]]
name = "libloading"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cadb8e769f070c45df05c78c7520eb4cd17061d4ab262e43cfc68b4d00ac71c"
checksum = "2443d8f0478b16759158b2f66d525991a05491138bc05814ef52a250148ef4f9"
dependencies = [
"cfg-if",
"winapi 0.3.9",
]
@ -1165,9 +1236,9 @@ checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
[[package]]
name = "llvm-sys"
version = "100.1.0"
version = "100.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5545bf9a09267c644e4d0ac68f37ac200af6579ae2e82aebce382654eb4abab1"
checksum = "9109e19fbfac3458f2970189719fa19f1007c6fd4e08c44fdebf4be0ddbe261d"
dependencies = [
"cc",
"lazy_static",
@ -1237,7 +1308,7 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
]
[[package]]
@ -1363,9 +1434,9 @@ checksum = "2b2820aca934aba5ed91c79acc72b6a44048ceacc5d36c035ed4e051f12d887d"
[[package]]
name = "net2"
version = "0.2.34"
version = "0.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853"
dependencies = [
"cfg-if",
"libc",
@ -1391,7 +1462,7 @@ version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
]
[[package]]
@ -1421,9 +1492,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d"
dependencies = [
"proc-macro-crate",
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
]
[[package]]
@ -1447,9 +1518,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
[[package]]
name = "oorandom"
@ -1457,6 +1528,12 @@ version = "11.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "ordered-float"
version = "1.1.0"
@ -1468,9 +1545,9 @@ dependencies = [
[[package]]
name = "owned_ttf_parser"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92cfbcc7f0436a5b1d93087200c04372acc8073ec59f973aa30a98ee8262fcce"
checksum = "fb477c7fd2a3a6e04e1dc6ca2e4e9b04f2df702021dc5a5d1cf078c587dc59f7"
dependencies = [
"ttf-parser",
]
@ -1514,9 +1591,9 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb44a25c5bba983be0fc8592dfaf3e6d0935ce8be0c6b15b2a39507af34a926"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
"synstructure",
"unicode-xid 0.2.1",
]
@ -1528,23 +1605,66 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pin-project"
version = "0.4.22"
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
dependencies = [
"pest",
"pest_meta",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.40",
]
[[package]]
name = "pest_meta"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
dependencies = [
"maplit",
"pest",
"sha-1",
]
[[package]]
name = "pin-project"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "0.4.22"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7"
checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
]
[[package]]
@ -1579,9 +1699,9 @@ dependencies = [
[[package]]
name = "ppv-lite86"
version = "0.2.8"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
[[package]]
name = "pretty_assertions"
@ -1609,9 +1729,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7"
dependencies = [
"proc-macro-error-attr",
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
"version_check",
]
@ -1621,18 +1741,18 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
"syn-mid",
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.16"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
[[package]]
name = "proc-macro-nested"
@ -1651,19 +1771,36 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.19"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
dependencies = [
"unicode-xid 0.2.1",
]
[[package]]
name = "pulldown-cmark"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
dependencies = [
"bitflags",
"memchr",
"unicase",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-error"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda"
[[package]]
name = "quickcheck"
version = "0.8.5"
@ -1702,7 +1839,7 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
]
[[package]]
@ -1878,11 +2015,11 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.3.1"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080"
checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"crossbeam-deque",
"either",
"rayon-core",
@ -1890,12 +2027,12 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.7.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280"
checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-queue",
"crossbeam-utils",
"lazy_static",
"num_cpus",
@ -2473,9 +2610,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.114"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
[[package]]
name = "serde_cbor"
@ -2489,20 +2626,20 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.114"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
]
[[package]]
name = "serde_json"
version = "1.0.56"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3"
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
dependencies = [
"itoa",
"ryu",
@ -2510,10 +2647,22 @@ dependencies = [
]
[[package]]
name = "signal-hook-registry"
version = "1.2.0"
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
"block-buffer",
"digest",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "signal-hook-registry"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
dependencies = [
"arc-swap",
"libc",
@ -2537,9 +2686,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
version = "1.4.1"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f"
checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
[[package]]
name = "smithay-client-toolkit"
@ -2559,9 +2708,9 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.3.12"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
dependencies = [
"cfg-if",
"libc",
@ -2626,11 +2775,11 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.35"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0"
checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"unicode-xid 0.2.1",
]
@ -2641,9 +2790,9 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
]
[[package]]
@ -2652,9 +2801,9 @@ version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
"unicode-xid 0.2.1",
]
@ -2746,9 +2895,9 @@ dependencies = [
[[package]]
name = "ttf-parser"
version = "0.7.0"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "660a1bf32af7e3380bad2b59aff906c96444bb3264c5c272e1054cc7864041f5"
checksum = "d973cfa0e6124166b50a1105a67c85de40bbc625082f35c0f56f84cb1fb0a827"
[[package]]
name = "twox-hash"
@ -2771,6 +2920,21 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
@ -2879,9 +3043,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasm-bindgen"
version = "0.2.65"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3edbcc9536ab7eababcc6d2374a0b7bfe13a2b6d562c5e07f370456b1a8f33d"
checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -2889,24 +3053,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.65"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ed2fb8c84bfad20ea66b26a3743f3e7ba8735a69fe7d95118c33ec8fc1244d"
checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.65"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb071268b031a64d92fc6cf691715ca5a40950694d8f683c5bb43db7c730929e"
checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038"
dependencies = [
"quote 1.0.7",
"wasm-bindgen-macro-support",
@ -2914,22 +3078,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.65"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf592c807080719d1ff2f245a687cbadb3ed28b2077ed7084b47aba8b691f2c6"
checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.35",
"syn 1.0.40",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.65"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b6c0220ded549d63860c78c38f3bcc558d1ca3f4efa74942c536ddbbb55e87"
checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
[[package]]
name = "wayland-client"
@ -2993,9 +3157,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.42"
version = "0.3.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be2398f326b7ba09815d0b403095f34dd708579220d099caae89be0b32137b2"
checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -3238,7 +3402,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb"
dependencies = [
"proc-macro2 1.0.19",
"syn 1.0.35",
"proc-macro2 1.0.21",
"syn 1.0.40",
"synstructure",
]

View file

@ -25,7 +25,8 @@ members = [
"vendor/pathfinding",
"vendor/pretty",
"editor",
"cli"
"cli",
"docs"
]
# Optimizations based on https://deterministic.space/high-performance-rust.html

225
cli/src/lib.rs Normal file
View file

@ -0,0 +1,225 @@
#[macro_use]
extern crate clap;
use bumpalo::Bump;
use clap::ArgMatches;
use clap::{App, Arg};
use roc_build::program::gen;
use roc_collections::all::MutMap;
use roc_gen::llvm::build::OptLevel;
use roc_load::file::LoadingProblem;
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf};
use std::process;
use std::process::Command;
use std::time::{Duration, SystemTime};
use target_lexicon::Triple;
pub mod repl;
pub static FLAG_OPTIMIZE: &str = "optimize";
pub static FLAG_ROC_FILE: &str = "ROC_FILE";
pub static DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
pub fn build_app<'a>() -> App<'a> {
App::new("roc")
.version(crate_version!())
.subcommand(App::new("build")
.about("Build a program")
.arg(
Arg::with_name(FLAG_ROC_FILE)
.help("The .roc file to build")
.required(true),
)
.arg(
Arg::with_name(FLAG_OPTIMIZE)
.long(FLAG_OPTIMIZE)
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
.required(false),
)
)
.subcommand(App::new("run")
.about("Build and run a program")
.arg(
Arg::with_name(FLAG_ROC_FILE)
.help("The .roc file to build and run")
.required(true),
)
.arg(
Arg::with_name(FLAG_OPTIMIZE)
.long(FLAG_OPTIMIZE)
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
.required(false),
)
)
.subcommand(App::new("repl")
.about("Launch the interactive Read Eval Print Loop (REPL)")
)
.subcommand(App::new("edit")
.about("Launch the Roc editor")
.arg(Arg::with_name(DIRECTORY_OR_FILES)
.index(1)
.multiple(true)
.required(false)
.help("(optional) The directory or files to open on launch.")
)
)
}
pub fn build(matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
let filename = matches.value_of(FLAG_ROC_FILE).unwrap();
let opt_level = if matches.is_present(FLAG_OPTIMIZE) {
OptLevel::Optimize
} else {
OptLevel::Normal
};
let path = Path::new(filename).canonicalize().unwrap();
let src_dir = path.parent().unwrap().canonicalize().unwrap();
// Spawn the root task
let path = path.canonicalize().unwrap_or_else(|err| {
use ErrorKind::*;
match err.kind() {
NotFound => {
match path.to_str() {
Some(path_str) => println!("File not found: {}", path_str),
None => println!("Malformed file path : {:?}", path),
}
process::exit(1);
}
_ => {
todo!("TODO Gracefully handle opening {:?} - {:?}", path, err);
}
}
});
let binary_path =
build_file(src_dir, path, opt_level).expect("TODO gracefully handle build_file failing");
if run_after_build {
// Run the compiled app
Command::new(binary_path)
.spawn()
.unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
.wait()
.expect("TODO gracefully handle block_on failing");
}
Ok(())
}
fn report_timing(buf: &mut String, label: &str, duration: Duration) {
buf.push_str(&format!(
" {:.3} ms {}\n",
duration.as_secs_f64() * 1000.0,
label,
));
}
fn build_file(
src_dir: PathBuf,
filename: PathBuf,
opt_level: OptLevel,
) -> Result<PathBuf, LoadingProblem> {
let compilation_start = SystemTime::now();
let arena = Bump::new();
// Step 1: compile the app and generate the .o file
let subs_by_module = MutMap::default();
// Release builds use uniqueness optimizations
let stdlib = match opt_level {
OptLevel::Normal => roc_builtins::std::standard_stdlib(),
OptLevel::Optimize => roc_builtins::unique::uniq_stdlib(),
};
let loaded =
roc_load::file::load(filename.clone(), &stdlib, src_dir.as_path(), subs_by_module)?;
let dest_filename = filename.with_extension("o");
let buf = &mut String::with_capacity(1024);
for (module_id, module_timing) in loaded.timings.iter() {
let module_name = loaded.interns.module_name(*module_id);
buf.push_str(" ");
buf.push_str(module_name);
buf.push_str("\n");
report_timing(buf, "Read .roc file from disk", module_timing.read_roc_file);
report_timing(buf, "Parse header", module_timing.parse_header);
report_timing(buf, "Parse body", module_timing.parse_body);
report_timing(buf, "Canonicalize", module_timing.canonicalize);
report_timing(buf, "Constrain", module_timing.constrain);
report_timing(buf, "Solve", module_timing.solve);
report_timing(buf, "Other", module_timing.other());
buf.push('\n');
report_timing(buf, "Total", module_timing.total());
}
println!(
"\n\nCompilation finished! Here's how long each module took to compile:\n\n{}",
buf
);
gen(
&arena,
loaded,
filename,
Triple::host(),
&dest_filename,
opt_level,
);
let compilation_end = compilation_start.elapsed().unwrap();
println!(
"Finished compilation and code gen in {} ms\n",
compilation_end.as_millis()
);
let cwd = dest_filename.parent().unwrap();
let lib_path = dest_filename.with_file_name("libroc_app.a");
// Step 2: turn the .o file into a .a static library
Command::new("ar") // TODO on Windows, use `link`
.args(&[
"rcs",
lib_path.to_str().unwrap(),
dest_filename.to_str().unwrap(),
])
.spawn()
.map_err(|_| {
todo!("gracefully handle `ar` failing to spawn.");
})?
.wait()
.map_err(|_| {
todo!("gracefully handle error after `ar` spawned");
})?;
// Step 3: have rustc compile the host and link in the .a file
let binary_path = cwd.join("app");
Command::new("rustc")
.args(&[
"-L",
".",
"--crate-type",
"bin",
"host.rs",
"-o",
binary_path.as_path().to_str().unwrap(),
])
.current_dir(cwd)
.spawn()
.map_err(|_| {
todo!("gracefully handle `rustc` failing to spawn.");
})?
.wait()
.map_err(|_| {
todo!("gracefully handle error after `rustc` spawned");
})?;
Ok(binary_path)
}

View file

@ -1,69 +1,6 @@
#[macro_use]
extern crate clap;
use bumpalo::Bump;
use clap::{App, Arg, ArgMatches};
use roc_build::program::gen;
use roc_collections::all::MutMap;
use roc_gen::llvm::build::OptLevel;
use roc_load::file::LoadingProblem;
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf};
use std::process;
use std::process::Command;
use std::time::{Duration, SystemTime};
use target_lexicon::Triple;
pub mod repl;
pub static FLAG_OPTIMIZE: &str = "optimize";
pub static FLAG_ROC_FILE: &str = "ROC_FILE";
pub static DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
pub fn build_app<'a>() -> App<'a> {
App::new("roc")
.version(crate_version!())
.subcommand(App::new("build")
.about("Build a program")
.arg(
Arg::with_name(FLAG_ROC_FILE)
.help("The .roc file to build")
.required(true),
)
.arg(
Arg::with_name(FLAG_OPTIMIZE)
.long(FLAG_OPTIMIZE)
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
.required(false),
)
)
.subcommand(App::new("run")
.about("Build and run a program")
.arg(
Arg::with_name(FLAG_ROC_FILE)
.help("The .roc file to build and run")
.required(true),
)
.arg(
Arg::with_name(FLAG_OPTIMIZE)
.long(FLAG_OPTIMIZE)
.help("Optimize the compiled program to run faster. (Optimization takes time to complete.)")
.required(false),
)
)
.subcommand(App::new("repl")
.about("Launch the interactive Read Eval Print Loop (REPL)")
)
.subcommand(App::new("edit")
.about("Launch the Roc editor")
.arg(Arg::with_name(DIRECTORY_OR_FILES)
.index(1)
.multiple(true)
.required(false)
.help("(optional) The directory or files to open on launch.")
)
)
}
use roc_cli::{build, build_app, repl, DIRECTORY_OR_FILES};
use std::io;
use std::path::Path;
fn main() -> io::Result<()> {
let matches = build_app().get_matches();
@ -92,161 +29,3 @@ fn main() -> io::Result<()> {
_ => unreachable!(),
}
}
pub fn build(matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
let filename = matches.value_of(FLAG_ROC_FILE).unwrap();
let opt_level = if matches.is_present(FLAG_OPTIMIZE) {
OptLevel::Optimize
} else {
OptLevel::Normal
};
let path = Path::new(filename).canonicalize().unwrap();
let src_dir = path.parent().unwrap().canonicalize().unwrap();
// Spawn the root task
let path = path.canonicalize().unwrap_or_else(|err| {
use ErrorKind::*;
match err.kind() {
NotFound => {
match path.to_str() {
Some(path_str) => println!("File not found: {}", path_str),
None => println!("Malformed file path : {:?}", path),
}
process::exit(1);
}
_ => {
todo!("TODO Gracefully handle opening {:?} - {:?}", path, err);
}
}
});
let binary_path =
build_file(src_dir, path, opt_level).expect("TODO gracefully handle build_file failing");
if run_after_build {
// Run the compiled app
Command::new(binary_path)
.spawn()
.unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
.wait()
.expect("TODO gracefully handle block_on failing");
}
Ok(())
}
fn report_timing(buf: &mut String, label: &str, duration: Duration) {
buf.push_str(&format!(
" {:.3} ms {}\n",
duration.as_secs_f64() * 1000.0,
label,
));
}
fn build_file(
src_dir: PathBuf,
filename: PathBuf,
opt_level: OptLevel,
) -> Result<PathBuf, LoadingProblem> {
let compilation_start = SystemTime::now();
let arena = Bump::new();
// Step 1: compile the app and generate the .o file
let subs_by_module = MutMap::default();
// Release builds use uniqueness optimizations
let stdlib = match opt_level {
OptLevel::Normal => roc_builtins::std::standard_stdlib(),
OptLevel::Optimize => roc_builtins::unique::uniq_stdlib(),
};
let loaded =
roc_load::file::load(filename.clone(), &stdlib, src_dir.as_path(), subs_by_module)?;
let dest_filename = filename.with_extension("o");
let buf = &mut String::with_capacity(1024);
for (module_id, module_timing) in loaded.timings.iter() {
let module_name = loaded.interns.module_name(*module_id);
buf.push_str(" ");
buf.push_str(module_name);
buf.push_str("\n");
report_timing(buf, "Read .roc file from disk", module_timing.read_roc_file);
report_timing(buf, "Parse header", module_timing.parse_header);
report_timing(buf, "Parse body", module_timing.parse_body);
report_timing(buf, "Canonicalize", module_timing.canonicalize);
report_timing(buf, "Constrain", module_timing.constrain);
report_timing(buf, "Solve", module_timing.solve);
report_timing(buf, "Other", module_timing.other());
buf.push('\n');
report_timing(buf, "Total", module_timing.total());
}
println!(
"\n\nCompilation finished! Here's how long each module took to compile:\n\n{}",
buf
);
gen(
&arena,
loaded,
filename,
Triple::host(),
&dest_filename,
opt_level,
);
let compilation_end = compilation_start.elapsed().unwrap();
println!(
"Finished compilation and code gen in {} ms\n",
compilation_end.as_millis()
);
let cwd = dest_filename.parent().unwrap();
let lib_path = dest_filename.with_file_name("libroc_app.a");
// Step 2: turn the .o file into a .a static library
Command::new("ar") // TODO on Windows, use `link`
.args(&[
"rcs",
lib_path.to_str().unwrap(),
dest_filename.to_str().unwrap(),
])
.spawn()
.map_err(|_| {
todo!("gracefully handle `ar` failing to spawn.");
})?
.wait()
.map_err(|_| {
todo!("gracefully handle error after `ar` spawned");
})?;
// Step 3: have rustc compile the host and link in the .a file
let binary_path = cwd.join("app");
Command::new("rustc")
.args(&[
"-L",
".",
"--crate-type",
"bin",
"host.rs",
"-o",
binary_path.as_path().to_str().unwrap(),
])
.current_dir(cwd)
.spawn()
.map_err(|_| {
todo!("gracefully handle `rustc` failing to spawn.");
})?
.wait()
.map_err(|_| {
todo!("gracefully handle error after `rustc` spawned");
})?;
Ok(binary_path)
}

View file

@ -1,7 +1,6 @@
use bumpalo::Bump;
use inkwell::context::Context;
use inkwell::execution_engine::ExecutionEngine;
use inkwell::types::BasicType;
use inkwell::OptimizationLevel;
use roc_builtins::unique::uniq_stdlib;
use roc_can::constraint::Constraint;
@ -15,7 +14,6 @@ use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Imp
use roc_fmt::annotation::{Formattable, Newlines, Parens};
use roc_gen::layout_id::LayoutIds;
use roc_gen::llvm::build::{build_proc, build_proc_header, OptLevel};
use roc_gen::llvm::convert::basic_type_from_layout;
use roc_module::ident::Ident;
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
use roc_mono::ir::Procs;
@ -223,10 +221,6 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
// in --release mode and then trying to eval anything in the repl.
ExecutionEngine::link_in_mc_jit();
let main_fn_type = basic_type_from_layout(&arena, &context, &main_ret_layout, ptr_bytes)
.fn_type(&[], false);
let main_fn_name = "$Test.main";
// Compile and add all the Procs before adding main
let mut env = roc_gen::llvm::build::Env {
arena: &arena,
@ -322,23 +316,10 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
}
}
// Add main to the module.
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
let cc = roc_gen::llvm::build::FAST_CALL_CONV;
main_fn.set_call_conventions(cc);
// Add main's body
let basic_block = context.append_basic_block(main_fn, "entry");
builder.position_at_end(basic_block);
// builds the function body (return statement included)
roc_gen::llvm::build::build_exp_stmt(
let (main_fn_name, main_fn) = roc_gen::llvm::build::make_main_function(
&env,
&mut layout_ids,
&mut roc_gen::llvm::build::Scope::default(),
main_fn,
&main_ret_layout,
&main_body,
);

View file

@ -118,20 +118,67 @@ fn jit_to_ast_help<'a>(
}
};
// Functions can return structs of either 8 or 16 bytes, depending
// on whether we're compiling for a 64-bit or 32-bit target.
match env.ptr_bytes {
// 64-bit target (8-byte pointers, 16-byte structs)
8 => jit_map!(
8 => match layout.stack_size(env.ptr_bytes) {
8 => {
// just one eightbyte, returned as-is
jit_map!(execution_engine, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
})
}
16 => {
// two eightbytes, returned as-is
jit_map!(
execution_engine,
main_fn_name,
[u8; 16],
|bytes: [u8; 16]| { ptr_to_ast((&bytes).as_ptr() as *const libc::c_void) }
),
// 32-bit target (4-byte pointers, 8-byte structs)
4 => jit_map!(execution_engine, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|bytes: [u8; 16]| {
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
}),
}
)
}
_ => {
// anything more than 2 eightbytes
// the return "value" is a pointer to the result
jit_map!(
execution_engine,
main_fn_name,
*const u8,
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
)
}
},
// 32-bit target (4-byte pointers, 8-byte structs)
4 => {
// TODO what are valid return sizes here?
// this is just extrapolated from the 64-bit case above
// and not (yet) actually tested on a 32-bit system
match layout.stack_size(env.ptr_bytes) {
4 => {
// just one fourbyte, returned as-is
jit_map!(execution_engine, main_fn_name, [u8; 4], |bytes: [u8; 4]| {
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
})
}
8 => {
// just one fourbyte, returned as-is
jit_map!(execution_engine, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
})
}
_ => {
// anything more than 2 fourbytes
// the return "value" is a pointer to the result
jit_map!(
execution_engine,
main_fn_name,
*const u8,
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
)
}
}
}
other => {
panic!("Unsupported target: Roc cannot currently compile to systems where pointers are {} bytes in length.", other);
}
@ -280,7 +327,7 @@ fn struct_to_ast<'a>(
output.push(loc_field);
// Advance the field pointer to the next field.
field_ptr = unsafe { ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) };
field_ptr = unsafe { field_ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) };
}
Expr::Record {

View file

@ -3,8 +3,8 @@ extern crate inlinable_string;
extern crate roc_collections;
extern crate roc_load;
extern crate roc_module;
// extern crate roc_cli; // TODO FIXME why doesn't this resolve?
use roc_cli::repl::{INSTRUCTIONS, PROMPT, WELCOME_MESSAGE};
use std::env;
use std::io::Write;
use std::path::PathBuf;
@ -16,12 +16,6 @@ pub struct Out {
pub status: ExitStatus,
}
// TODO get these from roc_cli::repl instead, after figuring out why
// `extern crate roc_cli;` doesn't work.
const WELCOME_MESSAGE: &str = "\n The rockin \u{001b}[36mroc repl\u{001b}[0m\n\u{001b}[35m────────────────────────\u{001b}[0m\n\n";
const INSTRUCTIONS: &str = "Enter an expression, or :help, or :exit.\n";
const PROMPT: &str = "\n\u{001b}[36m»\u{001b}[0m ";
pub fn path_to_roc_binary() -> PathBuf {
// Adapted from https://github.com/volta-cli/volta/blob/cefdf7436a15af3ce3a38b8fe53bb0cfdb37d3dd/tests/acceptance/support/sandbox.rs#L680 - BSD-2-Clause licensed
let mut path = env::var_os("CARGO_BIN_PATH")
@ -129,8 +123,6 @@ pub fn repl_eval(input: &str) -> Out {
// Remove the initial instructions from the output.
// TODO get these from roc_cli::repl instead, after figuring out why
// `extern crate roc_cli;` doesn't work.
let expected_instructions = format!("{}{}{}", WELCOME_MESSAGE, INSTRUCTIONS, PROMPT);
let stdout = String::from_utf8(output.stdout).unwrap();

View file

@ -232,6 +232,15 @@ mod repl_eval {
);
}
#[test]
fn three_element_record() {
// if this tests turns out to fail on 32-bit platforms, look at jit_to_ast_help
expect_success(
"{ a: 1, b: 2, c: 3 }",
"{ a: 1, b: 2, c: 3 } : { a : Num *, b : Num *, c : Num * }",
);
}
// #[test]
// fn multiline_string() {
// // If a string contains newlines, format it as a multiline string in the output

View file

@ -6,7 +6,7 @@ Builtins are the functions and modules that are implicitly imported into every m
Towards the bottom of `symbol.rs` there is a `define_builtins!` macro being used that takes many modules and function names. The first level (`List`, `Int` ..) is the module name, and the second level is the function or value name (`reverse`, `mod` ..). If you wanted to add a `Int` function called `addTwo` go to `2 Int: "Int" => {` and inside that case add to the bottom `38 INT_ADD_TWO: "addTwo"` (assuming there are 37 existing ones).
Some of these have `#` inside their name (`first#list`, #lt` ..). This is a trick we are doing to hide implementation details from Roc programmers. To a Roc programmer, a name with `#` in it is invalid, because `#` means everything after it is parsed to a comment. We are constructing these functions manually, so we are circumventing the parsing step and dont have such restrictions. We get to make functions and values with `#` which as a consequence are not accessible to Roc programmers. Roc programmers simply cannot reference them.
Some of these have `#` inside their name (`first#list`, `#lt` ..). This is a trick we are doing to hide implementation details from Roc programmers. To a Roc programmer, a name with `#` in it is invalid, because `#` means everything after it is parsed to a comment. We are constructing these functions manually, so we are circumventing the parsing step and dont have such restrictions. We get to make functions and values with `#` which as a consequence are not accessible to Roc programmers. Roc programmers simply cannot reference them.
But we can use these values and some of these are necessary for implementing builtins. For example, `List.get` returns tags, and it is not easy for us to create tags when composing LLVM. What is easier however, is:
- ..writing `List.#getUnsafe` that has the dangerous signature of `List elem, Int -> elem` in LLVM
@ -15,9 +15,9 @@ But we can use these values and some of these are necessary for implementing bui
### can/src/builtins.rs
Right at the top of this module is a function called `builtin_defs`. All this is doing is mapping the `Symbol` defined in `module/src/symbol.rs` to its implementation. Some of the builtins are quite complex, such as `list_get`, which is complex the reasons mentioned in the previous section; `list_get` returns tags, and it defers to lower-level functions via an if statement.
Right at the top of this module is a function called `builtin_defs`. All this is doing is mapping the `Symbol` defined in `module/src/symbol.rs` to its implementation. Some of the builtins are quite complex, such as `list_get`. What makes `list_get` is that it returns tags, and in order to return tags it first has to defer to lower-level functions via an if statement.
Lets look at `List.repeat : elem, Int -> List elem`, which is more straight forward, and points directly to its lower level implementation:
Lets look at `List.repeat : elem, Int -> List elem`, which is more straight-forward, and points directly to its lower level implementation:
```
fn list_repeat(symbol: Symbol, var_store: &mut VarStore) -> Def {
let elem_var = var_store.fresh();
@ -55,10 +55,14 @@ This `LowLevel` thing connects the builtin defined in this module to its impleme
This is where bottom-level functions that need to be written as LLVM are created. If the function leads to a tag thats a good sign it should not be written here in `build.rs`. If its simple fundamental stuff like `INT_ADD` then it certainly should be written here.
## Letting the compiler know these functions exist
Its one thing to actually write these functions, its _another_ thing to let the Roc compiler know they exist as part of the standard library. You have to tell the compiler "Hey, this function exists, and it has this type signature". That happens in these modules:
### builtins/src/std.rs
### builtins/src/unique.rs
Its one thing to actually write these functions, its _another_ thing to let the Roc compiler know they exist as part of the standard library. You have to tell the compiler "Hey, this function exists, and it has this type signature". That happens in `std.rs`.
## Specifying the uniqueness of a function
### builtins/src/unique.rs
One of the cool things about Roc is that it evaluates if a value in memory is shared between scopes or if it is used in just one place. If the value is used in one place then it is 'unique', and it therefore can be mutated in place. For a value created by a function, the uniqueness of the output is determined in part by the uniqueness of the input arguments. For example `List.single : elem -> List elem` can return a unique list if the `elem` is also unique.
We have to define the uniqueness constraints of a function just like we have to define a type signature. That is what happens in `unique.rs`. This can be tricky so it would be a good step to ask for help on if it is confusing.
# Mistakes that are easy to make!!

View file

@ -282,7 +282,11 @@ joinMap : List before, (before -> List after) -> List after
## >>> [ [ 1, 2, 3 ], [], [], [ 4, 5 ] ]
## >>> |> List.map List.first
## >>> |> List.joinOks
joinOks : List (Result elem *) -> List elem
##
## Eventually, `oks` type signature will be `List [Ok elem]* -> List elem`.
## The implementation for that is a lot tricker then `List (Result elem *)`
## so we're sticking with `Result` for now.
oks : List (Result elem *) -> List elem
## Iterates over the shortest of the given lists and returns a list of `Pair`
## tags, each wrapping one of the elements in that list, along with the elements

View file

@ -412,6 +412,18 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
// minFloat : Float
add_type(Symbol::NUM_MIN_FLOAT, float_type());
// pow : Float, Float -> Float
add_type(
Symbol::NUM_POW,
SolvedType::Func(vec![float_type(), float_type()], Box::new(float_type())),
);
// ceiling : Float -> Int
add_type(
Symbol::NUM_CEILING,
SolvedType::Func(vec![float_type()], Box::new(int_type())),
);
// Bool module
// and : Bool, Bool -> Bool

View file

@ -448,6 +448,21 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
unique_function(vec![num_type(star1, a)], bool_type(star2))
});
// pow : Float, Float -> Float
add_type(Symbol::NUM_POW, {
let_tvars! { star1, star2, star3 };
unique_function(
vec![float_type(star1), float_type(star2)],
float_type(star3),
)
});
// ceiling : Float -> Int
add_type(Symbol::NUM_CEILING, {
let_tvars! { star1, star2 };
unique_function(vec![float_type(star1)], int_type(star2))
});
// Bool module
// isEq or (==) : Attr * a, Attr * a -> Attr * Bool

View file

@ -91,6 +91,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
Symbol::NUM_IS_POSITIVE => num_is_positive,
Symbol::NUM_IS_NEGATIVE => num_is_negative,
Symbol::NUM_TO_FLOAT => num_to_float,
Symbol::NUM_POW => num_pow,
Symbol::NUM_CEILING => num_ceiling,
}
}
@ -579,6 +581,47 @@ fn num_round(symbol: Symbol, var_store: &mut VarStore) -> Def {
)
}
/// Num.pow : Float, Float -> Float
fn num_pow(symbol: Symbol, var_store: &mut VarStore) -> Def {
let float_var = var_store.fresh();
let body = RunLowLevel {
op: LowLevel::NumPow,
args: vec![
(float_var, Var(Symbol::ARG_1)),
(float_var, Var(Symbol::ARG_2)),
],
ret_var: float_var,
};
defn(
symbol,
vec![(float_var, Symbol::ARG_1), (float_var, Symbol::ARG_2)],
var_store,
body,
float_var,
)
}
/// Num.ceiling : Float -> Int
fn num_ceiling(symbol: Symbol, var_store: &mut VarStore) -> Def {
let float_var = var_store.fresh();
let int_var = var_store.fresh();
let body = RunLowLevel {
op: LowLevel::NumCeiling,
args: vec![(float_var, Var(Symbol::ARG_1))],
ret_var: int_var,
};
defn(
symbol,
vec![(float_var, Symbol::ARG_1)],
var_store,
body,
int_var,
)
}
/// List.isEmpty : List * -> Bool
fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh();

View file

@ -24,7 +24,8 @@ use inkwell::passes::{PassManager, PassManagerBuilder};
use inkwell::types::{BasicTypeEnum, FunctionType, IntType, StructType};
use inkwell::values::BasicValueEnum::{self, *};
use inkwell::values::{
BasicValue, CallSiteValue, FloatValue, FunctionValue, IntValue, PointerValue, StructValue,
BasicValue, CallSiteValue, FloatValue, FunctionValue, InstructionOpcode, IntValue,
PointerValue, StructValue,
};
use inkwell::OptimizationLevel;
use inkwell::{AddressSpace, IntPredicate};
@ -194,6 +195,34 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
let i8_type = ctx.i8_type();
let i8_ptr_type = i8_type.ptr_type(AddressSpace::Generic);
add_intrinsic(
module,
LLVM_MEMSET_I64,
void_type.fn_type(
&[
i8_ptr_type.into(),
i8_type.into(),
i64_type.into(),
i1_type.into(),
],
false,
),
);
add_intrinsic(
module,
LLVM_MEMSET_I32,
void_type.fn_type(
&[
i8_ptr_type.into(),
i8_type.into(),
i32_type.into(),
i1_type.into(),
],
false,
),
);
add_intrinsic(
module,
LLVM_SQRT_F64,
@ -226,30 +255,14 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
add_intrinsic(
module,
LLVM_MEMSET_I64,
void_type.fn_type(
&[
i8_ptr_type.into(),
i8_type.into(),
i64_type.into(),
i1_type.into(),
],
false,
),
LLVM_POW_F64,
f64_type.fn_type(&[f64_type.into(), f64_type.into()], false),
);
add_intrinsic(
module,
LLVM_MEMSET_I32,
void_type.fn_type(
&[
i8_ptr_type.into(),
i8_type.into(),
i32_type.into(),
i1_type.into(),
],
false,
),
LLVM_CEILING_F64,
f64_type.fn_type(&[f64_type.into()], false),
);
}
@ -260,6 +273,8 @@ static LLVM_LROUND_I64_F64: &str = "llvm.lround.i64.f64";
static LLVM_FABS_F64: &str = "llvm.fabs.f64";
static LLVM_SIN_F64: &str = "llvm.sin.f64";
static LLVM_COS_F64: &str = "llvm.cos.f64";
static LLVM_POW_F64: &str = "llvm.pow.f64";
static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
fn add_intrinsic<'ctx>(
module: &Module<'ctx>,
@ -320,6 +335,133 @@ pub fn construct_optimization_passes<'a>(
(mpm, fpm)
}
/// For communication with C (tests and platforms) we need to abide by the C calling convention
///
/// While small values are just returned like with the fast CC, larger structures need to
/// be written into a pointer (into the callers stack)
enum PassVia {
Register,
Memory,
}
impl PassVia {
fn from_layout(ptr_bytes: u32, layout: &Layout<'_>) -> Self {
if layout.stack_size(ptr_bytes) > 16 {
PassVia::Memory
} else {
PassVia::Register
}
}
}
pub fn make_main_function<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
layout: &Layout<'a>,
main_body: &roc_mono::ir::Stmt<'a>,
) -> (&'static str, &'a FunctionValue<'ctx>) {
use inkwell::types::BasicType;
use PassVia::*;
let context = env.context;
let builder = env.builder;
let arena = env.arena;
let ptr_bytes = env.ptr_bytes;
let return_type = basic_type_from_layout(&arena, context, &layout, ptr_bytes);
let roc_main_fn_name = "$Test.roc_main";
// make the roc main function
let roc_main_fn_type = return_type.fn_type(&[], false);
// Add main to the module.
let roc_main_fn = env
.module
.add_function(roc_main_fn_name, roc_main_fn_type, None);
// our exposed main function adheres to the C calling convention
roc_main_fn.set_call_conventions(FAST_CALL_CONV);
// Add main's body
let basic_block = context.append_basic_block(roc_main_fn, "entry");
builder.position_at_end(basic_block);
// builds the function body (return statement included)
build_exp_stmt(
env,
layout_ids,
&mut Scope::default(),
roc_main_fn,
main_body,
);
// build the C calling convention wrapper
let main_fn_name = "$Test.main";
let register_or_memory = PassVia::from_layout(env.ptr_bytes, layout);
let main_fn_type = match register_or_memory {
Memory => {
let return_value_ptr = context.i64_type().ptr_type(AddressSpace::Generic).into();
context.void_type().fn_type(&[return_value_ptr], false)
}
Register => return_type.fn_type(&[], false),
};
// Add main to the module.
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
// our exposed main function adheres to the C calling convention
main_fn.set_call_conventions(C_CALL_CONV);
// Add main's body
let basic_block = context.append_basic_block(main_fn, "entry");
builder.position_at_end(basic_block);
let call = builder.build_call(roc_main_fn, &[], "call_roc_main");
call.set_call_convention(FAST_CALL_CONV);
let call_result = call.try_as_basic_value().left().unwrap();
match register_or_memory {
Memory => {
// write the result into the supplied pointer
// this is a void function, therefore return None
let ptr_return_type = return_type.ptr_type(AddressSpace::Generic);
let ptr_as_int = main_fn.get_first_param().unwrap();
let ptr = builder.build_bitcast(ptr_as_int, ptr_return_type, "caller_ptr");
builder.build_store(ptr.into_pointer_value(), call_result);
builder.build_return(None);
}
Register => {
// construct a normal return
// values are passed to the caller via registers
builder.build_return(Some(&call_result));
}
}
(main_fn_name, env.arena.alloc(main_fn))
}
fn get_inplace_from_layout(layout: &Layout<'_>) -> InPlace {
match layout {
Layout::Builtin(Builtin::EmptyList) => InPlace::InPlace,
Layout::Builtin(Builtin::List(memory_mode, _)) => match memory_mode {
MemoryMode::Unique => InPlace::InPlace,
MemoryMode::Refcounted => InPlace::Clone,
},
Layout::Builtin(Builtin::EmptyStr) => InPlace::InPlace,
Layout::Builtin(Builtin::Str) => InPlace::Clone,
_ => unreachable!("Layout {:?} does not have an inplace", layout),
}
}
pub fn build_exp_literal<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
literal: &roc_mono::ir::Literal<'a>,
@ -418,8 +560,7 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
let len_type = env.ptr_int();
let len = len_type.const_int(bytes_len, false);
let ptr = allocate_list(env, &CHAR_LAYOUT, len);
let ptr = allocate_list(env, InPlace::Clone, &CHAR_LAYOUT, len);
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int = builder.build_ptr_to_int(ptr, int_type, "list_cast_ptr");
let struct_type = collection(ctx, ptr_bytes);
@ -461,6 +602,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
layout_ids: &mut LayoutIds<'a>,
scope: &Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>,
layout: &Layout<'a>,
expr: &roc_mono::ir::Expr<'a>,
) -> BasicValueEnum<'ctx> {
use roc_mono::ir::CallType::*;
@ -468,7 +610,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
match expr {
Literal(literal) => build_exp_literal(env, literal),
RunLowLevel(op, symbols) => run_low_level(env, scope, parent, *op, symbols),
RunLowLevel(op, symbols) => run_low_level(env, scope, parent, layout, *op, symbols),
FunctionCall {
call_type: ByName(name),
@ -833,7 +975,11 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
}
}
EmptyArray => empty_polymorphic_list(env),
Array { elem_layout, elems } => list_literal(env, scope, elem_layout, elems),
Array { elem_layout, elems } => {
let inplace = get_inplace_from_layout(layout);
list_literal(env, inplace, scope, elem_layout, elems)
}
FunctionPointer(symbol, layout) => {
let fn_name = layout_ids
.get(*symbol, layout)
@ -922,6 +1068,7 @@ pub fn allocate_with_refcount<'a, 'ctx, 'env>(
fn list_literal<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
inplace: InPlace,
scope: &Scope<'a, 'ctx>,
elem_layout: &Layout<'a>,
elems: &&[Symbol],
@ -937,7 +1084,7 @@ fn list_literal<'a, 'ctx, 'env>(
let len_type = env.ptr_int();
let len = len_type.const_int(bytes_len, false);
allocate_list(env, elem_layout, len)
allocate_list(env, inplace, elem_layout, len)
// TODO check if malloc returned null; if so, runtime error for OOM!
};
@ -995,7 +1142,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
Let(symbol, expr, layout, cont) => {
let context = &env.context;
let val = build_exp_expr(env, layout_ids, &scope, parent, &expr);
let val = build_exp_expr(env, layout_ids, &scope, parent, layout, &expr);
let expr_bt = if let Layout::RecursivePointer = layout {
match expr {
Expr::AccessAtIndex { field_layouts, .. } => {
@ -1611,6 +1758,7 @@ fn call_with_args<'a, 'ctx, 'env>(
.unwrap_or_else(|| panic!("LLVM error: Invalid call by name for name {:?}", symbol))
}
#[derive(Copy, Clone)]
pub enum InPlace {
InPlace,
Clone,
@ -1639,6 +1787,7 @@ fn run_low_level<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>,
layout: &Layout<'a>,
op: LowLevel,
args: &[Symbol],
) -> BasicValueEnum<'ctx> {
@ -1649,7 +1798,9 @@ fn run_low_level<'a, 'ctx, 'env>(
// Str.concat : Str, Str -> Str
debug_assert_eq!(args.len(), 2);
str_concat(env, scope, parent, args[0], args[1])
let inplace = get_inplace_from_layout(layout);
str_concat(env, inplace, scope, parent, args[0], args[1])
}
StrIsEmpty => {
// Str.isEmpty : Str -> Str
@ -1679,7 +1830,9 @@ fn run_low_level<'a, 'ctx, 'env>(
let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]);
list_single(env, arg, arg_layout)
let inplace = get_inplace_from_layout(layout);
list_single(env, inplace, arg, arg_layout)
}
ListRepeat => {
// List.repeat : Int, elem -> List elem
@ -1688,7 +1841,9 @@ fn run_low_level<'a, 'ctx, 'env>(
let list_len = load_symbol(env, scope, &args[0]).into_int_value();
let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]);
list_repeat(env, parent, list_len, elem, elem_layout)
let inplace = get_inplace_from_layout(layout);
list_repeat(env, inplace, parent, list_len, elem, elem_layout)
}
ListReverse => {
// List.reverse : List elem -> List elem
@ -1696,7 +1851,9 @@ fn run_low_level<'a, 'ctx, 'env>(
let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
list_reverse(env, parent, list, list_layout)
let inplace = get_inplace_from_layout(layout);
list_reverse(env, parent, inplace, list, list_layout)
}
ListConcat => {
debug_assert_eq!(args.len(), 2);
@ -1705,7 +1862,9 @@ fn run_low_level<'a, 'ctx, 'env>(
let second_list = load_symbol(env, scope, &args[1]);
list_concat(env, parent, first_list, second_list, list_layout)
let inplace = get_inplace_from_layout(layout);
list_concat(env, inplace, parent, first_list, second_list, list_layout)
}
ListMap => {
// List.map : List before, (before -> after) -> List after
@ -1715,7 +1874,9 @@ fn run_low_level<'a, 'ctx, 'env>(
let (func, func_layout) = load_symbol_and_layout(env, scope, &args[1]);
list_map(env, parent, func, func_layout, list, list_layout)
let inplace = get_inplace_from_layout(layout);
list_map(env, inplace, parent, func, func_layout, list, list_layout)
}
ListKeepIf => {
// List.keepIf : List elem, (elem -> Bool) -> List elem
@ -1725,7 +1886,9 @@ fn run_low_level<'a, 'ctx, 'env>(
let (func, func_layout) = load_symbol_and_layout(env, scope, &args[1]);
list_keep_if(env, parent, func, func_layout, list, list_layout)
let inplace = get_inplace_from_layout(layout);
list_keep_if(env, inplace, parent, func, func_layout, list, list_layout)
}
ListWalkRight => {
// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
@ -1755,7 +1918,9 @@ fn run_low_level<'a, 'ctx, 'env>(
let original_wrapper = load_symbol(env, scope, &args[0]).into_struct_value();
let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]);
list_append(env, original_wrapper, elem, elem_layout)
let inplace = get_inplace_from_layout(layout);
list_append(env, inplace, original_wrapper, elem, elem_layout)
}
ListPrepend => {
// List.prepend : List elem, elem -> List elem
@ -1764,7 +1929,9 @@ fn run_low_level<'a, 'ctx, 'env>(
let original_wrapper = load_symbol(env, scope, &args[0]).into_struct_value();
let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]);
list_prepend(env, original_wrapper, elem, elem_layout)
let inplace = get_inplace_from_layout(layout);
list_prepend(env, inplace, original_wrapper, elem, elem_layout)
}
ListJoin => {
// List.join : List (List elem) -> List elem
@ -1772,9 +1939,12 @@ fn run_low_level<'a, 'ctx, 'env>(
let (list, outer_list_layout) = load_symbol_and_layout(env, scope, &args[0]);
list_join(env, parent, list, outer_list_layout)
let inplace = get_inplace_from_layout(layout);
list_join(env, inplace, parent, list, outer_list_layout)
}
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumToFloat => {
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling
| NumToFloat => {
debug_assert_eq!(args.len(), 1);
let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]);
@ -1885,7 +2055,7 @@ fn run_low_level<'a, 'ctx, 'env>(
}
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
| NumDivUnchecked => {
| NumDivUnchecked | NumPow => {
debug_assert_eq!(args.len(), 2);
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
@ -1990,6 +2160,8 @@ fn run_low_level<'a, 'ctx, 'env>(
ListSetInPlace => {
let (list_symbol, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
let output_inplace = get_inplace_from_layout(layout);
list_set(
parent,
&[
@ -1999,6 +2171,7 @@ fn run_low_level<'a, 'ctx, 'env>(
],
env,
InPlace::InPlace,
output_inplace,
)
}
ListSet => {
@ -2010,10 +2183,43 @@ fn run_low_level<'a, 'ctx, 'env>(
(load_symbol_and_layout(env, scope, &args[2])),
];
let output_inplace = get_inplace_from_layout(layout);
let in_place = || list_set(parent, arguments, env, InPlace::InPlace, output_inplace);
let clone = || list_set(parent, arguments, env, InPlace::Clone, output_inplace);
let empty = || list_symbol;
maybe_inplace_list(
env,
parent,
list_layout,
list_symbol.into_struct_value(),
in_place,
clone,
empty,
)
}
}
}
fn maybe_inplace_list<'a, 'ctx, 'env, InPlace, CloneFirst, Empty>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
list_layout: &Layout<'a>,
original_wrapper: StructValue<'ctx>,
mut in_place: InPlace,
clone: CloneFirst,
mut empty: Empty,
) -> BasicValueEnum<'ctx>
where
InPlace: FnMut() -> BasicValueEnum<'ctx>,
CloneFirst: FnMut() -> BasicValueEnum<'ctx>,
Empty: FnMut() -> BasicValueEnum<'ctx>,
{
match list_layout {
Layout::Builtin(Builtin::List(MemoryMode::Unique, _)) => {
// the layout tells us this List.set can be done in-place
list_set(parent, arguments, env, InPlace::InPlace)
in_place()
}
Layout::Builtin(Builtin::List(MemoryMode::Refcounted, _)) => {
// no static guarantees, but all is not lost: we can check the refcount
@ -2021,11 +2227,9 @@ fn run_low_level<'a, 'ctx, 'env>(
let builder = env.builder;
let ctx = env.context;
let ret_type =
basic_type_from_layout(env.arena, ctx, list_layout, env.ptr_bytes);
let ret_type = basic_type_from_layout(env.arena, ctx, list_layout, env.ptr_bytes);
let refcount_ptr =
list_get_refcount_ptr(env, list_layout, list_symbol.into_struct_value());
let refcount_ptr = list_get_refcount_ptr(env, list_layout, original_wrapper);
let refcount = env
.builder
@ -2034,22 +2238,12 @@ fn run_low_level<'a, 'ctx, 'env>(
let comparison = refcount_is_one_comparison(builder, env.context, refcount);
// build then block
// refcount is 1, so work in-place
let build_pass = || list_set(parent, arguments, env, InPlace::InPlace);
// build else block
// refcount != 1, so clone first
let build_fail = || list_set(parent, arguments, env, InPlace::Clone);
crate::llvm::build_list::build_basic_phi2(
env, parent, comparison, build_pass, build_fail, ret_type,
env, parent, comparison, in_place, clone, ret_type,
)
}
Layout::Builtin(Builtin::EmptyList) => list_symbol,
other => unreachable!("List.set: weird layout {:?}", other),
}
}
Layout::Builtin(Builtin::EmptyList) => empty(),
other => unreachable!("Attempting list operation on invalid layout {:?}", other),
}
}
@ -2105,6 +2299,7 @@ fn build_float_binop<'a, 'ctx, 'env>(
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
NumRemUnchecked => bd.build_float_rem(lhs, rhs, "rem_float").into(),
NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(),
NumPow => env.call_intrinsic(LLVM_POW_F64, &[lhs.into(), rhs.into()]),
_ => {
unreachable!("Unrecognized int binary operation: {:?}", op);
}
@ -2202,6 +2397,12 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
NumSin => env.call_intrinsic(LLVM_SIN_F64, &[arg.into()]),
NumCos => env.call_intrinsic(LLVM_COS_F64, &[arg.into()]),
NumToFloat => arg.into(), /* Converting from Float to Float is a no-op */
NumCeiling => env.builder.build_cast(
InstructionOpcode::FPToSI,
env.call_intrinsic(LLVM_CEILING_F64, &[arg.into()]),
env.context.i64_type(),
"num_ceiling",
),
_ => {
unreachable!("Unrecognized int unary operation: {:?}", op);
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
use crate::llvm::build::{ptr_from_symbol, Env, Scope};
use crate::llvm::build::{ptr_from_symbol, Env, InPlace, Scope};
use crate::llvm::build_list::{
allocate_list, build_basic_phi2, empty_list, incrementing_elem_loop, load_list_ptr, store_list,
LoopListArg,
};
use crate::llvm::convert::{collection, ptr_int};
use inkwell::builder::Builder;
@ -16,6 +15,7 @@ pub static CHAR_LAYOUT: Layout = Layout::Builtin(Builtin::Int8);
/// Str.concat : Str, Str -> Str
pub fn str_concat<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
inplace: InPlace,
scope: &Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>,
first_str_symbol: Symbol,
@ -55,6 +55,7 @@ pub fn str_concat<'a, 'ctx, 'env>(
let if_second_str_is_nonempty = || {
let (new_wrapper, _) = clone_nonempty_str(
env,
inplace,
second_str_smallness,
second_str_len,
second_str_ptr,
@ -79,6 +80,7 @@ pub fn str_concat<'a, 'ctx, 'env>(
let if_second_str_is_empty = || {
let (new_wrapper, _) = clone_nonempty_str(
env,
inplace,
first_str_smallness,
first_str_len,
first_str_ptr,
@ -111,7 +113,7 @@ pub fn str_concat<'a, 'ctx, 'env>(
let if_big = || {
let combined_str_ptr =
allocate_list(env, &CHAR_LAYOUT, combined_str_len);
allocate_list(env, inplace, &CHAR_LAYOUT, combined_str_len);
// TODO replace FIRST_LOOP with a memcpy!
// FIRST LOOP
@ -133,14 +135,11 @@ pub fn str_concat<'a, 'ctx, 'env>(
let index_alloca = incrementing_elem_loop(
builder,
parent,
ctx,
LoopListArg {
ptr: first_str_ptr,
len: first_str_len,
},
parent,
first_str_ptr,
first_str_len,
index_name,
None,
first_loop,
);
@ -179,14 +178,11 @@ pub fn str_concat<'a, 'ctx, 'env>(
incrementing_elem_loop(
builder,
parent,
ctx,
LoopListArg {
ptr: second_str_ptr,
len: second_str_len,
},
parent,
second_str_ptr,
second_str_len,
index_name,
Some(index_alloca),
second_loop,
);
@ -220,14 +216,11 @@ pub fn str_concat<'a, 'ctx, 'env>(
let index_alloca = incrementing_elem_loop(
builder,
parent,
ctx,
LoopListArg {
ptr: first_str_ptr,
len: first_str_len,
},
parent,
first_str_ptr,
first_str_len,
index_name,
None,
first_loop,
);
@ -266,14 +259,11 @@ pub fn str_concat<'a, 'ctx, 'env>(
incrementing_elem_loop(
builder,
parent,
ctx,
LoopListArg {
ptr: second_str_ptr,
len: second_str_len,
},
parent,
second_str_ptr,
second_str_len,
index_name,
Some(index_alloca),
second_loop,
);
@ -445,6 +435,7 @@ enum Smallness {
fn clone_nonempty_str<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
inplace: InPlace,
smallness: Smallness,
len: IntValue<'ctx>,
bytes_ptr: PointerValue<'ctx>,
@ -465,7 +456,7 @@ fn clone_nonempty_str<'a, 'ctx, 'env>(
(wrapper_struct.into_struct_value(), alloca)
}
Smallness::Big => {
let clone_ptr = allocate_list(env, &CHAR_LAYOUT, len);
let clone_ptr = allocate_list(env, inplace, &CHAR_LAYOUT, len);
let int_type = ptr_int(ctx, ptr_bytes);
let ptr_as_int = builder.build_ptr_to_int(clone_ptr, int_type, "list_cast_ptr");

View file

@ -665,4 +665,14 @@ mod gen_num {
i64
);
}
#[test]
fn pow() {
assert_evals_to!("Num.pow 2.0 2.0", 4.0, f64);
}
#[test]
fn ceiling() {
assert_evals_to!("Num.ceiling 1.5", 2, i64);
}
}

View file

@ -533,4 +533,147 @@ mod gen_records {
i64
);
}
#[test]
fn return_record_2() {
assert_evals_to!(
indoc!(
r#"
{ x: 3, y: 5 }
"#
),
[3, 5],
[i64; 2]
);
}
#[test]
fn return_record_3() {
assert_evals_to!(
indoc!(
r#"
{ x: 3, y: 5, z: 4 }
"#
),
(3, 5, 4),
(i64, i64, i64)
);
}
#[test]
fn return_record_4() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2 }
"#
),
[3, 5, 4, 2],
[i64; 4]
);
}
#[test]
fn return_record_5() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2, e: 1 }
"#
),
[3, 5, 4, 2, 1],
[i64; 5]
);
}
#[test]
fn return_record_6() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
"#
),
[3, 5, 4, 2, 1, 7],
[i64; 6]
);
}
#[test]
fn return_record_7() {
assert_evals_to!(
indoc!(
r#"
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
"#
),
[3, 5, 4, 2, 1, 7, 8],
[i64; 7]
);
}
#[test]
fn return_record_float_int() {
assert_evals_to!(
indoc!(
r#"
{ a: 3.14, b: 0x1 }
"#
),
(3.14, 0x1),
(f64, i64)
);
}
#[test]
fn return_record_int_float() {
assert_evals_to!(
indoc!(
r#"
{ a: 0x1, b: 3.14 }
"#
),
(0x1, 3.14),
(i64, f64)
);
}
#[test]
fn return_record_float_float() {
assert_evals_to!(
indoc!(
r#"
{ a: 6.28, b: 3.14 }
"#
),
(6.28, 3.14),
(f64, f64)
);
}
#[test]
fn return_record_float_float_float() {
assert_evals_to!(
indoc!(
r#"
{ a: 6.28, b: 3.14, c: 0.1 }
"#
),
(6.28, 3.14, 0.1),
(f64, f64, f64)
);
}
#[test]
fn just_to_be_sure() {
assert_evals_to!(
indoc!(
r#"
{ a: 1, b : 2, c : 3 }
"#
),
[1, 2, 3],
[i64; 3]
);
}
}

View file

@ -8,11 +8,8 @@ pub fn helper_without_uniqueness<'a>(
context: &'a inkwell::context::Context,
) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) {
use crate::helpers::{can_expr, infer_expr, CanExprOut};
use inkwell::types::BasicType;
use inkwell::OptimizationLevel;
use roc_gen::llvm::build::Scope;
use roc_gen::llvm::build::{build_proc, build_proc_header};
use roc_gen::llvm::convert::basic_type_from_layout;
use roc_mono::layout::Layout;
let target = target_lexicon::Triple::host();
@ -66,7 +63,7 @@ pub fn helper_without_uniqueness<'a>(
roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
// Compute main_fn_type before moving subs to Env
let layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
let return_layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
panic!(
"Code gen error in NON-OPTIMIZED test: could not convert to layout. Err was {:?}",
err
@ -76,10 +73,6 @@ pub fn helper_without_uniqueness<'a>(
.create_jit_execution_engine(OptimizationLevel::None)
.expect("Error creating JIT execution engine for test");
let main_fn_type =
basic_type_from_layout(&arena, context, &layout, ptr_bytes).fn_type(&[], false);
let main_fn_name = "$Test.main";
// Compile and add all the Procs before adding main
let mut env = roc_gen::llvm::build::Env {
arena: &arena,
@ -163,25 +156,8 @@ pub fn helper_without_uniqueness<'a>(
}
}
// Add main to the module.
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
let cc = roc_gen::llvm::build::FAST_CALL_CONV;
main_fn.set_call_conventions(cc);
// Add main's body
let basic_block = context.append_basic_block(main_fn, "entry");
builder.position_at_end(basic_block);
// builds the function body (return statement included)
roc_gen::llvm::build::build_exp_stmt(
&env,
&mut layout_ids,
&mut Scope::default(),
main_fn,
&main_body,
);
let (main_fn_name, main_fn) =
roc_gen::llvm::build::make_main_function(&env, &mut layout_ids, &return_layout, &main_body);
// Uncomment this to see the module's un-optimized LLVM instruction output:
// env.module.print_to_stderr();
@ -212,11 +188,8 @@ pub fn helper_with_uniqueness<'a>(
context: &'a inkwell::context::Context,
) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) {
use crate::helpers::{infer_expr, uniq_expr};
use inkwell::types::BasicType;
use inkwell::OptimizationLevel;
use roc_gen::llvm::build::Scope;
use roc_gen::llvm::build::{build_proc, build_proc_header};
use roc_gen::llvm::convert::basic_type_from_layout;
use roc_mono::layout::Layout;
let target = target_lexicon::Triple::host();
@ -258,7 +231,7 @@ pub fn helper_with_uniqueness<'a>(
let (mpm, fpm) = roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
// Compute main_fn_type before moving subs to Env
let layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
let return_layout = Layout::new(&arena, content, &subs).unwrap_or_else(|err| {
panic!(
"Code gen error in OPTIMIZED test: could not convert to layout. Err was {:?}",
err
@ -269,11 +242,6 @@ pub fn helper_with_uniqueness<'a>(
.create_jit_execution_engine(OptimizationLevel::None)
.expect("Error creating JIT execution engine for test");
let main_fn_type = basic_type_from_layout(&arena, context, &layout, ptr_bytes)
.fn_type(&[], false)
.clone();
let main_fn_name = "$Test.main";
// Compile and add all the Procs before adding main
let mut env = roc_gen::llvm::build::Env {
arena: &arena,
@ -357,25 +325,8 @@ pub fn helper_with_uniqueness<'a>(
}
}
// Add main to the module.
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
let cc = roc_gen::llvm::build::FAST_CALL_CONV;
main_fn.set_call_conventions(cc);
// Add main's body
let basic_block = context.append_basic_block(main_fn, "entry");
builder.position_at_end(basic_block);
// builds the function body (return statement included)
roc_gen::llvm::build::build_exp_stmt(
&env,
&mut layout_ids,
&mut Scope::default(),
main_fn,
&main_body,
);
let (main_fn_name, main_fn) =
roc_gen::llvm::build::make_main_function(&env, &mut layout_ids, &return_layout, &main_body);
// you're in the version with uniqueness!

View file

@ -36,6 +36,8 @@ pub enum LowLevel {
NumSqrtUnchecked,
NumRound,
NumToFloat,
NumPow,
NumCeiling,
Eq,
NotEq,
And,

View file

@ -640,6 +640,8 @@ define_builtins! {
35 NUM_SQRT: "sqrt"
36 NUM_ROUND: "round"
37 NUM_COMPARE: "compare"
38 NUM_POW: "pow"
39 NUM_CEILING: "ceiling"
}
2 BOOL: "Bool" => {
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias

View file

@ -522,12 +522,11 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
ListWalkRight => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
Eq | NotEq | And | Or | NumAdd | NumSub | NumMul | NumGt | NumGte | NumLt | NumLte
| NumCompare | NumDivUnchecked | NumRemUnchecked => {
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow => {
arena.alloc_slice_copy(&[irrelevant, irrelevant])
}
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumToFloat | Not => {
arena.alloc_slice_copy(&[irrelevant])
}
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumToFloat
| NumCeiling | Not => arena.alloc_slice_copy(&[irrelevant]),
}
}

View file

@ -86,26 +86,33 @@ where
return err_unexpected();
}
}
next_ch if next_ch.is_ascii_digit() => {
has_parsed_digits = true;
'_' => {
// Underscores are ignored.
}
next_ch
if next_ch != '_' &&
// ASCII alphabetic chars (like 'a' and 'f') are allowed in Hex int literals.
// We parse them in any int literal, so we can give a more helpful error
// in canonicalization (e.g. "the character 'f' is not allowed in Octal literals"
// or "the character 'g' is outside the range of valid Hex literals")
!next_ch.is_ascii_alphabetic() =>
{
if has_parsed_digits {
// We hit an invalid number literal character; we're done!
break;
next_ch => {
if next_ch.is_ascii_digit() {
has_parsed_digits = true;
} else {
// No digits! We likely parsed a minus sign that's actually an operator.
if !has_parsed_digits {
// No digits! We likely parsed a minus sign
// that's actually a unary negation operator.
return err_unexpected();
}
// ASCII alphabetic chars (like 'a' and 'f') are
// allowed in Hex int literals. We verify them in
// canonicalization, so if there's a problem, we can
// give a more helpful error (e.g. "the character 'f'
// is not allowed in Octal literals" or
// "the character 'g' is outside the range of valid
// Hex literals") while still allowing the formatter
// to format them normally.
if !next_ch.is_ascii_alphabetic() {
// We hit an invalid number literal character; we're done!
break;
}
}
}
_ => {}
}
// Since we only consume characters in the ASCII range for number literals,

View file

@ -1405,6 +1405,23 @@ mod test_parse {
assert_eq!(Ok(expected), actual);
}
#[test]
fn unary_negation_access() {
// Regression test for https://github.com/rtfeldman/roc/issues/509
let arena = Bump::new();
let var = Var {
module_name: "",
ident: "rec1",
};
let loc_op = Located::new(0, 0, 0, 1, UnaryOp::Negate);
let access = Access(arena.alloc(var), "field");
let loc_access = Located::new(0, 0, 1, 11, access);
let expected = UnaryOp(arena.alloc(loc_access), loc_op);
let actual = parse_with(&arena, "-rec1.field");
assert_eq!(Ok(expected), actual);
}
// CLOSURE
#[test]

View file

@ -2382,6 +2382,30 @@ mod solve_expr {
);
}
#[test]
fn ceiling() {
infer_eq_without_problem(
indoc!(
r#"
Num.ceiling
"#
),
"Float -> Int",
);
}
#[test]
fn pow() {
infer_eq_without_problem(
indoc!(
r#"
Num.pow
"#
),
"Float, Float -> Float",
);
}
#[test]
fn reconstruct_path() {
infer_eq_without_problem(

15
docs/Cargo.toml Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "docs"
version = "0.1.0"
authors = ["Pablo Hirafuji <pablohirafuji@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
handlebars = "3.4.0"
serde = "1.0.0"
serde_json = "1.0.39"
serde_derive = "1.0.75"
fs_extra = "1.2.0"
pulldown-cmark = { version = "0.8", default-features = false }

250
docs/src/main.rs Normal file
View file

@ -0,0 +1,250 @@
extern crate fs_extra;
extern crate handlebars;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate pulldown_cmark;
extern crate serde_json;
use std::error::Error;
use std::fs;
#[derive(Serialize)]
pub struct Package {
name: String,
version: String,
docs: String,
modules: Vec<Module>,
}
#[derive(Serialize, Clone)]
pub struct Module {
name: String,
docs: String,
entries: Vec<ModuleEntry>,
}
#[derive(Serialize, Clone)]
pub struct ModuleEntry {
name: String,
docs: String,
}
#[derive(Serialize)]
pub struct Template {
package_name: String,
package_version: String,
module_name: String,
module_docs: String,
module_entries: Vec<ModuleEntry>,
module_links: Vec<TemplateLink>,
}
#[derive(Serialize)]
pub struct TemplateLink {
name: String,
href: String,
classes: String,
entries: Vec<TemplateLinkEntry>,
}
#[derive(Serialize)]
pub struct TemplateLinkEntry {
name: String,
}
fn main() -> Result<(), Box<dyn Error>> {
let package = Package {
name: "roc/builtins".to_string(),
version: "1.0.0".to_string(),
docs: "Package introduction or README.".to_string(),
modules: vec![
Module {
name: "Str".to_string(),
docs: "Module introduction".to_string(),
entries: vec![
ModuleEntry {
name: "Str".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "isEmpty".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "append".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "prepend".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "concat".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "join".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "split".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "countGraphemes".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "foldGraphemes".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
],
},
Module {
name: "Bool".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![
ModuleEntry {
name: "isEq".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "isNeq".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
],
},
Module {
name: "Num".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![
ModuleEntry {
name: "add".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "sub".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "mul".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
],
},
Module {
name: "List".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![],
},
Module {
name: "Set".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![],
},
Module {
name: "Map".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![],
},
Module {
name: "Result".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![],
},
],
};
// Make sure the directories exists
fs::create_dir_all(format!("./build/{}/{}", package.name, package.version))?;
// Register handlebar template
let mut handlebars = handlebars::Handlebars::new();
assert!(handlebars
.register_template_file("page", "./src/templates/page.hbs")
.is_ok());
let markdown_options = pulldown_cmark::Options::empty();
// Write each package's module docs
for module in &package.modules {
// Convert module docs from markdown to html
let docs_parser = pulldown_cmark::Parser::new_ext(&module.docs, markdown_options);
let mut docs_html: String = String::with_capacity(module.docs.len() * 3 / 2);
pulldown_cmark::html::push_html(&mut docs_html, docs_parser);
let template = Template {
package_name: package.name.clone(),
package_version: package.version.clone(),
module_name: module.name.clone(),
module_docs: docs_html,
module_entries: module
.entries
.clone()
.into_iter()
.map(|entry| {
// Convert entry docs from markdown to html
let entry_docs_parser =
pulldown_cmark::Parser::new_ext(&entry.docs, markdown_options);
let mut entry_docs_html: String =
String::with_capacity(entry.docs.len() * 3 / 2);
pulldown_cmark::html::push_html(&mut entry_docs_html, entry_docs_parser);
ModuleEntry {
name: entry.name.clone(),
docs: entry_docs_html,
}
})
.collect(),
module_links: package
.modules
.clone()
.into_iter()
.map(|module_link| TemplateLink {
name: module_link.name.clone(),
href: format!("./{}.html", module_link.name),
classes: "".to_string(),
entries: module_link
.entries
.into_iter()
.map(|entry| TemplateLinkEntry { name: entry.name })
.collect(),
})
.collect(),
};
let handlebars_data = handlebars::to_json(&template);
let mut output_file = fs::File::create(format!(
"./build/{}/{}/{}.html",
package.name, package.version, module.name
))?;
handlebars.render_to_write("page", &handlebars_data, &mut output_file)?;
}
// Copy /static folder content to /build
let copy_options = fs_extra::dir::CopyOptions {
overwrite: true,
skip_exist: false,
buffer_size: 64000, //64kb
copy_inside: false,
content_only: true,
depth: 0,
};
fs_extra::dir::copy("./src/static/", "./build", &copy_options)?;
println!("Docs generated at /build");
Ok(())
}

View file

@ -0,0 +1,4 @@
<svg viewBox="0 0 52 53" xmlns="http://www.w3.org/2000/svg">
<style>polygon {fill: #5c0bff;}@media (prefers-color-scheme: dark) {polygon {fill: #7733ff;}} </style>
<polygon points="0,0 23.8834,3.21052 37.2438,19.0101 45.9665,16.6324 50.5,22 45,22 44.0315,26.3689 26.4673,39.3424 27.4527,45.2132 17.655,53 23.6751,22.7086"/>
</svg>

After

Width:  |  Height:  |  Size: 335 B

44
docs/src/static/search.js Normal file
View file

@ -0,0 +1,44 @@
(() => {
let sidebar = document.getElementById("sidebar-nav");
let searchBox = document.getElementById("module-search");
function search() {
let text = searchBox.value.toLowerCase(); // Search is case-insensitive.
if (text === "") {
// Un-hide everything
sidebar.querySelectorAll(".sidebar-entry a").forEach((entry) => entry.classList.remove("hidden"));
// Re-hide all the sub-entries except for those of the current module
let currentModuleName = document.querySelector('.module-name').textContent;
sidebar.querySelectorAll(".sidebar-entry").forEach((entry) => {
let entryName = entry.querySelector('.sidebar-module-link').textContent;
if (currentModuleName === entryName) return;
entry.querySelectorAll(".sidebar-sub-entries a").forEach((subEntry) => subEntry.classList.add("hidden"));
})
} else {
// First, show/hide all the sub-entries within each module (top-level functions etc.)
sidebar.querySelectorAll(".sidebar-sub-entries a").forEach((entry) => {
if (entry.textContent.toLowerCase().includes(text)) {
entry.classList.remove("hidden");
} else {
entry.classList.add("hidden");
}
});
// Then, show/hide modules based on whether they match, or any of their sub-entries matched
sidebar.querySelectorAll(".sidebar-module-link").forEach((entry) => {
if (entry.textContent.toLowerCase().includes(text) || entry.parentNode.querySelectorAll(".sidebar-sub-entries a:not(.hidden)").length > 0) {
entry.classList.remove("hidden");
} else {
entry.classList.add("hidden");
}
});
}
}
searchBox.addEventListener("input", search);
search();
})();

489
docs/src/static/styles.css Normal file
View file

@ -0,0 +1,489 @@
:root {
--link-color: #612bde;
--code-link-color: #5721d4;
--text-color: #333333;
--code-color: #222222;
--code-bg-color: #eeeeee;
--body-bg-color: #fdfdfd;
--border-color: #e9e9e9;
--faded-color: #4c4c4c;
--monospace-font;
--font-sans: -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif;
--font-mono: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;
--top-header-height: 67px;
--sidebar-width: 280px;
--top-bar-bg: #8257e5;
--top-bar-fg: #ffffff;
--nav-link-hover-color: #000000;
}
a {
color: #972395;
}
.logo {
padding: 2px 8px;
}
.logo svg {
height: 48px;
width: 48px;
fill: var(--top-bar-fg);
}
.logo:hover {
text-decoration: none;
}
.logo svg:hover {
fill: var(--nav-link-hover-color);
}
.pkg-full-name {
color: var(--text-color);
display: flex;
align-items: center;
font-size: 32px;
margin: 0 8px;
font-weight: normal;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height: 100%;
}
.pkg-full-name a {
padding-top: 12px;
padding-bottom: 16px;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.pkg-and-logo {
min-width: 0;
/* necessary for text-overflow: ellipsis to work in descendants */
display: flex;
align-items: center;
height: 100%;
background-color: var(--top-bar-bg);
}
.pkg-and-logo a,
.pkg-and-logo a:visited {
color: var(--top-bar-fg);
}
.pkg-and-logo a:hover {
color: var(--nav-link-hover-color);
text-decoration: none;
}
.main-container {
min-width: 0;
/* necessary for text-overflow: ellipsis to work in descendants */
}
.search-button {
flex-shrink: 0;
/* always shrink the package name before these; they have a relatively constrained length */
padding: 12px 18px;
margin-right: 42px;
display: none;
/* only show this in the mobile view */
}
.version {
padding: 18px 10px;
min-width: 48px;
margin-right: 8px;
}
body {
display: grid;
grid-template-columns: [before-sidebar] 1fr [sidebar] var(--sidebar-width) [main-content] fit-content(calc(1280px - var(--sidebar-width))) [end] 1fr;
grid-template-rows: [top-header] var(--top-header-height) [above-footer] auto [footer] auto;
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: var(--font-sans);
color: var(--text-color);
background-color: var(--body-bg-color);
}
main {
grid-column-start: main-content;
grid-column-end: main-content;
grid-row-start: above-footer;
grid-row-end: above-footer;
box-sizing: border-box;
position: relative;
font-size: 18px;
line-height: 1.85em;
margin-top: 2px;
padding: 48px;
}
#sidebar-nav {
grid-column-start: sidebar;
grid-column-end: sidebar;
grid-row-start: above-footer;
grid-row-end: above-footer;
position: relative;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding-left: 56px;
padding-top: 6px;
width: 100%;
}
.top-header-extension {
grid-column-start: before-sidebar;
grid-column-end: sidebar;
grid-row-start: top-header;
grid-row-end: top-header;
background-color: var(--top-bar-bg);
}
.top-header {
grid-column-start: sidebar;
grid-column-end: end;
grid-row-start: top-header;
grid-row-end: top-header;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: nowrap;
flex-grow: 1;
box-sizing: border-box;
font-family: var(--font-sans);
font-size: 24px;
height: 100%;
min-width: 0;
/* necessary for text-overflow: ellipsis to work in descendants */
}
.top-header-triangle {
/* This used to be a clip-path, but Firefox on Android (at least as of early 2020)
* rendered the page extremely slowly in that version. With this approach it's super fast.
*/
width: 0;
height: 0;
border-style: solid;
border-width: var(--top-header-height) 0 0 48px;
border-color: transparent transparent transparent var(--top-bar-bg);
}
p {
overflow-wrap: break-word;
margin: 24px 0;
}
footer {
grid-column-start: main-content;
grid-column-end: main-content;
grid-row-start: footer;
grid-row-end: footer;
max-width: var(--main-content-max-width);
font-size: 14px;
box-sizing: border-box;
padding: 16px;
}
footer p {
display: inline-block;
margin-top: 0;
margin-bottom: 8px;
}
.content {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.sidebar-entry ul {
list-style-type: none;
margin: 0;
}
.sidebar-entry a {
box-sizing: border-box;
min-height: 48px;
min-width: 48px;
padding: 12px 16px;
font-family: var(--font-mono);
}
.sidebar-sub-entries a {
display: block;
line-height: 24px;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 36px;
}
.module-name {
font-size: 56px;
line-height: 1em;
font-family: var(--font-mono);
font-weight: bold;
margin-top: 18px;
margin-bottom: 48px;
}
.module-name a,
.module-name a:visited {
color: inherit;
}
.sidebar-module-link {
box-sizing: border-box;
font-size: 18px;
line-height: 24px;
font-family: var(--font-mono);
font-weight: bold;
display: block;
width: 100%;
padding: 8px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
a,
a:visited {
color: var(--link-color);
}
h3 {
font-size: 32px;
margin: 48px 0 24px 0;
}
h4 {
font-size: 24px;
}
.type-def {
font-size: 24px;
color: var(--link-color);
}
.code-snippet {
padding: 12px 16px;
display: block;
box-sizing: border-box;
font-family: var(--font-mono);
background-color: var(--code-bg-color);
}
code {
font-family: var(--font-mono);
color: var(--code-color);
background-color: var(--code-bg-color);
padding: 2px 8px;
display: inline-block;
}
code a {
color: var(--code-link-color);
}
code a:visited {
color: var(--code-link-color);
}
pre {
margin: 36px 0;
padding: 8px;
box-sizing: border-box;
background-color: var(--code-bg-color);
overflow-x: auto;
}
pre code {
padding: 6px 8px;
}
.hidden {
/* Use !important to win all specificity fights. */
display: none !important;
}
#module-search:placeholder-shown {
padding: 0;
opacity: 0;
height: 0;
}
#module-search,
#module-search:focus {
opacity: 1;
padding: 12px 16px;
height: 48px;
}
/* Show the "Search" label link when the text input has a placeholder */
#module-search:placeholder-shown+#search-link {
display: flex;
}
/* Hide the "Search" label link when the text input has focus */
#module-search:focus+#search-link {
display: none;
}
#module-search {
display: block;
box-sizing: border-box;
background-color: var(--code-bg-color);
width: 100%;
box-sizing: border-box;
font-size: 18px;
line-height: 18px;
margin-top: 6px;
border: none;
color: var(--faded-color);
background-color: var(--code-bg-color);
font-family: var(--font-serif);
}
#module-search::placeholder {
color: var(--faded-color);
opacity: 1;
}
#search-link {
box-sizing: border-box;
display: none;
align-items: center;
font-size: 18px;
line-height: 18px;
padding: 12px 16px;
height: 48px;
cursor: pointer;
color: var(--link-color);
}
#search-link:hover {
text-decoration: underline;
}
@media (prefers-color-scheme: dark) {
:root {
--body-bg-color: #303030;
--code-bg-color: #393939;
--border-color: #555555;
--code-color: #eeeeee;
--text-color: #cccccc;
--logo-solid: #777777;
--faded-color: #bbbbbb;
--link-color: #c5a8ff;
--code-link-color: #b894ff;
--top-bar-bg: #6845b9;
--top-bar-fg: #eeeeee;
}
html {
scrollbar-color: #444444 #2f2f2f;
}
}
@media only screen and (max-device-width: 480px) {
.search-button {
display: block;
/* This is only visible in mobile. */
}
.top-header {
width: auto;
}
.pkg-full-name {
margin-left: 8px;
margin-right: 12px;
font-size: 24px;
padding-bottom: 14px;
}
.pkg-full-name a {
vertical-align: middle;
padding: 18px 0;
}
.logo {
padding-left: 2px;
width: 50px;
height: 54px;
}
.version {
margin: 0;
font-weight: normal;
font-size: 18px;
padding-bottom: 16px;
}
.module-name {
font-size: 36px;
margin-top: 8px;
margin-bottom: 8px;
max-width: calc(100% - 18px);
overflow: hidden;
text-overflow: ellipsis;
}
main {
padding: 18px;
font-size: 16px;
}
.container {
margin: 0;
min-width: 320px;
max-width: 100%;
}
.content {
flex-direction: column;
}
.sidebar {
margin-top: 0;
padding-left: 0;
width: auto;
}
#sidebar-heading {
font-size: 24px;
margin: 16px;
}
h3 {
font-size: 18px;
margin: 0;
padding: 0;
}
h4 {
font-size: 16px;
}
.top-header {
justify-content: space-between;
}
.content {
/* Display the sidebar below <main> without affecting tab index */
flex-direction: column-reverse;
}
}

View file

@ -0,0 +1,65 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>The Roc Programming Language</title>
<meta name="description" content="A language for building fast, reliable software.">
<meta name="viewport" content="width=device-width">
<link rel="icon" href="/favicon.svg">
<script type="text/javascript" src="/search.js" defer></script>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<nav id="sidebar-nav">
<input id="module-search" aria-labelledby="search-link" type="text" placeholder="Search" />
<label for="module-search" id="search-link">Search</label>
<div class="module-links">
{{#each module_links as |link| ~}}
<div class="sidebar-entry">
<a class="sidebar-module-link" href="{{link.href}}">{{link.name}}</a>
<div class="sidebar-sub-entries">
{{#each link.entries as |entry| ~}}
<a class="{{link.classes}}" href="{{link.href}}#{{entry.name}}">{{entry.name}}</a></li>
{{/each~}}
</div>
</div>
{{/each~}}
</div>
</nav>
<div class="top-header-extension">
<!-- if the window gets big, this extends the purple bar on the top header to the left edge of the window -->
</div>
<header class="top-header">
<nav class="pkg-and-logo">
<a class="logo" href="/" aria-labelledby="logo-link">
<svg viewBox="0 -6 51 58" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="logo-link" role="img">
<title id="logo-link">Return to Roc packages</title>
<polygon role="presentation" points="0,0 23.8834,3.21052 37.2438,19.0101 45.9665,16.6324 50.5,22 45,22 44.0315,26.3689 26.4673,39.3424 27.4527,45.2132 17.655,53 23.6751,22.7086" />
</svg>
</a>
<h1 class="pkg-full-name">
<a href="/{{package_name}}">{{package_name}}</a>
</h1>
<a class="version" href="/{{package_name}}/{{package_version}}">{{package_version}}</a>
</nav>
<div class="top-header-triangle">
<!-- if the window gets big, this extends the purple bar on the top header to the left edge of the window -->
</div>
</header>
<main>
<h2 class="module-name"><a href="#">{{module_name}}</a></h2>
{{{module_docs}}}
{{#each module_entries as |entry| ~}}
<h3 id="{{entry.name}}"><a href="#{{entry.name}}">{{entry.name}}</a></h3>
{{{entry.docs}}}
{{/each~}}
</main>
<footer>
<p>Made by people who like to make nice things.</p>
<p>© 2020-present</p>
</footer>
</body>
</html>