mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 06:55:15 +00:00
Merge remote-tracking branch 'origin/trunk' into str-walk
This commit is contained in:
commit
1de9270ecf
107 changed files with 10287 additions and 9870 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3980,6 +3980,7 @@ dependencies = [
|
|||
"bumpalo",
|
||||
"distance",
|
||||
"indoc",
|
||||
"insta",
|
||||
"pretty_assertions",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
|
|
|
@ -23,7 +23,7 @@ pub fn load_types(
|
|||
mut type_problems,
|
||||
mut declarations_by_id,
|
||||
mut solved,
|
||||
interns,
|
||||
mut interns,
|
||||
..
|
||||
} = roc_load::load_and_typecheck(
|
||||
arena,
|
||||
|
@ -74,7 +74,7 @@ pub fn load_types(
|
|||
let types_and_targets = Architecture::iter()
|
||||
.map(|arch| {
|
||||
let target_info = arch.into();
|
||||
let mut env = Env::new(arena, subs, &interns, target_info);
|
||||
let mut env = Env::new(arena, subs, &mut interns, target_info);
|
||||
|
||||
(env.vars_to_types(variables.clone()), target_info)
|
||||
})
|
||||
|
|
|
@ -419,7 +419,12 @@ pub struct Env<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Env<'a> {
|
||||
pub fn new(arena: &'a Bump, subs: &'a Subs, interns: &'a Interns, target: TargetInfo) -> Self {
|
||||
pub fn new(
|
||||
arena: &'a Bump,
|
||||
subs: &'a Subs,
|
||||
interns: &'a mut Interns,
|
||||
target: TargetInfo,
|
||||
) -> Self {
|
||||
Env {
|
||||
arena,
|
||||
subs,
|
||||
|
|
|
@ -12,7 +12,7 @@ use roc_mono::ir::{
|
|||
Call, CallType, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement, Literal,
|
||||
ModifyRc, OptLevel, Proc, Stmt,
|
||||
};
|
||||
use roc_mono::layout::{Builtin, Layout, RawFunctionLayout, UnionLayout};
|
||||
use roc_mono::layout::{Builtin, CapturesNiche, Layout, RawFunctionLayout, UnionLayout};
|
||||
|
||||
// just using one module for now
|
||||
pub const MOD_APP: ModName = ModName(b"UserApp");
|
||||
|
@ -23,7 +23,13 @@ pub const STATIC_LIST_NAME: ConstName = ConstName(b"THIS IS A STATIC LIST");
|
|||
const ENTRY_POINT_NAME: &[u8] = b"mainForHost";
|
||||
|
||||
pub fn func_name_bytes(proc: &Proc) -> [u8; SIZE] {
|
||||
func_name_bytes_help(proc.name, proc.args.iter().map(|x| x.0), &proc.ret_layout)
|
||||
let bytes = func_name_bytes_help(
|
||||
proc.name.name(),
|
||||
proc.args.iter().map(|x| x.0),
|
||||
proc.name.captures_niche(),
|
||||
&proc.ret_layout,
|
||||
);
|
||||
bytes
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -64,6 +70,7 @@ impl TagUnionId {
|
|||
pub fn func_name_bytes_help<'a, I>(
|
||||
symbol: Symbol,
|
||||
argument_layouts: I,
|
||||
captures_niche: CapturesNiche<'a>,
|
||||
return_layout: &Layout<'a>,
|
||||
) -> [u8; SIZE]
|
||||
where
|
||||
|
@ -82,6 +89,8 @@ where
|
|||
layout.hash(&mut hasher);
|
||||
}
|
||||
|
||||
captures_niche.hash(&mut hasher);
|
||||
|
||||
return_layout.hash(&mut hasher);
|
||||
|
||||
hasher.finish()
|
||||
|
@ -173,13 +182,22 @@ where
|
|||
match layout {
|
||||
RawFunctionLayout::Function(_, _, _) => {
|
||||
let it = top_level.arguments.iter().copied();
|
||||
let bytes = func_name_bytes_help(*symbol, it, &top_level.result);
|
||||
let bytes = func_name_bytes_help(
|
||||
*symbol,
|
||||
it,
|
||||
CapturesNiche::no_niche(),
|
||||
&top_level.result,
|
||||
);
|
||||
|
||||
host_exposed_functions.push((bytes, top_level.arguments));
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
let bytes =
|
||||
func_name_bytes_help(*symbol, [Layout::UNIT], &top_level.result);
|
||||
let bytes = func_name_bytes_help(
|
||||
*symbol,
|
||||
[Layout::UNIT],
|
||||
CapturesNiche::no_niche(),
|
||||
&top_level.result,
|
||||
);
|
||||
|
||||
host_exposed_functions.push((bytes, top_level.arguments));
|
||||
}
|
||||
|
@ -207,6 +225,7 @@ where
|
|||
let roc_main_bytes = func_name_bytes_help(
|
||||
entry_point.symbol,
|
||||
entry_point.layout.arguments.iter().copied(),
|
||||
CapturesNiche::no_niche(),
|
||||
&entry_point.layout.result,
|
||||
);
|
||||
let roc_main = FuncName(&roc_main_bytes);
|
||||
|
@ -631,7 +650,7 @@ fn call_spec(
|
|||
|
||||
match &call.call_type {
|
||||
ByName {
|
||||
name: symbol,
|
||||
name,
|
||||
ret_layout,
|
||||
arg_layouts,
|
||||
specialization_id,
|
||||
|
@ -640,8 +659,9 @@ fn call_spec(
|
|||
let spec_var = CalleeSpecVar(&array);
|
||||
|
||||
let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?;
|
||||
let it = arg_layouts.iter().copied();
|
||||
let bytes = func_name_bytes_help(*symbol, it, ret_layout);
|
||||
let args_it = arg_layouts.iter().copied();
|
||||
let captures_niche = name.captures_niche();
|
||||
let bytes = func_name_bytes_help(name.name(), args_it, captures_niche, ret_layout);
|
||||
let name = FuncName(&bytes);
|
||||
let module = MOD_APP;
|
||||
builder.add_call(block, spec_var, module, name, arg_value_id)
|
||||
|
@ -684,9 +704,14 @@ fn call_spec(
|
|||
let mode = update_mode.to_bytes();
|
||||
let update_mode_var = UpdateModeVar(&mode);
|
||||
|
||||
let it = passed_function.argument_layouts.iter().copied();
|
||||
let bytes =
|
||||
func_name_bytes_help(passed_function.name, it, &passed_function.return_layout);
|
||||
let args_it = passed_function.argument_layouts.iter().copied();
|
||||
let captures_niche = passed_function.name.captures_niche();
|
||||
let bytes = func_name_bytes_help(
|
||||
passed_function.name.name(),
|
||||
args_it,
|
||||
captures_niche,
|
||||
&passed_function.return_layout,
|
||||
);
|
||||
let name = FuncName(&bytes);
|
||||
let module = MOD_APP;
|
||||
|
||||
|
@ -1201,14 +1226,12 @@ fn expr_spec<'a>(
|
|||
Call(call) => call_spec(builder, env, block, layout, call),
|
||||
Reuse {
|
||||
tag_layout,
|
||||
tag_name: _,
|
||||
tag_id,
|
||||
arguments,
|
||||
..
|
||||
}
|
||||
| Tag {
|
||||
tag_layout,
|
||||
tag_name: _,
|
||||
tag_id,
|
||||
arguments,
|
||||
} => {
|
||||
|
|
|
@ -143,6 +143,7 @@ const str = @import("str.zig");
|
|||
comptime {
|
||||
exportStrFn(str.init, "init");
|
||||
exportStrFn(str.strToScalarsC, "to_scalars");
|
||||
exportStrFn(str.strSplit, "str_split");
|
||||
exportStrFn(str.strSplitInPlaceC, "str_split_in_place");
|
||||
exportStrFn(str.countSegments, "count_segments");
|
||||
exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters");
|
||||
|
|
|
@ -752,6 +752,21 @@ fn strFromFloatHelp(comptime T: type, float: T) RocStr {
|
|||
}
|
||||
|
||||
// Str.split
|
||||
|
||||
// For dev backends
|
||||
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));
|
||||
|
||||
if (list.bytes) |bytes| {
|
||||
const strings = @ptrCast([*]RocStr, @alignCast(@alignOf(RocStr), bytes));
|
||||
strSplitInPlace(strings, string, delimiter);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// For LLVM backend
|
||||
pub fn strSplitInPlaceC(opt_array: ?[*]RocStr, string: RocStr, delimiter: RocStr) callconv(.C) void {
|
||||
if (opt_array) |array| {
|
||||
return @call(.{ .modifier = always_inline }, strSplitInPlace, .{ array, string, delimiter });
|
||||
|
@ -1595,7 +1610,7 @@ const CountAndStart = extern struct {
|
|||
start: usize,
|
||||
};
|
||||
|
||||
pub fn fromUtf8C(arg: RocList, update_mode: UpdateMode, output: *FromUtf8Result) callconv(.C) void {
|
||||
pub fn fromUtf8C(output: *FromUtf8Result, arg: RocList, update_mode: UpdateMode) callconv(.C) void {
|
||||
output.* = fromUtf8(arg, update_mode);
|
||||
}
|
||||
|
||||
|
@ -1650,7 +1665,7 @@ inline fn fromUtf8(arg: RocList, update_mode: UpdateMode) FromUtf8Result {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fromUtf8RangeC(arg: RocList, countAndStart: CountAndStart, output: *FromUtf8Result) callconv(.C) void {
|
||||
pub fn fromUtf8RangeC(output: *FromUtf8Result, arg: RocList, countAndStart: CountAndStart) callconv(.C) void {
|
||||
output.* = @call(.{ .modifier = always_inline }, fromUtf8Range, .{ arg, countAndStart });
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ pub enum DecWidth {
|
|||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
|
||||
pub enum FloatWidth {
|
||||
F32,
|
||||
F64,
|
||||
|
@ -76,7 +76,7 @@ impl FloatWidth {
|
|||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
|
||||
pub enum IntWidth {
|
||||
U8 = 0,
|
||||
U16 = 1,
|
||||
|
@ -310,6 +310,7 @@ pub const STR_INIT: &str = "roc_builtins.str.init";
|
|||
pub const STR_COUNT_SEGMENTS: &str = "roc_builtins.str.count_segments";
|
||||
pub const STR_CONCAT: &str = "roc_builtins.str.concat";
|
||||
pub const STR_JOIN_WITH: &str = "roc_builtins.str.joinWith";
|
||||
pub const STR_STR_SPLIT: &str = "roc_builtins.str.str_split";
|
||||
pub const STR_STR_SPLIT_IN_PLACE: &str = "roc_builtins.str.str_split_in_place";
|
||||
pub const STR_TO_SCALARS: &str = "roc_builtins.str.to_scalars";
|
||||
pub const STR_COUNT_GRAPEHEME_CLUSTERS: &str = "roc_builtins.str.count_grapheme_clusters";
|
||||
|
|
|
@ -1770,7 +1770,7 @@ fn str_from_utf8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
// Ok arg_2.str
|
||||
// else
|
||||
// # problem
|
||||
// Err (BadUtf8 { byteIndex: arg_2.byteIndex, problem : arg_2.problem })
|
||||
// Err (BadUtf8 arg_2.problem arg_2.byteIndex)
|
||||
|
||||
let def = crate::def::Def {
|
||||
loc_pattern: no_region(Pattern::Identifier(Symbol::ARG_2)),
|
||||
|
@ -1872,7 +1872,7 @@ fn str_from_utf8_range(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
// if arg_3.a then
|
||||
// Ok arg_3.str
|
||||
// else
|
||||
// Err (BadUtf8 { byteIndex: arg_3.byteIndex, problem : arg_3.problem })
|
||||
// Err (BadUtf8 arg_3.problem arg_3.byteIndex)
|
||||
|
||||
let def = crate::def::Def {
|
||||
loc_pattern: no_region(Pattern::Identifier(Symbol::ARG_3)),
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::annotation::make_apply_symbol;
|
|||
use crate::annotation::IntroducedVariables;
|
||||
use crate::annotation::OwnedNamedOrAble;
|
||||
use crate::env::Env;
|
||||
use crate::expr::AccessorData;
|
||||
use crate::expr::AnnotatedMark;
|
||||
use crate::expr::ClosureData;
|
||||
use crate::expr::Declarations;
|
||||
|
@ -1554,7 +1555,10 @@ fn canonicalize_pending_value_def<'a>(
|
|||
region: loc_ann.region,
|
||||
}
|
||||
} else {
|
||||
let symbol = scope.gen_unique_symbol();
|
||||
let symbol = match &loc_can_pattern.value {
|
||||
Pattern::Identifier(symbol) => *symbol,
|
||||
_ => scope.gen_unique_symbol(),
|
||||
};
|
||||
|
||||
// generate a fake pattern for each argument. this makes signatures
|
||||
// that are functions only crash when they are applied.
|
||||
|
@ -1725,6 +1729,36 @@ fn canonicalize_pending_body<'a>(
|
|||
(loc_can_expr, def_references)
|
||||
}
|
||||
|
||||
// Turn f = .foo into f = \rcd -[f]-> rcd.foo
|
||||
(
|
||||
Pattern::Identifier(defined_symbol)
|
||||
| Pattern::AbilityMemberSpecialization {
|
||||
ident: defined_symbol,
|
||||
..
|
||||
},
|
||||
ast::Expr::AccessorFunction(field),
|
||||
) => {
|
||||
let (loc_can_expr, can_output) = (
|
||||
Loc::at(
|
||||
loc_expr.region,
|
||||
Accessor(AccessorData {
|
||||
name: *defined_symbol,
|
||||
function_var: var_store.fresh(),
|
||||
record_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
closure_var: var_store.fresh(),
|
||||
field_var: var_store.fresh(),
|
||||
field: (*field).into(),
|
||||
}),
|
||||
),
|
||||
Output::default(),
|
||||
);
|
||||
let def_references = DefReferences::Value(can_output.references.clone());
|
||||
output.union(can_output);
|
||||
|
||||
(loc_can_expr, def_references)
|
||||
}
|
||||
|
||||
_ => {
|
||||
let (loc_can_expr, can_output) =
|
||||
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
|
||||
|
|
|
@ -97,6 +97,10 @@ impl<K: PartialEq, V> VecMap<K, V> {
|
|||
self.keys.iter().zip(self.values.iter())
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl ExactSizeIterator<Item = (&K, &mut V)> {
|
||||
self.keys.iter().zip(self.values.iter_mut())
|
||||
}
|
||||
|
||||
pub fn keys(&self) -> impl ExactSizeIterator<Item = &K> {
|
||||
self.keys.iter()
|
||||
}
|
||||
|
|
|
@ -699,26 +699,45 @@ fn fmt_when<'a, 'buf>(
|
|||
buf.push_str("is");
|
||||
buf.newline();
|
||||
|
||||
let mut it = branches.iter().peekable();
|
||||
while let Some(branch) = it.next() {
|
||||
let mut it = branches.iter().enumerate().peekable();
|
||||
|
||||
while let Some((branch_index, branch)) = it.next() {
|
||||
let expr = &branch.value;
|
||||
let patterns = &branch.patterns;
|
||||
let is_multiline_expr = expr.is_multiline();
|
||||
let is_multiline_patterns = is_when_patterns_multiline(branch);
|
||||
|
||||
for (index, pattern) in patterns.iter().enumerate() {
|
||||
if index != 0 {
|
||||
if index == 0 {
|
||||
match &pattern.value {
|
||||
Pattern::SpaceBefore(sub_pattern, spaces) if branch_index == 0 => {
|
||||
// Never include extra newlines before the first branch.
|
||||
// Instead, write the comments and that's it.
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent + INDENT);
|
||||
|
||||
fmt_pattern(buf, sub_pattern, indent + INDENT, Parens::NotNeeded);
|
||||
}
|
||||
other => {
|
||||
fmt_pattern(buf, other, indent + INDENT, Parens::NotNeeded);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if is_multiline_patterns {
|
||||
buf.newline();
|
||||
buf.indent(indent + INDENT);
|
||||
buf.ensure_ends_in_newline();
|
||||
// Indent an extra level for the `|`;
|
||||
// otherwise it'll be at the start of the line,
|
||||
// and will be incorrectly parsed as a pattern
|
||||
buf.indent(indent + INDENT + INDENT);
|
||||
buf.push('|');
|
||||
} else {
|
||||
buf.push_str(" |");
|
||||
}
|
||||
|
||||
buf.push_str(" |");
|
||||
buf.spaces(1);
|
||||
}
|
||||
|
||||
fmt_pattern(buf, &pattern.value, indent + INDENT, Parens::NotNeeded);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(guard_expr) = &branch.guard {
|
||||
buf.push_str(" if");
|
||||
|
|
|
@ -89,6 +89,14 @@ impl<'a> Buf<'a> {
|
|||
self.beginning_of_line = true;
|
||||
}
|
||||
|
||||
/// Ensures the current buffer ends in a newline, if it didn't already.
|
||||
/// Doesn't add a newline if the buffer already ends in one.
|
||||
pub fn ensure_ends_in_newline(&mut self) {
|
||||
if !self.text.ends_with('\n') {
|
||||
self.newline()
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_spaces(&mut self) {
|
||||
if self.spaces_to_flush > 0 {
|
||||
for _ in 0..self.spaces_to_flush {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::annotation::{Formattable, Newlines, Parens};
|
||||
use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt};
|
||||
use crate::Buf;
|
||||
use roc_parse::ast::{Base, Pattern};
|
||||
use roc_parse::ast::{Base, CommentOrNewline, Pattern};
|
||||
|
||||
pub fn fmt_pattern<'a, 'buf>(
|
||||
buf: &mut Buf<'buf>,
|
||||
|
@ -167,11 +167,16 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
} else {
|
||||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
}
|
||||
|
||||
sub_pattern.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
SpaceAfter(sub_pattern, spaces) => {
|
||||
sub_pattern.format_with_options(buf, parens, newlines, indent);
|
||||
// if only_comments {
|
||||
|
||||
if starts_with_inline_comment(spaces.iter()) {
|
||||
buf.spaces(1);
|
||||
}
|
||||
|
||||
if !sub_pattern.is_multiline() {
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent)
|
||||
} else {
|
||||
|
@ -196,3 +201,12 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn starts_with_inline_comment<'a, I: IntoIterator<Item = &'a CommentOrNewline<'a>>>(
|
||||
spaces: I,
|
||||
) -> bool {
|
||||
matches!(
|
||||
spaces.into_iter().next(),
|
||||
Some(CommentOrNewline::LineComment(_))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3373,6 +3373,18 @@ mod test_fmt {
|
|||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_with_integer_comments() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
when 0 is
|
||||
1 # comment
|
||||
| 2 -> "a"
|
||||
_ -> "b"
|
||||
"#
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_when() {
|
||||
expr_formats_same(indoc!(
|
||||
|
|
|
@ -14,9 +14,7 @@ use roc_mono::ir::{
|
|||
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc, ProcLayout,
|
||||
SelfRecursive, Stmt,
|
||||
};
|
||||
use roc_mono::layout::{
|
||||
Builtin, Layout, LayoutId, LayoutIds, TagIdIntType, TagOrClosure, UnionLayout,
|
||||
};
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutId, LayoutIds, TagIdIntType, UnionLayout};
|
||||
|
||||
mod generic64;
|
||||
mod object_builder;
|
||||
|
@ -106,8 +104,8 @@ trait Backend<'a> {
|
|||
proc: Proc<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
) -> (Vec<u8>, Vec<Relocation>, Vec<'a, (Symbol, String)>) {
|
||||
let layout_id = layout_ids.get(proc.name, &proc.ret_layout);
|
||||
let proc_name = self.symbol_to_string(proc.name, layout_id);
|
||||
let layout_id = layout_ids.get(proc.name.name(), &proc.ret_layout);
|
||||
let proc_name = self.symbol_to_string(proc.name.name(), layout_id);
|
||||
self.reset(proc_name, proc.is_self_recursive);
|
||||
self.load_args(proc.args, &proc.ret_layout);
|
||||
for (layout, sym) in proc.args {
|
||||
|
@ -263,7 +261,7 @@ trait Backend<'a> {
|
|||
..
|
||||
} => {
|
||||
if let LowLevelWrapperType::CanBeReplacedBy(lowlevel) =
|
||||
LowLevelWrapperType::from_symbol(*func_sym)
|
||||
LowLevelWrapperType::from_symbol(func_sym.name())
|
||||
{
|
||||
self.build_run_low_level(
|
||||
sym,
|
||||
|
@ -272,14 +270,20 @@ trait Backend<'a> {
|
|||
arg_layouts,
|
||||
ret_layout,
|
||||
)
|
||||
} else if self.defined_in_app_module(*func_sym) {
|
||||
let layout_id = LayoutIds::default().get(*func_sym, layout);
|
||||
let fn_name = self.symbol_to_string(*func_sym, layout_id);
|
||||
} else if self.defined_in_app_module(func_sym.name()) {
|
||||
let layout_id = LayoutIds::default().get(func_sym.name(), layout);
|
||||
let fn_name = self.symbol_to_string(func_sym.name(), layout_id);
|
||||
// Now that the arguments are needed, load them if they are literals.
|
||||
self.load_literal_symbols(arguments);
|
||||
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
||||
} else {
|
||||
self.build_builtin(sym, *func_sym, arguments, arg_layouts, ret_layout)
|
||||
self.build_builtin(
|
||||
sym,
|
||||
func_sym.name(),
|
||||
arguments,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -909,18 +913,9 @@ trait Backend<'a> {
|
|||
}
|
||||
}
|
||||
Expr::Reuse {
|
||||
symbol,
|
||||
arguments,
|
||||
tag_name,
|
||||
..
|
||||
symbol, arguments, ..
|
||||
} => {
|
||||
self.set_last_seen(*symbol, stmt);
|
||||
match tag_name {
|
||||
TagOrClosure::Closure(sym) => {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
TagOrClosure::Tag(_) => {}
|
||||
}
|
||||
for sym in *arguments {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
|
|
|
@ -62,7 +62,9 @@ use roc_mono::ir::{
|
|||
BranchInfo, CallType, EntryPoint, HigherOrderLowLevel, JoinPointId, ListLiteralElement,
|
||||
ModifyRc, OptLevel, ProcLayout,
|
||||
};
|
||||
use roc_mono::layout::{Builtin, LambdaSet, Layout, LayoutIds, TagIdIntType, UnionLayout};
|
||||
use roc_mono::layout::{
|
||||
Builtin, CapturesNiche, LambdaName, LambdaSet, Layout, LayoutIds, TagIdIntType, UnionLayout,
|
||||
};
|
||||
use roc_std::RocDec;
|
||||
use roc_target::{PtrWidth, TargetInfo};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
@ -715,7 +717,12 @@ fn promote_to_main_function<'a, 'ctx, 'env>(
|
|||
top_level: ProcLayout<'a>,
|
||||
) -> (&'static str, FunctionValue<'ctx>) {
|
||||
let it = top_level.arguments.iter().copied();
|
||||
let bytes = roc_alias_analysis::func_name_bytes_help(symbol, it, &top_level.result);
|
||||
let bytes = roc_alias_analysis::func_name_bytes_help(
|
||||
symbol,
|
||||
it,
|
||||
CapturesNiche::no_niche(),
|
||||
&top_level.result,
|
||||
);
|
||||
let func_name = FuncName(&bytes);
|
||||
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
|
||||
|
||||
|
@ -727,7 +734,14 @@ fn promote_to_main_function<'a, 'ctx, 'env>(
|
|||
);
|
||||
|
||||
// NOTE fake layout; it is only used for debug prints
|
||||
let roc_main_fn = function_value_by_func_spec(env, *func_spec, symbol, &[], &Layout::UNIT);
|
||||
let roc_main_fn = function_value_by_func_spec(
|
||||
env,
|
||||
*func_spec,
|
||||
symbol,
|
||||
&[],
|
||||
CapturesNiche::no_niche(),
|
||||
&Layout::UNIT,
|
||||
);
|
||||
|
||||
let main_fn_name = "$Test.main";
|
||||
|
||||
|
@ -3296,6 +3310,7 @@ fn expose_function_to_host<'a, 'ctx, 'env>(
|
|||
symbol: Symbol,
|
||||
roc_function: FunctionValue<'ctx>,
|
||||
arguments: &'a [Layout<'a>],
|
||||
captures_niche: CapturesNiche<'a>,
|
||||
return_layout: Layout<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
) {
|
||||
|
@ -3304,6 +3319,7 @@ fn expose_function_to_host<'a, 'ctx, 'env>(
|
|||
let proc_layout = ProcLayout {
|
||||
arguments,
|
||||
result: return_layout,
|
||||
captures_niche,
|
||||
};
|
||||
|
||||
let c_function_name: String = layout_ids
|
||||
|
@ -4185,7 +4201,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
|
|||
|
||||
// only have top-level thunks for this proc's module in scope
|
||||
// this retain is not needed for correctness, but will cause less confusion when debugging
|
||||
let home = proc.name.module_id();
|
||||
let home = proc.name.name().module_id();
|
||||
current_scope.retain_top_level_thunks_for_module(home);
|
||||
|
||||
build_proc(
|
||||
|
@ -4301,6 +4317,7 @@ fn build_proc_header<'a, 'ctx, 'env>(
|
|||
symbol,
|
||||
fn_val,
|
||||
arguments.into_bump_slice(),
|
||||
proc.name.captures_niche(),
|
||||
proc.ret_layout,
|
||||
layout_ids,
|
||||
);
|
||||
|
@ -4530,8 +4547,12 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
|||
// * roc__mainForHost_1_Update_result_size() -> i64
|
||||
|
||||
let it = top_level.arguments.iter().copied();
|
||||
let bytes =
|
||||
roc_alias_analysis::func_name_bytes_help(symbol, it, &top_level.result);
|
||||
let bytes = roc_alias_analysis::func_name_bytes_help(
|
||||
symbol,
|
||||
it,
|
||||
CapturesNiche::no_niche(),
|
||||
&top_level.result,
|
||||
);
|
||||
let func_name = FuncName(&bytes);
|
||||
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
|
||||
|
||||
|
@ -4548,6 +4569,7 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
|||
*func_spec,
|
||||
symbol,
|
||||
top_level.arguments,
|
||||
CapturesNiche::no_niche(),
|
||||
&top_level.result,
|
||||
)
|
||||
}
|
||||
|
@ -4559,7 +4581,7 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
|||
}
|
||||
};
|
||||
|
||||
let ident_string = proc.name.as_str(&env.interns);
|
||||
let ident_string = proc.name.name().as_str(&env.interns);
|
||||
let fn_name: String = format!("{}_1", ident_string);
|
||||
|
||||
build_closure_caller(
|
||||
|
@ -4624,17 +4646,19 @@ fn function_value_by_func_spec<'a, 'ctx, 'env>(
|
|||
func_spec: FuncSpec,
|
||||
symbol: Symbol,
|
||||
arguments: &[Layout<'a>],
|
||||
captures_niche: CapturesNiche<'a>,
|
||||
result: &Layout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
|
||||
let fn_name = fn_name.as_str();
|
||||
|
||||
function_value_by_name_help(env, arguments, result, symbol, fn_name)
|
||||
function_value_by_name_help(env, arguments, captures_niche, result, symbol, fn_name)
|
||||
}
|
||||
|
||||
fn function_value_by_name_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
arguments: &[Layout<'a>],
|
||||
_captures_niche: CapturesNiche<'a>,
|
||||
result: &Layout<'a>,
|
||||
symbol: Symbol,
|
||||
fn_name: &str,
|
||||
|
@ -4675,12 +4699,18 @@ fn roc_call_with_args<'a, 'ctx, 'env>(
|
|||
env: &Env<'a, 'ctx, 'env>,
|
||||
argument_layouts: &[Layout<'a>],
|
||||
result_layout: &Layout<'a>,
|
||||
symbol: Symbol,
|
||||
name: LambdaName<'a>,
|
||||
func_spec: FuncSpec,
|
||||
arguments: &[BasicValueEnum<'ctx>],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let fn_val =
|
||||
function_value_by_func_spec(env, func_spec, symbol, argument_layouts, result_layout);
|
||||
let fn_val = function_value_by_func_spec(
|
||||
env,
|
||||
func_spec,
|
||||
name.name(),
|
||||
argument_layouts,
|
||||
name.captures_niche(),
|
||||
result_layout,
|
||||
);
|
||||
|
||||
call_roc_function(env, fn_val, result_layout, arguments)
|
||||
}
|
||||
|
@ -4869,8 +4899,9 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
let function = function_value_by_func_spec(
|
||||
env,
|
||||
func_spec,
|
||||
function_name,
|
||||
function_name.name(),
|
||||
argument_layouts,
|
||||
function_name.captures_niche(),
|
||||
return_layout,
|
||||
);
|
||||
|
||||
|
|
|
@ -168,10 +168,10 @@ pub fn str_from_utf8_range<'a, 'ctx, 'env>(
|
|||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
result_ptr.into(),
|
||||
list_symbol_to_c_abi(env, scope, list).into(),
|
||||
count,
|
||||
start,
|
||||
result_ptr.into(),
|
||||
],
|
||||
bitcode::STR_FROM_UTF8_RANGE,
|
||||
);
|
||||
|
@ -194,9 +194,9 @@ pub fn str_from_utf8<'a, 'ctx, 'env>(
|
|||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
result_ptr.into(),
|
||||
list_symbol_to_c_abi(env, scope, list).into(),
|
||||
pass_update_mode(env, update_mode),
|
||||
result_ptr.into(),
|
||||
],
|
||||
bitcode::STR_FROM_UTF8,
|
||||
);
|
||||
|
|
|
@ -1374,12 +1374,14 @@ fn build_box_eq_help<'a, 'ctx, 'env>(
|
|||
box1.set_name(Symbol::ARG_1.as_str(&env.interns));
|
||||
box2.set_name(Symbol::ARG_2.as_str(&env.interns));
|
||||
|
||||
let entry = ctx.append_basic_block(parent, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
let return_true = ctx.append_basic_block(parent, "return_true");
|
||||
env.builder.position_at_end(return_true);
|
||||
env.builder
|
||||
.build_return(Some(&env.context.bool_type().const_all_ones()));
|
||||
|
||||
let entry = ctx.append_basic_block(parent, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
let ptr_equal = env.builder.build_int_compare(
|
||||
|
@ -1402,8 +1404,8 @@ fn build_box_eq_help<'a, 'ctx, 'env>(
|
|||
let box1 = box1.into_pointer_value();
|
||||
let box2 = box2.into_pointer_value();
|
||||
|
||||
let value1 = env.builder.build_load(box1, "load_box1");
|
||||
let value2 = env.builder.build_load(box2, "load_box2");
|
||||
let value1 = load_roc_value(env, *inner_layout, box1, "load_box1");
|
||||
let value2 = load_roc_value(env, *inner_layout, box2, "load_box2");
|
||||
|
||||
let is_equal = build_eq(
|
||||
env,
|
||||
|
|
|
@ -17,7 +17,7 @@ use roc_std::RocDec;
|
|||
|
||||
use crate::layout::{CallConv, ReturnMethod, WasmLayout};
|
||||
use crate::low_level::{call_higher_order_lowlevel, LowLevelCall};
|
||||
use crate::storage::{Storage, StoredValue, StoredVarKind};
|
||||
use crate::storage::{AddressValue, Storage, StoredValue, StoredVarKind};
|
||||
use crate::wasm_module::linking::{DataSymbol, WasmObjectSymbol};
|
||||
use crate::wasm_module::sections::{
|
||||
ConstExpr, DataMode, DataSegment, Export, Global, GlobalType, Import, ImportDesc, Limits,
|
||||
|
@ -380,7 +380,7 @@ impl<'a> WasmBackend<'a> {
|
|||
println!("\ngenerating procedure {:?}\n", proc.name);
|
||||
}
|
||||
|
||||
self.append_proc_debug_name(proc.name);
|
||||
self.append_proc_debug_name(proc.name.name());
|
||||
|
||||
self.start_proc(proc);
|
||||
|
||||
|
@ -928,7 +928,7 @@ impl<'a> WasmBackend<'a> {
|
|||
index,
|
||||
field_layouts,
|
||||
structure,
|
||||
} => self.expr_struct_at_index(sym, storage, *index, field_layouts, *structure),
|
||||
} => self.expr_struct_at_index(sym, *index, field_layouts, *structure),
|
||||
|
||||
Expr::Array { elems, elem_layout } => self.expr_array(sym, storage, elem_layout, elems),
|
||||
|
||||
|
@ -953,9 +953,9 @@ impl<'a> WasmBackend<'a> {
|
|||
index,
|
||||
} => self.expr_union_at_index(*structure, *tag_id, union_layout, *index, sym),
|
||||
|
||||
Expr::ExprBox { .. } | Expr::ExprUnbox { .. } => {
|
||||
todo!("Expression `{}`", expr.to_pretty(100))
|
||||
}
|
||||
Expr::ExprBox { symbol: arg_sym } => self.expr_box(sym, *arg_sym, layout, storage),
|
||||
|
||||
Expr::ExprUnbox { symbol: arg_sym } => self.expr_unbox(sym, *arg_sym),
|
||||
|
||||
Expr::Reuse {
|
||||
tag_layout,
|
||||
|
@ -1125,9 +1125,10 @@ impl<'a> WasmBackend<'a> {
|
|||
let proc_layout = ProcLayout {
|
||||
arguments: arg_layouts,
|
||||
result: **result,
|
||||
captures_niche: func_sym.captures_niche(),
|
||||
};
|
||||
self.expr_call_by_name(
|
||||
*func_sym,
|
||||
func_sym.name(),
|
||||
&proc_layout,
|
||||
arguments,
|
||||
ret_sym,
|
||||
|
@ -1353,16 +1354,15 @@ impl<'a> WasmBackend<'a> {
|
|||
fn expr_struct_at_index(
|
||||
&mut self,
|
||||
sym: Symbol,
|
||||
storage: &StoredValue,
|
||||
index: u64,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
structure: Symbol,
|
||||
) {
|
||||
self.storage
|
||||
.ensure_value_has_local(&mut self.code_builder, sym, storage.to_owned());
|
||||
let (local_id, mut offset) = match self.storage.get(&structure) {
|
||||
let (from_addr_val, mut offset) = match self.storage.get(&structure) {
|
||||
StoredValue::StackMemory { location, .. } => {
|
||||
location.local_and_offset(self.storage.stack_frame_pointer)
|
||||
let (local_id, offset) =
|
||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||
(AddressValue::NotLoaded(local_id), offset)
|
||||
}
|
||||
|
||||
StoredValue::Local {
|
||||
|
@ -1371,18 +1371,20 @@ impl<'a> WasmBackend<'a> {
|
|||
..
|
||||
} => {
|
||||
debug_assert!(matches!(value_type, ValueType::I32));
|
||||
(*local_id, 0)
|
||||
(AddressValue::NotLoaded(*local_id), 0)
|
||||
}
|
||||
|
||||
StoredValue::VirtualMachineStack { .. } => {
|
||||
internal_error!("ensure_value_has_local didn't work")
|
||||
self.storage
|
||||
.load_symbols(&mut self.code_builder, &[structure]);
|
||||
(AddressValue::Loaded, 0)
|
||||
}
|
||||
};
|
||||
for field in field_layouts.iter().take(index as usize) {
|
||||
offset += field.stack_size(TARGET_INFO);
|
||||
}
|
||||
self.storage
|
||||
.copy_value_from_memory(&mut self.code_builder, sym, local_id, offset);
|
||||
.copy_value_from_memory(&mut self.code_builder, sym, from_addr_val, offset);
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
|
@ -1700,20 +1702,83 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(TARGET_INFO);
|
||||
|
||||
let from_ptr = if stores_tag_id_in_pointer {
|
||||
let ptr = self.storage.create_anonymous_local(ValueType::I32);
|
||||
let from_addr_val = if stores_tag_id_in_pointer {
|
||||
self.code_builder.get_local(tag_local_id);
|
||||
self.code_builder.i32_const(-4); // 11111111...1100
|
||||
self.code_builder.i32_and();
|
||||
self.code_builder.set_local(ptr);
|
||||
ptr
|
||||
AddressValue::Loaded
|
||||
} else {
|
||||
tag_local_id
|
||||
AddressValue::NotLoaded(tag_local_id)
|
||||
};
|
||||
|
||||
let from_offset = tag_offset + field_offset;
|
||||
self.storage.copy_value_from_memory(
|
||||
&mut self.code_builder,
|
||||
symbol,
|
||||
from_addr_val,
|
||||
from_offset,
|
||||
);
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
* Box
|
||||
*******************************************************************/
|
||||
|
||||
fn expr_box(
|
||||
&mut self,
|
||||
ret_sym: Symbol,
|
||||
arg_sym: Symbol,
|
||||
layout: &Layout<'a>,
|
||||
storage: &StoredValue,
|
||||
) {
|
||||
// create a local variable for the heap pointer
|
||||
let ptr_local_id = match self.storage.ensure_value_has_local(
|
||||
&mut self.code_builder,
|
||||
ret_sym,
|
||||
storage.clone(),
|
||||
) {
|
||||
StoredValue::Local { local_id, .. } => local_id,
|
||||
_ => internal_error!("A heap pointer will always be an i32"),
|
||||
};
|
||||
|
||||
// allocate heap memory and load its data address onto the value stack
|
||||
let arg_layout = match layout {
|
||||
Layout::Boxed(arg) => *arg,
|
||||
_ => internal_error!("ExprBox should always produce a Boxed layout"),
|
||||
};
|
||||
let (size, alignment) = arg_layout.stack_size_and_alignment(TARGET_INFO);
|
||||
self.allocate_with_refcount(Some(size), alignment, 1);
|
||||
|
||||
// store the pointer value from the value stack into the local variable
|
||||
self.code_builder.set_local(ptr_local_id);
|
||||
|
||||
// copy the argument to the pointer address
|
||||
self.storage
|
||||
.copy_value_from_memory(&mut self.code_builder, symbol, from_ptr, from_offset);
|
||||
.copy_value_to_memory(&mut self.code_builder, ptr_local_id, 0, arg_sym);
|
||||
}
|
||||
|
||||
fn expr_unbox(&mut self, ret_sym: Symbol, arg_sym: Symbol) {
|
||||
let (from_addr_val, from_offset) = match self.storage.get(&arg_sym) {
|
||||
StoredValue::VirtualMachineStack { .. } => {
|
||||
self.storage
|
||||
.load_symbols(&mut self.code_builder, &[arg_sym]);
|
||||
(AddressValue::Loaded, 0)
|
||||
}
|
||||
StoredValue::Local { local_id, .. } => (AddressValue::NotLoaded(*local_id), 0),
|
||||
StoredValue::StackMemory { location, .. } => {
|
||||
let (local_id, offset) =
|
||||
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||
(AddressValue::NotLoaded(local_id), offset)
|
||||
}
|
||||
};
|
||||
|
||||
// Copy the value
|
||||
self.storage.copy_value_from_memory(
|
||||
&mut self.code_builder,
|
||||
ret_sym,
|
||||
from_addr_val,
|
||||
from_offset,
|
||||
);
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
|
|
|
@ -184,7 +184,8 @@ pub fn build_app_module<'a>(
|
|||
}
|
||||
|
||||
let (module, called_preload_fns) = backend.finalize();
|
||||
let main_function_index = maybe_main_fn_index.unwrap();
|
||||
let main_function_index =
|
||||
maybe_main_fn_index.expect("The app must expose at least one value to the host");
|
||||
|
||||
(module, called_preload_fns, main_function_index)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use roc_mono::low_level::HigherOrder;
|
|||
|
||||
use crate::backend::{ProcLookupData, ProcSource, WasmBackend};
|
||||
use crate::layout::{CallConv, StackMemoryFormat, WasmLayout};
|
||||
use crate::storage::{StackMemoryLocation, StoredValue};
|
||||
use crate::storage::{AddressValue, StackMemoryLocation, StoredValue};
|
||||
use crate::wasm_module::{Align, LocalId, ValueType};
|
||||
use crate::TARGET_INFO;
|
||||
|
||||
|
@ -236,15 +236,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
self.load_args_and_call_zig(backend, bitcode::STR_STARTS_WITH_SCALAR)
|
||||
}
|
||||
StrEndsWith => self.load_args_and_call_zig(backend, bitcode::STR_ENDS_WITH),
|
||||
StrSplit => {
|
||||
// LLVM implementation (build_str.rs) does the following
|
||||
// 1. Call bitcode::STR_COUNT_SEGMENTS
|
||||
// 2. Allocate a `List Str`
|
||||
// 3. Call bitcode::STR_STR_SPLIT_IN_PLACE
|
||||
// 4. Write the elements and length of the List
|
||||
// To do this here, we need full access to WasmBackend, or we could make a Zig wrapper
|
||||
todo!("{:?}", self.lowlevel);
|
||||
}
|
||||
StrSplit => self.load_args_and_call_zig(backend, bitcode::STR_STR_SPLIT),
|
||||
StrCountGraphemes => {
|
||||
self.load_args_and_call_zig(backend, bitcode::STR_COUNT_GRAPEHEME_CLUSTERS)
|
||||
}
|
||||
|
@ -272,10 +264,30 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
StrFromInt => self.num_to_str(backend),
|
||||
StrFromFloat => self.num_to_str(backend),
|
||||
StrFromUtf8 => self.load_args_and_call_zig(backend, bitcode::STR_FROM_UTF8),
|
||||
StrFromUtf8 => {
|
||||
/*
|
||||
Low-level op returns a struct with all the data for both Ok and Err.
|
||||
Roc AST wrapper converts this to a tag union, with app-dependent tag IDs.
|
||||
|
||||
fromUtf8C(output: *FromUtf8Result, arg: RocList, update_mode: UpdateMode) callconv(.C) void
|
||||
output: *FromUtf8Result i32
|
||||
arg: RocList i64, i32
|
||||
update_mode: UpdateMode i32
|
||||
*/
|
||||
backend.storage.load_symbols_for_call(
|
||||
backend.env.arena,
|
||||
&mut backend.code_builder,
|
||||
self.arguments,
|
||||
self.ret_symbol,
|
||||
&WasmLayout::new(&self.ret_layout),
|
||||
CallConv::Zig,
|
||||
);
|
||||
backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE);
|
||||
backend.call_host_fn_after_loading_args(bitcode::STR_FROM_UTF8, 4, false);
|
||||
}
|
||||
StrFromUtf8Range => self.load_args_and_call_zig(backend, bitcode::STR_FROM_UTF8_RANGE),
|
||||
StrTrimLeft => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_LEFT),
|
||||
StrTrimRight => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_RIGHT),
|
||||
StrFromUtf8Range => self.load_args_and_call_zig(backend, bitcode::STR_FROM_UTF8_RANGE),
|
||||
StrToUtf8 => self.load_args_and_call_zig(backend, bitcode::STR_TO_UTF8),
|
||||
StrReserve => self.load_args_and_call_zig(backend, bitcode::STR_RESERVE),
|
||||
StrRepeat => self.load_args_and_call_zig(backend, bitcode::STR_REPEAT),
|
||||
|
@ -325,14 +337,12 @@ impl<'a> LowLevelCall<'a> {
|
|||
|
||||
// Target element heap pointer
|
||||
backend.code_builder.i32_add(); // base + index*size
|
||||
let elem_heap_ptr = backend.storage.create_anonymous_local(ValueType::I32);
|
||||
backend.code_builder.set_local(elem_heap_ptr);
|
||||
|
||||
// Copy to stack
|
||||
backend.storage.copy_value_from_memory(
|
||||
&mut backend.code_builder,
|
||||
self.ret_symbol,
|
||||
elem_heap_ptr,
|
||||
AddressValue::Loaded,
|
||||
0,
|
||||
);
|
||||
|
||||
|
@ -1726,7 +1736,8 @@ impl<'a> LowLevelCall<'a> {
|
|||
Layout::Builtin(Builtin::Dict(_, _) | Builtin::Set(_) | Builtin::List(_))
|
||||
| Layout::Struct { .. }
|
||||
| Layout::Union(_)
|
||||
| Layout::LambdaSet(_) => {
|
||||
| Layout::LambdaSet(_)
|
||||
| Layout::Boxed(_) => {
|
||||
// Don't want Zig calling convention here, we're calling internal Roc functions
|
||||
backend
|
||||
.storage
|
||||
|
@ -1744,8 +1755,6 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Layout::Boxed(_) => todo!(),
|
||||
|
||||
Layout::RecursivePointer => {
|
||||
internal_error!(
|
||||
"Tried to apply `==` to RecursivePointer values {:?}",
|
||||
|
@ -1962,12 +1971,13 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
let passed_proc_layout = ProcLayout {
|
||||
arguments: argument_layouts,
|
||||
result: *result_layout,
|
||||
captures_niche: fn_name.captures_niche(),
|
||||
};
|
||||
let passed_proc_index = backend
|
||||
.proc_lookup
|
||||
.iter()
|
||||
.position(|ProcLookupData { name, layout, .. }| {
|
||||
name == fn_name && layout == &passed_proc_layout
|
||||
*name == fn_name.name() && layout == &passed_proc_layout
|
||||
})
|
||||
.unwrap();
|
||||
ProcSource::HigherOrderWrapper(passed_proc_index)
|
||||
|
@ -1995,6 +2005,7 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
ProcLayout {
|
||||
arguments: wrapper_arg_layouts.into_bump_slice(),
|
||||
result: Layout::UNIT,
|
||||
captures_niche: fn_name.captures_niche(),
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -76,6 +76,13 @@ impl StoredValue {
|
|||
}
|
||||
}
|
||||
|
||||
pub enum AddressValue {
|
||||
/// The address value has been loaded to the VM stack
|
||||
Loaded,
|
||||
/// The address value is in a local variable
|
||||
NotLoaded(LocalId),
|
||||
}
|
||||
|
||||
/// Helper structure for WasmBackend, to keep track of how values are stored,
|
||||
/// including the VM stack, local variables, and linear memory
|
||||
#[derive(Debug)]
|
||||
|
@ -610,7 +617,7 @@ impl<'a> Storage<'a> {
|
|||
&mut self,
|
||||
code_builder: &mut CodeBuilder,
|
||||
to_symbol: Symbol,
|
||||
from_ptr: LocalId,
|
||||
from_addr: AddressValue,
|
||||
from_offset: u32,
|
||||
) -> u32 {
|
||||
let to_storage = self.get(&to_symbol).to_owned();
|
||||
|
@ -625,6 +632,16 @@ impl<'a> Storage<'a> {
|
|||
self.stack_frame_pointer = Some(self.get_next_local_id());
|
||||
}
|
||||
|
||||
let from_ptr = match from_addr {
|
||||
AddressValue::NotLoaded(ptr) => ptr,
|
||||
AddressValue::Loaded => {
|
||||
// The `from` address is on the VM stack but we want it in a local for copying
|
||||
let tmp_local = self.create_anonymous_local(PTR_TYPE);
|
||||
code_builder.set_local(tmp_local);
|
||||
tmp_local
|
||||
}
|
||||
};
|
||||
|
||||
let (to_ptr, to_offset) = location.local_and_offset(self.stack_frame_pointer);
|
||||
copy_memory(
|
||||
code_builder,
|
||||
|
@ -648,7 +665,10 @@ impl<'a> Storage<'a> {
|
|||
} => {
|
||||
use crate::wasm_module::Align::*;
|
||||
|
||||
if let AddressValue::NotLoaded(from_ptr) = from_addr {
|
||||
code_builder.get_local(from_ptr);
|
||||
}
|
||||
|
||||
match (value_type, size) {
|
||||
(ValueType::I64, 8) => code_builder.i64_load(Bytes8, from_offset),
|
||||
(ValueType::I32, 4) => code_builder.i32_load(Bytes4, from_offset),
|
||||
|
|
|
@ -76,7 +76,7 @@ pub fn insert_wrapper_for_layout<'a>(
|
|||
bool::insert_wrapper(arena, module, wrapper_name, main_fn_index);
|
||||
}
|
||||
Layout::Union(UnionLayout::NonRecursive(_)) => stack_data_structure(),
|
||||
Layout::Union(_) => {
|
||||
Layout::Union(_) | Layout::Boxed(_) => {
|
||||
i32::insert_wrapper(arena, module, wrapper_name, main_fn_index);
|
||||
}
|
||||
_ => stack_data_structure(),
|
||||
|
|
|
@ -33,7 +33,7 @@ use roc_mono::ir::{
|
|||
CapturedSymbols, EntryPoint, ExternalSpecializations, PartialProc, Proc, ProcLayout, Procs,
|
||||
ProcsBase, UpdateModeIds,
|
||||
};
|
||||
use roc_mono::layout::{Layout, LayoutCache, LayoutProblem};
|
||||
use roc_mono::layout::{CapturesNiche, LambdaName, Layout, LayoutCache, LayoutProblem};
|
||||
use roc_parse::ast::{self, Defs, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation};
|
||||
use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent};
|
||||
use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName};
|
||||
|
@ -132,7 +132,7 @@ struct ModuleCache<'a> {
|
|||
typechecked: MutMap<ModuleId, TypeCheckedModule<'a>>,
|
||||
found_specializations: MutMap<ModuleId, FoundSpecializationsModule<'a>>,
|
||||
late_specializations: MutMap<ModuleId, LateSpecializationsModule<'a>>,
|
||||
external_specializations_requested: MutMap<ModuleId, Vec<ExternalSpecializations>>,
|
||||
external_specializations_requested: MutMap<ModuleId, Vec<ExternalSpecializations<'a>>>,
|
||||
|
||||
/// Various information
|
||||
imports: MutMap<ModuleId, MutSet<ModuleId>>,
|
||||
|
@ -715,7 +715,7 @@ enum Msg<'a> {
|
|||
module_id: ModuleId,
|
||||
ident_ids: IdentIds,
|
||||
layout_cache: LayoutCache<'a>,
|
||||
external_specializations_requested: BumpMap<ModuleId, ExternalSpecializations>,
|
||||
external_specializations_requested: BumpMap<ModuleId, ExternalSpecializations<'a>>,
|
||||
procs_base: ProcsBase<'a>,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
update_mode_ids: UpdateModeIds,
|
||||
|
@ -1007,7 +1007,7 @@ enum BuildTask<'a> {
|
|||
subs: Subs,
|
||||
procs_base: ProcsBase<'a>,
|
||||
layout_cache: LayoutCache<'a>,
|
||||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||
specializations_we_must_make: Vec<ExternalSpecializations<'a>>,
|
||||
module_timing: ModuleTiming,
|
||||
world_abilities: WorldAbilities,
|
||||
derived_symbols: GlobalDerivedSymbols,
|
||||
|
@ -2592,9 +2592,11 @@ fn finish_specialization(
|
|||
.into_inner()
|
||||
.into_module_ids();
|
||||
|
||||
let all_ident_ids = state.constrained_ident_ids;
|
||||
|
||||
let interns = Interns {
|
||||
module_ids,
|
||||
all_ident_ids: state.constrained_ident_ids,
|
||||
all_ident_ids,
|
||||
};
|
||||
|
||||
let State {
|
||||
|
@ -2660,6 +2662,7 @@ fn finish_specialization(
|
|||
layout: roc_mono::ir::ProcLayout {
|
||||
arguments: &[],
|
||||
result: Layout::struct_no_name_order(&[]),
|
||||
captures_niche: CapturesNiche::no_niche(),
|
||||
},
|
||||
symbol,
|
||||
}
|
||||
|
@ -4407,7 +4410,7 @@ fn make_specializations<'a>(
|
|||
mut subs: Subs,
|
||||
procs_base: ProcsBase<'a>,
|
||||
mut layout_cache: LayoutCache<'a>,
|
||||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||
specializations_we_must_make: Vec<ExternalSpecializations<'a>>,
|
||||
mut module_timing: ModuleTiming,
|
||||
target_info: TargetInfo,
|
||||
world_abilities: WorldAbilities,
|
||||
|
@ -4571,7 +4574,7 @@ fn build_pending_specializations<'a>(
|
|||
|
||||
procs_base.host_specializations.insert_host_exposed(
|
||||
mono_env.subs,
|
||||
symbol,
|
||||
LambdaName::no_niche(symbol),
|
||||
annotation,
|
||||
expr_var,
|
||||
);
|
||||
|
@ -4631,7 +4634,7 @@ fn build_pending_specializations<'a>(
|
|||
|
||||
procs_base.host_specializations.insert_host_exposed(
|
||||
mono_env.subs,
|
||||
symbol,
|
||||
LambdaName::no_niche(symbol),
|
||||
annotation,
|
||||
expr_var,
|
||||
);
|
||||
|
@ -4709,7 +4712,7 @@ fn build_pending_specializations<'a>(
|
|||
|
||||
procs_base.host_specializations.insert_host_exposed(
|
||||
mono_env.subs,
|
||||
symbol,
|
||||
LambdaName::no_niche(symbol),
|
||||
annotation,
|
||||
expr_var,
|
||||
);
|
||||
|
@ -4769,7 +4772,7 @@ fn build_pending_specializations<'a>(
|
|||
|
||||
procs_base.host_specializations.insert_host_exposed(
|
||||
mono_env.subs,
|
||||
symbol,
|
||||
LambdaName::no_niche(symbol),
|
||||
annotation,
|
||||
expr_var,
|
||||
);
|
||||
|
|
|
@ -163,7 +163,7 @@ impl<'a> DeclarationToIndex<'a> {
|
|||
}
|
||||
}
|
||||
unreachable!(
|
||||
"symbol/layout {:?} {:?} combo must be in DeclarationToIndex",
|
||||
"symbol/layout {:?} {:#?} combo must be in DeclarationToIndex",
|
||||
needle_symbol, needle_layout
|
||||
)
|
||||
}
|
||||
|
@ -263,7 +263,7 @@ impl<'a> ParamMap<'a> {
|
|||
self.declarations[index + i] = param;
|
||||
}
|
||||
|
||||
self.visit_stmt(arena, proc.name, &proc.body);
|
||||
self.visit_stmt(arena, proc.name.name(), &proc.body);
|
||||
}
|
||||
|
||||
fn visit_proc_always_owned(
|
||||
|
@ -282,7 +282,7 @@ impl<'a> ParamMap<'a> {
|
|||
self.declarations[index + i] = param;
|
||||
}
|
||||
|
||||
self.visit_stmt(arena, proc.name, &proc.body);
|
||||
self.visit_stmt(arena, proc.name.name(), &proc.body);
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, arena: &'a Bump, _fnid: Symbol, stmt: &Stmt<'a>) {
|
||||
|
@ -501,11 +501,12 @@ impl<'a> BorrowInfState<'a> {
|
|||
arg_layouts,
|
||||
..
|
||||
} => {
|
||||
let top_level = ProcLayout::new(self.arena, arg_layouts, **ret_layout);
|
||||
let top_level =
|
||||
ProcLayout::new(self.arena, arg_layouts, name.captures_niche(), **ret_layout);
|
||||
|
||||
// get the borrow signature of the applied function
|
||||
let ps = param_map
|
||||
.get_symbol(*name, top_level)
|
||||
.get_symbol(name.name(), top_level)
|
||||
.expect("function is defined");
|
||||
|
||||
// the return value will be owned
|
||||
|
@ -544,9 +545,11 @@ impl<'a> BorrowInfState<'a> {
|
|||
let closure_layout = ProcLayout {
|
||||
arguments: passed_function.argument_layouts,
|
||||
result: passed_function.return_layout,
|
||||
captures_niche: passed_function.name.captures_niche(),
|
||||
};
|
||||
|
||||
let function_ps = match param_map.get_symbol(passed_function.name, closure_layout) {
|
||||
let function_ps =
|
||||
match param_map.get_symbol(passed_function.name.name(), closure_layout) {
|
||||
Some(function_ps) => function_ps,
|
||||
None => unreachable!(),
|
||||
};
|
||||
|
@ -743,12 +746,13 @@ impl<'a> BorrowInfState<'a> {
|
|||
Stmt::Ret(z),
|
||||
) = (v, b)
|
||||
{
|
||||
let top_level = ProcLayout::new(self.arena, arg_layouts, **ret_layout);
|
||||
let top_level =
|
||||
ProcLayout::new(self.arena, arg_layouts, g.captures_niche(), **ret_layout);
|
||||
|
||||
if self.current_proc == *g && x == *z {
|
||||
if self.current_proc == g.name() && x == *z {
|
||||
// anonymous functions (for which the ps may not be known)
|
||||
// can never be tail-recursive, so this is fine
|
||||
if let Some(ps) = param_map.get_symbol(*g, top_level) {
|
||||
if let Some(ps) = param_map.get_symbol(g.name(), top_level) {
|
||||
self.own_params_using_args(ys, ps)
|
||||
}
|
||||
}
|
||||
|
@ -852,10 +856,10 @@ impl<'a> BorrowInfState<'a> {
|
|||
|
||||
let ys = Vec::from_iter_in(proc.args.iter().map(|t| t.1), self.arena).into_bump_slice();
|
||||
self.update_param_set_symbols(ys);
|
||||
self.current_proc = proc.name;
|
||||
self.current_proc = proc.name.name();
|
||||
|
||||
// ensure that current_proc is in the owned map
|
||||
self.owned.entry(proc.name).or_default();
|
||||
self.owned.entry(proc.name.name()).or_default();
|
||||
|
||||
self.collect_stmt(param_map, &proc.body);
|
||||
self.update_param_map_declaration(param_map, param_offset, proc.args.len());
|
||||
|
@ -978,7 +982,7 @@ fn call_info_call<'a>(call: &crate::ir::Call<'a>, info: &mut CallInfo<'a>) {
|
|||
|
||||
match call.call_type {
|
||||
ByName { name, .. } => {
|
||||
info.keys.push(name);
|
||||
info.keys.push(name.name());
|
||||
}
|
||||
Foreign { .. } => {}
|
||||
LowLevel { .. } => {}
|
||||
|
|
|
@ -530,12 +530,43 @@ fn eq_tag_fields<'a>(
|
|||
}
|
||||
|
||||
fn eq_boxed<'a>(
|
||||
_root: &mut CodeGenHelp<'a>,
|
||||
_ident_ids: &mut IdentIds,
|
||||
_ctx: &mut Context<'a>,
|
||||
_inner_layout: &'a Layout<'a>,
|
||||
root: &mut CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
ctx: &mut Context<'a>,
|
||||
inner_layout: &'a Layout<'a>,
|
||||
) -> Stmt<'a> {
|
||||
todo!()
|
||||
let a = root.create_symbol(ident_ids, "a");
|
||||
let b = root.create_symbol(ident_ids, "b");
|
||||
let result = root.create_symbol(ident_ids, "result");
|
||||
|
||||
let a_expr = Expr::ExprUnbox { symbol: ARG_1 };
|
||||
let b_expr = Expr::ExprUnbox { symbol: ARG_2 };
|
||||
let eq_call_expr = root
|
||||
.call_specialized_op(ident_ids, ctx, *inner_layout, root.arena.alloc([a, b]))
|
||||
.unwrap();
|
||||
|
||||
Stmt::Let(
|
||||
a,
|
||||
a_expr,
|
||||
*inner_layout,
|
||||
root.arena.alloc(
|
||||
//
|
||||
Stmt::Let(
|
||||
b,
|
||||
b_expr,
|
||||
*inner_layout,
|
||||
root.arena.alloc(
|
||||
//
|
||||
Stmt::Let(
|
||||
result,
|
||||
eq_call_expr,
|
||||
LAYOUT_BOOL,
|
||||
root.arena.alloc(Stmt::Ret(result)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/// List equality
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::ir::{
|
|||
Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, ModifyRc, Proc, ProcLayout,
|
||||
SelfRecursive, Stmt, UpdateModeId,
|
||||
};
|
||||
use crate::layout::{Builtin, Layout, UnionLayout};
|
||||
use crate::layout::{Builtin, CapturesNiche, LambdaName, Layout, UnionLayout};
|
||||
|
||||
mod equality;
|
||||
mod refcount;
|
||||
|
@ -170,7 +170,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
let arg_layouts = self.arena.alloc([layout]);
|
||||
let expr = Expr::Call(Call {
|
||||
call_type: CallType::ByName {
|
||||
name: proc_name,
|
||||
name: LambdaName::no_niche(proc_name),
|
||||
ret_layout,
|
||||
arg_layouts,
|
||||
specialization_id: CallSpecId::BACKEND_DUMMY,
|
||||
|
@ -262,7 +262,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
|
||||
Some(Expr::Call(Call {
|
||||
call_type: CallType::ByName {
|
||||
name: proc_name,
|
||||
name: LambdaName::no_niche(proc_name),
|
||||
ret_layout,
|
||||
arg_layouts,
|
||||
specialization_id: CallSpecId::BACKEND_DUMMY,
|
||||
|
@ -286,11 +286,11 @@ impl<'a> CodeGenHelp<'a> {
|
|||
&mut self,
|
||||
ident_ids: &mut IdentIds,
|
||||
ctx: &mut Context<'a>,
|
||||
layout: Layout<'a>,
|
||||
orig_layout: Layout<'a>,
|
||||
) -> Symbol {
|
||||
use HelperOp::*;
|
||||
|
||||
let layout = self.replace_rec_ptr(ctx, layout);
|
||||
let layout = self.replace_rec_ptr(ctx, orig_layout);
|
||||
|
||||
let found = self
|
||||
.specializations
|
||||
|
@ -343,7 +343,7 @@ impl<'a> CodeGenHelp<'a> {
|
|||
};
|
||||
|
||||
self.specializations[spec_index].proc = Some(Proc {
|
||||
name: proc_symbol,
|
||||
name: LambdaName::no_niche(proc_symbol),
|
||||
args,
|
||||
body,
|
||||
closure_data_layout: None,
|
||||
|
@ -375,19 +375,23 @@ impl<'a> CodeGenHelp<'a> {
|
|||
HelperOp::Inc => ProcLayout {
|
||||
arguments: self.arena.alloc([*layout, self.layout_isize]),
|
||||
result: LAYOUT_UNIT,
|
||||
captures_niche: CapturesNiche::no_niche(),
|
||||
},
|
||||
HelperOp::Dec => ProcLayout {
|
||||
arguments: self.arena.alloc([*layout]),
|
||||
result: LAYOUT_UNIT,
|
||||
captures_niche: CapturesNiche::no_niche(),
|
||||
},
|
||||
HelperOp::Reset => ProcLayout {
|
||||
arguments: self.arena.alloc([*layout]),
|
||||
result: *layout,
|
||||
captures_niche: CapturesNiche::no_niche(),
|
||||
},
|
||||
HelperOp::DecRef(_) => unreachable!("No generated Proc for DecRef"),
|
||||
HelperOp::Eq => ProcLayout {
|
||||
arguments: self.arena.alloc([*layout, *layout]),
|
||||
result: LAYOUT_BOOL,
|
||||
captures_niche: CapturesNiche::no_niche(),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -450,7 +454,9 @@ impl<'a> CodeGenHelp<'a> {
|
|||
layout
|
||||
}
|
||||
|
||||
Layout::Boxed(inner) => self.replace_rec_ptr(ctx, *inner),
|
||||
Layout::Boxed(inner) => {
|
||||
Layout::Boxed(self.arena.alloc(self.replace_rec_ptr(ctx, *inner)))
|
||||
}
|
||||
|
||||
Layout::LambdaSet(lambda_set) => {
|
||||
self.replace_rec_ptr(ctx, lambda_set.runtime_representation())
|
||||
|
|
|
@ -129,7 +129,9 @@ pub fn refcount_generic<'a>(
|
|||
Layout::RecursivePointer => unreachable!(
|
||||
"We should never call a refcounting helper on a RecursivePointer layout directly"
|
||||
),
|
||||
Layout::Boxed(_) => rc_todo(),
|
||||
Layout::Boxed(inner_layout) => {
|
||||
refcount_boxed(root, ident_ids, ctx, &layout, inner_layout, structure)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,7 +345,7 @@ pub fn is_rc_implemented_yet(layout: &Layout) -> bool {
|
|||
is_rc_implemented_yet(&lambda_set.runtime_representation())
|
||||
}
|
||||
Layout::RecursivePointer => true,
|
||||
Layout::Boxed(_) => false,
|
||||
Layout::Boxed(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1465,3 +1467,66 @@ fn refcount_tag_fields<'a>(
|
|||
|
||||
stmt
|
||||
}
|
||||
|
||||
fn refcount_boxed<'a>(
|
||||
root: &mut CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
ctx: &mut Context<'a>,
|
||||
layout: &Layout,
|
||||
inner_layout: &'a Layout,
|
||||
outer: Symbol,
|
||||
) -> Stmt<'a> {
|
||||
let arena = root.arena;
|
||||
|
||||
//
|
||||
// modify refcount of the inner and outer structures
|
||||
// RC on inner first, to avoid use-after-free for Dec
|
||||
// We're defining statements in reverse, so define outer first
|
||||
//
|
||||
|
||||
let rc_ptr = root.create_symbol(ident_ids, "rc_ptr");
|
||||
let alignment = layout.alignment_bytes(root.target_info);
|
||||
let ret_stmt = rc_return_stmt(root, ident_ids, ctx);
|
||||
let modify_outer = modify_refcount(
|
||||
root,
|
||||
ident_ids,
|
||||
ctx,
|
||||
rc_ptr,
|
||||
alignment,
|
||||
arena.alloc(ret_stmt),
|
||||
);
|
||||
|
||||
let get_rc_and_modify_outer = rc_ptr_from_data_ptr(
|
||||
root,
|
||||
ident_ids,
|
||||
outer,
|
||||
rc_ptr,
|
||||
false,
|
||||
arena.alloc(modify_outer),
|
||||
);
|
||||
|
||||
if inner_layout.is_refcounted() && !ctx.op.is_decref() {
|
||||
let inner = root.create_symbol(ident_ids, "inner");
|
||||
let inner_expr = Expr::ExprUnbox { symbol: outer };
|
||||
|
||||
let mod_inner_unit = root.create_symbol(ident_ids, "mod_inner_unit");
|
||||
let mod_inner_args = refcount_args(root, ctx, inner);
|
||||
let mod_inner_expr = root
|
||||
.call_specialized_op(ident_ids, ctx, *inner_layout, mod_inner_args)
|
||||
.unwrap();
|
||||
|
||||
Stmt::Let(
|
||||
inner,
|
||||
inner_expr,
|
||||
*inner_layout,
|
||||
arena.alloc(Stmt::Let(
|
||||
mod_inner_unit,
|
||||
mod_inner_expr,
|
||||
LAYOUT_UNIT,
|
||||
arena.alloc(get_rc_and_modify_outer),
|
||||
)),
|
||||
)
|
||||
} else {
|
||||
get_rc_and_modify_outer
|
||||
}
|
||||
}
|
||||
|
|
|
@ -564,12 +564,13 @@ impl<'a> Context<'a> {
|
|||
arg_layouts,
|
||||
..
|
||||
} => {
|
||||
let top_level = ProcLayout::new(self.arena, arg_layouts, **ret_layout);
|
||||
let top_level =
|
||||
ProcLayout::new(self.arena, arg_layouts, name.captures_niche(), **ret_layout);
|
||||
|
||||
// get the borrow signature
|
||||
let ps = self
|
||||
.param_map
|
||||
.get_symbol(*name, top_level)
|
||||
.get_symbol(name.name(), top_level)
|
||||
.expect("function is defined");
|
||||
|
||||
let v = Expr::Call(crate::ir::Call {
|
||||
|
@ -614,11 +615,12 @@ impl<'a> Context<'a> {
|
|||
let function_layout = ProcLayout {
|
||||
arguments: passed_function.argument_layouts,
|
||||
result: passed_function.return_layout,
|
||||
captures_niche: passed_function.name.captures_niche(),
|
||||
};
|
||||
|
||||
let function_ps = match self
|
||||
.param_map
|
||||
.get_symbol(passed_function.name, function_layout)
|
||||
.get_symbol(passed_function.name.name(), function_layout)
|
||||
{
|
||||
Some(function_ps) => function_ps,
|
||||
None => unreachable!(),
|
||||
|
@ -1406,7 +1408,7 @@ fn visit_proc<'a, 'i>(
|
|||
proc: &mut Proc<'a>,
|
||||
layout: ProcLayout<'a>,
|
||||
) {
|
||||
let params = match param_map.get_symbol(proc.name, layout) {
|
||||
let params = match param_map.get_symbol(proc.name.name(), layout) {
|
||||
Some(slice) => slice,
|
||||
None => Vec::from_iter_in(
|
||||
proc.args.iter().cloned().map(|(layout, symbol)| Param {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,6 @@ use roc_module::ident::{Lowercase, TagName};
|
|||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_problem::can::RuntimeError;
|
||||
use roc_target::{PtrWidth, TargetInfo};
|
||||
use roc_types::pretty_print::ResolvedLambdaSet;
|
||||
use roc_types::subs::{
|
||||
self, Content, FlatType, Label, RecordFields, Subs, UnionTags, UnsortedUnionLabels, Variable,
|
||||
};
|
||||
|
@ -226,7 +225,7 @@ impl<'a> RawFunctionLayout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct FieldOrderHash(u64);
|
||||
|
||||
impl FieldOrderHash {
|
||||
|
@ -248,7 +247,7 @@ impl FieldOrderHash {
|
|||
}
|
||||
|
||||
/// Types for code gen must be monomorphic. No type variables allowed!
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum Layout<'a> {
|
||||
Builtin(Builtin<'a>),
|
||||
Struct {
|
||||
|
@ -270,7 +269,7 @@ pub enum Layout<'a> {
|
|||
RecursivePointer,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum UnionLayout<'a> {
|
||||
/// A non-recursive tag union
|
||||
/// e.g. `Result a e : [Ok a, Err e]`
|
||||
|
@ -693,7 +692,74 @@ impl std::fmt::Debug for LambdaSet<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
/// Sometimes we can end up with lambdas of the same name and different captures in the same
|
||||
/// lambda set, like `fun` having lambda set `[[thunk U64, thunk U8]]` due to the following program:
|
||||
///
|
||||
/// ```roc
|
||||
/// capture : _ -> ({} -> Str)
|
||||
/// capture = \val ->
|
||||
/// thunk = \{} -> Num.toStr val
|
||||
/// thunk
|
||||
///
|
||||
/// fun = \x ->
|
||||
/// when x is
|
||||
/// True -> capture 123u64
|
||||
/// False -> capture 18u8
|
||||
/// ```
|
||||
///
|
||||
/// By recording the captures layouts this lambda expects in its identifier, we can distinguish
|
||||
/// between such differences when constructing closure capture data.
|
||||
///
|
||||
/// See also https://github.com/rtfeldman/roc/issues/3336.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct CapturesNiche<'a>(&'a [Layout<'a>]);
|
||||
|
||||
impl CapturesNiche<'_> {
|
||||
pub fn no_niche() -> Self {
|
||||
Self(&[])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct LambdaName<'a> {
|
||||
name: Symbol,
|
||||
captures_niche: CapturesNiche<'a>,
|
||||
}
|
||||
|
||||
impl<'a> LambdaName<'a> {
|
||||
#[inline(always)]
|
||||
pub fn name(&self) -> Symbol {
|
||||
self.name
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn captures_niche(&self) -> CapturesNiche<'a> {
|
||||
self.captures_niche
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn no_captures(&self) -> bool {
|
||||
self.captures_niche.0.is_empty()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn no_niche(name: Symbol) -> Self {
|
||||
Self {
|
||||
name,
|
||||
captures_niche: CapturesNiche::no_niche(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn replace_name(&self, name: Symbol) -> Self {
|
||||
Self {
|
||||
name,
|
||||
captures_niche: self.captures_niche,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct LambdaSet<'a> {
|
||||
/// collection of function names and their closure arguments
|
||||
pub set: &'a [(Symbol, &'a [Layout<'a>])],
|
||||
|
@ -741,23 +807,57 @@ impl<'a> LambdaSet<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn member_does_not_need_closure_argument(&self, function_symbol: Symbol) -> bool {
|
||||
match self.layout_for_member(function_symbol) {
|
||||
ClosureRepresentation::Union {
|
||||
alphabetic_order_fields,
|
||||
..
|
||||
} => alphabetic_order_fields.is_empty(),
|
||||
ClosureRepresentation::AlphabeticOrderStruct(fields) => fields.is_empty(),
|
||||
ClosureRepresentation::Other(_) => false,
|
||||
pub fn iter_set(&self) -> impl ExactSizeIterator<Item = LambdaName<'a>> {
|
||||
self.set.iter().map(|(name, captures_layouts)| LambdaName {
|
||||
name: *name,
|
||||
captures_niche: CapturesNiche(captures_layouts),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn layout_for_member_with_lambda_name(
|
||||
&self,
|
||||
lambda_name: LambdaName,
|
||||
) -> ClosureRepresentation<'a> {
|
||||
debug_assert!(self.contains(lambda_name.name));
|
||||
|
||||
let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| {
|
||||
other_name == lambda_name.name
|
||||
&& other_captures_layouts
|
||||
.iter()
|
||||
.eq(lambda_name.captures_niche.0)
|
||||
};
|
||||
|
||||
self.layout_for_member(comparator)
|
||||
}
|
||||
|
||||
/// Finds an alias name for a possible-multimorphic lambda variant in the lambda set.
|
||||
pub fn find_lambda_name(
|
||||
&self,
|
||||
function_symbol: Symbol,
|
||||
captures_layouts: &[Layout],
|
||||
) -> LambdaName<'a> {
|
||||
debug_assert!(self.contains(function_symbol), "function symbol not in set");
|
||||
|
||||
let comparator = |other_name: Symbol, other_captures_layouts: &[Layout]| {
|
||||
other_name == function_symbol && other_captures_layouts.iter().eq(captures_layouts)
|
||||
};
|
||||
|
||||
let (name, layouts) = self
|
||||
.set
|
||||
.iter()
|
||||
.find(|(name, layouts)| comparator(*name, layouts))
|
||||
.expect("no lambda set found");
|
||||
|
||||
LambdaName {
|
||||
name: *name,
|
||||
captures_niche: CapturesNiche(layouts),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn layout_for_member(&self, function_symbol: Symbol) -> ClosureRepresentation<'a> {
|
||||
debug_assert!(
|
||||
self.set.iter().any(|(s, _)| *s == function_symbol),
|
||||
"function symbol not in set"
|
||||
);
|
||||
|
||||
fn layout_for_member<F>(&self, comparator: F) -> ClosureRepresentation<'a>
|
||||
where
|
||||
F: Fn(Symbol, &[Layout]) -> bool,
|
||||
{
|
||||
match self.representation {
|
||||
Layout::Union(union) => {
|
||||
// here we rely on the fact that a union in a closure would be stored in a one-element record.
|
||||
|
@ -766,17 +866,19 @@ impl<'a> LambdaSet<'a> {
|
|||
UnionLayout::NonRecursive(_) => {
|
||||
// get the fields from the set, where they are sorted in alphabetic order
|
||||
// (and not yet sorted by their alignment)
|
||||
let (index, (_, fields)) = self
|
||||
let (index, (name, fields)) = self
|
||||
.set
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, (s, _))| *s == function_symbol)
|
||||
.find(|(_, (s, layouts))| comparator(*s, layouts))
|
||||
.unwrap();
|
||||
|
||||
let closure_name = *name;
|
||||
|
||||
ClosureRepresentation::Union {
|
||||
tag_id: index as TagIdIntType,
|
||||
alphabetic_order_fields: fields,
|
||||
closure_name: function_symbol,
|
||||
closure_name,
|
||||
union_layout: *union,
|
||||
}
|
||||
}
|
||||
|
@ -793,12 +895,14 @@ impl<'a> LambdaSet<'a> {
|
|||
}
|
||||
}
|
||||
Layout::Struct { .. } => {
|
||||
debug_assert_eq!(self.set.len(), 1);
|
||||
|
||||
// get the fields from the set, where they are sorted in alphabetic order
|
||||
// (and not yet sorted by their alignment)
|
||||
let (_, fields) = self
|
||||
.set
|
||||
.iter()
|
||||
.find(|(s, _)| *s == function_symbol)
|
||||
.find(|(s, layouts)| comparator(*s, layouts))
|
||||
.unwrap();
|
||||
|
||||
ClosureRepresentation::AlphabeticOrderStruct(fields)
|
||||
|
@ -846,12 +950,21 @@ impl<'a> LambdaSet<'a> {
|
|||
closure_var: Variable,
|
||||
target_info: TargetInfo,
|
||||
) -> Result<Self, LayoutProblem> {
|
||||
match roc_types::pretty_print::resolve_lambda_set(subs, closure_var) {
|
||||
match resolve_lambda_set(subs, closure_var) {
|
||||
ResolvedLambdaSet::Set(mut lambdas) => {
|
||||
// sort the tags; make sure ordering stays intact!
|
||||
lambdas.sort();
|
||||
lambdas.sort_by_key(|(sym, _)| *sym);
|
||||
|
||||
let mut set = Vec::with_capacity_in(lambdas.len(), arena);
|
||||
let mut set: Vec<(Symbol, &[Layout])> = Vec::with_capacity_in(lambdas.len(), arena);
|
||||
let mut set_with_variables: std::vec::Vec<(Symbol, std::vec::Vec<Variable>)> =
|
||||
std::vec::Vec::with_capacity(lambdas.len());
|
||||
|
||||
let mut last_function_symbol = None;
|
||||
let mut lambdas_it = lambdas.iter().peekable();
|
||||
|
||||
let mut has_duplicate_lambda_names = false;
|
||||
while let Some((function_symbol, variables)) = lambdas_it.next() {
|
||||
let mut arguments = Vec::with_capacity_in(variables.len(), arena);
|
||||
|
||||
let mut env = Env {
|
||||
arena,
|
||||
|
@ -860,18 +973,63 @@ impl<'a> LambdaSet<'a> {
|
|||
target_info,
|
||||
};
|
||||
|
||||
for (function_symbol, variables) in lambdas.iter() {
|
||||
let mut arguments = Vec::with_capacity_in(variables.len(), arena);
|
||||
|
||||
for var in variables {
|
||||
arguments.push(Layout::from_var(&mut env, *var)?);
|
||||
}
|
||||
|
||||
set.push((*function_symbol, arguments.into_bump_slice()));
|
||||
let arguments = arguments.into_bump_slice();
|
||||
|
||||
let is_multimorphic = match (last_function_symbol, lambdas_it.peek()) {
|
||||
(None, None) => false,
|
||||
(Some(sym), None) | (None, Some((sym, _))) => function_symbol == sym,
|
||||
(Some(sym1), Some((sym2, _))) => {
|
||||
function_symbol == sym1 || function_symbol == sym2
|
||||
}
|
||||
};
|
||||
|
||||
has_duplicate_lambda_names = has_duplicate_lambda_names || is_multimorphic;
|
||||
|
||||
set.push((*function_symbol, arguments));
|
||||
set_with_variables.push((*function_symbol, variables.to_vec()));
|
||||
|
||||
last_function_symbol = Some(function_symbol);
|
||||
}
|
||||
|
||||
let representation =
|
||||
arena.alloc(Self::make_representation(arena, subs, lambdas, target_info));
|
||||
let (set, set_with_variables) = if has_duplicate_lambda_names {
|
||||
// If we have a lambda set with duplicate names, then we sort first by name,
|
||||
// and break ties by sorting on the layout. We need to do this again since the
|
||||
// first sort would not have sorted on the layout.
|
||||
|
||||
// TODO: be more efficient, we can compute the permutation once and then apply
|
||||
// it to both vectors.
|
||||
let mut joined = set
|
||||
.into_iter()
|
||||
.zip(set_with_variables.into_iter())
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
joined.sort_by(|(lam_and_captures1, _), (lam_and_captures2, _)| {
|
||||
lam_and_captures1.cmp(lam_and_captures2)
|
||||
});
|
||||
// Remove duplicate lambda captures layouts unification can't see as
|
||||
// duplicates, for example [[Thunk {a: Str}, Thunk [A Str]]], each of which are
|
||||
// newtypes over the lambda layout `Thunk Str`.
|
||||
joined.dedup_by_key(|((name, captures), _)| (*name, *captures));
|
||||
|
||||
let (set, set_with_variables): (std::vec::Vec<_>, std::vec::Vec<_>) =
|
||||
joined.into_iter().unzip();
|
||||
|
||||
let set = Vec::from_iter_in(set, arena);
|
||||
|
||||
(set, set_with_variables)
|
||||
} else {
|
||||
(set, set_with_variables)
|
||||
};
|
||||
|
||||
let representation = arena.alloc(Self::make_representation(
|
||||
arena,
|
||||
subs,
|
||||
set_with_variables,
|
||||
target_info,
|
||||
));
|
||||
|
||||
Ok(LambdaSet {
|
||||
set: set.into_bump_slice(),
|
||||
|
@ -951,7 +1109,41 @@ impl<'a> LambdaSet<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
enum ResolvedLambdaSet {
|
||||
Set(std::vec::Vec<(Symbol, std::vec::Vec<Variable>)>),
|
||||
/// TODO: figure out if this can happen in a correct program, or is the result of a bug in our
|
||||
/// compiler. See https://github.com/rtfeldman/roc/issues/3163.
|
||||
Unbound,
|
||||
}
|
||||
|
||||
fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet {
|
||||
let mut set = vec![];
|
||||
loop {
|
||||
match subs.get_content_without_compacting(var) {
|
||||
Content::LambdaSet(subs::LambdaSet {
|
||||
solved,
|
||||
recursion_var: _,
|
||||
unspecialized,
|
||||
}) => {
|
||||
debug_assert!(
|
||||
unspecialized.is_empty(),
|
||||
"unspecialized lambda sets left over during resolution: {:?}",
|
||||
roc_types::subs::SubsFmtContent(subs.get_content_without_compacting(var), subs),
|
||||
);
|
||||
roc_types::pretty_print::push_union(subs, solved, &mut set);
|
||||
return ResolvedLambdaSet::Set(set);
|
||||
}
|
||||
Content::RecursionVar { structure, .. } => {
|
||||
var = *structure;
|
||||
}
|
||||
Content::FlexVar(_) => return ResolvedLambdaSet::Unbound,
|
||||
|
||||
c => internal_error!("called with a non-lambda set {:?}", c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum Builtin<'a> {
|
||||
Int(IntWidth),
|
||||
Float(FloatWidth),
|
||||
|
|
|
@ -97,7 +97,6 @@ fn function_s<'a, 'i>(
|
|||
Expr::Tag {
|
||||
tag_layout,
|
||||
tag_id,
|
||||
tag_name,
|
||||
arguments,
|
||||
} if may_reuse(*tag_layout, *tag_id, c) => {
|
||||
// for now, always overwrite the tag ID just to be sure
|
||||
|
@ -109,7 +108,6 @@ fn function_s<'a, 'i>(
|
|||
update_tag_id,
|
||||
tag_layout: *tag_layout,
|
||||
tag_id: *tag_id,
|
||||
tag_name: tag_name.clone(),
|
||||
arguments,
|
||||
};
|
||||
let new_stmt = Let(*symbol, new_expr, *layout, continuation);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![allow(clippy::manual_map)]
|
||||
|
||||
use crate::ir::{CallType, Expr, JoinPointId, Param, Stmt};
|
||||
use crate::layout::Layout;
|
||||
use crate::layout::{LambdaName, Layout};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
@ -31,7 +31,7 @@ use roc_module::symbol::Symbol;
|
|||
pub fn make_tail_recursive<'a>(
|
||||
arena: &'a Bump,
|
||||
id: JoinPointId,
|
||||
needle: Symbol,
|
||||
needle: LambdaName,
|
||||
stmt: Stmt<'a>,
|
||||
args: &'a [(Layout<'a>, Symbol, Symbol)],
|
||||
ret_layout: Layout,
|
||||
|
@ -71,7 +71,7 @@ fn insert_jumps<'a>(
|
|||
arena: &'a Bump,
|
||||
stmt: &'a Stmt<'a>,
|
||||
goal_id: JoinPointId,
|
||||
needle: Symbol,
|
||||
needle: LambdaName,
|
||||
needle_arguments: &'a [(Layout<'a>, Symbol, Symbol)],
|
||||
needle_result: Layout,
|
||||
) -> Option<&'a Stmt<'a>> {
|
||||
|
@ -80,7 +80,7 @@ fn insert_jumps<'a>(
|
|||
// to insert a tail-call, it must not just be a call to the function itself, but it must also
|
||||
// have the same layout. In particular when lambda sets get involved, a self-recursive call may
|
||||
// have a different type and should not be converted to a jump!
|
||||
let is_equal_function = |function_name: Symbol, arguments: &[_], result| {
|
||||
let is_equal_function = |function_name: LambdaName, arguments: &[_], result| {
|
||||
let it = needle_arguments.iter().map(|t| &t.0);
|
||||
needle == function_name && it.eq(arguments.iter()) && needle_result == result
|
||||
};
|
||||
|
|
|
@ -6953,6 +6953,101 @@ mod solve_expr {
|
|||
"#
|
||||
),
|
||||
"Str",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lambda_sets_collide_with_captured_var() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
capture : a -> ({} -> Str)
|
||||
capture = \val ->
|
||||
thunk =
|
||||
\{} ->
|
||||
when val is
|
||||
_ -> ""
|
||||
thunk
|
||||
|
||||
x : [True, False]
|
||||
|
||||
fun =
|
||||
when x is
|
||||
True -> capture ""
|
||||
# ^^^^^^^
|
||||
False -> capture {}
|
||||
# ^^^^^^^
|
||||
fun
|
||||
#^^^{-1}
|
||||
"#
|
||||
),
|
||||
&[
|
||||
"capture : Str -[[capture(1)]]-> ({} -[[thunk(5) {}, thunk(5) Str]]-> Str)",
|
||||
"capture : {} -[[capture(1)]]-> ({} -[[thunk(5) {}, thunk(5) Str]]-> Str)",
|
||||
"fun : {} -[[thunk(5) {}, thunk(5) Str]]-> Str",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lambda_sets_collide_with_captured_function() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
Lazy a : {} -> a
|
||||
|
||||
after : Lazy a, (a -> Lazy b) -> Lazy b
|
||||
after = \effect, map ->
|
||||
thunk = \{} ->
|
||||
when map (effect {}) is
|
||||
b -> b {}
|
||||
thunk
|
||||
|
||||
f = \_ -> \_ -> ""
|
||||
g = \{ s1 } -> \_ -> s1
|
||||
|
||||
x : [True, False]
|
||||
|
||||
fun =
|
||||
when x is
|
||||
True -> after (\{} -> "") f
|
||||
False -> after (\{} -> {s1: "s1"}) g
|
||||
fun
|
||||
#^^^{-1}
|
||||
"#
|
||||
),
|
||||
&[
|
||||
"fun : {} -[[thunk(9) (({} -[[15(15)]]-> { s1 : Str })) ({ s1 : Str } -[[g(4)]]-> ({} -[[13(13) Str]]-> Str)), \
|
||||
thunk(9) (({} -[[14(14)]]-> Str)) (Str -[[f(3)]]-> ({} -[[11(11)]]-> Str))]]-> Str",
|
||||
],
|
||||
print_only_under_alias = true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lambda_set_niche_same_layout_different_constructor() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
capture : a -> ({} -> Str)
|
||||
capture = \val ->
|
||||
thunk =
|
||||
\{} ->
|
||||
when val is
|
||||
_ -> ""
|
||||
thunk
|
||||
|
||||
x : [True, False]
|
||||
|
||||
fun =
|
||||
when x is
|
||||
True -> capture {a: ""}
|
||||
False -> capture (A "")
|
||||
fun
|
||||
#^^^{-1}
|
||||
"#
|
||||
),
|
||||
&["fun : {} -[[thunk(5) [A Str]*, thunk(5) { a : Str }]]-> Str",]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -672,3 +672,25 @@ fn compare_nullable_recursive_union_same_content() {
|
|||
bool
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn boxed_eq_int() {
|
||||
assert_evals_to!("Box.box 1 == Box.box 1", true, bool);
|
||||
assert_evals_to!("Box.box 2 == Box.box 1", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn boxed_eq_str() {
|
||||
assert_evals_to!(
|
||||
"Box.box \"Hello, world\" == Box.box \"Hello, world\"",
|
||||
true,
|
||||
bool
|
||||
);
|
||||
assert_evals_to!(
|
||||
"Box.box \"Hello, world\" == Box.box \"Hello, stranger\"",
|
||||
false,
|
||||
bool
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3260,7 +3260,7 @@ fn issue_2322() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn box_and_unbox_string() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -3278,7 +3278,7 @@ fn box_and_unbox_string() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn box_and_unbox_num() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -3292,7 +3292,7 @@ fn box_and_unbox_num() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn box_and_unbox_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -3306,7 +3306,7 @@ fn box_and_unbox_record() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn box_and_unbox_tag_union() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -3498,3 +3498,129 @@ fn polymorphic_lambda_captures_polymorphic_value() {
|
|||
u64
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn lambda_capture_niche_u64_vs_u8_capture() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
capture : _ -> ({} -> Str)
|
||||
capture = \val ->
|
||||
\{} ->
|
||||
Num.toStr val
|
||||
|
||||
x : [True, False]
|
||||
x = True
|
||||
|
||||
fun =
|
||||
when x is
|
||||
True -> capture 123u64
|
||||
False -> capture 18u8
|
||||
|
||||
fun {}
|
||||
"#
|
||||
),
|
||||
RocStr::from("123"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn lambda_capture_niches_with_other_lambda_capture() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
capture : _ -> ({} -> Str)
|
||||
capture = \val ->
|
||||
\{} ->
|
||||
Num.toStr val
|
||||
|
||||
capture2 = \val -> \{} -> val
|
||||
|
||||
f = \x ->
|
||||
g =
|
||||
when x is
|
||||
A -> capture 11u8
|
||||
B -> capture2 "lisa"
|
||||
C -> capture 187128u64
|
||||
g {}
|
||||
|
||||
{a: f A, b: f B, c: f C}
|
||||
"#
|
||||
),
|
||||
(
|
||||
RocStr::from("11"),
|
||||
RocStr::from("lisa"),
|
||||
RocStr::from("187128")
|
||||
),
|
||||
(RocStr, RocStr, RocStr)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn lambda_capture_niches_with_non_capturing_function() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
capture : _ -> ({} -> Str)
|
||||
capture = \val ->
|
||||
\{} ->
|
||||
Num.toStr val
|
||||
|
||||
triv = \{} -> "triv"
|
||||
|
||||
f = \x ->
|
||||
g =
|
||||
when x is
|
||||
A -> capture 11u8
|
||||
B -> triv
|
||||
C -> capture 187128u64
|
||||
g {}
|
||||
|
||||
{a: f A, b: f B, c: f C}
|
||||
"#
|
||||
),
|
||||
(
|
||||
RocStr::from("11"),
|
||||
RocStr::from("triv"),
|
||||
RocStr::from("187128")
|
||||
),
|
||||
(RocStr, RocStr, RocStr)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn lambda_capture_niches_have_captured_function_in_closure() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
Lazy a : {} -> a
|
||||
|
||||
after : Lazy a, (a -> Lazy b) -> Lazy b
|
||||
after = \effect, map ->
|
||||
thunk = \{} ->
|
||||
when map (effect {}) is
|
||||
b -> b {}
|
||||
thunk
|
||||
|
||||
f = \_ -> \_ -> "fun f"
|
||||
g = \{ s1 } -> \_ -> s1
|
||||
|
||||
fun = \x ->
|
||||
h =
|
||||
when x is
|
||||
True -> after (\{} -> "") f
|
||||
False -> after (\{} -> {s1: "s1"}) g
|
||||
h {}
|
||||
|
||||
{a: fun False, b: fun True}
|
||||
"#
|
||||
),
|
||||
(RocStr::from("s1"), RocStr::from("fun f")),
|
||||
(RocStr, RocStr)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -431,3 +431,46 @@ fn union_linked_list_long_dec() {
|
|||
&[Deallocated; 1_000]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-wasm"))]
|
||||
fn boxed_str_inc() {
|
||||
assert_refcounts!(
|
||||
indoc!(
|
||||
r#"
|
||||
s = Str.concat "A long enough string " "to be heap-allocated"
|
||||
b = Box.box s
|
||||
|
||||
Tuple b b
|
||||
"#
|
||||
),
|
||||
(Pointer, Pointer),
|
||||
&[
|
||||
Live(2), // s
|
||||
Live(2), // b
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-wasm"))]
|
||||
fn boxed_str_dec() {
|
||||
assert_refcounts!(
|
||||
indoc!(
|
||||
r#"
|
||||
s = Str.concat "A long enough string " "to be heap-allocated"
|
||||
b = Box.box s
|
||||
|
||||
if False then
|
||||
ReturnTheBox b
|
||||
else
|
||||
DeallocateEverything
|
||||
"#
|
||||
),
|
||||
(i32, i32),
|
||||
&[
|
||||
Deallocated, // s
|
||||
Deallocated, // b
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ fn str_split_empty_delimiter() {
|
|||
"#
|
||||
),
|
||||
1,
|
||||
i64
|
||||
usize
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
@ -41,7 +41,7 @@ fn str_split_empty_delimiter() {
|
|||
"#
|
||||
),
|
||||
3,
|
||||
i64
|
||||
usize
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ fn str_split_bigger_delimiter_small_str() {
|
|||
"#
|
||||
),
|
||||
1,
|
||||
i64
|
||||
usize
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
@ -71,7 +71,7 @@ fn str_split_bigger_delimiter_small_str() {
|
|||
"#
|
||||
),
|
||||
3,
|
||||
i64
|
||||
usize
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ fn str_split_small_str_big_delimiter() {
|
|||
"#
|
||||
),
|
||||
3,
|
||||
i64
|
||||
usize
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
|
|
@ -18,7 +18,7 @@ use roc_mono::ir::{
|
|||
Call, CallType, Expr, HostExposedLayouts, Literal, Proc, ProcLayout, SelfRecursive, Stmt,
|
||||
UpdateModeId,
|
||||
};
|
||||
use roc_mono::layout::{Builtin, Layout};
|
||||
use roc_mono::layout::{Builtin, CapturesNiche, LambdaName, Layout};
|
||||
|
||||
const LINKING_TEST_HOST_WASM: &str = "build/wasm_linking_test_host.wasm";
|
||||
const LINKING_TEST_HOST_NATIVE: &str = "build/wasm_linking_test_host";
|
||||
|
@ -110,7 +110,7 @@ fn build_app_mono<'a>(
|
|||
);
|
||||
|
||||
let proc = Proc {
|
||||
name: app_proc,
|
||||
name: LambdaName::no_niche(app_proc),
|
||||
args: &[],
|
||||
body,
|
||||
closure_data_layout: None,
|
||||
|
@ -123,6 +123,7 @@ fn build_app_mono<'a>(
|
|||
let proc_layout = ProcLayout {
|
||||
arguments: &[],
|
||||
result: int_layout,
|
||||
captures_niche: CapturesNiche::no_niche(),
|
||||
};
|
||||
|
||||
let mut app = MutMap::default();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
procedure List.6 (#Attr.2):
|
||||
let List.284 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.284;
|
||||
let List.266 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.266;
|
||||
|
||||
procedure Test.1 (Test.5):
|
||||
let Test.2 : I64 = 41i64;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
procedure Dict.1 ():
|
||||
let Dict.28 : Dict [] [] = lowlevel DictEmpty ;
|
||||
ret Dict.28;
|
||||
let Dict.16 : Dict [] [] = lowlevel DictEmpty ;
|
||||
ret Dict.16;
|
||||
|
||||
procedure Dict.7 (#Attr.2):
|
||||
let Dict.27 : U64 = lowlevel DictSize #Attr.2;
|
||||
let Dict.15 : U64 = lowlevel DictSize #Attr.2;
|
||||
dec #Attr.2;
|
||||
ret Dict.27;
|
||||
ret Dict.15;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.2 : Dict [] [] = CallByName Dict.1;
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
procedure List.2 (List.75, List.76):
|
||||
let List.290 : U64 = CallByName List.6 List.75;
|
||||
let List.286 : Int1 = CallByName Num.22 List.76 List.290;
|
||||
if List.286 then
|
||||
let List.288 : {} = CallByName List.60 List.75 List.76;
|
||||
let List.287 : [C {}, C {}] = Ok List.288;
|
||||
ret List.287;
|
||||
procedure List.2 (List.74, List.75):
|
||||
let List.272 : U64 = CallByName List.6 List.74;
|
||||
let List.268 : Int1 = CallByName Num.22 List.75 List.272;
|
||||
if List.268 then
|
||||
let List.270 : {} = CallByName List.60 List.74 List.75;
|
||||
let List.269 : [C {}, C {}] = TagId(1) List.270;
|
||||
ret List.269;
|
||||
else
|
||||
let List.285 : {} = Struct {};
|
||||
let List.284 : [C {}, C {}] = Err List.285;
|
||||
ret List.284;
|
||||
let List.267 : {} = Struct {};
|
||||
let List.266 : [C {}, C {}] = TagId(0) List.267;
|
||||
ret List.266;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.293 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.293;
|
||||
let List.275 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.275;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.292 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.292;
|
||||
let List.274 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.274;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.2 (Test.6):
|
||||
let Test.18 : Str = "bar";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure List.4 (#Attr.2, #Attr.3):
|
||||
let List.284 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3;
|
||||
ret List.284;
|
||||
let List.266 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3;
|
||||
ret List.266;
|
||||
|
||||
procedure Test.20 (Test.22):
|
||||
let Test.34 : {U8} = Struct {Test.22};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
procedure Num.20 (#Attr.2, #Attr.3):
|
||||
let Num.274 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Num.274;
|
||||
let Num.189 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Num.189;
|
||||
|
||||
procedure Num.21 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.1 (Test.15, Test.16):
|
||||
joinpoint Test.7 Test.2 Test.3:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Bool.7 (#Attr.2, #Attr.3):
|
||||
let Bool.14 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
ret Bool.14;
|
||||
let Bool.9 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
ret Bool.9;
|
||||
|
||||
procedure Test.1 (Test.3):
|
||||
let Test.6 : I64 = 10i64;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
procedure List.6 (#Attr.2):
|
||||
let List.284 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.284;
|
||||
let List.266 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.266;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.275 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.275;
|
||||
let Num.190 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.190;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.1 : List I64 = Array [1i64, 2i64];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.2 : I64 = 1i64;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.45 (#Attr.2):
|
||||
let Num.273 : I64 = lowlevel NumRound #Attr.2;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumRound #Attr.2;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.2 : Float64 = 3.6f64;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.1 : I64 = 3i64;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
procedure Num.40 (#Attr.2, #Attr.3):
|
||||
let Num.278 : I64 = 0i64;
|
||||
let Num.275 : Int1 = lowlevel NotEq #Attr.3 Num.278;
|
||||
if Num.275 then
|
||||
let Num.277 : I64 = lowlevel NumDivUnchecked #Attr.2 #Attr.3;
|
||||
let Num.276 : [C {}, C I64] = Ok Num.277;
|
||||
ret Num.276;
|
||||
let Num.193 : I64 = 0i64;
|
||||
let Num.190 : Int1 = lowlevel NotEq #Attr.3 Num.193;
|
||||
if Num.190 then
|
||||
let Num.192 : I64 = lowlevel NumDivUnchecked #Attr.2 #Attr.3;
|
||||
let Num.191 : [C {}, C I64] = TagId(1) Num.192;
|
||||
ret Num.191;
|
||||
else
|
||||
let Num.274 : {} = Struct {};
|
||||
let Num.273 : [C {}, C I64] = Err Num.274;
|
||||
ret Num.273;
|
||||
let Num.189 : {} = Struct {};
|
||||
let Num.188 : [C {}, C I64] = TagId(0) Num.189;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.8 : I64 = 1000i64;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.10 : I64 = 41i64;
|
||||
let Test.1 : [C I64, C ] = Just Test.10;
|
||||
let Test.1 : [C I64, C ] = TagId(0) Test.10;
|
||||
let Test.7 : U8 = 0i64;
|
||||
let Test.8 : U8 = GetTagId Test.1;
|
||||
let Test.9 : Int1 = lowlevel Eq Test.7 Test.8;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Test.0 ():
|
||||
let Test.9 : I64 = 3i64;
|
||||
let Test.3 : [C I64, C ] = Just Test.9;
|
||||
let Test.3 : [C I64, C ] = TagId(0) Test.9;
|
||||
let Test.6 : U8 = 0i64;
|
||||
let Test.7 : U8 = GetTagId Test.3;
|
||||
let Test.8 : Int1 = lowlevel Eq Test.6 Test.7;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
procedure Test.0 ():
|
||||
let Test.10 : I64 = 1i64;
|
||||
let Test.11 : I64 = 2i64;
|
||||
let Test.5 : [C I64, C I64 I64, C I64] = These Test.10 Test.11;
|
||||
let Test.5 : [C I64, C I64 I64, C I64] = TagId(1) Test.10 Test.11;
|
||||
let Test.9 : U8 = GetTagId Test.5;
|
||||
switch Test.9:
|
||||
case 2:
|
||||
|
|
|
@ -11,8 +11,8 @@ procedure Test.3 (Test.4):
|
|||
|
||||
procedure Test.0 ():
|
||||
let Test.16 : I64 = 2i64;
|
||||
let Test.17 : [<rnu><null>, C I64 *self] = Nil ;
|
||||
let Test.10 : [<rnu><null>, C I64 *self] = Cons Test.16 Test.17;
|
||||
let Test.17 : [<rnu><null>, C I64 *self] = TagId(1) ;
|
||||
let Test.10 : [<rnu><null>, C I64 *self] = TagId(0) Test.16 Test.17;
|
||||
let Test.9 : Int1 = CallByName Test.3 Test.10;
|
||||
dec Test.10;
|
||||
ret Test.9;
|
||||
|
|
|
@ -1,55 +1,55 @@
|
|||
procedure List.2 (List.75, List.76):
|
||||
let List.299 : U64 = CallByName List.6 List.75;
|
||||
let List.295 : Int1 = CallByName Num.22 List.76 List.299;
|
||||
if List.295 then
|
||||
let List.297 : I64 = CallByName List.60 List.75 List.76;
|
||||
let List.296 : [C {}, C I64] = Ok List.297;
|
||||
ret List.296;
|
||||
procedure List.2 (List.74, List.75):
|
||||
let List.281 : U64 = CallByName List.6 List.74;
|
||||
let List.277 : Int1 = CallByName Num.22 List.75 List.281;
|
||||
if List.277 then
|
||||
let List.279 : I64 = CallByName List.60 List.74 List.75;
|
||||
let List.278 : [C {}, C I64] = TagId(1) List.279;
|
||||
ret List.278;
|
||||
else
|
||||
let List.294 : {} = Struct {};
|
||||
let List.293 : [C {}, C I64] = Err List.294;
|
||||
ret List.293;
|
||||
let List.276 : {} = Struct {};
|
||||
let List.275 : [C {}, C I64] = TagId(0) List.276;
|
||||
ret List.275;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.300 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.300;
|
||||
let List.282 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.282;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.298 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.298;
|
||||
let List.280 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.280;
|
||||
|
||||
procedure List.9 (List.202):
|
||||
let List.291 : U64 = 0i64;
|
||||
let List.284 : [C {}, C I64] = CallByName List.2 List.202 List.291;
|
||||
let List.288 : U8 = 1i64;
|
||||
let List.289 : U8 = GetTagId List.284;
|
||||
let List.290 : Int1 = lowlevel Eq List.288 List.289;
|
||||
if List.290 then
|
||||
let List.203 : I64 = UnionAtIndex (Id 1) (Index 0) List.284;
|
||||
let List.285 : [C Int1, C I64] = Ok List.203;
|
||||
ret List.285;
|
||||
procedure List.9 (List.188):
|
||||
let List.273 : U64 = 0i64;
|
||||
let List.266 : [C {}, C I64] = CallByName List.2 List.188 List.273;
|
||||
let List.270 : U8 = 1i64;
|
||||
let List.271 : U8 = GetTagId List.266;
|
||||
let List.272 : Int1 = lowlevel Eq List.270 List.271;
|
||||
if List.272 then
|
||||
let List.189 : I64 = UnionAtIndex (Id 1) (Index 0) List.266;
|
||||
let List.267 : [C Int1, C I64] = TagId(1) List.189;
|
||||
ret List.267;
|
||||
else
|
||||
let List.287 : Int1 = true;
|
||||
let List.286 : [C Int1, C I64] = Err List.287;
|
||||
ret List.286;
|
||||
let List.269 : Int1 = true;
|
||||
let List.268 : [C Int1, C I64] = TagId(0) List.269;
|
||||
ret List.268;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Str.27 (#Attr.2):
|
||||
let #Attr.3 : {I64, U8} = lowlevel StrToNum #Attr.2;
|
||||
let Str.154 : U8 = StructAtIndex 1 #Attr.3;
|
||||
let Str.155 : U8 = 0i64;
|
||||
let Str.151 : Int1 = lowlevel NumGt Str.154 Str.155;
|
||||
if Str.151 then
|
||||
let Str.153 : Int1 = false;
|
||||
let Str.152 : [C Int1, C I64] = Err Str.153;
|
||||
ret Str.152;
|
||||
let Str.131 : U8 = StructAtIndex 1 #Attr.3;
|
||||
let Str.132 : U8 = 0i64;
|
||||
let Str.128 : Int1 = lowlevel NumGt Str.131 Str.132;
|
||||
if Str.128 then
|
||||
let Str.130 : Int1 = false;
|
||||
let Str.129 : [C Int1, C I64] = TagId(0) Str.130;
|
||||
ret Str.129;
|
||||
else
|
||||
let Str.150 : I64 = StructAtIndex 0 #Attr.3;
|
||||
let Str.149 : [C Int1, C I64] = Ok Str.150;
|
||||
ret Str.149;
|
||||
let Str.127 : I64 = StructAtIndex 0 #Attr.3;
|
||||
let Str.126 : [C Int1, C I64] = TagId(1) Str.127;
|
||||
ret Str.126;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.4 : Int1 = true;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Test.0 ():
|
||||
let Test.19 : [C [<rnnu>C [C *self, C ]], C ] = SystemTool ;
|
||||
let Test.17 : [<rnnu>C [C *self, C ]] = Job Test.19;
|
||||
let Test.16 : [C [<rnnu>C [C *self, C ]], C ] = FromJob Test.17;
|
||||
let Test.7 : [<rnnu>C [C *self, C ]] = Job Test.16;
|
||||
let Test.19 : [C [<rnnu>C [C *self, C ]], C ] = TagId(1) ;
|
||||
let Test.17 : [<rnnu>C [C *self, C ]] = TagId(0) Test.19;
|
||||
let Test.16 : [C [<rnnu>C [C *self, C ]], C ] = TagId(0) Test.17;
|
||||
let Test.7 : [<rnnu>C [C *self, C ]] = TagId(0) Test.16;
|
||||
ret Test.7;
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
procedure Num.94 (#Attr.2):
|
||||
let Num.188 : Str = lowlevel NumToStr #Attr.2;
|
||||
ret Num.188;
|
||||
|
||||
procedure Num.94 (#Attr.2):
|
||||
let Num.189 : Str = lowlevel NumToStr #Attr.2;
|
||||
ret Num.189;
|
||||
|
||||
procedure Test.1 (Test.4):
|
||||
let Test.16 : [C U8, C U64] = TagId(1) Test.4;
|
||||
ret Test.16;
|
||||
|
||||
procedure Test.1 (Test.4):
|
||||
let Test.22 : [C U8, C U64] = TagId(0) Test.4;
|
||||
ret Test.22;
|
||||
|
||||
procedure Test.5 (Test.17, #Attr.12):
|
||||
let Test.4 : U64 = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||
let Test.19 : Str = CallByName Num.94 Test.4;
|
||||
ret Test.19;
|
||||
|
||||
procedure Test.5 (Test.17, #Attr.12):
|
||||
let Test.4 : U8 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||
let Test.25 : Str = CallByName Num.94 Test.4;
|
||||
ret Test.25;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.2 : Int1 = true;
|
||||
joinpoint Test.13 Test.3:
|
||||
let Test.8 : {} = Struct {};
|
||||
let Test.9 : U8 = GetTagId Test.3;
|
||||
joinpoint Test.10 Test.7:
|
||||
ret Test.7;
|
||||
in
|
||||
switch Test.9:
|
||||
case 0:
|
||||
let Test.11 : Str = CallByName Test.5 Test.8 Test.3;
|
||||
jump Test.10 Test.11;
|
||||
|
||||
default:
|
||||
let Test.12 : Str = CallByName Test.5 Test.8 Test.3;
|
||||
jump Test.10 Test.12;
|
||||
|
||||
in
|
||||
let Test.26 : Int1 = true;
|
||||
let Test.27 : Int1 = lowlevel Eq Test.26 Test.2;
|
||||
if Test.27 then
|
||||
let Test.15 : U64 = 123i64;
|
||||
let Test.14 : [C U8, C U64] = CallByName Test.1 Test.15;
|
||||
jump Test.13 Test.14;
|
||||
else
|
||||
let Test.21 : U8 = 18i64;
|
||||
let Test.20 : [C U8, C U64] = CallByName Test.1 Test.21;
|
||||
jump Test.13 Test.20;
|
|
@ -0,0 +1,85 @@
|
|||
procedure Test.11 (Test.37):
|
||||
let Test.38 : Str = "";
|
||||
ret Test.38;
|
||||
|
||||
procedure Test.13 (Test.51, #Attr.12):
|
||||
let Test.12 : Str = StructAtIndex 0 #Attr.12;
|
||||
inc Test.12;
|
||||
dec #Attr.12;
|
||||
ret Test.12;
|
||||
|
||||
procedure Test.15 (Test.39):
|
||||
let Test.40 : Str = "";
|
||||
ret Test.40;
|
||||
|
||||
procedure Test.16 (Test.54):
|
||||
let Test.56 : Str = "s1";
|
||||
ret Test.56;
|
||||
|
||||
procedure Test.2 (Test.7, Test.8):
|
||||
let Test.9 : [C {} {}, C {} {}] = TagId(0) Test.7 Test.8;
|
||||
ret Test.9;
|
||||
|
||||
procedure Test.2 (Test.7, Test.8):
|
||||
let Test.9 : [C {} {}, C {} {}] = TagId(1) Test.7 Test.8;
|
||||
ret Test.9;
|
||||
|
||||
procedure Test.3 (Test.17):
|
||||
let Test.36 : {} = Struct {};
|
||||
ret Test.36;
|
||||
|
||||
procedure Test.4 (Test.18):
|
||||
let Test.50 : {Str} = Struct {Test.18};
|
||||
ret Test.50;
|
||||
|
||||
procedure Test.9 (Test.29, #Attr.12):
|
||||
let Test.8 : {} = UnionAtIndex (Id 0) (Index 1) #Attr.12;
|
||||
let Test.7 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||
let Test.35 : {} = Struct {};
|
||||
let Test.34 : Str = CallByName Test.15 Test.35;
|
||||
let Test.31 : {} = CallByName Test.3 Test.34;
|
||||
dec Test.34;
|
||||
let Test.33 : {} = Struct {};
|
||||
let Test.32 : Str = CallByName Test.11 Test.33;
|
||||
ret Test.32;
|
||||
|
||||
procedure Test.9 (Test.29, #Attr.12):
|
||||
let Test.8 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
||||
let Test.7 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||
let Test.49 : {} = Struct {};
|
||||
let Test.48 : Str = CallByName Test.16 Test.49;
|
||||
let Test.45 : {Str} = CallByName Test.4 Test.48;
|
||||
let Test.47 : {} = Struct {};
|
||||
let Test.46 : Str = CallByName Test.13 Test.47 Test.45;
|
||||
ret Test.46;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.5 : Int1 = true;
|
||||
joinpoint Test.25 Test.6:
|
||||
let Test.20 : {} = Struct {};
|
||||
let Test.21 : U8 = GetTagId Test.6;
|
||||
joinpoint Test.22 Test.19:
|
||||
ret Test.19;
|
||||
in
|
||||
switch Test.21:
|
||||
case 0:
|
||||
let Test.23 : Str = CallByName Test.9 Test.20 Test.6;
|
||||
jump Test.22 Test.23;
|
||||
|
||||
default:
|
||||
let Test.24 : Str = CallByName Test.9 Test.20 Test.6;
|
||||
jump Test.22 Test.24;
|
||||
|
||||
in
|
||||
let Test.57 : Int1 = true;
|
||||
let Test.58 : Int1 = lowlevel Eq Test.57 Test.5;
|
||||
if Test.58 then
|
||||
let Test.27 : {} = Struct {};
|
||||
let Test.28 : {} = Struct {};
|
||||
let Test.26 : [C {} {}, C {} {}] = CallByName Test.2 Test.27 Test.28;
|
||||
jump Test.25 Test.26;
|
||||
else
|
||||
let Test.42 : {} = Struct {};
|
||||
let Test.43 : {} = Struct {};
|
||||
let Test.41 : [C {} {}, C {} {}] = CallByName Test.2 Test.42 Test.43;
|
||||
jump Test.25 Test.41;
|
|
@ -0,0 +1,59 @@
|
|||
procedure Test.1 (Test.5):
|
||||
let Test.19 : [C , C U64, C {}] = TagId(2) Test.5;
|
||||
ret Test.19;
|
||||
|
||||
procedure Test.1 (Test.5):
|
||||
let Test.27 : [C , C U64, C {}] = TagId(1) Test.5;
|
||||
ret Test.27;
|
||||
|
||||
procedure Test.2 (Test.8):
|
||||
let Test.24 : Str = "";
|
||||
ret Test.24;
|
||||
|
||||
procedure Test.6 (Test.20, #Attr.12):
|
||||
let Test.5 : U64 = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||
let Test.30 : Str = "";
|
||||
ret Test.30;
|
||||
|
||||
procedure Test.6 (Test.20, #Attr.12):
|
||||
let Test.5 : {} = UnionAtIndex (Id 2) (Index 0) #Attr.12;
|
||||
let Test.22 : Str = "";
|
||||
ret Test.22;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.3 : U8 = 0u8;
|
||||
joinpoint Test.16 Test.4:
|
||||
let Test.10 : {} = Struct {};
|
||||
let Test.11 : U8 = GetTagId Test.4;
|
||||
joinpoint Test.12 Test.9:
|
||||
ret Test.9;
|
||||
in
|
||||
switch Test.11:
|
||||
case 0:
|
||||
let Test.13 : Str = CallByName Test.2 Test.10;
|
||||
jump Test.12 Test.13;
|
||||
|
||||
case 1:
|
||||
let Test.14 : Str = CallByName Test.6 Test.10 Test.4;
|
||||
jump Test.12 Test.14;
|
||||
|
||||
default:
|
||||
let Test.15 : Str = CallByName Test.6 Test.10 Test.4;
|
||||
jump Test.12 Test.15;
|
||||
|
||||
in
|
||||
switch Test.3:
|
||||
case 0:
|
||||
let Test.18 : {} = Struct {};
|
||||
let Test.17 : [C , C U64, C {}] = CallByName Test.1 Test.18;
|
||||
jump Test.16 Test.17;
|
||||
|
||||
case 1:
|
||||
let Test.2 : [C , C U64, C {}] = TagId(0) ;
|
||||
jump Test.16 Test.2;
|
||||
|
||||
default:
|
||||
let Test.26 : U64 = 1i64;
|
||||
let Test.25 : [C , C U64, C {}] = CallByName Test.1 Test.26;
|
||||
jump Test.16 Test.25;
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
procedure Test.1 (Test.5):
|
||||
let Test.20 : [C U64, C {}, C Str] = TagId(1) Test.5;
|
||||
ret Test.20;
|
||||
|
||||
procedure Test.1 (Test.5):
|
||||
let Test.32 : [C U64, C {}, C Str] = TagId(0) Test.5;
|
||||
ret Test.32;
|
||||
|
||||
procedure Test.2 (Test.7):
|
||||
let Test.26 : [C U64, C {}, C Str] = TagId(2) Test.7;
|
||||
ret Test.26;
|
||||
|
||||
procedure Test.6 (Test.21, #Attr.12):
|
||||
let Test.5 : U64 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||
dec #Attr.12;
|
||||
let Test.35 : Str = "";
|
||||
ret Test.35;
|
||||
|
||||
procedure Test.6 (Test.21, #Attr.12):
|
||||
let Test.5 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||
dec #Attr.12;
|
||||
let Test.23 : Str = "";
|
||||
ret Test.23;
|
||||
|
||||
procedure Test.8 (Test.27, #Attr.12):
|
||||
let Test.7 : Str = UnionAtIndex (Id 2) (Index 0) #Attr.12;
|
||||
inc Test.7;
|
||||
dec #Attr.12;
|
||||
ret Test.7;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.3 : U8 = 0u8;
|
||||
joinpoint Test.17 Test.4:
|
||||
let Test.11 : {} = Struct {};
|
||||
let Test.12 : U8 = GetTagId Test.4;
|
||||
joinpoint Test.13 Test.10:
|
||||
ret Test.10;
|
||||
in
|
||||
switch Test.12:
|
||||
case 0:
|
||||
let Test.14 : Str = CallByName Test.6 Test.11 Test.4;
|
||||
jump Test.13 Test.14;
|
||||
|
||||
case 1:
|
||||
let Test.15 : Str = CallByName Test.6 Test.11 Test.4;
|
||||
jump Test.13 Test.15;
|
||||
|
||||
default:
|
||||
let Test.16 : Str = CallByName Test.8 Test.11 Test.4;
|
||||
jump Test.13 Test.16;
|
||||
|
||||
in
|
||||
switch Test.3:
|
||||
case 0:
|
||||
let Test.19 : {} = Struct {};
|
||||
let Test.18 : [C U64, C {}, C Str] = CallByName Test.1 Test.19;
|
||||
jump Test.17 Test.18;
|
||||
|
||||
case 1:
|
||||
let Test.25 : Str = "foo";
|
||||
let Test.24 : [C U64, C {}, C Str] = CallByName Test.2 Test.25;
|
||||
jump Test.17 Test.24;
|
||||
|
||||
default:
|
||||
let Test.31 : U64 = 1i64;
|
||||
let Test.30 : [C U64, C {}, C Str] = CallByName Test.1 Test.31;
|
||||
jump Test.17 Test.30;
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
procedure Test.1 (Test.4):
|
||||
let Test.5 : {Str} = Struct {Test.4};
|
||||
ret Test.5;
|
||||
|
||||
procedure Test.5 (Test.12, #Attr.12):
|
||||
let Test.4 : Str = StructAtIndex 0 #Attr.12;
|
||||
inc Test.4;
|
||||
dec #Attr.12;
|
||||
let Test.14 : Str = "";
|
||||
ret Test.14;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.2 : Int1 = true;
|
||||
joinpoint Test.9 Test.3:
|
||||
ret Test.3;
|
||||
in
|
||||
let Test.19 : Int1 = true;
|
||||
let Test.20 : Int1 = lowlevel Eq Test.19 Test.2;
|
||||
if Test.20 then
|
||||
let Test.15 : Str = "";
|
||||
let Test.10 : {Str} = CallByName Test.1 Test.15;
|
||||
jump Test.9 Test.10;
|
||||
else
|
||||
let Test.18 : Str = "";
|
||||
let Test.16 : {Str} = CallByName Test.1 Test.18;
|
||||
jump Test.9 Test.16;
|
|
@ -1,6 +1,6 @@
|
|||
procedure List.4 (#Attr.2, #Attr.3):
|
||||
let List.284 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
|
||||
ret List.284;
|
||||
let List.266 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
|
||||
ret List.266;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.2 : List I64 = Array [1i64];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure List.4 (#Attr.2, #Attr.3):
|
||||
let List.284 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
|
||||
ret List.284;
|
||||
let List.266 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
|
||||
ret List.266;
|
||||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.6 : I64 = 42i64;
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
procedure List.3 (List.84, List.85, List.86):
|
||||
let List.287 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86;
|
||||
let List.286 : List I64 = StructAtIndex 0 List.287;
|
||||
inc List.286;
|
||||
dec List.287;
|
||||
ret List.286;
|
||||
procedure List.3 (List.82, List.83, List.84):
|
||||
let List.269 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84;
|
||||
let List.268 : List I64 = StructAtIndex 0 List.269;
|
||||
inc List.268;
|
||||
dec List.269;
|
||||
ret List.268;
|
||||
|
||||
procedure List.57 (List.81, List.82, List.83):
|
||||
let List.293 : U64 = CallByName List.6 List.81;
|
||||
let List.290 : Int1 = CallByName Num.22 List.82 List.293;
|
||||
if List.290 then
|
||||
let List.291 : {List I64, I64} = CallByName List.61 List.81 List.82 List.83;
|
||||
ret List.291;
|
||||
procedure List.57 (List.79, List.80, List.81):
|
||||
let List.275 : U64 = CallByName List.6 List.79;
|
||||
let List.272 : Int1 = CallByName Num.22 List.80 List.275;
|
||||
if List.272 then
|
||||
let List.273 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81;
|
||||
ret List.273;
|
||||
else
|
||||
let List.289 : {List I64, I64} = Struct {List.81, List.83};
|
||||
ret List.289;
|
||||
let List.271 : {List I64, I64} = Struct {List.79, List.81};
|
||||
ret List.271;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.285 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.285;
|
||||
let List.267 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.267;
|
||||
|
||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.292 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.292;
|
||||
let List.274 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.274;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.274 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.274;
|
||||
let Num.189 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.189;
|
||||
|
||||
procedure Test.1 ():
|
||||
let Test.8 : List I64 = Array [1i64, 2i64, 3i64];
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
procedure List.2 (List.75, List.76):
|
||||
let List.290 : U64 = CallByName List.6 List.75;
|
||||
let List.286 : Int1 = CallByName Num.22 List.76 List.290;
|
||||
if List.286 then
|
||||
let List.288 : I64 = CallByName List.60 List.75 List.76;
|
||||
let List.287 : [C {}, C I64] = Ok List.288;
|
||||
ret List.287;
|
||||
procedure List.2 (List.74, List.75):
|
||||
let List.272 : U64 = CallByName List.6 List.74;
|
||||
let List.268 : Int1 = CallByName Num.22 List.75 List.272;
|
||||
if List.268 then
|
||||
let List.270 : I64 = CallByName List.60 List.74 List.75;
|
||||
let List.269 : [C {}, C I64] = TagId(1) List.270;
|
||||
ret List.269;
|
||||
else
|
||||
let List.285 : {} = Struct {};
|
||||
let List.284 : [C {}, C I64] = Err List.285;
|
||||
ret List.284;
|
||||
let List.267 : {} = Struct {};
|
||||
let List.266 : [C {}, C I64] = TagId(0) List.267;
|
||||
ret List.266;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.293 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.293;
|
||||
let List.275 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.275;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.292 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.292;
|
||||
let List.274 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.274;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.6 : List I64 = Array [1i64, 2i64, 3i64];
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
procedure List.6 (#Attr.2):
|
||||
let List.284 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.284;
|
||||
let List.266 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.266;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.285 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.285;
|
||||
let List.267 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.267;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.1 : List I64 = Array [1i64, 2i64, 3i64];
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
procedure List.2 (List.75, List.76):
|
||||
let List.290 : U64 = CallByName List.6 List.75;
|
||||
let List.286 : Int1 = CallByName Num.22 List.76 List.290;
|
||||
if List.286 then
|
||||
let List.288 : Str = CallByName List.60 List.75 List.76;
|
||||
let List.287 : [C {}, C Str] = Ok List.288;
|
||||
ret List.287;
|
||||
procedure List.2 (List.74, List.75):
|
||||
let List.272 : U64 = CallByName List.6 List.74;
|
||||
let List.268 : Int1 = CallByName Num.22 List.75 List.272;
|
||||
if List.268 then
|
||||
let List.270 : Str = CallByName List.60 List.74 List.75;
|
||||
let List.269 : [C {}, C Str] = TagId(1) List.270;
|
||||
ret List.269;
|
||||
else
|
||||
let List.285 : {} = Struct {};
|
||||
let List.284 : [C {}, C Str] = Err List.285;
|
||||
ret List.284;
|
||||
let List.267 : {} = Struct {};
|
||||
let List.266 : [C {}, C Str] = TagId(0) List.267;
|
||||
ret List.266;
|
||||
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.292 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||
ret List.292;
|
||||
let List.274 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||
ret List.274;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.294 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.294;
|
||||
let List.276 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.276;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.293 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.293;
|
||||
let List.275 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.275;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Str.16 (#Attr.2, #Attr.3):
|
||||
let Str.149 : Str = lowlevel StrRepeat #Attr.2 #Attr.3;
|
||||
ret Str.149;
|
||||
let Str.126 : Str = lowlevel StrRepeat #Attr.2 #Attr.3;
|
||||
ret Str.126;
|
||||
|
||||
procedure Str.3 (#Attr.2, #Attr.3):
|
||||
let Str.150 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.150;
|
||||
let Str.127 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.127;
|
||||
|
||||
procedure Test.1 ():
|
||||
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
procedure List.2 (List.75, List.76):
|
||||
let List.290 : U64 = CallByName List.6 List.75;
|
||||
let List.286 : Int1 = CallByName Num.22 List.76 List.290;
|
||||
if List.286 then
|
||||
let List.288 : Str = CallByName List.60 List.75 List.76;
|
||||
let List.287 : [C {}, C Str] = Ok List.288;
|
||||
ret List.287;
|
||||
procedure List.2 (List.74, List.75):
|
||||
let List.272 : U64 = CallByName List.6 List.74;
|
||||
let List.268 : Int1 = CallByName Num.22 List.75 List.272;
|
||||
if List.268 then
|
||||
let List.270 : Str = CallByName List.60 List.74 List.75;
|
||||
let List.269 : [C {}, C Str] = TagId(1) List.270;
|
||||
ret List.269;
|
||||
else
|
||||
let List.285 : {} = Struct {};
|
||||
let List.284 : [C {}, C Str] = Err List.285;
|
||||
ret List.284;
|
||||
let List.267 : {} = Struct {};
|
||||
let List.266 : [C {}, C Str] = TagId(0) List.267;
|
||||
ret List.266;
|
||||
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
inc #Attr.2;
|
||||
let List.292 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||
let List.274 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.292;
|
||||
ret List.274;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.294 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.294;
|
||||
let List.276 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.276;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.293 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.293;
|
||||
let List.275 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.275;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Str.3 (#Attr.2, #Attr.3):
|
||||
let Str.150 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.150;
|
||||
let Str.127 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.127;
|
||||
|
||||
procedure Test.1 ():
|
||||
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
procedure List.3 (List.84, List.85, List.86):
|
||||
let List.285 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86;
|
||||
let List.284 : List I64 = StructAtIndex 0 List.285;
|
||||
inc List.284;
|
||||
dec List.285;
|
||||
ret List.284;
|
||||
procedure List.3 (List.82, List.83, List.84):
|
||||
let List.267 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84;
|
||||
let List.266 : List I64 = StructAtIndex 0 List.267;
|
||||
inc List.266;
|
||||
dec List.267;
|
||||
ret List.266;
|
||||
|
||||
procedure List.57 (List.81, List.82, List.83):
|
||||
let List.291 : U64 = CallByName List.6 List.81;
|
||||
let List.288 : Int1 = CallByName Num.22 List.82 List.291;
|
||||
if List.288 then
|
||||
let List.289 : {List I64, I64} = CallByName List.61 List.81 List.82 List.83;
|
||||
ret List.289;
|
||||
procedure List.57 (List.79, List.80, List.81):
|
||||
let List.273 : U64 = CallByName List.6 List.79;
|
||||
let List.270 : Int1 = CallByName Num.22 List.80 List.273;
|
||||
if List.270 then
|
||||
let List.271 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81;
|
||||
ret List.271;
|
||||
else
|
||||
let List.287 : {List I64, I64} = Struct {List.81, List.83};
|
||||
ret List.287;
|
||||
let List.269 : {List I64, I64} = Struct {List.79, List.81};
|
||||
ret List.269;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.292 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.292;
|
||||
let List.274 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.274;
|
||||
|
||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.290 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.290;
|
||||
let List.272 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.272;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.2 (Test.3):
|
||||
let Test.6 : U64 = 0i64;
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
procedure List.28 (#Attr.2, #Attr.3):
|
||||
let List.287 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
|
||||
let Bool.14 : Int1 = lowlevel ListIsUnique #Attr.2;
|
||||
if Bool.14 then
|
||||
ret List.287;
|
||||
let List.269 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
|
||||
let Bool.9 : Int1 = lowlevel ListIsUnique #Attr.2;
|
||||
if Bool.9 then
|
||||
ret List.269;
|
||||
else
|
||||
decref #Attr.2;
|
||||
ret List.287;
|
||||
ret List.269;
|
||||
|
||||
procedure List.54 (List.196):
|
||||
let List.285 : {} = Struct {};
|
||||
let List.284 : List I64 = CallByName List.28 List.196 List.285;
|
||||
ret List.284;
|
||||
procedure List.54 (List.183):
|
||||
let List.267 : {} = Struct {};
|
||||
let List.266 : List I64 = CallByName List.28 List.183 List.267;
|
||||
ret List.266;
|
||||
|
||||
procedure Num.46 (#Attr.2, #Attr.3):
|
||||
let Num.273 : U8 = lowlevel NumCompare #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : U8 = lowlevel NumCompare #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.2 : List I64 = Array [4i64, 3i64, 2i64, 1i64];
|
||||
|
|
|
@ -15,6 +15,6 @@ procedure Test.2 (Test.4):
|
|||
|
||||
procedure Test.0 ():
|
||||
let Test.13 : Str = "A";
|
||||
let Test.1 : [C Str, C Str] = A Test.13;
|
||||
let Test.1 : [C Str, C Str] = TagId(0) Test.13;
|
||||
let Test.7 : Str = CallByName Test.2 Test.1;
|
||||
ret Test.7;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.19 : I64 = 41i64;
|
||||
let Test.18 : [C I64, C ] = Just Test.19;
|
||||
let Test.2 : [C [C I64, C ], C ] = Just Test.18;
|
||||
let Test.18 : [C I64, C ] = TagId(0) Test.19;
|
||||
let Test.2 : [C [C I64, C ], C ] = TagId(0) Test.18;
|
||||
joinpoint Test.15:
|
||||
let Test.8 : I64 = 1i64;
|
||||
ret Test.8;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
procedure Test.3 (Test.4):
|
||||
let Test.8 : [C {}, C U8] = Ok Test.4;
|
||||
let Test.8 : [C {}, C U8] = TagId(1) Test.4;
|
||||
ret Test.8;
|
||||
|
||||
procedure Test.0 ():
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.21 (#Attr.2, #Attr.3):
|
||||
let Num.275 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Num.275;
|
||||
let Num.190 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Num.190;
|
||||
|
||||
procedure Test.1 (Test.6):
|
||||
let Test.21 : Int1 = false;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Test.0 ():
|
||||
let Test.11 : [<rnu><null>, C *self] = Z ;
|
||||
let Test.10 : [<rnu><null>, C *self] = S Test.11;
|
||||
let Test.9 : [<rnu><null>, C *self] = S Test.10;
|
||||
let Test.3 : [<rnu><null>, C *self] = S Test.9;
|
||||
let Test.11 : [<rnu><null>, C *self] = TagId(1) ;
|
||||
let Test.10 : [<rnu><null>, C *self] = TagId(0) Test.11;
|
||||
let Test.9 : [<rnu><null>, C *self] = TagId(0) Test.10;
|
||||
let Test.3 : [<rnu><null>, C *self] = TagId(0) Test.9;
|
||||
ret Test.3;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
procedure Test.0 ():
|
||||
let Test.15 : [<rnu><null>, C *self] = Z ;
|
||||
let Test.14 : [<rnu><null>, C *self] = S Test.15;
|
||||
let Test.13 : [<rnu><null>, C *self] = S Test.14;
|
||||
let Test.3 : [<rnu><null>, C *self] = S Test.13;
|
||||
let Test.15 : [<rnu><null>, C *self] = TagId(1) ;
|
||||
let Test.14 : [<rnu><null>, C *self] = TagId(0) Test.15;
|
||||
let Test.13 : [<rnu><null>, C *self] = TagId(0) Test.14;
|
||||
let Test.3 : [<rnu><null>, C *self] = TagId(0) Test.13;
|
||||
let Test.10 : Int1 = 1i64;
|
||||
let Test.11 : Int1 = GetTagId Test.3;
|
||||
dec Test.3;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
procedure Test.0 ():
|
||||
let Test.21 : [<rnu><null>, C *self] = Z ;
|
||||
let Test.20 : [<rnu><null>, C *self] = S Test.21;
|
||||
let Test.19 : [<rnu><null>, C *self] = S Test.20;
|
||||
let Test.3 : [<rnu><null>, C *self] = S Test.19;
|
||||
let Test.21 : [<rnu><null>, C *self] = TagId(1) ;
|
||||
let Test.20 : [<rnu><null>, C *self] = TagId(0) Test.21;
|
||||
let Test.19 : [<rnu><null>, C *self] = TagId(0) Test.20;
|
||||
let Test.3 : [<rnu><null>, C *self] = TagId(0) Test.19;
|
||||
let Test.16 : Int1 = 0i64;
|
||||
let Test.17 : Int1 = GetTagId Test.3;
|
||||
let Test.18 : Int1 = lowlevel Eq Test.16 Test.17;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Num.20 (#Attr.2, #Attr.3):
|
||||
let Num.274 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Num.274;
|
||||
let Num.189 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Num.189;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.275 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.275;
|
||||
let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.190;
|
||||
|
||||
procedure Test.1 (Test.24, Test.25, Test.26):
|
||||
joinpoint Test.12 Test.2 Test.3 Test.4:
|
||||
|
|
|
@ -1,47 +1,47 @@
|
|||
procedure List.2 (List.75, List.76):
|
||||
let List.304 : U64 = CallByName List.6 List.75;
|
||||
let List.300 : Int1 = CallByName Num.22 List.76 List.304;
|
||||
if List.300 then
|
||||
let List.302 : I64 = CallByName List.60 List.75 List.76;
|
||||
let List.301 : [C {}, C I64] = Ok List.302;
|
||||
ret List.301;
|
||||
procedure List.2 (List.74, List.75):
|
||||
let List.286 : U64 = CallByName List.6 List.74;
|
||||
let List.282 : Int1 = CallByName Num.22 List.75 List.286;
|
||||
if List.282 then
|
||||
let List.284 : I64 = CallByName List.60 List.74 List.75;
|
||||
let List.283 : [C {}, C I64] = TagId(1) List.284;
|
||||
ret List.283;
|
||||
else
|
||||
let List.299 : {} = Struct {};
|
||||
let List.298 : [C {}, C I64] = Err List.299;
|
||||
ret List.298;
|
||||
let List.281 : {} = Struct {};
|
||||
let List.280 : [C {}, C I64] = TagId(0) List.281;
|
||||
ret List.280;
|
||||
|
||||
procedure List.3 (List.84, List.85, List.86):
|
||||
let List.288 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86;
|
||||
let List.287 : List I64 = StructAtIndex 0 List.288;
|
||||
inc List.287;
|
||||
dec List.288;
|
||||
ret List.287;
|
||||
procedure List.3 (List.82, List.83, List.84):
|
||||
let List.270 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84;
|
||||
let List.269 : List I64 = StructAtIndex 0 List.270;
|
||||
inc List.269;
|
||||
dec List.270;
|
||||
ret List.269;
|
||||
|
||||
procedure List.57 (List.81, List.82, List.83):
|
||||
let List.310 : U64 = CallByName List.6 List.81;
|
||||
let List.307 : Int1 = CallByName Num.22 List.82 List.310;
|
||||
if List.307 then
|
||||
let List.308 : {List I64, I64} = CallByName List.61 List.81 List.82 List.83;
|
||||
ret List.308;
|
||||
procedure List.57 (List.79, List.80, List.81):
|
||||
let List.292 : U64 = CallByName List.6 List.79;
|
||||
let List.289 : Int1 = CallByName Num.22 List.80 List.292;
|
||||
if List.289 then
|
||||
let List.290 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81;
|
||||
ret List.290;
|
||||
else
|
||||
let List.306 : {List I64, I64} = Struct {List.81, List.83};
|
||||
ret List.306;
|
||||
let List.288 : {List I64, I64} = Struct {List.79, List.81};
|
||||
ret List.288;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.311 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.311;
|
||||
let List.293 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.293;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.312 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.312;
|
||||
let List.294 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.294;
|
||||
|
||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.309 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.309;
|
||||
let List.291 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.291;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.275 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.275;
|
||||
let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.190;
|
||||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.28 : U64 = 0i64;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.1 (Test.4):
|
||||
let Test.2 : I64 = StructAtIndex 0 Test.4;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.1 (Test.4):
|
||||
let Test.2 : I64 = 10i64;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.3 : I64 = StructAtIndex 0 Test.2;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.3 : I64 = 10i64;
|
||||
|
|
|
@ -1,47 +1,47 @@
|
|||
procedure List.2 (List.75, List.76):
|
||||
let List.304 : U64 = CallByName List.6 List.75;
|
||||
let List.300 : Int1 = CallByName Num.22 List.76 List.304;
|
||||
if List.300 then
|
||||
let List.302 : I64 = CallByName List.60 List.75 List.76;
|
||||
let List.301 : [C {}, C I64] = Ok List.302;
|
||||
ret List.301;
|
||||
procedure List.2 (List.74, List.75):
|
||||
let List.286 : U64 = CallByName List.6 List.74;
|
||||
let List.282 : Int1 = CallByName Num.22 List.75 List.286;
|
||||
if List.282 then
|
||||
let List.284 : I64 = CallByName List.60 List.74 List.75;
|
||||
let List.283 : [C {}, C I64] = TagId(1) List.284;
|
||||
ret List.283;
|
||||
else
|
||||
let List.299 : {} = Struct {};
|
||||
let List.298 : [C {}, C I64] = Err List.299;
|
||||
ret List.298;
|
||||
let List.281 : {} = Struct {};
|
||||
let List.280 : [C {}, C I64] = TagId(0) List.281;
|
||||
ret List.280;
|
||||
|
||||
procedure List.3 (List.84, List.85, List.86):
|
||||
let List.288 : {List I64, I64} = CallByName List.57 List.84 List.85 List.86;
|
||||
let List.287 : List I64 = StructAtIndex 0 List.288;
|
||||
inc List.287;
|
||||
dec List.288;
|
||||
ret List.287;
|
||||
procedure List.3 (List.82, List.83, List.84):
|
||||
let List.270 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84;
|
||||
let List.269 : List I64 = StructAtIndex 0 List.270;
|
||||
inc List.269;
|
||||
dec List.270;
|
||||
ret List.269;
|
||||
|
||||
procedure List.57 (List.81, List.82, List.83):
|
||||
let List.310 : U64 = CallByName List.6 List.81;
|
||||
let List.307 : Int1 = CallByName Num.22 List.82 List.310;
|
||||
if List.307 then
|
||||
let List.308 : {List I64, I64} = CallByName List.61 List.81 List.82 List.83;
|
||||
ret List.308;
|
||||
procedure List.57 (List.79, List.80, List.81):
|
||||
let List.292 : U64 = CallByName List.6 List.79;
|
||||
let List.289 : Int1 = CallByName Num.22 List.80 List.292;
|
||||
if List.289 then
|
||||
let List.290 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81;
|
||||
ret List.290;
|
||||
else
|
||||
let List.306 : {List I64, I64} = Struct {List.81, List.83};
|
||||
ret List.306;
|
||||
let List.288 : {List I64, I64} = Struct {List.79, List.81};
|
||||
ret List.288;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.311 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.311;
|
||||
let List.293 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.293;
|
||||
|
||||
procedure List.60 (#Attr.2, #Attr.3):
|
||||
let List.312 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.312;
|
||||
let List.294 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.294;
|
||||
|
||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.309 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.309;
|
||||
let List.291 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.291;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.275 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.275;
|
||||
let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.190;
|
||||
|
||||
procedure Test.1 (Test.2, Test.3, Test.4):
|
||||
let Test.29 : [C {}, C I64] = CallByName List.2 Test.4 Test.3;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.274 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.274;
|
||||
let Num.189 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.189;
|
||||
|
||||
procedure Num.21 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.1 (Test.2, Test.3):
|
||||
let Test.17 : U8 = GetTagId Test.2;
|
||||
|
@ -46,8 +46,8 @@ procedure Test.0 ():
|
|||
in
|
||||
let Test.25 : Int1 = true;
|
||||
if Test.25 then
|
||||
let Test.7 : [C I64, C I64 Int1] = ClosureTag(Test.7) Test.4;
|
||||
let Test.7 : [C I64, C I64 Int1] = TagId(0) Test.4;
|
||||
jump Test.22 Test.7;
|
||||
else
|
||||
let Test.8 : [C I64, C I64 Int1] = ClosureTag(Test.8) Test.5 Test.6;
|
||||
let Test.8 : [C I64, C I64 Int1] = TagId(1) Test.5 Test.6;
|
||||
jump Test.22 Test.8;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.274 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.274;
|
||||
let Num.189 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.189;
|
||||
|
||||
procedure Num.21 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.6 (Test.8, #Attr.12):
|
||||
let Test.4 : I64 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||
|
@ -37,8 +37,8 @@ procedure Test.0 ():
|
|||
in
|
||||
let Test.21 : Int1 = true;
|
||||
if Test.21 then
|
||||
let Test.6 : [C I64, C I64] = ClosureTag(Test.6) Test.4;
|
||||
let Test.6 : [C I64, C I64] = TagId(0) Test.4;
|
||||
jump Test.19 Test.6;
|
||||
else
|
||||
let Test.7 : [C I64, C I64] = ClosureTag(Test.7) Test.5;
|
||||
let Test.7 : [C I64, C I64] = TagId(1) Test.5;
|
||||
jump Test.19 Test.7;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Num.20 (#Attr.2, #Attr.3):
|
||||
let Num.274 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Num.274;
|
||||
let Num.189 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Num.189;
|
||||
|
||||
procedure Test.1 (Test.15, Test.16):
|
||||
joinpoint Test.7 Test.2 Test.3:
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.19 : I64 = 41i64;
|
||||
let Test.18 : [C I64, C ] = Just Test.19;
|
||||
let Test.2 : [C [C I64, C ], C ] = Just Test.18;
|
||||
let Test.18 : [C I64, C ] = TagId(0) Test.19;
|
||||
let Test.2 : [C [C I64, C ], C ] = TagId(0) Test.18;
|
||||
joinpoint Test.15:
|
||||
let Test.8 : I64 = 1i64;
|
||||
ret Test.8;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.5 : I64 = 2i64;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Test.1 (Test.5):
|
||||
let Test.19 : I64 = 2i64;
|
||||
let Test.2 : [C I64, C I64] = Ok Test.19;
|
||||
let Test.2 : [C I64, C I64] = TagId(1) Test.19;
|
||||
joinpoint Test.9 Test.3:
|
||||
ret Test.3;
|
||||
in
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.273 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.273;
|
||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.188;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.15 : I64 = 3i64;
|
||||
|
|
|
@ -1525,3 +1525,130 @@ fn tail_call_with_different_layout() {
|
|||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn lambda_capture_niche_u8_vs_u64() {
|
||||
indoc!(
|
||||
r#"
|
||||
capture : _ -> ({} -> Str)
|
||||
capture = \val ->
|
||||
\{} ->
|
||||
Num.toStr val
|
||||
|
||||
x : [True, False]
|
||||
x = True
|
||||
|
||||
fun =
|
||||
when x is
|
||||
True -> capture 123u64
|
||||
False -> capture 18u8
|
||||
|
||||
fun {}
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn lambda_capture_niches_with_other_lambda_capture() {
|
||||
indoc!(
|
||||
r#"
|
||||
capture : a -> ({} -> Str)
|
||||
capture = \val ->
|
||||
\{} ->
|
||||
when val is
|
||||
_ -> ""
|
||||
|
||||
capture2 = \val -> \{} -> "\(val)"
|
||||
|
||||
x : [A, B, C]
|
||||
x = A
|
||||
|
||||
fun =
|
||||
when x is
|
||||
A -> capture {}
|
||||
B -> capture2 "foo"
|
||||
C -> capture 1u64
|
||||
|
||||
fun {}
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn lambda_capture_niches_with_non_capturing_function() {
|
||||
indoc!(
|
||||
r#"
|
||||
capture : a -> ({} -> Str)
|
||||
capture = \val ->
|
||||
\{} ->
|
||||
when val is
|
||||
_ -> ""
|
||||
|
||||
triv = \{} -> ""
|
||||
|
||||
x : [A, B, C]
|
||||
x = A
|
||||
|
||||
fun =
|
||||
when x is
|
||||
A -> capture {}
|
||||
B -> triv
|
||||
C -> capture 1u64
|
||||
|
||||
fun {}
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn lambda_capture_niches_have_captured_function_in_closure() {
|
||||
indoc!(
|
||||
r#"
|
||||
Lazy a : {} -> a
|
||||
|
||||
after : Lazy a, (a -> Lazy b) -> Lazy b
|
||||
after = \effect, map ->
|
||||
thunk = \{} ->
|
||||
when map (effect {}) is
|
||||
b -> b {}
|
||||
thunk
|
||||
|
||||
f = \_ -> \_ -> ""
|
||||
g = \{ s1 } -> \_ -> s1
|
||||
|
||||
x : [True, False]
|
||||
x = True
|
||||
|
||||
fun =
|
||||
when x is
|
||||
True -> after (\{} -> "") f
|
||||
False -> after (\{} -> {s1: "s1"}) g
|
||||
|
||||
fun {}
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn lambda_set_niche_same_layout_different_constructor() {
|
||||
indoc!(
|
||||
r#"
|
||||
capture : a -> ({} -> Str)
|
||||
capture = \val ->
|
||||
thunk =
|
||||
\{} ->
|
||||
when val is
|
||||
_ -> ""
|
||||
thunk
|
||||
|
||||
x : [True, False]
|
||||
x = True
|
||||
|
||||
fun =
|
||||
when x is
|
||||
True -> capture {a: ""}
|
||||
False -> capture (A "")
|
||||
fun
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::subs::{
|
|||
};
|
||||
use crate::types::{name_type_var, RecordField, Uls};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
|
||||
|
@ -1152,7 +1151,7 @@ fn write_flat_type<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn push_union<'a, L: Label>(
|
||||
pub fn push_union<'a, L: Label>(
|
||||
subs: &'a Subs,
|
||||
tags: &UnionLabels<L>,
|
||||
fields: &mut Vec<(L, Vec<Variable>)>,
|
||||
|
@ -1196,40 +1195,6 @@ pub fn chase_ext_tag_union<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
pub enum ResolvedLambdaSet {
|
||||
Set(Vec<(Symbol, Vec<Variable>)>),
|
||||
/// TODO: figure out if this can happen in a correct program, or is the result of a bug in our
|
||||
/// compiler. See https://github.com/rtfeldman/roc/issues/3163.
|
||||
Unbound,
|
||||
}
|
||||
|
||||
pub fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet {
|
||||
let mut set = vec![];
|
||||
loop {
|
||||
match subs.get_content_without_compacting(var) {
|
||||
Content::LambdaSet(subs::LambdaSet {
|
||||
solved,
|
||||
recursion_var: _,
|
||||
unspecialized,
|
||||
}) => {
|
||||
debug_assert!(
|
||||
unspecialized.is_empty(),
|
||||
"unspecialized lambda sets left over during resolution: {:?}",
|
||||
crate::subs::SubsFmtContent(subs.get_content_without_compacting(var), subs),
|
||||
);
|
||||
push_union(subs, solved, &mut set);
|
||||
return ResolvedLambdaSet::Set(set);
|
||||
}
|
||||
Content::RecursionVar { structure, .. } => {
|
||||
var = *structure;
|
||||
}
|
||||
Content::FlexVar(_) => return ResolvedLambdaSet::Unbound,
|
||||
|
||||
c => internal_error!("called with a non-lambda set {:?}", c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_apply<'a>(
|
||||
env: &Env,
|
||||
ctx: &mut Context<'a>,
|
||||
|
|
|
@ -1872,6 +1872,10 @@ impl Subs {
|
|||
self.utable.unioned(left, right)
|
||||
}
|
||||
|
||||
pub fn equivalent_without_compacting(&self, left: Variable, right: Variable) -> bool {
|
||||
self.utable.unioned_without_compacting(left, right)
|
||||
}
|
||||
|
||||
pub fn redundant(&self, var: Variable) -> bool {
|
||||
self.utable.is_redirect(var)
|
||||
}
|
||||
|
@ -2646,7 +2650,8 @@ impl<L> UnionLabels<L>
|
|||
where
|
||||
L: Label + Ord,
|
||||
{
|
||||
pub fn is_sorted_no_duplicates(&self, subs: &Subs) -> bool {
|
||||
/// Checks if the union of labels is sorted by label, without duplicates.
|
||||
pub fn is_sorted(&self, subs: &Subs) -> bool {
|
||||
let mut iter = self.iter_from_subs(subs).peekable();
|
||||
while let Some((before, _)) = iter.next() {
|
||||
if let Some((after, _)) = iter.peek() {
|
||||
|
@ -2657,6 +2662,19 @@ where
|
|||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Checks if the union of labels is sorted by label, without duplicates.
|
||||
pub fn is_sorted_allow_duplicates(&self, subs: &Subs) -> bool {
|
||||
let mut iter = self.iter_from_subs(subs).peekable();
|
||||
while let Some((before, _)) = iter.next() {
|
||||
if let Some((after, _)) = iter.peek() {
|
||||
if before > after {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl UnionTags {
|
||||
|
|
|
@ -323,6 +323,10 @@ impl UnificationTable {
|
|||
self.root_key(a) == self.root_key(b)
|
||||
}
|
||||
|
||||
pub fn unioned_without_compacting(&self, a: Variable, b: Variable) -> bool {
|
||||
self.root_key_without_compacting(a) == self.root_key_without_compacting(b)
|
||||
}
|
||||
|
||||
// custom very specific helpers
|
||||
#[inline(always)]
|
||||
pub fn get_rank_set_mark(&mut self, key: Variable, mark: Mark) -> Rank {
|
||||
|
|
|
@ -966,6 +966,174 @@ fn extract_specialization_lambda_set<M: MetaCollector>(
|
|||
outcome
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Sides {
|
||||
left: Vec<(Symbol, VariableSubsSlice)>,
|
||||
right: Vec<(Symbol, VariableSubsSlice)>,
|
||||
}
|
||||
|
||||
impl Default for Sides {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
left: Vec::with_capacity(1),
|
||||
right: Vec::with_capacity(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SeparatedUnionLambdas {
|
||||
only_in_left: Vec<(Symbol, VariableSubsSlice)>,
|
||||
only_in_right: Vec<(Symbol, VariableSubsSlice)>,
|
||||
joined: Vec<(Symbol, VariableSubsSlice)>,
|
||||
}
|
||||
|
||||
fn separate_union_lambdas(
|
||||
subs: &mut Subs,
|
||||
pool: &mut Pool,
|
||||
fields1: UnionLambdas,
|
||||
fields2: UnionLambdas,
|
||||
) -> SeparatedUnionLambdas {
|
||||
debug_assert!(
|
||||
fields1.is_sorted_allow_duplicates(subs),
|
||||
"not sorted: {:?}",
|
||||
fields1.iter_from_subs(subs).collect::<Vec<_>>()
|
||||
);
|
||||
debug_assert!(
|
||||
fields2.is_sorted_allow_duplicates(subs),
|
||||
"not sorted: {:?}",
|
||||
fields2.iter_from_subs(subs).collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// lambda names -> (the captures for that lambda on the left side, the captures for that lambda on the right side)
|
||||
// e.g. [[F1 U8], [F1 U64], [F2 a]] ~ [[F1 Str], [F2 Str]] becomes
|
||||
// F1 -> { left: [ [U8], [U64] ], right: [ [Str] ] }
|
||||
// F2 -> { left: [ [a] ], right: [ [Str] ] }
|
||||
let mut buckets: VecMap<Symbol, Sides> = VecMap::with_capacity(fields1.len() + fields2.len());
|
||||
|
||||
let (mut fields_left, mut fields_right) = (
|
||||
fields1.iter_all().into_iter().peekable(),
|
||||
fields2.iter_all().into_iter().peekable(),
|
||||
);
|
||||
|
||||
loop {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
let ord = match (fields_left.peek(), fields_right.peek()) {
|
||||
(Some((l, _)), Some((r, _))) => Some((subs[*l]).cmp(&subs[*r])),
|
||||
(Some(_), None) => Some(Ordering::Less),
|
||||
(None, Some(_)) => Some(Ordering::Greater),
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
match ord {
|
||||
Some(Ordering::Less) => {
|
||||
let (sym, vars) = fields_left.next().unwrap();
|
||||
let bucket = buckets.get_or_insert(subs[sym], Sides::default);
|
||||
bucket.left.push((subs[sym], subs[vars]));
|
||||
}
|
||||
Some(Ordering::Greater) => {
|
||||
let (sym, vars) = fields_right.next().unwrap();
|
||||
let bucket = buckets.get_or_insert(subs[sym], Sides::default);
|
||||
bucket.right.push((subs[sym], subs[vars]));
|
||||
}
|
||||
Some(Ordering::Equal) => {
|
||||
let (sym, left_vars) = fields_left.next().unwrap();
|
||||
let (_sym, right_vars) = fields_right.next().unwrap();
|
||||
debug_assert_eq!(subs[sym], subs[_sym]);
|
||||
|
||||
let bucket = buckets.get_or_insert(subs[sym], Sides::default);
|
||||
bucket.left.push((subs[sym], subs[left_vars]));
|
||||
bucket.right.push((subs[sym], subs[right_vars]));
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
let mut only_in_left = Vec::with_capacity(fields1.len());
|
||||
let mut only_in_right = Vec::with_capacity(fields2.len());
|
||||
let mut joined = Vec::with_capacity(fields1.len() + fields2.len());
|
||||
for (lambda_name, Sides { left, mut right }) in buckets {
|
||||
match (left.as_slice(), right.as_slice()) {
|
||||
(&[], &[]) => internal_error!("somehow both are empty but there's an entry?"),
|
||||
(&[], _) => only_in_right.extend(right),
|
||||
(_, &[]) => only_in_left.extend(left),
|
||||
(_, _) => {
|
||||
'next_left: for (_, left_slice) in left {
|
||||
// Does the current slice on the left unify with a slice on the right?
|
||||
//
|
||||
// If yes, we unify then and the unified result to `joined`.
|
||||
//
|
||||
// Otherwise if no such slice on the right is found, then the slice on the `left` has no slice,
|
||||
// either on the left or right, it unifies with (since the left was constructed
|
||||
// inductively via the same procedure).
|
||||
//
|
||||
// At the end each slice in the left and right has been explored, so
|
||||
// - `joined` contains all the slices that can unify
|
||||
// - left contains unique captures slices that will unify with no other slice
|
||||
// - right contains unique captures slices that will unify with no other slice
|
||||
//
|
||||
// Note also if a slice l on the left and a slice r on the right unify, there
|
||||
// is no other r' != r on the right such that l ~ r', and respectively there is
|
||||
// no other l' != l on the left such that l' ~ r. Otherwise, it must be that l ~ l'
|
||||
// (resp. r ~ r'), but then l = l' (resp. r = r'), and they would have become the same
|
||||
// slice in a previous call to `separate_union_lambdas`.
|
||||
'try_next_right: for (right_index, (_, right_slice)) in right.iter().enumerate()
|
||||
{
|
||||
if left_slice.len() != right_slice.len() {
|
||||
continue 'try_next_right;
|
||||
}
|
||||
|
||||
let snapshot = subs.snapshot();
|
||||
for (var1, var2) in (left_slice.into_iter()).zip(right_slice.into_iter()) {
|
||||
let (var1, var2) = (subs[var1], subs[var2]);
|
||||
|
||||
// Lambda sets are effectively tags under another name, and their usage can also result
|
||||
// in the arguments of a lambda name being recursive. It very well may happen that
|
||||
// during unification, a lambda set previously marked as not recursive becomes
|
||||
// recursive. See the docs of [LambdaSet] for one example, or https://github.com/rtfeldman/roc/pull/2307.
|
||||
//
|
||||
// Like with tag unions, if it has, we'll always pass through this branch. So, take
|
||||
// this opportunity to promote the lambda set to recursive if need be.
|
||||
maybe_mark_union_recursive(subs, var1);
|
||||
maybe_mark_union_recursive(subs, var2);
|
||||
|
||||
let outcome =
|
||||
unify_pool::<NoCollector>(subs, pool, var1, var2, Mode::EQ);
|
||||
|
||||
if !outcome.mismatches.is_empty() {
|
||||
subs.rollback_to(snapshot);
|
||||
continue 'try_next_right;
|
||||
}
|
||||
}
|
||||
|
||||
// All the variables unified, so we can join the left + right.
|
||||
// The variables are unified in left and right slice, so just reuse the left slice.
|
||||
joined.push((lambda_name, left_slice));
|
||||
// Remove the right slice, it unifies with the left so this is its unique
|
||||
// unification.
|
||||
// Remove in-place so that the order is preserved.
|
||||
right.remove(right_index);
|
||||
continue 'next_left;
|
||||
}
|
||||
|
||||
// No slice on the right unified with the left, so the slice on the left is on
|
||||
// its own.
|
||||
only_in_left.push((lambda_name, left_slice));
|
||||
}
|
||||
|
||||
// Possible that there are items left over in the right, they are on their own.
|
||||
only_in_right.extend(right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SeparatedUnionLambdas {
|
||||
only_in_left,
|
||||
only_in_right,
|
||||
joined,
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_lambda_set_help<M: MetaCollector>(
|
||||
subs: &mut Subs,
|
||||
pool: &mut Pool,
|
||||
|
@ -994,60 +1162,26 @@ fn unify_lambda_set_help<M: MetaCollector>(
|
|||
"Recursion var is present, but it doesn't have a recursive content!"
|
||||
);
|
||||
|
||||
let Separate {
|
||||
only_in_1,
|
||||
only_in_2,
|
||||
in_both,
|
||||
} = separate_union_lambdas(subs, solved1, solved2);
|
||||
let SeparatedUnionLambdas {
|
||||
only_in_left,
|
||||
only_in_right,
|
||||
joined,
|
||||
} = separate_union_lambdas(subs, pool, solved1, solved2);
|
||||
|
||||
let num_shared = in_both.len();
|
||||
let all_lambdas = joined
|
||||
.into_iter()
|
||||
.map(|(name, slice)| (name, subs.get_subs_slice(slice).to_vec()));
|
||||
|
||||
let mut joined_lambdas = vec![];
|
||||
for (tag_name, (vars1, vars2)) in in_both {
|
||||
let mut matching_vars = vec![];
|
||||
|
||||
if vars1.len() != vars2.len() {
|
||||
continue; // this is a type mismatch; not adding the tag will trigger it below.
|
||||
}
|
||||
|
||||
let num_vars = vars1.len();
|
||||
for (var1, var2) in (vars1.into_iter()).zip(vars2.into_iter()) {
|
||||
let (var1, var2) = (subs[var1], subs[var2]);
|
||||
|
||||
// Lambda sets are effectively tags under another name, and their usage can also result
|
||||
// in the arguments of a lambda name being recursive. It very well may happen that
|
||||
// during unification, a lambda set previously marked as not recursive becomes
|
||||
// recursive. See the docs of [LambdaSet] for one example, or https://github.com/rtfeldman/roc/pull/2307.
|
||||
//
|
||||
// Like with tag unions, if it has, we'll always pass through this branch. So, take
|
||||
// this opportunity to promote the lambda set to recursive if need be.
|
||||
maybe_mark_union_recursive(subs, var1);
|
||||
maybe_mark_union_recursive(subs, var2);
|
||||
|
||||
let outcome = unify_pool::<M>(subs, pool, var1, var2, ctx.mode);
|
||||
|
||||
if outcome.mismatches.is_empty() {
|
||||
matching_vars.push(var1);
|
||||
}
|
||||
}
|
||||
|
||||
if matching_vars.len() == num_vars {
|
||||
joined_lambdas.push((tag_name, matching_vars));
|
||||
}
|
||||
}
|
||||
|
||||
if joined_lambdas.len() == num_shared {
|
||||
let all_lambdas = joined_lambdas;
|
||||
let all_lambdas = merge_sorted(
|
||||
let all_lambdas = merge_sorted_preserving_duplicates(
|
||||
all_lambdas,
|
||||
only_in_1.into_iter().map(|(name, subs_slice)| {
|
||||
only_in_left.into_iter().map(|(name, subs_slice)| {
|
||||
let vec = subs.get_subs_slice(subs_slice).to_vec();
|
||||
(name, vec)
|
||||
}),
|
||||
);
|
||||
let all_lambdas = merge_sorted(
|
||||
let all_lambdas = merge_sorted_preserving_duplicates(
|
||||
all_lambdas,
|
||||
only_in_2.into_iter().map(|(name, subs_slice)| {
|
||||
only_in_right.into_iter().map(|(name, subs_slice)| {
|
||||
let vec = subs.get_subs_slice(subs_slice).to_vec();
|
||||
(name, vec)
|
||||
}),
|
||||
|
@ -1090,13 +1224,6 @@ fn unify_lambda_set_help<M: MetaCollector>(
|
|||
});
|
||||
|
||||
merge(subs, ctx, new_lambda_set)
|
||||
} else {
|
||||
mismatch!(
|
||||
"Problem with lambda sets: there should be {:?} matching lambda, but only found {:?}",
|
||||
num_shared,
|
||||
&joined_lambdas
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures that a non-recursive tag union, when unified with a recursion var to become a recursive
|
||||
|
@ -1396,7 +1523,7 @@ struct Separate<K, V> {
|
|||
in_both: Vec<(K, (V, V))>,
|
||||
}
|
||||
|
||||
fn merge_sorted<K, V, I1, I2>(input1: I1, input2: I2) -> Vec<(K, V)>
|
||||
fn merge_sorted_help<K, V, I1, I2>(input1: I1, input2: I2, preserve_duplicates: bool) -> Vec<(K, V)>
|
||||
where
|
||||
K: Ord,
|
||||
I1: IntoIterator<Item = (K, V)>,
|
||||
|
@ -1426,8 +1553,11 @@ where
|
|||
}
|
||||
Some(Ordering::Equal) => {
|
||||
let (k, v) = it1.next().unwrap();
|
||||
let (_, _) = it2.next().unwrap();
|
||||
let (k2, v2) = it2.next().unwrap();
|
||||
result.push((k, v));
|
||||
if preserve_duplicates {
|
||||
result.push((k2, v2));
|
||||
}
|
||||
}
|
||||
Some(Ordering::Greater) => {
|
||||
result.push(it2.next().unwrap());
|
||||
|
@ -1439,6 +1569,24 @@ where
|
|||
result
|
||||
}
|
||||
|
||||
fn merge_sorted<K, V, I1, I2>(input1: I1, input2: I2) -> Vec<(K, V)>
|
||||
where
|
||||
K: Ord,
|
||||
I1: IntoIterator<Item = (K, V)>,
|
||||
I2: IntoIterator<Item = (K, V)>,
|
||||
{
|
||||
merge_sorted_help(input1, input2, false)
|
||||
}
|
||||
|
||||
fn merge_sorted_preserving_duplicates<K, V, I1, I2>(input1: I1, input2: I2) -> Vec<(K, V)>
|
||||
where
|
||||
K: Ord,
|
||||
I1: IntoIterator<Item = (K, V)>,
|
||||
I2: IntoIterator<Item = (K, V)>,
|
||||
{
|
||||
merge_sorted_help(input1, input2, true)
|
||||
}
|
||||
|
||||
fn separate<K, V, I1, I2>(input1: I1, input2: I2) -> Separate<K, V>
|
||||
where
|
||||
K: Ord,
|
||||
|
@ -1501,19 +1649,6 @@ fn separate_union_tags(
|
|||
(separate(it1, it2), new_ext1, new_ext2)
|
||||
}
|
||||
|
||||
fn separate_union_lambdas(
|
||||
subs: &Subs,
|
||||
fields1: UnionLambdas,
|
||||
fields2: UnionLambdas,
|
||||
) -> Separate<Symbol, VariableSubsSlice> {
|
||||
debug_assert!(fields1.is_sorted_no_duplicates(subs));
|
||||
debug_assert!(fields2.is_sorted_no_duplicates(subs));
|
||||
let it1 = fields1.iter_all().map(|(s, vars)| (subs[s], subs[vars]));
|
||||
let it2 = fields2.iter_all().map(|(s, vars)| (subs[s], subs[vars]));
|
||||
|
||||
separate(it1, it2)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Rec {
|
||||
None,
|
||||
|
|
|
@ -191,6 +191,8 @@ fn render_module_documentation(
|
|||
if should_render_entry {
|
||||
match entry {
|
||||
DocEntry::DocDef(doc_def) => {
|
||||
buf.push_str("<section>");
|
||||
|
||||
let mut href = String::new();
|
||||
href.push('#');
|
||||
href.push_str(doc_def.name.as_str());
|
||||
|
@ -239,6 +241,8 @@ fn render_module_documentation(
|
|||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
buf.push_str("</section>");
|
||||
}
|
||||
DocEntry::DetachedDoc(docs) => {
|
||||
let markdown = markdown_to_html(
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
--nav-link-hover-color: #000000;
|
||||
--type-signature-color: var(--purple-5);
|
||||
--type-signature-bg-color: var(--purple-1);
|
||||
--module-entry-border-color: var(--gray-3);
|
||||
}
|
||||
|
||||
a {
|
||||
|
@ -75,6 +76,8 @@ a {
|
|||
color: var(--type-signature-color);
|
||||
background-color: var(--type-signature-bg-color);
|
||||
width: fit-content;
|
||||
margin-top: 0;
|
||||
margin-bottom: 24px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
@ -158,6 +161,17 @@ main {
|
|||
min-width: 0; /* necessary for text-overflow: ellipsis to work in descendants */
|
||||
}
|
||||
|
||||
section {
|
||||
border: 1px solid var(--module-entry-border-color);
|
||||
border-radius: 8px;
|
||||
padding: 32px;
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
section > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#sidebar-nav {
|
||||
grid-column-start: sidebar;
|
||||
grid-column-end: sidebar;
|
||||
|
@ -447,6 +461,7 @@ pre {
|
|||
--top-bar-fg: #eeeeee;
|
||||
--type-signature-color: var(--grey-1);
|
||||
--type-signature-bg-color: var(--purple-4);
|
||||
--module-entry-border-color: var(--purple-7);
|
||||
}
|
||||
|
||||
html {
|
||||
|
|
|
@ -57,6 +57,7 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>(
|
|||
ProcLayout {
|
||||
arguments: [],
|
||||
result,
|
||||
captures_niche: _,
|
||||
} => {
|
||||
// this is a thunk
|
||||
jit_to_ast_help(&env, app, main_fn_name, &result, content)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue