mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into build-nix
This commit is contained in:
commit
0b5b3a8652
94 changed files with 4274 additions and 2474 deletions
|
@ -16,7 +16,7 @@ roc_types = { path = "../types" }
|
|||
roc_builtins = { path = "../builtins" }
|
||||
roc_constrain = { path = "../constrain" }
|
||||
roc_unify = { path = "../unify" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_solve_problem = { path = "../solve_problem" }
|
||||
roc_mono = { path = "../mono" }
|
||||
roc_load = { path = "../load" }
|
||||
roc_target = { path = "../roc_target" }
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
pub use roc_gen_llvm::llvm::build::FunctionIterator;
|
||||
use roc_gen_llvm::llvm::build::{module_from_builtins, LlvmBackendMode};
|
||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||
use roc_load::{LoadedModule, MonomorphizedModule};
|
||||
use roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_mono::ir::OptLevel;
|
||||
use roc_region::all::LineInfo;
|
||||
use roc_solve_problem::TypeError;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
|
@ -59,7 +61,7 @@ fn report_problems_help(
|
|||
sources: &MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||
interns: &Interns,
|
||||
can_problems: &mut MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||
type_problems: &mut MutMap<ModuleId, Vec<roc_solve::solve::TypeError>>,
|
||||
type_problems: &mut MutMap<ModuleId, Vec<TypeError>>,
|
||||
) -> Problems {
|
||||
use roc_reporting::report::{
|
||||
can_problem, type_problem, Report, RocDocAllocator, Severity::*, DEFAULT_PALETTE,
|
||||
|
@ -259,6 +261,10 @@ pub fn gen_from_mono_module_llvm(
|
|||
exposed_to_host: loaded.exposed_to_host.values.keys().copied().collect(),
|
||||
};
|
||||
|
||||
// does not add any externs for this mode (we have a host) but cleans up some functions around
|
||||
// expects that would confuse the surgical linker
|
||||
add_default_roc_externs(&env);
|
||||
|
||||
roc_gen_llvm::llvm::build::build_procedures(
|
||||
&env,
|
||||
opt_level,
|
||||
|
|
|
@ -16,7 +16,6 @@ lazy_static = "1.4.0"
|
|||
[build-dependencies]
|
||||
# dunce can be removed once ziglang/zig#5109 is fixed
|
||||
dunce = "1.0.2"
|
||||
fs_extra = "1.2.0"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.build-dependencies]
|
||||
tempfile = "3.2.0"
|
||||
|
|
|
@ -13,23 +13,18 @@ const O_CREAT: c_int = 64;
|
|||
pub const PROT_WRITE: c_int = 2;
|
||||
pub const MAP_SHARED: c_int = 0x0001;
|
||||
|
||||
// IMPORTANT: shared memory object names must begin with / and contain no other slashes!
|
||||
var SHARED_BUFFER: []u8 = undefined;
|
||||
|
||||
pub fn setSharedBuffer(ptr: [*]u8, length: usize) callconv(.C) usize {
|
||||
SHARED_BUFFER = ptr[0..length];
|
||||
|
||||
// the rust side expects that a pointer is returned
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn expectFailedStart() callconv(.C) [*]u8 {
|
||||
const name = "/roc_expect_buffer"; // IMPORTANT: shared memory object names must begin with / and contain no other slashes!
|
||||
|
||||
const shared_fd = shm_open(@ptrCast(*const i8, name), O_RDWR | O_CREAT, 0o666);
|
||||
|
||||
const shared_ptr = mmap(
|
||||
null,
|
||||
4096,
|
||||
PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
shared_fd,
|
||||
0,
|
||||
);
|
||||
|
||||
const ptr = @ptrCast([*]u8, shared_ptr);
|
||||
|
||||
return ptr;
|
||||
return SHARED_BUFFER.ptr;
|
||||
}
|
||||
|
||||
pub fn expectFailedFinalize() callconv(.C) void {
|
||||
|
|
|
@ -169,6 +169,9 @@ comptime {
|
|||
if (builtin.target.cpu.arch != .wasm32) {
|
||||
exportUtilsFn(expect.expectFailedStart, "expect_failed_start");
|
||||
exportUtilsFn(expect.expectFailedFinalize, "expect_failed_finalize");
|
||||
|
||||
// sets the buffer used for expect failures
|
||||
@export(expect.setSharedBuffer, .{ .name = "set_shared_buffer", .linkage = .Weak });
|
||||
}
|
||||
|
||||
if (builtin.target.cpu.arch == .aarch64) {
|
||||
|
|
|
@ -766,7 +766,6 @@ fn strFromFloatHelp(comptime T: type, float: T) RocStr {
|
|||
}
|
||||
|
||||
// Str.split
|
||||
|
||||
pub fn strSplit(string: RocStr, delimiter: RocStr) callconv(.C) RocList {
|
||||
const segment_count = countSegments(string, delimiter);
|
||||
const list = RocList.allocate(@alignOf(RocStr), segment_count, @sizeOf(RocStr));
|
||||
|
@ -790,7 +789,7 @@ fn strSplitHelp(array: [*]RocStr, string: RocStr, delimiter: RocStr) void {
|
|||
const delimiter_bytes_ptrs = delimiter.asU8ptr();
|
||||
const delimiter_len = delimiter.len();
|
||||
|
||||
if (str_len > delimiter_len and delimiter_len > 0) {
|
||||
if (str_len >= delimiter_len and delimiter_len > 0) {
|
||||
const end_index: usize = str_len - delimiter_len + 1;
|
||||
while (str_index <= end_index) {
|
||||
var delimiter_index: usize = 0;
|
||||
|
@ -892,6 +891,46 @@ test "strSplitHelp: no delimiter" {
|
|||
try expect(array[0].eq(expected[0]));
|
||||
}
|
||||
|
||||
test "strSplitHelp: empty start" {
|
||||
const str_arr = "/a";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
const delimiter_arr = "/";
|
||||
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
|
||||
const array_len: usize = 2;
|
||||
var array: [array_len]RocStr = [_]RocStr{
|
||||
undefined,
|
||||
undefined,
|
||||
};
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitHelp(array_ptr, str, delimiter);
|
||||
|
||||
const one = RocStr.init("a", 1);
|
||||
|
||||
var expected = [2]RocStr{
|
||||
RocStr.empty(), one,
|
||||
};
|
||||
|
||||
defer {
|
||||
for (array) |rocStr| {
|
||||
rocStr.deinit();
|
||||
}
|
||||
|
||||
for (expected) |rocStr| {
|
||||
rocStr.deinit();
|
||||
}
|
||||
|
||||
str.deinit();
|
||||
delimiter.deinit();
|
||||
}
|
||||
|
||||
try expectEqual(array.len, expected.len);
|
||||
try expect(array[0].eq(expected[0]));
|
||||
try expect(array[1].eq(expected[1]));
|
||||
}
|
||||
|
||||
test "strSplitHelp: empty end" {
|
||||
const str_arr = "1---- ---- ---- ---- ----2---- ---- ---- ---- ----";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
@ -935,6 +974,38 @@ test "strSplitHelp: empty end" {
|
|||
try expect(array[2].eq(expected[2]));
|
||||
}
|
||||
|
||||
test "strSplitHelp: string equals delimiter" {
|
||||
const str_delimiter_arr = "/";
|
||||
const str_delimiter = RocStr.init(str_delimiter_arr, str_delimiter_arr.len);
|
||||
|
||||
const array_len: usize = 2;
|
||||
var array: [array_len]RocStr = [_]RocStr{
|
||||
undefined,
|
||||
undefined,
|
||||
};
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitHelp(array_ptr, str_delimiter, str_delimiter);
|
||||
|
||||
var expected = [2]RocStr{ RocStr.empty(), RocStr.empty() };
|
||||
|
||||
defer {
|
||||
for (array) |rocStr| {
|
||||
rocStr.deinit();
|
||||
}
|
||||
|
||||
for (expected) |rocStr| {
|
||||
rocStr.deinit();
|
||||
}
|
||||
|
||||
str_delimiter.deinit();
|
||||
}
|
||||
|
||||
try expectEqual(array.len, expected.len);
|
||||
try expect(array[0].eq(expected[0]));
|
||||
try expect(array[1].eq(expected[1]));
|
||||
}
|
||||
|
||||
test "strSplitHelp: delimiter on sides" {
|
||||
const str_arr = "tttghittt";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
@ -1031,7 +1102,7 @@ pub fn countSegments(string: RocStr, delimiter: RocStr) callconv(.C) usize {
|
|||
|
||||
var count: usize = 1;
|
||||
|
||||
if (str_len > delimiter_len and delimiter_len > 0) {
|
||||
if (str_len >= delimiter_len and delimiter_len > 0) {
|
||||
var str_index: usize = 0;
|
||||
const end_cond: usize = str_len - delimiter_len + 1;
|
||||
|
||||
|
@ -1119,6 +1190,21 @@ test "countSegments: delimiter interspered" {
|
|||
try expectEqual(segments_count, 3);
|
||||
}
|
||||
|
||||
test "countSegments: string equals delimiter" {
|
||||
// Str.split "/" "/" == ["", ""]
|
||||
// 2 segments
|
||||
const str_delimiter_arr = "/";
|
||||
const str_delimiter = RocStr.init(str_delimiter_arr, str_delimiter_arr.len);
|
||||
|
||||
defer {
|
||||
str_delimiter.deinit();
|
||||
}
|
||||
|
||||
const segments_count = countSegments(str_delimiter, str_delimiter);
|
||||
|
||||
try expectEqual(segments_count, 2);
|
||||
}
|
||||
|
||||
// Str.countGraphemeClusters
|
||||
const grapheme = @import("helpers/grapheme.zig");
|
||||
pub fn countGraphemeClusters(string: RocStr) callconv(.C) usize {
|
||||
|
@ -2522,33 +2608,34 @@ test "getScalarUnsafe" {
|
|||
}
|
||||
|
||||
pub fn strCloneTo(
|
||||
string: RocStr,
|
||||
ptr: [*]u8,
|
||||
offset: usize,
|
||||
string: RocStr,
|
||||
extra_offset: usize,
|
||||
) callconv(.C) usize {
|
||||
const WIDTH: usize = @sizeOf(RocStr);
|
||||
if (string.isSmallStr()) {
|
||||
const array: [@sizeOf(RocStr)]u8 = @bitCast([@sizeOf(RocStr)]u8, string);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < array.len) : (i += 1) {
|
||||
while (i < WIDTH) : (i += 1) {
|
||||
ptr[offset + i] = array[i];
|
||||
}
|
||||
|
||||
return offset + WIDTH;
|
||||
return extra_offset;
|
||||
} else {
|
||||
const slice = string.asSlice();
|
||||
|
||||
var relative = string;
|
||||
relative.str_bytes = @intToPtr(?[*]u8, offset + WIDTH); // i.e. just after the string struct
|
||||
relative.str_bytes = @intToPtr(?[*]u8, extra_offset); // i.e. just after the string struct
|
||||
|
||||
// write the string struct
|
||||
const array = relative.asArray();
|
||||
@memcpy(ptr + offset, &array, WIDTH);
|
||||
|
||||
// write the string bytes just after the struct
|
||||
@memcpy(ptr + offset + WIDTH, slice.ptr, slice.len);
|
||||
@memcpy(ptr + extra_offset, slice.ptr, slice.len);
|
||||
|
||||
return offset + WIDTH + slice.len;
|
||||
return extra_offset + slice.len;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,16 +151,7 @@ fn copy_zig_builtins_to_target_dir(bitcode_path: &Path) {
|
|||
|
||||
let zig_src_dir = bitcode_path.join("src");
|
||||
|
||||
std::fs::create_dir_all(&target_profile_dir).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Failed to create output library directory for zig bitcode {:?}: {:?}",
|
||||
target_profile_dir, err
|
||||
);
|
||||
});
|
||||
let mut options = fs_extra::dir::CopyOptions::new();
|
||||
options.content_only = true;
|
||||
options.overwrite = true;
|
||||
fs_extra::dir::copy(&zig_src_dir, &target_profile_dir, &options).unwrap_or_else(|err| {
|
||||
cp_unless_zig_cache(&zig_src_dir, &target_profile_dir).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Failed to copy zig bitcode files {:?} to {:?}: {:?}",
|
||||
zig_src_dir, target_profile_dir, err
|
||||
|
@ -168,6 +159,39 @@ fn copy_zig_builtins_to_target_dir(bitcode_path: &Path) {
|
|||
});
|
||||
}
|
||||
|
||||
// recursively copy all the .zig files from this directory, but do *not* recurse into zig-cache/
|
||||
fn cp_unless_zig_cache(src_dir: &Path, target_dir: &Path) -> io::Result<()> {
|
||||
// Make sure the destination directory exists before we try to copy anything into it.
|
||||
std::fs::create_dir_all(&target_dir).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Failed to create output library directory for zig bitcode {:?}: {:?}",
|
||||
target_dir, err
|
||||
);
|
||||
});
|
||||
|
||||
for entry in fs::read_dir(src_dir)? {
|
||||
let src_path = entry?.path();
|
||||
let src_filename = src_path.file_name().unwrap();
|
||||
|
||||
// Only copy individual files if they have the .zig extension
|
||||
if src_path.extension().unwrap_or_default() == "zig" {
|
||||
let dest = target_dir.join(src_filename);
|
||||
|
||||
fs::copy(&src_path, &dest).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Failed to copy zig bitcode file {:?} to {:?}: {:?}",
|
||||
src_path, dest, err
|
||||
);
|
||||
});
|
||||
} else if src_path.is_dir() && src_filename != "zig-cache" {
|
||||
// Recursively copy all directories except zig-cache
|
||||
cp_unless_zig_cache(&src_path, &target_dir.join(src_filename))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_command<S, I: Copy, P: AsRef<Path> + Copy>(path: P, command_str: &str, args: I)
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
|
|
|
@ -70,9 +70,6 @@ impl AbilityMemberData<Resolved> {
|
|||
}
|
||||
}
|
||||
|
||||
/// (member, specialization type) -> specialization
|
||||
pub type ImplMap = VecMap<(Symbol, Symbol), MemberImpl>;
|
||||
|
||||
/// Solved lambda sets for an ability member specialization. For example, if we have
|
||||
///
|
||||
/// Default has default : {} -[[] + a:default:1]-> a | a has Default
|
||||
|
@ -152,7 +149,7 @@ pub struct IAbilitiesStore<Phase: ResolvePhase> {
|
|||
|
||||
/// Maps a tuple (member, type) specifying that `type` has an implementation of an ability
|
||||
/// member `member`, to how that implementation is defined.
|
||||
declared_implementations: ImplMap,
|
||||
declared_implementations: MutMap<ImplKey, MemberImpl>,
|
||||
|
||||
/// Information about specialized ability member implementations for a type.
|
||||
specializations: MutMap<Symbol, MemberSpecializationInfo<Phase>>,
|
||||
|
@ -233,8 +230,7 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
|||
self.specialization_to_root
|
||||
.insert(specialization_symbol, impl_key);
|
||||
}
|
||||
self.declared_implementations
|
||||
.insert((impl_key.ability_member, impl_key.opaque), member_impl);
|
||||
self.declared_implementations.insert(impl_key, member_impl);
|
||||
}
|
||||
|
||||
/// Records the implementations of an ability an opaque type declares to have.
|
||||
|
@ -286,6 +282,25 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
|||
self.specialization_to_root.get(&specializing_symbol)
|
||||
}
|
||||
|
||||
/// Answers the question, "does an opaque type claim to implement a particular ability?"
|
||||
///
|
||||
/// Whether the given opaque typ faithfully implements or derives all members of the given ability
|
||||
/// without errors is not validated.
|
||||
///
|
||||
/// When the given ability is not known to the current store, this call will return `false`.
|
||||
pub fn has_declared_implementation(&self, opaque: Symbol, ability: Symbol) -> bool {
|
||||
// Idea: choose an ability member and check whether there is a declared implementation for it.
|
||||
// During canonicalization, we would have added either all members as declared
|
||||
// implementations, or none if the opaque doesn't implement the ability.
|
||||
match self.members_of_ability(ability) {
|
||||
Some(members) => self.declared_implementations.contains_key(&ImplKey {
|
||||
opaque,
|
||||
ability_member: members[0],
|
||||
}),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a store from [`self`] that closes over the abilities/members given by the
|
||||
/// imported `symbols`, and their specializations (if any).
|
||||
pub fn closure_from_imported(&self, symbols: &VecSet<Symbol>) -> PendingAbilitiesStore {
|
||||
|
@ -344,12 +359,8 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
|||
// Add any specializations of the ability's members we know about.
|
||||
declared_implementations
|
||||
.iter()
|
||||
.filter(|((member, _), _)| members.contains(member))
|
||||
.for_each(|(&(member, typ), member_impl)| {
|
||||
let impl_key = ImplKey {
|
||||
ability_member: member,
|
||||
opaque: typ,
|
||||
};
|
||||
.filter(|(impl_key, _)| members.contains(&impl_key.ability_member))
|
||||
.for_each(|(&impl_key, member_impl)| {
|
||||
new.register_one_declared_impl(impl_key, *member_impl);
|
||||
|
||||
if let MemberImpl::Impl(spec_symbol) = member_impl {
|
||||
|
@ -398,26 +409,22 @@ impl IAbilitiesStore<Resolved> {
|
|||
/// the give type has an implementation of an ability member.
|
||||
pub fn iter_declared_implementations(
|
||||
&self,
|
||||
) -> impl Iterator<Item = ((Symbol, Symbol), &MemberImpl)> + '_ {
|
||||
) -> impl Iterator<Item = (ImplKey, &MemberImpl)> + '_ {
|
||||
self.declared_implementations.iter().map(|(k, v)| (*k, v))
|
||||
}
|
||||
|
||||
/// Retrieves the declared implementation of `member` for `typ`, if it exists.
|
||||
pub fn get_implementation(&self, member: Symbol, typ: Symbol) -> Option<&MemberImpl> {
|
||||
self.declared_implementations.get(&(member, typ))
|
||||
pub fn get_implementation(&self, impl_key: ImplKey) -> Option<&MemberImpl> {
|
||||
self.declared_implementations.get(&impl_key)
|
||||
}
|
||||
|
||||
/// Marks a declared implementation as either properly specializing, or as erroring.
|
||||
pub fn mark_implementation(
|
||||
&mut self,
|
||||
ability_member: Symbol,
|
||||
typ: Symbol,
|
||||
impl_key: ImplKey,
|
||||
mark: Result<MemberSpecializationInfo<Resolved>, ()>,
|
||||
) -> Result<(), MarkError> {
|
||||
match self
|
||||
.declared_implementations
|
||||
.get_mut(&(ability_member, typ))
|
||||
{
|
||||
match self.declared_implementations.get_mut(&impl_key) {
|
||||
Some(member_impl) => match *member_impl {
|
||||
MemberImpl::Impl(specialization_symbol) => {
|
||||
debug_assert!(!self.specializations.contains_key(&specialization_symbol));
|
||||
|
@ -466,11 +473,6 @@ impl IAbilitiesStore<Resolved> {
|
|||
|
||||
impl IAbilitiesStore<Pending> {
|
||||
pub fn import_implementation(&mut self, impl_key: ImplKey, resolved_impl: &ResolvedImpl) {
|
||||
let ImplKey {
|
||||
opaque,
|
||||
ability_member,
|
||||
} = impl_key;
|
||||
|
||||
let member_impl = match resolved_impl {
|
||||
ResolvedImpl::Impl(specialization) => {
|
||||
self.import_specialization(specialization);
|
||||
|
@ -480,9 +482,7 @@ impl IAbilitiesStore<Pending> {
|
|||
ResolvedImpl::Error => MemberImpl::Error,
|
||||
};
|
||||
|
||||
let old_declared_impl = self
|
||||
.declared_implementations
|
||||
.insert((ability_member, opaque), member_impl);
|
||||
let old_declared_impl = self.declared_implementations.insert(impl_key, member_impl);
|
||||
debug_assert!(
|
||||
old_declared_impl.is_none(),
|
||||
"Replacing existing declared impl!"
|
||||
|
@ -539,8 +539,8 @@ impl IAbilitiesStore<Pending> {
|
|||
debug_assert!(old_root.is_none() || old_root.unwrap() == member);
|
||||
}
|
||||
|
||||
for ((member, typ), impl_) in declared_implementations.into_iter() {
|
||||
let old_impl = self.declared_implementations.insert((member, typ), impl_);
|
||||
for (impl_key, impl_) in declared_implementations.into_iter() {
|
||||
let old_impl = self.declared_implementations.insert(impl_key, impl_);
|
||||
debug_assert!(old_impl.is_none() || old_impl.unwrap() == impl_);
|
||||
}
|
||||
|
||||
|
|
|
@ -593,7 +593,7 @@ pub fn find_ability_member_and_owning_type_at(
|
|||
abilities_store
|
||||
.iter_declared_implementations()
|
||||
.find(|(_, member_impl)| matches!(member_impl, MemberImpl::Impl(sym) if *sym == symbol))
|
||||
.map(|(spec, _)| spec.1)
|
||||
.map(|(impl_key, _)| impl_key.opaque)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -83,9 +83,9 @@ impl Env<'_> {
|
|||
}
|
||||
|
||||
fn unify(&mut self, left: Variable, right: Variable) {
|
||||
use roc_unify::unify::{unify, Mode, Unified};
|
||||
use roc_unify::unify::{unify, Env, Mode, Unified};
|
||||
|
||||
let unified = unify(self.subs, left, right, Mode::EQ);
|
||||
let unified = unify(&mut Env::new(self.subs), left, right, Mode::EQ);
|
||||
|
||||
match unified {
|
||||
Unified::Success {
|
||||
|
@ -109,12 +109,12 @@ impl Env<'_> {
|
|||
specialization_type: Variable,
|
||||
ability_member: Symbol,
|
||||
) -> SpecializationLambdaSets {
|
||||
use roc_unify::unify::{unify_introduced_ability_specialization, Mode, Unified};
|
||||
use roc_unify::unify::{unify_introduced_ability_specialization, Env, Mode, Unified};
|
||||
|
||||
let member_signature = self.import_encode_symbol(ability_member);
|
||||
|
||||
let unified = unify_introduced_ability_specialization(
|
||||
self.subs,
|
||||
&mut Env::new(self.subs),
|
||||
member_signature,
|
||||
specialization_type,
|
||||
Mode::EQ,
|
||||
|
|
|
@ -1264,12 +1264,12 @@ impl X86_64Assembler {
|
|||
}
|
||||
}
|
||||
const REX: u8 = 0x40;
|
||||
const REX_W: u8 = REX + 0x8;
|
||||
const REX_W: u8 = REX | 0x8;
|
||||
|
||||
#[inline(always)]
|
||||
fn add_rm_extension<T: RegTrait>(reg: T, byte: u8) -> u8 {
|
||||
if reg.value() > 7 {
|
||||
byte + 1
|
||||
byte | 1
|
||||
} else {
|
||||
byte
|
||||
}
|
||||
|
@ -1283,7 +1283,7 @@ fn add_opcode_extension(reg: X86_64GeneralReg, byte: u8) -> u8 {
|
|||
#[inline(always)]
|
||||
fn add_reg_extension<T: RegTrait>(reg: T, byte: u8) -> u8 {
|
||||
if reg.value() > 7 {
|
||||
byte + 4
|
||||
byte | 4
|
||||
} else {
|
||||
byte
|
||||
}
|
||||
|
@ -1300,7 +1300,7 @@ fn binop_reg64_reg64(
|
|||
let rex = add_reg_extension(src, rex);
|
||||
let dst_mod = dst as u8 % 8;
|
||||
let src_mod = (src as u8 % 8) << 3;
|
||||
buf.extend(&[rex, op_code, 0xC0 + dst_mod + src_mod]);
|
||||
buf.extend(&[rex, op_code, 0xC0 | dst_mod | src_mod]);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -1315,7 +1315,7 @@ fn extended_binop_reg64_reg64(
|
|||
let rex = add_reg_extension(src, rex);
|
||||
let dst_mod = dst as u8 % 8;
|
||||
let src_mod = (src as u8 % 8) << 3;
|
||||
buf.extend(&[rex, op_code1, op_code2, 0xC0 + dst_mod + src_mod]);
|
||||
buf.extend(&[rex, op_code1, op_code2, 0xC0 | dst_mod | src_mod]);
|
||||
}
|
||||
|
||||
// Below here are the functions for all of the assembly instructions.
|
||||
|
@ -1330,7 +1330,7 @@ fn add_reg64_imm32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i32) {
|
|||
let rex = add_rm_extension(dst, REX_W);
|
||||
let dst_mod = dst as u8 % 8;
|
||||
buf.reserve(7);
|
||||
buf.extend(&[rex, 0x81, 0xC0 + dst_mod]);
|
||||
buf.extend(&[rex, 0x81, 0xC0 | dst_mod]);
|
||||
buf.extend(&imm.to_le_bytes());
|
||||
}
|
||||
|
||||
|
@ -1350,13 +1350,13 @@ fn addsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl
|
|||
if dst_high || src_high {
|
||||
buf.extend(&[
|
||||
0xF2,
|
||||
0x40 + ((dst_high as u8) << 2) + (src_high as u8),
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x58,
|
||||
0xC0 + (dst_mod << 3) + (src_mod),
|
||||
0xC0 | (dst_mod << 3) | (src_mod),
|
||||
])
|
||||
} else {
|
||||
buf.extend(&[0xF2, 0x0F, 0x58, 0xC0 + (dst_mod << 3) + (src_mod)])
|
||||
buf.extend(&[0xF2, 0x0F, 0x58, 0xC0 | (dst_mod << 3) | (src_mod)])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1370,13 +1370,13 @@ fn andpd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl
|
|||
if dst_high || src_high {
|
||||
buf.extend(&[
|
||||
0x66,
|
||||
0x40 + ((dst_high as u8) << 2) + (src_high as u8),
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x54,
|
||||
0xC0 + (dst_mod << 3) + (src_mod),
|
||||
0xC0 | (dst_mod << 3) | (src_mod),
|
||||
])
|
||||
} else {
|
||||
buf.extend(&[0x66, 0x0F, 0x54, 0xC0 + (dst_mod << 3) + (src_mod)])
|
||||
buf.extend(&[0x66, 0x0F, 0x54, 0xC0 | (dst_mod << 3) | (src_mod)])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1385,7 +1385,7 @@ fn andpd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl
|
|||
fn and_reg64_imm8(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i8) {
|
||||
let rex = add_rm_extension(dst, REX_W);
|
||||
let dst_mod = dst as u8 % 8;
|
||||
buf.extend(&[rex, 0x83, 0xE0 + dst_mod, imm as u8]);
|
||||
buf.extend(&[rex, 0x83, 0xE0 | dst_mod, imm as u8]);
|
||||
}
|
||||
|
||||
/// `CMOVL r64,r/m64` -> Move if less (SF≠ OF).
|
||||
|
@ -1395,7 +1395,7 @@ fn cmovl_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64Ge
|
|||
let rex = add_rm_extension(src, rex);
|
||||
let dst_mod = (dst as u8 % 8) << 3;
|
||||
let src_mod = src as u8 % 8;
|
||||
buf.extend(&[rex, 0x0F, 0x4C, 0xC0 + dst_mod + src_mod]);
|
||||
buf.extend(&[rex, 0x0F, 0x4C, 0xC0 | dst_mod | src_mod]);
|
||||
}
|
||||
|
||||
/// `CMP r/m64,i32` -> Compare i32 to r/m64.
|
||||
|
@ -1404,7 +1404,7 @@ fn cmp_reg64_imm32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i32) {
|
|||
let rex = add_rm_extension(dst, REX_W);
|
||||
let dst_mod = dst as u8 % 8;
|
||||
buf.reserve(7);
|
||||
buf.extend(&[rex, 0x81, 0xF8 + dst_mod]);
|
||||
buf.extend(&[rex, 0x81, 0xF8 | dst_mod]);
|
||||
buf.extend(&imm.to_le_bytes());
|
||||
}
|
||||
|
||||
|
@ -1452,7 +1452,7 @@ fn mov_reg64_imm32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i32) {
|
|||
let rex = add_rm_extension(dst, REX_W);
|
||||
let dst_mod = dst as u8 % 8;
|
||||
buf.reserve(7);
|
||||
buf.extend(&[rex, 0xC7, 0xC0 + dst_mod]);
|
||||
buf.extend(&[rex, 0xC7, 0xC0 | dst_mod]);
|
||||
buf.extend(&imm.to_le_bytes());
|
||||
}
|
||||
|
||||
|
@ -1465,7 +1465,7 @@ fn mov_reg64_imm64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i64) {
|
|||
let rex = add_opcode_extension(dst, REX_W);
|
||||
let dst_mod = dst as u8 % 8;
|
||||
buf.reserve(10);
|
||||
buf.extend(&[rex, 0xB8 + dst_mod]);
|
||||
buf.extend(&[rex, 0xB8 | dst_mod]);
|
||||
buf.extend(&imm.to_le_bytes());
|
||||
}
|
||||
}
|
||||
|
@ -1501,7 +1501,7 @@ fn mov_base64_offset32_reg64(
|
|||
let src_mod = (src as u8 % 8) << 3;
|
||||
let base_mod = base as u8 % 8;
|
||||
buf.reserve(8);
|
||||
buf.extend(&[rex, 0x89, 0x80 + src_mod + base_mod]);
|
||||
buf.extend(&[rex, 0x89, 0x80 | src_mod | base_mod]);
|
||||
// Using RSP or R12 requires a secondary index byte.
|
||||
if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 {
|
||||
buf.push(0x24);
|
||||
|
@ -1522,7 +1522,7 @@ fn mov_reg64_base64_offset32(
|
|||
let dst_mod = (dst as u8 % 8) << 3;
|
||||
let base_mod = base as u8 % 8;
|
||||
buf.reserve(8);
|
||||
buf.extend(&[rex, 0x8B, 0x80 + dst_mod + base_mod]);
|
||||
buf.extend(&[rex, 0x8B, 0x80 | dst_mod | base_mod]);
|
||||
// Using RSP or R12 requires a secondary index byte.
|
||||
if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 {
|
||||
buf.push(0x24);
|
||||
|
@ -1543,7 +1543,7 @@ fn movzx_reg64_base8_offset32(
|
|||
let dst_mod = (dst as u8 % 8) << 3;
|
||||
let base_mod = base as u8 % 8;
|
||||
buf.reserve(9);
|
||||
buf.extend(&[rex, 0x0F, 0xB6, 0x80 + dst_mod + base_mod]);
|
||||
buf.extend(&[rex, 0x0F, 0xB6, 0x80 | dst_mod | base_mod]);
|
||||
// Using RSP or R12 requires a secondary index byte.
|
||||
if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 {
|
||||
buf.push(0x24);
|
||||
|
@ -1571,13 +1571,13 @@ fn raw_movsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_
|
|||
if dst_high || src_high {
|
||||
buf.extend(&[
|
||||
0xF2,
|
||||
0x40 + ((dst_high as u8) << 2) + (src_high as u8),
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x10,
|
||||
0xC0 + (dst_mod << 3) + (src_mod),
|
||||
0xC0 | (dst_mod << 3) | (src_mod),
|
||||
])
|
||||
} else {
|
||||
buf.extend(&[0xF2, 0x0F, 0x10, 0xC0 + (dst_mod << 3) + (src_mod)])
|
||||
buf.extend(&[0xF2, 0x0F, 0x10, 0xC0 | (dst_mod << 3) | (src_mod)])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1587,10 +1587,10 @@ fn movss_freg32_rip_offset32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, offset:
|
|||
let dst_mod = dst as u8 % 8;
|
||||
if dst as u8 > 7 {
|
||||
buf.reserve(9);
|
||||
buf.extend(&[0xF3, 0x44, 0x0F, 0x10, 0x05 + (dst_mod << 3)]);
|
||||
buf.extend(&[0xF3, 0x44, 0x0F, 0x10, 0x05 | (dst_mod << 3)]);
|
||||
} else {
|
||||
buf.reserve(8);
|
||||
buf.extend(&[0xF3, 0x0F, 0x10, 0x05 + (dst_mod << 3)]);
|
||||
buf.extend(&[0xF3, 0x0F, 0x10, 0x05 | (dst_mod << 3)]);
|
||||
}
|
||||
buf.extend(&offset.to_le_bytes());
|
||||
}
|
||||
|
@ -1601,10 +1601,10 @@ fn movsd_freg64_rip_offset32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, offset:
|
|||
let dst_mod = dst as u8 % 8;
|
||||
if dst as u8 > 7 {
|
||||
buf.reserve(9);
|
||||
buf.extend(&[0xF2, 0x44, 0x0F, 0x10, 0x05 + (dst_mod << 3)]);
|
||||
buf.extend(&[0xF2, 0x44, 0x0F, 0x10, 0x05 | (dst_mod << 3)]);
|
||||
} else {
|
||||
buf.reserve(8);
|
||||
buf.extend(&[0xF2, 0x0F, 0x10, 0x05 + (dst_mod << 3)]);
|
||||
buf.extend(&[0xF2, 0x0F, 0x10, 0x05 | (dst_mod << 3)]);
|
||||
}
|
||||
buf.extend(&offset.to_le_bytes());
|
||||
}
|
||||
|
@ -1626,7 +1626,7 @@ fn movsd_base64_offset32_freg64(
|
|||
if src as u8 > 7 || base as u8 > 7 {
|
||||
buf.push(rex);
|
||||
}
|
||||
buf.extend(&[0x0F, 0x11, 0x80 + src_mod + base_mod]);
|
||||
buf.extend(&[0x0F, 0x11, 0x80 | src_mod | base_mod]);
|
||||
// Using RSP or R12 requires a secondary index byte.
|
||||
if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 {
|
||||
buf.push(0x24);
|
||||
|
@ -1651,7 +1651,7 @@ fn movsd_freg64_base64_offset32(
|
|||
if dst as u8 > 7 || base as u8 > 7 {
|
||||
buf.push(rex);
|
||||
}
|
||||
buf.extend(&[0x0F, 0x10, 0x80 + dst_mod + base_mod]);
|
||||
buf.extend(&[0x0F, 0x10, 0x80 | dst_mod | base_mod]);
|
||||
// Using RSP or R12 requires a secondary index byte.
|
||||
if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 {
|
||||
buf.push(0x24);
|
||||
|
@ -1664,7 +1664,7 @@ fn movsd_freg64_base64_offset32(
|
|||
fn neg_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
|
||||
let rex = add_rm_extension(reg, REX_W);
|
||||
let reg_mod = reg as u8 % 8;
|
||||
buf.extend(&[rex, 0xF7, 0xD8 + reg_mod]);
|
||||
buf.extend(&[rex, 0xF7, 0xD8 | reg_mod]);
|
||||
}
|
||||
|
||||
// helper function for `set*` instructions
|
||||
|
@ -1677,10 +1677,10 @@ fn set_reg64_help(op_code: u8, buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
|
|||
let reg_mod = reg as u8 % 8;
|
||||
use X86_64GeneralReg::*;
|
||||
match reg {
|
||||
RAX | RCX | RDX | RBX => buf.extend(&[0x0F, op_code, 0xC0 + reg_mod]),
|
||||
RSP | RBP | RSI | RDI => buf.extend(&[REX, 0x0F, op_code, 0xC0 + reg_mod]),
|
||||
RAX | RCX | RDX | RBX => buf.extend(&[0x0F, op_code, 0xC0 | reg_mod]),
|
||||
RSP | RBP | RSI | RDI => buf.extend(&[REX, 0x0F, op_code, 0xC0 | reg_mod]),
|
||||
R8 | R9 | R10 | R11 | R12 | R13 | R14 | R15 => {
|
||||
buf.extend(&[REX + 1, 0x0F, op_code, 0xC0 + reg_mod])
|
||||
buf.extend(&[REX | 1, 0x0F, op_code, 0xC0 | reg_mod])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1702,7 +1702,7 @@ fn cvtsi2_help<T: RegTrait, U: RegTrait>(
|
|||
let mod1 = (dst.value() % 8) << 3;
|
||||
let mod2 = src.value() % 8;
|
||||
|
||||
buf.extend(&[op_code1, rex, 0x0F, op_code2, 0xC0 + mod1 + mod2])
|
||||
buf.extend(&[op_code1, rex, 0x0F, op_code2, 0xC0 | mod1 | mod2])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -1716,7 +1716,7 @@ fn cvtsx2_help<T: RegTrait, V: RegTrait>(
|
|||
let mod1 = (dst.value() % 8) << 3;
|
||||
let mod2 = src.value() % 8;
|
||||
|
||||
buf.extend(&[op_code1, 0x0F, op_code2, 0xC0 + mod1 + mod2])
|
||||
buf.extend(&[op_code1, 0x0F, op_code2, 0xC0 | mod1 | mod2])
|
||||
}
|
||||
|
||||
/// `SETE r/m64` -> Set Byte on Condition - zero/equal (ZF=1)
|
||||
|
@ -1794,7 +1794,7 @@ fn sub_reg64_imm32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i32) {
|
|||
let rex = add_rm_extension(dst, REX_W);
|
||||
let dst_mod = dst as u8 % 8;
|
||||
buf.reserve(7);
|
||||
buf.extend(&[rex, 0x81, 0xE8 + dst_mod]);
|
||||
buf.extend(&[rex, 0x81, 0xE8 | dst_mod]);
|
||||
buf.extend(&imm.to_le_bytes());
|
||||
}
|
||||
|
||||
|
@ -1810,9 +1810,9 @@ fn pop_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
|
|||
let reg_mod = reg as u8 % 8;
|
||||
if reg as u8 > 7 {
|
||||
let rex = add_opcode_extension(reg, REX);
|
||||
buf.extend(&[rex, 0x58 + reg_mod]);
|
||||
buf.extend(&[rex, 0x58 | reg_mod]);
|
||||
} else {
|
||||
buf.push(0x58 + reg_mod);
|
||||
buf.push(0x58 | reg_mod);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1822,9 +1822,9 @@ fn push_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
|
|||
let reg_mod = reg as u8 % 8;
|
||||
if reg as u8 > 7 {
|
||||
let rex = add_opcode_extension(reg, REX);
|
||||
buf.extend(&[rex, 0x50 + reg_mod]);
|
||||
buf.extend(&[rex, 0x50 | reg_mod]);
|
||||
} else {
|
||||
buf.push(0x50 + reg_mod);
|
||||
buf.push(0x50 | reg_mod);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -632,35 +632,36 @@ where
|
|||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let entry = env.builder.get_insert_block().unwrap();
|
||||
|
||||
// constant 1i64
|
||||
// constant 1usize
|
||||
let one = env.ptr_int().const_int(1, false);
|
||||
let zero = env.ptr_int().const_zero();
|
||||
|
||||
// allocate a stack slot for the current index
|
||||
let index_alloca = builder.build_alloca(env.ptr_int(), index_name);
|
||||
builder.build_store(index_alloca, env.ptr_int().const_zero());
|
||||
builder.build_store(index_alloca, zero);
|
||||
|
||||
let loop_bb = ctx.append_basic_block(parent, "loop");
|
||||
builder.build_unconditional_branch(loop_bb);
|
||||
builder.position_at_end(loop_bb);
|
||||
|
||||
let current_index_phi = env.builder.build_phi(env.ptr_int(), "current_index");
|
||||
let current_index = current_index_phi.as_basic_value().into_int_value();
|
||||
|
||||
let next_index = builder.build_int_add(current_index, one, "next_index");
|
||||
|
||||
current_index_phi.add_incoming(&[(&next_index, loop_bb), (&env.ptr_int().const_zero(), entry)]);
|
||||
|
||||
// The body of the loop
|
||||
loop_fn(current_index);
|
||||
|
||||
// #index < end
|
||||
let loop_end_cond = bounds_check_comparison(builder, next_index, end);
|
||||
|
||||
let after_loop_bb = ctx.append_basic_block(parent, "after_outer_loop_2");
|
||||
let after_loop_bb = ctx.append_basic_block(parent, "after_loop");
|
||||
|
||||
let loop_end_cond = bounds_check_comparison(builder, zero, end);
|
||||
builder.build_conditional_branch(loop_end_cond, loop_bb, after_loop_bb);
|
||||
|
||||
{
|
||||
builder.position_at_end(loop_bb);
|
||||
|
||||
let current_index = builder.build_load(index_alloca, "index").into_int_value();
|
||||
let next_index = builder.build_int_add(current_index, one, "next_index");
|
||||
builder.build_store(index_alloca, next_index);
|
||||
|
||||
// The body of the loop
|
||||
loop_fn(current_index);
|
||||
|
||||
// #index < end
|
||||
let loop_end_cond = bounds_check_comparison(builder, next_index, end);
|
||||
|
||||
builder.build_conditional_branch(loop_end_cond, loop_bb, after_loop_bb);
|
||||
}
|
||||
|
||||
builder.position_at_end(after_loop_bb);
|
||||
|
||||
index_alloca
|
||||
|
|
|
@ -1,17 +1,27 @@
|
|||
use crate::llvm::bitcode::call_bitcode_fn;
|
||||
use crate::llvm::build::Env;
|
||||
use crate::debug_info_init;
|
||||
use crate::llvm::bitcode::call_str_bitcode_fn;
|
||||
use crate::llvm::build::{get_tag_id, store_roc_value, Env};
|
||||
use crate::llvm::build_list::{self, incrementing_elem_loop};
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
use crate::llvm::convert::{basic_type_from_layout, RocUnion};
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::{BasicValueEnum, IntValue, PointerValue};
|
||||
use inkwell::module::Linkage;
|
||||
use inkwell::types::{BasicMetadataTypeEnum, BasicType};
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
|
||||
use roc_region::all::Region;
|
||||
|
||||
use super::build::{load_symbol_and_layout, Scope};
|
||||
use super::build::{
|
||||
add_func, load_roc_value, load_symbol_and_layout, use_roc_value, FunctionSpec, Scope,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Cursors<'ctx> {
|
||||
offset: IntValue<'ctx>,
|
||||
extra_offset: IntValue<'ctx>,
|
||||
}
|
||||
|
||||
fn pointer_at_offset<'ctx>(
|
||||
bd: &Builder<'ctx>,
|
||||
|
@ -110,18 +120,59 @@ pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
|||
|
||||
offset = write_header(env, original_ptr, offset, condition, region);
|
||||
|
||||
let after_header = offset;
|
||||
|
||||
let space_for_offsets = env
|
||||
.ptr_int()
|
||||
.const_int((lookups.len() * env.target_info.ptr_size()) as _, false);
|
||||
|
||||
let mut lookup_starts = bumpalo::collections::Vec::with_capacity_in(lookups.len(), env.arena);
|
||||
|
||||
offset = env
|
||||
.builder
|
||||
.build_int_add(offset, space_for_offsets, "offset");
|
||||
|
||||
for lookup in lookups.iter() {
|
||||
lookup_starts.push(offset);
|
||||
|
||||
let (value, layout) = load_symbol_and_layout(scope, lookup);
|
||||
|
||||
offset = build_clone(
|
||||
let stack_size = env
|
||||
.ptr_int()
|
||||
.const_int(layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let mut extra_offset = env.builder.build_int_add(offset, stack_size, "offset");
|
||||
|
||||
let cursors = Cursors {
|
||||
offset,
|
||||
extra_offset,
|
||||
};
|
||||
|
||||
extra_offset = build_clone(
|
||||
env,
|
||||
layout_ids,
|
||||
original_ptr,
|
||||
offset,
|
||||
cursors,
|
||||
value,
|
||||
*layout,
|
||||
WhenRecursive::Unreachable,
|
||||
);
|
||||
|
||||
offset = extra_offset;
|
||||
}
|
||||
|
||||
{
|
||||
let mut offset = after_header;
|
||||
|
||||
for lookup_start in lookup_starts {
|
||||
build_copy(env, original_ptr, offset, lookup_start.into());
|
||||
|
||||
let ptr_width = env
|
||||
.ptr_int()
|
||||
.const_int(env.target_info.ptr_size() as _, false);
|
||||
|
||||
offset = env.builder.build_int_add(offset, ptr_width, "offset")
|
||||
}
|
||||
}
|
||||
|
||||
let one = env.ptr_int().const_int(1, false);
|
||||
|
@ -136,50 +187,97 @@ enum WhenRecursive<'a> {
|
|||
Loop(UnionLayout<'a>),
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_clone<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
offset: IntValue<'ctx>,
|
||||
cursors: Cursors<'ctx>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
layout: Layout<'a>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
) -> IntValue<'ctx> {
|
||||
match layout {
|
||||
Layout::Builtin(builtin) => {
|
||||
build_clone_builtin(env, layout_ids, ptr, offset, value, builtin, when_recursive)
|
||||
}
|
||||
Layout::Builtin(builtin) => build_clone_builtin(
|
||||
env,
|
||||
layout_ids,
|
||||
ptr,
|
||||
cursors,
|
||||
value,
|
||||
builtin,
|
||||
when_recursive,
|
||||
),
|
||||
|
||||
Layout::Struct {
|
||||
field_layouts: _, ..
|
||||
} => {
|
||||
if layout.safe_to_memcpy() {
|
||||
build_copy(env, ptr, offset, value)
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Layout::Struct { field_layouts, .. } => build_clone_struct(
|
||||
env,
|
||||
layout_ids,
|
||||
ptr,
|
||||
cursors,
|
||||
value,
|
||||
field_layouts,
|
||||
when_recursive,
|
||||
),
|
||||
|
||||
Layout::LambdaSet(_) => unreachable!("cannot compare closures"),
|
||||
|
||||
Layout::Union(_union_layout) => {
|
||||
Layout::Union(union_layout) => {
|
||||
if layout.safe_to_memcpy() {
|
||||
build_copy(env, ptr, offset, value)
|
||||
let ptr = unsafe {
|
||||
env.builder
|
||||
.build_in_bounds_gep(ptr, &[cursors.offset], "at_current_offset")
|
||||
};
|
||||
|
||||
let ptr_type = value.get_type().ptr_type(AddressSpace::Generic);
|
||||
let ptr = env
|
||||
.builder
|
||||
.build_pointer_cast(ptr, ptr_type, "cast_ptr_type");
|
||||
|
||||
store_roc_value(env, layout, ptr, value);
|
||||
|
||||
cursors.extra_offset
|
||||
} else {
|
||||
todo!()
|
||||
build_clone_tag(
|
||||
env,
|
||||
layout_ids,
|
||||
ptr,
|
||||
cursors,
|
||||
value,
|
||||
union_layout,
|
||||
WhenRecursive::Loop(union_layout),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Layout::Boxed(inner_layout) => build_box_eq(
|
||||
env,
|
||||
layout_ids,
|
||||
when_recursive,
|
||||
lhs_layout,
|
||||
inner_layout,
|
||||
lhs_val,
|
||||
rhs_val,
|
||||
),
|
||||
Layout::Boxed(inner_layout) => {
|
||||
// write the offset
|
||||
build_copy(env, ptr, cursors.offset, cursors.extra_offset.into());
|
||||
|
||||
let source = value.into_pointer_value();
|
||||
let value = load_roc_value(env, *inner_layout, source, "inner");
|
||||
|
||||
let inner_width = env
|
||||
.ptr_int()
|
||||
.const_int(inner_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
let new_extra = env
|
||||
.builder
|
||||
.build_int_add(cursors.offset, inner_width, "new_extra");
|
||||
|
||||
let cursors = Cursors {
|
||||
offset: cursors.extra_offset,
|
||||
extra_offset: new_extra,
|
||||
};
|
||||
|
||||
build_clone(
|
||||
env,
|
||||
layout_ids,
|
||||
ptr,
|
||||
cursors,
|
||||
value,
|
||||
*inner_layout,
|
||||
when_recursive,
|
||||
)
|
||||
}
|
||||
|
||||
Layout::RecursivePointer => match when_recursive {
|
||||
WhenRecursive::Unreachable => {
|
||||
|
@ -192,27 +290,249 @@ fn build_clone<'a, 'ctx, 'env>(
|
|||
let bt = basic_type_from_layout(env, &layout);
|
||||
|
||||
// cast the i64 pointer to a pointer to block of memory
|
||||
let field1_cast = env
|
||||
.builder
|
||||
.build_bitcast(lhs_val, bt, "i64_to_opaque")
|
||||
.into_pointer_value();
|
||||
let field1_cast = env.builder.build_bitcast(value, bt, "i64_to_opaque");
|
||||
|
||||
let field2_cast = env
|
||||
.builder
|
||||
.build_bitcast(rhs_val, bt, "i64_to_opaque")
|
||||
.into_pointer_value();
|
||||
|
||||
build_tag_eq(
|
||||
build_clone_tag(
|
||||
env,
|
||||
layout_ids,
|
||||
ptr,
|
||||
cursors,
|
||||
field1_cast,
|
||||
union_layout,
|
||||
WhenRecursive::Loop(union_layout),
|
||||
&union_layout,
|
||||
field1_cast.into(),
|
||||
field2_cast.into(),
|
||||
)
|
||||
}
|
||||
},
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_clone_struct<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
cursors: Cursors<'ctx>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
field_layouts: &[Layout<'a>],
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
) -> IntValue<'ctx> {
|
||||
let layout = Layout::struct_no_name_order(field_layouts);
|
||||
|
||||
if layout.safe_to_memcpy() {
|
||||
build_copy(env, ptr, cursors.offset, value)
|
||||
} else {
|
||||
let mut cursors = cursors;
|
||||
|
||||
let structure = value.into_struct_value();
|
||||
|
||||
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||
let field = env
|
||||
.builder
|
||||
.build_extract_value(structure, i as _, "extract")
|
||||
.unwrap();
|
||||
|
||||
let field = use_roc_value(env, *field_layout, field, "field");
|
||||
|
||||
let new_extra = build_clone(
|
||||
env,
|
||||
layout_ids,
|
||||
ptr,
|
||||
cursors,
|
||||
field,
|
||||
*field_layout,
|
||||
when_recursive,
|
||||
);
|
||||
|
||||
let field_width = env
|
||||
.ptr_int()
|
||||
.const_int(field_layout.stack_size(env.target_info) as u64, false);
|
||||
|
||||
cursors.extra_offset = new_extra;
|
||||
cursors.offset = env
|
||||
.builder
|
||||
.build_int_add(cursors.offset, field_width, "offset");
|
||||
}
|
||||
|
||||
cursors.extra_offset
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_clone_tag<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
cursors: Cursors<'ctx>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
) -> IntValue<'ctx> {
|
||||
let layout = Layout::Union(union_layout);
|
||||
let layout_id = layout_ids.get(Symbol::CLONE, &layout);
|
||||
let fn_name = layout_id.to_symbol_string(Symbol::CLONE, &env.interns);
|
||||
|
||||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
None => {
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let function_type = env.ptr_int().fn_type(
|
||||
&[
|
||||
env.context.i8_type().ptr_type(AddressSpace::Generic).into(),
|
||||
env.ptr_int().into(),
|
||||
env.ptr_int().into(),
|
||||
BasicMetadataTypeEnum::from(value.get_type()),
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
let function_value = add_func(
|
||||
env.context,
|
||||
env.module,
|
||||
&fn_name,
|
||||
FunctionSpec::known_fastcc(function_type),
|
||||
Linkage::Private,
|
||||
);
|
||||
|
||||
let subprogram = env.new_subprogram(&fn_name);
|
||||
function_value.set_subprogram(subprogram);
|
||||
|
||||
env.dibuilder.finalize();
|
||||
|
||||
build_clone_tag_help(
|
||||
env,
|
||||
layout_ids,
|
||||
union_layout,
|
||||
when_recursive,
|
||||
function_value,
|
||||
);
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder
|
||||
.set_current_debug_location(env.context, di_location);
|
||||
|
||||
function_value
|
||||
}
|
||||
};
|
||||
|
||||
let call = env.builder.build_call(
|
||||
function,
|
||||
&[
|
||||
ptr.into(),
|
||||
cursors.offset.into(),
|
||||
cursors.extra_offset.into(),
|
||||
value.into(),
|
||||
],
|
||||
"build_clone_tag",
|
||||
);
|
||||
|
||||
call.set_call_convention(function.get_call_conventions());
|
||||
|
||||
let result = call.try_as_basic_value().left().unwrap();
|
||||
|
||||
result.into_int_value()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
fn_val: FunctionValue<'ctx>,
|
||||
) {
|
||||
use bumpalo::collections::Vec;
|
||||
|
||||
let context = &env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// Add a basic block for the entry point
|
||||
let entry = context.append_basic_block(fn_val, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, fn_val);
|
||||
|
||||
// Add args to scope
|
||||
// let arg_symbol = Symbol::ARG_1;
|
||||
// tag_value.set_name(arg_symbol.as_str(&env.interns));
|
||||
|
||||
let mut it = fn_val.get_param_iter();
|
||||
|
||||
let ptr = it.next().unwrap().into_pointer_value();
|
||||
let offset = it.next().unwrap().into_int_value();
|
||||
let extra_offset = it.next().unwrap().into_int_value();
|
||||
let tag_value = it.next().unwrap();
|
||||
|
||||
let cursors = Cursors {
|
||||
offset,
|
||||
extra_offset,
|
||||
};
|
||||
|
||||
let parent = fn_val;
|
||||
|
||||
debug_assert!(tag_value.is_pointer_value());
|
||||
|
||||
use UnionLayout::*;
|
||||
|
||||
match union_layout {
|
||||
NonRecursive(&[]) => {
|
||||
// we're comparing empty tag unions; this code is effectively unreachable
|
||||
env.builder.build_unreachable();
|
||||
}
|
||||
NonRecursive(tags) => {
|
||||
let id = get_tag_id(env, parent, &union_layout, tag_value);
|
||||
|
||||
let switch_block = env.context.append_basic_block(parent, "switch_block");
|
||||
env.builder.build_unconditional_branch(switch_block);
|
||||
|
||||
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
|
||||
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let raw_data_ptr = env
|
||||
.builder
|
||||
.build_struct_gep(
|
||||
tag_value.into_pointer_value(),
|
||||
RocUnion::TAG_DATA_INDEX,
|
||||
"tag_data",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let layout = Layout::struct_no_name_order(field_layouts);
|
||||
let basic_type = basic_type_from_layout(env, &layout);
|
||||
|
||||
let data_ptr = env.builder.build_pointer_cast(
|
||||
raw_data_ptr,
|
||||
basic_type.ptr_type(AddressSpace::Generic),
|
||||
"data_ptr",
|
||||
);
|
||||
|
||||
let data = env.builder.build_load(data_ptr, "load_data");
|
||||
|
||||
let answer =
|
||||
build_clone(env, layout_ids, ptr, cursors, data, layout, when_recursive);
|
||||
|
||||
env.builder.build_return(Some(&answer));
|
||||
|
||||
cases.push((id.get_type().const_int(tag_id as u64, false), block));
|
||||
}
|
||||
|
||||
env.builder.position_at_end(switch_block);
|
||||
|
||||
match cases.pop() {
|
||||
Some((_, default)) => {
|
||||
env.builder.build_switch(id, default, &cases);
|
||||
}
|
||||
None => {
|
||||
// we're serializing an empty tag union; this code is effectively unreachable
|
||||
env.builder.build_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -239,11 +559,12 @@ fn build_copy<'a, 'ctx, 'env>(
|
|||
env.builder.build_int_add(offset, width, "new_offset")
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_clone_builtin<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
offset: IntValue<'ctx>,
|
||||
cursors: Cursors<'ctx>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
builtin: Builtin<'a>,
|
||||
when_recursive: WhenRecursive<'a>,
|
||||
|
@ -251,14 +572,24 @@ fn build_clone_builtin<'a, 'ctx, 'env>(
|
|||
use Builtin::*;
|
||||
|
||||
match builtin {
|
||||
Int(_) | Float(_) | Bool | Decimal => build_copy(env, ptr, offset, value),
|
||||
Int(_) | Float(_) | Bool | Decimal => {
|
||||
build_copy(env, ptr, cursors.offset, value);
|
||||
|
||||
cursors.extra_offset
|
||||
}
|
||||
|
||||
Builtin::Str => {
|
||||
//
|
||||
|
||||
call_bitcode_fn(
|
||||
call_str_bitcode_fn(
|
||||
env,
|
||||
&[ptr.into(), offset.into(), value],
|
||||
&[value],
|
||||
&[
|
||||
ptr.into(),
|
||||
cursors.offset.into(),
|
||||
cursors.extra_offset.into(),
|
||||
],
|
||||
crate::llvm::bitcode::BitcodeReturns::Basic,
|
||||
bitcode::STR_CLONE_TO,
|
||||
)
|
||||
.into_int_value()
|
||||
|
@ -269,15 +600,10 @@ fn build_clone_builtin<'a, 'ctx, 'env>(
|
|||
let list = value.into_struct_value();
|
||||
let (elements, len, _cap) = build_list::destructure(env.builder, list);
|
||||
|
||||
let list_width = env
|
||||
.ptr_int()
|
||||
.const_int(env.target_info.ptr_size() as u64 * 3, false);
|
||||
let elements_offset = bd.build_int_add(offset, list_width, "new_offset");
|
||||
|
||||
let mut offset = offset;
|
||||
let mut offset = cursors.offset;
|
||||
|
||||
// we only copy the elements we actually have (and skip extra capacity)
|
||||
offset = build_copy(env, ptr, offset, elements_offset.into());
|
||||
offset = build_copy(env, ptr, offset, cursors.extra_offset.into());
|
||||
offset = build_copy(env, ptr, offset, len.into());
|
||||
offset = build_copy(env, ptr, offset, len.into());
|
||||
|
||||
|
@ -307,23 +633,45 @@ fn build_clone_builtin<'a, 'ctx, 'env>(
|
|||
"elements",
|
||||
);
|
||||
|
||||
let element_offset = bd.build_alloca(env.ptr_int(), "element_offset");
|
||||
bd.build_store(element_offset, elements_start_offset);
|
||||
// if the element has any pointers, we clone them to this offset
|
||||
let rest_offset = bd.build_alloca(env.ptr_int(), "rest_offset");
|
||||
|
||||
let body = |_index, element| {
|
||||
let current_offset = bd.build_load(element_offset, "element_offset");
|
||||
let element_stack_size = env
|
||||
.ptr_int()
|
||||
.const_int(elem.stack_size(env.target_info) as u64, false);
|
||||
let rest_start_offset = bd.build_int_add(
|
||||
cursors.extra_offset,
|
||||
bd.build_int_mul(len, element_stack_size, "elements_width"),
|
||||
"rest_start_offset",
|
||||
);
|
||||
bd.build_store(rest_offset, rest_start_offset);
|
||||
|
||||
let body = |index, element| {
|
||||
let current_offset =
|
||||
bd.build_int_mul(element_stack_size, index, "current_offset");
|
||||
let current_offset =
|
||||
bd.build_int_add(elements_start_offset, current_offset, "current_offset");
|
||||
let current_extra_offset = bd.build_load(rest_offset, "element_offset");
|
||||
|
||||
let offset = current_offset;
|
||||
let extra_offset = current_extra_offset.into_int_value();
|
||||
|
||||
let cursors = Cursors {
|
||||
offset,
|
||||
extra_offset,
|
||||
};
|
||||
|
||||
let new_offset = build_clone(
|
||||
env,
|
||||
layout_ids,
|
||||
ptr,
|
||||
current_offset.into_int_value(),
|
||||
cursors,
|
||||
element,
|
||||
*elem,
|
||||
when_recursive,
|
||||
);
|
||||
|
||||
bd.build_store(element_offset, new_offset);
|
||||
bd.build_store(rest_offset, new_offset);
|
||||
};
|
||||
|
||||
let parent = env
|
||||
|
@ -334,7 +682,7 @@ fn build_clone_builtin<'a, 'ctx, 'env>(
|
|||
|
||||
incrementing_elem_loop(env, parent, *elem, elements, len, "index", body);
|
||||
|
||||
bd.build_load(element_offset, "element_offset")
|
||||
bd.build_load(rest_offset, "rest_start_offset")
|
||||
.into_int_value()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,21 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
|||
let usize_type = env.ptr_int();
|
||||
let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
match env.mode {
|
||||
super::build::LlvmBackendMode::CliTest => {
|
||||
// expose this function
|
||||
if let Some(fn_val) = module.get_function("set_shared_buffer") {
|
||||
fn_val.set_linkage(Linkage::External);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// remove this function from the module
|
||||
if let Some(fn_val) = module.get_function("set_shared_buffer") {
|
||||
unsafe { fn_val.delete() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !env.mode.has_host() {
|
||||
// roc_alloc
|
||||
{
|
||||
|
|
|
@ -47,7 +47,7 @@ impl<T: Sized> From<RocCallResult<T>> for Result<T, String> {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! run_roc_dylib {
|
||||
($lib:expr, $main_fn_name:expr, $argument_type:ty, $return_type:ty, $errors:expr) => {{
|
||||
($lib:expr, $main_fn_name:expr, $argument_type:ty, $return_type:ty) => {{
|
||||
use inkwell::context::Context;
|
||||
use roc_builtins::bitcode;
|
||||
use roc_gen_llvm::run_roc::RocCallResult;
|
||||
|
|
|
@ -1610,13 +1610,46 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
}
|
||||
NumShiftRightBy => {
|
||||
backend.storage.load_symbols(
|
||||
&mut backend.code_builder,
|
||||
&[self.arguments[1], self.arguments[0]],
|
||||
);
|
||||
let bits = self.arguments[0];
|
||||
let num = self.arguments[1];
|
||||
match CodeGenNumType::from(self.ret_layout) {
|
||||
I32 => backend.code_builder.i32_shr_s(),
|
||||
I64 => backend.code_builder.i64_shr_s(),
|
||||
I32 => {
|
||||
// In most languages this operation is for signed numbers, but Roc defines it on all integers.
|
||||
// So the argument is implicitly converted to signed before the shift operator.
|
||||
// We need to make that conversion explicit for i8 and i16, which use Wasm's i32 type.
|
||||
let bit_width = 8 * self.ret_layout.stack_size(TARGET_INFO) as i32;
|
||||
if bit_width < 32 && !symbol_is_signed_int(backend, num) {
|
||||
// Sign-extend the number by shifting left and right again
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[num]);
|
||||
backend.code_builder.i32_const(32 - bit_width);
|
||||
backend.code_builder.i32_shl();
|
||||
backend.code_builder.i32_const(32 - bit_width);
|
||||
backend.code_builder.i32_shr_s();
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[bits]);
|
||||
|
||||
// Do the actual bitshift operation
|
||||
backend.code_builder.i32_shr_s();
|
||||
|
||||
// Restore to unsigned
|
||||
backend.code_builder.i32_const((1 << bit_width) - 1);
|
||||
backend.code_builder.i32_and();
|
||||
} else {
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
||||
backend.code_builder.i32_shr_s();
|
||||
}
|
||||
}
|
||||
I64 => {
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
||||
backend.code_builder.i64_shr_s();
|
||||
}
|
||||
I128 => todo!("{:?} for I128", self.lowlevel),
|
||||
_ => panic_ret_type(),
|
||||
}
|
||||
|
@ -1624,7 +1657,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
NumShiftRightZfBy => {
|
||||
match CodeGenNumType::from(self.ret_layout) {
|
||||
I32 => {
|
||||
// This is normally an unsigned operation, but Roc defines it on all integer types.
|
||||
// In most languages this operation is for unsigned numbers, but Roc defines it on all integers.
|
||||
// So the argument is implicitly converted to unsigned before the shift operator.
|
||||
// We need to make that conversion explicit for i8 and i16, which use Wasm's i32 type.
|
||||
let bit_width = 8 * self.ret_layout.stack_size(TARGET_INFO);
|
||||
|
|
|
@ -65,6 +65,11 @@ impl Wasm32Sized for usize {
|
|||
const ALIGN_OF_WASM: usize = 4;
|
||||
}
|
||||
|
||||
impl Wasm32Sized for isize {
|
||||
const SIZE_OF_WASM: usize = 4;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
}
|
||||
|
||||
impl<T: Wasm32Sized, U: Wasm32Sized> Wasm32Sized for (T, U) {
|
||||
const SIZE_OF_WASM: usize = T::SIZE_OF_WASM + U::SIZE_OF_WASM;
|
||||
const ALIGN_OF_WASM: usize = max(&[T::ALIGN_OF_WASM, U::ALIGN_OF_WASM]);
|
||||
|
|
|
@ -13,7 +13,7 @@ use roc_module::symbol::ModuleId;
|
|||
use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools};
|
||||
use roc_types::subs::{get_member_lambda_sets_at_region, Content, FlatType, LambdaSet};
|
||||
use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable};
|
||||
use roc_unify::unify::{unify as unify_unify, Mode, Unified};
|
||||
use roc_unify::unify::{unify as unify_unify, Env, Mode, Unified};
|
||||
|
||||
pub use roc_solve::ability::resolve_ability_specialization;
|
||||
pub use roc_solve::ability::Resolved;
|
||||
|
@ -260,7 +260,7 @@ pub fn unify(
|
|||
ModuleId::DERIVED_SYNTH,
|
||||
"derived module can only unify its subs in its own context!"
|
||||
);
|
||||
let unified = unify_unify(subs, left, right, Mode::EQ);
|
||||
let unified = unify_unify(&mut Env::new(subs), left, right, Mode::EQ);
|
||||
|
||||
match unified {
|
||||
Unified::Success {
|
||||
|
|
|
@ -20,6 +20,7 @@ roc_problem = { path = "../problem" }
|
|||
roc_unify = { path = "../unify" }
|
||||
roc_parse = { path = "../parse" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_solve_problem = { path = "../solve_problem" }
|
||||
roc_late_solve = { path = "../late_solve" }
|
||||
roc_mono = { path = "../mono" }
|
||||
roc_target = { path = "../roc_target" }
|
||||
|
|
|
@ -43,7 +43,7 @@ use roc_parse::parser::{FileError, Parser, SyntaxError};
|
|||
use roc_region::all::{LineInfo, Loc, Region};
|
||||
use roc_reporting::report::RenderTarget;
|
||||
use roc_solve::module::{extract_module_owned_implementations, Solved, SolvedModule};
|
||||
use roc_solve::solve;
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable};
|
||||
use roc_types::types::{Alias, AliasKind};
|
||||
|
@ -139,7 +139,7 @@ struct ModuleCache<'a> {
|
|||
top_level_thunks: MutMap<ModuleId, MutSet<Symbol>>,
|
||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||
can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||
type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
||||
type_problems: MutMap<ModuleId, Vec<TypeError>>,
|
||||
|
||||
sources: MutMap<ModuleId, (PathBuf, &'a str)>,
|
||||
}
|
||||
|
@ -556,7 +556,7 @@ pub struct LoadedModule {
|
|||
pub interns: Interns,
|
||||
pub solved: Solved<Subs>,
|
||||
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
||||
pub type_problems: MutMap<ModuleId, Vec<TypeError>>,
|
||||
pub declarations_by_id: MutMap<ModuleId, Declarations>,
|
||||
pub exposed_to_host: MutMap<Symbol, Variable>,
|
||||
pub dep_idents: IdentIdsByModule,
|
||||
|
@ -671,7 +671,7 @@ pub struct MonomorphizedModule<'a> {
|
|||
pub output_path: Box<Path>,
|
||||
pub platform_path: Box<Path>,
|
||||
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
||||
pub type_problems: MutMap<ModuleId, Vec<TypeError>>,
|
||||
pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
pub toplevel_expects: VecMap<Symbol, Region>,
|
||||
pub entry_point: EntryPoint<'a>,
|
||||
|
@ -4115,7 +4115,7 @@ fn run_solve_solve(
|
|||
Solved<Subs>,
|
||||
ResolvedImplementations,
|
||||
Vec<(Symbol, Variable)>,
|
||||
Vec<solve::TypeError>,
|
||||
Vec<TypeError>,
|
||||
AbilitiesStore,
|
||||
) {
|
||||
let Module {
|
||||
|
|
|
@ -1005,6 +1005,8 @@ define_builtins! {
|
|||
30 DEV_TMP5: "#dev_tmp5"
|
||||
|
||||
31 ATTR_INVALID: "#attr_invalid"
|
||||
|
||||
32 CLONE: "#clone" // internal function that clones a value into a buffer
|
||||
}
|
||||
// Fake module for synthesizing and storing derived implementations
|
||||
1 DERIVED_SYNTH: "#Derived" => {
|
||||
|
|
|
@ -96,6 +96,23 @@ enum Test<'a> {
|
|||
},
|
||||
}
|
||||
|
||||
impl<'a> Test<'a> {
|
||||
fn can_be_switch(&self) -> bool {
|
||||
match self {
|
||||
Test::IsCtor { .. } => true,
|
||||
Test::IsInt(_, int_width) => {
|
||||
// llvm does not like switching on 128-bit values
|
||||
!matches!(int_width, IntWidth::U128 | IntWidth::I128)
|
||||
}
|
||||
Test::IsFloat(_, _) => true,
|
||||
Test::IsDecimal(_) => false,
|
||||
Test::IsStr(_) => false,
|
||||
Test::IsBit(_) => true,
|
||||
Test::IsByte { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
impl<'a> Hash for Test<'a> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
|
@ -1370,8 +1387,6 @@ fn test_to_equality<'a>(
|
|||
}
|
||||
|
||||
Test::IsInt(test_int, precision) => {
|
||||
// TODO don't downcast i128 here
|
||||
debug_assert!(i128::from_ne_bytes(test_int) <= i64::MAX as i128);
|
||||
let lhs = Expr::Literal(Literal::Int(test_int));
|
||||
let lhs_symbol = env.unique_symbol();
|
||||
stores.push((lhs_symbol, Layout::int_width(precision), lhs));
|
||||
|
@ -1833,7 +1848,8 @@ fn decide_to_branching<'a>(
|
|||
Test::IsBit(v) => v as u64,
|
||||
Test::IsByte { tag_id, .. } => tag_id as u64,
|
||||
Test::IsCtor { tag_id, .. } => tag_id as u64,
|
||||
other => todo!("other {:?}", other),
|
||||
Test::IsDecimal(_) => unreachable!("decimals cannot be switched on"),
|
||||
Test::IsStr(_) => unreachable!("strings cannot be switched on"),
|
||||
};
|
||||
|
||||
// branch info is only useful for refcounted values
|
||||
|
@ -2004,15 +2020,30 @@ fn fanout_decider<'a>(
|
|||
edges: Vec<(GuardedTest<'a>, DecisionTree<'a>)>,
|
||||
) -> Decider<'a, u64> {
|
||||
let fallback_decider = tree_to_decider(fallback);
|
||||
let necessary_tests = edges
|
||||
let necessary_tests: Vec<_> = edges
|
||||
.into_iter()
|
||||
.map(|(test, tree)| fanout_decider_help(tree, test))
|
||||
.collect();
|
||||
|
||||
Decider::FanOut {
|
||||
path,
|
||||
tests: necessary_tests,
|
||||
fallback: Box::new(fallback_decider),
|
||||
if necessary_tests.iter().all(|(t, _)| t.can_be_switch()) {
|
||||
Decider::FanOut {
|
||||
path,
|
||||
tests: necessary_tests,
|
||||
fallback: Box::new(fallback_decider),
|
||||
}
|
||||
} else {
|
||||
// in llvm, we cannot switch on strings so must chain
|
||||
let mut decider = fallback_decider;
|
||||
|
||||
for (test, branch_decider) in necessary_tests.into_iter().rev() {
|
||||
decider = Decider::Chain {
|
||||
test_chain: vec![(path.clone(), test)],
|
||||
success: Box::new(branch_decider),
|
||||
failure: Box::new(decider),
|
||||
};
|
||||
}
|
||||
|
||||
decider
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3999,9 +3999,10 @@ pub fn with_hole<'a>(
|
|||
}
|
||||
|
||||
// creating a record from the var will unpack it if it's just a single field.
|
||||
let layout = layout_cache
|
||||
.from_var(env.arena, record_var, env.subs)
|
||||
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||
let layout = match layout_cache.from_var(env.arena, record_var, env.subs) {
|
||||
Ok(layout) => layout,
|
||||
Err(_) => return Stmt::RuntimeError("Can't create record with improper layout"),
|
||||
};
|
||||
|
||||
let field_symbols = field_symbols.into_bump_slice();
|
||||
|
||||
|
@ -8957,7 +8958,7 @@ impl NumLiteral {
|
|||
fn to_pattern(&self) -> Pattern<'static> {
|
||||
match *self {
|
||||
NumLiteral::Int(n, w) => Pattern::IntLiteral(n, w),
|
||||
NumLiteral::U128(_) => todo!(),
|
||||
NumLiteral::U128(n) => Pattern::IntLiteral(n, IntWidth::U128),
|
||||
NumLiteral::Float(n, w) => Pattern::FloatLiteral(f64::to_bits(n), w),
|
||||
NumLiteral::Decimal(n) => Pattern::DecimalLiteral(n),
|
||||
}
|
||||
|
|
|
@ -384,6 +384,7 @@ pub enum EString<'a> {
|
|||
UnknownEscape(Position),
|
||||
Format(&'a EExpr<'a>, Position),
|
||||
FormatEnd(Position),
|
||||
MultilineInsufficientIndent(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -37,6 +37,25 @@ impl<'a> State<'a> {
|
|||
self.pos().offset - self.line_start.offset
|
||||
}
|
||||
|
||||
/// Mutably advance the state by a given offset
|
||||
#[inline(always)]
|
||||
pub(crate) fn advance_mut(&mut self, offset: usize) {
|
||||
self.offset += offset;
|
||||
}
|
||||
|
||||
/// If the next `text.len()` bytes of the input match the provided `text`,
|
||||
/// mutably advance the state by that much.
|
||||
#[inline(always)]
|
||||
pub(crate) fn consume_mut(&mut self, text: &str) -> bool {
|
||||
let found = self.bytes().starts_with(text.as_bytes());
|
||||
|
||||
if found {
|
||||
self.advance_mut(text.len());
|
||||
}
|
||||
|
||||
found
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub(crate) const fn advance(mut self, offset: usize) -> State<'a> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ast::{EscapedChar, StrLiteral, StrSegment};
|
||||
use crate::expr;
|
||||
use crate::parser::Progress::*;
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{allocated, loc, specialize_ref, word1, BadInputError, EString, Parser};
|
||||
use crate::state::State;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
|
@ -9,7 +9,7 @@ use bumpalo::Bump;
|
|||
/// One or more ASCII hex digits. (Useful when parsing unicode escape codes,
|
||||
/// which must consist entirely of ASCII hex digits.)
|
||||
fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
move |arena, mut state: State<'a>| {
|
||||
let mut buf = bumpalo::collections::String::new_in(arena);
|
||||
|
||||
for &byte in state.bytes().iter() {
|
||||
|
@ -19,7 +19,7 @@ fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
|
|||
// We didn't find any hex digits!
|
||||
return Err((NoProgress, EString::CodePtEnd(state.pos()), state));
|
||||
} else {
|
||||
let state = state.advance(buf.len());
|
||||
state.advance_mut(buf.len());
|
||||
|
||||
return Ok((MadeProgress, buf.into_bump_str(), state));
|
||||
}
|
||||
|
@ -29,36 +29,27 @@ fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! advance_state {
|
||||
($state:expr, $n:expr) => {
|
||||
Ok($state.advance($n))
|
||||
};
|
||||
}
|
||||
|
||||
pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
|
||||
move |arena: &'a Bump, mut state: State<'a>| {
|
||||
if state.bytes().starts_with(b"\'") {
|
||||
if state.consume_mut("\'") {
|
||||
// we will be parsing a single-quote-string
|
||||
} else {
|
||||
return Err((NoProgress, EString::Open(state.pos()), state));
|
||||
}
|
||||
|
||||
// early return did not hit, just advance one byte
|
||||
state = advance_state!(state, 1)?;
|
||||
|
||||
// Handle back slaches in byte literal
|
||||
// - starts with a backslash and used as an escape character. ex: '\n', '\t'
|
||||
// - single quote floating (un closed single quote) should be an error
|
||||
match state.bytes().first() {
|
||||
Some(b'\\') => {
|
||||
state = advance_state!(state, 1)?;
|
||||
state.advance_mut(1);
|
||||
match state.bytes().first() {
|
||||
Some(&ch) => {
|
||||
state = advance_state!(state, 1)?;
|
||||
state.advance_mut(1);
|
||||
if (ch == b'n' || ch == b'r' || ch == b't' || ch == b'\'' || ch == b'\\')
|
||||
&& (state.bytes().first() == Some(&b'\''))
|
||||
{
|
||||
state = advance_state!(state, 1)?;
|
||||
state.advance_mut(1);
|
||||
let test = match ch {
|
||||
b'n' => '\n',
|
||||
b't' => '\t',
|
||||
|
@ -118,7 +109,7 @@ pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
|
|||
// ending up w/ a slice of bytes that we want to convert into an integer
|
||||
let raw_bytes = &state.bytes()[0..end_index - 1];
|
||||
|
||||
state = advance_state!(state, end_index)?;
|
||||
state.advance_mut(end_index);
|
||||
match std::str::from_utf8(raw_bytes) {
|
||||
Ok(string) => Ok((MadeProgress, string, state)),
|
||||
Err(_) => {
|
||||
|
@ -129,33 +120,78 @@ pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
fn consume_indent<'a>(
|
||||
mut state: State<'a>,
|
||||
mut indent: u32,
|
||||
) -> Result<State, (Progress, EString<'a>, State<'a>)> {
|
||||
while indent > 0 {
|
||||
match state.bytes().first() {
|
||||
Some(b' ') => {
|
||||
state.advance_mut(1);
|
||||
indent -= 1;
|
||||
}
|
||||
None | Some(b'\n') => {
|
||||
break;
|
||||
}
|
||||
Some(_) => {
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EString::MultilineInsufficientIndent(state.pos()),
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
fn utf8<'a>(
|
||||
state: State<'a>,
|
||||
string_bytes: &'a [u8],
|
||||
) -> Result<&'a str, (Progress, EString<'a>, State<'a>)> {
|
||||
std::str::from_utf8(string_bytes).map_err(|_| {
|
||||
// Note Based on where this `utf8` function is used, the fact that we know the whole string
|
||||
// in the parser is valid utf8, and barring bugs in the parser itself
|
||||
// (e.g. where we accidentally split a multibyte utf8 char), this error _should_ actually be unreachable.
|
||||
(
|
||||
MadeProgress,
|
||||
EString::Space(BadInputError::BadUtf8, state.pos()),
|
||||
state,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
||||
use StrLiteral::*;
|
||||
|
||||
move |arena: &'a Bump, mut state: State<'a>| {
|
||||
let is_multiline;
|
||||
let mut bytes;
|
||||
|
||||
if state.bytes().starts_with(b"\"\"\"") {
|
||||
// we will be parsing a multi-string
|
||||
let indent = state.column();
|
||||
|
||||
let start_state;
|
||||
|
||||
if state.consume_mut("\"\"\"") {
|
||||
start_state = state.clone();
|
||||
|
||||
// we will be parsing a multi-line string
|
||||
is_multiline = true;
|
||||
bytes = state.bytes()[3..].iter();
|
||||
state = advance_state!(state, 3)?;
|
||||
} else if state.bytes().starts_with(b"\"") {
|
||||
// we will be parsing a single-string
|
||||
|
||||
if state.consume_mut("\n") {
|
||||
state = consume_indent(state, indent)?;
|
||||
}
|
||||
} else if state.consume_mut("\"") {
|
||||
start_state = state.clone();
|
||||
|
||||
// we will be parsing a single-line string
|
||||
is_multiline = false;
|
||||
bytes = state.bytes()[1..].iter();
|
||||
state = advance_state!(state, 1)?;
|
||||
} else {
|
||||
return Err((NoProgress, EString::Open(state.pos()), state));
|
||||
}
|
||||
|
||||
// At the parsing stage we keep the entire raw string, because the formatter
|
||||
// needs the raw string. (For example, so it can "remember" whether you
|
||||
// wrote \u{...} or the actual unicode character itself.)
|
||||
//
|
||||
// Since we're keeping the entire raw string, all we need to track is
|
||||
// how many characters we've parsed. So far, that's 1 (the opening `"`).
|
||||
let mut bytes = state.bytes().iter();
|
||||
|
||||
let mut segment_parsed_bytes = 0;
|
||||
let mut segments = Vec::new_in(arena);
|
||||
|
||||
|
@ -165,7 +201,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
segments.push(StrSegment::EscapedChar($ch));
|
||||
|
||||
// Advance past the segment we just added
|
||||
state = advance_state!(state, segment_parsed_bytes)?;
|
||||
state.advance_mut(segment_parsed_bytes);
|
||||
|
||||
// Reset the segment
|
||||
segment_parsed_bytes = 0;
|
||||
|
@ -184,7 +220,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
|
||||
match std::str::from_utf8(string_bytes) {
|
||||
Ok(string) => {
|
||||
state = advance_state!(state, string.len())?;
|
||||
state.advance_mut(string.len());
|
||||
|
||||
segments.push($transform(string));
|
||||
}
|
||||
|
@ -220,7 +256,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
// special case of the empty string
|
||||
if is_multiline {
|
||||
if bytes.as_slice().starts_with(b"\"\"") {
|
||||
return Ok((MadeProgress, Block(&[]), advance_state!(state, 3)?));
|
||||
return Ok((MadeProgress, Block(&[]), state.advance(3)));
|
||||
} else {
|
||||
// this quote is in a block string
|
||||
continue;
|
||||
|
@ -228,7 +264,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
} else {
|
||||
// This is the end of the string!
|
||||
// Advance 1 for the close quote
|
||||
return Ok((MadeProgress, PlainLine(""), advance_state!(state, 1)?));
|
||||
return Ok((MadeProgress, PlainLine(""), state.advance(1)));
|
||||
}
|
||||
} else {
|
||||
// the string is non-empty, which means we need to convert any previous segments
|
||||
|
@ -250,7 +286,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
Block(arena.alloc([segments.into_bump_slice()]))
|
||||
};
|
||||
|
||||
return Ok((MadeProgress, expr, advance_state!(state, 3)?));
|
||||
return Ok((MadeProgress, expr, state.advance(3)));
|
||||
} else {
|
||||
// this quote is in a block string
|
||||
continue;
|
||||
|
@ -270,12 +306,30 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
};
|
||||
|
||||
// Advance the state 1 to account for the closing `"`
|
||||
return Ok((MadeProgress, expr, advance_state!(state, 1)?));
|
||||
return Ok((MadeProgress, expr, state.advance(1)));
|
||||
}
|
||||
};
|
||||
}
|
||||
b'\n' => {
|
||||
if is_multiline {
|
||||
let without_newline = &state.bytes()[0..(segment_parsed_bytes - 1)];
|
||||
let with_newline = &state.bytes()[0..segment_parsed_bytes];
|
||||
|
||||
state.advance_mut(segment_parsed_bytes);
|
||||
state = consume_indent(state, indent)?;
|
||||
bytes = state.bytes().iter();
|
||||
|
||||
if state.bytes().starts_with(b"\"\"\"") {
|
||||
// ending the string; don't use the last newline
|
||||
segments
|
||||
.push(StrSegment::Plaintext(utf8(state.clone(), without_newline)?));
|
||||
} else {
|
||||
segments
|
||||
.push(StrSegment::Plaintext(utf8(state.clone(), with_newline)?));
|
||||
}
|
||||
|
||||
segment_parsed_bytes = 0;
|
||||
|
||||
continue;
|
||||
} else {
|
||||
// This is a single-line string, which cannot have newlines!
|
||||
|
@ -283,7 +337,11 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
// all remaining chars. This will mask all other errors, but
|
||||
// it should make it easiest to debug; the file will be a giant
|
||||
// error starting from where the open quote appeared.
|
||||
return Err((MadeProgress, EString::EndlessSingle(state.pos()), state));
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EString::EndlessSingle(start_state.pos()),
|
||||
start_state,
|
||||
));
|
||||
}
|
||||
}
|
||||
b'\\' => {
|
||||
|
@ -301,7 +359,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
match bytes.next() {
|
||||
Some(b'(') => {
|
||||
// Advance past the `\(` before using the expr parser
|
||||
state = advance_state!(state, 2)?;
|
||||
state.advance_mut(2);
|
||||
|
||||
let original_byte_count = state.bytes().len();
|
||||
|
||||
|
@ -328,7 +386,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
}
|
||||
Some(b'u') => {
|
||||
// Advance past the `\u` before using the expr parser
|
||||
state = advance_state!(state, 2)?;
|
||||
state.advance_mut(2);
|
||||
|
||||
let original_byte_count = state.bytes().len();
|
||||
|
||||
|
@ -386,11 +444,11 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
|||
Err((
|
||||
MadeProgress,
|
||||
if is_multiline {
|
||||
EString::EndlessMulti(state.pos())
|
||||
EString::EndlessMulti(start_state.pos())
|
||||
} else {
|
||||
EString::EndlessSingle(state.pos())
|
||||
EString::EndlessSingle(start_state.pos())
|
||||
},
|
||||
state,
|
||||
start_state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
Index(2147483649),
|
||||
Index(2147483650),
|
||||
],
|
||||
regions: [
|
||||
@0-22,
|
||||
@23-49,
|
||||
@50-92,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
Slice(start = 1, length = 1),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 1, length = 0),
|
||||
Slice(start = 2, length = 0),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
Newline,
|
||||
],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-1 Identifier(
|
||||
"a",
|
||||
),
|
||||
@4-22 Str(
|
||||
Line(
|
||||
[
|
||||
Plaintext(
|
||||
"Hello,",
|
||||
),
|
||||
EscapedChar(
|
||||
Newline,
|
||||
),
|
||||
EscapedChar(
|
||||
Newline,
|
||||
),
|
||||
Plaintext(
|
||||
"World!",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Body(
|
||||
@23-24 Identifier(
|
||||
"b",
|
||||
),
|
||||
@27-49 Str(
|
||||
Block(
|
||||
[
|
||||
[
|
||||
Plaintext(
|
||||
"Hello,",
|
||||
),
|
||||
EscapedChar(
|
||||
Newline,
|
||||
),
|
||||
EscapedChar(
|
||||
Newline,
|
||||
),
|
||||
Plaintext(
|
||||
"World!",
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Body(
|
||||
@50-51 Identifier(
|
||||
"c",
|
||||
),
|
||||
@58-92 SpaceBefore(
|
||||
Str(
|
||||
Block(
|
||||
[
|
||||
[
|
||||
Plaintext(
|
||||
"Hello,\n",
|
||||
),
|
||||
Plaintext(
|
||||
"\n",
|
||||
),
|
||||
Plaintext(
|
||||
"World!",
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@93-95 SpaceBefore(
|
||||
Num(
|
||||
"42",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
)
|
|
@ -0,0 +1,9 @@
|
|||
a = "Hello,\n\nWorld!"
|
||||
b = """Hello,\n\nWorld!"""
|
||||
c =
|
||||
"""
|
||||
Hello,
|
||||
|
||||
World!
|
||||
"""
|
||||
42
|
|
@ -204,6 +204,7 @@ mod test_parse {
|
|||
pass/not_docs.expr,
|
||||
pass/number_literal_suffixes.expr,
|
||||
pass/one_backpassing.expr,
|
||||
pass/multiline_string.expr,
|
||||
pass/one_char_string.expr,
|
||||
pass/one_def.expr,
|
||||
pass/one_minus_two.expr,
|
||||
|
|
|
@ -16,6 +16,7 @@ roc_can = { path = "../can" }
|
|||
roc_derive_key = { path = "../derive_key" }
|
||||
roc_derive = { path = "../derive" }
|
||||
roc_problem = { path = "../problem" }
|
||||
roc_solve_problem = { path = "../solve_problem" }
|
||||
roc_unify = { path = "../unify" }
|
||||
roc_debug_flags = { path = "../debug_flags" }
|
||||
arrayvec = "0.7.2"
|
||||
|
|
|
@ -1,60 +1,28 @@
|
|||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_can::expr::PendingDerives;
|
||||
use roc_collections::VecMap;
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_solve_problem::{TypeError, UnderivableReason, Unfulfilled};
|
||||
use roc_types::subs::{instantiate_rigids, Content, FlatType, GetSubsSlice, Rank, Subs, Variable};
|
||||
use roc_types::types::{AliasKind, Category, ErrorType, PatternCategory};
|
||||
use roc_unify::unify::MustImplementConstraints;
|
||||
use roc_types::types::{AliasKind, Category, PatternCategory};
|
||||
use roc_unify::unify::{Env, MustImplementConstraints};
|
||||
use roc_unify::unify::{MustImplementAbility, Obligated};
|
||||
|
||||
use crate::solve::type_to_var;
|
||||
use crate::solve::{Aliases, Pools, TypeError};
|
||||
use crate::solve::{Aliases, Pools};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AbilityImplError {
|
||||
/// Promote this to an error that the type does not fully implement an ability
|
||||
IncompleteAbility,
|
||||
/// Promote this to a generic error that a type doesn't implement an ability
|
||||
DoesNotImplement,
|
||||
/// Promote this error to a `TypeError::BadExpr` from elsewhere
|
||||
BadExpr(Region, Category, Variable),
|
||||
/// Promote this error to a `TypeError::BadPattern` from elsewhere
|
||||
BadPattern(Region, PatternCategory, Variable),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum UnderivableReason {
|
||||
NotABuiltin,
|
||||
/// The surface type is not derivable
|
||||
SurfaceNotDerivable,
|
||||
/// A nested type is not derivable
|
||||
NestedNotDerivable(ErrorType),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Unfulfilled {
|
||||
/// Incomplete custom implementation for an ability by an opaque type.
|
||||
Incomplete {
|
||||
typ: Symbol,
|
||||
ability: Symbol,
|
||||
missing_members: Vec<Loc<Symbol>>,
|
||||
},
|
||||
/// Cannot derive implementation of an ability for a structural type.
|
||||
AdhocUnderivable {
|
||||
typ: ErrorType,
|
||||
ability: Symbol,
|
||||
reason: UnderivableReason,
|
||||
},
|
||||
/// Cannot derive implementation of an ability for an opaque type.
|
||||
OpaqueUnderivable {
|
||||
typ: ErrorType,
|
||||
ability: Symbol,
|
||||
opaque: Symbol,
|
||||
derive_region: Region,
|
||||
reason: UnderivableReason,
|
||||
},
|
||||
}
|
||||
|
||||
/// Indexes a requested deriving of an ability for an opaque type.
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub struct RequestedDeriveKey {
|
||||
|
@ -108,158 +76,124 @@ impl PendingDerivesTable {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DeferredObligations {
|
||||
/// Obligations, to be filled in during solving of a module.
|
||||
obligations: Vec<(MustImplementConstraints, AbilityImplError)>,
|
||||
/// Derives that module-defined opaques claim to have.
|
||||
pending_derives: PendingDerivesTable,
|
||||
/// Derives that are claimed, but have also been determined to have
|
||||
/// specializations. Maps to the first member specialization of the same
|
||||
/// ability.
|
||||
dominated_derives: VecMap<RequestedDeriveKey, Region>,
|
||||
type ObligationResult = Result<(), Unfulfilled>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ObligationCache {
|
||||
impl_cache: VecMap<ImplKey, ObligationResult>,
|
||||
derive_cache: VecMap<RequestedDeriveKey, ObligationResult>,
|
||||
}
|
||||
|
||||
impl DeferredObligations {
|
||||
pub fn new(pending_derives: PendingDerivesTable) -> Self {
|
||||
Self {
|
||||
obligations: Default::default(),
|
||||
pending_derives,
|
||||
dominated_derives: Default::default(),
|
||||
}
|
||||
}
|
||||
enum ReadCache {
|
||||
Impl,
|
||||
}
|
||||
|
||||
pub fn add(&mut self, must_implement: MustImplementConstraints, on_error: AbilityImplError) {
|
||||
self.obligations.push((must_implement, on_error));
|
||||
}
|
||||
pub struct CheckedDerives {
|
||||
pub legal_derives: Vec<RequestedDeriveKey>,
|
||||
pub problems: Vec<TypeError>,
|
||||
}
|
||||
|
||||
pub fn dominate(&mut self, key: RequestedDeriveKey, impl_region: Region) {
|
||||
// Only builtin abilities can be derived, and hence dominated.
|
||||
if self.pending_derives.0.contains_key(&key) && !self.dominated_derives.contains_key(&key) {
|
||||
self.dominated_derives.insert(key, impl_region);
|
||||
}
|
||||
}
|
||||
|
||||
// Rules for checking ability implementations:
|
||||
// - Ad-hoc derives for structural types are checked on-the-fly
|
||||
// - Opaque derives are registered as "pending" when we check a module
|
||||
// - Opaque derives are always checked and registered at the end to make sure opaque
|
||||
// specializations are found first
|
||||
// - If an opaque O both derives and specializes an ability A
|
||||
// - The specialization is recorded in the abilities store (this is done in solve/solve)
|
||||
// - The derive is checked, but will not be recorded in the abilities store (this is done here)
|
||||
// - Obligations for O to implement A will defer to whether the specialization is complete
|
||||
pub fn check_all(
|
||||
self,
|
||||
impl ObligationCache {
|
||||
#[must_use]
|
||||
pub fn check_derives(
|
||||
&mut self,
|
||||
subs: &mut Subs,
|
||||
abilities_store: &AbilitiesStore,
|
||||
) -> (Vec<TypeError>, Vec<RequestedDeriveKey>) {
|
||||
let mut problems = vec![];
|
||||
|
||||
let Self {
|
||||
obligations,
|
||||
pending_derives,
|
||||
dominated_derives,
|
||||
} = self;
|
||||
|
||||
let mut obligation_cache = ObligationCache {
|
||||
abilities_store,
|
||||
pending_derives: &pending_derives,
|
||||
dominated_derives: &dominated_derives,
|
||||
|
||||
impl_cache: VecMap::with_capacity(obligations.len()),
|
||||
derive_cache: VecMap::with_capacity(pending_derives.0.len()),
|
||||
};
|
||||
|
||||
pending_derives: PendingDerivesTable,
|
||||
) -> CheckedDerives {
|
||||
let mut legal_derives = Vec::with_capacity(pending_derives.0.len());
|
||||
let mut problems = vec![];
|
||||
|
||||
// First, check all derives.
|
||||
for (&derive_key, &(opaque_real_var, derive_region)) in pending_derives.0.iter() {
|
||||
obligation_cache.check_derive(subs, derive_key, opaque_real_var, derive_region);
|
||||
let result = obligation_cache.derive_cache.get(&derive_key).unwrap();
|
||||
self.check_derive(
|
||||
subs,
|
||||
abilities_store,
|
||||
derive_key,
|
||||
opaque_real_var,
|
||||
derive_region,
|
||||
);
|
||||
let result = self.derive_cache.get(&derive_key).unwrap();
|
||||
match result {
|
||||
Ok(()) => legal_derives.push(derive_key),
|
||||
Err(problem) => problems.push(TypeError::UnfulfilledAbility(problem.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
for (derive_key, impl_region) in dominated_derives.iter() {
|
||||
let derive_region = pending_derives.0.get(derive_key).unwrap().1;
|
||||
|
||||
problems.push(TypeError::DominatedDerive {
|
||||
opaque: derive_key.opaque,
|
||||
ability: derive_key.ability,
|
||||
derive_region,
|
||||
impl_region: *impl_region,
|
||||
});
|
||||
CheckedDerives {
|
||||
legal_derives,
|
||||
problems,
|
||||
}
|
||||
}
|
||||
|
||||
// Keep track of which types that have an incomplete ability were reported as part of
|
||||
// another type error (from an expression or pattern). If we reported an error for a type
|
||||
// that doesn't implement an ability in that context, we don't want to repeat the error
|
||||
// message.
|
||||
let mut reported_in_context = vec![];
|
||||
let mut incomplete_not_in_context = vec![];
|
||||
#[must_use]
|
||||
pub fn check_obligations(
|
||||
&mut self,
|
||||
subs: &mut Subs,
|
||||
abilities_store: &AbilitiesStore,
|
||||
must_implement: MustImplementConstraints,
|
||||
on_error: AbilityImplError,
|
||||
) -> Vec<TypeError> {
|
||||
let must_implement = must_implement.get_unique();
|
||||
|
||||
for (constraints, on_error) in obligations.into_iter() {
|
||||
let must_implement = constraints.get_unique();
|
||||
let mut get_unfulfilled = |must_implement: &[MustImplementAbility]| {
|
||||
must_implement
|
||||
.iter()
|
||||
.filter_map(|mia| {
|
||||
self.check_one(subs, abilities_store, *mia)
|
||||
.as_ref()
|
||||
.err()
|
||||
.cloned()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let mut get_unfulfilled = |must_implement: &[MustImplementAbility]| {
|
||||
must_implement
|
||||
.iter()
|
||||
.filter_map(|mia| {
|
||||
obligation_cache
|
||||
.check_one(subs, *mia)
|
||||
.as_ref()
|
||||
.err()
|
||||
.cloned()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let mut reported_in_context = VecSet::default();
|
||||
let mut incomplete_not_in_context = VecSet::default();
|
||||
let mut problems = vec![];
|
||||
|
||||
use AbilityImplError::*;
|
||||
match on_error {
|
||||
IncompleteAbility => {
|
||||
// These aren't attached to another type error, so if these must_implement
|
||||
// constraints aren't met, we'll emit a generic "this type doesn't implement an
|
||||
// ability" error message at the end. We only want to do this if it turns out
|
||||
// the "must implement" constraint indeed wasn't part of a more specific type
|
||||
// error.
|
||||
incomplete_not_in_context.extend(must_implement);
|
||||
use AbilityImplError::*;
|
||||
match on_error {
|
||||
DoesNotImplement => {
|
||||
// These aren't attached to another type error, so if these must_implement
|
||||
// constraints aren't met, we'll emit a generic "this type doesn't implement an
|
||||
// ability" error message at the end. We only want to do this if it turns out
|
||||
// the "must implement" constraint indeed wasn't part of a more specific type
|
||||
// error.
|
||||
incomplete_not_in_context.extend(must_implement);
|
||||
}
|
||||
BadExpr(region, category, var) => {
|
||||
let unfulfilled = get_unfulfilled(&must_implement);
|
||||
|
||||
if !unfulfilled.is_empty() {
|
||||
// Demote the bad variable that exposed this problem to an error, both so
|
||||
// that we have an ErrorType to report and so that codegen knows to deal
|
||||
// with the error later.
|
||||
let (error_type, _moar_ghosts_n_stuff) = subs.var_to_error_type(var);
|
||||
problems.push(TypeError::BadExprMissingAbility(
|
||||
region,
|
||||
category,
|
||||
error_type,
|
||||
unfulfilled,
|
||||
));
|
||||
reported_in_context.extend(must_implement);
|
||||
}
|
||||
BadExpr(region, category, var) => {
|
||||
let unfulfilled = get_unfulfilled(&must_implement);
|
||||
}
|
||||
BadPattern(region, category, var) => {
|
||||
let unfulfilled = get_unfulfilled(&must_implement);
|
||||
|
||||
if !unfulfilled.is_empty() {
|
||||
// Demote the bad variable that exposed this problem to an error, both so
|
||||
// that we have an ErrorType to report and so that codegen knows to deal
|
||||
// with the error later.
|
||||
let (error_type, _moar_ghosts_n_stuff) = subs.var_to_error_type(var);
|
||||
problems.push(TypeError::BadExprMissingAbility(
|
||||
region,
|
||||
category,
|
||||
error_type,
|
||||
unfulfilled,
|
||||
));
|
||||
reported_in_context.extend(must_implement);
|
||||
}
|
||||
}
|
||||
BadPattern(region, category, var) => {
|
||||
let unfulfilled = get_unfulfilled(&must_implement);
|
||||
|
||||
if !unfulfilled.is_empty() {
|
||||
// Demote the bad variable that exposed this problem to an error, both so
|
||||
// that we have an ErrorType to report and so that codegen knows to deal
|
||||
// with the error later.
|
||||
let (error_type, _moar_ghosts_n_stuff) = subs.var_to_error_type(var);
|
||||
problems.push(TypeError::BadPatternMissingAbility(
|
||||
region,
|
||||
category,
|
||||
error_type,
|
||||
unfulfilled,
|
||||
));
|
||||
reported_in_context.extend(must_implement);
|
||||
}
|
||||
if !unfulfilled.is_empty() {
|
||||
// Demote the bad variable that exposed this problem to an error, both so
|
||||
// that we have an ErrorType to report and so that codegen knows to deal
|
||||
// with the error later.
|
||||
let (error_type, _moar_ghosts_n_stuff) = subs.var_to_error_type(var);
|
||||
problems.push(TypeError::BadPatternMissingAbility(
|
||||
region,
|
||||
category,
|
||||
error_type,
|
||||
unfulfilled,
|
||||
));
|
||||
reported_in_context.extend(must_implement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,49 +201,59 @@ impl DeferredObligations {
|
|||
// Go through and attach generic "type does not implement ability" errors, if they were not
|
||||
// part of a larger context.
|
||||
for mia in incomplete_not_in_context.into_iter() {
|
||||
if let Err(unfulfilled) = obligation_cache.check_one(subs, mia) {
|
||||
if !reported_in_context.contains(&mia) {
|
||||
// If the obligation is already cached, we must have already reported it in another
|
||||
// context.
|
||||
if !self.has_cached(mia) && !reported_in_context.contains(&mia) {
|
||||
if let Err(unfulfilled) = self.check_one(subs, abilities_store, mia) {
|
||||
problems.push(TypeError::UnfulfilledAbility(unfulfilled.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(problems, legal_derives)
|
||||
problems
|
||||
}
|
||||
}
|
||||
|
||||
type ObligationResult = Result<(), Unfulfilled>;
|
||||
|
||||
struct ObligationCache<'a> {
|
||||
abilities_store: &'a AbilitiesStore,
|
||||
dominated_derives: &'a VecMap<RequestedDeriveKey, Region>,
|
||||
pending_derives: &'a PendingDerivesTable,
|
||||
|
||||
impl_cache: VecMap<ImplKey, ObligationResult>,
|
||||
derive_cache: VecMap<RequestedDeriveKey, ObligationResult>,
|
||||
}
|
||||
|
||||
enum ReadCache {
|
||||
Impl,
|
||||
Derive,
|
||||
}
|
||||
|
||||
impl ObligationCache<'_> {
|
||||
fn check_one(&mut self, subs: &mut Subs, mia: MustImplementAbility) -> ObligationResult {
|
||||
fn check_one(
|
||||
&mut self,
|
||||
subs: &mut Subs,
|
||||
abilities_store: &AbilitiesStore,
|
||||
mia: MustImplementAbility,
|
||||
) -> ObligationResult {
|
||||
let MustImplementAbility { typ, ability } = mia;
|
||||
|
||||
match typ {
|
||||
Obligated::Adhoc(var) => self.check_adhoc(subs, var, ability),
|
||||
Obligated::Opaque(opaque) => self.check_opaque_and_read(subs, opaque, ability).clone(),
|
||||
Obligated::Adhoc(var) => self.check_adhoc(subs, abilities_store, var, ability),
|
||||
Obligated::Opaque(opaque) => self
|
||||
.check_opaque_and_read(abilities_store, opaque, ability)
|
||||
.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_adhoc(&mut self, subs: &mut Subs, var: Variable, ability: Symbol) -> ObligationResult {
|
||||
fn has_cached(&self, mia: MustImplementAbility) -> bool {
|
||||
match mia.typ {
|
||||
Obligated::Opaque(opaque) => self.impl_cache.contains_key(&ImplKey {
|
||||
opaque,
|
||||
ability: mia.ability,
|
||||
}),
|
||||
Obligated::Adhoc(_) => {
|
||||
// ad-hoc obligations are never cached
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_adhoc(
|
||||
&mut self,
|
||||
subs: &mut Subs,
|
||||
abilities_store: &AbilitiesStore,
|
||||
var: Variable,
|
||||
ability: Symbol,
|
||||
) -> ObligationResult {
|
||||
// Not worth caching ad-hoc checks because variables are unlikely to be the same between
|
||||
// independent queries.
|
||||
|
||||
let opt_can_derive_builtin = match ability {
|
||||
Symbol::ENCODE_ENCODING => Some(self.can_derive_encoding(subs, var)),
|
||||
Symbol::ENCODE_ENCODING => Some(self.can_derive_encoding(subs, abilities_store, var)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
@ -340,73 +284,41 @@ impl ObligationCache<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_opaque(&mut self, subs: &mut Subs, opaque: Symbol, ability: Symbol) -> ReadCache {
|
||||
fn check_opaque(
|
||||
&mut self,
|
||||
abilities_store: &AbilitiesStore,
|
||||
opaque: Symbol,
|
||||
ability: Symbol,
|
||||
) -> ReadCache {
|
||||
let impl_key = ImplKey { opaque, ability };
|
||||
let derive_key = RequestedDeriveKey { opaque, ability };
|
||||
|
||||
match self.pending_derives.0.get(&derive_key) {
|
||||
Some(&(opaque_real_var, derive_region)) => {
|
||||
if self.dominated_derives.contains_key(&derive_key) {
|
||||
// We have a derive, but also a custom implementation. The custom
|
||||
// implementation takes priority because we'll use that for codegen.
|
||||
// We'll report an error for the conflict, and whether the derive is
|
||||
// legal will be checked out-of-band.
|
||||
self.check_impl(impl_key);
|
||||
ReadCache::Impl
|
||||
} else {
|
||||
// Only a derive
|
||||
self.check_derive(subs, derive_key, opaque_real_var, derive_region);
|
||||
ReadCache::Derive
|
||||
}
|
||||
}
|
||||
// Only an impl
|
||||
None => {
|
||||
self.check_impl(impl_key);
|
||||
ReadCache::Impl
|
||||
}
|
||||
}
|
||||
self.check_impl(abilities_store, impl_key);
|
||||
ReadCache::Impl
|
||||
}
|
||||
|
||||
fn check_opaque_and_read(
|
||||
&mut self,
|
||||
subs: &mut Subs,
|
||||
abilities_store: &AbilitiesStore,
|
||||
opaque: Symbol,
|
||||
ability: Symbol,
|
||||
) -> &ObligationResult {
|
||||
match self.check_opaque(subs, opaque, ability) {
|
||||
match self.check_opaque(abilities_store, opaque, ability) {
|
||||
ReadCache::Impl => self.impl_cache.get(&ImplKey { opaque, ability }).unwrap(),
|
||||
ReadCache::Derive => self
|
||||
.derive_cache
|
||||
.get(&RequestedDeriveKey { opaque, ability })
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl(&mut self, impl_key: ImplKey) {
|
||||
fn check_impl(&mut self, abilities_store: &AbilitiesStore, impl_key: ImplKey) {
|
||||
if self.impl_cache.get(&impl_key).is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ImplKey { opaque, ability } = impl_key;
|
||||
let has_declared_impl = abilities_store.has_declared_implementation(opaque, ability);
|
||||
|
||||
let members_of_ability = self.abilities_store.members_of_ability(ability).unwrap();
|
||||
let mut missing_members = Vec::new();
|
||||
for &member in members_of_ability {
|
||||
if self
|
||||
.abilities_store
|
||||
.get_implementation(member, opaque)
|
||||
.is_none()
|
||||
{
|
||||
let root_data = self.abilities_store.member_def(member).unwrap();
|
||||
missing_members.push(Loc::at(root_data.region, member));
|
||||
}
|
||||
}
|
||||
|
||||
let obligation_result = if !missing_members.is_empty() {
|
||||
Err(Unfulfilled::Incomplete {
|
||||
let obligation_result = if !has_declared_impl {
|
||||
Err(Unfulfilled::OpaqueDoesNotImplement {
|
||||
typ: opaque,
|
||||
ability,
|
||||
missing_members,
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -418,6 +330,7 @@ impl ObligationCache<'_> {
|
|||
fn check_derive(
|
||||
&mut self,
|
||||
subs: &mut Subs,
|
||||
abilities_store: &AbilitiesStore,
|
||||
derive_key: RequestedDeriveKey,
|
||||
opaque_real_var: Variable,
|
||||
derive_region: Region,
|
||||
|
@ -437,11 +350,6 @@ impl ObligationCache<'_> {
|
|||
ability: derive_key.ability,
|
||||
};
|
||||
let opt_specialization_result = self.impl_cache.insert(impl_key, fake_fulfilled.clone());
|
||||
let is_dominated = self.dominated_derives.contains_key(&derive_key);
|
||||
debug_assert!(
|
||||
opt_specialization_result.is_none() || is_dominated,
|
||||
"This derive also has a specialization but it's not marked as dominated!"
|
||||
);
|
||||
|
||||
let old_deriving = self.derive_cache.insert(derive_key, fake_fulfilled.clone());
|
||||
debug_assert!(
|
||||
|
@ -451,7 +359,8 @@ impl ObligationCache<'_> {
|
|||
|
||||
// Now we check whether the structural type behind the opaque is derivable, since that's
|
||||
// what we'll need to generate an implementation for during codegen.
|
||||
let real_var_result = self.check_adhoc(subs, opaque_real_var, derive_key.ability);
|
||||
let real_var_result =
|
||||
self.check_adhoc(subs, abilities_store, opaque_real_var, derive_key.ability);
|
||||
|
||||
let root_result = real_var_result.map_err(|err| match err {
|
||||
// Promote the failure, which should be related to a structural type not being
|
||||
|
@ -486,7 +395,12 @@ impl ObligationCache<'_> {
|
|||
// If we have a lot of these, consider using a visitor.
|
||||
// It will be very similar for most types (can't derive functions, can't derive unbound type
|
||||
// variables, can only derive opaques if they have an impl, etc).
|
||||
fn can_derive_encoding(&mut self, subs: &mut Subs, var: Variable) -> Result<(), Variable> {
|
||||
fn can_derive_encoding(
|
||||
&mut self,
|
||||
subs: &mut Subs,
|
||||
abilities_store: &AbilitiesStore,
|
||||
var: Variable,
|
||||
) -> Result<(), Variable> {
|
||||
let mut stack = vec![var];
|
||||
let mut seen_recursion_vars = vec![];
|
||||
|
||||
|
@ -584,7 +498,7 @@ impl ObligationCache<'_> {
|
|||
Alias(name, _, _, AliasKind::Opaque) => {
|
||||
let opaque = *name;
|
||||
if self
|
||||
.check_opaque_and_read(subs, opaque, Symbol::ENCODE_ENCODING)
|
||||
.check_opaque_and_read(abilities_store, opaque, Symbol::ENCODE_ENCODING)
|
||||
.is_err()
|
||||
{
|
||||
return Err(var);
|
||||
|
@ -659,10 +573,15 @@ pub fn resolve_ability_specialization(
|
|||
let signature_var = member_def.signature_var();
|
||||
|
||||
instantiate_rigids(subs, signature_var);
|
||||
let (_vars, must_implement_ability, _lambda_sets_to_specialize, _meta) =
|
||||
unify(subs, specialization_var, signature_var, Mode::EQ).expect_success(
|
||||
"If resolving a specialization, the specialization must be known to typecheck.",
|
||||
);
|
||||
let (_vars, must_implement_ability, _lambda_sets_to_specialize, _meta) = unify(
|
||||
&mut Env::new(subs),
|
||||
specialization_var,
|
||||
signature_var,
|
||||
Mode::EQ,
|
||||
)
|
||||
.expect_success(
|
||||
"If resolving a specialization, the specialization must be known to typecheck.",
|
||||
);
|
||||
|
||||
subs.rollback_to(snapshot);
|
||||
|
||||
|
@ -671,7 +590,12 @@ pub fn resolve_ability_specialization(
|
|||
|
||||
let resolved = match obligated {
|
||||
Obligated::Opaque(symbol) => {
|
||||
match abilities_store.get_implementation(ability_member, symbol)? {
|
||||
let impl_key = roc_can::abilities::ImplKey {
|
||||
opaque: symbol,
|
||||
ability_member,
|
||||
};
|
||||
|
||||
match abilities_store.get_implementation(impl_key)? {
|
||||
roc_types::types::MemberImpl::Impl(spec_symbol) => {
|
||||
Resolved::Specialization(*spec_symbol)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::solve::{self, Aliases};
|
||||
use roc_can::abilities::{AbilitiesStore, ImplKey, ResolvedImpl};
|
||||
use roc_can::abilities::{AbilitiesStore, ResolvedImpl};
|
||||
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
||||
use roc_can::expr::PendingDerives;
|
||||
use roc_can::module::{ExposedByModule, ResolvedImplementations, RigidVariables};
|
||||
|
@ -8,6 +8,7 @@ use roc_collections::VecMap;
|
|||
use roc_derive::SharedDerivedModule;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_types::subs::{Content, ExposedTypesStorageSubs, FlatType, StorageSubs, Subs, Variable};
|
||||
use roc_types::types::{Alias, MemberImpl};
|
||||
|
||||
|
@ -32,7 +33,7 @@ impl<T> Solved<T> {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct SolvedModule {
|
||||
pub problems: Vec<solve::TypeError>,
|
||||
pub problems: Vec<TypeError>,
|
||||
|
||||
/// all aliases and their definitions. this has to include non-exposed aliases
|
||||
/// because exposed aliases can depend on non-exposed ones)
|
||||
|
@ -64,12 +65,7 @@ pub fn run_solve(
|
|||
pending_derives: PendingDerives,
|
||||
exposed_by_module: &ExposedByModule,
|
||||
derived_module: SharedDerivedModule,
|
||||
) -> (
|
||||
Solved<Subs>,
|
||||
solve::Env,
|
||||
Vec<solve::TypeError>,
|
||||
AbilitiesStore,
|
||||
) {
|
||||
) -> (Solved<Subs>, solve::Env, Vec<TypeError>, AbilitiesStore) {
|
||||
for (var, name) in rigid_variables.named {
|
||||
subs.rigid_var(var, name);
|
||||
}
|
||||
|
@ -190,16 +186,14 @@ pub fn extract_module_owned_implementations(
|
|||
) -> ResolvedImplementations {
|
||||
abilities_store
|
||||
.iter_declared_implementations()
|
||||
.filter_map(|((member, typ), member_impl)| {
|
||||
.filter_map(|(impl_key, member_impl)| {
|
||||
// This module solved this specialization if either the member or the type comes from the
|
||||
// module.
|
||||
if member.module_id() != module_id && typ.module_id() != module_id {
|
||||
if impl_key.ability_member.module_id() != module_id
|
||||
&& impl_key.opaque.module_id() != module_id
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let impl_key = ImplKey {
|
||||
opaque: typ,
|
||||
ability_member: member,
|
||||
};
|
||||
|
||||
let resolved_impl = match member_impl {
|
||||
MemberImpl::Impl(impl_symbol) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ability::{
|
||||
resolve_ability_specialization, type_implementing_specialization, AbilityImplError,
|
||||
DeferredObligations, PendingDerivesTable, RequestedDeriveKey, Resolved, Unfulfilled,
|
||||
CheckedDerives, ObligationCache, PendingDerivesTable, Resolved,
|
||||
};
|
||||
use crate::module::Solved;
|
||||
use bumpalo::Bump;
|
||||
|
@ -21,6 +21,7 @@ use roc_module::ident::TagName;
|
|||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_problem::can::CycleEntry;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_types::subs::{
|
||||
self, get_member_lambda_sets_at_region, AliasVariables, Content, Descriptor, FlatType,
|
||||
GetSubsSlice, LambdaSet, Mark, OptVariable, Rank, RecordFields, Subs, SubsIndex, SubsSlice,
|
||||
|
@ -28,12 +29,12 @@ use roc_types::subs::{
|
|||
};
|
||||
use roc_types::types::Type::{self, *};
|
||||
use roc_types::types::{
|
||||
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, MemberImpl,
|
||||
OptAbleType, OptAbleVar, PatternCategory, Reason, TypeExtension, Uls,
|
||||
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, MemberImpl, OptAbleType,
|
||||
OptAbleVar, Reason, TypeExtension, Uls,
|
||||
};
|
||||
use roc_unify::unify::{
|
||||
unify, unify_introduced_ability_specialization, Mode, MustImplementConstraints, Obligated,
|
||||
SpecializationLsetCollector, Unified::*,
|
||||
unify, unify_introduced_ability_specialization, Env as UEnv, Mode, MustImplementConstraints,
|
||||
Obligated, SpecializationLsetCollector, Unified::*,
|
||||
};
|
||||
|
||||
// Type checking system adapted from Elm by Evan Czaplicki, BSD-3-Clause Licensed
|
||||
|
@ -86,32 +87,6 @@ use roc_unify::unify::{
|
|||
// Ranks are used to limit the number of type variables considered for generalization. Only those inside
|
||||
// of the let (so those used in inferring the type of `\x -> x`) are considered.
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TypeError {
|
||||
BadExpr(Region, Category, ErrorType, Expected<ErrorType>),
|
||||
BadPattern(Region, PatternCategory, ErrorType, PExpected<ErrorType>),
|
||||
CircularType(Region, Symbol, ErrorType),
|
||||
CircularDef(Vec<CycleEntry>),
|
||||
BadType(roc_types::types::Problem),
|
||||
UnexposedLookup(Symbol),
|
||||
UnfulfilledAbility(Unfulfilled),
|
||||
BadExprMissingAbility(Region, Category, ErrorType, Vec<Unfulfilled>),
|
||||
BadPatternMissingAbility(Region, PatternCategory, ErrorType, Vec<Unfulfilled>),
|
||||
Exhaustive(roc_exhaustive::Error),
|
||||
StructuralSpecialization {
|
||||
region: Region,
|
||||
typ: ErrorType,
|
||||
ability: Symbol,
|
||||
member: Symbol,
|
||||
},
|
||||
DominatedDerive {
|
||||
opaque: Symbol,
|
||||
ability: Symbol,
|
||||
derive_region: Region,
|
||||
impl_region: Region,
|
||||
},
|
||||
}
|
||||
|
||||
use roc_types::types::Alias;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -674,8 +649,14 @@ fn run_in_place(
|
|||
let rank = Rank::toplevel();
|
||||
let arena = Bump::new();
|
||||
|
||||
let mut obligation_cache = ObligationCache::default();
|
||||
|
||||
let pending_derives = PendingDerivesTable::new(subs, aliases, pending_derives);
|
||||
let mut deferred_obligations = DeferredObligations::new(pending_derives);
|
||||
let CheckedDerives {
|
||||
legal_derives: _,
|
||||
problems: derives_problems,
|
||||
} = obligation_cache.check_derives(subs, abilities_store, pending_derives);
|
||||
problems.extend(derives_problems);
|
||||
|
||||
// Because we don't know what ability specializations are available until the entire module is
|
||||
// solved, we must wait to solve unspecialized lambda sets then.
|
||||
|
@ -692,7 +673,7 @@ fn run_in_place(
|
|||
subs,
|
||||
constraint,
|
||||
abilities_store,
|
||||
&mut deferred_obligations,
|
||||
&mut obligation_cache,
|
||||
&mut deferred_uls_to_resolve,
|
||||
);
|
||||
|
||||
|
@ -708,11 +689,12 @@ fn run_in_place(
|
|||
&SolvePhase { abilities_store },
|
||||
exposed_by_module,
|
||||
);
|
||||
|
||||
deferred_obligations.add(new_must_implement, AbilityImplError::IncompleteAbility);
|
||||
|
||||
let (obligation_problems, _derived) = deferred_obligations.check_all(subs, abilities_store);
|
||||
problems.extend(obligation_problems);
|
||||
problems.extend(obligation_cache.check_obligations(
|
||||
subs,
|
||||
abilities_store,
|
||||
new_must_implement,
|
||||
AbilityImplError::DoesNotImplement,
|
||||
));
|
||||
|
||||
state.env
|
||||
}
|
||||
|
@ -765,7 +747,7 @@ fn solve(
|
|||
subs: &mut Subs,
|
||||
constraint: &Constraint,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
deferred_obligations: &mut DeferredObligations,
|
||||
obligation_cache: &mut ObligationCache,
|
||||
deferred_uls_to_resolve: &mut UlsOfVar,
|
||||
) -> State {
|
||||
let initial = Work::Constraint {
|
||||
|
@ -826,7 +808,6 @@ fn solve(
|
|||
rank,
|
||||
abilities_store,
|
||||
problems,
|
||||
deferred_obligations,
|
||||
deferred_uls_to_resolve,
|
||||
*symbol,
|
||||
*loc_var,
|
||||
|
@ -934,7 +915,6 @@ fn solve(
|
|||
rank,
|
||||
abilities_store,
|
||||
problems,
|
||||
deferred_obligations,
|
||||
deferred_uls_to_resolve,
|
||||
*symbol,
|
||||
*loc_var,
|
||||
|
@ -983,7 +963,7 @@ fn solve(
|
|||
let expectation = &constraints.expectations[expectation_index.index()];
|
||||
let expected = type_to_var(subs, rank, pools, aliases, expectation.get_type_ref());
|
||||
|
||||
match unify(subs, actual, expected, Mode::EQ) {
|
||||
match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) {
|
||||
Success {
|
||||
vars,
|
||||
must_implement_ability,
|
||||
|
@ -991,11 +971,15 @@ fn solve(
|
|||
extra_metadata: _,
|
||||
} => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
if !must_implement_ability.is_empty() {
|
||||
deferred_obligations.add(
|
||||
let new_problems = obligation_cache.check_obligations(
|
||||
subs,
|
||||
abilities_store,
|
||||
must_implement_ability,
|
||||
AbilityImplError::BadExpr(*region, category.clone(), actual),
|
||||
);
|
||||
problems.extend(new_problems);
|
||||
}
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
|
||||
|
@ -1037,7 +1021,7 @@ fn solve(
|
|||
);
|
||||
let target = *target;
|
||||
|
||||
match unify(subs, actual, target, Mode::EQ) {
|
||||
match unify(&mut UEnv::new(subs), actual, target, Mode::EQ) {
|
||||
Success {
|
||||
vars,
|
||||
// ERROR NOT REPORTED
|
||||
|
@ -1097,7 +1081,7 @@ fn solve(
|
|||
let expected =
|
||||
type_to_var(subs, rank, pools, aliases, expectation.get_type_ref());
|
||||
|
||||
match unify(subs, actual, expected, Mode::EQ) {
|
||||
match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) {
|
||||
Success {
|
||||
vars,
|
||||
must_implement_ability,
|
||||
|
@ -1105,8 +1089,11 @@ fn solve(
|
|||
extra_metadata: _,
|
||||
} => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
if !must_implement_ability.is_empty() {
|
||||
deferred_obligations.add(
|
||||
let new_problems = obligation_cache.check_obligations(
|
||||
subs,
|
||||
abilities_store,
|
||||
must_implement_ability,
|
||||
AbilityImplError::BadExpr(
|
||||
*region,
|
||||
|
@ -1114,6 +1101,7 @@ fn solve(
|
|||
actual,
|
||||
),
|
||||
);
|
||||
problems.extend(new_problems);
|
||||
}
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
|
||||
|
@ -1177,7 +1165,7 @@ fn solve(
|
|||
_ => Mode::EQ,
|
||||
};
|
||||
|
||||
match unify(subs, actual, expected, mode) {
|
||||
match unify(&mut UEnv::new(subs), actual, expected, mode) {
|
||||
Success {
|
||||
vars,
|
||||
must_implement_ability,
|
||||
|
@ -1185,11 +1173,15 @@ fn solve(
|
|||
extra_metadata: _,
|
||||
} => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
if !must_implement_ability.is_empty() {
|
||||
deferred_obligations.add(
|
||||
let new_problems = obligation_cache.check_obligations(
|
||||
subs,
|
||||
abilities_store,
|
||||
must_implement_ability,
|
||||
AbilityImplError::BadPattern(*region, category.clone(), actual),
|
||||
);
|
||||
problems.extend(new_problems);
|
||||
}
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
|
||||
|
@ -1342,7 +1334,7 @@ fn solve(
|
|||
);
|
||||
let includes = type_to_var(subs, rank, pools, aliases, &tag_ty);
|
||||
|
||||
match unify(subs, actual, includes, Mode::PRESENT) {
|
||||
match unify(&mut UEnv::new(subs), actual, includes, Mode::PRESENT) {
|
||||
Success {
|
||||
vars,
|
||||
must_implement_ability,
|
||||
|
@ -1350,8 +1342,11 @@ fn solve(
|
|||
extra_metadata: _,
|
||||
} => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
if !must_implement_ability.is_empty() {
|
||||
deferred_obligations.add(
|
||||
let new_problems = obligation_cache.check_obligations(
|
||||
subs,
|
||||
abilities_store,
|
||||
must_implement_ability,
|
||||
AbilityImplError::BadPattern(
|
||||
*region,
|
||||
|
@ -1359,6 +1354,7 @@ fn solve(
|
|||
actual,
|
||||
),
|
||||
);
|
||||
problems.extend(new_problems);
|
||||
}
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
|
||||
|
@ -1446,7 +1442,7 @@ fn solve(
|
|||
);
|
||||
|
||||
let snapshot = subs.snapshot();
|
||||
let outcome = unify(subs, real_var, branches_var, Mode::EQ);
|
||||
let outcome = unify(&mut UEnv::new(subs), real_var, branches_var, Mode::EQ);
|
||||
|
||||
let should_check_exhaustiveness;
|
||||
match outcome {
|
||||
|
@ -1460,8 +1456,12 @@ fn solve(
|
|||
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
deferred_obligations
|
||||
.add(must_implement_ability, AbilityImplError::IncompleteAbility);
|
||||
problems.extend(obligation_cache.check_obligations(
|
||||
subs,
|
||||
abilities_store,
|
||||
must_implement_ability,
|
||||
AbilityImplError::DoesNotImplement,
|
||||
));
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
|
||||
// Case 1: unify error types, but don't check exhaustiveness.
|
||||
|
@ -1477,7 +1477,7 @@ fn solve(
|
|||
// open_tag_union(subs, real_var);
|
||||
open_tag_union(subs, branches_var);
|
||||
let almost_eq = matches!(
|
||||
unify(subs, real_var, branches_var, Mode::EQ),
|
||||
unify(&mut UEnv::new(subs), real_var, branches_var, Mode::EQ),
|
||||
Success { .. }
|
||||
);
|
||||
|
||||
|
@ -1489,7 +1489,7 @@ fn solve(
|
|||
} else {
|
||||
// Case 4: incompatible types, report type error.
|
||||
// Re-run first failed unification to get the type diff.
|
||||
match unify(subs, real_var, branches_var, Mode::EQ) {
|
||||
match unify(&mut UEnv::new(subs), real_var, branches_var, Mode::EQ) {
|
||||
Failure(vars, actual_type, expected_type, _bad_impls) => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
|
@ -1697,7 +1697,6 @@ fn check_ability_specialization(
|
|||
rank: Rank,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
problems: &mut Vec<TypeError>,
|
||||
deferred_obligations: &mut DeferredObligations,
|
||||
deferred_uls_to_resolve: &mut UlsOfVar,
|
||||
symbol: Symbol,
|
||||
symbol_loc_var: Loc<Variable>,
|
||||
|
@ -1721,7 +1720,7 @@ fn check_ability_specialization(
|
|||
deep_copy_var_in(subs, Rank::toplevel(), pools, root_signature_var, arena);
|
||||
let snapshot = subs.snapshot();
|
||||
let unified = unify_introduced_ability_specialization(
|
||||
subs,
|
||||
&mut UEnv::new(subs),
|
||||
root_signature_var,
|
||||
symbol_loc_var.value,
|
||||
Mode::EQ,
|
||||
|
@ -1739,39 +1738,48 @@ fn check_ability_specialization(
|
|||
|
||||
match specialization_type {
|
||||
Some(Obligated::Opaque(opaque)) => {
|
||||
// This is a specialization for an opaque - that's allowed.
|
||||
// This is a specialization for an opaque - but is it the opaque the
|
||||
// specialization was claimed to be for?
|
||||
if opaque == impl_key.opaque {
|
||||
// It was! All is good.
|
||||
|
||||
subs.commit_snapshot(snapshot);
|
||||
introduce(subs, rank, pools, &vars);
|
||||
subs.commit_snapshot(snapshot);
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
let specialization_lambda_sets = specialization_lambda_sets
|
||||
.into_iter()
|
||||
.map(|((symbol, region), var)| {
|
||||
debug_assert_eq!(symbol, ability_member);
|
||||
(region, var)
|
||||
})
|
||||
.collect();
|
||||
let specialization_lambda_sets = specialization_lambda_sets
|
||||
.into_iter()
|
||||
.map(|((symbol, region), var)| {
|
||||
debug_assert_eq!(symbol, ability_member);
|
||||
(region, var)
|
||||
})
|
||||
.collect();
|
||||
|
||||
deferred_uls_to_resolve.union(other_lambda_sets_to_specialize);
|
||||
deferred_uls_to_resolve.union(other_lambda_sets_to_specialize);
|
||||
|
||||
let specialization_region = symbol_loc_var.region;
|
||||
let specialization =
|
||||
MemberSpecializationInfo::new(symbol, specialization_lambda_sets);
|
||||
let specialization =
|
||||
MemberSpecializationInfo::new(symbol, specialization_lambda_sets);
|
||||
|
||||
// Make sure we check that the opaque has specialized all members of the
|
||||
// ability, after we finish solving the module.
|
||||
deferred_obligations
|
||||
.add(must_implement_ability, AbilityImplError::IncompleteAbility);
|
||||
// This specialization dominates any derives that might be present.
|
||||
deferred_obligations.dominate(
|
||||
RequestedDeriveKey {
|
||||
opaque,
|
||||
ability: parent_ability,
|
||||
},
|
||||
specialization_region,
|
||||
);
|
||||
Ok(specialization)
|
||||
} else {
|
||||
// This def is not specialized for the claimed opaque type, that's an
|
||||
// error.
|
||||
|
||||
Ok(specialization)
|
||||
// Commit so that the bad signature and its error persists in subs.
|
||||
subs.commit_snapshot(snapshot);
|
||||
|
||||
let (_typ, _problems) = subs.var_to_error_type(symbol_loc_var.value);
|
||||
|
||||
let problem = TypeError::WrongSpecialization {
|
||||
region: symbol_loc_var.region,
|
||||
ability_member: impl_key.ability_member,
|
||||
expected_opaque: impl_key.opaque,
|
||||
found_opaque: opaque,
|
||||
};
|
||||
|
||||
problems.push(problem);
|
||||
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
Some(Obligated::Adhoc(var)) => {
|
||||
// This is a specialization of a structural type - never allowed.
|
||||
|
@ -1795,7 +1803,7 @@ fn check_ability_specialization(
|
|||
None => {
|
||||
// This can happen when every ability constriant on a type variable went
|
||||
// through only another type variable. That means this def is not specialized
|
||||
// for one concrete type - we won't admit this.
|
||||
// for one concrete type, and especially not our opaque - we won't admit this currently.
|
||||
|
||||
// Rollback the snapshot so we unlink the root signature with the specialization,
|
||||
// so we can have two separate error types.
|
||||
|
@ -1855,7 +1863,7 @@ fn check_ability_specialization(
|
|||
};
|
||||
|
||||
abilities_store
|
||||
.mark_implementation(impl_key.ability_member, impl_key.opaque, resolved_mark)
|
||||
.mark_implementation(impl_key, resolved_mark)
|
||||
.expect("marked as a custom implementation, but not recorded as such");
|
||||
}
|
||||
}
|
||||
|
@ -2230,7 +2238,8 @@ fn compact_lambda_set<P: Phase>(
|
|||
// 3. Unify `t_f1 ~ t_f2`.
|
||||
trace_compact!(3iter_start. subs, this_lambda_set, t_f1, t_f2);
|
||||
let (vars, new_must_implement_ability, new_lambda_sets_to_specialize, _meta) =
|
||||
unify(subs, t_f1, t_f2, Mode::EQ).expect_success("ambient functions don't unify");
|
||||
unify(&mut UEnv::new(subs), t_f1, t_f2, Mode::EQ)
|
||||
.expect_success("ambient functions don't unify");
|
||||
trace_compact!(3iter_end. subs, t_f1);
|
||||
|
||||
introduce(subs, target_rank, pools, &vars);
|
||||
|
@ -2310,8 +2319,12 @@ fn get_specialization_lambda_set_ambient_function<P: Phase>(
|
|||
let opaque_home = opaque.module_id();
|
||||
let external_specialized_lset =
|
||||
phase.with_module_abilities_store(opaque_home, |abilities_store| {
|
||||
let impl_key = roc_can::abilities::ImplKey {
|
||||
opaque,
|
||||
ability_member,
|
||||
};
|
||||
let opt_specialization =
|
||||
abilities_store.get_implementation(ability_member, opaque);
|
||||
abilities_store.get_implementation(impl_key);
|
||||
match (P::IS_LATE, opt_specialization) {
|
||||
(false, None) => {
|
||||
// doesn't specialize, we'll have reported an error for this
|
||||
|
|
|
@ -12,13 +12,16 @@ mod solve_expr {
|
|||
use crate::helpers::with_larger_debug_stack;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use roc_can::traverse::{find_ability_member_and_owning_type_at, find_type_at};
|
||||
use roc_can::{
|
||||
abilities::ImplKey,
|
||||
traverse::{find_ability_member_and_owning_type_at, find_type_at},
|
||||
};
|
||||
use roc_load::LoadedModule;
|
||||
use roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Region};
|
||||
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator};
|
||||
use roc_solve::solve::TypeError;
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_types::{
|
||||
pretty_print::{name_and_print_var, DebugPrint},
|
||||
types::MemberImpl,
|
||||
|
@ -367,12 +370,12 @@ mod solve_expr {
|
|||
}
|
||||
|
||||
let known_specializations = abilities_store.iter_declared_implementations().filter_map(
|
||||
|((member, typ), member_impl)| match member_impl {
|
||||
|(impl_key, member_impl)| match member_impl {
|
||||
MemberImpl::Impl(impl_symbol) => {
|
||||
let specialization = abilities_store.specialization_info(*impl_symbol).expect(
|
||||
"declared implementations should be resolved conclusively after solving",
|
||||
);
|
||||
Some((member, typ, specialization.clone()))
|
||||
Some((impl_key, specialization.clone()))
|
||||
}
|
||||
MemberImpl::Derived | MemberImpl::Error => None,
|
||||
},
|
||||
|
@ -381,13 +384,17 @@ mod solve_expr {
|
|||
use std::collections::HashSet;
|
||||
let pretty_specializations = known_specializations
|
||||
.into_iter()
|
||||
.map(|(member, typ, _)| {
|
||||
let member_data = abilities_store.member_def(member).unwrap();
|
||||
let member_str = member.as_str(&interns);
|
||||
.map(|(impl_key, _)| {
|
||||
let ImplKey {
|
||||
opaque,
|
||||
ability_member,
|
||||
} = impl_key;
|
||||
let member_data = abilities_store.member_def(ability_member).unwrap();
|
||||
let member_str = ability_member.as_str(&interns);
|
||||
let ability_str = member_data.parent_ability.as_str(&interns);
|
||||
(
|
||||
format!("{}:{}", ability_str, member_str),
|
||||
typ.as_str(&interns),
|
||||
opaque.as_str(&interns),
|
||||
)
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
@ -3444,7 +3451,7 @@ mod solve_expr {
|
|||
{ id1, id2 }
|
||||
"#
|
||||
),
|
||||
"{ id1 : q -> q, id2 : a -> a }",
|
||||
"{ id1 : q -> q, id2 : q1 -> q1 }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3959,7 +3966,7 @@ mod solve_expr {
|
|||
{ a, b }
|
||||
"#
|
||||
),
|
||||
"{ a : { x : I64, y : I64, z : Num c }, b : { blah : Str, x : I64, y : I64, z : Num a } }",
|
||||
"{ a : { x : I64, y : I64, z : Num c }, b : { blah : Str, x : I64, y : I64, z : Num c1 } }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3990,7 +3997,7 @@ mod solve_expr {
|
|||
{ a, b }
|
||||
"#
|
||||
),
|
||||
"{ a : { x : Num *, y : Float *, z : c }, b : { blah : Str, x : Num *, y : Float *, z : a } }",
|
||||
"{ a : { x : Num *, y : Float *, z : c }, b : { blah : Str, x : Num *, y : Float *, z : c1 } }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6150,7 +6157,7 @@ mod solve_expr {
|
|||
hashEq = \x, y -> hash x == hash y
|
||||
"#
|
||||
),
|
||||
"a, b -> Bool | a has Hash, b has Hash",
|
||||
"a, a1 -> Bool | a has Hash, a1 has Hash",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
15
crates/compiler/solve_problem/Cargo.toml
Normal file
15
crates/compiler/solve_problem/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "roc_solve_problem"
|
||||
version = "0.1.0"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
roc_collections = { path = "../collections" }
|
||||
roc_region = { path = "../region" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_types = { path = "../types" }
|
||||
roc_can = { path = "../can" }
|
||||
roc_problem = { path = "../problem" }
|
||||
roc_exhaustive = { path = "../exhaustive" }
|
61
crates/compiler/solve_problem/src/lib.rs
Normal file
61
crates/compiler/solve_problem/src/lib.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_problem::can::CycleEntry;
|
||||
use roc_region::all::Region;
|
||||
|
||||
use roc_types::types::{Category, ErrorType, PatternCategory};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TypeError {
|
||||
BadExpr(Region, Category, ErrorType, Expected<ErrorType>),
|
||||
BadPattern(Region, PatternCategory, ErrorType, PExpected<ErrorType>),
|
||||
CircularType(Region, Symbol, ErrorType),
|
||||
CircularDef(Vec<CycleEntry>),
|
||||
BadType(roc_types::types::Problem),
|
||||
UnexposedLookup(Symbol),
|
||||
UnfulfilledAbility(Unfulfilled),
|
||||
BadExprMissingAbility(Region, Category, ErrorType, Vec<Unfulfilled>),
|
||||
BadPatternMissingAbility(Region, PatternCategory, ErrorType, Vec<Unfulfilled>),
|
||||
Exhaustive(roc_exhaustive::Error),
|
||||
StructuralSpecialization {
|
||||
region: Region,
|
||||
typ: ErrorType,
|
||||
ability: Symbol,
|
||||
member: Symbol,
|
||||
},
|
||||
WrongSpecialization {
|
||||
region: Region,
|
||||
ability_member: Symbol,
|
||||
expected_opaque: Symbol,
|
||||
found_opaque: Symbol,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Unfulfilled {
|
||||
/// No claimed implementation of an ability for an opaque type.
|
||||
OpaqueDoesNotImplement { typ: Symbol, ability: Symbol },
|
||||
/// Cannot derive implementation of an ability for a structural type.
|
||||
AdhocUnderivable {
|
||||
typ: ErrorType,
|
||||
ability: Symbol,
|
||||
reason: UnderivableReason,
|
||||
},
|
||||
/// Cannot derive implementation of an ability for an opaque type.
|
||||
OpaqueUnderivable {
|
||||
typ: ErrorType,
|
||||
ability: Symbol,
|
||||
opaque: Symbol,
|
||||
derive_region: Region,
|
||||
reason: UnderivableReason,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum UnderivableReason {
|
||||
NotABuiltin,
|
||||
/// The surface type is not derivable
|
||||
SurfaceNotDerivable,
|
||||
/// A nested type is not derivable
|
||||
NestedNotDerivable(ErrorType),
|
||||
}
|
|
@ -62,7 +62,7 @@ fn roc_function<'a, 'b>(
|
|||
|
||||
assert!(errors.is_empty(), "Encountered errors:\n{}", errors);
|
||||
|
||||
run_roc_dylib!(arena.alloc(lib), main_fn_name, &Input, Output, errors)
|
||||
run_roc_dylib!(arena.alloc(lib), main_fn_name, &Input, Output)
|
||||
}
|
||||
|
||||
fn create_input_list() -> RocList<i64> {
|
||||
|
|
|
@ -265,6 +265,7 @@ fn encode() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
#[ignore = "running into weird let-generalization issue when a variable is only in output position, see #3660"]
|
||||
fn decode() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
|
|
@ -1107,13 +1107,6 @@ fn gen_rem_checked_div_by_zero_i64() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn gen_is_zero_i64() {
|
||||
assert_evals_to!("Num.isZero 0", true, bool);
|
||||
assert_evals_to!("Num.isZero 1", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_is_positive_i64() {
|
||||
|
@ -1147,14 +1140,19 @@ fn gen_is_negative_f64() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_is_zero_f64() {
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn gen_is_zero_i64() {
|
||||
assert_evals_to!("Num.isZero 0", true, bool);
|
||||
assert_evals_to!("Num.isZero 0_0", true, bool);
|
||||
assert_evals_to!("Num.isZero 0.0", true, bool);
|
||||
assert_evals_to!("Num.isZero 1", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_is_zero_f64() {
|
||||
assert_evals_to!("Num.isZero 0.0", true, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn gen_is_odd() {
|
||||
|
@ -2006,25 +2004,22 @@ fn shift_left_by() {
|
|||
fn shift_right_by() {
|
||||
// Sign Extended Right Shift
|
||||
|
||||
let is_wasm = cfg!(feature = "gen-wasm");
|
||||
let is_llvm_release_mode = cfg!(feature = "gen-llvm") && !cfg!(debug_assertions);
|
||||
|
||||
// FIXME (Brian) Something funny happening with 8-bit binary literals in tests
|
||||
if !is_wasm {
|
||||
assert_evals_to!(
|
||||
"Num.shiftRightBy 2 (Num.toI8 0b1100_0000u8)",
|
||||
0b1111_0000u8 as i8,
|
||||
i8
|
||||
);
|
||||
assert_evals_to!("Num.shiftRightBy 2 0b0100_0000i8", 0b0001_0000i8, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 1 0b1110_0000u8", 0b1111_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightBy 2 0b1100_0000u8", 0b1111_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightBy 12 0b0100_0000u8", 0b0000_0000u8, u8);
|
||||
assert_evals_to!(
|
||||
"Num.shiftRightBy 2 (Num.toI8 0b1100_0000u8)",
|
||||
0b1111_0000u8 as i8,
|
||||
i8
|
||||
);
|
||||
assert_evals_to!("Num.shiftRightBy 2 0b0100_0000i8", 0b0001_0000i8, i8);
|
||||
assert_evals_to!("Num.shiftRightBy 1 0b1110_0000u8", 0b1111_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightBy 2 0b1100_0000u8", 0b1111_0000u8, u8);
|
||||
assert_evals_to!("Num.shiftRightBy 12 0b0100_0000u8", 0b0000_0000u8, u8);
|
||||
|
||||
// LLVM in release mode returns 0 instead of -1 for some reason
|
||||
if !is_llvm_release_mode {
|
||||
assert_evals_to!("Num.shiftRightBy 12 0b1000_0000u8", 0b1111_1111u8, u8);
|
||||
}
|
||||
// LLVM in release mode returns 0 instead of -1 for some reason
|
||||
if !is_llvm_release_mode {
|
||||
assert_evals_to!("Num.shiftRightBy 12 0b1000_0000u8", 0b1111_1111u8, u8);
|
||||
}
|
||||
assert_evals_to!("Num.shiftRightBy 0 12", 12, i64);
|
||||
assert_evals_to!("Num.shiftRightBy 1 12", 6, i64);
|
||||
|
@ -3653,3 +3648,70 @@ fn promote_u128_number_layout() {
|
|||
u128
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn when_on_decimals() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when 42.42dec is
|
||||
42.42 -> 42
|
||||
0.05 -> 1
|
||||
3.14 -> 2
|
||||
_ -> 4
|
||||
"#
|
||||
),
|
||||
42,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when 42.42dec is
|
||||
0.05 -> 1
|
||||
3.14 -> 2
|
||||
_ -> 4
|
||||
"#
|
||||
),
|
||||
4,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn when_on_i128() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when 1701411834604692317316873037158841057i128 is
|
||||
1701411834604692317316873037158841057 -> 42
|
||||
32 -> 1
|
||||
64 -> 2
|
||||
_ -> 4
|
||||
"#
|
||||
),
|
||||
42,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn when_on_u128() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when 170141183460469231731687303715884105728u128 is
|
||||
170141183460469231731687303715884105728u128 -> 42
|
||||
32 -> 1
|
||||
64 -> 2
|
||||
_ -> 4
|
||||
"#
|
||||
),
|
||||
42,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
|
|
@ -102,30 +102,6 @@ fn fn_record() {
|
|||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { x: 15, y: 17, z: 19 }
|
||||
|
||||
rec.y
|
||||
"#
|
||||
),
|
||||
17,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { x: 15, y: 17, z: 19 }
|
||||
|
||||
rec.z
|
||||
"#
|
||||
),
|
||||
19,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
|
|
@ -1813,3 +1813,36 @@ fn llvm_wasm_str_layout_small() {
|
|||
[i32; 3]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn when_on_strings() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when "Deyr fé, deyja frændr" is
|
||||
"Deyr fé, deyja frændr" -> 42
|
||||
"deyr sjalfr it sama" -> 1
|
||||
"en orðstírr deyr aldregi" -> 2
|
||||
"hveim er sér góðan getr" -> 3
|
||||
_ -> 4
|
||||
"#
|
||||
),
|
||||
42,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when "Deyr fé, deyja frændr" is
|
||||
"deyr sjalfr it sama" -> 1
|
||||
"en orðstírr deyr aldregi" -> 2
|
||||
"hveim er sér góðan getr" -> 3
|
||||
_ -> 4
|
||||
"#
|
||||
),
|
||||
4,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1863,3 +1863,53 @@ fn error_type_in_tag_union_payload() {
|
|||
true // ignore type errors
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn issue_3653_recursion_pointer_in_naked_opaque() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Peano := [ Zero, Succ Peano ]
|
||||
|
||||
recurse = \@Peano peano ->
|
||||
when peano is
|
||||
Succ inner -> recurse inner
|
||||
_ -> {}
|
||||
|
||||
main =
|
||||
when recurse (@Peano Zero) is
|
||||
_ -> "we're back"
|
||||
"#
|
||||
),
|
||||
RocStr::from("we're back"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn issue_3653_recursion_pointer_in_naked_opaque_localized() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Peano := [ Zero, Succ Peano ]
|
||||
|
||||
recurse = \peano ->
|
||||
when peano is
|
||||
@Peano (Succ inner) -> recurse inner
|
||||
@Peano Zero -> {}
|
||||
|
||||
main =
|
||||
when recurse (@Peano Zero) is
|
||||
_ -> "we're back"
|
||||
"#
|
||||
),
|
||||
RocStr::from("we're back"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
|
|
@ -458,7 +458,16 @@ fn llvm_module_to_wasm_file(
|
|||
.unwrap();
|
||||
|
||||
if !output.stderr.is_empty() {
|
||||
panic!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let msg = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
if msg.contains("wasm-ld: error: unknown file type") {
|
||||
panic!(
|
||||
"{}\nThis can happen if multiple tests have the same input string",
|
||||
msg
|
||||
);
|
||||
} else {
|
||||
panic!("{}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(output.status.success(), "{:#?}", output);
|
||||
|
|
2
crates/compiler/test_gen/src/lib.rs
Normal file
2
crates/compiler/test_gen/src/lib.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
#[cfg(test)]
|
||||
pub mod helpers;
|
|
@ -1,7 +1,7 @@
|
|||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.317 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||
let List.385 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.317;
|
||||
ret List.385;
|
||||
|
||||
procedure Test.2 (Test.3):
|
||||
let Test.7 : {} = Struct {};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.317 : List [] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||
let List.385 : List [] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.317;
|
||||
ret List.385;
|
||||
|
||||
procedure Test.2 (Test.3):
|
||||
let Test.7 : {} = Struct {};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure List.6 (#Attr.2):
|
||||
let List.317 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.317;
|
||||
let List.385 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.385;
|
||||
|
||||
procedure Test.1 (Test.5):
|
||||
let Test.2 : I64 = 41i64;
|
||||
|
|
|
@ -7,8 +7,8 @@ procedure Dict.7 (Dict.96):
|
|||
ret Dict.101;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.317 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.317;
|
||||
let List.385 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.385;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.2 : List {[], []} = CallByName Dict.1;
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
procedure List.2 (List.82, List.83):
|
||||
let List.322 : U64 = CallByName List.6 List.82;
|
||||
let List.319 : Int1 = CallByName Num.22 List.83 List.322;
|
||||
if List.319 then
|
||||
let List.321 : {} = CallByName List.60 List.82 List.83;
|
||||
let List.320 : [C {}, C {}] = TagId(1) List.321;
|
||||
ret List.320;
|
||||
procedure List.2 (List.90, List.91):
|
||||
let List.390 : U64 = CallByName List.6 List.90;
|
||||
let List.387 : Int1 = CallByName Num.22 List.91 List.390;
|
||||
if List.387 then
|
||||
let List.389 : {} = CallByName List.66 List.90 List.91;
|
||||
let List.388 : [C {}, C {}] = TagId(1) List.389;
|
||||
ret List.388;
|
||||
else
|
||||
let List.318 : {} = Struct {};
|
||||
let List.317 : [C {}, C {}] = TagId(0) List.318;
|
||||
ret List.317;
|
||||
let List.386 : {} = Struct {};
|
||||
let List.385 : [C {}, C {}] = TagId(0) List.386;
|
||||
ret List.385;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.324 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.324;
|
||||
let List.392 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.392;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.323 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.323;
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.391 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.391;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
procedure List.4 (List.93, List.94):
|
||||
let List.319 : U64 = 1i64;
|
||||
let List.318 : List U8 = CallByName List.65 List.93 List.319;
|
||||
let List.317 : List U8 = CallByName List.66 List.318 List.94;
|
||||
ret List.317;
|
||||
procedure List.4 (List.101, List.102):
|
||||
let List.387 : U64 = 1i64;
|
||||
let List.386 : List U8 = CallByName List.70 List.101 List.387;
|
||||
let List.385 : List U8 = CallByName List.71 List.386 List.102;
|
||||
ret List.385;
|
||||
|
||||
procedure List.65 (#Attr.2, #Attr.3):
|
||||
let List.321 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.321;
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.389 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.389;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.320 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.320;
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.388 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.388;
|
||||
|
||||
procedure Test.23 (Test.24, Test.35, #Attr.12):
|
||||
let Test.22 : U8 = StructAtIndex 0 #Attr.12;
|
||||
|
|
|
@ -224,159 +224,159 @@ procedure Json.83 (Json.111, Json.112):
|
|||
else
|
||||
jump Json.166 Json.91;
|
||||
|
||||
procedure List.125 (List.126, List.127, #Attr.12):
|
||||
let List.124 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.366 : {List U8, U64} = CallByName Json.83 List.126 List.127;
|
||||
let List.365 : [C [], C {List U8, U64}] = TagId(1) List.366;
|
||||
ret List.365;
|
||||
procedure List.133 (List.134, List.135, #Attr.12):
|
||||
let List.132 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.434 : {List U8, U64} = CallByName Json.83 List.134 List.135;
|
||||
let List.433 : [C [], C {List U8, U64}] = TagId(1) List.434;
|
||||
ret List.433;
|
||||
|
||||
procedure List.125 (List.126, List.127, #Attr.12):
|
||||
let List.124 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.447 : {List U8, U64} = CallByName Json.83 List.126 List.127;
|
||||
let List.446 : [C [], C {List U8, U64}] = TagId(1) List.447;
|
||||
ret List.446;
|
||||
procedure List.133 (List.134, List.135, #Attr.12):
|
||||
let List.132 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.515 : {List U8, U64} = CallByName Json.83 List.134 List.135;
|
||||
let List.514 : [C [], C {List U8, U64}] = TagId(1) List.515;
|
||||
ret List.514;
|
||||
|
||||
procedure List.18 (List.122, List.123, List.124):
|
||||
let List.343 : {{}} = Struct {List.124};
|
||||
let List.337 : [C [], C {List U8, U64}] = CallByName List.63 List.122 List.123 List.343;
|
||||
let List.340 : U8 = 1i64;
|
||||
let List.341 : U8 = GetTagId List.337;
|
||||
let List.342 : Int1 = lowlevel Eq List.340 List.341;
|
||||
if List.342 then
|
||||
let List.129 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.337;
|
||||
inc List.129;
|
||||
dec List.337;
|
||||
ret List.129;
|
||||
procedure List.18 (List.130, List.131, List.132):
|
||||
let List.411 : {{}} = Struct {List.132};
|
||||
let List.405 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.411;
|
||||
let List.408 : U8 = 1i64;
|
||||
let List.409 : U8 = GetTagId List.405;
|
||||
let List.410 : Int1 = lowlevel Eq List.408 List.409;
|
||||
if List.410 then
|
||||
let List.137 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.405;
|
||||
inc List.137;
|
||||
dec List.405;
|
||||
ret List.137;
|
||||
else
|
||||
let List.130 : [] = UnionAtIndex (Id 0) (Index 0) List.337;
|
||||
dec List.337;
|
||||
let List.339 : {List U8, U64} = CallByName List.64 List.130;
|
||||
ret List.339;
|
||||
let List.138 : [] = UnionAtIndex (Id 0) (Index 0) List.405;
|
||||
dec List.405;
|
||||
let List.407 : {List U8, U64} = CallByName List.69 List.138;
|
||||
ret List.407;
|
||||
|
||||
procedure List.18 (List.122, List.123, List.124):
|
||||
let List.423 : {{}} = Struct {List.124};
|
||||
let List.417 : [C [], C {List U8, U64}] = CallByName List.63 List.122 List.123 List.423;
|
||||
let List.420 : U8 = 1i64;
|
||||
let List.421 : U8 = GetTagId List.417;
|
||||
let List.422 : Int1 = lowlevel Eq List.420 List.421;
|
||||
if List.422 then
|
||||
let List.129 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.417;
|
||||
inc List.129;
|
||||
dec List.417;
|
||||
ret List.129;
|
||||
procedure List.18 (List.130, List.131, List.132):
|
||||
let List.491 : {{}} = Struct {List.132};
|
||||
let List.485 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.491;
|
||||
let List.488 : U8 = 1i64;
|
||||
let List.489 : U8 = GetTagId List.485;
|
||||
let List.490 : Int1 = lowlevel Eq List.488 List.489;
|
||||
if List.490 then
|
||||
let List.137 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.485;
|
||||
inc List.137;
|
||||
dec List.485;
|
||||
ret List.137;
|
||||
else
|
||||
let List.130 : [] = UnionAtIndex (Id 0) (Index 0) List.417;
|
||||
dec List.417;
|
||||
let List.419 : {List U8, U64} = CallByName List.64 List.130;
|
||||
ret List.419;
|
||||
let List.138 : [] = UnionAtIndex (Id 0) (Index 0) List.485;
|
||||
dec List.485;
|
||||
let List.487 : {List U8, U64} = CallByName List.69 List.138;
|
||||
ret List.487;
|
||||
|
||||
procedure List.4 (List.93, List.94):
|
||||
let List.416 : U64 = 1i64;
|
||||
let List.415 : List U8 = CallByName List.65 List.93 List.416;
|
||||
let List.414 : List U8 = CallByName List.66 List.415 List.94;
|
||||
ret List.414;
|
||||
procedure List.4 (List.101, List.102):
|
||||
let List.484 : U64 = 1i64;
|
||||
let List.483 : List U8 = CallByName List.70 List.101 List.484;
|
||||
let List.482 : List U8 = CallByName List.71 List.483 List.102;
|
||||
ret List.482;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.317 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.317;
|
||||
let List.385 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.385;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.345 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.345;
|
||||
let List.413 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.413;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.426 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.426;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.364 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.364;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.445 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.445;
|
||||
|
||||
procedure List.63 (List.305, List.306, List.307):
|
||||
let List.350 : U64 = 0i64;
|
||||
let List.351 : U64 = CallByName List.6 List.305;
|
||||
let List.349 : [C [], C {List U8, U64}] = CallByName List.80 List.305 List.306 List.307 List.350 List.351;
|
||||
ret List.349;
|
||||
|
||||
procedure List.63 (List.305, List.306, List.307):
|
||||
let List.431 : U64 = 0i64;
|
||||
let List.432 : U64 = CallByName List.6 List.305;
|
||||
let List.430 : [C [], C {List U8, U64}] = CallByName List.80 List.305 List.306 List.307 List.431 List.432;
|
||||
ret List.430;
|
||||
|
||||
procedure List.64 (#Attr.2):
|
||||
let List.429 : {List U8, U64} = lowlevel Unreachable #Attr.2;
|
||||
ret List.429;
|
||||
|
||||
procedure List.65 (#Attr.2, #Attr.3):
|
||||
let List.428 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.428;
|
||||
let List.494 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.494;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.427 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.427;
|
||||
let List.432 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.432;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.513 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.513;
|
||||
|
||||
procedure List.69 (#Attr.2):
|
||||
let List.497 : {List U8, U64} = lowlevel Unreachable #Attr.2;
|
||||
ret List.497;
|
||||
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.496 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.496;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.495 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.495;
|
||||
|
||||
procedure List.75 (List.361, List.362, List.363):
|
||||
let List.418 : U64 = 0i64;
|
||||
let List.419 : U64 = CallByName List.6 List.361;
|
||||
let List.417 : [C [], C {List U8, U64}] = CallByName List.86 List.361 List.362 List.363 List.418 List.419;
|
||||
ret List.417;
|
||||
|
||||
procedure List.75 (List.361, List.362, List.363):
|
||||
let List.499 : U64 = 0i64;
|
||||
let List.500 : U64 = CallByName List.6 List.361;
|
||||
let List.498 : [C [], C {List U8, U64}] = CallByName List.86 List.361 List.362 List.363 List.499 List.500;
|
||||
ret List.498;
|
||||
|
||||
procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.425 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.425;
|
||||
let List.493 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.493;
|
||||
|
||||
procedure List.80 (List.380, List.381, List.382, List.383, List.384):
|
||||
joinpoint List.352 List.308 List.309 List.310 List.311 List.312:
|
||||
let List.354 : Int1 = CallByName Num.22 List.311 List.312;
|
||||
if List.354 then
|
||||
let List.363 : {Str, {Str}} = CallByName List.60 List.308 List.311;
|
||||
let List.355 : [C [], C {List U8, U64}] = CallByName List.125 List.309 List.363 List.310;
|
||||
let List.360 : U8 = 1i64;
|
||||
let List.361 : U8 = GetTagId List.355;
|
||||
let List.362 : Int1 = lowlevel Eq List.360 List.361;
|
||||
if List.362 then
|
||||
let List.313 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.355;
|
||||
inc List.313;
|
||||
dec List.355;
|
||||
let List.358 : U64 = 1i64;
|
||||
let List.357 : U64 = CallByName Num.19 List.311 List.358;
|
||||
jump List.352 List.308 List.313 List.310 List.357 List.312;
|
||||
procedure List.86 (List.448, List.449, List.450, List.451, List.452):
|
||||
joinpoint List.420 List.364 List.365 List.366 List.367 List.368:
|
||||
let List.422 : Int1 = CallByName Num.22 List.367 List.368;
|
||||
if List.422 then
|
||||
let List.431 : {Str, {Str}} = CallByName List.66 List.364 List.367;
|
||||
let List.423 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.431 List.366;
|
||||
let List.428 : U8 = 1i64;
|
||||
let List.429 : U8 = GetTagId List.423;
|
||||
let List.430 : Int1 = lowlevel Eq List.428 List.429;
|
||||
if List.430 then
|
||||
let List.369 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.423;
|
||||
inc List.369;
|
||||
dec List.423;
|
||||
let List.426 : U64 = 1i64;
|
||||
let List.425 : U64 = CallByName Num.19 List.367 List.426;
|
||||
jump List.420 List.364 List.369 List.366 List.425 List.368;
|
||||
else
|
||||
let List.314 : [] = UnionAtIndex (Id 0) (Index 0) List.355;
|
||||
dec List.355;
|
||||
let List.359 : [C [], C {List U8, U64}] = TagId(0) List.314;
|
||||
ret List.359;
|
||||
let List.370 : [] = UnionAtIndex (Id 0) (Index 0) List.423;
|
||||
dec List.423;
|
||||
let List.427 : [C [], C {List U8, U64}] = TagId(0) List.370;
|
||||
ret List.427;
|
||||
else
|
||||
let List.353 : [C [], C {List U8, U64}] = TagId(1) List.309;
|
||||
ret List.353;
|
||||
let List.421 : [C [], C {List U8, U64}] = TagId(1) List.365;
|
||||
ret List.421;
|
||||
in
|
||||
jump List.352 List.380 List.381 List.382 List.383 List.384;
|
||||
jump List.420 List.448 List.449 List.450 List.451 List.452;
|
||||
|
||||
procedure List.80 (List.461, List.462, List.463, List.464, List.465):
|
||||
joinpoint List.433 List.308 List.309 List.310 List.311 List.312:
|
||||
let List.435 : Int1 = CallByName Num.22 List.311 List.312;
|
||||
if List.435 then
|
||||
let List.444 : {Str, {Str}} = CallByName List.60 List.308 List.311;
|
||||
let List.436 : [C [], C {List U8, U64}] = CallByName List.125 List.309 List.444 List.310;
|
||||
let List.441 : U8 = 1i64;
|
||||
let List.442 : U8 = GetTagId List.436;
|
||||
let List.443 : Int1 = lowlevel Eq List.441 List.442;
|
||||
if List.443 then
|
||||
let List.313 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.436;
|
||||
inc List.313;
|
||||
dec List.436;
|
||||
let List.439 : U64 = 1i64;
|
||||
let List.438 : U64 = CallByName Num.19 List.311 List.439;
|
||||
jump List.433 List.308 List.313 List.310 List.438 List.312;
|
||||
procedure List.86 (List.529, List.530, List.531, List.532, List.533):
|
||||
joinpoint List.501 List.364 List.365 List.366 List.367 List.368:
|
||||
let List.503 : Int1 = CallByName Num.22 List.367 List.368;
|
||||
if List.503 then
|
||||
let List.512 : {Str, {Str}} = CallByName List.66 List.364 List.367;
|
||||
let List.504 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.512 List.366;
|
||||
let List.509 : U8 = 1i64;
|
||||
let List.510 : U8 = GetTagId List.504;
|
||||
let List.511 : Int1 = lowlevel Eq List.509 List.510;
|
||||
if List.511 then
|
||||
let List.369 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.504;
|
||||
inc List.369;
|
||||
dec List.504;
|
||||
let List.507 : U64 = 1i64;
|
||||
let List.506 : U64 = CallByName Num.19 List.367 List.507;
|
||||
jump List.501 List.364 List.369 List.366 List.506 List.368;
|
||||
else
|
||||
let List.314 : [] = UnionAtIndex (Id 0) (Index 0) List.436;
|
||||
dec List.436;
|
||||
let List.440 : [C [], C {List U8, U64}] = TagId(0) List.314;
|
||||
ret List.440;
|
||||
let List.370 : [] = UnionAtIndex (Id 0) (Index 0) List.504;
|
||||
dec List.504;
|
||||
let List.508 : [C [], C {List U8, U64}] = TagId(0) List.370;
|
||||
ret List.508;
|
||||
else
|
||||
let List.434 : [C [], C {List U8, U64}] = TagId(1) List.309;
|
||||
ret List.434;
|
||||
let List.502 : [C [], C {List U8, U64}] = TagId(1) List.365;
|
||||
ret List.502;
|
||||
in
|
||||
jump List.433 List.461 List.462 List.463 List.464 List.465;
|
||||
jump List.501 List.529 List.530 List.531 List.532 List.533;
|
||||
|
||||
procedure Num.123 (#Attr.2):
|
||||
let Num.283 : U8 = lowlevel NumIntCast #Attr.2;
|
||||
|
|
|
@ -129,95 +129,95 @@ procedure Json.83 (Json.114, Json.115):
|
|||
else
|
||||
jump Json.126 Json.91;
|
||||
|
||||
procedure List.125 (List.126, List.127, #Attr.12):
|
||||
let List.124 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.373 : {List U8, U64} = CallByName Json.83 List.126 List.127;
|
||||
let List.372 : [C [], C {List U8, U64}] = TagId(1) List.373;
|
||||
ret List.372;
|
||||
procedure List.133 (List.134, List.135, #Attr.12):
|
||||
let List.132 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.441 : {List U8, U64} = CallByName Json.83 List.134 List.135;
|
||||
let List.440 : [C [], C {List U8, U64}] = TagId(1) List.441;
|
||||
ret List.440;
|
||||
|
||||
procedure List.18 (List.122, List.123, List.124):
|
||||
let List.349 : {{}} = Struct {List.124};
|
||||
let List.343 : [C [], C {List U8, U64}] = CallByName List.63 List.122 List.123 List.349;
|
||||
let List.346 : U8 = 1i64;
|
||||
let List.347 : U8 = GetTagId List.343;
|
||||
let List.348 : Int1 = lowlevel Eq List.346 List.347;
|
||||
if List.348 then
|
||||
let List.129 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.343;
|
||||
inc List.129;
|
||||
dec List.343;
|
||||
ret List.129;
|
||||
procedure List.18 (List.130, List.131, List.132):
|
||||
let List.417 : {{}} = Struct {List.132};
|
||||
let List.411 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.417;
|
||||
let List.414 : U8 = 1i64;
|
||||
let List.415 : U8 = GetTagId List.411;
|
||||
let List.416 : Int1 = lowlevel Eq List.414 List.415;
|
||||
if List.416 then
|
||||
let List.137 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.411;
|
||||
inc List.137;
|
||||
dec List.411;
|
||||
ret List.137;
|
||||
else
|
||||
let List.130 : [] = UnionAtIndex (Id 0) (Index 0) List.343;
|
||||
dec List.343;
|
||||
let List.345 : {List U8, U64} = CallByName List.64 List.130;
|
||||
ret List.345;
|
||||
let List.138 : [] = UnionAtIndex (Id 0) (Index 0) List.411;
|
||||
dec List.411;
|
||||
let List.413 : {List U8, U64} = CallByName List.69 List.138;
|
||||
ret List.413;
|
||||
|
||||
procedure List.4 (List.93, List.94):
|
||||
let List.342 : U64 = 1i64;
|
||||
let List.341 : List U8 = CallByName List.65 List.93 List.342;
|
||||
let List.340 : List U8 = CallByName List.66 List.341 List.94;
|
||||
ret List.340;
|
||||
procedure List.4 (List.101, List.102):
|
||||
let List.410 : U64 = 1i64;
|
||||
let List.409 : List U8 = CallByName List.70 List.101 List.410;
|
||||
let List.408 : List U8 = CallByName List.71 List.409 List.102;
|
||||
ret List.408;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.317 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.317;
|
||||
let List.385 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.385;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.352 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.352;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.371 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.371;
|
||||
|
||||
procedure List.63 (List.305, List.306, List.307):
|
||||
let List.357 : U64 = 0i64;
|
||||
let List.358 : U64 = CallByName List.6 List.305;
|
||||
let List.356 : [C [], C {List U8, U64}] = CallByName List.80 List.305 List.306 List.307 List.357 List.358;
|
||||
ret List.356;
|
||||
|
||||
procedure List.64 (#Attr.2):
|
||||
let List.355 : {List U8, U64} = lowlevel Unreachable #Attr.2;
|
||||
ret List.355;
|
||||
|
||||
procedure List.65 (#Attr.2, #Attr.3):
|
||||
let List.354 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.354;
|
||||
let List.420 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.420;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.353 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.353;
|
||||
let List.439 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.439;
|
||||
|
||||
procedure List.69 (#Attr.2):
|
||||
let List.423 : {List U8, U64} = lowlevel Unreachable #Attr.2;
|
||||
ret List.423;
|
||||
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.422 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.422;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.421 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.421;
|
||||
|
||||
procedure List.75 (List.361, List.362, List.363):
|
||||
let List.425 : U64 = 0i64;
|
||||
let List.426 : U64 = CallByName List.6 List.361;
|
||||
let List.424 : [C [], C {List U8, U64}] = CallByName List.86 List.361 List.362 List.363 List.425 List.426;
|
||||
ret List.424;
|
||||
|
||||
procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.351 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.351;
|
||||
let List.419 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.419;
|
||||
|
||||
procedure List.80 (List.387, List.388, List.389, List.390, List.391):
|
||||
joinpoint List.359 List.308 List.309 List.310 List.311 List.312:
|
||||
let List.361 : Int1 = CallByName Num.22 List.311 List.312;
|
||||
if List.361 then
|
||||
let List.370 : {Str, {Str}} = CallByName List.60 List.308 List.311;
|
||||
let List.362 : [C [], C {List U8, U64}] = CallByName List.125 List.309 List.370 List.310;
|
||||
let List.367 : U8 = 1i64;
|
||||
let List.368 : U8 = GetTagId List.362;
|
||||
let List.369 : Int1 = lowlevel Eq List.367 List.368;
|
||||
if List.369 then
|
||||
let List.313 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.362;
|
||||
inc List.313;
|
||||
dec List.362;
|
||||
let List.365 : U64 = 1i64;
|
||||
let List.364 : U64 = CallByName Num.19 List.311 List.365;
|
||||
jump List.359 List.308 List.313 List.310 List.364 List.312;
|
||||
procedure List.86 (List.455, List.456, List.457, List.458, List.459):
|
||||
joinpoint List.427 List.364 List.365 List.366 List.367 List.368:
|
||||
let List.429 : Int1 = CallByName Num.22 List.367 List.368;
|
||||
if List.429 then
|
||||
let List.438 : {Str, {Str}} = CallByName List.66 List.364 List.367;
|
||||
let List.430 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.438 List.366;
|
||||
let List.435 : U8 = 1i64;
|
||||
let List.436 : U8 = GetTagId List.430;
|
||||
let List.437 : Int1 = lowlevel Eq List.435 List.436;
|
||||
if List.437 then
|
||||
let List.369 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.430;
|
||||
inc List.369;
|
||||
dec List.430;
|
||||
let List.433 : U64 = 1i64;
|
||||
let List.432 : U64 = CallByName Num.19 List.367 List.433;
|
||||
jump List.427 List.364 List.369 List.366 List.432 List.368;
|
||||
else
|
||||
let List.314 : [] = UnionAtIndex (Id 0) (Index 0) List.362;
|
||||
dec List.362;
|
||||
let List.366 : [C [], C {List U8, U64}] = TagId(0) List.314;
|
||||
ret List.366;
|
||||
let List.370 : [] = UnionAtIndex (Id 0) (Index 0) List.430;
|
||||
dec List.430;
|
||||
let List.434 : [C [], C {List U8, U64}] = TagId(0) List.370;
|
||||
ret List.434;
|
||||
else
|
||||
let List.360 : [C [], C {List U8, U64}] = TagId(1) List.309;
|
||||
ret List.360;
|
||||
let List.428 : [C [], C {List U8, U64}] = TagId(1) List.365;
|
||||
ret List.428;
|
||||
in
|
||||
jump List.359 List.387 List.388 List.389 List.390 List.391;
|
||||
jump List.427 List.455 List.456 List.457 List.458 List.459;
|
||||
|
||||
procedure Num.123 (#Attr.2):
|
||||
let Num.264 : U8 = lowlevel NumIntCast #Attr.2;
|
||||
|
|
|
@ -137,95 +137,95 @@ procedure Json.83 (Json.117, Json.118):
|
|||
else
|
||||
jump Json.129 Json.91;
|
||||
|
||||
procedure List.125 (List.126, List.127, #Attr.12):
|
||||
let List.124 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.373 : {List U8, U64} = CallByName Json.83 List.126 List.127;
|
||||
let List.372 : [C [], C {List U8, U64}] = TagId(1) List.373;
|
||||
ret List.372;
|
||||
procedure List.133 (List.134, List.135, #Attr.12):
|
||||
let List.132 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.441 : {List U8, U64} = CallByName Json.83 List.134 List.135;
|
||||
let List.440 : [C [], C {List U8, U64}] = TagId(1) List.441;
|
||||
ret List.440;
|
||||
|
||||
procedure List.18 (List.122, List.123, List.124):
|
||||
let List.349 : {{}} = Struct {List.124};
|
||||
let List.343 : [C [], C {List U8, U64}] = CallByName List.63 List.122 List.123 List.349;
|
||||
let List.346 : U8 = 1i64;
|
||||
let List.347 : U8 = GetTagId List.343;
|
||||
let List.348 : Int1 = lowlevel Eq List.346 List.347;
|
||||
if List.348 then
|
||||
let List.129 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.343;
|
||||
inc List.129;
|
||||
dec List.343;
|
||||
ret List.129;
|
||||
procedure List.18 (List.130, List.131, List.132):
|
||||
let List.417 : {{}} = Struct {List.132};
|
||||
let List.411 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.417;
|
||||
let List.414 : U8 = 1i64;
|
||||
let List.415 : U8 = GetTagId List.411;
|
||||
let List.416 : Int1 = lowlevel Eq List.414 List.415;
|
||||
if List.416 then
|
||||
let List.137 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.411;
|
||||
inc List.137;
|
||||
dec List.411;
|
||||
ret List.137;
|
||||
else
|
||||
let List.130 : [] = UnionAtIndex (Id 0) (Index 0) List.343;
|
||||
dec List.343;
|
||||
let List.345 : {List U8, U64} = CallByName List.64 List.130;
|
||||
ret List.345;
|
||||
let List.138 : [] = UnionAtIndex (Id 0) (Index 0) List.411;
|
||||
dec List.411;
|
||||
let List.413 : {List U8, U64} = CallByName List.69 List.138;
|
||||
ret List.413;
|
||||
|
||||
procedure List.4 (List.93, List.94):
|
||||
let List.342 : U64 = 1i64;
|
||||
let List.341 : List U8 = CallByName List.65 List.93 List.342;
|
||||
let List.340 : List U8 = CallByName List.66 List.341 List.94;
|
||||
ret List.340;
|
||||
procedure List.4 (List.101, List.102):
|
||||
let List.410 : U64 = 1i64;
|
||||
let List.409 : List U8 = CallByName List.70 List.101 List.410;
|
||||
let List.408 : List U8 = CallByName List.71 List.409 List.102;
|
||||
ret List.408;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.317 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.317;
|
||||
let List.385 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.385;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.352 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.352;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.371 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.371;
|
||||
|
||||
procedure List.63 (List.305, List.306, List.307):
|
||||
let List.357 : U64 = 0i64;
|
||||
let List.358 : U64 = CallByName List.6 List.305;
|
||||
let List.356 : [C [], C {List U8, U64}] = CallByName List.80 List.305 List.306 List.307 List.357 List.358;
|
||||
ret List.356;
|
||||
|
||||
procedure List.64 (#Attr.2):
|
||||
let List.355 : {List U8, U64} = lowlevel Unreachable #Attr.2;
|
||||
ret List.355;
|
||||
|
||||
procedure List.65 (#Attr.2, #Attr.3):
|
||||
let List.354 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.354;
|
||||
let List.420 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.420;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.353 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.353;
|
||||
let List.439 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.439;
|
||||
|
||||
procedure List.69 (#Attr.2):
|
||||
let List.423 : {List U8, U64} = lowlevel Unreachable #Attr.2;
|
||||
ret List.423;
|
||||
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.422 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.422;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.421 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.421;
|
||||
|
||||
procedure List.75 (List.361, List.362, List.363):
|
||||
let List.425 : U64 = 0i64;
|
||||
let List.426 : U64 = CallByName List.6 List.361;
|
||||
let List.424 : [C [], C {List U8, U64}] = CallByName List.86 List.361 List.362 List.363 List.425 List.426;
|
||||
ret List.424;
|
||||
|
||||
procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.351 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.351;
|
||||
let List.419 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.419;
|
||||
|
||||
procedure List.80 (List.387, List.388, List.389, List.390, List.391):
|
||||
joinpoint List.359 List.308 List.309 List.310 List.311 List.312:
|
||||
let List.361 : Int1 = CallByName Num.22 List.311 List.312;
|
||||
if List.361 then
|
||||
let List.370 : {Str, {Str}} = CallByName List.60 List.308 List.311;
|
||||
let List.362 : [C [], C {List U8, U64}] = CallByName List.125 List.309 List.370 List.310;
|
||||
let List.367 : U8 = 1i64;
|
||||
let List.368 : U8 = GetTagId List.362;
|
||||
let List.369 : Int1 = lowlevel Eq List.367 List.368;
|
||||
if List.369 then
|
||||
let List.313 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.362;
|
||||
inc List.313;
|
||||
dec List.362;
|
||||
let List.365 : U64 = 1i64;
|
||||
let List.364 : U64 = CallByName Num.19 List.311 List.365;
|
||||
jump List.359 List.308 List.313 List.310 List.364 List.312;
|
||||
procedure List.86 (List.455, List.456, List.457, List.458, List.459):
|
||||
joinpoint List.427 List.364 List.365 List.366 List.367 List.368:
|
||||
let List.429 : Int1 = CallByName Num.22 List.367 List.368;
|
||||
if List.429 then
|
||||
let List.438 : {Str, {Str}} = CallByName List.66 List.364 List.367;
|
||||
let List.430 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.438 List.366;
|
||||
let List.435 : U8 = 1i64;
|
||||
let List.436 : U8 = GetTagId List.430;
|
||||
let List.437 : Int1 = lowlevel Eq List.435 List.436;
|
||||
if List.437 then
|
||||
let List.369 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.430;
|
||||
inc List.369;
|
||||
dec List.430;
|
||||
let List.433 : U64 = 1i64;
|
||||
let List.432 : U64 = CallByName Num.19 List.367 List.433;
|
||||
jump List.427 List.364 List.369 List.366 List.432 List.368;
|
||||
else
|
||||
let List.314 : [] = UnionAtIndex (Id 0) (Index 0) List.362;
|
||||
dec List.362;
|
||||
let List.366 : [C [], C {List U8, U64}] = TagId(0) List.314;
|
||||
ret List.366;
|
||||
let List.370 : [] = UnionAtIndex (Id 0) (Index 0) List.430;
|
||||
dec List.430;
|
||||
let List.434 : [C [], C {List U8, U64}] = TagId(0) List.370;
|
||||
ret List.434;
|
||||
else
|
||||
let List.360 : [C [], C {List U8, U64}] = TagId(1) List.309;
|
||||
ret List.360;
|
||||
let List.428 : [C [], C {List U8, U64}] = TagId(1) List.365;
|
||||
ret List.428;
|
||||
in
|
||||
jump List.359 List.387 List.388 List.389 List.390 List.391;
|
||||
jump List.427 List.455 List.456 List.457 List.458 List.459;
|
||||
|
||||
procedure Num.123 (#Attr.2):
|
||||
let Num.264 : U8 = lowlevel NumIntCast #Attr.2;
|
||||
|
|
|
@ -34,27 +34,27 @@ procedure Json.65 (Json.66, Json.109, #Attr.12):
|
|||
let Json.111 : List U8 = CallByName List.4 Json.112 Json.113;
|
||||
ret Json.111;
|
||||
|
||||
procedure List.4 (List.93, List.94):
|
||||
let List.324 : U64 = 1i64;
|
||||
let List.323 : List U8 = CallByName List.65 List.93 List.324;
|
||||
let List.322 : List U8 = CallByName List.66 List.323 List.94;
|
||||
ret List.322;
|
||||
procedure List.4 (List.101, List.102):
|
||||
let List.392 : U64 = 1i64;
|
||||
let List.391 : List U8 = CallByName List.70 List.101 List.392;
|
||||
let List.390 : List U8 = CallByName List.71 List.391 List.102;
|
||||
ret List.390;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.317 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.317;
|
||||
let List.385 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.385;
|
||||
|
||||
procedure List.65 (#Attr.2, #Attr.3):
|
||||
let List.327 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.327;
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.395 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.395;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.326 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.326;
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.394 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.394;
|
||||
|
||||
procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.325 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.325;
|
||||
let List.393 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.393;
|
||||
|
||||
procedure Num.123 (#Attr.2):
|
||||
let Num.258 : U8 = lowlevel NumIntCast #Attr.2;
|
||||
|
|
|
@ -134,95 +134,95 @@ procedure Json.97 (Json.114, Json.103):
|
|||
else
|
||||
jump Json.128 Json.104;
|
||||
|
||||
procedure List.125 (List.126, List.127, #Attr.12):
|
||||
let List.124 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.379 : {List U8, U64} = CallByName Json.97 List.126 List.127;
|
||||
let List.378 : [C [], C {List U8, U64}] = TagId(1) List.379;
|
||||
ret List.378;
|
||||
procedure List.133 (List.134, List.135, #Attr.12):
|
||||
let List.132 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.447 : {List U8, U64} = CallByName Json.97 List.134 List.135;
|
||||
let List.446 : [C [], C {List U8, U64}] = TagId(1) List.447;
|
||||
ret List.446;
|
||||
|
||||
procedure List.18 (List.122, List.123, List.124):
|
||||
let List.355 : {{}} = Struct {List.124};
|
||||
let List.349 : [C [], C {List U8, U64}] = CallByName List.63 List.122 List.123 List.355;
|
||||
let List.352 : U8 = 1i64;
|
||||
let List.353 : U8 = GetTagId List.349;
|
||||
let List.354 : Int1 = lowlevel Eq List.352 List.353;
|
||||
if List.354 then
|
||||
let List.129 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.349;
|
||||
inc List.129;
|
||||
dec List.349;
|
||||
ret List.129;
|
||||
procedure List.18 (List.130, List.131, List.132):
|
||||
let List.423 : {{}} = Struct {List.132};
|
||||
let List.417 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.423;
|
||||
let List.420 : U8 = 1i64;
|
||||
let List.421 : U8 = GetTagId List.417;
|
||||
let List.422 : Int1 = lowlevel Eq List.420 List.421;
|
||||
if List.422 then
|
||||
let List.137 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.417;
|
||||
inc List.137;
|
||||
dec List.417;
|
||||
ret List.137;
|
||||
else
|
||||
let List.130 : [] = UnionAtIndex (Id 0) (Index 0) List.349;
|
||||
dec List.349;
|
||||
let List.351 : {List U8, U64} = CallByName List.64 List.130;
|
||||
ret List.351;
|
||||
let List.138 : [] = UnionAtIndex (Id 0) (Index 0) List.417;
|
||||
dec List.417;
|
||||
let List.419 : {List U8, U64} = CallByName List.69 List.138;
|
||||
ret List.419;
|
||||
|
||||
procedure List.4 (List.93, List.94):
|
||||
let List.348 : U64 = 1i64;
|
||||
let List.347 : List U8 = CallByName List.65 List.93 List.348;
|
||||
let List.346 : List U8 = CallByName List.66 List.347 List.94;
|
||||
ret List.346;
|
||||
procedure List.4 (List.101, List.102):
|
||||
let List.416 : U64 = 1i64;
|
||||
let List.415 : List U8 = CallByName List.70 List.101 List.416;
|
||||
let List.414 : List U8 = CallByName List.71 List.415 List.102;
|
||||
ret List.414;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.317 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.317;
|
||||
let List.385 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.385;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.356 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.356;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.377 : {Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.377;
|
||||
|
||||
procedure List.63 (List.305, List.306, List.307):
|
||||
let List.363 : U64 = 0i64;
|
||||
let List.364 : U64 = CallByName List.6 List.305;
|
||||
let List.362 : [C [], C {List U8, U64}] = CallByName List.80 List.305 List.306 List.307 List.363 List.364;
|
||||
ret List.362;
|
||||
|
||||
procedure List.64 (#Attr.2):
|
||||
let List.361 : {List U8, U64} = lowlevel Unreachable #Attr.2;
|
||||
ret List.361;
|
||||
|
||||
procedure List.65 (#Attr.2, #Attr.3):
|
||||
let List.360 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.360;
|
||||
let List.424 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.424;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.359 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.359;
|
||||
let List.445 : {Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.445;
|
||||
|
||||
procedure List.69 (#Attr.2):
|
||||
let List.429 : {List U8, U64} = lowlevel Unreachable #Attr.2;
|
||||
ret List.429;
|
||||
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.428 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.428;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.427 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.427;
|
||||
|
||||
procedure List.75 (List.361, List.362, List.363):
|
||||
let List.431 : U64 = 0i64;
|
||||
let List.432 : U64 = CallByName List.6 List.361;
|
||||
let List.430 : [C [], C {List U8, U64}] = CallByName List.86 List.361 List.362 List.363 List.431 List.432;
|
||||
ret List.430;
|
||||
|
||||
procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.358 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.358;
|
||||
let List.426 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.426;
|
||||
|
||||
procedure List.80 (List.393, List.394, List.395, List.396, List.397):
|
||||
joinpoint List.365 List.308 List.309 List.310 List.311 List.312:
|
||||
let List.367 : Int1 = CallByName Num.22 List.311 List.312;
|
||||
if List.367 then
|
||||
let List.376 : {Str} = CallByName List.60 List.308 List.311;
|
||||
let List.368 : [C [], C {List U8, U64}] = CallByName List.125 List.309 List.376 List.310;
|
||||
let List.373 : U8 = 1i64;
|
||||
let List.374 : U8 = GetTagId List.368;
|
||||
let List.375 : Int1 = lowlevel Eq List.373 List.374;
|
||||
if List.375 then
|
||||
let List.313 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.368;
|
||||
inc List.313;
|
||||
dec List.368;
|
||||
let List.371 : U64 = 1i64;
|
||||
let List.370 : U64 = CallByName Num.19 List.311 List.371;
|
||||
jump List.365 List.308 List.313 List.310 List.370 List.312;
|
||||
procedure List.86 (List.461, List.462, List.463, List.464, List.465):
|
||||
joinpoint List.433 List.364 List.365 List.366 List.367 List.368:
|
||||
let List.435 : Int1 = CallByName Num.22 List.367 List.368;
|
||||
if List.435 then
|
||||
let List.444 : {Str} = CallByName List.66 List.364 List.367;
|
||||
let List.436 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.444 List.366;
|
||||
let List.441 : U8 = 1i64;
|
||||
let List.442 : U8 = GetTagId List.436;
|
||||
let List.443 : Int1 = lowlevel Eq List.441 List.442;
|
||||
if List.443 then
|
||||
let List.369 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.436;
|
||||
inc List.369;
|
||||
dec List.436;
|
||||
let List.439 : U64 = 1i64;
|
||||
let List.438 : U64 = CallByName Num.19 List.367 List.439;
|
||||
jump List.433 List.364 List.369 List.366 List.438 List.368;
|
||||
else
|
||||
let List.314 : [] = UnionAtIndex (Id 0) (Index 0) List.368;
|
||||
dec List.368;
|
||||
let List.372 : [C [], C {List U8, U64}] = TagId(0) List.314;
|
||||
ret List.372;
|
||||
let List.370 : [] = UnionAtIndex (Id 0) (Index 0) List.436;
|
||||
dec List.436;
|
||||
let List.440 : [C [], C {List U8, U64}] = TagId(0) List.370;
|
||||
ret List.440;
|
||||
else
|
||||
let List.366 : [C [], C {List U8, U64}] = TagId(1) List.309;
|
||||
ret List.366;
|
||||
let List.434 : [C [], C {List U8, U64}] = TagId(1) List.365;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.365 List.393 List.394 List.395 List.396 List.397;
|
||||
jump List.433 List.461 List.462 List.463 List.464 List.465;
|
||||
|
||||
procedure Num.123 (#Attr.2):
|
||||
let Num.266 : U8 = lowlevel NumIntCast #Attr.2;
|
||||
|
|
|
@ -140,95 +140,95 @@ procedure Json.97 (Json.117, Json.103):
|
|||
else
|
||||
jump Json.131 Json.104;
|
||||
|
||||
procedure List.125 (List.126, List.127, #Attr.12):
|
||||
let List.124 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.379 : {List U8, U64} = CallByName Json.97 List.126 List.127;
|
||||
let List.378 : [C [], C {List U8, U64}] = TagId(1) List.379;
|
||||
ret List.378;
|
||||
procedure List.133 (List.134, List.135, #Attr.12):
|
||||
let List.132 : {} = StructAtIndex 0 #Attr.12;
|
||||
let List.447 : {List U8, U64} = CallByName Json.97 List.134 List.135;
|
||||
let List.446 : [C [], C {List U8, U64}] = TagId(1) List.447;
|
||||
ret List.446;
|
||||
|
||||
procedure List.18 (List.122, List.123, List.124):
|
||||
let List.355 : {{}} = Struct {List.124};
|
||||
let List.349 : [C [], C {List U8, U64}] = CallByName List.63 List.122 List.123 List.355;
|
||||
let List.352 : U8 = 1i64;
|
||||
let List.353 : U8 = GetTagId List.349;
|
||||
let List.354 : Int1 = lowlevel Eq List.352 List.353;
|
||||
if List.354 then
|
||||
let List.129 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.349;
|
||||
inc List.129;
|
||||
dec List.349;
|
||||
ret List.129;
|
||||
procedure List.18 (List.130, List.131, List.132):
|
||||
let List.423 : {{}} = Struct {List.132};
|
||||
let List.417 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.423;
|
||||
let List.420 : U8 = 1i64;
|
||||
let List.421 : U8 = GetTagId List.417;
|
||||
let List.422 : Int1 = lowlevel Eq List.420 List.421;
|
||||
if List.422 then
|
||||
let List.137 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.417;
|
||||
inc List.137;
|
||||
dec List.417;
|
||||
ret List.137;
|
||||
else
|
||||
let List.130 : [] = UnionAtIndex (Id 0) (Index 0) List.349;
|
||||
dec List.349;
|
||||
let List.351 : {List U8, U64} = CallByName List.64 List.130;
|
||||
ret List.351;
|
||||
let List.138 : [] = UnionAtIndex (Id 0) (Index 0) List.417;
|
||||
dec List.417;
|
||||
let List.419 : {List U8, U64} = CallByName List.69 List.138;
|
||||
ret List.419;
|
||||
|
||||
procedure List.4 (List.93, List.94):
|
||||
let List.348 : U64 = 1i64;
|
||||
let List.347 : List U8 = CallByName List.65 List.93 List.348;
|
||||
let List.346 : List U8 = CallByName List.66 List.347 List.94;
|
||||
ret List.346;
|
||||
procedure List.4 (List.101, List.102):
|
||||
let List.416 : U64 = 1i64;
|
||||
let List.415 : List U8 = CallByName List.70 List.101 List.416;
|
||||
let List.414 : List U8 = CallByName List.71 List.415 List.102;
|
||||
ret List.414;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.317 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.317;
|
||||
let List.385 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.385;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.356 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.356;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.377 : {Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.377;
|
||||
|
||||
procedure List.63 (List.305, List.306, List.307):
|
||||
let List.363 : U64 = 0i64;
|
||||
let List.364 : U64 = CallByName List.6 List.305;
|
||||
let List.362 : [C [], C {List U8, U64}] = CallByName List.80 List.305 List.306 List.307 List.363 List.364;
|
||||
ret List.362;
|
||||
|
||||
procedure List.64 (#Attr.2):
|
||||
let List.361 : {List U8, U64} = lowlevel Unreachable #Attr.2;
|
||||
ret List.361;
|
||||
|
||||
procedure List.65 (#Attr.2, #Attr.3):
|
||||
let List.360 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.360;
|
||||
let List.424 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.424;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.359 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.359;
|
||||
let List.445 : {Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.445;
|
||||
|
||||
procedure List.69 (#Attr.2):
|
||||
let List.429 : {List U8, U64} = lowlevel Unreachable #Attr.2;
|
||||
ret List.429;
|
||||
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.428 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.428;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.427 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.427;
|
||||
|
||||
procedure List.75 (List.361, List.362, List.363):
|
||||
let List.431 : U64 = 0i64;
|
||||
let List.432 : U64 = CallByName List.6 List.361;
|
||||
let List.430 : [C [], C {List U8, U64}] = CallByName List.86 List.361 List.362 List.363 List.431 List.432;
|
||||
ret List.430;
|
||||
|
||||
procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.358 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.358;
|
||||
let List.426 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.426;
|
||||
|
||||
procedure List.80 (List.393, List.394, List.395, List.396, List.397):
|
||||
joinpoint List.365 List.308 List.309 List.310 List.311 List.312:
|
||||
let List.367 : Int1 = CallByName Num.22 List.311 List.312;
|
||||
if List.367 then
|
||||
let List.376 : {Str} = CallByName List.60 List.308 List.311;
|
||||
let List.368 : [C [], C {List U8, U64}] = CallByName List.125 List.309 List.376 List.310;
|
||||
let List.373 : U8 = 1i64;
|
||||
let List.374 : U8 = GetTagId List.368;
|
||||
let List.375 : Int1 = lowlevel Eq List.373 List.374;
|
||||
if List.375 then
|
||||
let List.313 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.368;
|
||||
inc List.313;
|
||||
dec List.368;
|
||||
let List.371 : U64 = 1i64;
|
||||
let List.370 : U64 = CallByName Num.19 List.311 List.371;
|
||||
jump List.365 List.308 List.313 List.310 List.370 List.312;
|
||||
procedure List.86 (List.461, List.462, List.463, List.464, List.465):
|
||||
joinpoint List.433 List.364 List.365 List.366 List.367 List.368:
|
||||
let List.435 : Int1 = CallByName Num.22 List.367 List.368;
|
||||
if List.435 then
|
||||
let List.444 : {Str} = CallByName List.66 List.364 List.367;
|
||||
let List.436 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.444 List.366;
|
||||
let List.441 : U8 = 1i64;
|
||||
let List.442 : U8 = GetTagId List.436;
|
||||
let List.443 : Int1 = lowlevel Eq List.441 List.442;
|
||||
if List.443 then
|
||||
let List.369 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.436;
|
||||
inc List.369;
|
||||
dec List.436;
|
||||
let List.439 : U64 = 1i64;
|
||||
let List.438 : U64 = CallByName Num.19 List.367 List.439;
|
||||
jump List.433 List.364 List.369 List.366 List.438 List.368;
|
||||
else
|
||||
let List.314 : [] = UnionAtIndex (Id 0) (Index 0) List.368;
|
||||
dec List.368;
|
||||
let List.372 : [C [], C {List U8, U64}] = TagId(0) List.314;
|
||||
ret List.372;
|
||||
let List.370 : [] = UnionAtIndex (Id 0) (Index 0) List.436;
|
||||
dec List.436;
|
||||
let List.440 : [C [], C {List U8, U64}] = TagId(0) List.370;
|
||||
ret List.440;
|
||||
else
|
||||
let List.366 : [C [], C {List U8, U64}] = TagId(1) List.309;
|
||||
ret List.366;
|
||||
let List.434 : [C [], C {List U8, U64}] = TagId(1) List.365;
|
||||
ret List.434;
|
||||
in
|
||||
jump List.365 List.393 List.394 List.395 List.396 List.397;
|
||||
jump List.433 List.461 List.462 List.463 List.464 List.465;
|
||||
|
||||
procedure Num.123 (#Attr.2):
|
||||
let Num.266 : U8 = lowlevel NumIntCast #Attr.2;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure List.6 (#Attr.2):
|
||||
let List.317 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.317;
|
||||
let List.385 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.385;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.259 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
|
|
|
@ -2,40 +2,40 @@ procedure Bool.7 (#Attr.2, #Attr.3):
|
|||
let Bool.9 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
ret Bool.9;
|
||||
|
||||
procedure List.2 (List.82, List.83):
|
||||
let List.331 : U64 = CallByName List.6 List.82;
|
||||
let List.327 : Int1 = CallByName Num.22 List.83 List.331;
|
||||
if List.327 then
|
||||
let List.329 : I64 = CallByName List.60 List.82 List.83;
|
||||
let List.328 : [C {}, C I64] = TagId(1) List.329;
|
||||
ret List.328;
|
||||
procedure List.2 (List.90, List.91):
|
||||
let List.399 : U64 = CallByName List.6 List.90;
|
||||
let List.395 : Int1 = CallByName Num.22 List.91 List.399;
|
||||
if List.395 then
|
||||
let List.397 : I64 = CallByName List.66 List.90 List.91;
|
||||
let List.396 : [C {}, C I64] = TagId(1) List.397;
|
||||
ret List.396;
|
||||
else
|
||||
let List.326 : {} = Struct {};
|
||||
let List.325 : [C {}, C I64] = TagId(0) List.326;
|
||||
ret List.325;
|
||||
let List.394 : {} = Struct {};
|
||||
let List.393 : [C {}, C I64] = TagId(0) List.394;
|
||||
ret List.393;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.332 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.332;
|
||||
let List.400 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.400;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.330 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.330;
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.398 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.398;
|
||||
|
||||
procedure List.9 (List.210):
|
||||
let List.324 : U64 = 0i64;
|
||||
let List.317 : [C {}, C I64] = CallByName List.2 List.210 List.324;
|
||||
let List.321 : U8 = 1i64;
|
||||
let List.322 : U8 = GetTagId List.317;
|
||||
let List.323 : Int1 = lowlevel Eq List.321 List.322;
|
||||
if List.323 then
|
||||
let List.211 : I64 = UnionAtIndex (Id 1) (Index 0) List.317;
|
||||
let List.318 : [C Int1, C I64] = TagId(1) List.211;
|
||||
ret List.318;
|
||||
procedure List.9 (List.218):
|
||||
let List.392 : U64 = 0i64;
|
||||
let List.385 : [C {}, C I64] = CallByName List.2 List.218 List.392;
|
||||
let List.389 : U8 = 1i64;
|
||||
let List.390 : U8 = GetTagId List.385;
|
||||
let List.391 : Int1 = lowlevel Eq List.389 List.390;
|
||||
if List.391 then
|
||||
let List.219 : I64 = UnionAtIndex (Id 1) (Index 0) List.385;
|
||||
let List.386 : [C Int1, C I64] = TagId(1) List.219;
|
||||
ret List.386;
|
||||
else
|
||||
let List.320 : Int1 = true;
|
||||
let List.319 : [C Int1, C I64] = TagId(0) List.320;
|
||||
ret List.319;
|
||||
let List.388 : Int1 = true;
|
||||
let List.387 : [C Int1, C I64] = TagId(0) List.388;
|
||||
ret List.387;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
procedure List.4 (List.93, List.94):
|
||||
let List.319 : U64 = 1i64;
|
||||
let List.318 : List I64 = CallByName List.65 List.93 List.319;
|
||||
let List.317 : List I64 = CallByName List.66 List.318 List.94;
|
||||
ret List.317;
|
||||
procedure List.4 (List.101, List.102):
|
||||
let List.387 : U64 = 1i64;
|
||||
let List.386 : List I64 = CallByName List.70 List.101 List.387;
|
||||
let List.385 : List I64 = CallByName List.71 List.386 List.102;
|
||||
ret List.385;
|
||||
|
||||
procedure List.65 (#Attr.2, #Attr.3):
|
||||
let List.321 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.321;
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.389 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.389;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.320 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.320;
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.388 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.388;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.2 : List I64 = Array [1i64];
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
procedure List.4 (List.93, List.94):
|
||||
let List.319 : U64 = 1i64;
|
||||
let List.318 : List I64 = CallByName List.65 List.93 List.319;
|
||||
let List.317 : List I64 = CallByName List.66 List.318 List.94;
|
||||
ret List.317;
|
||||
procedure List.4 (List.101, List.102):
|
||||
let List.387 : U64 = 1i64;
|
||||
let List.386 : List I64 = CallByName List.70 List.101 List.387;
|
||||
let List.385 : List I64 = CallByName List.71 List.386 List.102;
|
||||
ret List.385;
|
||||
|
||||
procedure List.65 (#Attr.2, #Attr.3):
|
||||
let List.321 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.321;
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.389 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.389;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.320 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.320;
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.388 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.388;
|
||||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.6 : I64 = 42i64;
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
procedure List.3 (List.90, List.91, List.92):
|
||||
let List.320 : {List I64, I64} = CallByName List.57 List.90 List.91 List.92;
|
||||
let List.319 : List I64 = StructAtIndex 0 List.320;
|
||||
inc List.319;
|
||||
dec List.320;
|
||||
ret List.319;
|
||||
|
||||
procedure List.57 (List.87, List.88, List.89):
|
||||
let List.325 : U64 = CallByName List.6 List.87;
|
||||
let List.322 : Int1 = CallByName Num.22 List.88 List.325;
|
||||
if List.322 then
|
||||
let List.323 : {List I64, I64} = CallByName List.61 List.87 List.88 List.89;
|
||||
ret List.323;
|
||||
else
|
||||
let List.321 : {List I64, I64} = Struct {List.87, List.89};
|
||||
ret List.321;
|
||||
procedure List.3 (List.98, List.99, List.100):
|
||||
let List.388 : {List I64, I64} = CallByName List.64 List.98 List.99 List.100;
|
||||
let List.387 : List I64 = StructAtIndex 0 List.388;
|
||||
inc List.387;
|
||||
dec List.388;
|
||||
ret List.387;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.318 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.318;
|
||||
let List.386 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.386;
|
||||
|
||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.324 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.324;
|
||||
procedure List.64 (List.95, List.96, List.97):
|
||||
let List.393 : U64 = CallByName List.6 List.95;
|
||||
let List.390 : Int1 = CallByName Num.22 List.96 List.393;
|
||||
if List.390 then
|
||||
let List.391 : {List I64, I64} = CallByName List.67 List.95 List.96 List.97;
|
||||
ret List.391;
|
||||
else
|
||||
let List.389 : {List I64, I64} = Struct {List.95, List.97};
|
||||
ret List.389;
|
||||
|
||||
procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.392 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.392;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.257 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
procedure List.2 (List.82, List.83):
|
||||
let List.322 : U64 = CallByName List.6 List.82;
|
||||
let List.319 : Int1 = CallByName Num.22 List.83 List.322;
|
||||
if List.319 then
|
||||
let List.321 : I64 = CallByName List.60 List.82 List.83;
|
||||
let List.320 : [C {}, C I64] = TagId(1) List.321;
|
||||
ret List.320;
|
||||
procedure List.2 (List.90, List.91):
|
||||
let List.390 : U64 = CallByName List.6 List.90;
|
||||
let List.387 : Int1 = CallByName Num.22 List.91 List.390;
|
||||
if List.387 then
|
||||
let List.389 : I64 = CallByName List.66 List.90 List.91;
|
||||
let List.388 : [C {}, C I64] = TagId(1) List.389;
|
||||
ret List.388;
|
||||
else
|
||||
let List.318 : {} = Struct {};
|
||||
let List.317 : [C {}, C I64] = TagId(0) List.318;
|
||||
ret List.317;
|
||||
let List.386 : {} = Struct {};
|
||||
let List.385 : [C {}, C I64] = TagId(0) List.386;
|
||||
ret List.385;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.324 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.324;
|
||||
let List.392 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.392;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.323 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.323;
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.391 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.391;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
procedure List.6 (#Attr.2):
|
||||
let List.317 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.317;
|
||||
let List.385 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.385;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.318 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.318;
|
||||
let List.386 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.386;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.257 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
procedure List.2 (List.82, List.83):
|
||||
let List.322 : U64 = CallByName List.6 List.82;
|
||||
let List.319 : Int1 = CallByName Num.22 List.83 List.322;
|
||||
if List.319 then
|
||||
let List.321 : Str = CallByName List.60 List.82 List.83;
|
||||
let List.320 : [C {}, C Str] = TagId(1) List.321;
|
||||
ret List.320;
|
||||
procedure List.2 (List.90, List.91):
|
||||
let List.390 : U64 = CallByName List.6 List.90;
|
||||
let List.387 : Int1 = CallByName Num.22 List.91 List.390;
|
||||
if List.387 then
|
||||
let List.389 : Str = CallByName List.66 List.90 List.91;
|
||||
let List.388 : [C {}, C Str] = TagId(1) List.389;
|
||||
ret List.388;
|
||||
else
|
||||
let List.318 : {} = Struct {};
|
||||
let List.317 : [C {}, C Str] = TagId(0) List.318;
|
||||
ret List.317;
|
||||
let List.386 : {} = Struct {};
|
||||
let List.385 : [C {}, C Str] = TagId(0) List.386;
|
||||
ret List.385;
|
||||
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.323 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||
ret List.323;
|
||||
let List.391 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||
ret List.391;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.325 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.325;
|
||||
let List.393 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.393;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.324 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.324;
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.392 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.392;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
procedure List.2 (List.82, List.83):
|
||||
let List.322 : U64 = CallByName List.6 List.82;
|
||||
let List.319 : Int1 = CallByName Num.22 List.83 List.322;
|
||||
if List.319 then
|
||||
let List.321 : Str = CallByName List.60 List.82 List.83;
|
||||
let List.320 : [C {}, C Str] = TagId(1) List.321;
|
||||
ret List.320;
|
||||
procedure List.2 (List.90, List.91):
|
||||
let List.390 : U64 = CallByName List.6 List.90;
|
||||
let List.387 : Int1 = CallByName Num.22 List.91 List.390;
|
||||
if List.387 then
|
||||
let List.389 : Str = CallByName List.66 List.90 List.91;
|
||||
let List.388 : [C {}, C Str] = TagId(1) List.389;
|
||||
ret List.388;
|
||||
else
|
||||
let List.318 : {} = Struct {};
|
||||
let List.317 : [C {}, C Str] = TagId(0) List.318;
|
||||
ret List.317;
|
||||
let List.386 : {} = Struct {};
|
||||
let List.385 : [C {}, C Str] = TagId(0) List.386;
|
||||
ret List.385;
|
||||
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
inc #Attr.2;
|
||||
let List.323 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||
let List.391 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.323;
|
||||
ret List.391;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.325 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.325;
|
||||
let List.393 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.393;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.324 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.324;
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.392 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.392;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
procedure List.3 (List.90, List.91, List.92):
|
||||
let List.318 : {List I64, I64} = CallByName List.57 List.90 List.91 List.92;
|
||||
let List.317 : List I64 = StructAtIndex 0 List.318;
|
||||
inc List.317;
|
||||
dec List.318;
|
||||
ret List.317;
|
||||
|
||||
procedure List.57 (List.87, List.88, List.89):
|
||||
let List.323 : U64 = CallByName List.6 List.87;
|
||||
let List.320 : Int1 = CallByName Num.22 List.88 List.323;
|
||||
if List.320 then
|
||||
let List.321 : {List I64, I64} = CallByName List.61 List.87 List.88 List.89;
|
||||
ret List.321;
|
||||
else
|
||||
let List.319 : {List I64, I64} = Struct {List.87, List.89};
|
||||
ret List.319;
|
||||
procedure List.3 (List.98, List.99, List.100):
|
||||
let List.386 : {List I64, I64} = CallByName List.64 List.98 List.99 List.100;
|
||||
let List.385 : List I64 = StructAtIndex 0 List.386;
|
||||
inc List.385;
|
||||
dec List.386;
|
||||
ret List.385;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.324 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.324;
|
||||
let List.392 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.392;
|
||||
|
||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.322 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.322;
|
||||
procedure List.64 (List.95, List.96, List.97):
|
||||
let List.391 : U64 = CallByName List.6 List.95;
|
||||
let List.388 : Int1 = CallByName Num.22 List.96 List.391;
|
||||
if List.388 then
|
||||
let List.389 : {List I64, I64} = CallByName List.67 List.95 List.96 List.97;
|
||||
ret List.389;
|
||||
else
|
||||
let List.387 : {List I64, I64} = Struct {List.95, List.97};
|
||||
ret List.387;
|
||||
|
||||
procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.390 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.390;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
procedure List.28 (#Attr.2, #Attr.3):
|
||||
let List.319 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
|
||||
let List.387 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
|
||||
let #Derived_gen.0 : Int1 = lowlevel ListIsUnique #Attr.2;
|
||||
if #Derived_gen.0 then
|
||||
ret List.319;
|
||||
ret List.387;
|
||||
else
|
||||
decref #Attr.2;
|
||||
ret List.319;
|
||||
ret List.387;
|
||||
|
||||
procedure List.54 (List.205):
|
||||
let List.318 : {} = Struct {};
|
||||
let List.317 : List I64 = CallByName List.28 List.205 List.318;
|
||||
ret List.317;
|
||||
procedure List.59 (List.213):
|
||||
let List.386 : {} = Struct {};
|
||||
let List.385 : List I64 = CallByName List.28 List.213 List.386;
|
||||
ret List.385;
|
||||
|
||||
procedure Num.46 (#Attr.2, #Attr.3):
|
||||
let Num.257 : U8 = lowlevel NumCompare #Attr.2 #Attr.3;
|
||||
|
@ -18,5 +18,5 @@ procedure Num.46 (#Attr.2, #Attr.3):
|
|||
|
||||
procedure Test.0 ():
|
||||
let Test.2 : List I64 = Array [4i64, 3i64, 2i64, 1i64];
|
||||
let Test.1 : List I64 = CallByName List.54 Test.2;
|
||||
let Test.1 : List I64 = CallByName List.59 Test.2;
|
||||
ret Test.1;
|
||||
|
|
|
@ -1,43 +1,43 @@
|
|||
procedure List.2 (List.82, List.83):
|
||||
let List.332 : U64 = CallByName List.6 List.82;
|
||||
let List.329 : Int1 = CallByName Num.22 List.83 List.332;
|
||||
if List.329 then
|
||||
let List.331 : I64 = CallByName List.60 List.82 List.83;
|
||||
let List.330 : [C {}, C I64] = TagId(1) List.331;
|
||||
ret List.330;
|
||||
procedure List.2 (List.90, List.91):
|
||||
let List.400 : U64 = CallByName List.6 List.90;
|
||||
let List.397 : Int1 = CallByName Num.22 List.91 List.400;
|
||||
if List.397 then
|
||||
let List.399 : I64 = CallByName List.66 List.90 List.91;
|
||||
let List.398 : [C {}, C I64] = TagId(1) List.399;
|
||||
ret List.398;
|
||||
else
|
||||
let List.328 : {} = Struct {};
|
||||
let List.327 : [C {}, C I64] = TagId(0) List.328;
|
||||
ret List.327;
|
||||
let List.396 : {} = Struct {};
|
||||
let List.395 : [C {}, C I64] = TagId(0) List.396;
|
||||
ret List.395;
|
||||
|
||||
procedure List.3 (List.90, List.91, List.92):
|
||||
let List.320 : {List I64, I64} = CallByName List.57 List.90 List.91 List.92;
|
||||
let List.319 : List I64 = StructAtIndex 0 List.320;
|
||||
inc List.319;
|
||||
dec List.320;
|
||||
ret List.319;
|
||||
|
||||
procedure List.57 (List.87, List.88, List.89):
|
||||
let List.337 : U64 = CallByName List.6 List.87;
|
||||
let List.334 : Int1 = CallByName Num.22 List.88 List.337;
|
||||
if List.334 then
|
||||
let List.335 : {List I64, I64} = CallByName List.61 List.87 List.88 List.89;
|
||||
ret List.335;
|
||||
else
|
||||
let List.333 : {List I64, I64} = Struct {List.87, List.89};
|
||||
ret List.333;
|
||||
procedure List.3 (List.98, List.99, List.100):
|
||||
let List.388 : {List I64, I64} = CallByName List.64 List.98 List.99 List.100;
|
||||
let List.387 : List I64 = StructAtIndex 0 List.388;
|
||||
inc List.387;
|
||||
dec List.388;
|
||||
ret List.387;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.338 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.338;
|
||||
let List.406 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.406;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.339 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.339;
|
||||
procedure List.64 (List.95, List.96, List.97):
|
||||
let List.405 : U64 = CallByName List.6 List.95;
|
||||
let List.402 : Int1 = CallByName Num.22 List.96 List.405;
|
||||
if List.402 then
|
||||
let List.403 : {List I64, I64} = CallByName List.67 List.95 List.96 List.97;
|
||||
ret List.403;
|
||||
else
|
||||
let List.401 : {List I64, I64} = Struct {List.95, List.97};
|
||||
ret List.401;
|
||||
|
||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.336 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.336;
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.407 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.407;
|
||||
|
||||
procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.404 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.404;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.259 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
|
|
|
@ -1,43 +1,43 @@
|
|||
procedure List.2 (List.82, List.83):
|
||||
let List.332 : U64 = CallByName List.6 List.82;
|
||||
let List.329 : Int1 = CallByName Num.22 List.83 List.332;
|
||||
if List.329 then
|
||||
let List.331 : I64 = CallByName List.60 List.82 List.83;
|
||||
let List.330 : [C {}, C I64] = TagId(1) List.331;
|
||||
ret List.330;
|
||||
procedure List.2 (List.90, List.91):
|
||||
let List.400 : U64 = CallByName List.6 List.90;
|
||||
let List.397 : Int1 = CallByName Num.22 List.91 List.400;
|
||||
if List.397 then
|
||||
let List.399 : I64 = CallByName List.66 List.90 List.91;
|
||||
let List.398 : [C {}, C I64] = TagId(1) List.399;
|
||||
ret List.398;
|
||||
else
|
||||
let List.328 : {} = Struct {};
|
||||
let List.327 : [C {}, C I64] = TagId(0) List.328;
|
||||
ret List.327;
|
||||
let List.396 : {} = Struct {};
|
||||
let List.395 : [C {}, C I64] = TagId(0) List.396;
|
||||
ret List.395;
|
||||
|
||||
procedure List.3 (List.90, List.91, List.92):
|
||||
let List.320 : {List I64, I64} = CallByName List.57 List.90 List.91 List.92;
|
||||
let List.319 : List I64 = StructAtIndex 0 List.320;
|
||||
inc List.319;
|
||||
dec List.320;
|
||||
ret List.319;
|
||||
|
||||
procedure List.57 (List.87, List.88, List.89):
|
||||
let List.337 : U64 = CallByName List.6 List.87;
|
||||
let List.334 : Int1 = CallByName Num.22 List.88 List.337;
|
||||
if List.334 then
|
||||
let List.335 : {List I64, I64} = CallByName List.61 List.87 List.88 List.89;
|
||||
ret List.335;
|
||||
else
|
||||
let List.333 : {List I64, I64} = Struct {List.87, List.89};
|
||||
ret List.333;
|
||||
procedure List.3 (List.98, List.99, List.100):
|
||||
let List.388 : {List I64, I64} = CallByName List.64 List.98 List.99 List.100;
|
||||
let List.387 : List I64 = StructAtIndex 0 List.388;
|
||||
inc List.387;
|
||||
dec List.388;
|
||||
ret List.387;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.338 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.338;
|
||||
let List.406 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.406;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.339 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.339;
|
||||
procedure List.64 (List.95, List.96, List.97):
|
||||
let List.405 : U64 = CallByName List.6 List.95;
|
||||
let List.402 : Int1 = CallByName Num.22 List.96 List.405;
|
||||
if List.402 then
|
||||
let List.403 : {List I64, I64} = CallByName List.67 List.95 List.96 List.97;
|
||||
ret List.403;
|
||||
else
|
||||
let List.401 : {List I64, I64} = Struct {List.95, List.97};
|
||||
ret List.401;
|
||||
|
||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.336 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.336;
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.407 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.407;
|
||||
|
||||
procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.404 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.404;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.259 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::subs::{
|
|||
self, AliasVariables, Content, FlatType, GetSubsSlice, Label, Subs, SubsIndex, UnionLabels,
|
||||
UnionTags, UnsortedUnionLabels, Variable,
|
||||
};
|
||||
use crate::types::{name_type_var, RecordField, Uls};
|
||||
use crate::types::{name_type_var, name_type_var_with_hint, RecordField, Uls};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
|
@ -455,10 +455,34 @@ fn name_root(
|
|||
subs: &mut Subs,
|
||||
taken: &mut MutMap<Lowercase, Variable>,
|
||||
) -> u32 {
|
||||
let (generated_name, new_letters_used) =
|
||||
name_type_var(letters_used, &mut taken.keys(), |var, str| {
|
||||
let (generated_name, new_letters_used) = match subs.get_content_unchecked(root) {
|
||||
Content::FlexVar(Some(name))
|
||||
| Content::RigidVar(name)
|
||||
| Content::FlexAbleVar(Some(name), _)
|
||||
| Content::RigidAbleVar(name, _)
|
||||
| Content::RecursionVar {
|
||||
opt_name: Some(name),
|
||||
..
|
||||
} => {
|
||||
let name_hint = &subs[*name];
|
||||
if name_hint.as_str() == "*" {
|
||||
// Give a proper name to named wildcards!
|
||||
name_type_var(letters_used, &mut taken.keys(), |var, str| {
|
||||
var.as_str() == str
|
||||
})
|
||||
} else {
|
||||
let generated =
|
||||
name_type_var_with_hint(name_hint.as_str(), &mut taken.keys(), |var, str| {
|
||||
var.as_str() == str
|
||||
});
|
||||
|
||||
(generated, letters_used)
|
||||
}
|
||||
}
|
||||
_ => name_type_var(letters_used, &mut taken.keys(), |var, str| {
|
||||
var.as_str() == str
|
||||
});
|
||||
}),
|
||||
};
|
||||
|
||||
taken.insert(generated_name.clone(), root);
|
||||
|
||||
|
|
|
@ -2566,6 +2566,7 @@ fn write_type_ext(ext: TypeExt, buf: &mut String) {
|
|||
|
||||
static THE_LETTER_A: u32 = 'a' as u32;
|
||||
|
||||
/// Generates a fresh type variable name, composed of lowercase alphabetic characters in sequence.
|
||||
pub fn name_type_var<I, F: FnMut(&I, &str) -> bool>(
|
||||
letters_used: u32,
|
||||
taken: &mut impl Iterator<Item = I>,
|
||||
|
@ -2596,6 +2597,28 @@ pub fn name_type_var<I, F: FnMut(&I, &str) -> bool>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Generates a fresh type variable name given a hint, composed of the hint as a prefix and a
|
||||
/// number as a suffix. For example, given hint `a` we'll name the variable `a`, `a1`, or `a27`.
|
||||
pub fn name_type_var_with_hint<I, F: FnMut(&I, &str) -> bool>(
|
||||
hint: &str,
|
||||
taken: &mut impl Iterator<Item = I>,
|
||||
mut predicate: F,
|
||||
) -> Lowercase {
|
||||
if !taken.any(|item| predicate(&item, hint)) {
|
||||
return hint.into();
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
loop {
|
||||
i += 1;
|
||||
let cand = format!("{}{}", hint, i);
|
||||
|
||||
if !taken.any(|item| predicate(&item, &cand)) {
|
||||
return cand.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RecordFieldsError;
|
||||
|
||||
|
@ -2638,6 +2661,9 @@ pub fn gather_fields_unsorted_iter(
|
|||
// TODO investigate apparently this one pops up in the reporting tests!
|
||||
RigidVar(_) => break,
|
||||
|
||||
// Stop on errors in the record
|
||||
Error => break,
|
||||
|
||||
_ => return Err(RecordFieldsError),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,3 +22,9 @@ path = "../types"
|
|||
|
||||
[dependencies.roc_debug_flags]
|
||||
path = "../debug_flags"
|
||||
|
||||
[dependencies.roc_can]
|
||||
path = "../can"
|
||||
|
||||
[dependencies.roc_solve_problem]
|
||||
path = "../solve_problem"
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue