mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 03:42:17 +00:00
Improve representation of record fields in mono
This commit is contained in:
parent
073df686bc
commit
13efa083dd
6 changed files with 459 additions and 307 deletions
|
@ -17,4 +17,4 @@ pub use mono_module::{InternedStrId, Interns};
|
||||||
pub use mono_num::Number;
|
pub use mono_num::Number;
|
||||||
pub use mono_struct::MonoFieldId;
|
pub use mono_struct::MonoFieldId;
|
||||||
pub use mono_type::{MonoType, MonoTypeId, MonoTypes};
|
pub use mono_type::{MonoType, MonoTypeId, MonoTypes};
|
||||||
pub use specialize_type::{MonoCache, Problem, RecordFieldIds, TupleElemIds};
|
pub use specialize_type::{MonoTypeCache, Problem, RecordFieldIds, TupleElemIds};
|
||||||
|
|
|
@ -1,22 +1,52 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
mono_ir::{MonoExpr, MonoExprId, MonoExprs},
|
mono_ir::{MonoExpr, MonoExprId, MonoExprs, MonoStmt, MonoStmtId},
|
||||||
mono_module::Interns,
|
mono_module::Interns,
|
||||||
mono_num::Number,
|
mono_num::Number,
|
||||||
mono_type::{MonoType, MonoTypes, Primitive},
|
mono_type::{MonoType, MonoTypes, Primitive},
|
||||||
specialize_type::{MonoCache, Problem, RecordFieldIds, TupleElemIds},
|
specialize_type::{MonoTypeCache, Problem, RecordFieldIds, TupleElemIds},
|
||||||
DebugInfo,
|
DebugInfo, MonoTypeId,
|
||||||
};
|
};
|
||||||
use bumpalo::{collections::Vec, Bump};
|
use bumpalo::{collections::Vec, Bump};
|
||||||
use roc_can::expr::{Expr, IntValue};
|
use roc_can::expr::{Expr, IntValue};
|
||||||
use roc_collections::Push;
|
use roc_collections::{Push, VecMap};
|
||||||
|
use roc_module::{ident::Lowercase, symbol::ModuleId};
|
||||||
|
use roc_region::all::Region;
|
||||||
use roc_solve::module::Solved;
|
use roc_solve::module::Solved;
|
||||||
use roc_types::subs::{Content, Subs};
|
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
||||||
use soa::{Index, NonEmptySlice, Slice};
|
use soa::NonEmptySlice;
|
||||||
|
|
||||||
|
/// Function bodies that have already been specialized.
|
||||||
|
pub struct MonoFnCache {
|
||||||
|
inner: VecMap<(ModuleId, Variable, MonoTypeId), MonoExprId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MonoFnCache {
|
||||||
|
pub fn monomorphize_fn<'a, F: 'a + FnOnce(ModuleId, Variable) -> &'a Expr>(
|
||||||
|
&mut self,
|
||||||
|
// Sometimes we need to create specializations of functions that are defined in other modules.
|
||||||
|
module_id: ModuleId,
|
||||||
|
// The function Variable stored in the original function's canonical Expr. We use this as a way to
|
||||||
|
// uniquely identify the function expr within its module, since each fn Expr gets its own unique var.
|
||||||
|
// Doing it with Variable instead of IdentId lets us cache specializations of anonymous functions too.
|
||||||
|
fn_var: Variable,
|
||||||
|
// Given a ModuleId and Variable (to uniquely identify the canonical fn Expr within its module),
|
||||||
|
// get the canonical Expr of the function itself. We need this to create a specialization of it.
|
||||||
|
get_fn_expr: F,
|
||||||
|
// This tells us which specialization of the function we want.
|
||||||
|
mono_type_id: MonoTypeId,
|
||||||
|
) -> MonoExprId {
|
||||||
|
*self
|
||||||
|
.inner
|
||||||
|
.get_or_insert((module_id, fn_var, mono_type_id), || {
|
||||||
|
todo!("TODO lower the fn_expr using Env etc. (May need to add args to this method, not sure.)");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
pub struct Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
subs: &'s mut Subs,
|
subs: &'s mut Subs,
|
||||||
types_cache: &'c mut MonoCache,
|
types_cache: &'c mut MonoTypeCache,
|
||||||
mono_types: &'t mut MonoTypes,
|
mono_types: &'t mut MonoTypes,
|
||||||
mono_exprs: &'t mut MonoExprs,
|
mono_exprs: &'t mut MonoExprs,
|
||||||
record_field_ids: RecordFieldIds,
|
record_field_ids: RecordFieldIds,
|
||||||
|
@ -30,7 +60,7 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
subs: &'s mut Solved<Subs>,
|
subs: &'s mut Solved<Subs>,
|
||||||
types_cache: &'c mut MonoCache,
|
types_cache: &'c mut MonoTypeCache,
|
||||||
mono_types: &'t mut MonoTypes,
|
mono_types: &'t mut MonoTypes,
|
||||||
mono_exprs: &'t mut MonoExprs,
|
mono_exprs: &'t mut MonoExprs,
|
||||||
record_field_ids: RecordFieldIds,
|
record_field_ids: RecordFieldIds,
|
||||||
|
@ -58,6 +88,7 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
||||||
let mono_types = &mut self.mono_types;
|
let mono_types = &mut self.mono_types;
|
||||||
let mut mono_from_var = |var| {
|
let mut mono_from_var = |var| {
|
||||||
self.types_cache.monomorphize_var(
|
self.types_cache.monomorphize_var(
|
||||||
|
self.arena,
|
||||||
self.subs,
|
self.subs,
|
||||||
mono_types,
|
mono_types,
|
||||||
&mut self.record_field_ids,
|
&mut self.record_field_ids,
|
||||||
|
@ -151,59 +182,116 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
||||||
|
|
||||||
fields.sort_by(|(name1, _), (name2, _)| name1.cmp(name2));
|
fields.sort_by(|(name1, _), (name2, _)| name1.cmp(name2));
|
||||||
|
|
||||||
// Reserve a slice of IDs up front. This is so that we have a contiguous array
|
// We want to end up with a Slice<MonoExpr> of these, so accumulate a buffer of them
|
||||||
// of field IDs at the end of this, each corresponding to the appropriate record field.
|
// and then add them to MonoExprs all at once, so they're added as one contiguous slice
|
||||||
let field_ids: Slice<MonoExprId> = self.mono_exprs.reserve_ids(fields.len() as u16);
|
// regardless of what else got added to MonoExprs as we were monomorphizing them.
|
||||||
let mut next_field_id = field_ids.start();
|
let mut buf: Vec<(MonoExpr, Region)> =
|
||||||
|
Vec::with_capacity_in(fields.len(), self.arena);
|
||||||
|
|
||||||
// Generate a MonoExpr for each field, using the reserved IDs so that we end up with
|
buf.extend(
|
||||||
// that Slice being populated with the exprs in the fields, with the correct ordering.
|
// flat_map these so we discard all the fields that monomorphized to None
|
||||||
fields.retain(|(_name, field)| {
|
fields.into_iter().flat_map(|(_name, field)| {
|
||||||
match self.to_mono_expr(&field.loc_expr.value) {
|
self.to_mono_expr(&field.loc_expr.value)
|
||||||
Some(mono_expr) => {
|
.map(|mono_expr| (mono_expr, field.loc_expr.region))
|
||||||
// Safety: This will run *at most* field.len() times, possibly less,
|
}),
|
||||||
// so this will never create an index that's out of bounds.
|
);
|
||||||
let mono_expr_id =
|
|
||||||
unsafe { MonoExprId::new_unchecked(Index::new(next_field_id)) };
|
|
||||||
|
|
||||||
next_field_id += 1;
|
// If we ended up with exactly 1 field, return it unwrapped.
|
||||||
|
if buf.len() == 1 {
|
||||||
self.mono_exprs
|
return buf.pop().map(|(expr, _region)| expr);
|
||||||
.insert(mono_expr_id, mono_expr, field.loc_expr.region);
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// Discard all the zero-sized fields as we go.
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check for zero-sized and single-field records again now that we've discarded zero-sized fields,
|
|
||||||
// because we might have ended up with 0 or 1 remaining fields.
|
|
||||||
if fields.len() > 1 {
|
|
||||||
// Safety: We just verified that there's more than 1 field.
|
|
||||||
unsafe {
|
|
||||||
Some(MonoExpr::Struct(NonEmptySlice::new_unchecked(
|
|
||||||
field_ids.start,
|
|
||||||
fields.len() as u16,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If there are 0 fields remaining, return None. If there's 1, unwrap it.
|
|
||||||
fields
|
|
||||||
.first()
|
|
||||||
.and_then(|(_, field)| self.to_mono_expr(&field.loc_expr.value))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonEmptySlice::from_slice(self.mono_exprs.extend(buf.iter().copied()))
|
||||||
|
.map(MonoExpr::Struct)
|
||||||
}
|
}
|
||||||
|
// Expr::Call((fn_var, fn_expr, capture_var, ret_var), args, called_via) => {
|
||||||
|
// let opt_ret_type = mono_from_var(*var);
|
||||||
|
|
||||||
|
// if opt_ret_type.is_none() {
|
||||||
|
// let fn_type = match self.subs.get_content_without_compacting(fn_var) {
|
||||||
|
// Content::Structure(FlatType::Func(arg_vars, closure_var, ret_var)) => {
|
||||||
|
// let todo = (); // TODO make is_effectful actually use the function's effectfulness!
|
||||||
|
// let is_effectful = false;
|
||||||
|
|
||||||
|
// // Calls to pure functions that return zero-sized types should be discarded.
|
||||||
|
// if !is_effectful {
|
||||||
|
// return None;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Use the Content we already have to directly monomorphize the function, rather than
|
||||||
|
// // calling monomorphize_var and having it redo the Subs lookup and conditionals we just did.
|
||||||
|
// self.types_cache.monomorphize_fn(
|
||||||
|
// self.subs,
|
||||||
|
// self.mono_types,
|
||||||
|
// &mut self.record_field_ids,
|
||||||
|
// &mut self.tuple_elem_ids,
|
||||||
|
// &mut self.problems,
|
||||||
|
// self.debug_info,
|
||||||
|
// *arg_vars,
|
||||||
|
// *ret_var,
|
||||||
|
// )?
|
||||||
|
// }
|
||||||
|
// _ => {
|
||||||
|
// // This function didn't have a function type. Compiler bug!
|
||||||
|
// return Some(MonoExpr::CompilerBug(Problem::FnDidNotHaveFnType));
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// let todo = (); // TODO this is where we need to specialize, which means...duplicating the fn expr body maybe? and caching it under the mono type?
|
||||||
|
// let fn_expr = self.to_mono_expr(can_expr, stmts)?;
|
||||||
|
// let args = todo!(); // TODO compute the args. This is tricky because of preallocated slices!
|
||||||
|
// let capture_type = mono_from_var(*capture_var);
|
||||||
|
|
||||||
|
// let todo = (); // How do we pre-reserve the statements? Is that possible? It does seem necessary...might not be possible though. Maybe we just need to make Vec rather than Slice on these.
|
||||||
|
|
||||||
|
// // We aren't returning anything, and this is an effectful function, so just push a statement to call it and move on.
|
||||||
|
// stmts.push(self.mono_stmts.add(MonoStmt::CallVoid {
|
||||||
|
// fn_type,
|
||||||
|
// fn_expr,
|
||||||
|
// args,
|
||||||
|
// capture_type,
|
||||||
|
// }));
|
||||||
|
|
||||||
|
// None
|
||||||
|
// } else {
|
||||||
|
// let fn_type = mono_from_var(*fn_var)?;
|
||||||
|
// let todo = (); // TODO this is where we need to specialize, which means...duplicating the fn expr body maybe? and caching it under the mono type?
|
||||||
|
// let fn_expr = self.to_mono_expr(can_expr, stmts)?;
|
||||||
|
// let args = todo!(); // TODO compute the args. This is tricky because of preallocated slices!
|
||||||
|
// let capture_type = mono_from_var(*capture_var);
|
||||||
|
|
||||||
|
// Some(MonoExpr::Call {
|
||||||
|
// fn_type,
|
||||||
|
// fn_expr,
|
||||||
|
// args,
|
||||||
|
// capture_type,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// Expr::Var(symbol, var) => Some(MonoExpr::Lookup(*symbol, mono_from_var(*var)?)),
|
||||||
|
// Expr::LetNonRec(def, loc) => {
|
||||||
|
// let expr = self.to_mono_expr(def.loc_expr.value, stmts)?;
|
||||||
|
// let todo = (); // TODO if this is an underscore pattern and we're doing a fn call, convert it to Stmt::CallVoid
|
||||||
|
// let pattern = self.to_mono_pattern(def.loc_pattern.value);
|
||||||
|
|
||||||
|
// // TODO do we need to use any of these other fields? e.g. for the types?
|
||||||
|
// // pub struct Def {
|
||||||
|
// // pub loc_pattern: Loc<Pattern>,
|
||||||
|
// // pub loc_expr: Loc<Expr>,
|
||||||
|
// // pub expr_var: Variable,
|
||||||
|
// // pub pattern_vars: SendMap<Symbol, Variable>,
|
||||||
|
// // pub annotation: Option<Annotation>,
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// todo!("split up the pattern into various Assign statements.");
|
||||||
|
// }
|
||||||
|
// Expr::LetRec(vec, loc, illegal_cycle_mark) => todo!(),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
// Expr::List {
|
// Expr::List {
|
||||||
// elem_var,
|
// elem_var,
|
||||||
// loc_elems,
|
// loc_elems,
|
||||||
// } => todo!(),
|
// } => todo!(),
|
||||||
// Expr::IngestedFile(path_buf, arc, variable) => todo!(),
|
// Expr::IngestedFile(path_buf, arc, variable) => todo!(),
|
||||||
// Expr::Var(symbol, variable) => todo!(),
|
|
||||||
// Expr::ParamsVar {
|
// Expr::ParamsVar {
|
||||||
// symbol,
|
// symbol,
|
||||||
// var,
|
// var,
|
||||||
|
@ -226,8 +314,6 @@ impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
|
||||||
// branches,
|
// branches,
|
||||||
// final_else,
|
// final_else,
|
||||||
// } => todo!(),
|
// } => todo!(),
|
||||||
// Expr::LetRec(vec, loc, illegal_cycle_mark) => todo!(),
|
|
||||||
// Expr::LetNonRec(def, loc) => todo!(),
|
|
||||||
// Expr::Call(_, vec, called_via) => todo!(),
|
// Expr::Call(_, vec, called_via) => todo!(),
|
||||||
// Expr::RunLowLevel { op, args, ret_var } => todo!(),
|
// Expr::RunLowLevel { op, args, ret_var } => todo!(),
|
||||||
// Expr::ForeignCall {
|
// Expr::ForeignCall {
|
||||||
|
|
|
@ -117,8 +117,8 @@ impl MonoExprs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_slice(&self, expr_ids: Slice<MonoExprId>) -> impl Iterator<Item = &MonoExpr> {
|
pub fn iter_slice(&self, exprs: Slice<MonoExpr>) -> impl Iterator<Item = &MonoExpr> {
|
||||||
expr_ids.indices().into_iter().map(|index| {
|
exprs.indices().into_iter().map(|index| {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
self.exprs.get(index).is_some(),
|
self.exprs.get(index).is_some(),
|
||||||
"A Slice index was not found in MonoExprs. This should never happen!"
|
"A Slice index was not found in MonoExprs. This should never happen!"
|
||||||
|
@ -128,6 +128,20 @@ impl MonoExprs {
|
||||||
unsafe { self.exprs.get_unchecked(index) }
|
unsafe { self.exprs.get_unchecked(index) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extend(
|
||||||
|
&mut self,
|
||||||
|
exprs: impl Iterator<Item = (MonoExpr, Region)> + Clone,
|
||||||
|
) -> Slice<MonoExpr> {
|
||||||
|
let start = self.exprs.len();
|
||||||
|
|
||||||
|
self.exprs.extend(exprs.clone().map(|(expr, _region)| expr));
|
||||||
|
self.regions.extend(exprs.map(|(_expr, region)| region));
|
||||||
|
|
||||||
|
let len = self.exprs.len() - start;
|
||||||
|
|
||||||
|
Slice::new(start as u32, len as u16)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
@ -141,6 +155,82 @@ impl MonoExprId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct MonoStmtId {
|
||||||
|
inner: Id<MonoStmt>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MonoStmtId {
|
||||||
|
pub(crate) unsafe fn new_unchecked(inner: Id<MonoStmt>) -> Self {
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum MonoStmt {
|
||||||
|
/// Assign to a variable.
|
||||||
|
Assign(IdentId, MonoExprId),
|
||||||
|
AssignRec(IdentId, MonoExprId),
|
||||||
|
|
||||||
|
/// Introduce a variable, e.g. `var foo_` (we'll MonoStmt::Assign to it later.)
|
||||||
|
Declare(IdentId),
|
||||||
|
|
||||||
|
/// The `return` statement
|
||||||
|
Return(MonoExprId),
|
||||||
|
|
||||||
|
/// The "crash" keyword. Importantly, during code gen we must mark this as "nothing happens after this"
|
||||||
|
Crash {
|
||||||
|
msg: MonoExprId,
|
||||||
|
/// The type of the `crash` expression (which will have unified to whatever's around it)
|
||||||
|
expr_type: MonoTypeId,
|
||||||
|
},
|
||||||
|
|
||||||
|
Expect {
|
||||||
|
condition: MonoExprId,
|
||||||
|
/// If the expectation fails, we print the values of all the named variables
|
||||||
|
/// in the final expr. These are those values.
|
||||||
|
lookups_in_cond: Slice2<MonoTypeId, IdentId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Dbg {
|
||||||
|
source_location: InternedStrId,
|
||||||
|
source: InternedStrId,
|
||||||
|
expr: MonoExprId,
|
||||||
|
expr_type: MonoTypeId,
|
||||||
|
name: IdentId,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Call a function that has no return value (or which we are discarding due to an underscore pattern).
|
||||||
|
CallVoid {
|
||||||
|
fn_type: MonoTypeId,
|
||||||
|
fn_expr: MonoExprId,
|
||||||
|
args: Slice2<MonoTypeId, MonoExprId>,
|
||||||
|
/// This is the type of the closure based only on canonical IR info,
|
||||||
|
/// not considering what other closures might later influence it.
|
||||||
|
/// Lambda set specialization may change this type later!
|
||||||
|
capture_type: MonoTypeId,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Branching
|
||||||
|
When {
|
||||||
|
/// The actual condition of the when expression.
|
||||||
|
cond: MonoExprId,
|
||||||
|
cond_type: MonoTypeId,
|
||||||
|
/// Type of each branch (and therefore the type of the entire `when` expression)
|
||||||
|
branch_type: MonoTypeId,
|
||||||
|
/// Note: if the branches weren't exhaustive, we will have already generated a default
|
||||||
|
/// branch which crashes if it's reached. (The compiler will have reported an error already;
|
||||||
|
/// this is for if you want to run anyway.)
|
||||||
|
branches: NonEmptySlice<WhenBranch>,
|
||||||
|
},
|
||||||
|
If {
|
||||||
|
/// Type of each branch (and therefore the type of the entire `if` expression)
|
||||||
|
branch_type: MonoTypeId,
|
||||||
|
branches: Slice<(MonoStmtId, MonoStmtId)>,
|
||||||
|
final_else: Option<MonoTypeId>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum MonoExpr {
|
pub enum MonoExpr {
|
||||||
Str(InternedStrId),
|
Str(InternedStrId),
|
||||||
|
@ -159,35 +249,6 @@ pub enum MonoExpr {
|
||||||
params_type: MonoTypeId,
|
params_type: MonoTypeId,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Branching
|
|
||||||
When {
|
|
||||||
/// The actual condition of the when expression.
|
|
||||||
cond: MonoExprId,
|
|
||||||
cond_type: MonoTypeId,
|
|
||||||
/// Type of each branch (and therefore the type of the entire `when` expression)
|
|
||||||
branch_type: MonoTypeId,
|
|
||||||
/// Note: if the branches weren't exhaustive, we will have already generated a default
|
|
||||||
/// branch which crashes if it's reached. (The compiler will have reported an error already;
|
|
||||||
/// this is for if you want to run anyway.)
|
|
||||||
branches: NonEmptySlice<WhenBranch>,
|
|
||||||
},
|
|
||||||
If {
|
|
||||||
/// Type of each branch (and therefore the type of the entire `if` expression)
|
|
||||||
branch_type: MonoTypeId,
|
|
||||||
branches: Slice<(MonoExprId, MonoExprId)>,
|
|
||||||
final_else: Option<MonoTypeId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Let
|
|
||||||
LetRec {
|
|
||||||
defs: Slice<Def>,
|
|
||||||
ending_expr: MonoExprId,
|
|
||||||
},
|
|
||||||
LetNonRec {
|
|
||||||
def: Def,
|
|
||||||
ending_expr: MonoExprId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// This is *only* for calling functions, not for tag application.
|
/// This is *only* for calling functions, not for tag application.
|
||||||
/// The Tag variant contains any applied values inside it.
|
/// The Tag variant contains any applied values inside it.
|
||||||
Call {
|
Call {
|
||||||
|
@ -197,7 +258,7 @@ pub enum MonoExpr {
|
||||||
/// This is the type of the closure based only on canonical IR info,
|
/// This is the type of the closure based only on canonical IR info,
|
||||||
/// not considering what other closures might later influence it.
|
/// not considering what other closures might later influence it.
|
||||||
/// Lambda set specialization may change this type later!
|
/// Lambda set specialization may change this type later!
|
||||||
closure_type: MonoTypeId,
|
capture_type: MonoTypeId,
|
||||||
},
|
},
|
||||||
RunLowLevel {
|
RunLowLevel {
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
|
@ -220,14 +281,7 @@ pub enum MonoExpr {
|
||||||
|
|
||||||
/// A record literal or a tuple literal.
|
/// A record literal or a tuple literal.
|
||||||
/// These have already been sorted alphabetically.
|
/// These have already been sorted alphabetically.
|
||||||
Struct(NonEmptySlice<MonoExprId>),
|
Struct(NonEmptySlice<MonoExpr>),
|
||||||
|
|
||||||
/// The "crash" keyword. Importantly, during code gen we must mark this as "nothing happens after this"
|
|
||||||
Crash {
|
|
||||||
msg: MonoExprId,
|
|
||||||
/// The type of the `crash` expression (which will have unified to whatever's around it)
|
|
||||||
expr_type: MonoTypeId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Look up exactly one field on a record, tuple, or tag payload.
|
/// Look up exactly one field on a record, tuple, or tag payload.
|
||||||
/// At this point we've already unified those concepts and have
|
/// At this point we've already unified those concepts and have
|
||||||
|
@ -264,28 +318,18 @@ pub enum MonoExpr {
|
||||||
args: Slice2<MonoTypeId, MonoExprId>,
|
args: Slice2<MonoTypeId, MonoExprId>,
|
||||||
},
|
},
|
||||||
|
|
||||||
Expect {
|
Block {
|
||||||
condition: MonoExprId,
|
stmts: Slice<MonoStmtId>,
|
||||||
continuation: MonoExprId,
|
final_expr: MonoExprId,
|
||||||
/// If the expectation fails, we print the values of all the named variables
|
|
||||||
/// in the final expr. These are those values.
|
|
||||||
lookups_in_cond: Slice2<MonoTypeId, IdentId>,
|
|
||||||
},
|
|
||||||
Dbg {
|
|
||||||
source_location: InternedStrId,
|
|
||||||
source: InternedStrId,
|
|
||||||
msg: MonoExprId,
|
|
||||||
continuation: MonoExprId,
|
|
||||||
expr_type: MonoTypeId,
|
|
||||||
name: IdentId,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
CompilerBug(Problem),
|
CompilerBug(Problem),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct WhenBranch {
|
pub struct WhenBranch {
|
||||||
pub patterns: Slice<MonoPatternId>,
|
pub patterns: Slice<MonoPatternId>,
|
||||||
pub body: MonoExprId,
|
pub body: Slice<MonoStmtId>,
|
||||||
pub guard: Option<MonoExprId>,
|
pub guard: Option<MonoExprId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
mono_type::{MonoTypeId, MonoTypes},
|
mono_type::{MonoTypeId, MonoTypes},
|
||||||
MonoFieldId, MonoType,
|
MonoFieldId, MonoType,
|
||||||
};
|
};
|
||||||
|
use bumpalo::{collections::Vec, Bump};
|
||||||
use roc_collections::{Push, VecMap};
|
use roc_collections::{Push, VecMap};
|
||||||
use roc_module::{ident::Lowercase, symbol::Symbol};
|
use roc_module::{ident::Lowercase, symbol::Symbol};
|
||||||
use roc_solve::module::Solved;
|
use roc_solve::module::Solved;
|
||||||
|
@ -33,6 +34,7 @@ pub enum Problem {
|
||||||
),
|
),
|
||||||
BadNumTypeParam,
|
BadNumTypeParam,
|
||||||
UninitializedReservedExpr,
|
UninitializedReservedExpr,
|
||||||
|
FnDidNotHaveFnType,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For MonoTypes that are records, store their field indices.
|
/// For MonoTypes that are records, store their field indices.
|
||||||
|
@ -46,11 +48,11 @@ pub type RecordFieldIds = VecMap<MonoTypeId, VecMap<Lowercase, MonoFieldId>>;
|
||||||
pub type TupleElemIds = VecMap<MonoTypeId, VecMap<u16, MonoFieldId>>;
|
pub type TupleElemIds = VecMap<MonoTypeId, VecMap<u16, MonoFieldId>>;
|
||||||
|
|
||||||
/// Variables that have already been monomorphized.
|
/// Variables that have already been monomorphized.
|
||||||
pub struct MonoCache {
|
pub struct MonoTypeCache {
|
||||||
inner: VecMap<Variable, MonoTypeId>,
|
inner: VecMap<Variable, MonoTypeId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MonoCache {
|
impl MonoTypeCache {
|
||||||
pub fn from_solved_subs(subs: &Solved<Subs>) -> Self {
|
pub fn from_solved_subs(subs: &Solved<Subs>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: VecMap::with_capacity(subs.inner().len()),
|
inner: VecMap::with_capacity(subs.inner().len()),
|
||||||
|
@ -61,6 +63,7 @@ impl MonoCache {
|
||||||
/// (e.g. a zero-sized type like empty record, empty tuple, a record of just those, etc.)
|
/// (e.g. a zero-sized type like empty record, empty tuple, a record of just those, etc.)
|
||||||
pub fn monomorphize_var(
|
pub fn monomorphize_var(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
arena: &Bump,
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
mono_types: &mut MonoTypes,
|
mono_types: &mut MonoTypes,
|
||||||
field_indices: &mut RecordFieldIds,
|
field_indices: &mut RecordFieldIds,
|
||||||
|
@ -70,6 +73,7 @@ impl MonoCache {
|
||||||
var: Variable,
|
var: Variable,
|
||||||
) -> Option<MonoTypeId> {
|
) -> Option<MonoTypeId> {
|
||||||
let mut env = Env {
|
let mut env = Env {
|
||||||
|
arena,
|
||||||
cache: self,
|
cache: self,
|
||||||
mono_types,
|
mono_types,
|
||||||
field_ids: field_indices,
|
field_ids: field_indices,
|
||||||
|
@ -78,12 +82,13 @@ impl MonoCache {
|
||||||
debug_info,
|
debug_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
lower_var(&mut env, subs, var)
|
env.lower_var(subs, var)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Env<'c, 'd, 'e, 'f, 'm, 'p, P: Push<Problem>> {
|
struct Env<'a, 'c, 'd, 'e, 'f, 'm, 'p, P> {
|
||||||
cache: &'c mut MonoCache,
|
arena: &'a Bump,
|
||||||
|
cache: &'c mut MonoTypeCache,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mono_types: &'m mut MonoTypes,
|
mono_types: &'m mut MonoTypes,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -95,187 +100,226 @@ struct Env<'c, 'd, 'e, 'f, 'm, 'p, P: Push<Problem>> {
|
||||||
debug_info: &'d mut Option<DebugInfo>,
|
debug_info: &'d mut Option<DebugInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_var<P: Push<Problem>>(
|
impl<'a, 'c, 'd, 'e, 'f, 'm, 'p, P: Push<Problem>> Env<'a, 'c, 'd, 'e, 'f, 'm, 'p, P> {
|
||||||
env: &mut Env<'_, '_, '_, '_, '_, '_, P>,
|
fn lower_builtin(
|
||||||
subs: &Subs,
|
&mut self,
|
||||||
var: Variable,
|
subs: &Subs,
|
||||||
) -> Option<MonoTypeId> {
|
symbol: Symbol,
|
||||||
let root_var = subs.get_root_key_without_compacting(var);
|
args: SubsSlice<Variable>,
|
||||||
|
) -> MonoTypeId {
|
||||||
|
if symbol == Symbol::NUM_NUM {
|
||||||
|
number_args_to_mono_id(args, subs, self.problems)
|
||||||
|
} else if symbol == Symbol::NUM_FLOATINGPOINT {
|
||||||
|
num_num_args_to_mono_id(symbol, args, subs, self.problems)
|
||||||
|
} else if symbol == Symbol::LIST_LIST {
|
||||||
|
todo!();
|
||||||
|
// let mut new_args = args
|
||||||
|
// .into_iter()
|
||||||
|
// .flat_map(|var_index| self.lower_var( subs, subs[var_index]));
|
||||||
|
|
||||||
// TODO: we could replace this cache by having Subs store a Content::Monomorphic(MonoTypeId)
|
// let arg = new_args.next();
|
||||||
// and then overwrite it rather than having a separate cache. That memory is already in cache
|
} else {
|
||||||
// for sure, and the lookups should be faster because they're O(1) but don't require hashing.
|
todo!("implement lower_builtin for symbol {symbol:?} - or, if all the builtins are already in here, report a compiler bug instead of panicking like this.");
|
||||||
// Kinda creates a cyclic dep though.
|
}
|
||||||
if let Some(mono_id) = env.cache.inner.get(&root_var) {
|
|
||||||
return Some(*mono_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the Content to a MonoType, often by passing an iterator. None of these iterators introduce allocations.
|
/// Exposed separately because sometimes we already looked up the Content and know it's a function,
|
||||||
let mono_id = match dbg!(*subs.get_content_without_compacting(root_var)) {
|
/// and want to continue from there without redoing the lookup.
|
||||||
Content::Structure(flat_type) => match flat_type {
|
pub fn monomorphize_fn(
|
||||||
FlatType::Apply(symbol, args) => {
|
&mut self,
|
||||||
if symbol.is_builtin() {
|
subs: &Subs,
|
||||||
lower_builtin(env, subs, symbol, args)
|
arg_vars: SubsSlice<Variable>,
|
||||||
} else {
|
ret_var: Variable,
|
||||||
todo!("handle non-builtin Apply");
|
) -> MonoTypeId {
|
||||||
|
let func = self.lower_var(subs, ret_var);
|
||||||
|
let mut mono_args = Vec::with_capacity_in(arg_vars.len(), self.arena);
|
||||||
|
|
||||||
|
mono_args.extend(
|
||||||
|
arg_vars
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|var_index| self.lower_var(subs, subs[var_index])),
|
||||||
|
);
|
||||||
|
|
||||||
|
let todo = (); // TODO populate debuginfo as appropriate
|
||||||
|
|
||||||
|
self.mono_types.add_function(func, mono_args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_var(&mut self, subs: &Subs, var: Variable) -> Option<MonoTypeId> {
|
||||||
|
let root_var = subs.get_root_key_without_compacting(var);
|
||||||
|
|
||||||
|
// TODO: we could replace this cache by having Subs store a Content::Monomorphic(MonoTypeId)
|
||||||
|
// and then overwrite it rather than having a separate cache. That memory is already in cache
|
||||||
|
// for sure, and the lookups should be faster because they're O(1) but don't require hashing.
|
||||||
|
// Kinda creates a cyclic dep though.
|
||||||
|
if let Some(mono_id) = self.cache.inner.get(&root_var) {
|
||||||
|
return Some(*mono_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the Content to a MonoType, often by passing an iterator. None of these iterators introduce allocations.
|
||||||
|
let mono_id = match dbg!(*subs.get_content_without_compacting(root_var)) {
|
||||||
|
Content::Structure(flat_type) => match flat_type {
|
||||||
|
FlatType::Apply(symbol, args) => {
|
||||||
|
if symbol.is_builtin() {
|
||||||
|
self.lower_builtin(subs, symbol, args)
|
||||||
|
} else {
|
||||||
|
todo!("handle non-builtin Apply");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
FlatType::Func(args, _capture, ret) => self.monomorphize_fn(subs, args, ret),
|
||||||
// FlatType::Func(args, _capture, ret) => {
|
_ => {
|
||||||
// let mono_args = args
|
todo!();
|
||||||
// .into_iter()
|
} /*
|
||||||
// .flat_map(|var_index| lower_var(env, subs[var_index]));
|
FlatType::Record(fields, ext) => {
|
||||||
|
let mut labeled_mono_ids = lower_record(env, fields, ext);
|
||||||
|
|
||||||
// let todo = (); // TODO populate debuginfo (if it's Some, meaning we want it)
|
// Handle the special cases of 0 fields and 1 field.
|
||||||
// let func = lower_var(env, ret);
|
match labeled_mono_ids.first() {
|
||||||
// Some(env.mono_types.add_function(func, mono_args))
|
Some((label, first_field_id)) => {
|
||||||
// }
|
if labeled_mono_ids.len() == 1 {
|
||||||
_ => {
|
// If we ended up with a single field, return it unwrapped.
|
||||||
todo!();
|
let todo = (); // TODO populate debuginfo using the label (if it's Some, meaning we want it)
|
||||||
} /*
|
let todo = (); // To preserve debuginfo, we need to actually clone this mono_id and not just return the same one.
|
||||||
FlatType::Record(fields, ext) => {
|
return Some(*first_field_id);
|
||||||
let mut labeled_mono_ids = lower_record(env, fields, ext);
|
}
|
||||||
|
}
|
||||||
// Handle the special cases of 0 fields and 1 field.
|
None => {
|
||||||
match labeled_mono_ids.first() {
|
// If we ended up with an empty record,
|
||||||
Some((label, first_field_id)) => {
|
// after removing other empty things, return None.
|
||||||
if labeled_mono_ids.len() == 1 {
|
return None;
|
||||||
// If we ended up with a single field, return it unwrapped.
|
|
||||||
let todo = (); // TODO populate debuginfo using the label (if it's Some, meaning we want it)
|
|
||||||
let todo = (); // To preserve debuginfo, we need to actually clone this mono_id and not just return the same one.
|
|
||||||
return Some(*first_field_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
|
||||||
// If we ended up with an empty record,
|
// Now we know we have at least 2 fields, so sort them by field name.
|
||||||
// after removing other empty things, return None.
|
// This can be unstable sort because all field names are known to be unique,
|
||||||
return None;
|
// so sorting unstable won't be observable (and is faster than stable).
|
||||||
|
labeled_mono_ids.sort_unstable_by(|(label1, _), (label2, _)| label1.cmp(label2));
|
||||||
|
|
||||||
|
let todo = (); // TODO populate debuginfo (if it's Some, meaning we want it)
|
||||||
|
|
||||||
|
// Safety: we already verified that this has at least 2 elements, and
|
||||||
|
// we would have early returned before this point if we had fewer than 2.
|
||||||
|
let mono_id = unsafe {
|
||||||
|
mono_types.add_struct_unchecked(labeled_mono_ids.iter().map(|(_label, mono_id)| *mono_id))
|
||||||
|
};
|
||||||
|
|
||||||
|
let labeled_indices = VecMap::from_iter(labeled_mono_ids.into_iter().enumerate().map(|(index, (label, _mono_id))| (label, MonoFieldId::new(index as u16))));
|
||||||
|
|
||||||
|
self.field_ids.insert(mono_id, labeled_indices);
|
||||||
|
|
||||||
|
Some(mono_id)
|
||||||
|
}
|
||||||
|
FlatType::Tuple(elems, ext) => {
|
||||||
|
let indexed_mono_ids = lower_tuple(env, elems, ext);
|
||||||
|
|
||||||
|
// This can be unstable sort because all indices are known to be unique,
|
||||||
|
// so sorting unstable won't be observable (and is faster than stable).
|
||||||
|
indexed_mono_ids.sort_unstable_by(|(index1, _), (index2, _)| index1.cmp(index2));
|
||||||
|
|
||||||
|
let todo = (); // TODO populate debuginfo (if it's Some, meaning we want it)
|
||||||
|
mono_types.add_struct(indexed_mono_ids.iter().map(|(_, mono_id)| *mono_id))
|
||||||
|
}
|
||||||
|
FlatType::TagUnion(tags, ext) => {
|
||||||
|
let tagged_payload_ids = lower_tag_union(env, tags, ext);
|
||||||
|
|
||||||
|
// This can be unstable sort because all tag names are known to be unique,
|
||||||
|
// so sorting unstable won't be observable (and is faster than stable).
|
||||||
|
tagged_payload_ids.sort_unstable_by(|(tag1, _), (tag2, _)| tag1.cmp(tag2));
|
||||||
|
|
||||||
|
let todo = (); // TODO populate debuginfo (if it's Some, meaning we want it)
|
||||||
|
mono_types.add_tag_union(tagged_payload_ids.iter().map(|(_, mono_id)| *mono_id))
|
||||||
|
}
|
||||||
|
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 we know we have at least 2 fields, so sort them by field name.
|
// Now lower all the tags we gathered. Do this in a separate pass to avoid borrow errors on Subs.
|
||||||
// This can be unstable sort because all field names are known to be unique,
|
lower_vars(tags.iter_mut().flat_map(|(_, vars)| vars.iter_mut()), cache, subs, problems);
|
||||||
// so sorting unstable won't be observable (and is faster than stable).
|
|
||||||
labeled_mono_ids.sort_unstable_by(|(label1, _), (label2, _)| label1.cmp(label2));
|
|
||||||
|
|
||||||
let todo = (); // TODO populate debuginfo (if it's Some, meaning we want it)
|
Content::Structure(FlatType::RecursiveTagUnion(
|
||||||
|
lower_var(cache, subs, problems, *rec),
|
||||||
// Safety: we already verified that this has at least 2 elements, and
|
UnionTags::insert_into_subs(subs, tags),
|
||||||
// we would have early returned before this point if we had fewer than 2.
|
TagExt::Any(Variable::EMPTY_TAG_UNION),
|
||||||
let mono_id = unsafe {
|
))
|
||||||
mono_types.add_struct_unchecked(labeled_mono_ids.iter().map(|(_label, mono_id)| *mono_id))
|
|
||||||
};
|
|
||||||
|
|
||||||
let labeled_indices = VecMap::from_iter(labeled_mono_ids.into_iter().enumerate().map(|(index, (label, _mono_id))| (label, MonoFieldId::new(index as u16))));
|
|
||||||
|
|
||||||
env.field_ids.insert(mono_id, labeled_indices);
|
|
||||||
|
|
||||||
Some(mono_id)
|
|
||||||
}
|
|
||||||
FlatType::Tuple(elems, ext) => {
|
|
||||||
let indexed_mono_ids = lower_tuple(env, elems, ext);
|
|
||||||
|
|
||||||
// This can be unstable sort because all indices are known to be unique,
|
|
||||||
// so sorting unstable won't be observable (and is faster than stable).
|
|
||||||
indexed_mono_ids.sort_unstable_by(|(index1, _), (index2, _)| index1.cmp(index2));
|
|
||||||
|
|
||||||
let todo = (); // TODO populate debuginfo (if it's Some, meaning we want it)
|
|
||||||
mono_types.add_struct(indexed_mono_ids.iter().map(|(_, mono_id)| *mono_id))
|
|
||||||
}
|
|
||||||
FlatType::TagUnion(tags, ext) => {
|
|
||||||
let tagged_payload_ids = lower_tag_union(env, tags, ext);
|
|
||||||
|
|
||||||
// This can be unstable sort because all tag names are known to be unique,
|
|
||||||
// so sorting unstable won't be observable (and is faster than stable).
|
|
||||||
tagged_payload_ids.sort_unstable_by(|(tag1, _), (tag2, _)| tag1.cmp(tag2));
|
|
||||||
|
|
||||||
let todo = (); // TODO populate debuginfo (if it's Some, meaning we want it)
|
|
||||||
mono_types.add_tag_union(tagged_payload_ids.iter().map(|(_, mono_id)| *mono_id))
|
|
||||||
}
|
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
FlatType::EmptyRecord|
|
||||||
|
FlatType::EmptyTuple |
|
||||||
|
FlatType::EmptyTagUnion => None,
|
||||||
|
},
|
||||||
|
Content::Error => Content::Error,
|
||||||
|
*/
|
||||||
|
},
|
||||||
|
Content::RangedNumber(range) => {
|
||||||
|
use roc_types::num::NumericRange::*;
|
||||||
|
|
||||||
Content::Structure(FlatType::TagUnion(
|
match range {
|
||||||
UnionTags::insert_into_subs(subs, tags),
|
IntAtLeastSigned(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width),
|
||||||
TagExt::Any(Variable::EMPTY_TAG_UNION),
|
IntAtLeastEitherSign(int_lit_width) => {
|
||||||
))
|
int_lit_width_to_mono_type_id(int_lit_width)
|
||||||
}
|
}
|
||||||
FlatType::RecursiveTagUnion(rec, tags, ext) => {
|
NumAtLeastSigned(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width),
|
||||||
let mut tags = resolve_tag_ext(subs, problems, *tags, *ext);
|
NumAtLeastEitherSign(int_lit_width) => {
|
||||||
|
int_lit_width_to_mono_type_id(int_lit_width)
|
||||||
// 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|
|
|
||||||
FlatType::EmptyTuple |
|
|
||||||
FlatType::EmptyTagUnion => None,
|
|
||||||
},
|
|
||||||
Content::Error => Content::Error,
|
|
||||||
*/
|
|
||||||
},
|
|
||||||
Content::RangedNumber(range) => {
|
|
||||||
use roc_types::num::NumericRange::*;
|
|
||||||
|
|
||||||
match range {
|
|
||||||
IntAtLeastSigned(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width),
|
|
||||||
IntAtLeastEitherSign(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width),
|
|
||||||
NumAtLeastSigned(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width),
|
|
||||||
NumAtLeastEitherSign(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Content::Alias(symbol, args, real, kind) => {
|
|
||||||
match kind {
|
|
||||||
AliasKind::Opaque if symbol.is_builtin() => {
|
|
||||||
let args_slice = SubsSlice::new(args.variables_start, args.type_variables_len);
|
|
||||||
lower_builtin(env, subs, symbol, args_slice)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let mono_id = lower_var(env, subs, real)?;
|
|
||||||
let todo = (); // TODO record in debuginfo the alias name for whatever we're lowering.
|
|
||||||
|
|
||||||
mono_id
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Content::Alias(symbol, args, real, kind) => {
|
||||||
Content::FlexVar(_)
|
match kind {
|
||||||
| Content::RigidVar(_)
|
AliasKind::Opaque if symbol.is_builtin() => {
|
||||||
| Content::FlexAbleVar(_, _)
|
let args_slice =
|
||||||
| Content::RigidAbleVar(_, _) => {
|
SubsSlice::new(args.variables_start, args.type_variables_len);
|
||||||
// The only way we should reach this branch is in something like a `crash`.
|
self.lower_builtin(subs, symbol, args_slice)
|
||||||
MonoTypeId::CRASH
|
}
|
||||||
}
|
_ => {
|
||||||
Content::ErasedLambda | Content::LambdaSet(_) => {
|
let mono_id = self.lower_var(subs, real)?;
|
||||||
unreachable!(
|
let todo = (); // TODO record in debuginfo the alias name for whatever we're lowering.
|
||||||
|
|
||||||
|
mono_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Content::FlexVar(_)
|
||||||
|
| Content::RigidVar(_)
|
||||||
|
| Content::FlexAbleVar(_, _)
|
||||||
|
| Content::RigidAbleVar(_, _) => {
|
||||||
|
// The only way we should reach this branch is in something like a `crash`.
|
||||||
|
MonoTypeId::CRASH
|
||||||
|
}
|
||||||
|
Content::ErasedLambda | Content::LambdaSet(_) => {
|
||||||
|
unreachable!(
|
||||||
"This new monomorphization implementation must not do anything with lambda sets, because they'll be handled later!"
|
"This new monomorphization implementation must not do anything with lambda sets, because they'll be handled later!"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
content => {
|
content => {
|
||||||
todo!("specialize this Content: {content:?}");
|
todo!("specialize this Content: {content:?}");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This var is now known to be monomorphic, so we don't repeat this work again later.
|
// This var is now known to be monomorphic, so we don't repeat this work again later.
|
||||||
// (We don't insert entries for Unit values.)
|
// (We don't insert entries for Unit values.)
|
||||||
env.cache.inner.insert(root_var, mono_id);
|
self.cache.inner.insert(root_var, mono_id);
|
||||||
|
|
||||||
Some(mono_id)
|
Some(mono_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int_lit_width_to_mono_type_id(int_lit_width: roc_can::num::IntLitWidth) -> MonoTypeId {
|
fn int_lit_width_to_mono_type_id(int_lit_width: roc_can::num::IntLitWidth) -> MonoTypeId {
|
||||||
|
@ -521,7 +565,7 @@ fn number_args_to_mono_id(
|
||||||
// labeled_mono_ids.extend(
|
// labeled_mono_ids.extend(
|
||||||
// fields
|
// fields
|
||||||
// .sorted_iterator(subs, ext)
|
// .sorted_iterator(subs, ext)
|
||||||
// .map(|(label, field)| (label, lower_var(env, subs, *field.as_inner()))),
|
// .map(|(label, field)| (label, self.lower_var( subs, *field.as_inner()))),
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// // If the ext record is nonempty, set its fields to be the next ones we handle, and loop back.
|
// // If the ext record is nonempty, set its fields to be the next ones we handle, and loop back.
|
||||||
|
@ -602,29 +646,7 @@ fn number_args_to_mono_id(
|
||||||
// problems: &mut impl Push<Problem>,
|
// problems: &mut impl Push<Problem>,
|
||||||
// ) {
|
// ) {
|
||||||
// for var in vars {
|
// for var in vars {
|
||||||
// if let Some(var) = lower_var(env, *var) // hmm not sure if this is still a good idea as a helper function
|
// if let Some(var) = self.lower_var( *var) // hmm not sure if this is still a good idea as a helper function
|
||||||
// *var = ;
|
// *var = ;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
fn lower_builtin<P: Push<Problem>>(
|
|
||||||
env: &mut Env<'_, '_, '_, '_, '_, '_, P>,
|
|
||||||
subs: &Subs,
|
|
||||||
symbol: Symbol,
|
|
||||||
args: SubsSlice<Variable>,
|
|
||||||
) -> MonoTypeId {
|
|
||||||
if symbol == Symbol::NUM_NUM {
|
|
||||||
number_args_to_mono_id(args, subs, env.problems)
|
|
||||||
} else if symbol == Symbol::NUM_FLOATINGPOINT {
|
|
||||||
num_num_args_to_mono_id(symbol, args, subs, env.problems)
|
|
||||||
} else if symbol == Symbol::LIST_LIST {
|
|
||||||
todo!();
|
|
||||||
// let mut new_args = args
|
|
||||||
// .into_iter()
|
|
||||||
// .flat_map(|var_index| lower_var(env, subs, subs[var_index]));
|
|
||||||
|
|
||||||
// let arg = new_args.next();
|
|
||||||
} else {
|
|
||||||
todo!("implement lower_builtin for symbol {symbol:?} - or, if all the builtins are already in here, report a compiler bug instead of panicking like this.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use roc_load::LoadedModule;
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use roc_solve::FunctionKind;
|
use roc_solve::FunctionKind;
|
||||||
use roc_specialize_types::{
|
use roc_specialize_types::{
|
||||||
DebugInfo, Env, Interns, MonoCache, MonoExpr, MonoExprs, MonoTypes, RecordFieldIds,
|
DebugInfo, Env, Interns, MonoExpr, MonoExprs, MonoTypeCache, MonoTypes, RecordFieldIds,
|
||||||
TupleElemIds,
|
TupleElemIds,
|
||||||
};
|
};
|
||||||
use test_compile::{trim_and_deindent, SpecializedExprOut};
|
use test_compile::{trim_and_deindent, SpecializedExprOut};
|
||||||
|
@ -59,7 +59,7 @@ fn specialize_expr<'a>(
|
||||||
|
|
||||||
let mut problems = Vec::new();
|
let mut problems = Vec::new();
|
||||||
let mut debug_info: Option<DebugInfo> = None;
|
let mut debug_info: Option<DebugInfo> = None;
|
||||||
let mut types_cache = MonoCache::from_solved_subs(&solved);
|
let mut types_cache = MonoTypeCache::from_solved_subs(&solved);
|
||||||
let mut mono_types = MonoTypes::new();
|
let mut mono_types = MonoTypes::new();
|
||||||
let mut mono_exprs = MonoExprs::new();
|
let mut mono_exprs = MonoExprs::new();
|
||||||
|
|
||||||
|
@ -152,10 +152,10 @@ fn dbg_mono_expr_help<'a>(
|
||||||
MonoExpr::Number(number) => {
|
MonoExpr::Number(number) => {
|
||||||
write!(buf, "Number({:?})", number).unwrap();
|
write!(buf, "Number({:?})", number).unwrap();
|
||||||
}
|
}
|
||||||
MonoExpr::Struct(expr_ids) => {
|
MonoExpr::Struct(field_exprs) => {
|
||||||
write!(buf, "Struct([").unwrap();
|
write!(buf, "Struct([").unwrap();
|
||||||
|
|
||||||
for (index, expr) in mono_exprs.iter_slice(expr_ids.as_slice()).enumerate() {
|
for (index, expr) in mono_exprs.iter_slice(field_exprs.as_slice()).enumerate() {
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
write!(buf, ", ").unwrap();
|
write!(buf, ", ").unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ use crate::SolvedExpr;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use roc_specialize_types::{
|
use roc_specialize_types::{
|
||||||
DebugInfo, Env, Interns, MonoCache, MonoExprId, MonoExprs, MonoTypes, Problem, RecordFieldIds,
|
DebugInfo, Env, Interns, MonoExprId, MonoExprs, MonoTypeCache, MonoTypes, Problem,
|
||||||
TupleElemIds,
|
RecordFieldIds, TupleElemIds,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -29,7 +29,7 @@ impl SpecializedExpr {
|
||||||
let mut solved_out = self.solved_expr.solve_expr(input);
|
let mut solved_out = self.solved_expr.solve_expr(input);
|
||||||
let mut problems = Vec::new();
|
let mut problems = Vec::new();
|
||||||
let mut debug_info: Option<DebugInfo> = None;
|
let mut debug_info: Option<DebugInfo> = None;
|
||||||
let mut types_cache = MonoCache::from_solved_subs(&solved_out.subs);
|
let mut types_cache = MonoTypeCache::from_solved_subs(&solved_out.subs);
|
||||||
let mut mono_types = MonoTypes::new();
|
let mut mono_types = MonoTypes::new();
|
||||||
let mut mono_exprs = MonoExprs::new();
|
let mut mono_exprs = MonoExprs::new();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue