Merge remote-tracking branch 'origin/trunk' into task

This commit is contained in:
Folkert 2020-12-08 14:51:46 +01:00
commit 4c2654d4ed
84 changed files with 3514 additions and 2186 deletions

697
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,7 @@ use target_lexicon::Triple;
fn report_timing(buf: &mut String, label: &str, duration: Duration) { fn report_timing(buf: &mut String, label: &str, duration: Duration) {
buf.push_str(&format!( buf.push_str(&format!(
" {:.3} ms {}\n", " {:9.3} ms {}\n",
duration.as_secs_f64() * 1000.0, duration.as_secs_f64() * 1000.0,
label, label,
)); ));
@ -45,6 +45,9 @@ pub fn build_file(
src_dir.as_path(), src_dir.as_path(),
subs_by_module, subs_by_module,
)?; )?;
let path_to_platform = loaded.platform_path.clone();
let app_o_file = roc_file_path.with_file_name("roc_app.o"); let app_o_file = roc_file_path.with_file_name("roc_app.o");
let buf = &mut String::with_capacity(1024); let buf = &mut String::with_capacity(1024);
@ -53,7 +56,14 @@ pub fn build_file(
let module_name = loaded.interns.module_name(*module_id); let module_name = loaded.interns.module_name(*module_id);
buf.push_str(" "); buf.push_str(" ");
if module_name.is_empty() {
// the App module
buf.push_str("Application Module");
} else {
buf.push_str(module_name); buf.push_str(module_name);
}
buf.push('\n'); buf.push('\n');
report_timing(buf, "Read .roc file from disk", module_timing.read_roc_file); report_timing(buf, "Read .roc file from disk", module_timing.read_roc_file);
@ -81,15 +91,10 @@ pub fn build_file(
} }
} }
println!(
"\n\nCompilation finished! Here's how long each module took to compile:\n\n{}",
buf
);
let cwd = app_o_file.parent().unwrap(); let cwd = app_o_file.parent().unwrap();
let binary_path = cwd.join(&*loaded.output_path); // TODO should join ".exe" on Windows let binary_path = cwd.join(&*loaded.output_path); // TODO should join ".exe" on Windows
program::gen_from_mono_module( let code_gen_timing = program::gen_from_mono_module(
&arena, &arena,
loaded, loaded,
roc_file_path, roc_file_path,
@ -99,6 +104,19 @@ pub fn build_file(
emit_debug_info, emit_debug_info,
); );
buf.push('\n');
buf.push_str(" ");
buf.push_str("Code Generation");
buf.push('\n');
report_timing(buf, "Generate LLVM IR", code_gen_timing.code_gen);
report_timing(buf, "Emit .o file", code_gen_timing.emit_o_file);
println!(
"\n\nCompilation finished! Here's how long each module took to compile:\n\n{}",
buf
);
println!("\nSuccess! 🎉\n\n\t{}\n", app_o_file.display()); println!("\nSuccess! 🎉\n\n\t{}\n", app_o_file.display());
let compilation_end = compilation_start.elapsed().unwrap(); let compilation_end = compilation_start.elapsed().unwrap();
@ -112,7 +130,9 @@ pub fn build_file(
); );
// Step 2: link the precompiled host and compiled app // Step 2: link the precompiled host and compiled app
let host_input_path = cwd.join("platform").join("host.o"); let mut host_input_path = PathBuf::from(cwd);
host_input_path.push(&*path_to_platform);
host_input_path.push("host.o");
// TODO we should no longer need to do this once we have platforms on // 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. // a package repository, as we can then get precompiled hosts from there.

View file

