fix bug with optional record fields

This commit is contained in:
Folkert 2020-09-07 14:13:22 +02:00
parent 46755973d6
commit 127dd06586
10 changed files with 202 additions and 162 deletions

View File

@ -648,11 +648,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
.struct_type(field_types.into_bump_slice(), false);
// cast the argument bytes into the desired shape for this tag
println!(
"{} {:?}",
structure,
load_symbol_and_layout(env, scope, structure)
);
let argument = load_symbol(env, scope, structure).into_struct_value();
let struct_value = cast_struct_struct(builder, argument, struct_type);

View File

@ -611,7 +611,8 @@ mod gen_primitives {
Cons _ rest -> 1 + length rest
length one + length one
# TODO actually calculate twice
2 * length one
"#
),
2,

View File

@ -190,7 +190,7 @@ mod gen_records {
}
#[test]
fn when_on_record_with_guard_pattern() {
fn when_record_with_guard_pattern() {
assert_evals_to!(
indoc!(
r#"

View File

@ -124,7 +124,6 @@ pub fn helper_without_uniqueness<'a>(
);
let (mut procs, param_map) = procs.get_specialized_procs_help(mono_env.arena);
dbg!(&main_body);
let main_body = roc_mono::inc_dec::visit_declaration(
mono_env.arena,
param_map,
@ -318,7 +317,6 @@ pub fn helper_with_uniqueness<'a>(
);
let (mut procs, param_map) = procs.get_specialized_procs_help(mono_env.arena);
dbg!(&main_body);
let main_body = roc_mono::inc_dec::visit_declaration(
mono_env.arena,
param_map,

View File

@ -602,7 +602,6 @@ fn to_relevant_branch_help<'a>(
Wrapped::MultiTagUnion => {
let sub_positions = arguments.into_iter().enumerate().map(
|(index, (pattern, _))| {
dbg!(index, &pattern, &tag_name, tag_id);
(
Path::Index {
index: 1 + index as u64,
@ -956,7 +955,6 @@ fn path_to_expr_help2<'a>(
let mut stores = bumpalo::collections::Vec::new_in(env.arena);
loop {
dbg!(&layout);
match path {
Path::Unbox(unboxed) => {
path = unboxed;
@ -980,7 +978,6 @@ fn path_to_expr_help2<'a>(
debug_assert!(*index < field_layouts.len() as u64);
let inner_layout = field_layouts[*index as usize].clone();
println!("loc 5 {}", symbol);
let inner_expr = Expr::AccessAtIndex {
index: *index,
field_layouts: env.arena.alloc(field_layouts),
@ -1002,12 +999,10 @@ fn path_to_expr_help2<'a>(
other => vec![other.clone()],
};
dbg!(&field_layouts, tag_id, index);
debug_assert!(*index < field_layouts.len() as u64);
let inner_layout = field_layouts[*index as usize].clone();
println!("loc 6 {}", symbol);
let inner_expr = Expr::AccessAtIndex {
index: *index,
field_layouts: env.arena.alloc(field_layouts),
@ -1063,7 +1058,6 @@ fn test_to_equality<'a>(
}
let field_layouts = field_layouts.into_bump_slice();
println!("loc 7 {}", path_symbol);
let rhs = Expr::AccessAtIndex {
index: 0,
field_layouts,
@ -1184,7 +1178,7 @@ fn decide_to_branching<'a>(
.expect("jump not in list of jumps");
expr.clone()
}
Leaf(Inline(expr)) => dbg!(expr),
Leaf(Inline(expr)) => expr,
Chain {
test_chain,
success,

View File

@ -550,7 +550,7 @@ impl Wrapped {
}
pub fn opt_from_layout(layout: &Layout<'_>) -> Option<Self> {
let result = match layout {
match layout {
Layout::Struct(fields) => match fields.len() {
0 => Some(Wrapped::EmptyRecord),
1 => Some(Wrapped::SingleElementRecord),
@ -567,9 +567,7 @@ impl Wrapped {
_ => Some(Wrapped::MultiTagUnion),
},
_ => None,
};
result
}
}
}
@ -1895,7 +1893,6 @@ pub fn with_hole<'a>(
}
};
println!("loc 3 {}", record_symbol);
let expr = Expr::AccessAtIndex {
index: index.expect("field not in its own type") as u64,
field_layouts: field_layouts.into_bump_slice(),
@ -2923,7 +2920,6 @@ fn store_pattern<'a>(
for (index, (argument, arg_layout)) in arguments.iter().enumerate().rev() {
let index = if write_tag { index + 1 } else { index };
println!("loc 1 {}", outer_symbol);
let load = Expr::AccessAtIndex {
wrapped,
index: index as u64,
@ -3003,7 +2999,6 @@ fn store_record_destruct<'a>(
let wrapped = Wrapped::from_layout(&Layout::Struct(sorted_fields));
// TODO wrapped could be SingleElementRecord
println!("loc 2 {}", outer_symbol);
let load = Expr::AccessAtIndex {
index,
field_layouts: sorted_fields,
@ -3543,20 +3538,24 @@ pub fn from_can_pattern<'a>(
destructs,
..
} => {
// sorted fields based on the type
let sorted_fields = crate::layout::sort_record_fields(env.arena, *whole_var, env.subs);
let type_identifiers_by_key = sorted_fields
.iter()
.map(|(label, _)| label)
.collect::<MutSet<_>>();
let destructs_by_key = destructs
.iter()
.map(|destruct| destruct.value.label.clone())
.collect::<MutSet<_>>();
// 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));
dbg!(&destructs);
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);
dbg!(&sorted_fields);
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
// next we step through both sequences of fields. The outer loop is the sequence based
@ -3567,85 +3566,113 @@ pub fn from_can_pattern<'a>(
// 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();
let mut it1 = sorted_fields.into_iter();
let mut opt_sorted = it1.next();
mono_destructs.push(from_can_record_destruct(
env,
layout_cache,
&destruct.value,
field_layout.clone(),
));
} else {
// insert underscore pattern
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
layout: field_layout.clone(),
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),
});
}
let mut it2 = destructs.iter();
let mut opt_destruct = it2.next();
loop {
match (opt_sorted, opt_destruct) {
(Some((label, Ok(field_layout))), Some(destruct)) => {
if destruct.value.label == label {
mono_destructs.push(from_can_record_destruct(
env,
layout_cache,
&destruct.value,
field_layout.clone(),
));
opt_sorted = it1.next();
opt_destruct = it2.next();
} else {
// insert underscore pattern
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
});
opt_sorted = it1.next();
}
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();
(Some((label, Err(field_layout))), Some(destruct)) => {
if destruct.value.label == label {
opt_destruct = it2.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 {
label: label.clone(),
symbol: env.unique_symbol(),
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
});
}
}
None => {
// this field does not exist, and was not request in the pattern match
continue;
}
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"
),
},
});
}
opt_sorted = it1.next();
}
(Some((label, Err(field_layout))), 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),
});
opt_sorted = it1.next();
}
(Some((label, Ok(field_layout))), 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);
opt_sorted = it1.next();
}
(None, Some(destruct)) => {
// destruct is not in the type, but is in the pattern
// it must be an optional field, and we will use the default
match &destruct.value.typ {
roc_can::pattern::DestructType::Optional(field_var, loc_expr) => {
let field_layout = layout_cache
.from_var(env.arena, *field_var, env.subs)
.unwrap_or_else(|err| {
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
});
mono_destructs.push(RecordDestruct {
label: destruct.value.label.clone(),
symbol: destruct.value.symbol,
layout: field_layout,
typ: DestructType::Optional(loc_expr.value.clone()),
})
}
_ => unreachable!("only optional destructs can be optional fields"),
}
opt_sorted = None;
opt_destruct = it2.next();
}
(None, None) => {
break;
}
}
}

View File

@ -116,7 +116,6 @@ impl<'a> Layout<'a> {
Ok(Layout::RecursivePointer)
} else {
let content = env.subs.get_without_compacting(var).content;
// println!("{:?} {:?}", var, &content);
Self::new_help(env, content)
}
}
@ -527,7 +526,6 @@ pub fn sort_record_fields<'a>(
seen: MutSet::default(),
};
dbg!(var);
match roc_types::pretty_print::chase_ext_record(subs, var, &mut fields_map) {
Ok(()) | Err((_, Content::FlexVar(_))) => {
// Sort the fields by label

View File

@ -457,23 +457,24 @@ mod test_mono {
indoc!(
r#"
procedure Num.14 (#Attr.2, #Attr.3):
let Test.20 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Test.20;
let Test.21 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Test.21;
let Test.16 = 0i64;
let Test.18 = 0i64;
let Test.19 = 41i64;
let Test.17 = Just Test.18 Test.19;
let Test.1 = Just Test.16 Test.17;
let Test.17 = 0i64;
let Test.19 = 0i64;
let Test.20 = 41i64;
let Test.18 = Just Test.19 Test.20;
let Test.1 = Just Test.17 Test.18;
let Test.8 = true;
let Test.10 = Index 0 Test.1;
let Test.9 = 0i64;
let Test.15 = lowlevel Eq Test.9 Test.10;
let Test.13 = lowlevel And Test.15 Test.8;
let Test.12 = Index 0 Test.1;
let Test.11 = 0i64;
let Test.14 = lowlevel Eq Test.11 Test.12;
let Test.7 = lowlevel And Test.14 Test.13;
let Test.10 = 0i64;
let Test.9 = Index 1 Test.1;
let Test.11 = Index 0 Test.9;
let Test.16 = lowlevel Eq Test.10 Test.11;
let Test.14 = lowlevel And Test.16 Test.8;
let Test.12 = 0i64;
let Test.13 = Index 0 Test.1;
let Test.15 = lowlevel Eq Test.12 Test.13;
let Test.7 = lowlevel And Test.15 Test.14;
if Test.7 then
let Test.5 = Index 1 Test.1;
let Test.2 = Index 1 Test.5;
@ -1642,25 +1643,26 @@ mod test_mono {
),
indoc!(
r#"
let Test.16 = 0i64;
let Test.18 = 0i64;
let Test.20 = 0i64;
let Test.22 = 1i64;
let Test.21 = Z Test.22;
let Test.19 = S Test.20 Test.21;
let Test.17 = S Test.18 Test.19;
let Test.1 = S Test.16 Test.17;
let Test.12 = true;
let Test.14 = Index 0 Test.1;
let Test.13 = 0i64;
let Test.15 = lowlevel Eq Test.13 Test.14;
let Test.11 = lowlevel And Test.15 Test.12;
if Test.11 then
let Test.17 = 0i64;
let Test.19 = 0i64;
let Test.21 = 0i64;
let Test.23 = 1i64;
let Test.22 = Z Test.23;
let Test.20 = S Test.21 Test.22;
let Test.18 = S Test.19 Test.20;
let Test.1 = S Test.17 Test.18;
let Test.13 = true;
let Test.14 = 0i64;
let Test.15 = Index 0 Test.1;
let Test.16 = lowlevel Eq Test.14 Test.15;
let Test.12 = lowlevel And Test.16 Test.13;
if Test.12 then
let Test.7 = true;
let Test.9 = Index 0 Test.1;
let Test.8 = 0i64;
let Test.10 = lowlevel Eq Test.8 Test.9;
let Test.6 = lowlevel And Test.10 Test.7;
let Test.9 = 0i64;
let Test.8 = Index 1 Test.1;
let Test.10 = Index 0 Test.8;
let Test.11 = lowlevel Eq Test.9 Test.10;
let Test.6 = lowlevel And Test.11 Test.7;
if Test.6 then
let Test.3 = Index 1 Test.1;
let Test.2 = 1i64;
@ -1697,22 +1699,53 @@ mod test_mono {
indoc!(
r#"
procedure Num.16 (#Attr.2, #Attr.3):
let Test.15 = lowlevel NumMul #Attr.2 #Attr.3;
ret Test.15;
let Test.38 = lowlevel NumMul #Attr.2 #Attr.3;
ret Test.38;
procedure Test.0 (Test.4):
ret Test.6;
procedure Test.0 (Test.6):
let Test.27 = true;
let Test.29 = false;
let Test.28 = Index 0 Test.6;
let Test.30 = lowlevel Eq Test.29 Test.28;
let Test.26 = lowlevel And Test.30 Test.27;
if Test.26 then
let Test.8 = 3i64;
ret Test.8;
else
let Test.10 = 5i64;
ret Test.10;
procedure Test.0 (Test.4):
ret Test.6;
procedure Test.0 (Test.6):
let Test.34 = true;
let Test.36 = false;
let Test.35 = Index 0 Test.6;
let Test.37 = lowlevel Eq Test.36 Test.35;
let Test.33 = lowlevel And Test.37 Test.34;
if Test.33 then
let Test.8 = Index 1 Test.6;
ret Test.8;
else
let Test.10 = Index 1 Test.6;
ret Test.10;
let Test.10 = 7i64;
let Test.9 = Struct {Test.10};
let Test.1 = CallByName Test.0 Test.9;
let Test.8 = Struct {};
let Test.2 = CallByName Test.0 Test.8;
let Test.7 = CallByName Num.16 Test.1 Test.2;
ret Test.7;
let Test.22 = true;
let Test.23 = 11i64;
let Test.21 = Struct {Test.22, Test.23};
let Test.3 = CallByName Test.0 Test.21;
let Test.20 = true;
let Test.19 = Struct {Test.20};
let Test.4 = CallByName Test.0 Test.19;
let Test.17 = false;
let Test.18 = 7i64;
let Test.16 = Struct {Test.17, Test.18};
let Test.1 = CallByName Test.0 Test.16;
let Test.15 = false;
let Test.14 = Struct {Test.15};
let Test.2 = CallByName Test.0 Test.14;
let Test.13 = CallByName Num.16 Test.1 Test.2;
let Test.12 = CallByName Num.16 Test.13 Test.3;
let Test.11 = CallByName Num.16 Test.12 Test.4;
ret Test.11;
"#
),
)

View File

@ -611,7 +611,7 @@ pub fn chase_ext_record(
use crate::subs::Content::*;
use crate::subs::FlatType::*;
match dbg!(subs.get_without_compacting(var).content) {
match subs.get_without_compacting(var).content {
Structure(Record(sub_fields, sub_ext)) => {
fields.extend(sub_fields.into_iter());

View File

@ -705,17 +705,11 @@ fn unify_flat_type(
(EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(left.clone())),
(Record(fields, ext), EmptyRecord) if has_only_optional_fields(&mut fields.values()) => {
let mut outcome = unify_pool(subs, pool, *ext, ctx.second);
outcome.extend(merge(subs, ctx, Structure(FlatType::EmptyRecord)));
outcome
unify_pool(subs, pool, *ext, ctx.second)
}
(EmptyRecord, Record(fields, ext)) if has_only_optional_fields(&mut fields.values()) => {
let mut outcome = unify_pool(subs, pool, ctx.first, *ext);
outcome.extend(merge(subs, ctx, Structure(FlatType::EmptyRecord)));
outcome
unify_pool(subs, pool, ctx.first, *ext)
}
(Record(fields1, ext1), Record(fields2, ext2)) => {