Merge branch 'trunk' of github.com:rtfeldman/roc into build-nix

This commit is contained in:
Anton-4 2022-08-01 16:51:07 +02:00
commit 0b5b3a8652
No known key found for this signature in database
GPG key ID: A13F4A6E21141925
94 changed files with 4274 additions and 2474 deletions

View file

@ -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" }

View file

@ -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,

View file

@ -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"

View file

@ -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 {

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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>,

View file

@ -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_);
}

View file

@ -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)
}
}

View file

@ -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,

View file

@ -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);
}
}

View file

@ -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

View file

@ -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()
}
}

View file

@ -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
{

View file

@ -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;

View file

@ -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);

View file

@ -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]);

View file

@ -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 {

View file

@ -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" }

View file

@ -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 {

View file

@ -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" => {

View file

@ -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
}
}

View file

@ -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),
}

View file

@ -384,6 +384,7 @@ pub enum EString<'a> {
UnknownEscape(Position),
Format(&'a EExpr<'a>, Position),
FormatEnd(Position),
MultilineInsufficientIndent(Position),
}
#[derive(Debug, Clone, PartialEq, Eq)]

View file

@ -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> {

View file

@ -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,
))
}
}

View file

@ -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,
],
),
)

View file

@ -0,0 +1,9 @@
a = "Hello,\n\nWorld!"
b = """Hello,\n\nWorld!"""
c =
"""
Hello,
World!
"""
42

View file

@ -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,

View file

@ -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"

View file

@ -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)
}

View file

@ -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) => {

View file

@ -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

View file

@ -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",
)
}

View 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" }

View 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),
}

View file

@ -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> {

View file

@ -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!(

View file

@ -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
);
}

View file

@ -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#"

View file

@ -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
);
}

View file

@ -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
)
}

View file

@ -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);

View file

@ -0,0 +1,2 @@
#[cfg(test)]
pub mod helpers;

View file

@ -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 {};

View file

@ -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 {};

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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];

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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),
}
}

View file

@ -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