mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Merge pull request #450 from rtfeldman/gen-optional-field
Gen optional fields
This commit is contained in:
commit
27b2d10b2a
6 changed files with 471 additions and 98 deletions
|
@ -274,7 +274,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
FunctionCall {
|
||||
call_type: ByName(name),
|
||||
layout,
|
||||
full_layout,
|
||||
args,
|
||||
..
|
||||
} => {
|
||||
|
@ -287,7 +287,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
call_with_args(
|
||||
env,
|
||||
layout_ids,
|
||||
layout,
|
||||
&full_layout,
|
||||
*name,
|
||||
parent,
|
||||
arg_tuples.into_bump_slice(),
|
||||
|
@ -296,7 +296,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
FunctionCall {
|
||||
call_type: ByPointer(name),
|
||||
layout: _,
|
||||
args,
|
||||
..
|
||||
} => {
|
||||
|
|
|
@ -399,4 +399,110 @@ mod gen_records {
|
|||
(i64, i64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_field_when_use_default() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \r ->
|
||||
when r is
|
||||
{ x: Blue, y ? 3 } -> y
|
||||
{ x: Red, y ? 5 } -> y
|
||||
|
||||
a = f { x: Blue, y: 7 }
|
||||
b = f { x: Blue }
|
||||
c = f { x: Red, y: 11 }
|
||||
d = f { x: Red }
|
||||
|
||||
a * b * c * d
|
||||
"#
|
||||
),
|
||||
3 * 5 * 7 * 11,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_field_when_no_use_default() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \r ->
|
||||
{ x ? 10, y } = r
|
||||
x + y
|
||||
|
||||
f { x: 4, y: 9 }
|
||||
"#
|
||||
),
|
||||
13,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_field_let_use_default() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \r ->
|
||||
{ x ? 10, y } = r
|
||||
x + y
|
||||
|
||||
f { y: 9 }
|
||||
"#
|
||||
),
|
||||
19,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_field_let_no_use_default() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \r ->
|
||||
{ x ? 10, y } = r
|
||||
x + y
|
||||
|
||||
f { x: 4, y: 9 }
|
||||
"#
|
||||
),
|
||||
13,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_field_function_use_default() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \{ x ? 10, y } -> x + y
|
||||
|
||||
|
||||
f { y: 9 }
|
||||
"#
|
||||
),
|
||||
19,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_field_function_no_use_default() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \{ x ? 10, y } -> x + y
|
||||
|
||||
|
||||
f { x: 4, y: 9 }
|
||||
"#
|
||||
),
|
||||
13,
|
||||
i64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -409,7 +409,7 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V
|
|||
arguments.push((Pattern::Underscore, destruct.layout.clone()));
|
||||
}
|
||||
DestructType::Optional(_expr) => {
|
||||
todo!("test_at_type for optional destruct");
|
||||
arguments.push((Pattern::Underscore, destruct.layout.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -541,9 +541,7 @@ fn to_relevant_branch_help<'a>(
|
|||
let pattern = match destruct.typ {
|
||||
DestructType::Guard(guard) => guard.clone(),
|
||||
DestructType::Required => Pattern::Underscore,
|
||||
DestructType::Optional(_expr) => {
|
||||
todo!("TODO decision tree for optional field branch");
|
||||
}
|
||||
DestructType::Optional(_expr) => Pattern::Underscore,
|
||||
};
|
||||
|
||||
(
|
||||
|
|
|
@ -467,7 +467,8 @@ pub enum Expr<'a> {
|
|||
FunctionPointer(Symbol, Layout<'a>),
|
||||
FunctionCall {
|
||||
call_type: CallType,
|
||||
layout: Layout<'a>,
|
||||
full_layout: Layout<'a>,
|
||||
ret_layout: Layout<'a>,
|
||||
arg_layouts: &'a [Layout<'a>],
|
||||
args: &'a [Symbol],
|
||||
},
|
||||
|
@ -1409,7 +1410,8 @@ pub fn with_hole<'a>(
|
|||
for (label, layout) in sorted_fields.into_iter() {
|
||||
field_layouts.push(layout);
|
||||
|
||||
let field = fields.remove(&label).unwrap();
|
||||
match fields.remove(&label) {
|
||||
Some(field) => {
|
||||
if let roc_can::expr::Expr::Var(symbol) = field.loc_expr.value {
|
||||
field_symbols.push(symbol);
|
||||
can_fields.push(None);
|
||||
|
@ -1418,6 +1420,12 @@ pub fn with_hole<'a>(
|
|||
can_fields.push(Some(field));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// this field was optional, but not given
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// creating a record from the var will unpack it if it's just a single field.
|
||||
let layout = layout_cache
|
||||
|
@ -1704,12 +1712,23 @@ pub fn with_hole<'a>(
|
|||
let mut index = None;
|
||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||
|
||||
for (current, (label, field_layout)) in sorted_fields.into_iter().enumerate() {
|
||||
let mut current = 0;
|
||||
for (label, opt_field_layout) in sorted_fields.into_iter() {
|
||||
match opt_field_layout {
|
||||
Err(_) => {
|
||||
// this was an optional field, and now does not exist!
|
||||
// do not increment `current`!
|
||||
}
|
||||
Ok(field_layout) => {
|
||||
field_layouts.push(field_layout);
|
||||
|
||||
if label == field {
|
||||
index = Some(current);
|
||||
}
|
||||
|
||||
current += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let record_symbol = if let roc_can::expr::Expr::Var(symbol) = loc_expr.value {
|
||||
|
@ -1831,13 +1850,13 @@ pub fn with_hole<'a>(
|
|||
arg_symbols.push(env.unique_symbol());
|
||||
}
|
||||
|
||||
let layout = layout_cache
|
||||
let full_layout = layout_cache
|
||||
.from_var(env.arena, fn_var, env.subs)
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
||||
});
|
||||
|
||||
let arg_layouts = match layout {
|
||||
let arg_layouts = match full_layout {
|
||||
Layout::FunctionPointer(args, _) => args,
|
||||
_ => unreachable!("function has layout that is not function pointer"),
|
||||
};
|
||||
|
@ -1854,7 +1873,8 @@ pub fn with_hole<'a>(
|
|||
assigned,
|
||||
Expr::FunctionCall {
|
||||
call_type: CallType::ByPointer(function_symbol),
|
||||
layout,
|
||||
full_layout,
|
||||
ret_layout: ret_layout.clone(),
|
||||
args: arg_symbols,
|
||||
arg_layouts,
|
||||
},
|
||||
|
@ -2136,8 +2156,13 @@ pub fn from_can<'a>(
|
|||
// convert the continuation
|
||||
let mut stmt = from_can(env, cont.value, procs, layout_cache);
|
||||
|
||||
if let roc_can::expr::Expr::Var(outer_symbol) = def.loc_expr.value {
|
||||
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||
.unwrap()
|
||||
} else {
|
||||
let outer_symbol = env.unique_symbol();
|
||||
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||
stmt =
|
||||
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||
.unwrap();
|
||||
|
||||
// convert the def body, store in outer_symbol
|
||||
|
@ -2151,6 +2176,7 @@ pub fn from_can<'a>(
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
let symbol = env.unique_symbol();
|
||||
|
@ -2534,7 +2560,8 @@ fn substitute_in_expr<'a>(
|
|||
call_type,
|
||||
args,
|
||||
arg_layouts,
|
||||
layout,
|
||||
ret_layout,
|
||||
full_layout,
|
||||
} => {
|
||||
let opt_call_type = match call_type {
|
||||
CallType::ByName(s) => substitute(subs, *s).map(CallType::ByName),
|
||||
|
@ -2562,7 +2589,8 @@ fn substitute_in_expr<'a>(
|
|||
call_type,
|
||||
args,
|
||||
arg_layouts: *arg_layouts,
|
||||
layout: layout.clone(),
|
||||
ret_layout: ret_layout.clone(),
|
||||
full_layout: full_layout.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -2822,8 +2850,15 @@ fn store_record_destruct<'a>(
|
|||
env.arena.alloc(stmt),
|
||||
);
|
||||
}
|
||||
DestructType::Optional(_expr) => {
|
||||
todo!("TODO monomorphize optional field destructure's default expr");
|
||||
DestructType::Optional(expr) => {
|
||||
stmt = with_hole(
|
||||
env,
|
||||
expr.clone(),
|
||||
procs,
|
||||
layout_cache,
|
||||
destruct.symbol,
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
}
|
||||
DestructType::Guard(guard_pattern) => match &guard_pattern {
|
||||
Identifier(symbol) => {
|
||||
|
@ -2909,24 +2944,30 @@ fn call_by_name<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
let full_layout = layout.clone();
|
||||
|
||||
// TODO does this work?
|
||||
let empty = &[] as &[_];
|
||||
let (arg_layouts, layout) = if let Layout::FunctionPointer(args, rlayout) = layout {
|
||||
let (arg_layouts, ret_layout) = if let Layout::FunctionPointer(args, rlayout) = layout {
|
||||
(args, rlayout)
|
||||
} else {
|
||||
(empty, &layout)
|
||||
};
|
||||
|
||||
// If we've already specialized this one, no further work is needed.
|
||||
if procs.specialized.contains_key(&(proc_name, layout.clone())) {
|
||||
if procs
|
||||
.specialized
|
||||
.contains_key(&(proc_name, full_layout.clone()))
|
||||
{
|
||||
let call = Expr::FunctionCall {
|
||||
call_type: CallType::ByName(proc_name),
|
||||
layout: layout.clone(),
|
||||
ret_layout: ret_layout.clone(),
|
||||
full_layout: full_layout.clone(),
|
||||
arg_layouts,
|
||||
args: field_symbols,
|
||||
};
|
||||
|
||||
let mut result = Stmt::Let(assigned, call, layout.clone(), hole);
|
||||
let mut result = Stmt::Let(assigned, call, ret_layout.clone(), hole);
|
||||
|
||||
for ((_, loc_arg), symbol) in
|
||||
loc_args.into_iter().rev().zip(field_symbols.iter().rev())
|
||||
|
@ -2967,16 +3008,22 @@ fn call_by_name<'a>(
|
|||
match &mut procs.pending_specializations {
|
||||
Some(pending_specializations) => {
|
||||
// register the pending specialization, so this gets code genned later
|
||||
add_pending(pending_specializations, proc_name, layout.clone(), pending);
|
||||
add_pending(
|
||||
pending_specializations,
|
||||
proc_name,
|
||||
full_layout.clone(),
|
||||
pending,
|
||||
);
|
||||
|
||||
let call = Expr::FunctionCall {
|
||||
call_type: CallType::ByName(proc_name),
|
||||
layout: layout.clone(),
|
||||
ret_layout: ret_layout.clone(),
|
||||
full_layout: full_layout.clone(),
|
||||
arg_layouts,
|
||||
args: field_symbols,
|
||||
};
|
||||
|
||||
let mut result = Stmt::Let(assigned, call, layout.clone(), hole);
|
||||
let mut result = Stmt::Let(assigned, call, ret_layout.clone(), hole);
|
||||
|
||||
for ((_, loc_arg), symbol) in
|
||||
loc_args.into_iter().rev().zip(field_symbols.iter().rev())
|
||||
|
@ -3010,7 +3057,7 @@ fn call_by_name<'a>(
|
|||
// (We had a bug around this before this system existed!)
|
||||
procs
|
||||
.specialized
|
||||
.insert((proc_name, layout.clone()), InProgress);
|
||||
.insert((proc_name, full_layout.clone()), InProgress);
|
||||
|
||||
match specialize(
|
||||
env,
|
||||
|
@ -3023,17 +3070,18 @@ fn call_by_name<'a>(
|
|||
Ok(proc) => {
|
||||
procs
|
||||
.specialized
|
||||
.insert((proc_name, layout.clone()), Done(proc));
|
||||
.insert((proc_name, full_layout.clone()), Done(proc));
|
||||
|
||||
let call = Expr::FunctionCall {
|
||||
call_type: CallType::ByName(proc_name),
|
||||
layout: layout.clone(),
|
||||
ret_layout: ret_layout.clone(),
|
||||
full_layout: full_layout.clone(),
|
||||
arg_layouts,
|
||||
args: field_symbols,
|
||||
};
|
||||
|
||||
let mut result =
|
||||
Stmt::Let(assigned, call, layout.clone(), hole);
|
||||
Stmt::Let(assigned, call, ret_layout.clone(), hole);
|
||||
|
||||
for ((_, loc_arg), symbol) in loc_args
|
||||
.into_iter()
|
||||
|
@ -3325,6 +3373,7 @@ pub fn from_can_pattern<'a>(
|
|||
destructs,
|
||||
..
|
||||
} => {
|
||||
// sorted fields based on the destruct
|
||||
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
|
||||
let mut destructs = destructs.clone();
|
||||
destructs.sort_by(|a, b| a.value.label.cmp(&b.value.label));
|
||||
|
@ -3332,12 +3381,24 @@ pub fn from_can_pattern<'a>(
|
|||
let mut it = destructs.iter();
|
||||
let mut opt_destruct = it.next();
|
||||
|
||||
// sorted fields based on the type
|
||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, *whole_var, env.subs);
|
||||
|
||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||
|
||||
for (label, field_layout) in sorted_fields.into_iter() {
|
||||
if let Some(destruct) = opt_destruct {
|
||||
// next we step through both sequences of fields. The outer loop is the sequence based
|
||||
// on the type, since not all fields need to actually be destructured in the source
|
||||
// language.
|
||||
//
|
||||
// However in mono patterns, we do destruct all patterns (but use Underscore) when
|
||||
// in the source the field is not matche in the source language.
|
||||
//
|
||||
// Optional fields somewhat complicate the matter here
|
||||
for (label, opt_field_layout) in sorted_fields.into_iter() {
|
||||
match opt_field_layout {
|
||||
Ok(field_layout) => {
|
||||
match opt_destruct {
|
||||
Some(destruct) => {
|
||||
if destruct.value.label == label {
|
||||
opt_destruct = it.next();
|
||||
|
||||
|
@ -3356,6 +3417,47 @@ pub fn from_can_pattern<'a>(
|
|||
typ: DestructType::Guard(Pattern::Underscore),
|
||||
});
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// the remainder of the fields (from the type) is not matched on in
|
||||
// this pattern; to fill it out, we put underscores
|
||||
mono_destructs.push(RecordDestruct {
|
||||
label: label.clone(),
|
||||
symbol: env.unique_symbol(),
|
||||
layout: field_layout.clone(),
|
||||
typ: DestructType::Guard(Pattern::Underscore),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
field_layouts.push(field_layout);
|
||||
}
|
||||
Err(field_layout) => {
|
||||
// this field was optional, and now does not exist
|
||||
// if it was actually matched on, we need to evaluate the default
|
||||
match opt_destruct {
|
||||
Some(destruct) => {
|
||||
if destruct.value.label == label {
|
||||
opt_destruct = it.next();
|
||||
|
||||
mono_destructs.push(RecordDestruct {
|
||||
label: destruct.value.label.clone(),
|
||||
symbol: destruct.value.symbol,
|
||||
layout: field_layout,
|
||||
typ: match &destruct.value.typ {
|
||||
roc_can::pattern::DestructType::Optional(
|
||||
_,
|
||||
loc_expr,
|
||||
) => {
|
||||
// if we reach this stage, the optional field is not present
|
||||
// so use the default
|
||||
DestructType::Optional(loc_expr.value.clone())
|
||||
}
|
||||
_ => unreachable!(
|
||||
"only optional destructs can be optional fields"
|
||||
),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// insert underscore pattern
|
||||
mono_destructs.push(RecordDestruct {
|
||||
|
@ -3365,7 +3467,14 @@ pub fn from_can_pattern<'a>(
|
|||
typ: DestructType::Guard(Pattern::Underscore),
|
||||
});
|
||||
}
|
||||
field_layouts.push(field_layout);
|
||||
}
|
||||
None => {
|
||||
// this field does not exist, and was not request in the pattern match
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pattern::RecordDestructure(
|
||||
|
@ -3388,8 +3497,10 @@ fn from_can_record_destruct<'a>(
|
|||
layout: field_layout,
|
||||
typ: match &can_rd.typ {
|
||||
roc_can::pattern::DestructType::Required => DestructType::Required,
|
||||
roc_can::pattern::DestructType::Optional(_, loc_expr) => {
|
||||
DestructType::Optional(loc_expr.value.clone())
|
||||
roc_can::pattern::DestructType::Optional(_, _) => {
|
||||
// if we reach this stage, the optional field is present
|
||||
// DestructType::Optional(loc_expr.value.clone())
|
||||
DestructType::Required
|
||||
}
|
||||
roc_can::pattern::DestructType::Guard(_, loc_pattern) => {
|
||||
DestructType::Guard(from_can_pattern(env, layout_cache, &loc_pattern.value))
|
||||
|
|
|
@ -366,7 +366,17 @@ fn layout_from_flat_type<'a>(
|
|||
for (_, field) in sorted_fields {
|
||||
use LayoutProblem::*;
|
||||
|
||||
let field_var = field.into_inner();
|
||||
let field_var = {
|
||||
use roc_types::types::RecordField::*;
|
||||
match field {
|
||||
Optional(_) => {
|
||||
// optional values are not available at this point
|
||||
continue;
|
||||
}
|
||||
Required(var) => var,
|
||||
Demanded(var) => var,
|
||||
}
|
||||
};
|
||||
let field_content = subs.get_without_compacting(field_var).content;
|
||||
|
||||
match Layout::new(arena, field_content, subs) {
|
||||
|
@ -414,7 +424,7 @@ pub fn sort_record_fields<'a>(
|
|||
arena: &'a Bump,
|
||||
var: Variable,
|
||||
subs: &Subs,
|
||||
) -> Vec<'a, (Lowercase, Layout<'a>)> {
|
||||
) -> Vec<'a, (Lowercase, Result<Layout<'a>, Layout<'a>>)> {
|
||||
let mut fields_map = MutMap::default();
|
||||
|
||||
match roc_types::pretty_print::chase_ext_record(subs, var, &mut fields_map) {
|
||||
|
@ -422,13 +432,24 @@ pub fn sort_record_fields<'a>(
|
|||
// Sort the fields by label
|
||||
let mut sorted_fields = Vec::with_capacity_in(fields_map.len(), arena);
|
||||
|
||||
use roc_types::types::RecordField;
|
||||
for (label, field) in fields_map {
|
||||
let var = field.into_inner();
|
||||
let var = match field {
|
||||
RecordField::Demanded(v) => v,
|
||||
RecordField::Required(v) => v,
|
||||
RecordField::Optional(v) => {
|
||||
let layout =
|
||||
Layout::from_var(arena, v, subs).expect("invalid layout from var");
|
||||
sorted_fields.push((label, Err(layout)));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let layout = Layout::from_var(arena, var, subs).expect("invalid layout from var");
|
||||
|
||||
// Drop any zero-sized fields like {}
|
||||
if !layout.is_zero_sized() {
|
||||
sorted_fields.push((label, layout));
|
||||
sorted_fields.push((label, Ok(layout)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -526,15 +526,15 @@ mod test_mono {
|
|||
"#,
|
||||
indoc!(
|
||||
r#"
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let Test.9 = lowlevel ListAppend #Attr.2 #Attr.3;
|
||||
ret Test.9;
|
||||
|
||||
procedure Test.0 (Test.2):
|
||||
let Test.8 = 42i64;
|
||||
let Test.7 = CallByName List.5 Test.2 Test.8;
|
||||
ret Test.7;
|
||||
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let Test.9 = lowlevel ListAppend #Attr.2 #Attr.3;
|
||||
ret Test.9;
|
||||
|
||||
let Test.5 = 1i64;
|
||||
let Test.6 = 2i64;
|
||||
let Test.4 = Array [Test.5, Test.6];
|
||||
|
@ -580,12 +580,16 @@ mod test_mono {
|
|||
"#,
|
||||
indoc!(
|
||||
r#"
|
||||
procedure Num.14 (#Attr.2, #Attr.3):
|
||||
let Test.13 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.13;
|
||||
|
||||
procedure List.7 (#Attr.2):
|
||||
let Test.9 = lowlevel ListLen #Attr.2;
|
||||
ret Test.9;
|
||||
|
||||
procedure Num.14 (#Attr.2, #Attr.3):
|
||||
let Test.11 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
procedure List.7 (#Attr.2):
|
||||
let Test.11 = lowlevel ListLen #Attr.2;
|
||||
ret Test.11;
|
||||
|
||||
let Test.8 = 1f64;
|
||||
|
@ -901,6 +905,12 @@ mod test_mono {
|
|||
),
|
||||
indoc!(
|
||||
r#"
|
||||
procedure Test.1 (Test.3):
|
||||
let Test.9 = 0i64;
|
||||
let Test.10 = 0i64;
|
||||
let Test.8 = CallByName List.4 Test.3 Test.9 Test.10;
|
||||
ret Test.8;
|
||||
|
||||
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Test.14 = lowlevel ListLen #Attr.2;
|
||||
let Test.12 = lowlevel NumLt #Attr.3 Test.14;
|
||||
|
@ -910,12 +920,6 @@ mod test_mono {
|
|||
else
|
||||
ret #Attr.2;
|
||||
|
||||
procedure Test.1 (Test.3):
|
||||
let Test.9 = 0i64;
|
||||
let Test.10 = 0i64;
|
||||
let Test.8 = CallByName List.4 Test.3 Test.9 Test.10;
|
||||
ret Test.8;
|
||||
|
||||
let Test.5 = 1i64;
|
||||
let Test.6 = 2i64;
|
||||
let Test.7 = 3i64;
|
||||
|
@ -928,6 +932,140 @@ mod test_mono {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_optional_field_let_no_use_default() {
|
||||
compiles_to_ir(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \r ->
|
||||
{ x ? 10, y } = r
|
||||
x + y
|
||||
|
||||
|
||||
f { x: 4, y: 9 }
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
procedure Num.14 (#Attr.2, #Attr.3):
|
||||
let Test.10 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.10;
|
||||
|
||||
procedure Test.0 (Test.2):
|
||||
let Test.3 = Index 0 Test.2;
|
||||
let Test.4 = Index 1 Test.2;
|
||||
let Test.9 = CallByName Num.14 Test.3 Test.4;
|
||||
ret Test.9;
|
||||
|
||||
let Test.7 = 4i64;
|
||||
let Test.8 = 9i64;
|
||||
let Test.6 = Struct {Test.7, Test.8};
|
||||
let Test.5 = CallByName Test.0 Test.6;
|
||||
ret Test.5;
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_optional_field_let_use_default() {
|
||||
compiles_to_ir(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \r ->
|
||||
{ x ? 10, y } = r
|
||||
x + y
|
||||
|
||||
|
||||
f { y: 9 }
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
procedure Num.14 (#Attr.2, #Attr.3):
|
||||
let Test.9 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.9;
|
||||
|
||||
procedure Test.0 (Test.2):
|
||||
let Test.3 = 10i64;
|
||||
let Test.4 = Index 1 Test.2;
|
||||
let Test.8 = CallByName Num.14 Test.3 Test.4;
|
||||
ret Test.8;
|
||||
|
||||
let Test.7 = 9i64;
|
||||
let Test.6 = Struct {Test.7};
|
||||
let Test.5 = CallByName Test.0 Test.6;
|
||||
ret Test.5;
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_optional_field_function_no_use_default() {
|
||||
compiles_to_ir(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \{ x ? 10, y } -> x + y
|
||||
|
||||
|
||||
f { x: 4, y: 9 }
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
procedure Num.14 (#Attr.2, #Attr.3):
|
||||
let Test.10 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.10;
|
||||
|
||||
procedure Test.0 (Test.4):
|
||||
let Test.2 = Index 0 Test.4;
|
||||
let Test.3 = Index 1 Test.4;
|
||||
let Test.9 = CallByName Num.14 Test.2 Test.3;
|
||||
ret Test.9;
|
||||
|
||||
let Test.7 = 4i64;
|
||||
let Test.8 = 9i64;
|
||||
let Test.6 = Struct {Test.7, Test.8};
|
||||
let Test.5 = CallByName Test.0 Test.6;
|
||||
ret Test.5;
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_optional_field_function_use_default() {
|
||||
compiles_to_ir(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \{ x ? 10, y } -> x + y
|
||||
|
||||
|
||||
f { y: 9 }
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
procedure Num.14 (#Attr.2, #Attr.3):
|
||||
let Test.9 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.9;
|
||||
|
||||
procedure Test.0 (Test.4):
|
||||
let Test.2 = 10i64;
|
||||
let Test.3 = Index 1 Test.4;
|
||||
let Test.8 = CallByName Num.14 Test.2 Test.3;
|
||||
ret Test.8;
|
||||
|
||||
let Test.7 = 9i64;
|
||||
let Test.6 = Struct {Test.7};
|
||||
let Test.5 = CallByName Test.0 Test.6;
|
||||
ret Test.5;
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn quicksort_help() {
|
||||
crate::helpers::with_larger_debug_stack(|| {
|
||||
|
@ -1094,14 +1232,14 @@ mod test_mono {
|
|||
let Test.10 = CallByName Num.16 Test.2 Test.3;
|
||||
jump Test.20 Test.9 Test.10;
|
||||
|
||||
procedure Num.15 (#Attr.2, #Attr.3):
|
||||
let Test.14 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Test.14;
|
||||
|
||||
procedure Num.16 (#Attr.2, #Attr.3):
|
||||
let Test.11 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Test.11;
|
||||
|
||||
procedure Num.15 (#Attr.2, #Attr.3):
|
||||
let Test.14 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Test.14;
|
||||
|
||||
let Test.5 = 10i64;
|
||||
let Test.6 = 1i64;
|
||||
let Test.4 = CallByName Test.0 Test.5 Test.6;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue