mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
Pass 1 of polymorphic specialization of defs
This commit is contained in:
parent
293bc6b15b
commit
7182180622
5 changed files with 309 additions and 257 deletions
|
@ -26,6 +26,7 @@ use roc_types::subs::{
|
||||||
Content, ExhaustiveMark, FlatType, RedundantMark, StorageSubs, Subs, Variable,
|
Content, ExhaustiveMark, FlatType, RedundantMark, StorageSubs, Subs, Variable,
|
||||||
VariableSubsSlice,
|
VariableSubsSlice,
|
||||||
};
|
};
|
||||||
|
use roc_unify::unify::Mode;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
||||||
|
|
||||||
|
@ -263,80 +264,24 @@ impl<'a> PartialProc<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
enum PolymorphicExpr {
|
struct AbilityMember(Symbol);
|
||||||
/// 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),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// A table of aliases of ability member symbols.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum PartialExprLink {
|
struct AbilityAliases(BumpMap<Symbol, AbilityMember>);
|
||||||
/// The root polymorphic expression
|
|
||||||
Sink(PolymorphicExpr),
|
|
||||||
/// A hop in a partial expression alias chain
|
|
||||||
Aliases(Symbol),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A table of symbols to polymorphic expressions. For example, in the program
|
impl AbilityAliases {
|
||||||
///
|
|
||||||
/// 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 {
|
fn new_in(arena: &Bump) -> Self {
|
||||||
Self(BumpMap::new_in(arena))
|
Self(BumpMap::new_in(arena))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, symbol: Symbol, expr: PolymorphicExpr) {
|
fn insert(&mut self, symbol: Symbol, member: AbilityMember) {
|
||||||
self.0.insert(symbol, PartialExprLink::Sink(expr));
|
self.0.insert(symbol, member);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_alias(&mut self, symbol: Symbol, aliases: Symbol) {
|
fn get(&self, symbol: Symbol) -> Option<&AbilityMember> {
|
||||||
self.0.insert(symbol, PartialExprLink::Aliases(aliases));
|
self.0.get(&symbol)
|
||||||
}
|
|
||||||
|
|
||||||
fn contains(&self, symbol: Symbol) -> bool {
|
|
||||||
self.0.contains_key(&symbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&mut self, mut symbol: Symbol) -> Option<&PolymorphicExpr> {
|
|
||||||
// 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::Sink(expr)) => {
|
|
||||||
return Some(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove(&mut self, symbol: Symbol) {
|
|
||||||
debug_assert!(self.contains(symbol));
|
|
||||||
self.0.remove(&symbol);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,26 +746,28 @@ impl<'a> Specialized<'a> {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Procs<'a> {
|
pub struct Procs<'a> {
|
||||||
pub partial_procs: PartialProcs<'a>,
|
pub partial_procs: PartialProcs<'a>,
|
||||||
partial_exprs: PartialExprs,
|
ability_member_aliases: AbilityAliases,
|
||||||
pub imported_module_thunks: &'a [Symbol],
|
pub imported_module_thunks: &'a [Symbol],
|
||||||
pub module_thunks: &'a [Symbol],
|
pub module_thunks: &'a [Symbol],
|
||||||
pending_specializations: PendingSpecializations<'a>,
|
pending_specializations: PendingSpecializations<'a>,
|
||||||
specialized: Specialized<'a>,
|
specialized: Specialized<'a>,
|
||||||
pub runtime_errors: BumpMap<Symbol, &'a str>,
|
pub runtime_errors: BumpMap<Symbol, &'a str>,
|
||||||
pub externals_we_need: BumpMap<ModuleId, ExternalSpecializations>,
|
pub externals_we_need: BumpMap<ModuleId, ExternalSpecializations>,
|
||||||
|
pub needed_symbol_specializations: BumpMap<(Symbol, Layout<'a>), (Variable, Symbol)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Procs<'a> {
|
impl<'a> Procs<'a> {
|
||||||
pub fn new_in(arena: &'a Bump) -> Self {
|
pub fn new_in(arena: &'a Bump) -> Self {
|
||||||
Self {
|
Self {
|
||||||
partial_procs: PartialProcs::new_in(arena),
|
partial_procs: PartialProcs::new_in(arena),
|
||||||
partial_exprs: PartialExprs::new_in(arena),
|
ability_member_aliases: AbilityAliases::new_in(arena),
|
||||||
imported_module_thunks: &[],
|
imported_module_thunks: &[],
|
||||||
module_thunks: &[],
|
module_thunks: &[],
|
||||||
pending_specializations: PendingSpecializations::Finding(Suspended::new_in(arena)),
|
pending_specializations: PendingSpecializations::Finding(Suspended::new_in(arena)),
|
||||||
specialized: Specialized::default(),
|
specialized: Specialized::default(),
|
||||||
runtime_errors: BumpMap::new_in(arena),
|
runtime_errors: BumpMap::new_in(arena),
|
||||||
externals_we_need: BumpMap::new_in(arena),
|
externals_we_need: BumpMap::new_in(arena),
|
||||||
|
needed_symbol_specializations: BumpMap::new_in(arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4081,7 +4028,7 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
CopyExisting(index) => {
|
CopyExisting(index) => {
|
||||||
let record_needs_specialization =
|
let record_needs_specialization =
|
||||||
procs.partial_exprs.contains(structure);
|
procs.ability_member_aliases.get(structure).is_some();
|
||||||
let specialized_structure_sym = if record_needs_specialization {
|
let specialized_structure_sym = if record_needs_specialization {
|
||||||
// We need to specialize the record now; create a new one for it.
|
// We need to specialize the record now; create a new one for it.
|
||||||
// TODO: reuse this symbol for all updates
|
// TODO: reuse this symbol for all updates
|
||||||
|
@ -4308,70 +4255,71 @@ pub fn with_hole<'a>(
|
||||||
unreachable!("calling a non-closure layout")
|
unreachable!("calling a non-closure layout")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UnspecializedExpr(symbol) => match procs.partial_exprs.get(symbol).unwrap()
|
UnspecializedExpr(symbol) => {
|
||||||
{
|
match procs.ability_member_aliases.get(symbol).unwrap() {
|
||||||
&PolymorphicExpr::AbilityMember(member) => {
|
&AbilityMember(member) => {
|
||||||
let proc_name = get_specialization(env, fn_var, member).expect("Recorded as an ability member, but it doesn't have a specialization");
|
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
|
// a call by a known name
|
||||||
return call_by_name(
|
return call_by_name(
|
||||||
env,
|
env,
|
||||||
procs,
|
procs,
|
||||||
fn_var,
|
fn_var,
|
||||||
proc_name,
|
proc_name,
|
||||||
loc_args,
|
loc_args,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
assigned,
|
assigned,
|
||||||
hole,
|
hole,
|
||||||
);
|
);
|
||||||
|
} // TODO(POLYEXPR)
|
||||||
|
// 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(
|
||||||
|
// env,
|
||||||
|
// lambda_set,
|
||||||
|
// closure_data_symbol,
|
||||||
|
// arg_symbols,
|
||||||
|
// arg_layouts,
|
||||||
|
// ret_layout,
|
||||||
|
// assigned,
|
||||||
|
// hole,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// let snapshot = env.subs.snapshot();
|
||||||
|
// let cache_snapshot = layout_cache.snapshot();
|
||||||
|
// let _unified = roc_unify::unify::unify(
|
||||||
|
// env.subs,
|
||||||
|
// fn_var,
|
||||||
|
// *lambda_expr_var,
|
||||||
|
// roc_unify::unify::Mode::EQ,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// result = with_hole(
|
||||||
|
// env,
|
||||||
|
// lambda_expr.clone(),
|
||||||
|
// fn_var,
|
||||||
|
// procs,
|
||||||
|
// layout_cache,
|
||||||
|
// closure_data_symbol,
|
||||||
|
// env.arena.alloc(result),
|
||||||
|
// );
|
||||||
|
// env.subs.rollback_to(snapshot);
|
||||||
|
// layout_cache.rollback_to(cache_snapshot);
|
||||||
|
// }
|
||||||
|
// RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||||
|
// unreachable!("calling a non-closure layout")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
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(
|
|
||||||
env,
|
|
||||||
lambda_set,
|
|
||||||
closure_data_symbol,
|
|
||||||
arg_symbols,
|
|
||||||
arg_layouts,
|
|
||||||
ret_layout,
|
|
||||||
assigned,
|
|
||||||
hole,
|
|
||||||
);
|
|
||||||
|
|
||||||
let snapshot = env.subs.snapshot();
|
|
||||||
let cache_snapshot = layout_cache.snapshot();
|
|
||||||
let _unified = roc_unify::unify::unify(
|
|
||||||
env.subs,
|
|
||||||
fn_var,
|
|
||||||
*lambda_expr_var,
|
|
||||||
roc_unify::unify::Mode::EQ,
|
|
||||||
);
|
|
||||||
|
|
||||||
result = with_hole(
|
|
||||||
env,
|
|
||||||
lambda_expr.clone(),
|
|
||||||
fn_var,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
closure_data_symbol,
|
|
||||||
env.arena.alloc(result),
|
|
||||||
);
|
|
||||||
env.subs.rollback_to(snapshot);
|
|
||||||
layout_cache.rollback_to(cache_snapshot);
|
|
||||||
}
|
|
||||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
|
||||||
unreachable!("calling a non-closure layout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
NotASymbol => {
|
NotASymbol => {
|
||||||
// the expression is not a symbol. That means it's an expression
|
// the expression is not a symbol. That means it's an expression
|
||||||
// evaluating to a function value.
|
// evaluating to a function value.
|
||||||
|
@ -4746,8 +4694,10 @@ fn get_specialization<'a>(
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn construct_closure_data<'a, I>(
|
fn construct_closure_data<'a, I>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
// TODO(POLYEXPR): remove?
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
_procs: &mut Procs<'a>,
|
||||||
|
// TODO(POLYEXPR): remove?
|
||||||
|
_layout_cache: &mut LayoutCache<'a>,
|
||||||
lambda_set: LambdaSet<'a>,
|
lambda_set: LambdaSet<'a>,
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
symbols: I,
|
symbols: I,
|
||||||
|
@ -4764,7 +4714,7 @@ where
|
||||||
// arguments with a polymorphic type that we have to deal with
|
// arguments with a polymorphic type that we have to deal with
|
||||||
let mut polymorphic_arguments = Vec::new_in(env.arena);
|
let mut polymorphic_arguments = Vec::new_in(env.arena);
|
||||||
|
|
||||||
let mut result = match lambda_set.layout_for_member(name) {
|
let result = match lambda_set.layout_for_member(name) {
|
||||||
ClosureRepresentation::Union {
|
ClosureRepresentation::Union {
|
||||||
tag_id,
|
tag_id,
|
||||||
alphabetic_order_fields: field_layouts,
|
alphabetic_order_fields: field_layouts,
|
||||||
|
@ -4775,9 +4725,9 @@ where
|
||||||
// them ordered by their alignment requirements
|
// them ordered by their alignment requirements
|
||||||
let mut combined = Vec::with_capacity_in(symbols.len(), env.arena);
|
let mut combined = Vec::with_capacity_in(symbols.len(), env.arena);
|
||||||
for ((symbol, variable), layout) in symbols.zip(field_layouts.iter()) {
|
for ((symbol, variable), layout) in symbols.zip(field_layouts.iter()) {
|
||||||
if procs.partial_exprs.contains(*symbol) {
|
// if procs.partial_exprs.contains(*symbol) {
|
||||||
polymorphic_arguments.push((*symbol, *variable));
|
// polymorphic_arguments.push((*symbol, *variable));
|
||||||
}
|
// }
|
||||||
|
|
||||||
combined.push((*symbol, layout))
|
combined.push((*symbol, layout))
|
||||||
}
|
}
|
||||||
|
@ -4810,9 +4760,9 @@ where
|
||||||
// them ordered by their alignment requirements
|
// them ordered by their alignment requirements
|
||||||
let mut combined = Vec::with_capacity_in(symbols.len(), env.arena);
|
let mut combined = Vec::with_capacity_in(symbols.len(), env.arena);
|
||||||
for ((symbol, variable), layout) in symbols.zip(field_layouts.iter()) {
|
for ((symbol, variable), layout) in symbols.zip(field_layouts.iter()) {
|
||||||
if procs.partial_exprs.contains(*symbol) {
|
// if procs.partial_exprs.contains(*symbol) {
|
||||||
polymorphic_arguments.push((*symbol, *variable));
|
// polymorphic_arguments.push((*symbol, *variable));
|
||||||
}
|
// }
|
||||||
|
|
||||||
combined.push((*symbol, layout))
|
combined.push((*symbol, layout))
|
||||||
}
|
}
|
||||||
|
@ -4868,9 +4818,12 @@ where
|
||||||
// TODO: this is not quite right. What we should actually be doing is removing references to
|
// 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
|
// polymorphic expressions from the captured symbols, and allowing the specializations of those
|
||||||
// symbols to be inlined when specializing the closure body elsewhere.
|
// symbols to be inlined when specializing the closure body elsewhere.
|
||||||
for (symbol, var) in polymorphic_arguments {
|
// TODO(POLYEXPR)
|
||||||
result = specialize_symbol(env, procs, layout_cache, Some(var), symbol, result, symbol);
|
// for &&(symbol, var) in symbols {
|
||||||
}
|
// if procs.ability_member_aliases.contains(symbol) {
|
||||||
|
// result = specialize_symbol(env, procs, layout_cache, Some(var), symbol, result, symbol);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -5232,7 +5185,7 @@ fn sorted_field_symbols<'a>(
|
||||||
|
|
||||||
let alignment = layout.alignment_bytes(env.target_info);
|
let alignment = layout.alignment_bytes(env.target_info);
|
||||||
|
|
||||||
let symbol = possible_reuse_symbol(env, procs, &arg.value);
|
let symbol = possible_reuse_symbol_or_spec(env, procs, layout_cache, &arg.value, var);
|
||||||
field_symbols_temp.push((alignment, symbol, ((var, arg), &*env.arena.alloc(symbol))));
|
field_symbols_temp.push((alignment, symbol, ((var, arg), &*env.arena.alloc(symbol))));
|
||||||
}
|
}
|
||||||
field_symbols_temp.sort_by(|a, b| b.0.cmp(&a.0));
|
field_symbols_temp.sort_by(|a, b| b.0.cmp(&a.0));
|
||||||
|
@ -5366,32 +5319,6 @@ 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 { .. }
|
|
||||||
| Call(..)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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>(
|
pub fn from_can<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
variable: Variable,
|
variable: Variable,
|
||||||
|
@ -5662,38 +5589,91 @@ pub fn from_can<'a>(
|
||||||
|
|
||||||
return from_can(env, variable, new_outer, procs, layout_cache);
|
return from_can(env, variable, new_outer, procs, layout_cache);
|
||||||
}
|
}
|
||||||
ref body if expr_is_polymorphic(env, body, def.expr_var) => {
|
// TODO(POLYEXPR)
|
||||||
// This is a pattern like
|
// ref body if expr_is_polymorphic(env, body, def.expr_var) => {
|
||||||
//
|
// // This is a pattern like
|
||||||
// n = 1
|
// //
|
||||||
// asU8 n
|
// // 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
|
// // At the definition site `n = 1` we only know `1` to have the type `[Int *]`,
|
||||||
// that will be specialized at each concrete usage site.
|
// // which won't be refined until the call `asU8 n`. Add it as a partial expression
|
||||||
procs.partial_exprs.insert(
|
// // that will be specialized at each concrete usage site.
|
||||||
*symbol,
|
// procs.ability_member_aliases.insert(
|
||||||
PolymorphicExpr::Expr(def.loc_expr.value, def.expr_var),
|
// *symbol,
|
||||||
);
|
// PolymorphicExpr::Expr(def.loc_expr.value, def.expr_var),
|
||||||
|
// );
|
||||||
|
|
||||||
let result = from_can(env, variable, cont.value, procs, layout_cache);
|
// let result = from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
|
|
||||||
// We won't see this symbol again.
|
// // We won't see this symbol again.
|
||||||
procs.partial_exprs.remove(*symbol);
|
// procs.ability_member_aliases.remove(*symbol);
|
||||||
|
|
||||||
return result;
|
// return result;
|
||||||
}
|
// }
|
||||||
_ => {
|
_ => {
|
||||||
let rest = from_can(env, variable, cont.value, procs, layout_cache);
|
let rest = from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
return with_hole(
|
|
||||||
env,
|
let needs_def_specializations = procs
|
||||||
def.loc_expr.value,
|
.needed_symbol_specializations
|
||||||
def.expr_var,
|
.keys()
|
||||||
procs,
|
.find(|(s, _)| s == symbol)
|
||||||
layout_cache,
|
.is_some();
|
||||||
*symbol,
|
|
||||||
env.arena.alloc(rest),
|
if !needs_def_specializations {
|
||||||
);
|
return with_hole(
|
||||||
|
env,
|
||||||
|
def.loc_expr.value,
|
||||||
|
def.expr_var,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
*symbol,
|
||||||
|
env.arena.alloc(rest),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do need specializations
|
||||||
|
|
||||||
|
let mut stmt = rest;
|
||||||
|
let needed_specializations = procs
|
||||||
|
.needed_symbol_specializations
|
||||||
|
.drain_filter(|(s, _), _| s == symbol)
|
||||||
|
.collect::<std::vec::Vec<_>>();
|
||||||
|
|
||||||
|
for ((_, wanted_layout), (var, specialized_symbol)) in
|
||||||
|
needed_specializations
|
||||||
|
{
|
||||||
|
// let res =
|
||||||
|
// roc_unify::unify::unify(env.subs, var, def.expr_var, Mode::EQ);
|
||||||
|
let content = env.subs.get_content_without_compacting(def.expr_var);
|
||||||
|
let c = roc_types::subs::SubsFmtContent(content, env.subs);
|
||||||
|
let content2 = env.subs.get_content_without_compacting(var);
|
||||||
|
let c2 = roc_types::subs::SubsFmtContent(content2, env.subs);
|
||||||
|
let layout = layout_cache
|
||||||
|
.from_var(env.arena, def.expr_var, env.subs)
|
||||||
|
.unwrap();
|
||||||
|
dbg!(
|
||||||
|
specialized_symbol,
|
||||||
|
c,
|
||||||
|
c2,
|
||||||
|
layout,
|
||||||
|
wanted_layout,
|
||||||
|
var,
|
||||||
|
def.expr_var,
|
||||||
|
);
|
||||||
|
stmt = with_hole(
|
||||||
|
env,
|
||||||
|
def.loc_expr.value.clone(),
|
||||||
|
// def.expr_var,
|
||||||
|
var,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
specialized_symbol,
|
||||||
|
env.arena.alloc(stmt),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stmt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6335,19 +6315,22 @@ fn store_pattern_help<'a>(
|
||||||
|
|
||||||
match can_pat {
|
match can_pat {
|
||||||
Identifier(symbol) => {
|
Identifier(symbol) => {
|
||||||
if let Some(&PolymorphicExpr::Expr(_, var)) = procs.partial_exprs.get(outer_symbol) {
|
// TODO(POLYEXPR)
|
||||||
// It might be the case that symbol we're storing hasn't been reified to a value
|
// if let Some(&PolymorphicExpr::Expr(_, var)) =
|
||||||
// yet, if it's polymorphic. Do that now.
|
// procs.ability_member_aliases.get(outer_symbol)
|
||||||
stmt = specialize_symbol(
|
// {
|
||||||
env,
|
// // It might be the case that symbol we're storing hasn't been reified to a value
|
||||||
procs,
|
// // yet, if it's polymorphic. Do that now.
|
||||||
layout_cache,
|
// stmt = specialize_symbol(
|
||||||
Some(var),
|
// env,
|
||||||
*symbol,
|
// procs,
|
||||||
stmt,
|
// layout_cache,
|
||||||
outer_symbol,
|
// Some(var),
|
||||||
);
|
// *symbol,
|
||||||
}
|
// stmt,
|
||||||
|
// outer_symbol,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
substitute_in_exprs(env.arena, &mut stmt, *symbol, outer_symbol);
|
substitute_in_exprs(env.arena, &mut stmt, *symbol, outer_symbol);
|
||||||
}
|
}
|
||||||
|
@ -6712,7 +6695,7 @@ fn can_reuse_symbol<'a>(
|
||||||
Imported(symbol)
|
Imported(symbol)
|
||||||
} else if procs.partial_procs.contains_key(symbol) {
|
} else if procs.partial_procs.contains_key(symbol) {
|
||||||
LocalFunction(symbol)
|
LocalFunction(symbol)
|
||||||
} else if procs.partial_exprs.contains(symbol) {
|
} else if procs.ability_member_aliases.get(symbol).is_some() {
|
||||||
UnspecializedExpr(symbol)
|
UnspecializedExpr(symbol)
|
||||||
} else {
|
} else {
|
||||||
Value(symbol)
|
Value(symbol)
|
||||||
|
@ -6733,6 +6716,39 @@ fn possible_reuse_symbol<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(POLYEXPR): unify with possible_reuse_symbol
|
||||||
|
fn possible_reuse_symbol_or_spec<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
expr: &roc_can::expr::Expr,
|
||||||
|
var: Variable,
|
||||||
|
) -> Symbol {
|
||||||
|
match can_reuse_symbol(env, procs, expr) {
|
||||||
|
ReuseSymbol::Value(symbol) => {
|
||||||
|
let wanted_layout = layout_cache.from_var(env.arena, var, env.subs).unwrap();
|
||||||
|
|
||||||
|
let mut fake_subs = env.subs.clone();
|
||||||
|
let new_var = roc_types::subs::deep_copy_var_to(&mut fake_subs, env.subs, var);
|
||||||
|
let content = roc_types::subs::SubsFmtContent(
|
||||||
|
env.subs.get_content_without_compacting(new_var),
|
||||||
|
env.subs,
|
||||||
|
);
|
||||||
|
dbg!(new_var, content);
|
||||||
|
|
||||||
|
let (_, specialized_symbol) = procs
|
||||||
|
.needed_symbol_specializations
|
||||||
|
.entry((symbol, wanted_layout))
|
||||||
|
.or_insert_with(|| (new_var, env.unique_symbol()));
|
||||||
|
|
||||||
|
dbg!(symbol, *specialized_symbol, wanted_layout, var);
|
||||||
|
|
||||||
|
*specialized_symbol
|
||||||
|
}
|
||||||
|
_ => env.unique_symbol(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_variable_aliasing<'a, BuildRest>(
|
fn handle_variable_aliasing<'a, BuildRest>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
|
@ -6747,15 +6763,15 @@ where
|
||||||
{
|
{
|
||||||
if env.abilities_store.is_ability_member_name(right) {
|
if env.abilities_store.is_ability_member_name(right) {
|
||||||
procs
|
procs
|
||||||
.partial_exprs
|
.ability_member_aliases
|
||||||
.insert(left, PolymorphicExpr::AbilityMember(right));
|
.insert(left, AbilityMember(right));
|
||||||
return build_rest(env, procs, layout_cache);
|
return build_rest(env, procs, layout_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
if procs.partial_exprs.contains(right) {
|
if let Some(&ability_member) = procs.ability_member_aliases.get(right) {
|
||||||
// If `right` links to a partial expression, make sure we link `left` to it as well, so
|
// 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.
|
// that usages of it will be specialized when building the rest of the program.
|
||||||
procs.partial_exprs.insert_alias(left, right);
|
procs.ability_member_aliases.insert(left, ability_member);
|
||||||
return build_rest(env, procs, layout_cache);
|
return build_rest(env, procs, layout_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6790,7 +6806,28 @@ where
|
||||||
} else {
|
} else {
|
||||||
// This should be a fully specialized value. Replace the alias with the original symbol.
|
// This should be a fully specialized value. Replace the alias with the original symbol.
|
||||||
let mut result = build_rest(env, procs, layout_cache);
|
let mut result = build_rest(env, procs, layout_cache);
|
||||||
|
|
||||||
|
// We need to lift all specializations of "left" to be specializations of "right".
|
||||||
|
let to_update = procs
|
||||||
|
.needed_symbol_specializations
|
||||||
|
.drain_filter(|(s, _), _| s == &left)
|
||||||
|
.collect::<std::vec::Vec<_>>();
|
||||||
|
let mut scratchpad_update_specializations = std::vec::Vec::new();
|
||||||
|
for ((_, layout), (specialized_var, specialized_sym)) in to_update.into_iter() {
|
||||||
|
let old_specialized_sym = procs
|
||||||
|
.needed_symbol_specializations
|
||||||
|
.insert((right, layout), (specialized_var, specialized_sym));
|
||||||
|
|
||||||
|
if let Some((_, old_specialized_sym)) = old_specialized_sym {
|
||||||
|
scratchpad_update_specializations.push((old_specialized_sym, specialized_sym));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
substitute_in_exprs(env.arena, &mut result, left, right);
|
substitute_in_exprs(env.arena, &mut result, left, right);
|
||||||
|
for (old_specialized_sym, specialized_sym) in scratchpad_update_specializations.into_iter()
|
||||||
|
{
|
||||||
|
substitute_in_exprs(env.arena, &mut result, old_specialized_sym, specialized_sym);
|
||||||
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6829,34 +6866,36 @@ fn specialize_symbol<'a>(
|
||||||
result: Stmt<'a>,
|
result: Stmt<'a>,
|
||||||
original: Symbol,
|
original: Symbol,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
if let Some(PolymorphicExpr::Expr(expr, expr_var)) = procs.partial_exprs.get(original) {
|
// TODO(POLYEXPR)
|
||||||
// Specialize the expression type now, based off the `arg_var` we've been given.
|
// if let Some(PolymorphicExpr::Expr(expr, expr_var)) = procs.ability_member_aliases.get(original)
|
||||||
// TODO: cache the specialized result
|
// {
|
||||||
let snapshot = env.subs.snapshot();
|
// // Specialize the expression type now, based off the `arg_var` we've been given.
|
||||||
let cache_snapshot = layout_cache.snapshot();
|
// // TODO: cache the specialized result
|
||||||
let _unified = roc_unify::unify::unify(
|
// let snapshot = env.subs.snapshot();
|
||||||
env.subs,
|
// let cache_snapshot = layout_cache.snapshot();
|
||||||
arg_var.unwrap(),
|
// let _unified = roc_unify::unify::unify(
|
||||||
*expr_var,
|
// env.subs,
|
||||||
roc_unify::unify::Mode::EQ,
|
// arg_var.unwrap(),
|
||||||
);
|
// *expr_var,
|
||||||
|
// roc_unify::unify::Mode::EQ,
|
||||||
|
// );
|
||||||
|
|
||||||
let result = with_hole(
|
// let result = with_hole(
|
||||||
env,
|
// env,
|
||||||
expr.clone(),
|
// expr.clone(),
|
||||||
*expr_var,
|
// *expr_var,
|
||||||
procs,
|
// procs,
|
||||||
layout_cache,
|
// layout_cache,
|
||||||
symbol,
|
// symbol,
|
||||||
env.arena.alloc(result),
|
// env.arena.alloc(result),
|
||||||
);
|
// );
|
||||||
|
|
||||||
// Restore the prior state so as not to interfere with future specializations.
|
// // Restore the prior state so as not to interfere with future specializations.
|
||||||
env.subs.rollback_to(snapshot);
|
// env.subs.rollback_to(snapshot);
|
||||||
layout_cache.rollback_to(cache_snapshot);
|
// layout_cache.rollback_to(cache_snapshot);
|
||||||
|
|
||||||
return result;
|
// return result;
|
||||||
}
|
// }
|
||||||
|
|
||||||
match procs.get_partial_proc(original) {
|
match procs.get_partial_proc(original) {
|
||||||
None => {
|
None => {
|
||||||
|
@ -7040,8 +7079,17 @@ fn assign_to_symbol<'a>(
|
||||||
original,
|
original,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Value(_) => {
|
Value(_symbol) => {
|
||||||
// symbol is already defined; nothing else to do here
|
//let wanted_layout = layout_cache.from_var(env.arena, arg_var, env.subs).unwrap();
|
||||||
|
//let (_, specialized_symbol) = procs
|
||||||
|
// .needed_symbol_specializations
|
||||||
|
// .entry((symbol, wanted_layout))
|
||||||
|
// .or_insert_with(|| (arg_var, env.unique_symbol()));
|
||||||
|
|
||||||
|
//dbg!(symbol, wanted_layout);
|
||||||
|
|
||||||
|
//let mut result = result;
|
||||||
|
//substitute_in_exprs(env.arena, &mut result, symbol, *specialized_symbol);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
NotASymbol => with_hole(
|
NotASymbol => with_hole(
|
||||||
|
@ -7188,8 +7236,14 @@ fn call_by_name<'a>(
|
||||||
|
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let arg_symbols = Vec::from_iter_in(
|
let arg_symbols = Vec::from_iter_in(
|
||||||
loc_args.iter().map(|(_, arg_expr)| {
|
loc_args.iter().map(|(arg_var, arg_expr)| {
|
||||||
possible_reuse_symbol(env, procs, &arg_expr.value)
|
possible_reuse_symbol_or_spec(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
&arg_expr.value,
|
||||||
|
*arg_var,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
|
@ -7280,11 +7334,9 @@ fn call_by_name_help<'a>(
|
||||||
|
|
||||||
// the arguments given to the function, stored in symbols
|
// the arguments given to the function, stored in symbols
|
||||||
let mut field_symbols = Vec::with_capacity_in(loc_args.len(), arena);
|
let mut field_symbols = Vec::with_capacity_in(loc_args.len(), arena);
|
||||||
field_symbols.extend(
|
field_symbols.extend(loc_args.iter().map(|(arg_var, arg_expr)| {
|
||||||
loc_args
|
possible_reuse_symbol_or_spec(env, procs, layout_cache, &arg_expr.value, *arg_var)
|
||||||
.iter()
|
}));
|
||||||
.map(|(_, arg_expr)| possible_reuse_symbol(env, procs, &arg_expr.value)),
|
|
||||||
);
|
|
||||||
|
|
||||||
// If required, add an extra argument to the layout that is the captured environment
|
// If required, add an extra argument to the layout that is the captured environment
|
||||||
// afterwards, we MUST make sure the number of arguments in the layout matches the
|
// afterwards, we MUST make sure the number of arguments in the layout matches the
|
||||||
|
|
|
@ -440,7 +440,7 @@ impl Env {
|
||||||
const DEFAULT_POOLS: usize = 8;
|
const DEFAULT_POOLS: usize = 8;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct Pools(Vec<Vec<Variable>>);
|
pub struct Pools(Vec<Vec<Variable>>);
|
||||||
|
|
||||||
impl Default for Pools {
|
impl Default for Pools {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -2868,7 +2868,7 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deep_copy_var_in(
|
pub fn deep_copy_var_in(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
rank: Rank,
|
rank: Rank,
|
||||||
pools: &mut Pools,
|
pools: &mut Pools,
|
||||||
|
|
|
@ -612,15 +612,12 @@ fn i64_abs() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
#[should_panic(
|
|
||||||
expected = r#"Roc failed with message: "integer absolute overflowed because its argument is the minimum value"#
|
|
||||||
)]
|
|
||||||
fn abs_min_int_overflow() {
|
fn abs_min_int_overflow() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
Num.abs Num.minI64
|
Num.abs Num.minI64
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
0,
|
0,
|
||||||
i64
|
i64
|
||||||
|
@ -3072,7 +3069,7 @@ fn sub_saturated() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
fn monomorphized_ints() {
|
fn monomorphized_ints1() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
|
|
@ -1319,7 +1319,7 @@ fn monomorphized_applied_tag() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
fn monomorphized_tag_with_polymorphic_arg() {
|
fn monomorphized_tag_with_polymorphic_arg1() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
|
|
@ -240,6 +240,9 @@ fn create_llvm_module<'a>(
|
||||||
|
|
||||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||||
// env.module.print_to_stderr();
|
// env.module.print_to_stderr();
|
||||||
|
env.module
|
||||||
|
.print_to_file(&std::path::PathBuf::from("/tmp/out.ll"))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
(main_fn_name, delayed_errors.join("\n"), env.module)
|
(main_fn_name, delayed_errors.join("\n"), env.module)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue