Handle aliasing of ability members

This commit is contained in:
Ayaz Hafiz 2022-04-15 10:21:35 -04:00
parent 7d4a006377
commit 95783e03a1
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
2 changed files with 119 additions and 54 deletions

View file

@ -252,11 +252,22 @@ impl<'a> PartialProc<'a> {
}
#[derive(Clone, Debug)]
enum PartialExprLink {
Aliases(Symbol),
enum PolymorphicExpr {
/// A root ability member, which must be specialized at a call site, for example
/// "hash" which must be specialized to an exact symbol implementing "hash" for a type.
AbilityMember(Symbol),
/// A polymorphic expression we inline at the usage site.
Expr(roc_can::expr::Expr, Variable),
}
#[derive(Clone, Debug)]
enum PartialExprLink {
/// The root polymorphic expresison
Sink(PolymorphicExpr),
/// A hop in a partial expression alias chain
Aliases(Symbol),
}
/// A table of symbols to polymorphic expressions. For example, in the program
///
/// n = 1
@ -282,8 +293,8 @@ impl PartialExprs {
Self(BumpMap::new_in(arena))
}
fn insert(&mut self, symbol: Symbol, expr: roc_can::expr::Expr, expr_var: Variable) {
self.0.insert(symbol, PartialExprLink::Expr(expr, expr_var));
fn insert(&mut self, symbol: Symbol, expr: PolymorphicExpr) {
self.0.insert(symbol, PartialExprLink::Sink(expr));
}
fn insert_alias(&mut self, symbol: Symbol, aliases: Symbol) {
@ -294,7 +305,7 @@ impl PartialExprs {
self.0.contains_key(&symbol)
}
fn get(&mut self, mut symbol: Symbol) -> Option<(&roc_can::expr::Expr, Variable)> {
fn get(&mut self, mut symbol: Symbol) -> Option<&PolymorphicExpr> {
// In practice the alias chain is very short
loop {
match self.0.get(&symbol) {
@ -304,8 +315,8 @@ impl PartialExprs {
Some(&PartialExprLink::Aliases(real_symbol)) => {
symbol = real_symbol;
}
Some(PartialExprLink::Expr(expr, var)) => {
return Some((expr, *var));
Some(PartialExprLink::Sink(expr)) => {
return Some(expr);
}
}
}
@ -4226,7 +4237,7 @@ pub fn with_hole<'a>(
match loc_expr.value {
roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => {
// This might be an ability member - if so, use the appropriate specialization.
let proc_name = repoint_to_specialization(env, fn_var, proc_name);
let proc_name = get_specialization(env, fn_var, proc_name).unwrap_or(proc_name);
// a call by a known name
call_by_name(
@ -4336,8 +4347,30 @@ pub fn with_hole<'a>(
unreachable!("calling a non-closure layout")
}
},
UnspecializedExpr(symbol) => match full_layout {
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
UnspecializedExpr(symbol) => match procs.partial_exprs.get(symbol).unwrap()
{
&PolymorphicExpr::AbilityMember(member) => {
let proc_name = get_specialization(env, fn_var, member).expect("Recorded as an ability member, but it doesn't have a specialization");
// a call by a known name
return call_by_name(
env,
procs,
fn_var,
proc_name,
loc_args,
layout_cache,
assigned,
hole,
);
}
PolymorphicExpr::Expr(lambda_expr, lambda_expr_var) => {
match full_layout {
RawFunctionLayout::Function(
arg_layouts,
lambda_set,
ret_layout,
) => {
let closure_data_symbol = env.unique_symbol();
result = match_on_lambda_set(
@ -4351,15 +4384,12 @@ pub fn with_hole<'a>(
hole,
);
let (lambda_expr, lambda_expr_var) =
procs.partial_exprs.get(symbol).unwrap();
let snapshot = env.subs.snapshot();
let cache_snapshot = layout_cache.snapshot();
let _unified = roc_unify::unify::unify(
env.subs,
fn_var,
lambda_expr_var,
*lambda_expr_var,
roc_unify::unify::Mode::EQ,
);
@ -4378,6 +4408,8 @@ pub fn with_hole<'a>(
RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!("calling a non-closure layout")
}
}
}
},
NotASymbol => {
// the expression is not a symbol. That means it's an expression
@ -4712,18 +4744,18 @@ pub fn with_hole<'a>(
}
#[inline(always)]
fn repoint_to_specialization<'a>(
fn get_specialization<'a>(
env: &mut Env<'a, '_>,
symbol_var: Variable,
symbol: Symbol,
) -> Symbol {
) -> Option<Symbol> {
use roc_solve::ability::type_implementing_member;
use roc_unify::unify::unify;
match env.abilities_store.member_def(symbol) {
None => {
// This is not an ability member, it doesn't need specialization.
symbol
None
}
Some(member) => {
let snapshot = env.subs.snapshot();
@ -4743,7 +4775,7 @@ fn repoint_to_specialization<'a>(
.get_specialization(symbol, specializing_type)
.expect("No specialization is recorded - I thought there would only be a type error here.");
specialization.symbol
Some(specialization.symbol)
}
}
}
@ -5664,9 +5696,10 @@ pub fn from_can<'a>(
// At the definition site `n = 1` we only know `1` to have the type `[Int *]`,
// which won't be refined until the call `asU8 n`. Add it as a partial expression
// that will be specialized at each concrete usage site.
procs
.partial_exprs
.insert(*symbol, def.loc_expr.value, def.expr_var);
procs.partial_exprs.insert(
*symbol,
PolymorphicExpr::Expr(def.loc_expr.value, def.expr_var),
);
let result = from_can(env, variable, cont.value, procs, layout_cache);
@ -6372,7 +6405,7 @@ fn store_pattern_help<'a>(
match can_pat {
Identifier(symbol) => {
if let Some((_, var)) = procs.partial_exprs.get(outer_symbol) {
if let Some(&PolymorphicExpr::Expr(_, var)) = procs.partial_exprs.get(outer_symbol) {
// It might be the case that symbol we're storing hasn't been reified to a value
// yet, if it's polymorphic. Do that now.
stmt = specialize_symbol(
@ -6770,6 +6803,13 @@ fn handle_variable_aliasing<'a, BuildRest>(
where
BuildRest: FnOnce(&mut Env<'a, '_>, &mut Procs<'a>, &mut LayoutCache<'a>) -> Stmt<'a>,
{
if env.abilities_store.is_ability_member_name(right) {
procs
.partial_exprs
.insert(left, PolymorphicExpr::AbilityMember(right));
return build_rest(env, procs, layout_cache);
}
if procs.partial_exprs.contains(right) {
// If `right` links to a partial expression, make sure we link `left` to it as well, so
// that usages of it will be specialized when building the rest of the program.
@ -6847,7 +6887,7 @@ fn specialize_symbol<'a>(
result: Stmt<'a>,
original: Symbol,
) -> Stmt<'a> {
if let Some((expr, expr_var)) = procs.partial_exprs.get(original) {
if let Some(PolymorphicExpr::Expr(expr, expr_var)) = procs.partial_exprs.get(original) {
// Specialize the expression type now, based off the `arg_var` we've been given.
// TODO: cache the specialized result
let snapshot = env.subs.snapshot();
@ -6855,14 +6895,14 @@ fn specialize_symbol<'a>(
let _unified = roc_unify::unify::unify(
env.subs,
arg_var.unwrap(),
expr_var,
*expr_var,
roc_unify::unify::Mode::EQ,
);
let result = with_hole(
env,
expr.clone(),
expr_var,
*expr_var,
procs,
layout_cache,
symbol,

View file

@ -59,3 +59,28 @@ fn hash_specialization_multiple_add() {
u64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn alias_member_specialization() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [ main ] to "./platform"
Hash has
hash : a -> U64 | a has Hash
Id := U64
hash = \$Id n -> n
main =
aliasedHash = hash
aliasedHash ($Id 1234)
"#
),
1234,
u64
);
}