mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Merge branch 'trunk' into fix-module-formatting
This commit is contained in:
commit
881bae7267
114 changed files with 4260 additions and 1986 deletions
|
@ -1,4 +1,4 @@
|
|||
[alias]
|
||||
test-gen-llvm = "test -p test_gen"
|
||||
test-gen-dev = "test -p roc_gen_dev -p test_gen --no-default-features --features gen-dev"
|
||||
test-gen-wasm = "test -p test_gen --no-default-features --features gen-wasm"
|
||||
test-gen-wasm = "test -p roc_gen_wasm -p test_gen --no-default-features --features gen-wasm"
|
||||
|
|
36
Cargo.lock
generated
36
Cargo.lock
generated
|
@ -3204,6 +3204,17 @@ version = "0.7.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157"
|
||||
|
||||
[[package]]
|
||||
name = "repl_test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"indoc",
|
||||
"roc_cli",
|
||||
"roc_repl_cli",
|
||||
"roc_test_utils",
|
||||
"strip-ansi-escapes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.6.7"
|
||||
|
@ -3669,14 +3680,20 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"bumpalo",
|
||||
"const_format",
|
||||
"indoc",
|
||||
"inkwell 0.1.0",
|
||||
"libloading 0.7.1",
|
||||
"roc_build",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
"roc_gen_llvm",
|
||||
"roc_load",
|
||||
"roc_mono",
|
||||
"roc_parse",
|
||||
"roc_repl_eval",
|
||||
"roc_test_utils",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"rustyline",
|
||||
"rustyline-derive",
|
||||
"strip-ansi-escapes",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
|
@ -3685,14 +3702,10 @@ name = "roc_repl_eval"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"inkwell 0.1.0",
|
||||
"libloading 0.7.1",
|
||||
"roc_build",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_fmt",
|
||||
"roc_gen_llvm",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
|
@ -3701,7 +3714,14 @@ dependencies = [
|
|||
"roc_reporting",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_repl_wasm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"roc_parse",
|
||||
"roc_repl_eval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -35,6 +35,8 @@ members = [
|
|||
"reporting",
|
||||
"repl_cli",
|
||||
"repl_eval",
|
||||
"repl_test",
|
||||
"repl_wasm",
|
||||
"roc_std",
|
||||
"test_utils",
|
||||
"utils",
|
||||
|
|
|
@ -47,7 +47,7 @@ install-zig-llvm-valgrind-clippy-rustfmt:
|
|||
|
||||
copy-dirs:
|
||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||
COPY --dir cli cli_utils compiler docs editor ast code_markup error_macros utils test_utils reporting repl_cli repl_eval roc_std vendor examples linker Cargo.toml Cargo.lock version.txt ./
|
||||
COPY --dir cli cli_utils compiler docs editor ast code_markup error_macros utils test_utils reporting repl_cli repl_eval repl_test repl_wasm roc_std vendor examples linker Cargo.toml Cargo.lock version.txt ./
|
||||
|
||||
test-zig:
|
||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use bumpalo::Bump;
|
||||
use roc_can::expr::Recursive;
|
||||
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
|
||||
use roc_can::expr::{IntValue, Recursive};
|
||||
use roc_can::num::{
|
||||
finish_parsing_base, finish_parsing_float, finish_parsing_num, ParsedNumResult,
|
||||
};
|
||||
use roc_can::operator::desugar_expr;
|
||||
use roc_collections::all::MutSet;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
@ -52,7 +54,7 @@ pub fn expr_to_expr2<'a>(
|
|||
match parse_expr {
|
||||
Float(string) => {
|
||||
match finish_parsing_float(string) {
|
||||
Ok(float) => {
|
||||
Ok((float, _bound)) => {
|
||||
let expr = Expr2::Float {
|
||||
number: FloatVal::F64(float),
|
||||
var: env.var_store.fresh(),
|
||||
|
@ -73,10 +75,13 @@ pub fn expr_to_expr2<'a>(
|
|||
}
|
||||
}
|
||||
Num(string) => {
|
||||
match finish_parsing_int(string) {
|
||||
Ok(int) => {
|
||||
match finish_parsing_num(string) {
|
||||
Ok(ParsedNumResult::UnknownNum(int) | ParsedNumResult::Int(int, _)) => {
|
||||
let expr = Expr2::SmallInt {
|
||||
number: IntVal::I64(int),
|
||||
number: IntVal::I64(match int {
|
||||
IntValue::U128(_) => todo!(),
|
||||
IntValue::I128(n) => n as i64, // FIXME
|
||||
}),
|
||||
var: env.var_store.fresh(),
|
||||
// TODO non-hardcode
|
||||
style: IntStyle::Decimal,
|
||||
|
@ -85,6 +90,15 @@ pub fn expr_to_expr2<'a>(
|
|||
|
||||
(expr, Output::default())
|
||||
}
|
||||
Ok(ParsedNumResult::Float(float, _)) => {
|
||||
let expr = Expr2::Float {
|
||||
number: FloatVal::F64(float),
|
||||
var: env.var_store.fresh(),
|
||||
text: PoolStr::new(string, env.pool),
|
||||
};
|
||||
|
||||
(expr, Output::default())
|
||||
}
|
||||
Err((raw, error)) => {
|
||||
// emit runtime error
|
||||
let runtime_error = RuntimeError::InvalidInt(
|
||||
|
@ -107,9 +121,12 @@ pub fn expr_to_expr2<'a>(
|
|||
is_negative,
|
||||
} => {
|
||||
match finish_parsing_base(string, *base, *is_negative) {
|
||||
Ok(int) => {
|
||||
Ok((int, _bound)) => {
|
||||
let expr = Expr2::SmallInt {
|
||||
number: IntVal::I64(int),
|
||||
number: IntVal::I64(match int {
|
||||
IntValue::U128(_) => todo!(),
|
||||
IntValue::I128(n) => n as i64, // FIXME
|
||||
}),
|
||||
var: env.var_store.fresh(),
|
||||
// TODO non-hardcode
|
||||
style: IntStyle::from_base(*base),
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
#![allow(unused_imports)]
|
||||
|
||||
use bumpalo::collections::Vec as BumpVec;
|
||||
use roc_can::expr::unescape_char;
|
||||
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
|
||||
use roc_can::expr::{unescape_char, IntValue};
|
||||
use roc_can::num::{
|
||||
finish_parsing_base, finish_parsing_float, finish_parsing_num, ParsedNumResult,
|
||||
};
|
||||
use roc_collections::all::BumpMap;
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_parse::ast::{StrLiteral, StrSegment};
|
||||
|
@ -183,18 +185,35 @@ pub fn to_pattern2<'a>(
|
|||
let problem = MalformedPatternProblem::MalformedFloat;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(float) => Pattern2::FloatLiteral(FloatVal::F64(float)),
|
||||
Ok((float, _bound)) => Pattern2::FloatLiteral(FloatVal::F64(float)),
|
||||
},
|
||||
ptype => unsupported_pattern(env, ptype, region),
|
||||
},
|
||||
|
||||
NumLiteral(string) => match pattern_type {
|
||||
WhenBranch => match finish_parsing_int(string) {
|
||||
WhenBranch => match finish_parsing_num(string) {
|
||||
Err(_error) => {
|
||||
let problem = MalformedPatternProblem::MalformedInt;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(int) => Pattern2::NumLiteral(env.var_store.fresh(), int),
|
||||
Ok(ParsedNumResult::UnknownNum(int)) => {
|
||||
Pattern2::NumLiteral(
|
||||
env.var_store.fresh(),
|
||||
match int {
|
||||
IntValue::U128(_) => todo!(),
|
||||
IntValue::I128(n) => n as i64, // FIXME
|
||||
},
|
||||
)
|
||||
}
|
||||
Ok(ParsedNumResult::Int(int, _bound)) => {
|
||||
Pattern2::IntLiteral(IntVal::I64(match int {
|
||||
IntValue::U128(_) => todo!(),
|
||||
IntValue::I128(n) => n as i64, // FIXME
|
||||
}))
|
||||
}
|
||||
Ok(ParsedNumResult::Float(int, _bound)) => {
|
||||
Pattern2::FloatLiteral(FloatVal::F64(int))
|
||||
}
|
||||
},
|
||||
ptype => unsupported_pattern(env, ptype, region),
|
||||
},
|
||||
|
@ -209,7 +228,11 @@ pub fn to_pattern2<'a>(
|
|||
let problem = MalformedPatternProblem::MalformedBase(*base);
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(int) => {
|
||||
Ok((int, _bound)) => {
|
||||
let int = match int {
|
||||
IntValue::U128(_) => todo!(),
|
||||
IntValue::I128(n) => n as i64, // FIXME
|
||||
};
|
||||
if *is_negative {
|
||||
Pattern2::IntLiteral(IntVal::I64(-int))
|
||||
} else {
|
||||
|
|
|
@ -12,8 +12,8 @@ use roc_parse::ast::{
|
|||
TypeAnnotation, WhenBranch,
|
||||
};
|
||||
use roc_parse::header::{
|
||||
AppHeader, Effects, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName,
|
||||
PackageEntry, PackageName, PlatformHeader, PlatformRequires, To, TypedIdent,
|
||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
|
||||
PackageName, PlatformHeader, PlatformRequires, To, TypedIdent,
|
||||
};
|
||||
use roc_parse::{
|
||||
ast::{Def, Module},
|
||||
|
@ -199,14 +199,6 @@ impl<'a> RemoveSpaces<'a> for Module<'a> {
|
|||
packages: header.packages.remove_spaces(arena),
|
||||
imports: header.imports.remove_spaces(arena),
|
||||
provides: header.provides.remove_spaces(arena),
|
||||
effects: Effects {
|
||||
spaces_before_effects_keyword: &[],
|
||||
spaces_after_effects_keyword: &[],
|
||||
spaces_after_type_name: &[],
|
||||
effect_shortname: header.effects.effect_shortname.remove_spaces(arena),
|
||||
effect_type_name: header.effects.effect_type_name.remove_spaces(arena),
|
||||
entries: header.effects.entries.remove_spaces(arena),
|
||||
},
|
||||
before_header: &[],
|
||||
after_platform_keyword: &[],
|
||||
before_requires: &[],
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "examples/multi-module"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "examples/multi-dep-thunk"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
79
cli_utils/Cargo.lock
generated
79
cli_utils/Cargo.lock
generated
|
@ -2475,12 +2475,13 @@ dependencies = [
|
|||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_parse",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"snafu",
|
||||
|
@ -2510,6 +2511,7 @@ dependencies = [
|
|||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"serde_json",
|
||||
|
@ -2524,6 +2526,7 @@ dependencies = [
|
|||
"roc_collections",
|
||||
"roc_module",
|
||||
"roc_region",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
]
|
||||
|
||||
|
@ -2549,31 +2552,24 @@ dependencies = [
|
|||
"bumpalo",
|
||||
"clap 3.0.0-beta.5",
|
||||
"const_format",
|
||||
"inkwell 0.1.0",
|
||||
"libloading 0.7.1",
|
||||
"mimalloc",
|
||||
"roc_build",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_constrain",
|
||||
"roc_docs",
|
||||
"roc_editor",
|
||||
"roc_error_macros",
|
||||
"roc_fmt",
|
||||
"roc_gen_llvm",
|
||||
"roc_linker",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_parse",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_repl_cli",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"rustyline",
|
||||
"rustyline-derive",
|
||||
"roc_target",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
]
|
||||
|
@ -2631,6 +2627,7 @@ dependencies = [
|
|||
"roc_module",
|
||||
"roc_parse",
|
||||
"roc_region",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"snafu",
|
||||
]
|
||||
|
@ -2680,6 +2677,10 @@ dependencies = [
|
|||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_error_macros"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "roc_fmt"
|
||||
version = "0.1.0"
|
||||
|
@ -2700,12 +2701,13 @@ dependencies = [
|
|||
"packed_struct",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"target-lexicon",
|
||||
|
@ -2720,10 +2722,11 @@ dependencies = [
|
|||
"morphic_lib",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_reporting",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
|
@ -2734,10 +2737,11 @@ dependencies = [
|
|||
"bumpalo",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_reporting",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2782,6 +2786,7 @@ dependencies = [
|
|||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"ven_pretty",
|
||||
|
@ -2815,6 +2820,7 @@ dependencies = [
|
|||
"roc_region",
|
||||
"roc_solve",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"static_assertions",
|
||||
|
@ -2850,6 +2856,44 @@ dependencies = [
|
|||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_repl_cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"const_format",
|
||||
"roc_mono",
|
||||
"roc_parse",
|
||||
"roc_repl_eval",
|
||||
"rustyline",
|
||||
"rustyline-derive",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_repl_eval"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"inkwell 0.1.0",
|
||||
"libloading 0.7.1",
|
||||
"roc_build",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_fmt",
|
||||
"roc_gen_llvm",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_parse",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_reporting"
|
||||
version = "0.1.0"
|
||||
|
@ -2886,6 +2930,13 @@ dependencies = [
|
|||
name = "roc_std"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "roc_target"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_types"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -26,7 +26,7 @@ pub fn build(b: *Builder) void {
|
|||
.default_target = CrossTarget{
|
||||
.cpu_model = .baseline,
|
||||
// TODO allow for native target for maximum speed
|
||||
}
|
||||
},
|
||||
});
|
||||
const i386_target = makeI386Target();
|
||||
const wasm32_target = makeWasm32Target();
|
||||
|
|
|
@ -750,7 +750,8 @@ pub fn dictWalk(
|
|||
const alignment_u32 = alignment.toU32();
|
||||
// allocate space to write the result of the stepper into
|
||||
// experimentally aliasing the accum and output pointers is not a good idea
|
||||
const bytes_ptr: [*]u8 = utils.alloc(accum_width, alignment_u32);
|
||||
// TODO handle alloc failing!
|
||||
const bytes_ptr: [*]u8 = utils.alloc(accum_width, alignment_u32) orelse unreachable;
|
||||
var b1 = output orelse unreachable;
|
||||
var b2 = bytes_ptr;
|
||||
|
||||
|
|
162
compiler/builtins/bitcode/src/expect.zig
Normal file
162
compiler/builtins/bitcode/src/expect.zig
Normal file
|
@ -0,0 +1,162 @@
|
|||
const std = @import("std");
|
||||
const utils = @import("utils.zig");
|
||||
const CSlice = utils.CSlice;
|
||||
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
||||
|
||||
const Failure = struct {
|
||||
start_line: u32,
|
||||
end_line: u32,
|
||||
start_col: u16,
|
||||
end_col: u16,
|
||||
};
|
||||
|
||||
// BEGIN FAILURES GLOBALS ///////////////////
|
||||
var failures_mutex = std.Thread.Mutex{};
|
||||
var failures: [*]Failure = undefined;
|
||||
var failure_length: usize = 0;
|
||||
var failure_capacity: usize = 0;
|
||||
// END FAILURES GLOBALS /////////////////////
|
||||
|
||||
pub fn expectFailed(
|
||||
start_line: u32,
|
||||
end_line: u32,
|
||||
start_col: u16,
|
||||
end_col: u16,
|
||||
) void {
|
||||
const new_failure = Failure{ .start_line = start_line, .end_line = end_line, .start_col = start_col, .end_col = end_col };
|
||||
|
||||
// Lock the failures mutex before reading from any of the failures globals,
|
||||
// and then release the lock once we're done modifying things.
|
||||
|
||||
// TODO FOR ZIG 0.9: this API changed in https://github.com/ziglang/zig/commit/008b0ec5e58fc7e31f3b989868a7d1ea4df3f41d
|
||||
// to this: https://github.com/ziglang/zig/blob/c710d5eefe3f83226f1651947239730e77af43cb/lib/std/Thread/Mutex.zig
|
||||
//
|
||||
// ...so just use these two lines of code instead of the non-commented-out ones to make this work in Zig 0.9:
|
||||
//
|
||||
// failures_mutex.lock();
|
||||
// defer failures_mutex.release();
|
||||
//
|
||||
// 👆 👆 👆 IF UPGRADING TO ZIG 0.9, LOOK HERE! 👆 👆 👆
|
||||
const held = failures_mutex.acquire();
|
||||
defer held.release();
|
||||
|
||||
// If we don't have enough capacity to add a failure, allocate a new failures pointer.
|
||||
if (failure_length >= failure_capacity) {
|
||||
if (failure_capacity > 0) {
|
||||
// We already had previous failures allocated, so try to realloc in order
|
||||
// to grow the size in-place without having to memcpy bytes over.
|
||||
const old_pointer = failures;
|
||||
const old_bytes = failure_capacity * @sizeOf(Failure);
|
||||
|
||||
failure_capacity *= 2;
|
||||
|
||||
const new_bytes = failure_capacity * @sizeOf(Failure);
|
||||
const failures_u8 = @ptrCast([*]u8, @alignCast(@alignOf(Failure), failures));
|
||||
const raw_pointer = utils.realloc(failures_u8, new_bytes, old_bytes, @alignOf(Failure));
|
||||
|
||||
failures = @ptrCast([*]Failure, @alignCast(@alignOf(Failure), raw_pointer));
|
||||
|
||||
// If realloc wasn't able to expand in-place (that is, it returned a different pointer),
|
||||
// then copy the data into the new pointer and dealloc the old one.
|
||||
if (failures != old_pointer) {
|
||||
const old_pointer_u8 = @ptrCast([*]u8, old_pointer);
|
||||
utils.memcpy(@ptrCast([*]u8, failures), old_pointer_u8, old_bytes);
|
||||
utils.dealloc(old_pointer_u8, @alignOf(Failure));
|
||||
}
|
||||
} else {
|
||||
// We've never had any failures before, so allocate the failures for the first time.
|
||||
failure_capacity = 10;
|
||||
|
||||
const raw_pointer = utils.alloc(failure_capacity * @sizeOf(Failure), @alignOf(Failure));
|
||||
|
||||
failures = @ptrCast([*]Failure, @alignCast(@alignOf(Failure), raw_pointer));
|
||||
}
|
||||
}
|
||||
|
||||
failures[failure_length] = new_failure;
|
||||
failure_length += 1;
|
||||
}
|
||||
|
||||
pub fn expectFailedC(
|
||||
start_line: u32,
|
||||
end_line: u32,
|
||||
start_col: u16,
|
||||
end_col: u16,
|
||||
) callconv(.C) void {
|
||||
return @call(.{ .modifier = always_inline }, expectFailed, .{ start_line, end_line, start_col, end_col });
|
||||
}
|
||||
|
||||
pub fn getExpectFailures() []Failure {
|
||||
// TODO FOR ZIG 0.9: this API changed in https://github.com/ziglang/zig/commit/008b0ec5e58fc7e31f3b989868a7d1ea4df3f41d
|
||||
// to this: https://github.com/ziglang/zig/blob/c710d5eefe3f83226f1651947239730e77af43cb/lib/std/Thread/Mutex.zig
|
||||
//
|
||||
// ...so just use these two lines of code instead of the non-commented-out ones to make this work in Zig 0.9:
|
||||
//
|
||||
// failures_mutex.lock();
|
||||
// defer failures_mutex.release();
|
||||
//
|
||||
// 👆 👆 👆 IF UPGRADING TO ZIG 0.9, LOOK HERE! 👆 👆 👆
|
||||
const held = failures_mutex.acquire();
|
||||
defer held.release();
|
||||
|
||||
if (failure_length > 0) {
|
||||
// defensively clone failures, in case someone modifies the originals after the mutex has been released.
|
||||
const num_bytes = failure_length * @sizeOf(Failure);
|
||||
// TODO handle the possibility of alloc failing
|
||||
const raw_clones = utils.alloc(num_bytes, @alignOf(Failure)) orelse unreachable;
|
||||
|
||||
utils.memcpy(raw_clones, @ptrCast([*]u8, failures), num_bytes);
|
||||
|
||||
const clones = @ptrCast([*]Failure, @alignCast(@alignOf(Failure), raw_clones));
|
||||
|
||||
return clones[0..failure_length];
|
||||
} else {
|
||||
return failures[0..0];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getExpectFailuresC() callconv(.C) CSlice {
|
||||
var bytes = @ptrCast(*c_void, failures);
|
||||
|
||||
return .{ .pointer = bytes, .len = failure_length };
|
||||
}
|
||||
|
||||
pub fn deinitFailures() void {
|
||||
// TODO FOR ZIG 0.9: this API changed in https://github.com/ziglang/zig/commit/008b0ec5e58fc7e31f3b989868a7d1ea4df3f41d
|
||||
// to this: https://github.com/ziglang/zig/blob/c710d5eefe3f83226f1651947239730e77af43cb/lib/std/Thread/Mutex.zig
|
||||
//
|
||||
// ...so just use these two lines of code instead of the non-commented-out ones to make this work in Zig 0.9:
|
||||
//
|
||||
// failures_mutex.lock();
|
||||
// defer failures_mutex.release();
|
||||
//
|
||||
// 👆 👆 👆 IF UPGRADING TO ZIG 0.9, LOOK HERE! 👆 👆 👆
|
||||
const held = failures_mutex.acquire();
|
||||
defer held.release();
|
||||
|
||||
utils.dealloc(@ptrCast([*]u8, failures), @alignOf(Failure));
|
||||
failure_length = 0;
|
||||
}
|
||||
|
||||
pub fn deinitFailuresC() callconv(.C) void {
|
||||
return @call(.{ .modifier = always_inline }, deinitFailures, .{});
|
||||
}
|
||||
|
||||
test "expectFailure does something" {
|
||||
defer deinitFailures();
|
||||
|
||||
var fails = getExpectFailures();
|
||||
try std.testing.expectEqual(fails.len, 0);
|
||||
|
||||
expectFailed(1, 2, 3, 4);
|
||||
|
||||
fails = getExpectFailures();
|
||||
try std.testing.expectEqual(fails.len, 1);
|
||||
utils.dealloc(@ptrCast([*]u8, fails.ptr), @alignOf([*]Failure));
|
||||
|
||||
const what_it_should_look_like = Failure{ .start_line = 1, .end_line = 2, .start_col = 3, .end_col = 4 };
|
||||
|
||||
fails = getExpectFailures();
|
||||
try std.testing.expectEqual(fails[0], what_it_should_look_like);
|
||||
utils.dealloc(@ptrCast([*]u8, fails.ptr), @alignOf([*]Failure));
|
||||
}
|
|
@ -550,7 +550,8 @@ pub fn listKeepResult(
|
|||
var output = RocList.allocate(alignment, list.len(), list.len() * after_width);
|
||||
const target_ptr = output.bytes orelse unreachable;
|
||||
|
||||
var temporary = @ptrCast([*]u8, utils.alloc(result_width, alignment));
|
||||
// TODO handle alloc failing!
|
||||
var temporary = utils.alloc(result_width, alignment) orelse unreachable;
|
||||
|
||||
if (data_is_owned) {
|
||||
inc_n_data(data, size);
|
||||
|
@ -614,7 +615,8 @@ pub fn listWalk(
|
|||
inc_n_data(data, list.len());
|
||||
}
|
||||
|
||||
const bytes_ptr: [*]u8 = utils.alloc(accum_width, alignment);
|
||||
// TODO handle alloc failing!
|
||||
const bytes_ptr: [*]u8 = utils.alloc(accum_width, alignment) orelse unreachable;
|
||||
var b1 = output orelse unreachable;
|
||||
var b2 = bytes_ptr;
|
||||
|
||||
|
@ -660,7 +662,8 @@ pub fn listWalkBackwards(
|
|||
inc_n_data(data, list.len());
|
||||
}
|
||||
|
||||
const bytes_ptr: [*]u8 = utils.alloc(accum_width, alignment);
|
||||
// TODO handle alloc failing!
|
||||
const bytes_ptr: [*]u8 = utils.alloc(accum_width, alignment) orelse unreachable;
|
||||
var b1 = output orelse unreachable;
|
||||
var b2 = bytes_ptr;
|
||||
|
||||
|
@ -708,7 +711,8 @@ pub fn listWalkUntil(
|
|||
return;
|
||||
}
|
||||
|
||||
const bytes_ptr: [*]u8 = utils.alloc(continue_stop_width, alignment);
|
||||
// TODO handle alloc failing!
|
||||
const bytes_ptr: [*]u8 = utils.alloc(continue_stop_width, alignment) orelse unreachable;
|
||||
|
||||
// NOTE: assumes data bytes are the first bytes in a tag
|
||||
@memcpy(bytes_ptr, accum orelse unreachable, accum_width);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const std = @import("std");
|
||||
const math = std.math;
|
||||
const utils = @import("utils.zig");
|
||||
const expect = @import("expect.zig");
|
||||
|
||||
const ROC_BUILTINS = "roc_builtins";
|
||||
const NUM = "num";
|
||||
|
@ -141,12 +142,14 @@ comptime {
|
|||
}
|
||||
|
||||
// Utils
|
||||
|
||||
comptime {
|
||||
exportUtilsFn(utils.test_panic, "test_panic");
|
||||
exportUtilsFn(utils.increfC, "incref");
|
||||
exportUtilsFn(utils.decrefC, "decref");
|
||||
exportUtilsFn(utils.decrefCheckNullC, "decref_check_null");
|
||||
exportExpectFn(expect.expectFailedC, "expect_failed");
|
||||
exportExpectFn(expect.getExpectFailuresC, "get_expect_failures");
|
||||
exportExpectFn(expect.deinitFailuresC, "deinit_failures");
|
||||
|
||||
@export(utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
|
||||
}
|
||||
|
@ -175,6 +178,10 @@ fn exportUtilsFn(comptime func: anytype, comptime func_name: []const u8) void {
|
|||
exportBuiltinFn(func, "utils." ++ func_name);
|
||||
}
|
||||
|
||||
fn exportExpectFn(comptime func: anytype, comptime func_name: []const u8) void {
|
||||
exportBuiltinFn(func, "expect." ++ func_name);
|
||||
}
|
||||
|
||||
// Custom panic function, as builtin Zig version errors during LLVM verification
|
||||
pub fn panic(message: []const u8, stacktrace: ?*std.builtin.StackTrace) noreturn {
|
||||
const builtin = @import("builtin");
|
||||
|
|
|
@ -18,14 +18,18 @@ extern fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void;
|
|||
// Signals to the host that the program has panicked
|
||||
extern fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void;
|
||||
|
||||
// should work just like libc memcpy (we can't assume libc is present)
|
||||
extern fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
|
||||
|
||||
comptime {
|
||||
const builtin = @import("builtin");
|
||||
// During tetsts, use the testing allocators to satisfy these functions.
|
||||
// During tests, use the testing allocators to satisfy these functions.
|
||||
if (builtin.is_test) {
|
||||
@export(testing_roc_alloc, .{ .name = "roc_alloc", .linkage = .Strong });
|
||||
@export(testing_roc_realloc, .{ .name = "roc_realloc", .linkage = .Strong });
|
||||
@export(testing_roc_dealloc, .{ .name = "roc_dealloc", .linkage = .Strong });
|
||||
@export(testing_roc_panic, .{ .name = "roc_panic", .linkage = .Strong });
|
||||
@export(testing_roc_memcpy, .{ .name = "roc_memcpy", .linkage = .Strong });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,8 +57,16 @@ fn testing_roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
|||
@panic("Roc panicked");
|
||||
}
|
||||
|
||||
pub fn alloc(size: usize, alignment: u32) [*]u8 {
|
||||
return @ptrCast([*]u8, @call(.{ .modifier = always_inline }, roc_alloc, .{ size, alignment }));
|
||||
fn testing_roc_memcpy(dest: *c_void, src: *c_void, bytes: usize) callconv(.C) ?*c_void {
|
||||
const zig_dest = @ptrCast([*]u8, dest);
|
||||
const zig_src = @ptrCast([*]u8, src);
|
||||
|
||||
@memcpy(zig_dest, zig_src, bytes);
|
||||
return dest;
|
||||
}
|
||||
|
||||
pub fn alloc(size: usize, alignment: u32) ?[*]u8 {
|
||||
return @ptrCast(?[*]u8, @call(.{ .modifier = always_inline }, roc_alloc, .{ size, alignment }));
|
||||
}
|
||||
|
||||
pub fn realloc(c_ptr: [*]u8, new_size: usize, old_size: usize, alignment: u32) [*]u8 {
|
||||
|
@ -70,6 +82,10 @@ pub fn panic(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
|||
return @call(.{ .modifier = always_inline }, roc_panic, .{ c_ptr, alignment });
|
||||
}
|
||||
|
||||
pub fn memcpy(dst: [*]u8, src: [*]u8, size: usize) void {
|
||||
@call(.{ .modifier = always_inline }, roc_memcpy, .{ dst, src, size });
|
||||
}
|
||||
|
||||
// indirection because otherwise zig creates an alias to the panic function which our LLVM code
|
||||
// does not know how to deal with
|
||||
pub fn test_panic(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||
|
@ -173,7 +189,8 @@ pub fn allocateWithRefcount(
|
|||
|
||||
switch (alignment) {
|
||||
16 => {
|
||||
var new_bytes: [*]align(16) u8 = @alignCast(16, alloc(length, alignment));
|
||||
// TODO handle alloc failing!
|
||||
var new_bytes: [*]align(16) u8 = @alignCast(16, alloc(length, alignment) orelse unreachable);
|
||||
|
||||
var as_usize_array = @ptrCast([*]usize, new_bytes);
|
||||
as_usize_array[0] = 0;
|
||||
|
@ -185,7 +202,8 @@ pub fn allocateWithRefcount(
|
|||
return first_slot;
|
||||
},
|
||||
8 => {
|
||||
var raw = alloc(length, alignment);
|
||||
// TODO handle alloc failing!
|
||||
var raw = alloc(length, alignment) orelse unreachable;
|
||||
var new_bytes: [*]align(8) u8 = @alignCast(8, raw);
|
||||
|
||||
var as_isize_array = @ptrCast([*]isize, new_bytes);
|
||||
|
@ -197,7 +215,8 @@ pub fn allocateWithRefcount(
|
|||
return first_slot;
|
||||
},
|
||||
4 => {
|
||||
var raw = alloc(length, alignment);
|
||||
// TODO handle alloc failing!
|
||||
var raw = alloc(length, alignment) orelse unreachable;
|
||||
var new_bytes: [*]align(@alignOf(isize)) u8 = @alignCast(@alignOf(isize), raw);
|
||||
|
||||
var as_isize_array = @ptrCast([*]isize, new_bytes);
|
||||
|
@ -217,6 +236,11 @@ pub fn allocateWithRefcount(
|
|||
}
|
||||
}
|
||||
|
||||
pub const CSlice = extern struct {
|
||||
pointer: *c_void,
|
||||
len: usize,
|
||||
};
|
||||
|
||||
pub fn unsafeReallocate(
|
||||
source_ptr: [*]u8,
|
||||
alignment: u32,
|
||||
|
|
|
@ -336,3 +336,6 @@ pub const UTILS_TEST_PANIC: &str = "roc_builtins.utils.test_panic";
|
|||
pub const UTILS_INCREF: &str = "roc_builtins.utils.incref";
|
||||
pub const UTILS_DECREF: &str = "roc_builtins.utils.decref";
|
||||
pub const UTILS_DECREF_CHECK_NULL: &str = "roc_builtins.utils.decref_check_null";
|
||||
pub const UTILS_EXPECT_FAILED: &str = "roc_builtins.expect.expect_failed";
|
||||
pub const UTILS_GET_EXPECT_FAILURES: &str = "roc_builtins.expect.get_expect_failures";
|
||||
pub const UTILS_DEINIT_FAILURES: &str = "roc_builtins.expect.deinit_failures";
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::def::Def;
|
||||
use crate::expr::{self, ClosureData, Expr::*};
|
||||
use crate::expr::{self, ClosureData, Expr::*, IntValue};
|
||||
use crate::expr::{Expr, Field, Recursive};
|
||||
use crate::num::{FloatWidth, IntWidth, NumWidth, NumericBound};
|
||||
use crate::pattern::Pattern;
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_module::called_via::CalledVia;
|
||||
|
@ -793,7 +794,7 @@ fn num_is_zero(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::Eq,
|
||||
args: vec![
|
||||
(arg_var, Var(Symbol::ARG_1)),
|
||||
(arg_var, num(unbound_zero_var, 0)),
|
||||
(arg_var, num(unbound_zero_var, 0, num_no_bound())),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
};
|
||||
|
@ -816,7 +817,7 @@ fn num_is_negative(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let body = RunLowLevel {
|
||||
op: LowLevel::NumGt,
|
||||
args: vec![
|
||||
(arg_var, num(unbound_zero_var, 0)),
|
||||
(arg_var, num(unbound_zero_var, 0, num_no_bound())),
|
||||
(arg_var, Var(Symbol::ARG_1)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
|
@ -841,7 +842,7 @@ fn num_is_positive(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::NumGt,
|
||||
args: vec![
|
||||
(arg_var, Var(Symbol::ARG_1)),
|
||||
(arg_var, num(unbound_zero_var, 0)),
|
||||
(arg_var, num(unbound_zero_var, 0, num_no_bound())),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
};
|
||||
|
@ -866,7 +867,7 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
args: vec![
|
||||
(
|
||||
arg_var,
|
||||
int::<i128>(var_store.fresh(), var_store.fresh(), 1),
|
||||
int::<i128>(var_store.fresh(), var_store.fresh(), 1, num_no_bound()),
|
||||
),
|
||||
(
|
||||
arg_var,
|
||||
|
@ -874,7 +875,7 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::NumRemUnchecked,
|
||||
args: vec![
|
||||
(arg_var, Var(Symbol::ARG_1)),
|
||||
(arg_var, num(unbound_two_var, 2)),
|
||||
(arg_var, num(unbound_two_var, 2, num_no_bound())),
|
||||
],
|
||||
ret_var: arg_var,
|
||||
},
|
||||
|
@ -901,14 +902,14 @@ fn num_is_even(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let body = RunLowLevel {
|
||||
op: LowLevel::Eq,
|
||||
args: vec![
|
||||
(arg_var, num(arg_num_var, 0)),
|
||||
(arg_var, num(arg_num_var, 0, num_no_bound())),
|
||||
(
|
||||
arg_var,
|
||||
RunLowLevel {
|
||||
op: LowLevel::NumRemUnchecked,
|
||||
args: vec![
|
||||
(arg_var, Var(Symbol::ARG_1)),
|
||||
(arg_var, num(arg_num_var, 2)),
|
||||
(arg_var, num(arg_num_var, 2, num_no_bound())),
|
||||
],
|
||||
ret_var: arg_var,
|
||||
},
|
||||
|
@ -962,7 +963,10 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::NumGte,
|
||||
args: vec![
|
||||
(float_var, Var(Symbol::ARG_1)),
|
||||
(float_var, float(unbound_zero_var, precision_var, 0.0)),
|
||||
(
|
||||
float_var,
|
||||
float(unbound_zero_var, precision_var, 0.0, num_no_bound()),
|
||||
),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
}),
|
||||
|
@ -1008,7 +1012,10 @@ fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::NumGt,
|
||||
args: vec![
|
||||
(float_var, Var(Symbol::ARG_1)),
|
||||
(float_var, float(unbound_zero_var, precision_var, 0.0)),
|
||||
(
|
||||
float_var,
|
||||
float(unbound_zero_var, precision_var, 0.0, num_no_bound()),
|
||||
),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
}),
|
||||
|
@ -1246,92 +1253,182 @@ fn num_int_cast(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
|
||||
/// Num.minI8: I8
|
||||
fn num_min_i8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i8>(symbol, var_store, i8::MIN)
|
||||
int_min_or_max::<i8>(
|
||||
symbol,
|
||||
var_store,
|
||||
i8::MIN,
|
||||
NumericBound::Exact(IntWidth::I8),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.maxI8: I8
|
||||
fn num_max_i8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i8>(symbol, var_store, i8::MAX)
|
||||
int_min_or_max::<i8>(
|
||||
symbol,
|
||||
var_store,
|
||||
i8::MAX,
|
||||
NumericBound::Exact(IntWidth::I8),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.minU8: U8
|
||||
fn num_min_u8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u8>(symbol, var_store, u8::MIN)
|
||||
int_min_or_max::<u8>(
|
||||
symbol,
|
||||
var_store,
|
||||
u8::MIN,
|
||||
NumericBound::Exact(IntWidth::U8),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.maxU8: U8
|
||||
fn num_max_u8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u8>(symbol, var_store, u8::MAX)
|
||||
int_min_or_max::<u8>(
|
||||
symbol,
|
||||
var_store,
|
||||
u8::MAX,
|
||||
NumericBound::Exact(IntWidth::U8),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.minI16: I16
|
||||
fn num_min_i16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i16>(symbol, var_store, i16::MIN)
|
||||
int_min_or_max::<i16>(
|
||||
symbol,
|
||||
var_store,
|
||||
i16::MIN,
|
||||
NumericBound::Exact(IntWidth::I16),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.maxI16: I16
|
||||
fn num_max_i16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i16>(symbol, var_store, i16::MAX)
|
||||
int_min_or_max::<i16>(
|
||||
symbol,
|
||||
var_store,
|
||||
i16::MAX,
|
||||
NumericBound::Exact(IntWidth::I16),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.minU16: U16
|
||||
fn num_min_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u16>(symbol, var_store, u16::MIN)
|
||||
int_min_or_max::<u16>(
|
||||
symbol,
|
||||
var_store,
|
||||
u16::MIN,
|
||||
NumericBound::Exact(IntWidth::U16),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.maxU16: U16
|
||||
fn num_max_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u16>(symbol, var_store, u16::MAX)
|
||||
int_min_or_max::<u16>(
|
||||
symbol,
|
||||
var_store,
|
||||
u16::MAX,
|
||||
NumericBound::Exact(IntWidth::U16),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.minI32: I32
|
||||
fn num_min_i32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i32>(symbol, var_store, i32::MIN)
|
||||
int_min_or_max::<i32>(
|
||||
symbol,
|
||||
var_store,
|
||||
i32::MIN,
|
||||
NumericBound::Exact(IntWidth::I32),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.maxI32: I32
|
||||
fn num_max_i32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i32>(symbol, var_store, i32::MAX)
|
||||
int_min_or_max::<i32>(
|
||||
symbol,
|
||||
var_store,
|
||||
i32::MAX,
|
||||
NumericBound::Exact(IntWidth::I32),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.minU32: U32
|
||||
fn num_min_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u32>(symbol, var_store, u32::MIN)
|
||||
int_min_or_max::<u32>(
|
||||
symbol,
|
||||
var_store,
|
||||
u32::MIN,
|
||||
NumericBound::Exact(IntWidth::U32),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.maxU32: U32
|
||||
fn num_max_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u32>(symbol, var_store, u32::MAX)
|
||||
int_min_or_max::<u32>(
|
||||
symbol,
|
||||
var_store,
|
||||
u32::MAX,
|
||||
NumericBound::Exact(IntWidth::U32),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.minI64: I64
|
||||
fn num_min_i64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i64>(symbol, var_store, i64::MIN)
|
||||
int_min_or_max::<i64>(
|
||||
symbol,
|
||||
var_store,
|
||||
i64::MIN,
|
||||
NumericBound::Exact(IntWidth::I64),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.maxI64: I64
|
||||
fn num_max_i64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i64>(symbol, var_store, i64::MAX)
|
||||
int_min_or_max::<i64>(
|
||||
symbol,
|
||||
var_store,
|
||||
i64::MAX,
|
||||
NumericBound::Exact(IntWidth::I64),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.minU64: U64
|
||||
fn num_min_u64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u64>(symbol, var_store, u64::MIN)
|
||||
int_min_or_max::<u64>(
|
||||
symbol,
|
||||
var_store,
|
||||
u64::MIN,
|
||||
NumericBound::Exact(IntWidth::U64),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.maxU64: U64
|
||||
fn num_max_u64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<u64>(symbol, var_store, u64::MAX)
|
||||
int_min_or_max::<u64>(
|
||||
symbol,
|
||||
var_store,
|
||||
u64::MAX,
|
||||
NumericBound::Exact(IntWidth::U64),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.minI128: I128
|
||||
fn num_min_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i128>(symbol, var_store, i128::MIN)
|
||||
int_min_or_max::<i128>(
|
||||
symbol,
|
||||
var_store,
|
||||
i128::MIN,
|
||||
NumericBound::Exact(IntWidth::I128),
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.maxI128: I128
|
||||
fn num_max_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
int_min_or_max::<i128>(symbol, var_store, i128::MAX)
|
||||
int_min_or_max::<i128>(
|
||||
symbol,
|
||||
var_store,
|
||||
i128::MAX,
|
||||
NumericBound::Exact(IntWidth::I128),
|
||||
)
|
||||
}
|
||||
|
||||
/// List.isEmpty : List * -> Bool
|
||||
|
@ -1344,7 +1441,7 @@ fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let body = RunLowLevel {
|
||||
op: LowLevel::Eq,
|
||||
args: vec![
|
||||
(len_var, num(unbound_zero_var, 0)),
|
||||
(len_var, num(unbound_zero_var, 0, num_no_bound())),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
|
@ -1458,7 +1555,12 @@ fn str_to_num(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
),
|
||||
(
|
||||
errorcode_var,
|
||||
int::<i128>(errorcode_var, Variable::UNSIGNED8, 0),
|
||||
int::<i128>(
|
||||
errorcode_var,
|
||||
Variable::UNSIGNED8,
|
||||
0,
|
||||
NumericBound::Exact(IntWidth::U8),
|
||||
),
|
||||
),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
|
@ -2201,7 +2303,12 @@ fn list_swap(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
fn list_take_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let list_var = var_store.fresh();
|
||||
let len_var = var_store.fresh();
|
||||
let zero = int::<i128>(len_var, Variable::NATURAL, 0);
|
||||
let zero = int::<i128>(
|
||||
len_var,
|
||||
Variable::NATURAL,
|
||||
0,
|
||||
NumericBound::Exact(IntWidth::Nat),
|
||||
);
|
||||
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::ListSublist,
|
||||
|
@ -2227,7 +2334,12 @@ fn list_take_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let list_var = var_store.fresh();
|
||||
let len_var = var_store.fresh();
|
||||
|
||||
let zero = int::<i128>(len_var, Variable::NATURAL, 0);
|
||||
let zero = int::<i128>(
|
||||
len_var,
|
||||
Variable::NATURAL,
|
||||
0,
|
||||
NumericBound::Exact(IntWidth::Nat),
|
||||
);
|
||||
let bool_var = var_store.fresh();
|
||||
|
||||
let get_list_len = RunLowLevel {
|
||||
|
@ -2337,7 +2449,12 @@ fn list_intersperse(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let clos_elem_sym = Symbol::ARG_4;
|
||||
|
||||
let int_var = var_store.fresh();
|
||||
let zero = int::<i128>(int_var, Variable::NATURAL, 0);
|
||||
let zero = int::<i128>(
|
||||
int_var,
|
||||
Variable::NATURAL,
|
||||
0,
|
||||
NumericBound::Exact(IntWidth::Nat),
|
||||
);
|
||||
|
||||
// \acc, elem -> acc |> List.append sep |> List.append elem
|
||||
let clos = Closure(ClosureData {
|
||||
|
@ -2417,7 +2534,12 @@ fn list_split(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let clos_ret_var = var_store.fresh();
|
||||
|
||||
let ret_var = var_store.fresh();
|
||||
let zero = int::<i128>(index_var, Variable::NATURAL, 0);
|
||||
let zero = int::<i128>(
|
||||
index_var,
|
||||
Variable::NATURAL,
|
||||
0,
|
||||
NumericBound::Exact(IntWidth::Nat),
|
||||
);
|
||||
|
||||
let clos = Closure(ClosureData {
|
||||
function_type: clos_fun_var,
|
||||
|
@ -2579,7 +2701,10 @@ fn list_drop_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::ListDropAt,
|
||||
args: vec![
|
||||
(list_var, Var(Symbol::ARG_1)),
|
||||
(index_var, int::<i128>(num_var, num_precision_var, 0)),
|
||||
(
|
||||
index_var,
|
||||
int::<i128>(num_var, num_precision_var, 0, num_no_bound()),
|
||||
),
|
||||
],
|
||||
ret_var: list_var,
|
||||
};
|
||||
|
@ -2676,7 +2801,10 @@ fn list_drop_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
ret_var: len_var,
|
||||
},
|
||||
),
|
||||
(arg_var, int::<i128>(num_var, num_precision_var, 1)),
|
||||
(
|
||||
arg_var,
|
||||
int::<i128>(num_var, num_precision_var, 1, num_no_bound()),
|
||||
),
|
||||
],
|
||||
ret_var: len_var,
|
||||
},
|
||||
|
@ -2874,7 +3002,10 @@ fn list_min(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
RunLowLevel {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(len_var, int::<i128>(num_var, num_precision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
int::<i128>(num_var, num_precision_var, 0, num_no_bound()),
|
||||
),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
|
@ -2905,7 +3036,15 @@ fn list_min(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::ListGetUnsafe,
|
||||
args: vec![
|
||||
(list_var, Var(Symbol::ARG_1)),
|
||||
(arg_var, int::<i128>(num_var, num_precision_var, 0)),
|
||||
(
|
||||
arg_var,
|
||||
int::<i128>(
|
||||
num_var,
|
||||
num_precision_var,
|
||||
0,
|
||||
num_no_bound(),
|
||||
),
|
||||
),
|
||||
],
|
||||
ret_var: list_elem_var,
|
||||
},
|
||||
|
@ -3004,7 +3143,10 @@ fn list_max(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
RunLowLevel {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(len_var, int::<i128>(num_var, num_precision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
int::<i128>(num_var, num_precision_var, 0, num_no_bound()),
|
||||
),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
|
@ -3035,7 +3177,15 @@ fn list_max(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::ListGetUnsafe,
|
||||
args: vec![
|
||||
(list_var, Var(Symbol::ARG_1)),
|
||||
(arg_var, int::<i128>(num_var, num_precision_var, 0)),
|
||||
(
|
||||
arg_var,
|
||||
int::<i128>(
|
||||
num_var,
|
||||
num_precision_var,
|
||||
0,
|
||||
num_no_bound(),
|
||||
),
|
||||
),
|
||||
],
|
||||
ret_var: list_elem_var,
|
||||
},
|
||||
|
@ -3129,7 +3279,10 @@ fn list_sum(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
Box::new(function),
|
||||
vec![
|
||||
(list_var, Loc::at_zero(Var(Symbol::ARG_1))),
|
||||
(num_var, Loc::at_zero(num(var_store.fresh(), 0))),
|
||||
(
|
||||
num_var,
|
||||
Loc::at_zero(num(var_store.fresh(), 0, num_no_bound())),
|
||||
),
|
||||
(closure_var, Loc::at_zero(Var(Symbol::NUM_ADD))),
|
||||
],
|
||||
CalledVia::Space,
|
||||
|
@ -3161,7 +3314,10 @@ fn list_product(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
Box::new(function),
|
||||
vec![
|
||||
(list_var, Loc::at_zero(Var(Symbol::ARG_1))),
|
||||
(num_var, Loc::at_zero(num(var_store.fresh(), 1))),
|
||||
(
|
||||
num_var,
|
||||
Loc::at_zero(num(var_store.fresh(), 1, num_no_bound())),
|
||||
),
|
||||
(closure_var, Loc::at_zero(Var(Symbol::NUM_MUL))),
|
||||
],
|
||||
CalledVia::Space,
|
||||
|
@ -3815,7 +3971,7 @@ fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(num_var, Var(Symbol::ARG_2)),
|
||||
(num_var, num(unbound_zero_var, 0)),
|
||||
(num_var, num(unbound_zero_var, 0, num_no_bound())),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
|
@ -3918,7 +4074,10 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(num_var, Var(Symbol::ARG_2)),
|
||||
(num_var, float(unbound_zero_var, precision_var, 0.0)),
|
||||
(
|
||||
num_var,
|
||||
float(unbound_zero_var, precision_var, 0.0, num_no_bound()),
|
||||
),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
|
@ -3983,7 +4142,12 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
(num_var, Var(Symbol::ARG_2)),
|
||||
(
|
||||
num_var,
|
||||
int::<i128>(unbound_zero_var, unbound_zero_precision_var, 0),
|
||||
int::<i128>(
|
||||
unbound_zero_var,
|
||||
unbound_zero_precision_var,
|
||||
0,
|
||||
num_no_bound(),
|
||||
),
|
||||
),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
|
@ -4049,7 +4213,12 @@ fn num_div_ceil(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
(num_var, Var(Symbol::ARG_2)),
|
||||
(
|
||||
num_var,
|
||||
int::<i128>(unbound_zero_var, unbound_zero_precision_var, 0),
|
||||
int::<i128>(
|
||||
unbound_zero_var,
|
||||
unbound_zero_precision_var,
|
||||
0,
|
||||
num_no_bound(),
|
||||
),
|
||||
),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
|
@ -4119,7 +4288,10 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
RunLowLevel {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(len_var, int::<i128>(zero_var, zero_precision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
int::<i128>(zero_var, zero_precision_var, 0, num_no_bound()),
|
||||
),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
|
@ -4143,7 +4315,10 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
op: LowLevel::ListGetUnsafe,
|
||||
args: vec![
|
||||
(list_var, Var(Symbol::ARG_1)),
|
||||
(len_var, int::<i128>(zero_var, zero_precision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
int::<i128>(zero_var, zero_precision_var, 0, num_no_bound()),
|
||||
),
|
||||
],
|
||||
ret_var: list_elem_var,
|
||||
},
|
||||
|
@ -4200,7 +4375,10 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
RunLowLevel {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(len_var, int::<i128>(num_var, num_precision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
int::<i128>(num_var, num_precision_var, 0, num_no_bound()),
|
||||
),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
|
@ -4239,7 +4417,15 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
ret_var: len_var,
|
||||
},
|
||||
),
|
||||
(arg_var, int::<i128>(num_var, num_precision_var, 1)),
|
||||
(
|
||||
arg_var,
|
||||
int::<i128>(
|
||||
num_var,
|
||||
num_precision_var,
|
||||
1,
|
||||
num_no_bound(),
|
||||
),
|
||||
),
|
||||
],
|
||||
ret_var: len_var,
|
||||
},
|
||||
|
@ -4867,7 +5053,10 @@ fn num_bytes_to(symbol: Symbol, var_store: &mut VarStore, offset: i64, low_level
|
|||
add_var,
|
||||
RunLowLevel {
|
||||
ret_var: cast_var,
|
||||
args: vec![(cast_var, num(var_store.fresh(), offset))],
|
||||
args: vec![(
|
||||
cast_var,
|
||||
num(var_store.fresh(), offset, num_no_bound()),
|
||||
)],
|
||||
op: LowLevel::NumIntCast,
|
||||
},
|
||||
),
|
||||
|
@ -4955,13 +5144,18 @@ fn defn_help(
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn int_min_or_max<I128>(symbol: Symbol, var_store: &mut VarStore, i: I128) -> Def
|
||||
fn int_min_or_max<I128>(
|
||||
symbol: Symbol,
|
||||
var_store: &mut VarStore,
|
||||
i: I128,
|
||||
bound: NumericBound<IntWidth>,
|
||||
) -> Def
|
||||
where
|
||||
I128: Into<i128>,
|
||||
{
|
||||
let int_var = var_store.fresh();
|
||||
let int_precision_var = var_store.fresh();
|
||||
let body = int::<I128>(int_var, int_precision_var, i);
|
||||
let body = int::<I128>(int_var, int_precision_var, i, bound);
|
||||
|
||||
let std = roc_builtins::std::types();
|
||||
let solved = std.get(&symbol).unwrap();
|
||||
|
@ -4984,21 +5178,53 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn num_no_bound<W: Copy>() -> NumericBound<W> {
|
||||
NumericBound::None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn int<I128>(num_var: Variable, precision_var: Variable, i: I128) -> Expr
|
||||
fn int<I128>(
|
||||
num_var: Variable,
|
||||
precision_var: Variable,
|
||||
i: I128,
|
||||
bound: NumericBound<IntWidth>,
|
||||
) -> Expr
|
||||
where
|
||||
I128: Into<i128>,
|
||||
{
|
||||
let ii = i.into();
|
||||
Int(num_var, precision_var, ii.to_string().into_boxed_str(), ii)
|
||||
Int(
|
||||
num_var,
|
||||
precision_var,
|
||||
ii.to_string().into_boxed_str(),
|
||||
IntValue::I128(ii),
|
||||
bound,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn float(num_var: Variable, precision_var: Variable, f: f64) -> Expr {
|
||||
Float(num_var, precision_var, f.to_string().into_boxed_str(), f)
|
||||
fn float(
|
||||
num_var: Variable,
|
||||
precision_var: Variable,
|
||||
f: f64,
|
||||
bound: NumericBound<FloatWidth>,
|
||||
) -> Expr {
|
||||
Float(
|
||||
num_var,
|
||||
precision_var,
|
||||
f.to_string().into_boxed_str(),
|
||||
f,
|
||||
bound,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn num(num_var: Variable, i: i64) -> Expr {
|
||||
Num(num_var, i.to_string().into_boxed_str(), i)
|
||||
fn num<I: Into<i128>>(num_var: Variable, i: I, bound: NumericBound<NumWidth>) -> Expr {
|
||||
let i = i.into();
|
||||
Num(
|
||||
num_var,
|
||||
i.to_string().into_boxed_str(),
|
||||
IntValue::I128(i),
|
||||
bound,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -840,9 +840,9 @@ fn pattern_to_vars_by_symbol(
|
|||
}
|
||||
}
|
||||
|
||||
NumLiteral(_, _, _)
|
||||
| IntLiteral(_, _, _)
|
||||
| FloatLiteral(_, _, _)
|
||||
NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| MalformedPattern(_, _)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use roc_can::annotation::IntroducedVariables;
|
||||
use roc_can::def::{Declaration, Def};
|
||||
use roc_can::env::Env;
|
||||
use roc_can::expr::{ClosureData, Expr, Recursive};
|
||||
use roc_can::pattern::Pattern;
|
||||
use roc_can::scope::Scope;
|
||||
use crate::annotation::IntroducedVariables;
|
||||
use crate::def::{Declaration, Def};
|
||||
use crate::env::Env;
|
||||
use crate::expr::{ClosureData, Expr, Recursive};
|
||||
use crate::pattern::Pattern;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::all::{MutSet, SendMap};
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::TagName;
|
||||
|
@ -220,7 +220,7 @@ fn build_effect_always(
|
|||
)
|
||||
};
|
||||
|
||||
let def_annotation = roc_can::def::Annotation {
|
||||
let def_annotation = crate::def::Annotation {
|
||||
signature,
|
||||
introduced_variables,
|
||||
aliases: SendMap::default(),
|
||||
|
@ -432,7 +432,7 @@ fn build_effect_map(
|
|||
)
|
||||
};
|
||||
|
||||
let def_annotation = roc_can::def::Annotation {
|
||||
let def_annotation = crate::def::Annotation {
|
||||
signature,
|
||||
introduced_variables,
|
||||
aliases: SendMap::default(),
|
||||
|
@ -599,7 +599,7 @@ fn build_effect_after(
|
|||
)
|
||||
};
|
||||
|
||||
let def_annotation = roc_can::def::Annotation {
|
||||
let def_annotation = crate::def::Annotation {
|
||||
signature,
|
||||
introduced_variables,
|
||||
aliases: SendMap::default(),
|
||||
|
@ -852,7 +852,7 @@ fn build_effect_forever(
|
|||
)
|
||||
};
|
||||
|
||||
let def_annotation = roc_can::def::Annotation {
|
||||
let def_annotation = crate::def::Annotation {
|
||||
signature,
|
||||
introduced_variables,
|
||||
aliases: SendMap::default(),
|
||||
|
@ -1150,7 +1150,7 @@ fn build_effect_loop(
|
|||
)
|
||||
};
|
||||
|
||||
let def_annotation = roc_can::def::Annotation {
|
||||
let def_annotation = crate::def::Annotation {
|
||||
signature,
|
||||
introduced_variables,
|
||||
aliases: SendMap::default(),
|
||||
|
@ -1334,7 +1334,7 @@ fn build_effect_loop_inner_body(
|
|||
|
||||
let step_pattern = applied_tag_pattern(step_tag_name, &[new_state_symbol], var_store);
|
||||
|
||||
roc_can::expr::WhenBranch {
|
||||
crate::expr::WhenBranch {
|
||||
patterns: vec![Loc::at_zero(step_pattern)],
|
||||
value: Loc::at_zero(force_thunk2),
|
||||
guard: None,
|
||||
|
@ -1345,7 +1345,7 @@ fn build_effect_loop_inner_body(
|
|||
let done_tag_name = TagName::Global("Done".into());
|
||||
let done_pattern = applied_tag_pattern(done_tag_name, &[done_symbol], var_store);
|
||||
|
||||
roc_can::expr::WhenBranch {
|
||||
crate::expr::WhenBranch {
|
||||
patterns: vec![Loc::at_zero(done_pattern)],
|
||||
value: Loc::at_zero(Expr::Var(done_symbol)),
|
||||
guard: None,
|
||||
|
@ -1376,7 +1376,7 @@ pub fn build_host_exposed_def(
|
|||
ident: &str,
|
||||
effect_tag_name: TagName,
|
||||
var_store: &mut VarStore,
|
||||
annotation: roc_can::annotation::Annotation,
|
||||
annotation: crate::annotation::Annotation,
|
||||
) -> Def {
|
||||
let expr_var = var_store.fresh();
|
||||
let pattern = Pattern::Identifier(symbol);
|
||||
|
@ -1520,7 +1520,7 @@ pub fn build_host_exposed_def(
|
|||
}
|
||||
};
|
||||
|
||||
let def_annotation = roc_can::def::Annotation {
|
||||
let def_annotation = crate::def::Annotation {
|
||||
signature: annotation.typ,
|
||||
introduced_variables: annotation.introduced_variables,
|
||||
aliases: annotation.aliases,
|
|
@ -3,8 +3,8 @@ use crate::builtins::builtin_defs_map;
|
|||
use crate::def::{can_defs_with_return, Def};
|
||||
use crate::env::Env;
|
||||
use crate::num::{
|
||||
finish_parsing_base, finish_parsing_float, finish_parsing_int, float_expr_from_result,
|
||||
int_expr_from_result, num_expr_from_result,
|
||||
finish_parsing_base, finish_parsing_float, finish_parsing_num, float_expr_from_result,
|
||||
int_expr_from_result, num_expr_from_result, FloatWidth, IntWidth, NumWidth, NumericBound,
|
||||
};
|
||||
use crate::pattern::{canonicalize_pattern, Pattern};
|
||||
use crate::procedure::References;
|
||||
|
@ -20,7 +20,7 @@ use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
|||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::Alias;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::{char, u32};
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq)]
|
||||
|
@ -46,17 +46,38 @@ impl Output {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum IntValue {
|
||||
I128(i128),
|
||||
U128(u128),
|
||||
}
|
||||
|
||||
impl Display for IntValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IntValue::I128(n) => Display::fmt(&n, f),
|
||||
IntValue::U128(n) => Display::fmt(&n, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Expr {
|
||||
// Literals
|
||||
|
||||
// Num stores the `a` variable in `Num a`. Not the same as the variable
|
||||
// stored in Int and Float below, which is strictly for better error messages
|
||||
Num(Variable, Box<str>, i64),
|
||||
Num(Variable, Box<str>, IntValue, NumericBound<NumWidth>),
|
||||
|
||||
// Int and Float store a variable to generate better error messages
|
||||
Int(Variable, Variable, Box<str>, i128),
|
||||
Float(Variable, Variable, Box<str>, f64),
|
||||
Int(
|
||||
Variable,
|
||||
Variable,
|
||||
Box<str>,
|
||||
IntValue,
|
||||
NumericBound<IntWidth>,
|
||||
),
|
||||
Float(Variable, Variable, Box<str>, f64, NumericBound<FloatWidth>),
|
||||
Str(Box<str>),
|
||||
List {
|
||||
elem_var: Variable,
|
||||
|
@ -208,20 +229,20 @@ pub fn canonicalize_expr<'a>(
|
|||
use Expr::*;
|
||||
|
||||
let (expr, output) = match expr {
|
||||
ast::Expr::Num(str) => {
|
||||
&ast::Expr::Num(str) => {
|
||||
let answer = num_expr_from_result(
|
||||
var_store,
|
||||
finish_parsing_int(*str).map(|int| (*str, int)),
|
||||
finish_parsing_num(str).map(|result| (str, result)),
|
||||
region,
|
||||
env,
|
||||
);
|
||||
|
||||
(answer, Output::default())
|
||||
}
|
||||
ast::Expr::Float(str) => {
|
||||
&ast::Expr::Float(str) => {
|
||||
let answer = float_expr_from_result(
|
||||
var_store,
|
||||
finish_parsing_float(str).map(|f| (*str, f)),
|
||||
finish_parsing_float(str).map(|(f, bound)| (str, f, bound)),
|
||||
region,
|
||||
env,
|
||||
);
|
||||
|
@ -790,21 +811,21 @@ pub fn canonicalize_expr<'a>(
|
|||
|
||||
(RuntimeError(problem), Output::default())
|
||||
}
|
||||
ast::Expr::NonBase10Int {
|
||||
&ast::Expr::NonBase10Int {
|
||||
string,
|
||||
base,
|
||||
is_negative,
|
||||
} => {
|
||||
// the minus sign is added before parsing, to get correct overflow/underflow behavior
|
||||
let answer = match finish_parsing_base(string, *base, *is_negative) {
|
||||
Ok(int) => {
|
||||
let answer = match finish_parsing_base(string, base, is_negative) {
|
||||
Ok((int, bound)) => {
|
||||
// Done in this kinda round about way with intermediate variables
|
||||
// to keep borrowed values around and make this compile
|
||||
let int_string = int.to_string();
|
||||
let int_str = int_string.as_str();
|
||||
int_expr_from_result(var_store, Ok((int_str, int as i128)), region, *base, env)
|
||||
int_expr_from_result(var_store, Ok((int_str, int, bound)), region, base, env)
|
||||
}
|
||||
Err(e) => int_expr_from_result(var_store, Err(e), region, *base, env),
|
||||
Err(e) => int_expr_from_result(var_store, Err(e), region, base, env),
|
||||
};
|
||||
|
||||
(answer, Output::default())
|
||||
|
@ -1226,9 +1247,9 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
|||
match expr {
|
||||
// Num stores the `a` variable in `Num a`. Not the same as the variable
|
||||
// stored in Int and Float below, which is strictly for better error messages
|
||||
other @ Num(_, _, _)
|
||||
| other @ Int(_, _, _, _)
|
||||
| other @ Float(_, _, _, _)
|
||||
other @ Num(..)
|
||||
| other @ Int(..)
|
||||
| other @ Float(..)
|
||||
| other @ Str { .. }
|
||||
| other @ RuntimeError(_)
|
||||
| other @ EmptyRecord
|
||||
|
|
|
@ -5,6 +5,7 @@ pub mod annotation;
|
|||
pub mod builtins;
|
||||
pub mod constraint;
|
||||
pub mod def;
|
||||
pub mod effect_module;
|
||||
pub mod env;
|
||||
pub mod expected;
|
||||
pub mod expr;
|
||||
|
|
|
@ -6,15 +6,16 @@ use crate::pattern::Pattern;
|
|||
use crate::scope::Scope;
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::all::{MutMap, MutSet, SendMap};
|
||||
use roc_module::ident::Ident;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::ident::{Ident, TagName};
|
||||
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
||||
use roc_parse::ast;
|
||||
use roc_parse::header::HeaderFor;
|
||||
use roc_parse::pattern::PatternType;
|
||||
use roc_problem::can::{Problem, RuntimeError};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::Alias;
|
||||
use roc_types::types::{Alias, Type};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Module {
|
||||
|
@ -44,6 +45,7 @@ pub struct ModuleOutput {
|
|||
pub fn canonicalize_module_defs<'a, F>(
|
||||
arena: &Bump,
|
||||
loc_defs: &'a [Loc<ast::Def<'a>>],
|
||||
header_for: &roc_parse::header::HeaderFor,
|
||||
home: ModuleId,
|
||||
module_ids: &ModuleIds,
|
||||
exposed_ident_ids: IdentIds,
|
||||
|
@ -59,12 +61,49 @@ where
|
|||
{
|
||||
let mut can_exposed_imports = MutMap::default();
|
||||
let mut scope = Scope::new(home, var_store);
|
||||
let mut env = Env::new(home, dep_idents, module_ids, exposed_ident_ids);
|
||||
let num_deps = dep_idents.len();
|
||||
|
||||
for (name, alias) in aliases.into_iter() {
|
||||
scope.add_alias(name, alias.region, alias.type_variables, alias.typ);
|
||||
}
|
||||
|
||||
let effect_symbol = if let HeaderFor::Hosted { generates, .. } = header_for {
|
||||
// TODO extract effect name from the header
|
||||
let name: &str = generates.into();
|
||||
let effect_symbol = scope
|
||||
.introduce(
|
||||
name.into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let effect_tag_name = TagName::Private(effect_symbol);
|
||||
|
||||
{
|
||||
let a_var = var_store.fresh();
|
||||
|
||||
let actual = crate::effect_module::build_effect_actual(
|
||||
effect_tag_name,
|
||||
Type::Variable(a_var),
|
||||
var_store,
|
||||
);
|
||||
|
||||
scope.add_alias(
|
||||
effect_symbol,
|
||||
Region::zero(),
|
||||
vec![Loc::at_zero(("a".into(), a_var))],
|
||||
actual,
|
||||
);
|
||||
}
|
||||
|
||||
Some(effect_symbol)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Desugar operators (convert them to Apply calls, taking into account
|
||||
// operator precedence and associativity rules), before doing other canonicalization.
|
||||
//
|
||||
|
@ -82,7 +121,6 @@ where
|
|||
}));
|
||||
}
|
||||
|
||||
let mut env = Env::new(home, dep_idents, module_ids, exposed_ident_ids);
|
||||
let mut lookups = Vec::with_capacity(num_deps);
|
||||
let mut rigid_variables = MutMap::default();
|
||||
|
||||
|
@ -143,7 +181,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let (defs, scope, output, symbols_introduced) = canonicalize_defs(
|
||||
let (defs, mut scope, output, symbols_introduced) = canonicalize_defs(
|
||||
&mut env,
|
||||
Output::default(),
|
||||
var_store,
|
||||
|
@ -208,7 +246,21 @@ where
|
|||
(Ok(mut declarations), output) => {
|
||||
use crate::def::Declaration::*;
|
||||
|
||||
for decl in declarations.iter() {
|
||||
if let Some(effect_symbol) = effect_symbol {
|
||||
let mut exposed_symbols = MutSet::default();
|
||||
|
||||
// NOTE this currently builds all functions, not just the ones that the user requested
|
||||
crate::effect_module::build_effect_builtins(
|
||||
&mut env,
|
||||
&mut scope,
|
||||
effect_symbol,
|
||||
var_store,
|
||||
&mut exposed_symbols,
|
||||
&mut declarations,
|
||||
);
|
||||
}
|
||||
|
||||
for decl in declarations.iter_mut() {
|
||||
match decl {
|
||||
Declare(def) => {
|
||||
for (symbol, _) in def.pattern_vars.iter() {
|
||||
|
@ -221,6 +273,59 @@ where
|
|||
exposed_but_not_defined.remove(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
// Temporary hack: we don't know exactly what symbols are hosted symbols,
|
||||
// and which are meant to be normal definitions without a body. So for now
|
||||
// we just assume they are hosted functions (meant to be provided by the platform)
|
||||
if let Some(effect_symbol) = effect_symbol {
|
||||
macro_rules! make_hosted_def {
|
||||
() => {
|
||||
let symbol = def.pattern_vars.iter().next().unwrap().0;
|
||||
let ident_id = symbol.ident_id();
|
||||
let ident =
|
||||
env.ident_ids.get_name(ident_id).unwrap().to_string();
|
||||
let def_annotation = def.annotation.clone().unwrap();
|
||||
let annotation = crate::annotation::Annotation {
|
||||
typ: def_annotation.signature,
|
||||
introduced_variables: def_annotation.introduced_variables,
|
||||
references: Default::default(),
|
||||
aliases: Default::default(),
|
||||
};
|
||||
|
||||
let hosted_def = crate::effect_module::build_host_exposed_def(
|
||||
&mut env,
|
||||
&mut scope,
|
||||
*symbol,
|
||||
&ident,
|
||||
TagName::Private(effect_symbol),
|
||||
var_store,
|
||||
annotation,
|
||||
);
|
||||
|
||||
*def = hosted_def;
|
||||
};
|
||||
}
|
||||
|
||||
match &def.loc_expr.value {
|
||||
Expr::RuntimeError(RuntimeError::NoImplementationNamed {
|
||||
..
|
||||
}) => {
|
||||
make_hosted_def!();
|
||||
}
|
||||
Expr::Closure(closure_data)
|
||||
if matches!(
|
||||
closure_data.loc_body.value,
|
||||
Expr::RuntimeError(
|
||||
RuntimeError::NoImplementationNamed { .. }
|
||||
)
|
||||
) =>
|
||||
{
|
||||
make_hosted_def!();
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
DeclareRec(defs) => {
|
||||
for def in defs {
|
||||
|
@ -253,6 +358,18 @@ where
|
|||
|
||||
let mut aliases = MutMap::default();
|
||||
|
||||
if let Some(effect_symbol) = effect_symbol {
|
||||
// Remove this from exposed_symbols,
|
||||
// so that at the end of the process,
|
||||
// we can see if there were any
|
||||
// exposed symbols which did not have
|
||||
// corresponding defs.
|
||||
exposed_but_not_defined.remove(&effect_symbol);
|
||||
|
||||
let hosted_alias = scope.lookup_alias(effect_symbol).unwrap().clone();
|
||||
aliases.insert(effect_symbol, hosted_alias);
|
||||
}
|
||||
|
||||
for (symbol, alias) in output.aliases {
|
||||
// Remove this from exposed_symbols,
|
||||
// so that at the end of the process,
|
||||
|
@ -397,9 +514,9 @@ fn fix_values_captured_in_closure_pattern(
|
|||
}
|
||||
}
|
||||
Identifier(_)
|
||||
| NumLiteral(_, _, _)
|
||||
| IntLiteral(_, _, _)
|
||||
| FloatLiteral(_, _, _)
|
||||
| NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| Shadowed(..)
|
||||
|
@ -453,9 +570,9 @@ fn fix_values_captured_in_closure_expr(
|
|||
fix_values_captured_in_closure_expr(&mut loc_body.value, no_capture_symbols);
|
||||
}
|
||||
|
||||
Num(_, _, _)
|
||||
| Int(_, _, _, _)
|
||||
| Float(_, _, _, _)
|
||||
Num(..)
|
||||
| Int(..)
|
||||
| Float(..)
|
||||
| Str(_)
|
||||
| Var(_)
|
||||
| EmptyRecord
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::env::Env;
|
||||
use crate::expr::Expr;
|
||||
use crate::expr::{Expr, IntValue};
|
||||
use roc_parse::ast::Base;
|
||||
use roc_problem::can::Problem;
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
@ -7,21 +7,33 @@ use roc_problem::can::{FloatErrorKind, IntErrorKind};
|
|||
use roc_region::all::Region;
|
||||
use roc_types::subs::VarStore;
|
||||
use std::i64;
|
||||
|
||||
// TODO use rust's integer parsing again
|
||||
//
|
||||
// We're waiting for libcore here, see https://github.com/rust-lang/rust/issues/22639
|
||||
// There is a nightly API for exposing the parse error.
|
||||
use std::str;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_expr_from_result(
|
||||
var_store: &mut VarStore,
|
||||
result: Result<(&str, i64), (&str, IntErrorKind)>,
|
||||
result: Result<(&str, ParsedNumResult), (&str, IntErrorKind)>,
|
||||
region: Region,
|
||||
env: &mut Env,
|
||||
) -> Expr {
|
||||
match result {
|
||||
Ok((str, num)) => Expr::Num(var_store.fresh(), (*str).into(), num),
|
||||
Ok((str, ParsedNumResult::UnknownNum(num))) => {
|
||||
Expr::Num(var_store.fresh(), (*str).into(), num, NumericBound::None)
|
||||
}
|
||||
Ok((str, ParsedNumResult::Int(num, bound))) => Expr::Int(
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
(*str).into(),
|
||||
num,
|
||||
bound,
|
||||
),
|
||||
Ok((str, ParsedNumResult::Float(num, bound))) => Expr::Float(
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
(*str).into(),
|
||||
num,
|
||||
bound,
|
||||
),
|
||||
Err((raw, error)) => {
|
||||
// (Num *) compiles to Int if it doesn't
|
||||
// get specialized to something else first,
|
||||
|
@ -38,14 +50,20 @@ pub fn num_expr_from_result(
|
|||
#[inline(always)]
|
||||
pub fn int_expr_from_result(
|
||||
var_store: &mut VarStore,
|
||||
result: Result<(&str, i128), (&str, IntErrorKind)>,
|
||||
result: Result<(&str, IntValue, NumericBound<IntWidth>), (&str, IntErrorKind)>,
|
||||
region: Region,
|
||||
base: Base,
|
||||
env: &mut Env,
|
||||
) -> Expr {
|
||||
// Int stores a variable to generate better error messages
|
||||
match result {
|
||||
Ok((str, int)) => Expr::Int(var_store.fresh(), var_store.fresh(), (*str).into(), int),
|
||||
Ok((str, int, bound)) => Expr::Int(
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
(*str).into(),
|
||||
int,
|
||||
bound,
|
||||
),
|
||||
Err((raw, error)) => {
|
||||
let runtime_error = InvalidInt(error, base, region, raw.into());
|
||||
|
||||
|
@ -59,13 +77,19 @@ pub fn int_expr_from_result(
|
|||
#[inline(always)]
|
||||
pub fn float_expr_from_result(
|
||||
var_store: &mut VarStore,
|
||||
result: Result<(&str, f64), (&str, FloatErrorKind)>,
|
||||
result: Result<(&str, f64, NumericBound<FloatWidth>), (&str, FloatErrorKind)>,
|
||||
region: Region,
|
||||
env: &mut Env,
|
||||
) -> Expr {
|
||||
// Float stores a variable to generate better error messages
|
||||
match result {
|
||||
Ok((str, float)) => Expr::Float(var_store.fresh(), var_store.fresh(), (*str).into(), float),
|
||||
Ok((str, float, bound)) => Expr::Float(
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
(*str).into(),
|
||||
float,
|
||||
bound,
|
||||
),
|
||||
Err((raw, error)) => {
|
||||
let runtime_error = InvalidFloat(error, region, raw.into());
|
||||
|
||||
|
@ -76,11 +100,32 @@ pub fn float_expr_from_result(
|
|||
}
|
||||
}
|
||||
|
||||
pub enum ParsedNumResult {
|
||||
Int(IntValue, NumericBound<IntWidth>),
|
||||
Float(f64, NumericBound<FloatWidth>),
|
||||
UnknownNum(IntValue),
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn finish_parsing_int(raw: &str) -> Result<i64, (&str, IntErrorKind)> {
|
||||
pub fn finish_parsing_num(raw: &str) -> Result<ParsedNumResult, (&str, IntErrorKind)> {
|
||||
// Ignore underscores.
|
||||
let radix = 10;
|
||||
from_str_radix::<i64>(raw.replace("_", "").as_str(), radix).map_err(|e| (raw, e.kind))
|
||||
let (num, bound) =
|
||||
from_str_radix(raw.replace("_", "").as_str(), radix).map_err(|e| (raw, e))?;
|
||||
// Let's try to specialize the number
|
||||
Ok(match bound {
|
||||
NumericBound::None => ParsedNumResult::UnknownNum(num),
|
||||
NumericBound::Exact(NumWidth::Int(iw)) => {
|
||||
ParsedNumResult::Int(num, NumericBound::Exact(iw))
|
||||
}
|
||||
NumericBound::Exact(NumWidth::Float(fw)) => {
|
||||
let num = match num {
|
||||
IntValue::I128(n) => n as f64,
|
||||
IntValue::U128(n) => n as f64,
|
||||
};
|
||||
ParsedNumResult::Float(num, NumericBound::Exact(fw))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -88,7 +133,7 @@ pub fn finish_parsing_base(
|
|||
raw: &str,
|
||||
base: Base,
|
||||
is_negative: bool,
|
||||
) -> Result<i64, (&str, IntErrorKind)> {
|
||||
) -> Result<(IntValue, NumericBound<IntWidth>), (&str, IntErrorKind)> {
|
||||
let radix = match base {
|
||||
Base::Hex => 16,
|
||||
Base::Decimal => 10,
|
||||
|
@ -98,18 +143,36 @@ pub fn finish_parsing_base(
|
|||
|
||||
// Ignore underscores, insert - when negative to get correct underflow/overflow behavior
|
||||
(if is_negative {
|
||||
from_str_radix::<i64>(format!("-{}", raw.replace("_", "")).as_str(), radix)
|
||||
from_str_radix(format!("-{}", raw.replace("_", "")).as_str(), radix)
|
||||
} else {
|
||||
from_str_radix::<i64>(raw.replace("_", "").as_str(), radix)
|
||||
from_str_radix(raw.replace("_", "").as_str(), radix)
|
||||
})
|
||||
.map_err(|e| (raw, e.kind))
|
||||
.and_then(|(n, bound)| {
|
||||
let bound = match bound {
|
||||
NumericBound::None => NumericBound::None,
|
||||
NumericBound::Exact(NumWidth::Int(iw)) => NumericBound::Exact(iw),
|
||||
NumericBound::Exact(NumWidth::Float(_)) => return Err(IntErrorKind::FloatSuffix),
|
||||
};
|
||||
Ok((n, bound))
|
||||
})
|
||||
.map_err(|e| (raw, e))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn finish_parsing_float(raw: &str) -> Result<f64, (&str, FloatErrorKind)> {
|
||||
pub fn finish_parsing_float(
|
||||
raw: &str,
|
||||
) -> Result<(f64, NumericBound<FloatWidth>), (&str, FloatErrorKind)> {
|
||||
let (opt_bound, raw_without_suffix) = parse_literal_suffix(raw);
|
||||
|
||||
let bound = match opt_bound {
|
||||
None => NumericBound::None,
|
||||
Some(NumWidth::Float(fw)) => NumericBound::Exact(fw),
|
||||
Some(NumWidth::Int(_)) => return Err((raw, FloatErrorKind::IntSuffix)),
|
||||
};
|
||||
|
||||
// Ignore underscores.
|
||||
match raw.replace("_", "").parse::<f64>() {
|
||||
Ok(float) if float.is_finite() => Ok(float),
|
||||
match raw_without_suffix.replace("_", "").parse::<f64>() {
|
||||
Ok(float) if float.is_finite() => Ok((float, bound)),
|
||||
Ok(float) => {
|
||||
if float.is_sign_positive() {
|
||||
Err((raw, FloatErrorKind::PositiveInfinity))
|
||||
|
@ -121,6 +184,35 @@ pub fn finish_parsing_float(raw: &str) -> Result<f64, (&str, FloatErrorKind)> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_literal_suffix(num_str: &str) -> (Option<NumWidth>, &str) {
|
||||
macro_rules! parse_num_suffix {
|
||||
($($suffix:expr, $width:expr)*) => {$(
|
||||
if num_str.ends_with($suffix) {
|
||||
return (Some($width), num_str.get(0..num_str.len() - $suffix.len()).unwrap());
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
parse_num_suffix! {
|
||||
"u8", NumWidth::Int(IntWidth::U8)
|
||||
"u16", NumWidth::Int(IntWidth::U16)
|
||||
"u32", NumWidth::Int(IntWidth::U32)
|
||||
"u64", NumWidth::Int(IntWidth::U64)
|
||||
"u128", NumWidth::Int(IntWidth::U128)
|
||||
"i8", NumWidth::Int(IntWidth::I8)
|
||||
"i16", NumWidth::Int(IntWidth::I16)
|
||||
"i32", NumWidth::Int(IntWidth::I32)
|
||||
"i64", NumWidth::Int(IntWidth::I64)
|
||||
"i128", NumWidth::Int(IntWidth::I128)
|
||||
"nat", NumWidth::Int(IntWidth::Nat)
|
||||
"dec", NumWidth::Float(FloatWidth::Dec)
|
||||
"f32", NumWidth::Float(FloatWidth::F32)
|
||||
"f64", NumWidth::Float(FloatWidth::F64)
|
||||
}
|
||||
|
||||
(None, num_str)
|
||||
}
|
||||
|
||||
/// Integer parsing code taken from the rust libcore,
|
||||
/// pulled in so we can give custom error messages
|
||||
///
|
||||
|
@ -129,44 +221,11 @@ pub fn finish_parsing_float(raw: &str) -> Result<f64, (&str, FloatErrorKind)> {
|
|||
/// the LEGAL_DETAILS file in the root directory of this distribution.
|
||||
///
|
||||
/// Thanks to the Rust project and its contributors!
|
||||
trait FromStrRadixHelper: PartialOrd + Copy {
|
||||
fn min_value() -> Self;
|
||||
fn max_value() -> Self;
|
||||
fn from_u32(u: u32) -> Self;
|
||||
fn checked_mul(&self, other: u32) -> Option<Self>;
|
||||
fn checked_sub(&self, other: u32) -> Option<Self>;
|
||||
fn checked_add(&self, other: u32) -> Option<Self>;
|
||||
}
|
||||
|
||||
macro_rules! doit {
|
||||
($($t:ty)*) => ($(impl FromStrRadixHelper for $t {
|
||||
#[inline]
|
||||
fn min_value() -> Self { Self::min_value() }
|
||||
#[inline]
|
||||
fn max_value() -> Self { Self::max_value() }
|
||||
#[inline]
|
||||
fn from_u32(u: u32) -> Self { u as Self }
|
||||
#[inline]
|
||||
fn checked_mul(&self, other: u32) -> Option<Self> {
|
||||
Self::checked_mul(*self, other as Self)
|
||||
}
|
||||
#[inline]
|
||||
fn checked_sub(&self, other: u32) -> Option<Self> {
|
||||
Self::checked_sub(*self, other as Self)
|
||||
}
|
||||
#[inline]
|
||||
fn checked_add(&self, other: u32) -> Option<Self> {
|
||||
Self::checked_add(*self, other as Self)
|
||||
}
|
||||
})*)
|
||||
}
|
||||
// We only need the i64 implementation, but libcore defines
|
||||
// doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
|
||||
doit! { i64 }
|
||||
|
||||
fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, ParseIntError> {
|
||||
fn from_str_radix(
|
||||
src: &str,
|
||||
radix: u32,
|
||||
) -> Result<(IntValue, NumericBound<NumWidth>), IntErrorKind> {
|
||||
use self::IntErrorKind::*;
|
||||
use self::ParseIntError as PIE;
|
||||
|
||||
assert!(
|
||||
(2..=36).contains(&radix),
|
||||
|
@ -174,86 +233,260 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par
|
|||
radix
|
||||
);
|
||||
|
||||
if src.is_empty() {
|
||||
return Err(PIE { kind: Empty });
|
||||
}
|
||||
let (opt_exact_bound, src) = parse_literal_suffix(src);
|
||||
|
||||
let is_signed_ty = T::from_u32(0) > T::min_value();
|
||||
|
||||
// all valid digits are ascii, so we will just iterate over the utf8 bytes
|
||||
// and cast them to chars. .to_digit() will safely return None for anything
|
||||
// other than a valid ascii digit for the given radix, including the first-byte
|
||||
// of multi-byte sequences
|
||||
let src = src.as_bytes();
|
||||
|
||||
let (is_positive, digits) = match src[0] {
|
||||
b'+' => (true, &src[1..]),
|
||||
b'-' if is_signed_ty => (false, &src[1..]),
|
||||
_ => (true, src),
|
||||
use std::num::IntErrorKind as StdIEK;
|
||||
let result = match i128::from_str_radix(src, radix) {
|
||||
Ok(result) => IntValue::I128(result),
|
||||
Err(pie) => match pie.kind() {
|
||||
StdIEK::Empty => return Err(IntErrorKind::Empty),
|
||||
StdIEK::InvalidDigit => return Err(IntErrorKind::InvalidDigit),
|
||||
StdIEK::NegOverflow => return Err(IntErrorKind::Underflow),
|
||||
StdIEK::PosOverflow => {
|
||||
// try a u128
|
||||
match u128::from_str_radix(src, radix) {
|
||||
Ok(result) => IntValue::U128(result),
|
||||
Err(pie) => match pie.kind() {
|
||||
StdIEK::InvalidDigit => return Err(IntErrorKind::InvalidDigit),
|
||||
StdIEK::PosOverflow => return Err(IntErrorKind::Overflow),
|
||||
StdIEK::Empty | StdIEK::Zero | StdIEK::NegOverflow => unreachable!(),
|
||||
_ => unreachable!("I thought all possibilities were exhausted, but std::num added a new one")
|
||||
},
|
||||
}
|
||||
}
|
||||
StdIEK::Zero => unreachable!("Parsed a i128"),
|
||||
_ => unreachable!(
|
||||
"I thought all possibilities were exhausted, but std::num added a new one"
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
if digits.is_empty() {
|
||||
return Err(PIE { kind: Empty });
|
||||
}
|
||||
let (lower_bound, is_negative) = match result {
|
||||
IntValue::I128(num) => (lower_bound_of_int(num), num <= 0),
|
||||
IntValue::U128(_) => (IntWidth::U128, false),
|
||||
};
|
||||
|
||||
let mut result = T::from_u32(0);
|
||||
if is_positive {
|
||||
// The number is positive
|
||||
for &c in digits {
|
||||
let x = match (c as char).to_digit(radix) {
|
||||
Some(x) => x,
|
||||
None => return Err(PIE { kind: InvalidDigit }),
|
||||
};
|
||||
result = match result.checked_mul(radix) {
|
||||
Some(result) => result,
|
||||
None => return Err(PIE { kind: Overflow }),
|
||||
};
|
||||
result = match result.checked_add(x) {
|
||||
Some(result) => result,
|
||||
None => return Err(PIE { kind: Overflow }),
|
||||
};
|
||||
match opt_exact_bound {
|
||||
None => {
|
||||
// TODO: use the lower bound
|
||||
Ok((result, NumericBound::None))
|
||||
}
|
||||
Some(bound @ NumWidth::Float(_)) => {
|
||||
// For now, assume floats can represent all integers
|
||||
// TODO: this is somewhat incorrect, revisit
|
||||
Ok((result, NumericBound::Exact(bound)))
|
||||
}
|
||||
Some(NumWidth::Int(exact_width)) => {
|
||||
// We need to check if the exact bound >= lower bound.
|
||||
if exact_width.is_superset(&lower_bound, is_negative) {
|
||||
// Great! Use the exact bound.
|
||||
Ok((result, NumericBound::Exact(NumWidth::Int(exact_width))))
|
||||
} else {
|
||||
// This is something like 200i8; the lower bound is u8, which holds strictly more
|
||||
// ints on the positive side than i8 does. Report an error depending on which side
|
||||
// of the integers we checked.
|
||||
let err = if is_negative {
|
||||
UnderflowsSuffix {
|
||||
suffix_type: exact_width.type_str(),
|
||||
min_value: exact_width.min_value(),
|
||||
}
|
||||
} else {
|
||||
OverflowsSuffix {
|
||||
suffix_type: exact_width.type_str(),
|
||||
max_value: exact_width.max_value(),
|
||||
}
|
||||
};
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_bound_of_int(result: i128) -> IntWidth {
|
||||
use IntWidth::*;
|
||||
if result >= 0 {
|
||||
// Positive
|
||||
let result = result as u128;
|
||||
if result > U64.max_value() {
|
||||
I128
|
||||
} else if result > I64.max_value() {
|
||||
U64
|
||||
} else if result > U32.max_value() {
|
||||
I64
|
||||
} else if result > I32.max_value() {
|
||||
U32
|
||||
} else if result > U16.max_value() {
|
||||
I32
|
||||
} else if result > I16.max_value() {
|
||||
U16
|
||||
} else if result > U8.max_value() {
|
||||
I16
|
||||
} else if result > I8.max_value() {
|
||||
U8
|
||||
} else {
|
||||
I8
|
||||
}
|
||||
} else {
|
||||
// The number is negative
|
||||
for &c in digits {
|
||||
let x = match (c as char).to_digit(radix) {
|
||||
Some(x) => x,
|
||||
None => return Err(PIE { kind: InvalidDigit }),
|
||||
};
|
||||
result = match result.checked_mul(radix) {
|
||||
Some(result) => result,
|
||||
None => return Err(PIE { kind: Underflow }),
|
||||
};
|
||||
result = match result.checked_sub(x) {
|
||||
Some(result) => result,
|
||||
None => return Err(PIE { kind: Underflow }),
|
||||
};
|
||||
// Negative
|
||||
if result < I64.min_value() {
|
||||
I128
|
||||
} else if result < I32.min_value() {
|
||||
I64
|
||||
} else if result < I16.min_value() {
|
||||
I32
|
||||
} else if result < I8.min_value() {
|
||||
I16
|
||||
} else {
|
||||
I8
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// An error which can be returned when parsing an integer.
|
||||
///
|
||||
/// This error is used as the error type for the `from_str_radix()` functions
|
||||
/// on the primitive integer types, such as [`i8::from_str_radix`].
|
||||
///
|
||||
/// # Potential causes
|
||||
///
|
||||
/// Among other causes, `ParseIntError` can be thrown because of leading or trailing whitespace
|
||||
/// in the string e.g., when it is obtained from the standard input.
|
||||
/// Using the [`str.trim()`] method ensures that no whitespace remains before parsing.
|
||||
///
|
||||
/// [`str.trim()`]: ../../std/primitive.str.html#method.trim
|
||||
/// [`i8::from_str_radix`]: ../../std/primitive.i8.html#method.from_str_radix
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ParseIntError {
|
||||
kind: IntErrorKind,
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
enum IntSign {
|
||||
Unsigned,
|
||||
Signed,
|
||||
}
|
||||
|
||||
impl ParseIntError {
|
||||
/// Outputs the detailed cause of parsing an integer failing.
|
||||
pub fn kind(&self) -> &IntErrorKind {
|
||||
&self.kind
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum IntWidth {
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
Nat,
|
||||
}
|
||||
|
||||
impl IntWidth {
|
||||
/// Returns the `IntSign` and bit width of a variant.
|
||||
fn sign_and_width(&self) -> (IntSign, u32) {
|
||||
use IntSign::*;
|
||||
use IntWidth::*;
|
||||
match self {
|
||||
U8 => (Unsigned, 8),
|
||||
U16 => (Unsigned, 16),
|
||||
U32 => (Unsigned, 32),
|
||||
U64 => (Unsigned, 64),
|
||||
U128 => (Unsigned, 128),
|
||||
I8 => (Signed, 8),
|
||||
I16 => (Signed, 16),
|
||||
I32 => (Signed, 32),
|
||||
I64 => (Signed, 64),
|
||||
I128 => (Signed, 128),
|
||||
// TODO: this is platform specific!
|
||||
Nat => (Unsigned, 64),
|
||||
}
|
||||
}
|
||||
|
||||
fn type_str(&self) -> &'static str {
|
||||
use IntWidth::*;
|
||||
match self {
|
||||
U8 => "U8",
|
||||
U16 => "U16",
|
||||
U32 => "U32",
|
||||
U64 => "U64",
|
||||
U128 => "U128",
|
||||
I8 => "I8",
|
||||
I16 => "I16",
|
||||
I32 => "I32",
|
||||
I64 => "I64",
|
||||
I128 => "I128",
|
||||
Nat => "Nat",
|
||||
}
|
||||
}
|
||||
|
||||
fn max_value(&self) -> u128 {
|
||||
use IntWidth::*;
|
||||
match self {
|
||||
U8 => u8::MAX as u128,
|
||||
U16 => u16::MAX as u128,
|
||||
U32 => u32::MAX as u128,
|
||||
U64 => u64::MAX as u128,
|
||||
U128 => u128::MAX,
|
||||
I8 => i8::MAX as u128,
|
||||
I16 => i16::MAX as u128,
|
||||
I32 => i32::MAX as u128,
|
||||
I64 => i64::MAX as u128,
|
||||
I128 => i128::MAX as u128,
|
||||
// TODO: this is platform specific!
|
||||
Nat => u64::MAX as u128,
|
||||
}
|
||||
}
|
||||
|
||||
fn min_value(&self) -> i128 {
|
||||
use IntWidth::*;
|
||||
match self {
|
||||
U8 | U16 | U32 | U64 | U128 | Nat => 0,
|
||||
I8 => i8::MIN as i128,
|
||||
I16 => i16::MIN as i128,
|
||||
I32 => i32::MIN as i128,
|
||||
I64 => i64::MIN as i128,
|
||||
I128 => i128::MIN,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `self` represents superset of integers that `lower_bound` represents, on a particular
|
||||
/// side of the integers relative to 0.
|
||||
///
|
||||
/// If `is_negative` is true, the negative side is checked; otherwise the positive side is checked.
|
||||
pub fn is_superset(&self, lower_bound: &Self, is_negative: bool) -> bool {
|
||||
use IntSign::*;
|
||||
|
||||
if is_negative {
|
||||
match (self.sign_and_width(), lower_bound.sign_and_width()) {
|
||||
((Signed, us), (Signed, lower_bound)) => us >= lower_bound,
|
||||
// Unsigned ints can never represent negative numbers; signed (non-zero width)
|
||||
// ints always can.
|
||||
((Unsigned, _), (Signed, _)) => false,
|
||||
((Signed, _), (Unsigned, _)) => true,
|
||||
// Trivially true; both can only express 0.
|
||||
((Unsigned, _), (Unsigned, _)) => true,
|
||||
}
|
||||
} else {
|
||||
match (self.sign_and_width(), lower_bound.sign_and_width()) {
|
||||
((Signed, us), (Signed, lower_bound))
|
||||
| ((Unsigned, us), (Unsigned, lower_bound)) => us >= lower_bound,
|
||||
|
||||
// Unsigned ints with the same bit width as their unsigned counterparts can always
|
||||
// express 2x more integers on the positive side as unsigned ints.
|
||||
((Unsigned, us), (Signed, lower_bound)) => us >= lower_bound,
|
||||
|
||||
// ...but that means signed int widths can represent less than their unsigned
|
||||
// counterparts, so the below is true iff the bit width is strictly greater. E.g.
|
||||
// i16 is a superset of u8, but i16 is not a superset of u16.
|
||||
((Signed, us), (Unsigned, lower_bound)) => us > lower_bound,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum FloatWidth {
|
||||
Dec,
|
||||
F32,
|
||||
F64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum NumWidth {
|
||||
Int(IntWidth),
|
||||
Float(FloatWidth),
|
||||
}
|
||||
|
||||
/// Describes a bound on the width of a numeric literal.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum NumericBound<W>
|
||||
where
|
||||
W: Copy,
|
||||
{
|
||||
/// There is no bound on the width.
|
||||
None,
|
||||
/// Must have exactly the width `W`.
|
||||
Exact(W),
|
||||
}
|
||||
|
|
|
@ -121,8 +121,8 @@ pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
|
|||
/// then replace the BinOp nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes.
|
||||
pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc<Expr<'a>> {
|
||||
match &loc_expr.value {
|
||||
Float(_)
|
||||
| Num(_)
|
||||
Float(..)
|
||||
| Num(..)
|
||||
| NonBase10Int { .. }
|
||||
| Str(_)
|
||||
| AccessorFunction(_)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use crate::env::Env;
|
||||
use crate::expr::{canonicalize_expr, unescape_char, Expr, Output};
|
||||
use crate::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
|
||||
use crate::expr::{canonicalize_expr, unescape_char, Expr, IntValue, Output};
|
||||
use crate::num::{
|
||||
finish_parsing_base, finish_parsing_float, finish_parsing_num, FloatWidth, IntWidth, NumWidth,
|
||||
NumericBound, ParsedNumResult,
|
||||
};
|
||||
use crate::scope::Scope;
|
||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
|
@ -9,6 +12,7 @@ use roc_parse::pattern::PatternType;
|
|||
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
|
||||
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||
/// codegen can generate a runtime error if this pattern is reached.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -25,9 +29,15 @@ pub enum Pattern {
|
|||
ext_var: Variable,
|
||||
destructs: Vec<Loc<RecordDestruct>>,
|
||||
},
|
||||
IntLiteral(Variable, Box<str>, i64),
|
||||
NumLiteral(Variable, Box<str>, i64),
|
||||
FloatLiteral(Variable, Box<str>, f64),
|
||||
NumLiteral(Variable, Box<str>, IntValue, NumericBound<NumWidth>),
|
||||
IntLiteral(
|
||||
Variable,
|
||||
Variable,
|
||||
Box<str>,
|
||||
IntValue,
|
||||
NumericBound<IntWidth>,
|
||||
),
|
||||
FloatLiteral(Variable, Variable, Box<str>, f64, NumericBound<FloatWidth>),
|
||||
StrLiteral(Box<str>),
|
||||
Underscore,
|
||||
|
||||
|
@ -85,9 +95,9 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
|
|||
}
|
||||
}
|
||||
|
||||
NumLiteral(_, _, _)
|
||||
| IntLiteral(_, _, _)
|
||||
| FloatLiteral(_, _, _)
|
||||
NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| MalformedPattern(_, _)
|
||||
|
@ -184,13 +194,19 @@ pub fn canonicalize_pattern<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
FloatLiteral(str) => match pattern_type {
|
||||
&FloatLiteral(str) => match pattern_type {
|
||||
WhenBranch => match finish_parsing_float(str) {
|
||||
Err(_error) => {
|
||||
let problem = MalformedPatternProblem::MalformedFloat;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(float) => Pattern::FloatLiteral(var_store.fresh(), (*str).into(), float),
|
||||
Ok((float, bound)) => Pattern::FloatLiteral(
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
(str).into(),
|
||||
float,
|
||||
bound,
|
||||
),
|
||||
},
|
||||
ptype => unsupported_pattern(env, ptype, region),
|
||||
},
|
||||
|
@ -200,32 +216,58 @@ pub fn canonicalize_pattern<'a>(
|
|||
TopLevelDef | DefExpr => bad_underscore(env, region),
|
||||
},
|
||||
|
||||
NumLiteral(str) => match pattern_type {
|
||||
WhenBranch => match finish_parsing_int(str) {
|
||||
&NumLiteral(str) => match pattern_type {
|
||||
WhenBranch => match finish_parsing_num(str) {
|
||||
Err(_error) => {
|
||||
let problem = MalformedPatternProblem::MalformedInt;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(int) => Pattern::NumLiteral(var_store.fresh(), (*str).into(), int),
|
||||
Ok(ParsedNumResult::UnknownNum(int)) => {
|
||||
Pattern::NumLiteral(var_store.fresh(), (str).into(), int, NumericBound::None)
|
||||
}
|
||||
Ok(ParsedNumResult::Int(int, bound)) => Pattern::IntLiteral(
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
(str).into(),
|
||||
int,
|
||||
bound,
|
||||
),
|
||||
Ok(ParsedNumResult::Float(float, bound)) => Pattern::FloatLiteral(
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
(str).into(),
|
||||
float,
|
||||
bound,
|
||||
),
|
||||
},
|
||||
ptype => unsupported_pattern(env, ptype, region),
|
||||
},
|
||||
|
||||
NonBase10Literal {
|
||||
&NonBase10Literal {
|
||||
string,
|
||||
base,
|
||||
is_negative,
|
||||
} => match pattern_type {
|
||||
WhenBranch => match finish_parsing_base(string, *base, *is_negative) {
|
||||
WhenBranch => match finish_parsing_base(string, base, is_negative) {
|
||||
Err(_error) => {
|
||||
let problem = MalformedPatternProblem::MalformedBase(*base);
|
||||
let problem = MalformedPatternProblem::MalformedBase(base);
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(int) => {
|
||||
let sign_str = if *is_negative { "-" } else { "" };
|
||||
Ok((IntValue::U128(_), _)) if is_negative => {
|
||||
// Can't negate a u128; that doesn't fit in any integer literal type we support.
|
||||
let problem = MalformedPatternProblem::MalformedInt;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok((int, bound)) => {
|
||||
let sign_str = if is_negative { "-" } else { "" };
|
||||
let int_str = format!("{}{}", sign_str, int.to_string()).into_boxed_str();
|
||||
let i = if *is_negative { -int } else { int };
|
||||
Pattern::IntLiteral(var_store.fresh(), int_str, i)
|
||||
let i = match int {
|
||||
// Safety: this is fine because I128::MAX = |I128::MIN| - 1
|
||||
IntValue::I128(n) if is_negative => IntValue::I128(-n),
|
||||
IntValue::I128(n) => IntValue::I128(n),
|
||||
IntValue::U128(_) => unreachable!(),
|
||||
};
|
||||
Pattern::IntLiteral(var_store.fresh(), var_store.fresh(), int_str, i, bound)
|
||||
}
|
||||
},
|
||||
ptype => unsupported_pattern(env, ptype, region),
|
||||
|
@ -473,9 +515,9 @@ fn add_bindings_from_patterns(
|
|||
answer.push((*symbol, *region));
|
||||
}
|
||||
}
|
||||
NumLiteral(_, _, _)
|
||||
| IntLiteral(_, _, _)
|
||||
| FloatLiteral(_, _, _)
|
||||
NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| MalformedPattern(_, _)
|
||||
|
|
|
@ -15,7 +15,7 @@ mod test_can {
|
|||
use crate::helpers::{can_expr_with, test_home, CanExprOut};
|
||||
use bumpalo::Bump;
|
||||
use roc_can::expr::Expr::{self, *};
|
||||
use roc_can::expr::{ClosureData, Recursive};
|
||||
use roc_can::expr::{ClosureData, IntValue, Recursive};
|
||||
use roc_problem::can::{CycleEntry, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
|
||||
use roc_region::all::{Position, Region};
|
||||
use std::{f64, i64};
|
||||
|
@ -32,7 +32,7 @@ mod test_can {
|
|||
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||
|
||||
match actual_out.loc_expr.value {
|
||||
Expr::Float(_, _, _, actual) => {
|
||||
Expr::Float(_, _, _, actual, _) => {
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
actual => {
|
||||
|
@ -46,8 +46,8 @@ mod test_can {
|
|||
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||
|
||||
match actual_out.loc_expr.value {
|
||||
Expr::Int(_, _, _, actual) => {
|
||||
assert_eq!(expected, actual);
|
||||
Expr::Int(_, _, _, actual, _) => {
|
||||
assert_eq!(IntValue::I128(expected), actual);
|
||||
}
|
||||
actual => {
|
||||
panic!("Expected an Int *, but got: {:?}", actual);
|
||||
|
@ -55,13 +55,13 @@ mod test_can {
|
|||
}
|
||||
}
|
||||
|
||||
fn assert_can_num(input: &str, expected: i64) {
|
||||
fn assert_can_num(input: &str, expected: i128) {
|
||||
let arena = Bump::new();
|
||||
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||
|
||||
match actual_out.loc_expr.value {
|
||||
Expr::Num(_, _, actual) => {
|
||||
assert_eq!(expected, actual);
|
||||
Expr::Num(_, _, actual, _) => {
|
||||
assert_eq!(IntValue::I128(expected), actual);
|
||||
}
|
||||
actual => {
|
||||
panic!("Expected a Num, but got: {:?}", actual);
|
||||
|
@ -79,7 +79,7 @@ mod test_can {
|
|||
fn int_too_large() {
|
||||
use roc_parse::ast::Base;
|
||||
|
||||
let string = (i64::MAX as i128 + 1).to_string();
|
||||
let string = "340_282_366_920_938_463_463_374_607_431_768_211_456".to_string();
|
||||
|
||||
assert_can(
|
||||
&string.clone(),
|
||||
|
@ -96,7 +96,7 @@ mod test_can {
|
|||
fn int_too_small() {
|
||||
use roc_parse::ast::Base;
|
||||
|
||||
let string = (i64::MIN as i128 - 1).to_string();
|
||||
let string = "-170_141_183_460_469_231_731_687_303_715_884_105_729".to_string();
|
||||
|
||||
assert_can(
|
||||
&string.clone(),
|
||||
|
@ -186,12 +186,12 @@ mod test_can {
|
|||
|
||||
#[test]
|
||||
fn num_max() {
|
||||
assert_can_num(&(i64::MAX.to_string()), i64::MAX);
|
||||
assert_can_num(&(i64::MAX.to_string()), i64::MAX.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_min() {
|
||||
assert_can_num(&(i64::MIN.to_string()), i64::MIN);
|
||||
assert_can_num(&(i64::MIN.to_string()), i64::MIN.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use roc_can::constraint::Constraint::{self, *};
|
||||
use roc_can::constraint::LetConstraint;
|
||||
use roc_can::expected::Expected::{self, *};
|
||||
use roc_can::num::{FloatWidth, IntWidth, NumWidth, NumericBound};
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
|
@ -10,28 +11,49 @@ use roc_types::types::Category;
|
|||
use roc_types::types::Reason;
|
||||
use roc_types::types::Type::{self, *};
|
||||
|
||||
pub fn add_numeric_bound_constr(
|
||||
constrs: &mut Vec<Constraint>,
|
||||
num_type: Type,
|
||||
bound: impl TypedNumericBound,
|
||||
region: Region,
|
||||
category: Category,
|
||||
) {
|
||||
if let Some(typ) = bound.concrete_num_type() {
|
||||
constrs.push(Eq(
|
||||
num_type,
|
||||
Expected::ForReason(Reason::NumericLiteralSuffix, typ, region),
|
||||
category,
|
||||
region,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn int_literal(
|
||||
num_var: Variable,
|
||||
precision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
bound: NumericBound<IntWidth>,
|
||||
) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::IntLiteral;
|
||||
|
||||
exists(
|
||||
vec![num_var],
|
||||
And(vec![
|
||||
Eq(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_int(Type::Variable(precision_var)), region),
|
||||
Category::Int,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Int, region),
|
||||
]),
|
||||
)
|
||||
let mut constrs = Vec::with_capacity(3);
|
||||
// Always add the bound first; this improves the resolved type quality in case it's an alias
|
||||
// like "U8".
|
||||
add_numeric_bound_constr(&mut constrs, num_type.clone(), bound, region, Category::Num);
|
||||
constrs.extend(vec![
|
||||
Eq(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_int(Type::Variable(precision_var)), region),
|
||||
Category::Int,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Int, region),
|
||||
]);
|
||||
|
||||
exists(vec![num_var], And(constrs))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -40,22 +62,46 @@ pub fn float_literal(
|
|||
precision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
bound: NumericBound<FloatWidth>,
|
||||
) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::FloatLiteral;
|
||||
|
||||
exists(
|
||||
vec![num_var, precision_var],
|
||||
And(vec![
|
||||
Eq(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_float(Type::Variable(precision_var)), region),
|
||||
Category::Float,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Float, region),
|
||||
]),
|
||||
)
|
||||
let mut constrs = Vec::with_capacity(3);
|
||||
add_numeric_bound_constr(
|
||||
&mut constrs,
|
||||
num_type.clone(),
|
||||
bound,
|
||||
region,
|
||||
Category::Float,
|
||||
);
|
||||
constrs.extend(vec![
|
||||
Eq(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_float(Type::Variable(precision_var)), region),
|
||||
Category::Float,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Float, region),
|
||||
]);
|
||||
|
||||
exists(vec![num_var, precision_var], And(constrs))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_literal(
|
||||
num_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
bound: NumericBound<NumWidth>,
|
||||
) -> Constraint {
|
||||
let num_type = crate::builtins::num_num(Type::Variable(num_var));
|
||||
|
||||
let mut constrs = Vec::with_capacity(3);
|
||||
add_numeric_bound_constr(&mut constrs, num_type.clone(), bound, region, Category::Num);
|
||||
constrs.extend(vec![Eq(num_type, expected, Category::Num, region)]);
|
||||
|
||||
exists(vec![num_var], And(constrs))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -148,6 +194,57 @@ pub fn num_int(range: Type) -> Type {
|
|||
)
|
||||
}
|
||||
|
||||
macro_rules! num_types {
|
||||
// Represent
|
||||
// num_u8 ~ U8 : Num Integer Unsigned8 = @Num (@Integer (@Unsigned8))
|
||||
// int_u8 ~ Integer Unsigned8 = @Integer (@Unsigned8)
|
||||
//
|
||||
// num_f32 ~ F32 : Num FloaingPoint Binary32 = @Num (@FloaingPoint (@Binary32))
|
||||
// float_f32 ~ FloatingPoint Binary32 = @FloatingPoint (@Binary32)
|
||||
// and so on, for all numeric types.
|
||||
($($num_fn:ident, $sub_fn:ident, $num_type:ident, $alias:path, $inner_alias:path, $inner_private_tag:path)*) => {
|
||||
$(
|
||||
#[inline(always)]
|
||||
fn $sub_fn() -> Type {
|
||||
builtin_alias(
|
||||
$inner_alias,
|
||||
vec![],
|
||||
Box::new(Type::TagUnion(
|
||||
vec![(TagName::Private($inner_private_tag), vec![])],
|
||||
Box::new(Type::EmptyTagUnion)
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn $num_fn() -> Type {
|
||||
builtin_alias(
|
||||
$alias,
|
||||
vec![],
|
||||
Box::new($num_type($sub_fn()))
|
||||
)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
num_types! {
|
||||
num_u8, int_u8, num_int, Symbol::NUM_U8, Symbol::NUM_UNSIGNED8, Symbol::NUM_AT_UNSIGNED8
|
||||
num_u16, int_u16, num_int, Symbol::NUM_U16, Symbol::NUM_UNSIGNED16, Symbol::NUM_AT_UNSIGNED16
|
||||
num_u32, int_u32, num_int, Symbol::NUM_U32, Symbol::NUM_UNSIGNED32, Symbol::NUM_AT_UNSIGNED32
|
||||
num_u64, int_u64, num_int, Symbol::NUM_U64, Symbol::NUM_UNSIGNED64, Symbol::NUM_AT_UNSIGNED64
|
||||
num_u128, int_u128, num_int, Symbol::NUM_U128, Symbol::NUM_UNSIGNED128, Symbol::NUM_AT_UNSIGNED128
|
||||
num_i8, int_i8, num_int, Symbol::NUM_I8, Symbol::NUM_SIGNED8, Symbol::NUM_AT_SIGNED8
|
||||
num_i16, int_i16, num_int, Symbol::NUM_I16, Symbol::NUM_SIGNED16, Symbol::NUM_AT_SIGNED16
|
||||
num_i32, int_i32, num_int, Symbol::NUM_I32, Symbol::NUM_SIGNED32, Symbol::NUM_AT_SIGNED32
|
||||
num_i64, int_i64, num_int, Symbol::NUM_I64, Symbol::NUM_SIGNED64, Symbol::NUM_AT_SIGNED64
|
||||
num_i128, int_i128, num_int, Symbol::NUM_I128, Symbol::NUM_SIGNED128, Symbol::NUM_AT_SIGNED128
|
||||
num_nat, int_nat, num_int, Symbol::NUM_NAT, Symbol::NUM_NATURAL, Symbol::NUM_AT_NATURAL
|
||||
num_dec, float_dec, num_float, Symbol::NUM_DEC, Symbol::NUM_DECIMAL, Symbol::NUM_AT_DECIMAL
|
||||
num_f32, float_f32, num_float, Symbol::NUM_F32, Symbol::NUM_BINARY32, Symbol::NUM_AT_BINARY32
|
||||
num_f64, float_f64, num_float, Symbol::NUM_F64, Symbol::NUM_BINARY64, Symbol::NUM_AT_BINARY64
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_signed64() -> Type {
|
||||
let alias_content = Type::TagUnion(
|
||||
|
@ -188,3 +285,55 @@ pub fn num_num(typ: Type) -> Type {
|
|||
Box::new(alias_content),
|
||||
)
|
||||
}
|
||||
|
||||
pub trait TypedNumericBound {
|
||||
/// Get a concrete type for this number, if one exists.
|
||||
/// Returns `None` e.g. if the bound is open, like `Int *`.
|
||||
fn concrete_num_type(&self) -> Option<Type>;
|
||||
}
|
||||
|
||||
impl TypedNumericBound for NumericBound<IntWidth> {
|
||||
fn concrete_num_type(&self) -> Option<Type> {
|
||||
match self {
|
||||
NumericBound::None => None,
|
||||
NumericBound::Exact(w) => Some(match w {
|
||||
IntWidth::U8 => num_u8(),
|
||||
IntWidth::U16 => num_u16(),
|
||||
IntWidth::U32 => num_u32(),
|
||||
IntWidth::U64 => num_u64(),
|
||||
IntWidth::U128 => num_u128(),
|
||||
IntWidth::I8 => num_i8(),
|
||||
IntWidth::I16 => num_i16(),
|
||||
IntWidth::I32 => num_i32(),
|
||||
IntWidth::I64 => num_i64(),
|
||||
IntWidth::I128 => num_i128(),
|
||||
IntWidth::Nat => num_nat(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedNumericBound for NumericBound<FloatWidth> {
|
||||
fn concrete_num_type(&self) -> Option<Type> {
|
||||
match self {
|
||||
NumericBound::None => None,
|
||||
NumericBound::Exact(w) => Some(match w {
|
||||
FloatWidth::Dec => num_dec(),
|
||||
FloatWidth::F32 => num_f32(),
|
||||
FloatWidth::F64 => num_f64(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedNumericBound for NumericBound<NumWidth> {
|
||||
fn concrete_num_type(&self) -> Option<Type> {
|
||||
match self {
|
||||
NumericBound::None => None,
|
||||
NumericBound::Exact(NumWidth::Int(iw)) => NumericBound::Exact(*iw).concrete_num_type(),
|
||||
NumericBound::Exact(NumWidth::Float(fw)) => {
|
||||
NumericBound::Exact(*fw).concrete_num_type()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::builtins::{empty_list_type, float_literal, int_literal, list_type, str_type};
|
||||
use crate::builtins::{
|
||||
empty_list_type, float_literal, int_literal, list_type, num_literal, str_type,
|
||||
};
|
||||
use crate::pattern::{constrain_pattern, PatternState};
|
||||
use roc_can::annotation::IntroducedVariables;
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
|
@ -96,17 +98,11 @@ pub fn constrain_expr(
|
|||
expected: Expected<Type>,
|
||||
) -> Constraint {
|
||||
match expr {
|
||||
Int(var, precision, _, _) => int_literal(*var, *precision, expected, region),
|
||||
Num(var, _, _) => exists(
|
||||
vec![*var],
|
||||
Eq(
|
||||
crate::builtins::num_num(Type::Variable(*var)),
|
||||
expected,
|
||||
Category::Num,
|
||||
region,
|
||||
),
|
||||
),
|
||||
Float(var, precision, _, _) => float_literal(*var, *precision, expected, region),
|
||||
&Int(var, precision, _, _, bound) => int_literal(var, precision, expected, region, bound),
|
||||
&Num(var, _, _, bound) => num_literal(var, expected, region, bound),
|
||||
&Float(var, precision, _, _, bound) => {
|
||||
float_literal(var, precision, expected, region, bound)
|
||||
}
|
||||
EmptyRecord => constrain_empty_record(region, expected),
|
||||
Expr::Record { record_var, fields } => {
|
||||
if fields.is_empty() {
|
||||
|
|
|
@ -55,9 +55,9 @@ fn headers_from_annotation_help(
|
|||
Underscore
|
||||
| MalformedPattern(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
| NumLiteral(_, _, _)
|
||||
| IntLiteral(_, _, _)
|
||||
| FloatLiteral(_, _, _)
|
||||
| NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| StrLiteral(_) => true,
|
||||
|
||||
RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() {
|
||||
|
@ -178,31 +178,83 @@ pub fn constrain_pattern(
|
|||
);
|
||||
}
|
||||
|
||||
NumLiteral(var, _, _) => {
|
||||
state.vars.push(*var);
|
||||
&NumLiteral(var, _, _, bound) => {
|
||||
state.vars.push(var);
|
||||
|
||||
let num_type = builtins::num_num(Type::Variable(var));
|
||||
|
||||
builtins::add_numeric_bound_constr(
|
||||
&mut state.constraints,
|
||||
num_type.clone(),
|
||||
bound,
|
||||
region,
|
||||
Category::Num,
|
||||
);
|
||||
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Num,
|
||||
builtins::num_num(Type::Variable(*var)),
|
||||
num_type,
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
||||
IntLiteral(precision_var, _, _) => {
|
||||
&IntLiteral(num_var, precision_var, _, _, bound) => {
|
||||
// First constraint on the free num var; this improves the resolved type quality in
|
||||
// case the bound is an alias.
|
||||
builtins::add_numeric_bound_constr(
|
||||
&mut state.constraints,
|
||||
Type::Variable(num_var),
|
||||
bound,
|
||||
region,
|
||||
Category::Int,
|
||||
);
|
||||
|
||||
// Link the free num var with the int var and our expectation.
|
||||
let int_type = builtins::num_int(Type::Variable(precision_var));
|
||||
|
||||
state.constraints.push(Constraint::Eq(
|
||||
Type::Variable(num_var),
|
||||
Expected::NoExpectation(int_type),
|
||||
Category::Int,
|
||||
region,
|
||||
));
|
||||
|
||||
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Int,
|
||||
builtins::num_int(Type::Variable(*precision_var)),
|
||||
Type::Variable(num_var),
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
||||
FloatLiteral(precision_var, _, _) => {
|
||||
&FloatLiteral(num_var, precision_var, _, _, bound) => {
|
||||
// First constraint on the free num var; this improves the resolved type quality in
|
||||
// case the bound is an alias.
|
||||
builtins::add_numeric_bound_constr(
|
||||
&mut state.constraints,
|
||||
Type::Variable(num_var),
|
||||
bound,
|
||||
region,
|
||||
Category::Float,
|
||||
);
|
||||
|
||||
// Link the free num var with the float var and our expectation.
|
||||
let float_type = builtins::num_float(Type::Variable(precision_var));
|
||||
|
||||
state.constraints.push(Constraint::Eq(
|
||||
Type::Variable(num_var),
|
||||
Expected::NoExpectation(float_type),
|
||||
Category::Float,
|
||||
region,
|
||||
));
|
||||
|
||||
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Float,
|
||||
builtins::num_float(Type::Variable(*precision_var)),
|
||||
Type::Variable(num_var),
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
|
|
@ -46,7 +46,9 @@ impl<'a> Formattable for Def<'a> {
|
|||
indent + INDENT,
|
||||
);
|
||||
} else {
|
||||
buf.push_str(" : ");
|
||||
buf.spaces(1);
|
||||
buf.push_str(":");
|
||||
buf.spaces(1);
|
||||
loc_annotation.format_with_options(
|
||||
buf,
|
||||
Parens::NotNeeded,
|
||||
|
|
|
@ -27,8 +27,8 @@ impl<'a> Formattable for Expr<'a> {
|
|||
}
|
||||
|
||||
// These expressions never have newlines
|
||||
Float(_)
|
||||
| Num(_)
|
||||
Float(..)
|
||||
| Num(..)
|
||||
| NonBase10Int { .. }
|
||||
| Access(_, _)
|
||||
| AccessorFunction(_)
|
||||
|
@ -196,17 +196,25 @@ impl<'a> Formattable for Expr<'a> {
|
|||
buf.push(')');
|
||||
}
|
||||
}
|
||||
Num(string) | Float(string) | GlobalTag(string) | PrivateTag(string) => {
|
||||
&Num(string) => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(string);
|
||||
}
|
||||
&Float(string) => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(string);
|
||||
}
|
||||
GlobalTag(string) | PrivateTag(string) => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(string)
|
||||
}
|
||||
NonBase10Int {
|
||||
&NonBase10Int {
|
||||
base,
|
||||
string,
|
||||
is_negative,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
if *is_negative {
|
||||
if is_negative {
|
||||
buf.push('-');
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ use crate::spaces::{fmt_default_spaces, fmt_spaces, INDENT};
|
|||
use crate::Buf;
|
||||
use roc_parse::ast::{Collection, Module, Spaced};
|
||||
use roc_parse::header::{
|
||||
AppHeader, Effects, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName,
|
||||
PackageEntry, PackageName, PlatformHeader, PlatformRequires, To, TypedIdent,
|
||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
|
||||
PackageName, PlatformHeader, PlatformRequires, To, TypedIdent,
|
||||
};
|
||||
use roc_parse::ident::UppercaseIdent;
|
||||
use roc_region::all::Loc;
|
||||
|
@ -170,8 +170,6 @@ pub fn fmt_platform_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a PlatformHe
|
|||
buf.push_str("provides");
|
||||
fmt_default_spaces(buf, header.after_provides, indent);
|
||||
fmt_provides(buf, header.provides, None, indent);
|
||||
|
||||
fmt_effects(buf, &header.effects, indent);
|
||||
}
|
||||
|
||||
fn fmt_requires<'a, 'buf>(buf: &mut Buf<'buf>, requires: &PlatformRequires<'a>, indent: u16) {
|
||||
|
@ -183,29 +181,6 @@ fn fmt_requires<'a, 'buf>(buf: &mut Buf<'buf>, requires: &PlatformRequires<'a>,
|
|||
buf.push_str(" }");
|
||||
}
|
||||
|
||||
fn fmt_effects<'a, 'buf>(buf: &mut Buf<'buf>, effects: &Effects<'a>, indent: u16) {
|
||||
fmt_default_spaces(buf, effects.spaces_before_effects_keyword, indent);
|
||||
buf.indent(indent);
|
||||
buf.push_str("effects");
|
||||
fmt_default_spaces(buf, effects.spaces_after_effects_keyword, indent);
|
||||
|
||||
buf.indent(indent);
|
||||
buf.push_str(effects.effect_shortname);
|
||||
buf.push('.');
|
||||
buf.push_str(effects.effect_type_name);
|
||||
|
||||
fmt_default_spaces(buf, effects.spaces_after_type_name, indent);
|
||||
|
||||
fmt_collection(
|
||||
buf,
|
||||
indent + INDENT,
|
||||
'{',
|
||||
'}',
|
||||
effects.entries,
|
||||
Newlines::No,
|
||||
)
|
||||
}
|
||||
|
||||
impl<'a> Formattable for TypedIdent<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
false
|
||||
|
|
|
@ -31,9 +31,9 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
| Pattern::GlobalTag(_)
|
||||
| Pattern::PrivateTag(_)
|
||||
| Pattern::Apply(_, _)
|
||||
| Pattern::NumLiteral(_)
|
||||
| Pattern::NumLiteral(..)
|
||||
| Pattern::NonBase10Literal { .. }
|
||||
| Pattern::FloatLiteral(_)
|
||||
| Pattern::FloatLiteral(..)
|
||||
| Pattern::StrLiteral(_)
|
||||
| Pattern::Underscore(_)
|
||||
| Pattern::Malformed(_)
|
||||
|
@ -116,17 +116,17 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
loc_pattern.format(buf, indent);
|
||||
}
|
||||
|
||||
NumLiteral(string) => {
|
||||
&NumLiteral(string) => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(string);
|
||||
}
|
||||
NonBase10Literal {
|
||||
&NonBase10Literal {
|
||||
base,
|
||||
string,
|
||||
is_negative,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
if *is_negative {
|
||||
if is_negative {
|
||||
buf.push('-');
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
|
||||
buf.push_str(string);
|
||||
}
|
||||
FloatLiteral(string) => {
|
||||
&FloatLiteral(string) => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(string);
|
||||
}
|
||||
|
|
|
@ -2675,13 +2675,7 @@ mod test_fmt {
|
|||
exposes [] \
|
||||
packages {} \
|
||||
imports [ Task.{ Task } ] \
|
||||
provides [ mainForHost ] \
|
||||
effects fx.Effect \
|
||||
{ \
|
||||
putLine : Str -> Effect {}, \
|
||||
putInt : I64 -> Effect {}, \
|
||||
getInt : Effect { value : I64, errorCode : [ A, B ], isError : Bool } \
|
||||
}",
|
||||
provides [ mainForHost ]",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -771,11 +771,13 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
|||
env.context.bool_type().const_int(*int as u64, false).into()
|
||||
}
|
||||
Layout::Builtin(Builtin::Int(int_width)) => {
|
||||
int_with_precision(env, *int as i128, *int_width).into()
|
||||
int_with_precision(env, *int, *int_width).into()
|
||||
}
|
||||
_ => panic!("Invalid layout for int literal = {:?}", layout),
|
||||
},
|
||||
|
||||
U128(int) => const_u128(env, *int).into(),
|
||||
|
||||
Float(float) => match layout {
|
||||
Layout::Builtin(Builtin::Float(float_width)) => {
|
||||
float_with_precision(env, *float, *float_width)
|
||||
|
@ -3073,6 +3075,19 @@ fn const_i128<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, value: i128) -> IntValu
|
|||
.const_int_arbitrary_precision(&[a, b])
|
||||
}
|
||||
|
||||
fn const_u128<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, value: u128) -> IntValue<'ctx> {
|
||||
// truncate the lower 64 bits
|
||||
let value = value as u128;
|
||||
let a = value as u64;
|
||||
|
||||
// get the upper 64 bits
|
||||
let b = (value >> 64) as u64;
|
||||
|
||||
env.context
|
||||
.i128_type()
|
||||
.const_int_arbitrary_precision(&[a, b])
|
||||
}
|
||||
|
||||
fn build_switch_ir<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
|
@ -5284,11 +5299,6 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Fix me! I should be different in tests vs. user code!
|
||||
fn expect_failed() {
|
||||
panic!("An expectation failed!");
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn run_low_level<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
@ -5299,7 +5309,6 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
op: LowLevel,
|
||||
args: &[Symbol],
|
||||
update_mode: UpdateMode,
|
||||
// expect_failed: *const (),
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use LowLevel::*;
|
||||
|
||||
|
@ -6063,21 +6072,28 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
match env.target_info.ptr_width() {
|
||||
roc_target::PtrWidth::Bytes8 => {
|
||||
let fn_ptr_type = context
|
||||
.void_type()
|
||||
.fn_type(&[], false)
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
let fn_addr = env
|
||||
.ptr_int()
|
||||
.const_int(expect_failed as *const () as u64, false);
|
||||
let func: PointerValue<'ctx> = bd.build_int_to_ptr(
|
||||
fn_addr,
|
||||
fn_ptr_type,
|
||||
"cast_expect_failed_addr_to_ptr",
|
||||
);
|
||||
let func = env
|
||||
.module
|
||||
.get_function(bitcode::UTILS_EXPECT_FAILED)
|
||||
.unwrap();
|
||||
// TODO get the actual line info instead of
|
||||
// hardcoding as zero!
|
||||
let callable = CallableValue::try_from(func).unwrap();
|
||||
let start_line = context.i32_type().const_int(0, false);
|
||||
let end_line = context.i32_type().const_int(0, false);
|
||||
let start_col = context.i16_type().const_int(0, false);
|
||||
let end_col = context.i16_type().const_int(0, false);
|
||||
|
||||
bd.build_call(callable, &[], "call_expect_failed");
|
||||
bd.build_call(
|
||||
callable,
|
||||
&[
|
||||
start_line.into(),
|
||||
end_line.into(),
|
||||
start_col.into(),
|
||||
end_col.into(),
|
||||
],
|
||||
"call_expect_failed",
|
||||
);
|
||||
|
||||
bd.build_unconditional_branch(then_block);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,43 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
|||
}
|
||||
}
|
||||
|
||||
// roc_memcpy
|
||||
{
|
||||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_memcpy").unwrap();
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let dest_arg = params.next().unwrap();
|
||||
let dest_alignment = 1;
|
||||
let src_arg = params.next().unwrap();
|
||||
let src_alignment = 1;
|
||||
let bytes_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = ctx.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// Call libc memcpy()
|
||||
let _retval = builder
|
||||
.build_memcpy(
|
||||
dest_arg.into_pointer_value(),
|
||||
dest_alignment,
|
||||
src_arg.into_pointer_value(),
|
||||
src_alignment,
|
||||
bytes_arg.into_int_value(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
builder.build_return(None);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
crate::llvm::build::verify_fn(fn_val);
|
||||
}
|
||||
}
|
||||
|
||||
// roc_realloc
|
||||
{
|
||||
let libc_realloc_val = {
|
||||
|
|
|
@ -38,10 +38,23 @@ macro_rules! run_jit_function {
|
|||
}};
|
||||
|
||||
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr) => {{
|
||||
run_jit_function!($lib, $main_fn_name, $ty, $transform, $errors, &[])
|
||||
}};
|
||||
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr, $expect_failures:expr) => {{
|
||||
use inkwell::context::Context;
|
||||
use roc_builtins::bitcode;
|
||||
use roc_gen_llvm::run_roc::RocCallResult;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
struct Failure {
|
||||
start_line: u32,
|
||||
end_line: u32,
|
||||
start_col: u16,
|
||||
end_col: u16,
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let main: libloading::Symbol<unsafe extern "C" fn(*mut RocCallResult<$ty>) -> ()> =
|
||||
$lib.get($main_fn_name.as_bytes())
|
||||
|
@ -49,11 +62,50 @@ macro_rules! run_jit_function {
|
|||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||
.expect("errored");
|
||||
|
||||
let mut result = MaybeUninit::uninit();
|
||||
#[repr(C)]
|
||||
struct Failures {
|
||||
failures: *const Failure,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
main(result.as_mut_ptr());
|
||||
impl Drop for Failures {
|
||||
fn drop(&mut self) {
|
||||
use std::alloc::{dealloc, Layout};
|
||||
use std::mem;
|
||||
|
||||
match result.assume_init().into() {
|
||||
unsafe {
|
||||
let layout = Layout::from_size_align_unchecked(
|
||||
mem::size_of::<Failure>(),
|
||||
mem::align_of::<Failure>(),
|
||||
);
|
||||
|
||||
dealloc(self.failures as *mut u8, layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let get_expect_failures: libloading::Symbol<unsafe extern "C" fn() -> Failures> = $lib
|
||||
.get(bitcode::UTILS_GET_EXPECT_FAILURES.as_bytes())
|
||||
.ok()
|
||||
.ok_or(format!(
|
||||
"Unable to JIT compile `{}`",
|
||||
bitcode::UTILS_GET_EXPECT_FAILURES
|
||||
))
|
||||
.expect("errored");
|
||||
let mut main_result = MaybeUninit::uninit();
|
||||
|
||||
main(main_result.as_mut_ptr());
|
||||
let failures = get_expect_failures();
|
||||
|
||||
if failures.count > 0 {
|
||||
// TODO tell the user about the failures!
|
||||
let failures =
|
||||
unsafe { core::slice::from_raw_parts(failures.failures, failures.count) };
|
||||
|
||||
panic!("Failed with {} failures. Failures: ", failures.len());
|
||||
}
|
||||
|
||||
match main_result.assume_init().into() {
|
||||
Ok(success) => {
|
||||
// only if there are no exceptions thrown, check for errors
|
||||
assert!($errors.is_empty(), "Encountered errors:\n{}", $errors);
|
||||
|
|
|
@ -13,7 +13,7 @@ use roc_constrain::module::{
|
|||
constrain_imports, pre_constrain_imports, ConstrainableImports, Import,
|
||||
};
|
||||
use roc_constrain::module::{constrain_module, ExposedModuleTypes, SubsByModule};
|
||||
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName, TagName};
|
||||
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
|
||||
use roc_module::symbol::{
|
||||
IdentIds, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, PackageQualified,
|
||||
Symbol,
|
||||
|
@ -23,9 +23,9 @@ use roc_mono::ir::{
|
|||
UpdateModeIds,
|
||||
};
|
||||
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
|
||||
use roc_parse::ast::{self, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation};
|
||||
use roc_parse::header::PackageName;
|
||||
use roc_parse::ast::{self, ExtractSpaces, Spaced, StrLiteral};
|
||||
use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent};
|
||||
use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName};
|
||||
use roc_parse::ident::UppercaseIdent;
|
||||
use roc_parse::module::module_defs;
|
||||
use roc_parse::parser::{FileError, Parser, SyntaxError};
|
||||
|
@ -35,7 +35,7 @@ use roc_solve::solve;
|
|||
use roc_target::TargetInfo;
|
||||
use roc_types::solved_types::Solved;
|
||||
use roc_types::subs::{Subs, VarStore, Variable};
|
||||
use roc_types::types::{Alias, Type};
|
||||
use roc_types::types::Alias;
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::io;
|
||||
|
@ -165,37 +165,6 @@ impl<'a> Dependencies<'a> {
|
|||
output
|
||||
}
|
||||
|
||||
pub fn add_effect_module(
|
||||
&mut self,
|
||||
module_id: ModuleId,
|
||||
dependencies: &MutSet<ModuleId>,
|
||||
goal_phase: Phase,
|
||||
) -> MutSet<(ModuleId, Phase)> {
|
||||
// add dependencies for self
|
||||
// phase i + 1 of a file always depends on phase i being completed
|
||||
{
|
||||
let mut i = 2;
|
||||
|
||||
// platform modules should only start at CanonicalizeAndConstrain
|
||||
debug_assert!(PHASES[i] == Phase::CanonicalizeAndConstrain);
|
||||
while PHASES[i] < goal_phase {
|
||||
self.add_dependency_help(module_id, module_id, PHASES[i + 1], PHASES[i]);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
self.add_to_status(module_id, goal_phase);
|
||||
|
||||
let mut output = MutSet::default();
|
||||
|
||||
// all the dependencies can be loaded
|
||||
for dep in dependencies {
|
||||
output.insert((*dep, Phase::LoadHeader));
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn add_to_status(&mut self, module_id: ModuleId, goal_phase: Phase) {
|
||||
for phase in PHASES.iter() {
|
||||
if *phase > goal_phase {
|
||||
|
@ -674,24 +643,7 @@ struct ModuleHeader<'a> {
|
|||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
parse_state: roc_parse::state::State<'a>,
|
||||
module_timing: ModuleTiming,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum HeaderFor<'a> {
|
||||
App {
|
||||
to_platform: To<'a>,
|
||||
},
|
||||
PkgConfig {
|
||||
/// usually `pf`
|
||||
config_shorthand: &'a str,
|
||||
/// the type scheme of the main function (required by the platform)
|
||||
/// (currently unused)
|
||||
#[allow(dead_code)]
|
||||
platform_main_type: TypedIdent<'a>,
|
||||
/// provided symbol to host (commonly `mainForHost`)
|
||||
main_for_host: Symbol,
|
||||
},
|
||||
Interface,
|
||||
header_for: HeaderFor<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -774,7 +726,6 @@ impl<'a> MonomorphizedModule<'a> {
|
|||
#[derive(Debug)]
|
||||
struct ParsedModule<'a> {
|
||||
module_id: ModuleId,
|
||||
module_name: ModuleNameEnum<'a>,
|
||||
module_path: PathBuf,
|
||||
src: &'a str,
|
||||
module_timing: ModuleTiming,
|
||||
|
@ -783,6 +734,8 @@ struct ParsedModule<'a> {
|
|||
exposed_ident_ids: IdentIds,
|
||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
parsed_defs: &'a [Loc<roc_parse::ast::Def<'a>>],
|
||||
module_name: ModuleNameEnum<'a>,
|
||||
header_for: HeaderFor<'a>,
|
||||
}
|
||||
|
||||
/// A message sent out _from_ a worker thread,
|
||||
|
@ -790,19 +743,13 @@ struct ParsedModule<'a> {
|
|||
#[derive(Debug)]
|
||||
enum Msg<'a> {
|
||||
Many(Vec<Msg<'a>>),
|
||||
Header(ModuleHeader<'a>, HeaderFor<'a>),
|
||||
Header(ModuleHeader<'a>),
|
||||
Parsed(ParsedModule<'a>),
|
||||
CanonicalizedAndConstrained {
|
||||
constrained_module: ConstrainedModule,
|
||||
canonicalization_problems: Vec<roc_problem::can::Problem>,
|
||||
module_docs: Option<ModuleDocumentation>,
|
||||
},
|
||||
MadeEffectModule {
|
||||
type_shortname: &'a str,
|
||||
constrained_module: ConstrainedModule,
|
||||
canonicalization_problems: Vec<roc_problem::can::Problem>,
|
||||
module_docs: ModuleDocumentation,
|
||||
},
|
||||
SolvedTypes {
|
||||
module_id: ModuleId,
|
||||
ident_ids: IdentIds,
|
||||
|
@ -861,6 +808,7 @@ enum PlatformPath<'a> {
|
|||
NotSpecified,
|
||||
Valid(To<'a>),
|
||||
RootIsInterface,
|
||||
RootIsHosted,
|
||||
RootIsPkgConfig,
|
||||
}
|
||||
|
||||
|
@ -1695,7 +1643,7 @@ fn update<'a>(
|
|||
|
||||
Ok(state)
|
||||
}
|
||||
Header(header, header_extra) => {
|
||||
Header(header) => {
|
||||
use HeaderFor::*;
|
||||
|
||||
log!("loaded header for {:?}", header.module_id);
|
||||
|
@ -1712,13 +1660,13 @@ fn update<'a>(
|
|||
|
||||
if let PkgConfig {
|
||||
config_shorthand, ..
|
||||
} = header_extra
|
||||
} = header.header_for
|
||||
{
|
||||
work.extend(state.dependencies.notify_package(config_shorthand));
|
||||
}
|
||||
}
|
||||
|
||||
match header_extra {
|
||||
match header.header_for {
|
||||
App { to_platform } => {
|
||||
debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified));
|
||||
state.platform_path = PlatformPath::Valid(to_platform);
|
||||
|
@ -1742,6 +1690,12 @@ fn update<'a>(
|
|||
state.platform_path = PlatformPath::RootIsInterface;
|
||||
}
|
||||
}
|
||||
Hosted { .. } => {
|
||||
if header.is_root_module {
|
||||
debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified));
|
||||
state.platform_path = PlatformPath::RootIsHosted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// store an ID to name mapping, so we know the file to read when fetching dependencies' headers
|
||||
|
@ -1863,57 +1817,6 @@ fn update<'a>(
|
|||
|
||||
Ok(state)
|
||||
}
|
||||
MadeEffectModule {
|
||||
constrained_module,
|
||||
canonicalization_problems,
|
||||
module_docs,
|
||||
type_shortname,
|
||||
} => {
|
||||
let module_id = constrained_module.module.module_id;
|
||||
|
||||
log!("made effect module for {:?}", module_id);
|
||||
state
|
||||
.module_cache
|
||||
.can_problems
|
||||
.insert(module_id, canonicalization_problems);
|
||||
|
||||
state
|
||||
.module_cache
|
||||
.documentation
|
||||
.insert(module_id, module_docs);
|
||||
|
||||
state
|
||||
.module_cache
|
||||
.aliases
|
||||
.insert(module_id, constrained_module.module.aliases.clone());
|
||||
|
||||
state
|
||||
.module_cache
|
||||
.constrained
|
||||
.insert(module_id, constrained_module);
|
||||
|
||||
let mut work = state.dependencies.add_effect_module(
|
||||
module_id,
|
||||
&MutSet::default(),
|
||||
state.goal_phase,
|
||||
);
|
||||
|
||||
work.extend(state.dependencies.notify_package(type_shortname));
|
||||
|
||||
work.extend(state.dependencies.notify(module_id, Phase::LoadHeader));
|
||||
|
||||
work.extend(state.dependencies.notify(module_id, Phase::Parse));
|
||||
|
||||
work.extend(
|
||||
state
|
||||
.dependencies
|
||||
.notify(module_id, Phase::CanonicalizeAndConstrain),
|
||||
);
|
||||
|
||||
start_tasks(arena, &mut state, work, injector, worker_listeners)?;
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
SolvedTypes {
|
||||
module_id,
|
||||
ident_ids,
|
||||
|
@ -2366,14 +2269,10 @@ fn load_pkg_config<'a>(
|
|||
|
||||
// Insert the first entries for this module's timings
|
||||
let mut pkg_module_timing = ModuleTiming::new(module_start_time);
|
||||
let mut effect_module_timing = ModuleTiming::new(module_start_time);
|
||||
|
||||
pkg_module_timing.read_roc_file = file_io_duration;
|
||||
pkg_module_timing.parse_header = parse_header_duration;
|
||||
|
||||
effect_module_timing.read_roc_file = file_io_duration;
|
||||
effect_module_timing.parse_header = parse_header_duration;
|
||||
|
||||
match parsed {
|
||||
Ok((ast::Module::Interface { header }, _parse_state)) => {
|
||||
Err(LoadingProblem::UnexpectedHeader(format!(
|
||||
|
@ -2396,23 +2295,13 @@ fn load_pkg_config<'a>(
|
|||
filename,
|
||||
parser_state,
|
||||
module_ids.clone(),
|
||||
ident_ids_by_module.clone(),
|
||||
ident_ids_by_module,
|
||||
&header,
|
||||
pkg_module_timing,
|
||||
)
|
||||
.1;
|
||||
|
||||
let effects_module_msg = fabricate_effects_module(
|
||||
arena,
|
||||
header.effects.effect_shortname,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
header,
|
||||
effect_module_timing,
|
||||
)
|
||||
.1;
|
||||
|
||||
Ok(Msg::Many(vec![effects_module_msg, pkg_config_module_msg]))
|
||||
Ok(pkg_config_module_msg)
|
||||
}
|
||||
Ok((ast::Module::Hosted { header }, _parse_state)) => {
|
||||
Err(LoadingProblem::UnexpectedHeader(format!(
|
||||
|
@ -2551,7 +2440,33 @@ fn parse_header<'a>(
|
|||
packages: &[],
|
||||
exposes: unspace(arena, header.exposes.items),
|
||||
imports: unspace(arena, header.imports.items),
|
||||
to_platform: None,
|
||||
extra: HeaderFor::Interface,
|
||||
};
|
||||
|
||||
Ok(send_header(
|
||||
info,
|
||||
parse_state,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
module_timing,
|
||||
))
|
||||
}
|
||||
Ok((ast::Module::Hosted { header }, parse_state)) => {
|
||||
let info = HeaderInfo {
|
||||
loc_name: Loc {
|
||||
region: header.name.region,
|
||||
value: ModuleNameEnum::Hosted(header.name.value),
|
||||
},
|
||||
filename,
|
||||
is_root_module,
|
||||
opt_shorthand,
|
||||
packages: &[],
|
||||
exposes: unspace(arena, header.exposes.items),
|
||||
imports: unspace(arena, header.imports.items),
|
||||
extra: HeaderFor::Hosted {
|
||||
generates: header.generates,
|
||||
generates_with: unspace(arena, header.generates_with.items),
|
||||
},
|
||||
};
|
||||
|
||||
Ok(send_header(
|
||||
|
@ -2593,7 +2508,9 @@ fn parse_header<'a>(
|
|||
packages,
|
||||
exposes,
|
||||
imports: unspace(arena, header.imports.items),
|
||||
to_platform: Some(header.to.value),
|
||||
extra: HeaderFor::App {
|
||||
to_platform: header.to.value,
|
||||
},
|
||||
};
|
||||
|
||||
let (module_id, app_module_header_msg) = send_header(
|
||||
|
@ -2661,37 +2578,13 @@ fn parse_header<'a>(
|
|||
To::NewPackage(_package_name) => Ok((module_id, app_module_header_msg)),
|
||||
}
|
||||
}
|
||||
Ok((ast::Module::Platform { header }, _parse_state)) => Ok(fabricate_effects_module(
|
||||
arena,
|
||||
"",
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
header,
|
||||
module_timing,
|
||||
)),
|
||||
Ok((ast::Module::Hosted { header }, parse_state)) => {
|
||||
let info = HeaderInfo {
|
||||
loc_name: Loc {
|
||||
region: header.name.region,
|
||||
value: ModuleNameEnum::Hosted(header.name.value),
|
||||
},
|
||||
filename,
|
||||
is_root_module,
|
||||
opt_shorthand,
|
||||
packages: &[],
|
||||
exposes: unspace(arena, header.exposes.items),
|
||||
imports: unspace(arena, header.imports.items),
|
||||
to_platform: None,
|
||||
};
|
||||
|
||||
Ok(send_header(
|
||||
info,
|
||||
parse_state,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
module_timing,
|
||||
))
|
||||
Ok((ast::Module::Platform { header }, _parse_state)) => {
|
||||
Err(LoadingProblem::UnexpectedHeader(format!(
|
||||
"got an unexpected platform header\n{:?}",
|
||||
header
|
||||
)))
|
||||
}
|
||||
|
||||
Err(fail) => Err(LoadingProblem::ParsingFailed(
|
||||
fail.map_problem(SyntaxError::Header)
|
||||
.into_file_error(filename),
|
||||
|
@ -2760,15 +2653,6 @@ fn load_from_str<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ModuleNameEnum<'a> {
|
||||
/// A filename
|
||||
App(StrLiteral<'a>),
|
||||
Interface(roc_parse::header::ModuleName<'a>),
|
||||
Hosted(roc_parse::header::ModuleName<'a>),
|
||||
PkgConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HeaderInfo<'a> {
|
||||
loc_name: Loc<ModuleNameEnum<'a>>,
|
||||
|
@ -2778,7 +2662,7 @@ struct HeaderInfo<'a> {
|
|||
packages: &'a [Loc<PackageEntry<'a>>],
|
||||
exposes: &'a [Loc<ExposedName<'a>>],
|
||||
imports: &'a [Loc<ImportsEntry<'a>>],
|
||||
to_platform: Option<To<'a>>,
|
||||
extra: HeaderFor<'a>,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -2799,7 +2683,7 @@ fn send_header<'a>(
|
|||
packages,
|
||||
exposes,
|
||||
imports,
|
||||
to_platform,
|
||||
extra,
|
||||
} = info;
|
||||
|
||||
let declared_name: ModuleName = match &loc_name.value {
|
||||
|
@ -2936,11 +2820,6 @@ fn send_header<'a>(
|
|||
// We always need to send these, even if deps is empty,
|
||||
// because the coordinator thread needs to receive this message
|
||||
// to decrement its "pending" count.
|
||||
let extra = match to_platform {
|
||||
Some(to_platform) => HeaderFor::App { to_platform },
|
||||
None => HeaderFor::Interface,
|
||||
};
|
||||
|
||||
let mut package_qualified_imported_modules = MutSet::default();
|
||||
for (pq_module_name, module_id) in &deps_by_name {
|
||||
match pq_module_name {
|
||||
|
@ -2957,24 +2836,22 @@ fn send_header<'a>(
|
|||
|
||||
(
|
||||
home,
|
||||
Msg::Header(
|
||||
ModuleHeader {
|
||||
module_id: home,
|
||||
module_path: filename,
|
||||
is_root_module,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name: loc_name.value,
|
||||
packages: package_entries,
|
||||
imported_modules,
|
||||
package_qualified_imported_modules,
|
||||
deps_by_name,
|
||||
exposes: exposed,
|
||||
parse_state,
|
||||
exposed_imports: scope,
|
||||
module_timing,
|
||||
},
|
||||
extra,
|
||||
),
|
||||
Msg::Header(ModuleHeader {
|
||||
module_id: home,
|
||||
module_path: filename,
|
||||
is_root_module,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name: loc_name.value,
|
||||
packages: package_entries,
|
||||
imported_modules,
|
||||
package_qualified_imported_modules,
|
||||
deps_by_name,
|
||||
exposes: exposed,
|
||||
parse_state,
|
||||
exposed_imports: scope,
|
||||
module_timing,
|
||||
header_for: extra,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -3023,7 +2900,6 @@ fn send_header_two<'a>(
|
|||
HashMap::with_capacity_and_hasher(num_exposes, default_hasher());
|
||||
|
||||
// add standard imports
|
||||
// TODO add Effect by default
|
||||
imported_modules.insert(app_module_id, Region::zero());
|
||||
deps_by_name.insert(
|
||||
PQModuleName::Unqualified(ModuleName::APP.into()),
|
||||
|
@ -3198,24 +3074,22 @@ fn send_header_two<'a>(
|
|||
|
||||
(
|
||||
home,
|
||||
Msg::Header(
|
||||
ModuleHeader {
|
||||
module_id: home,
|
||||
module_path: filename,
|
||||
is_root_module,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name,
|
||||
packages: package_entries,
|
||||
imported_modules,
|
||||
package_qualified_imported_modules,
|
||||
deps_by_name,
|
||||
exposes: exposed,
|
||||
parse_state,
|
||||
exposed_imports: scope,
|
||||
module_timing,
|
||||
},
|
||||
extra,
|
||||
),
|
||||
Msg::Header(ModuleHeader {
|
||||
module_id: home,
|
||||
module_path: filename,
|
||||
is_root_module,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name,
|
||||
packages: package_entries,
|
||||
imported_modules,
|
||||
package_qualified_imported_modules,
|
||||
deps_by_name,
|
||||
exposes: exposed,
|
||||
parse_state,
|
||||
exposed_imports: scope,
|
||||
module_timing,
|
||||
header_for: extra,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -3383,272 +3257,6 @@ fn fabricate_pkg_config_module<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn fabricate_effects_module<'a>(
|
||||
arena: &'a Bump,
|
||||
shorthand: &'a str,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
header: PlatformHeader<'a>,
|
||||
module_timing: ModuleTiming,
|
||||
) -> (ModuleId, Msg<'a>) {
|
||||
let num_exposes = header.provides.len() + 1;
|
||||
let mut exposed: Vec<Symbol> = Vec::with_capacity(num_exposes);
|
||||
|
||||
let effects = header.effects;
|
||||
|
||||
let module_id: ModuleId;
|
||||
|
||||
let effect_entries = unpack_exposes_entries(arena, effects.entries.items);
|
||||
let name = effects.effect_type_name;
|
||||
let declared_name: ModuleName = name.into();
|
||||
|
||||
let hardcoded_effect_symbols = {
|
||||
let mut functions: Vec<_> = crate::effect_module::BUILTIN_EFFECT_FUNCTIONS
|
||||
.iter()
|
||||
.map(|x| x.0)
|
||||
.collect();
|
||||
functions.push(name);
|
||||
|
||||
functions
|
||||
};
|
||||
|
||||
{
|
||||
let mut module_ids = (*module_ids).lock();
|
||||
|
||||
for exposed in header.exposes.iter() {
|
||||
let module_name = exposed.value.extract_spaces().item;
|
||||
|
||||
module_ids.get_or_insert(&PQModuleName::Qualified(
|
||||
shorthand,
|
||||
module_name.as_str().into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let exposed_ident_ids = {
|
||||
// Lock just long enough to perform the minimal operations necessary.
|
||||
let mut module_ids = (*module_ids).lock();
|
||||
let mut ident_ids_by_module = (*ident_ids_by_module).lock();
|
||||
|
||||
let name = PQModuleName::Qualified(shorthand, declared_name);
|
||||
module_id = module_ids.get_or_insert(&name);
|
||||
|
||||
// Ensure this module has an entry in the exposed_ident_ids map.
|
||||
ident_ids_by_module
|
||||
.entry(module_id)
|
||||
.or_insert_with(IdentIds::default);
|
||||
|
||||
let ident_ids = ident_ids_by_module.get_mut(&module_id).unwrap();
|
||||
|
||||
// Generate IdentIds entries for all values this module exposes.
|
||||
// This way, when we encounter them in Defs later, they already
|
||||
// have an IdentIds entry.
|
||||
//
|
||||
// We must *not* add them to scope yet, or else the Defs will
|
||||
// incorrectly think they're shadowing them!
|
||||
for (loc_exposed, _) in effect_entries.iter() {
|
||||
// Use get_or_insert here because the ident_ids may already
|
||||
// created an IdentId for this, when it was imported exposed
|
||||
// in a dependent module.
|
||||
//
|
||||
// For example, if module A has [ B.{ foo } ], then
|
||||
// when we get here for B, `foo` will already have
|
||||
// an IdentId. We must reuse that!
|
||||
let ident_id = ident_ids.get_or_insert(&loc_exposed.value.into());
|
||||
let symbol = Symbol::new(module_id, ident_id);
|
||||
|
||||
exposed.push(symbol);
|
||||
}
|
||||
|
||||
for hardcoded in hardcoded_effect_symbols {
|
||||
// Use get_or_insert here because the ident_ids may already
|
||||
// created an IdentId for this, when it was imported exposed
|
||||
// in a dependent module.
|
||||
//
|
||||
// For example, if module A has [ B.{ foo } ], then
|
||||
// when we get here for B, `foo` will already have
|
||||
// an IdentId. We must reuse that!
|
||||
let ident_id = ident_ids.get_or_insert(&hardcoded.into());
|
||||
let symbol = Symbol::new(module_id, ident_id);
|
||||
|
||||
exposed.push(symbol);
|
||||
}
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
module_id.register_debug_idents(ident_ids);
|
||||
}
|
||||
|
||||
ident_ids.clone()
|
||||
};
|
||||
|
||||
// a platform module has no dependencies, hence empty
|
||||
let dep_idents: MutMap<ModuleId, IdentIds> = IdentIds::exposed_builtins(0);
|
||||
|
||||
let mut var_store = VarStore::default();
|
||||
|
||||
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 can_env =
|
||||
roc_can::env::Env::new(module_id, &dep_idents, &module_ids, exposed_ident_ids);
|
||||
|
||||
let effect_symbol = scope
|
||||
.introduce(
|
||||
name.into(),
|
||||
&can_env.exposed_ident_ids,
|
||||
&mut can_env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let effect_tag_name = TagName::Private(effect_symbol);
|
||||
|
||||
let mut aliases = MutMap::default();
|
||||
let alias = {
|
||||
let a_var = var_store.fresh();
|
||||
|
||||
let actual = crate::effect_module::build_effect_actual(
|
||||
effect_tag_name,
|
||||
Type::Variable(a_var),
|
||||
&mut var_store,
|
||||
);
|
||||
|
||||
scope.add_alias(
|
||||
effect_symbol,
|
||||
Region::zero(),
|
||||
vec![Loc::at_zero(("a".into(), a_var))],
|
||||
actual,
|
||||
);
|
||||
|
||||
scope.lookup_alias(effect_symbol).unwrap().clone()
|
||||
};
|
||||
|
||||
aliases.insert(effect_symbol, alias);
|
||||
|
||||
let mut declarations = Vec::new();
|
||||
|
||||
let exposed_symbols: MutSet<Symbol> = {
|
||||
let mut exposed_symbols = MutSet::default();
|
||||
|
||||
{
|
||||
for (ident, ann) in effect_entries {
|
||||
let symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
ident.value.into(),
|
||||
&can_env.exposed_ident_ids,
|
||||
&mut can_env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let annotation = roc_can::annotation::canonicalize_annotation(
|
||||
&mut can_env,
|
||||
&mut scope,
|
||||
&ann.value,
|
||||
Region::zero(),
|
||||
&mut var_store,
|
||||
);
|
||||
|
||||
let def = crate::effect_module::build_host_exposed_def(
|
||||
&mut can_env,
|
||||
&mut scope,
|
||||
symbol,
|
||||
ident.value,
|
||||
TagName::Private(effect_symbol),
|
||||
&mut var_store,
|
||||
annotation,
|
||||
);
|
||||
exposed_symbols.insert(symbol);
|
||||
|
||||
declarations.push(Declaration::Declare(def));
|
||||
}
|
||||
}
|
||||
|
||||
// define Effect.after, Effect.map etc.
|
||||
crate::effect_module::build_effect_builtins(
|
||||
&mut can_env,
|
||||
&mut scope,
|
||||
effect_symbol,
|
||||
&mut var_store,
|
||||
&mut exposed_symbols,
|
||||
&mut declarations,
|
||||
);
|
||||
|
||||
exposed_symbols
|
||||
};
|
||||
|
||||
use roc_can::module::ModuleOutput;
|
||||
let module_output = ModuleOutput {
|
||||
aliases,
|
||||
rigid_variables: MutMap::default(),
|
||||
declarations,
|
||||
exposed_imports: MutMap::default(),
|
||||
lookups: Vec::new(),
|
||||
problems: can_env.problems,
|
||||
ident_ids: can_env.ident_ids,
|
||||
references: MutSet::default(),
|
||||
scope,
|
||||
};
|
||||
|
||||
let constraint = constrain_module(&module_output.declarations, module_id);
|
||||
|
||||
let module = Module {
|
||||
module_id,
|
||||
exposed_imports: module_output.exposed_imports,
|
||||
exposed_symbols,
|
||||
references: module_output.references,
|
||||
aliases: module_output.aliases,
|
||||
rigid_variables: module_output.rigid_variables,
|
||||
};
|
||||
|
||||
let imported_modules = MutMap::default();
|
||||
|
||||
// Should a effect module ever have a ModuleDocumentation?
|
||||
let module_docs = ModuleDocumentation {
|
||||
name: String::from(name),
|
||||
entries: Vec::new(),
|
||||
scope: module_output.scope,
|
||||
};
|
||||
|
||||
let constrained_module = ConstrainedModule {
|
||||
module,
|
||||
declarations: module_output.declarations,
|
||||
imported_modules,
|
||||
var_store,
|
||||
constraint,
|
||||
ident_ids: module_output.ident_ids,
|
||||
dep_idents,
|
||||
module_timing,
|
||||
};
|
||||
|
||||
(
|
||||
module_id,
|
||||
Msg::MadeEffectModule {
|
||||
type_shortname: effects.effect_shortname,
|
||||
constrained_module,
|
||||
canonicalization_problems: module_output.problems,
|
||||
module_docs,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn unpack_exposes_entries<'a>(
|
||||
arena: &'a Bump,
|
||||
entries: &'a [Loc<Spaced<'a, TypedIdent<'a>>>],
|
||||
) -> bumpalo::collections::Vec<'a, (Loc<&'a str>, Loc<TypeAnnotation<'a>>)> {
|
||||
use bumpalo::collections::Vec;
|
||||
|
||||
let iter = entries.iter().map(|entry| {
|
||||
let entry: TypedIdent<'a> = entry.value.extract_spaces().item;
|
||||
(entry.ident, entry.ann)
|
||||
});
|
||||
|
||||
Vec::from_iter_in(iter, arena)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn canonicalize_and_constrain<'a, F>(
|
||||
|
@ -3668,6 +3276,7 @@ where
|
|||
let ParsedModule {
|
||||
module_id,
|
||||
module_name,
|
||||
header_for,
|
||||
exposed_ident_ids,
|
||||
parsed_defs,
|
||||
exposed_imports,
|
||||
|
@ -3680,6 +3289,7 @@ where
|
|||
let canonicalized = canonicalize_module_defs(
|
||||
arena,
|
||||
parsed_defs,
|
||||
&header_for,
|
||||
module_id,
|
||||
module_ids,
|
||||
exposed_ident_ids,
|
||||
|
@ -3702,12 +3312,14 @@ where
|
|||
ModuleNameEnum::PkgConfig => None,
|
||||
ModuleNameEnum::App(_) => None,
|
||||
ModuleNameEnum::Interface(name) | ModuleNameEnum::Hosted(name) => {
|
||||
Some(crate::docs::generate_module_docs(
|
||||
let docs = crate::docs::generate_module_docs(
|
||||
module_output.scope,
|
||||
name.as_str().into(),
|
||||
&module_output.ident_ids,
|
||||
parsed_defs,
|
||||
))
|
||||
);
|
||||
|
||||
Some(docs)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3784,6 +3396,7 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
|
|||
exposed_ident_ids,
|
||||
exposed_imports,
|
||||
module_path,
|
||||
header_for,
|
||||
..
|
||||
} = header;
|
||||
|
||||
|
@ -3798,6 +3411,7 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
|
|||
exposed_ident_ids,
|
||||
exposed_imports,
|
||||
parsed_defs,
|
||||
header_for,
|
||||
};
|
||||
|
||||
Ok(Msg::Parsed(parsed))
|
||||
|
@ -4430,7 +4044,23 @@ fn to_missing_platform_report(module_id: ModuleId, other: PlatformPath) -> Strin
|
|||
}
|
||||
RootIsInterface => {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(r"The input file is a interface file, but only app modules can be ran."),
|
||||
alloc.reflow(r"The input file is an interface module, but only app modules can be ran."),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies, "),
|
||||
alloc.reflow(r"but won't output any executable."),
|
||||
])
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "NO PLATFORM".to_string(),
|
||||
severity: Severity::RuntimeError,
|
||||
}
|
||||
}
|
||||
RootIsHosted => {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(r"The input file is a hosted module, but only app modules can be ran."),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies, "),
|
||||
alloc.reflow(r"but won't output any executable."),
|
||||
|
|
|
@ -2,5 +2,4 @@
|
|||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod docs;
|
||||
pub mod effect_module;
|
||||
pub mod file;
|
||||
|
|
|
@ -1638,7 +1638,9 @@ fn literal_spec(
|
|||
|
||||
match literal {
|
||||
Str(_) => new_static_string(builder, block),
|
||||
Int(_) | Float(_) | Decimal(_) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]),
|
||||
Int(_) | U128(_) | Float(_) | Decimal(_) | Bool(_) | Byte(_) => {
|
||||
builder.add_make_tuple(block, &[])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -87,6 +87,7 @@ enum Test<'a> {
|
|||
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
||||
},
|
||||
IsInt(i128, IntWidth),
|
||||
IsU128(u128),
|
||||
IsFloat(u64, FloatWidth),
|
||||
IsDecimal(RocDec),
|
||||
IsStr(Box<str>),
|
||||
|
@ -136,6 +137,10 @@ impl<'a> Hash for Test<'a> {
|
|||
state.write_u8(6);
|
||||
v.0.hash(state);
|
||||
}
|
||||
IsU128(v) => {
|
||||
state.write_u8(7);
|
||||
v.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -311,6 +316,7 @@ fn tests_are_complete_help(last_test: &Test, number_of_tests: usize) -> bool {
|
|||
Test::IsByte { num_alts, .. } => number_of_tests == *num_alts,
|
||||
Test::IsBit(_) => number_of_tests == 2,
|
||||
Test::IsInt(_, _) => false,
|
||||
Test::IsU128(_) => false,
|
||||
Test::IsFloat(_, _) => false,
|
||||
Test::IsDecimal(_) => false,
|
||||
Test::IsStr(_) => false,
|
||||
|
@ -565,6 +571,7 @@ fn test_at_path<'a>(
|
|||
num_alts: union.alternatives.len(),
|
||||
},
|
||||
IntLiteral(v, precision) => IsInt(*v, *precision),
|
||||
U128Literal(v) => IsU128(*v),
|
||||
FloatLiteral(v, precision) => IsFloat(*v, *precision),
|
||||
DecimalLiteral(v) => IsDecimal(*v),
|
||||
StrLiteral(v) => IsStr(v.clone()),
|
||||
|
@ -823,6 +830,18 @@ fn to_relevant_branch_help<'a>(
|
|||
_ => None,
|
||||
},
|
||||
|
||||
U128Literal(int) => match test {
|
||||
IsU128(is_int) if int == *is_int => {
|
||||
start.extend(end);
|
||||
Some(Branch {
|
||||
goal: branch.goal,
|
||||
guard: branch.guard.clone(),
|
||||
patterns: start,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
|
||||
FloatLiteral(float, p1) => match test {
|
||||
IsFloat(test_float, p2) if float == *test_float => {
|
||||
debug_assert_eq!(p1, *p2);
|
||||
|
@ -934,6 +953,7 @@ fn needs_tests(pattern: &Pattern) -> bool {
|
|||
| BitLiteral { .. }
|
||||
| EnumLiteral { .. }
|
||||
| IntLiteral(_, _)
|
||||
| U128Literal(_)
|
||||
| FloatLiteral(_, _)
|
||||
| DecimalLiteral(_)
|
||||
| StrLiteral(_) => true,
|
||||
|
@ -1305,6 +1325,14 @@ fn test_to_equality<'a>(
|
|||
(stores, lhs_symbol, rhs_symbol, None)
|
||||
}
|
||||
|
||||
Test::IsU128(test_int) => {
|
||||
let lhs = Expr::Literal(Literal::U128(test_int));
|
||||
let lhs_symbol = env.unique_symbol();
|
||||
stores.push((lhs_symbol, Layout::int_width(IntWidth::U128), lhs));
|
||||
|
||||
(stores, lhs_symbol, rhs_symbol, None)
|
||||
}
|
||||
|
||||
Test::IsFloat(test_int, precision) => {
|
||||
// TODO maybe we can actually use i64 comparison here?
|
||||
let test_float = f64::from_bits(test_int as u64);
|
||||
|
|
|
@ -54,6 +54,7 @@ pub enum Pattern {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Literal {
|
||||
Int(i128),
|
||||
U128(u128),
|
||||
Bit(bool),
|
||||
Byte(u8),
|
||||
Float(u64),
|
||||
|
@ -66,6 +67,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
|||
|
||||
match pattern {
|
||||
IntLiteral(v, _) => Literal(Literal::Int(*v)),
|
||||
U128Literal(v) => Literal(Literal::U128(*v)),
|
||||
FloatLiteral(v, _) => Literal(Literal::Float(*v)),
|
||||
DecimalLiteral(v) => Literal(Literal::Decimal(*v)),
|
||||
StrLiteral(v) => Literal(Literal::Str(v.clone())),
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::layout::{
|
|||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_can::expr::ClosureData;
|
||||
use roc_can::expr::{ClosureData, IntValue};
|
||||
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
|
@ -1278,6 +1278,7 @@ impl ModifyRc {
|
|||
pub enum Literal<'a> {
|
||||
// Literals
|
||||
Int(i128),
|
||||
U128(u128),
|
||||
Float(f64),
|
||||
Decimal(RocDec),
|
||||
Str(&'a str),
|
||||
|
@ -1524,6 +1525,7 @@ impl<'a> Literal<'a> {
|
|||
|
||||
match self {
|
||||
Int(lit) => alloc.text(format!("{}i64", lit)),
|
||||
U128(lit) => alloc.text(format!("{}u128", lit)),
|
||||
Float(lit) => alloc.text(format!("{}f64", lit)),
|
||||
// TODO: Add proper Dec.to_str
|
||||
Decimal(lit) => alloc.text(format!("{}Dec", lit.0)),
|
||||
|
@ -2020,7 +2022,7 @@ fn pattern_to_when<'a>(
|
|||
(symbol, Loc::at_zero(wrapped_body))
|
||||
}
|
||||
|
||||
IntLiteral(_, _, _) | NumLiteral(_, _, _) | FloatLiteral(_, _, _) | StrLiteral(_) => {
|
||||
IntLiteral(..) | NumLiteral(..) | FloatLiteral(..) | StrLiteral(_) => {
|
||||
// These patters are refutable, and thus should never occur outside a `when` expression
|
||||
// They should have been replaced with `UnsupportedPattern` during canonicalization
|
||||
unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value)
|
||||
|
@ -3009,14 +3011,17 @@ fn try_make_literal<'a>(
|
|||
use roc_can::expr::Expr::*;
|
||||
|
||||
match can_expr {
|
||||
Int(_, precision, _, int) => {
|
||||
Int(_, precision, _, int, _bound) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *precision, false) {
|
||||
IntOrFloat::Int(_) => Some(Literal::Int(*int)),
|
||||
IntOrFloat::Int(_) => Some(match *int {
|
||||
IntValue::I128(n) => Literal::Int(n),
|
||||
IntValue::U128(n) => Literal::U128(n),
|
||||
}),
|
||||
_ => unreachable!("unexpected float precision for integer"),
|
||||
}
|
||||
}
|
||||
|
||||
Float(_, precision, float_str, float) => {
|
||||
Float(_, precision, float_str, float, _bound) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *precision, true) {
|
||||
IntOrFloat::Float(_) => Some(Literal::Float(*float)),
|
||||
IntOrFloat::DecimalFloatType => {
|
||||
|
@ -3036,11 +3041,17 @@ fn try_make_literal<'a>(
|
|||
|
||||
// TODO investigate lifetime trouble
|
||||
// Str(string) => Some(Literal::Str(env.arena.alloc(string))),
|
||||
Num(var, num_str, num) => {
|
||||
Num(var, num_str, num, _bound) => {
|
||||
// first figure out what kind of number this is
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *var, false) {
|
||||
IntOrFloat::Int(_) => Some(Literal::Int((*num).into())),
|
||||
IntOrFloat::Float(_) => Some(Literal::Float(*num as f64)),
|
||||
IntOrFloat::Int(_) => Some(match *num {
|
||||
IntValue::I128(n) => Literal::Int(n),
|
||||
IntValue::U128(n) => Literal::U128(n),
|
||||
}),
|
||||
IntOrFloat::Float(_) => Some(match *num {
|
||||
IntValue::I128(n) => Literal::Float(n as f64),
|
||||
IntValue::U128(n) => Literal::Float(n as f64),
|
||||
}),
|
||||
IntOrFloat::DecimalFloatType => {
|
||||
let dec = match RocDec::from_str(num_str) {
|
||||
Some(d) => d,
|
||||
|
@ -3072,11 +3083,14 @@ pub fn with_hole<'a>(
|
|||
let arena = env.arena;
|
||||
|
||||
match can_expr {
|
||||
Int(_, precision, _, int) => {
|
||||
Int(_, precision, _, int, _bound) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, precision, false) {
|
||||
IntOrFloat::Int(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(int)),
|
||||
Expr::Literal(match int {
|
||||
IntValue::I128(n) => Literal::Int(n),
|
||||
IntValue::U128(n) => Literal::U128(n),
|
||||
}),
|
||||
Layout::Builtin(Builtin::Int(precision)),
|
||||
hole,
|
||||
),
|
||||
|
@ -3084,7 +3098,7 @@ pub fn with_hole<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Float(_, precision, float_str, float) => {
|
||||
Float(_, precision, float_str, float, _bound) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, precision, true) {
|
||||
IntOrFloat::Float(precision) => Stmt::Let(
|
||||
assigned,
|
||||
|
@ -3115,18 +3129,24 @@ pub fn with_hole<'a>(
|
|||
hole,
|
||||
),
|
||||
|
||||
Num(var, num_str, num) => {
|
||||
Num(var, num_str, num, _bound) => {
|
||||
// first figure out what kind of number this is
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, var, false) {
|
||||
IntOrFloat::Int(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num.into())),
|
||||
Expr::Literal(match num {
|
||||
IntValue::I128(n) => Literal::Int(n),
|
||||
IntValue::U128(n) => Literal::U128(n),
|
||||
}),
|
||||
Layout::int_width(precision),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::Float(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Expr::Literal(match num {
|
||||
IntValue::I128(n) => Literal::Float(n as f64),
|
||||
IntValue::U128(n) => Literal::Float(n as f64),
|
||||
}),
|
||||
Layout::float_width(precision),
|
||||
hole,
|
||||
),
|
||||
|
@ -6211,6 +6231,7 @@ fn store_pattern_help<'a>(
|
|||
return StorePattern::NotProductive(stmt);
|
||||
}
|
||||
IntLiteral(_, _)
|
||||
| U128Literal(_)
|
||||
| FloatLiteral(_, _)
|
||||
| DecimalLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
|
@ -7583,6 +7604,7 @@ fn call_specialized_proc<'a>(
|
|||
pub enum Pattern<'a> {
|
||||
Identifier(Symbol),
|
||||
Underscore,
|
||||
U128Literal(u128),
|
||||
IntLiteral(i128, IntWidth),
|
||||
FloatLiteral(u64, FloatWidth),
|
||||
DecimalLiteral(RocDec),
|
||||
|
@ -7662,9 +7684,15 @@ fn from_can_pattern_help<'a>(
|
|||
match can_pattern {
|
||||
Underscore => Ok(Pattern::Underscore),
|
||||
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
|
||||
IntLiteral(var, _, int) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *var, false) {
|
||||
IntOrFloat::Int(precision) => Ok(Pattern::IntLiteral(*int as i128, precision)),
|
||||
IntLiteral(_, precision_var, _, int, _bound) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *precision_var, false) {
|
||||
IntOrFloat::Int(precision) => {
|
||||
let int = match *int {
|
||||
IntValue::I128(n) => Pattern::IntLiteral(n, precision),
|
||||
IntValue::U128(n) => Pattern::U128Literal(n),
|
||||
};
|
||||
Ok(int)
|
||||
}
|
||||
other => {
|
||||
panic!(
|
||||
"Invalid precision for int pattern: {:?} has {:?}",
|
||||
|
@ -7673,11 +7701,11 @@ fn from_can_pattern_help<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
FloatLiteral(var, float_str, float) => {
|
||||
FloatLiteral(_, precision_var, float_str, float, _bound) => {
|
||||
// TODO: Can I reuse num_argument_to_int_or_float here if I pass in true?
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *var, true) {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *precision_var, true) {
|
||||
IntOrFloat::Int(_) => {
|
||||
panic!("Invalid precision for float pattern {:?}", var)
|
||||
panic!("Invalid precision for float pattern {:?}", precision_var)
|
||||
}
|
||||
IntOrFloat::Float(precision) => {
|
||||
Ok(Pattern::FloatLiteral(f64::to_bits(*float), precision))
|
||||
|
@ -7704,10 +7732,20 @@ fn from_can_pattern_help<'a>(
|
|||
// TODO preserve malformed problem information here?
|
||||
Err(RuntimeError::UnsupportedPattern(*region))
|
||||
}
|
||||
NumLiteral(var, num_str, num) => {
|
||||
NumLiteral(var, num_str, num, _bound) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *var, false) {
|
||||
IntOrFloat::Int(precision) => Ok(Pattern::IntLiteral(*num as i128, precision)),
|
||||
IntOrFloat::Float(precision) => Ok(Pattern::FloatLiteral(*num as u64, precision)),
|
||||
IntOrFloat::Int(precision) => Ok(match num {
|
||||
IntValue::I128(num) => Pattern::IntLiteral(*num, precision),
|
||||
IntValue::U128(num) => Pattern::U128Literal(*num),
|
||||
}),
|
||||
IntOrFloat::Float(precision) => {
|
||||
// TODO: this may be lossy
|
||||
let num = match *num {
|
||||
IntValue::I128(n) => f64::to_bits(n as f64),
|
||||
IntValue::U128(n) => f64::to_bits(n as f64),
|
||||
};
|
||||
Ok(Pattern::FloatLiteral(num, precision))
|
||||
}
|
||||
IntOrFloat::DecimalFloatType => {
|
||||
let dec = match RocDec::from_str(num_str) {
|
||||
Some(d) => d,
|
||||
|
|
|
@ -514,7 +514,7 @@ fn numeric_negate_expression<'a, T>(
|
|||
let start = state.pos();
|
||||
let region = Region::new(start, expr.region.end());
|
||||
|
||||
let new_expr = match &expr.value {
|
||||
let new_expr = match expr.value {
|
||||
Expr::Num(string) => {
|
||||
let new_string =
|
||||
unsafe { std::str::from_utf8_unchecked(&state.bytes()[..string.len() + 1]) };
|
||||
|
@ -536,7 +536,7 @@ fn numeric_negate_expression<'a, T>(
|
|||
Expr::NonBase10Int {
|
||||
is_negative: !is_negative,
|
||||
string,
|
||||
base: *base,
|
||||
base,
|
||||
}
|
||||
}
|
||||
_ => Expr::UnaryOp(arena.alloc(expr), Loc::at(loc_op.region, UnaryOp::Negate)),
|
||||
|
@ -1450,8 +1450,8 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
|||
Ok(Pattern::RecordDestructure(patterns))
|
||||
}
|
||||
|
||||
Expr::Float(string) => Ok(Pattern::FloatLiteral(string)),
|
||||
Expr::Num(string) => Ok(Pattern::NumLiteral(string)),
|
||||
&Expr::Float(string) => Ok(Pattern::FloatLiteral(string)),
|
||||
&Expr::Num(string) => Ok(Pattern::NumLiteral(string)),
|
||||
Expr::NonBase10Int {
|
||||
string,
|
||||
base,
|
||||
|
|
|
@ -8,6 +8,28 @@ use crate::string_literal;
|
|||
use bumpalo::collections::Vec;
|
||||
use roc_region::all::Loc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum HeaderFor<'a> {
|
||||
App {
|
||||
to_platform: To<'a>,
|
||||
},
|
||||
Hosted {
|
||||
generates: UppercaseIdent<'a>,
|
||||
generates_with: &'a [Loc<ExposedName<'a>>],
|
||||
},
|
||||
PkgConfig {
|
||||
/// usually `pf`
|
||||
config_shorthand: &'a str,
|
||||
/// the type scheme of the main function (required by the platform)
|
||||
/// (currently unused)
|
||||
#[allow(dead_code)]
|
||||
platform_main_type: TypedIdent<'a>,
|
||||
/// provided symbol to host (commonly `mainForHost`)
|
||||
main_for_host: roc_module::symbol::Symbol,
|
||||
},
|
||||
Interface,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Version<'a> {
|
||||
Exact(&'a str),
|
||||
|
@ -47,6 +69,15 @@ impl<'a> ModuleName<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ModuleNameEnum<'a> {
|
||||
/// A filename
|
||||
App(StrLiteral<'a>),
|
||||
Interface(ModuleName<'a>),
|
||||
Hosted(ModuleName<'a>),
|
||||
PkgConfig,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ExposedName<'a>(&'a str);
|
||||
|
||||
|
@ -162,7 +193,6 @@ pub struct PlatformHeader<'a> {
|
|||
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub effects: Effects<'a>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub before_header: &'a [CommentOrNewline<'a>],
|
||||
|
@ -179,17 +209,6 @@ pub struct PlatformHeader<'a> {
|
|||
pub after_provides: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
/// e.g. fx.Effects
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Effects<'a> {
|
||||
pub spaces_before_effects_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub spaces_after_effects_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub spaces_after_type_name: &'a [CommentOrNewline<'a>],
|
||||
pub effect_shortname: &'a str,
|
||||
pub effect_type_name: &'a str,
|
||||
pub entries: Collection<'a, Loc<Spaced<'a, TypedIdent<'a>>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ImportsEntry<'a> {
|
||||
/// e.g. `Task` or `Task.{ Task, after }`
|
||||
|
|
|
@ -22,6 +22,12 @@ impl<'a> From<UppercaseIdent<'a>> for &'a str {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a UppercaseIdent<'a>> for &'a str {
|
||||
fn from(ident: &'a UppercaseIdent<'a>) -> Self {
|
||||
ident.0
|
||||
}
|
||||
}
|
||||
|
||||
/// The parser accepts all of these in any position where any one of them could
|
||||
/// appear. This way, canonicalization can give more helpful error messages like
|
||||
/// "you can't redefine this tag!" if you wrote `Foo = ...` or
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
use crate::ast::{Collection, CommentOrNewline, Def, Module, Spaced};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::header::{
|
||||
package_entry, package_name, AppHeader, Effects, ExposedName, HostedHeader, ImportsEntry,
|
||||
package_entry, package_name, AppHeader, ExposedName, HostedHeader, ImportsEntry,
|
||||
InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, PlatformRequires, To, TypedIdent,
|
||||
};
|
||||
use crate::ident::{
|
||||
self, lowercase_ident, unqualified_ident, uppercase, uppercase_ident, UppercaseIdent,
|
||||
};
|
||||
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{
|
||||
backtrackable, optional, specialize, specialize_region, word1, EEffects, EExposes, EGenerates,
|
||||
backtrackable, optional, specialize, specialize_region, word1, EExposes, EGenerates,
|
||||
EGeneratesWith, EHeader, EImports, EPackages, EProvides, ERequires, ETypedIdent, Parser,
|
||||
SourceError, SyntaxError,
|
||||
};
|
||||
|
@ -323,8 +321,6 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
|||
let (_, ((before_provides, after_provides), (provides, _provides_type)), state) =
|
||||
specialize(EHeader::Provides, provides_without_to()).parse(arena, state)?;
|
||||
|
||||
let (_, effects, state) = specialize(EHeader::Effects, effects()).parse(arena, state)?;
|
||||
|
||||
let header = PlatformHeader {
|
||||
name,
|
||||
requires,
|
||||
|
@ -332,7 +328,6 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
|||
packages: packages.entries,
|
||||
imports,
|
||||
provides,
|
||||
effects,
|
||||
before_header: &[] as &[_],
|
||||
after_platform_keyword,
|
||||
before_requires,
|
||||
|
@ -822,63 +817,6 @@ fn imports<'a>() -> impl Parser<
|
|||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn effects<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
|
||||
move |arena, state| {
|
||||
let min_indent = 1;
|
||||
|
||||
let (_, (spaces_before_effects_keyword, spaces_after_effects_keyword), state) =
|
||||
spaces_around_keyword(
|
||||
min_indent,
|
||||
"effects",
|
||||
EEffects::Effects,
|
||||
EEffects::Space,
|
||||
EEffects::IndentEffects,
|
||||
EEffects::IndentListStart,
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
// e.g. `fx.`
|
||||
let (_, type_shortname, state) = skip_second!(
|
||||
specialize(|_, pos| EEffects::Shorthand(pos), lowercase_ident()),
|
||||
word1(b'.', EEffects::ShorthandDot)
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
// the type name, e.g. Effects
|
||||
let (_, (type_name, spaces_after_type_name), state) = and!(
|
||||
specialize(|_, pos| EEffects::TypeName(pos), uppercase_ident()),
|
||||
space0_e(min_indent, EEffects::Space, EEffects::IndentListStart)
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
let (_, entries, state) = collection_trailing_sep_e!(
|
||||
word1(b'{', EEffects::ListStart),
|
||||
specialize(EEffects::TypedIdent, loc!(typed_ident())),
|
||||
word1(b',', EEffects::ListEnd),
|
||||
word1(b'}', EEffects::ListEnd),
|
||||
min_indent,
|
||||
EEffects::Open,
|
||||
EEffects::Space,
|
||||
EEffects::IndentListEnd,
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Effects {
|
||||
spaces_before_effects_keyword,
|
||||
spaces_after_effects_keyword,
|
||||
spaces_after_type_name,
|
||||
effect_shortname: type_shortname,
|
||||
effect_type_name: type_name,
|
||||
entries,
|
||||
},
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> {
|
||||
// e.g.
|
||||
|
|
|
@ -71,7 +71,6 @@ pub enum EHeader<'a> {
|
|||
Imports(EImports, Position),
|
||||
Requires(ERequires<'a>, Position),
|
||||
Packages(EPackages<'a>, Position),
|
||||
Effects(EEffects<'a>, Position),
|
||||
Generates(EGenerates, Position),
|
||||
GeneratesWith(EGeneratesWith, Position),
|
||||
|
||||
|
@ -167,22 +166,6 @@ pub enum EPackageEntry<'a> {
|
|||
Space(BadInputError, Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EEffects<'a> {
|
||||
Space(BadInputError, Position),
|
||||
Effects(Position),
|
||||
Open(Position),
|
||||
IndentEffects(Position),
|
||||
ListStart(Position),
|
||||
ListEnd(Position),
|
||||
IndentListStart(Position),
|
||||
IndentListEnd(Position),
|
||||
TypedIdent(ETypedIdent<'a>, Position),
|
||||
ShorthandDot(Position),
|
||||
Shorthand(Position),
|
||||
TypeName(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EImports {
|
||||
Open(Position),
|
||||
|
|
|
@ -18,14 +18,6 @@ Platform {
|
|||
packages: [],
|
||||
imports: [],
|
||||
provides: [],
|
||||
effects: Effects {
|
||||
spaces_before_effects_keyword: [],
|
||||
spaces_after_effects_keyword: [],
|
||||
spaces_after_type_name: [],
|
||||
effect_shortname: "fx",
|
||||
effect_type_name: "Blah",
|
||||
entries: [],
|
||||
},
|
||||
before_header: [],
|
||||
after_platform_keyword: [],
|
||||
before_requires: [],
|
||||
|
|
|
@ -1 +1 @@
|
|||
platform "rtfeldman/blah" requires {} { main : {} } exposes [] packages {} imports [] provides [] effects fx.Blah {}
|
||||
platform "rtfeldman/blah" requires {} { main : {} } exposes [] packages {} imports [] provides []
|
||||
|
|
|
@ -43,105 +43,6 @@ Platform {
|
|||
"mainForHost",
|
||||
),
|
||||
],
|
||||
effects: Effects {
|
||||
spaces_before_effects_keyword: [
|
||||
Newline,
|
||||
],
|
||||
spaces_after_effects_keyword: [],
|
||||
spaces_after_type_name: [
|
||||
Newline,
|
||||
],
|
||||
effect_shortname: "fx",
|
||||
effect_type_name: "Effect",
|
||||
entries: [
|
||||
@208-228 SpaceBefore(
|
||||
TypedIdent {
|
||||
ident: @208-215 "getLine",
|
||||
spaces_before_colon: [],
|
||||
ann: @218-228 Apply(
|
||||
"",
|
||||
"Effect",
|
||||
[
|
||||
@225-228 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
),
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@242-268 SpaceBefore(
|
||||
TypedIdent {
|
||||
ident: @242-249 "putLine",
|
||||
spaces_before_colon: [],
|
||||
ann: @259-268 Function(
|
||||
[
|
||||
@252-255 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
@259-268 Apply(
|
||||
"",
|
||||
"Effect",
|
||||
[
|
||||
@266-268 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@282-318 SpaceBefore(
|
||||
SpaceAfter(
|
||||
TypedIdent {
|
||||
ident: @282-294 "twoArguments",
|
||||
spaces_before_colon: [],
|
||||
ann: @309-318 Function(
|
||||
[
|
||||
@297-300 Apply(
|
||||
"",
|
||||
"Int",
|
||||
[],
|
||||
),
|
||||
@302-305 Apply(
|
||||
"",
|
||||
"Int",
|
||||
[],
|
||||
),
|
||||
],
|
||||
@309-318 Apply(
|
||||
"",
|
||||
"Effect",
|
||||
[
|
||||
@316-318 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
},
|
||||
before_header: [],
|
||||
after_platform_keyword: [],
|
||||
before_requires: [
|
||||
|
|
|
@ -4,9 +4,3 @@ platform "examples/cli"
|
|||
packages {}
|
||||
imports [ Task.{ Task } ]
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect
|
||||
{
|
||||
getLine : Effect Str,
|
||||
putLine : Str -> Effect {},
|
||||
twoArguments : Int, Int -> Effect {}
|
||||
}
|
||||
|
|
|
@ -34,16 +34,6 @@ Platform {
|
|||
"mainForHost",
|
||||
),
|
||||
],
|
||||
effects: Effects {
|
||||
spaces_before_effects_keyword: [
|
||||
Newline,
|
||||
],
|
||||
spaces_after_effects_keyword: [],
|
||||
spaces_after_type_name: [],
|
||||
effect_shortname: "fx",
|
||||
effect_type_name: "Effect",
|
||||
entries: [],
|
||||
},
|
||||
before_header: [],
|
||||
after_platform_keyword: [],
|
||||
before_requires: [
|
||||
|
|
|
@ -4,4 +4,3 @@ platform "foo/barbaz"
|
|||
packages { foo: "./foo" }
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
|
|
@ -0,0 +1,451 @@
|
|||
Record(
|
||||
Collection {
|
||||
items: [
|
||||
@4-15 SpaceBefore(
|
||||
RequiredValue(
|
||||
@4-6 "u8",
|
||||
[],
|
||||
@10-15 Num(
|
||||
"123u8",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@19-31 SpaceBefore(
|
||||
RequiredValue(
|
||||
@19-22 "u16",
|
||||
[],
|
||||
@25-31 Num(
|
||||
"123u16",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@35-47 SpaceBefore(
|
||||
RequiredValue(
|
||||
@35-38 "u32",
|
||||
[],
|
||||
@41-47 Num(
|
||||
"123u32",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@51-63 SpaceBefore(
|
||||
RequiredValue(
|
||||
@51-54 "u64",
|
||||
[],
|
||||
@57-63 Num(
|
||||
"123u64",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@67-80 SpaceBefore(
|
||||
RequiredValue(
|
||||
@67-71 "u128",
|
||||
[],
|
||||
@73-80 Num(
|
||||
"123u128",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@84-95 SpaceBefore(
|
||||
RequiredValue(
|
||||
@84-86 "i8",
|
||||
[],
|
||||
@90-95 Num(
|
||||
"123i8",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@99-111 SpaceBefore(
|
||||
RequiredValue(
|
||||
@99-102 "i16",
|
||||
[],
|
||||
@105-111 Num(
|
||||
"123i16",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@115-127 SpaceBefore(
|
||||
RequiredValue(
|
||||
@115-118 "i32",
|
||||
[],
|
||||
@121-127 Num(
|
||||
"123i32",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@131-143 SpaceBefore(
|
||||
RequiredValue(
|
||||
@131-134 "i64",
|
||||
[],
|
||||
@137-143 Num(
|
||||
"123i64",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@147-160 SpaceBefore(
|
||||
RequiredValue(
|
||||
@147-151 "i128",
|
||||
[],
|
||||
@153-160 Num(
|
||||
"123i128",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@164-176 SpaceBefore(
|
||||
RequiredValue(
|
||||
@164-167 "nat",
|
||||
[],
|
||||
@170-176 Num(
|
||||
"123nat",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@180-192 SpaceBefore(
|
||||
RequiredValue(
|
||||
@180-183 "dec",
|
||||
[],
|
||||
@186-192 Num(
|
||||
"123dec",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@196-211 SpaceBefore(
|
||||
RequiredValue(
|
||||
@196-201 "u8Neg",
|
||||
[],
|
||||
@205-211 Num(
|
||||
"-123u8",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@215-231 SpaceBefore(
|
||||
RequiredValue(
|
||||
@215-221 "u16Neg",
|
||||
[],
|
||||
@224-231 Num(
|
||||
"-123u16",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@235-251 SpaceBefore(
|
||||
RequiredValue(
|
||||
@235-241 "u32Neg",
|
||||
[],
|
||||
@244-251 Num(
|
||||
"-123u32",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@255-271 SpaceBefore(
|
||||
RequiredValue(
|
||||
@255-261 "u64Neg",
|
||||
[],
|
||||
@264-271 Num(
|
||||
"-123u64",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@275-292 SpaceBefore(
|
||||
RequiredValue(
|
||||
@275-282 "u128Neg",
|
||||
[],
|
||||
@284-292 Num(
|
||||
"-123u128",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@296-311 SpaceBefore(
|
||||
RequiredValue(
|
||||
@296-301 "i8Neg",
|
||||
[],
|
||||
@305-311 Num(
|
||||
"-123i8",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@315-331 SpaceBefore(
|
||||
RequiredValue(
|
||||
@315-321 "i16Neg",
|
||||
[],
|
||||
@324-331 Num(
|
||||
"-123i16",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@335-351 SpaceBefore(
|
||||
RequiredValue(
|
||||
@335-341 "i32Neg",
|
||||
[],
|
||||
@344-351 Num(
|
||||
"-123i32",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@355-371 SpaceBefore(
|
||||
RequiredValue(
|
||||
@355-361 "i64Neg",
|
||||
[],
|
||||
@364-371 Num(
|
||||
"-123i64",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@375-392 SpaceBefore(
|
||||
RequiredValue(
|
||||
@375-382 "i128Neg",
|
||||
[],
|
||||
@384-392 Num(
|
||||
"-123i128",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@396-412 SpaceBefore(
|
||||
RequiredValue(
|
||||
@396-402 "natNeg",
|
||||
[],
|
||||
@405-412 Num(
|
||||
"-123nat",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@416-432 SpaceBefore(
|
||||
RequiredValue(
|
||||
@416-422 "decNeg",
|
||||
[],
|
||||
@425-432 Num(
|
||||
"-123dec",
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@436-452 SpaceBefore(
|
||||
RequiredValue(
|
||||
@436-441 "u8Bin",
|
||||
[],
|
||||
@445-452 NonBase10Int {
|
||||
string: "101u8",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@456-473 SpaceBefore(
|
||||
RequiredValue(
|
||||
@456-462 "u16Bin",
|
||||
[],
|
||||
@465-473 NonBase10Int {
|
||||
string: "101u16",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@477-494 SpaceBefore(
|
||||
RequiredValue(
|
||||
@477-483 "u32Bin",
|
||||
[],
|
||||
@486-494 NonBase10Int {
|
||||
string: "101u32",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@498-515 SpaceBefore(
|
||||
RequiredValue(
|
||||
@498-504 "u64Bin",
|
||||
[],
|
||||
@507-515 NonBase10Int {
|
||||
string: "101u64",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@519-537 SpaceBefore(
|
||||
RequiredValue(
|
||||
@519-526 "u128Bin",
|
||||
[],
|
||||
@528-537 NonBase10Int {
|
||||
string: "101u128",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@541-557 SpaceBefore(
|
||||
RequiredValue(
|
||||
@541-546 "i8Bin",
|
||||
[],
|
||||
@550-557 NonBase10Int {
|
||||
string: "101i8",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@561-578 SpaceBefore(
|
||||
RequiredValue(
|
||||
@561-567 "i16Bin",
|
||||
[],
|
||||
@570-578 NonBase10Int {
|
||||
string: "101i16",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@582-599 SpaceBefore(
|
||||
RequiredValue(
|
||||
@582-588 "i32Bin",
|
||||
[],
|
||||
@591-599 NonBase10Int {
|
||||
string: "101i32",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@603-620 SpaceBefore(
|
||||
RequiredValue(
|
||||
@603-609 "i64Bin",
|
||||
[],
|
||||
@612-620 NonBase10Int {
|
||||
string: "101i64",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@624-642 SpaceBefore(
|
||||
RequiredValue(
|
||||
@624-631 "i128Bin",
|
||||
[],
|
||||
@633-642 NonBase10Int {
|
||||
string: "101i128",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@646-663 SpaceBefore(
|
||||
RequiredValue(
|
||||
@646-652 "natBin",
|
||||
[],
|
||||
@655-663 NonBase10Int {
|
||||
string: "101nat",
|
||||
base: Binary,
|
||||
is_negative: false,
|
||||
},
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
final_comments: [
|
||||
Newline,
|
||||
],
|
||||
},
|
||||
)
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
u8: 123u8,
|
||||
u16: 123u16,
|
||||
u32: 123u32,
|
||||
u64: 123u64,
|
||||
u128: 123u128,
|
||||
i8: 123i8,
|
||||
i16: 123i16,
|
||||
i32: 123i32,
|
||||
i64: 123i64,
|
||||
i128: 123i128,
|
||||
nat: 123nat,
|
||||
dec: 123dec,
|
||||
u8Neg: -123u8,
|
||||
u16Neg: -123u16,
|
||||
u32Neg: -123u32,
|
||||
u64Neg: -123u64,
|
||||
u128Neg: -123u128,
|
||||
i8Neg: -123i8,
|
||||
i16Neg: -123i16,
|
||||
i32Neg: -123i32,
|
||||
i64Neg: -123i64,
|
||||
i128Neg: -123i128,
|
||||
natNeg: -123nat,
|
||||
decNeg: -123dec,
|
||||
u8Bin: 0b101u8,
|
||||
u16Bin: 0b101u16,
|
||||
u32Bin: 0b101u32,
|
||||
u64Bin: 0b101u64,
|
||||
u128Bin: 0b101u128,
|
||||
i8Bin: 0b101i8,
|
||||
i16Bin: 0b101i16,
|
||||
i32Bin: 0b101i32,
|
||||
i64Bin: 0b101i64,
|
||||
i128Bin: 0b101i128,
|
||||
natBin: 0b101nat,
|
||||
}
|
|
@ -41,16 +41,6 @@ Platform {
|
|||
"mainForHost",
|
||||
),
|
||||
],
|
||||
effects: Effects {
|
||||
spaces_before_effects_keyword: [
|
||||
Newline,
|
||||
],
|
||||
spaces_after_effects_keyword: [],
|
||||
spaces_after_type_name: [],
|
||||
effect_shortname: "fx",
|
||||
effect_type_name: "Effect",
|
||||
entries: [],
|
||||
},
|
||||
before_header: [],
|
||||
after_platform_keyword: [],
|
||||
before_requires: [
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "test/types"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : App Flags Model
|
||||
mainForHost = main
|
||||
|
|
|
@ -182,6 +182,7 @@ mod test_parse {
|
|||
pass/newline_singleton_list.expr,
|
||||
pass/nonempty_platform_header.header,
|
||||
pass/not_docs.expr,
|
||||
pass/number_literal_suffixes.expr,
|
||||
pass/one_backpassing.expr,
|
||||
pass/one_char_string.expr,
|
||||
pass/one_def.expr,
|
||||
|
|
|
@ -107,6 +107,18 @@ pub enum IntErrorKind {
|
|||
Overflow,
|
||||
/// Integer is too small to store in target integer type.
|
||||
Underflow,
|
||||
/// This is an integer, but it has a float numeric suffix.
|
||||
FloatSuffix,
|
||||
/// The integer literal overflows the width of the suffix associated with it.
|
||||
OverflowsSuffix {
|
||||
suffix_type: &'static str,
|
||||
max_value: u128,
|
||||
},
|
||||
/// The integer literal underflows the width of the suffix associated with it.
|
||||
UnderflowsSuffix {
|
||||
suffix_type: &'static str,
|
||||
min_value: i128,
|
||||
},
|
||||
}
|
||||
|
||||
/// Enum to store the various types of errors that can cause parsing a float to fail.
|
||||
|
@ -118,6 +130,8 @@ pub enum FloatErrorKind {
|
|||
NegativeInfinity,
|
||||
/// the literal is too large for f64
|
||||
PositiveInfinity,
|
||||
/// This is a float, but it has an integer numeric suffix.
|
||||
IntSuffix,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
|
@ -5044,4 +5044,156 @@ mod solve_expr {
|
|||
"[ Email Str ] -> Bool",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric_literal_suffixes() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
{
|
||||
u8: 123u8,
|
||||
u16: 123u16,
|
||||
u32: 123u32,
|
||||
u64: 123u64,
|
||||
u128: 123u128,
|
||||
|
||||
i8: 123i8,
|
||||
i16: 123i16,
|
||||
i32: 123i32,
|
||||
i64: 123i64,
|
||||
i128: 123i128,
|
||||
|
||||
nat: 123nat,
|
||||
|
||||
bu8: 0b11u8,
|
||||
bu16: 0b11u16,
|
||||
bu32: 0b11u32,
|
||||
bu64: 0b11u64,
|
||||
bu128: 0b11u128,
|
||||
|
||||
bi8: 0b11i8,
|
||||
bi16: 0b11i16,
|
||||
bi32: 0b11i32,
|
||||
bi64: 0b11i64,
|
||||
bi128: 0b11i128,
|
||||
|
||||
bnat: 0b11nat,
|
||||
|
||||
dec: 123.0dec,
|
||||
f32: 123.0f32,
|
||||
f64: 123.0f64,
|
||||
|
||||
fdec: 123dec,
|
||||
ff32: 123f32,
|
||||
ff64: 123f64,
|
||||
}
|
||||
"#
|
||||
),
|
||||
r#"{ bi128 : I128, bi16 : I16, bi32 : I32, bi64 : I64, bi8 : I8, bnat : Nat, bu128 : U128, bu16 : U16, bu32 : U32, bu64 : U64, bu8 : U8, dec : Dec, f32 : F32, f64 : F64, fdec : Dec, ff32 : F32, ff64 : F64, i128 : I128, i16 : I16, i32 : I32, i64 : I64, i8 : I8, nat : Nat, u128 : U128, u16 : U16, u32 : U32, u64 : U64, u8 : U8 }"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric_literal_suffixes_in_pattern() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
{
|
||||
u8: (\n ->
|
||||
when n is
|
||||
123u8 -> n),
|
||||
u16: (\n ->
|
||||
when n is
|
||||
123u16 -> n),
|
||||
u32: (\n ->
|
||||
when n is
|
||||
123u32 -> n),
|
||||
u64: (\n ->
|
||||
when n is
|
||||
123u64 -> n),
|
||||
u128: (\n ->
|
||||
when n is
|
||||
123u128 -> n),
|
||||
|
||||
i8: (\n ->
|
||||
when n is
|
||||
123i8 -> n),
|
||||
i16: (\n ->
|
||||
when n is
|
||||
123i16 -> n),
|
||||
i32: (\n ->
|
||||
when n is
|
||||
123i32 -> n),
|
||||
i64: (\n ->
|
||||
when n is
|
||||
123i64 -> n),
|
||||
i128: (\n ->
|
||||
when n is
|
||||
123i128 -> n),
|
||||
|
||||
nat: (\n ->
|
||||
when n is
|
||||
123nat -> n),
|
||||
|
||||
bu8: (\n ->
|
||||
when n is
|
||||
0b11u8 -> n),
|
||||
bu16: (\n ->
|
||||
when n is
|
||||
0b11u16 -> n),
|
||||
bu32: (\n ->
|
||||
when n is
|
||||
0b11u32 -> n),
|
||||
bu64: (\n ->
|
||||
when n is
|
||||
0b11u64 -> n),
|
||||
bu128: (\n ->
|
||||
when n is
|
||||
0b11u128 -> n),
|
||||
|
||||
bi8: (\n ->
|
||||
when n is
|
||||
0b11i8 -> n),
|
||||
bi16: (\n ->
|
||||
when n is
|
||||
0b11i16 -> n),
|
||||
bi32: (\n ->
|
||||
when n is
|
||||
0b11i32 -> n),
|
||||
bi64: (\n ->
|
||||
when n is
|
||||
0b11i64 -> n),
|
||||
bi128: (\n ->
|
||||
when n is
|
||||
0b11i128 -> n),
|
||||
|
||||
bnat: (\n ->
|
||||
when n is
|
||||
0b11nat -> n),
|
||||
|
||||
dec: (\n ->
|
||||
when n is
|
||||
123.0dec -> n),
|
||||
f32: (\n ->
|
||||
when n is
|
||||
123.0f32 -> n),
|
||||
f64: (\n ->
|
||||
when n is
|
||||
123.0f64 -> n),
|
||||
|
||||
fdec: (\n ->
|
||||
when n is
|
||||
123dec -> n),
|
||||
ff32: (\n ->
|
||||
when n is
|
||||
123f32 -> n),
|
||||
ff64: (\n ->
|
||||
when n is
|
||||
123f64 -> n),
|
||||
}
|
||||
"#
|
||||
),
|
||||
r#"{ bi128 : I128 -> I128, bi16 : I16 -> I16, bi32 : I32 -> I32, bi64 : I64 -> I64, bi8 : I8 -> I8, bnat : Nat -> Nat, bu128 : U128 -> U128, bu16 : U16 -> U16, bu32 : U32 -> U32, bu64 : U64 -> U64, bu8 : U8 -> U8, dec : Dec -> Dec, f32 : F32 -> F32, f64 : F64 -> F64, fdec : Dec -> Dec, ff32 : F32 -> F32, ff64 : F64 -> F64, i128 : I128 -> I128, i16 : I16 -> I16, i32 : I32 -> I32, i64 : I64 -> I64, i8 : I8 -> I8, nat : Nat -> Nat, u128 : U128 -> U128, u16 : U16 -> U16, u32 : U32 -> U32, u64 : U64 -> U64, u8 : U8 -> U8 }"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::assert_evals_to;
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::assert_expect_failed;
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::assert_llvm_evals_to;
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::assert_non_opt_evals_to;
|
||||
|
@ -8,6 +10,8 @@ use crate::helpers::llvm::assert_non_opt_evals_to;
|
|||
#[cfg(feature = "gen-dev")]
|
||||
use crate::helpers::dev::assert_evals_to;
|
||||
// #[cfg(feature = "gen-dev")]
|
||||
// use crate::helpers::dev::assert_expect_failed;
|
||||
// #[cfg(feature = "gen-dev")]
|
||||
// use crate::helpers::dev::assert_evals_to as assert_llvm_evals_to;
|
||||
// #[cfg(feature = "gen-dev")]
|
||||
// use crate::helpers::dev::assert_evals_to as assert_non_opt_evals_to;
|
||||
|
@ -15,6 +19,8 @@ use crate::helpers::dev::assert_evals_to;
|
|||
#[cfg(feature = "gen-wasm")]
|
||||
use crate::helpers::wasm::assert_evals_to;
|
||||
// #[cfg(feature = "gen-wasm")]
|
||||
// use crate::helpers::dev::assert_expect_failed;
|
||||
// #[cfg(feature = "gen-wasm")]
|
||||
// use crate::helpers::wasm::assert_evals_to as assert_llvm_evals_to;
|
||||
// #[cfg(feature = "gen-wasm")]
|
||||
// use crate::helpers::wasm::assert_evals_to as assert_non_opt_evals_to;
|
||||
|
@ -2485,9 +2491,9 @@ fn call_invalid_layout() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[should_panic(expected = "An expectation failed!")]
|
||||
#[should_panic(expected = "Failed with 1 failures. Failures: ")]
|
||||
fn expect_fail() {
|
||||
assert_evals_to!(
|
||||
assert_expect_failed!(
|
||||
indoc!(
|
||||
r#"
|
||||
expect 1 == 2
|
||||
|
|
|
@ -257,5 +257,25 @@ macro_rules! assert_evals_to {
|
|||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_expect_failed {
|
||||
($src:expr, $expected:expr, $ty:ty, $failures:expr) => {{
|
||||
use bumpalo::Bump;
|
||||
use roc_gen_dev::run_jit_function_raw;
|
||||
let stdlib = roc_builtins::std::standard_stdlib();
|
||||
|
||||
let arena = Bump::new();
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::dev::helper(&arena, $src, stdlib, true, true);
|
||||
|
||||
let transform = |success| {
|
||||
let expected = $expected;
|
||||
assert_eq!(&success, &expected);
|
||||
};
|
||||
run_jit_function_raw!(lib, main_fn_name, $ty, transform, errors);
|
||||
}};
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_evals_to;
|
||||
pub(crate) use assert_expect_failed;
|
||||
|
|
|
@ -192,7 +192,11 @@ fn create_llvm_module<'a>(
|
|||
for function in FunctionIterator::from_module(module) {
|
||||
let name = function.get_name().to_str().unwrap();
|
||||
if name.starts_with("roc_builtins") {
|
||||
function.set_linkage(Linkage::Internal);
|
||||
if name.starts_with("roc_builtins.expect") {
|
||||
function.set_linkage(Linkage::External);
|
||||
} else {
|
||||
function.set_linkage(Linkage::Internal);
|
||||
}
|
||||
}
|
||||
|
||||
if name.starts_with("roc_builtins.dict") {
|
||||
|
@ -601,6 +605,46 @@ macro_rules! assert_evals_to {
|
|||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! assert_expect_failed {
|
||||
($src:expr, $expected:expr, $ty:ty) => {
|
||||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use roc_gen_llvm::run_jit_function;
|
||||
|
||||
let arena = Bump::new();
|
||||
let context = Context::create();
|
||||
|
||||
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
||||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||
|
||||
let is_gen_test = true;
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::llvm::helper(&arena, $src, stdlib, is_gen_test, false, &context);
|
||||
|
||||
let transform = |success| {
|
||||
let expected = $expected;
|
||||
assert_eq!(&success, &expected, "LLVM test failed");
|
||||
};
|
||||
|
||||
run_jit_function!(lib, main_fn_name, $ty, transform, errors)
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty) => {
|
||||
$crate::helpers::llvm::assert_llvm_evals_to!(
|
||||
$src,
|
||||
$expected,
|
||||
$ty,
|
||||
$crate::helpers::llvm::identity,
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! expect_runtime_error_panic {
|
||||
($src:expr) => {{
|
||||
#[cfg(feature = "wasm-cli-run")]
|
||||
|
@ -651,6 +695,8 @@ macro_rules! assert_non_opt_evals_to {
|
|||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_expect_failed;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_llvm_evals_to;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use assert_non_opt_evals_to;
|
||||
|
|
|
@ -24,6 +24,11 @@ pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
|||
libc::malloc(size)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_memcpy(dest: *mut c_void, src: *const c_void, bytes: usize) -> *mut c_void {
|
||||
libc::memcpy(dest, src, bytes)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::subs::{Content, FlatType, GetSubsSlice, Subs, UnionTags, Variable};
|
||||
use crate::subs::{AliasVariables, Content, FlatType, GetSubsSlice, Subs, UnionTags, Variable};
|
||||
use crate::types::{name_type_var, RecordField};
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
|
@ -289,6 +289,17 @@ pub fn content_to_string(
|
|||
buf
|
||||
}
|
||||
|
||||
pub fn get_single_arg<'a>(subs: &'a Subs, args: &'a AliasVariables) -> &'a Content {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let arg_var_index = args
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("Num was not applied to a type argument!");
|
||||
let arg_var = subs[arg_var_index];
|
||||
subs.get_content_without_compacting(arg_var)
|
||||
}
|
||||
|
||||
fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, parens: Parens) {
|
||||
use crate::subs::Content::*;
|
||||
|
||||
|
@ -306,18 +317,19 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
|||
|
||||
match *symbol {
|
||||
Symbol::NUM_NUM => {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let arg_var_index = args
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("Num was not applied to a type argument!");
|
||||
let arg_var = subs[arg_var_index];
|
||||
let content = subs.get_content_without_compacting(arg_var);
|
||||
|
||||
match &content {
|
||||
Alias(nested, _, _) => match *nested {
|
||||
Symbol::NUM_INTEGER => buf.push_str("I64"),
|
||||
let content = get_single_arg(subs, args);
|
||||
match *content {
|
||||
Alias(nested, args, _actual) => match nested {
|
||||
Symbol::NUM_INTEGER => {
|
||||
write_integer(
|
||||
env,
|
||||
get_single_arg(subs, &args),
|
||||
subs,
|
||||
buf,
|
||||
parens,
|
||||
false,
|
||||
);
|
||||
}
|
||||
Symbol::NUM_FLOATINGPOINT => buf.push_str("F64"),
|
||||
|
||||
_ => write_parens!(write_parens, buf, {
|
||||
|
@ -333,6 +345,33 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
|||
}
|
||||
}
|
||||
|
||||
Symbol::NUM_INT => {
|
||||
let content = get_single_arg(subs, args);
|
||||
|
||||
write_integer(env, content, subs, buf, parens, write_parens)
|
||||
}
|
||||
|
||||
Symbol::NUM_FLOAT => {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let arg_var_index = args
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("Num was not applied to a type argument!");
|
||||
let arg_var = subs[arg_var_index];
|
||||
let content = subs.get_content_without_compacting(arg_var);
|
||||
|
||||
match content {
|
||||
Alias(Symbol::NUM_BINARY32, _, _) => buf.push_str("F32"),
|
||||
Alias(Symbol::NUM_BINARY64, _, _) => buf.push_str("F64"),
|
||||
Alias(Symbol::NUM_DECIMAL, _, _) => buf.push_str("Dec"),
|
||||
_ => write_parens!(write_parens, buf, {
|
||||
buf.push_str("Float ");
|
||||
write_content(env, content, subs, buf, parens);
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
_ => write_parens!(write_parens, buf, {
|
||||
write_symbol(env, *symbol, buf);
|
||||
|
||||
|
@ -362,6 +401,51 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
|||
}
|
||||
}
|
||||
|
||||
fn write_integer(
|
||||
env: &Env,
|
||||
content: &Content,
|
||||
subs: &Subs,
|
||||
buf: &mut String,
|
||||
parens: Parens,
|
||||
write_parens: bool,
|
||||
) {
|
||||
use crate::subs::Content::*;
|
||||
|
||||
macro_rules! derive_num_writes {
|
||||
($($lit:expr, $tag:path)*) => {
|
||||
write_parens!(
|
||||
write_parens,
|
||||
buf,
|
||||
match content {
|
||||
$(
|
||||
&Alias($tag, _, _) => {
|
||||
buf.push_str($lit)
|
||||
},
|
||||
)*
|
||||
actual => {
|
||||
buf.push_str("Int ");
|
||||
write_content(env, actual, subs, buf, parens);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
derive_num_writes! {
|
||||
"U8", Symbol::NUM_UNSIGNED8
|
||||
"U16", Symbol::NUM_UNSIGNED16
|
||||
"U32", Symbol::NUM_UNSIGNED32
|
||||
"U64", Symbol::NUM_UNSIGNED64
|
||||
"U128", Symbol::NUM_UNSIGNED128
|
||||
"I8", Symbol::NUM_SIGNED8
|
||||
"I16", Symbol::NUM_SIGNED16
|
||||
"I32", Symbol::NUM_SIGNED32
|
||||
"I64", Symbol::NUM_SIGNED64
|
||||
"I128", Symbol::NUM_SIGNED128
|
||||
"Nat", Symbol::NUM_NATURAL
|
||||
}
|
||||
}
|
||||
|
||||
enum ExtContent<'a> {
|
||||
Empty,
|
||||
Content(Variable, &'a Content),
|
||||
|
|
|
@ -1626,6 +1626,25 @@ pub enum FlatType {
|
|||
EmptyTagUnion,
|
||||
}
|
||||
|
||||
impl FlatType {
|
||||
pub fn get_singleton_tag_union<'a>(&'a self, subs: &'a Subs) -> Option<&'a TagName> {
|
||||
match self {
|
||||
Self::TagUnion(tags, ext) => {
|
||||
let tags = tags.unsorted_tags_and_ext(subs, *ext).0.tags;
|
||||
if tags.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
let (tag_name, vars) = tags[0];
|
||||
if !vars.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(tag_name)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum Builtin {
|
||||
Str,
|
||||
|
|
|
@ -1186,6 +1186,7 @@ pub enum Reason {
|
|||
RecordUpdateValue(Lowercase),
|
||||
RecordUpdateKeys(Symbol, SendMap<Lowercase, Region>),
|
||||
RecordDefaultField(Lowercase),
|
||||
NumericLiteralSuffix,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
|
|
|
@ -33,7 +33,6 @@ platform "test-platform"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
10
examples/benchmarks/platform/Effect.roc
Normal file
10
examples/benchmarks/platform/Effect.roc
Normal file
|
@ -0,0 +1,10 @@
|
|||
hosted Effect
|
||||
exposes [ Effect, after, map, always, forever, loop, putLine, putInt, getInt ]
|
||||
imports []
|
||||
generates Effect with [ after, map, always, forever, loop ]
|
||||
|
||||
putLine : Str -> Effect {}
|
||||
|
||||
putInt : I64 -> Effect {}
|
||||
|
||||
getInt : Effect { value : I64, errorCode : [ A, B ], isError : Bool }
|
|
@ -4,12 +4,6 @@ platform "folkertdev/foo"
|
|||
packages {}
|
||||
imports [ Task.{ Task } ]
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect
|
||||
{
|
||||
putLine : Str -> Effect {},
|
||||
putInt : I64 -> Effect {},
|
||||
getInt : Effect { value : I64, errorCode : [ A, B ], isError : Bool },
|
||||
}
|
||||
|
||||
mainForHost : Task {} [] as Fx
|
||||
mainForHost = main
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
interface Task
|
||||
exposes [ Task, succeed, fail, after, map, putLine, putInt, getInt, forever, loop ]
|
||||
imports [ fx.Effect ]
|
||||
imports [ pf.Effect ]
|
||||
|
||||
Task ok err : Effect.Effect (Result ok err)
|
||||
|
||||
|
|
8
examples/cli/platform/Effect.roc
Normal file
8
examples/cli/platform/Effect.roc
Normal file
|
@ -0,0 +1,8 @@
|
|||
hosted Effect
|
||||
exposes [ Effect, after, map, always, forever, loop, putLine, getLine ]
|
||||
imports []
|
||||
generates Effect with [ after, map, always, forever, loop ]
|
||||
|
||||
putLine : Str -> Effect {}
|
||||
|
||||
getLine : Effect Str
|
|
@ -4,7 +4,6 @@ platform "examples/cli"
|
|||
packages {}
|
||||
imports [ Task.{ Task } ]
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect { putLine : Str -> Effect {}, getLine : Effect Str }
|
||||
|
||||
mainForHost : Task {} [] as Fx
|
||||
mainForHost = main
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
interface Stdin
|
||||
exposes [ line ]
|
||||
imports [ fx.Effect, Task ]
|
||||
imports [ pf.Effect, Task ]
|
||||
|
||||
line : Task.Task Str *
|
||||
line = Effect.after Effect.getLine Task.succeed# TODO FIXME Effect.getLine should suffice
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
interface Stdout
|
||||
exposes [ line ]
|
||||
imports [ fx.Effect, Task.{ Task } ]
|
||||
imports [ pf.Effect, Task.{ Task } ]
|
||||
|
||||
# line : Str -> Task.Task {} *
|
||||
# line = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
interface Task
|
||||
exposes [ Task, succeed, fail, await, map, onFail, attempt, forever, loop ]
|
||||
imports [ fx.Effect ]
|
||||
imports [ pf.Effect ]
|
||||
|
||||
Task ok err : Effect.Effect (Result ok err)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
app "effect-example"
|
||||
packages { pf: "thing/platform-dir" }
|
||||
imports [ fx.Effect ]
|
||||
imports [ pf.Effect ]
|
||||
provides [ main ] to pf
|
||||
|
||||
main : Effect.Effect {}
|
||||
|
|
8
examples/effect/thing/platform-dir/Effect.roc
Normal file
8
examples/effect/thing/platform-dir/Effect.roc
Normal file
|
@ -0,0 +1,8 @@
|
|||
hosted Effect
|
||||
exposes [ Effect, after, map, always, forever, putLine, getLine ]
|
||||
imports []
|
||||
generates Effect with [ after, map, always, forever ]
|
||||
|
||||
putLine : Str -> Effect {}
|
||||
|
||||
getLine : Effect Str
|
|
@ -2,13 +2,8 @@ platform "roc-examples/cli"
|
|||
requires {} { main : Effect {} }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [ fx.Effect ]
|
||||
imports [ pf.Effect ]
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect
|
||||
{
|
||||
putLine : Str -> Effect {},
|
||||
getLine : Effect Str,
|
||||
}
|
||||
|
||||
mainForHost : Effect.Effect {} as Fx
|
||||
mainForHost = main
|
||||
|
|
22
examples/false-interpreter/platform/Effect.roc
Normal file
22
examples/false-interpreter/platform/Effect.roc
Normal file
|
@ -0,0 +1,22 @@
|
|||
hosted Effect
|
||||
exposes [ Effect, after, map, always, forever, loop, openFile, closeFile, withFileOpen, getFileLine, getFileBytes, putLine, putRaw, getLine, getChar ]
|
||||
imports []
|
||||
generates Effect with [ after, map, always, forever, loop ]
|
||||
|
||||
openFile : Str -> Effect U64
|
||||
|
||||
closeFile : U64 -> Effect {}
|
||||
|
||||
withFileOpen : Str, (U64 -> Effect (Result ok err)) -> Effect {}
|
||||
|
||||
getFileLine : U64 -> Effect Str
|
||||
|
||||
getFileBytes : U64 -> Effect (List U8)
|
||||
|
||||
putLine : Str -> Effect {}
|
||||
|
||||
putRaw : Str -> Effect {}
|
||||
|
||||
getLine : Effect Str
|
||||
|
||||
getChar : Effect U8
|
|
@ -1,6 +1,6 @@
|
|||
interface File
|
||||
exposes [ line, Handle, withOpen, chunk ]
|
||||
imports [ fx.Effect, Task.{ Task } ]
|
||||
imports [ pf.Effect, Task.{ Task } ]
|
||||
|
||||
Handle : [ @Handle U64 ]
|
||||
|
||||
|
@ -24,4 +24,4 @@ withOpen = \path, callback ->
|
|||
handle <- Task.await (open path)
|
||||
result <- Task.attempt (callback handle)
|
||||
{ } <- Task.await (close handle)
|
||||
Task.fromResult result
|
||||
Task.fromResult result
|
||||
|
|
|
@ -4,19 +4,6 @@ platform "examples/cli"
|
|||
packages {}
|
||||
imports [ Task.{ Task } ]
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect
|
||||
{
|
||||
openFile : Str -> Effect U64,
|
||||
closeFile : U64 -> Effect {},
|
||||
withFileOpen : Str, (U64 -> Effect (Result ok err)) -> Effect {},
|
||||
getFileLine : U64 -> Effect Str,
|
||||
getFileBytes : U64 -> Effect (List U8),
|
||||
putLine : Str -> Effect {},
|
||||
putRaw : Str -> Effect {},
|
||||
# Is there a limit to the number of effect, uncomment the next line and it crashes
|
||||
# getLine : Effect Str,
|
||||
getChar : Effect U8,
|
||||
}
|
||||
|
||||
mainForHost : Str -> Task {} [] as Fx
|
||||
mainForHost = \file -> main file
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
interface Stdin
|
||||
exposes [ char ]
|
||||
imports [ fx.Effect, Task ]
|
||||
imports [ pf.Effect, Task ]
|
||||
|
||||
# line : Task.Task Str *
|
||||
# line = Effect.after Effect.getLine Task.succeed # TODO FIXME Effect.getLine should suffice
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
interface Stdout
|
||||
exposes [ line, raw ]
|
||||
imports [ fx.Effect, Task.{ Task } ]
|
||||
imports [ pf.Effect, Task.{ Task } ]
|
||||
|
||||
line : Str -> Task {} *
|
||||
line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {})
|
||||
|
||||
raw : Str -> Task {} *
|
||||
raw = \str -> Effect.map (Effect.putRaw str) (\_ -> Ok {})
|
||||
raw = \str -> Effect.map (Effect.putRaw str) (\_ -> Ok {})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
interface Task
|
||||
exposes [ Task, succeed, fail, await, map, onFail, attempt, fromResult, loop ]
|
||||
imports [ fx.Effect ]
|
||||
imports [ pf.Effect ]
|
||||
|
||||
Task ok err : Effect.Effect (Result ok err)
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "examples/add"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : I64 -> I64
|
||||
mainForHost = \a -> main a
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "examples/hello-world"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "examples/hello-swift"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "examples/hello-world"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "examples/hello-world"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "examples/hello-world"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "examples/quicksort"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : List I64 -> List I64
|
||||
mainForHost = \list -> quicksort list
|
||||
|
|
|
@ -4,7 +4,6 @@ platform "folkertdev/foo"
|
|||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : { init : ({} -> Model) as Init, update : (Model, Str -> Model) as Update, view : (Model -> Str) as View }
|
||||
mainForHost = main
|
||||
|
|
|
@ -8,18 +8,22 @@ version = "0.1.0"
|
|||
[dependencies]
|
||||
bumpalo = {version = "3.8.0", features = ["collections"]}
|
||||
const_format = "0.2.22"
|
||||
inkwell = {path = "../vendor/inkwell"}
|
||||
libloading = {version = "0.7.1"}
|
||||
rustyline = {git = "https://github.com/rtfeldman/rustyline", tag = "v9.1.1"}
|
||||
rustyline-derive = {git = "https://github.com/rtfeldman/rustyline", tag = "v9.1.1"}
|
||||
target-lexicon = "0.12.2"
|
||||
|
||||
roc_build = {path = "../compiler/build"}
|
||||
roc_collections = {path = "../compiler/collections"}
|
||||
roc_gen_llvm = {path = "../compiler/gen_llvm"}
|
||||
roc_load = {path = "../compiler/load"}
|
||||
roc_mono = {path = "../compiler/mono"}
|
||||
roc_parse = {path = "../compiler/parse"}
|
||||
roc_repl_eval = {path = "../repl_eval"}
|
||||
|
||||
[dev-dependencies]
|
||||
indoc = "1.0.3"
|
||||
roc_test_utils = {path = "../test_utils"}
|
||||
strip-ansi-escapes = "0.1.1"
|
||||
roc_target = {path = "../compiler/roc_target"}
|
||||
roc_types = {path = "../compiler/types"}
|
||||
roc_builtins = {path = "../compiler/builtins"}
|
||||
|
||||
[lib]
|
||||
name = "roc_repl_cli"
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
use bumpalo::Bump;
|
||||
use const_format::concatcp;
|
||||
use inkwell::context::Context;
|
||||
use libloading::Library;
|
||||
use rustyline::highlight::{Highlighter, PromptInfo};
|
||||
use rustyline::validate::{self, ValidationContext, ValidationResult, Validator};
|
||||
use rustyline_derive::{Completer, Helper, Hinter};
|
||||
use std::borrow::Cow;
|
||||
use std::io;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
use roc_build::link::module_to_dylib;
|
||||
use roc_collections::all::MutSet;
|
||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||
use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type};
|
||||
use roc_load::file::MonomorphizedModule;
|
||||
use roc_mono::ir::OptLevel;
|
||||
use roc_parse::ast::Expr;
|
||||
use roc_parse::parser::{EExpr, ELambda, SyntaxError};
|
||||
use roc_repl_eval::gen::{gen_and_eval, ReplOutput};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
use roc_repl_eval::eval::jit_to_ast;
|
||||
use roc_repl_eval::gen::{compile_to_mono, format_answer, ReplOutput};
|
||||
use roc_repl_eval::ReplApp;
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
||||
|
||||
const BLUE: &str = "\u{001b}[36m";
|
||||
const PINK: &str = "\u{001b}[35m";
|
||||
|
@ -105,6 +117,210 @@ impl Validator for InputValidator {
|
|||
}
|
||||
}
|
||||
|
||||
struct CliReplApp {
|
||||
lib: Library,
|
||||
}
|
||||
|
||||
macro_rules! deref_number {
|
||||
($name: ident, $t: ty) => {
|
||||
fn $name(&self, addr: usize) -> $t {
|
||||
let ptr = addr as *const _;
|
||||
unsafe { *ptr }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl ReplApp for CliReplApp {
|
||||
deref_number!(deref_bool, bool);
|
||||
|
||||
deref_number!(deref_u8, u8);
|
||||
deref_number!(deref_u16, u16);
|
||||
deref_number!(deref_u32, u32);
|
||||
deref_number!(deref_u64, u64);
|
||||
deref_number!(deref_u128, u128);
|
||||
deref_number!(deref_usize, usize);
|
||||
|
||||
deref_number!(deref_i8, i8);
|
||||
deref_number!(deref_i16, i16);
|
||||
deref_number!(deref_i32, i32);
|
||||
deref_number!(deref_i64, i64);
|
||||
deref_number!(deref_i128, i128);
|
||||
deref_number!(deref_isize, isize);
|
||||
|
||||
deref_number!(deref_f32, f32);
|
||||
deref_number!(deref_f64, f64);
|
||||
|
||||
fn deref_str(&self, addr: usize) -> &str {
|
||||
unsafe { *(addr as *const &'static str) }
|
||||
}
|
||||
|
||||
/// Run user code that returns a type with a `Builtin` layout
|
||||
/// Size of the return value is statically determined from its Rust type
|
||||
fn call_function<'a, T: Sized, F: Fn(T) -> Expr<'a>>(
|
||||
&self,
|
||||
main_fn_name: &str,
|
||||
transform: F,
|
||||
) -> Expr<'a> {
|
||||
run_jit_function!(self.lib, main_fn_name, T, transform)
|
||||
}
|
||||
|
||||
/// Run user code that returns a struct or union, whose size is provided as an argument
|
||||
fn call_function_dynamic_size<T: Sized, F: Fn(usize) -> T>(
|
||||
&self,
|
||||
main_fn_name: &str,
|
||||
bytes: usize,
|
||||
transform: F,
|
||||
) -> T {
|
||||
run_jit_function_dynamic_type!(self.lib, main_fn_name, bytes, transform)
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_and_eval_llvm<'a>(
|
||||
src: &str,
|
||||
target: Triple,
|
||||
opt_level: OptLevel,
|
||||
) -> Result<ReplOutput, SyntaxError<'a>> {
|
||||
let arena = Bump::new();
|
||||
let target_info = TargetInfo::from(&target);
|
||||
|
||||
let loaded = match compile_to_mono(&arena, src, target_info) {
|
||||
Ok(x) => x,
|
||||
Err(prob_strings) => {
|
||||
return Ok(ReplOutput::Problems(prob_strings));
|
||||
}
|
||||
};
|
||||
|
||||
let MonomorphizedModule {
|
||||
procedures,
|
||||
entry_point,
|
||||
interns,
|
||||
exposed_to_host,
|
||||
mut subs,
|
||||
module_id: home,
|
||||
..
|
||||
} = loaded;
|
||||
|
||||
let context = Context::create();
|
||||
let builder = context.create_builder();
|
||||
let module = arena.alloc(roc_gen_llvm::llvm::build::module_from_builtins(
|
||||
&target, &context, "",
|
||||
));
|
||||
|
||||
debug_assert_eq!(exposed_to_host.values.len(), 1);
|
||||
let (main_fn_symbol, main_fn_var) = exposed_to_host.values.iter().next().unwrap();
|
||||
let main_fn_symbol = *main_fn_symbol;
|
||||
let main_fn_var = *main_fn_var;
|
||||
|
||||
// pretty-print the expr type string for later.
|
||||
name_all_type_vars(main_fn_var, &mut subs);
|
||||
let content = subs.get_content_without_compacting(main_fn_var);
|
||||
let expr_type_str = content_to_string(content, &subs, home, &interns);
|
||||
|
||||
let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) {
|
||||
Some(layout) => *layout,
|
||||
None => {
|
||||
return Ok(ReplOutput::NoProblems {
|
||||
expr: "<function>".to_string(),
|
||||
expr_type: expr_type_str,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let module = arena.alloc(module);
|
||||
let (module_pass, function_pass) =
|
||||
roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level);
|
||||
|
||||
let (dibuilder, compile_unit) = roc_gen_llvm::llvm::build::Env::new_debug_info(module);
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
let env = roc_gen_llvm::llvm::build::Env {
|
||||
arena: &arena,
|
||||
builder: &builder,
|
||||
dibuilder: &dibuilder,
|
||||
compile_unit: &compile_unit,
|
||||
context: &context,
|
||||
interns,
|
||||
module,
|
||||
target_info,
|
||||
is_gen_test: true, // so roc_panic is generated
|
||||
// important! we don't want any procedures to get the C calling convention
|
||||
exposed_to_host: MutSet::default(),
|
||||
};
|
||||
|
||||
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
||||
// platform to provide them.
|
||||
add_default_roc_externs(&env);
|
||||
|
||||
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
|
||||
&env,
|
||||
opt_level,
|
||||
procedures,
|
||||
entry_point,
|
||||
);
|
||||
|
||||
env.dibuilder.finalize();
|
||||
|
||||
// we don't use the debug info, and it causes weird errors.
|
||||
module.strip_debug_info();
|
||||
|
||||
// Uncomment this to see the module's un-optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
if main_fn.verify(true) {
|
||||
function_pass.run_on(&main_fn);
|
||||
} else {
|
||||
panic!("Main function {} failed LLVM verification in build. Uncomment things nearby to see more details.", main_fn_name);
|
||||
}
|
||||
|
||||
module_pass.run_on(env.module);
|
||||
|
||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
// Verify the module
|
||||
if let Err(errors) = env.module.verify() {
|
||||
panic!(
|
||||
"Errors defining module:\n{}\n\nUncomment things nearby to see more details.",
|
||||
errors.to_string()
|
||||
);
|
||||
}
|
||||
|
||||
let lib = module_to_dylib(env.module, &target, opt_level)
|
||||
.expect("Error loading compiled dylib for test");
|
||||
|
||||
let app = CliReplApp { lib };
|
||||
|
||||
let res_answer = jit_to_ast(
|
||||
&arena,
|
||||
&app,
|
||||
main_fn_name,
|
||||
main_fn_layout,
|
||||
content,
|
||||
&env.interns,
|
||||
home,
|
||||
&subs,
|
||||
target_info,
|
||||
);
|
||||
|
||||
let formatted = format_answer(&arena, res_answer, expr_type_str);
|
||||
Ok(formatted)
|
||||
}
|
||||
|
||||
fn eval_and_format<'a>(src: &str) -> Result<String, SyntaxError<'a>> {
|
||||
let format_output = |output| match output {
|
||||
ReplOutput::NoProblems { expr, expr_type } => {
|
||||
format!("\n{} {}:{} {}", expr, PINK, END_COL, expr_type)
|
||||
}
|
||||
ReplOutput::Problems(lines) => format!("\n{}\n", lines.join("\n\n")),
|
||||
};
|
||||
|
||||
gen_and_eval_llvm(src, Triple::host(), OptLevel::Normal).map(format_output)
|
||||
}
|
||||
|
||||
fn report_parse_error(fail: SyntaxError) {
|
||||
println!("TODO Gracefully report parse error in repl: {:?}", fail);
|
||||
}
|
||||
|
||||
pub fn main() -> io::Result<()> {
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::Editor;
|
||||
|
@ -222,19 +438,3 @@ pub fn main() -> io::Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn report_parse_error(fail: SyntaxError) {
|
||||
println!("TODO Gracefully report parse error in repl: {:?}", fail);
|
||||
}
|
||||
|
||||
fn eval_and_format<'a>(src: &str) -> Result<String, SyntaxError<'a>> {
|
||||
use roc_mono::ir::OptLevel;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
gen_and_eval(src.as_bytes(), Triple::host(), OptLevel::Normal).map(|output| match output {
|
||||
ReplOutput::NoProblems { expr, expr_type } => {
|
||||
format!("\n{} {}:{} {}", expr, PINK, END_COL, expr_type)
|
||||
}
|
||||
ReplOutput::Problems(lines) => format!("\n{}\n", lines.join("\n\n")),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,22 +5,13 @@ version = "0.1.0"
|
|||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["llvm"]
|
||||
llvm = ["inkwell", "libloading", "roc_gen_llvm", "roc_build/llvm"]
|
||||
|
||||
[dependencies]
|
||||
bumpalo = {version = "3.8.0", features = ["collections"]}
|
||||
inkwell = {path = "../vendor/inkwell", optional = true}
|
||||
libloading = {version = "0.7.1", optional = true}
|
||||
target-lexicon = "0.12.2"
|
||||
|
||||
roc_build = {path = "../compiler/build", default-features = false}
|
||||
roc_builtins = {path = "../compiler/builtins"}
|
||||
roc_can = {path = "../compiler/can"}
|
||||
roc_collections = {path = "../compiler/collections"}
|
||||
roc_fmt = {path = "../compiler/fmt"}
|
||||
roc_gen_llvm = {path = "../compiler/gen_llvm", optional = true}
|
||||
roc_load = {path = "../compiler/load"}
|
||||
roc_module = {path = "../compiler/module"}
|
||||
roc_mono = {path = "../compiler/mono"}
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
use std::mem::size_of;
|
||||
|
||||
pub trait AppMemory {
|
||||
fn deref_bool(&self, addr: usize) -> bool;
|
||||
|
||||
fn deref_u8(&self, addr: usize) -> u8;
|
||||
fn deref_u16(&self, addr: usize) -> u16;
|
||||
fn deref_u32(&self, addr: usize) -> u32;
|
||||
fn deref_u64(&self, addr: usize) -> u64;
|
||||
fn deref_u128(&self, addr: usize) -> u128;
|
||||
fn deref_usize(&self, addr: usize) -> usize;
|
||||
|
||||
fn deref_i8(&self, addr: usize) -> i8;
|
||||
fn deref_i16(&self, addr: usize) -> i16;
|
||||
fn deref_i32(&self, addr: usize) -> i32;
|
||||
fn deref_i64(&self, addr: usize) -> i64;
|
||||
fn deref_i128(&self, addr: usize) -> i128;
|
||||
fn deref_isize(&self, addr: usize) -> isize;
|
||||
|
||||
fn deref_f32(&self, addr: usize) -> f32;
|
||||
fn deref_f64(&self, addr: usize) -> f64;
|
||||
|
||||
fn deref_str(&self, addr: usize) -> &str;
|
||||
}
|
||||
|
||||
/// A block of app memory in the same address space as the compiler
|
||||
pub struct AppMemoryInternal;
|
||||
|
||||
macro_rules! internal_number_type {
|
||||
($name: ident, $t: ty) => {
|
||||
fn $name(&self, addr: usize) -> $t {
|
||||
let ptr = addr as *const _;
|
||||
unsafe { *ptr }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl AppMemory for AppMemoryInternal {
|
||||
internal_number_type!(deref_bool, bool);
|
||||
|
||||
internal_number_type!(deref_u8, u8);
|
||||
internal_number_type!(deref_u16, u16);
|
||||
internal_number_type!(deref_u32, u32);
|
||||
internal_number_type!(deref_u64, u64);
|
||||
internal_number_type!(deref_u128, u128);
|
||||
internal_number_type!(deref_usize, usize);
|
||||
|
||||
internal_number_type!(deref_i8, i8);
|
||||
internal_number_type!(deref_i16, i16);
|
||||
internal_number_type!(deref_i32, i32);
|
||||
internal_number_type!(deref_i64, i64);
|
||||
internal_number_type!(deref_i128, i128);
|
||||
internal_number_type!(deref_isize, isize);
|
||||
|
||||
internal_number_type!(deref_f32, f32);
|
||||
internal_number_type!(deref_f64, f64);
|
||||
|
||||
fn deref_str(&self, addr: usize) -> &str {
|
||||
unsafe { *(addr as *const &'static str) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A block of app memory copied from an exteral address space outside the compiler
|
||||
/// (e.g. compiler and app are in separate Wasm modules)
|
||||
pub struct AppMemoryExternal<'a> {
|
||||
bytes: &'a [u8],
|
||||
}
|
||||
|
||||
macro_rules! external_number_type {
|
||||
($name: ident, $t: ty) => {
|
||||
fn $name(&self, address: usize) -> $t {
|
||||
const N: usize = size_of::<$t>();
|
||||
let mut array = [0; N];
|
||||
array.copy_from_slice(&self.bytes[address..][..N]);
|
||||
<$t>::from_le_bytes(array)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'a> AppMemory for AppMemoryExternal<'a> {
|
||||
fn deref_bool(&self, address: usize) -> bool {
|
||||
self.bytes[address] != 0
|
||||
}
|
||||
|
||||
external_number_type!(deref_u8, u8);
|
||||
external_number_type!(deref_u16, u16);
|
||||
external_number_type!(deref_u32, u32);
|
||||
external_number_type!(deref_u64, u64);
|
||||
external_number_type!(deref_u128, u128);
|
||||
external_number_type!(deref_usize, usize);
|
||||
|
||||
external_number_type!(deref_i8, i8);
|
||||
external_number_type!(deref_i16, i16);
|
||||
external_number_type!(deref_i32, i32);
|
||||
external_number_type!(deref_i64, i64);
|
||||
external_number_type!(deref_i128, i128);
|
||||
external_number_type!(deref_isize, isize);
|
||||
|
||||
external_number_type!(deref_f32, f32);
|
||||
external_number_type!(deref_f64, f64);
|
||||
|
||||
fn deref_str(&self, addr: usize) -> &str {
|
||||
let elems_addr = self.deref_usize(addr);
|
||||
let len = self.deref_usize(addr + size_of::<usize>());
|
||||
let bytes = &self.bytes[elems_addr..][..len];
|
||||
std::str::from_utf8(bytes).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn internal_u8() {
|
||||
let value: u8 = 123;
|
||||
let ptr = &value as *const u8;
|
||||
let addr = ptr as usize;
|
||||
let memory = AppMemoryInternal;
|
||||
let recovered: u8 = memory.deref_u8(addr);
|
||||
assert_eq!(value, recovered);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_u8() {
|
||||
let value: u8 = 123;
|
||||
let memory = AppMemoryExternal {
|
||||
bytes: &[0, 0, value, 0, 0],
|
||||
};
|
||||
let addr = 2;
|
||||
let recovered: u8 = memory.deref_u8(addr);
|
||||
assert_eq!(value, recovered);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn internal_i64() {
|
||||
let value: i64 = -123 << 33;
|
||||
let ptr = &value as *const i64;
|
||||
let addr = ptr as usize;
|
||||
let memory = AppMemoryInternal;
|
||||
let recovered: i64 = memory.deref_i64(addr);
|
||||
assert_eq!(value, recovered);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_i64() {
|
||||
let value: i64 = -1 << 33;
|
||||
let memory = AppMemoryExternal {
|
||||
bytes: &[
|
||||
0, 0, //
|
||||
0, 0, 0, 0, 0xfe, 0xff, 0xff, 0xff, //
|
||||
0, 0,
|
||||
],
|
||||
};
|
||||
let addr = 2;
|
||||
let recovered: i64 = memory.deref_i64(addr);
|
||||
assert_eq!(value, recovered);
|
||||
}
|
||||
}
|
|
@ -16,18 +16,12 @@ use roc_region::all::{Loc, Region};
|
|||
use roc_target::TargetInfo;
|
||||
use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, UnionTags, Variable};
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type};
|
||||
use crate::ReplApp;
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
type AppExecutable = libloading::Library;
|
||||
|
||||
use super::app_memory::AppMemory;
|
||||
|
||||
struct Env<'a, 'env, M> {
|
||||
struct Env<'a, 'env, A> {
|
||||
arena: &'a Bump,
|
||||
subs: &'env Subs,
|
||||
app_memory: &'a M,
|
||||
app: &'a A,
|
||||
target_info: TargetInfo,
|
||||
interns: &'env Interns,
|
||||
home: ModuleId,
|
||||
|
@ -46,9 +40,9 @@ pub enum ToAstProblem {
|
|||
/// we get to a struct or tag, we know what the labels are and can turn them
|
||||
/// back into the appropriate user-facing literals.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub unsafe fn jit_to_ast<'a, M: AppMemory>(
|
||||
pub fn jit_to_ast<'a, A: ReplApp>(
|
||||
arena: &'a Bump,
|
||||
app: AppExecutable,
|
||||
app: &'a A,
|
||||
main_fn_name: &str,
|
||||
layout: ProcLayout<'a>,
|
||||
content: &'a Content,
|
||||
|
@ -56,12 +50,11 @@ pub unsafe fn jit_to_ast<'a, M: AppMemory>(
|
|||
home: ModuleId,
|
||||
subs: &'a Subs,
|
||||
target_info: TargetInfo,
|
||||
app_memory: &'a M,
|
||||
) -> Result<Expr<'a>, ToAstProblem> {
|
||||
let env = Env {
|
||||
arena,
|
||||
subs,
|
||||
app_memory,
|
||||
app,
|
||||
target_info,
|
||||
interns,
|
||||
home,
|
||||
|
@ -73,7 +66,7 @@ pub unsafe fn jit_to_ast<'a, M: AppMemory>(
|
|||
result,
|
||||
} => {
|
||||
// this is a thunk
|
||||
jit_to_ast_help(&env, app, main_fn_name, &result, content)
|
||||
jit_to_ast_help(&env, main_fn_name, &result, content)
|
||||
}
|
||||
_ => Err(ToAstProblem::FunctionLayout),
|
||||
}
|
||||
|
@ -93,8 +86,8 @@ enum NewtypeKind<'a> {
|
|||
///
|
||||
/// The returned list of newtype containers is ordered by increasing depth. As an example,
|
||||
/// `A ({b : C 123})` will have the unrolled list `[Tag(A), RecordField(b), Tag(C)]`.
|
||||
fn unroll_newtypes<'a, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
fn unroll_newtypes<'a, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
mut content: &'a Content,
|
||||
) -> (Vec<'a, NewtypeKind<'a>>, &'a Content) {
|
||||
let mut newtype_containers = Vec::with_capacity_in(1, env.arena);
|
||||
|
@ -127,8 +120,8 @@ fn unroll_newtypes<'a, M: AppMemory>(
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_newtypes<'a, M: AppMemory>(
|
||||
env: &Env<'a, '_, M>,
|
||||
fn apply_newtypes<'a, A: ReplApp>(
|
||||
env: &Env<'a, '_, A>,
|
||||
newtype_containers: Vec<'a, NewtypeKind<'a>>,
|
||||
mut expr: Expr<'a>,
|
||||
) -> Expr<'a> {
|
||||
|
@ -155,15 +148,15 @@ fn apply_newtypes<'a, M: AppMemory>(
|
|||
expr
|
||||
}
|
||||
|
||||
fn unroll_aliases<'a, M: AppMemory>(env: &Env<'a, 'a, M>, mut content: &'a Content) -> &'a Content {
|
||||
fn unroll_aliases<'a, A: ReplApp>(env: &Env<'a, 'a, A>, mut content: &'a Content) -> &'a Content {
|
||||
while let Content::Alias(_, _, real) = content {
|
||||
content = env.subs.get_content_without_compacting(*real);
|
||||
}
|
||||
content
|
||||
}
|
||||
|
||||
fn unroll_recursion_var<'a, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
fn unroll_recursion_var<'a, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
mut content: &'a Content,
|
||||
) -> &'a Content {
|
||||
while let Content::RecursionVar { structure, .. } = content {
|
||||
|
@ -172,8 +165,8 @@ fn unroll_recursion_var<'a, M: AppMemory>(
|
|||
content
|
||||
}
|
||||
|
||||
fn get_tags_vars_and_variant<'a, M: AppMemory>(
|
||||
env: &Env<'a, '_, M>,
|
||||
fn get_tags_vars_and_variant<'a, A: ReplApp>(
|
||||
env: &Env<'a, '_, A>,
|
||||
tags: &UnionTags,
|
||||
opt_rec_var: Option<Variable>,
|
||||
) -> (MutMap<TagName, std::vec::Vec<Variable>>, UnionVariant<'a>) {
|
||||
|
@ -190,8 +183,8 @@ fn get_tags_vars_and_variant<'a, M: AppMemory>(
|
|||
(vars_of_tag, union_variant)
|
||||
}
|
||||
|
||||
fn expr_of_tag<'a, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
fn expr_of_tag<'a, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
data_addr: usize,
|
||||
tag_name: &TagName,
|
||||
arg_layouts: &'a [Layout<'a>],
|
||||
|
@ -213,8 +206,8 @@ fn expr_of_tag<'a, M: AppMemory>(
|
|||
|
||||
/// Gets the tag ID of a union variant, assuming that the tag ID is stored alongside (after) the
|
||||
/// tag data. The caller is expected to check that the tag ID is indeed stored this way.
|
||||
fn tag_id_from_data<'a, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
fn tag_id_from_data<'a, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
union_layout: UnionLayout,
|
||||
data_addr: usize,
|
||||
) -> i64 {
|
||||
|
@ -224,13 +217,13 @@ fn tag_id_from_data<'a, M: AppMemory>(
|
|||
let tag_id_addr = data_addr + offset as usize;
|
||||
|
||||
match union_layout.tag_id_builtin() {
|
||||
Builtin::Bool => env.app_memory.deref_bool(tag_id_addr) as i64,
|
||||
Builtin::Int(IntWidth::U8) => env.app_memory.deref_u8(tag_id_addr) as i64,
|
||||
Builtin::Int(IntWidth::U16) => env.app_memory.deref_u16(tag_id_addr) as i64,
|
||||
Builtin::Bool => env.app.deref_bool(tag_id_addr) as i64,
|
||||
Builtin::Int(IntWidth::U8) => env.app.deref_u8(tag_id_addr) as i64,
|
||||
Builtin::Int(IntWidth::U16) => env.app.deref_u16(tag_id_addr) as i64,
|
||||
Builtin::Int(IntWidth::U64) => {
|
||||
// used by non-recursive unions at the
|
||||
// moment, remove if that is no longer the case
|
||||
env.app_memory.deref_i64(tag_id_addr)
|
||||
env.app.deref_i64(tag_id_addr)
|
||||
}
|
||||
_ => unreachable!("invalid tag id layout"),
|
||||
}
|
||||
|
@ -240,13 +233,13 @@ fn tag_id_from_data<'a, M: AppMemory>(
|
|||
/// pointer to the data of the union variant). Returns
|
||||
/// - the tag ID
|
||||
/// - the address of the data of the union variant, unmasked if the pointer held the tag ID
|
||||
fn tag_id_from_recursive_ptr<'a, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
fn tag_id_from_recursive_ptr<'a, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
union_layout: UnionLayout,
|
||||
rec_addr: usize,
|
||||
) -> (i64, usize) {
|
||||
let tag_in_ptr = union_layout.stores_tag_id_in_pointer(env.target_info);
|
||||
let addr_with_id = env.app_memory.deref_usize(rec_addr);
|
||||
let addr_with_id = env.app.deref_usize(rec_addr);
|
||||
|
||||
if tag_in_ptr {
|
||||
let (_, tag_id_mask) = UnionLayout::tag_id_pointer_bits_and_mask(env.target_info);
|
||||
|
@ -264,9 +257,8 @@ const OPAQUE_FUNCTION: Expr = Expr::Var {
|
|||
ident: "<function>",
|
||||
};
|
||||
|
||||
fn jit_to_ast_help<'a, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
app: AppExecutable,
|
||||
fn jit_to_ast_help<'a, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
main_fn_name: &str,
|
||||
layout: &Layout<'a>,
|
||||
content: &'a Content,
|
||||
|
@ -274,26 +266,25 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
|||
let (newtype_containers, content) = unroll_newtypes(env, content);
|
||||
let content = unroll_aliases(env, content);
|
||||
let result = match layout {
|
||||
Layout::Builtin(Builtin::Bool) => Ok(run_jit_function!(app, main_fn_name, bool, |num| {
|
||||
bool_to_ast(env, num, content)
|
||||
})),
|
||||
Layout::Builtin(Builtin::Bool) => Ok(env
|
||||
.app
|
||||
.call_function(main_fn_name, |num: bool| bool_to_ast(env, num, content))),
|
||||
Layout::Builtin(Builtin::Int(int_width)) => {
|
||||
use IntWidth::*;
|
||||
|
||||
macro_rules! helper {
|
||||
($ty:ty) => {
|
||||
run_jit_function!(app, main_fn_name, $ty, |num| num_to_ast(
|
||||
env,
|
||||
number_literal_to_ast(env.arena, num),
|
||||
content
|
||||
))
|
||||
env.app.call_function(main_fn_name, |num: $ty| {
|
||||
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
let result = match int_width {
|
||||
U8 | I8 => {
|
||||
// NOTE: this is does not handle 8-bit numbers yet
|
||||
run_jit_function!(app, main_fn_name, u8, |num| byte_to_ast(env, num, content))
|
||||
// NOTE: `helper!` does not handle 8-bit numbers yet
|
||||
env.app
|
||||
.call_function(main_fn_name, |num: u8| byte_to_ast(env, num, content))
|
||||
}
|
||||
U16 => helper!(u16),
|
||||
U32 => helper!(u32),
|
||||
|
@ -312,11 +303,9 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
|||
|
||||
macro_rules! helper {
|
||||
($ty:ty) => {
|
||||
run_jit_function!(app, main_fn_name, $ty, |num| num_to_ast(
|
||||
env,
|
||||
number_literal_to_ast(env.arena, num),
|
||||
content
|
||||
))
|
||||
env.app.call_function(main_fn_name, |num: $ty| {
|
||||
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -328,18 +317,16 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
|||
|
||||
Ok(result)
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) => Ok(run_jit_function!(
|
||||
app,
|
||||
main_fn_name,
|
||||
&'static str,
|
||||
|string: &'static str| { str_to_ast(env.arena, env.arena.alloc(string)) }
|
||||
)),
|
||||
Layout::Builtin(Builtin::List(elem_layout)) => Ok(run_jit_function!(
|
||||
app,
|
||||
main_fn_name,
|
||||
(usize, usize),
|
||||
|(addr, len): (usize, usize)| { list_to_ast(env, addr, len, elem_layout, content) }
|
||||
)),
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
Ok(env.app.call_function(main_fn_name, |string: &'static str| {
|
||||
str_to_ast(env.arena, env.arena.alloc(string))
|
||||
}))
|
||||
}
|
||||
Layout::Builtin(Builtin::List(elem_layout)) => Ok(env
|
||||
.app
|
||||
.call_function(main_fn_name, |(addr, len): (usize, usize)| {
|
||||
list_to_ast(env, addr, len, elem_layout, content)
|
||||
})),
|
||||
Layout::Builtin(other) => {
|
||||
todo!("add support for rendering builtin {:?} to the REPL", other)
|
||||
}
|
||||
|
@ -395,37 +382,30 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
|||
|
||||
let result_stack_size = layout.stack_size(env.target_info);
|
||||
|
||||
run_jit_function_dynamic_type!(
|
||||
app,
|
||||
env.app.call_function_dynamic_size(
|
||||
main_fn_name,
|
||||
result_stack_size as usize,
|
||||
|bytes_addr: usize| { struct_addr_to_ast(bytes_addr as usize) }
|
||||
struct_addr_to_ast,
|
||||
)
|
||||
}
|
||||
Layout::Union(UnionLayout::NonRecursive(_)) => {
|
||||
let size = layout.stack_size(env.target_info);
|
||||
Ok(run_jit_function_dynamic_type!(
|
||||
app,
|
||||
main_fn_name,
|
||||
size as usize,
|
||||
|addr: usize| {
|
||||
Ok(env
|
||||
.app
|
||||
.call_function_dynamic_size(main_fn_name, size as usize, |addr: usize| {
|
||||
addr_to_ast(env, addr, layout, WhenRecursive::Unreachable, content)
|
||||
}
|
||||
))
|
||||
}))
|
||||
}
|
||||
Layout::Union(UnionLayout::Recursive(_))
|
||||
| Layout::Union(UnionLayout::NonNullableUnwrapped(_))
|
||||
| Layout::Union(UnionLayout::NullableUnwrapped { .. })
|
||||
| Layout::Union(UnionLayout::NullableWrapped { .. }) => {
|
||||
let size = layout.stack_size(env.target_info);
|
||||
Ok(run_jit_function_dynamic_type!(
|
||||
app,
|
||||
main_fn_name,
|
||||
size as usize,
|
||||
|addr: usize| {
|
||||
Ok(env
|
||||
.app
|
||||
.call_function_dynamic_size(main_fn_name, size as usize, |addr: usize| {
|
||||
addr_to_ast(env, addr, layout, WhenRecursive::Loop(*layout), content)
|
||||
}
|
||||
))
|
||||
}))
|
||||
}
|
||||
Layout::RecursivePointer => {
|
||||
unreachable!("RecursivePointers can only be inside structures")
|
||||
|
@ -435,7 +415,7 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
|||
result.map(|e| apply_newtypes(env, newtype_containers, e))
|
||||
}
|
||||
|
||||
fn tag_name_to_expr<'a, M: AppMemory>(env: &Env<'a, '_, M>, tag_name: &TagName) -> Expr<'a> {
|
||||
fn tag_name_to_expr<'a, A: ReplApp>(env: &Env<'a, '_, A>, tag_name: &TagName) -> Expr<'a> {
|
||||
match tag_name {
|
||||
TagName::Global(_) => Expr::GlobalTag(
|
||||
env.arena
|
||||
|
@ -457,8 +437,8 @@ enum WhenRecursive<'a> {
|
|||
Loop(Layout<'a>),
|
||||
}
|
||||
|
||||
fn addr_to_ast<'a, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
fn addr_to_ast<'a, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
addr: usize,
|
||||
layout: &Layout<'a>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
|
@ -466,7 +446,7 @@ fn addr_to_ast<'a, M: AppMemory>(
|
|||
) -> Expr<'a> {
|
||||
macro_rules! helper {
|
||||
($method: ident, $ty: ty) => {{
|
||||
let num: $ty = env.app_memory.$method(addr);
|
||||
let num: $ty = env.app.$method(addr);
|
||||
|
||||
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
|
||||
}};
|
||||
|
@ -480,7 +460,7 @@ fn addr_to_ast<'a, M: AppMemory>(
|
|||
(_, Layout::Builtin(Builtin::Bool)) => {
|
||||
// TODO: bits are not as expected here.
|
||||
// num is always false at the moment.
|
||||
let num: bool = env.app_memory.deref_bool(addr);
|
||||
let num: bool = env.app.deref_bool(addr);
|
||||
|
||||
bool_to_ast(env, num, content)
|
||||
}
|
||||
|
@ -510,13 +490,13 @@ fn addr_to_ast<'a, M: AppMemory>(
|
|||
}
|
||||
}
|
||||
(_, Layout::Builtin(Builtin::List(elem_layout))) => {
|
||||
let elem_addr = env.app_memory.deref_usize(addr);
|
||||
let len = env.app_memory.deref_usize(addr + env.target_info.ptr_width() as usize);
|
||||
let elem_addr = env.app.deref_usize(addr);
|
||||
let len = env.app.deref_usize(addr + env.target_info.ptr_width() as usize);
|
||||
|
||||
list_to_ast(env, elem_addr, len, elem_layout, content)
|
||||
}
|
||||
(_, Layout::Builtin(Builtin::Str)) => {
|
||||
let arena_str = env.app_memory.deref_str(addr);
|
||||
let arena_str = env.app.deref_str(addr);
|
||||
|
||||
str_to_ast(env.arena, arena_str)
|
||||
}
|
||||
|
@ -636,7 +616,7 @@ fn addr_to_ast<'a, M: AppMemory>(
|
|||
_ => unreachable!("any other variant would have a different layout"),
|
||||
};
|
||||
|
||||
let data_addr = env.app_memory.deref_usize(addr);
|
||||
let data_addr = env.app.deref_usize(addr);
|
||||
|
||||
expr_of_tag(
|
||||
env,
|
||||
|
@ -666,7 +646,7 @@ fn addr_to_ast<'a, M: AppMemory>(
|
|||
_ => unreachable!("any other variant would have a different layout"),
|
||||
};
|
||||
|
||||
let data_addr = env.app_memory.deref_usize(addr);
|
||||
let data_addr = env.app.deref_usize(addr);
|
||||
if data_addr == 0 {
|
||||
tag_name_to_expr(env, &nullable_name)
|
||||
} else {
|
||||
|
@ -697,7 +677,7 @@ fn addr_to_ast<'a, M: AppMemory>(
|
|||
_ => unreachable!("any other variant would have a different layout"),
|
||||
};
|
||||
|
||||
let data_addr = env.app_memory.deref_usize(addr);
|
||||
let data_addr = env.app.deref_usize(addr);
|
||||
if data_addr == 0 {
|
||||
tag_name_to_expr(env, &nullable_name)
|
||||
} else {
|
||||
|
@ -726,8 +706,8 @@ fn addr_to_ast<'a, M: AppMemory>(
|
|||
apply_newtypes(env, newtype_containers, expr)
|
||||
}
|
||||
|
||||
fn list_to_ast<'a, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
fn list_to_ast<'a, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
addr: usize,
|
||||
len: usize,
|
||||
elem_layout: &Layout<'a>,
|
||||
|
@ -775,8 +755,8 @@ fn list_to_ast<'a, M: AppMemory>(
|
|||
Expr::List(Collection::with_items(output))
|
||||
}
|
||||
|
||||
fn single_tag_union_to_ast<'a, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
fn single_tag_union_to_ast<'a, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
addr: usize,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
tag_name: &TagName,
|
||||
|
@ -801,8 +781,8 @@ fn single_tag_union_to_ast<'a, M: AppMemory>(
|
|||
Expr::Apply(loc_tag_expr, output, CalledVia::Space)
|
||||
}
|
||||
|
||||
fn sequence_of_expr<'a, I, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
fn sequence_of_expr<'a, I, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
addr: usize,
|
||||
sequence: I,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
|
@ -832,8 +812,8 @@ where
|
|||
output
|
||||
}
|
||||
|
||||
fn struct_to_ast<'a, M: AppMemory>(
|
||||
env: &Env<'a, 'a, M>,
|
||||
fn struct_to_ast<'a, A: ReplApp>(
|
||||
env: &Env<'a, 'a, A>,
|
||||
addr: usize,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
record_fields: RecordFields,
|
||||
|
@ -949,7 +929,7 @@ fn unpack_two_element_tag_union(
|
|||
(tag_name1, payload_vars1, tag_name2, payload_vars2)
|
||||
}
|
||||
|
||||
fn bool_to_ast<'a, M: AppMemory>(env: &Env<'a, '_, M>, value: bool, content: &Content) -> Expr<'a> {
|
||||
fn bool_to_ast<'a, A: ReplApp>(env: &Env<'a, '_, A>, value: bool, content: &Content) -> Expr<'a> {
|
||||
use Content::*;
|
||||
|
||||
let arena = env.arena;
|
||||
|
@ -1027,7 +1007,7 @@ fn bool_to_ast<'a, M: AppMemory>(env: &Env<'a, '_, M>, value: bool, content: &Co
|
|||
}
|
||||
}
|
||||
|
||||
fn byte_to_ast<'a, M: AppMemory>(env: &Env<'a, '_, M>, value: u8, content: &Content) -> Expr<'a> {
|
||||
fn byte_to_ast<'a, A: ReplApp>(env: &Env<'a, '_, A>, value: u8, content: &Content) -> Expr<'a> {
|
||||
use Content::*;
|
||||
|
||||
let arena = env.arena;
|
||||
|
@ -1119,8 +1099,8 @@ fn byte_to_ast<'a, M: AppMemory>(env: &Env<'a, '_, M>, value: u8, content: &Cont
|
|||
}
|
||||
}
|
||||
|
||||
fn num_to_ast<'a, M: AppMemory>(
|
||||
env: &Env<'a, '_, M>,
|
||||
fn num_to_ast<'a, A: ReplApp>(
|
||||
env: &Env<'a, '_, A>,
|
||||
num_expr: Expr<'a>,
|
||||
content: &Content,
|
||||
) -> Expr<'a> {
|
||||
|
@ -1199,15 +1179,15 @@ fn number_literal_to_ast<T: std::fmt::Display>(arena: &Bump, num: T) -> Expr<'_>
|
|||
}
|
||||
|
||||
#[cfg(target_endian = "little")]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
/// TODO implement this for 32-bit and big-endian targets. NOTE: As of this writing,
|
||||
/// we don't have big-endian small strings implemented yet!
|
||||
/// NOTE: As of this writing, we don't have big-endian small strings implemented yet!
|
||||
fn str_to_ast<'a>(arena: &'a Bump, string: &'a str) -> Expr<'a> {
|
||||
let bytes: [u8; 16] = unsafe { std::mem::transmute::<&'a str, [u8; 16]>(string) };
|
||||
let is_small = (bytes[15] & 0b1000_0000) != 0;
|
||||
const STR_SIZE: usize = 2 * std::mem::size_of::<usize>();
|
||||
|
||||
let bytes: [u8; STR_SIZE] = unsafe { std::mem::transmute(string) };
|
||||
let is_small = (bytes[STR_SIZE - 1] & 0b1000_0000) != 0;
|
||||
|
||||
if is_small {
|
||||
let len = (bytes[15] & 0b0111_1111) as usize;
|
||||
let len = (bytes[STR_SIZE - 1] & 0b0111_1111) as usize;
|
||||
let mut string = bumpalo::collections::String::with_capacity_in(len, arena);
|
||||
|
||||
for byte in bytes.iter().take(len) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue