mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
Register accessor closures when they are bound
Previously we only registered record accessor closures in anonymous contexts, where we assume they must already be specialized based on the surrounding contexts. This is not true in general since one might bind an accessor to a name. Closes #2567
This commit is contained in:
parent
d7c8c8de42
commit
3bff99b0a2
5 changed files with 154 additions and 26 deletions
|
@ -144,6 +144,7 @@ pub enum Expr {
|
|||
name: Symbol,
|
||||
function_var: Variable,
|
||||
record_var: Variable,
|
||||
closure_var: Variable,
|
||||
closure_ext_var: Variable,
|
||||
ext_var: Variable,
|
||||
field_var: Variable,
|
||||
|
@ -740,6 +741,7 @@ pub fn canonicalize_expr<'a>(
|
|||
function_var: var_store.fresh(),
|
||||
record_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
closure_var: var_store.fresh(),
|
||||
closure_ext_var: var_store.fresh(),
|
||||
field_var: var_store.fresh(),
|
||||
field: (*field).into(),
|
||||
|
|
|
@ -769,7 +769,8 @@ pub fn constrain_expr(
|
|||
function_var,
|
||||
field,
|
||||
record_var,
|
||||
closure_ext_var: closure_var,
|
||||
closure_var,
|
||||
closure_ext_var,
|
||||
ext_var,
|
||||
field_var,
|
||||
} => {
|
||||
|
@ -795,16 +796,24 @@ pub fn constrain_expr(
|
|||
|
||||
let lambda_set = Type::ClosureTag {
|
||||
name: *closure_name,
|
||||
ext: *closure_var,
|
||||
ext: *closure_ext_var,
|
||||
};
|
||||
|
||||
let closure_type = Type::Variable(*closure_var);
|
||||
|
||||
let function_type = Type::Function(
|
||||
vec![record_type],
|
||||
Box::new(lambda_set),
|
||||
Box::new(closure_type.clone()),
|
||||
Box::new(field_type),
|
||||
);
|
||||
|
||||
let cons = [
|
||||
constraints.equal_types(
|
||||
closure_type,
|
||||
NoExpectation(lambda_set),
|
||||
category.clone(),
|
||||
region,
|
||||
),
|
||||
constraints.equal_types(function_type.clone(), expected, category.clone(), region),
|
||||
constraints.equal_types(
|
||||
function_type,
|
||||
|
@ -816,7 +825,14 @@ pub fn constrain_expr(
|
|||
];
|
||||
|
||||
constraints.exists_many(
|
||||
[*record_var, *function_var, *closure_var, field_var, ext_var],
|
||||
[
|
||||
*record_var,
|
||||
*function_var,
|
||||
*closure_var,
|
||||
*closure_ext_var,
|
||||
field_var,
|
||||
ext_var,
|
||||
],
|
||||
cons,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -816,6 +816,11 @@ impl<'a> Procs<'a> {
|
|||
ret_var: Variable,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
) -> Result<ProcLayout<'a>, RuntimeError> {
|
||||
dbg!(env
|
||||
.subs
|
||||
.get_content_without_compacting(annotation)
|
||||
.clone()
|
||||
.dbg(env.subs));
|
||||
let raw_layout = layout_cache
|
||||
.raw_from_var(env.arena, annotation, env.subs)
|
||||
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||
|
@ -3088,6 +3093,53 @@ fn try_make_literal<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn accessor_to_closure<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
name: Symbol,
|
||||
function_var: Variable,
|
||||
record_var: Variable,
|
||||
closure_var: Variable,
|
||||
closure_ext_var: Variable,
|
||||
ext_var: Variable,
|
||||
field_var: Variable,
|
||||
field: Lowercase,
|
||||
) -> ClosureData {
|
||||
// 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(Loc::at_zero(roc_can::expr::Expr::Var(record_symbol))),
|
||||
field,
|
||||
};
|
||||
|
||||
let loc_body = Loc::at_zero(body);
|
||||
|
||||
let arguments = vec![(
|
||||
record_var,
|
||||
Loc::at_zero(roc_can::pattern::Pattern::Identifier(record_symbol)),
|
||||
)];
|
||||
|
||||
ClosureData {
|
||||
function_type: function_var,
|
||||
closure_type: closure_var,
|
||||
closure_ext_var,
|
||||
return_type: field_var,
|
||||
name,
|
||||
captured_symbols: vec![],
|
||||
recursive: roc_can::expr::Recursive::NotRecursive,
|
||||
arguments,
|
||||
loc_body: Box::new(loc_body),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_hole<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
can_expr: roc_can::expr::Expr,
|
||||
|
@ -3886,40 +3938,36 @@ pub fn with_hole<'a>(
|
|||
name,
|
||||
function_var,
|
||||
record_var,
|
||||
closure_ext_var: _,
|
||||
closure_var,
|
||||
closure_ext_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 {
|
||||
let ClosureData {
|
||||
name,
|
||||
function_type,
|
||||
arguments,
|
||||
loc_body,
|
||||
..
|
||||
} = accessor_to_closure(
|
||||
env,
|
||||
name,
|
||||
function_var,
|
||||
record_var,
|
||||
closure_var,
|
||||
closure_ext_var,
|
||||
ext_var,
|
||||
field_var,
|
||||
loc_expr: Box::new(Loc::at_zero(roc_can::expr::Expr::Var(record_symbol))),
|
||||
field,
|
||||
};
|
||||
|
||||
let loc_body = Loc::at_zero(body);
|
||||
|
||||
let arguments = vec![(
|
||||
record_var,
|
||||
Loc::at_zero(roc_can::pattern::Pattern::Identifier(record_symbol)),
|
||||
)];
|
||||
);
|
||||
|
||||
match procs.insert_anonymous(
|
||||
env,
|
||||
name,
|
||||
function_var,
|
||||
function_type,
|
||||
arguments,
|
||||
loc_body,
|
||||
*loc_body,
|
||||
CapturedSymbols::None,
|
||||
field_var,
|
||||
layout_cache,
|
||||
|
@ -5445,6 +5493,38 @@ pub fn from_can<'a>(
|
|||
|
||||
return from_can(env, variable, cont.value, procs, layout_cache);
|
||||
}
|
||||
roc_can::expr::Expr::Accessor {
|
||||
name,
|
||||
function_var,
|
||||
record_var,
|
||||
closure_var,
|
||||
closure_ext_var,
|
||||
ext_var,
|
||||
field_var,
|
||||
field,
|
||||
} => {
|
||||
let closure_data = accessor_to_closure(
|
||||
env,
|
||||
name,
|
||||
function_var,
|
||||
record_var,
|
||||
closure_var,
|
||||
closure_ext_var,
|
||||
ext_var,
|
||||
field_var,
|
||||
field,
|
||||
);
|
||||
|
||||
register_noncapturing_closure(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
*symbol,
|
||||
closure_data,
|
||||
);
|
||||
|
||||
return from_can(env, variable, cont.value, procs, layout_cache);
|
||||
}
|
||||
roc_can::expr::Expr::Var(original) => {
|
||||
// a variable is aliased, e.g.
|
||||
//
|
||||
|
|
|
@ -5529,4 +5529,18 @@ mod solve_expr {
|
|||
r#"a -> Effect a"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generalized_accessor_function_applied() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
returnFoo = .foo
|
||||
|
||||
returnFoo { foo: "foo" }
|
||||
"#
|
||||
),
|
||||
"Str",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::helpers::wasm::assert_evals_to;
|
|||
use indoc::indoc;
|
||||
|
||||
#[cfg(test)]
|
||||
use roc_std::RocList;
|
||||
use roc_std::{RocList, RocStr};
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
|
@ -1058,3 +1058,19 @@ fn call_with_bad_record_runtime_error() {
|
|||
"#
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn generalized_accessor() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
returnFoo = .foo
|
||||
|
||||
returnFoo { foo: "foo" }
|
||||
"#
|
||||
),
|
||||
RocStr::from("foo"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue