This commit is contained in:
Richard Feldman 2024-10-09 22:54:29 -04:00
parent 58aaf32475
commit 0d459cc84b
No known key found for this signature in database
GPG key ID: 5DE4EE30BB738EDF
4 changed files with 728 additions and 0 deletions

View file

@ -13,6 +13,8 @@ use roc_types::{
types::{AliasKind, RecordField},
};
mod mono_type;
#[derive(Debug, PartialEq, Eq)]
pub enum Problem {
// Compiler bugs; these should never happen!

View file

@ -0,0 +1,76 @@
use core::fmt;
/// A slice into the Vec<T> of MonoTypes
///
/// The starting position is a u32 which should be plenty
/// We limit slices to u16::MAX = 65535 elements
pub struct MonoSlice<T> {
pub start: u32,
pub length: u16,
_marker: std::marker::PhantomData<T>,
}
impl<T> Copy for MonoSlice<T> {}
impl<T> Clone for MonoSlice<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> std::fmt::Debug for MonoSlice<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"MonoSlice {{ start: {}, length: {} }}",
self.start, self.length
)
}
}
#[derive(Clone, Copy, Debug)]
struct Symbol {
inner: u64,
}
#[derive(Clone, Copy, Debug)]
struct MonoTypeId {
inner: u32,
}
#[derive(Clone, Copy, Debug)]
pub enum MonoType {
Apply(Symbol, MonoSlice<MonoTypeId>),
Func {
args: MonoSlice<MonoTypeId>,
ret: MonoTypeId,
},
Record(RecordFields),
Tuple(TupleElems),
TagUnion(UnionTags),
EmptyRecord,
EmptyTuple,
EmptyTagUnion,
}
#[derive(Clone, Copy, Debug)]
pub struct RecordFields {
pub length: u16,
pub field_names_start: u32,
pub field_type_ids_start: u32,
pub field_types_start: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct TupleElems {
pub length: u16,
pub elem_index_start: u32,
pub type_ids_start: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct UnionTags {
pub length: u16,
pub labels_start: u32,
pub values_start: u32,
}

View file

@ -0,0 +1,310 @@
use crate::expr::{self, Declarations, Expr, FunctionDef};
use crate::specialize_type::{MonoCache, Problem};
use crate::subs::{Subs, Variable};
use crate::symbol::Symbol;
use roc_collections::VecMap;
struct Context {
symbols: Symbol,
fresh_tvar: Box<dyn Fn() -> Variable>,
specializations: Specializations,
}
struct Specializations {
symbols: Symbol,
fenv: Vec<(Symbol, expr::FunctionDef)>,
specializations: Vec<(SpecializationKey, NeededSpecialization)>,
}
#[derive(PartialEq, Eq)]
struct SpecializationKey(Symbol, Variable);
struct NeededSpecialization {
def: expr::FunctionDef,
name_new: Symbol,
t_new: Variable,
specialized: Option<expr::Def>,
}
impl Specializations {
fn make(symbols: Symbol, program: &expr::Declarations) -> Self {
let fenv = program
.iter_top_down()
.filter_map(|(_, tag)| match tag {
expr::DeclarationTag::Function(idx)
| expr::DeclarationTag::Recursive(idx)
| expr::DeclarationTag::TailRecursive(idx) => {
let func = &program.function_bodies[idx.index()];
Some((func.value.name, func.value.clone()))
}
_ => None,
})
.collect();
Specializations {
symbols,
fenv,
specializations: Vec::new(),
}
}
fn specialize_fn(
&mut self,
mono_cache: &mut MonoCache,
name: Symbol,
t_new: Variable,
) -> Option<Symbol> {
let specialization = (name, t_new);
if let Some((_, needed)) = self
.specializations
.iter()
.find(|(key, _)| *key == specialization)
{
Some(needed.name_new)
} else {
let def = self.fenv.iter().find(|(n, _)| *n == name)?.1.clone();
let name_new = self.symbols.fresh_symbol_named(name);
let needed_specialization = NeededSpecialization {
def,
name_new,
t_new,
specialized: None,
};
self.specializations
.push((specialization, needed_specialization));
Some(name_new)
}
}
fn next_needed_specialization(&mut self) -> Option<&mut NeededSpecialization> {
self.specializations.iter_mut().find_map(|(_, ns)| {
if ns.specialized.is_none() {
Some(ns)
} else {
None
}
})
}
fn solved_specializations(&self) -> Vec<expr::Def> {
self.specializations
.iter()
.filter_map(|(_, ns)| ns.specialized.clone())
.collect()
}
}
fn specialize_expr(
ctx: &mut Context,
ty_cache: &mut Subs,
mono_cache: &mut MonoCache,
expr: &Expr,
) -> Expr {
match expr {
Expr::Var(x) => {
if let Some(y) = ctx
.specializations
.specialize_fn(mono_cache, *x, expr.get_type())
{
Expr::Var(y)
} else {
expr.clone()
}
}
Expr::Int(i) => Expr::Int(*i),
Expr::Str(s) => Expr::Str(s.clone()),
Expr::Tag(t, args) => {
let new_args = args
.iter()
.map(|a| specialize_expr(ctx, ty_cache, mono_cache, a))
.collect();
Expr::Tag(*t, new_args)
}
Expr::Record(fields) => {
let new_fields = fields
.iter()
.map(|(f, e)| (*f, specialize_expr(ctx, ty_cache, mono_cache, e)))
.collect();
Expr::Record(new_fields)
}
Expr::Access(e, f) => {
Expr::Access(Box::new(specialize_expr(ctx, ty_cache, mono_cache, e)), *f)
}
Expr::Let(def, rest) => {
let new_def = match def {
expr::Def::Letfn(f) => expr::Def::Letfn(FunctionDef {
recursive: f.recursive,
bind: (
mono_cache.monomorphize_var(ty_cache, &mut Vec::new(), f.bind.0),
f.bind.1,
),
arg: (
mono_cache.monomorphize_var(ty_cache, &mut Vec::new(), f.arg.0),
f.arg.1,
),
body: Box::new(specialize_expr(ctx, ty_cache, mono_cache, &f.body)),
}),
expr::Def::Letval(v) => expr::Def::Letval(expr::Letval {
bind: (
mono_cache.monomorphize_var(ty_cache, &mut Vec::new(), v.bind.0),
v.bind.1,
),
body: Box::new(specialize_expr(ctx, ty_cache, mono_cache, &v.body)),
}),
};
let new_rest = Box::new(specialize_expr(ctx, ty_cache, mono_cache, rest));
Expr::Let(Box::new(new_def), new_rest)
}
Expr::Clos { arg, body } => {
let new_arg = (
mono_cache.monomorphize_var(ty_cache, &mut Vec::new(), arg.0),
arg.1,
);
let new_body = Box::new(specialize_expr(ctx, ty_cache, mono_cache, body));
Expr::Clos {
arg: new_arg,
body: new_body,
}
}
Expr::Call(f, a) => {
let new_f = Box::new(specialize_expr(ctx, ty_cache, mono_cache, f));
let new_a = Box::new(specialize_expr(ctx, ty_cache, mono_cache, a));
Expr::Call(new_f, new_a)
}
Expr::KCall(kfn, args) => {
let new_args = args
.iter()
.map(|a| specialize_expr(ctx, ty_cache, mono_cache, a))
.collect();
Expr::KCall(*kfn, new_args)
}
Expr::When(e, branches) => {
let new_e = Box::new(specialize_expr(ctx, ty_cache, mono_cache, e));
let new_branches = branches
.iter()
.map(|(p, e)| {
let new_p = specialize_pattern(ctx, ty_cache, mono_cache, p);
let new_e = specialize_expr(ctx, ty_cache, mono_cache, e);
(new_p, new_e)
})
.collect();
Expr::When(new_e, new_branches)
}
}
}
fn specialize_pattern(
ctx: &mut Context,
ty_cache: &mut Subs,
mono_cache: &mut MonoCache,
pattern: &expr::Pattern,
) -> expr::Pattern {
match pattern {
expr::Pattern::PVar(x) => expr::Pattern::PVar(*x),
expr::Pattern::PTag(tag, args) => {
let new_args = args
.iter()
.map(|a| specialize_pattern(ctx, ty_cache, mono_cache, a))
.collect();
expr::Pattern::PTag(*tag, new_args)
}
}
}
fn specialize_let_fn(
ctx: &mut Context,
ty_cache: &mut Subs,
mono_cache: &mut MonoCache,
t_new: Variable,
name_new: Symbol,
f: &expr::FunctionDef,
) -> expr::Def {
mono_cache.monomorphize_var(ty_cache, &mut Vec::new(), t_new);
let t = mono_cache.monomorphize_var(ty_cache, &mut Vec::new(), f.bind.0);
let t_arg = mono_cache.monomorphize_var(ty_cache, &mut Vec::new(), f.arg.0);
let body = specialize_expr(ctx, ty_cache, mono_cache, &f.body);
expr::Def::Letfn(FunctionDef {
recursive: f.recursive,
bind: (t, name_new),
arg: (t_arg, f.arg.1),
body: Box::new(body),
})
}
fn specialize_let_val(ctx: &mut Context, v: &expr::Letval) -> expr::Def {
let mut ty_cache = Subs::new();
let mut mono_cache = MonoCache::from_subs(&ty_cache);
let t = mono_cache.monomorphize_var(&mut ty_cache, &mut Vec::new(), v.bind.0);
let body = specialize_expr(ctx, &mut ty_cache, &mut mono_cache, &v.body);
expr::Def::Letval(expr::Letval {
bind: (t, v.bind.1),
body: Box::new(body),
})
}
fn specialize_run_def(ctx: &mut Context, run: &expr::Run) -> expr::Run {
let mut ty_cache = Subs::new();
let mut mono_cache = MonoCache::from_subs(&ty_cache);
let t = mono_cache.monomorphize_var(&mut ty_cache, &mut Vec::new(), run.bind.0);
let body = specialize_expr(ctx, &mut ty_cache, &mut mono_cache, &run.body);
expr::Run {
bind: (t, run.bind.1),
body: Box::new(body),
ty: run.ty,
}
}
fn make_context(
symbols: Symbol,
fresh_tvar: Box<dyn Fn() -> Variable>,
program: &expr::Declarations,
) -> Context {
Context {
symbols,
fresh_tvar,
specializations: Specializations::make(symbols, program),
}
}
fn loop_specializations(ctx: &mut Context) {
while let Some(needed) = ctx.specializations.next_needed_specialization() {
let mut ty_cache = Subs::new();
let mut mono_cache = MonoCache::from_subs(&ty_cache);
let def = specialize_let_fn(
ctx,
&mut ty_cache,
&mut mono_cache,
needed.t_new,
needed.name_new,
&needed.def,
);
needed.specialized = Some(def);
}
}
pub fn lower(ctx: &mut Context, program: &expr::Declarations) -> expr::Declarations {
let mut new_program = expr::Declarations::new();
for (idx, tag) in program.iter_top_down() {
match tag {
expr::DeclarationTag::Value => {
let def = specialize_let_val(ctx, &program.expressions[idx]);
new_program.push_def(def);
}
expr::DeclarationTag::Run(run_idx) => {
let run = specialize_run_def(ctx, &program.expressions[run_idx.index()]);
new_program.push_run(run);
}
_ => {}
}
}
loop_specializations(ctx);
let other_defs = ctx.specializations.solved_specializations();
new_program.extend(other_defs);
new_program
}

View file

@ -0,0 +1,340 @@
/// Given a Subs that's been populated from type inference, and a Variable,
/// ensure that Variable is monomorphic by going through and creating
/// specializations of that type wherever necessary.
///
/// This only operates at the type level. It does not create new function implementations (for example).
use bitvec::vec::BitVec;
use roc_module::ident::{Lowercase, TagName};
use roc_types::{
subs::{
Content, FlatType, RecordFields, Subs, TagExt, TupleElems, UnionLabels, UnionTags,
Variable, VariableSubsSlice,
},
types::RecordField,
};
#[derive(Debug, PartialEq, Eq)]
pub enum Problem {
// Compiler bugs; these should never happen!
TagUnionExtWasNotTagUnion,
RecordExtWasNotRecord,
TupleExtWasNotTuple,
}
/// Variables that have already been monomorphized.
pub struct MonoCache {
inner: BitVec,
}
impl MonoCache {
pub fn from_subs(subs: &Subs) -> Self {
Self {
inner: BitVec::repeat(false, subs.len()),
}
}
/// Returns true iff we know this Variable is monomorphic because
/// we've visited it before in the monomorphization process
/// (and either it was already monomorphic, or we made it so).
pub fn is_known_monomorphic(&self, var: Variable) -> bool {
match self.inner.get(var.index() as usize) {
Some(initialized) => {
// false if it has never been set, because we initialized all the bits to 0
*initialized
}
None => false,
}
}
/// Records that the given variable is now known to be monomorphic.
fn set_monomorphic(&mut self, var: Variable) {
self.inner.set(var.index() as usize, true);
}
pub fn monomorphize_var(
&mut self,
subs: &mut Subs,
problems: &mut Vec<Problem>,
var: Variable,
) {
lower_var(self, subs, problems, var);
}
}
fn lower_var(
cache: &mut MonoCache,
subs: &mut Subs,
problems: &mut Vec<Problem>,
var: Variable,
) -> Variable {
let root_var = subs.get_root_key_without_compacting(var);
if !cache.is_known_monomorphic(root_var) {
let content = match *subs.get_content_without_compacting(root_var) {
Content::Structure(flat_type) => match flat_type {
FlatType::Apply(symbol, args) => {
let new_args = args
.into_iter()
.map(|var_index| lower_var(cache, subs, problems, subs[var_index]))
.collect::<Vec<Variable>>();
Content::Structure(FlatType::Apply(
*symbol,
VariableSubsSlice::insert_into_subs(subs, new_args),
))
}
FlatType::Func(args, closure, ret) => {
let new_args = args
.into_iter()
.map(|var_index| lower_var(cache, subs, problems, subs[var_index]))
.collect::<Vec<_>>();
let new_closure = lower_var(cache, subs, problems, *closure);
let new_ret = lower_var(cache, subs, problems, *ret);
Content::Structure(FlatType::Func(
VariableSubsSlice::insert_into_subs(subs, new_args),
new_closure,
new_ret,
))
}
FlatType::Record(fields, ext) => {
let mut fields = resolve_record_ext(subs, problems, *fields, *ext);
// Now lower all the fields we gathered. Do this in a separate pass to avoid borrow errors on Subs.
for (_, field) in fields.iter_mut() {
let var = match field {
RecordField::Required(v) | RecordField::Optional(v) | RecordField::Demanded(v) => v,
RecordField::RigidRequired(v) | RecordField::RigidOptional(v) => v,
};
*var = lower_var(cache, subs, problems, *var);
}
Content::Structure(FlatType::Record(
RecordFields::insert_into_subs(subs, fields),
Variable::EMPTY_RECORD,
))
}
FlatType::Tuple(elems, ext) => {
let mut elems = resolve_tuple_ext(subs, problems, *elems, *ext);
// Now lower all the elems we gathered. Do this in a separate pass to avoid borrow errors on Subs.
lower_vars(elems.iter_mut().map(|(_, var)| var), cache, subs, problems);
Content::Structure(FlatType::Tuple(
TupleElems::insert_into_subs(subs, elems),
Variable::EMPTY_TUPLE,
))
}
FlatType::TagUnion(tags, ext) => {
let mut tags = resolve_tag_ext(subs, problems, *tags, *ext);
// Now lower all the tags we gathered. Do this in a separate pass to avoid borrow errors on Subs.
lower_vars(tags.iter_mut().flat_map(|(_, vars)| vars.iter_mut()), cache, subs, problems);
Content::Structure(FlatType::TagUnion(
UnionTags::insert_into_subs(subs, tags),
TagExt::Any(Variable::EMPTY_TAG_UNION),
))
}
FlatType::FunctionOrTagUnion(tag_names, _symbols, ext) => {
// If this is still a FunctionOrTagUnion, turn it into a TagUnion.
// First, resolve the ext var.
let mut tags = resolve_tag_ext(subs, problems, UnionTags::default(), *ext);
// Now lower all the tags we gathered from the ext var.
// (Do this in a separate pass to avoid borrow errors on Subs.)
lower_vars(tags.iter_mut().flat_map(|(_, vars)| vars.iter_mut()), cache, subs, problems);
// Then, add the tag names with no payloads. (There are no variables to lower here.)
for index in tag_names.into_iter() {
tags.push(((subs[index]).clone(), Vec::new()));
}
Content::Structure(FlatType::TagUnion(
UnionTags::insert_into_subs(subs, tags),
TagExt::Any(Variable::EMPTY_TAG_UNION),
))
}
FlatType::RecursiveTagUnion(rec, tags, ext) => {
let mut tags = resolve_tag_ext(subs, problems, *tags, *ext);
// Now lower all the tags we gathered. Do this in a separate pass to avoid borrow errors on Subs.
lower_vars(tags.iter_mut().flat_map(|(_, vars)| vars.iter_mut()), cache, subs, problems);
Content::Structure(FlatType::RecursiveTagUnion(
lower_var(cache, subs, problems, *rec),
UnionTags::insert_into_subs(subs, tags),
TagExt::Any(Variable::EMPTY_TAG_UNION),
))
}
FlatType::EmptyRecord => Content::Structure(FlatType::EmptyRecord),
FlatType::EmptyTuple => Content::Structure(FlatType::EmptyTuple),
FlatType::EmptyTagUnion => Content::Structure(FlatType::EmptyTagUnion),
},
Content::RangedNumber(_) // RangedNumber goes in Num's type parameter slot, so monomorphize it to []
| Content::FlexVar(_)
| Content::RigidVar(_)
| Content::FlexAbleVar(_, _)
| Content::RigidAbleVar(_, _)
| Content::RecursionVar { .. } => Content::Structure(FlatType::EmptyTagUnion),
Content::LambdaSet(lambda_set) => Content::LambdaSet(*lambda_set),
Content::ErasedLambda => Content::ErasedLambda,
Content::Alias(symbol, args, real, kind) => {
let todo = (); // TODO we should unwrap this, but doing that probably means changing this from root_var to other stuff.
let new_real = lower_var(cache, subs, problems, *real);
Content::Alias(*symbol, *args, new_real, *kind)
}
Content::Error => Content::Error,
};
// Update Subs so when we look up this var in the future, it's the monomorphized Content.
subs.set_content(root_var, content);
// This var is now known to be monomorphic.
cache.set_monomorphic(root_var);
}
root_var
}
fn resolve_tag_ext(
subs: &mut Subs,
problems: &mut Vec<Problem>,
mut tags: UnionTags,
mut ext: TagExt,
) -> Vec<(TagName, Vec<Variable>)> {
let mut all_tags = Vec::new();
// Collapse (recursively) all the tags in ext_var into a flat list of tags.
loop {
for (tag, vars) in tags.iter_from_subs(subs) {
all_tags.push((tag.clone(), vars.to_vec()));
}
match subs.get_content_without_compacting(ext.var()) {
Content::Structure(FlatType::TagUnion(new_tags, new_ext)) => {
// Update tags and ext and loop back again to process them.
tags = *new_tags;
ext = *new_ext;
}
Content::Structure(FlatType::FunctionOrTagUnion(tag_names, _symbols, new_ext)) => {
for index in tag_names.into_iter() {
all_tags.push((subs[index].clone(), Vec::new()));
}
ext = *new_ext;
}
Content::Structure(FlatType::EmptyTagUnion) => break,
Content::FlexVar(_) | Content::FlexAbleVar(_, _) => break,
Content::Alias(_, _, real, _) => {
// Follow the alias and process it on the next iteration of the loop.
ext = TagExt::Any(*real);
// We just processed these tags, so don't process them again!
tags = UnionLabels::default();
}
_ => {
// This should never happen! If it does, record a Problem and break.
problems.push(Problem::TagUnionExtWasNotTagUnion);
break;
}
}
}
all_tags
}
fn resolve_record_ext(
subs: &mut Subs,
problems: &mut Vec<Problem>,
mut fields: RecordFields,
mut ext: Variable,
) -> Vec<(Lowercase, RecordField<Variable>)> {
let mut all_fields = Vec::new();
// Collapse (recursively) all the fields in ext into a flat list of fields.
loop {
for (label, field) in fields.sorted_iterator(subs, ext) {
all_fields.push((label.clone(), field));
}
match subs.get_content_without_compacting(ext) {
Content::Structure(FlatType::Record(new_fields, new_ext)) => {
// Update fields and ext and loop back again to process them.
fields = *new_fields;
ext = *new_ext;
}
Content::Structure(FlatType::EmptyRecord) => break,
Content::FlexVar(_) | Content::FlexAbleVar(_, _) => break,
Content::Alias(_, _, real, _) => {
// Follow the alias and process it on the next iteration of the loop.
ext = *real;
// We just processed these fields, so don't process them again!
fields = RecordFields::empty();
}
_ => {
// This should never happen! If it does, record a Problem and break.
problems.push(Problem::RecordExtWasNotRecord);
break;
}
}
}
all_fields
}
fn resolve_tuple_ext(
subs: &mut Subs,
problems: &mut Vec<Problem>,
mut elems: TupleElems,
mut ext: Variable,
) -> Vec<(usize, Variable)> {
let mut all_elems = Vec::new();
// Collapse (recursively) all the elements in ext into a flat list of elements.
loop {
for (idx, var_index) in elems.iter_all() {
all_elems.push((idx.index as usize, subs[var_index]));
}
match subs.get_content_without_compacting(ext) {
Content::Structure(FlatType::Tuple(new_elems, new_ext)) => {
// Update elems and ext and loop back again to process them.
elems = *new_elems;
ext = *new_ext;
}
Content::Structure(FlatType::EmptyTuple) => break,
Content::FlexVar(_) | Content::FlexAbleVar(_, _) => break,
Content::Alias(_, _, real, _) => {
// Follow the alias and process it on the next iteration of the loop.
ext = *real;
// We just processed these elements, so don't process them again!
elems = TupleElems::empty();
}
_ => {
// This should never happen! If it does, record a Problem and break.
problems.push(Problem::TupleExtWasNotTuple);
break;
}
}
}
all_elems
}
/// Lower the given vars in-place.
fn lower_vars<'a>(
vars: impl Iterator<Item = &'a mut Variable>,
cache: &mut MonoCache,
subs: &mut Subs,
problems: &mut Vec<Problem>,
) {
for var in vars {
*var = lower_var(cache, subs, problems, *var);
}
}