diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index aef77617ba..d915954456 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -126,6 +126,7 @@ pub enum Expr { }, /// field accessor as a function, e.g. (.foo) expr Accessor { + function_var: Variable, record_var: Variable, closure_var: Variable, ext_var: Variable, @@ -550,6 +551,7 @@ pub fn canonicalize_expr<'a>( } ast::Expr::AccessorFunction(field) => ( Accessor { + function_var: var_store.fresh(), record_var: var_store.fresh(), ext_var: var_store.fresh(), closure_var: var_store.fresh(), diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 4fccddbff6..57e79ce785 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -675,6 +675,7 @@ pub fn constrain_expr( ) } Accessor { + function_var, field, record_var, closure_var, @@ -701,16 +702,19 @@ pub fn constrain_expr( region, ); + let function_type = Type::Function( + vec![record_type], + Box::new(Type::Variable(*closure_var)), + Box::new(field_type), + ); + exists( - vec![*record_var, *closure_var, field_var, ext_var], + vec![*record_var, *function_var, *closure_var, field_var, ext_var], And(vec![ + Eq(function_type.clone(), expected, category.clone(), region), Eq( - Type::Function( - vec![record_type], - Box::new(Type::Variable(*closure_var)), - Box::new(field_type), - ), - expected, + function_type, + NoExpectation(Variable(*function_var)), category, region, ), diff --git a/compiler/constrain/src/uniq.rs b/compiler/constrain/src/uniq.rs index 7a59332de3..942cf8c650 100644 --- a/compiler/constrain/src/uniq.rs +++ b/compiler/constrain/src/uniq.rs @@ -1445,6 +1445,7 @@ pub fn constrain_expr( } Accessor { + function_var, field, record_var, closure_var, @@ -1490,6 +1491,7 @@ pub fn constrain_expr( exists( vec![ *record_var, + *function_var, *closure_var, *field_var, *ext_var, @@ -1497,7 +1499,16 @@ pub fn constrain_expr( field_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, diff --git a/compiler/gen/tests/gen_records.rs b/compiler/gen/tests/gen_records.rs index ffcb65cca3..0e4f5859fd 100644 --- a/compiler/gen/tests/gen_records.rs +++ b/compiler/gen/tests/gen_records.rs @@ -678,15 +678,15 @@ mod gen_records { } #[test] - fn just_to_be_sure() { + fn accessor() { assert_evals_to!( indoc!( r#" - { a: 1, b : 2, c : 3 } + .foo { foo: 4 } + .foo { bar: 6.28, foo: 3 } "# ), - [1, 2, 3], - [i64; 3] + 7, + i64 ); } } diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 42a4c5217f..0f2305b1ac 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1959,7 +1959,65 @@ pub fn with_hole<'a>( 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 { function_type, diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 94233d33e7..ad9f4d16fd 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -406,15 +406,19 @@ fn layout_from_flat_type<'a>( )) } Record(fields, ext_var) => { - debug_assert!(ext_var_is_empty_record(subs, ext_var)); - // Sort the fields by label let mut sorted_fields = Vec::with_capacity_in(fields.len(), arena); + sorted_fields.extend(fields.into_iter()); - for tuple in fields { - sorted_fields.push(tuple); + // extract any values from the ext_var + 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)); // Determine the layouts of the fields, maintaining sort order @@ -781,16 +785,6 @@ fn ext_var_is_empty_tag_union(_: &Subs, _: Variable) -> bool { 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))] fn ext_var_is_empty_record(_: &Subs, _: Variable) -> bool { // This should only ever be used in debug_assert! macros