mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
Add basic inline function and a failing test
This commit is contained in:
parent
6466fff6b1
commit
a1870457bf
2 changed files with 255 additions and 0 deletions
|
@ -1007,3 +1007,193 @@ fn canonicalize_lookup(
|
||||||
|
|
||||||
(can_expr, output)
|
(can_expr, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Currently uses the heuristic of "only inline if it's a builtin"
|
||||||
|
pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> Expr {
|
||||||
|
use Expr::*;
|
||||||
|
|
||||||
|
match expr {
|
||||||
|
// Num stores the `a` variable in `Num a`. Not the same as the variable
|
||||||
|
// stored in Int and Float below, which is strictly for better error messages
|
||||||
|
other @ Num(_, _)
|
||||||
|
| other @ Int(_, _)
|
||||||
|
| other @ Float(_, _)
|
||||||
|
| other @ Str(_)
|
||||||
|
| other @ BlockStr(_)
|
||||||
|
| other @ RuntimeError(_)
|
||||||
|
| other @ EmptyRecord
|
||||||
|
| other @ Accessor { .. }
|
||||||
|
| other @ Update { .. }
|
||||||
|
| other @ Var(_) => other,
|
||||||
|
|
||||||
|
List {
|
||||||
|
elem_var,
|
||||||
|
loc_elems,
|
||||||
|
} => {
|
||||||
|
let mut new_elems = Vec::with_capacity(loc_elems.len());
|
||||||
|
|
||||||
|
for loc_elem in loc_elems {
|
||||||
|
let value = inline_calls(var_store, scope, loc_elem.value);
|
||||||
|
|
||||||
|
new_elems.push(Located {
|
||||||
|
value,
|
||||||
|
region: loc_elem.region,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List {
|
||||||
|
elem_var,
|
||||||
|
loc_elems: new_elems,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Branching
|
||||||
|
When {
|
||||||
|
cond_var,
|
||||||
|
expr_var,
|
||||||
|
region,
|
||||||
|
loc_cond,
|
||||||
|
branches,
|
||||||
|
} => {
|
||||||
|
let loc_cond = Box::new(Located {
|
||||||
|
region: loc_cond.region,
|
||||||
|
value: inline_calls(var_store, scope, loc_cond.value),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut new_branches = Vec::with_capacity(branches.len());
|
||||||
|
|
||||||
|
for branch in branches {
|
||||||
|
let value = Located {
|
||||||
|
value: inline_calls(var_store, scope, branch.value.value),
|
||||||
|
region: branch.value.region,
|
||||||
|
};
|
||||||
|
let guard = match branch.guard {
|
||||||
|
Some(loc_expr) => Some(Located {
|
||||||
|
region: loc_expr.region,
|
||||||
|
value: inline_calls(var_store, scope, loc_expr.value),
|
||||||
|
}),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let new_branch = WhenBranch {
|
||||||
|
patterns: branch.patterns,
|
||||||
|
value,
|
||||||
|
guard,
|
||||||
|
};
|
||||||
|
|
||||||
|
new_branches.push(new_branch);
|
||||||
|
}
|
||||||
|
|
||||||
|
When {
|
||||||
|
cond_var,
|
||||||
|
expr_var,
|
||||||
|
region,
|
||||||
|
loc_cond,
|
||||||
|
branches: new_branches,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
If {
|
||||||
|
cond_var,
|
||||||
|
branch_var,
|
||||||
|
branches,
|
||||||
|
final_else,
|
||||||
|
} => {
|
||||||
|
let mut new_branches = Vec::with_capacity(branches.len());
|
||||||
|
|
||||||
|
for (loc_cond, loc_expr) in branches {
|
||||||
|
let loc_cond = Located {
|
||||||
|
value: inline_calls(var_store, scope, loc_cond.value),
|
||||||
|
region: loc_cond.region,
|
||||||
|
};
|
||||||
|
|
||||||
|
let loc_expr = Located {
|
||||||
|
value: inline_calls(var_store, scope, loc_expr.value),
|
||||||
|
region: loc_expr.region,
|
||||||
|
};
|
||||||
|
|
||||||
|
new_branches.push((loc_cond, loc_expr));
|
||||||
|
}
|
||||||
|
|
||||||
|
let final_else = Box::new(Located {
|
||||||
|
region: final_else.region,
|
||||||
|
value: inline_calls(var_store, scope, final_else.value),
|
||||||
|
});
|
||||||
|
|
||||||
|
If {
|
||||||
|
cond_var,
|
||||||
|
branch_var,
|
||||||
|
branches: new_branches,
|
||||||
|
final_else,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LetRec(_, _, _, _ /*Vec<Def>, Box<Located<Expr>>, Variable, Aliases */) => {
|
||||||
|
todo!("inline for LetRec");
|
||||||
|
}
|
||||||
|
|
||||||
|
LetNonRec(_, _, _, _ /*Box<Def>, Box<Located<Expr>>, Variable, Aliases*/) => {
|
||||||
|
todo!("inline for LetNonRec");
|
||||||
|
}
|
||||||
|
|
||||||
|
Closure(var, symbol, recursive, patterns, boxed_expr) => {
|
||||||
|
let (loc_expr, expr_var) = *boxed_expr;
|
||||||
|
let loc_expr = Located {
|
||||||
|
value: inline_calls(var_store, scope, loc_expr.value),
|
||||||
|
region: loc_expr.region,
|
||||||
|
};
|
||||||
|
|
||||||
|
Closure(
|
||||||
|
var,
|
||||||
|
symbol,
|
||||||
|
recursive,
|
||||||
|
patterns,
|
||||||
|
Box::new((loc_expr, expr_var)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Record { record_var, fields } => {
|
||||||
|
todo!(
|
||||||
|
"Inlining for Record with record_var {:?} and fields {:?}",
|
||||||
|
record_var,
|
||||||
|
fields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Access {
|
||||||
|
record_var,
|
||||||
|
ext_var,
|
||||||
|
field_var,
|
||||||
|
loc_expr,
|
||||||
|
field,
|
||||||
|
} => {
|
||||||
|
todo!("Inlining for Access with record_var {:?}, ext_var {:?}, field_var {:?}, loc_expr {:?}, field {:?}", record_var, ext_var, field_var, loc_expr, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag {
|
||||||
|
variant_var,
|
||||||
|
ext_var,
|
||||||
|
name,
|
||||||
|
arguments,
|
||||||
|
} => {
|
||||||
|
todo!(
|
||||||
|
"Inlining for Tag with variant_var {:?}, ext_var {:?}, name {:?}, arguments {:?}",
|
||||||
|
variant_var,
|
||||||
|
ext_var,
|
||||||
|
name,
|
||||||
|
arguments
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Call(boxed_tuple, args, called_via) => {
|
||||||
|
let (fn_var, loc_expr, expr_var) = *boxed_tuple;
|
||||||
|
|
||||||
|
match loc_expr.value {
|
||||||
|
Var(symbol) if symbol.is_builtin() => {
|
||||||
|
todo!("Inline this builtin: {:?}", symbol);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// For now, we only inline calls to builtins. Leave this alone!
|
||||||
|
Call(Box::new((fn_var, loc_expr, expr_var)), args, called_via)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
65
compiler/can/tests/can_inline.rs
Normal file
65
compiler/can/tests/can_inline.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate pretty_assertions;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate indoc;
|
||||||
|
|
||||||
|
extern crate bumpalo;
|
||||||
|
extern crate roc_can;
|
||||||
|
extern crate roc_parse;
|
||||||
|
extern crate roc_region;
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod can_inline {
|
||||||
|
use crate::helpers::{can_expr_with, test_home};
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use roc_can::expr::inline_calls;
|
||||||
|
use roc_can::expr::Expr::{self, *};
|
||||||
|
use roc_can::scope::Scope;
|
||||||
|
use roc_module::operator::CalledVia;
|
||||||
|
use roc_module::symbol::Symbol;
|
||||||
|
use roc_region::all::{Located, Region};
|
||||||
|
use roc_types::subs::VarStore;
|
||||||
|
|
||||||
|
fn assert_inlines_to(input: &str, expected: Expr, var_store: &mut VarStore) {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let scope = &mut Scope::new(test_home());
|
||||||
|
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||||
|
let actual = inline_calls(var_store, scope, actual_out.loc_expr.value);
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_list_len() {
|
||||||
|
let var_store = &mut VarStore::default();
|
||||||
|
|
||||||
|
assert_inlines_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Int.isZero 5
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
Expr::Call(
|
||||||
|
Box::new((
|
||||||
|
var_store.fresh(),
|
||||||
|
Located {
|
||||||
|
region: Region::zero(),
|
||||||
|
value: Expr::Var(Symbol::FLOAT_EQ),
|
||||||
|
},
|
||||||
|
var_store.fresh(),
|
||||||
|
)),
|
||||||
|
vec![(
|
||||||
|
var_store.fresh(),
|
||||||
|
Located {
|
||||||
|
region: Region::zero(),
|
||||||
|
value: Int(var_store.fresh(), 5),
|
||||||
|
},
|
||||||
|
)],
|
||||||
|
CalledVia::Space,
|
||||||
|
),
|
||||||
|
var_store,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue