mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 08:17:40 +03:00
Merge pull request #2375 from rtfeldman/expression-monomorphization-inlined
Monomorphize polymorphic non-function values
This commit is contained in:
commit
c0ea664141
@ -207,6 +207,72 @@ impl<'a> PartialProc<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum PartialExprLink {
|
||||
Aliases(Symbol),
|
||||
Expr(roc_can::expr::Expr, Variable),
|
||||
}
|
||||
|
||||
/// A table of symbols to polymorphic expressions. For example, in the program
|
||||
///
|
||||
/// n = 1
|
||||
///
|
||||
/// asU8 : U8 -> U8
|
||||
/// asU8 = \_ -> 1
|
||||
///
|
||||
/// asU32 : U32 -> U8
|
||||
/// asU32 = \_ -> 1
|
||||
///
|
||||
/// asU8 n + asU32 n
|
||||
///
|
||||
/// The expression bound by `n` doesn't have a definite layout until it is used
|
||||
/// at the call sites `asU8 n`, `asU32 n`.
|
||||
///
|
||||
/// Polymorphic *functions* are stored in `PartialProc`s, since functions are
|
||||
/// non longer first-class once we finish lowering to the IR.
|
||||
#[derive(Clone, Debug)]
|
||||
struct PartialExprs(BumpMap<Symbol, PartialExprLink>);
|
||||
|
||||
impl PartialExprs {
|
||||
fn new_in(arena: &Bump) -> Self {
|
||||
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_alias(&mut self, symbol: Symbol, aliases: Symbol) {
|
||||
self.0.insert(symbol, PartialExprLink::Aliases(aliases));
|
||||
}
|
||||
|
||||
fn contains(&self, symbol: Symbol) -> bool {
|
||||
self.0.contains_key(&symbol)
|
||||
}
|
||||
|
||||
fn get(&mut self, mut symbol: Symbol) -> Option<(&roc_can::expr::Expr, Variable)> {
|
||||
// In practice the alias chain is very short
|
||||
loop {
|
||||
match self.0.get(&symbol) {
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
Some(&PartialExprLink::Aliases(real_symbol)) => {
|
||||
symbol = real_symbol;
|
||||
}
|
||||
Some(PartialExprLink::Expr(expr, var)) => {
|
||||
return Some((expr, *var));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove(&mut self, symbol: Symbol) {
|
||||
debug_assert!(self.contains(symbol));
|
||||
self.0.remove(&symbol);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum CapturedSymbols<'a> {
|
||||
None,
|
||||
@ -668,6 +734,7 @@ impl<'a> Specialized<'a> {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Procs<'a> {
|
||||
pub partial_procs: PartialProcs<'a>,
|
||||
partial_exprs: PartialExprs,
|
||||
pub imported_module_thunks: &'a [Symbol],
|
||||
pub module_thunks: &'a [Symbol],
|
||||
pending_specializations: PendingSpecializations<'a>,
|
||||
@ -680,6 +747,7 @@ impl<'a> Procs<'a> {
|
||||
pub fn new_in(arena: &'a Bump) -> Self {
|
||||
Self {
|
||||
partial_procs: PartialProcs::new_in(arena),
|
||||
partial_exprs: PartialExprs::new_in(arena),
|
||||
imported_module_thunks: &[],
|
||||
module_thunks: &[],
|
||||
pending_specializations: PendingSpecializations::Finding(Suspended::new_in(arena)),
|
||||
@ -3107,16 +3175,20 @@ pub fn with_hole<'a>(
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// continue with the default path
|
||||
let mut stmt = with_hole(
|
||||
env,
|
||||
cont.value,
|
||||
variable,
|
||||
procs,
|
||||
layout_cache,
|
||||
assigned,
|
||||
hole,
|
||||
);
|
||||
let build_rest =
|
||||
|env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>| {
|
||||
with_hole(
|
||||
env,
|
||||
cont.value,
|
||||
variable,
|
||||
procs,
|
||||
layout_cache,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
};
|
||||
|
||||
// a variable is aliased
|
||||
if let roc_can::expr::Expr::Var(original) = def.loc_expr.value {
|
||||
@ -3128,18 +3200,17 @@ pub fn with_hole<'a>(
|
||||
//
|
||||
// foo = RBTRee.empty
|
||||
|
||||
stmt = handle_variable_aliasing(
|
||||
handle_variable_aliasing(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
def.expr_var,
|
||||
symbol,
|
||||
original,
|
||||
stmt,
|
||||
);
|
||||
|
||||
stmt
|
||||
build_rest,
|
||||
)
|
||||
} else {
|
||||
let rest = build_rest(env, procs, layout_cache);
|
||||
with_hole(
|
||||
env,
|
||||
def.loc_expr.value,
|
||||
@ -3147,7 +3218,7 @@ pub fn with_hole<'a>(
|
||||
procs,
|
||||
layout_cache,
|
||||
symbol,
|
||||
env.arena.alloc(stmt),
|
||||
env.arena.alloc(rest),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@ -3332,6 +3403,7 @@ pub fn with_hole<'a>(
|
||||
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
|
||||
|
||||
enum Field {
|
||||
// TODO: rename this since it can handle unspecialized expressions now too
|
||||
Function(Symbol, Variable),
|
||||
ValueSymbol,
|
||||
Field(roc_can::expr::Field),
|
||||
@ -3342,7 +3414,7 @@ pub fn with_hole<'a>(
|
||||
use ReuseSymbol::*;
|
||||
match fields.remove(&label) {
|
||||
Some(field) => match can_reuse_symbol(env, procs, &field.loc_expr.value) {
|
||||
Imported(symbol) | LocalFunction(symbol) => {
|
||||
Imported(symbol) | LocalFunction(symbol) | UnspecializedExpr(symbol) => {
|
||||
field_symbols.push(symbol);
|
||||
can_fields.push(Field::Function(symbol, variable));
|
||||
}
|
||||
@ -3798,9 +3870,16 @@ pub fn with_hole<'a>(
|
||||
);
|
||||
|
||||
match raw_layout {
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||
construct_closure_data(env, lambda_set, name, &[], assigned, hole)
|
||||
}
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => construct_closure_data(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
lambda_set,
|
||||
name,
|
||||
&[],
|
||||
assigned,
|
||||
hole,
|
||||
),
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
@ -3933,13 +4012,36 @@ pub fn with_hole<'a>(
|
||||
);
|
||||
}
|
||||
CopyExisting(index) => {
|
||||
let record_needs_specialization =
|
||||
procs.partial_exprs.contains(structure);
|
||||
let specialized_structure_sym = if record_needs_specialization {
|
||||
// We need to specialize the record now; create a new one for it.
|
||||
// TODO: reuse this symbol for all updates
|
||||
env.unique_symbol()
|
||||
} else {
|
||||
// The record is already good.
|
||||
structure
|
||||
};
|
||||
|
||||
let access_expr = Expr::StructAtIndex {
|
||||
structure,
|
||||
structure: specialized_structure_sym,
|
||||
index,
|
||||
field_layouts,
|
||||
};
|
||||
stmt =
|
||||
Stmt::Let(*symbol, access_expr, *field_layout, arena.alloc(stmt));
|
||||
|
||||
if record_needs_specialization {
|
||||
stmt = reuse_function_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
Some(record_var),
|
||||
specialized_structure_sym,
|
||||
stmt,
|
||||
structure,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3994,10 +4096,18 @@ pub fn with_hole<'a>(
|
||||
// define the closure data
|
||||
|
||||
let symbols =
|
||||
Vec::from_iter_in(captured_symbols.iter().map(|x| x.0), env.arena)
|
||||
.into_bump_slice();
|
||||
Vec::from_iter_in(captured_symbols.iter(), env.arena).into_bump_slice();
|
||||
|
||||
construct_closure_data(env, lambda_set, name, symbols, assigned, hole)
|
||||
construct_closure_data(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
lambda_set,
|
||||
name,
|
||||
symbols,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4068,6 +4178,9 @@ pub fn with_hole<'a>(
|
||||
LocalFunction(_) => {
|
||||
unreachable!("if this was known to be a function, we would not be here")
|
||||
}
|
||||
UnspecializedExpr(_) => {
|
||||
unreachable!("if this was known to be an unspecialized expression, we would not be here")
|
||||
}
|
||||
Imported(thunk_name) => {
|
||||
debug_assert!(procs.is_imported_module_thunk(thunk_name));
|
||||
|
||||
@ -4446,17 +4559,20 @@ pub fn with_hole<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn construct_closure_data<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
lambda_set: LambdaSet<'a>,
|
||||
name: Symbol,
|
||||
symbols: &'a [Symbol],
|
||||
symbols: &'a [&(Symbol, Variable)],
|
||||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
let lambda_set_layout = Layout::LambdaSet(lambda_set);
|
||||
|
||||
match lambda_set.layout_for_member(name) {
|
||||
let mut result = match lambda_set.layout_for_member(name) {
|
||||
ClosureRepresentation::Union {
|
||||
tag_id,
|
||||
alphabetic_order_fields: field_layouts,
|
||||
@ -4465,8 +4581,10 @@ fn construct_closure_data<'a>(
|
||||
} => {
|
||||
// captured variables are in symbol-alphabetic order, but now we want
|
||||
// them ordered by their alignment requirements
|
||||
let mut combined =
|
||||
Vec::from_iter_in(symbols.iter().zip(field_layouts.iter()), env.arena);
|
||||
let mut combined = Vec::from_iter_in(
|
||||
symbols.iter().map(|&&(s, _)| s).zip(field_layouts.iter()),
|
||||
env.arena,
|
||||
);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
@ -4478,7 +4596,7 @@ fn construct_closure_data<'a>(
|
||||
});
|
||||
|
||||
let symbols =
|
||||
Vec::from_iter_in(combined.iter().map(|(a, _)| **a), env.arena).into_bump_slice();
|
||||
Vec::from_iter_in(combined.iter().map(|(a, _)| *a), env.arena).into_bump_slice();
|
||||
|
||||
let expr = Expr::Tag {
|
||||
tag_id,
|
||||
@ -4494,8 +4612,10 @@ fn construct_closure_data<'a>(
|
||||
|
||||
// captured variables are in symbol-alphabetic order, but now we want
|
||||
// them ordered by their alignment requirements
|
||||
let mut combined =
|
||||
Vec::from_iter_in(symbols.iter().zip(field_layouts.iter()), env.arena);
|
||||
let mut combined = Vec::from_iter_in(
|
||||
symbols.iter().map(|&(s, _)| s).zip(field_layouts.iter()),
|
||||
env.arena,
|
||||
);
|
||||
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
@ -4539,7 +4659,23 @@ fn construct_closure_data<'a>(
|
||||
Stmt::Let(assigned, expr, lambda_set_layout, hole)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Some of the captured symbols may be references to polymorphic expressions,
|
||||
// which have not been specialized yet. We need to perform those
|
||||
// specializations now so that there are real symbols to capture.
|
||||
//
|
||||
// TODO: this is not quite right. What we should actually be doing is removing references to
|
||||
// polymorphic expressions from the captured symbols, and allowing the specializations of those
|
||||
// symbols to be inlined when specializing the closure body elsewhere.
|
||||
for &&(symbol, var) in symbols {
|
||||
if procs.partial_exprs.contains(symbol) {
|
||||
result =
|
||||
reuse_function_symbol(env, procs, layout_cache, Some(var), symbol, result, symbol);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -4842,9 +4978,16 @@ fn tag_union_to_function<'a>(
|
||||
);
|
||||
|
||||
match raw_layout {
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||
construct_closure_data(env, lambda_set, proc_symbol, &[], assigned, hole)
|
||||
}
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => construct_closure_data(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
lambda_set,
|
||||
proc_symbol,
|
||||
&[],
|
||||
assigned,
|
||||
hole,
|
||||
),
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
@ -5027,6 +5170,31 @@ fn register_capturing_closure<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
fn is_literal_like(expr: &roc_can::expr::Expr) -> bool {
|
||||
use roc_can::expr::Expr::*;
|
||||
matches!(
|
||||
expr,
|
||||
Num(..)
|
||||
| Int(..)
|
||||
| Float(..)
|
||||
| List { .. }
|
||||
| Str(_)
|
||||
| ZeroArgumentTag { .. }
|
||||
| Tag { .. }
|
||||
| Record { .. }
|
||||
)
|
||||
}
|
||||
|
||||
fn expr_is_polymorphic<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
expr: &roc_can::expr::Expr,
|
||||
expr_var: Variable,
|
||||
) -> bool {
|
||||
// TODO: I don't think we need the `is_literal_like` check, but taking it slow for now...
|
||||
let is_flex_or_rigid = |c: &Content| matches!(c, Content::FlexVar(_) | Content::RigidVar(_));
|
||||
is_literal_like(expr) && env.subs.var_contains_content(expr_var, is_flex_or_rigid)
|
||||
}
|
||||
|
||||
pub fn from_can<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
variable: Variable,
|
||||
@ -5181,19 +5349,26 @@ pub fn from_can<'a>(
|
||||
// or
|
||||
//
|
||||
// foo = RBTRee.empty
|
||||
let mut rest = from_can(env, def.expr_var, cont.value, procs, layout_cache);
|
||||
|
||||
rest = handle_variable_aliasing(
|
||||
// TODO: right now we need help out rustc with the closure types;
|
||||
// it isn't able to infer the right lifetime bounds. See if we
|
||||
// can remove the annotations in the future.
|
||||
let build_rest =
|
||||
|env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>| {
|
||||
from_can(env, def.expr_var, cont.value, procs, layout_cache)
|
||||
};
|
||||
|
||||
return handle_variable_aliasing(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
def.expr_var,
|
||||
*symbol,
|
||||
original,
|
||||
rest,
|
||||
build_rest,
|
||||
);
|
||||
|
||||
return rest;
|
||||
}
|
||||
roc_can::expr::Expr::LetNonRec(nested_def, nested_cont, nested_annotation) => {
|
||||
use roc_can::expr::Expr::*;
|
||||
@ -5277,6 +5452,26 @@ pub fn from_can<'a>(
|
||||
|
||||
return from_can(env, variable, new_outer, procs, layout_cache);
|
||||
}
|
||||
ref body if expr_is_polymorphic(env, body, def.expr_var) => {
|
||||
// This is a pattern like
|
||||
//
|
||||
// n = 1
|
||||
// asU8 n
|
||||
//
|
||||
// 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);
|
||||
|
||||
let result = from_can(env, variable, cont.value, procs, layout_cache);
|
||||
|
||||
// We won't see this symbol again.
|
||||
procs.partial_exprs.remove(*symbol);
|
||||
|
||||
return result;
|
||||
}
|
||||
_ => {
|
||||
let rest = from_can(env, variable, cont.value, procs, layout_cache);
|
||||
return with_hole(
|
||||
@ -6294,6 +6489,7 @@ enum ReuseSymbol {
|
||||
Imported(Symbol),
|
||||
LocalFunction(Symbol),
|
||||
Value(Symbol),
|
||||
UnspecializedExpr(Symbol),
|
||||
NotASymbol,
|
||||
}
|
||||
|
||||
@ -6311,6 +6507,8 @@ fn can_reuse_symbol<'a>(
|
||||
Imported(symbol)
|
||||
} else if procs.partial_procs.contains_key(symbol) {
|
||||
LocalFunction(symbol)
|
||||
} else if procs.partial_exprs.contains(symbol) {
|
||||
UnspecializedExpr(symbol)
|
||||
} else {
|
||||
Value(symbol)
|
||||
}
|
||||
@ -6330,15 +6528,29 @@ fn possible_reuse_symbol<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_variable_aliasing<'a>(
|
||||
fn handle_variable_aliasing<'a, BuildRest>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
variable: Variable,
|
||||
left: Symbol,
|
||||
right: Symbol,
|
||||
mut result: Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
build_rest: BuildRest,
|
||||
) -> Stmt<'a>
|
||||
where
|
||||
BuildRest: FnOnce(&mut Env<'a, '_>, &mut Procs<'a>, &mut LayoutCache<'a>) -> Stmt<'a>,
|
||||
{
|
||||
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.
|
||||
procs.partial_exprs.insert_alias(left, right);
|
||||
return build_rest(env, procs, layout_cache);
|
||||
}
|
||||
|
||||
// Otherwise we're dealing with an alias to something that doesn't need to be specialized, or
|
||||
// whose usages will already be specialized in the rest of the program. Let's just build the
|
||||
// rest of the program now to get our hole.
|
||||
let mut result = build_rest(env, procs, layout_cache);
|
||||
if procs.is_imported_module_thunk(right) {
|
||||
// if this is an imported symbol, then we must make sure it is
|
||||
// specialized, and wrap the original in a function pointer.
|
||||
@ -6396,6 +6608,7 @@ fn let_empty_struct<'a>(assigned: Symbol, hole: &'a Stmt<'a>) -> Stmt<'a> {
|
||||
}
|
||||
|
||||
/// If the symbol is a function, make sure it is properly specialized
|
||||
// TODO: rename this now that we handle polymorphic non-function expressions too
|
||||
fn reuse_function_symbol<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
@ -6405,6 +6618,35 @@ fn reuse_function_symbol<'a>(
|
||||
result: Stmt<'a>,
|
||||
original: Symbol,
|
||||
) -> Stmt<'a> {
|
||||
if let Some((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();
|
||||
let cache_snapshot = layout_cache.snapshot();
|
||||
let _unified = roc_unify::unify::unify(
|
||||
env.subs,
|
||||
arg_var.unwrap(),
|
||||
expr_var,
|
||||
roc_unify::unify::Mode::Eq,
|
||||
);
|
||||
|
||||
let result = with_hole(
|
||||
env,
|
||||
expr.clone(),
|
||||
expr_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
symbol,
|
||||
env.arena.alloc(result),
|
||||
);
|
||||
|
||||
// Restore the prior state so as not to interfere with future specializations.
|
||||
env.subs.rollback_to(snapshot);
|
||||
layout_cache.rollback_to(cache_snapshot);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
match procs.get_partial_proc(original) {
|
||||
None => {
|
||||
match arg_var {
|
||||
@ -6495,7 +6737,7 @@ fn reuse_function_symbol<'a>(
|
||||
|
||||
let symbols = match captured {
|
||||
CapturedSymbols::Captured(captured_symbols) => {
|
||||
Vec::from_iter_in(captured_symbols.iter().map(|x| x.0), env.arena)
|
||||
Vec::from_iter_in(captured_symbols.iter(), env.arena)
|
||||
.into_bump_slice()
|
||||
}
|
||||
CapturedSymbols::None => unreachable!(),
|
||||
@ -6503,6 +6745,8 @@ fn reuse_function_symbol<'a>(
|
||||
|
||||
construct_closure_data(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
lambda_set,
|
||||
original,
|
||||
symbols,
|
||||
@ -6539,6 +6783,8 @@ fn reuse_function_symbol<'a>(
|
||||
// unification may still cause it to have an extra argument
|
||||
construct_closure_data(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
lambda_set,
|
||||
original,
|
||||
&[],
|
||||
@ -6570,7 +6816,7 @@ fn assign_to_symbol<'a>(
|
||||
) -> Stmt<'a> {
|
||||
use ReuseSymbol::*;
|
||||
match can_reuse_symbol(env, procs, &loc_arg.value) {
|
||||
Imported(original) | LocalFunction(original) => {
|
||||
Imported(original) | LocalFunction(original) | UnspecializedExpr(original) => {
|
||||
// for functions we must make sure they are specialized correctly
|
||||
reuse_function_symbol(
|
||||
env,
|
||||
@ -7222,8 +7468,8 @@ fn call_specialized_proc<'a>(
|
||||
.map(|pp| &pp.captured_symbols)
|
||||
{
|
||||
Some(&CapturedSymbols::Captured(captured_symbols)) => {
|
||||
let symbols = Vec::from_iter_in(captured_symbols.iter().map(|x| x.0), env.arena)
|
||||
.into_bump_slice();
|
||||
let symbols =
|
||||
Vec::from_iter_in(captured_symbols.iter(), env.arena).into_bump_slice();
|
||||
|
||||
let closure_data_symbol = env.unique_symbol();
|
||||
|
||||
@ -7248,6 +7494,8 @@ fn call_specialized_proc<'a>(
|
||||
|
||||
let result = construct_closure_data(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
lambda_set,
|
||||
proc_name,
|
||||
symbols,
|
||||
|
@ -2631,3 +2631,22 @@ fn list_find_empty_layout() {
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn monomorphized_lists() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
l = [1, 2, 3]
|
||||
|
||||
f : List U8, List U16 -> Nat
|
||||
f = \_, _ -> 18
|
||||
|
||||
f l l
|
||||
"#
|
||||
),
|
||||
18,
|
||||
u64
|
||||
)
|
||||
}
|
||||
|
@ -2289,3 +2289,92 @@ fn sub_saturated() {
|
||||
u8
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn monomorphized_ints() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = 100
|
||||
|
||||
f : U8, U32 -> Nat
|
||||
f = \_, _ -> 18
|
||||
|
||||
f x x
|
||||
"#
|
||||
),
|
||||
18,
|
||||
u64
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn monomorphized_floats() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = 100.0
|
||||
|
||||
f : F32, F64 -> Nat
|
||||
f = \_, _ -> 18
|
||||
|
||||
f x x
|
||||
"#
|
||||
),
|
||||
18,
|
||||
u64
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn monomorphized_ints_names_dont_conflict() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : U8 -> Nat
|
||||
f = \_ -> 9
|
||||
x =
|
||||
n = 100
|
||||
f n
|
||||
|
||||
y =
|
||||
n = 100
|
||||
f n
|
||||
|
||||
x + y
|
||||
"#
|
||||
),
|
||||
18,
|
||||
u64
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn monomorphized_ints_aliased() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
y = 100
|
||||
w1 = y
|
||||
w2 = y
|
||||
|
||||
f1 : U8, U32 -> U8
|
||||
f1 = \_, _ -> 1
|
||||
|
||||
f2 : U32, U8 -> U8
|
||||
f2 = \_, _ -> 2
|
||||
|
||||
f1 w1 w2 + f2 w1 w2
|
||||
"#
|
||||
),
|
||||
3,
|
||||
u8
|
||||
)
|
||||
}
|
||||
|
@ -3182,3 +3182,28 @@ fn recursively_build_effect() {
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "TODO; currently generates bad code because `a` isn't specialized inside the closure."]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn polymophic_expression_captured_inside_closure() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
asU8 : U8 -> U8
|
||||
asU8 = \_ -> 30
|
||||
|
||||
main =
|
||||
a = 15
|
||||
f = \{} ->
|
||||
asU8 a
|
||||
|
||||
f {}
|
||||
"#
|
||||
),
|
||||
30,
|
||||
u8
|
||||
);
|
||||
}
|
||||
|
@ -59,7 +59,10 @@ fn list_int_inc() {
|
||||
),
|
||||
RocList<RocList<i64>>,
|
||||
&[
|
||||
3, // list
|
||||
// TODO be smarter about coalescing polymorphic list values
|
||||
1, // list0
|
||||
1, // list1
|
||||
1, // list2
|
||||
1 // result
|
||||
]
|
||||
);
|
||||
@ -77,7 +80,10 @@ fn list_int_dealloc() {
|
||||
),
|
||||
usize,
|
||||
&[
|
||||
0, // list
|
||||
// TODO be smarter about coalescing polymorphic list values
|
||||
0, // list0
|
||||
0, // list1
|
||||
0, // list2
|
||||
0 // result
|
||||
]
|
||||
);
|
||||
@ -130,6 +136,7 @@ fn struct_inc() {
|
||||
indoc!(
|
||||
r#"
|
||||
s = Str.concat "A long enough string " "to be heap-allocated"
|
||||
r1 : { a: I64, b: Str, c: Str }
|
||||
r1 = { a: 123, b: s, c: s }
|
||||
{ y: r1, z: r1 }
|
||||
"#
|
||||
@ -146,6 +153,7 @@ fn struct_dealloc() {
|
||||
indoc!(
|
||||
r#"
|
||||
s = Str.concat "A long enough string " "to be heap-allocated"
|
||||
r1 : { a: I64, b: Str, c: Str }
|
||||
r1 = { a: 123, b: s, c: s }
|
||||
r2 = { x: 456, y: r1, z: r1 }
|
||||
r2.x
|
||||
|
@ -1262,3 +1262,108 @@ fn recursive_tag_union_into_flat_tag_union() {
|
||||
|_| 0
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn monomorphized_tag() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
b = False
|
||||
f : Bool, [True, False, Idk] -> U8
|
||||
f = \_, _ -> 18
|
||||
f b b
|
||||
"#
|
||||
),
|
||||
18,
|
||||
u8
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn monomorphized_applied_tag() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
main =
|
||||
a = A "abc"
|
||||
f = \x ->
|
||||
when x is
|
||||
A y -> y
|
||||
B y -> y
|
||||
f a
|
||||
"#
|
||||
),
|
||||
RocStr::from_slice(b"abc"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn monomorphized_tag_with_polymorphic_arg() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
a = A
|
||||
wrap = Wrapped a
|
||||
|
||||
useWrap1 : [Wrapped [A], Other] -> U8
|
||||
useWrap1 =
|
||||
\w -> when w is
|
||||
Wrapped A -> 2
|
||||
Other -> 3
|
||||
|
||||
useWrap2 : [Wrapped [A, B]] -> U8
|
||||
useWrap2 =
|
||||
\w -> when w is
|
||||
Wrapped A -> 5
|
||||
Wrapped B -> 7
|
||||
|
||||
useWrap1 wrap * useWrap2 wrap
|
||||
"#
|
||||
),
|
||||
10,
|
||||
u8
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn monomorphized_tag_with_polymorphic_arg_and_monomorphic_arg() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
mono : U8
|
||||
mono = 15
|
||||
poly = A
|
||||
wrap = Wrapped poly mono
|
||||
|
||||
useWrap1 : [Wrapped [A] U8, Other] -> U8
|
||||
useWrap1 =
|
||||
\w -> when w is
|
||||
Wrapped A n -> n
|
||||
Other -> 0
|
||||
|
||||
useWrap2 : [Wrapped [A, B] U8] -> U8
|
||||
useWrap2 =
|
||||
\w -> when w is
|
||||
Wrapped A n -> n
|
||||
Wrapped B _ -> 0
|
||||
|
||||
useWrap1 wrap * useWrap2 wrap
|
||||
"#
|
||||
),
|
||||
225,
|
||||
u8
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
procedure Test.0 ():
|
||||
let Test.1 : Builtin(Int(I64)) = 5i64;
|
||||
let Test.3 : Builtin(Int(I64)) = 3i64;
|
||||
ret Test.3;
|
||||
|
@ -1,3 +1,3 @@
|
||||
procedure Test.0 ():
|
||||
let Test.1 : Builtin(Int(I64)) = 5i64;
|
||||
ret Test.1;
|
||||
let Test.2 : Builtin(Int(I64)) = 5i64;
|
||||
ret Test.2;
|
||||
|
@ -3,13 +3,13 @@ procedure List.7 (#Attr.2):
|
||||
ret Test.7;
|
||||
|
||||
procedure Test.1 (Test.5):
|
||||
let Test.2 : Builtin(Int(I64)) = 41i64;
|
||||
let Test.11 : LambdaSet([( Test.3, [Builtin(Int(I64))])]LambdaSet { set: Ok(()), representation: Struct([Builtin(Int(I64))]) }) = Struct {Test.2};
|
||||
let Test.10 : Builtin(List(LambdaSet([( Test.3, [Builtin(Int(I64))])]LambdaSet { set: Ok(()), representation: Struct([Builtin(Int(I64))]) }))) = Array [Test.11];
|
||||
ret Test.10;
|
||||
|
||||
procedure Test.3 (Test.9, #Attr.12):
|
||||
let Test.2 : Builtin(Int(I64)) = StructAtIndex 0 #Attr.12;
|
||||
let Test.2 : Builtin(Int(I64)) = 41i64;
|
||||
ret Test.2;
|
||||
|
||||
procedure Test.0 ():
|
||||
|
@ -15,7 +15,6 @@ procedure Test.2 (Test.6):
|
||||
ret Test.24;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.1 : Builtin(List(LambdaSet([]LambdaSet { set: Ok(()), representation: Struct([]) }))) = Array [];
|
||||
joinpoint Test.22 Test.3:
|
||||
let Test.14 : Builtin(Int(U64)) = 0i64;
|
||||
let Test.7 : Union(NonRecursive([[Struct([])], [LambdaSet([( Test.2, [])]LambdaSet { set: Ok(()), representation: Struct([]) })]])) = CallByName List.3 Test.3 Test.14;
|
||||
@ -35,9 +34,9 @@ procedure Test.0 ():
|
||||
in
|
||||
let Test.25 : Builtin(Bool) = false;
|
||||
if Test.25 then
|
||||
let Test.1 : Builtin(List(LambdaSet([( Test.2, [])]LambdaSet { set: Ok(()), representation: Struct([]) }))) = Array [];
|
||||
jump Test.22 Test.1;
|
||||
else
|
||||
dec Test.1;
|
||||
let Test.23 : LambdaSet([( Test.2, [])]LambdaSet { set: Ok(()), representation: Struct([]) }) = Struct {};
|
||||
let Test.21 : Builtin(List(LambdaSet([( Test.2, [])]LambdaSet { set: Ok(()), representation: Struct([]) }))) = Array [Test.23];
|
||||
jump Test.22 Test.21;
|
||||
|
@ -1,19 +1,19 @@
|
||||
procedure List.7 (#Attr.2):
|
||||
let Test.6 : Builtin(Int(U64)) = lowlevel ListLen #Attr.2;
|
||||
ret Test.6;
|
||||
let Test.7 : Builtin(Int(U64)) = lowlevel ListLen #Attr.2;
|
||||
ret Test.7;
|
||||
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.5 : Builtin(Int(U64)) = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.5;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.1 : Builtin(List(Builtin(Int(I64)))) = Array [1i64, 2i64];
|
||||
let Test.9 : Builtin(Int(U64)) = 5i64;
|
||||
let Test.10 : Builtin(Int(U64)) = 4i64;
|
||||
let Test.7 : Builtin(Int(U64)) = CallByName Num.24 Test.9 Test.10;
|
||||
let Test.8 : Builtin(Int(U64)) = 3i64;
|
||||
let Test.3 : Builtin(Int(U64)) = CallByName Num.24 Test.7 Test.8;
|
||||
let Test.4 : Builtin(Int(U64)) = CallByName List.7 Test.1;
|
||||
dec Test.1;
|
||||
let Test.10 : Builtin(Int(U64)) = 5i64;
|
||||
let Test.11 : Builtin(Int(U64)) = 4i64;
|
||||
let Test.8 : Builtin(Int(U64)) = CallByName Num.24 Test.10 Test.11;
|
||||
let Test.9 : Builtin(Int(U64)) = 3i64;
|
||||
let Test.3 : Builtin(Int(U64)) = CallByName Num.24 Test.8 Test.9;
|
||||
let Test.6 : Builtin(List(Builtin(Int(I64)))) = Array [1i64, 2i64];
|
||||
let Test.4 : Builtin(Int(U64)) = CallByName List.7 Test.6;
|
||||
dec Test.6;
|
||||
let Test.2 : Builtin(Int(U64)) = CallByName Num.24 Test.3 Test.4;
|
||||
ret Test.2;
|
||||
|
@ -1,9 +1,9 @@
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.4 : Builtin(Int(I64)) = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.4;
|
||||
let Test.6 : Builtin(Int(I64)) = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.6;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.1 : Builtin(Int(I64)) = 3i64;
|
||||
let Test.2 : Builtin(Int(I64)) = 4i64;
|
||||
let Test.3 : Builtin(Int(I64)) = CallByName Num.24 Test.1 Test.2;
|
||||
let Test.4 : Builtin(Int(I64)) = 3i64;
|
||||
let Test.5 : Builtin(Int(I64)) = 4i64;
|
||||
let Test.3 : Builtin(Int(I64)) = CallByName Num.24 Test.4 Test.5;
|
||||
ret Test.3;
|
||||
|
@ -1,5 +1,3 @@
|
||||
procedure Test.0 ():
|
||||
let Test.1 : Builtin(Int(I64)) = 5i64;
|
||||
let Test.4 : Builtin(Int(I64)) = 17i64;
|
||||
let Test.2 : Builtin(Int(I64)) = 1337i64;
|
||||
ret Test.2;
|
||||
|
@ -1,8 +1,6 @@
|
||||
procedure Test.0 ():
|
||||
let Test.1 : Builtin(Int(I64)) = 5i64;
|
||||
let Test.4 : Builtin(Int(I64)) = 17i64;
|
||||
let Test.5 : Builtin(Int(I64)) = 1i64;
|
||||
let Test.2 : Builtin(Int(I64)) = 1337i64;
|
||||
let Test.7 : Struct([Builtin(Int(I64)), Builtin(Int(I64))]) = Struct {Test.2, Test.4};
|
||||
let Test.3 : Builtin(Int(I64)) = 17i64;
|
||||
let Test.7 : Struct([Builtin(Int(I64)), Builtin(Int(I64))]) = Struct {Test.2, Test.3};
|
||||
let Test.6 : Builtin(Int(I64)) = StructAtIndex 0 Test.7;
|
||||
ret Test.6;
|
||||
|
@ -1,6 +1,6 @@
|
||||
procedure List.7 (#Attr.2):
|
||||
let Test.7 : Builtin(Int(U64)) = lowlevel ListLen #Attr.2;
|
||||
ret Test.7;
|
||||
let Test.10 : Builtin(Int(U64)) = lowlevel ListLen #Attr.2;
|
||||
ret Test.10;
|
||||
|
||||
procedure List.7 (#Attr.2):
|
||||
let Test.8 : Builtin(Int(U64)) = lowlevel ListLen #Attr.2;
|
||||
@ -11,11 +11,11 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||
ret Test.6;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.1 : Builtin(List(Builtin(Int(I64)))) = Array [1i64, 2i64, 3i64];
|
||||
let Test.2 : Builtin(List(Builtin(Float(F64)))) = Array [1f64];
|
||||
let Test.4 : Builtin(Int(U64)) = CallByName List.7 Test.1;
|
||||
dec Test.1;
|
||||
let Test.5 : Builtin(Int(U64)) = CallByName List.7 Test.2;
|
||||
dec Test.2;
|
||||
let Test.9 : Builtin(List(Builtin(Int(I64)))) = Array [1i64, 2i64, 3i64];
|
||||
let Test.4 : Builtin(Int(U64)) = CallByName List.7 Test.9;
|
||||
dec Test.9;
|
||||
let Test.7 : Builtin(List(Builtin(Float(F64)))) = Array [1f64];
|
||||
let Test.5 : Builtin(Int(U64)) = CallByName List.7 Test.7;
|
||||
dec Test.7;
|
||||
let Test.3 : Builtin(Int(U64)) = CallByName Num.24 Test.4 Test.5;
|
||||
ret Test.3;
|
||||
|
20
compiler/test_mono/generated/monomorphized_applied_tag.txt
Normal file
20
compiler/test_mono/generated/monomorphized_applied_tag.txt
Normal file
@ -0,0 +1,20 @@
|
||||
procedure Test.2 (Test.4):
|
||||
let Test.11 : Builtin(Int(U8)) = 0i64;
|
||||
let Test.12 : Builtin(Int(U8)) = GetTagId Test.4;
|
||||
let Test.13 : Builtin(Bool) = lowlevel Eq Test.11 Test.12;
|
||||
if Test.13 then
|
||||
let Test.5 : Builtin(Str) = UnionAtIndex (Id 0) (Index 0) Test.4;
|
||||
inc Test.5;
|
||||
dec Test.4;
|
||||
ret Test.5;
|
||||
else
|
||||
let Test.6 : Builtin(Str) = UnionAtIndex (Id 1) (Index 0) Test.4;
|
||||
inc Test.6;
|
||||
dec Test.4;
|
||||
ret Test.6;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.14 : Builtin(Str) = "A";
|
||||
let Test.8 : Union(NonRecursive([[Builtin(Str)], [Builtin(Str)]])) = A Test.14;
|
||||
let Test.7 : Builtin(Str) = CallByName Test.2 Test.8;
|
||||
ret Test.7;
|
9
compiler/test_mono/generated/monomorphized_floats.txt
Normal file
9
compiler/test_mono/generated/monomorphized_floats.txt
Normal file
@ -0,0 +1,9 @@
|
||||
procedure Test.2 (Test.3, Test.4):
|
||||
let Test.8 : Builtin(Int(U64)) = 18i64;
|
||||
ret Test.8;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.6 : Builtin(Float(F32)) = 100f64;
|
||||
let Test.7 : Builtin(Float(F64)) = 100f64;
|
||||
let Test.5 : Builtin(Int(U64)) = CallByName Test.2 Test.6 Test.7;
|
||||
ret Test.5;
|
9
compiler/test_mono/generated/monomorphized_ints.txt
Normal file
9
compiler/test_mono/generated/monomorphized_ints.txt
Normal file
@ -0,0 +1,9 @@
|
||||
procedure Test.2 (Test.3, Test.4):
|
||||
let Test.8 : Builtin(Int(U64)) = 18i64;
|
||||
ret Test.8;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.6 : Builtin(Int(U8)) = 100i64;
|
||||
let Test.7 : Builtin(Int(U32)) = 100i64;
|
||||
let Test.5 : Builtin(Int(U64)) = CallByName Test.2 Test.6 Test.7;
|
||||
ret Test.5;
|
23
compiler/test_mono/generated/monomorphized_ints_aliased.txt
Normal file
23
compiler/test_mono/generated/monomorphized_ints_aliased.txt
Normal file
@ -0,0 +1,23 @@
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.12 : Builtin(Int(U64)) = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.12;
|
||||
|
||||
procedure Test.4 (Test.7, Test.8):
|
||||
let Test.17 : Builtin(Int(U64)) = 1i64;
|
||||
ret Test.17;
|
||||
|
||||
procedure Test.4 (Test.7, Test.8):
|
||||
let Test.18 : Builtin(Int(U64)) = 1i64;
|
||||
ret Test.18;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.4 : LambdaSet([( Test.4, [])]LambdaSet { set: Ok(()), representation: Struct([]) }) = Struct {};
|
||||
let Test.4 : LambdaSet([( Test.4, [])]LambdaSet { set: Ok(()), representation: Struct([]) }) = Struct {};
|
||||
let Test.15 : Builtin(Int(U8)) = 100i64;
|
||||
let Test.16 : Builtin(Int(U32)) = 100i64;
|
||||
let Test.10 : Builtin(Int(U64)) = CallByName Test.4 Test.15 Test.16;
|
||||
let Test.13 : Builtin(Int(U32)) = 100i64;
|
||||
let Test.14 : Builtin(Int(U8)) = 100i64;
|
||||
let Test.11 : Builtin(Int(U64)) = CallByName Test.4 Test.13 Test.14;
|
||||
let Test.9 : Builtin(Int(U64)) = CallByName Num.24 Test.10 Test.11;
|
||||
ret Test.9;
|
11
compiler/test_mono/generated/monomorphized_list.txt
Normal file
11
compiler/test_mono/generated/monomorphized_list.txt
Normal file
@ -0,0 +1,11 @@
|
||||
procedure Test.2 (Test.3, Test.4):
|
||||
let Test.8 : Builtin(Int(U64)) = 18i64;
|
||||
ret Test.8;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.6 : Builtin(List(Builtin(Int(U8)))) = Array [1i64, 2i64, 3i64];
|
||||
let Test.7 : Builtin(List(Builtin(Int(U16)))) = Array [1i64, 2i64, 3i64];
|
||||
let Test.5 : Builtin(Int(U64)) = CallByName Test.2 Test.6 Test.7;
|
||||
dec Test.7;
|
||||
dec Test.6;
|
||||
ret Test.5;
|
9
compiler/test_mono/generated/monomorphized_tag.txt
Normal file
9
compiler/test_mono/generated/monomorphized_tag.txt
Normal file
@ -0,0 +1,9 @@
|
||||
procedure Test.2 (Test.4, Test.5):
|
||||
let Test.9 : Builtin(Int(U8)) = 18i64;
|
||||
ret Test.9;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.7 : Builtin(Bool) = false;
|
||||
let Test.8 : Builtin(Int(U8)) = 0u8;
|
||||
let Test.6 : Builtin(Int(U8)) = CallByName Test.2 Test.7 Test.8;
|
||||
ret Test.6;
|
@ -0,0 +1,10 @@
|
||||
procedure Test.4 (Test.8):
|
||||
let Test.11 : Builtin(Int(U64)) = 1i64;
|
||||
ret Test.11;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.13 : Builtin(Bool) = false;
|
||||
let Test.12 : Builtin(Bool) = false;
|
||||
let Test.10 : Struct([Builtin(Bool), Builtin(Bool)]) = Struct {Test.12, Test.13};
|
||||
let Test.9 : Builtin(Int(U64)) = CallByName Test.4 Test.10;
|
||||
ret Test.9;
|
@ -1,10 +1,10 @@
|
||||
procedure Test.1 (Test.5):
|
||||
let Test.2 : Builtin(Int(I64)) = 42i64;
|
||||
let Test.3 : LambdaSet([( Test.3, [Builtin(Int(I64))])]LambdaSet { set: Ok(()), representation: Struct([Builtin(Int(I64))]) }) = Struct {Test.2};
|
||||
ret Test.3;
|
||||
|
||||
procedure Test.3 (Test.9, #Attr.12):
|
||||
let Test.2 : Builtin(Int(I64)) = StructAtIndex 0 #Attr.12;
|
||||
let Test.2 : Builtin(Int(I64)) = 42i64;
|
||||
ret Test.2;
|
||||
|
||||
procedure Test.0 ():
|
||||
|
@ -1,13 +1,13 @@
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.8 : Builtin(Int(I64)) = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.8;
|
||||
let Test.9 : Builtin(Int(I64)) = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.9;
|
||||
|
||||
procedure Test.1 (Test.4):
|
||||
let Test.2 : Builtin(Int(I64)) = 10i64;
|
||||
let Test.7 : Builtin(Int(I64)) = CallByName Num.24 Test.2 Test.4;
|
||||
let Test.8 : Builtin(Int(I64)) = 10i64;
|
||||
let Test.7 : Builtin(Int(I64)) = CallByName Num.24 Test.8 Test.4;
|
||||
ret Test.7;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.9 : Builtin(Int(I64)) = 9i64;
|
||||
let Test.5 : Builtin(Int(I64)) = CallByName Test.1 Test.9;
|
||||
let Test.10 : Builtin(Int(I64)) = 9i64;
|
||||
let Test.5 : Builtin(Int(I64)) = CallByName Test.1 Test.10;
|
||||
ret Test.5;
|
||||
|
@ -1111,6 +1111,131 @@ fn empty_list_of_function_type() {
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn monomorphized_ints() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
x = 100
|
||||
|
||||
f : U8, U32 -> Nat
|
||||
f = \_, _ -> 18
|
||||
|
||||
f x x
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn monomorphized_floats() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
x = 100.0
|
||||
|
||||
f : F32, F64 -> Nat
|
||||
f = \_, _ -> 18
|
||||
|
||||
f x x
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
#[ignore = "TODO"]
|
||||
fn monomorphized_ints_aliased() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
y = 100
|
||||
w1 = y
|
||||
w2 = y
|
||||
|
||||
f = \_, _ -> 1
|
||||
|
||||
f1 : U8, U32 -> Nat
|
||||
f1 = f
|
||||
|
||||
f2 : U32, U8 -> Nat
|
||||
f2 = f
|
||||
|
||||
f1 w1 w2 + f2 w1 w2
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn monomorphized_tag() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
b = False
|
||||
f : Bool, [True, False, Idk] -> U8
|
||||
f = \_, _ -> 18
|
||||
f b b
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn monomorphized_tag_with_aliased_args() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
b = False
|
||||
c = False
|
||||
a = A b c
|
||||
f : [A Bool Bool] -> Nat
|
||||
f = \_ -> 1
|
||||
f a
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn monomorphized_list() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
l = [1, 2, 3]
|
||||
|
||||
f : List U8, List U16 -> Nat
|
||||
f = \_, _ -> 18
|
||||
|
||||
f l l
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn monomorphized_applied_tag() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
main =
|
||||
a = A "A"
|
||||
f = \x ->
|
||||
when x is
|
||||
A y -> y
|
||||
B y -> y
|
||||
f a
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
// #[ignore]
|
||||
// #[mono_test]
|
||||
// fn static_str_closure() {
|
||||
|
@ -1364,6 +1364,15 @@ impl Subs {
|
||||
pub fn commit_snapshot(&mut self, snapshot: Snapshot<InPlace<Variable>>) {
|
||||
self.utable.commit(snapshot)
|
||||
}
|
||||
|
||||
/// Checks whether the content of `var`, or any nested content, satisfies the `predicate`.
|
||||
pub fn var_contains_content<P>(&self, var: Variable, predicate: P) -> bool
|
||||
where
|
||||
P: Fn(&Content) -> bool + Copy,
|
||||
{
|
||||
let mut seen_recursion_vars = MutSet::default();
|
||||
var_contains_content_help(self, var, predicate, &mut seen_recursion_vars)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -3464,3 +3473,79 @@ fn deep_copy_var_to_help<'a>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn var_contains_content_help<P>(
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
predicate: P,
|
||||
seen_recursion_vars: &mut MutSet<Variable>,
|
||||
) -> bool
|
||||
where
|
||||
P: Fn(&Content) -> bool + Copy,
|
||||
{
|
||||
let mut stack = vec![var];
|
||||
|
||||
macro_rules! push_var_slice {
|
||||
($slice:expr) => {
|
||||
stack.extend(subs.get_subs_slice($slice))
|
||||
};
|
||||
}
|
||||
|
||||
while let Some(var) = stack.pop() {
|
||||
if seen_recursion_vars.contains(&var) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let content = subs.get_content_without_compacting(var);
|
||||
|
||||
if predicate(content) {
|
||||
return true;
|
||||
}
|
||||
|
||||
use Content::*;
|
||||
use FlatType::*;
|
||||
match content {
|
||||
FlexVar(_) | RigidVar(_) => {}
|
||||
RecursionVar {
|
||||
structure,
|
||||
opt_name: _,
|
||||
} => {
|
||||
seen_recursion_vars.insert(var);
|
||||
stack.push(*structure);
|
||||
}
|
||||
Structure(flat_type) => match flat_type {
|
||||
Apply(_, vars) => push_var_slice!(*vars),
|
||||
Func(args, clos, ret) => {
|
||||
push_var_slice!(*args);
|
||||
stack.push(*clos);
|
||||
stack.push(*ret);
|
||||
}
|
||||
Record(fields, var) => {
|
||||
push_var_slice!(fields.variables());
|
||||
stack.push(*var);
|
||||
}
|
||||
TagUnion(tags, ext_var) => {
|
||||
for i in tags.variables() {
|
||||
push_var_slice!(subs[i]);
|
||||
}
|
||||
stack.push(*ext_var);
|
||||
}
|
||||
FunctionOrTagUnion(_, _, var) => stack.push(*var),
|
||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||
seen_recursion_vars.insert(*rec_var);
|
||||
for i in tags.variables() {
|
||||
push_var_slice!(subs[i]);
|
||||
}
|
||||
stack.push(*ext_var);
|
||||
}
|
||||
Erroneous(_) | EmptyRecord | EmptyTagUnion => {}
|
||||
},
|
||||
Alias(_, arguments, real_type_var) => {
|
||||
push_var_slice!(arguments.variables());
|
||||
stack.push(*real_type_var);
|
||||
}
|
||||
Error => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user