@ -54,12 +54,12 @@ mod repl_eval {
#[test] #[test]
fn literal_0x0() { fn literal_0x0() {
expect_success("0x0", "0 : Int"); expect_success("0x0", "0 : I64");
} }
#[test] #[test]
fn literal_0x42() { fn literal_0x42() {
expect_success("0x42", "66 : Int"); expect_success("0x42", "66 : I64");
} }
#[test] #[test]
@ -79,7 +79,7 @@ mod repl_eval {
#[test] #[test]
fn int_addition() { fn int_addition() {
expect_success("0x1 + 2", "3 : Int"); expect_success("0x1 + 2", "3 : I64");
} }
#[test] #[test]
@ -89,7 +89,7 @@ mod repl_eval {
#[test] #[test]
fn num_rem() { fn num_rem() {
expect_success("299 % 10", "Ok 9 : Result Int [ DivByZero ]*"); expect_success("299 % 10", "Ok 9 : Result I64 [ DivByZero ]*");
} }
#[test] #[test]
@ -191,7 +191,7 @@ mod repl_eval {
#[test] #[test]
fn str_count_graphemes() { fn str_count_graphemes() {
expect_success("Str.countGraphemes \"å🤔\"", "2 : Int"); expect_success("Str.countGraphemes \"å🤔\"", "2 : I64");
} }
#[test] #[test]
@ -206,7 +206,7 @@ mod repl_eval {
#[test] #[test]
fn literal_int_list() { fn literal_int_list() {
expect_success("[ 0x1, 0x2, 0x3 ]", "[ 1, 2, 3 ] : List Int"); expect_success("[ 0x1, 0x2, 0x3 ]", "[ 1, 2, 3 ] : List I64");
} }
#[test] #[test]
@ -239,7 +239,7 @@ mod repl_eval {
fn nested_int_list() { fn nested_int_list() {
expect_success( expect_success(
r#"[ [ [ 4, 3, 2 ], [ 1, 0x0 ] ], [ [] ], [] ]"#, r#"[ [ [ 4, 3, 2 ], [ 1, 0x0 ] ], [ [] ], [] ]"#,
r#"[ [ [ 4, 3, 2 ], [ 1, 0 ] ], [ [] ], [] ] : List (List (List Int))"#, r#"[ [ [ 4, 3, 2 ], [ 1, 0 ] ], [ [] ], [] ] : List (List (List I64))"#,
); );
} }
@ -316,7 +316,7 @@ mod repl_eval {
fn basic_2_field_i64_record() { fn basic_2_field_i64_record() {
expect_success( expect_success(
"{ foo: 0x4, bar: 0x2 }", "{ foo: 0x4, bar: 0x2 }",
"{ bar: 2, foo: 4 } : { bar : Int, foo : Int }", "{ bar: 2, foo: 4 } : { bar : I64, foo : I64 }",
); );
} }
@ -340,7 +340,7 @@ mod repl_eval {
fn basic_3_field_record() { fn basic_3_field_record() {
expect_success( expect_success(
"{ foo: 4.1, bar: 2, baz: 0x5 }", "{ foo: 4.1, bar: 2, baz: 0x5 }",
"{ bar: 2, baz: 5, foo: 4.1 } : { bar : Num *, baz : Int, foo : F64 }", "{ bar: 2, baz: 5, foo: 4.1 } : { bar : Num *, baz : I64, foo : F64 }",
); );
} }
@ -387,7 +387,7 @@ mod repl_eval {
fn list_of_3_field_records() { fn list_of_3_field_records() {
expect_success( expect_success(
"[ { foo: 4.1, bar: 2, baz: 0x3 } ]", "[ { foo: 4.1, bar: 2, baz: 0x3 } ]",
"[ { bar: 2, baz: 3, foo: 4.1 } ] : List { bar : Num *, baz : Int, foo : F64 }", "[ { bar: 2, baz: 3, foo: 4.1 } ] : List { bar : Num *, baz : I64, foo : F64 }",
); );
} }

View file

@ -93,7 +93,7 @@ pub fn rebuild_host(host_input_path: &Path) {
.output() .output()
.unwrap(); .unwrap();
validate_output("host.rs", "cargo build --release", output); validate_output("src/lib.rs", "cargo build --release", output);
let output = Command::new("ld") let output = Command::new("ld")
.env_clear() .env_clear()

View file

@ -7,8 +7,15 @@ use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins,
use roc_load::file::MonomorphizedModule; use roc_load::file::MonomorphizedModule;
use roc_mono::layout::LayoutIds; use roc_mono::layout::LayoutIds;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};
use target_lexicon::Triple; use target_lexicon::Triple;
#[derive(Debug, Clone, Copy, Default)]
pub struct CodeGenTiming {
pub code_gen: Duration,
pub emit_o_file: Duration,
}
// TODO how should imported modules factor into this? What if those use builtins too? // TODO how should imported modules factor into this? What if those use builtins too?
// TODO this should probably use more helper functions // TODO this should probably use more helper functions
// TODO make this polymorphic in the llvm functions so it can be reused for another backend. // TODO make this polymorphic in the llvm functions so it can be reused for another backend.
@ -21,11 +28,13 @@ pub fn gen_from_mono_module(
app_o_file: &Path, app_o_file: &Path,
opt_level: OptLevel, opt_level: OptLevel,
emit_debug_info: bool, emit_debug_info: bool,
) { ) -> CodeGenTiming {
use roc_reporting::report::{ use roc_reporting::report::{
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE, can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
}; };
let code_gen_start = SystemTime::now();
for (home, (module_path, src)) in loaded.sources { for (home, (module_path, src)) in loaded.sources {
let src_lines: Vec<&str> = src.split('\n').collect(); let src_lines: Vec<&str> = src.split('\n').collect();
let palette = DEFAULT_PALETTE; let palette = DEFAULT_PALETTE;
@ -160,6 +169,9 @@ pub fn gen_from_mono_module(
// Uncomment this to see the module's optimized LLVM instruction output: // Uncomment this to see the module's optimized LLVM instruction output:
// env.module.print_to_stderr(); // env.module.print_to_stderr();
let code_gen = code_gen_start.elapsed().unwrap();
let emit_o_file_start = SystemTime::now();
// annotate the LLVM IR output with debug info // annotate the LLVM IR output with debug info
// so errors are reported with the line number of the LLVM source // so errors are reported with the line number of the LLVM source
if emit_debug_info { if emit_debug_info {
@ -232,6 +244,13 @@ pub fn gen_from_mono_module(
.write_to_file(&env.module, FileType::Object, &app_o_file) .write_to_file(&env.module, FileType::Object, &app_o_file)
.expect("Writing .o file failed"); .expect("Writing .o file failed");
} }
let emit_o_file = emit_o_file_start.elapsed().unwrap();
CodeGenTiming {
emit_o_file,
code_gen,
}
} }
pub struct FunctionIterator<'ctx> { pub struct FunctionIterator<'ctx> {

1
compiler/builtins/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
builtins.ll

View file

@ -9,16 +9,13 @@ pub fn build(b: *Builder) void {
// Options // Options
const fallback_main_path = "./src/main.zig"; const fallback_main_path = "./src/main.zig";
const main_path_desc = b.fmt("Override path to main.zig. Used by \"ir\", \"bc\", and \"test\". Defaults to \"{}\". ", .{fallback_main_path}); const main_path_desc = b.fmt("Override path to main.zig. Used by \"ir\" and \"test\". Defaults to \"{}\". ", .{fallback_main_path});
const main_path = b.option([]const u8, "main-path", main_path_desc) orelse fallback_main_path; const main_path = b.option([]const u8, "main-path", main_path_desc) orelse fallback_main_path;
const fallback_bitcode_path = "./builtins.bc";
const bitcode_path_desc = b.fmt("Override path to generated bitcode file. Used by \"ir\" and \"bc\". Defaults to \"{}\". ", .{fallback_bitcode_path});
const bitcode_path = b.option([]const u8, "bc-path", bitcode_path_desc) orelse fallback_bitcode_path;
// Tests // Tests
var main_tests = b.addTest(main_path); var main_tests = b.addTest(main_path);
main_tests.setBuildMode(mode); main_tests.setBuildMode(mode);
main_tests.linkSystemLibrary("c");
const test_step = b.step("test", "Run tests"); const test_step = b.step("test", "Run tests");
test_step.dependOn(&main_tests.step); test_step.dependOn(&main_tests.step);
@ -26,25 +23,13 @@ pub fn build(b: *Builder) void {
const obj_name = "builtins"; const obj_name = "builtins";
const obj = b.addObject(obj_name, main_path); const obj = b.addObject(obj_name, main_path);
obj.setBuildMode(mode); obj.setBuildMode(mode);
obj.linkSystemLibrary("c");
obj.strip = true; obj.strip = true;
obj.emit_llvm_ir = true; obj.emit_llvm_ir = true;
obj.emit_bin = false; obj.emit_bin = false;
const ir = b.step("ir", "Build LLVM ir"); const ir = b.step("ir", "Build LLVM ir");
ir.dependOn(&obj.step); ir.dependOn(&obj.step);
// IR to Bitcode
const bitcode_path_arg = b.fmt("-o={}", .{bitcode_path});
const ir_out_file = b.fmt("{}.ll", .{obj_name});
const ir_to_bitcode = b.addSystemCommand(&[_][]const u8{
"llvm-as-10",
ir_out_file,
bitcode_path_arg,
});
const bicode = b.step("bc", "Build LLVM ir and convert to bitcode");
bicode.dependOn(ir);
bicode.dependOn(&ir_to_bitcode.step);
b.default_step = ir; b.default_step = ir;
removeInstallSteps(b); removeInstallSteps(b);
} }

View file

@ -3,7 +3,7 @@
set -euxo pipefail set -euxo pipefail
# Test every zig # Test every zig
find src/*.zig -type f -print0 | xargs -n 1 -0 zig test --library c zig build test
# fmt every zig # fmt every zig
find src/*.zig -type f -print0 | xargs -n 1 -0 zig fmt --check find src/*.zig -type f -print0 | xargs -n 1 -0 zig fmt --check

View file

@ -4,7 +4,7 @@ const expectEqual = std.testing.expectEqual;
// This whole module is a translation of grapheme breaks from // This whole module is a translation of grapheme breaks from
// the https://github.com/JuliaStrings/utf8proc library. // the https://github.com/JuliaStrings/utf8proc library.
// Thanks so much to those developer! // Thanks so much to those developers!
// //
// The only function this file exposes is `isGraphemeBreak` // The only function this file exposes is `isGraphemeBreak`
// //

View file

@ -15,25 +15,25 @@ comptime {
// Str Module // Str Module
const str = @import("str.zig"); const str = @import("str.zig");
comptime { comptime {
exportStrFn(str.strSplitInPlace, "str_split_in_place"); exportStrFn(str.strSplitInPlaceC, "str_split_in_place");
exportStrFn(str.countSegments, "count_segments"); exportStrFn(str.countSegments, "count_segments");
exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters"); exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters");
exportStrFn(str.startsWith, "starts_with"); exportStrFn(str.startsWith, "starts_with");
exportStrFn(str.endsWith, "ends_with"); exportStrFn(str.endsWith, "ends_with");
exportStrFn(str.strConcat, "concat"); exportStrFn(str.strConcatC, "concat");
exportStrFn(str.strNumberOfBytes, "number_of_bytes"); exportStrFn(str.strNumberOfBytes, "number_of_bytes");
exportStrFn(str.strFromInt, "from_int"); exportStrFn(str.strFromIntC, "from_int");
} }
// Export helpers - Must be run inside a comptime // Export helpers - Must be run inside a comptime
fn exportBuiltinFn(comptime fn_target: anytype, comptime fn_name: []const u8) void { fn exportBuiltinFn(comptime func: anytype, comptime func_name: []const u8) void {
@export(fn_target, .{ .name = "roc_builtins." ++ fn_name, .linkage = .Strong }); @export(func, .{ .name = "roc_builtins." ++ func_name, .linkage = .Strong });
} }
fn exportNumFn(comptime fn_target: anytype, comptime fn_name: []const u8) void { fn exportNumFn(comptime func: anytype, comptime func_name: []const u8) void {
exportBuiltinFn(fn_target, "num." ++ fn_name); exportBuiltinFn(func, "num." ++ func_name);
} }
fn exportStrFn(comptime fn_target: anytype, comptime fn_name: []const u8) void { fn exportStrFn(comptime func: anytype, comptime func_name: []const u8) void {
exportBuiltinFn(fn_target, "str." ++ fn_name); exportBuiltinFn(func, "str." ++ func_name);
} }
// Run all tests in imported modules // Run all tests in imported modules

View file

@ -1,36 +1,35 @@
const std = @import("std"); const std = @import("std");
const mem = std.mem;
const Allocator = mem.Allocator;
const unicode = std.unicode; const unicode = std.unicode;
const testing = std.testing; const testing = std.testing;
const expectEqual = testing.expectEqual; const expectEqual = testing.expectEqual;
const expect = testing.expect; const expect = testing.expect;
extern fn malloc(size: usize) ?*u8;
extern fn free([*]u8) void;
const RocStr = extern struct { const RocStr = extern struct {
str_bytes: ?[*]u8, str_bytes: ?[*]u8,
str_len: usize, str_len: usize,
pub fn empty() RocStr { pub inline fn empty() RocStr {
return RocStr{ return RocStr{
.str_len = 0, .str_len = 0,
.str_bytes = null, .str_bytes = null,
}; };
} }
// This takes ownership of the pointed-to bytes if they won't fit in a // This clones the pointed-to bytes if they won't fit in a
// small string, and returns a (pointer, len) tuple which points to them. // small string, and returns a (pointer, len) tuple which points to them.
pub fn init(bytes: [*]const u8, length: usize) RocStr { pub fn init(allocator: *Allocator, bytes_ptr: [*]const u8, length: usize) RocStr {
const rocStrSize = @sizeOf(RocStr); const roc_str_size = @sizeOf(RocStr);
if (length < rocStrSize) { if (length < roc_str_size) {
var ret_small_str = RocStr.empty(); var ret_small_str = RocStr.empty();
const target_ptr = @ptrToInt(&ret_small_str); const target_ptr = @ptrToInt(&ret_small_str);
var index: u8 = 0; var index: u8 = 0;
// TODO isn't there a way to bulk-zero data in Zig? // TODO isn't there a way to bulk-zero data in Zig?
// Zero out the data, just to be safe // Zero out the data, just to be safe
while (index < rocStrSize) { while (index < roc_str_size) {
var offset_ptr = @intToPtr(*u8, target_ptr + index); var offset_ptr = @intToPtr(*u8, target_ptr + index);
offset_ptr.* = 0; offset_ptr.* = 0;
index += 1; index += 1;
@ -40,19 +39,19 @@ const RocStr = extern struct {
index = 0; index = 0;
while (index < length) { while (index < length) {
var offset_ptr = @intToPtr(*u8, target_ptr + index); var offset_ptr = @intToPtr(*u8, target_ptr + index);
offset_ptr.* = bytes[index]; offset_ptr.* = bytes_ptr[index];
index += 1; index += 1;
} }
// set the final byte to be the length // set the final byte to be the length
const final_byte_ptr = @intToPtr(*u8, target_ptr + rocStrSize - 1); const final_byte_ptr = @intToPtr(*u8, target_ptr + roc_str_size - 1);
final_byte_ptr.* = @truncate(u8, length) ^ 0b10000000; final_byte_ptr.* = @truncate(u8, length) ^ 0b10000000;
return ret_small_str; return ret_small_str;
} else { } else {
var result = allocate_str(u64, InPlace.Clone, length); var result = allocateStr(allocator, u64, InPlace.Clone, length);
@memcpy(@ptrCast([*]u8, result.str_bytes), bytes, length); @memcpy(@ptrCast([*]u8, result.str_bytes), bytes_ptr, length);
return result; return result;
} }
@ -61,25 +60,28 @@ const RocStr = extern struct {
// This takes ownership of the pointed-to bytes if they won't fit in a // This takes ownership of the pointed-to bytes if they won't fit in a
// small string, and returns a (pointer, len) tuple which points to them. // small string, and returns a (pointer, len) tuple which points to them.
pub fn withCapacity(length: usize) RocStr { pub fn withCapacity(length: usize) RocStr {
const rocStrSize = @sizeOf(RocStr); const roc_str_size = @sizeOf(RocStr);
if (length < rocStrSize) { if (length < roc_str_size) {
return RocStr.empty(); return RocStr.empty();
} else { } else {
var new_bytes: [*]u8 = @ptrCast([*]u8, malloc(length)); var new_bytes: []T = allocator.alloc(u8, length) catch unreachable;
var new_bytes_ptr: [*]u8 = @ptrCast([*]u8, &new_bytes);
return RocStr{ return RocStr{
.str_bytes = new_bytes, .str_bytes = new_bytes_ptr,
.str_len = length, .str_len = length,
}; };
} }
} }
pub fn drop(self: RocStr) void { pub fn deinit(self: RocStr, allocator: *Allocator) void {
if (!self.is_small_str()) { if (!self.isSmallStr() and !self.isEmpty()) {
const str_bytes: [*]u8 = self.str_bytes orelse unreachable; const str_bytes_ptr: [*]u8 = self.str_bytes orelse unreachable;
free(str_bytes); const str_bytes: []u8 = str_bytes_ptr[0..self.str_len];
allocator.free(str_bytes);
} }
} }
@ -102,8 +104,8 @@ const RocStr = extern struct {
const self_u8_ptr: [*]const u8 = @ptrCast([*]const u8, &self); const self_u8_ptr: [*]const u8 = @ptrCast([*]const u8, &self);
const other_u8_ptr: [*]const u8 = @ptrCast([*]const u8, &other); const other_u8_ptr: [*]const u8 = @ptrCast([*]const u8, &other);
const self_bytes: [*]const u8 = if (self.is_small_str() or self.is_empty()) self_u8_ptr else self_bytes_ptr orelse unreachable; const self_bytes: [*]const u8 = if (self.isSmallStr() or self.isEmpty()) self_u8_ptr else self_bytes_ptr orelse unreachable;
const other_bytes: [*]const u8 = if (other.is_small_str() or other.is_empty()) other_u8_ptr else other_bytes_ptr orelse unreachable; const other_bytes: [*]const u8 = if (other.isSmallStr() or other.isEmpty()) other_u8_ptr else other_bytes_ptr orelse unreachable;
var index: usize = 0; var index: usize = 0;
@ -120,7 +122,7 @@ const RocStr = extern struct {
return true; return true;
} }
pub fn is_small_str(self: RocStr) bool { pub fn isSmallStr(self: RocStr) bool {
return @bitCast(isize, self.str_len) < 0; return @bitCast(isize, self.str_len) < 0;
} }
@ -132,17 +134,17 @@ const RocStr = extern struct {
// Since this conditional would be prone to branch misprediction, // Since this conditional would be prone to branch misprediction,
// make sure it will compile to a cmov. // make sure it will compile to a cmov.
return if (self.is_small_str()) small_len else big_len; return if (self.isSmallStr()) small_len else big_len;
} }
pub fn is_empty(self: RocStr) bool { pub fn isEmpty(self: RocStr) bool {
return self.len() == 0; return self.len() == 0;
} }
pub fn as_u8_ptr(self: RocStr) [*]u8 { pub fn asU8ptr(self: RocStr) [*]u8 {
const if_small = &@bitCast([16]u8, self); const if_small = &@bitCast([16]u8, self);
const if_big = @ptrCast([*]u8, self.str_bytes); const if_big = @ptrCast([*]u8, self.str_bytes);
return if (self.is_small_str() or self.is_empty()) if_small else if_big; return if (self.isSmallStr() or self.isEmpty()) if_small else if_big;
} }
// Given a pointer to some bytes, write the first (len) bytes of this // Given a pointer to some bytes, write the first (len) bytes of this
@ -160,7 +162,7 @@ const RocStr = extern struct {
// Since this conditional would be prone to branch misprediction, // Since this conditional would be prone to branch misprediction,
// make sure it will compile to a cmov. // make sure it will compile to a cmov.
const src: [*]u8 = if (self.is_small_str()) small_src else big_src; const src: [*]u8 = if (self.isSmallStr()) small_src else big_src;
@memcpy(dest, src, len); @memcpy(dest, src, len);
} }
@ -169,70 +171,77 @@ const RocStr = extern struct {
const str1_len = 3; const str1_len = 3;
var str1: [str1_len]u8 = "abc".*; var str1: [str1_len]u8 = "abc".*;
const str1_ptr: [*]u8 = &str1; const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len); var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
const str2_len = 3; const str2_len = 3;
var str2: [str2_len]u8 = "abc".*; var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2; const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len); var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
// TODO: fix those tests // TODO: fix those tests
// expect(roc_str1.eq(roc_str2)); // expect(roc_str1.eq(roc_str2));
roc_str1.drop(); roc_str1.deinit(testing.allocator);
roc_str2.drop(); roc_str2.deinit(testing.allocator);
} }
test "RocStr.eq: not equal different length" { test "RocStr.eq: not equal different length" {
const str1_len = 4; const str1_len = 4;
var str1: [str1_len]u8 = "abcd".*; var str1: [str1_len]u8 = "abcd".*;
const str1_ptr: [*]u8 = &str1; const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len); var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
const str2_len = 3; const str2_len = 3;
var str2: [str2_len]u8 = "abc".*; var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2; const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len); var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
defer {
roc_str1.deinit(testing.allocator);
roc_str2.deinit(testing.allocator);
}
expect(!roc_str1.eq(roc_str2)); expect(!roc_str1.eq(roc_str2));
roc_str1.drop();
roc_str2.drop();
} }
test "RocStr.eq: not equal same length" { test "RocStr.eq: not equal same length" {
const str1_len = 3; const str1_len = 3;
var str1: [str1_len]u8 = "acb".*; var str1: [str1_len]u8 = "acb".*;
const str1_ptr: [*]u8 = &str1; const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len); var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
const str2_len = 3; const str2_len = 3;
var str2: [str2_len]u8 = "abc".*; var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2; const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len); var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
defer {
roc_str1.deinit(testing.allocator);
roc_str2.deinit(testing.allocator);
}
// TODO: fix those tests // TODO: fix those tests
// expect(!roc_str1.eq(roc_str2)); // expect(!roc_str1.eq(roc_str2));
roc_str1.drop();
roc_str2.drop();
} }
}; };
// Str.numberOfBytes // Str.numberOfBytes
pub fn strNumberOfBytes(string: RocStr) callconv(.C) usize { pub fn strNumberOfBytes(string: RocStr) callconv(.C) usize {
return string.len(); return string.len();
} }
// Str.fromInt // Str.fromInt
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
pub fn strFromInt(int: i64) callconv(.C) RocStr { pub fn strFromIntC(int: i64) callconv(.C) RocStr {
// prepare for having multiple integer types in the future return strFromInt(std.heap.c_allocator, int);
return strFromIntHelp(i64, int);
} }
fn strFromIntHelp(comptime T: type, int: T) RocStr { inline fn strFromInt(allocator: *Allocator, int: i64) RocStr {
// prepare for having multiple integer types in the future
return strFromIntHelp(allocator, i64, int);
}
fn strFromIntHelp(allocator: *Allocator, comptime T: type, int: T) RocStr {
// determine maximum size for this T // determine maximum size for this T
comptime const size = comptime blk: { comptime const size = comptime blk: {
// the string representation of the minimum i128 value uses at most 40 characters // the string representation of the minimum i128 value uses at most 40 characters
@ -244,20 +253,24 @@ fn strFromIntHelp(comptime T: type, int: T) RocStr {
var buf: [size]u8 = undefined; var buf: [size]u8 = undefined;
const result = std.fmt.bufPrint(&buf, "{}", .{int}) catch unreachable; const result = std.fmt.bufPrint(&buf, "{}", .{int}) catch unreachable;
return RocStr.init(&buf, result.len); return RocStr.init(allocator, &buf, result.len);
} }
// Str.split // Str.split
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
pub fn strSplitInPlaceC(array: [*]RocStr, string: RocStr, delimiter: RocStr) callconv(.C) void {
strSplitInPlace(std.heap.c_allocator, array, string, delimiter);
}
pub fn strSplitInPlace(array: [*]RocStr, array_len: usize, string: RocStr, delimiter: RocStr) callconv(.C) void { inline fn strSplitInPlace(allocator: *Allocator, array: [*]RocStr, string: RocStr, delimiter: RocStr) void {
var ret_array_index: usize = 0; var ret_array_index: usize = 0;
var sliceStart_index: usize = 0; var slice_start_index: usize = 0;
var str_index: usize = 0; var str_index: usize = 0;
const str_bytes = string.as_u8_ptr(); const str_bytes = string.asU8ptr();
const str_len = string.len(); const str_len = string.len();
const delimiter_bytes_ptrs = delimiter.as_u8_ptr(); const delimiter_bytes_ptrs = delimiter.asU8ptr();
const delimiter_len = delimiter.len(); const delimiter_len = delimiter.len();
if (str_len > delimiter_len) { if (str_len > delimiter_len) {
@ -279,10 +292,10 @@ pub fn strSplitInPlace(array: [*]RocStr, array_len: usize, string: RocStr, delim
} }
if (matches_delimiter) { if (matches_delimiter) {
const segment_len: usize = str_index - sliceStart_index; const segment_len: usize = str_index - slice_start_index;
array[ret_array_index] = RocStr.init(str_bytes + sliceStart_index, segment_len); array[ret_array_index] = RocStr.init(allocator, str_bytes + slice_start_index, segment_len);
sliceStart_index = str_index + delimiter_len; slice_start_index = str_index + delimiter_len;
ret_array_index += 1; ret_array_index += 1;
str_index += delimiter_len; str_index += delimiter_len;
} else { } else {
@ -291,44 +304,49 @@ pub fn strSplitInPlace(array: [*]RocStr, array_len: usize, string: RocStr, delim
} }
} }
array[ret_array_index] = RocStr.init(str_bytes + sliceStart_index, str_len - sliceStart_index); array[ret_array_index] = RocStr.init(allocator, str_bytes + slice_start_index, str_len - slice_start_index);
} }
test "strSplitInPlace: no delimiter" { test "strSplitInPlace: no delimiter" {
// Str.split "abc" "!" == [ "abc" ] // Str.split "abc" "!" == [ "abc" ]
const str_arr = "abc"; const str_arr = "abc";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "!"; const delimiter_arr = "!";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
var array: [1]RocStr = undefined; var array: [1]RocStr = undefined;
const array_ptr: [*]RocStr = &array; const array_ptr: [*]RocStr = &array;
strSplitInPlace(array_ptr, 1, str, delimiter); strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
var expected = [1]RocStr{ var expected = [1]RocStr{
str, str,
}; };
expectEqual(array.len, expected.len); defer {
expect(array[0].eq(expected[0]));
for (array) |roc_str| { for (array) |roc_str| {
roc_str.drop(); roc_str.deinit(testing.allocator);
} }
for (expected) |roc_str| { for (expected) |roc_str| {
roc_str.drop(); roc_str.deinit(testing.allocator);
} }
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
expectEqual(array.len, expected.len);
expect(array[0].eq(expected[0]));
} }
test "strSplitInPlace: empty end" { test "strSplitInPlace: empty end" {
const str_arr = "1---- ---- ---- ---- ----2---- ---- ---- ---- ----"; const str_arr = "1---- ---- ---- ---- ----2---- ---- ---- ---- ----";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "---- ---- ---- ---- ----"; const delimiter_arr = "---- ---- ---- ---- ----";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
const array_len: usize = 3; const array_len: usize = 3;
var array: [array_len]RocStr = [_]RocStr{ var array: [array_len]RocStr = [_]RocStr{
@ -338,15 +356,28 @@ test "strSplitInPlace: empty end" {
}; };
const array_ptr: [*]RocStr = &array; const array_ptr: [*]RocStr = &array;
strSplitInPlace(array_ptr, array_len, str, delimiter); strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
const one = RocStr.init("1", 1); const one = RocStr.init(testing.allocator, "1", 1);
const two = RocStr.init("2", 1); const two = RocStr.init(testing.allocator, "2", 1);
var expected = [3]RocStr{ var expected = [3]RocStr{
one, two, RocStr.empty(), one, two, RocStr.empty(),
}; };
defer {
for (array) |rocStr| {
rocStr.deinit(testing.allocator);
}
for (expected) |rocStr| {
rocStr.deinit(testing.allocator);
}
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
expectEqual(array.len, expected.len); expectEqual(array.len, expected.len);
expect(array[0].eq(expected[0])); expect(array[0].eq(expected[0]));
expect(array[1].eq(expected[1])); expect(array[1].eq(expected[1]));
@ -355,10 +386,10 @@ test "strSplitInPlace: empty end" {
test "strSplitInPlace: delimiter on sides" { test "strSplitInPlace: delimiter on sides" {
const str_arr = "tttghittt"; const str_arr = "tttghittt";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "ttt"; const delimiter_arr = "ttt";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
const array_len: usize = 3; const array_len: usize = 3;
var array: [array_len]RocStr = [_]RocStr{ var array: [array_len]RocStr = [_]RocStr{
@ -367,15 +398,28 @@ test "strSplitInPlace: delimiter on sides" {
undefined, undefined,
}; };
const array_ptr: [*]RocStr = &array; const array_ptr: [*]RocStr = &array;
strSplitInPlace(array_ptr, array_len, str, delimiter); strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
const ghi_arr = "ghi"; const ghi_arr = "ghi";
const ghi = RocStr.init(ghi_arr, ghi_arr.len); const ghi = RocStr.init(testing.allocator, ghi_arr, ghi_arr.len);
var expected = [3]RocStr{ var expected = [3]RocStr{
RocStr.empty(), ghi, RocStr.empty(), RocStr.empty(), ghi, RocStr.empty(),
}; };
defer {
for (array) |rocStr| {
rocStr.deinit(testing.allocator);
}
for (expected) |rocStr| {
rocStr.deinit(testing.allocator);
}
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
expectEqual(array.len, expected.len); expectEqual(array.len, expected.len);
expect(array[0].eq(expected[0])); expect(array[0].eq(expected[0]));
expect(array[1].eq(expected[1])); expect(array[1].eq(expected[1]));
@ -385,25 +429,38 @@ test "strSplitInPlace: delimiter on sides" {
test "strSplitInPlace: three pieces" { test "strSplitInPlace: three pieces" {
// Str.split "a!b!c" "!" == [ "a", "b", "c" ] // Str.split "a!b!c" "!" == [ "a", "b", "c" ]
const str_arr = "a!b!c"; const str_arr = "a!b!c";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "!"; const delimiter_arr = "!";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
const array_len: usize = 3; const array_len: usize = 3;
var array: [array_len]RocStr = undefined; var array: [array_len]RocStr = undefined;
const array_ptr: [*]RocStr = &array; const array_ptr: [*]RocStr = &array;
strSplitInPlace(array_ptr, array_len, str, delimiter); strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
const a = RocStr.init("a", 1); const a = RocStr.init(testing.allocator, "a", 1);
const b = RocStr.init("b", 1); const b = RocStr.init(testing.allocator, "b", 1);
const c = RocStr.init("c", 1); const c = RocStr.init(testing.allocator, "c", 1);
var expected_array = [array_len]RocStr{ var expected_array = [array_len]RocStr{
a, b, c, a, b, c,
}; };
defer {
for (array) |roc_str| {
roc_str.deinit(testing.allocator);
}
for (expected_array) |roc_str| {
roc_str.deinit(testing.allocator);
}
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
expectEqual(expected_array.len, array.len); expectEqual(expected_array.len, array.len);
expect(array[0].eq(expected_array[0])); expect(array[0].eq(expected_array[0]));
expect(array[1].eq(expected_array[1])); expect(array[1].eq(expected_array[1]));
@ -415,10 +472,10 @@ test "strSplitInPlace: three pieces" {
// needs to be broken into, so that we can allocate a array // needs to be broken into, so that we can allocate a array
// of that size. It always returns at least 1. // of that size. It always returns at least 1.
pub fn countSegments(string: RocStr, delimiter: RocStr) callconv(.C) usize { pub fn countSegments(string: RocStr, delimiter: RocStr) callconv(.C) usize {
const str_bytes = string.as_u8_ptr(); const str_bytes = string.asU8ptr();
const str_len = string.len(); const str_len = string.len();
const delimiter_bytes_ptrs = delimiter.as_u8_ptr(); const delimiter_bytes_ptrs = delimiter.asU8ptr();
const delimiter_len = delimiter.len(); const delimiter_len = delimiter.len();
var count: usize = 1; var count: usize = 1;
@ -459,13 +516,17 @@ test "countSegments: long delimiter" {
// Str.split "str" "delimiter" == [ "str" ] // Str.split "str" "delimiter" == [ "str" ]
// 1 segment // 1 segment
const str_arr = "str"; const str_arr = "str";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "delimiter"; const delimiter_arr = "delimiter";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
defer {
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
const segments_count = countSegments(str, delimiter); const segments_count = countSegments(str, delimiter);
expectEqual(segments_count, 1); expectEqual(segments_count, 1);
} }
@ -473,10 +534,15 @@ test "countSegments: delimiter at start" {
// Str.split "hello there" "hello" == [ "", " there" ] // Str.split "hello there" "hello" == [ "", " there" ]
// 2 segments // 2 segments
const str_arr = "hello there"; const str_arr = "hello there";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "hello"; const delimiter_arr = "hello";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
defer {
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
const segments_count = countSegments(str, delimiter); const segments_count = countSegments(str, delimiter);
@ -487,10 +553,15 @@ test "countSegments: delimiter interspered" {
// Str.split "a!b!c" "!" == [ "a", "b", "c" ] // Str.split "a!b!c" "!" == [ "a", "b", "c" ]
// 3 segments // 3 segments
const str_arr = "a!b!c"; const str_arr = "a!b!c";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "!"; const delimiter_arr = "!";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
defer {
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
const segments_count = countSegments(str, delimiter); const segments_count = countSegments(str, delimiter);
@ -499,14 +570,13 @@ test "countSegments: delimiter interspered" {
// Str.countGraphemeClusters // Str.countGraphemeClusters
const grapheme = @import("helpers/grapheme.zig"); const grapheme = @import("helpers/grapheme.zig");
pub fn countGraphemeClusters(string: RocStr) callconv(.C) usize { pub fn countGraphemeClusters(string: RocStr) callconv(.C) usize {
if (string.is_empty()) { if (string.isEmpty()) {
return 0; return 0;
} }
const bytes_len = string.len(); const bytes_len = string.len();
const bytes_ptr = string.as_u8_ptr(); const bytes_ptr = string.asU8ptr();
var bytes = bytes_ptr[0..bytes_len]; var bytes = bytes_ptr[0..bytes_len];
var iter = (unicode.Utf8View.init(bytes) catch unreachable).iterator(); var iter = (unicode.Utf8View.init(bytes) catch unreachable).iterator();
@ -535,7 +605,7 @@ pub fn countGraphemeClusters(string: RocStr) callconv(.C) usize {
return count; return count;
} }
fn roc_str_from_literal(bytes_arr: *const []u8) RocStr {} fn rocStrFromLiteral(bytes_arr: *const []u8) RocStr {}
test "countGraphemeClusters: empty string" { test "countGraphemeClusters: empty string" {
const count = countGraphemeClusters(RocStr.empty()); const count = countGraphemeClusters(RocStr.empty());
@ -545,46 +615,60 @@ test "countGraphemeClusters: empty string" {
test "countGraphemeClusters: ascii characters" { test "countGraphemeClusters: ascii characters" {
const bytes_arr = "abcd"; const bytes_arr = "abcd";
const bytes_len = bytes_arr.len; const bytes_len = bytes_arr.len;
const count = countGraphemeClusters(RocStr.init(bytes_arr, bytes_len)); const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
defer str.deinit(testing.allocator);
const count = countGraphemeClusters(str);
expectEqual(count, 4); expectEqual(count, 4);
} }
test "countGraphemeClusters: utf8 characters" { test "countGraphemeClusters: utf8 characters" {
const bytes_arr = "ãxā"; const bytes_arr = "ãxā";
const bytes_len = bytes_arr.len; const bytes_len = bytes_arr.len;
const count = countGraphemeClusters(RocStr.init(bytes_arr, bytes_len)); const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
defer str.deinit(testing.allocator);
const count = countGraphemeClusters(str);
expectEqual(count, 3); expectEqual(count, 3);
} }
test "countGraphemeClusters: emojis" { test "countGraphemeClusters: emojis" {
const bytes_arr = "🤔🤔🤔"; const bytes_arr = "🤔🤔🤔";
const bytes_len = bytes_arr.len; const bytes_len = bytes_arr.len;
const count = countGraphemeClusters(RocStr.init(bytes_arr, bytes_len)); const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
defer str.deinit(testing.allocator);
const count = countGraphemeClusters(str);
expectEqual(count, 3); expectEqual(count, 3);
} }
test "countGraphemeClusters: emojis and ut8 characters" { test "countGraphemeClusters: emojis and ut8 characters" {
const bytes_arr = "🤔å🤔¥🤔ç"; const bytes_arr = "🤔å🤔¥🤔ç";
const bytes_len = bytes_arr.len; const bytes_len = bytes_arr.len;
const count = countGraphemeClusters(RocStr.init(bytes_arr, bytes_len)); const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
defer str.deinit(testing.allocator);
const count = countGraphemeClusters(str);
expectEqual(count, 6); expectEqual(count, 6);
} }
test "countGraphemeClusters: emojis, ut8, and ascii characters" { test "countGraphemeClusters: emojis, ut8, and ascii characters" {
const bytes_arr = "6🤔å🤔e¥🤔çpp"; const bytes_arr = "6🤔å🤔e¥🤔çpp";
const bytes_len = bytes_arr.len; const bytes_len = bytes_arr.len;
const count = countGraphemeClusters(RocStr.init(bytes_arr, bytes_len)); const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
defer str.deinit(testing.allocator);
const count = countGraphemeClusters(str);
expectEqual(count, 10); expectEqual(count, 10);
} }
// Str.startsWith // Str.startsWith
pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool { pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool {
const bytes_len = string.len(); const bytes_len = string.len();
const bytes_ptr = string.as_u8_ptr(); const bytes_ptr = string.asU8ptr();
const prefix_len = prefix.len(); const prefix_len = prefix.len();
const prefix_ptr = prefix.as_u8_ptr(); const prefix_ptr = prefix.asU8ptr();
if (prefix_len > bytes_len) { if (prefix_len > bytes_len) {
return false; return false;
@ -602,31 +686,33 @@ pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool {
} }
test "startsWith: foo starts with fo" { test "startsWith: foo starts with fo" {
const foo = RocStr.init("foo", 3); const foo = RocStr.init(testing.allocator, "foo", 3);
const fo = RocStr.init("fo", 2); const fo = RocStr.init(testing.allocator, "fo", 2);
expect(startsWith(foo, fo)); expect(startsWith(foo, fo));
} }
test "startsWith: 123456789123456789 starts with 123456789123456789" { test "startsWith: 123456789123456789 starts with 123456789123456789" {
const str = RocStr.init("123456789123456789", 18); const str = RocStr.init(testing.allocator, "123456789123456789", 18);
defer str.deinit(testing.allocator);
expect(startsWith(str, str)); expect(startsWith(str, str));
} }
test "startsWith: 12345678912345678910 starts with 123456789123456789" { test "startsWith: 12345678912345678910 starts with 123456789123456789" {
const str = RocStr.init("12345678912345678910", 20); const str = RocStr.init(testing.allocator, "12345678912345678910", 20);
const prefix = RocStr.init("123456789123456789", 18); defer str.deinit(testing.allocator);
const prefix = RocStr.init(testing.allocator, "123456789123456789", 18);
defer prefix.deinit(testing.allocator);
expect(startsWith(str, prefix)); expect(startsWith(str, prefix));
} }
// Str.endsWith // Str.endsWith
pub fn endsWith(string: RocStr, suffix: RocStr) callconv(.C) bool { pub fn endsWith(string: RocStr, suffix: RocStr) callconv(.C) bool {
const bytes_len = string.len(); const bytes_len = string.len();
const bytes_ptr = string.as_u8_ptr(); const bytes_ptr = string.asU8ptr();
const suffix_len = suffix.len(); const suffix_len = suffix.len();
const suffix_ptr = suffix.as_u8_ptr(); const suffix_ptr = suffix.asU8ptr();
if (suffix_len > bytes_len) { if (suffix_len > bytes_len) {
return false; return false;
@ -644,71 +730,57 @@ pub fn endsWith(string: RocStr, suffix: RocStr) callconv(.C) bool {
} }
test "endsWith: foo ends with oo" { test "endsWith: foo ends with oo" {
const foo = RocStr.init("foo", 3); const foo = RocStr.init(testing.allocator, "foo", 3);
const oo = RocStr.init("oo", 2); const oo = RocStr.init(testing.allocator, "oo", 2);
defer foo.deinit(testing.allocator);
defer oo.deinit(testing.allocator);
expect(endsWith(foo, oo)); expect(endsWith(foo, oo));
} }
test "endsWith: 123456789123456789 ends with 123456789123456789" { test "endsWith: 123456789123456789 ends with 123456789123456789" {
const str = RocStr.init("123456789123456789", 18); const str = RocStr.init(testing.allocator, "123456789123456789", 18);
defer str.deinit(testing.allocator);
expect(endsWith(str, str)); expect(endsWith(str, str));
} }
test "endsWith: 12345678912345678910 ends with 345678912345678910" { test "endsWith: 12345678912345678910 ends with 345678912345678910" {
const str = RocStr.init("12345678912345678910", 20); const str = RocStr.init(testing.allocator, "12345678912345678910", 20);
const suffix = RocStr.init("345678912345678910", 18); const suffix = RocStr.init(testing.allocator, "345678912345678910", 18);
defer str.deinit(testing.allocator);
defer suffix.deinit(testing.allocator);
expect(endsWith(str, suffix)); expect(endsWith(str, suffix));
} }
test "endsWith: hello world ends with world" { test "endsWith: hello world ends with world" {
const str = RocStr.init("hello world", 11); const str = RocStr.init(testing.allocator, "hello world", 11);
const suffix = RocStr.init("world", 5); const suffix = RocStr.init(testing.allocator, "world", 5);
defer str.deinit(testing.allocator);
defer suffix.deinit(testing.allocator);
expect(endsWith(str, suffix)); expect(endsWith(str, suffix));
} }
// Str.concat // Str.concat
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
test "RocStr.concat: small concat small" { pub fn strConcatC(ptr_size: u32, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) callconv(.C) RocStr {
const str1_len = 3; return strConcat(std.heap.c_allocator, ptr_size, result_in_place, arg1, arg2);
var str1: [str1_len]u8 = "foo".*;
const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len);
const str2_len = 3;
var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len);
const str3_len = 6;
var str3: [str3_len]u8 = "fooabc".*;
const str3_ptr: [*]u8 = &str3;
var roc_str3 = RocStr.init(str3_ptr, str3_len);
const result = strConcat(8, InPlace.Clone, roc_str1, roc_str2);
expect(roc_str3.eq(result));
roc_str1.drop();
roc_str2.drop();
roc_str3.drop();
result.drop();
} }
pub fn strConcat(ptr_size: u32, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) callconv(.C) RocStr { inline fn strConcat(allocator: *Allocator, ptr_size: u32, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
return switch (ptr_size) { return switch (ptr_size) {
4 => strConcatHelp(i32, result_in_place, arg1, arg2), 4 => strConcatHelp(allocator, i32, result_in_place, arg1, arg2),
8 => strConcatHelp(i64, result_in_place, arg1, arg2), 8 => strConcatHelp(allocator, i64, result_in_place, arg1, arg2),
else => unreachable, else => unreachable,
}; };
} }
fn strConcatHelp(comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr { fn strConcatHelp(allocator: *Allocator, comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
if (arg1.is_empty()) { if (arg1.isEmpty()) {
return cloneStr(T, result_in_place, arg2); return cloneStr(allocator, T, result_in_place, arg2);
} else if (arg2.is_empty()) { } else if (arg2.isEmpty()) {
return cloneStr(T, result_in_place, arg1); return cloneStr(allocator, T, result_in_place, arg1);
} else { } else {
const combined_length = arg1.len() + arg2.len(); const combined_length = arg1.len() + arg2.len();
@ -716,12 +788,12 @@ fn strConcatHelp(comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2:
const result_is_big = combined_length >= small_str_bytes; const result_is_big = combined_length >= small_str_bytes;
if (result_is_big) { if (result_is_big) {
var result = allocate_str(T, result_in_place, combined_length); var result = allocateStr(allocator, T, result_in_place, combined_length);
{ {
const old_if_small = &@bitCast([16]u8, arg1); const old_if_small = &@bitCast([16]u8, arg1);
const old_if_big = @ptrCast([*]u8, arg1.str_bytes); const old_if_big = @ptrCast([*]u8, arg1.str_bytes);
const old_bytes = if (arg1.is_small_str()) old_if_small else old_if_big; const old_bytes = if (arg1.isSmallStr()) old_if_small else old_if_big;
const new_bytes: [*]u8 = @ptrCast([*]u8, result.str_bytes); const new_bytes: [*]u8 = @ptrCast([*]u8, result.str_bytes);
@ -731,7 +803,7 @@ fn strConcatHelp(comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2:
{ {
const old_if_small = &@bitCast([16]u8, arg2); const old_if_small = &@bitCast([16]u8, arg2);
const old_if_big = @ptrCast([*]u8, arg2.str_bytes); const old_if_big = @ptrCast([*]u8, arg2.str_bytes);
const old_bytes = if (arg2.is_small_str()) old_if_small else old_if_big; const old_bytes = if (arg2.isSmallStr()) old_if_small else old_if_big;
const new_bytes = @ptrCast([*]u8, result.str_bytes) + arg1.len(); const new_bytes = @ptrCast([*]u8, result.str_bytes) + arg1.len();
@ -775,12 +847,12 @@ const InPlace = packed enum(u8) {
Clone, Clone,
}; };
fn cloneStr(comptime T: type, in_place: InPlace, str: RocStr) RocStr { fn cloneStr(allocator: *Allocator, comptime T: type, in_place: InPlace, str: RocStr) RocStr {
if (str.is_small_str() or str.is_empty()) { if (str.isSmallStr() or str.isEmpty()) {
// just return the bytes // just return the bytes
return str; return str;
} else { } else {
var new_str = allocate_str(T, in_place, str.str_len); var new_str = allocateStr(allocator, T, in_place, str.str_len);
var old_bytes: [*]u8 = @ptrCast([*]u8, str.str_bytes); var old_bytes: [*]u8 = @ptrCast([*]u8, str.str_bytes);
var new_bytes: [*]u8 = @ptrCast([*]u8, new_str.str_bytes); var new_bytes: [*]u8 = @ptrCast([*]u8, new_str.str_bytes);
@ -791,9 +863,9 @@ fn cloneStr(comptime T: type, in_place: InPlace, str: RocStr) RocStr {
} }
} }
fn allocate_str(comptime T: type, in_place: InPlace, number_of_chars: u64) RocStr { fn allocateStr(allocator: *Allocator, comptime T: type, in_place: InPlace, number_of_chars: u64) RocStr {
const length = @sizeOf(T) + number_of_chars; const length = @sizeOf(T) + number_of_chars;
var new_bytes: [*]T = @ptrCast([*]T, @alignCast(@alignOf(T), malloc(length))); var new_bytes: []T = allocator.alloc(T, length) catch unreachable;
if (in_place == InPlace.InPlace) { if (in_place == InPlace.InPlace) {
new_bytes[0] = @intCast(T, number_of_chars); new_bytes[0] = @intCast(T, number_of_chars);
@ -802,10 +874,39 @@ fn allocate_str(comptime T: type, in_place: InPlace, number_of_chars: u64) RocSt
} }
var first_element = @ptrCast([*]align(@alignOf(T)) u8, new_bytes); var first_element = @ptrCast([*]align(@alignOf(T)) u8, new_bytes);
first_element += 8; first_element += @sizeOf(usize);
return RocStr{ return RocStr{
.str_bytes = first_element, .str_bytes = first_element,
.str_len = number_of_chars, .str_len = number_of_chars,
}; };
} }
test "RocStr.concat: small concat small" {
const str1_len = 3;
var str1: [str1_len]u8 = "foo".*;
const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
const str2_len = 3;
var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
const str3_len = 6;
var str3: [str3_len]u8 = "fooabc".*;
const str3_ptr: [*]u8 = &str3;
var roc_str3 = RocStr.init(testing.allocator, str3_ptr, str3_len);
defer {
roc_str1.deinit(testing.allocator);
roc_str2.deinit(testing.allocator);
roc_str3.deinit(testing.allocator);
}
const result = strConcat(testing.allocator, 8, InPlace.Clone, roc_str1, roc_str2);
defer result.deinit(testing.allocator);
expect(roc_str3.eq(result));
}

View file

@ -1,6 +1,8 @@
use std::convert::AsRef; use std::convert::AsRef;
use std::env; use std::env;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs::{self};
use std::io;
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use std::str; use std::str;
@ -11,65 +13,104 @@ fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
// "." is relative to where "build.rs" is // "." is relative to where "build.rs" is
let src_path = Path::new(".").join("bitcode").join("src"); let build_script_dir_path = Path::new(".");
let main_zig_path = src_path.join("main.zig");
let src_path_str = src_path.to_str().expect("Invalid src path"); let bitcode_path = build_script_dir_path.join("bitcode");
println!("Building main.zig from: {}", src_path_str);
let zig_cache_dir = Path::new(&out_dir).join("zig-cache"); let src_path = bitcode_path.join("src");
let zig_cache_dir_str = zig_cache_dir.to_str().expect("Invalid zig cache dir");
println!("Setting zig cache to: {}", zig_cache_dir_str);
let dest_ll_path = Path::new(&out_dir).join("builtins.ll"); let build_zig_path = bitcode_path.join("build.zig");
let dest_ll = dest_ll_path.to_str().expect("Invalid dest ir path"); let build_zig = build_zig_path.to_str().expect("Invalid build path");
let emit_ir_arg = "-femit-llvm-ir=".to_owned() + dest_ll;
println!("Compiling zig llvm-ir to: {}", dest_ll); let dest_ir_path = build_script_dir_path.join("builtins.ll");
let dest_ir = dest_ir_path.to_str().expect("Invalid dest ir path");
println!("Compiling ir to: {}", dest_ir);
run_command( run_command(
"zig", "zig",
&[ &["build", "ir", "-Drelease=true", "--build-file", build_zig],
"build-obj",
main_zig_path.to_str().unwrap(),
&emit_ir_arg,
"-fno-emit-bin",
"--strip",
"-O",
"ReleaseFast",
"--cache-dir",
zig_cache_dir_str,
],
); );
let dest_bc_path = Path::new(&out_dir).join("builtins.bc"); let dest_bc_path = Path::new(&out_dir).join("builtins.bc");
let dest_bc = dest_bc_path.to_str().expect("Invalid dest bc path"); let dest_bc = dest_bc_path.to_str().expect("Invalid dest bc path");
println!("Compiling bitcode to: {}", dest_bc); println!("Compiling bitcode to: {}", dest_bc);
run_command("llvm-as-10", &[dest_ll, "-o", dest_bc]); run_command("llvm-as-10", &[dest_ir, "-o", dest_bc]);
// TODO: Recursivly search zig src dir to watch for each file
println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed={}", src_path_str);
println!("cargo:rustc-env=BUILTINS_BC={}", dest_bc); println!("cargo:rustc-env=BUILTINS_BC={}", dest_bc);
get_zig_files(src_path.as_path(), &|path| {
let path: &Path = path;
println!(
"cargo:rerun-if-changed={}",
path.to_str().expect("Failed to convert path to str")
);
})
.unwrap();
} }
fn run_command<S, I>(command: &str, args: I) fn run_command<S, I>(command_str: &str, args: I)
where where
I: IntoIterator<Item = S>, I: IntoIterator<Item = S>,
S: AsRef<OsStr>, S: AsRef<OsStr>,
{ {
let output_result = Command::new(OsStr::new(&command)).args(args).output(); let output_result = Command::new(OsStr::new(&command_str)).args(args).output();
match output_result { match output_result {
Ok(output) => match output.status.success() { Ok(output) => match output.status.success() {
true => (), true => (),
false => { false => {
let error_str = match str::from_utf8(&output.stderr) { let error_str = match str::from_utf8(&output.stderr) {
Ok(stderr) => stderr.to_string(), Ok(stderr) => stderr.to_string(),
Err(_) => format!("Failed to run \"{}\"", command), Err(_) => format!("Failed to run \"{}\"", command_str),
}; };
panic!("{} failed: {}", command, error_str); panic!("{} failed: {}", command_str, error_str);
} }
}, },
Err(reason) => panic!("{} failed: {}", command, reason), Err(reason) => panic!("{} failed: {}", command_str, reason),
} }
} }
fn get_zig_files(dir: &Path, cb: &dyn Fn(&Path)) -> io::Result<()> {
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path_buf = entry.path();
if path_buf.is_dir() {
get_zig_files(&path_buf, cb).unwrap();
} else {
let path = path_buf.as_path();
match path.extension() {
Some(osstr) if osstr == "zig" => {
cb(path);
}
_ => {}
}
}
}
}
Ok(())
}
// fn get_zig_files(dir: &Path) -> io::Result<Vec<&Path>> {
// let mut vec = Vec::new();
// if dir.is_dir() {
// for entry in fs::read_dir(dir)? {
// let entry = entry?;
// let path_buf = entry.path();
// if path_buf.is_dir() {
// match get_zig_files(&path_buf) {
// Ok(sub_files) => vec = [vec, sub_files].concat(),
// Err(_) => (),
// };
// } else {
// let path = path_buf.as_path();
// let path_ext = path.extension().unwrap();
// if path_ext == "zig" {
// vec.push(path.clone());
// }
// }
// }
// }
// Ok(vec)
// }

View file

@ -1207,7 +1207,7 @@ fn int_type(u: VarId) -> SolvedType {
vec![ vec![
flex(u), flex(u),
SolvedType::Alias( SolvedType::Alias(
Symbol::NUM_INT, Symbol::NUM_I64,
Vec::new(), Vec::new(),
Box::new(builtin_aliases::num_type(SolvedType::Apply( Box::new(builtin_aliases::num_type(SolvedType::Apply(
Symbol::ATTR_ATTR, Symbol::ATTR_ATTR,

View file

@ -172,9 +172,9 @@ pub struct Field {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Recursive { pub enum Recursive {
Recursive, NotRecursive = 0,
TailRecursive, Recursive = 1,
NotRecursive, TailRecursive = 2,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]

View file

@ -97,6 +97,7 @@ pub fn canonicalize_module_defs<'a>(
let first_char = ident.as_inline_str().chars().next().unwrap(); let first_char = ident.as_inline_str().chars().next().unwrap();
if first_char.is_lowercase() { if first_char.is_lowercase() {
// this is a value definition
let expr_var = var_store.fresh(); let expr_var = var_store.fresh();
match scope.import(ident, symbol, region) { match scope.import(ident, symbol, region) {
@ -116,7 +117,10 @@ pub fn canonicalize_module_defs<'a>(
} }
} }
} else { } else {
panic!("TODO add type aliases to type alias dictionary, based on exposed types"); // This is a type alias
// the should already be added to the scope when this module is canonicalized
debug_assert!(scope.contains_alias(symbol));
} }
} }

View file

@ -192,4 +192,8 @@ impl Scope {
self.aliases.insert(name, alias); self.aliases.insert(name, alias);
} }
pub fn contains_alias(&mut self, name: Symbol) -> bool {
self.aliases.contains_key(&name)
}
} }

View file

@ -50,7 +50,7 @@ mod test_can {
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }
actual => { actual => {
panic!("Expected an Int, but got: {:?}", actual); panic!("Expected an I64, but got: {:?}", actual);
} }
} }
} }
@ -249,7 +249,7 @@ mod test_can {
fn correct_annotated_body() { fn correct_annotated_body() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int -> Int f : I64 -> I64
f = \ a -> a f = \ a -> a
f f
@ -265,7 +265,7 @@ mod test_can {
fn correct_annotated_body_with_comments() { fn correct_annotated_body_with_comments() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int -> Int # comment f : I64 -> I64 # comment
f = \ a -> a f = \ a -> a
f f
@ -281,7 +281,7 @@ mod test_can {
fn name_mismatch_annotated_body() { fn name_mismatch_annotated_body() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int -> Int f : I64 -> I64
g = \ a -> a g = \ a -> a
g g
@ -307,7 +307,7 @@ mod test_can {
fn name_mismatch_annotated_body_with_comment() { fn name_mismatch_annotated_body_with_comment() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int -> Int # comment f : I64 -> I64 # comment
g = \ a -> a g = \ a -> a
g g
@ -333,7 +333,7 @@ mod test_can {
fn separated_annotated_body() { fn separated_annotated_body() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int -> Int f : I64 -> I64
f = \ a -> a f = \ a -> a
@ -354,7 +354,7 @@ mod test_can {
fn separated_annotated_body_with_comment() { fn separated_annotated_body_with_comment() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int -> Int f : I64 -> I64
# comment # comment
f = \ a -> a f = \ a -> a
@ -375,9 +375,9 @@ mod test_can {
fn shadowed_annotation() { fn shadowed_annotation() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int -> Int f : I64 -> I64
f : Int -> Int f : I64 -> I64
f f
"# "#
@ -397,7 +397,7 @@ mod test_can {
fn correct_nested_unannotated_body() { fn correct_nested_unannotated_body() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int f : I64
f = f =
g = 42 g = 42
@ -416,9 +416,9 @@ mod test_can {
fn correct_nested_annotated_body() { fn correct_nested_annotated_body() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int f : I64
f = f =
g : Int g : I64
g = 42 g = 42
g + 1 g + 1
@ -436,11 +436,11 @@ mod test_can {
fn correct_nested_body_annotated_multiple_lines() { fn correct_nested_body_annotated_multiple_lines() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int f : I64
f = f =
g : Int g : I64
g = 42 g = 42
h : Int h : I64
h = 5 h = 5
z = 4 z = 4
g + h + z g + h + z
@ -458,10 +458,10 @@ mod test_can {
fn correct_nested_body_unannotated_multiple_lines() { fn correct_nested_body_unannotated_multiple_lines() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int f : I64
f = f =
g = 42 g = 42
h : Int h : I64
h = 5 h = 5
z = 4 z = 4
g + h + z g + h + z
@ -478,7 +478,7 @@ mod test_can {
fn correct_double_nested_body() { fn correct_double_nested_body() {
let src = indoc!( let src = indoc!(
r#" r#"
f : Int f : I64
f = f =
g = g =
h = 42 h = 42
@ -499,7 +499,7 @@ mod test_can {
fn annotation_followed_with_unrelated_affectation() { fn annotation_followed_with_unrelated_affectation() {
let src = indoc!( let src = indoc!(
r#" r#"
F : Int F : I64
x = 1 x = 1
@ -520,9 +520,9 @@ mod test_can {
fn two_annotations_followed_with_unrelated_affectation() { fn two_annotations_followed_with_unrelated_affectation() {
let src = indoc!( let src = indoc!(
r#" r#"
G : Int G : I64
F : Int F : I64
x = 1 x = 1

View file

@ -92,7 +92,7 @@ pub fn num_floatingpoint() -> Type {
#[inline(always)] #[inline(always)]
pub fn num_int() -> Type { pub fn num_int() -> Type {
Type::Alias(Symbol::NUM_INT, vec![], Box::new(num_num(num_integer()))) Type::Alias(Symbol::NUM_I64, vec![], Box::new(num_num(num_integer())))
} }
#[inline(always)] #[inline(always)]

View file

@ -762,8 +762,8 @@ mod test_fmt {
expr_formats_to( expr_formats_to(
indoc!( indoc!(
r#" r#"
f: { y : Int, f: { y : I64,
x : Int , x : I64 ,
} }
f"# f"#
@ -772,8 +772,8 @@ mod test_fmt {
r#" r#"
f : f :
{ {
y : Int, y : I64,
x : Int, x : I64,
} }
f"# f"#
@ -787,8 +787,8 @@ mod test_fmt {
r#" r#"
f : f :
{ {
y : Int, y : I64,
x : Int, x : I64,
} }
f"# f"#
@ -800,7 +800,7 @@ mod test_fmt {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
f : f :
Int I64
f"# f"#
)); ));
@ -880,7 +880,7 @@ mod test_fmt {
r#" r#"
f : f :
{ {
x: Int # comment 1 x: I64 # comment 1
, ,
# comment 2 # comment 2
} }
@ -891,7 +891,7 @@ mod test_fmt {
r#" r#"
f : f :
{ {
x : Int, x : I64,
# comment 1 # comment 1
# comment 2 # comment 2
} }
@ -2458,7 +2458,7 @@ mod test_fmt {
fn record_type() { fn record_type() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
f : { foo : Int } f : { foo : I64 }
f = { foo: 1000 } f = { foo: 1000 }
a a
@ -2509,11 +2509,11 @@ mod test_fmt {
// r#" // r#"
// f : // f :
// Result a // Result a
// { x : Int // { x : I64
// , y : Float // , y : Float
// } // }
// c // c
// -> Int // -> I64
// f = // f =
// \_ -> 4 // \_ -> 4
// "# // "#

View file

@ -1230,7 +1230,20 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
use roc_mono::ir::Stmt::*; use roc_mono::ir::Stmt::*;
match stmt { match stmt {
Let(symbol, expr, layout, cont) => { Let(first_symbol, first_expr, first_layout, mut cont) => {
let mut queue = Vec::new_in(env.arena);
queue.push((first_symbol, first_expr, first_layout));
while let Let(symbol, expr, layout, new_cont) = cont {
queue.push((symbol, expr, layout));
cont = new_cont;
}
let mut stack = Vec::with_capacity_in(queue.len(), env.arena);
for (symbol, expr, layout) in queue {
let context = &env.context; let context = &env.context;
let val = build_exp_expr(env, layout_ids, &scope, parent, layout, &expr); let val = build_exp_expr(env, layout_ids, &scope, parent, layout, &expr);
@ -1248,8 +1261,12 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
} else { } else {
basic_type_from_layout(env.arena, context, &layout, env.ptr_bytes) basic_type_from_layout(env.arena, context, &layout, env.ptr_bytes)
}; };
let alloca = let alloca = create_entry_block_alloca(
create_entry_block_alloca(env, parent, expr_bt, symbol.ident_string(&env.interns)); env,
parent,
expr_bt,
symbol.ident_string(&env.interns),
);
env.builder.build_store(alloca, val); env.builder.build_store(alloca, val);
@ -1261,8 +1278,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
// scope = scope.clone(); // scope = scope.clone();
scope.insert(*symbol, (layout.clone(), alloca)); scope.insert(*symbol, (layout.clone(), alloca));
stack.push(*symbol);
}
let result = build_exp_stmt(env, layout_ids, scope, parent, cont); let result = build_exp_stmt(env, layout_ids, scope, parent, cont);
scope.remove(symbol);
for symbol in stack {
scope.remove(&symbol);
}
result result
} }

View file

@ -49,12 +49,7 @@ pub fn str_split<'a, 'ctx, 'env>(
call_void_bitcode_fn( call_void_bitcode_fn(
env, env,
&[ &[ret_list_ptr_zig_rocstr, str_i128.into(), delim_i128.into()],
ret_list_ptr_zig_rocstr,
BasicValueEnum::IntValue(segment_count),
str_i128.into(),
delim_i128.into(),
],
&bitcode::STR_STR_SPLIT_IN_PLACE, &bitcode::STR_STR_SPLIT_IN_PLACE,
); );

View file

@ -162,7 +162,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
initThrees : List Int initThrees : List I64
initThrees = initThrees =
[] []
@ -204,7 +204,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
init : List Int init : List I64
init = init =
[] []
@ -251,7 +251,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
empty : List Int empty : List I64
empty = empty =
[] []
@ -330,7 +330,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
empty : List Int empty : List I64
empty = empty =
[] []
@ -347,7 +347,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
alwaysTrue : Int -> Bool alwaysTrue : I64 -> Bool
alwaysTrue = \_ -> alwaysTrue = \_ ->
True True
@ -365,11 +365,11 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
alwaysTrue : Int -> Bool alwaysTrue : I64 -> Bool
alwaysTrue = \_ -> alwaysTrue = \_ ->
True True
oneThroughEight : List Int oneThroughEight : List I64
oneThroughEight = oneThroughEight =
[1,2,3,4,5,6,7,8] [1,2,3,4,5,6,7,8]
@ -386,7 +386,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
alwaysFalse : Int -> Bool alwaysFalse : I64 -> Bool
alwaysFalse = \_ -> alwaysFalse = \_ ->
False False
@ -403,7 +403,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
intIsLessThanThree : Int -> Bool intIsLessThanThree : I64 -> Bool
intIsLessThanThree = \i -> intIsLessThanThree = \i ->
i < 3 i < 3
@ -440,7 +440,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
empty : List Int empty : List I64
empty = empty =
[] []
@ -457,7 +457,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
nonEmpty : List Int nonEmpty : List I64
nonEmpty = nonEmpty =
[ 1 ] [ 1 ]
@ -474,7 +474,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
nonEmpty : List Int nonEmpty : List I64
nonEmpty = nonEmpty =
[ 1 ] [ 1 ]
@ -491,7 +491,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
nonEmpty : List Int nonEmpty : List I64
nonEmpty = nonEmpty =
[ 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 ] [ 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 ]
@ -510,7 +510,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
nonEmpty : List Int nonEmpty : List I64
nonEmpty = nonEmpty =
[ 1, 1, -4, 1, 2 ] [ 1, 1, -4, 1, 2 ]
@ -528,11 +528,11 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
nonEmpty : List Int nonEmpty : List I64
nonEmpty = nonEmpty =
[ 2, 2, -4, 2, 3 ] [ 2, 2, -4, 2, 3 ]
greaterThanOne : Int -> Bool greaterThanOne : I64 -> Bool
greaterThanOne = \i -> greaterThanOne = \i ->
i > 1 i > 1
@ -730,7 +730,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
emptyList : List Int emptyList : List I64
emptyList = emptyList =
[] []
@ -752,11 +752,11 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
firstList : List Int firstList : List I64
firstList = firstList =
[] []
secondList : List Int secondList : List I64
secondList = secondList =
[] []
@ -778,11 +778,11 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
firstList : List Int firstList : List I64
firstList = firstList =
[] []
secondList : List Int secondList : List I64
secondList = secondList =
[] []
@ -1229,7 +1229,7 @@ mod gen_list {
app "quicksort" provides [ main ] to "./platform" app "quicksort" provides [ main ] to "./platform"
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -1254,7 +1254,7 @@ mod gen_list {
// assert_evals_to!( // assert_evals_to!(
// indoc!( // indoc!(
// r#" // r#"
// swap : Int, Int, List a -> List a // swap : I64, I64, List a -> List a
// swap = \i, j, list -> // swap = \i, j, list ->
// when Pair (List.get list i) (List.get list j) is // when Pair (List.get list i) (List.get list j) is
// Pair (Ok atI) (Ok atJ) -> // Pair (Ok atI) (Ok atJ) ->
@ -1264,7 +1264,7 @@ mod gen_list {
// //
// _ -> // _ ->
// [] // []
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] // partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
// partition = \low, high, initialList -> // partition = \low, high, initialList ->
// when List.get initialList high is // when List.get initialList high is
// Ok pivot -> // Ok pivot ->
@ -1276,7 +1276,7 @@ mod gen_list {
// Pair (low - 1) initialList // Pair (low - 1) initialList
// //
// //
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ] // partitionHelp : I64, I64, List (Num a), I64, I64 -> [ Pair I64 (List (Num a)) ]
// partitionHelp = \i, j, list, high, pivot -> // partitionHelp = \i, j, list, high, pivot ->
// if j < high then // if j < high then
// when List.get list j is // when List.get list j is
@ -1306,7 +1306,7 @@ mod gen_list {
// assert_evals_to!( // assert_evals_to!(
// indoc!( // indoc!(
// r#" // r#"
// swap : Int, Int, List a -> List a // swap : I64, I64, List a -> List a
// swap = \i, j, list -> // swap = \i, j, list ->
// when Pair (List.get list i) (List.get list j) is // when Pair (List.get list i) (List.get list j) is
// Pair (Ok atI) (Ok atJ) -> // Pair (Ok atI) (Ok atJ) ->
@ -1316,7 +1316,7 @@ mod gen_list {
// //
// _ -> // _ ->
// [] // []
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] // partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
// partition = \low, high, initialList -> // partition = \low, high, initialList ->
// when List.get initialList high is // when List.get initialList high is
// Ok pivot -> // Ok pivot ->
@ -1328,7 +1328,7 @@ mod gen_list {
// Pair (low - 1) initialList // Pair (low - 1) initialList
// //
// //
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ] // partitionHelp : I64, I64, List (Num a), I64, I64 -> [ Pair I64 (List (Num a)) ]
// //
// # when partition 0 0 [ 1,2,3,4,5 ] is // # when partition 0 0 [ 1,2,3,4,5 ] is
// # Pair list _ -> list // # Pair list _ -> list
@ -1352,7 +1352,7 @@ mod gen_list {
quicksortHelp list 0 (n - 1) quicksortHelp list 0 (n - 1)
quicksortHelp : List (Num a), Int, Int -> List (Num a) quicksortHelp : List (Num a), I64, I64 -> List (Num a)
quicksortHelp = \list, low, high -> quicksortHelp = \list, low, high ->
if low < high then if low < high then
when partition low high list is when partition low high list is
@ -1364,7 +1364,7 @@ mod gen_list {
list list
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -1375,7 +1375,7 @@ mod gen_list {
_ -> _ ->
[] []
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -1387,7 +1387,7 @@ mod gen_list {
Pair (low - 1) initialList Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is
@ -1422,7 +1422,7 @@ mod gen_list {
quicksortHelp list 0 (List.len list - 1) quicksortHelp list 0 (List.len list - 1)
quicksortHelp : List (Num a), Int, Int -> List (Num a) quicksortHelp : List (Num a), I64, I64 -> List (Num a)
quicksortHelp = \list, low, high -> quicksortHelp = \list, low, high ->
if low < high then if low < high then
when partition low high list is when partition low high list is
@ -1434,7 +1434,7 @@ mod gen_list {
list list
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -1445,7 +1445,7 @@ mod gen_list {
_ -> _ ->
[] []
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -1457,7 +1457,7 @@ mod gen_list {
Pair (low - 1) initialList Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
# if j < high then # if j < high then
if False then if False then
@ -1495,7 +1495,7 @@ mod gen_list {
quicksortHelp list 0 (List.len list - 1) quicksortHelp list 0 (List.len list - 1)
quicksortHelp : List (Num a), Int, Int -> List (Num a) quicksortHelp : List (Num a), I64, I64 -> List (Num a)
quicksortHelp = \list, low, high -> quicksortHelp = \list, low, high ->
if low < high then if low < high then
when partition low high list is when partition low high list is
@ -1507,7 +1507,7 @@ mod gen_list {
list list
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -1518,7 +1518,7 @@ mod gen_list {
_ -> _ ->
[] []
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -1530,7 +1530,7 @@ mod gen_list {
Pair (low - 1) initialList Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is
@ -1562,7 +1562,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : List Int x : List I64
x = [] x = []
List.len x + List.len x List.len x + List.len x
@ -1578,7 +1578,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : List Int x : List I64
x = [1,2,3] x = [1,2,3]
List.len x + List.len x List.len x + List.len x
@ -1594,10 +1594,10 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : List Int x : List I64
x = [1,2,3] x = [1,2,3]
id : List Int -> List Int id : List I64 -> List I64
id = \y -> y id = \y -> y
id x id x
@ -1613,10 +1613,10 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : List Int x : List I64
x = [1,2,3] x = [1,2,3]
id : List Int -> List Int id : List I64 -> List I64
id = \y -> List.set y 0 0 id = \y -> List.set y 0 0
id x id x
@ -1632,7 +1632,7 @@ mod gen_list {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
id : List Int -> [ Pair (List Int) Int ] id : List I64 -> [ Pair (List I64) I64 ]
id = \y -> Pair y 4 id = \y -> Pair y 4
when id [1,2,3] is when id [1,2,3] is

View file

@ -135,7 +135,7 @@ mod gen_primitives {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : [ Pair Int Int ] x : [ Pair I64 I64 ]
x = Pair 0x2 0x3 x = Pair 0x2 0x3
when x is when x is
@ -152,7 +152,7 @@ mod gen_primitives {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : [A Int, B Int] x : [A I64, B I64]
x = A 0x2 x = A 0x2
when x is when x is
@ -170,7 +170,7 @@ mod gen_primitives {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : [A Int, B Int] x : [A I64, B I64]
x = B 0x3 x = B 0x3
when x is when x is
@ -293,7 +293,7 @@ mod gen_primitives {
indoc!( indoc!(
r#" r#"
wrapper = \{} -> wrapper = \{} ->
alwaysFloatIdentity : Int -> (F64 -> F64) alwaysFloatIdentity : I64 -> (F64 -> F64)
alwaysFloatIdentity = \_ -> alwaysFloatIdentity = \_ ->
(\a -> a) (\a -> a)
@ -557,14 +557,14 @@ mod gen_primitives {
LinkedList a : [ Nil, Cons a (LinkedList a) ] LinkedList a : [ Nil, Cons a (LinkedList a) ]
len : LinkedList a -> Int len : LinkedList a -> I64
len = \list -> len = \list ->
when list is when list is
Nil -> 0 Nil -> 0
Cons _ rest -> 1 + len rest Cons _ rest -> 1 + len rest
main = main =
nil : LinkedList Int nil : LinkedList I64
nil = Nil nil = Nil
len nil len nil
@ -584,10 +584,10 @@ mod gen_primitives {
LinkedList a : [ Nil, Cons a (LinkedList a) ] LinkedList a : [ Nil, Cons a (LinkedList a) ]
nil : LinkedList Int nil : LinkedList I64
nil = Nil nil = Nil
length : LinkedList a -> Int length : LinkedList a -> I64
length = \list -> length = \list ->
when list is when list is
Nil -> 0 Nil -> 0
@ -611,10 +611,10 @@ mod gen_primitives {
LinkedList a : [ Nil, Cons a (LinkedList a) ] LinkedList a : [ Nil, Cons a (LinkedList a) ]
one : LinkedList Int one : LinkedList I64
one = Cons 1 Nil one = Cons 1 Nil
length : LinkedList a -> Int length : LinkedList a -> I64
length = \list -> length = \list ->
when list is when list is
Nil -> 0 Nil -> 0
@ -638,10 +638,10 @@ mod gen_primitives {
LinkedList a : [ Nil, Cons a (LinkedList a) ] LinkedList a : [ Nil, Cons a (LinkedList a) ]
one : LinkedList Int one : LinkedList I64
one = Cons 1 Nil one = Cons 1 Nil
length : LinkedList a -> Int length : LinkedList a -> I64
length = \list -> length = \list ->
when list is when list is
Nil -> 0 Nil -> 0
@ -665,10 +665,10 @@ mod gen_primitives {
LinkedList a : [ Nil, Cons a (LinkedList a) ] LinkedList a : [ Nil, Cons a (LinkedList a) ]
three : LinkedList Int three : LinkedList I64
three = Cons 3 (Cons 2 (Cons 1 Nil)) three = Cons 3 (Cons 2 (Cons 1 Nil))
length : LinkedList a -> Int length : LinkedList a -> I64
length = \list -> length = \list ->
when list is when list is
Nil -> 0 Nil -> 0
@ -693,7 +693,7 @@ mod gen_primitives {
LinkedList a : [ Nil, Cons a (LinkedList a) ] LinkedList a : [ Nil, Cons a (LinkedList a) ]
three : LinkedList Int three : LinkedList I64
three = Cons 3 (Cons 2 (Cons 1 Nil)) three = Cons 3 (Cons 2 (Cons 1 Nil))
@ -721,10 +721,10 @@ mod gen_primitives {
LinkedList a : [ Nil, Cons a (LinkedList a) ] LinkedList a : [ Nil, Cons a (LinkedList a) ]
zero : LinkedList Int zero : LinkedList I64
zero = Nil zero = Nil
sum : LinkedList Int -> Int sum : LinkedList I64 -> I64
sum = \list -> sum = \list ->
when list is when list is
Nil -> 0 Nil -> 0
@ -748,7 +748,7 @@ mod gen_primitives {
LinkedList a : [ Nil, Cons a (LinkedList a) ] LinkedList a : [ Nil, Cons a (LinkedList a) ]
three : LinkedList Int three : LinkedList I64
three = Cons 3 (Cons 2 (Cons 1 Nil)) three = Cons 3 (Cons 2 (Cons 1 Nil))
sum : LinkedList (Num a) -> Num a sum : LinkedList (Num a) -> Num a
@ -779,7 +779,7 @@ mod gen_primitives {
r#" r#"
Maybe a : [ Nothing, Just a ] Maybe a : [ Nothing, Just a ]
x : Maybe (Maybe Int) x : Maybe (Maybe I64)
x = Just (Just 41) x = Just (Just 41)
when x is when x is
@ -796,7 +796,7 @@ mod gen_primitives {
r#" r#"
Maybe a : [ Nothing, Just a ] Maybe a : [ Nothing, Just a ]
x : Maybe (Maybe Int) x : Maybe (Maybe I64)
x = Just Nothing x = Just Nothing
when x is when x is
@ -814,7 +814,7 @@ mod gen_primitives {
r#" r#"
Maybe a : [ Nothing, Just a ] Maybe a : [ Nothing, Just a ]
x : Maybe (Maybe Int) x : Maybe (Maybe I64)
x = Nothing x = Nothing
when x is when x is
@ -1128,7 +1128,7 @@ mod gen_primitives {
main : Bool main : Bool
main = main =
myList : ConsList Int myList : ConsList I64
myList = empty myList = empty
isEmpty myList isEmpty myList
@ -1159,7 +1159,7 @@ mod gen_primitives {
main : Bool main : Bool
main = main =
myList : ConsList Int myList : ConsList I64
myList = Cons 0x1 Nil myList = Cons 0x1 Nil
isEmpty myList isEmpty myList
@ -1177,16 +1177,16 @@ mod gen_primitives {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
State a : { count : Int, x : a } State a : { count : I64, x : a }
foo : State a -> Int foo : State a -> I64
foo = \state -> foo = \state ->
if state.count == 0 then if state.count == 0 then
0 0
else else
1 + foo { count: state.count - 1, x: state.x } 1 + foo { count: state.count - 1, x: state.x }
main : Int main : I64
main = main =
foo { count: 3, x: {} } foo { count: 3, x: {} }
"# "#
@ -1267,7 +1267,7 @@ mod gen_primitives {
_ -> _ ->
Node color key value left right Node color key value left right
main : Dict Int {} main : Dict I64 {}
main = main =
insert 0 {} Empty insert 0 {} Empty
"# "#
@ -1308,7 +1308,7 @@ mod gen_primitives {
_ -> _ ->
Empty Empty
main : Dict Int main : Dict I64
main = main =
balance Red 0 Empty Empty balance Red 0 Empty Empty
"# "#
@ -1331,7 +1331,7 @@ mod gen_primitives {
balance = \key, left -> balance = \key, left ->
Node key left Empty Node key left Empty
main : Dict Int main : Dict I64
main = main =
balance 0 Empty balance 0 Empty
"# "#
@ -1378,7 +1378,7 @@ mod gen_primitives {
_ -> _ ->
Empty Empty
main : Dict Int Int main : Dict I64 I64
main = main =
balance Red 0 0 Empty Empty balance Red 0 0 Empty Empty
"# "#
@ -1428,7 +1428,7 @@ mod gen_primitives {
_ -> _ ->
Node color key value left right Node color key value left right
main : Dict Int Int main : Dict I64 I64
main = main =
balance Red 0 0 Empty Empty balance Red 0 0 Empty Empty
"# "#
@ -1448,7 +1448,7 @@ mod gen_primitives {
ConsList a : [ Cons a (ConsList a), Nil ] ConsList a : [ Cons a (ConsList a), Nil ]
balance : ConsList Int -> Int balance : ConsList I64 -> I64
balance = \right -> balance = \right ->
when right is when right is
Cons 1 foo -> Cons 1 foo ->
@ -1457,7 +1457,7 @@ mod gen_primitives {
_ -> 3 _ -> 3
_ -> 3 _ -> 3
main : Int main : I64
main = main =
when balance Nil is when balance Nil is
_ -> 3 _ -> 3
@ -1474,13 +1474,13 @@ mod gen_primitives {
ConsList a : [ Cons a (ConsList a), Nil ] ConsList a : [ Cons a (ConsList a), Nil ]
balance : ConsList Int -> Int balance : ConsList I64 -> I64
balance = \right -> balance = \right ->
when right is when right is
Cons 1 (Cons 1 _) -> 3 Cons 1 (Cons 1 _) -> 3
_ -> 3 _ -> 3
main : Int main : I64
main = main =
when balance Nil is when balance Nil is
_ -> 3 _ -> 3
@ -1502,7 +1502,7 @@ mod gen_primitives {
ConsList a : [ Cons a (ConsList a), Nil ] ConsList a : [ Cons a (ConsList a), Nil ]
balance : ConsList Int -> Int balance : ConsList I64 -> I64
balance = \right -> balance = \right ->
when right is when right is
Cons 1 foo -> Cons 1 foo ->
@ -1511,7 +1511,7 @@ mod gen_primitives {
_ -> 3 _ -> 3
_ -> 3 _ -> 3
main : Int main : I64
main = main =
when balance Nil is when balance Nil is
_ -> 3 _ -> 3
@ -1531,13 +1531,13 @@ mod gen_primitives {
ConsList a : [ Cons a (ConsList a), Nil ] ConsList a : [ Cons a (ConsList a), Nil ]
foo : ConsList Int -> Int foo : ConsList I64 -> I64
foo = \list -> foo = \list ->
when list is when list is
Cons _ (Cons x _) -> x Cons _ (Cons x _) -> x
_ -> 0 _ -> 0
main : Int main : I64
main = main =
foo (Cons 1 (Cons 32 Nil)) foo (Cons 1 (Cons 32 Nil))
"# "#
@ -1554,15 +1554,15 @@ mod gen_primitives {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
BTree : [ Node BTree BTree, Leaf Int ] BTree : [ Node BTree BTree, Leaf I64 ]
foo : BTree -> Int foo : BTree -> I64
foo = \btree -> foo = \btree ->
when btree is when btree is
Node (Node (Leaf x) _) _ -> x Node (Node (Leaf x) _) _ -> x
_ -> 0 _ -> 0
main : Int main : I64
main = main =
foo (Node (Node (Leaf 32) (Leaf 0)) (Leaf 0)) foo (Node (Node (Leaf 32) (Leaf 0)) (Leaf 0))
"# "#

View file

@ -20,7 +20,7 @@ mod gen_tags {
r#" r#"
Maybe a : [ Just a, Nothing ] Maybe a : [ Just a, Nothing ]
x : Maybe Int x : Maybe I64
x = Nothing x = Nothing
x x
@ -39,7 +39,7 @@ mod gen_tags {
r#" r#"
Maybe a : [ Just a, Nothing ] Maybe a : [ Just a, Nothing ]
x : Maybe Int x : Maybe I64
x = Nothing x = Nothing
x x
@ -58,7 +58,7 @@ mod gen_tags {
r#" r#"
Maybe a : [ Just a, Nothing ] Maybe a : [ Just a, Nothing ]
y : Maybe Int y : Maybe I64
y = Just 0x4 y = Just 0x4
y y
@ -76,7 +76,7 @@ mod gen_tags {
r#" r#"
Maybe a : [ Just a, Nothing ] Maybe a : [ Just a, Nothing ]
y : Maybe Int y : Maybe I64
y = Just 0x4 y = Just 0x4
y y
@ -114,7 +114,7 @@ mod gen_tags {
// assert_evals_to!( // assert_evals_to!(
// indoc!( // indoc!(
// r#" // r#"
// x : Result Int Int // x : Result I64 I64
// x = Err 41 // x = Err 41
// x // x
@ -185,7 +185,7 @@ mod gen_tags {
// r#" // r#"
// LinkedList a : [ Cons a (LinkedList a), Nil ] // LinkedList a : [ Cons a (LinkedList a), Nil ]
// //
// empty : LinkedList Int // empty : LinkedList I64
// empty = Nil // empty = Nil
// //
// 1 // 1
@ -203,7 +203,7 @@ mod gen_tags {
// r#" // r#"
// LinkedList a : [ Cons a (LinkedList a), Nil ] // LinkedList a : [ Cons a (LinkedList a), Nil ]
// //
// singleton : LinkedList Int // singleton : LinkedList I64
// singleton = Cons 0x1 Nil // singleton = Cons 0x1 Nil
// //
// 1 // 1
@ -290,7 +290,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : [ Nothing, Just Int ] x : [ Nothing, Just I64 ]
x = Nothing x = Nothing
when x is when x is
@ -308,7 +308,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : [ Nothing, Just Int ] x : [ Nothing, Just I64 ]
x = Just 41 x = Just 41
when x is when x is
@ -326,7 +326,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : Result Int Int x : Result I64 I64
x = Err 41 x = Err 41
when x is when x is
@ -346,7 +346,7 @@ mod gen_tags {
r#" r#"
These a b : [ This a, That b, These a b ] These a b : [ This a, That b, These a b ]
x : These Int Int x : These I64 I64
x = These 0x3 0x2 x = These 0x3 0x2
when x is when x is
@ -398,7 +398,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : Result Int Int x : Result I64 I64
x = Ok 2 x = Ok 2
when x is when x is
@ -464,7 +464,7 @@ mod gen_tags {
r#" r#"
Maybe a : [ Nothing, Just a ] Maybe a : [ Nothing, Just a ]
x : Maybe (Maybe Int) x : Maybe (Maybe I64)
x = Just (Just 41) x = Just (Just 41)
when x is when x is
@ -558,7 +558,7 @@ mod gen_tags {
r#" r#"
Unit : [ Unit ] Unit : [ Unit ]
f : Unit -> Int f : Unit -> I64
f = \Unit -> 42 f = \Unit -> 42
f Unit f Unit
@ -587,7 +587,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
f : {} -> Int f : {} -> I64
f = \{} -> 42 f = \{} -> 42
f {} f {}
@ -614,7 +614,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
x : [ Pair Int ] x : [ Pair I64 ]
x = Pair 2 x = Pair 2
x x
@ -634,7 +634,7 @@ mod gen_tags {
Maybe a : [ Nothing, Just a ] Maybe a : [ Nothing, Just a ]
x : Maybe (Maybe Int) x : Maybe (Maybe I64)
x = Just (Just 41) x = Just (Just 41)
main = main =
@ -806,7 +806,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r"# r"#
x : [ Three Bool Int, Empty ] x : [ Three Bool I64, Empty ]
x = Three (1 == 1) 32 x = Three (1 == 1) 32
x x
@ -820,7 +820,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r"# r"#
x : [ Three Bool [ Red, Green, Blue ] Int, Empty ] x : [ Three Bool [ Red, Green, Blue ] I64, Empty ]
x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32 x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32
x x
@ -836,7 +836,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r"# r"#
x : [ Three Bool Int, Empty ] x : [ Three Bool I64, Empty ]
x = Three (1 == 1) 32 x = Three (1 == 1) 32
when x is when x is
@ -854,7 +854,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r"# r"#
x : [ Three Bool [ Red, Green, Blue ] Int, Empty ] x : [ Three Bool [ Red, Green, Blue ] I64, Empty ]
x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32 x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32
when x is when x is

View file

@ -1,5 +1,6 @@
#[macro_use] #[macro_use]
extern crate pretty_assertions; extern crate pretty_assertions;
#[macro_use] #[macro_use]
extern crate indoc; extern crate indoc;

View file

@ -75,7 +75,6 @@ pub fn helper<'a>(
println!("{:?}", exposed_to_host); println!("{:?}", exposed_to_host);
println!("=================================\n"); println!("=================================\n");
*/ */
debug_assert_eq!(exposed_to_host.len(), 1); debug_assert_eq!(exposed_to_host.len(), 1);
let main_fn_symbol = exposed_to_host.keys().copied().nth(0).unwrap(); let main_fn_symbol = exposed_to_host.keys().copied().nth(0).unwrap();

View file

@ -13,14 +13,18 @@ use roc_constrain::module::{
constrain_imports, pre_constrain_imports, ConstrainableImports, Import, constrain_imports, pre_constrain_imports, ConstrainableImports, Import,
}; };
use roc_constrain::module::{constrain_module, ExposedModuleTypes, SubsByModule}; use roc_constrain::module::{constrain_module, ExposedModuleTypes, SubsByModule};
use roc_module::ident::{Ident, Lowercase, ModuleName, TagName}; use roc_module::ident::{Ident, Lowercase, ModuleName, QualifiedModuleName, TagName};
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol}; use roc_module::symbol::{
IdentIds, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, Symbol,
};
use roc_mono::ir::{ use roc_mono::ir::{
CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs, CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs,
}; };
use roc_mono::layout::{Layout, LayoutCache}; use roc_mono::layout::{Layout, LayoutCache};
use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation}; use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation};
use roc_parse::header::{ExposesEntry, ImportsEntry, PlatformHeader, TypedIdent}; use roc_parse::header::{
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
};
use roc_parse::module::module_defs; use roc_parse::module::module_defs;
use roc_parse::parser::{self, Fail, Parser}; use roc_parse::parser::{self, Fail, Parser};
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
@ -101,6 +105,7 @@ impl Dependencies {
pub fn add_module( pub fn add_module(
&mut self, &mut self,
module_id: ModuleId, module_id: ModuleId,
opt_effect_module: Option<ModuleId>,
dependencies: &MutSet<ModuleId>, dependencies: &MutSet<ModuleId>,
goal_phase: Phase, goal_phase: Phase,
) -> MutSet<(ModuleId, Phase)> { ) -> MutSet<(ModuleId, Phase)> {
@ -143,7 +148,8 @@ impl Dependencies {
// all the dependencies can be loaded // all the dependencies can be loaded
for dep in dependencies { for dep in dependencies {
// TODO figure out how to "load" (because it doesn't exist on the file system) the Effect module // TODO figure out how to "load" (because it doesn't exist on the file system) the Effect module
if !format!("{:?}", dep).contains("Effect") {
if Some(*dep) != opt_effect_module {
output.insert((*dep, LoadHeader)); output.insert((*dep, LoadHeader));
} }
} }
@ -226,7 +232,13 @@ impl Dependencies {
self.add_dependency_help(a, b, phase, phase); self.add_dependency_help(a, b, phase, phase);
} }
/// phase_a of module a is waiting for phase_b of module_b
fn add_dependency_help(&mut self, a: ModuleId, b: ModuleId, phase_a: Phase, phase_b: Phase) { fn add_dependency_help(&mut self, a: ModuleId, b: ModuleId, phase_a: Phase, phase_b: Phase) {
// no need to wait if the dependency is already done!
if let Some(Status::Done) = self.status.get(&(b, phase_b)) {
return;
}
let key = (a, phase_a); let key = (a, phase_a);
let value = (b, phase_b); let value = (b, phase_b);
match self.waiting_for.get_mut(&key) { match self.waiting_for.get_mut(&key) {
@ -275,7 +287,7 @@ impl Dependencies {
/// Struct storing various intermediate stages by their ModuleId /// Struct storing various intermediate stages by their ModuleId
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct ModuleCache<'a> { struct ModuleCache<'a> {
module_names: MutMap<ModuleId, ModuleName>, module_names: MutMap<ModuleId, PQModuleName<'a>>,
/// Phases /// Phases
headers: MutMap<ModuleId, ModuleHeader<'a>>, headers: MutMap<ModuleId, ModuleHeader<'a>>,
@ -338,14 +350,16 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
let dep_name = state let dep_name = state
.module_cache .module_cache
.module_names .module_names
.remove(&module_id) .get(&module_id)
.expect("module id is present"); .expect("module id is present")
.clone();
BuildTask::LoadModule { BuildTask::LoadModule {
module_name: dep_name, module_name: dep_name,
// Provide mutexes of ModuleIds and IdentIds by module, // Provide mutexes of ModuleIds and IdentIds by module,
// so other modules can populate them as they load. // so other modules can populate them as they load.
module_ids: Arc::clone(&state.arc_modules), module_ids: Arc::clone(&state.arc_modules),
shorthands: Arc::clone(&state.arc_shorthands),
ident_ids_by_module: Arc::clone(&state.ident_ids_by_module), ident_ids_by_module: Arc::clone(&state.ident_ids_by_module),
mode: state.stdlib.mode, mode: state.stdlib.mode,
} }
@ -394,8 +408,10 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
// This should be small, and cloning it should be quick. // This should be small, and cloning it should be quick.
// We release the lock as soon as we're done cloning, so we don't have // We release the lock as soon as we're done cloning, so we don't have
// to lock the global module_ids while canonicalizing any given module. // to lock the global module_ids while canonicalizing any given module.
let module_ids = Arc::clone(&state.arc_modules); let qualified_module_ids = Arc::clone(&state.arc_modules);
let module_ids = { (*module_ids).lock().clone() }; let qualified_module_ids = { (*qualified_module_ids).lock().clone() };
let module_ids = qualified_module_ids.into_module_ids();
let exposed_symbols = state let exposed_symbols = state
.exposed_symbols_by_module .exposed_symbols_by_module
@ -407,9 +423,8 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
for imported in parsed.imported_modules.iter() { for imported in parsed.imported_modules.iter() {
match state.module_cache.aliases.get(imported) { match state.module_cache.aliases.get(imported) {
None => unreachable!( None => unreachable!(
"imported module {:?} did not register its aliases, so {:?} cannot use them", r"imported module {:?} did not register its aliases, so {:?} cannot use them",
imported, imported, parsed.module_id,
parsed.module_id,
), ),
Some(new) => { Some(new) => {
// TODO filter to only add imported aliases // TODO filter to only add imported aliases
@ -539,10 +554,12 @@ struct ModuleHeader<'a> {
module_name: AppOrInterfaceName<'a>, module_name: AppOrInterfaceName<'a>,
module_path: PathBuf, module_path: PathBuf,
exposed_ident_ids: IdentIds, exposed_ident_ids: IdentIds,
deps_by_name: MutMap<ModuleName, ModuleId>, deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
packages: MutMap<&'a str, PackageOrPath<'a>>,
imported_modules: MutSet<ModuleId>, imported_modules: MutSet<ModuleId>,
exposes: Vec<Symbol>, exposes: Vec<Symbol>,
exposed_imports: MutMap<Ident, (Symbol, Region)>, exposed_imports: MutMap<Ident, (Symbol, Region)>,
to_platform: Option<To<'a>>,
src: &'a [u8], src: &'a [u8],
module_timing: ModuleTiming, module_timing: ModuleTiming,
} }
@ -584,6 +601,7 @@ pub struct MonomorphizedModule<'a> {
pub interns: Interns, pub interns: Interns,
pub subs: Subs, pub subs: Subs,
pub output_path: Box<str>, pub output_path: Box<str>,
pub platform_path: Box<str>,
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>, pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>, pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
pub mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>, pub mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
@ -606,7 +624,7 @@ struct ParsedModule<'a> {
module_path: PathBuf, module_path: PathBuf,
src: &'a str, src: &'a str,
module_timing: ModuleTiming, module_timing: ModuleTiming,
deps_by_name: MutMap<ModuleName, ModuleId>, deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
imported_modules: MutSet<ModuleId>, imported_modules: MutSet<ModuleId>,
exposed_ident_ids: IdentIds, exposed_ident_ids: IdentIds,
exposed_imports: MutMap<Ident, (Symbol, Region)>, exposed_imports: MutMap<Ident, (Symbol, Region)>,
@ -676,6 +694,8 @@ struct State<'a> {
pub stdlib: StdLib, pub stdlib: StdLib,
pub exposed_types: SubsByModule, pub exposed_types: SubsByModule,
pub output_path: Option<&'a str>, pub output_path: Option<&'a str>,
pub platform_path: Option<To<'a>>,
pub opt_effect_module: Option<ModuleId>,
pub headers_parsed: MutSet<ModuleId>, pub headers_parsed: MutSet<ModuleId>,
@ -689,7 +709,8 @@ struct State<'a> {
pub constrained_ident_ids: MutMap<ModuleId, IdentIds>, pub constrained_ident_ids: MutMap<ModuleId, IdentIds>,
/// From now on, these will be used by multiple threads; time to make an Arc<Mutex<_>>! /// From now on, these will be used by multiple threads; time to make an Arc<Mutex<_>>!
pub arc_modules: Arc<Mutex<ModuleIds>>, pub arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
pub arc_shorthands: Arc<Mutex<MutMap<&'a str, PackageOrPath<'a>>>>,
pub ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, pub ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
@ -817,13 +838,15 @@ impl ModuleTiming {
#[allow(dead_code)] #[allow(dead_code)]
enum BuildTask<'a> { enum BuildTask<'a> {
LoadModule { LoadModule {
module_name: ModuleName, module_name: PQModuleName<'a>,
module_ids: Arc<Mutex<ModuleIds>>, module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
shorthands: Arc<Mutex<MutMap<&'a str, PackageOrPath<'a>>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
mode: Mode, mode: Mode,
}, },
LoadPkgConfig { LoadPkgConfig {
module_ids: Arc<Mutex<ModuleIds>>, shorthand: &'a str,
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
mode: Mode, mode: Mode,
}, },
@ -877,6 +900,7 @@ pub enum LoadingProblem {
FileProblem { FileProblem {
filename: PathBuf, filename: PathBuf,
error: io::ErrorKind, error: io::ErrorKind,
msg: &'static str,
}, },
ParsingFailed { ParsingFailed {
filename: PathBuf, filename: PathBuf,
@ -988,7 +1012,7 @@ pub fn load_and_monomorphize_from_str<'a>(
} }
struct LoadStart<'a> { struct LoadStart<'a> {
pub arc_modules: Arc<Mutex<ModuleIds>>, pub arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
pub ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, pub ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
pub root_id: ModuleId, pub root_id: ModuleId,
pub root_msg: Msg<'a>, pub root_msg: Msg<'a>,
@ -1000,7 +1024,7 @@ impl<'a> LoadStart<'a> {
filename: PathBuf, filename: PathBuf,
mode: Mode, mode: Mode,
) -> Result<Self, LoadingProblem> { ) -> Result<Self, LoadingProblem> {
let arc_modules = Arc::new(Mutex::new(ModuleIds::default())); let arc_modules = Arc::new(Mutex::new(PackageModuleIds::default()));
let root_exposed_ident_ids = IdentIds::exposed_builtins(0); let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
let ident_ids_by_module = Arc::new(Mutex::new(root_exposed_ident_ids)); let ident_ids_by_module = Arc::new(Mutex::new(root_exposed_ident_ids));
@ -1011,6 +1035,7 @@ impl<'a> LoadStart<'a> {
load_filename( load_filename(
arena, arena,
filename, filename,
None,
Arc::clone(&arc_modules), Arc::clone(&arc_modules),
Arc::clone(&ident_ids_by_module), Arc::clone(&ident_ids_by_module),
root_start_time, root_start_time,
@ -1032,7 +1057,7 @@ impl<'a> LoadStart<'a> {
src: &'a str, src: &'a str,
mode: Mode, mode: Mode,
) -> Result<Self, LoadingProblem> { ) -> Result<Self, LoadingProblem> {
let arc_modules = Arc::new(Mutex::new(ModuleIds::default())); let arc_modules = Arc::new(Mutex::new(PackageModuleIds::default()));
let root_exposed_ident_ids = IdentIds::exposed_builtins(0); let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
let ident_ids_by_module = Arc::new(Mutex::new(root_exposed_ident_ids)); let ident_ids_by_module = Arc::new(Mutex::new(root_exposed_ident_ids));
@ -1126,6 +1151,8 @@ where
root_msg, root_msg,
} = load_start; } = load_start;
let arc_shorthands = Arc::new(Mutex::new(MutMap::default()));
let (msg_tx, msg_rx) = bounded(1024); let (msg_tx, msg_rx) = bounded(1024);
msg_tx msg_tx
.send(root_msg) .send(root_msg)
@ -1248,6 +1275,8 @@ where
goal_phase, goal_phase,
stdlib, stdlib,
output_path: None, output_path: None,
platform_path: None,
opt_effect_module: None,
module_cache: ModuleCache::default(), module_cache: ModuleCache::default(),
dependencies: Dependencies::default(), dependencies: Dependencies::default(),
procedures: MutMap::default(), procedures: MutMap::default(),
@ -1256,6 +1285,7 @@ where
headers_parsed, headers_parsed,
loading_started, loading_started,
arc_modules, arc_modules,
arc_shorthands,
constrained_ident_ids: IdentIds::exposed_builtins(0), constrained_ident_ids: IdentIds::exposed_builtins(0),
ident_ids_by_module, ident_ids_by_module,
declarations_by_id: MutMap::default(), declarations_by_id: MutMap::default(),
@ -1388,6 +1418,16 @@ fn update<'a>(
log!("loaded header for {:?}", header.module_id); log!("loaded header for {:?}", header.module_id);
let home = header.module_id; let home = header.module_id;
{
let mut shorthands = (*state.arc_shorthands).lock();
for (shorthand, package_or_path) in header.packages.iter() {
shorthands.insert(shorthand, package_or_path.clone());
}
}
state.platform_path = state.platform_path.or_else(|| header.to_platform.clone());
// store an ID to name mapping, so we know the file to read when fetching dependencies' headers // store an ID to name mapping, so we know the file to read when fetching dependencies' headers
for (name, id) in header.deps_by_name.iter() { for (name, id) in header.deps_by_name.iter() {
state.module_cache.module_names.insert(*id, name.clone()); state.module_cache.module_names.insert(*id, name.clone());
@ -1412,6 +1452,7 @@ fn update<'a>(
let work = state.dependencies.add_module( let work = state.dependencies.add_module(
header.module_id, header.module_id,
state.opt_effect_module,
&header.imported_modules, &header.imported_modules,
state.goal_phase, state.goal_phase,
); );
@ -1499,6 +1540,9 @@ fn update<'a>(
module_docs, module_docs,
} => { } => {
let module_id = constrained_module.module.module_id; let module_id = constrained_module.module.module_id;
state.opt_effect_module = Some(module_id);
log!("made effect module for {:?}", module_id); log!("made effect module for {:?}", module_id);
state state
.module_cache .module_cache
@ -1761,7 +1805,8 @@ fn finish_specialization<'a>(
) -> MonomorphizedModule<'a> { ) -> MonomorphizedModule<'a> {
let module_ids = Arc::try_unwrap(state.arc_modules) let module_ids = Arc::try_unwrap(state.arc_modules)
.unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids")) .unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids"))
.into_inner(); .into_inner()
.into_module_ids();
let interns = Interns { let interns = Interns {
module_ids, module_ids,
@ -1772,6 +1817,7 @@ fn finish_specialization<'a>(
procedures, procedures,
module_cache, module_cache,
output_path, output_path,
platform_path,
.. ..
} = state; } = state;
@ -1788,11 +1834,33 @@ fn finish_specialization<'a>(
.map(|(id, (path, src))| (id, (path, src.into()))) .map(|(id, (path, src))| (id, (path, src.into())))
.collect(); .collect();
let path_to_platform = {
let package_or_path = match platform_path {
Some(To::ExistingPackage(shorthand)) => {
match (*state.arc_shorthands).lock().get(shorthand) {
Some(p_or_p) => p_or_p.clone(),
None => unreachable!(),
}
}
Some(To::NewPackage(p_or_p)) => p_or_p,
None => panic!("no platform!"),
};
match package_or_path {
PackageOrPath::Path(StrLiteral::PlainLine(path)) => path,
PackageOrPath::Path(_) => unreachable!("invalid"),
_ => todo!("packages"),
}
};
let platform_path = path_to_platform.into();
MonomorphizedModule { MonomorphizedModule {
can_problems, can_problems,
mono_problems, mono_problems,
type_problems, type_problems,
output_path: output_path.unwrap_or(DEFAULT_APP_OUTPUT_PATH).into(), output_path: output_path.unwrap_or(DEFAULT_APP_OUTPUT_PATH).into(),
platform_path,
exposed_to_host, exposed_to_host,
module_id: state.root_id, module_id: state.root_id,
subs, subs,
@ -1811,7 +1879,8 @@ fn finish<'a>(
) -> LoadedModule { ) -> LoadedModule {
let module_ids = Arc::try_unwrap(state.arc_modules) let module_ids = Arc::try_unwrap(state.arc_modules)
.unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids")) .unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids"))
.into_inner(); .into_inner()
.into_module_ids();
let interns = Interns { let interns = Interns {
module_ids, module_ids,
@ -1843,19 +1912,14 @@ fn finish<'a>(
fn load_pkg_config<'a>( fn load_pkg_config<'a>(
arena: &'a Bump, arena: &'a Bump,
src_dir: &Path, src_dir: &Path,
module_ids: Arc<Mutex<ModuleIds>>, shorthand: &'a str,
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
mode: Mode, mode: Mode,
) -> Result<Msg<'a>, LoadingProblem> { ) -> Result<Msg<'a>, LoadingProblem> {
let module_start_time = SystemTime::now(); let module_start_time = SystemTime::now();
let mut filename = PathBuf::from(src_dir); let filename = PathBuf::from(src_dir);
filename.push("platform");
filename.push(PKG_CONFIG_FILE_NAME);
// End with .roc
filename.set_extension(ROC_FILE_EXTENSION);
let file_io_start = SystemTime::now(); let file_io_start = SystemTime::now();
let file = fs::read(&filename); let file = fs::read(&filename);
@ -1889,6 +1953,7 @@ fn load_pkg_config<'a>(
} }
Ok((ast::Module::Platform { header }, _parse_state)) => fabricate_effects_module( Ok((ast::Module::Platform { header }, _parse_state)) => fabricate_effects_module(
arena, arena,
shorthand,
module_ids, module_ids,
ident_ids_by_module, ident_ids_by_module,
mode, mode,
@ -1903,6 +1968,7 @@ fn load_pkg_config<'a>(
Err(err) => Err(LoadingProblem::FileProblem { Err(err) => Err(LoadingProblem::FileProblem {
filename, filename,
error: err.kind(), error: err.kind(),
msg: "while reading a Pkg-Config.roc file",
}), }),
} }
} }
@ -1911,8 +1977,9 @@ fn load_pkg_config<'a>(
fn load_module<'a>( fn load_module<'a>(
arena: &'a Bump, arena: &'a Bump,
src_dir: &Path, src_dir: &Path,
module_name: ModuleName, module_name: PQModuleName<'a>,
module_ids: Arc<Mutex<ModuleIds>>, module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
arc_shorthands: Arc<Mutex<MutMap<&'a str, PackageOrPath<'a>>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
mode: Mode, mode: Mode,
) -> Result<(ModuleId, Msg<'a>), LoadingProblem> { ) -> Result<(ModuleId, Msg<'a>), LoadingProblem> {
@ -1921,10 +1988,36 @@ fn load_module<'a>(
filename.push(src_dir); filename.push(src_dir);
let opt_shorthand;
match module_name {
PQModuleName::Unqualified(name) => {
opt_shorthand = None;
// Convert dots in module name to directories // Convert dots in module name to directories
for part in module_name.as_str().split(MODULE_SEPARATOR) { for part in name.split(MODULE_SEPARATOR) {
filename.push(part); filename.push(part);
} }
}
PQModuleName::Qualified(shorthand, name) => {
opt_shorthand = Some(shorthand);
let shorthands = arc_shorthands.lock();
match shorthands.get(shorthand) {
Some(PackageOrPath::Path(StrLiteral::PlainLine(path))) => {
filename.push(path);
}
Some(PackageOrPath::Path(_str_liteal)) => {
unreachable!("invalid structure for path")
}
Some(PackageOrPath::Package(_name, _version)) => todo!("packages"),
None => unreachable!("there is no shorthand named {:?}", shorthand),
}
// Convert dots in module name to directories
for part in name.split(MODULE_SEPARATOR) {
filename.push(part);
}
}
}
// End with .roc // End with .roc
filename.set_extension(ROC_FILE_EXTENSION); filename.set_extension(ROC_FILE_EXTENSION);
@ -1932,6 +2025,7 @@ fn load_module<'a>(
load_filename( load_filename(
arena, arena,
filename, filename,
opt_shorthand,
module_ids, module_ids,
ident_ids_by_module, ident_ids_by_module,
module_start_time, module_start_time,
@ -1970,7 +2064,8 @@ fn parse_header<'a>(
arena: &'a Bump, arena: &'a Bump,
read_file_duration: Duration, read_file_duration: Duration,
filename: PathBuf, filename: PathBuf,
module_ids: Arc<Mutex<ModuleIds>>, opt_shorthand: Option<&'a str>,
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
mode: Mode, mode: Mode,
src_bytes: &'a [u8], src_bytes: &'a [u8],
@ -1994,8 +2089,11 @@ fn parse_header<'a>(
value: AppOrInterfaceName::Interface(header.name.value), value: AppOrInterfaceName::Interface(header.name.value),
}, },
filename, filename,
opt_shorthand,
&[],
header.exposes.into_bump_slice(), header.exposes.into_bump_slice(),
header.imports.into_bump_slice(), header.imports.into_bump_slice(),
None,
parse_state, parse_state,
module_ids, module_ids,
ident_ids_by_module, ident_ids_by_module,
@ -2005,30 +2103,63 @@ fn parse_header<'a>(
let mut pkg_config_dir = filename.clone(); let mut pkg_config_dir = filename.clone();
pkg_config_dir.pop(); pkg_config_dir.pop();
let packages = header.packages.into_bump_slice();
let (module_id, app_module_header_msg) = send_header( let (module_id, app_module_header_msg) = send_header(
Located { Located {
region: header.name.region, region: header.name.region,
value: AppOrInterfaceName::App(header.name.value), value: AppOrInterfaceName::App(header.name.value),
}, },
filename, filename,
opt_shorthand,
packages,
header.provides.into_bump_slice(), header.provides.into_bump_slice(),
header.imports.into_bump_slice(), header.imports.into_bump_slice(),
Some(header.to.value.clone()),
parse_state, parse_state,
module_ids.clone(), module_ids.clone(),
ident_ids_by_module.clone(), ident_ids_by_module.clone(),
module_timing, module_timing,
); );
match header.to.value {
To::ExistingPackage(existing_package) => {
let opt_base_package = packages.iter().find(|loc_package_entry| {
let Located { value, .. } = loc_package_entry;
match value {
PackageEntry::Entry { shorthand, .. } => shorthand == &existing_package,
_ => false,
}
});
match opt_base_package {
Some(Located {
value:
PackageEntry::Entry {
shorthand,
package_or_path:
Located {
value: package_or_path,
..
},
..
},
..
}) => {
match package_or_path {
PackageOrPath::Path(StrLiteral::PlainLine(package)) => {
// check whether we can find a Pkg-Config.roc file // check whether we can find a Pkg-Config.roc file
let mut pkg_config_roc = pkg_config_dir.clone(); let mut pkg_config_roc = pkg_config_dir;
pkg_config_roc.push("platform"); pkg_config_roc.push(package);
pkg_config_roc.push(PKG_CONFIG_FILE_NAME); pkg_config_roc.push(PKG_CONFIG_FILE_NAME);
pkg_config_roc.set_extension(ROC_FILE_EXTENSION); pkg_config_roc.set_extension(ROC_FILE_EXTENSION);
if pkg_config_roc.as_path().exists() { if pkg_config_roc.as_path().exists() {
let load_pkg_config_msg = load_pkg_config( let load_pkg_config_msg = load_pkg_config(
arena, arena,
&pkg_config_dir, &pkg_config_roc,
shorthand,
module_ids, module_ids,
ident_ids_by_module, ident_ids_by_module,
mode, mode,
@ -2036,14 +2167,38 @@ fn parse_header<'a>(
Ok(( Ok((
module_id, module_id,
Msg::Many(vec![app_module_header_msg, load_pkg_config_msg]), Msg::Many(vec![
app_module_header_msg,
load_pkg_config_msg,
]),
)) ))
} else { } else {
Ok((module_id, app_module_header_msg)) Ok((module_id, app_module_header_msg))
} }
} }
_ => unreachable!(),
}
}
_ => panic!("could not find base"),
}
}
To::NewPackage(package_or_path) => match package_or_path {
PackageOrPath::Package(_, _) => panic!("TODO implement packages"),
PackageOrPath::Path(StrLiteral::PlainLine(_package)) => {
Ok((module_id, app_module_header_msg))
}
PackageOrPath::Path(StrLiteral::Block(_)) => {
panic!("TODO implement block package path")
}
PackageOrPath::Path(StrLiteral::Line(_)) => {
panic!("TODO implement line package path")
}
},
}
}
Ok((ast::Module::Platform { header }, _parse_state)) => fabricate_effects_module( Ok((ast::Module::Platform { header }, _parse_state)) => fabricate_effects_module(
arena, arena,
&"",
module_ids, module_ids,
ident_ids_by_module, ident_ids_by_module,
mode, mode,
@ -2058,7 +2213,8 @@ fn parse_header<'a>(
fn load_filename<'a>( fn load_filename<'a>(
arena: &'a Bump, arena: &'a Bump,
filename: PathBuf, filename: PathBuf,
module_ids: Arc<Mutex<ModuleIds>>, opt_shorthand: Option<&'a str>,
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
module_start_time: SystemTime, module_start_time: SystemTime,
mode: Mode, mode: Mode,
@ -2072,6 +2228,7 @@ fn load_filename<'a>(
arena, arena,
file_io_duration, file_io_duration,
filename, filename,
opt_shorthand,
module_ids, module_ids,
ident_ids_by_module, ident_ids_by_module,
mode, mode,
@ -2081,17 +2238,19 @@ fn load_filename<'a>(
Err(err) => Err(LoadingProblem::FileProblem { Err(err) => Err(LoadingProblem::FileProblem {
filename, filename,
error: err.kind(), error: err.kind(),
msg: "in `load_filename`",
}), }),
} }
} }
/// Load a module from a str /// Load a module from a str
/// the `filename` is never read, but used for the module name /// the `filename` is never read, but used for the module name
#[allow(clippy::too_many_arguments)]
fn load_from_str<'a>( fn load_from_str<'a>(
arena: &'a Bump, arena: &'a Bump,
filename: PathBuf, filename: PathBuf,
src: &'a str, src: &'a str,
module_ids: Arc<Mutex<ModuleIds>>, module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
module_start_time: SystemTime, module_start_time: SystemTime,
mode: Mode, mode: Mode,
@ -2103,6 +2262,7 @@ fn load_from_str<'a>(
arena, arena,
file_io_duration, file_io_duration,
filename, filename,
None,
module_ids, module_ids,
ident_ids_by_module, ident_ids_by_module,
mode, mode,
@ -2122,10 +2282,13 @@ enum AppOrInterfaceName<'a> {
fn send_header<'a>( fn send_header<'a>(
loc_name: Located<AppOrInterfaceName<'a>>, loc_name: Located<AppOrInterfaceName<'a>>,
filename: PathBuf, filename: PathBuf,
opt_shorthand: Option<&'a str>,
packages: &'a [Located<PackageEntry<'a>>],
exposes: &'a [Located<ExposesEntry<'a, &'a str>>], exposes: &'a [Located<ExposesEntry<'a, &'a str>>],
imports: &'a [Located<ImportsEntry<'a>>], imports: &'a [Located<ImportsEntry<'a>>],
to_platform: Option<To<'a>>,
parse_state: parser::State<'a>, parse_state: parser::State<'a>,
module_ids: Arc<Mutex<ModuleIds>>, module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
module_timing: ModuleTiming, module_timing: ModuleTiming,
) -> (ModuleId, Msg<'a>) { ) -> (ModuleId, Msg<'a>) {
@ -2141,20 +2304,21 @@ fn send_header<'a>(
} }
}; };
let mut imported: Vec<(ModuleName, Vec<Ident>, Region)> = Vec::with_capacity(imports.len()); let mut imported: Vec<(QualifiedModuleName, Vec<Ident>, Region)> =
Vec::with_capacity(imports.len());
let mut imported_modules: MutSet<ModuleId> = MutSet::default(); let mut imported_modules: MutSet<ModuleId> = MutSet::default();
let mut scope_size = 0; let mut scope_size = 0;
for loc_entry in imports { for loc_entry in imports {
let (module_name, exposed) = exposed_from_import(&loc_entry.value); let (qualified_module_name, exposed) = exposed_from_import(&loc_entry.value);
scope_size += exposed.len(); scope_size += exposed.len();
imported.push((module_name, exposed, loc_entry.region)); imported.push((qualified_module_name, exposed, loc_entry.region));
} }
let num_exposes = exposes.len(); let num_exposes = exposes.len();
let mut deps_by_name: MutMap<ModuleName, ModuleId> = let mut deps_by_name: MutMap<PQModuleName, ModuleId> =
HashMap::with_capacity_and_hasher(num_exposes, default_hasher()); HashMap::with_capacity_and_hasher(num_exposes, default_hasher());
let mut exposed: Vec<Symbol> = Vec::with_capacity(num_exposes); let mut exposed: Vec<Symbol> = Vec::with_capacity(num_exposes);
@ -2169,7 +2333,13 @@ fn send_header<'a>(
let mut module_ids = (*module_ids).lock(); let mut module_ids = (*module_ids).lock();
let mut ident_ids_by_module = (*ident_ids_by_module).lock(); let mut ident_ids_by_module = (*ident_ids_by_module).lock();
home = module_ids.get_or_insert(&declared_name.as_inline_str()); let name = match opt_shorthand {
Some(shorthand) => {
PQModuleName::Qualified(&shorthand, declared_name.as_inline_str().clone())
}
None => PQModuleName::Unqualified(declared_name.as_inline_str().clone()),
};
home = module_ids.get_or_insert(&name);
// Ensure this module has an entry in the exposed_ident_ids map. // Ensure this module has an entry in the exposed_ident_ids map.
ident_ids_by_module ident_ids_by_module
@ -2178,17 +2348,28 @@ fn send_header<'a>(
// For each of our imports, add an entry to deps_by_name // For each of our imports, add an entry to deps_by_name
// //
// e.g. for `imports [ Foo.{ bar } ]`, add `Foo` to deps_by_name // e.g. for `imports [ base.Foo.{ bar } ]`, add `Foo` to deps_by_name
// //
// Also build a list of imported_values_to_expose (like `bar` above.) // Also build a list of imported_values_to_expose (like `bar` above.)
for (module_name, exposed_idents, region) in imported.into_iter() { for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
let cloned_module_name = module_name.clone(); let cloned_module_name = qualified_module_name.module.clone();
let module_id = module_ids.get_or_insert(&module_name.into()); let pq_module_name = match qualified_module_name.opt_package {
None => match opt_shorthand {
deps_by_name.insert(cloned_module_name, module_id); Some(shorthand) => {
PQModuleName::Qualified(shorthand, qualified_module_name.module.into())
}
None => PQModuleName::Unqualified(qualified_module_name.module.into()),
},
Some(package) => {
PQModuleName::Qualified(package, cloned_module_name.clone().into())
}
};
let module_id = module_ids.get_or_insert(&pq_module_name);
imported_modules.insert(module_id); imported_modules.insert(module_id);
deps_by_name.insert(pq_module_name, module_id);
// Add the new exposed idents to the dep module's IdentIds, so // Add the new exposed idents to the dep module's IdentIds, so
// once that module later gets loaded, its lookups will resolve // once that module later gets loaded, its lookups will resolve
// to the same symbols as the ones we're using here. // to the same symbols as the ones we're using here.
@ -2236,6 +2417,25 @@ fn send_header<'a>(
ident_ids.clone() ident_ids.clone()
}; };
let mut parse_entries: Vec<_> = (&packages).iter().map(|x| &x.value).collect();
let mut package_entries = MutMap::default();
while let Some(parse_entry) = parse_entries.pop() {
use PackageEntry::*;
match parse_entry {
Entry {
shorthand,
package_or_path,
..
} => {
package_entries.insert(*shorthand, package_or_path.value.clone());
}
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
parse_entries.push(inner);
}
}
}
// Send the deps to the coordinator thread for processing, // Send the deps to the coordinator thread for processing,
// then continue on to parsing and canonicalizing defs. // then continue on to parsing and canonicalizing defs.
// //
@ -2249,9 +2449,11 @@ fn send_header<'a>(
module_path: filename, module_path: filename,
exposed_ident_ids: ident_ids, exposed_ident_ids: ident_ids,
module_name: loc_name.value, module_name: loc_name.value,
packages: package_entries,
imported_modules, imported_modules,
deps_by_name, deps_by_name,
exposes: exposed, exposes: exposed,
to_platform,
src: parse_state.bytes, src: parse_state.bytes,
exposed_imports: scope, exposed_imports: scope,
module_timing, module_timing,
@ -2370,9 +2572,11 @@ fn run_solve<'a>(
} }
} }
#[allow(clippy::too_many_arguments)]
fn fabricate_effects_module<'a>( fn fabricate_effects_module<'a>(
arena: &'a Bump, arena: &'a Bump,
module_ids: Arc<Mutex<ModuleIds>>, shorthand: &'a str,
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>, ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
mode: Mode, mode: Mode,
header: PlatformHeader<'a>, header: PlatformHeader<'a>,
@ -2398,12 +2602,23 @@ fn fabricate_effects_module<'a>(
functions functions
}; };
{
let mut module_ids = (*module_ids).lock();
for exposed in header.exposes {
if let ExposesEntry::Exposed(module_name) = exposed.value {
module_ids.get_or_insert(&PQModuleName::Qualified(shorthand, module_name.into()));
}
}
}
let exposed_ident_ids = { let exposed_ident_ids = {
// Lock just long enough to perform the minimal operations necessary. // Lock just long enough to perform the minimal operations necessary.
let mut module_ids = (*module_ids).lock(); let mut module_ids = (*module_ids).lock();
let mut ident_ids_by_module = (*ident_ids_by_module).lock(); let mut ident_ids_by_module = (*ident_ids_by_module).lock();
module_id = module_ids.get_or_insert(&declared_name.as_inline_str()); let name = PQModuleName::Qualified(shorthand, declared_name.as_inline_str().clone());
module_id = module_ids.get_or_insert(&name);
// Ensure this module has an entry in the exposed_ident_ids map. // Ensure this module has an entry in the exposed_ident_ids map.
ident_ids_by_module ident_ids_by_module
@ -2458,7 +2673,7 @@ fn fabricate_effects_module<'a>(
let mut var_store = VarStore::default(); let mut var_store = VarStore::default();
let module_ids = { (*module_ids).lock().clone() }; let module_ids = { (*module_ids).lock().clone() }.into_module_ids();
let mut scope = roc_can::scope::Scope::new(module_id, &mut var_store); let mut scope = roc_can::scope::Scope::new(module_id, &mut var_store);
let mut can_env = roc_can::env::Env::new(module_id, dep_idents, &module_ids, exposed_ident_ids); let mut can_env = roc_can::env::Env::new(module_id, dep_idents, &module_ids, exposed_ident_ids);
@ -2782,7 +2997,7 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
Ok(Msg::Parsed(parsed)) Ok(Msg::Parsed(parsed))
} }
fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec<Ident>) { fn exposed_from_import<'a>(entry: &ImportsEntry<'a>) -> (QualifiedModuleName<'a>, Vec<Ident>) {
use roc_parse::header::ImportsEntry::*; use roc_parse::header::ImportsEntry::*;
match entry { match entry {
@ -2793,11 +3008,27 @@ fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec<Ident>) {
exposed.push(ident_from_exposed(&loc_entry.value)); exposed.push(ident_from_exposed(&loc_entry.value));
} }
(module_name.as_str().into(), exposed) let qualified_module_name = QualifiedModuleName {
opt_package: None,
module: module_name.as_str().into(),
};
(qualified_module_name, exposed)
} }
Package(_package_name, _module_name, _exposes) => { Package(package_name, module_name, exposes) => {
todo!("TODO support exposing package-qualified module names."); let mut exposed = Vec::with_capacity(exposes.len());
for loc_entry in exposes {
exposed.push(ident_from_exposed(&loc_entry.value));
}
let qualified_module_name = QualifiedModuleName {
opt_package: Some(package_name),
module: module_name.as_str().into(),
};
(qualified_module_name, exposed)
} }
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => { SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => {
@ -3074,6 +3305,7 @@ fn run_task<'a>(
LoadModule { LoadModule {
module_name, module_name,
module_ids, module_ids,
shorthands,
ident_ids_by_module, ident_ids_by_module,
mode, mode,
} => load_module( } => load_module(
@ -3081,15 +3313,24 @@ fn run_task<'a>(
src_dir, src_dir,
module_name, module_name,
module_ids, module_ids,
shorthands,
ident_ids_by_module, ident_ids_by_module,
mode, mode,
) )
.map(|(_, msg)| msg), .map(|(_, msg)| msg),
LoadPkgConfig { LoadPkgConfig {
shorthand,
module_ids, module_ids,
ident_ids_by_module, ident_ids_by_module,
mode, mode,
} => load_pkg_config(arena, src_dir, module_ids, ident_ids_by_module, mode), } => load_pkg_config(
arena,
src_dir,
shorthand,
module_ids,
ident_ids_by_module,
mode,
),
Parse { header } => parse(arena, header), Parse { header } => parse(arena, header),
CanonicalizeAndConstrain { CanonicalizeAndConstrain {
parsed, parsed,

View file

@ -1,6 +1,6 @@
app "quicksort" provides [ swap, partition, partitionHelp, quicksort ] to "./platform" app "quicksort" provides [ swap, partition, partitionHelp, quicksort ] to "./platform"
quicksort : List (Num a), Int, Int -> List (Num a) quicksort : List (Num a), I64, I64 -> List (Num a)
quicksort = \list, low, high -> quicksort = \list, low, high ->
when partition low high list is when partition low high list is
Pair partitionIndex partitioned -> Pair partitionIndex partitioned ->
@ -9,7 +9,7 @@ quicksort = \list, low, high ->
|> quicksort (partitionIndex + 1) high |> quicksort (partitionIndex + 1) high
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -21,7 +21,7 @@ swap = \i, j, list ->
[] []
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -33,7 +33,7 @@ partition = \low, high, initialList ->
Pair (low - 1) initialList Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is

View file

@ -1,7 +1,7 @@
app "quicksort" provides [ quicksort ] to "./platform" app "quicksort" provides [ quicksort ] to "./platform"
quicksort = \originalList -> quicksort = \originalList ->
quicksortHelp : List (Num a), Int, Int -> List (Num a) quicksortHelp : List (Num a), I64, I64 -> List (Num a)
quicksortHelp = \list, low, high -> quicksortHelp = \list, low, high ->
if low < high then if low < high then
when partition low high list is when partition low high list is
@ -13,7 +13,7 @@ quicksort = \originalList ->
list list
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -24,7 +24,7 @@ quicksort = \originalList ->
_ -> _ ->
[] []
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -36,7 +36,7 @@ quicksort = \originalList ->
Pair (low - 1) initialList Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is

View file

@ -2,7 +2,7 @@ interface Quicksort
exposes [ swap, partition, quicksort ] exposes [ swap, partition, quicksort ]
imports [] imports []
quicksort : List (Num a), Int, Int -> List (Num a) quicksort : List (Num a), I64, I64 -> List (Num a)
quicksort = \list, low, high -> quicksort = \list, low, high ->
when partition low high list is when partition low high list is
Pair partitionIndex partitioned -> Pair partitionIndex partitioned ->
@ -11,7 +11,7 @@ quicksort = \list, low, high ->
|> quicksort (partitionIndex + 1) high |> quicksort (partitionIndex + 1) high
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -23,7 +23,7 @@ swap = \i, j, list ->
[] []
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -35,7 +35,7 @@ partition = \low, high, initialList ->
Pair (low - 1) initialList Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is

View file

@ -265,7 +265,7 @@ mod test_load {
imports [ RBTree ] imports [ RBTree ]
provides [ main ] to blah provides [ main ] to blah
empty : RBTree.Dict Int Int empty : RBTree.Dict I64 I64
empty = RBTree.empty empty = RBTree.empty
main = empty main = empty
@ -360,7 +360,7 @@ mod test_load {
"floatTest" => "F64", "floatTest" => "F64",
"divisionFn" => "F64, F64 -> Result F64 [ DivByZero ]*", "divisionFn" => "F64, F64 -> Result F64 [ DivByZero ]*",
"divisionTest" => "Result F64 [ DivByZero ]*", "divisionTest" => "Result F64 [ DivByZero ]*",
"intTest" => "Int", "intTest" => "I64",
"x" => "F64", "x" => "F64",
"constantNum" => "Num *", "constantNum" => "Num *",
"divDep1ByDep2" => "Result F64 [ DivByZero ]*", "divDep1ByDep2" => "Result F64 [ DivByZero ]*",
@ -377,10 +377,10 @@ mod test_load {
expect_types( expect_types(
loaded_module, loaded_module,
hashmap! { hashmap! {
"swap" => "Int, Int, List a -> List a", "swap" => "I64, I64, List a -> List a",
"partition" => "Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]", "partition" => "I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]",
"partitionHelp" => "Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ]", "partitionHelp" => "I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]",
"quicksort" => "List (Num a), Int, Int -> List (Num a)", "quicksort" => "List (Num a), I64, I64 -> List (Num a)",
}, },
); );
} }
@ -406,10 +406,10 @@ mod test_load {
expect_types( expect_types(
loaded_module, loaded_module,
hashmap! { hashmap! {
"swap" => "Int, Int, List a -> List a", "swap" => "I64, I64, List a -> List a",
"partition" => "Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]", "partition" => "I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]",
"partitionHelp" => "Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ]", "partitionHelp" => "I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]",
"quicksort" => "List (Num a), Int, Int -> List (Num a)", "quicksort" => "List (Num a), I64, I64 -> List (Num a)",
}, },
); );
} }

View file

@ -235,7 +235,7 @@ mod test_uniq_load {
"floatTest" => "Attr Shared F64", "floatTest" => "Attr Shared F64",
"divisionFn" => "Attr Shared (Attr * F64, Attr * F64 -> Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*)))", "divisionFn" => "Attr Shared (Attr * F64, Attr * F64 -> Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*)))",
"divisionTest" => "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))", "divisionTest" => "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))",
"intTest" => "Attr * Int", "intTest" => "Attr * I64",
"x" => "Attr * F64", "x" => "Attr * F64",
"constantNum" => "Attr * (Num (Attr * *))", "constantNum" => "Attr * (Num (Attr * *))",
"divDep1ByDep2" => "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))", "divDep1ByDep2" => "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))",
@ -271,11 +271,11 @@ mod test_uniq_load {
expect_types( expect_types(
loaded_module, loaded_module,
hashmap! { hashmap! {
"swap" => "Attr * (Attr * Int, Attr * Int, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))", "swap" => "Attr * (Attr * I64, Attr * I64, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))",
"partition" => "Attr * (Attr Shared Int, Attr Shared Int, Attr b (List (Attr Shared (Num (Attr Shared a)))) -> Attr * [ Pair (Attr * Int) (Attr b (List (Attr Shared (Num (Attr Shared a))))) ])", "partition" => "Attr * (Attr Shared I64, Attr Shared I64, Attr b (List (Attr Shared (Num (Attr Shared a)))) -> Attr * [ Pair (Attr * I64) (Attr b (List (Attr Shared (Num (Attr Shared a))))) ])",
"partitionHelp" => "Attr Shared (Attr b Int, Attr Shared Int, Attr c (List (Attr Shared (Num (Attr Shared a)))), Attr Shared Int, Attr Shared (Num (Attr Shared a)) -> Attr * [ Pair (Attr b Int) (Attr c (List (Attr Shared (Num (Attr Shared a))))) ])", "partitionHelp" => "Attr Shared (Attr b I64, Attr Shared I64, Attr c (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared (Num (Attr Shared a)) -> Attr * [ Pair (Attr b I64) (Attr c (List (Attr Shared (Num (Attr Shared a))))) ])",
"quicksort" => "Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared Int, Attr Shared Int -> Attr b (List (Attr Shared (Num (Attr Shared a)))))", "quicksort" => "Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared I64 -> Attr b (List (Attr Shared (Num (Attr Shared a)))))",
}, },
); );
} }

View file

@ -12,6 +12,11 @@ impl Ident {
} }
} }
pub struct QualifiedModuleName<'a> {
pub opt_package: Option<&'a str>,
pub module: ModuleName,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ModuleName(InlinableString); pub struct ModuleName(InlinableString);

View file

@ -310,6 +310,107 @@ impl fmt::Debug for ModuleId {
} }
} }
/// base.Task
/// 1. build mapping from short name to package
/// 2. when adding new modules from package we need to register them in some other map (this module id goes with short name) (shortname, module-name) -> moduleId
/// 3. pass this around to other modules getting headers parsed. when parsing interfaces we need to use this map to reference shortnames
/// 4. throw away short names. stash the module id in the can env under the resolved module name
/// 5. test:
/// Package-qualified module name
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PQModuleName<'a> {
Unqualified(InlinableString),
Qualified(&'a str, InlinableString),
}
impl<'a> PQModuleName<'a> {
pub fn to_module_name(&self) -> &InlinableString {
match self {
PQModuleName::Unqualified(name) => name,
PQModuleName::Qualified(_, name) => name,
}
}
}
#[derive(Debug, Clone)]
pub struct PackageModuleIds<'a> {
by_name: MutMap<PQModuleName<'a>, ModuleId>,
by_id: Vec<PQModuleName<'a>>,
}
impl<'a> PackageModuleIds<'a> {
pub fn get_or_insert(&mut self, module_name: &PQModuleName<'a>) -> ModuleId {
match self.by_name.get(module_name) {
Some(id) => *id,
None => {
let by_id = &mut self.by_id;
let module_id = ModuleId(by_id.len() as u32);
by_id.push(module_name.clone());
self.by_name.insert(module_name.clone(), module_id);
if cfg!(debug_assertions) {
Self::insert_debug_name(module_id, &module_name);
}
module_id
}
}
}
pub fn into_module_ids(self) -> ModuleIds {
let by_name: MutMap<InlinableString, ModuleId> = self
.by_name
.into_iter()
.map(|(pqname, module_id)| (pqname.to_module_name().clone(), module_id))
.collect();
let by_id: Vec<InlinableString> = self
.by_id
.into_iter()
.map(|pqname| pqname.to_module_name().clone())
.collect();
ModuleIds { by_name, by_id }
}
#[cfg(debug_assertions)]
fn insert_debug_name(module_id: ModuleId, module_name: &PQModuleName) {
let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked.");
if !names.contains_key(&module_id.0) {
match module_name {
PQModuleName::Unqualified(module) => {
names.insert(module_id.0, module.to_string().into());
}
PQModuleName::Qualified(package, module) => {
let name = format!("{}.{}", package, module).into();
names.insert(module_id.0, name);
}
}
}
}
#[cfg(not(debug_assertions))]
fn insert_debug_name(_module_id: ModuleId, _module_name: &PQModuleName) {
// By design, this is a no-op in release builds!
}
pub fn get_id(&self, module_name: &PQModuleName<'a>) -> Option<&ModuleId> {
self.by_name.get(module_name)
}
pub fn get_name(&self, id: ModuleId) -> Option<&PQModuleName> {
self.by_id.get(id.0 as usize)
}
pub fn available_modules(&self) -> impl Iterator<Item = &PQModuleName> {
self.by_id.iter()
}
}
/// Stores a mapping between ModuleId and InlinableString. /// Stores a mapping between ModuleId and InlinableString.
/// ///
/// Each module name is stored twice, for faster lookups. /// Each module name is stored twice, for faster lookups.
@ -346,8 +447,11 @@ impl ModuleIds {
fn insert_debug_name(module_id: ModuleId, module_name: &InlinableString) { fn insert_debug_name(module_id: ModuleId, module_name: &InlinableString) {
let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked."); let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked.");
// TODO make sure modules are never added more than once!
if !names.contains_key(&module_id.0) {
names.insert(module_id.0, module_name.to_string().into()); names.insert(module_id.0, module_name.to_string().into());
} }
}
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
fn insert_debug_name(_module_id: ModuleId, _module_name: &InlinableString) { fn insert_debug_name(_module_id: ModuleId, _module_name: &InlinableString) {
@ -495,7 +599,8 @@ macro_rules! define_builtins {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
let module_id = ModuleId($module_id); let module_id = ModuleId($module_id);
ModuleIds::insert_debug_name(module_id, &$module_name.into()); let name = PQModuleName::Unqualified($module_name.into());
PackageModuleIds::insert_debug_name(module_id, &name);
module_id.register_debug_idents(&ident_ids); module_id.register_debug_idents(&ident_ids);
} }
@ -551,6 +656,34 @@ macro_rules! define_builtins {
} }
} }
impl<'a> Default for PackageModuleIds<'a> {
fn default() -> Self {
// +1 because the user will be compiling at least 1 non-builtin module!
let capacity = $total + 1;
let mut by_name = HashMap::with_capacity_and_hasher(capacity, default_hasher());
let mut by_id = Vec::with_capacity(capacity);
let mut insert_both = |id: ModuleId, name_str: &'static str| {
let raw_name: InlinableString = name_str.into();
let name = PQModuleName::Unqualified(raw_name);
if cfg!(debug_assertions) {
Self::insert_debug_name(id, &name);
}
by_name.insert(name.clone(), id);
by_id.push(name);
};
$(
insert_both(ModuleId($module_id), $module_name);
)+
PackageModuleIds { by_name, by_id }
}
}
impl Symbol { impl Symbol {
$( $(
$( $(
@ -609,7 +742,7 @@ define_builtins! {
1 NUM: "Num" => { 1 NUM: "Num" => {
0 NUM_NUM: "Num" imported // the Num.Num type alias 0 NUM_NUM: "Num" imported // the Num.Num type alias
1 NUM_AT_NUM: "@Num" // the Num.@Num private tag 1 NUM_AT_NUM: "@Num" // the Num.@Num private tag
2 NUM_INT: "Int" imported // the Int.Int type alias 2 NUM_I64: "I64" imported // the Num.I64 type alias
3 NUM_INTEGER: "Integer" imported // Int : Num Integer 3 NUM_INTEGER: "Integer" imported // Int : Num Integer
4 NUM_AT_INTEGER: "@Integer" // the Int.@Integer private tag 4 NUM_AT_INTEGER: "@Integer" // the Int.@Integer private tag
5 NUM_F64: "F64" imported // the Num.F64 type alias 5 NUM_F64: "F64" imported // the Num.F64 type alias

View file

@ -323,7 +323,7 @@ impl<'a> Layout<'a> {
} }
Structure(flat_type) => layout_from_flat_type(env, flat_type), Structure(flat_type) => layout_from_flat_type(env, flat_type),
Alias(Symbol::NUM_INT, args, _) => { Alias(Symbol::NUM_I64, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int64)) Ok(Layout::Builtin(Builtin::Int64))
} }
@ -723,7 +723,7 @@ fn layout_from_flat_type<'a>(
match flat_type { match flat_type {
Apply(symbol, args) => { Apply(symbol, args) => {
match symbol { match symbol {
Symbol::NUM_INT => { Symbol::NUM_I64 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int64)) Ok(Layout::Builtin(Builtin::Int64))
} }

View file

@ -375,7 +375,7 @@ mod test_mono {
fn ir_when_just() { fn ir_when_just() {
compiles_to_ir( compiles_to_ir(
r#" r#"
x : [ Nothing, Just Int ] x : [ Nothing, Just I64 ]
x = Just 41 x = Just 41
when x is when x is
@ -412,7 +412,7 @@ mod test_mono {
fn one_element_tag() { fn one_element_tag() {
compiles_to_ir( compiles_to_ir(
r#" r#"
x : [ Pair Int ] x : [ Pair I64 ]
x = Pair 2 x = Pair 2
x x
@ -502,7 +502,7 @@ mod test_mono {
r#" r#"
Maybe a : [ Nothing, Just a ] Maybe a : [ Nothing, Just a ]
x : Maybe (Maybe Int) x : Maybe (Maybe I64)
x = Just (Just 41) x = Just (Just 41)
when x is when x is
@ -795,7 +795,7 @@ mod test_mono {
compiles_to_ir( compiles_to_ir(
r#" r#"
wrapper = \{} -> wrapper = \{} ->
x : Result Int Int x : Result I64 I64
x = Ok 2 x = Ok 2
y = y =
@ -1002,10 +1002,10 @@ mod test_mono {
compiles_to_ir( compiles_to_ir(
indoc!( indoc!(
r#" r#"
x : List Int x : List I64
x = [1,2,3] x = [1,2,3]
id : List Int -> List Int id : List I64 -> List I64
id = \y -> List.set y 0 0 id = \y -> List.set y 0 0
id x id x
@ -1185,7 +1185,7 @@ mod test_mono {
compiles_to_ir( compiles_to_ir(
indoc!( indoc!(
r#" r#"
quicksortHelp : List (Num a), Int, Int -> List (Num a) quicksortHelp : List (Num a), I64, I64 -> List (Num a)
quicksortHelp = \list, low, high -> quicksortHelp = \list, low, high ->
if low < high then if low < high then
(Pair partitionIndex partitioned) = Pair 0 [] (Pair partitionIndex partitioned) = Pair 0 []
@ -1376,7 +1376,7 @@ mod test_mono {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is
@ -1412,7 +1412,7 @@ mod test_mono {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
quicksortHelp : List (Num a), Int, Int -> List (Num a) quicksortHelp : List (Num a), I64, I64 -> List (Num a)
quicksortHelp = \list, low, high -> quicksortHelp = \list, low, high ->
if low < high then if low < high then
(Pair partitionIndex partitioned) = partition low high list (Pair partitionIndex partitioned) = partition low high list
@ -1424,7 +1424,7 @@ mod test_mono {
list list
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -1435,7 +1435,7 @@ mod test_mono {
_ -> _ ->
[] []
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -1447,7 +1447,7 @@ mod test_mono {
Pair (low - 1) initialList Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is
@ -1690,10 +1690,10 @@ mod test_mono {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
x : List Int x : List I64
x = [1,2,3] x = [1,2,3]
add : List Int -> List Int add : List I64 -> List I64
add = \y -> List.set y 0 0 add = \y -> List.set y 0 0
main = main =
@ -1851,7 +1851,6 @@ mod test_mono {
let Test.2 = S Test.9 Test.8; let Test.2 = S Test.9 Test.8;
let Test.5 = 1i64; let Test.5 = 1i64;
let Test.6 = Index 0 Test.2; let Test.6 = Index 0 Test.2;
dec Test.2;
let Test.7 = lowlevel Eq Test.5 Test.6; let Test.7 = lowlevel Eq Test.5 Test.6;
if Test.7 then if Test.7 then
let Test.3 = 0i64; let Test.3 = 0i64;
@ -1903,15 +1902,12 @@ mod test_mono {
let Test.10 = lowlevel Eq Test.8 Test.9; let Test.10 = lowlevel Eq Test.8 Test.9;
if Test.10 then if Test.10 then
let Test.4 = Index 1 Test.2; let Test.4 = Index 1 Test.2;
dec Test.2;
let Test.3 = 1i64; let Test.3 = 1i64;
ret Test.3; ret Test.3;
else else
dec Test.2;
let Test.5 = 0i64; let Test.5 = 0i64;
ret Test.5; ret Test.5;
else else
dec Test.2;
let Test.6 = 0i64; let Test.6 = 0i64;
ret Test.6; ret Test.6;
"# "#
@ -1996,7 +1992,7 @@ mod test_mono {
r#" r#"
Maybe a : [ Nothing, Just a ] Maybe a : [ Nothing, Just a ]
x : Maybe (Maybe Int) x : Maybe (Maybe I64)
x = Just (Just 41) x = Just (Just 41)
when x is when x is
@ -2051,10 +2047,10 @@ mod test_mono {
r#" r#"
LinkedList a : [ Nil, Cons a (LinkedList a) ] LinkedList a : [ Nil, Cons a (LinkedList a) ]
nil : LinkedList Int nil : LinkedList I64
nil = Nil nil = Nil
length : LinkedList a -> Int length : LinkedList a -> I64
length = \list -> length = \list ->
when list is when list is
Nil -> 0 Nil -> 0
@ -2106,7 +2102,7 @@ mod test_mono {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->

View file

@ -1608,8 +1608,8 @@ fn to_diff<'b>(
let right = to_doc(alloc, Parens::Unnecessary, type2); let right = to_doc(alloc, Parens::Unnecessary, type2);
let is_int = |t: &ErrorType| match t { let is_int = |t: &ErrorType| match t {
ErrorType::Type(Symbol::NUM_INT, _) => true, ErrorType::Type(Symbol::NUM_I64, _) => true,
ErrorType::Alias(Symbol::NUM_INT, _, _) => true, ErrorType::Alias(Symbol::NUM_I64, _, _) => true,
ErrorType::Type(Symbol::NUM_NUM, args) => match &args.get(0) { ErrorType::Type(Symbol::NUM_NUM, args) => match &args.get(0) {
Some(ErrorType::Type(Symbol::NUM_INTEGER, _)) => true, Some(ErrorType::Type(Symbol::NUM_INTEGER, _)) => true,

View file

@ -623,7 +623,7 @@ mod test_reporting {
Num Num
Set Set
Result Result
Int F64
"# "#
), ),
); );
@ -634,7 +634,7 @@ mod test_reporting {
// report_problem_as( // report_problem_as(
// indoc!( // indoc!(
// r#" // r#"
// foo : Int as Int // foo : I64 as I64
// foo = 42 // foo = 42
// //
// foo // foo
@ -657,7 +657,7 @@ mod test_reporting {
// report_problem_as( // report_problem_as(
// indoc!( // indoc!(
// r#" // r#"
// foo : Int as a // foo : I64 as a
// foo = 42 // foo = 42
// //
// foo // foo
@ -961,7 +961,7 @@ mod test_reporting {
r#" r#"
bar = { bar : 0x3 } bar = { bar : 0x3 }
f : { foo : Int } -> Bool f : { foo : I64 } -> Bool
f = \_ -> True f = \_ -> True
f bar f bar
@ -978,11 +978,11 @@ mod test_reporting {
This `bar` value is a: This `bar` value is a:
{ bar : Int } { bar : I64 }
But `f` needs the 1st argument to be: But `f` needs the 1st argument to be:
{ foo : Int } { foo : I64 }
Tip: Seems like a record field typo. Maybe `bar` should be `foo`? Tip: Seems like a record field typo. Maybe `bar` should be `foo`?
@ -1037,7 +1037,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : [ Red Int, Green Bool ] -> Bool f : [ Red I64, Green Bool ] -> Bool
f = \_ -> True f = \_ -> True
f (Blue 3.14) f (Blue 3.14)
@ -1058,7 +1058,7 @@ mod test_reporting {
But `f` needs the 1st argument to be: But `f` needs the 1st argument to be:
[ Green Bool, Red Int ] [ Green Bool, Red I64 ]
Tip: Seems like a tag typo. Maybe `Blue` should be `Red`? Tip: Seems like a tag typo. Maybe `Blue` should be `Red`?
@ -1075,7 +1075,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
x : Int x : I64
x = if True then 3.14 else 4 x = if True then 3.14 else 4
x x
@ -1096,7 +1096,7 @@ mod test_reporting {
But the type annotation on `x` says it should be: But the type annotation on `x` says it should be:
Int I64
Tip: You can convert between Int and Float using functions like Tip: You can convert between Int and Float using functions like
`Num.toFloat` and `Num.round`. `Num.toFloat` and `Num.round`.
@ -1110,7 +1110,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
x : Int x : I64
x = x =
when True is when True is
_ -> 3.14 _ -> 3.14
@ -1124,7 +1124,7 @@ mod test_reporting {
Something is off with the body of the `x` definition: Something is off with the body of the `x` definition:
1 x : Int 1 x : I64
2 x = 2 x =
3> when True is 3> when True is
4> _ -> 3.14 4> _ -> 3.14
@ -1135,7 +1135,7 @@ mod test_reporting {
But the type annotation on `x` says it should be: But the type annotation on `x` says it should be:
Int I64
Tip: You can convert between Int and Float using functions like Tip: You can convert between Int and Float using functions like
`Num.toFloat` and `Num.round`. `Num.toFloat` and `Num.round`.
@ -1149,7 +1149,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
x : Int -> Int x : I64 -> I64
x = \_ -> 3.14 x = \_ -> 3.14
x x
@ -1161,7 +1161,7 @@ mod test_reporting {
Something is off with the body of the `x` definition: Something is off with the body of the `x` definition:
1 x : Int -> Int 1 x : I64 -> I64
2 x = \_ -> 3.14 2 x = \_ -> 3.14
^^^^ ^^^^
@ -1171,7 +1171,7 @@ mod test_reporting {
But the type annotation on `x` says it should be: But the type annotation on `x` says it should be:
Int I64
Tip: You can convert between Int and Float using functions like Tip: You can convert between Int and Float using functions like
`Num.toFloat` and `Num.round`. `Num.toFloat` and `Num.round`.
@ -1185,7 +1185,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
x : Int x : I64
x = 42 x = 42
x 3 x 3
@ -1211,7 +1211,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : Int -> Int f : I64 -> I64
f = \_ -> 42 f = \_ -> 42
f 1 2 f 1 2
@ -1237,7 +1237,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : Int, Int -> Int f : I64, I64 -> I64
f = \_, _ -> 42 f = \_, _ -> 42
f 1 f 1
@ -1372,9 +1372,9 @@ mod test_reporting {
these names seem close though: these names seem close though:
Bool Bool
Int
F64 F64
Num Num
Map
"# "#
), ),
) )
@ -1483,7 +1483,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
{ x } : { x : Int } { x } : { x : I64 }
{ x } = { x: 4.0 } { x } = { x: 4.0 }
x x
@ -1495,7 +1495,7 @@ mod test_reporting {
Something is off with the body of this definition: Something is off with the body of this definition:
1 { x } : { x : Int } 1 { x } : { x : I64 }
2 { x } = { x: 4.0 } 2 { x } = { x: 4.0 }
^^^^^^^^^^ ^^^^^^^^^^
@ -1505,7 +1505,7 @@ mod test_reporting {
But the type annotation says it should be: But the type annotation says it should be:
{ x : Int } { x : I64 }
Tip: You can convert between Int and Float using functions like Tip: You can convert between Int and Float using functions like
`Num.toFloat` and `Num.round`. `Num.toFloat` and `Num.round`.
@ -1644,7 +1644,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
x : { a : Int, b : F64, c : Bool } x : { a : I64, b : F64, c : Bool }
x = { b: 4.0 } x = { b: 4.0 }
x x
@ -1656,7 +1656,7 @@ mod test_reporting {
Something is off with the body of the `x` definition: Something is off with the body of the `x` definition:
1 x : { a : Int, b : F64, c : Bool } 1 x : { a : I64, b : F64, c : Bool }
2 x = { b: 4.0 } 2 x = { b: 4.0 }
^^^^^^^^^^ ^^^^^^^^^^
@ -1666,7 +1666,7 @@ mod test_reporting {
But the type annotation on `x` says it should be: But the type annotation on `x` says it should be:
{ a : Int, b : F64, c : Bool } { a : I64, b : F64, c : Bool }
Tip: Looks like the c and a fields are missing. Tip: Looks like the c and a fields are missing.
"# "#
@ -1788,7 +1788,7 @@ mod test_reporting {
The body is an integer of type: The body is an integer of type:
Int I64
But the type annotation on `f` says it should be: But the type annotation on `f` says it should be:
@ -1796,7 +1796,7 @@ mod test_reporting {
Tip: The type annotation uses the type variable `msg` to say that this Tip: The type annotation uses the type variable `msg` to say that this
definition can produce any type of value. But in the body I see that definition can produce any type of value. But in the body I see that
it will only produce a `Int` value of a single specific type. Maybe it will only produce a `I64` value of a single specific type. Maybe
change the type annotation to be more specific? Maybe change the code change the type annotation to be more specific? Maybe change the code
to be more general? to be more general?
"# "#
@ -1810,7 +1810,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : Bool -> [ Ok Int, InvalidFoo ] f : Bool -> [ Ok I64, InvalidFoo ]
f = \_ -> ok 4 f = \_ -> ok 4
f f
@ -1828,9 +1828,9 @@ mod test_reporting {
these names seem close though: these names seem close though:
f f
Int
F64 F64
Num Num
Map
"# "#
), ),
) )
@ -1842,7 +1842,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : Bool -> Int f : Bool -> I64
f = \_ -> f = \_ ->
ok = 3 ok = 3
@ -1867,7 +1867,7 @@ mod test_reporting {
Something is off with the body of the `f` definition: Something is off with the body of the `f` definition:
1 f : Bool -> Int 1 f : Bool -> I64
2 f = \_ -> 2 f = \_ ->
3 ok = 3 3 ok = 3
4 4
@ -1880,7 +1880,7 @@ mod test_reporting {
But the type annotation on `f` says it should be: But the type annotation on `f` says it should be:
Int I64
"# "#
), ),
) )
@ -2005,7 +2005,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : { fo: Int }ext -> Int f : { fo: I64 }ext -> I64
f = \r -> f = \r ->
r2 = { r & foo: r.fo } r2 = { r & foo: r.fo }
@ -2026,7 +2026,7 @@ mod test_reporting {
This is usually a typo. Here are the `r` fields that are most similar: This is usually a typo. Here are the `r` fields that are most similar:
{ fo : Int { fo : I64
}ext }ext
So maybe `.foo` should be `.fo`? So maybe `.foo` should be `.fo`?
@ -2259,12 +2259,12 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
Either : [ Left Int, Right Bool ] Either : [ Left I64, Right Bool ]
x : Either x : Either
x = Left 42 x = Left 42
f : Either -> Int f : Either -> I64
f = \Left v -> v f = \Left v -> v
f x f x
@ -2296,7 +2296,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
x : [ Left Int, Right Bool ] x : [ Left I64, Right Bool ]
x = Left 42 x = Left 42
@ -2425,7 +2425,7 @@ mod test_reporting {
r#" r#"
RemoteData e a : [ NotAsked, Loading, Failure e, Success a ] RemoteData e a : [ NotAsked, Loading, Failure e, Success a ]
x : RemoteData Int Str x : RemoteData I64 Str
when x is when x is
NotAsked -> 3 NotAsked -> 3
@ -2488,7 +2488,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
y : [ Nothing, Just Int ] y : [ Nothing, Just I64 ]
y = Just 4 y = Just 4
x = { a: y, b: 42} x = { a: y, b: 42}
@ -2581,9 +2581,9 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
Foo : { x : Int } Foo : { x : I64 }
f : Foo -> Int f : Foo -> I64
f = \r -> r.x f = \r -> r.x
f { y: 3.14 } f { y: 3.14 }
@ -2605,7 +2605,7 @@ mod test_reporting {
But `f` needs the 1st argument to be: But `f` needs the 1st argument to be:
{ x : Int } { x : I64 }
Tip: Seems like a record field typo. Maybe `y` should be `x`? Tip: Seems like a record field typo. Maybe `y` should be `x`?
@ -2835,7 +2835,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
a : { foo : Int, bar : F64, foo : Str } a : { foo : I64, bar : F64, foo : Str }
a = { bar: 3.0, foo: "foo" } a = { bar: 3.0, foo: "foo" }
a a
@ -2847,12 +2847,12 @@ mod test_reporting {
This record type defines the `.foo` field twice! This record type defines the `.foo` field twice!
1 a : { foo : Int, bar : F64, foo : Str } 1 a : { foo : I64, bar : F64, foo : Str }
^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^
In the rest of the program, I will only use the latter definition: In the rest of the program, I will only use the latter definition:
1 a : { foo : Int, bar : F64, foo : Str } 1 a : { foo : I64, bar : F64, foo : Str }
^^^^^^^^^ ^^^^^^^^^
For clarity, remove the previous `.foo` definitions from this record For clarity, remove the previous `.foo` definitions from this record
@ -2867,7 +2867,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
a : [ Foo Int, Bar F64, Foo Str ] a : [ Foo I64, Bar F64, Foo Str ]
a = Foo "foo" a = Foo "foo"
a a
@ -2879,12 +2879,12 @@ mod test_reporting {
This tag union type defines the `Foo` tag twice! This tag union type defines the `Foo` tag twice!
1 a : [ Foo Int, Bar F64, Foo Str ] 1 a : [ Foo I64, Bar F64, Foo Str ]
^^^^^^^ ^^^^^^^ ^^^^^^^ ^^^^^^^
In the rest of the program, I will only use the latter definition: In the rest of the program, I will only use the latter definition:
1 a : [ Foo Int, Bar F64, Foo Str ] 1 a : [ Foo I64, Bar F64, Foo Str ]
^^^^^^^ ^^^^^^^
For clarity, remove the previous `Foo` definitions from this tag union For clarity, remove the previous `Foo` definitions from this tag union
@ -2899,7 +2899,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
bar : Int bar : I64
foo = \x -> x foo = \x -> x
# NOTE: neither bar or foo are defined at this point # NOTE: neither bar or foo are defined at this point
@ -2913,7 +2913,7 @@ mod test_reporting {
This annotation does not match the definition immediately following This annotation does not match the definition immediately following
it: it:
1> bar : Int 1> bar : I64
2> foo = \x -> x 2> foo = \x -> x
Is it a typo? If not, put either a newline or comment between them. Is it a typo? If not, put either a newline or comment between them.
@ -2927,7 +2927,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
bar : Int bar : I64
foo = \x -> x foo = \x -> x
@ -2943,7 +2943,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
MyAlias 1 : Int MyAlias 1 : I64
4 4
"# "#
@ -2954,7 +2954,7 @@ mod test_reporting {
This pattern in the definition of `MyAlias` is not what I expect: This pattern in the definition of `MyAlias` is not what I expect:
1 MyAlias 1 : Int 1 MyAlias 1 : I64
^ ^
Only type variables like `a` or `value` can occur in this position. Only type variables like `a` or `value` can occur in this position.
@ -2963,7 +2963,7 @@ mod test_reporting {
`MyAlias` is not used anywhere in your code. `MyAlias` is not used anywhere in your code.
1 MyAlias 1 : Int 1 MyAlias 1 : I64
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
If you didn't intend on using `MyAlias` then remove it so future readers If you didn't intend on using `MyAlias` then remove it so future readers
@ -2978,7 +2978,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
a : Num Int F64 a : Num I64 F64
a = 3 a = 3
a a
@ -2990,7 +2990,7 @@ mod test_reporting {
The `Num` alias expects 1 type argument, but it got 2 instead: The `Num` alias expects 1 type argument, but it got 2 instead:
1 a : Num Int F64 1 a : Num I64 F64
^^^^^^^^^^^ ^^^^^^^^^^^
Are there missing parentheses? Are there missing parentheses?
@ -3004,7 +3004,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : Bool -> Num Int F64 f : Bool -> Num I64 F64
f = \_ -> 3 f = \_ -> 3
f f
@ -3016,7 +3016,7 @@ mod test_reporting {
The `Num` alias expects 1 type argument, but it got 2 instead: The `Num` alias expects 1 type argument, but it got 2 instead:
1 f : Bool -> Num Int F64 1 f : Bool -> Num I64 F64
^^^^^^^^^^^ ^^^^^^^^^^^
Are there missing parentheses? Are there missing parentheses?
@ -3032,7 +3032,7 @@ mod test_reporting {
r#" r#"
Pair a b : [ Pair a b ] Pair a b : [ Pair a b ]
x : Pair Int x : Pair I64
x = Pair 2 3 x = Pair 2 3
x x
@ -3044,7 +3044,7 @@ mod test_reporting {
The `Pair` alias expects 2 type arguments, but it got 1 instead: The `Pair` alias expects 2 type arguments, but it got 1 instead:
3 x : Pair Int 3 x : Pair I64
^^^^^^^^ ^^^^^^^^
Are there missing parentheses? Are there missing parentheses?
@ -3060,7 +3060,7 @@ mod test_reporting {
r#" r#"
Pair a b : [ Pair a b ] Pair a b : [ Pair a b ]
x : Pair Int Int Int x : Pair I64 I64 I64
x = 3 x = 3
x x
@ -3072,7 +3072,7 @@ mod test_reporting {
The `Pair` alias expects 2 type arguments, but it got 3 instead: The `Pair` alias expects 2 type arguments, but it got 3 instead:
3 x : Pair Int Int Int 3 x : Pair I64 I64 I64
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
Are there missing parentheses? Are there missing parentheses?
@ -3088,7 +3088,7 @@ mod test_reporting {
r#" r#"
Foo a : [ Foo ] Foo a : [ Foo ]
f : Foo Int f : Foo I64
f f
"# "#
@ -3176,7 +3176,7 @@ mod test_reporting {
AList a b : [ ACons a (BList a b), ANil ] AList a b : [ ACons a (BList a b), ANil ]
BList a b : [ BCons a (AList a b), BNil ] BList a b : [ BCons a (AList a b), BNil ]
x : AList Int Int x : AList I64 I64
x = ACons 0 (BCons 1 (ACons "foo" BNil )) x = ACons 0 (BCons 1 (ACons "foo" BNil ))
y : BList a a y : BList a a
@ -3191,19 +3191,19 @@ mod test_reporting {
Something is off with the body of the `x` definition: Something is off with the body of the `x` definition:
4 x : AList Int Int 4 x : AList I64 I64
5 x = ACons 0 (BCons 1 (ACons "foo" BNil )) 5 x = ACons 0 (BCons 1 (ACons "foo" BNil ))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This `ACons` global tag application has the type: This `ACons` global tag application has the type:
[ ACons (Num Integer) [ BCons (Num Integer) [ ACons Str [ [ ACons (Num Integer) [ BCons (Num Integer) [ ACons Str [
BCons Int [ ACons Int (BList Int Int), ANil ] as a, BNil ], ANil BCons I64 [ ACons I64 (BList I64 I64), ANil ] as a, BNil ], ANil
], BNil ], ANil ] ], BNil ], ANil ]
But the type annotation on `x` says it should be: But the type annotation on `x` says it should be:
[ ACons Int (BList Int Int), ANil ] as a [ ACons I64 (BList I64 I64), ANil ] as a
"# "#
), ),
) )
@ -3575,7 +3575,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : { x : Int, y ? Int } -> Int f : { x : I64, y ? I64 } -> I64
f = \{ x, y ? "foo" } -> (\g, _ -> g) x y f = \{ x, y ? "foo" } -> (\g, _ -> g) x y
f f
@ -3592,11 +3592,11 @@ mod test_reporting {
The argument is a pattern that matches record values of type: The argument is a pattern that matches record values of type:
{ x : Int, y ? Str } { x : I64, y ? Str }
But the annotation on `f` says the 1st argument should be: But the annotation on `f` says the 1st argument should be:
{ x : Int, y ? Int } { x : I64, y ? I64 }
"# "#
), ),
) )
@ -3608,7 +3608,7 @@ mod test_reporting {
indoc!( indoc!(
r#" r#"
\rec -> \rec ->
{ x, y } : { x : Int, y ? Bool } { x, y } : { x : I64, y ? Bool }
{ x, y } = rec { x, y } = rec
{ x, y } { x, y }
@ -3620,16 +3620,16 @@ mod test_reporting {
Something is off with the body of this definition: Something is off with the body of this definition:
2> { x, y } : { x : Int, y ? Bool } 2> { x, y } : { x : I64, y ? Bool }
3> { x, y } = rec 3> { x, y } = rec
The body is a value of type: The body is a value of type:
{ x : Int, y : Bool } { x : I64, y : Bool }
But the type annotation says it should be: But the type annotation says it should be:
{ x : Int, y ? Bool } { x : I64, y ? Bool }
Tip: To extract the `.y` field it must be non-optional, but the type Tip: To extract the `.y` field it must be non-optional, but the type
says this field is optional. Learn more about optional fields at TODO. says this field is optional. Learn more about optional fields at TODO.
@ -3643,7 +3643,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : { x : Int, y ? Int } -> Int f : { x : I64, y ? I64 } -> I64
f = \{ x, y } -> x + y f = \{ x, y } -> x + y
f f
@ -3660,11 +3660,11 @@ mod test_reporting {
The argument is a pattern that matches record values of type: The argument is a pattern that matches record values of type:
{ x : Int, y : Int } { x : I64, y : I64 }
But the annotation on `f` says the 1st argument should be: But the annotation on `f` says the 1st argument should be:
{ x : Int, y ? Int } { x : I64, y ? I64 }
Tip: To extract the `.y` field it must be non-optional, but the type Tip: To extract the `.y` field it must be non-optional, but the type
says this field is optional. Learn more about optional fields at TODO. says this field is optional. Learn more about optional fields at TODO.
@ -3678,7 +3678,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : { x : Int, y ? Int } -> Int f : { x : I64, y ? I64 } -> I64
f = \r -> f = \r ->
when r is when r is
{ x, y } -> x + y { x, y } -> x + y
@ -3697,11 +3697,11 @@ mod test_reporting {
The first pattern is trying to match record values of type: The first pattern is trying to match record values of type:
{ x : Int, y : Int } { x : I64, y : I64 }
But the expression between `when` and `is` has the type: But the expression between `when` and `is` has the type:
{ x : Int, y ? Int } { x : I64, y ? I64 }
Tip: To extract the `.y` field it must be non-optional, but the type Tip: To extract the `.y` field it must be non-optional, but the type
says this field is optional. Learn more about optional fields at TODO. says this field is optional. Learn more about optional fields at TODO.
@ -3715,7 +3715,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : { x : Int, y ? Int } -> Int f : { x : I64, y ? I64 } -> I64
f = \r -> r.y f = \r -> r.y
f f
@ -3732,11 +3732,11 @@ mod test_reporting {
This `r` value is a: This `r` value is a:
{ x : Int, y ? Int } { x : I64, y ? I64 }
But you are trying to use it as: But you are trying to use it as:
{ x : Int, y : Int } { x : I64, y : I64 }
Tip: To extract the `.y` field it must be non-optional, but the type Tip: To extract the `.y` field it must be non-optional, but the type
says this field is optional. Learn more about optional fields at TODO. says this field is optional. Learn more about optional fields at TODO.
@ -3750,7 +3750,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : { x : Int, y ? Int } -> Int f : { x : I64, y ? I64 } -> I64
f = \r -> .y r f = \r -> .y r
f f
@ -3767,11 +3767,11 @@ mod test_reporting {
This `r` value is a: This `r` value is a:
{ x : Int, y ? Int } { x : I64, y ? I64 }
But this function needs the 1st argument to be: But this function needs the 1st argument to be:
{ x : Int, y : Int } { x : I64, y : I64 }
Tip: To extract the `.y` field it must be non-optional, but the type Tip: To extract the `.y` field it must be non-optional, but the type
says this field is optional. Learn more about optional fields at TODO. says this field is optional. Learn more about optional fields at TODO.
@ -3785,7 +3785,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : { x : Int, y : Int } -> Int f : { x : I64, y : I64 } -> I64
f = \r -> f = \r ->
when r is when r is
{ x, y : "foo" } -> x + 0 { x, y : "foo" } -> x + 0
@ -3805,11 +3805,11 @@ mod test_reporting {
The first pattern is trying to match record values of type: The first pattern is trying to match record values of type:
{ x : Int, y : Str } { x : I64, y : Str }
But the expression between `when` and `is` has the type: But the expression between `when` and `is` has the type:
{ x : Int, y : Int } { x : I64, y : I64 }
"# "#
), ),
) )
@ -3820,7 +3820,7 @@ mod test_reporting {
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
f : { x : Int, y ? Int } -> Int f : { x : I64, y ? I64 } -> I64
f = \r -> f = \r ->
when r is when r is
{ x, y ? "foo" } -> (\g, _ -> g) x y { x, y ? "foo" } -> (\g, _ -> g) x y
@ -3840,11 +3840,11 @@ mod test_reporting {
The first pattern is trying to match record values of type: The first pattern is trying to match record values of type:
{ x : Int, y ? Str } { x : I64, y ? Str }
But the expression between `when` and `is` has the type: But the expression between `when` and `is` has the type:
{ x : Int, y ? Int } { x : I64, y ? I64 }
"# "#
), ),
) )

View file

@ -1326,12 +1326,12 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
int : Int int : I64
int int
"# "#
), ),
"Int", "I64",
); );
} }
@ -1345,7 +1345,7 @@ mod solve_expr {
int int
"# "#
), ),
"Int", "I64",
); );
} }
@ -1354,13 +1354,13 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
int : Int int : I64
int = 5 int = 5
int int
"# "#
), ),
"Int", "I64",
); );
} }
@ -1369,13 +1369,13 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
int : Num.Int int : Num.I64
int = 5 int = 5
int int
"# "#
), ),
"Int", "I64",
); );
} }
@ -1390,7 +1390,7 @@ mod solve_expr {
int int
"# "#
), ),
"Int", "I64",
); );
} }
@ -1405,7 +1405,7 @@ mod solve_expr {
int int
"# "#
), ),
"Int", "I64",
); );
} }
@ -1489,13 +1489,13 @@ mod solve_expr {
r#" r#"
Res a e : [ Okay a, Error e ] Res a e : [ Okay a, Error e ]
ok : Res Int * ok : Res I64 *
ok = Okay 5 ok = Okay 5
ok ok
"# "#
), ),
"Res Int *", "Res I64 *",
); );
} }
@ -1521,13 +1521,13 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
ok : Result Int * ok : Result I64 *
ok = Ok 5 ok = Ok 5
ok ok
"# "#
), ),
"Result Int *", "Result I64 *",
); );
} }
@ -1551,7 +1551,7 @@ mod solve_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
ok : Result Int * ok : Result I64 *
ok = Ok 5 ok = Ok 5
err : Result * Str err : Result * Str
@ -1563,7 +1563,7 @@ mod solve_expr {
err err
"# "#
), ),
"Result Int Str", "Result I64 Str",
); );
} }
@ -1584,19 +1584,19 @@ mod solve_expr {
// #[test] // #[test]
// fn annotation_using_num_used() { // fn annotation_using_num_used() {
// // There was a problem where `Int`, because it is only an annotation // // There was a problem where `I64`, because it is only an annotation
// // wasn't added to the vars_by_symbol. // // wasn't added to the vars_by_symbol.
// infer_eq_without_problem( // infer_eq_without_problem(
// indoc!( // indoc!(
// r#" // r#"
// int : Int // int : I64
// p = (\x -> x) int // p = (\x -> x) int
// p // p
// "# // "#
// ), // ),
// "Int", // "I64",
// ); // );
// } // }
@ -1631,7 +1631,7 @@ mod solve_expr {
x x
"# "#
), ),
"Int", "I64",
); );
} }
@ -1840,7 +1840,7 @@ mod solve_expr {
v v
"# "#
), ),
"Foo Int", "Foo I64",
); );
} }
@ -1907,7 +1907,7 @@ mod solve_expr {
length length
"# "#
), ),
"Peano -> Int", "Peano -> I64",
); );
} }
@ -2253,13 +2253,13 @@ mod solve_expr {
// infer_eq_without_problem( // infer_eq_without_problem(
// indoc!( // indoc!(
// r#" // r#"
// UserId x : [ UserId Int ] // UserId x : [ UserId I64 ]
// UserId x = UserId 42 // UserId x = UserId 42
// x // x
// "# // "#
// ), // ),
// "Int", // "I64",
// ); // );
// } // }
@ -2340,13 +2340,13 @@ mod solve_expr {
ListB a : [ Cons a (ListC a) ] ListB a : [ Cons a (ListC a) ]
ListC a : [ Cons a (ListA a), Nil ] ListC a : [ Cons a (ListA a), Nil ]
val : ListC Num.Int val : ListC Num.I64
val = Cons 1 (Cons 2 (Cons 3 Nil)) val = Cons 1 (Cons 2 (Cons 3 Nil))
val val
"# "#
), ),
"ListC Int", "ListC I64",
); );
} }
@ -2388,7 +2388,7 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
partition : Int, Int, List Int -> [ Pair Int (List Int) ] partition : I64, I64, List I64 -> [ Pair I64 (List I64) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok _ -> Ok _ ->
@ -2400,7 +2400,7 @@ mod solve_expr {
partition partition
"# "#
), ),
"Int, Int, List Int -> [ Pair Int (List Int) ]", "I64, I64, List I64 -> [ Pair I64 (List I64) ]",
); );
} }
@ -2410,7 +2410,7 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -2421,7 +2421,7 @@ mod solve_expr {
_ -> _ ->
list list
partition : Int, Int, List Int -> [ Pair Int (List Int) ] partition : I64, I64, List I64 -> [ Pair I64 (List I64) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -2449,7 +2449,7 @@ mod solve_expr {
partition partition
"# "#
), ),
"Int, Int, List Int -> [ Pair Int (List Int) ]", "I64, I64, List I64 -> [ Pair I64 (List I64) ]",
); );
}); });
} }
@ -2462,14 +2462,14 @@ mod solve_expr {
idList : List a -> List a idList : List a -> List a
idList = \list -> list idList = \list -> list
foo : List Int -> List Int foo : List I64 -> List I64
foo = \initialList -> idList initialList foo = \initialList -> idList initialList
foo foo
"# "#
), ),
"List Int -> List Int", "List I64 -> List I64",
); );
} }
@ -2547,7 +2547,7 @@ mod solve_expr {
Num.ceiling Num.ceiling
"# "#
), ),
"F64 -> Int", "F64 -> I64",
); );
} }
@ -2559,7 +2559,7 @@ mod solve_expr {
Num.floor Num.floor
"# "#
), ),
"F64 -> Int", "F64 -> I64",
); );
} }
@ -2571,7 +2571,7 @@ mod solve_expr {
Num.powInt Num.powInt
"# "#
), ),
"Int, Int -> Int", "I64, I64 -> I64",
); );
} }
@ -2789,15 +2789,15 @@ mod solve_expr {
#[should_panic] #[should_panic]
fn rigid_record_quantification() { fn rigid_record_quantification() {
// the ext here is qualified on the outside (because we have rank 1 types, not rank 2). // the ext here is qualified on the outside (because we have rank 1 types, not rank 2).
// That means e.g. `f : { bar : String, foo : Int } -> Bool }` is a valid argument, but // That means e.g. `f : { bar : String, foo : I64 } -> Bool }` is a valid argument, but
// that function could not be applied to the `{ foo : Int }` list. Therefore, this function // that function could not be applied to the `{ foo : I64 }` list. Therefore, this function
// is not allowed. // is not allowed.
// //
// should hit a debug_assert! in debug mode, and produce a type error in release mode // should hit a debug_assert! in debug mode, and produce a type error in release mode
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
test : ({ foo : Int }ext -> Bool), { foo : Int } -> Bool test : ({ foo : I64 }ext -> Bool), { foo : I64 } -> Bool
test = \fn, a -> fn a test = \fn, a -> fn a
test test
@ -2814,12 +2814,12 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
negatePoint : { x : Int, y : Int, z ? Num c } -> { x : Int, y : Int, z : Num c } negatePoint : { x : I64, y : I64, z ? Num c } -> { x : I64, y : I64, z : Num c }
negatePoint { x: 1, y: 2 } negatePoint { x: 1, y: 2 }
"# "#
), ),
"{ x : Int, y : Int, z : Num c }", "{ x : I64, y : I64, z : Num c }",
); );
} }
@ -2828,7 +2828,7 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
negatePoint : { x : Int, y : Int, z ? Num c }r -> { x : Int, y : Int, z : Num c }r negatePoint : { x : I64, y : I64, z ? Num c }r -> { x : I64, y : I64, z : Num c }r
a = negatePoint { x: 1, y: 2 } a = negatePoint { x: 1, y: 2 }
b = negatePoint { x: 1, y: 2, blah : "hi" } b = negatePoint { x: 1, y: 2, blah : "hi" }
@ -2836,7 +2836,7 @@ mod solve_expr {
{ a, b } { a, b }
"# "#
), ),
"{ a : { x : Int, y : Int, z : Num c }, b : { blah : Str, x : Int, y : Int, z : Num c } }", "{ a : { x : I64, y : I64, z : Num c }, b : { blah : Str, x : I64, y : I64, z : Num c } }",
); );
} }
@ -2850,7 +2850,7 @@ mod solve_expr {
negatePoint { x: 1, y: 2.1, z: 0x3 } negatePoint { x: 1, y: 2.1, z: 0x3 }
"# "#
), ),
"{ x : Num a, y : F64, z : Int }", "{ x : Num a, y : F64, z : I64 }",
); );
} }
@ -2917,13 +2917,13 @@ mod solve_expr {
indoc!( indoc!(
r#" r#"
\rec -> \rec ->
{ x, y } : { x : Int, y ? Bool }* { x, y } : { x : I64, y ? Bool }*
{ x, y ? False } = rec { x, y ? False } = rec
{ x, y } { x, y }
"# "#
), ),
"{ x : Int, y ? Bool }* -> { x : Int, y : Bool }", "{ x : I64, y ? Bool }* -> { x : I64, y : Bool }",
); );
} }
@ -2944,14 +2944,14 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
empty : List Int empty : List I64
empty = empty =
[] []
List.walkBackwards empty (\a, b -> a + b) 0 List.walkBackwards empty (\a, b -> a + b) 0
"# "#
), ),
"Int", "I64",
); );
} }
@ -2970,7 +2970,7 @@ mod solve_expr {
g g
"# "#
), ),
"Int", "I64",
); );
} }
@ -3023,7 +3023,7 @@ mod solve_expr {
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
Bar : [ Bar ] Bar : [ Bar ]
Foo : [ Foo Bar Int, Empty ] Foo : [ Foo Bar I64, Empty ]
foo : Foo foo : Foo
foo = Foo Bar 1 foo = Foo Bar 1
@ -3037,7 +3037,7 @@ mod solve_expr {
x x
"# "#
), ),
"[ Empty, Foo [ Bar ] Int ]", "[ Empty, Foo [ Bar ] I64 ]",
); );
} }
@ -3048,7 +3048,7 @@ mod solve_expr {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
Foo : [ @Foo [ @Bar ] Int, @Empty ] Foo : [ @Foo [ @Bar ] I64, @Empty ]
foo : Foo foo : Foo
foo = @Foo @Bar 1 foo = @Foo @Bar 1
@ -3062,7 +3062,7 @@ mod solve_expr {
x x
"# "#
), ),
"[ @Empty, @Foo [ @Bar ] Int ]", "[ @Empty, @Foo [ @Bar ] I64 ]",
); );
} }
@ -3073,21 +3073,21 @@ mod solve_expr {
r#" r#"
app "test" provides [ main ] to "./platform" app "test" provides [ main ] to "./platform"
State a : { count : Int, x : a } State a : { count : I64, x : a }
foo : State a -> Int foo : State a -> I64
foo = \state -> foo = \state ->
if state.count == 0 then if state.count == 0 then
0 0
else else
1 + foo { count: state.count - 1, x: state.x } 1 + foo { count: state.count - 1, x: state.x }
main : Int main : I64
main = main =
foo { count: 3, x: {} } foo { count: 3, x: {} }
"# "#
), ),
"Int", "I64",
); );
} }
@ -3108,15 +3108,15 @@ mod solve_expr {
empty = empty =
Empty Empty
foo : Dict Int Int foo : Dict I64 I64
foo = empty foo = empty
main : Dict Int Int main : Dict I64 I64
main = main =
foo foo
"# "#
), ),
"Dict Int Int", "Dict I64 I64",
); );
} }
@ -3370,12 +3370,12 @@ mod solve_expr {
removeHelpEQGT targetKey (removeHelpPrepEQGT targetKey dict color key value left right) removeHelpEQGT targetKey (removeHelpPrepEQGT targetKey dict color key value left right)
main : Dict Int Int main : Dict I64 I64
main = main =
removeHelp 1 Empty removeHelp 1 Empty
"# "#
), ),
"Dict Int Int", "Dict I64 I64",
); );
} }
@ -3410,12 +3410,12 @@ mod solve_expr {
Empty Empty
main : Dict Int main : Dict I64
main = main =
removeHelp 1 Empty removeHelp 1 Empty
"# "#
), ),
"Dict Int", "Dict I64",
); );
} }
@ -3486,12 +3486,12 @@ mod solve_expr {
removeMin : Dict k v -> Dict k v removeMin : Dict k v -> Dict k v
main : Dict Int Int main : Dict I64 I64
main = main =
removeHelp 1 Empty removeHelp 1 Empty
"# "#
), ),
"Dict Int Int", "Dict I64 I64",
); );
} }
@ -3502,7 +3502,7 @@ mod solve_expr {
r#" r#"
app "test" provides [ partitionHelp ] to "./platform" app "test" provides [ partitionHelp ] to "./platform"
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -3513,7 +3513,7 @@ mod solve_expr {
_ -> _ ->
[] []
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is
@ -3529,7 +3529,7 @@ mod solve_expr {
Pair i list Pair i list
"# "#
), ),
"Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ]", "I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]",
); );
} }
@ -3546,12 +3546,12 @@ mod solve_expr {
balance = \key, left -> balance = \key, left ->
Node key left Empty Node key left Empty
main : Dict Int main : Dict I64
main = main =
balance 0 Empty balance 0 Empty
"# "#
), ),
"Dict Int", "Dict I64",
); );
} }
@ -3570,12 +3570,12 @@ mod solve_expr {
balance = \key, left -> balance = \key, left ->
node key left Empty node key left Empty
main : Dict Int main : Dict I64
main = main =
balance 0 Empty balance 0 Empty
"# "#
), ),
"Dict Int", "Dict I64",
); );
} }
@ -3619,12 +3619,12 @@ mod solve_expr {
_ -> _ ->
Node color key value left right Node color key value left right
main : Dict Int Int main : Dict I64 I64
main = main =
balance Red 0 0 Empty Empty balance Red 0 0 Empty Empty
"# "#
), ),
"Dict Int Int", "Dict I64 I64",
); );
} }
@ -3648,12 +3648,12 @@ mod solve_expr {
Empty Empty
main : Dict Int main : Dict I64
main = main =
balance 0 Empty balance 0 Empty
"# "#
), ),
"Dict Int", "Dict I64",
); );
} }
} }

View file

@ -1124,7 +1124,7 @@ mod solve_uniq_expr {
x x
"# "#
), ),
"Attr * Int", "Attr * I64",
); );
} }
@ -1377,7 +1377,7 @@ mod solve_uniq_expr {
x x
"# "#
), ),
"Attr * Int", "Attr * I64",
); );
} }
@ -1405,7 +1405,7 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -1418,7 +1418,7 @@ mod solve_uniq_expr {
swap swap
"# "#
), ),
"Attr * (Attr * Int, Attr * Int, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))" "Attr * (Attr * I64, Attr * I64, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))"
); );
} }
@ -1431,7 +1431,7 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
quicksort : List (Num a), Int, Int -> List (Num a) quicksort : List (Num a), I64, I64 -> List (Num a)
quicksort = \list, low, high -> quicksort = \list, low, high ->
when partition low high list is when partition low high list is
Pair partitionIndex partitioned -> Pair partitionIndex partitioned ->
@ -1440,7 +1440,7 @@ mod solve_uniq_expr {
|> quicksort (partitionIndex + 1) high |> quicksort (partitionIndex + 1) high
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -1452,7 +1452,7 @@ mod solve_uniq_expr {
[] []
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -1480,7 +1480,7 @@ mod solve_uniq_expr {
quicksort quicksort
"# "#
), ),
"Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared Int, Attr Shared Int -> Attr b (List (Attr Shared (Num (Attr Shared a)))))" "Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared I64 -> Attr b (List (Attr Shared (Num (Attr Shared a)))))"
); );
}) })
} }
@ -2149,7 +2149,7 @@ mod solve_uniq_expr {
Num.maxInt // Num.maxInt Num.maxInt // Num.maxInt
"# "#
), ),
"Attr * (Result (Attr * Int) (Attr * [ DivByZero ]*))", "Attr * (Result (Attr * I64) (Attr * [ DivByZero ]*))",
); );
} }
@ -2161,7 +2161,7 @@ mod solve_uniq_expr {
3 // 4 3 // 4
"# "#
), ),
"Attr * (Result (Attr * Int) (Attr * [ DivByZero ]*))", "Attr * (Result (Attr * I64) (Attr * [ DivByZero ]*))",
); );
} }
@ -2173,7 +2173,7 @@ mod solve_uniq_expr {
3 // Num.maxInt 3 // Num.maxInt
"# "#
), ),
"Attr * (Result (Attr * Int) (Attr * [ DivByZero ]*))", "Attr * (Result (Attr * I64) (Attr * [ DivByZero ]*))",
); );
} }
@ -2264,17 +2264,17 @@ mod solve_uniq_expr {
#[test] #[test]
fn list_len() { fn list_len() {
infer_eq("List.len", "Attr * (Attr * (List *) -> Attr * Int)"); infer_eq("List.len", "Attr * (Attr * (List *) -> Attr * I64)");
} }
#[test] #[test]
fn list_get() { fn list_get() {
infer_eq("List.get", "Attr * (Attr (* | b) (List (Attr b a)), Attr * Int -> Attr * (Result (Attr b a) (Attr * [ OutOfBounds ]*)))"); infer_eq("List.get", "Attr * (Attr (* | b) (List (Attr b a)), Attr * I64 -> Attr * (Result (Attr b a) (Attr * [ OutOfBounds ]*)))");
} }
#[test] #[test]
fn list_set() { fn list_set() {
infer_eq("List.set", "Attr * (Attr (* | b | c) (List (Attr b a)), Attr * Int, Attr (b | c) a -> Attr * (List (Attr b a)))"); infer_eq("List.set", "Attr * (Attr (* | b | c) (List (Attr b a)), Attr * I64, Attr (b | c) a -> Attr * (List (Attr b a)))");
} }
#[test] #[test]
@ -2286,7 +2286,7 @@ mod solve_uniq_expr {
fn list_repeat() { fn list_repeat() {
infer_eq( infer_eq(
"List.repeat", "List.repeat",
"Attr * (Attr * Int, Attr Shared a -> Attr * (List (Attr Shared a)))", "Attr * (Attr * I64, Attr Shared a -> Attr * (List (Attr Shared a)))",
); );
} }
@ -2487,15 +2487,15 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
Model : { foo : Int } Model : { foo : I64 }
extract : Model -> Int extract : Model -> I64
extract = \{ foo } -> foo extract = \{ foo } -> foo
extract extract
"# "#
), ),
"Attr * (Attr (* | a) Model -> Attr a Int)", "Attr * (Attr (* | a) Model -> Attr a I64)",
); );
} }
@ -2504,15 +2504,15 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
Model : { foo : Int, bar : Int } Model : { foo : I64, bar : I64 }
extract : Model -> Int extract : Model -> I64
extract = \{ foo } -> foo extract = \{ foo } -> foo
extract extract
"# "#
), ),
"Attr * (Attr (* | a) Model -> Attr a Int)", "Attr * (Attr (* | a) Model -> Attr a I64)",
); );
} }
@ -2521,15 +2521,15 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
Model : { foo : Int, bar : Int } Model : { foo : I64, bar : I64 }
extract : Model -> Int extract : Model -> I64
extract = \{foo, bar} -> foo + bar extract = \{foo, bar} -> foo + bar
extract extract
"# "#
), ),
"Attr * (Attr (* | * | *) Model -> Attr * Int)", "Attr * (Attr (* | * | *) Model -> Attr * I64)",
); );
} }
@ -2627,13 +2627,13 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
f : Int, Int -> Int f : I64, I64 -> I64
f = \a, b -> a + b f = \a, b -> a + b
f f
"# "#
), ),
"Attr * (Attr * Int, Attr * Int -> Attr * Int)", "Attr * (Attr * I64, Attr * I64 -> Attr * I64)",
); );
} }
@ -2642,13 +2642,13 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
foobar : Int -> Int foobar : I64 -> I64
foobar = \x -> Num.abs x foobar = \x -> Num.abs x
foobar foobar
"# "#
), ),
"Attr * (Attr * Int -> Attr * Int)", "Attr * (Attr * I64 -> Attr * I64)",
); );
} }
@ -2662,7 +2662,7 @@ mod solve_uniq_expr {
f f
"# "#
), ),
"Attr * (Attr a Int, Attr b Int -> Attr c Int)", "Attr * (Attr a I64, Attr b I64 -> Attr c I64)",
); );
} }
@ -3001,13 +3001,13 @@ mod solve_uniq_expr {
Model a : { foo : Set a } Model a : { foo : Set a }
initialModel : position -> Model Int initialModel : position -> Model I64
initialModel = \_ -> { foo : Set.empty } initialModel = \_ -> { foo : Set.empty }
initialModel initialModel
"# "#
), ),
"Attr * (Attr * position -> Attr * (Model (Attr * Int)))", "Attr * (Attr * position -> Attr * (Model (Attr * I64)))",
); );
} }
@ -3035,12 +3035,12 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
negatePoint : { x : Int, y : Int, z ? Num c } -> { x : Int, y : Int, z : Num c } negatePoint : { x : I64, y : I64, z ? Num c } -> { x : I64, y : I64, z : Num c }
negatePoint { x: 1, y: 2 } negatePoint { x: 1, y: 2 }
"# "#
), ),
"Attr * { x : Attr * Int, y : Attr * Int, z : Attr * (Num (Attr * c)) }", "Attr * { x : Attr * I64, y : Attr * I64, z : Attr * (Num (Attr * c)) }",
); );
} }
@ -3049,7 +3049,7 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
negatePoint : { x : Int, y : Int, z ? Num c }r -> { x : Int, y : Int, z : Num c }r negatePoint : { x : I64, y : I64, z ? Num c }r -> { x : I64, y : I64, z : Num c }r
a = negatePoint { x: 1, y: 2 } a = negatePoint { x: 1, y: 2 }
b = negatePoint { x: 1, y: 2, blah : "hi" } b = negatePoint { x: 1, y: 2, blah : "hi" }
@ -3057,7 +3057,7 @@ mod solve_uniq_expr {
{ a, b } { a, b }
"# "#
), ),
"Attr * { a : Attr * { x : Attr * Int, y : Attr * Int, z : Attr * (Num (Attr * c)) }, b : Attr * { blah : Attr * Str, x : Attr * Int, y : Attr * Int, z : Attr * (Num (Attr * c)) } }" "Attr * { a : Attr * { x : Attr * I64, y : Attr * I64, z : Attr * (Num (Attr * c)) }, b : Attr * { blah : Attr * Str, x : Attr * I64, y : Attr * I64, z : Attr * (Num (Attr * c)) } }"
); );
} }
@ -3071,7 +3071,7 @@ mod solve_uniq_expr {
negatePoint { x: 1, y: 2.1, z: 0x3 } negatePoint { x: 1, y: 2.1, z: 0x3 }
"# "#
), ),
"Attr * { x : Attr * (Num (Attr * a)), y : Attr * F64, z : Attr * Int }", "Attr * { x : Attr * (Num (Attr * a)), y : Attr * F64, z : Attr * I64 }",
); );
} }
@ -3149,14 +3149,14 @@ mod solve_uniq_expr {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
empty : List Int empty : List I64
empty = empty =
[] []
List.walkBackwards empty (\a, b -> a + b) 0 List.walkBackwards empty (\a, b -> a + b) 0
"# "#
), ),
"Attr a Int", "Attr a I64",
); );
} }
} }

View file

@ -50,7 +50,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
// Int : Num Integer // Int : Num Integer
add_alias( add_alias(
Symbol::NUM_INT, Symbol::NUM_I64,
BuiltinAlias { BuiltinAlias {
region: Region::zero(), region: Region::zero(),
vars: Vec::new(), vars: Vec::new(),
@ -155,7 +155,7 @@ fn float_alias_content() -> SolvedType {
#[inline(always)] #[inline(always)]
pub fn int_type() -> SolvedType { pub fn int_type() -> SolvedType {
SolvedType::Alias(Symbol::NUM_INT, Vec::new(), Box::new(int_alias_content())) SolvedType::Alias(Symbol::NUM_I64, Vec::new(), Box::new(int_alias_content()))
} }
#[inline(always)] #[inline(always)]

View file

@ -19,8 +19,8 @@ static EMPTY_TAG_UNION: &str = "[]";
/// ///
/// Separately, if we're inside a type parameter, we may need to use parens: /// Separately, if we're inside a type parameter, we may need to use parens:
/// ///
/// List Int /// List I64
/// List (List Int) /// List (List I64)
/// ///
/// Otherwise, parens are unnecessary. /// Otherwise, parens are unnecessary.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -330,7 +330,7 @@ fn write_content(env: &Env, content: Content, subs: &Subs, buf: &mut String, par
match &content { match &content {
Alias(nested, _, _) => match *nested { Alias(nested, _, _) => match *nested {
Symbol::NUM_INTEGER => buf.push_str("Int"), Symbol::NUM_INTEGER => buf.push_str("I64"),
Symbol::NUM_FLOATINGPOINT => buf.push_str("F64"), Symbol::NUM_FLOATINGPOINT => buf.push_str("F64"),
_ => write_parens!(write_parens, buf, { _ => write_parens!(write_parens, buf, {
@ -343,7 +343,7 @@ fn write_content(env: &Env, content: Content, subs: &Subs, buf: &mut String, par
let attr_content = subs.get_without_compacting(nested_args[1]).content; let attr_content = subs.get_without_compacting(nested_args[1]).content;
match &attr_content { match &attr_content {
Alias(nested, _, _) => match *nested { Alias(nested, _, _) => match *nested {
Symbol::NUM_INTEGER => buf.push_str("Int"), Symbol::NUM_INTEGER => buf.push_str("I64"),
Symbol::NUM_FLOATINGPOINT => buf.push_str("F64"), Symbol::NUM_FLOATINGPOINT => buf.push_str("F64"),
_ => write_parens!(write_parens, buf, { _ => write_parens!(write_parens, buf, {
buf.push_str("Num "); buf.push_str("Num ");
@ -403,7 +403,7 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
Record(fields, ext_var) => { Record(fields, ext_var) => {
use crate::types::{gather_fields, RecordStructure}; use crate::types::{gather_fields, RecordStructure};
// If the `ext` has concrete fields (e.g. { foo : Int}{ bar : Bool }), merge them // If the `ext` has concrete fields (e.g. { foo : I64}{ bar : Bool }), merge them
let RecordStructure { fields, ext } = gather_fields(subs, fields, ext_var); let RecordStructure { fields, ext } = gather_fields(subs, fields, ext_var);
let ext_var = ext; let ext_var = ext;
@ -465,8 +465,8 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
// This is an open record, so print the variable // This is an open record, so print the variable
// right after the '}' // right after the '}'
// //
// e.g. the "*" at the end of `{ x: Int }*` // e.g. the "*" at the end of `{ x: I64 }*`
// or the "r" at the end of `{ x: Int }r` // or the "r" at the end of `{ x: I64 }r`
write_content(env, content, subs, buf, parens) write_content(env, content, subs, buf, parens)
} }
} }
@ -523,8 +523,8 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
// This is an open tag union, so print the variable // This is an open tag union, so print the variable
// right after the ']' // right after the ']'
// //
// e.g. the "*" at the end of `{ x: Int }*` // e.g. the "*" at the end of `{ x: I64 }*`
// or the "r" at the end of `{ x: Int }r` // or the "r" at the end of `{ x: I64 }r`
write_content(env, content, subs, buf, parens) write_content(env, content, subs, buf, parens)
} }
} }
@ -576,8 +576,8 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
// This is an open tag union, so print the variable // This is an open tag union, so print the variable
// right after the ']' // right after the ']'
// //
// e.g. the "*" at the end of `{ x: Int }*` // e.g. the "*" at the end of `{ x: I64 }*`
// or the "r" at the end of `{ x: Int }r` // or the "r" at the end of `{ x: I64 }r`
write_content(env, content, subs, buf, parens) write_content(env, content, subs, buf, parens)
} }
@ -754,7 +754,7 @@ fn write_apply(
match &arg_content { match &arg_content {
Content::Structure(FlatType::Apply(symbol, nested_args)) => match *symbol { Content::Structure(FlatType::Apply(symbol, nested_args)) => match *symbol {
Symbol::NUM_INTEGER if nested_args.is_empty() => { Symbol::NUM_INTEGER if nested_args.is_empty() => {
buf.push_str("Int"); buf.push_str("I64");
} }
Symbol::NUM_FLOATINGPOINT if nested_args.is_empty() => { Symbol::NUM_FLOATINGPOINT if nested_args.is_empty() => {
buf.push_str("F64"); buf.push_str("F64");
@ -768,7 +768,7 @@ fn write_apply(
double_nested_args, double_nested_args,
))) => match double_nested_symbol { ))) => match double_nested_symbol {
Symbol::NUM_INTEGER if double_nested_args.is_empty() => { Symbol::NUM_INTEGER if double_nested_args.is_empty() => {
buf.push_str("Int"); buf.push_str("I64");
} }
Symbol::NUM_FLOATINGPOINT if double_nested_args.is_empty() => { Symbol::NUM_FLOATINGPOINT if double_nested_args.is_empty() => {
buf.push_str("F64"); buf.push_str("F64");

View file

@ -14,7 +14,7 @@ use roc_types::types::Type::{self, *};
pub fn int_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint { pub fn int_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
let num_type = Variable(num_var); let num_type = Variable(num_var);
let reason = Reason::IntLiteral; let reason = Reason::IntLiteral;
let int_type = builtin_type(Symbol::NUM_INT, vec![]); let int_type = builtin_type(Symbol::NUM_I64, vec![]);
let expected_literal = ForReason(reason, int_type, region); let expected_literal = ForReason(reason, int_type, region);
exists( exists(

View file

@ -23,13 +23,17 @@ roc_solve = { path = "../compiler/solve" }
roc_mono = { path = "../compiler/mono" } roc_mono = { path = "../compiler/mono" }
roc_load = { path = "../compiler/load" } roc_load = { path = "../compiler/load" }
roc_gen = { path = "../compiler/gen" } roc_gen = { path = "../compiler/gen" }
roc_fmt = { path = "../compiler/fmt" }
roc_reporting = { path = "../compiler/reporting" } roc_reporting = { path = "../compiler/reporting" }
# TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it # TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it
im = "14" # im and im-rc should always have the same version! im = "14" # im and im-rc should always have the same version!
im-rc = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version!
bumpalo = { version = "3.2", features = ["collections"] } bumpalo = { version = "3.2", features = ["collections"] }
inlinable_string = "0.1" inlinable_string = "0.1"
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", "process", "io-driver"] } arraystring = "0.3.0"
libc = "0.2"
page_size = "0.4"
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything. # NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
# #
# The reason for this fork is that the way Inkwell is designed, you have to use # The reason for this fork is that the way Inkwell is designed, you have to use
@ -57,7 +61,11 @@ zerocopy = "0.3"
env_logger = "0.7" env_logger = "0.7"
futures = "0.3" futures = "0.3"
wgpu_glyph = "0.10" wgpu_glyph = "0.10"
bytemuck = "1.4" cgmath = "0.17.0"
[dependencies.bytemuck]
version = "1.4"
features = ["derive"]
[dev-dependencies] [dev-dependencies]
pretty_assertions = "0.5.1" pretty_assertions = "0.5.1"

File diff suppressed because it is too large Load diff

457
editor/src/bucket.rs Normal file
View file

@ -0,0 +1,457 @@
/// A bucket of 16-byte nodes. The node value 0 is reserved for the bucket's
/// use, and valid nodes may never have that value.
///
/// By design, each bucket is 4096 bytes large. When you make a bucket, it
/// uses mmap to reserve one anonymous memory page in which to store nodes.
/// Since nodes are 16 bytes, one bucket can store 256 nodes; you can access
/// a particular node by its BucketSlot, which is an opaque wrapper around a u8.
///
/// Buckets also use the node value 0 (all 0 bits) to mark slots as unoccupied.
/// This is important for performance.
use libc::{c_void, calloc, free, mmap, munmap, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE};
use std::marker::PhantomData;
use std::mem::size_of;
use std::ptr::null;
pub const BUCKET_BYTES: usize = 4096;
#[derive(Debug)]
pub struct NodeId<T: Sized> {
pub bucket_id: BucketId<T>,
pub slot: BucketSlot<T>,
}
#[test]
fn size_of_node_id() {
assert_eq!(std::mem::size_of::<NodeId<()>>(), 4);
}
impl<T> Clone for NodeId<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for NodeId<T> {}
impl<T: Sized> NodeId<T> {
fn next_slot(&self) -> Self {
NodeId {
bucket_id: self.bucket_id,
slot: self.slot.increment(),
}
}
}
impl<T> PartialEq for NodeId<T> {
fn eq(&self, other: &Self) -> bool {
self.bucket_id == other.bucket_id && self.slot == other.slot
}
}
impl<T> Eq for NodeId<T> {}
#[derive(Debug)]
#[repr(transparent)]
pub struct BucketId<T: Sized> {
value: u16,
_phantom: PhantomData<T>,
}
#[test]
fn size_of_bucket_id() {
assert_eq!(std::mem::size_of::<BucketId<()>>(), 2);
}
impl<T> Clone for BucketId<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for BucketId<T> {}
impl<T> PartialEq for BucketId<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T> Eq for BucketId<T> {}
impl<T: Sized> BucketId<T> {
fn from_u16(value: u16) -> Self {
BucketId {
value,
_phantom: PhantomData::default(),
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct BucketSlot<T: Sized> {
value: u8,
_phantom: PhantomData<T>,
}
#[test]
fn size_of_bucket_slot() {
assert_eq!(std::mem::size_of::<BucketSlot<()>>(), 1);
}
impl<T> Clone for BucketSlot<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for BucketSlot<T> {}
impl<T> PartialEq for BucketSlot<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T> Eq for BucketSlot<T> {}
impl<T: Sized> BucketSlot<T> {
#[allow(dead_code)]
fn from_u8(value: u8) -> Self {
BucketSlot {
value,
_phantom: PhantomData::default(),
}
}
fn increment(&self) -> Self {
BucketSlot {
value: self.value + 1,
_phantom: PhantomData::default(),
}
}
}
pub struct Buckets {
buckets: Vec<Bucket>,
// free_1node_slots: Vec<NodeId<T>>,
}
impl Buckets {
// fn find_space_for(&mut self, nodes: u8) -> Result<BucketId<T>, ()> {}
pub fn add<T: Sized>(&mut self) -> Result<BucketId<T>, ()> {
let num_buckets = self.buckets.len();
if num_buckets <= u16::MAX as usize {
let bucket_id = BucketId::from_u16(num_buckets as u16);
let bucket = Bucket::default();
self.buckets.push(bucket);
Ok(bucket_id)
} else {
Err(())
}
}
fn get_unchecked<'a, T: Sized>(&'a self, node_id: NodeId<T>) -> &'a T {
unsafe {
self.buckets
.get(node_id.bucket_id.value as usize)
.unwrap()
.get_unchecked(node_id.slot.value)
}
}
pub fn get<'a, T: Sized>(&'a self, node_id: NodeId<T>) -> Option<&'a T> {
self.buckets
.get(node_id.bucket_id.value as usize)
.and_then(|bucket| bucket.get(node_id.slot))
}
}
struct Bucket {
#[allow(dead_code)]
next_unused_slot: u16,
first_slot: *mut [u8; 16],
}
impl Bucket {
/// If there's room left in the bucket, adds the item and returns
/// the slot where it was put. If there was no room left, returns Err(()).
#[allow(dead_code)]
pub fn add<T: Sized>(&mut self, node: T) -> Result<BucketSlot<T>, ()> {
// It's only safe to store this as a *const T if T is 16 bytes.
// This is designed to be used exclusively with 16-byte nodes!
debug_assert_eq!(size_of::<T>(), 16);
// Once next_unused_slot exceeds u8::MAX, we have no room left.
if self.next_unused_slot <= u8::MAX as u16 {
let chosen_slot = self.next_unused_slot as u8;
unsafe { self.put_unchecked(node, chosen_slot) };
self.next_unused_slot += 1;
Ok(BucketSlot::from_u8(chosen_slot))
} else {
// No room left!
Err(())
}
}
/// If the given slot is available, inserts the given node into it.
/// Otherwise, returns the node that was in the already-occupied slot.
#[allow(dead_code)]
pub fn insert<T: Sized>(&mut self, node: T, slot: BucketSlot<T>) -> Result<(), &T> {
// It's only safe to use this if T is 16 bytes.
// This is designed to be used exclusively with 16-byte nodes!
debug_assert_eq!(size_of::<T>(), 16);
let slot = slot.value;
unsafe {
if self.is_available(slot) {
self.put_unchecked(node, slot);
Ok(())
} else {
Err(self.get_unchecked(slot))
}
}
}
pub fn get<'a, T: Sized>(&'a self, slot: BucketSlot<T>) -> Option<&'a T> {
// It's only safe to store this as a *const T if T is 16 bytes.
// This is designed to be used exclusively with 16-byte nodes!
debug_assert_eq!(size_of::<T>(), 16);
unsafe {
let slot_ptr = self.first_slot.offset(slot.value as isize) as *const T;
let value: &[u8; 16] = &*(slot_ptr as *const [u8; 16]);
if *value != [0; 16] {
Some(&*(value as *const [u8; 16] as *const T))
} else {
None
}
}
}
unsafe fn put_unchecked<T: Sized>(&mut self, node: T, slot: u8) {
// It's only safe to store this as a *const T if T is 16 bytes.
// This is designed to be used exclusively with 16-byte nodes!
debug_assert_eq!(size_of::<T>(), 16);
let slot_ptr = self.first_slot.offset(slot as isize) as *mut T;
*slot_ptr = node;
}
unsafe fn get_unchecked<T>(&self, slot: u8) -> &T {
&*(self.first_slot.offset(slot as isize) as *const T)
}
// A slot is available iff its bytes are all zeroes
unsafe fn is_available(&self, slot: u8) -> bool {
let slot_ptr = self.first_slot.offset(slot as isize) as *const [u8; 16];
*slot_ptr == [0; 16]
}
}
impl Default for Bucket {
fn default() -> Self {
let first_slot = if page_size::get() == 4096 {
unsafe {
// mmap exactly one memory page (4096 bytes)
mmap(
null::<c_void>() as *mut c_void,
BUCKET_BYTES,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
0,
0,
)
}
} else {
// Somehow the page size is not 4096 bytes, so fall back on calloc.
// (We use calloc over malloc because we rely on the bytes having
// been zeroed to tell which slots are available.)
unsafe { calloc(1, BUCKET_BYTES) }
} as *mut [u8; 16];
Bucket {
next_unused_slot: 0,
first_slot,
}
}
}
impl Drop for Bucket {
fn drop(&mut self) {
if page_size::get() == 4096 {
unsafe {
munmap(self.first_slot as *mut c_void, BUCKET_BYTES);
}
} else {
unsafe {
free(self.first_slot as *mut c_void);
}
}
}
}
#[derive(Debug)]
pub struct BucketStr {
first_node_id: NodeId<()>,
first_segment_len: u8,
}
#[test]
fn size_of_bucket_str() {
assert_eq!(std::mem::size_of::<BucketList<()>>(), 4);
}
/// A non-empty list inside a bucket. It takes 4B of memory.
///
/// This is internally represented as an array of at most 255 nodes, which
/// can grow to 256+ nodes by having the last nodeent be a linked list Cons
/// cell which points to another such backing array which has more nodes.
///
/// In practice, these will almost be far below 256 nodes, but in theory
/// they can be enormous in length thanks to the linked list fallback.
///
/// Since these are non-empty lists, we need separate variants for collections
/// that can be empty, e.g. EmptyRecord and EmptyList. In contrast, we don't
/// need an EmptyList or EmptyWhen, since although those use BucketList
/// to store their branches, having zero branches is syntactically invalid.
/// Same with Call and Closure, since all functions must have 1+ arguments.
#[derive(Debug)]
pub struct BucketList<T: Sized> {
first_node_id: BucketId<T>,
first_node_sl: BucketSlot<T>,
first_segment_len: u8,
}
#[test]
fn size_of_bucket_list() {
assert_eq!(std::mem::size_of::<BucketList<()>>(), 4);
}
impl<'a, T: 'a + Sized> BucketList<T> {
/// If given a first_segment_len of 0, that means this is a BucketList
/// consisting of 256+ nodes. The first 255 are stored in the usual
/// array, and then there's one more nodeent at the end which continues
/// the list with a new length and NodeId value. BucketList iterators
/// automatically do these jumps behind the scenes when necessary.
pub fn new(first_node_id: NodeId<T>, first_segment_len: u8) -> Self {
BucketList {
first_segment_len,
first_node_id: first_node_id.bucket_id,
first_node_sl: first_node_id.slot,
}
}
pub fn into_iter(self, buckets: &'a Buckets) -> impl Iterator<Item = &'a T> {
self.bucket_list_iter(buckets)
}
/// Private version of into_iter which exposes the implementation detail
/// of BucketListIter. We don't want that struct to be public, but we
/// actually do want to have this separate function for code reuse
/// in the iterator's next() method.
fn bucket_list_iter(&self, buckets: &'a Buckets) -> BucketListIter<'a, T> {
let first_segment_len = self.first_segment_len;
let continues_with_cons = first_segment_len == 0;
let len_remaining = if continues_with_cons {
// We have 255 nodes followed by a Cons cell continuing the list.
u8::MAX
} else {
first_segment_len
};
BucketListIter {
continues_with_cons,
len_remaining,
bucket_id: self.first_node_id,
slot: self.first_node_sl,
buckets,
}
}
}
struct BucketListIter<'a, T: Sized> {
bucket_id: BucketId<T>,
slot: BucketSlot<T>,
len_remaining: u8,
continues_with_cons: bool,
buckets: &'a Buckets,
}
impl<'a, T: Sized> Iterator for BucketListIter<'a, T>
where
T: 'a,
{
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
match self.len_remaining {
0 => match self.continues_with_cons {
// We're done! This is by far the most common case, so we put
// it first to avoid branch mispredictions.
false => None,
// We need to continue with a Cons cell.
true => {
let node_id = NodeId {
bucket_id: self.bucket_id,
slot: self.slot,
}
.next_slot();
// Since we have continues_with_cons set, the next slot
// will definitely be occupied with a BucketList struct.
let node = self.buckets.get_unchecked(node_id);
let next_list = unsafe { &*(node as *const T as *const BucketList<T>) };
// Replace the current iterator with an iterator into that
// list, and then continue with next() on that iterator.
let next_iter = next_list.bucket_list_iter(self.buckets);
self.bucket_id = next_iter.bucket_id;
self.slot = next_iter.slot;
self.len_remaining = next_iter.len_remaining;
self.continues_with_cons = next_iter.continues_with_cons;
self.next()
}
},
1 => {
self.len_remaining = 0;
// Don't advance the node pointer's slot, because that might
// advance past the end of the bucket!
Some(self.buckets.get_unchecked(NodeId {
bucket_id: self.bucket_id,
slot: self.slot,
}))
}
len_remaining => {
// Get the current node
let node_id = NodeId {
bucket_id: self.bucket_id,
slot: self.slot,
};
let node = self.buckets.get_unchecked(node_id);
// Advance the node pointer to the next slot in the current bucket
self.slot = self.slot.increment();
self.len_remaining = len_remaining - 1;
Some(node)
}
}
}
}

166
editor/src/buffer.rs Normal file
View file

@ -0,0 +1,166 @@
// Adapted from https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
use crate::rect::Rect;
use crate::util::size_of_slice;
use crate::vertex::Vertex;
use wgpu::util::{BufferInitDescriptor, DeviceExt};
pub struct QuadBufferBuilder {
vertex_data: Vec<Vertex>,
index_data: Vec<u32>,
current_quad: u32,
}
impl QuadBufferBuilder {
pub fn new() -> Self {
Self {
vertex_data: Vec::new(),
index_data: Vec::new(),
current_quad: 0,
}
}
pub fn push_rect(self, rect: &Rect) -> Self {
let coords = rect.top_left_coords;
self.push_quad(
coords.x,
coords.y - rect.height,
coords.x + rect.width,
coords.y,
rect.color,
)
}
pub fn push_quad(
mut self,
min_x: f32,
min_y: f32,
max_x: f32,
max_y: f32,
color: [f32; 3],
) -> Self {
self.vertex_data.extend(&[
Vertex {
position: (min_x, min_y).into(),
color,
},
Vertex {
position: (max_x, min_y).into(),
color,
},
Vertex {
position: (max_x, max_y).into(),
color,
},
Vertex {
position: (min_x, max_y).into(),
color,
},
]);
self.index_data.extend(&[
self.current_quad * 4,
self.current_quad * 4 + 1,
self.current_quad * 4 + 2,
self.current_quad * 4,
self.current_quad * 4 + 2,
self.current_quad * 4 + 3,
]);
self.current_quad += 1;
self
}
pub fn build(self, device: &wgpu::Device) -> (StagingBuffer, StagingBuffer, u32) {
(
StagingBuffer::new(device, &self.vertex_data),
StagingBuffer::new(device, &self.index_data),
self.index_data.len() as u32,
)
}
}
pub struct RectBuffers {
pub vertex_buffer: wgpu::Buffer,
pub index_buffer: wgpu::Buffer,
pub num_rects: u32,
}
pub fn create_rect_buffers(
gpu_device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
) -> RectBuffers {
// Test Rectangles
let test_rect_1 = Rect {
top_left_coords: (-0.2, 0.6).into(),
width: 0.1,
height: 0.5,
color: [0.0, 0.0, 1.0],
};
let test_rect_2 = Rect {
top_left_coords: (-0.5, 0.0).into(),
width: 0.5,
height: 0.5,
color: [0.0, 1.0, 0.0],
};
let test_rect_3 = Rect {
top_left_coords: (0.3, 0.3).into(),
width: 0.6,
height: 0.1,
color: [1.0, 0.0, 0.0],
};
let vertex_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: Vertex::SIZE * 4 * 3,
usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
mapped_at_creation: false,
});
let u32_size = std::mem::size_of::<u32>() as wgpu::BufferAddress;
let index_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: u32_size * 6 * 3,
usage: wgpu::BufferUsage::INDEX | wgpu::BufferUsage::COPY_DST,
mapped_at_creation: false,
});
let num_rects = {
let (stg_vertex, stg_index, num_indices) = QuadBufferBuilder::new()
.push_rect(&test_rect_1)
.push_rect(&test_rect_2)
.push_rect(&test_rect_3)
.build(&gpu_device);
stg_vertex.copy_to_buffer(encoder, &vertex_buffer);
stg_index.copy_to_buffer(encoder, &index_buffer);
num_indices
};
RectBuffers {
vertex_buffer,
index_buffer,
num_rects,
}
}
pub struct StagingBuffer {
buffer: wgpu::Buffer,
size: wgpu::BufferAddress,
}
impl StagingBuffer {
pub fn new<T: bytemuck::Pod + Sized>(device: &wgpu::Device, data: &[T]) -> StagingBuffer {
StagingBuffer {
buffer: device.create_buffer_init(&BufferInitDescriptor {
contents: bytemuck::cast_slice(data),
usage: wgpu::BufferUsage::COPY_SRC,
label: Some("Staging Buffer"),
}),
size: size_of_slice(data) as wgpu::BufferAddress,
}
}
pub fn copy_to_buffer(&self, encoder: &mut wgpu::CommandEncoder, other: &wgpu::Buffer) {
encoder.copy_buffer_to_buffer(&self.buffer, 0, other, 0, self.size)
}
}

97
editor/src/file.rs Normal file
View file

@ -0,0 +1,97 @@
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_fmt::def::fmt_def;
use roc_fmt::module::fmt_module;
use roc_parse::ast::{Attempting, Def, Module};
use roc_parse::module::module_defs;
use roc_parse::parser;
use roc_parse::parser::Parser;
use roc_region::all::Located;
use std::ffi::OsStr;
use std::path::Path;
use std::{fs, io};
#[derive(Debug)]
pub struct File<'a> {
path: &'a Path,
module_header: Module<'a>,
content: Vec<'a, Located<Def<'a>>>,
}
#[derive(Debug)]
pub enum ReadError {
Read(std::io::Error),
ParseDefs(parser::Fail),
ParseHeader(parser::Fail),
DoesntHaveRocExtension,
}
impl<'a> File<'a> {
pub fn read(path: &'a Path, arena: &'a Bump) -> Result<File<'a>, ReadError> {
if path.extension() != Some(OsStr::new("roc")) {
return Err(ReadError::DoesntHaveRocExtension);
}
let bytes = fs::read(path).map_err(ReadError::Read)?;
let allocation = arena.alloc(bytes);
let module_parse_state = parser::State::new(allocation, Attempting::Module);
let parsed_module = roc_parse::module::header().parse(&arena, module_parse_state);
match parsed_module {
Ok((module, state)) => {
let parsed_defs = module_defs().parse(&arena, state);
match parsed_defs {
Ok((defs, _)) => Ok(File {
path,
module_header: module,
content: defs,
}),
Err((error, _)) => Err(ReadError::ParseDefs(error)),
}
}
Err((error, _)) => Err(ReadError::ParseHeader(error)),
}
}
pub fn fmt(&self) -> String {
let arena = Bump::new();
let mut formatted_file = String::new();
let mut module_header_buf = bumpalo::collections::String::new_in(&arena);
fmt_module(&mut module_header_buf, &self.module_header);
formatted_file.push_str(module_header_buf.as_str());
for def in &self.content {
let mut def_buf = bumpalo::collections::String::new_in(&arena);
fmt_def(&mut def_buf, &def.value, 0);
formatted_file.push_str(def_buf.as_str());
}
formatted_file
}
pub fn fmt_then_write_to(&self, write_path: &'a Path) -> io::Result<()> {
let formatted_file = self.fmt();
fs::write(write_path, formatted_file)
}
pub fn fmt_then_write_with_name(&self, new_name: &str) -> io::Result<()> {
self.fmt_then_write_to(
self.path
.with_file_name(new_name)
.with_extension("roc")
.as_path(),
)
}
pub fn fmt_then_write(&self) -> io::Result<()> {
self.fmt_then_write_to(self.path)
}
}

View file

@ -0,0 +1,153 @@
use winit::event::{ElementState, ModifiersState, VirtualKeyCode};
pub fn handle_keydown(
elem_state: ElementState,
virtual_keycode: VirtualKeyCode,
_modifiers: ModifiersState,
) {
use winit::event::VirtualKeyCode::*;
if let ElementState::Released = elem_state {
return;
}
match virtual_keycode {
Copy => {
todo!("copy");
}
Paste => {
todo!("paste");
}
Cut => {
todo!("cut");
}
_ => {}
}
}
// pub fn handle_text_input(
// text_state: &mut String,
// elem_state: ElementState,
// virtual_keycode: VirtualKeyCode,
// _modifiers: ModifiersState,
// ) {
// use winit::event::VirtualKeyCode::*;
// if let ElementState::Released = elem_state {
// return;
// }
// match virtual_keycode {
// Key1 | Numpad1 => text_state.push('1'),
// Key2 | Numpad2 => text_state.push('2'),
// Key3 | Numpad3 => text_state.push('3'),
// Key4 | Numpad4 => text_state.push('4'),
// Key5 | Numpad5 => text_state.push('5'),
// Key6 | Numpad6 => text_state.push('6'),
// Key7 | Numpad7 => text_state.push('7'),
// Key8 | Numpad8 => text_state.push('8'),
// Key9 | Numpad9 => text_state.push('9'),
// Key0 | Numpad0 => text_state.push('0'),
// A => text_state.push('a'),
// B => text_state.push('b'),
// C => text_state.push('c'),
// D => text_state.push('d'),
// E => text_state.push('e'),
// F => text_state.push('f'),
// G => text_state.push('g'),
// H => text_state.push('h'),
// I => text_state.push('i'),
// J => text_state.push('j'),
// K => text_state.push('k'),
// L => text_state.push('l'),
// M => text_state.push('m'),
// N => text_state.push('n'),
// O => text_state.push('o'),
// P => text_state.push('p'),
// Q => text_state.push('q'),
// R => text_state.push('r'),
// S => text_state.push('s'),
// T => text_state.push('t'),
// U => text_state.push('u'),
// V => text_state.push('v'),
// W => text_state.push('w'),
// X => text_state.push('x'),
// Y => text_state.push('y'),
// Z => text_state.push('z'),
// Escape | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | F14 | F15
// | F16 | F17 | F18 | F19 | F20 | F21 | F22 | F23 | F24 | Snapshot | Scroll | Pause
// | Insert | Home | Delete | End | PageDown | PageUp | Left | Up | Right | Down | Compose
// | Caret | Numlock | AbntC1 | AbntC2 | Ax | Calculator | Capital | Convert | Kana
// | Kanji | LAlt | LBracket | LControl | LShift | LWin | Mail | MediaSelect | PlayPause
// | Power | PrevTrack | MediaStop | Mute | MyComputer | NavigateForward
// | NavigateBackward | NextTrack | NoConvert | OEM102 | RAlt | Sysrq | RBracket
// | RControl | RShift | RWin | Sleep | Stop | Unlabeled | VolumeDown | VolumeUp | Wake
// | WebBack | WebFavorites | WebForward | WebHome | WebRefresh | WebSearch | Apps | Tab
// | WebStop => {
// // TODO handle
// }
// Back => {
// text_state.pop();
// }
// Return | NumpadEnter => {
// text_state.push('\n');
// }
// Space => {
// text_state.push(' ');
// }
// Comma | NumpadComma => {
// text_state.push(',');
// }
// Add => {
// text_state.push('+');
// }
// Apostrophe => {
// text_state.push('\'');
// }
// At => {
// text_state.push('@');
// }
// Backslash => {
// text_state.push('\\');
// }
// Colon => {
// text_state.push(':');
// }
// Period | Decimal => {
// text_state.push('.');
// }
// Equals | NumpadEquals => {
// text_state.push('=');
// }
// Grave => {
// text_state.push('`');
// }
// Minus | Subtract => {
// text_state.push('-');
// }
// Multiply => {
// text_state.push('*');
// }
// Semicolon => {
// text_state.push(';');
// }
// Slash | Divide => {
// text_state.push('/');
// }
// Underline => {
// text_state.push('_');
// }
// Yen => {
// text_state.push('¥');
// }
// Copy => {
// todo!("copy");
// }
// Paste => {
// todo!("paste");
// }
// Cut => {
// todo!("cut");
// }
// }
// }

View file

@ -11,19 +11,29 @@
// re-enable this when working on performance optimizations than have it block PRs. // re-enable this when working on performance optimizations than have it block PRs.
#![allow(clippy::large_enum_variant)] #![allow(clippy::large_enum_variant)]
use crate::rect::Rect; // Inspired by https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
// See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/
use crate::buffer::create_rect_buffers;
use crate::text::{build_glyph_brush, Text};
use crate::vertex::Vertex; use crate::vertex::Vertex;
use std::error::Error; use std::error::Error;
use std::io; use std::io;
use std::path::Path; use std::path::Path;
use wgpu::util::DeviceExt; use winit::event;
use wgpu_glyph::{ab_glyph, GlyphBrushBuilder, Section, Text}; use winit::event::{Event, ModifiersState};
use winit::event::{ElementState, ModifiersState, VirtualKeyCode};
use winit::event_loop::ControlFlow; use winit::event_loop::ControlFlow;
pub mod ast; pub mod ast;
pub mod bucket;
mod buffer;
pub mod file;
mod keyboard_input;
mod rect; mod rect;
pub mod text_state; pub mod text;
mod util;
mod vertex; mod vertex;
/// The editor is actually launched from the CLI if you pass it zero arguments, /// The editor is actually launched from the CLI if you pass it zero arguments,
@ -51,7 +61,7 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
let surface = unsafe { instance.create_surface(&window) }; let surface = unsafe { instance.create_surface(&window) };
// Initialize GPU // Initialize GPU
let (device, queue) = futures::executor::block_on(async { let (gpu_device, cmd_queue) = futures::executor::block_on(async {
let adapter = instance let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance, power_preference: wgpu::PowerPreference::HighPerformance,
@ -81,59 +91,21 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
// Prepare swap chain // Prepare swap chain
let render_format = wgpu::TextureFormat::Bgra8UnormSrgb; let render_format = wgpu::TextureFormat::Bgra8UnormSrgb;
let mut size = window.inner_size(); let mut size = window.inner_size();
let mut swap_chain = device.create_swap_chain(
&surface, let swap_chain_descr = wgpu::SwapChainDescriptor {
&wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: render_format, format: render_format,
width: size.width, width: size.width,
height: size.height, height: size.height,
//Immediate may cause tearing, change present_mode if this becomes a problem
present_mode: wgpu::PresentMode::Immediate, present_mode: wgpu::PresentMode::Immediate,
}, };
);
// Prepare Triangle Pipeline let mut swap_chain = gpu_device.create_swap_chain(&surface, &swap_chain_descr);
let triangle_vs_module =
device.create_shader_module(wgpu::include_spirv!("shaders/rect.vert.spv"));
let triangle_fs_module =
device.create_shader_module(wgpu::include_spirv!("shaders/rect.frag.spv"));
let triangle_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { let rect_pipeline = make_rect_pipeline(&gpu_device, &swap_chain_descr);
label: None,
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let triangle_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { let mut glyph_brush = build_glyph_brush(&gpu_device, render_format)?;
label: None,
layout: Some(&triangle_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &triangle_vs_module,
entry_point: "main",
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
module: &triangle_fs_module,
entry_point: "main",
}),
// Use the default rasterizer state: no culling, no depth bias
rasterization_state: None,
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::TextureFormat::Bgra8UnormSrgb.into()],
depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[Vertex::buffer_descriptor()],
},
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
});
// Prepare glyph_brush
let inconsolata =
ab_glyph::FontArc::try_from_slice(include_bytes!("../Inconsolata-Regular.ttf"))?;
let mut glyph_brush = GlyphBrushBuilder::using_font(inconsolata).build(&device, render_format);
let is_animating = true; let is_animating = true;
let mut text_state = String::new(); let mut text_state = String::new();
@ -152,178 +124,97 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
} }
match event { match event {
winit::event::Event::WindowEvent { Event::WindowEvent {
event: winit::event::WindowEvent::CloseRequested, event: event::WindowEvent::CloseRequested,
.. ..
} => *control_flow = winit::event_loop::ControlFlow::Exit, } => *control_flow = winit::event_loop::ControlFlow::Exit,
winit::event::Event::WindowEvent { Event::WindowEvent {
event: winit::event::WindowEvent::Resized(new_size), event: event::WindowEvent::Resized(new_size),
.. ..
} => { } => {
size = new_size; size = new_size;
swap_chain = device.create_swap_chain( swap_chain = gpu_device.create_swap_chain(
&surface, &surface,
&wgpu::SwapChainDescriptor { &wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: render_format, format: render_format,
width: size.width, width: size.width,
height: size.height, height: size.height,
//Immediate may cause tearing, change present_mode if this becomes a problem
present_mode: wgpu::PresentMode::Immediate, present_mode: wgpu::PresentMode::Immediate,
}, },
); );
} }
winit::event::Event::WindowEvent { Event::WindowEvent {
event: winit::event::WindowEvent::ReceivedCharacter(ch), event: event::WindowEvent::ReceivedCharacter(ch),
.. ..
} => { } => {
match ch { update_text_state(&mut text_state, &ch);
'\u{8}' | '\u{7f}' => {
// In Linux, we get a '\u{8}' when you press backspace,
// but in macOS we get '\u{7f}'.
text_state.pop();
} }
'\u{e000}'..='\u{f8ff}' Event::WindowEvent {
| '\u{f0000}'..='\u{ffffd}' event: event::WindowEvent::KeyboardInput { input, .. },
| '\u{100000}'..='\u{10fffd}' => {
// These are private use characters; ignore them.
// See http://www.unicode.org/faq/private_use.html
}
_ => {
text_state.push(ch);
}
}
}
winit::event::Event::WindowEvent {
event: winit::event::WindowEvent::KeyboardInput { input, .. },
.. ..
} => { } => {
if let Some(virtual_keycode) = input.virtual_keycode { if let Some(virtual_keycode) = input.virtual_keycode {
handle_keydown(input.state, virtual_keycode, keyboard_modifiers); keyboard_input::handle_keydown(
input.state,
virtual_keycode,
keyboard_modifiers,
);
} }
} }
winit::event::Event::WindowEvent { Event::WindowEvent {
event: winit::event::WindowEvent::ModifiersChanged(modifiers), event: event::WindowEvent::ModifiersChanged(modifiers),
.. ..
} => { } => {
keyboard_modifiers = modifiers; keyboard_modifiers = modifiers;
} }
winit::event::Event::MainEventsCleared => window.request_redraw(), Event::MainEventsCleared => window.request_redraw(),
winit::event::Event::RedrawRequested { .. } => { Event::RedrawRequested { .. } => {
// Get a command encoder for the current frame // Get a command encoder for the current frame
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder =
gpu_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Redraw"), label: Some("Redraw"),
}); });
// Get the next frame let rect_buffers = create_rect_buffers(&gpu_device, &mut encoder);
let frame = swap_chain let frame = swap_chain
.get_current_frame() .get_current_frame()
.expect("Failed to acquire next swap chain texture") .expect("Failed to acquire next SwapChainFrame")
.output; .output;
// Test Rectangle
let test_rect_1 = Rect {
top: 0.9,
left: -0.8,
width: 0.2,
height: 0.3,
color: [0.0, 1.0, 1.0],
};
let test_rect_2 = Rect {
top: 0.0,
left: 0.0,
width: 0.5,
height: 0.5,
color: [1.0, 1.0, 0.0],
};
let mut rectangles = Vec::new();
rectangles.extend_from_slice(&test_rect_1.as_array());
rectangles.extend_from_slice(&test_rect_2.as_array());
let mut rect_index_buffers = Vec::new();
rect_index_buffers.extend_from_slice(&Rect::INDEX_BUFFER);
rect_index_buffers.extend_from_slice(&Rect::INDEX_BUFFER);
// Vertex Buffer for drawing rectangles
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(&rectangles),
usage: wgpu::BufferUsage::VERTEX,
});
// Index Buffer for drawing rectangles
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(&rect_index_buffers),
usage: wgpu::BufferUsage::INDEX,
});
// Clear frame
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations::default(),
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.007,
g: 0.007,
b: 0.007,
a: 1.0,
}),
store: true,
},
}], }],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
render_pass.set_pipeline(&triangle_pipeline); if rect_buffers.num_rects > 0 {
render_pass.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..));
render_pass.set_vertex_buffer( render_pass.set_index_buffer(rect_buffers.index_buffer.slice(..));
0, // The buffer slot to use for this vertex buffer. render_pass.set_pipeline(&rect_pipeline);
vertex_buffer.slice(..), // Use the entire buffer. render_pass.draw_indexed(0..rect_buffers.num_rects, 0, 0..1);
);
render_pass.set_index_buffer(index_buffer.slice(..));
render_pass.draw_indexed(
0..((&rect_index_buffers).len() as u32), // Draw all of the vertices from our test data.
0, // Base Vertex
0..1, // Instances
);
} }
glyph_brush.queue(Section { drop(render_pass);
screen_position: (30.0, 30.0),
bounds: (size.width as f32, size.height as f32),
text: vec![Text::new("Enter some text:")
.with_color([0.4666, 0.2, 1.0, 1.0])
.with_scale(40.0)],
..Section::default()
});
glyph_brush.queue(Section { draw_all_text(
screen_position: (30.0, 90.0), &gpu_device,
bounds: (size.width as f32, size.height as f32),
text: vec![Text::new(format!("{}|", text_state).as_str())
.with_color([1.0, 1.0, 1.0, 1.0])
.with_scale(40.0)],
..Section::default()
});
// Draw the text!
glyph_brush
.draw_queued(
&device,
&mut staging_belt, &mut staging_belt,
&mut encoder, &mut encoder,
&frame.view, &frame,
size.width, &size,
size.height, &text_state,
) &mut glyph_brush,
.expect("Draw queued"); );
staging_belt.finish(); staging_belt.finish();
queue.submit(Some(encoder.finish())); cmd_queue.submit(Some(encoder.finish()));
// Recall unused staging buffers // Recall unused staging buffers
use futures::task::SpawnExt; use futures::task::SpawnExt;
@ -341,27 +232,126 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
}) })
} }
fn handle_keydown( fn make_rect_pipeline(
elem_state: ElementState, gpu_device: &wgpu::Device,
virtual_keycode: VirtualKeyCode, swap_chain_descr: &wgpu::SwapChainDescriptor,
_modifiers: ModifiersState, ) -> wgpu::RenderPipeline {
let pipeline_layout = gpu_device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[],
push_constant_ranges: &[],
label: Some("Pipeline Layout"),
});
let pipeline = create_render_pipeline(
&gpu_device,
&pipeline_layout,
swap_chain_descr.format,
&[Vertex::DESC],
wgpu::include_spirv!("shaders/rect.vert.spv"),
wgpu::include_spirv!("shaders/rect.frag.spv"),
);
pipeline
}
fn create_render_pipeline(
device: &wgpu::Device,
layout: &wgpu::PipelineLayout,
color_format: wgpu::TextureFormat,
vertex_descs: &[wgpu::VertexBufferDescriptor],
vs_src: wgpu::ShaderModuleSource,
fs_src: wgpu::ShaderModuleSource,
) -> wgpu::RenderPipeline {
let vs_module = device.create_shader_module(vs_src);
let fs_module = device.create_shader_module(fs_src);
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
module: &fs_module,
entry_point: "main",
}),
rasterization_state: None,
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
format: color_format,
color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint32,
vertex_buffers: vertex_descs,
},
})
}
fn draw_all_text(
gpu_device: &wgpu::Device,
staging_belt: &mut wgpu::util::StagingBelt,
encoder: &mut wgpu::CommandEncoder,
frame: &wgpu::SwapChainTexture,
size: &winit::dpi::PhysicalSize<u32>,
text_state: &str,
glyph_brush: &mut wgpu_glyph::GlyphBrush<()>,
) { ) {
use winit::event::VirtualKeyCode::*; let bounds = (size.width as f32, size.height as f32).into();
if let ElementState::Released = elem_state { let main_label = Text {
return; position: (30.0, 30.0).into(),
} bounds,
color: (0.4666, 0.2, 1.0, 1.0).into(),
text: String::from("Enter some text:"),
size: 40.0,
..Default::default()
};
match virtual_keycode { let code_text = Text {
Copy => { position: (30.0, 90.0).into(),
todo!("copy"); bounds,
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::from(format!("{}|", text_state).as_str()),
size: 40.0,
..Default::default()
};
text::queue_text_draw(&main_label, glyph_brush);
text::queue_text_draw(&code_text, glyph_brush);
glyph_brush
.draw_queued(
gpu_device,
staging_belt,
encoder,
&frame.view,
size.width,
size.height,
)
.expect("Draw queued");
}
fn update_text_state(text_state: &mut String, received_char: &char) {
match received_char {
'\u{8}' | '\u{7f}' => {
// In Linux, we get a '\u{8}' when you press backspace,
// but in macOS we get '\u{7f}'.
text_state.pop();
} }
Paste => { '\u{e000}'..='\u{f8ff}' | '\u{f0000}'..='\u{ffffd}' | '\u{100000}'..='\u{10fffd}' => {
todo!("paste"); // These are private use characters; ignore them.
// See http://www.unicode.org/faq/private_use.html
} }
Cut => { _ => {
todo!("cut"); text_state.push(*received_char);
} }
_ => {}
} }
} }

View file

@ -1,35 +1,8 @@
use crate::vertex::Vertex; use cgmath::Vector2;
pub struct Rect { pub struct Rect {
pub top: f32, pub top_left_coords: Vector2<f32>,
pub left: f32,
pub width: f32, pub width: f32,
pub height: f32, pub height: f32,
pub color: [f32; 3], pub color: [f32; 3],
} }
impl Rect {
pub fn as_array(&self) -> [Vertex; 4] {
[
Vertex {
position: [self.left, self.top, 0.0],
color: self.color,
},
Vertex {
position: [self.left + self.width, self.top, 0.0],
color: self.color,
},
Vertex {
position: [self.left + self.width, self.top - self.height, 0.0],
color: self.color,
},
Vertex {
position: [self.left, self.top - self.height, 0.0],
color: self.color,
},
]
}
// Currently broken - needs to be offset when additional rectangles are appended
pub const INDEX_BUFFER: [u16; 6] = [0, 1, 3, 1, 2, 3];
}

View file

@ -1,11 +1,11 @@
#version 450 #version 450
// Taken from https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
// The fragment shader's "in" values come from the "out" values of the vertex shader.
layout(location=0) in vec3 color; layout(location=0) in vec3 color;
// The actual color that is rendered to the screen based on the vertex. layout(location=0) out vec4 fColor;
layout(location=0) out vec4 f_color;
void main() { void main() {
f_color = vec4(color, 1.0); fColor = vec4(color, 1.0);
} }

View file

@ -1,14 +1,13 @@
#version 450 #version 450
// Taken from https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
// Layout value labelled "in" acquire data from the vertex buffer, layout(location=0) in vec2 aPosition;
// as defined in the buffer descriptor for this shader. layout(location=1) in vec3 aColor;
layout(location=0) in vec3 position;
layout(location=1) in vec3 color;
// Layout values labelled "out" send their data to the fragment shader. layout(location=0) out vec3 vColor;
layout(location=0) out vec3 v_color;
void main() { void main() {
v_color = color; gl_Position = vec4(aPosition, 0, 1);
gl_Position = vec4(position, 1.0); vColor = aColor;
} }

62
editor/src/text.rs Normal file
View file

@ -0,0 +1,62 @@
// Adapted from https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
use ab_glyph::{FontArc, InvalidFont};
use cgmath::{Vector2, Vector4};
use wgpu_glyph::{ab_glyph, GlyphBrush, GlyphBrushBuilder, Section};
#[derive(Debug)]
pub struct Text {
pub position: Vector2<f32>,
pub bounds: Vector2<f32>,
pub color: Vector4<f32>,
pub text: String,
pub size: f32,
pub visible: bool,
pub centered: bool,
}
impl Default for Text {
fn default() -> Self {
Self {
position: (0.0, 0.0).into(),
bounds: (std::f32::INFINITY, std::f32::INFINITY).into(),
color: (1.0, 1.0, 1.0, 1.0).into(),
text: String::new(),
size: 16.0,
visible: true,
centered: false,
}
}
}
pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) {
let layout = wgpu_glyph::Layout::default().h_align(if text.centered {
wgpu_glyph::HorizontalAlign::Center
} else {
wgpu_glyph::HorizontalAlign::Left
});
let section = Section {
screen_position: text.position.into(),
bounds: text.bounds.into(),
layout,
..Section::default()
}
.add_text(
wgpu_glyph::Text::new(&text.text)
.with_color(text.color)
.with_scale(text.size),
);
glyph_brush.queue(section);
}
pub fn build_glyph_brush(
gpu_device: &wgpu::Device,
render_format: wgpu::TextureFormat,
) -> Result<GlyphBrush<()>, InvalidFont> {
let inconsolata = FontArc::try_from_slice(include_bytes!("../Inconsolata-Regular.ttf"))?;
Ok(GlyphBrushBuilder::using_font(inconsolata).build(&gpu_device, render_format))
}

View file

@ -1,128 +0,0 @@
use winit::event::{ElementState, ModifiersState, VirtualKeyCode};
pub fn handle_text_input(
text_state: &mut String,
elem_state: ElementState,
virtual_keycode: VirtualKeyCode,
_modifiers: ModifiersState,
) {
use winit::event::VirtualKeyCode::*;
if let ElementState::Released = elem_state {
return;
}
match virtual_keycode {
Key1 | Numpad1 => text_state.push('1'),
Key2 | Numpad2 => text_state.push('2'),
Key3 | Numpad3 => text_state.push('3'),
Key4 | Numpad4 => text_state.push('4'),
Key5 | Numpad5 => text_state.push('5'),
Key6 | Numpad6 => text_state.push('6'),
Key7 | Numpad7 => text_state.push('7'),
Key8 | Numpad8 => text_state.push('8'),
Key9 | Numpad9 => text_state.push('9'),
Key0 | Numpad0 => text_state.push('0'),
A => text_state.push('a'),
B => text_state.push('b'),
C => text_state.push('c'),
D => text_state.push('d'),
E => text_state.push('e'),
F => text_state.push('f'),
G => text_state.push('g'),
H => text_state.push('h'),
I => text_state.push('i'),
J => text_state.push('j'),
K => text_state.push('k'),
L => text_state.push('l'),
M => text_state.push('m'),
N => text_state.push('n'),
O => text_state.push('o'),
P => text_state.push('p'),
Q => text_state.push('q'),
R => text_state.push('r'),
S => text_state.push('s'),
T => text_state.push('t'),
U => text_state.push('u'),
V => text_state.push('v'),
W => text_state.push('w'),
X => text_state.push('x'),
Y => text_state.push('y'),
Z => text_state.push('z'),
Escape | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | F14 | F15
| F16 | F17 | F18 | F19 | F20 | F21 | F22 | F23 | F24 | Snapshot | Scroll | Pause
| Insert | Home | Delete | End | PageDown | PageUp | Left | Up | Right | Down | Compose
| Caret | Numlock | AbntC1 | AbntC2 | Ax | Calculator | Capital | Convert | Kana
| Kanji | LAlt | LBracket | LControl | LShift | LWin | Mail | MediaSelect | PlayPause
| Power | PrevTrack | MediaStop | Mute | MyComputer | NavigateForward
| NavigateBackward | NextTrack | NoConvert | OEM102 | RAlt | Sysrq | RBracket
| RControl | RShift | RWin | Sleep | Stop | Unlabeled | VolumeDown | VolumeUp | Wake
| WebBack | WebFavorites | WebForward | WebHome | WebRefresh | WebSearch | Apps | Tab
| WebStop => {
// TODO handle
}
Back => {
text_state.pop();
}
Return | NumpadEnter => {
text_state.push('\n');
}
Space => {
text_state.push(' ');
}
Comma | NumpadComma => {
text_state.push(',');
}
Add => {
text_state.push('+');
}
Apostrophe => {
text_state.push('\'');
}
At => {
text_state.push('@');
}
Backslash => {
text_state.push('\\');
}
Colon => {
text_state.push(':');
}
Period | Decimal => {
text_state.push('.');
}
Equals | NumpadEquals => {
text_state.push('=');
}
Grave => {
text_state.push('`');
}
Minus | Subtract => {
text_state.push('-');
}
Multiply => {
text_state.push('*');
}
Semicolon => {
text_state.push(';');
}
Slash | Divide => {
text_state.push('/');
}
Underline => {
text_state.push('_');
}
Yen => {
text_state.push('¥');
}
Copy => {
todo!("copy");
}
Paste => {
todo!("paste");
}
Cut => {
todo!("cut");
}
}
}

5
editor/src/util.rs Normal file
View file

@ -0,0 +1,5 @@
// Taken from https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
pub fn size_of_slice<T: Sized>(slice: &[T]) -> usize {
std::mem::size_of::<T>() * slice.len()
}

View file

@ -1,7 +1,11 @@
#[repr(C)] // Taken from https://github.com/sotrh/learn-wgpu
#[derive(Copy, Clone, Debug)] // by Benjamin Hansen, licensed under the MIT license
use cgmath::Vector2;
#[derive(Copy, Clone)]
pub struct Vertex { pub struct Vertex {
pub position: [f32; 3], #[allow(dead_code)]
pub position: Vector2<f32>,
pub color: [f32; 3], pub color: [f32; 3],
} }
@ -9,25 +13,23 @@ unsafe impl bytemuck::Pod for Vertex {}
unsafe impl bytemuck::Zeroable for Vertex {} unsafe impl bytemuck::Zeroable for Vertex {}
impl Vertex { impl Vertex {
// Defines how the shader will use this data structure. pub const SIZE: wgpu::BufferAddress = std::mem::size_of::<Self>() as wgpu::BufferAddress;
pub fn buffer_descriptor<'a>() -> wgpu::VertexBufferDescriptor<'a> { pub const DESC: wgpu::VertexBufferDescriptor<'static> = wgpu::VertexBufferDescriptor {
wgpu::VertexBufferDescriptor { stride: Self::SIZE,
stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex, step_mode: wgpu::InputStepMode::Vertex,
attributes: &[ attributes: &[
// position // position
wgpu::VertexAttributeDescriptor { wgpu::VertexAttributeDescriptor {
offset: 0, offset: 0,
shader_location: 0, shader_location: 0,
format: wgpu::VertexFormat::Float3, format: wgpu::VertexFormat::Float2,
}, },
// color // color
wgpu::VertexAttributeDescriptor { wgpu::VertexAttributeDescriptor {
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, offset: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float3, format: wgpu::VertexFormat::Float3,
}, },
], ],
} };
}
} }

View file

@ -0,0 +1,26 @@
interface Simple
exposes [
v, x
]
imports []
v : Str
v = "Value!"
x : Int
x = 4

View file

@ -0,0 +1,68 @@
interface Storage
exposes [
Storage,
decoder,
get,
listener,
set
]
imports [
Map.{ Map },
Json.Decode.{ Decoder } as Decode
Json.Encode as Encode
Ports.FromJs as FromJs
Ports.ToJs as ToJs
]
################################################################################
## TYPES ##
################################################################################
Storage : [
@Storage (Map Str Decode.Value)
]
################################################################################
## API ##
################################################################################
get : Storage, Str, Decoder a -> [ Ok a, NotInStorage, DecodeError Decode.Error ]*
get = \key, decoder, @Storage map ->
when Map.get map key is
Ok json ->
Decode.decodeValue decoder json
Err NotFound ->
NotInStorage
set : Encode.Value, Str -> Effect {}
set json str =
ToJs.type "setStorage"
|> ToJs.setFields [
Field "key" (Encode.str str),
Field "value" json
]
|> ToJs.send
decoder : Decoder Storage
decoder =
Decode.mapType Decode.value
|> Decode.map \map -> @Storage map
################################################################################
## PORTS INCOMING ##
################################################################################
listener : (Storage -> msg) -> FromJs.Listener msg
listener toMsg =
FromJs.listen "storageUpdated"
(Decode.map decoder toMsg)

40
editor/tests/test_file.rs Normal file
View file

@ -0,0 +1,40 @@
#[macro_use]
extern crate pretty_assertions;
#[macro_use]
extern crate indoc;
#[cfg(test)]
mod test_file {
use bumpalo::Bump;
use roc_editor::file::File;
use std::path::Path;
#[test]
fn read_and_fmt_simple_roc_module() {
let simple_module_path = Path::new("./tests/modules/Simple.roc");
let arena = Bump::new();
let file = File::read(simple_module_path, &arena)
.expect("Could not read Simple.roc in test_file test");
assert_eq!(
file.fmt(),
indoc!(
r#"
interface Simple
exposes [
v, x
]
imports []
v : Str
v = "Value!"
x : Int
x = 4"#
)
);
}
}

1
examples/balance/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
main

View file

@ -1,7 +1,7 @@
app Main provides [ rocMain ] imports [ Effect ] app "main" imports [ Effect ] provides [ rocMain ] to "./platform"
rocMain : Effect.Effect {} as Fx rocMain : Effect.Effect {} as Fx
rocMain = rocMain =
when List.len (Str.split "hello" "JJJJ there") is when List.len (Str.split "hello" "JJJJ there") is
_ -> Effect.putLine "Yay" _ -> Effect.putLine "Yay"

View file

@ -1,7 +1,9 @@
platform folkertdev/foo platform folkertdev/foo
provides [ mainForHost ] requires { rocMain : Effect {} }
requires { main : Effect {} } exposes []
packages {}
imports [] imports []
provides [ mainForHost ]
effects Effect effects Effect
{ {
putChar : Int -> Effect {}, putChar : Int -> Effect {},
@ -10,4 +12,4 @@ platform folkertdev/foo
} }
mainForHost : Effect {} as Fx mainForHost : Effect {} as Fx
mainForHost = main mainForHost = rocMain

View file

@ -7,16 +7,16 @@ use std::alloc::Layout;
use std::time::SystemTime; use std::time::SystemTime;
extern "C" { extern "C" {
#[link_name = "Main_rocMain_1_exposed"] #[link_name = "roc__rocMain_1_exposed"]
fn roc_main(output: *mut u8) -> (); fn roc_main(output: *mut u8) -> ();
#[link_name = "Main_rocMain_1_size"] #[link_name = "roc__rocMain_1_size"]
fn roc_main_size() -> i64; fn roc_main_size() -> i64;
#[link_name = "Main_rocMain_1_Fx_caller"] #[link_name = "roc__rocMain_1_Fx_caller"]
fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> (); fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
#[link_name = "Main_rocMain_1_Fx_size"] #[link_name = "roc__rocMain_1_Fx_size"]
fn size_Fx() -> i64; fn size_Fx() -> i64;
} }
@ -62,11 +62,10 @@ unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const
let output = &*(buffer as *mut RocCallResult<i64>); let output = &*(buffer as *mut RocCallResult<i64>);
// match output.into() { match output.into() {
// Ok(v) => v, Ok(_) => 0,
// Err(e) => panic!("failed with {}", e), Err(e) => panic!("failed with {}", e),
// } }
32
}) })
} }
@ -96,7 +95,12 @@ pub fn rust_main() -> isize {
let closure_data_ptr = buffer.offset(16); let closure_data_ptr = buffer.offset(16);
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8) let result =
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8);
std::alloc::dealloc(buffer, layout);
result
} }
Err(msg) => { Err(msg) => {
std::alloc::dealloc(buffer, layout); std::alloc::dealloc(buffer, layout);

1
examples/closure/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
closure

View file

@ -1,6 +1,6 @@
app "closure" provides [ makeClosure ] to "./platform/" app "closure" provides [ makeClosure ] to "./platform/"
makeClosure : ({} -> Int) as MyClosure makeClosure : ({} -> I64) as MyClosure
makeClosure = makeClosure =
x = 42 x = 42
y = 42 y = 42

View file

@ -1,19 +1,21 @@
#![allow(non_snake_case)]
use roc_std::alloca; use roc_std::alloca;
use roc_std::RocCallResult; use roc_std::RocCallResult;
use std::alloc::Layout; use std::alloc::Layout;
use std::time::SystemTime; use std::time::SystemTime;
extern "C" { extern "C" {
#[link_name = "Main_makeClosure_1_exposed"] #[link_name = "roc__makeClosure_1_exposed"]
fn make_closure(output: *mut u8) -> (); fn make_closure(output: *mut u8) -> ();
#[link_name = "Main_makeClosure_1_size"] #[link_name = "roc__makeClosure_1_size"]
fn closure_size() -> i64; fn closure_size() -> i64;
#[link_name = "Main_makeClosure_1_MyClosure_caller"] #[link_name = "roc__makeClosure_1_MyClosure_caller"]
fn call_MyClosure(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> (); fn call_MyClosure(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
#[link_name = "Main_makeClosure_1_MyClosure_size"] #[link_name = "roc__makeClosure_1_MyClosure_size"]
fn size_MyClosure() -> i64; fn size_MyClosure() -> i64;
} }
@ -65,7 +67,12 @@ pub fn rust_main() -> isize {
let closure_data_ptr = buffer.offset(16); let closure_data_ptr = buffer.offset(16);
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8) let result =
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8);
std::alloc::dealloc(buffer, layout);
result
} }
Err(msg) => { Err(msg) => {
std::alloc::dealloc(buffer, layout); std::alloc::dealloc(buffer, layout);
@ -80,7 +87,6 @@ pub fn rust_main() -> isize {
println!( println!(
"Roc closure took {:.4} ms to compute this answer: {:?}", "Roc closure took {:.4} ms to compute this answer: {:?}",
duration.as_secs_f64() * 1000.0, duration.as_secs_f64() * 1000.0,
// truncate the answer, so stdout is not swamped
answer answer
); );

1
examples/effect/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
effect-example

View file

@ -5,7 +5,7 @@ ConsList a : [ Cons a (ConsList a), Nil ]
empty : ConsList a empty : ConsList a
empty = Nil empty = Nil
len : ConsList a -> Int len : ConsList a -> I64
len = \list -> len = \list ->
when list is when list is
Cons _ rest -> 1 + len rest Cons _ rest -> 1 + len rest

View file

@ -1,11 +1,16 @@
app "effect-example" imports [ Effect ] provides [ main ] to "./platform" app "effect-example"
packages { base: "thing/platform-dir" }
imports [ base.Task.{ Task, after } ]
provides [ main ] to base
# TODO `main : Task {}` does not work
main : Effect.Effect {} as Fx # it will then think that the `Task` module is unused
# (if we also don't use any of the other importd symbols)
main : Task.Task {} as Fx
main = main =
when if 1 == 1 then True 3 else False 3.14 is when if 1 == 1 then True 3 else False 3.14 is
True n -> Effect.putLine (Str.fromInt n) True n -> Task.putLine (Str.fromInt n)
_ -> Effect.putLine "Yay" _ -> Task.putLine "Yay"
# main : Effect.Effect {} as Fx # main : Effect.Effect {} as Fx
# main = # main =

View file

@ -21,11 +21,11 @@ singleton = \key, value ->
Node Black key value Empty Empty Node Black key value Empty Empty
# {-| Determine the number of key-value pairs in the dictionary. -} # {-| Determine the number of key-value pairs in the dictionary. -}
size : Dict k v -> Int size : Dict k v -> I64
size = \dict -> size = \dict ->
sizeHelp 0 dict sizeHelp 0 dict
sizeHelp : Int, Dict k v -> Int sizeHelp : I64, Dict k v -> I64
sizeHelp = \n, dict -> sizeHelp = \n, dict ->
when dict is when dict is
Empty -> Empty ->

View file

@ -8,6 +8,6 @@ edition = "2018"
crate-type = ["staticlib"] crate-type = ["staticlib"]
[dependencies] [dependencies]
roc_std = { path = "../../../roc_std" } roc_std = { path = "../../../../roc_std" }
[workspace] [workspace]

View file

@ -1,6 +1,6 @@
platform folkertdev/foo platform folkertdev/foo
requires { main : Effect {} } requires { main : Effect {} }
exposes [] exposes [ Task ]
packages {} packages {}
imports [] imports []
provides [ mainForHost ] provides [ mainForHost ]

View file

@ -0,0 +1,9 @@
interface Task
exposes [ Task, putLine, after ]
imports [ Effect ]
Task a : Effect.Effect a
putLine = \line -> Effect.putLine line
after = Effect.after

View file

@ -1,9 +1,9 @@
app "quicksort" imports [ Utils.{ swap } ] provides [ quicksort ] to "./platform" app "quicksort" imports [ Utils.{ swap } ] provides [ quicksort ] to "./platform"
quicksort : List Int -> List Int quicksort : List I64 -> List I64
quicksort = \originalList -> quicksort = \originalList ->
quicksortHelp : List (Num a), Int, Int -> List (Num a) quicksortHelp : List (Num a), I64, I64 -> List (Num a)
quicksortHelp = \list, low, high -> quicksortHelp = \list, low, high ->
if low < high then if low < high then
when partition low high list is when partition low high list is
@ -14,7 +14,7 @@ quicksort = \originalList ->
else else
list list
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -26,7 +26,7 @@ quicksort = \originalList ->
Pair (low - 1) initialList Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is

View file

@ -1,6 +1,6 @@
interface Utils exposes [ swap ] imports [] interface Utils exposes [ swap ] imports []
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->

View file

@ -2,7 +2,7 @@ app "quicksort" provides [ quicksort ] to "./platform"
quicksort = \originalList -> quicksort = \originalList ->
quicksortHelp : List (Num a), Int, Int -> List (Num a) quicksortHelp : List (Num a), I64, I64 -> List (Num a)
quicksortHelp = \list, low, high -> quicksortHelp = \list, low, high ->
if low < high then if low < high then
when partition low high list is when partition low high list is
@ -14,7 +14,7 @@ quicksort = \originalList ->
list list
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -25,7 +25,7 @@ quicksort = \originalList ->
Err _ -> Err _ ->
Pair (low - 1) initialList Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is
@ -41,7 +41,7 @@ quicksort = \originalList ->
Pair i list Pair i list
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->

1
examples/shared-quicksort/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
quicksort

View file

@ -1,12 +1,12 @@
app "quicksort" packages { base: "./platform" } provides [ quicksort ] to base app "quicksort" packages { base: "./platform" } provides [ quicksort ] to base
quicksort : List Int -> List Int quicksort : List I64 -> List I64
quicksort = \originalList -> helper originalList quicksort = \originalList -> helper originalList
helper : List Int -> List Int helper : List I64 -> List I64
helper = \originalList -> helper = \originalList ->
quicksortHelp : List (Num a), Int, Int -> List (Num a) quicksortHelp : List (Num a), I64, I64 -> List (Num a)
quicksortHelp = \list, low, high -> quicksortHelp = \list, low, high ->
if low < high then if low < high then
when partition low high list is when partition low high list is
@ -18,7 +18,7 @@ helper = \originalList ->
list list
swap : Int, Int, List a -> List a swap : I64, I64, List a -> List a
swap = \i, j, list -> swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) -> Pair (Ok atI) (Ok atJ) ->
@ -29,7 +29,7 @@ helper = \originalList ->
_ -> _ ->
[] []
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ] partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
partition = \low, high, initialList -> partition = \low, high, initialList ->
when List.get initialList high is when List.get initialList high is
Ok pivot -> Ok pivot ->
@ -41,7 +41,7 @@ helper = \originalList ->
Pair (low - 1) initialList Pair (low - 1) initialList
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ] partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot -> partitionHelp = \i, j, list, high, pivot ->
if j < high then if j < high then
when List.get list j is when List.get list j is

View file

@ -1,9 +1,11 @@
#![allow(non_snake_case)]
use roc_std::RocCallResult; use roc_std::RocCallResult;
use roc_std::RocList; use roc_std::RocList;
use std::time::SystemTime; use std::time::SystemTime;
extern "C" { extern "C" {
#[link_name = "_quicksort_1_exposed"] #[link_name = "roc__quicksort_1_exposed"]
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> (); fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
} }

View file

@ -39,6 +39,7 @@ let
linux-inputs = linux-inputs =
if isLinux then if isLinux then
[ [
valgrind
vulkan-headers vulkan-headers
vulkan-loader vulkan-loader
vulkan-tools vulkan-tools
@ -71,7 +72,6 @@ let
python3 python3
llvmPkgs.llvm llvmPkgs.llvm
llvmPkgs.clang llvmPkgs.clang
valgrind
pkg-config pkg-config
zig zig
# llb deps # llb deps