mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge branch 'trunk' into valgrind
This commit is contained in:
commit
f73a6c0da4
50 changed files with 1425 additions and 579 deletions
|
@ -1,9 +1,17 @@
|
|||
# Building the Roc compiler from source
|
||||
|
||||
|
||||
## Installing LLVM and libc++abi
|
||||
## Installing LLVM, libunwind, and libc++abi
|
||||
|
||||
To build the compiler, you need both `libc++abi` and a particular version of LLVM installed on your system. Some systems may already have `libc++abi` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `apt-get install libc++abi-dev`.)
|
||||
To build the compiler, you need these installed:
|
||||
|
||||
* `libunwind` (macOS should already have this one installed)
|
||||
* `libc++-dev`
|
||||
* a particular version of LLVM
|
||||
|
||||
Some systems may already have `libc++-dev` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `sudo apt-get install libc++-dev`.) macOS systems
|
||||
should already have `libunwind`, but other systems will need to install it
|
||||
(e.g. with `sudo apt-get install libunwind-dev`).
|
||||
|
||||
To see which version of LLVM you need, take a look at `Cargo.toml`, in particular the `branch` section of the `inkwell` dependency. It should have something like `llvmX-Y` where X and Y are the major and minor revisions of LLVM you need.
|
||||
|
||||
|
|
|
@ -59,4 +59,4 @@ esac
|
|||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||
add-apt-repository "${REPO_NAME}"
|
||||
apt-get update
|
||||
apt-get install -y clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION
|
||||
apt-get install -y clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION libc++abi-dev libunwind-dev
|
||||
|
|
112
cli/src/build.rs
Normal file
112
cli/src/build.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
use bumpalo::Bump;
|
||||
use roc_build::{link::link, program};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_gen::llvm::build::OptLevel;
|
||||
use roc_load::file::LoadingProblem;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
fn report_timing(buf: &mut String, label: &str, duration: Duration) {
|
||||
buf.push_str(&format!(
|
||||
" {:.3} ms {}\n",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
label,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn build_file(
|
||||
target: &Triple,
|
||||
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_file_name("roc_app.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
|
||||
);
|
||||
|
||||
program::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();
|
||||
|
||||
// Step 2: link the precompiled host and compiled app
|
||||
let host_input_path = cwd.join("platform").join("host.o");
|
||||
let binary_path = cwd.join("app"); // TODO should be app.exe on Windows
|
||||
|
||||
// TODO try to move as much of this linking as possible to the precompiled
|
||||
// host, to minimize the amount of host-application linking required.
|
||||
let cmd_result = // TODO use lld
|
||||
link(
|
||||
target,
|
||||
binary_path.as_path(),
|
||||
host_input_path.as_path(),
|
||||
dest_filename.as_path(),
|
||||
)
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle `rustc` failing to spawn.");
|
||||
})?
|
||||
.wait()
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle error after `rustc` spawned");
|
||||
});
|
||||
|
||||
// Clean up the leftover .o file from the Roc, if possible.
|
||||
// (If cleaning it up fails, that's fine. No need to take action.)
|
||||
// TODO compile the dest_filename to a tmpdir, as an extra precaution.
|
||||
let _ = fs::remove_file(dest_filename);
|
||||
|
||||
// If the cmd errored out, return the Err.
|
||||
cmd_result?;
|
||||
|
||||
Ok(binary_path)
|
||||
}
|
138
cli/src/lib.rs
138
cli/src/lib.rs
|
@ -1,20 +1,16 @@
|
|||
#[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::io;
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::process::Command;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
pub mod build;
|
||||
pub mod repl;
|
||||
|
||||
pub static FLAG_OPTIMIZE: &str = "optimize";
|
||||
|
@ -66,7 +62,7 @@ pub fn build_app<'a>() -> App<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn build(matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
|
||||
pub fn build(target: &Triple, 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
|
||||
|
@ -78,7 +74,7 @@ pub fn build(matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
|
|||
|
||||
// Spawn the root task
|
||||
let path = path.canonicalize().unwrap_or_else(|err| {
|
||||
use ErrorKind::*;
|
||||
use io::ErrorKind::*;
|
||||
|
||||
match err.kind() {
|
||||
NotFound => {
|
||||
|
@ -95,8 +91,8 @@ pub fn build(matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
|
|||
}
|
||||
});
|
||||
|
||||
let binary_path =
|
||||
build_file(src_dir, path, opt_level).expect("TODO gracefully handle build_file failing");
|
||||
let binary_path = build::build_file(target, src_dir, path, opt_level)
|
||||
.expect("TODO gracefully handle build_file failing");
|
||||
|
||||
if run_after_build {
|
||||
// Run the compiled app
|
||||
|
@ -109,123 +105,3 @@ pub fn build(matches: &ArgMatches, run_after_build: bool) -> io::Result<()> {
|
|||
|
||||
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(),
|
||||
// ensure we don't make a position-independent executable
|
||||
"-C",
|
||||
"link-arg=-no-pie",
|
||||
// explicitly link in the c++ stdlib, for exceptions
|
||||
"-C",
|
||||
"link-arg=-lc++",
|
||||
])
|
||||
.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)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
use roc_cli::{build, build_app, repl, DIRECTORY_OR_FILES};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let matches = build_app().get_matches();
|
||||
|
||||
match matches.subcommand_name() {
|
||||
None => roc_editor::launch(&[]),
|
||||
Some("build") => build(matches.subcommand_matches("build").unwrap(), false),
|
||||
Some("run") => build(matches.subcommand_matches("run").unwrap(), true),
|
||||
Some("build") => build(
|
||||
&Triple::host(),
|
||||
matches.subcommand_matches("build").unwrap(),
|
||||
false,
|
||||
),
|
||||
Some("run") => build(
|
||||
&Triple::host(),
|
||||
matches.subcommand_matches("run").unwrap(),
|
||||
true,
|
||||
),
|
||||
Some("repl") => repl::main(),
|
||||
Some("edit") => {
|
||||
match matches
|
||||
|
|
|
@ -10,4 +10,6 @@
|
|||
// and encouraging shortcuts here creates bad incentives. I would rather temporarily
|
||||
// re-enable this when working on performance optimizations than have it block PRs.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod link;
|
||||
pub mod program;
|
||||
pub mod target;
|
||||
|
|
192
compiler/build/src/link.rs
Normal file
192
compiler/build/src/link.rs
Normal file
|
@ -0,0 +1,192 @@
|
|||
use crate::target::arch_str;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::process::{Child, Command};
|
||||
use target_lexicon::{Architecture, OperatingSystem, Triple};
|
||||
|
||||
pub fn link(
|
||||
target: &Triple,
|
||||
binary_path: &Path,
|
||||
host_input_path: &Path,
|
||||
dest_filename: &Path,
|
||||
) -> io::Result<Child> {
|
||||
// TODO we should no longer need to do this once we have platforms on
|
||||
// a package repository, as we can then get precompiled hosts from there.
|
||||
rebuild_host(host_input_path);
|
||||
|
||||
match target {
|
||||
Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
operating_system: OperatingSystem::Linux,
|
||||
..
|
||||
} => link_linux(target, binary_path, host_input_path, dest_filename),
|
||||
Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
operating_system: OperatingSystem::Darwin,
|
||||
..
|
||||
} => link_macos(target, binary_path, host_input_path, dest_filename),
|
||||
_ => panic!("TODO gracefully handle unsupported target: {:?}", target),
|
||||
}
|
||||
}
|
||||
|
||||
fn rebuild_host(host_input_path: &Path) {
|
||||
let c_host_src = host_input_path.with_file_name("host.c");
|
||||
let c_host_dest = host_input_path.with_file_name("c_host.o");
|
||||
let rust_host_src = host_input_path.with_file_name("host.rs");
|
||||
let rust_host_dest = host_input_path.with_file_name("rust_host.o");
|
||||
let cargo_host_src = host_input_path.with_file_name("Cargo.toml");
|
||||
let host_dest = host_input_path.with_file_name("host.o");
|
||||
|
||||
// Compile host.c
|
||||
Command::new("clang")
|
||||
.env_clear()
|
||||
.args(&[
|
||||
"-c",
|
||||
c_host_src.to_str().unwrap(),
|
||||
"-o",
|
||||
c_host_dest.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if cargo_host_src.exists() {
|
||||
// Compile and link Cargo.toml, if it exists
|
||||
let cargo_dir = host_input_path.parent().unwrap();
|
||||
let libhost_dir = cargo_dir.join("target").join("release");
|
||||
|
||||
Command::new("cargo")
|
||||
.args(&["build", "--release"])
|
||||
.current_dir(cargo_dir)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
Command::new("ld")
|
||||
.env_clear()
|
||||
.args(&[
|
||||
"-r",
|
||||
"-L",
|
||||
libhost_dir.to_str().unwrap(),
|
||||
c_host_dest.to_str().unwrap(),
|
||||
"-lhost",
|
||||
"-o",
|
||||
host_dest.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
} else if rust_host_src.exists() {
|
||||
// Compile and link host.rs, if it exists
|
||||
Command::new("rustc")
|
||||
.args(&[
|
||||
rust_host_src.to_str().unwrap(),
|
||||
"-o",
|
||||
rust_host_dest.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
Command::new("ld")
|
||||
.env_clear()
|
||||
.args(&[
|
||||
"-r",
|
||||
c_host_dest.to_str().unwrap(),
|
||||
rust_host_dest.to_str().unwrap(),
|
||||
"-o",
|
||||
host_dest.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// Clean up rust_host.o
|
||||
Command::new("rm")
|
||||
.env_clear()
|
||||
.args(&[
|
||||
"-f",
|
||||
rust_host_dest.to_str().unwrap(),
|
||||
c_host_dest.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
} else {
|
||||
// Clean up rust_host.o
|
||||
Command::new("mv")
|
||||
.env_clear()
|
||||
.args(&[c_host_dest, host_dest])
|
||||
.output()
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn link_linux(
|
||||
target: &Triple,
|
||||
binary_path: &Path,
|
||||
host_input_path: &Path,
|
||||
dest_filename: &Path,
|
||||
) -> io::Result<Child> {
|
||||
// NOTE: order of arguments to `ld` matters here!
|
||||
// The `-l` flags should go after the `.o` arguments
|
||||
Command::new("ld")
|
||||
// Don't allow LD_ env vars to affect this
|
||||
.env_clear()
|
||||
.args(&[
|
||||
"-arch",
|
||||
arch_str(target),
|
||||
"/usr/lib/x86_64-linux-gnu/crti.o",
|
||||
"/usr/lib/x86_64-linux-gnu/crtn.o",
|
||||
"/usr/lib/x86_64-linux-gnu/Scrt1.o",
|
||||
"-dynamic-linker",
|
||||
"/lib64/ld-linux-x86-64.so.2",
|
||||
// Inputs
|
||||
host_input_path.to_str().unwrap(), // host.o
|
||||
dest_filename.to_str().unwrap(), // app.o
|
||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496365925
|
||||
// for discussion and further references
|
||||
"-lc",
|
||||
"-lm",
|
||||
"-lpthread",
|
||||
"-ldl",
|
||||
"-lrt",
|
||||
"-lutil",
|
||||
"-lc_nonshared",
|
||||
"-lc++",
|
||||
"-lunwind",
|
||||
// "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
||||
// Output
|
||||
"-o",
|
||||
binary_path.to_str().unwrap(), // app
|
||||
])
|
||||
.spawn()
|
||||
}
|
||||
|
||||
fn link_macos(
|
||||
target: &Triple,
|
||||
binary_path: &Path,
|
||||
host_input_path: &Path,
|
||||
dest_filename: &Path,
|
||||
) -> io::Result<Child> {
|
||||
// NOTE: order of arguments to `ld` matters here!
|
||||
// The `-l` flags should go after the `.o` arguments
|
||||
Command::new("ld")
|
||||
// Don't allow LD_ env vars to affect this
|
||||
.env_clear()
|
||||
.args(&[
|
||||
"-arch",
|
||||
target.architecture.to_string().as_str(),
|
||||
// Inputs
|
||||
host_input_path.to_str().unwrap(), // host.o
|
||||
dest_filename.to_str().unwrap(), // roc_app.o
|
||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274
|
||||
// for discussion and further references
|
||||
"-lSystem",
|
||||
"-lresolv",
|
||||
"-lpthread",
|
||||
// "-lrt", // TODO shouldn't we need this?
|
||||
// "-lc_nonshared", // TODO shouldn't we need this?
|
||||
// "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
||||
// "-lunwind", // TODO will eventually need this, see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
||||
"-lc++", // TODO shouldn't we need this?
|
||||
// Output
|
||||
"-o",
|
||||
binary_path.to_str().unwrap(), // app
|
||||
])
|
||||
.spawn()
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
use crate::target;
|
||||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::targets::{
|
||||
CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetTriple,
|
||||
};
|
||||
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
||||
use inkwell::OptimizationLevel;
|
||||
use roc_collections::all::default_hasher;
|
||||
use roc_gen::layout_id::LayoutIds;
|
||||
|
@ -12,7 +11,7 @@ use roc_mono::ir::{Env, PartialProc, Procs};
|
|||
use roc_mono::layout::{Layout, LayoutCache};
|
||||
use std::collections::HashSet;
|
||||
use std::path::{Path, PathBuf};
|
||||
use target_lexicon::{Architecture, OperatingSystem, Triple, Vendor};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
// TODO how should imported modules factor into this? What if those use builtins too?
|
||||
// TODO this should probably use more helper functions
|
||||
|
@ -105,11 +104,18 @@ pub fn gen(
|
|||
Declare(def) | Builtin(def) => match def.loc_pattern.value {
|
||||
Identifier(symbol) => {
|
||||
match def.loc_expr.value {
|
||||
Closure(annotation, _, recursivity, loc_args, boxed_body) => {
|
||||
Closure {
|
||||
function_type: annotation,
|
||||
return_type: ret_var,
|
||||
recursive: recursivity,
|
||||
arguments: loc_args,
|
||||
loc_body: boxed_body,
|
||||
..
|
||||
} => {
|
||||
let is_tail_recursive =
|
||||
matches!(recursivity, roc_can::expr::Recursive::TailRecursive);
|
||||
|
||||
let (loc_body, ret_var) = *boxed_body;
|
||||
let loc_body = *boxed_body;
|
||||
|
||||
// If this is an exposed symbol, we need to
|
||||
// register it as such. Otherwise, since it
|
||||
|
@ -288,80 +294,10 @@ pub fn gen(
|
|||
|
||||
// Emit the .o file
|
||||
|
||||
// NOTE: arch_str is *not* the same as the beginning of the magic target triple
|
||||
// string! For example, if it's "x86-64" here, the magic target triple string
|
||||
// will begin with "x86_64" (with an underscore) instead.
|
||||
let arch_str = match target.architecture {
|
||||
Architecture::X86_64 => {
|
||||
Target::initialize_x86(&InitializationConfig::default());
|
||||
|
||||
"x86-64"
|
||||
}
|
||||
Architecture::Arm(_) if cfg!(feature = "target-arm") => {
|
||||
// NOTE: why not enable arm and wasm by default?
|
||||
//
|
||||
// We had some trouble getting them to link properly. This may be resolved in the
|
||||
// future, or maybe it was just some weird configuration on one machine.
|
||||
Target::initialize_arm(&InitializationConfig::default());
|
||||
|
||||
"arm"
|
||||
}
|
||||
Architecture::Wasm32 if cfg!(feature = "target-webassembly") => {
|
||||
Target::initialize_webassembly(&InitializationConfig::default());
|
||||
|
||||
"wasm32"
|
||||
}
|
||||
_ => panic!(
|
||||
"TODO gracefully handle unsupported target architecture: {:?}",
|
||||
target.architecture
|
||||
),
|
||||
};
|
||||
|
||||
let opt = OptimizationLevel::Aggressive;
|
||||
let reloc = RelocMode::Default;
|
||||
let model = CodeModel::Default;
|
||||
|
||||
// Best guide I've found on how to determine these magic strings:
|
||||
//
|
||||
// https://stackoverflow.com/questions/15036909/clang-how-to-list-supported-target-architectures
|
||||
let target_triple_str = match target {
|
||||
Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
vendor: Vendor::Unknown,
|
||||
operating_system: OperatingSystem::Linux,
|
||||
..
|
||||
} => "x86_64-unknown-linux-gnu",
|
||||
Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
vendor: Vendor::Pc,
|
||||
operating_system: OperatingSystem::Linux,
|
||||
..
|
||||
} => "x86_64-pc-linux-gnu",
|
||||
Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
vendor: Vendor::Unknown,
|
||||
operating_system: OperatingSystem::Darwin,
|
||||
..
|
||||
} => "x86_64-unknown-darwin10",
|
||||
Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
vendor: Vendor::Apple,
|
||||
operating_system: OperatingSystem::Darwin,
|
||||
..
|
||||
} => "x86_64-apple-darwin10",
|
||||
_ => panic!("TODO gracefully handle unsupported target: {:?}", target),
|
||||
};
|
||||
let target_machine = Target::from_name(arch_str)
|
||||
.unwrap()
|
||||
.create_target_machine(
|
||||
&TargetTriple::create(target_triple_str),
|
||||
arch_str,
|
||||
"+avx2", // TODO this string was used uncritically from an example, and should be reexamined
|
||||
opt,
|
||||
reloc,
|
||||
model,
|
||||
)
|
||||
.unwrap();
|
||||
let target_machine = target::target_machine(&target, opt, reloc, model).unwrap();
|
||||
|
||||
target_machine
|
||||
.write_to_file(&env.module, FileType::Object, &dest_filename)
|
||||
|
|
76
compiler/build/src/target.rs
Normal file
76
compiler/build/src/target.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
use inkwell::targets::{
|
||||
CodeModel, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple,
|
||||
};
|
||||
use inkwell::OptimizationLevel;
|
||||
use target_lexicon::{Architecture, OperatingSystem, Triple};
|
||||
|
||||
pub fn target_triple_str(target: &Triple) -> &'static str {
|
||||
// Best guide I've found on how to determine these magic strings:
|
||||
//
|
||||
// https://stackoverflow.com/questions/15036909/clang-how-to-list-supported-target-architectures
|
||||
match target {
|
||||
Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
operating_system: OperatingSystem::Linux,
|
||||
..
|
||||
} => "x86_64-unknown-linux-gnu",
|
||||
Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
operating_system: OperatingSystem::Darwin,
|
||||
..
|
||||
} => "x86_64-unknown-darwin10",
|
||||
_ => panic!("TODO gracefully handle unsupported target: {:?}", target),
|
||||
}
|
||||
}
|
||||
|
||||
/// NOTE: arch_str is *not* the same as the beginning of the magic target triple
|
||||
/// string! For example, if it's "x86-64" here, the magic target triple string
|
||||
/// will begin with "x86_64" (with an underscore) instead.
|
||||
pub fn arch_str(target: &Triple) -> &'static str {
|
||||
// Best guide I've found on how to determine these magic strings:
|
||||
//
|
||||
// https://stackoverflow.com/questions/15036909/clang-how-to-list-supported-target-architectures
|
||||
match target.architecture {
|
||||
Architecture::X86_64 => {
|
||||
Target::initialize_x86(&InitializationConfig::default());
|
||||
|
||||
"x86-64"
|
||||
}
|
||||
Architecture::Arm(_) if cfg!(feature = "target-arm") => {
|
||||
// NOTE: why not enable arm and wasm by default?
|
||||
//
|
||||
// We had some trouble getting them to link properly. This may be resolved in the
|
||||
// future, or maybe it was just some weird configuration on one machine.
|
||||
Target::initialize_arm(&InitializationConfig::default());
|
||||
|
||||
"arm"
|
||||
}
|
||||
Architecture::Wasm32 if cfg!(feature = "target-webassembly") => {
|
||||
Target::initialize_webassembly(&InitializationConfig::default());
|
||||
|
||||
"wasm32"
|
||||
}
|
||||
_ => panic!(
|
||||
"TODO gracefully handle unsupported target architecture: {:?}",
|
||||
target.architecture
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn target_machine(
|
||||
target: &Triple,
|
||||
opt: OptimizationLevel,
|
||||
reloc: RelocMode,
|
||||
model: CodeModel,
|
||||
) -> Option<TargetMachine> {
|
||||
let arch = arch_str(target);
|
||||
|
||||
Target::from_name(arch).unwrap().create_target_machine(
|
||||
&TargetTriple::create(target_triple_str(target)),
|
||||
arch,
|
||||
"+avx2", // TODO this string was used uncritically from an example, and should be reexamined
|
||||
opt,
|
||||
reloc,
|
||||
model,
|
||||
)
|
||||
}
|
|
@ -42,6 +42,8 @@ const NUM_BUILTIN_IMPORTS: usize = 7;
|
|||
const TVAR1: VarId = VarId::from_u32(1);
|
||||
const TVAR2: VarId = VarId::from_u32(2);
|
||||
const TVAR3: VarId = VarId::from_u32(3);
|
||||
const TVAR4: VarId = VarId::from_u32(4);
|
||||
const TOP_LEVEL_CLOSURE_VAR: VarId = VarId::from_u32(5);
|
||||
|
||||
pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
||||
let mut aliases = HashMap::with_capacity_and_hasher(NUM_BUILTIN_IMPORTS, default_hasher());
|
||||
|
@ -181,7 +183,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// add or (+) : Num a, Num a -> Num a
|
||||
add_type(
|
||||
Symbol::NUM_ADD,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||
Box::new(num_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -195,7 +197,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
|
||||
add_type(
|
||||
Symbol::NUM_ADD_CHECKED,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||
Box::new(result_type(num_type(flex(TVAR1)), overflow)),
|
||||
),
|
||||
|
@ -204,13 +206,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// addWrap : Int, Int -> Int
|
||||
add_type(
|
||||
Symbol::NUM_ADD_WRAP,
|
||||
SolvedType::Func(vec![int_type(), int_type()], Box::new(int_type())),
|
||||
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
|
||||
);
|
||||
|
||||
// sub or (-) : Num a, Num a -> Num a
|
||||
add_type(
|
||||
Symbol::NUM_SUB,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||
Box::new(num_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -219,7 +221,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// mul or (*) : Num a, Num a -> Num a
|
||||
add_type(
|
||||
Symbol::NUM_MUL,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||
Box::new(num_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -228,31 +230,31 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// abs : Num a -> Num a
|
||||
add_type(
|
||||
Symbol::NUM_ABS,
|
||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(num_type(flex(TVAR1)))),
|
||||
top_level_function(vec![num_type(flex(TVAR1))], Box::new(num_type(flex(TVAR1)))),
|
||||
);
|
||||
|
||||
// neg : Num a -> Num a
|
||||
add_type(
|
||||
Symbol::NUM_NEG,
|
||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(num_type(flex(TVAR1)))),
|
||||
top_level_function(vec![num_type(flex(TVAR1))], Box::new(num_type(flex(TVAR1)))),
|
||||
);
|
||||
|
||||
// isEq or (==) : a, a -> Bool
|
||||
add_type(
|
||||
Symbol::BOOL_EQ,
|
||||
SolvedType::Func(vec![flex(TVAR1), flex(TVAR1)], Box::new(bool_type())),
|
||||
top_level_function(vec![flex(TVAR1), flex(TVAR1)], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// isNeq or (!=) : a, a -> Bool
|
||||
add_type(
|
||||
Symbol::BOOL_NEQ,
|
||||
SolvedType::Func(vec![flex(TVAR1), flex(TVAR1)], Box::new(bool_type())),
|
||||
top_level_function(vec![flex(TVAR1), flex(TVAR1)], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// isLt or (<) : Num a, Num a -> Bool
|
||||
add_type(
|
||||
Symbol::NUM_LT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||
Box::new(bool_type()),
|
||||
),
|
||||
|
@ -261,7 +263,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// isLte or (<=) : Num a, Num a -> Bool
|
||||
add_type(
|
||||
Symbol::NUM_LTE,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||
Box::new(bool_type()),
|
||||
),
|
||||
|
@ -270,7 +272,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// isGt or (>) : Num a, Num a -> Bool
|
||||
add_type(
|
||||
Symbol::NUM_GT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||
Box::new(bool_type()),
|
||||
),
|
||||
|
@ -279,7 +281,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// isGte or (>=) : Num a, Num a -> Bool
|
||||
add_type(
|
||||
Symbol::NUM_GTE,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||
Box::new(bool_type()),
|
||||
),
|
||||
|
@ -288,7 +290,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// compare : Num a, Num a -> [ LT, EQ, GT ]
|
||||
add_type(
|
||||
Symbol::NUM_COMPARE,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||
Box::new(ordering_type()),
|
||||
),
|
||||
|
@ -297,37 +299,37 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// toFloat : Num a -> Float
|
||||
add_type(
|
||||
Symbol::NUM_TO_FLOAT,
|
||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(float_type())),
|
||||
top_level_function(vec![num_type(flex(TVAR1))], Box::new(float_type())),
|
||||
);
|
||||
|
||||
// isNegative : Num a -> Bool
|
||||
add_type(
|
||||
Symbol::NUM_IS_NEGATIVE,
|
||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// isPositive : Num a -> Bool
|
||||
add_type(
|
||||
Symbol::NUM_IS_POSITIVE,
|
||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// isZero : Num a -> Bool
|
||||
add_type(
|
||||
Symbol::NUM_IS_ZERO,
|
||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// isEven : Num a -> Bool
|
||||
add_type(
|
||||
Symbol::NUM_IS_EVEN,
|
||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// isOdd : Num a -> Bool
|
||||
add_type(
|
||||
Symbol::NUM_IS_ODD,
|
||||
SolvedType::Func(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
top_level_function(vec![num_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// maxInt : Int
|
||||
|
@ -344,7 +346,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
|
||||
add_type(
|
||||
Symbol::NUM_DIV_INT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![int_type(), int_type()],
|
||||
Box::new(result_type(int_type(), div_by_zero.clone())),
|
||||
),
|
||||
|
@ -353,7 +355,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// rem : Int, Int -> Result Int [ DivByZero ]*
|
||||
add_type(
|
||||
Symbol::NUM_REM,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![int_type(), int_type()],
|
||||
Box::new(result_type(int_type(), div_by_zero.clone())),
|
||||
),
|
||||
|
@ -362,7 +364,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// mod : Int, Int -> Result Int [ DivByZero ]*
|
||||
add_type(
|
||||
Symbol::NUM_MOD_INT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![int_type(), int_type()],
|
||||
Box::new(result_type(int_type(), div_by_zero.clone())),
|
||||
),
|
||||
|
@ -373,7 +375,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// div : Float, Float -> Float
|
||||
add_type(
|
||||
Symbol::NUM_DIV_FLOAT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![float_type(), float_type()],
|
||||
Box::new(result_type(float_type(), div_by_zero.clone())),
|
||||
),
|
||||
|
@ -382,7 +384,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// mod : Float, Float -> Result Int [ DivByZero ]*
|
||||
add_type(
|
||||
Symbol::NUM_MOD_FLOAT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![float_type(), float_type()],
|
||||
Box::new(result_type(float_type(), div_by_zero)),
|
||||
),
|
||||
|
@ -396,7 +398,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
|
||||
add_type(
|
||||
Symbol::NUM_SQRT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![float_type()],
|
||||
Box::new(result_type(float_type(), sqrt_of_negative)),
|
||||
),
|
||||
|
@ -405,25 +407,25 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// round : Float -> Int
|
||||
add_type(
|
||||
Symbol::NUM_ROUND,
|
||||
SolvedType::Func(vec![float_type()], Box::new(int_type())),
|
||||
top_level_function(vec![float_type()], Box::new(int_type())),
|
||||
);
|
||||
|
||||
// sin : Float -> Float
|
||||
add_type(
|
||||
Symbol::NUM_SIN,
|
||||
SolvedType::Func(vec![float_type()], Box::new(float_type())),
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
);
|
||||
|
||||
// cos : Float -> Float
|
||||
add_type(
|
||||
Symbol::NUM_COS,
|
||||
SolvedType::Func(vec![float_type()], Box::new(float_type())),
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
);
|
||||
|
||||
// tan : Float -> Float
|
||||
add_type(
|
||||
Symbol::NUM_TAN,
|
||||
SolvedType::Func(vec![float_type()], Box::new(float_type())),
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
);
|
||||
|
||||
// maxFloat : Float
|
||||
|
@ -435,31 +437,31 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// pow : Float, Float -> Float
|
||||
add_type(
|
||||
Symbol::NUM_POW,
|
||||
SolvedType::Func(vec![float_type(), float_type()], Box::new(float_type())),
|
||||
top_level_function(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())),
|
||||
top_level_function(vec![float_type()], Box::new(int_type())),
|
||||
);
|
||||
|
||||
// powInt : Int, Int -> Int
|
||||
add_type(
|
||||
Symbol::NUM_POW_INT,
|
||||
SolvedType::Func(vec![int_type(), int_type()], Box::new(int_type())),
|
||||
top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
|
||||
);
|
||||
|
||||
// floor : Float -> Int
|
||||
add_type(
|
||||
Symbol::NUM_FLOOR,
|
||||
SolvedType::Func(vec![float_type()], Box::new(int_type())),
|
||||
top_level_function(vec![float_type()], Box::new(int_type())),
|
||||
);
|
||||
|
||||
// atan : Float -> Float
|
||||
add_type(
|
||||
Symbol::NUM_ATAN,
|
||||
SolvedType::Func(vec![float_type()], Box::new(float_type())),
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
);
|
||||
|
||||
// Bool module
|
||||
|
@ -467,25 +469,25 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// and : Bool, Bool -> Bool
|
||||
add_type(
|
||||
Symbol::BOOL_AND,
|
||||
SolvedType::Func(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
||||
top_level_function(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// or : Bool, Bool -> Bool
|
||||
add_type(
|
||||
Symbol::BOOL_OR,
|
||||
SolvedType::Func(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
||||
top_level_function(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// xor : Bool, Bool -> Bool
|
||||
add_type(
|
||||
Symbol::BOOL_XOR,
|
||||
SolvedType::Func(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
||||
top_level_function(vec![bool_type(), bool_type()], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// not : Bool -> Bool
|
||||
add_type(
|
||||
Symbol::BOOL_NOT,
|
||||
SolvedType::Func(vec![bool_type()], Box::new(bool_type())),
|
||||
top_level_function(vec![bool_type()], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// Str module
|
||||
|
@ -493,13 +495,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// Str.concat : Str, Str -> Str
|
||||
add_type(
|
||||
Symbol::STR_CONCAT,
|
||||
SolvedType::Func(vec![str_type(), str_type()], Box::new(str_type())),
|
||||
top_level_function(vec![str_type(), str_type()], Box::new(str_type())),
|
||||
);
|
||||
|
||||
// isEmpty : Str -> Bool
|
||||
add_type(
|
||||
Symbol::STR_IS_EMPTY,
|
||||
SolvedType::Func(vec![str_type()], Box::new(bool_type())),
|
||||
top_level_function(vec![str_type()], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// List module
|
||||
|
@ -512,7 +514,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
|
||||
add_type(
|
||||
Symbol::LIST_GET,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![list_type(flex(TVAR1)), int_type()],
|
||||
Box::new(result_type(flex(TVAR1), index_out_of_bounds)),
|
||||
),
|
||||
|
@ -526,7 +528,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
|
||||
add_type(
|
||||
Symbol::LIST_FIRST,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![list_type(flex(TVAR1))],
|
||||
Box::new(result_type(flex(TVAR1), list_was_empty)),
|
||||
),
|
||||
|
@ -535,7 +537,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// set : List elem, Int, elem -> List elem
|
||||
add_type(
|
||||
Symbol::LIST_SET,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![list_type(flex(TVAR1)), int_type(), flex(TVAR1)],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -544,7 +546,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// concat : List elem, List elem -> List elem
|
||||
add_type(
|
||||
Symbol::LIST_CONCAT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![list_type(flex(TVAR1)), list_type(flex(TVAR1))],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -553,10 +555,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
||||
add_type(
|
||||
Symbol::LIST_WALK_RIGHT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![
|
||||
list_type(flex(TVAR1)),
|
||||
SolvedType::Func(vec![flex(TVAR1), flex(TVAR2)], Box::new(flex(TVAR2))),
|
||||
closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))),
|
||||
flex(TVAR2),
|
||||
],
|
||||
Box::new(flex(TVAR2)),
|
||||
|
@ -566,10 +568,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// keepIf : List elem, (elem -> Bool) -> List elem
|
||||
add_type(
|
||||
Symbol::LIST_KEEP_IF,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![
|
||||
list_type(flex(TVAR1)),
|
||||
SolvedType::Func(vec![flex(TVAR1)], Box::new(bool_type())),
|
||||
closure(vec![flex(TVAR1)], TVAR2, Box::new(bool_type())),
|
||||
],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -578,10 +580,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// map : List before, (before -> after) -> List after
|
||||
add_type(
|
||||
Symbol::LIST_MAP,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![
|
||||
list_type(flex(TVAR1)),
|
||||
SolvedType::Func(vec![flex(TVAR1)], Box::new(flex(TVAR2))),
|
||||
closure(vec![flex(TVAR1)], TVAR3, Box::new(flex(TVAR2))),
|
||||
],
|
||||
Box::new(list_type(flex(TVAR2))),
|
||||
),
|
||||
|
@ -590,7 +592,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// append : List elem, elem -> List elem
|
||||
add_type(
|
||||
Symbol::LIST_APPEND,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![list_type(flex(TVAR1)), flex(TVAR1)],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -599,7 +601,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// prepend : List elem, elem -> List elem
|
||||
add_type(
|
||||
Symbol::LIST_PREPEND,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![list_type(flex(TVAR1)), flex(TVAR1)],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -608,7 +610,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// join : List (List elem) -> List elem
|
||||
add_type(
|
||||
Symbol::LIST_JOIN,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![list_type(list_type(flex(TVAR1)))],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -617,13 +619,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// single : a -> List a
|
||||
add_type(
|
||||
Symbol::LIST_SINGLE,
|
||||
SolvedType::Func(vec![flex(TVAR1)], Box::new(list_type(flex(TVAR1)))),
|
||||
top_level_function(vec![flex(TVAR1)], Box::new(list_type(flex(TVAR1)))),
|
||||
);
|
||||
|
||||
// repeat : Int, elem -> List elem
|
||||
add_type(
|
||||
Symbol::LIST_REPEAT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![int_type(), flex(TVAR1)],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -632,7 +634,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// reverse : List elem -> List elem
|
||||
add_type(
|
||||
Symbol::LIST_REVERSE,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![list_type(flex(TVAR1))],
|
||||
Box::new(list_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -641,13 +643,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// len : List * -> Int
|
||||
add_type(
|
||||
Symbol::LIST_LEN,
|
||||
SolvedType::Func(vec![list_type(flex(TVAR1))], Box::new(int_type())),
|
||||
top_level_function(vec![list_type(flex(TVAR1))], Box::new(int_type())),
|
||||
);
|
||||
|
||||
// isEmpty : List * -> Bool
|
||||
add_type(
|
||||
Symbol::LIST_IS_EMPTY,
|
||||
SolvedType::Func(vec![list_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
top_level_function(vec![list_type(flex(TVAR1))], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// Map module
|
||||
|
@ -658,7 +660,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// singleton : k, v -> Map k v
|
||||
add_type(
|
||||
Symbol::MAP_SINGLETON,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![flex(TVAR1), flex(TVAR2)],
|
||||
Box::new(map_type(flex(TVAR1), flex(TVAR2))),
|
||||
),
|
||||
|
@ -672,7 +674,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
|
||||
add_type(
|
||||
Symbol::MAP_GET,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![map_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1)],
|
||||
Box::new(result_type(flex(TVAR2), key_not_found)),
|
||||
),
|
||||
|
@ -680,7 +682,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
|
||||
add_type(
|
||||
Symbol::MAP_INSERT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![map_type(flex(TVAR1), flex(TVAR2)), flex(TVAR1), flex(TVAR2)],
|
||||
Box::new(map_type(flex(TVAR1), flex(TVAR2))),
|
||||
),
|
||||
|
@ -694,13 +696,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// singleton : a -> Set a
|
||||
add_type(
|
||||
Symbol::SET_SINGLETON,
|
||||
SolvedType::Func(vec![flex(TVAR1)], Box::new(set_type(flex(TVAR1)))),
|
||||
top_level_function(vec![flex(TVAR1)], Box::new(set_type(flex(TVAR1)))),
|
||||
);
|
||||
|
||||
// union : Set a, Set a -> Set a
|
||||
add_type(
|
||||
Symbol::SET_UNION,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))],
|
||||
Box::new(set_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -709,7 +711,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// diff : Set a, Set a -> Set a
|
||||
add_type(
|
||||
Symbol::SET_DIFF,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![set_type(flex(TVAR1)), set_type(flex(TVAR1))],
|
||||
Box::new(set_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -718,10 +720,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// foldl : Set a, (a -> b -> b), b -> b
|
||||
add_type(
|
||||
Symbol::SET_FOLDL,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![
|
||||
set_type(flex(TVAR1)),
|
||||
SolvedType::Func(vec![flex(TVAR1), flex(TVAR2)], Box::new(flex(TVAR2))),
|
||||
closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))),
|
||||
flex(TVAR2),
|
||||
],
|
||||
Box::new(flex(TVAR2)),
|
||||
|
@ -730,7 +732,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
|
||||
add_type(
|
||||
Symbol::SET_INSERT,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![set_type(flex(TVAR1)), flex(TVAR1)],
|
||||
Box::new(set_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -738,7 +740,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
|
||||
add_type(
|
||||
Symbol::SET_REMOVE,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![set_type(flex(TVAR1)), flex(TVAR1)],
|
||||
Box::new(set_type(flex(TVAR1))),
|
||||
),
|
||||
|
@ -749,10 +751,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// map : Result a err, (a -> b) -> Result b err
|
||||
add_type(
|
||||
Symbol::RESULT_MAP,
|
||||
SolvedType::Func(
|
||||
top_level_function(
|
||||
vec![
|
||||
result_type(flex(TVAR1), flex(TVAR3)),
|
||||
SolvedType::Func(vec![flex(TVAR1)], Box::new(flex(TVAR2))),
|
||||
closure(vec![flex(TVAR1)], TVAR4, Box::new(flex(TVAR2))),
|
||||
],
|
||||
Box::new(result_type(flex(TVAR2), flex(TVAR3))),
|
||||
),
|
||||
|
@ -766,6 +768,20 @@ fn flex(tvar: VarId) -> SolvedType {
|
|||
SolvedType::Flex(tvar)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn top_level_function(arguments: Vec<SolvedType>, ret: Box<SolvedType>) -> SolvedType {
|
||||
SolvedType::Func(
|
||||
arguments,
|
||||
Box::new(SolvedType::Flex(TOP_LEVEL_CLOSURE_VAR)),
|
||||
ret,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn closure(arguments: Vec<SolvedType>, closure_var: VarId, ret: Box<SolvedType>) -> SolvedType {
|
||||
SolvedType::Func(arguments, Box::new(SolvedType::Flex(closure_var)), ret)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn float_type() -> SolvedType {
|
||||
SolvedType::Apply(Symbol::NUM_FLOAT, Vec::new())
|
||||
|
|
|
@ -37,6 +37,7 @@ const NUM_BUILTIN_IMPORTS: usize = 7;
|
|||
|
||||
/// These can be shared between definitions, they will get instantiated when converted to Type
|
||||
const FUVAR: VarId = VarId::from_u32(1000);
|
||||
const TOP_LEVEL_CLOSURE_VAR: VarId = VarId::from_u32(1001);
|
||||
|
||||
fn shared(base: SolvedType) -> SolvedType {
|
||||
SolvedType::Apply(
|
||||
|
@ -831,12 +832,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// , Attr Shared (a -> b)
|
||||
// -> Attr * (List b)
|
||||
add_type(Symbol::LIST_MAP, {
|
||||
let_tvars! { a, b, star1, star2 };
|
||||
let_tvars! { a, b, star1, star2, closure };
|
||||
|
||||
unique_function(
|
||||
vec![
|
||||
list_type(star1, a),
|
||||
shared(SolvedType::Func(vec![flex(a)], Box::new(flex(b)))),
|
||||
shared(SolvedType::Func(
|
||||
vec![flex(a)],
|
||||
Box::new(flex(closure)),
|
||||
Box::new(flex(b)),
|
||||
)),
|
||||
],
|
||||
list_type(star2, b),
|
||||
)
|
||||
|
@ -846,12 +851,16 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// , Attr Shared (a -> Attr * Bool)
|
||||
// -> Attr * (List a)
|
||||
add_type(Symbol::LIST_KEEP_IF, {
|
||||
let_tvars! { a, star1, star2, star3 };
|
||||
let_tvars! { a, star1, star2, star3, closure };
|
||||
|
||||
unique_function(
|
||||
vec![
|
||||
list_type(star1, a),
|
||||
shared(SolvedType::Func(vec![flex(a)], Box::new(bool_type(star2)))),
|
||||
shared(SolvedType::Func(
|
||||
vec![flex(a)],
|
||||
Box::new(flex(closure)),
|
||||
Box::new(bool_type(star2)),
|
||||
)),
|
||||
],
|
||||
list_type(star3, a),
|
||||
)
|
||||
|
@ -862,7 +871,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// , b
|
||||
// -> b
|
||||
add_type(Symbol::LIST_WALK_RIGHT, {
|
||||
let_tvars! { u, a, b, star1 };
|
||||
let_tvars! { u, a, b, star1, closure };
|
||||
|
||||
unique_function(
|
||||
vec![
|
||||
|
@ -875,6 +884,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
),
|
||||
shared(SolvedType::Func(
|
||||
vec![attr_type(u, a), flex(b)],
|
||||
Box::new(flex(closure)),
|
||||
Box::new(flex(b)),
|
||||
)),
|
||||
flex(b),
|
||||
|
@ -1032,7 +1042,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// , b
|
||||
// -> b
|
||||
add_type(Symbol::SET_FOLDL, {
|
||||
let_tvars! { star, u, a, b };
|
||||
let_tvars! { star, u, a, b, closure };
|
||||
|
||||
unique_function(
|
||||
vec![
|
||||
|
@ -1045,6 +1055,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
),
|
||||
shared(SolvedType::Func(
|
||||
vec![attr_type(u, a), flex(b)],
|
||||
Box::new(flex(closure)),
|
||||
Box::new(flex(b)),
|
||||
)),
|
||||
flex(b),
|
||||
|
@ -1129,7 +1140,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
// , Attr * (a -> b)
|
||||
// -> Attr * (Result b e)
|
||||
add_type(Symbol::RESULT_MAP, {
|
||||
let_tvars! { star1, star2, star3, a, b, e };
|
||||
let_tvars! { star1, star2, star3, a, b, e, closure };
|
||||
unique_function(
|
||||
vec![
|
||||
SolvedType::Apply(
|
||||
|
@ -1143,7 +1154,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
Symbol::ATTR_ATTR,
|
||||
vec![
|
||||
flex(star2),
|
||||
SolvedType::Func(vec![flex(a)], Box::new(flex(b))),
|
||||
SolvedType::Func(vec![flex(a)], Box::new(flex(closure)), Box::new(flex(b))),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@ -1169,7 +1180,14 @@ fn flex(tvar: VarId) -> SolvedType {
|
|||
fn unique_function(args: Vec<SolvedType>, ret: SolvedType) -> SolvedType {
|
||||
SolvedType::Apply(
|
||||
Symbol::ATTR_ATTR,
|
||||
vec![flex(FUVAR), SolvedType::Func(args, Box::new(ret))],
|
||||
vec![
|
||||
flex(FUVAR),
|
||||
SolvedType::Func(
|
||||
args,
|
||||
Box::new(SolvedType::Flex(TOP_LEVEL_CLOSURE_VAR)),
|
||||
Box::new(ret),
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,9 @@ fn can_annotation_help(
|
|||
references,
|
||||
);
|
||||
|
||||
Type::Function(args, Box::new(ret))
|
||||
let closure = Type::Variable(var_store.fresh());
|
||||
|
||||
Type::Function(args, Box::new(closure), Box::new(ret))
|
||||
}
|
||||
Apply(module_name, ident, type_arguments) => {
|
||||
let symbol = if module_name.is_empty() {
|
||||
|
|
|
@ -1590,13 +1590,15 @@ fn defn(
|
|||
.map(|(var, symbol)| (var, no_region(Identifier(symbol))))
|
||||
.collect();
|
||||
|
||||
let expr = Closure(
|
||||
var_store.fresh(),
|
||||
fn_name,
|
||||
Recursive::NotRecursive,
|
||||
closure_args,
|
||||
Box::new((no_region(body), ret_var)),
|
||||
);
|
||||
let expr = Closure {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: ret_var,
|
||||
name: fn_name,
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments: closure_args,
|
||||
loc_body: Box::new(no_region(body)),
|
||||
};
|
||||
|
||||
Def {
|
||||
loc_pattern: Located {
|
||||
|
|
|
@ -650,12 +650,12 @@ fn group_to_declaration(
|
|||
let mut new_def = can_def.clone();
|
||||
|
||||
// Determine recursivity of closures that are not tail-recursive
|
||||
if let Closure(fn_var, name, Recursive::NotRecursive, args, body) =
|
||||
new_def.loc_expr.value
|
||||
if let Closure {
|
||||
recursive: recursive @ Recursive::NotRecursive,
|
||||
..
|
||||
} = &mut new_def.loc_expr.value
|
||||
{
|
||||
let recursion = closure_recursivity(*symbol, closures);
|
||||
|
||||
new_def.loc_expr.value = Closure(fn_var, name, recursion, args, body);
|
||||
*recursive = closure_recursivity(*symbol, closures);
|
||||
}
|
||||
|
||||
let is_recursive = successors(&symbol).contains(&symbol);
|
||||
|
@ -678,12 +678,12 @@ fn group_to_declaration(
|
|||
let mut new_def = can_def.clone();
|
||||
|
||||
// Determine recursivity of closures that are not tail-recursive
|
||||
if let Closure(fn_var, name, Recursive::NotRecursive, args, body) =
|
||||
new_def.loc_expr.value
|
||||
if let Closure {
|
||||
recursive: recursive @ Recursive::NotRecursive,
|
||||
..
|
||||
} = &mut new_def.loc_expr.value
|
||||
{
|
||||
let recursion = closure_recursivity(symbol, closures);
|
||||
|
||||
new_def.loc_expr.value = Closure(fn_var, name, recursion, args, body);
|
||||
*recursive = closure_recursivity(symbol, closures);
|
||||
}
|
||||
|
||||
if !seen_pattern_regions.contains(&new_def.loc_pattern.region) {
|
||||
|
@ -808,16 +808,16 @@ fn canonicalize_pending_def<'a>(
|
|||
region: loc_ann.region,
|
||||
};
|
||||
|
||||
let body = Box::new((body_expr, var_store.fresh()));
|
||||
|
||||
Located {
|
||||
value: Closure(
|
||||
var_store.fresh(),
|
||||
symbol,
|
||||
Recursive::NotRecursive,
|
||||
underscores,
|
||||
body,
|
||||
),
|
||||
value: Closure {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: symbol,
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments: underscores,
|
||||
loc_body: Box::new(body_expr),
|
||||
},
|
||||
region: loc_ann.region,
|
||||
}
|
||||
};
|
||||
|
@ -962,7 +962,15 @@ fn canonicalize_pending_def<'a>(
|
|||
if let (
|
||||
&ast::Pattern::Identifier(ref _name),
|
||||
&Pattern::Identifier(ref defined_symbol),
|
||||
&Closure(fn_var, ref symbol, _, ref arguments, ref body),
|
||||
&Closure {
|
||||
function_type,
|
||||
closure_type,
|
||||
return_type,
|
||||
name: ref symbol,
|
||||
ref arguments,
|
||||
loc_body: ref body,
|
||||
..
|
||||
},
|
||||
) = (
|
||||
&loc_pattern.value,
|
||||
&loc_can_pattern.value,
|
||||
|
@ -1000,13 +1008,15 @@ fn canonicalize_pending_def<'a>(
|
|||
});
|
||||
|
||||
// renamed_closure_def = Some(&defined_symbol);
|
||||
loc_can_expr.value = Closure(
|
||||
fn_var,
|
||||
*symbol,
|
||||
is_recursive,
|
||||
arguments.clone(),
|
||||
body.clone(),
|
||||
);
|
||||
loc_can_expr.value = Closure {
|
||||
function_type,
|
||||
closure_type,
|
||||
return_type,
|
||||
name: *symbol,
|
||||
recursive: is_recursive,
|
||||
arguments: arguments.clone(),
|
||||
loc_body: body.clone(),
|
||||
};
|
||||
}
|
||||
|
||||
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
||||
|
@ -1086,7 +1096,15 @@ fn canonicalize_pending_def<'a>(
|
|||
if let (
|
||||
&ast::Pattern::Identifier(ref _name),
|
||||
&Pattern::Identifier(ref defined_symbol),
|
||||
&Closure(fn_var, ref symbol, _, ref arguments, ref body),
|
||||
&Closure {
|
||||
function_type,
|
||||
closure_type,
|
||||
return_type,
|
||||
name: ref symbol,
|
||||
ref arguments,
|
||||
loc_body: ref body,
|
||||
..
|
||||
},
|
||||
) = (
|
||||
&loc_pattern.value,
|
||||
&loc_can_pattern.value,
|
||||
|
@ -1123,13 +1141,15 @@ fn canonicalize_pending_def<'a>(
|
|||
refs.lookups = refs.lookups.without(defined_symbol);
|
||||
});
|
||||
|
||||
loc_can_expr.value = Closure(
|
||||
fn_var,
|
||||
*symbol,
|
||||
is_recursive,
|
||||
arguments.clone(),
|
||||
body.clone(),
|
||||
);
|
||||
loc_can_expr.value = Closure {
|
||||
function_type,
|
||||
closure_type,
|
||||
return_type,
|
||||
name: *symbol,
|
||||
recursive: is_recursive,
|
||||
arguments: arguments.clone(),
|
||||
loc_body: body.clone(),
|
||||
};
|
||||
}
|
||||
|
||||
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
||||
|
|
|
@ -87,7 +87,7 @@ pub enum Expr {
|
|||
/// This is *only* for calling functions, not for tag application.
|
||||
/// The Tag variant contains any applied values inside it.
|
||||
Call(
|
||||
Box<(Variable, Located<Expr>, Variable)>,
|
||||
Box<(Variable, Located<Expr>, Variable, Variable)>,
|
||||
Vec<(Variable, Located<Expr>)>,
|
||||
CalledVia,
|
||||
),
|
||||
|
@ -97,13 +97,15 @@ pub enum Expr {
|
|||
ret_var: Variable,
|
||||
},
|
||||
|
||||
Closure(
|
||||
Variable,
|
||||
Symbol,
|
||||
Recursive,
|
||||
Vec<(Variable, Located<Pattern>)>,
|
||||
Box<(Located<Expr>, Variable)>,
|
||||
),
|
||||
Closure {
|
||||
function_type: Variable,
|
||||
closure_type: Variable,
|
||||
return_type: Variable,
|
||||
name: Symbol,
|
||||
recursive: Recursive,
|
||||
arguments: Vec<(Variable, Located<Pattern>)>,
|
||||
loc_body: Box<Located<Expr>>,
|
||||
},
|
||||
|
||||
// Product Types
|
||||
Record {
|
||||
|
@ -124,7 +126,9 @@ pub enum Expr {
|
|||
},
|
||||
/// field accessor as a function, e.g. (.foo) expr
|
||||
Accessor {
|
||||
function_var: Variable,
|
||||
record_var: Variable,
|
||||
closure_var: Variable,
|
||||
ext_var: Variable,
|
||||
field_var: Variable,
|
||||
field: Lowercase,
|
||||
|
@ -323,7 +327,12 @@ pub fn canonicalize_expr<'a>(
|
|||
};
|
||||
|
||||
Call(
|
||||
Box::new((var_store.fresh(), fn_expr, var_store.fresh())),
|
||||
Box::new((
|
||||
var_store.fresh(),
|
||||
fn_expr,
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
)),
|
||||
args,
|
||||
*application_style,
|
||||
)
|
||||
|
@ -346,7 +355,12 @@ pub fn canonicalize_expr<'a>(
|
|||
_ => {
|
||||
// This could be something like ((if True then fn1 else fn2) arg1 arg2).
|
||||
Call(
|
||||
Box::new((var_store.fresh(), fn_expr, var_store.fresh())),
|
||||
Box::new((
|
||||
var_store.fresh(),
|
||||
fn_expr,
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
)),
|
||||
args,
|
||||
*application_style,
|
||||
)
|
||||
|
@ -471,13 +485,15 @@ pub fn canonicalize_expr<'a>(
|
|||
env.register_closure(symbol, output.references.clone());
|
||||
|
||||
(
|
||||
Closure(
|
||||
var_store.fresh(),
|
||||
symbol,
|
||||
Recursive::NotRecursive,
|
||||
can_args,
|
||||
Box::new((loc_body_expr, var_store.fresh())),
|
||||
),
|
||||
Closure {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: symbol,
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments: can_args,
|
||||
loc_body: Box::new(loc_body_expr),
|
||||
},
|
||||
output,
|
||||
)
|
||||
}
|
||||
|
@ -535,8 +551,10 @@ pub fn canonicalize_expr<'a>(
|
|||
}
|
||||
ast::Expr::AccessorFunction(field) => (
|
||||
Accessor {
|
||||
function_var: var_store.fresh(),
|
||||
record_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
closure_var: var_store.fresh(),
|
||||
field_var: var_store.fresh(),
|
||||
field: (*field).into(),
|
||||
},
|
||||
|
@ -1193,20 +1211,30 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
LetNonRec(Box::new(def), Box::new(loc_expr), var, aliases)
|
||||
}
|
||||
|
||||
Closure(var, symbol, recursive, patterns, boxed_expr) => {
|
||||
let (loc_expr, expr_var) = *boxed_expr;
|
||||
Closure {
|
||||
function_type,
|
||||
closure_type,
|
||||
return_type,
|
||||
recursive,
|
||||
name,
|
||||
arguments,
|
||||
loc_body,
|
||||
} => {
|
||||
let loc_expr = *loc_body;
|
||||
let loc_expr = Located {
|
||||
value: inline_calls(var_store, scope, loc_expr.value),
|
||||
region: loc_expr.region,
|
||||
};
|
||||
|
||||
Closure(
|
||||
var,
|
||||
symbol,
|
||||
Closure {
|
||||
function_type,
|
||||
closure_type,
|
||||
return_type,
|
||||
recursive,
|
||||
patterns,
|
||||
Box::new((loc_expr, expr_var)),
|
||||
)
|
||||
name,
|
||||
arguments,
|
||||
loc_body: Box::new(loc_expr),
|
||||
}
|
||||
}
|
||||
|
||||
Record { record_var, fields } => {
|
||||
|
@ -1243,14 +1271,20 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
}
|
||||
|
||||
Call(boxed_tuple, args, called_via) => {
|
||||
let (fn_var, loc_expr, expr_var) = *boxed_tuple;
|
||||
let (fn_var, loc_expr, closure_var, expr_var) = *boxed_tuple;
|
||||
|
||||
match loc_expr.value {
|
||||
Var(symbol) if symbol.is_builtin() => match builtin_defs(var_store).get(&symbol) {
|
||||
Some(Def {
|
||||
loc_expr:
|
||||
Located {
|
||||
value: Closure(_var, _, recursive, params, boxed_body),
|
||||
value:
|
||||
Closure {
|
||||
recursive,
|
||||
arguments: params,
|
||||
loc_body: boxed_body,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
|
@ -1263,7 +1297,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
debug_assert_eq!(params.len(), args.len());
|
||||
|
||||
// Start with the function's body as the answer.
|
||||
let (mut loc_answer, _body_var) = *boxed_body.clone();
|
||||
let mut loc_answer = *boxed_body.clone();
|
||||
|
||||
// Wrap the body in one LetNonRec for each argument,
|
||||
// such that at the end we have all the arguments in
|
||||
|
@ -1311,7 +1345,11 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
},
|
||||
_ => {
|
||||
// For now, we only inline calls to builtins. Leave this alone!
|
||||
Call(Box::new((fn_var, loc_expr, expr_var)), args, called_via)
|
||||
Call(
|
||||
Box::new((fn_var, loc_expr, closure_var, expr_var)),
|
||||
args,
|
||||
called_via,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1458,7 +1496,12 @@ fn desugar_str_segments(var_store: &mut VarStore, segments: Vec<StrSegment>) ->
|
|||
|
||||
let fn_expr = Located::new(0, 0, 0, 0, Expr::Var(Symbol::STR_CONCAT));
|
||||
let expr = Expr::Call(
|
||||
Box::new((var_store.fresh(), fn_expr, var_store.fresh())),
|
||||
Box::new((
|
||||
var_store.fresh(),
|
||||
fn_expr,
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
)),
|
||||
vec![
|
||||
(var_store.fresh(), loc_new_expr),
|
||||
(var_store.fresh(), loc_expr),
|
||||
|
|
|
@ -309,7 +309,10 @@ mod test_can {
|
|||
match expr {
|
||||
LetRec(assignments, body, _, _) => {
|
||||
match &assignments.get(i).map(|def| &def.loc_expr.value) {
|
||||
Some(Closure(_, _, recursion, _, _)) => recursion.clone(),
|
||||
Some(Closure {
|
||||
recursive: recursion,
|
||||
..
|
||||
}) => recursion.clone(),
|
||||
Some(other @ _) => {
|
||||
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
||||
}
|
||||
|
@ -328,7 +331,10 @@ mod test_can {
|
|||
get_closure(&body.value, i - 1)
|
||||
} else {
|
||||
match &def.loc_expr.value {
|
||||
Closure(_, _, recursion, _, _) => recursion.clone(),
|
||||
Closure {
|
||||
recursive: recursion,
|
||||
..
|
||||
} => recursion.clone(),
|
||||
other @ _ => {
|
||||
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ pub fn constrain_expr(
|
|||
}
|
||||
}
|
||||
Call(boxed, loc_args, _application_style) => {
|
||||
let (fn_var, loc_fn, ret_var) = &**boxed;
|
||||
let (fn_var, loc_fn, closure_var, ret_var) = &**boxed;
|
||||
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||
// (foo) bar baz
|
||||
let opt_symbol = if let Var(symbol) = loc_fn.value {
|
||||
|
@ -262,11 +262,15 @@ pub fn constrain_expr(
|
|||
// The function's return type
|
||||
let ret_type = Variable(*ret_var);
|
||||
|
||||
// type of values captured in the closure
|
||||
let closure_type = Variable(*closure_var);
|
||||
|
||||
// This will be used in the occurs check
|
||||
let mut vars = Vec::with_capacity(2 + loc_args.len());
|
||||
|
||||
vars.push(*fn_var);
|
||||
vars.push(*ret_var);
|
||||
vars.push(*closure_var);
|
||||
|
||||
let mut arg_types = Vec::with_capacity(loc_args.len());
|
||||
let mut arg_cons = Vec::with_capacity(loc_args.len());
|
||||
|
@ -289,7 +293,11 @@ pub fn constrain_expr(
|
|||
|
||||
let expected_fn_type = ForReason(
|
||||
fn_reason,
|
||||
Function(arg_types, Box::new(ret_type.clone())),
|
||||
Function(
|
||||
arg_types,
|
||||
Box::new(closure_type),
|
||||
Box::new(ret_type.clone()),
|
||||
),
|
||||
region,
|
||||
);
|
||||
|
||||
|
@ -306,21 +314,31 @@ pub fn constrain_expr(
|
|||
)
|
||||
}
|
||||
Var(symbol) => Lookup(*symbol, expected, region),
|
||||
Closure(fn_var, _symbol, _recursive, args, boxed) => {
|
||||
let (loc_body_expr, ret_var) = boxed.as_ref();
|
||||
Closure {
|
||||
function_type: fn_var,
|
||||
closure_type: closure_var,
|
||||
return_type: ret_var,
|
||||
arguments,
|
||||
loc_body: boxed,
|
||||
..
|
||||
} => {
|
||||
let loc_body_expr = &**boxed;
|
||||
let mut state = PatternState {
|
||||
headers: SendMap::default(),
|
||||
vars: Vec::with_capacity(args.len()),
|
||||
vars: Vec::with_capacity(arguments.len()),
|
||||
constraints: Vec::with_capacity(1),
|
||||
};
|
||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||
let ret_var = *ret_var;
|
||||
let closure_var = *closure_var;
|
||||
let ret_type = Type::Variable(ret_var);
|
||||
let closure_type = Type::Variable(closure_var);
|
||||
|
||||
vars.push(ret_var);
|
||||
vars.push(closure_var);
|
||||
|
||||
for (pattern_var, loc_pattern) in args {
|
||||
for (pattern_var, loc_pattern) in arguments {
|
||||
let pattern_type = Type::Variable(*pattern_var);
|
||||
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
||||
|
||||
|
@ -337,7 +355,11 @@ pub fn constrain_expr(
|
|||
vars.push(*pattern_var);
|
||||
}
|
||||
|
||||
let fn_type = Type::Function(pattern_types, Box::new(ret_type.clone()));
|
||||
let fn_type = Type::Function(
|
||||
pattern_types,
|
||||
Box::new(closure_type),
|
||||
Box::new(ret_type.clone()),
|
||||
);
|
||||
let body_type = NoExpectation(ret_type);
|
||||
let ret_constraint =
|
||||
constrain_expr(env, loc_body_expr.region, &loc_body_expr.value, body_type);
|
||||
|
@ -653,8 +675,10 @@ pub fn constrain_expr(
|
|||
)
|
||||
}
|
||||
Accessor {
|
||||
function_var,
|
||||
field,
|
||||
record_var,
|
||||
closure_var,
|
||||
ext_var,
|
||||
field_var,
|
||||
} => {
|
||||
|
@ -678,12 +702,19 @@ pub fn constrain_expr(
|
|||
region,
|
||||
);
|
||||
|
||||
let function_type = Type::Function(
|
||||
vec![record_type],
|
||||
Box::new(Type::Variable(*closure_var)),
|
||||
Box::new(field_type),
|
||||
);
|
||||
|
||||
exists(
|
||||
vec![*record_var, field_var, ext_var],
|
||||
vec![*record_var, *function_var, *closure_var, field_var, ext_var],
|
||||
And(vec![
|
||||
Eq(function_type.clone(), expected, category.clone(), region),
|
||||
Eq(
|
||||
Type::Function(vec![record_type], Box::new(field_type)),
|
||||
expected,
|
||||
function_type,
|
||||
NoExpectation(Variable(*function_var)),
|
||||
category,
|
||||
region,
|
||||
),
|
||||
|
@ -1032,26 +1063,36 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
|||
// instead of the more generic "something is wrong with the body of `f`"
|
||||
match (&def.loc_expr.value, &signature) {
|
||||
(
|
||||
Closure(fn_var, _symbol, _recursive, args, boxed),
|
||||
Type::Function(arg_types, _),
|
||||
Closure {
|
||||
function_type: fn_var,
|
||||
closure_type: closure_var,
|
||||
return_type: ret_var,
|
||||
arguments,
|
||||
loc_body,
|
||||
..
|
||||
},
|
||||
Type::Function(arg_types, _, _),
|
||||
) => {
|
||||
let expected = annotation_expected;
|
||||
let region = def.loc_expr.region;
|
||||
|
||||
let (loc_body_expr, ret_var) = boxed.as_ref();
|
||||
let loc_body_expr = &**loc_body;
|
||||
let mut state = PatternState {
|
||||
headers: SendMap::default(),
|
||||
vars: Vec::with_capacity(args.len()),
|
||||
vars: Vec::with_capacity(arguments.len()),
|
||||
constraints: Vec::with_capacity(1),
|
||||
};
|
||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||
let ret_var = *ret_var;
|
||||
let closure_var = *closure_var;
|
||||
let ret_type = Type::Variable(ret_var);
|
||||
let closure_type = Type::Variable(closure_var);
|
||||
|
||||
vars.push(ret_var);
|
||||
vars.push(closure_var);
|
||||
|
||||
let it = args.iter().zip(arg_types.iter()).enumerate();
|
||||
let it = arguments.iter().zip(arg_types.iter()).enumerate();
|
||||
for (index, ((pattern_var, loc_pattern), loc_ann)) in it {
|
||||
{
|
||||
// ensure type matches the one in the annotation
|
||||
|
@ -1098,7 +1139,11 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
|||
}
|
||||
}
|
||||
|
||||
let fn_type = Type::Function(pattern_types, Box::new(ret_type.clone()));
|
||||
let fn_type = Type::Function(
|
||||
pattern_types,
|
||||
Box::new(closure_type),
|
||||
Box::new(ret_type.clone()),
|
||||
);
|
||||
let body_type = NoExpectation(ret_type);
|
||||
let ret_constraint =
|
||||
constrain_expr(env, loc_body_expr.region, &loc_body_expr.value, body_type);
|
||||
|
|
|
@ -178,7 +178,7 @@ fn to_type(solved_type: &SolvedType, free_vars: &mut FreeVars, var_store: &mut V
|
|||
use roc_types::solved_types::SolvedType::*;
|
||||
|
||||
match solved_type {
|
||||
Func(args, ret) => {
|
||||
Func(args, closure, ret) => {
|
||||
let mut new_args = Vec::with_capacity(args.len());
|
||||
|
||||
for arg in args {
|
||||
|
@ -186,8 +186,9 @@ fn to_type(solved_type: &SolvedType, free_vars: &mut FreeVars, var_store: &mut V
|
|||
}
|
||||
|
||||
let new_ret = to_type(&ret, free_vars, var_store);
|
||||
let new_closure = to_type(&closure, free_vars, var_store);
|
||||
|
||||
Type::Function(new_args, Box::new(new_ret))
|
||||
Type::Function(new_args, Box::new(new_closure), Box::new(new_ret))
|
||||
}
|
||||
Apply(symbol, args) => {
|
||||
let mut new_args = Vec::with_capacity(args.len());
|
||||
|
|
|
@ -707,24 +707,35 @@ pub fn constrain_expr(
|
|||
expected,
|
||||
)
|
||||
}
|
||||
Closure(fn_var, _symbol, recursion, args, boxed) => {
|
||||
Closure {
|
||||
function_type: fn_var,
|
||||
return_type: ret_var,
|
||||
closure_type: closure_var,
|
||||
recursive: recursion,
|
||||
arguments,
|
||||
loc_body: boxed,
|
||||
..
|
||||
} => {
|
||||
use roc_can::expr::Recursive;
|
||||
|
||||
let (loc_body_expr, ret_var) = &**boxed;
|
||||
let loc_body_expr = &**boxed;
|
||||
let mut state = PatternState {
|
||||
headers: SendMap::default(),
|
||||
vars: Vec::with_capacity(args.len()),
|
||||
vars: Vec::with_capacity(arguments.len()),
|
||||
constraints: Vec::with_capacity(1),
|
||||
};
|
||||
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||
let ret_var = *ret_var;
|
||||
let ret_type = Type::Variable(ret_var);
|
||||
let closure_var = *closure_var;
|
||||
let closure_type = Type::Variable(closure_var);
|
||||
|
||||
vars.push(ret_var);
|
||||
vars.push(closure_var);
|
||||
vars.push(*fn_var);
|
||||
|
||||
for (pattern_var, loc_pattern) in args {
|
||||
for (pattern_var, loc_pattern) in arguments {
|
||||
let pattern_type = Type::Variable(*pattern_var);
|
||||
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
||||
|
||||
|
@ -755,7 +766,11 @@ pub fn constrain_expr(
|
|||
|
||||
let fn_type = attr_type(
|
||||
fn_uniq_type,
|
||||
Type::Function(pattern_types, Box::new(ret_type.clone())),
|
||||
Type::Function(
|
||||
pattern_types,
|
||||
Box::new(closure_type),
|
||||
Box::new(ret_type.clone()),
|
||||
),
|
||||
);
|
||||
let body_type = Expected::NoExpectation(ret_type);
|
||||
let ret_constraint = constrain_expr(
|
||||
|
@ -795,9 +810,10 @@ pub fn constrain_expr(
|
|||
}
|
||||
|
||||
Call(boxed, loc_args, _) => {
|
||||
let (fn_var, fn_expr, ret_var) = &**boxed;
|
||||
let (fn_var, fn_expr, closure_var, ret_var) = &**boxed;
|
||||
let fn_type = Variable(*fn_var);
|
||||
let ret_type = Variable(*ret_var);
|
||||
let closure_type = Variable(*closure_var);
|
||||
let fn_expected = Expected::NoExpectation(fn_type.clone());
|
||||
let fn_region = fn_expr.region;
|
||||
|
||||
|
@ -811,6 +827,7 @@ pub fn constrain_expr(
|
|||
|
||||
vars.push(*fn_var);
|
||||
vars.push(*ret_var);
|
||||
vars.push(*closure_var);
|
||||
|
||||
// Canonicalize the function expression and its arguments
|
||||
let fn_con = constrain_expr(
|
||||
|
@ -862,7 +879,11 @@ pub fn constrain_expr(
|
|||
fn_reason,
|
||||
attr_type(
|
||||
Bool::variable(expected_uniq_type),
|
||||
Function(arg_types, Box::new(ret_type.clone())),
|
||||
Function(
|
||||
arg_types,
|
||||
Box::new(closure_type),
|
||||
Box::new(ret_type.clone()),
|
||||
),
|
||||
),
|
||||
region,
|
||||
);
|
||||
|
@ -1424,8 +1445,10 @@ pub fn constrain_expr(
|
|||
}
|
||||
|
||||
Accessor {
|
||||
function_var,
|
||||
field,
|
||||
record_var,
|
||||
closure_var,
|
||||
field_var,
|
||||
ext_var,
|
||||
} => {
|
||||
|
@ -1455,21 +1478,37 @@ pub fn constrain_expr(
|
|||
);
|
||||
|
||||
let fn_uniq_var = var_store.fresh();
|
||||
let closure_type = Type::Variable(*closure_var);
|
||||
let fn_type = attr_type(
|
||||
Bool::variable(fn_uniq_var),
|
||||
Type::Function(vec![record_type], Box::new(field_type)),
|
||||
Type::Function(
|
||||
vec![record_type],
|
||||
Box::new(closure_type),
|
||||
Box::new(field_type),
|
||||
),
|
||||
);
|
||||
|
||||
exists(
|
||||
vec![
|
||||
*record_var,
|
||||
*function_var,
|
||||
*closure_var,
|
||||
*field_var,
|
||||
*ext_var,
|
||||
fn_uniq_var,
|
||||
field_uniq_var,
|
||||
record_uniq_var,
|
||||
],
|
||||
And(vec![Eq(fn_type, expected, category, region), record_con]),
|
||||
And(vec![
|
||||
Eq(fn_type.clone(), expected, category.clone(), region),
|
||||
Eq(
|
||||
fn_type,
|
||||
Expected::NoExpectation(Variable(*function_var)),
|
||||
category,
|
||||
region,
|
||||
),
|
||||
record_con,
|
||||
]),
|
||||
)
|
||||
}
|
||||
RuntimeError(_) => True,
|
||||
|
@ -1873,21 +1912,33 @@ fn annotation_to_attr_type(
|
|||
)
|
||||
}
|
||||
|
||||
Function(arguments, result) => {
|
||||
Function(arguments, closure, result) => {
|
||||
let uniq_var = var_store.fresh();
|
||||
let (mut arg_vars, args_lifted) =
|
||||
annotation_to_attr_type_many(var_store, arguments, rigids, change_var_kind);
|
||||
let (closure_vars, closure_lifted) =
|
||||
annotation_to_attr_type(var_store, closure, rigids, change_var_kind);
|
||||
let (result_vars, result_lifted) =
|
||||
annotation_to_attr_type(var_store, result, rigids, change_var_kind);
|
||||
|
||||
arg_vars.extend(result_vars);
|
||||
arg_vars.extend(closure_vars);
|
||||
arg_vars.push(uniq_var);
|
||||
|
||||
match **closure {
|
||||
Type::Variable(c) => arg_vars.push(c),
|
||||
_ => unreachable!("closure must contain a type variable"),
|
||||
}
|
||||
|
||||
(
|
||||
arg_vars,
|
||||
attr_type(
|
||||
Bool::variable(uniq_var),
|
||||
Type::Function(args_lifted, Box::new(result_lifted)),
|
||||
Type::Function(
|
||||
args_lifted,
|
||||
Box::new(closure_lifted),
|
||||
Box::new(result_lifted),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -2542,8 +2593,9 @@ fn fix_mutual_recursive_alias_help_help(rec_var: Variable, attribute: &Type, int
|
|||
use Type::*;
|
||||
|
||||
match into_type {
|
||||
Function(args, ret) => {
|
||||
Function(args, closure, ret) => {
|
||||
fix_mutual_recursive_alias_help(rec_var, attribute, ret);
|
||||
fix_mutual_recursive_alias_help(rec_var, attribute, closure);
|
||||
args.iter_mut()
|
||||
.for_each(|arg| fix_mutual_recursive_alias_help(rec_var, attribute, arg));
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ impl LayoutId {
|
|||
// Returns something like "foo#1" when given a symbol that interns to "foo"
|
||||
// and a LayoutId of 1.
|
||||
pub fn to_symbol_string(self, symbol: Symbol, interns: &Interns) -> String {
|
||||
format!("{}#{}", symbol.ident_string(interns), self.0)
|
||||
format!("{}_{}", symbol.ident_string(interns), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -678,15 +678,58 @@ mod gen_records {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn just_to_be_sure() {
|
||||
fn accessor() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: 1, b : 2, c : 3 }
|
||||
.foo { foo: 4 } + .foo { bar: 6.28, foo: 3 }
|
||||
"#
|
||||
),
|
||||
[1, 2, 3],
|
||||
[i64; 3]
|
||||
7,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accessor_single_element_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
.foo { foo: 4 }
|
||||
"#
|
||||
),
|
||||
4,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { foo: 42, bar: 6.28 }
|
||||
|
||||
{ rec & foo: rec.foo + 1 }
|
||||
"#
|
||||
),
|
||||
(6.28, 43),
|
||||
(f64, i64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_single_element_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { foo: 42}
|
||||
|
||||
{ rec & foo: rec.foo + 1 }
|
||||
"#
|
||||
),
|
||||
43,
|
||||
i64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -337,6 +337,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
self.own_var(*x);
|
||||
}
|
||||
}
|
||||
|
||||
FunctionCall {
|
||||
call_type,
|
||||
args,
|
||||
|
|
|
@ -438,7 +438,9 @@ impl<'a> Procs<'a> {
|
|||
|
||||
fn get_args_ret_var(subs: &Subs, var: Variable) -> Option<(std::vec::Vec<Variable>, Variable)> {
|
||||
match subs.get_without_compacting(var).content {
|
||||
Content::Structure(FlatType::Func(pattern_vars, ret_var)) => Some((pattern_vars, ret_var)),
|
||||
Content::Structure(FlatType::Func(pattern_vars, _closure_var, ret_var)) => {
|
||||
Some((pattern_vars, ret_var))
|
||||
}
|
||||
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, args)) => {
|
||||
get_args_ret_var(subs, args[1])
|
||||
}
|
||||
|
@ -678,6 +680,7 @@ pub enum Expr<'a> {
|
|||
arguments: &'a [Symbol],
|
||||
},
|
||||
Struct(&'a [Symbol]),
|
||||
|
||||
AccessAtIndex {
|
||||
index: u64,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
|
@ -1334,24 +1337,32 @@ pub fn with_hole<'a>(
|
|||
},
|
||||
LetNonRec(def, cont, _, _) => {
|
||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||
if let Closure(ann, _, recursivity, loc_args, boxed_body) = def.loc_expr.value {
|
||||
if let Closure {
|
||||
function_type,
|
||||
return_type,
|
||||
recursive,
|
||||
arguments,
|
||||
loc_body: boxed_body,
|
||||
..
|
||||
} = def.loc_expr.value
|
||||
{
|
||||
// Extract Procs, but discard the resulting Expr::Load.
|
||||
// That Load looks up the pointer, which we won't use here!
|
||||
|
||||
let (loc_body, ret_var) = *boxed_body;
|
||||
let loc_body = *boxed_body;
|
||||
|
||||
let is_self_recursive =
|
||||
!matches!(recursivity, roc_can::expr::Recursive::NotRecursive);
|
||||
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
||||
|
||||
procs.insert_named(
|
||||
env,
|
||||
layout_cache,
|
||||
*symbol,
|
||||
ann,
|
||||
loc_args,
|
||||
function_type,
|
||||
arguments,
|
||||
loc_body,
|
||||
is_self_recursive,
|
||||
ret_var,
|
||||
return_type,
|
||||
);
|
||||
|
||||
return with_hole(env, cont.value, procs, layout_cache, assigned, hole);
|
||||
|
@ -1418,24 +1429,32 @@ pub fn with_hole<'a>(
|
|||
// because Roc is strict, only functions can be recursive!
|
||||
for def in defs.into_iter() {
|
||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||
if let Closure(ann, _, recursivity, loc_args, boxed_body) = def.loc_expr.value {
|
||||
if let Closure {
|
||||
function_type,
|
||||
return_type,
|
||||
recursive,
|
||||
arguments,
|
||||
loc_body: boxed_body,
|
||||
..
|
||||
} = def.loc_expr.value
|
||||
{
|
||||
// Extract Procs, but discard the resulting Expr::Load.
|
||||
// That Load looks up the pointer, which we won't use here!
|
||||
|
||||
let (loc_body, ret_var) = *boxed_body;
|
||||
let loc_body = *boxed_body;
|
||||
|
||||
let is_self_recursive =
|
||||
!matches!(recursivity, roc_can::expr::Recursive::NotRecursive);
|
||||
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
||||
|
||||
procs.insert_named(
|
||||
env,
|
||||
layout_cache,
|
||||
*symbol,
|
||||
ann,
|
||||
loc_args,
|
||||
function_type,
|
||||
arguments,
|
||||
loc_body,
|
||||
is_self_recursive,
|
||||
ret_var,
|
||||
return_type,
|
||||
);
|
||||
|
||||
continue;
|
||||
|
@ -1941,13 +1960,188 @@ pub fn with_hole<'a>(
|
|||
stmt
|
||||
}
|
||||
|
||||
Accessor { .. } | Update { .. } => todo!("record access/accessor/update"),
|
||||
Accessor {
|
||||
function_var,
|
||||
record_var,
|
||||
closure_var: _,
|
||||
ext_var,
|
||||
field_var,
|
||||
field,
|
||||
} => {
|
||||
// IDEA: convert accessor fromt
|
||||
//
|
||||
// .foo
|
||||
//
|
||||
// into
|
||||
//
|
||||
// (\r -> r.foo)
|
||||
let record_symbol = env.unique_symbol();
|
||||
let body = roc_can::expr::Expr::Access {
|
||||
record_var,
|
||||
ext_var,
|
||||
field_var,
|
||||
loc_expr: Box::new(Located::at_zero(roc_can::expr::Expr::Var(record_symbol))),
|
||||
field,
|
||||
};
|
||||
|
||||
Closure(ann, name, _, loc_args, boxed_body) => {
|
||||
let (loc_body, ret_var) = *boxed_body;
|
||||
let loc_body = Located::at_zero(body);
|
||||
|
||||
match procs.insert_anonymous(env, name, ann, loc_args, loc_body, ret_var, layout_cache)
|
||||
{
|
||||
let name = env.unique_symbol();
|
||||
|
||||
let arguments = vec![(
|
||||
record_var,
|
||||
Located::at_zero(roc_can::pattern::Pattern::Identifier(record_symbol)),
|
||||
)];
|
||||
|
||||
match procs.insert_anonymous(
|
||||
env,
|
||||
name,
|
||||
function_var,
|
||||
arguments,
|
||||
loc_body,
|
||||
field_var,
|
||||
layout_cache,
|
||||
) {
|
||||
Ok(layout) => {
|
||||
// TODO should the let have layout Pointer?
|
||||
Stmt::Let(
|
||||
assigned,
|
||||
Expr::FunctionPointer(name, layout.clone()),
|
||||
layout,
|
||||
hole,
|
||||
)
|
||||
}
|
||||
|
||||
Err(_error) => Stmt::RuntimeError(
|
||||
"TODO convert anonymous function error to a RuntimeError string",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
Update {
|
||||
record_var,
|
||||
symbol: structure,
|
||||
updates,
|
||||
..
|
||||
} => {
|
||||
use FieldType::*;
|
||||
|
||||
enum FieldType<'a> {
|
||||
CopyExisting(u64),
|
||||
UpdateExisting(&'a roc_can::expr::Field),
|
||||
};
|
||||
|
||||
// Strategy: turn a record update into the creation of a new record.
|
||||
// This has the benefit that we don't need to do anything special for reference
|
||||
// counting
|
||||
|
||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
|
||||
|
||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||
|
||||
let mut symbols = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||
let mut fields = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||
|
||||
let mut current = 0;
|
||||
for (label, opt_field_layout) in sorted_fields.into_iter() {
|
||||
match opt_field_layout {
|
||||
Err(_) => {
|
||||
debug_assert!(!updates.contains_key(&label));
|
||||
// this was an optional field, and now does not exist!
|
||||
// do not increment `current`!
|
||||
}
|
||||
Ok(field_layout) => {
|
||||
field_layouts.push(field_layout);
|
||||
|
||||
if let Some(field) = updates.get(&label) {
|
||||
// TODO
|
||||
let field_symbol =
|
||||
possible_reuse_symbol(env, procs, &field.loc_expr.value);
|
||||
|
||||
fields.push(UpdateExisting(field));
|
||||
symbols.push(field_symbol);
|
||||
} else {
|
||||
fields.push(CopyExisting(current));
|
||||
symbols.push(env.unique_symbol());
|
||||
}
|
||||
|
||||
current += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
let symbols = symbols.into_bump_slice();
|
||||
|
||||
let record_layout = layout_cache
|
||||
.from_var(env.arena, record_var, env.subs)
|
||||
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||
|
||||
let field_layouts = match &record_layout {
|
||||
Layout::Struct(layouts) => *layouts,
|
||||
other => arena.alloc([other.clone()]),
|
||||
};
|
||||
|
||||
let wrapped = if field_layouts.len() == 1 {
|
||||
Wrapped::SingleElementRecord
|
||||
} else {
|
||||
Wrapped::RecordOrSingleTagUnion
|
||||
};
|
||||
|
||||
let expr = Expr::Struct(symbols);
|
||||
let mut stmt = Stmt::Let(assigned, expr, record_layout, hole);
|
||||
|
||||
let it = field_layouts.iter().zip(symbols.iter()).zip(fields);
|
||||
for ((field_layout, symbol), what_to_do) in it {
|
||||
match what_to_do {
|
||||
UpdateExisting(field) => {
|
||||
stmt = assign_to_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
field.var,
|
||||
*field.loc_expr.clone(),
|
||||
*symbol,
|
||||
stmt,
|
||||
);
|
||||
}
|
||||
CopyExisting(index) => {
|
||||
let access_expr = Expr::AccessAtIndex {
|
||||
structure,
|
||||
index,
|
||||
field_layouts,
|
||||
wrapped,
|
||||
};
|
||||
stmt = Stmt::Let(
|
||||
*symbol,
|
||||
access_expr,
|
||||
field_layout.clone(),
|
||||
arena.alloc(stmt),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stmt
|
||||
}
|
||||
|
||||
Closure {
|
||||
function_type,
|
||||
return_type,
|
||||
name,
|
||||
arguments,
|
||||
loc_body: boxed_body,
|
||||
..
|
||||
} => {
|
||||
let loc_body = *boxed_body;
|
||||
|
||||
match procs.insert_anonymous(
|
||||
env,
|
||||
name,
|
||||
function_type,
|
||||
arguments,
|
||||
loc_body,
|
||||
return_type,
|
||||
layout_cache,
|
||||
) {
|
||||
Ok(layout) => {
|
||||
// TODO should the let have layout Pointer?
|
||||
Stmt::Let(
|
||||
|
@ -1965,7 +2159,7 @@ pub fn with_hole<'a>(
|
|||
}
|
||||
|
||||
Call(boxed, loc_args, _) => {
|
||||
let (fn_var, loc_expr, ret_var) = *boxed;
|
||||
let (fn_var, loc_expr, _closure_var, ret_var) = *boxed;
|
||||
|
||||
// even if a call looks like it's by name, it may in fact be by-pointer.
|
||||
// E.g. in `(\f, x -> f x)` the call is in fact by pointer.
|
||||
|
@ -2197,24 +2391,31 @@ pub fn from_can<'a>(
|
|||
// Now that we know for sure it's a closure, get an owned
|
||||
// version of these variant args so we can use them properly.
|
||||
match def.loc_expr.value {
|
||||
Closure(ann, _, recursivity, loc_args, boxed_body) => {
|
||||
Closure {
|
||||
function_type,
|
||||
return_type,
|
||||
recursive,
|
||||
arguments,
|
||||
loc_body: boxed_body,
|
||||
..
|
||||
} => {
|
||||
// Extract Procs, but discard the resulting Expr::Load.
|
||||
// That Load looks up the pointer, which we won't use here!
|
||||
|
||||
let (loc_body, ret_var) = *boxed_body;
|
||||
let loc_body = *boxed_body;
|
||||
|
||||
let is_self_recursive =
|
||||
!matches!(recursivity, roc_can::expr::Recursive::NotRecursive);
|
||||
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
||||
|
||||
procs.insert_named(
|
||||
env,
|
||||
layout_cache,
|
||||
*symbol,
|
||||
ann,
|
||||
loc_args,
|
||||
function_type,
|
||||
arguments,
|
||||
loc_body,
|
||||
is_self_recursive,
|
||||
ret_var,
|
||||
return_type,
|
||||
);
|
||||
|
||||
continue;
|
||||
|
@ -2229,28 +2430,35 @@ pub fn from_can<'a>(
|
|||
}
|
||||
LetNonRec(def, cont, _, _) => {
|
||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||
if let Closure(_, _, _, _, _) = &def.loc_expr.value {
|
||||
if let Closure { .. } = &def.loc_expr.value {
|
||||
// Now that we know for sure it's a closure, get an owned
|
||||
// version of these variant args so we can use them properly.
|
||||
match def.loc_expr.value {
|
||||
Closure(ann, _, recursivity, loc_args, boxed_body) => {
|
||||
Closure {
|
||||
function_type,
|
||||
return_type,
|
||||
recursive,
|
||||
arguments,
|
||||
loc_body: boxed_body,
|
||||
..
|
||||
} => {
|
||||
// Extract Procs, but discard the resulting Expr::Load.
|
||||
// That Load looks up the pointer, which we won't use here!
|
||||
|
||||
let (loc_body, ret_var) = *boxed_body;
|
||||
let loc_body = *boxed_body;
|
||||
|
||||
let is_self_recursive =
|
||||
!matches!(recursivity, roc_can::expr::Recursive::NotRecursive);
|
||||
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
||||
|
||||
procs.insert_named(
|
||||
env,
|
||||
layout_cache,
|
||||
*symbol,
|
||||
ann,
|
||||
loc_args,
|
||||
function_type,
|
||||
arguments,
|
||||
loc_body,
|
||||
is_self_recursive,
|
||||
ret_var,
|
||||
return_type,
|
||||
);
|
||||
|
||||
return from_can(env, cont.value, procs, layout_cache);
|
||||
|
|
|
@ -391,7 +391,7 @@ fn layout_from_flat_type<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
Func(args, ret_var) => {
|
||||
Func(args, _, ret_var) => {
|
||||
let mut fn_args = Vec::with_capacity_in(args.len(), arena);
|
||||
|
||||
for arg_var in args {
|
||||
|
@ -406,15 +406,19 @@ fn layout_from_flat_type<'a>(
|
|||
))
|
||||
}
|
||||
Record(fields, ext_var) => {
|
||||
debug_assert!(ext_var_is_empty_record(subs, ext_var));
|
||||
|
||||
// Sort the fields by label
|
||||
let mut sorted_fields = Vec::with_capacity_in(fields.len(), arena);
|
||||
sorted_fields.extend(fields.into_iter());
|
||||
|
||||
for tuple in fields {
|
||||
sorted_fields.push(tuple);
|
||||
// extract any values from the ext_var
|
||||
let mut fields_map = MutMap::default();
|
||||
match roc_types::pretty_print::chase_ext_record(subs, ext_var, &mut fields_map) {
|
||||
Ok(()) | Err((_, Content::FlexVar(_))) => {}
|
||||
Err(_) => unreachable!("this would have been a type error"),
|
||||
}
|
||||
|
||||
sorted_fields.extend(fields_map.into_iter());
|
||||
|
||||
sorted_fields.sort_by(|(label1, _), (label2, _)| label1.cmp(label2));
|
||||
|
||||
// Determine the layouts of the fields, maintaining sort order
|
||||
|
@ -781,16 +785,6 @@ fn ext_var_is_empty_tag_union(_: &Subs, _: Variable) -> bool {
|
|||
unreachable!();
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn ext_var_is_empty_record(subs: &Subs, ext_var: Variable) -> bool {
|
||||
// the ext_var is empty
|
||||
let mut ext_fields = MutMap::default();
|
||||
match roc_types::pretty_print::chase_ext_record(subs, ext_var, &mut ext_fields) {
|
||||
Ok(()) | Err((_, Content::FlexVar(_))) => ext_fields.is_empty(),
|
||||
Err((_, content)) => panic!("invalid content in ext_var: {:?}", content),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn ext_var_is_empty_record(_: &Subs, _: Variable) -> bool {
|
||||
// This should only ever be used in debug_assert! macros
|
||||
|
|
|
@ -796,7 +796,7 @@ fn count_arguments(tipe: &ErrorType) -> usize {
|
|||
use ErrorType::*;
|
||||
|
||||
match tipe {
|
||||
Function(args, _) => args.len(),
|
||||
Function(args, _, _) => args.len(),
|
||||
Type(Symbol::ATTR_ATTR, args) => count_arguments(&args[1]),
|
||||
Alias(_, _, actual) => count_arguments(actual),
|
||||
_ => 0,
|
||||
|
@ -1334,7 +1334,7 @@ pub fn to_doc<'b>(
|
|||
use ErrorType::*;
|
||||
|
||||
match tipe {
|
||||
Function(args, ret) => report_text::function(
|
||||
Function(args, _, ret) => report_text::function(
|
||||
alloc,
|
||||
parens,
|
||||
args.into_iter()
|
||||
|
@ -1474,7 +1474,7 @@ fn to_diff<'b>(
|
|||
(FlexVar(x), FlexVar(y)) if x == y => same(alloc, parens, type1),
|
||||
(RigidVar(x), RigidVar(y)) if x == y => same(alloc, parens, type1),
|
||||
|
||||
(Function(args1, ret1), Function(args2, ret2)) => {
|
||||
(Function(args1, _, ret1), Function(args2, _, ret2)) => {
|
||||
if args1.len() == args2.len() {
|
||||
let mut status = Status::Similar;
|
||||
let arg_diff = traverse(alloc, Parens::InFn, args1, args2);
|
||||
|
@ -2406,7 +2406,7 @@ fn type_problem_to_pretty<'b>(
|
|||
match tipe {
|
||||
Infinite | Error | FlexVar(_) => alloc.nil(),
|
||||
RigidVar(y) => bad_double_rigid(x, y),
|
||||
Function(_, _) => bad_rigid_var(x, alloc.reflow("a function value")),
|
||||
Function(_, _, _) => bad_rigid_var(x, alloc.reflow("a function value")),
|
||||
Record(_, _) => bad_rigid_var(x, alloc.reflow("a record value")),
|
||||
TagUnion(_, _) | RecursiveTagUnion(_, _, _) => {
|
||||
bad_rigid_var(x, alloc.reflow("a tag value"))
|
||||
|
|
|
@ -599,7 +599,7 @@ fn type_to_variable(
|
|||
|
||||
register(subs, rank, pools, content)
|
||||
}
|
||||
Function(args, ret_type) => {
|
||||
Function(args, closure_type, ret_type) => {
|
||||
let mut arg_vars = Vec::with_capacity(args.len());
|
||||
|
||||
for arg in args {
|
||||
|
@ -607,7 +607,8 @@ fn type_to_variable(
|
|||
}
|
||||
|
||||
let ret_var = type_to_variable(subs, rank, pools, cached, ret_type);
|
||||
let content = Content::Structure(FlatType::Func(arg_vars, ret_var));
|
||||
let closure_var = type_to_variable(subs, rank, pools, cached, closure_type);
|
||||
let content = Content::Structure(FlatType::Func(arg_vars, closure_var, ret_var));
|
||||
|
||||
register(subs, rank, pools, content)
|
||||
}
|
||||
|
@ -1075,9 +1076,17 @@ fn adjust_rank_content(
|
|||
rank
|
||||
}
|
||||
|
||||
Func(arg_vars, ret_var) => {
|
||||
Func(arg_vars, closure_var, ret_var) => {
|
||||
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, ret_var);
|
||||
|
||||
rank = rank.max(adjust_rank(
|
||||
subs,
|
||||
young_mark,
|
||||
visit_mark,
|
||||
group_rank,
|
||||
closure_var,
|
||||
));
|
||||
|
||||
for var in arg_vars {
|
||||
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||
}
|
||||
|
@ -1239,14 +1248,15 @@ fn deep_copy_var_help(
|
|||
Apply(symbol, args)
|
||||
}
|
||||
|
||||
Func(arg_vars, ret_var) => {
|
||||
Func(arg_vars, closure_var, ret_var) => {
|
||||
let new_ret_var = deep_copy_var_help(subs, max_rank, pools, ret_var);
|
||||
let new_closure_var = deep_copy_var_help(subs, max_rank, pools, closure_var);
|
||||
let arg_vars = arg_vars
|
||||
.into_iter()
|
||||
.map(|var| deep_copy_var_help(subs, max_rank, pools, var))
|
||||
.collect();
|
||||
|
||||
Func(arg_vars, new_ret_var)
|
||||
Func(arg_vars, new_closure_var, new_ret_var)
|
||||
}
|
||||
|
||||
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
|
||||
|
|
|
@ -150,7 +150,7 @@ fn find_names_needed(
|
|||
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||
}
|
||||
}
|
||||
Structure(Func(arg_vars, ret_var)) => {
|
||||
Structure(Func(arg_vars, _closure_var, ret_var)) => {
|
||||
for var in arg_vars {
|
||||
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||
}
|
||||
|
@ -376,7 +376,7 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
|
|||
Apply(symbol, args) => write_apply(env, symbol, args, subs, buf, parens),
|
||||
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
||||
EmptyTagUnion => buf.push_str(EMPTY_TAG_UNION),
|
||||
Func(args, ret) => write_fn(env, args, ret, subs, buf, parens),
|
||||
Func(args, _closure, ret) => write_fn(env, args, ret, subs, buf, parens),
|
||||
Record(fields, ext_var) => {
|
||||
use crate::types::{gather_fields, RecordStructure};
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ impl<T> Solved<T> {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SolvedType {
|
||||
/// A function. The types of its arguments, then the type of its return value.
|
||||
Func(Vec<SolvedType>, Box<SolvedType>),
|
||||
Func(Vec<SolvedType>, Box<SolvedType>, Box<SolvedType>),
|
||||
/// Applying a type to some arguments (e.g. Map.Map String Int)
|
||||
Apply(Symbol, Vec<SolvedType>),
|
||||
/// A bound type variable, e.g. `a` in `(a -> a)`
|
||||
|
@ -107,8 +107,9 @@ impl SolvedType {
|
|||
|
||||
SolvedType::Apply(symbol, solved_types)
|
||||
}
|
||||
Function(args, box_ret) => {
|
||||
Function(args, box_closure, box_ret) => {
|
||||
let solved_ret = Self::from_type(solved_subs, *box_ret);
|
||||
let solved_closure = Self::from_type(solved_subs, *box_closure);
|
||||
let mut solved_args = Vec::with_capacity(args.len());
|
||||
|
||||
for arg in args.into_iter() {
|
||||
|
@ -117,7 +118,7 @@ impl SolvedType {
|
|||
solved_args.push(solved_arg);
|
||||
}
|
||||
|
||||
SolvedType::Func(solved_args, Box::new(solved_ret))
|
||||
SolvedType::Func(solved_args, Box::new(solved_closure), Box::new(solved_ret))
|
||||
}
|
||||
Record(fields, box_ext) => {
|
||||
let solved_ext = Self::from_type(solved_subs, *box_ext);
|
||||
|
@ -227,7 +228,7 @@ impl SolvedType {
|
|||
|
||||
SolvedType::Apply(symbol, new_args)
|
||||
}
|
||||
Func(args, ret) => {
|
||||
Func(args, closure, ret) => {
|
||||
let mut new_args = Vec::with_capacity(args.len());
|
||||
|
||||
for var in args {
|
||||
|
@ -235,8 +236,9 @@ impl SolvedType {
|
|||
}
|
||||
|
||||
let ret = Self::from_var(subs, ret);
|
||||
let closure = Self::from_var(subs, closure);
|
||||
|
||||
SolvedType::Func(new_args, Box::new(ret))
|
||||
SolvedType::Func(new_args, Box::new(closure), Box::new(ret))
|
||||
}
|
||||
Record(fields, ext_var) => {
|
||||
let mut new_fields = Vec::with_capacity(fields.len());
|
||||
|
|
|
@ -565,7 +565,7 @@ impl Content {
|
|||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum FlatType {
|
||||
Apply(Symbol, Vec<Variable>),
|
||||
Func(Vec<Variable>, Variable),
|
||||
Func(Vec<Variable>, Variable, Variable),
|
||||
Record(MutMap<Lowercase, RecordField<Variable>>, Variable),
|
||||
TagUnion(MutMap<TagName, Vec<Variable>>, Variable),
|
||||
RecursiveTagUnion(Variable, MutMap<TagName, Vec<Variable>>, Variable),
|
||||
|
@ -606,8 +606,10 @@ fn occurs(
|
|||
|
||||
match flat_type {
|
||||
Apply(_, args) => short_circuit(subs, root_var, &new_seen, args.iter()),
|
||||
Func(arg_vars, ret_var) => {
|
||||
let it = once(&ret_var).chain(arg_vars.iter());
|
||||
Func(arg_vars, closure_var, ret_var) => {
|
||||
let it = once(&ret_var)
|
||||
.chain(once(&closure_var))
|
||||
.chain(arg_vars.iter());
|
||||
short_circuit(subs, root_var, &new_seen, it)
|
||||
}
|
||||
Record(vars_by_field, ext_var) => {
|
||||
|
@ -699,14 +701,19 @@ fn explicit_substitute(
|
|||
|
||||
subs.set_content(in_var, Structure(Apply(symbol, new_args)));
|
||||
}
|
||||
Func(arg_vars, ret_var) => {
|
||||
Func(arg_vars, closure_var, ret_var) => {
|
||||
let new_arg_vars = arg_vars
|
||||
.iter()
|
||||
.map(|var| explicit_substitute(subs, from, to, *var, seen))
|
||||
.collect();
|
||||
let new_ret_var = explicit_substitute(subs, from, to, ret_var, seen);
|
||||
let new_closure_var =
|
||||
explicit_substitute(subs, from, to, closure_var, seen);
|
||||
|
||||
subs.set_content(in_var, Structure(Func(new_arg_vars, new_ret_var)));
|
||||
subs.set_content(
|
||||
in_var,
|
||||
Structure(Func(new_arg_vars, new_closure_var, new_ret_var)),
|
||||
);
|
||||
}
|
||||
TagUnion(mut tags, ext_var) => {
|
||||
let new_ext_var = explicit_substitute(subs, from, to, ext_var, seen);
|
||||
|
@ -804,8 +811,9 @@ fn get_var_names(
|
|||
})
|
||||
}
|
||||
|
||||
FlatType::Func(arg_vars, ret_var) => {
|
||||
FlatType::Func(arg_vars, closure_var, ret_var) => {
|
||||
let taken_names = get_var_names(subs, ret_var, taken_names);
|
||||
let taken_names = get_var_names(subs, closure_var, taken_names);
|
||||
|
||||
arg_vars.into_iter().fold(taken_names, |answer, arg_var| {
|
||||
get_var_names(subs, arg_var, answer)
|
||||
|
@ -998,14 +1006,15 @@ fn flat_type_to_err_type(
|
|||
ErrorType::Type(symbol, arg_types)
|
||||
}
|
||||
|
||||
Func(arg_vars, ret_var) => {
|
||||
Func(arg_vars, closure_var, ret_var) => {
|
||||
let args = arg_vars
|
||||
.into_iter()
|
||||
.map(|arg_var| var_to_err_type(subs, state, arg_var))
|
||||
.collect();
|
||||
let ret = var_to_err_type(subs, state, ret_var);
|
||||
let closure = var_to_err_type(subs, state, closure_var);
|
||||
|
||||
ErrorType::Function(args, Box::new(ret))
|
||||
ErrorType::Function(args, Box::new(closure), Box::new(ret))
|
||||
}
|
||||
|
||||
EmptyRecord => ErrorType::Record(SendMap::default(), TypeExt::Closed),
|
||||
|
@ -1145,12 +1154,13 @@ fn restore_content(subs: &mut Subs, content: &Content) {
|
|||
}
|
||||
}
|
||||
|
||||
Func(arg_vars, ret_var) => {
|
||||
Func(arg_vars, closure_var, ret_var) => {
|
||||
for &var in arg_vars {
|
||||
subs.restore(var);
|
||||
}
|
||||
|
||||
subs.restore(*ret_var);
|
||||
subs.restore(*closure_var);
|
||||
}
|
||||
|
||||
EmptyRecord => (),
|
||||
|
|
|
@ -138,8 +138,8 @@ impl RecordField<Type> {
|
|||
pub enum Type {
|
||||
EmptyRec,
|
||||
EmptyTagUnion,
|
||||
/// A function. The types of its arguments, then the type of its return value.
|
||||
Function(Vec<Type>, Box<Type>),
|
||||
/// A function. The types of its arguments, size of its closure, then the type of its return value.
|
||||
Function(Vec<Type>, Box<Type>, Box<Type>),
|
||||
Record(SendMap<Lowercase, RecordField<Type>>, Box<Type>),
|
||||
TagUnion(Vec<(TagName, Vec<Type>)>, Box<Type>),
|
||||
Alias(Symbol, Vec<(Lowercase, Type)>, Box<Type>),
|
||||
|
@ -158,7 +158,7 @@ impl fmt::Debug for Type {
|
|||
match self {
|
||||
Type::EmptyRec => write!(f, "{{}}"),
|
||||
Type::EmptyTagUnion => write!(f, "[]"),
|
||||
Type::Function(args, ret) => {
|
||||
Type::Function(args, closure, ret) => {
|
||||
write!(f, "Fn(")?;
|
||||
|
||||
for (index, arg) in args.iter().enumerate() {
|
||||
|
@ -169,6 +169,8 @@ impl fmt::Debug for Type {
|
|||
arg.fmt(f)?;
|
||||
}
|
||||
|
||||
write!(f, " -")?;
|
||||
closure.fmt(f)?;
|
||||
write!(f, "-> ")?;
|
||||
|
||||
ret.fmt(f)?;
|
||||
|
@ -339,7 +341,7 @@ impl fmt::Debug for Type {
|
|||
|
||||
impl Type {
|
||||
pub fn arity(&self) -> usize {
|
||||
if let Type::Function(args, _) = self {
|
||||
if let Type::Function(args, _, _) = self {
|
||||
args.len()
|
||||
} else {
|
||||
0
|
||||
|
@ -369,10 +371,11 @@ impl Type {
|
|||
*self = replacement.clone();
|
||||
}
|
||||
}
|
||||
Function(args, ret) => {
|
||||
Function(args, closure, ret) => {
|
||||
for arg in args {
|
||||
arg.substitute(substitutions);
|
||||
}
|
||||
closure.substitute(substitutions);
|
||||
ret.substitute(substitutions);
|
||||
}
|
||||
TagUnion(tags, ext) => {
|
||||
|
@ -427,10 +430,11 @@ impl Type {
|
|||
use Type::*;
|
||||
|
||||
match self {
|
||||
Function(args, ret) => {
|
||||
Function(args, closure, ret) => {
|
||||
for arg in args {
|
||||
arg.substitute_alias(rep_symbol, actual);
|
||||
}
|
||||
closure.substitute_alias(rep_symbol, actual);
|
||||
ret.substitute_alias(rep_symbol, actual);
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
|
@ -472,8 +476,9 @@ impl Type {
|
|||
use Type::*;
|
||||
|
||||
match self {
|
||||
Function(args, ret) => {
|
||||
Function(args, closure, ret) => {
|
||||
ret.contains_symbol(rep_symbol)
|
||||
|| closure.contains_symbol(rep_symbol)
|
||||
|| args.iter().any(|arg| arg.contains_symbol(rep_symbol))
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
|
@ -503,8 +508,9 @@ impl Type {
|
|||
|
||||
match self {
|
||||
Variable(v) => *v == rep_variable,
|
||||
Function(args, ret) => {
|
||||
Function(args, closure, ret) => {
|
||||
ret.contains_variable(rep_variable)
|
||||
|| closure.contains_variable(rep_variable)
|
||||
|| args.iter().any(|arg| arg.contains_variable(rep_variable))
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
|
@ -553,10 +559,11 @@ impl Type {
|
|||
use Type::*;
|
||||
|
||||
match self {
|
||||
Function(args, ret) => {
|
||||
Function(args, closure, ret) => {
|
||||
for arg in args {
|
||||
arg.instantiate_aliases(region, aliases, var_store, introduced);
|
||||
}
|
||||
closure.instantiate_aliases(region, aliases, var_store, introduced);
|
||||
ret.instantiate_aliases(region, aliases, var_store, introduced);
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
|
@ -726,8 +733,9 @@ fn symbols_help(tipe: &Type, accum: &mut ImSet<Symbol>) {
|
|||
use Type::*;
|
||||
|
||||
match tipe {
|
||||
Function(args, ret) => {
|
||||
Function(args, closure, ret) => {
|
||||
symbols_help(&ret, accum);
|
||||
symbols_help(&closure, accum);
|
||||
args.iter().for_each(|arg| symbols_help(arg, accum));
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
|
@ -776,10 +784,11 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
|||
accum.insert(*v);
|
||||
}
|
||||
|
||||
Function(args, ret) => {
|
||||
Function(args, closure, ret) => {
|
||||
for arg in args {
|
||||
variables_help(arg, accum);
|
||||
}
|
||||
variables_help(closure, accum);
|
||||
variables_help(ret, accum);
|
||||
}
|
||||
Record(fields, ext) => {
|
||||
|
@ -1014,7 +1023,7 @@ pub enum ErrorType {
|
|||
Record(SendMap<Lowercase, RecordField<ErrorType>>, TypeExt),
|
||||
TagUnion(SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
||||
RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
||||
Function(Vec<ErrorType>, Box<ErrorType>),
|
||||
Function(Vec<ErrorType>, Box<ErrorType>, Box<ErrorType>),
|
||||
Alias(Symbol, Vec<(Lowercase, ErrorType)>, Box<ErrorType>),
|
||||
Boolean(boolean_algebra::Bool),
|
||||
Error,
|
||||
|
@ -1102,7 +1111,7 @@ fn write_error_type_help(
|
|||
}
|
||||
}
|
||||
}
|
||||
Function(arguments, result) => {
|
||||
Function(arguments, _closure, result) => {
|
||||
let write_parens = parens != Parens::Unnecessary;
|
||||
|
||||
if write_parens {
|
||||
|
@ -1246,7 +1255,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
|||
buf.push(')');
|
||||
}
|
||||
}
|
||||
Function(arguments, result) => {
|
||||
Function(arguments, _closure, result) => {
|
||||
let write_parens = parens != Parens::Unnecessary;
|
||||
|
||||
if write_parens {
|
||||
|
|
|
@ -140,6 +140,13 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
|||
println!("\n --- \n");
|
||||
dbg!(ctx.second, type2);
|
||||
println!("\n --------------- \n");
|
||||
println!(
|
||||
"{:?} {:?} ~ {:?} {:?}",
|
||||
ctx.first,
|
||||
subs.get(ctx.first).content,
|
||||
ctx.second,
|
||||
subs.get(ctx.second).content
|
||||
);
|
||||
}
|
||||
match &ctx.first_desc.content {
|
||||
FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content),
|
||||
|
@ -849,15 +856,23 @@ fn unify_flat_type(
|
|||
problems
|
||||
}
|
||||
}
|
||||
(Func(l_args, l_ret), Func(r_args, r_ret)) if l_args.len() == r_args.len() => {
|
||||
(Func(l_args, l_closure, l_ret), Func(r_args, r_closure, r_ret))
|
||||
if l_args.len() == r_args.len() =>
|
||||
{
|
||||
let arg_problems = unify_zip(subs, pool, l_args.iter(), r_args.iter());
|
||||
let ret_problems = unify_pool(subs, pool, *l_ret, *r_ret);
|
||||
let closure_problems = unify_pool(subs, pool, *l_closure, *r_closure);
|
||||
|
||||
if arg_problems.is_empty() && ret_problems.is_empty() {
|
||||
merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret)))
|
||||
if arg_problems.is_empty() && closure_problems.is_empty() && ret_problems.is_empty() {
|
||||
merge(
|
||||
subs,
|
||||
ctx,
|
||||
Structure(Func((*r_args).clone(), *r_closure, *r_ret)),
|
||||
)
|
||||
} else {
|
||||
let mut problems = ret_problems;
|
||||
|
||||
problems.extend(closure_problems);
|
||||
problems.extend(arg_problems);
|
||||
|
||||
problems
|
||||
|
|
|
@ -923,13 +923,17 @@ pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) {
|
|||
annotate_usage(&arg.value, usage);
|
||||
}
|
||||
}
|
||||
Closure(_, _, _, args, body) => {
|
||||
Closure {
|
||||
arguments,
|
||||
loc_body,
|
||||
..
|
||||
} => {
|
||||
// annotate defaults of optional record fields
|
||||
for (_, loc_pattern) in args {
|
||||
for (_, loc_pattern) in arguments {
|
||||
annotate_usage_pattern(&loc_pattern.value, usage);
|
||||
}
|
||||
|
||||
annotate_usage(&body.0.value, usage);
|
||||
annotate_usage(&loc_body.value, usage);
|
||||
}
|
||||
|
||||
Tag { arguments, .. } => {
|
||||
|
|
3
examples/.gitignore
vendored
3
examples/.gitignore
vendored
|
@ -1,3 +1,2 @@
|
|||
app
|
||||
*.o
|
||||
*.a
|
||||
host.o
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[link(name = "roc_app", kind = "static")]
|
||||
extern "C" {
|
||||
#[link_name = "main#1"]
|
||||
fn str_from_roc() -> *const c_char;
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let c_str = unsafe { CStr::from_ptr(str_from_roc()) };
|
||||
|
||||
println!("Roc says: {}", c_str.to_str().unwrap());
|
||||
}
|
8
examples/hello-world/platform/README.md
Normal file
8
examples/hello-world/platform/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Rebuilding the host from source
|
||||
|
||||
Run `build.sh` to manually rebuild this platform's host.
|
||||
|
||||
Note that the compiler currently has its own logic for rebuilding these hosts
|
||||
(in `link.rs`). It's hardcoded for now, but the long-term goal is that
|
||||
hosts will be precompiled by platform authors and distributed in packages,
|
||||
at which point only package authors will need to think about rebuilding hosts.
|
4
examples/hello-world/platform/build.sh
Executable file
4
examples/hello-world/platform/build.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
# We have a C host, so this is only one step.
|
||||
clang -c host.c -o host.o
|
13
examples/hello-world/platform/host.c
Normal file
13
examples/hello-world/platform/host.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include <stdio.h>
|
||||
|
||||
// TODO: use an actual RocStr here instead of char* - which is not the correct
|
||||
// type here, and which only works out to be equivalent in this particular
|
||||
// example by pure coincidence. This should be easy to segfault by returning
|
||||
// (for example) small or empty strings from Hello.roc. We should fix this!
|
||||
extern char* main_1();
|
||||
|
||||
int main() {
|
||||
printf("Roc says: %s\n", main_1());
|
||||
|
||||
return 0;
|
||||
}
|
23
examples/quicksort/platform/Cargo.lock
generated
Normal file
23
examples/quicksort/platform/Cargo.lock
generated
Normal file
|
@ -0,0 +1,23 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"roc_std 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
13
examples/quicksort/platform/Cargo.toml
Normal file
13
examples/quicksort/platform/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../roc_std" }
|
||||
|
||||
[workspace]
|
8
examples/quicksort/platform/README.md
Normal file
8
examples/quicksort/platform/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Rebuilding the host from source
|
||||
|
||||
Run `build.sh` to manually rebuild this platform's host.
|
||||
|
||||
Note that the compiler currently has its own logic for rebuilding these hosts
|
||||
(in `link.rs`). It's hardcoded for now, but the long-term goal is that
|
||||
hosts will be precompiled by platform authors and distributed in packages,
|
||||
at which point only package authors will need to think about rebuilding hosts.
|
12
examples/quicksort/platform/build.sh
Executable file
12
examples/quicksort/platform/build.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash
|
||||
|
||||
# compile c_host.o and rust_host.o
|
||||
clang -c host.c -o c_host.o
|
||||
rustc host.rs -o rust_host.o
|
||||
|
||||
# link them together into host.o
|
||||
ld -r c_host.o rust_host.o -o host.o
|
||||
|
||||
# clean up
|
||||
rm -f c_host.o
|
||||
rm -f rust_host.o
|
7
examples/quicksort/platform/host.c
Normal file
7
examples/quicksort/platform/host.c
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
extern int rust_main();
|
||||
|
||||
int main() {
|
||||
return rust_main();
|
||||
}
|
40
examples/quicksort/platform/src/lib.rs
Normal file
40
examples/quicksort/platform/src/lib.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use roc_std::RocList;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "quicksort_1"]
|
||||
fn quicksort(list: RocList<i64>) -> RocList<i64>;
|
||||
}
|
||||
|
||||
const NUM_NUMS: usize = 1_000_000;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn rust_main() -> isize {
|
||||
let nums: RocList<i64> = {
|
||||
let mut nums = Vec::with_capacity(NUM_NUMS);
|
||||
|
||||
for index in 0..nums.capacity() {
|
||||
let num = index as i64 % 12345;
|
||||
|
||||
nums.push(num);
|
||||
}
|
||||
|
||||
RocList::from_slice(&nums)
|
||||
};
|
||||
|
||||
println!("Running Roc quicksort on {} numbers...", nums.len());
|
||||
let start_time = SystemTime::now();
|
||||
let answer = unsafe { quicksort(nums) };
|
||||
let end_time = SystemTime::now();
|
||||
let duration = end_time.duration_since(start_time).unwrap();
|
||||
|
||||
println!(
|
||||
"Roc quicksort took {:.4} ms to compute this answer: {:?}",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
// truncate the answer, so stdout is not swamped
|
||||
&answer.as_slice()[0..20]
|
||||
);
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
use std::time::SystemTime;
|
||||
|
||||
#[link(name = "roc_app", kind = "static")]
|
||||
extern "C" {
|
||||
#[allow(improper_ctypes)]
|
||||
#[link_name = "quicksort#1"]
|
||||
fn quicksort(list: &[i64]) -> Box<[i64]>;
|
||||
}
|
||||
|
||||
const NUM_NUMS: usize = 1_000_000;
|
||||
|
||||
pub fn main() {
|
||||
let nums = {
|
||||
let mut nums = Vec::with_capacity(NUM_NUMS + 1);
|
||||
|
||||
// give this list refcount 1
|
||||
nums.push((std::usize::MAX - 1) as i64);
|
||||
|
||||
for index in 1..nums.capacity() {
|
||||
let num = index as i64 % 12345;
|
||||
|
||||
nums.push(num);
|
||||
}
|
||||
|
||||
nums
|
||||
};
|
||||
|
||||
println!("Running Roc shared quicksort");
|
||||
let start_time = SystemTime::now();
|
||||
let answer = unsafe { quicksort(&nums[1..]) };
|
||||
let end_time = SystemTime::now();
|
||||
let duration = end_time.duration_since(start_time).unwrap();
|
||||
|
||||
println!(
|
||||
"Roc quicksort took {:.4} ms to compute this answer: {:?}",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
// truncate the answer, so stdout is not swamped
|
||||
// NOTE index 0 is the refcount!
|
||||
&answer[1..20]
|
||||
);
|
||||
|
||||
// the pointer is to the first _element_ of the list,
|
||||
// but the refcount precedes it. Thus calling free() on
|
||||
// this pointer would segfault/cause badness. Therefore, we
|
||||
// leak it for now
|
||||
Box::leak(answer);
|
||||
}
|
49
examples/shared-quicksort/platform/README.md
Normal file
49
examples/shared-quicksort/platform/README.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Rebuilding the host from source
|
||||
|
||||
Here are the current steps to rebuild this host. These
|
||||
steps can likely be moved into a `build.rs` script after
|
||||
turning `host.rs` into a `cargo` project, but that hasn't
|
||||
been attempted yet.
|
||||
|
||||
## Compile the Rust and C sources
|
||||
|
||||
Currently this host has both a `host.rs` and a `host.c`.
|
||||
This is only because we haven't figured out a way to convince
|
||||
Rust to emit a `.o` file that doesn't define a `main` entrypoint,
|
||||
but which is capable of being linked into one later.
|
||||
|
||||
As a workaround, we have `host.rs` expose a function called
|
||||
`rust_main` instead of `main`, and all `host.c` does is provide
|
||||
an actual `main` which imports and then calls `rust_main` from
|
||||
the compiled `host.rs`. It's not the most elegant workaround,
|
||||
but [asking on `users.rust-lang.org`](https://users.rust-lang.org/t/error-when-compiling-linking-with-o-files/49635/4)
|
||||
didn't turn up any nicer approaches. Maybe they're out there though!
|
||||
|
||||
To make this workaround happen, we need to compile both `host.rs`
|
||||
and `host.c`. First, `cd` into `platform/host/src/` and then run:
|
||||
|
||||
```
|
||||
$ clang -c host.c -o c_host.o
|
||||
$ rustc host.rs -o rust_host.o
|
||||
```
|
||||
|
||||
Now we should have `c_host.o` and `rust_host.o` in the curent directory.
|
||||
|
||||
## Link together the `.o` files
|
||||
|
||||
Next, combine `c_host.o` and `rust_host.o` into `host.o` using `ld -r` like so:
|
||||
|
||||
```
|
||||
$ ld -r c_host.o rust_host.o -o host.o
|
||||
```
|
||||
|
||||
Move `host.o` into the appropriate `platform/` subdirectory
|
||||
based on your architecture and operating system. For example,
|
||||
on macOS, you'd move `host.o` into the `platform/host/x86_64-unknown-darwin10/` directory.
|
||||
|
||||
## All done!
|
||||
|
||||
Congratulations! You now have an updated host.
|
||||
|
||||
It's now fine to delete `c_host.o` and `rust_host.o`,
|
||||
since they were only needed to produce `host.o`.
|
12
examples/shared-quicksort/platform/build.sh
Executable file
12
examples/shared-quicksort/platform/build.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash
|
||||
|
||||
# compile c_host.o and rust_host.o
|
||||
clang -c host.c -o c_host.o
|
||||
rustc host.rs -o rust_host.o
|
||||
|
||||
# link them together into host.o
|
||||
ld -r c_host.o rust_host.o -o host.o
|
||||
|
||||
# clean up
|
||||
rm -f c_host.o
|
||||
rm -f rust_host.o
|
7
examples/shared-quicksort/platform/host.c
Normal file
7
examples/shared-quicksort/platform/host.c
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
extern int rust_main();
|
||||
|
||||
int main() {
|
||||
return rust_main();
|
||||
}
|
|
@ -1,15 +1,17 @@
|
|||
#![crate_type = "staticlib"]
|
||||
|
||||
use std::time::SystemTime;
|
||||
|
||||
#[link(name = "roc_app", kind = "static")]
|
||||
extern "C" {
|
||||
#[allow(improper_ctypes)]
|
||||
#[link_name = "quicksort#1"]
|
||||
#[link_name = "quicksort_1"]
|
||||
fn quicksort(list: Box<[i64]>) -> Box<[i64]>;
|
||||
}
|
||||
|
||||
const NUM_NUMS: usize = 10_000;
|
||||
|
||||
pub fn main() {
|
||||
#[no_mangle]
|
||||
pub fn rust_main() -> isize {
|
||||
let nums: Box<[i64]> = {
|
||||
let mut nums = Vec::with_capacity(NUM_NUMS);
|
||||
|
||||
|
@ -39,4 +41,7 @@ pub fn main() {
|
|||
// this pointer would segfault/cause badness. Therefore, we
|
||||
// leak it for now
|
||||
Box::leak(answer);
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
|
@ -80,17 +80,9 @@ impl<T> RocList<T> {
|
|||
}
|
||||
|
||||
fn get_storage_ptr(&self) -> *const usize {
|
||||
let elem_alignment = core::mem::align_of::<T>();
|
||||
|
||||
unsafe {
|
||||
if elem_alignment <= core::mem::align_of::<usize>() {
|
||||
let ptr = self.elements as *const usize;
|
||||
ptr.offset(-1)
|
||||
} else {
|
||||
let ptr = self.elements as *const (usize, usize);
|
||||
(ptr.offset(-1)) as *const usize
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { ptr.offset(-1) }
|
||||
}
|
||||
|
||||
fn get_storage_ptr_mut(&mut self) -> *mut usize {
|
||||
|
@ -99,14 +91,17 @@ impl<T> RocList<T> {
|
|||
|
||||
fn get_element_ptr<Q>(elements: *const Q) -> *const usize {
|
||||
let elem_alignment = core::mem::align_of::<T>();
|
||||
let ptr = elements as *const usize;
|
||||
|
||||
unsafe {
|
||||
if elem_alignment <= core::mem::align_of::<usize>() {
|
||||
let ptr = elements as *const usize;
|
||||
ptr.offset(1)
|
||||
} else {
|
||||
let ptr = elements as *const (usize, usize);
|
||||
(ptr.offset(1)) as *const usize
|
||||
// If elements have an alignment bigger than usize (e.g. an i128),
|
||||
// we will have necessarily allocated two usize slots worth of
|
||||
// space for the storage value (with the first usize slot being
|
||||
// padding for alignment's sake), and we need to skip past both.
|
||||
ptr.offset(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue