code gen Accessor

This commit is contained in:
Folkert 2020-10-03 21:08:41 +02:00
parent bb6f36ad28
commit c8e5acf142
6 changed files with 96 additions and 27 deletions

View file

@ -126,6 +126,7 @@ pub enum Expr {
}, },
/// field accessor as a function, e.g. (.foo) expr /// field accessor as a function, e.g. (.foo) expr
Accessor { Accessor {
function_var: Variable,
record_var: Variable, record_var: Variable,
closure_var: Variable, closure_var: Variable,
ext_var: Variable, ext_var: Variable,
@ -550,6 +551,7 @@ pub fn canonicalize_expr<'a>(
} }
ast::Expr::AccessorFunction(field) => ( ast::Expr::AccessorFunction(field) => (
Accessor { Accessor {
function_var: var_store.fresh(),
record_var: var_store.fresh(), record_var: var_store.fresh(),
ext_var: var_store.fresh(), ext_var: var_store.fresh(),
closure_var: var_store.fresh(), closure_var: var_store.fresh(),

View file

@ -675,6 +675,7 @@ pub fn constrain_expr(
) )
} }
Accessor { Accessor {
function_var,
field, field,
record_var, record_var,
closure_var, closure_var,
@ -701,16 +702,19 @@ pub fn constrain_expr(
region, region,
); );
exists( let function_type = Type::Function(
vec![*record_var, *closure_var, field_var, ext_var],
And(vec![
Eq(
Type::Function(
vec![record_type], vec![record_type],
Box::new(Type::Variable(*closure_var)), Box::new(Type::Variable(*closure_var)),
Box::new(field_type), Box::new(field_type),
), );
expected,
exists(
vec![*record_var, *function_var, *closure_var, field_var, ext_var],
And(vec![
Eq(function_type.clone(), expected, category.clone(), region),
Eq(
function_type,
NoExpectation(Variable(*function_var)),
category, category,
region, region,
), ),

View file

@ -1445,6 +1445,7 @@ pub fn constrain_expr(
} }
Accessor { Accessor {
function_var,
field, field,
record_var, record_var,
closure_var, closure_var,
@ -1490,6 +1491,7 @@ pub fn constrain_expr(
exists( exists(
vec![ vec![
*record_var, *record_var,
*function_var,
*closure_var, *closure_var,
*field_var, *field_var,
*ext_var, *ext_var,
@ -1497,7 +1499,16 @@ pub fn constrain_expr(
field_uniq_var, field_uniq_var,
record_uniq_var, record_uniq_var,
], ],
And(vec![Eq(fn_type, expected, category, region), record_con]), And(vec![
Eq(fn_type.clone(), expected, category.clone(), region),
Eq(
fn_type,
Expected::NoExpectation(Variable(*function_var)),
category,
region,
),
record_con,
]),
) )
} }
RuntimeError(_) => True, RuntimeError(_) => True,

View file

@ -678,15 +678,15 @@ mod gen_records {
} }
#[test] #[test]
fn just_to_be_sure() { fn accessor() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
{ a: 1, b : 2, c : 3 } .foo { foo: 4 } + .foo { bar: 6.28, foo: 3 }
"# "#
), ),
[1, 2, 3], 7,
[i64; 3] i64
); );
} }
} }

View file

@ -1959,7 +1959,65 @@ pub fn with_hole<'a>(
stmt stmt
} }
Accessor { .. } | Update { .. } => todo!("record access/accessor/update"), Accessor {
function_var,
record_var,
closure_var: _,
ext_var,
field_var,
field,
} => {
// IDEA: convert accessor fromt
//
// .foo
//
// into
//
// (\r -> r.foo)
let record_symbol = env.unique_symbol();
let body = roc_can::expr::Expr::Access {
record_var,
ext_var,
field_var,
loc_expr: Box::new(Located::at_zero(roc_can::expr::Expr::Var(record_symbol))),
field: field.clone(),
};
let loc_body = Located::at_zero(body);
let name = env.unique_symbol();
let arguments = vec![(
record_var,
Located::at_zero(roc_can::pattern::Pattern::Identifier(record_symbol)),
)];
match procs.insert_anonymous(
env,
name,
function_var,
arguments,
loc_body,
field_var,
layout_cache,
) {
Ok(layout) => {
// TODO should the let have layout Pointer?
Stmt::Let(
assigned,
Expr::FunctionPointer(name, layout.clone()),
layout,
hole,
)
}
Err(_error) => Stmt::RuntimeError(
"TODO convert anonymous function error to a RuntimeError string",
),
}
}
Update { .. } => todo!("record access/accessor/update"),
Closure { Closure {
function_type, function_type,

View file

@ -406,15 +406,19 @@ fn layout_from_flat_type<'a>(
)) ))
} }
Record(fields, ext_var) => { Record(fields, ext_var) => {
debug_assert!(ext_var_is_empty_record(subs, ext_var));
// Sort the fields by label // Sort the fields by label
let mut sorted_fields = Vec::with_capacity_in(fields.len(), arena); let mut sorted_fields = Vec::with_capacity_in(fields.len(), arena);
sorted_fields.extend(fields.into_iter());
for tuple in fields { // extract any values from the ext_var
sorted_fields.push(tuple); let mut fields_map = MutMap::default();
match roc_types::pretty_print::chase_ext_record(subs, ext_var, &mut fields_map) {
Ok(()) | Err((_, Content::FlexVar(_))) => {}
Err(_) => unreachable!("this would have been a type error"),
} }
sorted_fields.extend(fields_map.into_iter());
sorted_fields.sort_by(|(label1, _), (label2, _)| label1.cmp(label2)); sorted_fields.sort_by(|(label1, _), (label2, _)| label1.cmp(label2));
// Determine the layouts of the fields, maintaining sort order // Determine the layouts of the fields, maintaining sort order
@ -781,16 +785,6 @@ fn ext_var_is_empty_tag_union(_: &Subs, _: Variable) -> bool {
unreachable!(); unreachable!();
} }
#[cfg(debug_assertions)]
fn ext_var_is_empty_record(subs: &Subs, ext_var: Variable) -> bool {
// the ext_var is empty
let mut ext_fields = MutMap::default();
match roc_types::pretty_print::chase_ext_record(subs, ext_var, &mut ext_fields) {
Ok(()) | Err((_, Content::FlexVar(_))) => ext_fields.is_empty(),
Err((_, content)) => panic!("invalid content in ext_var: {:?}", content),
}
}
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
fn ext_var_is_empty_record(_: &Subs, _: Variable) -> bool { fn ext_var_is_empty_record(_: &Subs, _: Variable) -> bool {
// This should only ever be used in debug_assert! macros // This should only ever be used in debug_assert! macros