mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 05:34:11 +03:00
fix bug with optional record fields
This commit is contained in:
parent
46755973d6
commit
127dd06586
@ -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);
|
||||
|
@ -611,7 +611,8 @@ mod gen_primitives {
|
||||
Cons _ rest -> 1 + length rest
|
||||
|
||||
|
||||
length one + length one
|
||||
# TODO actually calculate twice
|
||||
2 * length one
|
||||
"#
|
||||
),
|
||||
2,
|
||||
|
@ -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#"
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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)) => {
|
||||
|
Loading…
Reference in New Issue
Block a user