Use Derived_synth for synthesizing implementations, and Derived_gen for codegen

This commit is contained in:
Ayaz Hafiz 2022-07-12 13:36:14 -04:00
parent 7c2e806a02
commit df9bcb1a0c
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
16 changed files with 1543 additions and 628 deletions

View file

@ -7,7 +7,7 @@ use roc_can::abilities::SpecializationLambdaSets;
use roc_can::expr::Expr;
use roc_can::pattern::Pattern;
use roc_can::{def::Def, module::ExposedByModule};
use roc_collections::MutMap;
use roc_collections::{MutMap, VecMap};
use roc_derive_key::DeriveKey;
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_region::all::Loc;
@ -17,7 +17,7 @@ use roc_types::subs::{
mod encoding;
pub(crate) const DERIVED_MODULE: ModuleId = ModuleId::DERIVED;
pub(crate) const DERIVED_SYNTH: ModuleId = ModuleId::DERIVED_SYNTH;
pub fn synth_var(subs: &mut Subs, content: Content) -> Variable {
let descriptor = Descriptor {
@ -33,20 +33,13 @@ pub fn synth_var(subs: &mut Subs, content: Content) -> Variable {
}
/// Map of [`DeriveKey`]s to their derived symbols.
///
/// This represents the [`Derived_synth`][Symbol::DERIVED_SYNTH] module.
#[derive(Debug, Default)]
pub struct DerivedModule {
map: MutMap<DeriveKey, (Symbol, Def, SpecializationLambdaSets)>,
subs: Subs,
derived_ident_ids: IdentIds,
/// Has someone stolen subs/ident ids from us?
#[cfg(debug_assertions)]
stolen: bool,
}
pub struct StolenFromDerived {
pub subs: Subs,
pub ident_ids: IdentIds,
}
pub(crate) struct DerivedBody {
@ -97,24 +90,12 @@ impl DerivedModule {
exposed_by_module: &ExposedByModule,
key: DeriveKey,
) -> &(Symbol, Def, SpecializationLambdaSets) {
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen, "attempting to add to stolen symbols!");
}
match self.map.get(&key) {
Some(entry) => {
// Might happen that during rollback in specializing the Derived module, we wink
// away the variables associated with a derived impl. In this case, just rebuild
// the impl.
dbg!(entry.0);
if entry.2.iter().all(|(_, var)| self.subs.contains(*var)) {
// rustc won't let us return an immutable reference *and* continue using
// `self.map` immutably below, but this is safe, because we are not returning
// an immutable reference to the entry.
return unsafe { std::mem::transmute(entry) };
}
dbg!(entry.0);
// rustc won't let us return an immutable reference *and* continue using
// `self.map` immutably below, but this is safe, because we are not returning
// an immutable reference to the entry.
return unsafe { std::mem::transmute(entry) };
}
None => {}
}
@ -129,7 +110,7 @@ impl DerivedModule {
// This is expensive, but yields much better symbols when debugging.
// TODO: hide behind debug_flags?
DERIVED_MODULE.register_debug_idents(&self.derived_ident_ids);
DERIVED_SYNTH.register_debug_idents(&self.derived_ident_ids);
ident_id
} else {
@ -137,7 +118,7 @@ impl DerivedModule {
self.derived_ident_ids.gen_unique()
};
let derived_symbol = Symbol::new(DERIVED_MODULE, ident_id);
let derived_symbol = Symbol::new(DERIVED_SYNTH, ident_id);
let (derived_def, specialization_lsets) = build_derived_body(
&mut self.subs,
&mut self.derived_ident_ids,
@ -147,35 +128,12 @@ impl DerivedModule {
);
let triple = (derived_symbol, derived_def, specialization_lsets);
self.map.remove(&key);
self.map.entry(key).or_insert(triple)
}
/// TERRIBLE HACK to deal with rollbacks: in specializing the Derived module, we snapshot and
/// rollback Subs every time we specialize a particular procedure. However in specializing a
/// procedure we may have added a new derived impl to the [`DerivedModule`] whose types are
/// then rolled back! Hence, we need to re-initialize the derived impl in such cases.
///
/// But there must be a better way!!
pub fn refresh_stale_specializations(&mut self, exposed_by_module: &ExposedByModule) {
let all_derived: Vec<_> = self.map.keys().cloned().collect();
//dbg!(&all_derived);
all_derived
.into_iter()
// get_or_insert will handle re-initializing stale defs
.for_each(|derive_key| {
let _ = self.get_or_insert(exposed_by_module, derive_key);
});
}
pub fn iter_all(
&self,
) -> impl Iterator<Item = (&DeriveKey, &(Symbol, Def, SpecializationLambdaSets))> {
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen);
}
self.map.iter()
}
@ -183,83 +141,67 @@ impl DerivedModule {
/// module; other modules should use [`Self::get_or_insert`] to generate a symbol for a derived
/// ability member usage.
pub fn gen_unique(&mut self) -> Symbol {
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen);
}
let ident_id = self.derived_ident_ids.gen_unique();
Symbol::new(DERIVED_MODULE, ident_id)
}
/// Steal all created derived ident Ids.
/// After this is called, [`Self::get_or_insert`] may no longer be called.
pub fn steal(&mut self) -> StolenFromDerived {
let mut ident_ids = Default::default();
std::mem::swap(&mut self.derived_ident_ids, &mut ident_ids);
let mut subs = Default::default();
std::mem::swap(&mut self.subs, &mut subs);
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen);
self.stolen = true;
}
StolenFromDerived { subs, ident_ids }
}
pub fn return_stolen(&mut self, stolen: StolenFromDerived) {
#[cfg(debug_assertions)]
{
debug_assert!(self.stolen);
self.stolen = false;
}
let StolenFromDerived { subs, ident_ids } = stolen;
self.subs = subs;
self.derived_ident_ids = ident_ids;
Symbol::new(DERIVED_SYNTH, ident_id)
}
// TODO: just pass and copy the ambient function directly, don't pass the lambda set var.
pub fn copy_lambda_set_ambient_function_to_subs(
&self,
lambda_set_var: Variable,
target_subs_home: ModuleId,
target: &mut Subs,
target_rank: Rank,
) -> Variable {
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen);
}
let ambient_function_var = self.subs.get_lambda_set(lambda_set_var).ambient_function;
if target_subs_home == ModuleId::DERIVED {
ambient_function_var
} else {
let copied_import = copy_import_to(
&self.subs,
target,
// bookkeep unspecialized lambda sets of var - I think we want this here
true,
ambient_function_var,
// TODO: I think this is okay because the only use of `copy_lambda_set_var_to_subs`
// (at least right now) is for lambda set compaction, which will automatically unify
// and lower ranks, and never generalize.
//
// However this is a bad coupling and maybe not a good assumption, we should revisit
// this when possible.
Rank::import(),
// target_rank,
);
let copied_import = copy_import_to(
&self.subs,
target,
// bookkeep unspecialized lambda sets of var - I think we want this here
true,
ambient_function_var,
// TODO: I think this is okay because the only use of `copy_lambda_set_var_to_subs`
// (at least right now) is for lambda set compaction, which will automatically unify
// and lower ranks, and never generalize.
//
// However this is a bad coupling and maybe not a good assumption, we should revisit
// this when possible.
Rank::import(),
// target_rank,
);
copied_import.variable
}
copied_import.variable
}
/// Gets the derived defs that should be loaded into the derived gen module, skipping over the
/// defs that have already been loaded.
pub fn iter_load_for_gen_module(
&mut self,
gen_subs: &mut Subs,
should_load_def: impl Fn(Symbol) -> bool,
) -> VecMap<Symbol, Expr> {
self.map
.values()
.filter_map(|(symbol, def, _)| {
if should_load_def(*symbol) {
let (_new_expr_var, new_expr) = roc_can::copy::deep_copy_expr_across_subs(
&mut self.subs,
gen_subs,
def.expr_var,
&def.loc_expr.value,
);
Some((*symbol, new_expr))
} else {
None
}
})
.collect()
}
pub fn decompose(self) -> IdentIds {
self.derived_ident_ids
}
}
/// Thread-sharable [`DerivedMethods`].
/// Thread-sharable [`DerivedModule`].
pub type SharedDerivedModule = Arc<Mutex<DerivedModule>>;